Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b06b8ea1b | ||
|
|
0c07f53ab5 | ||
|
|
520e50ce62 | ||
|
|
33b86a4d6b |
@@ -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(¤t).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);
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user