Checkpoint source 7 workflows and in-progress diagram authoring redesign
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.
This commit is contained in:
Codex
2026-09-10 10:06:55 -07:00
parent 4b6b5fc197
commit e2ecfea69b
162 changed files with 17595 additions and 926 deletions
@@ -1,4 +1,4 @@
//! HTTP admission limits must reject endpoint decisions before transition planning.
//! HTTP admission limits reject bounded explicit decisions before planning.
use super::*;
use oa_core::{semantic::SemanticTransitionRequest, transitions::*};
@@ -28,7 +28,7 @@ async fn unchanged(server: &TestServer, before: &StoredProject, disk: &Path, byt
assert_eq!(std::fs::read(disk).unwrap(), bytes);
}
async fn endpoint_limit(semantic: bool) {
async fn decision_limit(semantic: bool, operations: bool) {
let directory = tempfile::tempdir().unwrap();
let server = TestServer::start(directory.path(), None).await;
let mut model = Project::empty();
@@ -40,19 +40,24 @@ async fn endpoint_limit(semantic: bool) {
let disk = directory.path().join(format!("{id}.json"));
let bytes = std::fs::read(&disk).unwrap();
let transition = TransitionRequest::new(TransitionDirection::OaToSa, vec![source]);
let (route, mut request, decision_path, applied_decision_path) = if semantic {
let decision_name = if operations {
"operation_omissions"
} else {
"communication_endpoints"
};
let (route, mut request, decision_parent, applied_decision_parent) = if semantic {
(
"semantic-transitions",
serde_json::to_value(SemanticTransitionRequest::new(transition)).unwrap(),
"/transition/communication_endpoints",
"/plan/request/transition/communication_endpoints",
"/transition",
"/plan/request/transition",
)
} else {
(
"transitions",
serde_json::to_value(transition).unwrap(),
"/communication_endpoints",
"/plan/request/communication_endpoints",
"",
"/plan/request",
)
};
// Obtain a real prepared envelope first: apply must enforce the same bound,
@@ -67,12 +72,24 @@ async fn endpoint_limit(semantic: bool) {
// Syntactically typed but semantically inapplicable decisions deliberately
// distinguish capacity admission (400) from core semantic rejection (422 or
// a preview containing conflicts). The bound counts entries, not unique IDs.
let decision = CommunicationEndpointDecision {
source,
source_port: CommunicationPortChoice::Reuse { port: source },
target_port: CommunicationPortChoice::Reuse { port: source },
let decision = if operations {
json!(OperationOmission {
source,
rationale: "Explicitly abstract technical operation".into()
})
} else {
json!(CommunicationEndpointDecision {
source,
source_port: CommunicationPortChoice::Reuse { port: source },
target_port: CommunicationPortChoice::Reuse { port: source },
})
};
*request.pointer_mut(decision_path).unwrap() = json!(vec![decision.clone(); 200]);
request
.pointer_mut(decision_parent)
.unwrap()
.as_object_mut()
.unwrap()
.insert(decision_name.into(), json!(vec![decision.clone(); 200]));
let response = post(&server, id, &format!("{route}/preview"), &etag, &request).await;
let status = response.status();
let body: Value = response.json().await.unwrap();
@@ -83,8 +100,17 @@ async fn endpoint_limit(semantic: bool) {
unchanged(&server, &before, &disk, &bytes).await;
let over_limit = json!(vec![decision; 201]);
*request.pointer_mut(decision_path).unwrap() = over_limit.clone();
*plan.pointer_mut(applied_decision_path).unwrap() = over_limit;
request
.pointer_mut(decision_parent)
.unwrap()
.as_object_mut()
.unwrap()
.insert(decision_name.into(), over_limit.clone());
plan.pointer_mut(applied_decision_parent)
.unwrap()
.as_object_mut()
.unwrap()
.insert(decision_name.into(), over_limit);
for (operation, body) in [("preview", request), ("apply", plan)] {
let response = post(&server, id, &format!("{route}/{operation}"), &etag, &body).await;
let status = response.status();
@@ -97,8 +123,9 @@ async fn endpoint_limit(semantic: bool) {
assert_eq!(error["code"], "invalid_request", "{error}");
let detail = error["detail"].as_str().unwrap().to_lowercase();
assert!(
detail.contains("200") && detail.contains("endpoint"),
"Capacity rejection must identify endpoint decisions: {error}"
detail.contains("200")
&& detail.contains(if operations { "omission" } else { "endpoint" }),
"Capacity rejection must identify the bounded decisions: {error}"
);
unchanged(&server, &before, &disk, &bytes).await;
}
@@ -110,10 +137,20 @@ async fn endpoint_limit(semantic: bool) {
#[tokio::test]
async fn ordinary_transition_endpoint_decision_limit_rejects_201_without_mutation() {
endpoint_limit(false).await;
decision_limit(false, false).await;
}
#[tokio::test]
async fn semantic_transition_endpoint_decision_limit_rejects_201_without_mutation() {
endpoint_limit(true).await;
decision_limit(true, false).await;
}
#[tokio::test]
async fn ordinary_transition_operation_omission_limit_rejects_201_without_mutation() {
decision_limit(false, true).await;
}
#[tokio::test]
async fn semantic_transition_operation_omission_limit_rejects_201_without_mutation() {
decision_limit(true, true).await;
}
@@ -185,7 +185,7 @@ async fn published_data_contract_queries_atomic_edits_identity_and_restart() {
.text()
.await
.unwrap();
assert!(source.starts_with("arcadia 6;"));
assert!(source.starts_with("arcadia 7;"));
assert_eq!(oa_core::language::parse(&source).unwrap(), removed.model);
server.stop().await;
let server = TestServer::start(directory.path(), None).await;
@@ -244,7 +244,7 @@ async fn authored_schema3_bundle_upgrade_reports_data_defaults_and_preserves_fil
1
);
assert_eq!(stored.source_project.files.len(), 2);
assert!(stored.source_project.files["legacy.arcadia"].contains("arcadia 6;"));
assert!(stored.source_project.files["legacy.arcadia"].contains("arcadia 7;"));
for text in [
"// Event rationale",
"// Interface rationale",
@@ -288,7 +288,7 @@ async fn existing_schema3_coupled_envelope_upgrades_in_memory_until_explicit_sav
assert_eq!(opened.revision, 17);
assert_eq!(opened.model.schema_version, oa_core::SCHEMA_VERSION);
assert_eq!(opened.migration.as_ref().unwrap().from_schema, 3);
assert!(opened.source_project.files["project.arcadia"].contains("arcadia 6;"));
assert!(opened.source_project.files["project.arcadia"].contains("arcadia 7;"));
assert_pair(&opened);
assert_eq!(std::fs::read(&filename).unwrap(), original);
assert_eq!(
@@ -475,6 +475,7 @@ async fn qualified_call_reply_values_reject_invalid_edits_and_survive_identity_s
receiver: Some(to),
message_kind,
reply_to,
operation: None,
values: vec![],
},
precondition: String::new(),
@@ -720,7 +721,7 @@ async fn qualified_call_reply_values_reject_invalid_edits_and_survive_identity_s
.text()
.await
.unwrap();
assert!(source.starts_with("arcadia 6;"));
assert!(source.starts_with("arcadia 7;"));
assert_eq!(oa_core::language::parse(&source).unwrap(), changed.model);
let saved = std::fs::read(&filename).unwrap();
server.stop().await;
@@ -576,6 +576,7 @@ fn cleanup_fixture() -> (Project, Id, Id, Id) {
receiver: None,
message_kind: MessageKind::Asynchronous,
reply_to: None,
operation: None,
values: vec![MessageValue {
path: vec![field],
field: leaf,
+1 -1
View File
@@ -113,7 +113,7 @@ fn assert_pair(stored: &StoredProject) {
let compiled = compile_bundle(&stored.source_project, ProjectLimits::default()).unwrap();
assert_eq!(compiled.bundle, stored.source_project);
assert_eq!(compiled.decoded.project, stored.model);
assert_eq!(stored.model.schema_version, 6);
assert_eq!(stored.model.schema_version, 7);
}
async fn transact(
@@ -0,0 +1,326 @@
//! Real HTTP checks for caller-instance identity and operation allocation identity.
use super::*;
use oa_core::{
ContextualLinkPurpose, Element, OccurrenceBody, OccurrenceEndpoint, OperationUse, Relation,
RelationBody, SharedBody, language, persistence,
source_project::{SourceBundle, compile_bundle},
};
use std::collections::BTreeMap;
fn operation_id(n: u128) -> Id {
Id::from_u128(0x52060906000040008000000000000000 + n)
}
fn occurrence(model: &mut Project, owner: Id, name: &str, body: OccurrenceBody) -> Id {
let mut element = model
.make_element(Kind::Occurrence, Layer::Oa, name, Some(owner))
.unwrap();
*element.shared_mut().unwrap() = SharedBody::Occurrence {
body,
precondition: String::new(),
postcondition: String::new(),
};
let id = element.id;
model.elements.insert(id, element);
model
.elements
.get_mut(&owner)
.unwrap()
.steps_mut()
.unwrap()
.push(id);
id
}
fn select_operation(model: &Project, allocation: Id) -> Element {
let mut element = model.elements[&operation_id(43)].clone();
let Some(SharedBody::Occurrence {
body: OccurrenceBody::Exchange { operation, .. },
..
}) = element.shared_mut()
else {
panic!("fixture Call");
};
*operation = Some(OperationUse {
allocation: Some(allocation),
});
element
}
struct Fixture {
model: Project,
bundle: SourceBundle,
definition: Id,
r1: Id,
r2: Id,
link: Id,
}
fn fixture() -> Fixture {
// Frozen source6 absence probe contains two parameterless operations on one
// concrete boundary. Its old bytes remain compatibility inputs.
let mut model = persistence::decode(include_bytes!(
"../../../../harness/fixtures/interface-operation-probe/output/boundary-only.json"
))
.unwrap();
let selected = select_operation(&model, operation_id(30));
model.elements.insert(selected.id, selected);
let caller = model
.add_element(Kind::Process, Layer::Oa, "Caller", None)
.unwrap();
let definition = model
.add_element(Kind::Process, Layer::Oa, "Reusable assessment", None)
.unwrap();
let work = model
.add_element(Kind::Activity, Layer::Oa, "Assess", None)
.unwrap();
let leaf = occurrence(
&mut model,
definition,
"Assessment contribution",
OccurrenceBody::Action {
action: Some(work),
lifeline: None,
},
);
let x = occurrence(
&mut model,
caller,
"Caller check",
OccurrenceBody::Action {
action: Some(work),
lifeline: None,
},
);
let r1 = occurrence(
&mut model,
caller,
"First assessment",
OccurrenceBody::SequenceReference {
sequence: Some(definition),
},
);
let r2 = occurrence(
&mut model,
caller,
"Second assessment",
OccurrenceBody::SequenceReference {
sequence: Some(definition),
},
);
let link = Id::new_v4();
model.relations.insert(
link,
Relation {
id: link,
alias: "qualified_assessment".into(),
name: "Check the first assessment".into(),
description: "Caller intent survives source and REST editing.".into(),
body: RelationBody::ContextualLink {
owner: caller,
source: OccurrenceEndpoint {
references: vec![],
occurrence: x,
},
target: OccurrenceEndpoint {
references: vec![r1],
occurrence: leaf,
},
purpose: ContextualLinkPurpose::Precedence {
guard: "check accepted".into(),
},
},
},
);
let source =
language::format(&model).replacen("{\n", "{\n include \"rationale.arcadia\";\n", 1);
let bundle = SourceBundle {
entry: "assembly.arcadia".into(),
files: BTreeMap::from([
("assembly.arcadia".into(), source),
(
"rationale.arcadia".into(),
"// Authored rationale: retain 雨 and CRLF.\r\n".into(),
),
]),
};
assert_eq!(
compile_bundle(&bundle, Default::default())
.unwrap()
.decoded
.project,
model
);
Fixture {
model,
bundle,
definition,
r1,
r2,
link,
}
}
async fn transact(
server: &TestServer,
stored: &StoredProject,
commands: Vec<Command>,
) -> reqwest::Response {
server
.client
.post(server.url(&format!("/projects/{}/transactions", stored.resource_id)))
.header("if-match", stored.etag())
.json(&json!({"commands": commands}))
.send()
.await
.unwrap()
}
async fn unchanged(server: &TestServer, before: &StoredProject, disk: &Path, bytes: &[u8]) {
let after = server.get(before.resource_id).await;
assert_eq!(after.model, before.model);
assert_eq!(after.source_project, before.source_project);
assert_eq!(after.revision, before.revision);
assert_eq!(after.etag(), before.etag());
assert_eq!(std::fs::read(disk).unwrap(), bytes);
}
#[tokio::test]
async fn source7_http_instance_and_operation_edits_preserve_identity_source_and_restart() {
let f = fixture();
let directory = tempfile::tempdir().unwrap();
let server = TestServer::start(directory.path(), None).await;
let response = server
.client
.post(server.url("/projects/import-source-project"))
.json(&f.bundle)
.send()
.await
.unwrap();
let status = response.status();
let body: Value = response.json().await.unwrap();
assert_eq!(status, StatusCode::CREATED, "{body}");
let id: Id = body["resource_id"].as_str().unwrap().parse().unwrap();
let before = server.get(id).await;
assert_eq!(before.model, f.model);
assert_eq!(before.source_project, f.bundle);
let queried: Value = server
.client
.get(server.url(&format!("/projects/{id}/relations?kind=ContextualLink")))
.send()
.await
.unwrap()
.error_for_status()
.unwrap()
.json()
.await
.unwrap();
assert_eq!(queried["items"].as_array().unwrap().len(), 1);
assert_eq!(queried["items"][0]["id"], f.link.to_string());
// Only two explicit records change: caller-instance address and allocation.
// The shared procedure, both reference instances and operation items persist.
let mut relation = before.model.relations[&f.link].clone();
if let RelationBody::ContextualLink { target, .. } = &mut relation.body {
target.references = vec![f.r2];
}
let selected = select_operation(&before.model, operation_id(31));
let response = transact(
&server,
&before,
vec![
Command::EditRelation(f.link, relation.clone()),
Command::EditElement(selected.id, selected.clone()),
],
)
.await;
let status = response.status();
let body: Value = response.json().await.unwrap();
assert_eq!(status, StatusCode::OK, "{body}");
let after = server.get(id).await;
let mut expected = before.model.clone();
expected.relations.insert(f.link, relation);
expected.elements.insert(selected.id, selected);
assert_eq!(after.model, expected);
assert_eq!(after.revision, before.revision + 1);
assert_eq!(
after.source_project.files["rationale.arcadia"],
f.bundle.files["rationale.arcadia"]
);
assert_eq!(
compile_bundle(&after.source_project, Default::default())
.unwrap()
.decoded
.project,
expected
);
assert_eq!(
after.model.elements[&f.definition],
before.model.elements[&f.definition]
);
assert_eq!(after.model.elements[&f.r1], before.model.elements[&f.r1]);
let disk = directory.path().join(format!("{id}.json"));
let bytes = std::fs::read(&disk).unwrap();
let mut bad_link = after.model.relations[&f.link].clone();
if let RelationBody::ContextualLink { target, .. } = &mut bad_link.body {
target.references = vec![f.definition]; // Definition is not an instance.
}
let wrong_operation = select_operation(&after.model, operation_id(20)); // Item is not an allocation.
for commands in [
vec![Command::EditRelation(f.link, bad_link)],
vec![Command::EditElement(wrong_operation.id, wrong_operation)],
] {
let response = transact(&server, &after, commands).await;
assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
unchanged(&server, &after, &disk, &bytes).await;
}
let mut old_json = serde_json::to_value(&after.model).unwrap();
old_json["schema_version"] = json!(6);
// Isolate admission gates: one new feature must not mask another.
let mut context_only = old_json.clone();
for element in context_only["elements"]
.as_object_mut()
.unwrap()
.values_mut()
{
if let Some(body) = element.pointer_mut("/body/Sa/Shared/Occurrence/body/Exchange") {
body.as_object_mut().unwrap().remove("operation");
}
}
let mut operation_null_only: Value = serde_json::from_slice(include_bytes!(
"../../../../harness/fixtures/interface-operation-probe/output/boundary-only.json"
))
.unwrap();
operation_null_only["elements"][operation_id(43).to_string()]["body"]["Sa"]["Shared"]["Occurrence"]
["body"]["Exchange"]["operation"] = Value::Null;
for candidate in [old_json, context_only, operation_null_only] {
let response = server
.client
.put(server.url(&format!("/projects/{id}/model")))
.header("if-match", after.etag())
.json(&candidate)
.send()
.await
.unwrap();
assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
unchanged(&server, &after, &disk, &bytes).await;
}
let mut old_source = after.source_project.clone();
let entry = old_source.files.get_mut(&old_source.entry).unwrap();
*entry = entry.replacen("arcadia 7;", "arcadia 6;", 1);
let response = server
.client
.put(server.url(&format!("/projects/{id}/source-project")))
.header("if-match", after.etag())
.json(&old_source)
.send()
.await
.unwrap();
assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
unchanged(&server, &after, &disk, &bytes).await;
server.stop().await;
let server = TestServer::start(directory.path(), None).await;
unchanged(&server, &after, &disk, &bytes).await;
server.stop().await;
}