feat: publish Dever core with project scaffolding and versioned AI skills

This commit is contained in:
Antigravity committed 2026-10-04 13:37:30 +00:00
1 parent 70fa73cdc2
commit ac458ffa7f
960 files changed
+221319 -1163

No files matched your search

@@ -0,0 +1,350 @@
#[path = "support/temp.rs"]
mod temp;
use std::fs;
use dever_core::diagnostic::Diagnostic;
use dever_core::source::SourceMap;
use temp::TemporaryDirectory;
fn sources(files: &[(&str, &str)]) -> SourceMap {
let mut sources = SourceMap::default();
for (path, text) in files {
sources.add(*path, *text);
}
sources
}
fn checked(files: &[(&str, &str)]) -> dever_core::hir::Program {
let sources = sources(files);
dever_core::check(&sources).unwrap_or_else(|errors| {
panic!(
"{}",
errors
.iter()
.map(|error| error.render(&sources))
.collect::<Vec<_>>()
.join("\n")
)
})
}
fn rejected(files: &[(&str, &str)], code: &str, message: &str) -> Vec<Diagnostic> {
let sources = sources(files);
let errors = dever_core::check(&sources).expect_err("source must be rejected");
assert!(
errors
.iter()
.any(|error| error.code == code && error.message.contains(message)),
"expected {code}: {message}; got {errors:?}"
);
errors
}
#[test]
fn app_role_exports_domain_capabilities_without_exposing_topic_files() {
let program = checked(&[
(
"main.dever",
"main() (response: user.account.Greeting) { response = user.account.greet() }",
),
(
"user/account/app/commands.dever",
"type Greeting { message: Text }\ngreet() (response: Greeting) { response = Greeting { message = int.to_text(count()) } }",
),
(
"user/account/app/queries.dever",
"count() (response: Int) { response = 1 }",
),
]);
let snapshot = program.api_snapshot();
assert!(snapshot.contains("function user.account.greet"));
assert!(snapshot.contains("function user.account.count"));
assert!(snapshot.contains("type user.account.Greeting"));
assert!(!snapshot.contains("commands"));
assert!(!snapshot.contains("queries"));
let warnings = program.application_warnings("main.main").unwrap();
assert!(warnings.iter().any(|warning| {
warning.code == "W007" && warning.message.contains("user.account.count")
}));
}
#[test]
fn source_roles_replace_public_and_enforce_cohesive_layouts() {
rejected(
&[(
"user/account/app.dever",
"public greet() (response: Text) { response = \"hello\" }",
)],
"P001",
"source role",
);
rejected(
&[(
"user/account/app/commands.dever",
"greet() (response: Text) { response = \"hello\" }",
)],
"C003",
"at least two cohesive topic files",
);
rejected(
&[
(
"user/account/app.dever",
"greet() (response: Text) { response = \"hello\" }",
),
(
"user/account/app/queries.dever",
"count() (response: Int) { response = 1 }",
),
],
"C003",
"mutually exclusive",
);
rejected(
&[(
"user/common/app.dever",
"greet() (response: Text) { response = \"hello\" }",
)],
"C003",
"generic source bucket",
);
}
#[test]
fn model_root_and_topic_keep_independent_persisted_identities() {
let root = ("user/account/model.dever", "type Account { name: Text }");
let topic = (
"user/account/model/session.dever",
"type Session { token: Text }",
);
let root_schema = checked(&[root]).model_snapshot();
let topic_schema = checked(&[topic]).model_snapshot();
let combined_schema = checked(&[root, topic]).model_snapshot();
assert!(root_schema.contains("model user.account.model name=Account table=account "));
assert!(topic_schema.contains("model user.account.model.session name=Session table=session "));
assert_eq!(
combined_schema,
format!(
"{root_schema}{}",
topic_schema.strip_prefix("dever-model 1\n").unwrap()
)
);
}
#[test]
fn api_paths_allow_one_topic_with_or_without_a_root_file() {
// Empty declarations isolate layout checks from API declaration syntax.
let root = ("user/account/api.dever", "");
let topic = ("user/account/api/session/history.dever", "");
checked(&[topic]);
checked(&[root, topic]);
}
#[test]
fn business_role_topics_still_require_cohesive_exclusive_directories() {
for role in ["app", "domain", "port", "adapter", "job"] {
let root = format!("user/account/{role}.dever");
let topic = format!("user/account/{role}/registration.dever");
rejected(&[(&topic, "")], "C003", "at least two cohesive topic files");
let errors = rejected(&[(&root, ""), (&topic, "")], "C003", "mutually exclusive");
let conflict = errors
.iter()
.find(|error| error.message.contains("mutually exclusive"))
.unwrap();
assert_eq!(conflict.related.len(), 1);
assert_eq!(conflict.related[0].message, "role file declared here");
}
}
#[test]
fn model_and_api_exceptions_preserve_topic_name_and_depth_checks() {
rejected(
&[("user/account/model/session/history.dever", "")],
"C003",
"topic directories are flat",
);
for role in ["model", "api"] {
let topic = format!("user/account/{role}/helpers.dever");
rejected(&[(&topic, "")], "C003", "generic source bucket");
}
}
#[test]
fn strict_project_loading_rejects_the_removed_service_layout() {
checked(&[
("main.dever", "main() () {}"),
(
"user/service/greeting.dever",
"greet() (response: Text) { response = \"hello\" }",
),
]);
let root = TemporaryDirectory::new();
fs::create_dir_all(root.path().join("user/service")).unwrap();
fs::write(root.path().join("main.dever"), "main() () {}\n").unwrap();
fs::write(
root.path().join("user/service/greeting.dever"),
"greet() (response: Text) { response = \"hello\" }\n",
)
.unwrap();
let sources = SourceMap::load(root.path()).unwrap();
let errors = dever_core::check(&sources).unwrap_err();
assert!(errors.iter().any(|error| {
error.code == "C003" && error.message.contains("application source must be")
}));
}
#[test]
fn only_app_crosses_domain_boundaries() {
checked(&[
(
"user/account/domain.dever",
"normalize(value: Text) (response: Text) { response = text.trim(value) }",
),
(
"user/account/app.dever",
"normalize(value: Text) (response: Text) { response = domain.normalize(value) }",
),
(
"news/article/app.dever",
"author(value: Text) (response: Text) { response = user.account.normalize(value) }",
),
(
"user/profile/app.dever",
"label(value: Text) (response: Text) { response = account.normalize(value) }",
),
(
"main.dever",
"main() (response: Text) { response = news.article.author(\" editor \" ) }",
),
]);
rejected(
&[
(
"user/account/domain.dever",
"normalize(value: Text) (response: Text) { response = text.trim(value) }",
),
(
"news/article/app.dever",
"steal(value: Text) (response: Text) { response = user.account.domain.normalize(value) }",
),
],
"C006",
"not callable from this source role",
);
}
#[test]
fn app_failures_must_use_app_owned_error_types() {
rejected(
&[
(
"user/account/domain.dever",
"type RegistrationError { error InvalidName }\nreject() () { fail(RegistrationError.InvalidName) }",
),
(
"user/account/app.dever",
"register() () { domain.reject() }",
),
],
"C006",
"cannot expose private error type",
);
checked(&[
(
"user/account/app.dever",
"type RegistrationError { error InvalidName }\nregister() () { domain.reject() }",
),
(
"user/account/domain.dever",
"reject() () { fail(app.RegistrationError.InvalidName) }",
),
]);
}
#[test]
fn model_operations_belong_to_the_owning_domain_app() {
checked(&[
("user/account/model.dever", "type Account { name: Text }"),
(
"user/account/app.dever",
"count() (total: Int) { total = model.count() }",
),
(
"main.dever",
"main() (total: Int) { total = user.account.count() }",
),
]);
rejected(
&[
("user/account/model.dever", "type Account { name: Text }"),
(
"main.dever",
"main() (total: Int) { total = user.account.model.count() }",
),
],
"C006",
"Model operations are private",
);
}
#[test]
fn api_is_an_http_entry_and_not_a_source_capability() {
let sources = sources(&[
(
"user/account/app.dever",
"greet(name: Text) (response: Text) { response = \"hello, \" + name }",
),
("user/account/api.dever", "get greeting = app.greet"),
]);
let program = dever_core::check(&sources).unwrap();
let generated = dever_core::native::emit_project(
&program,
&sources,
dever_runtime::config::RuntimeProfile::default(),
)
.unwrap();
assert!(generated.contains("(\"GET\", \"/user/account/greeting\")"));
rejected(
&[
("user/account/api.dever", "get greeting = app.greet"),
(
"user/account/app.dever",
"greet() (response: Text) { response = \"hello\" }",
),
(
"news/article/app.dever",
"steal() (response: Text) { response = user.account.api.greeting() }",
),
],
"C004",
"unknown",
);
}
#[test]
fn main_contains_only_the_boot_function_and_port_rejects_business_bodies() {
let root = TemporaryDirectory::new();
fs::write(
root.path().join("main.dever"),
"main() () {}\nhelper() () {}\n",
)
.unwrap();
let sources = SourceMap::load(root.path()).unwrap();
let errors = dever_core::check(&sources).unwrap_err();
assert!(
errors.iter().any(|error| {
error.code == "C005" && error.message.contains("only the ordinary main")
})
);
rejected(
&[(
"user/account/port.dever",
"load() (response: Text) { response = \"value\" }",
)],
"C005",
"bodyless",
);
}