Compare commits

...
9 changed files with 510 additions and 13 deletions
+8 -4
View File
@@ -3,7 +3,7 @@ mod raw_tokens;
#[path = "language/semantic_source.rs"]
mod semantic_source;
mod source3_bridge;
// Staged syntax foundation only. Public source8 admission waits for complete
// Staged typed analysis only. Public mixed source8 admission waits for complete
// declaration lowering, source updates and the root-owned resource gate.
#[cfg(test)]
mod analysis_source;
@@ -338,7 +338,7 @@ fn enum_field(key: &str) -> bool {
)
}
/// Parse V5, or explicitly migrate a valid V1/V2/V3/V4 source candidate. Parsing never
/// Parse ordinary V8, or explicitly migrate a supported older source candidate. Parsing never
/// adopts a model and never overwrites an editor draft.
pub fn parse(source: &str) -> Result<Project, Vec<Diagnostic>> {
parse_limited(source, ParseLimits::UNLIMITED)
@@ -1548,12 +1548,12 @@ impl Parser<'_> {
if if mixed_mode {
self.text() != "8"
} else {
!matches!(self.text(), "3" | "4" | "5" | "6" | "7")
!matches!(self.text(), "3" | "4" | "5" | "6" | "7" | "8")
} {
return Err(self.error(if mixed_mode {
"Private mixed syntax requires arcadia 8."
} else {
"Unsupported schema version: expected arcadia 3, 4, 5, 6 or 7."
"Unsupported schema version: expected arcadia 3, 4, 5, 6, 7 or 8."
}));
}
let schema_version = self.text().parse().expect("checked source version");
@@ -2283,6 +2283,10 @@ pub fn analyze(source: &str) -> Analysis {
#[path = "language_nesting_tests.rs"]
mod nesting_tests;
#[cfg(test)]
#[path = "language_ordinary_source8_tests.rs"]
mod ordinary_source8;
pub(crate) mod legacy {
//! Arcadia text: a readable, lossless representation of a valid OA project.
//!
@@ -380,7 +380,7 @@ fn missing_id_metadata_is_stable_and_parameter_ids_are_owner_scoped() {
}
#[test]
fn inherited_version_root_and_short_word_contracts_do_not_enable_public8() {
fn inherited_version_root_and_short_word_contracts_do_not_enable_public_typed8() {
let source = "oa_check c \"C\" { predicate unbound; }";
for version in [1, 2, 3, 4, 5, 6, 7, 9] {
let mut budget = budget();
@@ -431,8 +431,12 @@ fn inherited_version_root_and_short_word_contracts_do_not_enable_public8() {
let contextual =
declaration("calculation unit \"C\" { input and : Number; result Number; body $and; }");
assert_eq!(contextual.header.alias.name, "unit");
assert!(crate::language::parse("arcadia 8; project p \"P\" {}").is_err());
for version in 3..=7 {
let ordinary = crate::language::parse_with_report("arcadia 8; project p \"P\" {}").unwrap();
assert!(ordinary.migration.is_none());
assert_eq!(ordinary.project.schema_version, 8);
assert_eq!(ordinary.project.alias, "p");
assert_eq!(ordinary.project.name, "P");
for version in 3..=8 {
assert!(
crate::language::parse(&format!(
"arcadia {version}; project p \"P\" {{ {source} }}"
@@ -96,7 +96,16 @@ fn staged_oa_and_sa_lowering_match_complete_independent_17_uuid_oracles() {
assert_eq!(actual, expected);
admit(&actual); // the complete model's one semantic admission
}
assert!(crate::language::parse(&ORDINARY_OA.replacen("arcadia 7", "arcadia 8", 1)).is_err());
for (ordinary_source, typed_source) in [(ORDINARY_OA, OA), (ORDINARY_SA, SA)] {
let current = ordinary_source.replacen("arcadia 7", "arcadia 8", 1);
let decoded = crate::language::parse_with_report(&current).unwrap();
assert!(decoded.migration.is_none());
assert_eq!(decoded.project, ordinary(ordinary_source));
assert!(
crate::language::parse(&format!("arcadia 8; project p \"P\" {{ {typed_source} }}"))
.is_err()
);
}
}
#[test]
@@ -522,8 +522,18 @@ fn partial_wide_and_deep_input_cleanup_does_not_poison_a_fresh_parse() {
}
#[test]
fn public_source_headers_and_existing_numeric_token_controls_are_unchanged() {
assert!(crate::language::parse("arcadia 8; project p \"P\" {}").is_err());
fn public_ordinary_source8_preserves_typed_refusal_and_numeric_controls() {
let ordinary = crate::language::parse_with_report("arcadia 8; project p \"P\" {}").unwrap();
assert!(ordinary.migration.is_none());
assert_eq!(ordinary.project.schema_version, 8);
assert_eq!(ordinary.project.alias, "p");
assert_eq!(ordinary.project.name, "P");
assert!(
crate::language::parse(
"arcadia 8; project p \"P\" { calculation c \"C\" { result Number; body 1; } }"
)
.is_err()
);
for accepted in ["-1.5", "1e-2", "0", "-0"] {
let tokens = crate::language::tokenize(accepted);
assert_eq!(tokens.len(), 1);
@@ -133,6 +133,16 @@ fn typed_oa_sa_canonical_roundtrips_match_independent_projects() {
);
}
let text = canonical(&expected);
let ordinary8 = ordinary_source.replacen("arcadia 7", "arcadia 8", 1);
let ordinary_decoded = crate::language::parse_with_report(&ordinary8).unwrap();
assert!(ordinary_decoded.migration.is_none());
assert_eq!(
ordinary_decoded.project,
crate::language::parse(ordinary_source).unwrap()
);
assert!(
crate::language::parse(&format!("arcadia 8; project p \"P\" {{ {text} }}")).is_err()
);
let actual = parse_typed(crate::language::parse(ordinary_source).unwrap(), &text);
assert_eq!(actual, expected);
a::admit(&actual, a::AdmissionLimits::REFERENCE).unwrap();
@@ -147,7 +157,6 @@ fn typed_oa_sa_canonical_roundtrips_match_independent_projects() {
assert!(text.contains(&format!("{prefix}_{word} ")));
}
}
assert!(crate::language::parse("arcadia 8; project p \"P\" {}").is_err());
}
#[test]
@@ -0,0 +1,353 @@
//! Ordinary current-schema compatibility; typed analysis authoring remains private.
use super::*;
use crate::source_project::{self, ProjectLimits, SourceBundle, compile_bundle};
fn example_bundle() -> SourceBundle {
SourceBundle {
entry: "project.arcadia".into(),
files: [
(
"project.arcadia",
include_str!("../../../examples/source-project/project.arcadia"),
),
(
"oa.arcadia",
include_str!("../../../examples/source-project/oa.arcadia"),
),
(
"sa/participants.arcadia",
include_str!("../../../examples/source-project/sa/participants.arcadia"),
),
(
"sa/reporting.arcadia",
include_str!("../../../examples/source-project/sa/reporting.arcadia"),
),
(
"sa/assurance.arcadia",
include_str!("../../../examples/source-project/sa/assurance.arcadia"),
),
(
"views.arcadia",
include_str!("../../../examples/source-project/views.arcadia"),
),
]
.into_iter()
.map(|(path, text)| (path.into(), text.into()))
.collect(),
}
}
fn document(version: &str, body: &str) -> String {
format!(
"arcadia {version};\nproject p \"Project\" id \"00000000-0000-0000-0000-000000000001\" {{\n{body}\n}}\n"
)
}
fn identity(project: &Project, alias: &str) -> Id {
project
.identities()
.into_iter()
.find(|(_, name)| *name == alias)
.unwrap()
.0
}
#[test]
fn current_formatter_roundtrips_complete_ordinary_examples_and_history() {
let input = example_bundle();
let before = input.clone();
let compiled = compile_bundle(&input, ProjectLimits::default()).unwrap();
let mut example = compiled.decoded.project;
assert!(example.elements.values().any(|e| e.layer() == Layer::Oa));
assert!(example.elements.values().any(|e| e.layer() == Layer::Sa));
assert!(!example.diagrams.is_empty());
// The published example has no traces: author an explicit participant link
// rather than assuming that the example already exercises this record class.
let trace_id = Id::from_u128(0x188b_0000_0000_0000_0000_0000_0000_0001);
let trace = Trace {
id: trace_id,
alias: "rescue_participant_trace".into(),
name: "Coordination participant realization".into(),
description: "Relates the operational coordination team to its selected system.".into(),
oa: Id::parse_str("00000000-0000-4000-8000-000000000102").unwrap(),
sa: Id::parse_str("00000000-0000-4000-8000-000000000301").unwrap(),
kind: TraceKind::Participant,
baseline: None,
};
assert_eq!(example.elements[&trace.oa].kind(), Kind::Entity);
assert_eq!(example.elements[&trace.sa].kind(), Kind::System);
assert!(example.traces.insert(trace_id, trace.clone()).is_none());
assert_eq!(example.traces[&trace_id], trace);
let historical =
crate::persistence::decode(include_bytes!("../tests/fixtures/v4-reviewed-prose.json"))
.unwrap();
assert!(historical.traces.values().any(|t| t.baseline.is_some()));
for project in [example, historical] {
assert!(project.definitions.is_empty());
let source = format(&project);
assert!(source.starts_with("arcadia 8;"));
let decoded = parse_with_report(&source).unwrap();
assert_eq!(decoded.project, project);
assert!(decoded.migration.is_none());
let bundle = source_project::format_bundle(&project);
let compiled = compile_bundle(&bundle, ProjectLimits::default()).unwrap();
assert_eq!(compiled.bundle, bundle);
assert_eq!(compiled.decoded.project, project);
assert!(compiled.decoded.migration.is_none());
let again = compile_bundle(&compiled.bundle, ProjectLimits::default()).unwrap();
assert_eq!(again.bundle, compiled.bundle);
assert_eq!(again.index, compiled.index);
assert_eq!(again.decoded.project, project);
assert_eq!(again.diagnostics, compiled.diagnostics);
assert!(again.decoded.migration.is_none());
}
assert_eq!(input, before);
}
#[test]
fn versions_three_through_seven_migrate_to_idempotent_ordinary_eight() {
for version in ["3", "4", "5", "6", "7"] {
let source = document(version, "");
let decoded = parse_with_report(&source).unwrap();
assert_eq!(decoded.project.schema_version, 8);
assert!(decoded.migration.is_some());
let input = SourceBundle {
entry: "project.arcadia".into(),
files: BTreeMap::from([("project.arcadia".into(), source.clone())]),
};
let compiled = compile_bundle(&input, ProjectLimits::default()).unwrap();
assert_eq!(compiled.decoded.project, decoded.project);
assert!(compiled.decoded.migration.is_some());
assert!(compiled.bundle.files["project.arcadia"].starts_with("arcadia 8;"));
let again = compile_bundle(&compiled.bundle, ProjectLimits::default()).unwrap();
assert!(again.decoded.migration.is_none());
assert_eq!(again.decoded.project, decoded.project);
assert_eq!(again.bundle, compiled.bundle);
assert_eq!(again.index, compiled.index);
assert_eq!(input.files["project.arcadia"], source);
}
}
#[test]
fn included_nested_omitted_ids_keep_actual_index_and_unicode_crlf() {
let entry =
"// 海岸\r\narcadia 8;\r\nproject coast \"Coast\" { include \"team.arcadia\"; }\r\n";
let fragment = "// Keep nested ownership é\r\noa_entity team \"Team\" {\r\n oa_actor sailor \"Sailor\" { description \"海\"; }\r\n}\r\n";
let input = SourceBundle {
entry: "project.arcadia".into(),
files: BTreeMap::from([
("project.arcadia".into(), entry.into()),
("team.arcadia".into(), fragment.into()),
]),
};
let before = input.clone();
let compiled = compile_bundle(&input, ProjectLimits::default()).unwrap();
assert!(compiled.decoded.migration.is_none());
let p = &compiled.decoded.project;
let team = identity(p, "team");
let sailor = identity(p, "sailor");
assert_eq!(p.elements[&sailor].parent, Some(team));
assert_eq!(p.elements[&sailor].description, "海");
assert_eq!(compiled.index.lexical_owners[&sailor], team);
for (id, prefix) in [
(team, "oa_entity team \"Team\""),
(sailor, "oa_actor sailor \"Sailor\""),
] {
let location = &compiled.index.declarations[&id];
assert_eq!(location.path, "team.arcadia");
let actual = &compiled.bundle.files[&location.path][location.span.start..location.span.end];
assert!(actual.starts_with(prefix));
assert!(actual.contains(&format!("id \"{id}\"")));
assert!(actual.ends_with('}'));
}
assert!(compiled.bundle.files["project.arcadia"].starts_with("// 海岸\r\narcadia 8;\r\n"));
assert!(compiled.bundle.files["team.arcadia"].starts_with("// Keep nested ownership é\r\n"));
for text in compiled.bundle.files.values() {
assert_eq!(text.matches('\n').count(), text.matches("\r\n").count());
}
assert!(compiled.diagnostics.is_empty());
let again = compile_bundle(&compiled.bundle, ProjectLimits::default()).unwrap();
assert_eq!(again.bundle, compiled.bundle);
assert_eq!(again.index, compiled.index);
assert_eq!(again.decoded.project, *p);
let mut stale = again.clone();
stale.decoded.project.name = "stale".into();
let error = source_project::update_bundle(&stale, p, ProjectLimits::default()).unwrap_err();
assert_eq!(error[0].diagnostic.code, "stale_source_index");
assert_eq!(input, before);
}
#[test]
fn typed_analysis_future_headers_and_limits_still_refuse() {
for word in [
"quantity_kind",
"unit",
"calculation",
"oa_property",
"sa_property",
"oa_check",
"sa_check",
"oa_analysis",
"oa_case",
"sa_analysis",
"sa_case",
"oa_engineering_property",
"sa_engineering_property",
"oa_analysis_check",
"sa_analysis_check",
"oa_analysis_case",
"sa_analysis_case",
] {
let fragment = format!("{word} x \"X\" id \"00000000-0000-0000-0000-000000000002\" {{}}\n");
let source = document("8", &fragment);
assert!(parse(&source).is_err(), "{word}");
let input = SourceBundle {
entry: "project.arcadia".into(),
files: BTreeMap::from([
(
"project.arcadia".into(),
document("8", "include \"part.arcadia\";"),
),
("part.arcadia".into(), fragment.clone()),
]),
};
let error = compile_bundle(&input, ProjectLimits::default()).unwrap_err();
assert_eq!(error[0].path, "part.arcadia", "{word}");
assert_eq!(error[0].diagnostic.code, "invalid_source");
assert_eq!(error[0].diagnostic.span.start, 0);
if word.contains("engineering_property")
|| word.contains("analysis_check")
|| word.contains("analysis_case")
{
assert_eq!(
error[0].diagnostic.message,
"Analysis records require the source8 authoring implementation."
);
}
assert_eq!(input.files["part.arcadia"], fragment);
}
for version in ["0", "9", "99", "8.0"] {
let error = parse(&document(version, "")).unwrap_err();
assert_eq!(
error[0].message,
"Unsupported schema version: expected arcadia 3, 4, 5, 6, 7 or 8."
);
assert_eq!(error[0].span.start, 8);
}
let source = document("8", "");
let error = parse_limited(
&source,
ParseLimits {
max_source_bytes: source.len() - 1,
..ParseLimits::UNLIMITED
},
)
.unwrap_err();
assert_eq!(error[0].code, "source_limit");
let error = parse_limited(
&source,
ParseLimits {
max_records: 0,
..ParseLimits::UNLIMITED
},
)
.unwrap_err();
assert_eq!(error[0].code, "model_limit");
assert!(parse("arcadia 8; project").is_err());
}
#[test]
fn public_checked_save_reopens_ordinary_source_and_json_but_refuses_lost_definition() {
let project = parse_with_report(include_str!("../../../examples/text-first/project.arcadia"))
.unwrap()
.project;
let dir = tempfile::tempdir().unwrap();
for name in ["ordinary.arcadia", "ordinary.json"] {
let path = dir.path().join(name);
let bytes = crate::persistence::save_checked(&path, &project, None).unwrap();
assert_eq!(std::fs::read(&path).unwrap(), bytes);
let decoded = crate::persistence::decode_with_report(&bytes).unwrap();
assert_eq!(decoded.project, project);
assert!(decoded.migration.is_none());
assert_eq!(
crate::persistence::save_checked(&path, &project, Some(&bytes)).unwrap(),
bytes
);
}
let mut with_definition = Project::empty();
let id = Id::parse_str("188b911c-5f8b-431c-9eea-2d41f3953767").unwrap();
with_definition.definitions.insert(
id,
crate::analysis::Definition {
id,
alias: "duration".into(),
name: "Duration".into(),
description: String::new(),
body: crate::analysis::DefinitionBody::QuantityKind(
crate::analysis::QuantityKindDefinition {
dimension: crate::analysis_numeric::Dimension::new([0, 0, 1, 0, 0, 0, 0])
.unwrap(),
algebra: crate::analysis::QuantityAlgebra::Linear,
},
),
},
);
let path = dir.path().join("protected.arcadia");
let sentinel = b"external unfinished draft\r\n";
std::fs::write(&path, sentinel).unwrap();
let count = std::fs::read_dir(dir.path()).unwrap().count();
let error =
crate::persistence::save_checked(&path, &with_definition, Some(sentinel)).unwrap_err();
assert!(
matches!(error,crate::persistence::FileError::Invalid(message) if message=="Generated source does not reproduce the complete requested project.")
);
assert_eq!(std::fs::read(&path).unwrap(), sentinel);
assert_eq!(std::fs::read_dir(dir.path()).unwrap().count(), count);
}
#[test]
fn cli_preview_literals_compile_to_independent_complete_projects() {
let entry = "// 雨\r\narcadia 8;\r\nproject p \"Project\" id \"00000000-0000-0000-0000-000000000001\" {\r\ninclude \"participants.arcadia\"; include \"children.arcadia\";\r\n}\r\n";
let before_team = "// @team remains documentation\r\noa_entity team \"Team @team\" id \"00000000-0000-0000-0000-000000000002\" {}\r\n";
let after_team = "// @team remains documentation\r\noa_entity rescue_team \"Team @team\" id \"00000000-0000-0000-0000-000000000002\" {}\r\n";
let before_child = "oa_entity child \"Child\" id \"00000000-0000-0000-0000-000000000003\" { parent @team; }\r\n";
let after_child = "oa_entity child \"Child\" id \"00000000-0000-0000-0000-000000000003\" { parent @rescue_team; }\r\n";
let project_id = Id::from_u128(1);
let team_id = Id::from_u128(2);
let child_id = Id::from_u128(3);
let mut expected = Project::empty();
expected.id = project_id;
expected.alias = "p".into();
expected.name = "Project".into();
for (id, alias, name, parent) in [
(team_id, "team", "Team @team", None),
(child_id, "child", "Child", Some(team_id)),
] {
expected.elements.insert(
id,
Element {
id,
alias: alias.into(),
name: name.into(),
description: String::new(),
parent,
body: ElementBody::new(Kind::Entity, Layer::Oa).unwrap(),
},
);
}
for (team, child, alias) in [
(before_team, before_child, "team"),
(after_team, after_child, "rescue_team"),
] {
let bundle = SourceBundle {
entry: "project.arcadia".into(),
files: [
("project.arcadia", entry),
("participants.arcadia", team),
("children.arcadia", child),
]
.into_iter()
.map(|(path, source)| (path.into(), source.into()))
.collect(),
};
expected.elements.get_mut(&team_id).unwrap().alias = alias.into();
let compiled = compile_bundle(&bundle, ProjectLimits::default()).unwrap();
assert_eq!(compiled.decoded.project, expected);
assert!(compiled.decoded.migration.is_none());
assert!(compiled.diagnostics.is_empty());
assert_eq!(compiled.bundle, bundle);
}
}
+3
View File
@@ -25,7 +25,10 @@ fn frozen_v4_json_and_source_preserve_the_old_review_without_fabricating_semanti
assert_eq!(baseline.sa.name, "Approved SA name");
assert_ne!(baseline.oa.name, decoded.project.named(trace.oa));
let mut expected: Value = serde_json::from_slice(JSON4).unwrap();
assert!(expected.as_object().unwrap().get("definitions").is_none());
assert!(decoded.project.definitions.is_empty());
expected["schema_version"] = oa_core::SCHEMA_VERSION.into();
expected["definitions"] = serde_json::json!({});
assert_eq!(serde_json::to_value(&decoded.project).unwrap(), expected);
let reopened =
persistence::decode_with_report(&serde_json::to_vec(&decoded.project).unwrap())
+5 -2
View File
@@ -1036,16 +1036,19 @@ fn source7_source_bundle_migration_is_token_only_and_new_link_stays_with_caller(
let f = chain(Layer::Oa);
let mut bundle = source_project::format_bundle(&f.p);
let entry = bundle.files.get_mut(&bundle.entry).unwrap();
assert!(entry.starts_with("arcadia 8;"));
*entry = entry
.replacen("arcadia 7;", "arcadia // preserve this header\r\n 6;", 1)
.replacen("arcadia 8;", "arcadia // preserve this header\r\n 6;", 1)
.replace('\n', "\r\n");
assert!(entry.starts_with("arcadia // preserve this header"));
assert_eq!(entry.matches(" 6;").count(), 1);
let old = bundle.clone();
let compiled = source_project::compile_bundle(&bundle, Default::default()).unwrap();
assert_eq!(compiled.decoded.project, f.p);
assert_eq!(compiled.decoded.migration.as_ref().unwrap().from_schema, 6);
for (path, source) in &old.files {
let expected = if path == &old.entry {
source.replacen(" 6;", " 7;", 1)
source.replacen(" 6;", " 8;", 1)
} else {
source.clone()
};
@@ -0,0 +1,102 @@
# Ordinary source8 compatibility implementation
Base `edf096cd33350865ecfc3c65ec266a6c0d3c4000`, selected parent contract
`harness/ordinary-source8-compatibility-contract-20260925.md` at root fb9b0c5c.
The public writer already emits8. The ordinary Parser::unit version list now
accepts8 and its unsupported-version diagnostic names the accurate range.
Current ordinary resolution already checks model limits and returns no migration;
3–7 retain their existing migration paths. No new converter or dispatch adapter
was introduced. Comments distinguish this ordinary repair from private mixed
analysis authoring.
Production change is intentionally confined to that version boundary. Long
analysis constructors retain their explicit conversion refusal, and short typed
declarations remain outside the ordinary grammar. Mixed cfg gates, formatter,
public SourceIndex/update semantics, ID stabilization, resource/depth policies
and codec switches are unchanged. Ordinary8 is not complete analysis/source8.
Six new tests use the ordinary_source8 module filter. They reuse the substantial
six-file source-project example and historical reviewed-prose fixture for complete
model/current formatter and bundle/index idempotence. Other groups cover3–7
migration to stable8; nested included declarations with omitted IDs, UTF-8/CRLF,
comments and actual returned index/identity spans; stale compiled input; short
and long analysis rejection, future/malformed headers and caller limits. Public
checked-save exercises both source and JSON, actual returned bytes/reopen/repeat
save, and rejection of the real formatter's omitted definition catalog while
preserving existing destination bytes. These are authored evaluators, not passing
execution claims. The three-file CLI preview literals additionally compile to an independently constructed complete Project before and after only the team alias changes; actual CLI execution remains a separate parent gate.
The prior test exception below repairs fixture construction without weakening
its semantics; all other prior test bodies/expectations remain unchanged. The16 formerly header-masked
library workflows must run their intended nested/update/migration/transaction
assertions; new downstream failures must be investigated on their own merits.
Parent owns focused/full/integration/CLI/non-test consumer/Clippy/format gates,
including previously unreached ordinary JSON branches. DEPTH_128_VALIDATED stays
false, and ordinary8 does not qualify the private depth candidate, user-deferred
Linux or the separately recorded full-pipeline setup limitation. Source/public
save equality and no-staging-before-validation safeguards remain mandatory.
Only changed Rust files were directly formatted with skip_children; source review
found no production blocker. Compiler/runtime acceptance remains parent-owned.
## Authorized pre-execution evaluator repair
Independent integration inventory found source7_contract's
source7_source_bundle_migration_is_token_only_and_new_link_stays_with_caller
attempted to replace formatted header7 with commented6, although the formatter
already emits8. That replacement was a no-op; the later expected7 was stale too.
Parent authorized this one existing evaluator correction: first assert actual
header8, replace8 with the original commented6 fixture, assert the comment and
single6 token, and expect only6->8 during migration. All exact file/comment/module,
whole-model, caller ownership and recompilation assertions remain, as does the
original test name. This is a static finding before execution, not a reported
runtime failure; no other legacy evaluator or formatter is changed.
## Focused fixture repair after initial execution
Parent's focused run at 33b86a4d completed five tests and failed one: the example
bundle has no trace records, contrary to the new fixture's nonempty assertion.
The repair explicitly authors a Participant trace from fixed OA rescue-team UUID
102 to fixed SA system UUID301, checks endpoint kinds and the complete trace,
and includes it in the unchanged whole-model source/bundle roundtrip assertions.
Historical reviewed-prose trace/baseline coverage remains. Independent source
review also identified the actual short case spellings oa_analysis/sa_analysis;
both now appear in the ordinary refusal table alongside the invalid case aliases.
Production and the other evaluators are unchanged. These corrections are not a
runtime pass claim; the initial five-pass/one-failure record remains preserved.
## Obsolete private header assertions after full execution
Parent's full run at 520e50ce completed 298 tests: 294 passed and four failed.
All 16 previously header-masked ordinary workflows passed. The four failures
were private analysis tests that still expected an empty ordinary schema8
project to be rejected. The scoped repair accepts ordinary8 with no migration,
compares complete ordinary OA/SA projects where the existing fixed-ID fixtures
provide that oracle, and explicitly embeds actual typed source or canonical
text in schema8 to preserve public typed refusal. Declaration refusal coverage
now includes versions3 through8. Existing numeric/lowering/model assertions stay.
Only these evaluator names change:
- inherited_version_root_and_short_word_contracts_do_not_enable_public8 ->
inherited_version_root_and_short_word_contracts_do_not_enable_public_typed8
- public_source_headers_and_existing_numeric_token_controls_are_unchanged ->
public_ordinary_source8_preserves_typed_refusal_and_numeric_controls
Production is unchanged. The initial 294-pass/four-failure evidence remains;
repaired outcomes require the parent's next runtime gate.
## Historical schema4 JSON oracle reconciliation
Parent's r3_migration integration run at 0c07f53a completed four passes and one
failure. Its whole-JSON expected value updated only schema_version, omitting the
current definitions catalog. Project4::into_current already creates an empty
catalog; Project serialization requires that schema8 field. The test now asserts
that the original fixture has no definitions field and the decoded catalog is
empty, then adds exactly definitions:{} to expected JSON. Complete JSON equality,
historical baseline/prose checks, migration and reopen checks remain unchanged.
No production behavior or frozen historical fixture changes.
The parent reports completed core, six other selected integration gates and both
actual CLI operations at 0c07f53a; that evidence remains reusable because this
repair changes only the affected integration evaluator and this note. The initial
r3 four-pass/one-failure record remains preserved. Parent owns the affected r3
inventory/rerun and all-target Clippy/format checks on the repaired pin; this note
does not claim their outcome or repeat unrelated runtime acceptance.