Rust checks / check (ubuntu-latest) (push) Canceled after 0s
Rust checks / check (windows-latest) (push) Canceled after 0s
Preserve current core, source, REST, desktop, recovery, documentation and validation work, including AGENTS.md and the development summary. This is a work-in-progress checkpoint: the new compact palette has outstanding compilation errors and native acceptance; earlier passing gates retain their documented scope.
516 lines
17 KiB
Rust
516 lines
17 KiB
Rust
//! Native source7 fixture and independent exact-model/source oracle.
|
|
//! generate[-incidence] DIR; check MODE ENTRY; verify-incidence-contract.
|
|
use oa_core::{source_project::*, *};
|
|
use std::{collections::BTreeMap, fs, path::Path};
|
|
|
|
fn operation_id(n: u128) -> Id {
|
|
Id::from_u128(0x52060906000040008000000000000000 + n)
|
|
}
|
|
fn id(alias: &str) -> Id {
|
|
Id::new_v5(&operation_id(1), alias.as_bytes())
|
|
}
|
|
fn element(
|
|
p: &mut Project,
|
|
layer: Layer,
|
|
kind: Kind,
|
|
alias: &str,
|
|
name: &str,
|
|
parent: Option<Id>,
|
|
) -> Id {
|
|
let mut e = p.make_element(kind, layer, name, parent).unwrap();
|
|
e.id = id(alias);
|
|
e.alias = alias.into();
|
|
assert!(p.elements.insert(e.id, e).is_none());
|
|
id(alias)
|
|
}
|
|
fn occurrence(
|
|
p: &mut Project,
|
|
layer: Layer,
|
|
owner: Id,
|
|
alias: &str,
|
|
name: &str,
|
|
body: OccurrenceBody,
|
|
) -> Id {
|
|
let key = element(p, layer, Kind::Occurrence, alias, name, Some(owner));
|
|
*p.elements.get_mut(&key).unwrap().shared_mut().unwrap() = SharedBody::Occurrence {
|
|
body,
|
|
precondition: String::new(),
|
|
postcondition: String::new(),
|
|
};
|
|
p.elements
|
|
.get_mut(&owner)
|
|
.unwrap()
|
|
.steps_mut()
|
|
.unwrap()
|
|
.push(key);
|
|
key
|
|
}
|
|
fn fixture() -> Project {
|
|
let mut p = persistence::decode(include_bytes!(
|
|
"fixtures/interface-operation-probe/output/boundary-only.json"
|
|
))
|
|
.unwrap();
|
|
p.name = "Reusable procedures and operation identity".into();
|
|
p.alias = "source7_native_contract".into();
|
|
for (prefix, layer, sequence, work) in [
|
|
("oa", Layer::Oa, Kind::Process, Kind::Activity),
|
|
("sa", Layer::Sa, Kind::FunctionalChain, Kind::Function),
|
|
] {
|
|
let caller = element(
|
|
&mut p,
|
|
layer,
|
|
sequence,
|
|
&format!("{prefix}_caller"),
|
|
"Caller with three assessments",
|
|
None,
|
|
);
|
|
let procedure = element(
|
|
&mut p,
|
|
layer,
|
|
sequence,
|
|
&format!("{prefix}_procedure"),
|
|
"Reusable assessment A to C",
|
|
None,
|
|
);
|
|
let nested = element(
|
|
&mut p,
|
|
layer,
|
|
sequence,
|
|
&format!("{prefix}_nested_definition"),
|
|
"Nested assessment wrapper",
|
|
None,
|
|
);
|
|
for (suffix, name, owner) in [
|
|
("a", "A · Begin assessment", procedure),
|
|
("b", "B · Review assessment", procedure),
|
|
("c", "C · Finish assessment", procedure),
|
|
("x", "X · Check authorization", caller),
|
|
] {
|
|
let action = element(
|
|
&mut p,
|
|
layer,
|
|
work,
|
|
&format!("{prefix}_work_{suffix}"),
|
|
name,
|
|
None,
|
|
);
|
|
occurrence(
|
|
&mut p,
|
|
layer,
|
|
owner,
|
|
&format!("{prefix}_{suffix}"),
|
|
name,
|
|
OccurrenceBody::Action {
|
|
action: Some(action),
|
|
lifeline: None,
|
|
},
|
|
);
|
|
}
|
|
for (suffix, name, target, owner) in [
|
|
("r1", "First assessment", procedure, caller),
|
|
("r2", "Second assessment", procedure, caller),
|
|
("r3", "Nested assessment", nested, caller),
|
|
("inner", "Inner assessment", procedure, nested),
|
|
] {
|
|
occurrence(
|
|
&mut p,
|
|
layer,
|
|
owner,
|
|
&format!("{prefix}_{suffix}"),
|
|
name,
|
|
OccurrenceBody::SequenceReference {
|
|
sequence: Some(target),
|
|
},
|
|
);
|
|
}
|
|
for (a, b) in [("a", "b"), ("b", "c")] {
|
|
let alias = format!("{prefix}_{a}_then_{b}");
|
|
let key = id(&alias);
|
|
p.relations.insert(
|
|
key,
|
|
Relation {
|
|
id: key,
|
|
alias,
|
|
name: format!("{a} then {b}"),
|
|
description: String::new(),
|
|
body: RelationBody::SequenceLink {
|
|
source: id(&format!("{prefix}_{a}")),
|
|
target: id(&format!("{prefix}_{b}")),
|
|
guard: String::new(),
|
|
},
|
|
},
|
|
);
|
|
}
|
|
}
|
|
occurrence(
|
|
&mut p,
|
|
Layer::Sa,
|
|
operation_id(40),
|
|
"reset_reply",
|
|
"Correlated reset reply",
|
|
OccurrenceBody::Exchange {
|
|
exchange: Some(operation_id(34)),
|
|
sender: Some(operation_id(42)),
|
|
receiver: Some(operation_id(41)),
|
|
message_kind: MessageKind::Reply,
|
|
reply_to: Some(operation_id(43)),
|
|
values: vec![],
|
|
operation: None,
|
|
},
|
|
);
|
|
let errors: Vec<_> = validation::validate(&p)
|
|
.into_iter()
|
|
.filter(|i| i.severity == validation::Severity::Error)
|
|
.collect();
|
|
assert!(errors.is_empty(), "{errors:?}");
|
|
p
|
|
}
|
|
fn incidence_fixture() -> Project {
|
|
let p = fixture();
|
|
execute_batch(
|
|
&p,
|
|
&[Command::CreateRelation(Relation {
|
|
id: id("oa_assessment_handoff"),
|
|
alias: "oa_assessment_handoff".into(),
|
|
name: "Assessment handoff X to B".into(),
|
|
description:
|
|
"Exact work exchange used by the caller assembly; no precedence is implied.".into(),
|
|
body: RelationBody::Interaction {
|
|
source: id("oa_work_x"),
|
|
target: id("oa_work_b"),
|
|
items: vec![],
|
|
},
|
|
})],
|
|
)
|
|
.unwrap()
|
|
}
|
|
fn incidence_relation(p: &Project, mode: &str, fresh: Id) -> Relation {
|
|
let (owner, source, target, purpose, name) = match mode {
|
|
"oa-exchange-assembly" => (
|
|
id("oa_caller"),
|
|
OccurrenceEndpoint {
|
|
references: vec![],
|
|
occurrence: id("oa_x"),
|
|
},
|
|
OccurrenceEndpoint {
|
|
references: vec![id("oa_r1")],
|
|
occurrence: id("oa_b"),
|
|
},
|
|
ContextualLinkPurpose::Exchange {
|
|
exchange: id("oa_assessment_handoff"),
|
|
},
|
|
"Caller assessment handoff",
|
|
),
|
|
"sa-common-work" => (
|
|
id("sa_caller"),
|
|
OccurrenceEndpoint {
|
|
references: vec![id("sa_r1")],
|
|
occurrence: id("sa_b"),
|
|
},
|
|
OccurrenceEndpoint {
|
|
references: vec![id("sa_r2")],
|
|
occurrence: id("sa_b"),
|
|
},
|
|
ContextualLinkPurpose::CommonWork {
|
|
work: id("sa_work_b"),
|
|
},
|
|
"Shared assessment work",
|
|
),
|
|
_ => panic!("Unknown incidence mode"),
|
|
};
|
|
Relation {
|
|
id: fresh,
|
|
alias: p.fresh_alias("contextual_link", "relation", fresh),
|
|
name: name.into(),
|
|
description: String::new(),
|
|
body: RelationBody::ContextualLink {
|
|
owner,
|
|
source,
|
|
target,
|
|
purpose,
|
|
},
|
|
}
|
|
}
|
|
fn verify_incidence(p: &Project, relation: &Relation) -> Project {
|
|
use topology_context::{AnalysisLimits, TopologyIndex};
|
|
let RelationBody::ContextualLink {
|
|
owner,
|
|
source,
|
|
target,
|
|
purpose,
|
|
} = &relation.body
|
|
else {
|
|
panic!()
|
|
};
|
|
let before = TopologyIndex::new(p).analyze(*owner, AnalysisLimits::default());
|
|
let expected = execute_batch(p, &[Command::CreateRelation(relation.clone())]).unwrap();
|
|
let after = TopologyIndex::new(&expected).analyze(*owner, AnalysisLimits::default());
|
|
assert!(before.complete && after.complete);
|
|
assert!(before.assembly.is_empty());
|
|
assert_eq!(after.assembly.len(), 1);
|
|
let assembly = &after.assembly[0];
|
|
assert_eq!(assembly.relation, relation.id);
|
|
assert!(assembly.instance.is_empty());
|
|
assert_eq!(assembly.source.owner, *owner);
|
|
assert_eq!(&assembly.source.endpoint, source);
|
|
assert_eq!(assembly.target.owner, *owner);
|
|
assert_eq!(&assembly.target.endpoint, target);
|
|
assert_eq!(&assembly.purpose, purpose);
|
|
assert_eq!(
|
|
after.precedence, before.precedence,
|
|
"Incidence must not create execution order"
|
|
);
|
|
assert_eq!(after.entries, before.entries);
|
|
assert_eq!(after.exits, before.exits);
|
|
assert!(
|
|
!after
|
|
.notices
|
|
.iter()
|
|
.any(|n| n.code == "ambiguous_sequence_branch")
|
|
);
|
|
let mut invalid = relation.clone();
|
|
match &mut invalid.body {
|
|
RelationBody::ContextualLink {
|
|
source,
|
|
target,
|
|
purpose: ContextualLinkPurpose::Exchange { exchange },
|
|
..
|
|
} => {
|
|
let evidence = *exchange;
|
|
std::mem::swap(source, target);
|
|
assert!(
|
|
view_options::behavior_evidence(&expected, [*owner])
|
|
.exchanges
|
|
.contains(&evidence)
|
|
);
|
|
assert!(
|
|
!view_options::behavior_evidence(&expected, [id("oa_procedure")])
|
|
.exchanges
|
|
.contains(&evidence)
|
|
);
|
|
let deleted = execute_batch(&expected, &[Command::Delete(evidence)]).unwrap();
|
|
assert!(!deleted.relations.contains_key(&relation.id));
|
|
}
|
|
RelationBody::ContextualLink {
|
|
purpose: ContextualLinkPurpose::CommonWork { work },
|
|
..
|
|
} => {
|
|
*work = id("sa_work_x");
|
|
}
|
|
_ => panic!(),
|
|
}
|
|
assert!(
|
|
execute_batch(p, &[Command::CreateRelation(invalid)]).is_err(),
|
|
"Wrong incidence identity/direction must be rejected"
|
|
);
|
|
assert_eq!(
|
|
expected.elements, p.elements,
|
|
"No duplicated or merged work definitions"
|
|
);
|
|
expected
|
|
}
|
|
fn bundle(p: &Project) -> CompiledSourceProject {
|
|
let source = language::format(p).replacen("{\n", "{\n include \"rationale.arcadia\";\n", 1);
|
|
let b = SourceBundle {
|
|
entry: "project.arcadia".into(),
|
|
files: BTreeMap::from([
|
|
("project.arcadia".into(), source),
|
|
(
|
|
"rationale.arcadia".into(),
|
|
"// Native oracle: retain this authored 雨 note and CRLF.\r\n".into(),
|
|
),
|
|
]),
|
|
};
|
|
let c = compile_bundle(&b, ProjectLimits::default()).unwrap();
|
|
assert_eq!(c.bundle, b);
|
|
assert_eq!(&c.decoded.project, p);
|
|
c
|
|
}
|
|
fn disk(entry: &Path) -> CompiledSourceProject {
|
|
let read = source_files::load_project(entry, ProjectLimits::default()).unwrap();
|
|
assert!(
|
|
!read
|
|
.diagnostics
|
|
.iter()
|
|
.any(|d| d.diagnostic.severity == validation::Severity::Error)
|
|
);
|
|
let raw = read.reachable_bundle();
|
|
let compiled = compile_bundle(&raw, ProjectLimits::default()).unwrap();
|
|
assert_eq!(
|
|
compiled.bundle, raw,
|
|
"Native source must already be stable current source7; no migration/UUID normalization."
|
|
);
|
|
compiled
|
|
}
|
|
fn main() {
|
|
assert_eq!(SCHEMA_VERSION, 7, "This oracle measures source7 only.");
|
|
let args: Vec<_> = std::env::args().collect();
|
|
if args
|
|
.get(1)
|
|
.is_some_and(|s| s == "verify-incidence-contract")
|
|
{
|
|
assert_eq!(args.len(), 2);
|
|
for mode in ["oa-exchange-assembly", "sa-common-work"] {
|
|
let p = if mode == "oa-exchange-assembly" {
|
|
incidence_fixture()
|
|
} else {
|
|
fixture()
|
|
};
|
|
let base = bundle(&p);
|
|
let expected = verify_incidence(&p, &incidence_relation(&p, mode, operation_id(900)));
|
|
let updated = update_bundle(&base, &expected, ProjectLimits::default()).unwrap();
|
|
assert_eq!(
|
|
updated.bundle.files["rationale.arcadia"],
|
|
base.bundle.files["rationale.arcadia"]
|
|
);
|
|
assert_eq!(updated.decoded.project, expected);
|
|
println!(
|
|
"PASS {mode}: exact contextual incidence, unchanged precedence, rejected counterfactual and stable source update"
|
|
);
|
|
}
|
|
return;
|
|
}
|
|
let uses_incidence_base = args.get(1).is_some_and(|s| s == "generate-incidence")
|
|
|| (args.get(1).is_some_and(|s| s == "check")
|
|
&& args.get(2).is_some_and(|s| {
|
|
matches!(s.as_str(), "incidence-baseline" | "oa-exchange-assembly")
|
|
}));
|
|
let p = if uses_incidence_base {
|
|
incidence_fixture()
|
|
} else {
|
|
fixture()
|
|
};
|
|
let base = bundle(&p);
|
|
if args
|
|
.get(1)
|
|
.is_some_and(|s| matches!(s.as_str(), "generate" | "generate-incidence"))
|
|
{
|
|
assert_eq!(args.len(), 3);
|
|
let dir = Path::new(&args[2]);
|
|
assert!(
|
|
!dir.exists(),
|
|
"Generate into a new directory; preserve prior measured fixtures."
|
|
);
|
|
fs::create_dir_all(dir).unwrap();
|
|
for (name, bytes) in &base.bundle.files {
|
|
fs::write(dir.join(name), bytes).unwrap();
|
|
}
|
|
fs::write(
|
|
dir.join("expected-baseline.json"),
|
|
serde_json::to_vec_pretty(&p).unwrap(),
|
|
)
|
|
.unwrap();
|
|
println!(
|
|
"Generated source7 native fixture: {} elements, {} relations, {} source files",
|
|
p.elements.len(),
|
|
p.relations.len(),
|
|
base.bundle.files.len()
|
|
);
|
|
return;
|
|
}
|
|
assert_eq!(args.len(), 4, "check MODE ENTRY");
|
|
assert_eq!(args[1], "check");
|
|
let mode = args[2].as_str();
|
|
let actual = disk(Path::new(&args[3]));
|
|
let expected = if matches!(mode, "baseline" | "incidence-baseline") {
|
|
base
|
|
} else if matches!(mode, "oa-exchange-assembly" | "sa-common-work") {
|
|
let added: Vec<_> = actual
|
|
.decoded
|
|
.project
|
|
.relations
|
|
.keys()
|
|
.filter(|key| !p.relations.contains_key(key))
|
|
.copied()
|
|
.collect();
|
|
assert_eq!(added.len(), 1, "Exactly one new incidence link is expected");
|
|
let fresh = added[0];
|
|
assert!(!p.contains(fresh));
|
|
assert_eq!(fresh.get_version_num(), 4);
|
|
let expected = verify_incidence(&p, &incidence_relation(&p, mode, fresh));
|
|
update_bundle(&base, &expected, ProjectLimits::default()).unwrap()
|
|
} else if matches!(mode, "operation-reset" | "operation-status") {
|
|
let mut e = p.elements[&operation_id(43)].clone();
|
|
let Some(SharedBody::Occurrence {
|
|
body: OccurrenceBody::Exchange { operation, .. },
|
|
..
|
|
}) = e.shared_mut()
|
|
else {
|
|
panic!()
|
|
};
|
|
*operation = Some(OperationUse {
|
|
allocation: Some(operation_id(if mode == "operation-reset" {
|
|
30
|
|
} else {
|
|
31
|
|
})),
|
|
});
|
|
let expected = execute_batch(&p, &[Command::EditElement(e.id, e)]).unwrap();
|
|
update_bundle(&base, &expected, ProjectLimits::default()).unwrap()
|
|
} else {
|
|
let (prefix, nested) = match mode {
|
|
"oa-link" => ("oa", false),
|
|
"sa-link" => ("sa", false),
|
|
"oa-nested" => ("oa", true),
|
|
"sa-nested" => ("sa", true),
|
|
_ => panic!("Unknown mode"),
|
|
};
|
|
// Creation allocates one fresh UUID. Only that nonce comes from the native
|
|
// result; all authored fields, dependencies, aliases and source are predicted.
|
|
let added: Vec<_> = actual
|
|
.decoded
|
|
.project
|
|
.relations
|
|
.keys()
|
|
.filter(|key| !p.relations.contains_key(key))
|
|
.copied()
|
|
.collect();
|
|
assert_eq!(
|
|
added.len(),
|
|
1,
|
|
"Exactly one new caller-owned relation is expected."
|
|
);
|
|
let fresh = added[0];
|
|
assert!(!p.contains(fresh));
|
|
assert_eq!(fresh.get_version_num(), 4);
|
|
let relation = Relation {
|
|
id: fresh,
|
|
alias: p.fresh_alias("contextual_link", "relation", fresh),
|
|
name: "First assessment gate".into(),
|
|
description: String::new(),
|
|
body: RelationBody::ContextualLink {
|
|
owner: id(&format!("{prefix}_caller")),
|
|
source: OccurrenceEndpoint {
|
|
references: vec![],
|
|
occurrence: id(&format!("{prefix}_x")),
|
|
},
|
|
target: OccurrenceEndpoint {
|
|
references: if nested {
|
|
vec![id(&format!("{prefix}_r3")), id(&format!("{prefix}_inner"))]
|
|
} else {
|
|
vec![id(&format!("{prefix}_r1"))]
|
|
},
|
|
occurrence: id(&format!("{prefix}_b")),
|
|
},
|
|
purpose: ContextualLinkPurpose::Precedence {
|
|
guard: "Check accepted".into(),
|
|
},
|
|
},
|
|
};
|
|
let expected = execute_batch(&p, &[Command::CreateRelation(relation)]).unwrap();
|
|
update_bundle(&base, &expected, ProjectLimits::default()).unwrap()
|
|
};
|
|
assert_eq!(
|
|
actual.decoded.project, expected.decoded.project,
|
|
"Complete model mismatch; no identity/metadata exclusions."
|
|
);
|
|
assert_eq!(
|
|
actual.bundle, expected.bundle,
|
|
"Complete authored source mismatch; no whitespace or version normalization."
|
|
);
|
|
println!(
|
|
"PASS {mode}: exact model and all{}source files",
|
|
expected.bundle.files.len()
|
|
);
|
|
}
|