Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93d2a6f9af | ||
|
|
e68a839599 | ||
|
|
c655c5eeaa | ||
|
|
6f247af795 | ||
|
|
72b1bb7aa7 | ||
|
|
44217307f9 | ||
|
|
340a5c56b0 | ||
|
|
57aec979f6 |
@@ -1308,6 +1308,27 @@ impl App {
|
||||
let preview_read_only = self.process_preview_primary().is_some();
|
||||
let selection_boundary = self.multiselect_boundary();
|
||||
// Reject delayed gestures before they can retire newer input ownership.
|
||||
if let Message::Connection(
|
||||
crate::connection_edit::Event::Endpoint(captured, _)
|
||||
| crate::connection_edit::Event::Restart(captured),
|
||||
) = &message
|
||||
&& (self.route_context() != Some(*captured)
|
||||
|| self
|
||||
.connection
|
||||
.as_ref()
|
||||
.is_none_or(|session| session.context != *captured))
|
||||
{
|
||||
return Task::none();
|
||||
}
|
||||
// Selection can change without advancing the captured canvas context.
|
||||
if let Message::Route(
|
||||
crate::route_edit::Event::Commit(captured, changed, ..)
|
||||
| crate::route_edit::Event::CommitAttachment(captured, changed, ..),
|
||||
) = &message
|
||||
&& (self.route_context() != Some(*captured) || changed.edge != self.selected)
|
||||
{
|
||||
return Task::none();
|
||||
}
|
||||
if let Message::DiagramSelection(event)
|
||||
| Message::StateDiagram(crate::behavior::state_edit::Event::Selection(event)) = &message
|
||||
&& !self.multiselect_envelope(event)
|
||||
|
||||
@@ -2,6 +2,11 @@ use super::*;
|
||||
use crate::app::multiselect::{self, Choice};
|
||||
use crate::diagram_usage::Target as SelectionTarget;
|
||||
|
||||
#[path = "group/connection_lifetime.rs"]
|
||||
mod connection_lifetime;
|
||||
#[path = "group/route_lifetime.rs"]
|
||||
mod route_lifetime;
|
||||
|
||||
struct Group {
|
||||
f: Fixture,
|
||||
y: Id,
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
//! Delayed captured drawing packets must not retire a newer Allocation owner.
|
||||
use super::*;
|
||||
use crate::connection_edit::Event as ConnectionEvent;
|
||||
use crate::connection_tool::Tool;
|
||||
|
||||
fn drawing_tool(layer: Layer) -> Tool {
|
||||
match layer {
|
||||
Layer::Oa => Tool::Interaction,
|
||||
Layer::Sa => Tool::FunctionalExchange,
|
||||
}
|
||||
}
|
||||
|
||||
fn drawing_source(g: &mut Group, layer: Layer) -> crate::route_edit::Context {
|
||||
let _ =
|
||||
g.f.app
|
||||
.update(Message::Connection(ConnectionEvent::Choose(Some(
|
||||
drawing_tool(layer),
|
||||
))));
|
||||
let context = g.f.app.route_context().unwrap();
|
||||
let _ =
|
||||
g.f.app
|
||||
.update(Message::Connection(ConnectionEvent::Endpoint(
|
||||
context, g.f.work,
|
||||
)));
|
||||
let session = g.f.app.connection.as_ref().unwrap();
|
||||
assert_eq!(session.context, context);
|
||||
assert_eq!(session.source, Some(g.f.work));
|
||||
assert!(session.candidates.contains(&g.y));
|
||||
assert_eq!(g.f.app.route_context(), Some(context));
|
||||
context
|
||||
}
|
||||
|
||||
fn assert_captured_noop(app: &mut App, captured: crate::route_edit::Context, target: Id) {
|
||||
for event in [
|
||||
ConnectionEvent::Endpoint(captured, target),
|
||||
ConnectionEvent::Restart(captured),
|
||||
] {
|
||||
assert_connection_noop(app, event);
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_connection_noop(app: &mut App, event: ConnectionEvent) {
|
||||
let document = app.document.snapshot();
|
||||
let selected = selection(app);
|
||||
let context = app.route_context();
|
||||
let camera = (app.pan, app.zoom, app.canvas_epoch, app.camera_epoch);
|
||||
let ownership = (app.allocation_drag.epoch, app.allocation_drag.arrange_next);
|
||||
let active = app
|
||||
.allocation_drag
|
||||
.active
|
||||
.as_ref()
|
||||
.map(|a| (a.press.clone(), a.crossed, a.cache.len()));
|
||||
let preview = app.allocation_drag.preview.as_ref().map(|p| {
|
||||
(
|
||||
p.token,
|
||||
p.placement.diagram.clone(),
|
||||
p.placement.delta,
|
||||
p.project().clone(),
|
||||
p.routed.clone(),
|
||||
)
|
||||
});
|
||||
let feedback = app.allocation_drag.feedback.as_ref().map(|f| {
|
||||
(
|
||||
f.press.token,
|
||||
f.text.clone(),
|
||||
f.refused,
|
||||
f.targets.clone(),
|
||||
f.inspection_targets.clone(),
|
||||
)
|
||||
});
|
||||
let connection = |app: &App| {
|
||||
app.connection
|
||||
.as_ref()
|
||||
.map(|s| (s.context, s.tool, s.source, s.candidates.clone()))
|
||||
};
|
||||
let session = connection(app);
|
||||
let interaction = app.source_symbols.interaction;
|
||||
let status = app.status.clone();
|
||||
let _ = app.update(Message::Connection(event));
|
||||
assert_eq!(selection(app), selected);
|
||||
assert_eq!(app.route_context(), context);
|
||||
assert_eq!(
|
||||
(app.pan, app.zoom, app.canvas_epoch, app.camera_epoch),
|
||||
camera
|
||||
);
|
||||
assert_eq!(
|
||||
(app.allocation_drag.epoch, app.allocation_drag.arrange_next),
|
||||
ownership,
|
||||
"rejected drawing packet must not retire the newer input owner"
|
||||
);
|
||||
match (active, app.allocation_drag.active.as_ref()) {
|
||||
(Some((press, crossed, cached)), Some(current)) => {
|
||||
assert!(Arc::ptr_eq(&press, ¤t.press));
|
||||
assert_eq!((current.crossed, current.cache.len()), (crossed, cached));
|
||||
}
|
||||
(None, None) => {}
|
||||
_ => panic!("rejected drawing packet changed the active Allocation token"),
|
||||
}
|
||||
match (preview, app.allocation_drag.preview.as_ref()) {
|
||||
(Some((token, diagram, delta, project, routed)), Some(current)) => {
|
||||
assert_eq!(current.token, token);
|
||||
assert_eq!(current.placement.diagram, diagram);
|
||||
assert_eq!(current.placement.delta, delta);
|
||||
assert_eq!(current.project(), &project);
|
||||
assert!(Arc::ptr_eq(¤t.routed, &routed));
|
||||
}
|
||||
(None, None) => {}
|
||||
_ => panic!("rejected drawing packet changed the Allocation preview"),
|
||||
}
|
||||
assert_eq!(
|
||||
app.allocation_drag.feedback.as_ref().map(|f| (
|
||||
f.press.token,
|
||||
f.text.clone(),
|
||||
f.refused,
|
||||
f.targets.clone(),
|
||||
f.inspection_targets.clone(),
|
||||
)),
|
||||
feedback
|
||||
);
|
||||
assert_eq!(connection(app), session);
|
||||
assert_eq!(app.source_symbols.interaction, interaction);
|
||||
assert_eq!(app.status, status);
|
||||
assert!(Arc::ptr_eq(&document, &app.document.snapshot()));
|
||||
assert_eq!(app.source_workspace.bundle(), document.bundle);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn captured_connection_after_cancel_preserves_arrange_arm_and_live_group() {
|
||||
for layer in [Layer::Oa, Layer::Sa] {
|
||||
let mut g = group(layer);
|
||||
let old = drawing_source(&mut g, layer);
|
||||
let _ = g.f.app.update(Message::Escape);
|
||||
assert!(g.f.app.connection.is_none());
|
||||
select(&mut g.f.app, &[g.an, g.yn]);
|
||||
let _ = g.f.app.update(Message::WorkDrag(Event::ArrangeNext));
|
||||
assert!(g.f.app.allocation_drag.arrange_next);
|
||||
let current = g.f.app.route_context().unwrap();
|
||||
assert_captured_noop(&mut g.f.app, old, g.y);
|
||||
// Matching context with no session isolates the missing-owner guard.
|
||||
assert_captured_noop(&mut g.f.app, current, g.y);
|
||||
|
||||
let before = g.f.app.project().clone();
|
||||
let source = g.f.app.document.bundle().clone();
|
||||
let held = selection(&g.f.app);
|
||||
let from = node_point(&g.f.app, g.an);
|
||||
let to = from + Vector::new(400., 0.);
|
||||
let mut state = CanvasState::default();
|
||||
let press = begin(&mut g.f.app, &mut state, from);
|
||||
assert_eq!(press.mode, Mode::Arrange);
|
||||
assert!(!g.f.app.allocation_drag.arrange_next);
|
||||
move_pointer(&mut g.f.app, &mut state, to);
|
||||
assert!(matches!(
|
||||
g.f.app
|
||||
.allocation_drag
|
||||
.preview
|
||||
.as_ref()
|
||||
.map(|p| &p.candidate),
|
||||
Some(Candidate::Arrange(_))
|
||||
));
|
||||
let current = g.f.app.route_context().unwrap();
|
||||
assert_captured_noop(&mut g.f.app, old, g.y);
|
||||
assert_captured_noop(&mut g.f.app, current, g.y);
|
||||
release(&mut g.f.app, &mut state, to);
|
||||
let mut expected = before.clone();
|
||||
let diagram = expected.diagrams.get_mut(&g.f.diagram).unwrap();
|
||||
diagram.nodes.get_mut(&g.an).unwrap().position = xy(500., 100.);
|
||||
diagram.nodes.get_mut(&g.f.node).unwrap().position = xy(540., 200.);
|
||||
diagram.nodes.get_mut(&g.yn).unwrap().position = xy(540., 600.);
|
||||
assert_eq!(selection(&g.f.app), held);
|
||||
exact_history(&mut g.f.app, &before, &source, &expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn captured_connection_context_mismatches_preserve_the_current_session() {
|
||||
let mut g = group(Layer::Oa);
|
||||
let current = drawing_source(&mut g, Layer::Oa);
|
||||
// Session and current context agree: each old captured field is the sole
|
||||
// invalid relationship, not hidden behind a missing session.
|
||||
for captured in [
|
||||
crate::route_edit::Context {
|
||||
document: Id::new_v4(),
|
||||
..current
|
||||
},
|
||||
crate::route_edit::Context {
|
||||
diagram: Id::new_v4(),
|
||||
..current
|
||||
},
|
||||
crate::route_edit::Context {
|
||||
revision: current.revision + 1,
|
||||
..current
|
||||
},
|
||||
crate::route_edit::Context {
|
||||
epoch: current.epoch + 1,
|
||||
..current
|
||||
},
|
||||
] {
|
||||
assert_eq!(g.f.app.connection.as_ref().unwrap().context, current);
|
||||
assert_captured_noop(&mut g.f.app, captured, g.y);
|
||||
}
|
||||
// Captured/session contexts agree, but the rendered view has advanced.
|
||||
// This isolates the live-route predicate from session ownership.
|
||||
g.f.app.canvas_epoch += 1;
|
||||
assert_ne!(g.f.app.route_context(), Some(current));
|
||||
assert_eq!(g.f.app.connection.as_ref().unwrap().context, current);
|
||||
assert_captured_noop(&mut g.f.app, current, g.y);
|
||||
g.f.app.canvas_epoch = current.epoch;
|
||||
|
||||
// Conversely, incoming/current contexts agree. Retain obsolete session
|
||||
// metadata explicitly to isolate the session-context admission branch.
|
||||
g.f.app.connection.as_mut().unwrap().context.epoch += 1;
|
||||
assert_eq!(g.f.app.route_context(), Some(current));
|
||||
assert_captured_noop(&mut g.f.app, current, g.y);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn captured_connection_current_restart_and_pair_commit_after_tool_boundary() {
|
||||
for begin_tool in [false, true] {
|
||||
let mut g = group(Layer::Oa);
|
||||
select(&mut g.f.app, &[g.an, g.yn]);
|
||||
let _ = g.f.app.update(Message::WorkDrag(Event::ArrangeNext));
|
||||
let from = node_point(&g.f.app, g.an);
|
||||
let mut state = CanvasState::default();
|
||||
begin(&mut g.f.app, &mut state, from);
|
||||
move_pointer(&mut g.f.app, &mut state, from + Vector::new(400., 0.));
|
||||
assert!(g.f.app.allocation_drag.preview.is_some());
|
||||
let before = g.f.app.project().clone();
|
||||
let source = g.f.app.document.bundle().clone();
|
||||
let epoch = g.f.app.allocation_drag.epoch;
|
||||
let event = if begin_tool {
|
||||
ConnectionEvent::Begin(Tool::Interaction, g.f.work)
|
||||
} else {
|
||||
ConnectionEvent::Choose(Some(Tool::Interaction))
|
||||
};
|
||||
let _ = g.f.app.update(Message::Connection(event));
|
||||
assert!(g.f.app.allocation_drag.epoch > epoch);
|
||||
assert!(g.f.app.allocation_drag.active.is_none());
|
||||
assert!(g.f.app.allocation_drag.preview.is_none());
|
||||
assert!(!g.f.app.allocation_drag.arrange_next);
|
||||
let context = g.f.app.route_context().unwrap();
|
||||
if !begin_tool {
|
||||
let _ =
|
||||
g.f.app
|
||||
.update(Message::Connection(ConnectionEvent::Endpoint(
|
||||
context, g.f.work,
|
||||
)));
|
||||
}
|
||||
assert_eq!(g.f.app.connection.as_ref().unwrap().source, Some(g.f.work));
|
||||
assert!(
|
||||
g.f.app
|
||||
.connection
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.candidates
|
||||
.contains(&g.y)
|
||||
);
|
||||
let document = g.f.app.document.snapshot();
|
||||
let _ =
|
||||
g.f.app
|
||||
.update(Message::Connection(ConnectionEvent::Restart(context)));
|
||||
let context = g.f.app.route_context().unwrap();
|
||||
let restarted = g.f.app.connection.as_ref().unwrap();
|
||||
assert_eq!(restarted.context, context);
|
||||
assert_eq!(restarted.tool, Tool::Interaction);
|
||||
assert_eq!(restarted.source, None);
|
||||
assert!(restarted.candidates.contains(&g.f.work));
|
||||
assert!(Arc::ptr_eq(&document, &g.f.app.document.snapshot()));
|
||||
let _ =
|
||||
g.f.app
|
||||
.update(Message::Connection(ConnectionEvent::Endpoint(
|
||||
context, g.f.work,
|
||||
)));
|
||||
assert_eq!(g.f.app.connection.as_ref().unwrap().source, Some(g.f.work));
|
||||
let revision = g.f.app.document.revision();
|
||||
let _ =
|
||||
g.f.app
|
||||
.update(Message::Connection(ConnectionEvent::Endpoint(context, g.y)));
|
||||
assert!(g.f.app.connection.is_none());
|
||||
assert_eq!(g.f.app.document.revision(), revision + 1);
|
||||
let relation = &g.f.app.project().relations[&g.f.app.selected];
|
||||
assert_eq!(relation.kind(), RelationKind::Interaction);
|
||||
assert_eq!((relation.source(), relation.target()), (g.f.work, g.y));
|
||||
let mut expected = before.clone();
|
||||
assert!(
|
||||
expected
|
||||
.relations
|
||||
.insert(relation.id, relation.clone())
|
||||
.is_none()
|
||||
);
|
||||
exact_history(&mut g.f.app, &before, &source, &expected);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
//! Captured route commits cannot retire a newer Allocation input owner.
|
||||
use super::*;
|
||||
use crate::route_edit::{Context, Event as RouteEvent};
|
||||
|
||||
fn routed_group() -> (Group, Id) {
|
||||
let mut g = group(Layer::Oa);
|
||||
let mut project = g.f.app.project().clone();
|
||||
let edge = project.relate(
|
||||
RelationKind::Interaction,
|
||||
"Reviewed exchange",
|
||||
g.f.work,
|
||||
g.y,
|
||||
);
|
||||
project
|
||||
.diagrams
|
||||
.get_mut(&g.f.diagram)
|
||||
.unwrap()
|
||||
.routes
|
||||
.push(EdgeRoute {
|
||||
edge,
|
||||
waypoints: vec![xy(700., 400.)],
|
||||
label: Some(EdgeLabel {
|
||||
fraction: 0.4,
|
||||
offset: xy(5., 10.),
|
||||
}),
|
||||
source_attachment: None,
|
||||
target_attachment: Some(EdgeAttachment {
|
||||
side: PortAnchorSide::Left,
|
||||
offset: 0.6,
|
||||
}),
|
||||
});
|
||||
validate_candidate(&project).unwrap();
|
||||
g.f.app = App::with_project(project);
|
||||
g.f.app.active_diagram = Some(g.f.diagram);
|
||||
g.f.app.zoom = 1.;
|
||||
g.f.app.pan = Vector::ZERO;
|
||||
g.f.app.fit_to_view = false;
|
||||
choose(&mut g.f.app, SelectionTarget::Edge(edge), Choice::Replace);
|
||||
assert_eq!(g.f.app.selected, edge);
|
||||
let view = projection::project_view(g.f.app.project(), g.f.diagram);
|
||||
assert!(view.edges.iter().any(|e| e.id == edge));
|
||||
assert_eq!(
|
||||
crate::route_edit::attachment_node(
|
||||
g.f.app.project(),
|
||||
&g.f.app.project().diagrams[&g.f.diagram],
|
||||
&view,
|
||||
edge,
|
||||
EdgeEnd::Source,
|
||||
)
|
||||
.unwrap()
|
||||
.id,
|
||||
g.f.node
|
||||
);
|
||||
(g, edge)
|
||||
}
|
||||
|
||||
fn packet(g: &Group, edge: Id, captured: Context, attachment: bool) -> RouteEvent {
|
||||
let mut route =
|
||||
crate::route_edit::stored_route(&g.f.app.project().diagrams[&g.f.diagram], edge);
|
||||
if attachment {
|
||||
route.source_attachment = Some(EdgeAttachment {
|
||||
side: PortAnchorSide::Top,
|
||||
offset: 0.25,
|
||||
});
|
||||
RouteEvent::CommitAttachment(captured, route, EdgeEnd::Source, g.f.node)
|
||||
} else {
|
||||
route.waypoints[0] = xy(750., 450.);
|
||||
RouteEvent::Commit(captured, route, Some(0))
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_route_noop(app: &mut App, event: RouteEvent) {
|
||||
let document = app.document.snapshot();
|
||||
let selected = selection(app);
|
||||
let context = app.route_context();
|
||||
let camera = (app.pan, app.zoom, app.canvas_epoch, app.camera_epoch);
|
||||
let ownership = (app.allocation_drag.epoch, app.allocation_drag.arrange_next);
|
||||
let press = app.allocation_drag.active.as_ref().map(|a| a.press.clone());
|
||||
let preview = app.allocation_drag.preview.clone();
|
||||
let feedback = app.allocation_drag.feedback.as_ref().map(|f| {
|
||||
(
|
||||
f.press.token,
|
||||
f.text.clone(),
|
||||
f.refused,
|
||||
f.targets.clone(),
|
||||
f.inspection_targets.clone(),
|
||||
)
|
||||
});
|
||||
let handles = (app.selected_bend, app.selected_attachment);
|
||||
let interaction = app.source_symbols.interaction;
|
||||
let status = app.status.clone();
|
||||
let _ = app.update(Message::Route(event));
|
||||
assert_eq!(selection(app), selected);
|
||||
assert_eq!(app.route_context(), context);
|
||||
assert_eq!(
|
||||
(app.pan, app.zoom, app.canvas_epoch, app.camera_epoch),
|
||||
camera
|
||||
);
|
||||
assert_eq!(
|
||||
(app.allocation_drag.epoch, app.allocation_drag.arrange_next),
|
||||
ownership
|
||||
);
|
||||
match (press, app.allocation_drag.active.as_ref()) {
|
||||
(Some(before), Some(after)) => assert!(Arc::ptr_eq(&before, &after.press)),
|
||||
(None, None) => {}
|
||||
_ => panic!("stale route packet changed the active Allocation token"),
|
||||
}
|
||||
match (preview, app.allocation_drag.preview.as_ref()) {
|
||||
(Some(before), Some(after)) => assert!(Arc::ptr_eq(&before, after)),
|
||||
(None, None) => {}
|
||||
_ => panic!("stale route packet changed the Allocation preview"),
|
||||
}
|
||||
assert_eq!(
|
||||
app.allocation_drag.feedback.as_ref().map(|f| (
|
||||
f.press.token,
|
||||
f.text.clone(),
|
||||
f.refused,
|
||||
f.targets.clone(),
|
||||
f.inspection_targets.clone(),
|
||||
)),
|
||||
feedback
|
||||
);
|
||||
assert_eq!((app.selected_bend, app.selected_attachment), handles);
|
||||
assert_eq!(app.source_symbols.interaction, interaction);
|
||||
assert_eq!(app.status, status);
|
||||
assert!(Arc::ptr_eq(&document, &app.document.snapshot()));
|
||||
assert_eq!(app.source_workspace.bundle(), document.bundle);
|
||||
}
|
||||
|
||||
fn stale_route_preserves_new_group(attachment: bool, escape: bool) {
|
||||
let (mut g, edge) = routed_group();
|
||||
let old = g.f.app.route_context().unwrap();
|
||||
let delayed = packet(&g, edge, old, attachment);
|
||||
if escape {
|
||||
let _ = g.f.app.update(Message::Escape);
|
||||
assert_ne!(g.f.app.route_context(), Some(old));
|
||||
}
|
||||
select(&mut g.f.app, &[g.an, g.yn]);
|
||||
let _ = g.f.app.update(Message::WorkDrag(Event::ArrangeNext));
|
||||
assert!(g.f.app.allocation_drag.arrange_next);
|
||||
if !escape {
|
||||
assert_eq!(g.f.app.route_context(), Some(old));
|
||||
assert_ne!(g.f.app.selected, edge);
|
||||
}
|
||||
assert_route_noop(&mut g.f.app, delayed.clone());
|
||||
let before = g.f.app.project().clone();
|
||||
let source = g.f.app.document.bundle().clone();
|
||||
let selected = selection(&g.f.app);
|
||||
let from = node_point(&g.f.app, g.an);
|
||||
let to = from + Vector::new(400., 0.);
|
||||
let mut state = CanvasState::default();
|
||||
let press = begin(&mut g.f.app, &mut state, from);
|
||||
assert_eq!(press.mode, Mode::Arrange);
|
||||
move_pointer(&mut g.f.app, &mut state, to);
|
||||
assert!(g.f.app.allocation_drag.preview.is_some());
|
||||
if !escape {
|
||||
assert_eq!(g.f.app.route_context(), Some(old));
|
||||
assert_ne!(g.f.app.selected, edge);
|
||||
}
|
||||
assert_route_noop(&mut g.f.app, delayed);
|
||||
release(&mut g.f.app, &mut state, to);
|
||||
let mut expected = before.clone();
|
||||
let diagram = expected.diagrams.get_mut(&g.f.diagram).unwrap();
|
||||
diagram.nodes.get_mut(&g.an).unwrap().position = xy(500., 100.);
|
||||
diagram.nodes.get_mut(&g.f.node).unwrap().position = xy(540., 200.);
|
||||
diagram.nodes.get_mut(&g.yn).unwrap().position = xy(540., 600.);
|
||||
// Both endpoint placements move by (400, 0); the saved pin follows once,
|
||||
// while label offset and endpoint attachment settings stay unchanged.
|
||||
diagram
|
||||
.routes
|
||||
.iter_mut()
|
||||
.find(|r| r.edge == edge)
|
||||
.unwrap()
|
||||
.waypoints = vec![xy(1100., 400.)];
|
||||
assert_eq!(selection(&g.f.app), selected);
|
||||
exact_history(&mut g.f.app, &before, &source, &expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stale_route_commit_preserves_arrange_arm_and_live_group() {
|
||||
stale_route_preserves_new_group(false, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stale_route_attachment_preserves_arrange_arm_and_live_group() {
|
||||
stale_route_preserves_new_group(true, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_context_route_commit_after_selection_preserves_arrange_arm_and_live_group() {
|
||||
stale_route_preserves_new_group(false, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_context_route_attachment_after_selection_preserves_arrange_arm_and_live_group() {
|
||||
stale_route_preserves_new_group(true, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn captured_route_context_fields_independently_reject_before_retirement() {
|
||||
for attachment in [false, true] {
|
||||
let (mut g, edge) = routed_group();
|
||||
let current = g.f.app.route_context().unwrap();
|
||||
// The selected edge and route payload are valid. Each capture mismatch
|
||||
// is isolated; no nonexistent route session can mask admission.
|
||||
for captured in [
|
||||
Context {
|
||||
document: Id::new_v4(),
|
||||
..current
|
||||
},
|
||||
Context {
|
||||
diagram: Id::new_v4(),
|
||||
..current
|
||||
},
|
||||
Context {
|
||||
revision: current.revision + 1,
|
||||
..current
|
||||
},
|
||||
Context {
|
||||
epoch: current.epoch + 1,
|
||||
..current
|
||||
},
|
||||
] {
|
||||
let event = packet(&g, edge, captured, attachment);
|
||||
assert_route_noop(&mut g.f.app, event);
|
||||
}
|
||||
// Hold the captured packet fixed and advance the live context instead.
|
||||
let event = packet(&g, edge, current, attachment);
|
||||
g.f.app.canvas_epoch += 1;
|
||||
assert_route_noop(&mut g.f.app, event.clone());
|
||||
g.f.app.canvas_epoch = current.epoch;
|
||||
g.f.app.active_diagram = None;
|
||||
assert_route_noop(&mut g.f.app, event);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn current_route_commits_without_session_preserve_exact_route_history() {
|
||||
for attachment in [false, true] {
|
||||
let (mut g, edge) = routed_group();
|
||||
let current = g.f.app.route_context().unwrap();
|
||||
let event = packet(&g, edge, current, attachment);
|
||||
let before = g.f.app.project().clone();
|
||||
let source = g.f.app.document.bundle().clone();
|
||||
let revision = g.f.app.document.revision();
|
||||
assert!(g.f.app.connection.is_none());
|
||||
assert!(g.f.app.selected_attachment.is_none());
|
||||
let _ = g.f.app.update(Message::Route(event));
|
||||
assert_eq!(g.f.app.document.revision(), revision + 1);
|
||||
assert_eq!(g.f.app.selected, edge);
|
||||
assert_eq!(
|
||||
g.f.app.selected_bend,
|
||||
if attachment { None } else { Some((edge, 0)) }
|
||||
);
|
||||
let mut expected = before.clone();
|
||||
let route = expected
|
||||
.diagrams
|
||||
.get_mut(&g.f.diagram)
|
||||
.unwrap()
|
||||
.routes
|
||||
.iter_mut()
|
||||
.find(|r| r.edge == edge)
|
||||
.unwrap();
|
||||
if attachment {
|
||||
route.source_attachment = Some(EdgeAttachment {
|
||||
side: PortAnchorSide::Top,
|
||||
offset: 0.25,
|
||||
});
|
||||
} else {
|
||||
route.waypoints = vec![xy(750., 450.)];
|
||||
}
|
||||
exact_history(&mut g.f.app, &before, &source, &expected);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
# Allocation group App and native checkpoint - 2026-09-24
|
||||
|
||||
At source `495464e84b5bae8ddad93e9fc69ef353e7f1c884`, Allocation group assignment, explicit Arrange and ordinary performer movement are wired through the desktop input lifecycle and have passed the bounded Windows native workflows below. This supersedes the App/native-pending status of the [assignment](allocation-assignment-planner-20260923.md) and [Arrange](allocation-arrange-planner-20260923.md) planner checkpoints for these exercised workflows. Required behavior remains in the [multiselect contract](multiselect-contract-20260923.md), [Allocation v2](release-activity-allocation-drag-contract-v2-20260911.md) and [Arrange amendment](allocation-arrange-amendment-20260923.md). No user-owned specification or source schema changed.
|
||||
|
||||
## Implementation and automated evidence
|
||||
|
||||
App admission binds the actual typed press target, exact selection snapshot, accepted Document and gesture lifetime. Unsupported sets refuse as a whole; selecting a nonprimary anchor does not promote it. Assignment, explicit Arrange and ordinary performer movement preserve their distinct policies while sharing owned press/preview/release handling. Bare saved-move paths cannot bypass that ownership. Release recomputes the intended edit and commits through one Document operation; complete equality preserves the existing history branch.
|
||||
|
||||
Review found and corrected two consequential gaps: dirty route controls now use the same 8/zoom hit domain as clean controls, and ordinary off-grid performer movement returning exactly to its captured anchor produces zero displacement. Mounted property-Name focus/caret and isolated geometry tests cover the control-ring regression. These do not independently establish a native dirty source-editor or attachment-ring workflow.
|
||||
|
||||
The seven final gates all exited zero with unchanged captured inputs and exact source proofs for the named commit (754 paths; map SHA-256 `dba5bc7789a56998fe9b877af26ab8a442b31de55443aaaea23930d58c8610ed`):
|
||||
|
||||
| Gate | Command/result |
|
||||
| --- | --- |
|
||||
| `app-full-1` | `cargo test -p oa-desktop --bin arcadia-studio --message-format=json -- --test-threads=1`: 1,362 passed, 6 ignored, 0 failed |
|
||||
| `gui-1` | `cargo build -p oa-desktop --bin arcadia-studio --message-format=json` |
|
||||
| `native-build-1` | `cargo build -p oa-core --example allocation_multiselect_native --message-format=json` |
|
||||
| `final-format-1` | `cargo fmt --all -- --check` |
|
||||
| `app-lint-2` | `cargo clippy -p oa-desktop --all-targets --message-format=json -- -D warnings` |
|
||||
| `native-evaluator-4` | `cargo test -p oa-core --example allocation_multiselect_native --message-format=json -- --test-threads=1`: 5 passed |
|
||||
| `native-lint-1` | `cargo clippy -p oa-core --example allocation_multiselect_native --message-format=json -- -D warnings` |
|
||||
|
||||
Windows used Rust/Cargo 1.98.1, `stable-x86_64-pc-windows-gnullvm`, LLVM-MinGW 20260826, one build job and incremental compilation disabled. The exact captured environment/tool versions and commands remain in the gate receipts. Some historical receipt HEAD fields predate the tested edits; the separate full input-inventory/Git-blob proofs establish the source identity. Earlier maps are not attributed to the final commit.
|
||||
|
||||
## Observed native workflows
|
||||
|
||||
Each accepted run opened a fresh generated 16-file source bundle in the built GUI, performed the operation through real controls, saved, and exercised exactly one Undo and one Redo as observed by the owner, Codex-9e8r6t /root. Independent review checked captures, literal source diffs, complete-Project checker reports and byte-history relationships. Additive selection used the observed context-menu Add to selection action.
|
||||
|
||||
| Run | Accepted scope | Captures / passing snapshots | Reopen |
|
||||
| --- | --- | --- | --- |
|
||||
| 3 | OA X/Y assignment from A/B to C; Y remains primary; truthful 2-of-2 feedback | 15 / 5 | In-process |
|
||||
| 4 | SA exact work-plus-typed-input-port whole-set refusal, then X/Y assignment to C; Y remains primary | 19 / 6 | Not exercised |
|
||||
| 5 | OA mixed P/V ordinary refusal, explicit Arrange of P, contained unselected U and V; arm consumed; 2 selected/3 placements | 26 / 6 | In-process |
|
||||
| 6 | SA mixed P/V refusal and explicit Arrange; V anchor with P primary; arm consumed; 2 selected/3 placements | 21 / 5 | Not exercised |
|
||||
| 7 | Ordinary OA A/B performer movement from off-grid originals; A anchor, B primary; contained X/Y and route move once | 14 / 4 | Not exercised |
|
||||
|
||||
Assignment changes only the respective `oa/participants.arcadia` or `sa/participants.arcadia` and `views/diagram_<layer>_assign.arcadia`: allocation sources A/B -> C, X/Y x80 -> 560 and route pins x340 -> 820. OA/SA Arrange each changes only its corresponding `views/diagram_<layer>_arrange.arcadia`, moving P/U/V and both pins by y+240. Ordinary performers change only `views/diagram_oa_assign.arcadia`, translating A/B/X/Y and both pins by exactly (77,43). Relation identities, metadata, inactive diagrams and every other source byte remain unchanged.
|
||||
|
||||
For every accepted run, fixture==baseline==undo, including recorded unchanged refusal snapshots; changed==redo and, where exercised, ==reopened. Whole-directory inventories match, final working bytes match the final history state, and each file retains its exact leading fixture comment including newline. This is bounded comment preservation, not a general claim about arbitrary internal comments or formatting. All seven GUI lifetimes ended with exit 0 and empty pinned app streams.
|
||||
|
||||
Totals are **102 captures, 27 snapshots, 26 positive checker reports and 5 successful history reports**. Accepted runs3-7 account for **95 captures and all 26 positive snapshots**; the other seven captures and failed snapshot remain failure/preflight evidence.
|
||||
|
||||
Owner observation detail: launch PIDs/window IDs were respectively 3932/786628, 2376/1901592, 6984/11994140, 3648/88540156 and 6380/2818912 for runs 3-7; the actual process main-window handles matched the returned Computer Use targets. Run 3 pressed nonprimary X and moved it +480 world x. Run 4 first added Y's visible input port and observed its Port inspector before the refused X drag; it then selected Y's body and repeated the assignment. Run 5 used the Arrange control and pressed P, with V primary. Run 6 armed with Alt+A and pressed nonprimary V, with P primary. Both Arrange movements were +240 world y. Run 7 pressed A with B primary and ordinary mode unarmed; snapping from A=(43,47) produced the literal (77,43) delta. After each changed save the owner sent one Ctrl+Z, saved, sent one Ctrl+Y, and saved. Runs 3/5 reopened through Home/recent models within the same process; their automatic fit changed framing, so unchanged camera across reopen is not claimed.
|
||||
|
||||
## Failures, warnings and limits
|
||||
|
||||
Preserved gate predecessors explain the repair sequence: `app-1` failed test compilation; `app-2` passed 130/failed 1 because the zero-delta assignment fixture could hit an overlapping empty performer, repaired with deterministic placement ordering and explicit press/candidate assertions. `native-evaluator-1/2` exposed SA fixture routing `NoClearPath`; fixture geometry was corrected before final evaluator/build acceptance. Historical passes `app-lint-1` and `native-evaluator-3` retain their earlier maps; evaluator 3 is a test harness, not the native checker executable.
|
||||
|
||||
Native run 1 is an aborted chooser preflight with original helper bytes retained. A discovered rooted-Windows-path guard defect was corrected before fresh run 2. Run 2 retains its unchanged source and failed baseline checker exit 3221225781; the owner diagnosed missing toolchain DLL search PATH. Neither run earns operation acceptance.
|
||||
|
||||
The fixture intentionally retains 18 advisory model warnings: 8 `work_without_behavior_context`, 4 `exchange_without_behavior_context`, 2 `unallocated_leaf` and4 `empty_port_contract`. These describe incomplete behavior/payload coverage and the independent Arrange leaves, remain unchanged through the operations, and are distinct from runtime or routing failures.
|
||||
|
||||
No native held-Shift, held-preview, interruption/Escape, out-and-back/no-op or compact-window acceptance is claimed. Run 7's historical `compact-*` labels describe a failed resize attempt; images remain 1442x932. Native readability/feedback observations apply to that normal viewport. Automated off-grid no-op regressions and successful native off-grid movement are different evidence. No SA reopen occurred. Screenshot metadata does not independently bind a window to a PID or count physical Undo/Redo actions; those facts rely on owner observation. No separately saved executed capture script exists; its manifest field remains null rather than reconstructed.
|
||||
|
||||
## Evidence, replay and delivery boundary
|
||||
|
||||
Portable evidence belongs under [allocation-group-app-evidence-20260924](allocation-group-app-evidence-20260924/): `checks/` preserves 13 gates (four recorded failures, seven final source proofs); `native/` preserves all seven raw run trees, historical helpers, fixtures, captures, logs, snapshots and build provenance; `reviews/` preserves the relevant independent review notes. Each manifest gives exact members and hashes. Archive verifiers inspect bytes/receipts without extracting or executing archived code; they do not replay the GUI or Rust checker. Native build capsules are retained once within that package, with source-commit proofs in sibling checks.
|
||||
|
||||
From the delivered repository, using a working Python interpreter:
|
||||
|
||||
```powershell
|
||||
python -B harness/allocation-group-app-evidence-20260924/checks/verify.py
|
||||
python -B harness/allocation-group-app-evidence-20260924/native/verify.py
|
||||
```
|
||||
|
||||
Both use package-local files and require no original machine paths. The Cargo commands above describe fresh build/test reruns from the named source with a compatible Windows toolchain; a fresh native replay additionally needs the [helper usage notes](allocation-group-app-evidence-20260924/reviews/native-replay.md), DLL PATH and explicit observed gesture workflow. Historical receipts are preserved, not relabeled as new runs. Negative verification rejects missing/corrupt members, swapped source proofs and repinned wrong history modes, intended paths or sentinel inventories.
|
||||
|
||||
Archive SHA-256: checks `789a18e1a1f302e89275a69b12efb5218df17e2166a9349709cb3eb36252ceb5`; native `6c4fb3151d46141b56e0e5df24d8ded0c644fa01bf6b2b575952eea55ae3c61e`. The observed GUI SHA-256 is `5775dfd7327c77b936290a3dc4cb0cd827cd3f8d23fc00f337622c56911d1281`; checker is `1b060346b367afd6dd447919fbd3483aad9d5740f3d36c05fbb05266bff0c3e1`. The archives hold build receipts and source, not those executable binaries.
|
||||
|
||||
This is a contribution-source milestone. Publishing its report/evidence does not by itself establish remote-default integration, a tested installer/runnable release, Linux acceptance or completion of every multiselect/input requirement. Integration and versioned release claims require their own identified commit, packaged-launch verification and confirmed Gitea publication.
|
||||
@@ -0,0 +1,2 @@
|
||||
# Preserve measured package files and historical review bytes verbatim.
|
||||
* -text whitespace=cr-at-eol
|
||||
@@ -0,0 +1,24 @@
|
||||
# Allocation captured-gate evidence tools
|
||||
|
||||
Adapted from the reviewed Process prepare/verify/selfcheck tools. Read-only preparation consumes explicitly selected **closed** runs in CHECKOUT/.local/allocation-group-gates. No Cargo, source execution, checkout, GUI, network or archived helper execution occurs.
|
||||
|
||||
```powershell
|
||||
python -B prepare.py --root CHECKOUT --runs experiment-1 experiment-2 experiment-3 planner-document-1 probe-lint-1 format-1 --check-only
|
||||
```
|
||||
|
||||
Keep failed experiment-2: verification preserves its recorded compile failure rather than converting archive consistency into test success. The example selects the first six closed gates. Later closed planner runs can be explicitly listed with the same interface; the final package manifest is authoritative for its exact inventory. No implicit discovery includes active directories.
|
||||
|
||||
To create a portable package, replace --check-only with `--output NEW_DIRECTORY`. Existing output refuses. The exact seven raw files are required, with only `source-proof.json` accepted as an optional eighth file. No raw byte normalization or receipt rewriting occurs. Maps and nested input ZIPs deduplicate by SHA-256; other logical raw files keep their names. All original helper bytes, failed-run streams and optional proof bytes remain archived. The Allocation-specific manifest kind distinguishes this extension from the seven-file Process schema.
|
||||
|
||||
The gate.py and source-proof.py helper catalog is preserved. Every gate's recorded helper must match available bytes; use repeatable `--helper PATH` for older captures. If a proof exists, its exact comparator bytes must be available at the default `.local/process-source-evidence-tools/verify.py` or a repeatable `--comparator PATH`. They are archived under their content hash and never executed.
|
||||
|
||||
An optional source proof is independently pinned in the manifest with its full named commit. Verification binds it to the enclosing run, exact result receipt, captured map/count, completed gate time, status and comparator bytes/hash. Captured HEAD is preserved as historical metadata and is not substituted for the named source commit. This packager checks historical proof consistency; it does **not** repeat Git blob comparison. A source proof on a failed gate is source-identity evidence only, never a passing-test claim.
|
||||
|
||||
```powershell
|
||||
python -B PACKAGE/verify.py
|
||||
python -B selfcheck.py --root CHECKOUT --runs experiment-1 experiment-2 experiment-3 planner-document-1 probe-lint-1 format-1 --output TOOLS/NEW_SELFCHECK_DIRECTORY
|
||||
```
|
||||
|
||||
The verifier checks pins, exact ZIP inventory, process/start/result relationships, streams, command/exit identities, source capsules, vendor inventory and Cargo artifact relationships. Selfchecks compare packaged raw bytes to originals, reject existing output, and mutate only in-memory copies to test stream/helper/exit/capsule/proof corruption. The package contains tools, manifest, files.json and evidence.zip; selfcheck is a preparation utility, not packaged runtime input.
|
||||
|
||||
Limits: internal consistency is not external authentication, hermetic build proof, native usability, a new source comparison or production acceptance. Review exact run results and source identity separately. Run with `python -B` to avoid incidental bytecode writes.
|
||||
Binary file not shown.
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"prepare.py": {
|
||||
"bytes": 6316,
|
||||
"sha256": "aa52592fb8e373c2a90007e187734d29129d38d9fce9dc45461482c7f6376bff"
|
||||
},
|
||||
"verify.py": {
|
||||
"bytes": 11440,
|
||||
"sha256": "584187370a1076e13190914158fc72a9ab65f07e72cc6a0d3b5c319f3dc7f05f"
|
||||
},
|
||||
"README.md": {
|
||||
"bytes": 3284,
|
||||
"sha256": "3ff0a61fb15ca26e54b8903962c99ba7802a87f0204e7b71f2d04c812b9588b7"
|
||||
},
|
||||
"manifest.json": {
|
||||
"bytes": 31675,
|
||||
"sha256": "853737c4a8bda98e2b259f409ce21abbefbaf456bcce7e52cc00545817427be8"
|
||||
},
|
||||
"evidence.zip": {
|
||||
"bytes": 21040643,
|
||||
"sha256": "789a18e1a1f302e89275a69b12efb5218df17e2166a9349709cb3eb36252ceb5"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,745 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"kind": "allocation-multiselect-cargo-gates",
|
||||
"runs": {
|
||||
"app-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--bin",
|
||||
"arcadia-studio",
|
||||
"--message-format=json",
|
||||
"allocation",
|
||||
"--",
|
||||
"--test-threads=1"
|
||||
],
|
||||
"head": "16b30a67600eff2b3e10d96376cb57585223995c",
|
||||
"exitCode": 101,
|
||||
"inputCount": 753,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py"
|
||||
},
|
||||
"app-2": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--bin",
|
||||
"arcadia-studio",
|
||||
"--message-format=json",
|
||||
"allocation",
|
||||
"--",
|
||||
"--test-threads=1"
|
||||
],
|
||||
"head": "16b30a67600eff2b3e10d96376cb57585223995c",
|
||||
"exitCode": 101,
|
||||
"inputCount": 753,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py"
|
||||
},
|
||||
"app-lint-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"clippy",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--all-targets",
|
||||
"--message-format=json",
|
||||
"--",
|
||||
"-D",
|
||||
"warnings"
|
||||
],
|
||||
"head": "16b30a67600eff2b3e10d96376cb57585223995c",
|
||||
"exitCode": 0,
|
||||
"inputCount": 753,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py"
|
||||
},
|
||||
"native-evaluator-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-core",
|
||||
"--example",
|
||||
"allocation_multiselect_native",
|
||||
"--message-format=json",
|
||||
"--",
|
||||
"--test-threads=1"
|
||||
],
|
||||
"head": "16b30a67600eff2b3e10d96376cb57585223995c",
|
||||
"exitCode": 101,
|
||||
"inputCount": 754,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py"
|
||||
},
|
||||
"native-evaluator-2": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-core",
|
||||
"--example",
|
||||
"allocation_multiselect_native",
|
||||
"--message-format=json",
|
||||
"deterministic_create_only_generation_and_all_literal_modes",
|
||||
"--",
|
||||
"--nocapture",
|
||||
"--test-threads=1"
|
||||
],
|
||||
"head": "16b30a67600eff2b3e10d96376cb57585223995c",
|
||||
"exitCode": 101,
|
||||
"inputCount": 754,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py"
|
||||
},
|
||||
"native-evaluator-3": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-core",
|
||||
"--example",
|
||||
"allocation_multiselect_native",
|
||||
"--message-format=json",
|
||||
"--",
|
||||
"--test-threads=1"
|
||||
],
|
||||
"head": "16b30a67600eff2b3e10d96376cb57585223995c",
|
||||
"exitCode": 0,
|
||||
"inputCount": 754,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py"
|
||||
},
|
||||
"app-full-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--bin",
|
||||
"arcadia-studio",
|
||||
"--message-format=json",
|
||||
"--",
|
||||
"--test-threads=1"
|
||||
],
|
||||
"head": "16b30a67600eff2b3e10d96376cb57585223995c",
|
||||
"exitCode": 0,
|
||||
"inputCount": 754,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "495464e84b5bae8ddad93e9fc69ef353e7f1c884",
|
||||
"pin": {
|
||||
"bytes": 589,
|
||||
"sha256": "4392a0bd617a56eb99c33f166953ed6c6c279c1f8419f267c0699e4554fe205a"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"gui-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"build",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--bin",
|
||||
"arcadia-studio",
|
||||
"--message-format=json"
|
||||
],
|
||||
"head": "16b30a67600eff2b3e10d96376cb57585223995c",
|
||||
"exitCode": 0,
|
||||
"inputCount": 754,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "495464e84b5bae8ddad93e9fc69ef353e7f1c884",
|
||||
"pin": {
|
||||
"bytes": 584,
|
||||
"sha256": "854c6054fa52373be2343df11ca435f74195201bebb2cfa18a9042a8354f9f32"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"native-build-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"build",
|
||||
"-p",
|
||||
"oa-core",
|
||||
"--example",
|
||||
"allocation_multiselect_native",
|
||||
"--message-format=json"
|
||||
],
|
||||
"head": "495464e84b5bae8ddad93e9fc69ef353e7f1c884",
|
||||
"exitCode": 0,
|
||||
"inputCount": 754,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "495464e84b5bae8ddad93e9fc69ef353e7f1c884",
|
||||
"pin": {
|
||||
"bytes": 593,
|
||||
"sha256": "3717f51afcabbaa98352097d86454ab01716340f29c04ea91d5a5cef63e19e76"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"final-format-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"fmt",
|
||||
"--all",
|
||||
"--",
|
||||
"--check"
|
||||
],
|
||||
"head": "495464e84b5bae8ddad93e9fc69ef353e7f1c884",
|
||||
"exitCode": 0,
|
||||
"inputCount": 754,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "495464e84b5bae8ddad93e9fc69ef353e7f1c884",
|
||||
"pin": {
|
||||
"bytes": 593,
|
||||
"sha256": "175655f7ae6e59b946cb11acaefeb3b93a1b25bb5811c2aaaf55a34130ca45a9"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"app-lint-2": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"clippy",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--all-targets",
|
||||
"--message-format=json",
|
||||
"--",
|
||||
"-D",
|
||||
"warnings"
|
||||
],
|
||||
"head": "495464e84b5bae8ddad93e9fc69ef353e7f1c884",
|
||||
"exitCode": 0,
|
||||
"inputCount": 754,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "495464e84b5bae8ddad93e9fc69ef353e7f1c884",
|
||||
"pin": {
|
||||
"bytes": 589,
|
||||
"sha256": "645e0e80418c8789d29c6b19df9b772f42c4cea4031bbc7551629e13551c509b"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"native-evaluator-4": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-core",
|
||||
"--example",
|
||||
"allocation_multiselect_native",
|
||||
"--message-format=json",
|
||||
"--",
|
||||
"--test-threads=1"
|
||||
],
|
||||
"head": "495464e84b5bae8ddad93e9fc69ef353e7f1c884",
|
||||
"exitCode": 0,
|
||||
"inputCount": 754,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "495464e84b5bae8ddad93e9fc69ef353e7f1c884",
|
||||
"pin": {
|
||||
"bytes": 597,
|
||||
"sha256": "bb6b581c2777f9f7d8bd760c5e9445595178e51b819e79da7bdcf4a53507485f"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"native-lint-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"clippy",
|
||||
"-p",
|
||||
"oa-core",
|
||||
"--example",
|
||||
"allocation_multiselect_native",
|
||||
"--message-format=json",
|
||||
"--",
|
||||
"-D",
|
||||
"warnings"
|
||||
],
|
||||
"head": "495464e84b5bae8ddad93e9fc69ef353e7f1c884",
|
||||
"exitCode": 0,
|
||||
"inputCount": 754,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "495464e84b5bae8ddad93e9fc69ef353e7f1c884",
|
||||
"pin": {
|
||||
"bytes": 592,
|
||||
"sha256": "96aeabb5ceba339ec73cd9421abec725a3a4c435321bffcfb7f5e3059cba920e"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
}
|
||||
},
|
||||
"captureHelpers": {
|
||||
"gate.py": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"source-proof.py": "helpers/58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119.py"
|
||||
},
|
||||
"logicalFiles": {
|
||||
"cargo/app-1/inputs.json": "capsules/8dd6b387c8d3df6d77c878784037679ebecdc775c835c76645caf03b6b31d26b.json",
|
||||
"cargo/app-1/inputs.zip": "capsules/69b201c72f3e3a533c3cc7e74660820bd7d6eda9164d7b47dc81c3bedbd410d7.zip",
|
||||
"cargo/app-1/process.json": "cargo/app-1/process.json",
|
||||
"cargo/app-1/result.json": "cargo/app-1/result.json",
|
||||
"cargo/app-1/started.json": "cargo/app-1/started.json",
|
||||
"cargo/app-1/stderr.log": "cargo/app-1/stderr.log",
|
||||
"cargo/app-1/stdout.log": "cargo/app-1/stdout.log",
|
||||
"cargo/app-2/inputs.json": "capsules/9746cd48ca8eb9ad7a354114770980e0316ec821d5dd14cc129b16b2bde94c28.json",
|
||||
"cargo/app-2/inputs.zip": "capsules/66ba48b04e28287483a5a398a1b4666c657f1ee58468b6f971cd05166bf941e2.zip",
|
||||
"cargo/app-2/process.json": "cargo/app-2/process.json",
|
||||
"cargo/app-2/result.json": "cargo/app-2/result.json",
|
||||
"cargo/app-2/started.json": "cargo/app-2/started.json",
|
||||
"cargo/app-2/stderr.log": "cargo/app-2/stderr.log",
|
||||
"cargo/app-2/stdout.log": "cargo/app-2/stdout.log",
|
||||
"cargo/app-full-1/inputs.json": "capsules/dba5bc7789a56998fe9b877af26ab8a442b31de55443aaaea23930d58c8610ed.json",
|
||||
"cargo/app-full-1/inputs.zip": "capsules/39d4b0d3f85c4ea3d9aa506571d46c649f7071f8dd247c5af9b20dea980bcbc6.zip",
|
||||
"cargo/app-full-1/process.json": "cargo/app-full-1/process.json",
|
||||
"cargo/app-full-1/result.json": "cargo/app-full-1/result.json",
|
||||
"cargo/app-full-1/source-proof.json": "cargo/app-full-1/source-proof.json",
|
||||
"cargo/app-full-1/started.json": "cargo/app-full-1/started.json",
|
||||
"cargo/app-full-1/stderr.log": "cargo/app-full-1/stderr.log",
|
||||
"cargo/app-full-1/stdout.log": "cargo/app-full-1/stdout.log",
|
||||
"cargo/app-lint-1/inputs.json": "capsules/38a95be43c9c97bc4aa807d93fe4baf3ffbb95f8034ef680765917cb0d8e356a.json",
|
||||
"cargo/app-lint-1/inputs.zip": "capsules/4d022704e970828afec2106bfb27ed9bac9b1e70a2eee35303eab8a0123e62ce.zip",
|
||||
"cargo/app-lint-1/process.json": "cargo/app-lint-1/process.json",
|
||||
"cargo/app-lint-1/result.json": "cargo/app-lint-1/result.json",
|
||||
"cargo/app-lint-1/started.json": "cargo/app-lint-1/started.json",
|
||||
"cargo/app-lint-1/stderr.log": "cargo/app-lint-1/stderr.log",
|
||||
"cargo/app-lint-1/stdout.log": "cargo/app-lint-1/stdout.log",
|
||||
"cargo/app-lint-2/inputs.json": "capsules/dba5bc7789a56998fe9b877af26ab8a442b31de55443aaaea23930d58c8610ed.json",
|
||||
"cargo/app-lint-2/inputs.zip": "capsules/39d4b0d3f85c4ea3d9aa506571d46c649f7071f8dd247c5af9b20dea980bcbc6.zip",
|
||||
"cargo/app-lint-2/process.json": "cargo/app-lint-2/process.json",
|
||||
"cargo/app-lint-2/result.json": "cargo/app-lint-2/result.json",
|
||||
"cargo/app-lint-2/source-proof.json": "cargo/app-lint-2/source-proof.json",
|
||||
"cargo/app-lint-2/started.json": "cargo/app-lint-2/started.json",
|
||||
"cargo/app-lint-2/stderr.log": "cargo/app-lint-2/stderr.log",
|
||||
"cargo/app-lint-2/stdout.log": "cargo/app-lint-2/stdout.log",
|
||||
"cargo/final-format-1/inputs.json": "capsules/dba5bc7789a56998fe9b877af26ab8a442b31de55443aaaea23930d58c8610ed.json",
|
||||
"cargo/final-format-1/inputs.zip": "capsules/39d4b0d3f85c4ea3d9aa506571d46c649f7071f8dd247c5af9b20dea980bcbc6.zip",
|
||||
"cargo/final-format-1/process.json": "cargo/final-format-1/process.json",
|
||||
"cargo/final-format-1/result.json": "cargo/final-format-1/result.json",
|
||||
"cargo/final-format-1/source-proof.json": "cargo/final-format-1/source-proof.json",
|
||||
"cargo/final-format-1/started.json": "cargo/final-format-1/started.json",
|
||||
"cargo/final-format-1/stderr.log": "cargo/final-format-1/stderr.log",
|
||||
"cargo/final-format-1/stdout.log": "cargo/final-format-1/stdout.log",
|
||||
"cargo/gui-1/inputs.json": "capsules/dba5bc7789a56998fe9b877af26ab8a442b31de55443aaaea23930d58c8610ed.json",
|
||||
"cargo/gui-1/inputs.zip": "capsules/39d4b0d3f85c4ea3d9aa506571d46c649f7071f8dd247c5af9b20dea980bcbc6.zip",
|
||||
"cargo/gui-1/process.json": "cargo/gui-1/process.json",
|
||||
"cargo/gui-1/result.json": "cargo/gui-1/result.json",
|
||||
"cargo/gui-1/source-proof.json": "cargo/gui-1/source-proof.json",
|
||||
"cargo/gui-1/started.json": "cargo/gui-1/started.json",
|
||||
"cargo/gui-1/stderr.log": "cargo/gui-1/stderr.log",
|
||||
"cargo/gui-1/stdout.log": "cargo/gui-1/stdout.log",
|
||||
"cargo/native-build-1/inputs.json": "capsules/dba5bc7789a56998fe9b877af26ab8a442b31de55443aaaea23930d58c8610ed.json",
|
||||
"cargo/native-build-1/inputs.zip": "capsules/39d4b0d3f85c4ea3d9aa506571d46c649f7071f8dd247c5af9b20dea980bcbc6.zip",
|
||||
"cargo/native-build-1/process.json": "cargo/native-build-1/process.json",
|
||||
"cargo/native-build-1/result.json": "cargo/native-build-1/result.json",
|
||||
"cargo/native-build-1/source-proof.json": "cargo/native-build-1/source-proof.json",
|
||||
"cargo/native-build-1/started.json": "cargo/native-build-1/started.json",
|
||||
"cargo/native-build-1/stderr.log": "cargo/native-build-1/stderr.log",
|
||||
"cargo/native-build-1/stdout.log": "cargo/native-build-1/stdout.log",
|
||||
"cargo/native-evaluator-1/inputs.json": "capsules/2846cbf5a6b9be6d66ce8261af407d6eb84079961d1adb04fd8f265fef69669d.json",
|
||||
"cargo/native-evaluator-1/inputs.zip": "capsules/186806bcac27975f774ae0c9f92e39592950efb8479c46cf85e11db6f6368774.zip",
|
||||
"cargo/native-evaluator-1/process.json": "cargo/native-evaluator-1/process.json",
|
||||
"cargo/native-evaluator-1/result.json": "cargo/native-evaluator-1/result.json",
|
||||
"cargo/native-evaluator-1/started.json": "cargo/native-evaluator-1/started.json",
|
||||
"cargo/native-evaluator-1/stderr.log": "cargo/native-evaluator-1/stderr.log",
|
||||
"cargo/native-evaluator-1/stdout.log": "cargo/native-evaluator-1/stdout.log",
|
||||
"cargo/native-evaluator-2/inputs.json": "capsules/c90dfc8830587ea9d6018ab4dd01765c5090ea31a8d518c2f87f95676f2f0d09.json",
|
||||
"cargo/native-evaluator-2/inputs.zip": "capsules/7305e898bfb082695359e07a8dd327bbab7ad91eba5ac0e7ee4876f9afbf55dc.zip",
|
||||
"cargo/native-evaluator-2/process.json": "cargo/native-evaluator-2/process.json",
|
||||
"cargo/native-evaluator-2/result.json": "cargo/native-evaluator-2/result.json",
|
||||
"cargo/native-evaluator-2/started.json": "cargo/native-evaluator-2/started.json",
|
||||
"cargo/native-evaluator-2/stderr.log": "cargo/native-evaluator-2/stderr.log",
|
||||
"cargo/native-evaluator-2/stdout.log": "cargo/native-evaluator-2/stdout.log",
|
||||
"cargo/native-evaluator-3/inputs.json": "capsules/bd15948fb069a031f225667ddfe9812b62027ff750de9898329e43ba6a6f8b25.json",
|
||||
"cargo/native-evaluator-3/inputs.zip": "capsules/4da16ce91cfe02315396cff0e56c47747210d157199123176f738caf867f7f81.zip",
|
||||
"cargo/native-evaluator-3/process.json": "cargo/native-evaluator-3/process.json",
|
||||
"cargo/native-evaluator-3/result.json": "cargo/native-evaluator-3/result.json",
|
||||
"cargo/native-evaluator-3/started.json": "cargo/native-evaluator-3/started.json",
|
||||
"cargo/native-evaluator-3/stderr.log": "cargo/native-evaluator-3/stderr.log",
|
||||
"cargo/native-evaluator-3/stdout.log": "cargo/native-evaluator-3/stdout.log",
|
||||
"cargo/native-evaluator-4/inputs.json": "capsules/dba5bc7789a56998fe9b877af26ab8a442b31de55443aaaea23930d58c8610ed.json",
|
||||
"cargo/native-evaluator-4/inputs.zip": "capsules/39d4b0d3f85c4ea3d9aa506571d46c649f7071f8dd247c5af9b20dea980bcbc6.zip",
|
||||
"cargo/native-evaluator-4/process.json": "cargo/native-evaluator-4/process.json",
|
||||
"cargo/native-evaluator-4/result.json": "cargo/native-evaluator-4/result.json",
|
||||
"cargo/native-evaluator-4/source-proof.json": "cargo/native-evaluator-4/source-proof.json",
|
||||
"cargo/native-evaluator-4/started.json": "cargo/native-evaluator-4/started.json",
|
||||
"cargo/native-evaluator-4/stderr.log": "cargo/native-evaluator-4/stderr.log",
|
||||
"cargo/native-evaluator-4/stdout.log": "cargo/native-evaluator-4/stdout.log",
|
||||
"cargo/native-lint-1/inputs.json": "capsules/dba5bc7789a56998fe9b877af26ab8a442b31de55443aaaea23930d58c8610ed.json",
|
||||
"cargo/native-lint-1/inputs.zip": "capsules/39d4b0d3f85c4ea3d9aa506571d46c649f7071f8dd247c5af9b20dea980bcbc6.zip",
|
||||
"cargo/native-lint-1/process.json": "cargo/native-lint-1/process.json",
|
||||
"cargo/native-lint-1/result.json": "cargo/native-lint-1/result.json",
|
||||
"cargo/native-lint-1/source-proof.json": "cargo/native-lint-1/source-proof.json",
|
||||
"cargo/native-lint-1/started.json": "cargo/native-lint-1/started.json",
|
||||
"cargo/native-lint-1/stderr.log": "cargo/native-lint-1/stderr.log",
|
||||
"cargo/native-lint-1/stdout.log": "cargo/native-lint-1/stdout.log",
|
||||
"comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py",
|
||||
"helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"helpers/58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119.py": "helpers/58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119.py"
|
||||
},
|
||||
"members": {
|
||||
"capsules/186806bcac27975f774ae0c9f92e39592950efb8479c46cf85e11db6f6368774.zip": {
|
||||
"bytes": 3037479,
|
||||
"sha256": "186806bcac27975f774ae0c9f92e39592950efb8479c46cf85e11db6f6368774"
|
||||
},
|
||||
"capsules/2846cbf5a6b9be6d66ce8261af407d6eb84079961d1adb04fd8f265fef69669d.json": {
|
||||
"bytes": 126361,
|
||||
"sha256": "2846cbf5a6b9be6d66ce8261af407d6eb84079961d1adb04fd8f265fef69669d"
|
||||
},
|
||||
"capsules/38a95be43c9c97bc4aa807d93fe4baf3ffbb95f8034ef680765917cb0d8e356a.json": {
|
||||
"bytes": 126187,
|
||||
"sha256": "38a95be43c9c97bc4aa807d93fe4baf3ffbb95f8034ef680765917cb0d8e356a"
|
||||
},
|
||||
"capsules/39d4b0d3f85c4ea3d9aa506571d46c649f7071f8dd247c5af9b20dea980bcbc6.zip": {
|
||||
"bytes": 3037830,
|
||||
"sha256": "39d4b0d3f85c4ea3d9aa506571d46c649f7071f8dd247c5af9b20dea980bcbc6"
|
||||
},
|
||||
"capsules/4d022704e970828afec2106bfb27ed9bac9b1e70a2eee35303eab8a0123e62ce.zip": {
|
||||
"bytes": 3029723,
|
||||
"sha256": "4d022704e970828afec2106bfb27ed9bac9b1e70a2eee35303eab8a0123e62ce"
|
||||
},
|
||||
"capsules/4da16ce91cfe02315396cff0e56c47747210d157199123176f738caf867f7f81.zip": {
|
||||
"bytes": 3037580,
|
||||
"sha256": "4da16ce91cfe02315396cff0e56c47747210d157199123176f738caf867f7f81"
|
||||
},
|
||||
"capsules/66ba48b04e28287483a5a398a1b4666c657f1ee58468b6f971cd05166bf941e2.zip": {
|
||||
"bytes": 3029450,
|
||||
"sha256": "66ba48b04e28287483a5a398a1b4666c657f1ee58468b6f971cd05166bf941e2"
|
||||
},
|
||||
"capsules/69b201c72f3e3a533c3cc7e74660820bd7d6eda9164d7b47dc81c3bedbd410d7.zip": {
|
||||
"bytes": 3029442,
|
||||
"sha256": "69b201c72f3e3a533c3cc7e74660820bd7d6eda9164d7b47dc81c3bedbd410d7"
|
||||
},
|
||||
"capsules/7305e898bfb082695359e07a8dd327bbab7ad91eba5ac0e7ee4876f9afbf55dc.zip": {
|
||||
"bytes": 3037499,
|
||||
"sha256": "7305e898bfb082695359e07a8dd327bbab7ad91eba5ac0e7ee4876f9afbf55dc"
|
||||
},
|
||||
"capsules/8dd6b387c8d3df6d77c878784037679ebecdc775c835c76645caf03b6b31d26b.json": {
|
||||
"bytes": 126187,
|
||||
"sha256": "8dd6b387c8d3df6d77c878784037679ebecdc775c835c76645caf03b6b31d26b"
|
||||
},
|
||||
"capsules/9746cd48ca8eb9ad7a354114770980e0316ec821d5dd14cc129b16b2bde94c28.json": {
|
||||
"bytes": 126187,
|
||||
"sha256": "9746cd48ca8eb9ad7a354114770980e0316ec821d5dd14cc129b16b2bde94c28"
|
||||
},
|
||||
"capsules/bd15948fb069a031f225667ddfe9812b62027ff750de9898329e43ba6a6f8b25.json": {
|
||||
"bytes": 126361,
|
||||
"sha256": "bd15948fb069a031f225667ddfe9812b62027ff750de9898329e43ba6a6f8b25"
|
||||
},
|
||||
"capsules/c90dfc8830587ea9d6018ab4dd01765c5090ea31a8d518c2f87f95676f2f0d09.json": {
|
||||
"bytes": 126361,
|
||||
"sha256": "c90dfc8830587ea9d6018ab4dd01765c5090ea31a8d518c2f87f95676f2f0d09"
|
||||
},
|
||||
"capsules/dba5bc7789a56998fe9b877af26ab8a442b31de55443aaaea23930d58c8610ed.json": {
|
||||
"bytes": 126361,
|
||||
"sha256": "dba5bc7789a56998fe9b877af26ab8a442b31de55443aaaea23930d58c8610ed"
|
||||
},
|
||||
"cargo/app-1/process.json": {
|
||||
"bytes": 203,
|
||||
"sha256": "781bef373e607b766725b8551fb186423dd2ad79c7a81bdf4c19be924991bce2"
|
||||
},
|
||||
"cargo/app-1/result.json": {
|
||||
"bytes": 1993,
|
||||
"sha256": "8e8bcdacfc7910d86609960884232a84e3c26445b6818966e87083c1ecaa4e1b"
|
||||
},
|
||||
"cargo/app-1/started.json": {
|
||||
"bytes": 1595,
|
||||
"sha256": "def856f3e61baf63463eda10b7862aed828c660c58eda2e053ea03b751bcbffd"
|
||||
},
|
||||
"cargo/app-1/stderr.log": {
|
||||
"bytes": 167,
|
||||
"sha256": "3df24f6df5880c92b890771c47c6d5c15c3f1bb217ec93c47ed511fcba167932"
|
||||
},
|
||||
"cargo/app-1/stdout.log": {
|
||||
"bytes": 305019,
|
||||
"sha256": "13a98d50d7db5c0b49dcaf76b708de5a24a7537baf275bd1b8ca4499fc32899b"
|
||||
},
|
||||
"cargo/app-2/process.json": {
|
||||
"bytes": 203,
|
||||
"sha256": "0a5e6383d0798f77f0ab28b62930e4900e23d869ae5c4f8ff681f9fa57925fa2"
|
||||
},
|
||||
"cargo/app-2/result.json": {
|
||||
"bytes": 3331,
|
||||
"sha256": "abf5ebe442a74a2decb2d7ce7cfde1d403a3fd5549c2eb19e2a0681705037325"
|
||||
},
|
||||
"cargo/app-2/started.json": {
|
||||
"bytes": 1595,
|
||||
"sha256": "d51fa60dd8321e24104e80ef4e242a3de16c6f17277f12ba9280f4de41259e0c"
|
||||
},
|
||||
"cargo/app-2/stderr.log": {
|
||||
"bytes": 358,
|
||||
"sha256": "76aaf395cea9081260c438107cf3bf6f29e5e953c84834c9fa5ce89e119897a3"
|
||||
},
|
||||
"cargo/app-2/stdout.log": {
|
||||
"bytes": 309567,
|
||||
"sha256": "ea6c7da18b095fe6e0ff68a3a93ebba8d70a25ba0d776e709b1abbb5893d2ec6"
|
||||
},
|
||||
"cargo/app-full-1/process.json": {
|
||||
"bytes": 189,
|
||||
"sha256": "0d76b9249c4fa562d4447a4913ece97271a656b312e9e59abce7745210ad8b50"
|
||||
},
|
||||
"cargo/app-full-1/result.json": {
|
||||
"bytes": 3309,
|
||||
"sha256": "70cdeb8325d2637d3d303fe6a2846f2b6e078d3760ac3276044e2dd21d8951c4"
|
||||
},
|
||||
"cargo/app-full-1/source-proof.json": {
|
||||
"bytes": 589,
|
||||
"sha256": "4392a0bd617a56eb99c33f166953ed6c6c279c1f8419f267c0699e4554fe205a"
|
||||
},
|
||||
"cargo/app-full-1/started.json": {
|
||||
"bytes": 1576,
|
||||
"sha256": "2fe5b0ce3693d83070136c8316c783d5e969ebf5884f797444b4c6d709323971"
|
||||
},
|
||||
"cargo/app-full-1/stderr.log": {
|
||||
"bytes": 287,
|
||||
"sha256": "ee833f27d0f172fc7e83894d6de8d239a808107ef68ab97a1b30eca189e6f98e"
|
||||
},
|
||||
"cargo/app-full-1/stdout.log": {
|
||||
"bytes": 451111,
|
||||
"sha256": "638a86289ef3465aa163d3d4e9347a04e180dbb969e5412c7d0b6221f9208631"
|
||||
},
|
||||
"cargo/app-lint-1/process.json": {
|
||||
"bytes": 179,
|
||||
"sha256": "ab0fca4258bb3f1c7f770518cf822b0c4cdedbcc1f47eb9e5c8eec085f671ba9"
|
||||
},
|
||||
"cargo/app-lint-1/result.json": {
|
||||
"bytes": 1962,
|
||||
"sha256": "09df89ad0e6aa6841037dcc0d070f9d2bb680ac4a902d469f9426b67c9872cac"
|
||||
},
|
||||
"cargo/app-lint-1/started.json": {
|
||||
"bytes": 1566,
|
||||
"sha256": "8a0926416850695c1efdbaf380db1e1866afbda38db90195be1cfe0810d523e5"
|
||||
},
|
||||
"cargo/app-lint-1/stderr.log": {
|
||||
"bytes": 137,
|
||||
"sha256": "9bd4b4f1c7c5543b160a6ffc825ab5d83252d3aa65175ca3776573190f1074b4"
|
||||
},
|
||||
"cargo/app-lint-1/stdout.log": {
|
||||
"bytes": 269521,
|
||||
"sha256": "4f5198ca82bb40c1c4c7d6e04c1a11e4627f401467dde902661a99de5059da83"
|
||||
},
|
||||
"cargo/app-lint-2/process.json": {
|
||||
"bytes": 179,
|
||||
"sha256": "208e9f1cfc0bdddb51ba6ead2e1ba59c082f7bd2d3349287b533b29cdd4cb5d3"
|
||||
},
|
||||
"cargo/app-lint-2/result.json": {
|
||||
"bytes": 1961,
|
||||
"sha256": "87077e438040fec609e24c05a7a9ccca2b84ba6ca75b67d335dff2db94c7ffe1"
|
||||
},
|
||||
"cargo/app-lint-2/source-proof.json": {
|
||||
"bytes": 589,
|
||||
"sha256": "645e0e80418c8789d29c6b19df9b772f42c4cea4031bbc7551629e13551c509b"
|
||||
},
|
||||
"cargo/app-lint-2/started.json": {
|
||||
"bytes": 1566,
|
||||
"sha256": "a5e12f08ab943a8cc4ffd80e99b667cd94fb3e8ccc29c7feaaf38ab47040cb4d"
|
||||
},
|
||||
"cargo/app-lint-2/stderr.log": {
|
||||
"bytes": 137,
|
||||
"sha256": "5213006f22a050ae9ca14c243f932a92bc0a0a97a48ff3e775ba47dd39a1db5d"
|
||||
},
|
||||
"cargo/app-lint-2/stdout.log": {
|
||||
"bytes": 269521,
|
||||
"sha256": "4c0e37ea210fcfba44b06a08890ade25747d641f6944026157d9d995bddb8cb2"
|
||||
},
|
||||
"cargo/final-format-1/process.json": {
|
||||
"bytes": 116,
|
||||
"sha256": "ab12917196bfb954f9d810e5bdfdfb360e8d488f9828db392bd18be44436ad94"
|
||||
},
|
||||
"cargo/final-format-1/result.json": {
|
||||
"bytes": 1871,
|
||||
"sha256": "1e3ffd415b3f42ec7e27ca533296eee38a8e93210af34e10877cc67bfb33d2d2"
|
||||
},
|
||||
"cargo/final-format-1/source-proof.json": {
|
||||
"bytes": 593,
|
||||
"sha256": "175655f7ae6e59b946cb11acaefeb3b93a1b25bb5811c2aaaf55a34130ca45a9"
|
||||
},
|
||||
"cargo/final-format-1/started.json": {
|
||||
"bytes": 1483,
|
||||
"sha256": "d4f82d0119208abf4cdbd5f3537363ed3579dc1ff1793eaad3b96b5078d2cf93"
|
||||
},
|
||||
"cargo/final-format-1/stderr.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"cargo/final-format-1/stdout.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"cargo/gui-1/process.json": {
|
||||
"bytes": 164,
|
||||
"sha256": "d5a811c5f166f44727ae7fad075afd52c1086eeaee18cb4d9e03198a21b68034"
|
||||
},
|
||||
"cargo/gui-1/result.json": {
|
||||
"bytes": 3231,
|
||||
"sha256": "2299a1310705b25d3f626e38ba57d430253b79af8fd44af909537c3dcf628990"
|
||||
},
|
||||
"cargo/gui-1/source-proof.json": {
|
||||
"bytes": 584,
|
||||
"sha256": "854c6054fa52373be2343df11ca435f74195201bebb2cfa18a9042a8354f9f32"
|
||||
},
|
||||
"cargo/gui-1/started.json": {
|
||||
"bytes": 1541,
|
||||
"sha256": "bb179d903614f80278f6f08d28b96c938aef8f8cf727f360285eca4c2d0bf69c"
|
||||
},
|
||||
"cargo/gui-1/stderr.log": {
|
||||
"bytes": 137,
|
||||
"sha256": "db1dbe32124fb30504eae6320a2e6b4bac2a325789da8bf41cb7d1150a259df5"
|
||||
},
|
||||
"cargo/gui-1/stdout.log": {
|
||||
"bytes": 284179,
|
||||
"sha256": "4fc2c61d96e534155e0bbec2293ff15b6bd0985c4ddd5b14026e786a203a4542"
|
||||
},
|
||||
"cargo/native-build-1/process.json": {
|
||||
"bytes": 180,
|
||||
"sha256": "cdf0a274913e65315c9efcd3de3b6d550e989777bcf231cc2d08eddb2f7f543e"
|
||||
},
|
||||
"cargo/native-build-1/result.json": {
|
||||
"bytes": 3336,
|
||||
"sha256": "ecefec521a6a182f81d2a8ec7081e709953ed229a0175f9738839dac7b6eee1e"
|
||||
},
|
||||
"cargo/native-build-1/source-proof.json": {
|
||||
"bytes": 593,
|
||||
"sha256": "3717f51afcabbaa98352097d86454ab01716340f29c04ea91d5a5cef63e19e76"
|
||||
},
|
||||
"cargo/native-build-1/started.json": {
|
||||
"bytes": 1557,
|
||||
"sha256": "e4f0ccf85e38485f290c5a19fc11fb28d0ed4c0ddd94acac5c772453175d5798"
|
||||
},
|
||||
"cargo/native-build-1/stderr.log": {
|
||||
"bytes": 130,
|
||||
"sha256": "b1597874a325dbd287977a8e33b5c1f2751f93dae697c266449c48556d78943c"
|
||||
},
|
||||
"cargo/native-build-1/stdout.log": {
|
||||
"bytes": 41744,
|
||||
"sha256": "5409535ec78668a696ebf6664fd25c3f33de4d92f891462d0a8615d20ccfbcb5"
|
||||
},
|
||||
"cargo/native-evaluator-1/process.json": {
|
||||
"bytes": 205,
|
||||
"sha256": "d111ec2e42ed4ec15c073fcad0c7a422ec8ae9516b0f1aaf8da33a82388a5ee0"
|
||||
},
|
||||
"cargo/native-evaluator-1/result.json": {
|
||||
"bytes": 3406,
|
||||
"sha256": "96471ad771fd168b98577e74b564c4b0d6875db04dc08603dfbbc13855e43f6a"
|
||||
},
|
||||
"cargo/native-evaluator-1/started.json": {
|
||||
"bytes": 1592,
|
||||
"sha256": "2bbc7c17ee5fcc8f54a3f68e4ddfc8b62eb283eacddb023e17866f44da01f1df"
|
||||
},
|
||||
"cargo/native-evaluator-1/stderr.log": {
|
||||
"bytes": 417,
|
||||
"sha256": "ed258cdfd6b424766a0737672221f42f1d3f8f8082caaf6ec545c3519d044291"
|
||||
},
|
||||
"cargo/native-evaluator-1/stdout.log": {
|
||||
"bytes": 42830,
|
||||
"sha256": "a1e3bfcfefaaceaf4ca6cdd5a7b25d79219b458674d77c9babd96c23f2c589a7"
|
||||
},
|
||||
"cargo/native-evaluator-2/process.json": {
|
||||
"bytes": 282,
|
||||
"sha256": "d1d942f30a6e7f7cdf8e9fafd53e1822c12d8418b03aa75fa729576c4ff08ee7"
|
||||
},
|
||||
"cargo/native-evaluator-2/result.json": {
|
||||
"bytes": 3494,
|
||||
"sha256": "5466a6dff5060c6c445f036aff13649ef11ee6b2e35d063dd55d03aba6065a52"
|
||||
},
|
||||
"cargo/native-evaluator-2/started.json": {
|
||||
"bytes": 1679,
|
||||
"sha256": "464d9c26ee57e34028eb56fee64fb5fafa6e1da35209041cf834004da32ad4f3"
|
||||
},
|
||||
"cargo/native-evaluator-2/stderr.log": {
|
||||
"bytes": 2805,
|
||||
"sha256": "17f4f9f1630cc0b4b7842a6d482cd51538b4c173c65a5115af5fdf80bc2fb18a"
|
||||
},
|
||||
"cargo/native-evaluator-2/stdout.log": {
|
||||
"bytes": 42067,
|
||||
"sha256": "bad6783ab071f7c32d71ea63d32c0a2c327e4c97a3f888aa46b4fe46beb497f7"
|
||||
},
|
||||
"cargo/native-evaluator-3/process.json": {
|
||||
"bytes": 205,
|
||||
"sha256": "4f28e9ebb18d9a21ddda5ba54b964a461527fcb94918ad0f962b45a72d667e3d"
|
||||
},
|
||||
"cargo/native-evaluator-3/result.json": {
|
||||
"bytes": 3405,
|
||||
"sha256": "6309e798859497b4f9b8c70defb30aa057107f940741c381401730d07d35d8b9"
|
||||
},
|
||||
"cargo/native-evaluator-3/started.json": {
|
||||
"bytes": 1592,
|
||||
"sha256": "f2067809a616c8d8b25ee9550b2ce103953af9682d7c2eaaf018b10326aa3b19"
|
||||
},
|
||||
"cargo/native-evaluator-3/stderr.log": {
|
||||
"bytes": 329,
|
||||
"sha256": "47f31fb0ea403c55bb53f7223b132f819be13f41f9959cea9f3f01a9a9753918"
|
||||
},
|
||||
"cargo/native-evaluator-3/stdout.log": {
|
||||
"bytes": 42296,
|
||||
"sha256": "69ea9486c5e0d2a44d32eb614c18993a47096fcae3a3c5bcdcc28969f45adff0"
|
||||
},
|
||||
"cargo/native-evaluator-4/process.json": {
|
||||
"bytes": 205,
|
||||
"sha256": "9d9cb56eb7f0d93db8e5778770ec77b283e98e8c976937c8531ba8fae16b1cab"
|
||||
},
|
||||
"cargo/native-evaluator-4/result.json": {
|
||||
"bytes": 3402,
|
||||
"sha256": "3886843d7f2ee1c15644be84eba8d45503c535cdda889402db9499c06ca44a48"
|
||||
},
|
||||
"cargo/native-evaluator-4/source-proof.json": {
|
||||
"bytes": 597,
|
||||
"sha256": "bb6b581c2777f9f7d8bd760c5e9445595178e51b819e79da7bdcf4a53507485f"
|
||||
},
|
||||
"cargo/native-evaluator-4/started.json": {
|
||||
"bytes": 1592,
|
||||
"sha256": "7c4399f605e50f6efdf92beeb3ee5a52bb707f7f8847ab67632b95a44df3b09e"
|
||||
},
|
||||
"cargo/native-evaluator-4/stderr.log": {
|
||||
"bytes": 259,
|
||||
"sha256": "1d7fdabc93e927cdac3cccaebf9e265ac033d33265c6bf8a5d4a582eab396a0c"
|
||||
},
|
||||
"cargo/native-evaluator-4/stdout.log": {
|
||||
"bytes": 42296,
|
||||
"sha256": "af8d5ea8bc6c521922d44459d0c7713fa7357635da4d8004176079249b540ac2"
|
||||
},
|
||||
"cargo/native-lint-1/process.json": {
|
||||
"bytes": 205,
|
||||
"sha256": "f4963c685ed65f9ffd81bebac148fa0110e59e4e0654f72f5afce2aac4416f69"
|
||||
},
|
||||
"cargo/native-lint-1/result.json": {
|
||||
"bytes": 1991,
|
||||
"sha256": "63ac32d42f3c08809d74357a1f12dcf356e67ff96ccc005c31a6badea10052c2"
|
||||
},
|
||||
"cargo/native-lint-1/source-proof.json": {
|
||||
"bytes": 592,
|
||||
"sha256": "96aeabb5ceba339ec73cd9421abec725a3a4c435321bffcfb7f5e3059cba920e"
|
||||
},
|
||||
"cargo/native-lint-1/started.json": {
|
||||
"bytes": 1597,
|
||||
"sha256": "d1e9179f51fd7f00afb36b302b68f64360457c7350e5e557a9beea8caed444d0"
|
||||
},
|
||||
"cargo/native-lint-1/stderr.log": {
|
||||
"bytes": 130,
|
||||
"sha256": "cb2e7c507ccecb98120d548382a5eb047cc5d05c1b8dbc1fc465743234db4e8f"
|
||||
},
|
||||
"cargo/native-lint-1/stdout.log": {
|
||||
"bytes": 38955,
|
||||
"sha256": "edf033a085751b0892df88fcbe287d5a00e9fc53fd4d12da310037e7720f7342"
|
||||
},
|
||||
"comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py": {
|
||||
"bytes": 10094,
|
||||
"sha256": "7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a"
|
||||
},
|
||||
"helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py": {
|
||||
"bytes": 3827,
|
||||
"sha256": "1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20"
|
||||
},
|
||||
"helpers/58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119.py": {
|
||||
"bytes": 1399,
|
||||
"sha256": "58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
"""Package explicitly selected closed gate captures without running Cargo."""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import json
|
||||
import zipfile
|
||||
from verify import RAW_FILES, pin, parsed, need, run_name, verify_data, verify_package
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def collect(root, runs, helpers, comparators=()):
|
||||
gates = root / ".local/allocation-group-gates"
|
||||
candidates = [gates / name for name in ("gate.py", "source-proof.py")] + helpers
|
||||
helper_bytes = [path.read_bytes() for path in candidates]
|
||||
comparator_paths = [root / ".local/process-source-evidence-tools/verify.py", *comparators]
|
||||
comparator_bytes = [path.read_bytes() for path in comparator_paths if path.is_file()]
|
||||
payload, logical, specs = {}, {}, {}
|
||||
def add(name, data):
|
||||
stored = ("capsules/" + pin(data)["sha256"] + Path(name).suffix
|
||||
if name.rsplit("/", 1)[1] in {"inputs.json", "inputs.zip"} else name)
|
||||
need(stored not in payload or payload[stored] == data, "deduplication collision")
|
||||
payload[stored] = data
|
||||
logical[name] = stored
|
||||
helper_catalog = {}
|
||||
for name, data in zip(("gate.py", "source-proof.py"), helper_bytes):
|
||||
helper = "helpers/" + pin(data)["sha256"] + ".py"
|
||||
add(helper, data)
|
||||
helper_catalog[name] = helper
|
||||
for run in runs:
|
||||
folder = gates / run
|
||||
need(folder.is_dir(), "run directory missing: " + run)
|
||||
paths = list(folder.iterdir())
|
||||
names = {path.name for path in paths}
|
||||
need(all(path.is_file() and not path.is_symlink() for path in paths)
|
||||
and names in (RAW_FILES, RAW_FILES | {"source-proof.json"}), "exact closed raw files required: " + run)
|
||||
raw = {path.name: path.read_bytes() for path in paths}
|
||||
result = parsed(raw["result.json"])
|
||||
need("finish" in result and result["inputsUnchanged"] is True, "unfinished/changed run refused: " + run)
|
||||
matches = [data for data in helper_bytes if pin(data) == result["helper"]]
|
||||
need(bool(matches), "exact historical helper unavailable for " + run + "; supply --helper PATH")
|
||||
helper = "helpers/" + result["helper"]["sha256"] + ".py"
|
||||
add(helper, matches[0])
|
||||
for name, data in sorted(raw.items()):
|
||||
add("cargo/" + run + "/" + name, data)
|
||||
specs[run] = {key: result[key] for key in ("command", "head", "exitCode", "inputCount")}
|
||||
specs[run]["helper"] = helper
|
||||
if "source-proof.json" in raw:
|
||||
proof = parsed(raw["source-proof.json"])
|
||||
matches = [data for data in comparator_bytes if pin(data) == proof["comparator"]]
|
||||
need(bool(matches), "exact source comparator unavailable for " + run + "; supply --comparator PATH")
|
||||
comparator = "comparators/" + proof["comparator"]["sha256"] + ".py"
|
||||
add(comparator, matches[0])
|
||||
specs[run]["sourceProof"] = {"commit": proof["commit"], "pin": pin(raw["source-proof.json"]), "comparator": comparator}
|
||||
manifest = {"schema": 1, "kind": "allocation-multiselect-cargo-gates", "runs": specs,
|
||||
"captureHelpers": helper_catalog,
|
||||
"logicalFiles": dict(sorted(logical.items())),
|
||||
"members": {name: pin(data) for name, data in sorted(payload.items())}}
|
||||
summary = verify_data(manifest, payload.__getitem__)
|
||||
return payload, manifest, summary
|
||||
|
||||
|
||||
def write_json(path, value):
|
||||
with path.open("xb") as output:
|
||||
output.write((json.dumps(value, indent=2) + "\n").encode("utf-8"))
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--root", type=Path, required=True, help="checkout holding .local/allocation-group-gates")
|
||||
parser.add_argument("--runs", nargs="+", required=True, help="explicit run names, including failures to retain")
|
||||
parser.add_argument("--helper", type=Path, action="append", default=[], help="additional exact historical helper (repeatable)")
|
||||
parser.add_argument("--comparator", type=Path, action="append", default=[], help="additional exact historical source comparator (repeatable)")
|
||||
parser.add_argument("--output", type=Path, help="fresh, nonexistent package directory (required unless --check-only)")
|
||||
parser.add_argument("--check-only", action="store_true", help="validate raw captures in memory without creating an archive")
|
||||
args = parser.parse_args()
|
||||
need(all(run_name(run) for run in args.runs) and len(args.runs) == len(set(args.runs)), "unique safe run names required")
|
||||
need(args.check_only or args.output is not None, "--output is required unless --check-only")
|
||||
if not args.check_only:
|
||||
need(not args.output.exists(), "output must be a fresh directory: " + str(args.output))
|
||||
payload, manifest, summary = collect(args.root.resolve(), args.runs, args.helper, args.comparator)
|
||||
if args.check_only:
|
||||
print("PASS raw consistency; no package written:", summary)
|
||||
return
|
||||
out = args.output.resolve()
|
||||
out.mkdir(parents=True, exist_ok=False)
|
||||
for name in ("evidence.zip", "manifest.json", "files.json"):
|
||||
need(not (out / name).exists(), "existing package output refused: " + name)
|
||||
for name in ("prepare.py", "verify.py", "README.md"):
|
||||
data = (HERE / name).read_bytes()
|
||||
destination = out / name
|
||||
if destination.exists():
|
||||
need(destination.read_bytes() == data, "different existing package tool: " + name)
|
||||
else:
|
||||
with destination.open("xb") as output:
|
||||
output.write(data)
|
||||
with zipfile.ZipFile(out / "evidence.zip", "x", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as archive:
|
||||
for name, data in sorted(payload.items()):
|
||||
info = zipfile.ZipInfo(name, date_time=(2026, 9, 23, 0, 0, 0))
|
||||
info.compress_type = zipfile.ZIP_DEFLATED
|
||||
info.external_attr = 0o100644 << 16
|
||||
archive.writestr(info, data)
|
||||
write_json(out / "manifest.json", manifest)
|
||||
names = ("prepare.py", "verify.py", "README.md", "manifest.json", "evidence.zip")
|
||||
write_json(out / "files.json", {name: pin((out / name).read_bytes()) for name in names})
|
||||
need(verify_package(out) == summary, "written package differs from validated captures")
|
||||
print("PASS package readback:", out, summary)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,194 @@
|
||||
"""Offline consistency checks only; never execute or extract archived inputs."""
|
||||
from pathlib import Path, PurePosixPath
|
||||
from datetime import datetime
|
||||
import argparse
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import math
|
||||
import re
|
||||
import stat
|
||||
import zipfile
|
||||
|
||||
RAW_FILES = {"started.json", "process.json", "result.json", "stdout.log", "stderr.log", "inputs.json", "inputs.zip"}
|
||||
OUTER_FILES = {"prepare.py", "verify.py", "README.md", "manifest.json", "evidence.zip"}
|
||||
|
||||
|
||||
def need(condition, message):
|
||||
if not condition:
|
||||
raise ValueError(message)
|
||||
|
||||
|
||||
def pin(data):
|
||||
return {"bytes": len(data), "sha256": hashlib.sha256(data).hexdigest()}
|
||||
|
||||
|
||||
def valid_pin(value):
|
||||
return (isinstance(value, dict) and set(value) == {"bytes", "sha256"}
|
||||
and type(value["bytes"]) is int and value["bytes"] >= 0
|
||||
and isinstance(value["sha256"], str)
|
||||
and re.fullmatch(r"[0-9a-f]{64}", value["sha256"]) is not None)
|
||||
|
||||
|
||||
def safe(name):
|
||||
return (isinstance(name, str) and bool(name) and "\\" not in name and ":" not in name
|
||||
and all(ord(char) >= 32 for char in name)
|
||||
and not PurePosixPath(name).is_absolute()
|
||||
and all(part not in ("", ".", "..") for part in name.split("/")))
|
||||
|
||||
|
||||
def run_name(name):
|
||||
return isinstance(name, str) and re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]*", name) is not None
|
||||
|
||||
|
||||
def parsed(data):
|
||||
def pairs(items):
|
||||
result = {}
|
||||
for key, value in items:
|
||||
need(key not in result, "duplicate JSON key: " + key)
|
||||
result[key] = value
|
||||
return result
|
||||
def constant(value):
|
||||
raise ValueError("nonfinite JSON constant: " + value)
|
||||
return json.loads(data, object_pairs_hook=pairs, parse_constant=constant)
|
||||
|
||||
|
||||
def inventory(archive, expected):
|
||||
names = archive.namelist()
|
||||
need(len(names) == len(set(names)) and set(names) == set(expected), "exact ZIP membership")
|
||||
for entry in archive.infolist():
|
||||
need(safe(entry.filename) and not entry.is_dir(), "unsafe ZIP member: " + entry.filename)
|
||||
need(not stat.S_ISLNK(entry.external_attr >> 16), "ZIP symlink: " + entry.filename)
|
||||
need(valid_pin(expected[entry.filename]), "invalid member pin: " + entry.filename)
|
||||
need(entry.file_size == expected[entry.filename]["bytes"], "ZIP recorded size: " + entry.filename)
|
||||
|
||||
|
||||
def ordered_times(*values):
|
||||
times = [datetime.fromisoformat(value) for value in values]
|
||||
return all(value.utcoffset() is not None for value in times) and times == sorted(times)
|
||||
|
||||
|
||||
def verify_data(manifest, read):
|
||||
"""Shared read-only verifier for packaged bytes or prepare's in-memory payload."""
|
||||
need(manifest["schema"] == 1 and manifest["kind"] == "allocation-multiselect-cargo-gates", "manifest schema")
|
||||
runs, logical, stored = manifest["runs"], manifest["logicalFiles"], manifest["members"]
|
||||
helpers = manifest["captureHelpers"]
|
||||
need(isinstance(helpers, dict) and set(helpers) == {"gate.py", "source-proof.py"}, "capture helper names")
|
||||
need(all(isinstance(name, str) and re.fullmatch(r"helpers/[0-9a-f]{64}\.py", name)
|
||||
for name in helpers.values()), "capture helper mapping")
|
||||
need(isinstance(runs, dict) and bool(runs) and all(run_name(run) for run in runs), "explicit run inventory")
|
||||
need(all(safe(name) for name in logical) and all(safe(name) for name in stored), "unsafe manifest path")
|
||||
need(set(logical.values()) == set(stored), "unreferenced or absent stored members")
|
||||
for name, expected in stored.items():
|
||||
need(valid_pin(expected) and pin(read(name)) == expected, "stored member hash: " + name)
|
||||
expected_logical = {"cargo/" + run + "/" + name for run in runs for name in RAW_FILES}
|
||||
expected_logical.update(spec["helper"] for spec in runs.values())
|
||||
expected_logical.update(helpers.values())
|
||||
for run, spec in runs.items():
|
||||
if "sourceProof" in spec:
|
||||
proof_spec = spec["sourceProof"]
|
||||
need(set(proof_spec) == {"commit", "pin", "comparator"}, run + ": exact source proof spec")
|
||||
need(isinstance(proof_spec["commit"], str) and re.fullmatch(r"[0-9a-f]{40}", proof_spec["commit"]), run + ": named full source commit")
|
||||
need(valid_pin(proof_spec["pin"]), run + ": source proof pin")
|
||||
need(isinstance(proof_spec["comparator"], str) and re.fullmatch(r"comparators/[0-9a-f]{64}\.py", proof_spec["comparator"]), run + ": comparator path")
|
||||
expected_logical.update({"cargo/" + run + "/source-proof.json", proof_spec["comparator"]})
|
||||
need(set(logical) == expected_logical, "exact logical raw/helper inventory")
|
||||
for name, destination in logical.items():
|
||||
if name.startswith("cargo/") and name.rsplit("/", 1)[1] in {"inputs.json", "inputs.zip"}:
|
||||
expected = "capsules/" + stored[destination]["sha256"] + PurePosixPath(name).suffix
|
||||
need(destination == expected, "input capsule mapping: " + name)
|
||||
else:
|
||||
need(destination == name, "only input maps/archives may be deduplicated: " + name)
|
||||
def raw(name):
|
||||
return read(logical[name])
|
||||
for name, helper in helpers.items():
|
||||
need(helper == "helpers/" + pin(raw(helper))["sha256"] + ".py", "named helper hash: " + name)
|
||||
seen_capsules = set()
|
||||
for run, spec in runs.items():
|
||||
prefix = "cargo/" + run + "/"
|
||||
start = parsed(raw(prefix + "started.json"))
|
||||
process = parsed(raw(prefix + "process.json"))
|
||||
result = parsed(raw(prefix + "result.json"))
|
||||
required_start = {"start", "command", "cwd", "head", "scope", "inputs", "archive",
|
||||
"inputCount", "environment", "helper", "cargo", "rustc"}
|
||||
need(required_start <= start.keys(), run + ": incomplete start receipt")
|
||||
need(all(result.get(key) == value for key, value in start.items()), run + ": start/result relationship")
|
||||
command = result["command"]
|
||||
need(isinstance(command, list) and command and command[0] == "cargo"
|
||||
and all(isinstance(arg, str) for arg in command), run + ": Cargo command")
|
||||
need(command == process["command"] == spec["command"], run + ": command relationship")
|
||||
need(result["head"] == spec["head"] and re.fullmatch(r"(?:[0-9a-f]{40}|[0-9a-f]{64})", result["head"]), run + ": HEAD")
|
||||
need(type(result["exitCode"]) is int and result["exitCode"] == spec["exitCode"], run + ": recorded exit")
|
||||
need(result["inputsUnchanged"] is True, run + ": changed inputs refused")
|
||||
need(type(result["seconds"]) in (int, float) and math.isfinite(result["seconds"])
|
||||
and result["seconds"] >= 0, run + ": duration")
|
||||
need(type(process["pid"]) is int and process["pid"] > 0, run + ": process identity")
|
||||
need(ordered_times(start["start"], process["observed"], result["finish"]), run + ": completed timestamp order")
|
||||
for field, filename in [("inputs", "inputs.json"), ("archive", "inputs.zip"), ("stdout", "stdout.log"), ("stderr", "stderr.log")]:
|
||||
need(result[field] == pin(raw(prefix + filename)), run + ": " + field + " pin")
|
||||
need(valid_pin(result["helper"]) and result["helper"] == pin(raw(spec["helper"])), run + ": helper pin")
|
||||
need(spec["helper"] == "helpers/" + result["helper"]["sha256"] + ".py", run + ": helper mapping")
|
||||
inputs = parsed(raw(prefix + "inputs.json"))
|
||||
need(isinstance(inputs, dict) and all(safe(name) for name in inputs), run + ": safe input inventory")
|
||||
need(type(result["inputCount"]) is int and len(inputs) == result["inputCount"] == spec["inputCount"], run + ": input counts")
|
||||
if "sourceProof" in spec:
|
||||
proof_spec = spec["sourceProof"]
|
||||
proof_bytes = raw(prefix + "source-proof.json")
|
||||
proof = parsed(proof_bytes)
|
||||
need(pin(proof_bytes) == proof_spec["pin"], run + ": source proof pinned bytes")
|
||||
need(proof["run"] == run and proof["commit"] == proof_spec["commit"], run + ": proof run/commit identity")
|
||||
need(proof["status"] == "PASS exact captured input inventory and Git blob bytes", run + ": source proof status")
|
||||
need(proof["inputCount"] == len(inputs) and proof["inputMap"] == result["inputs"], run + ": proof input relationship")
|
||||
need(proof["result"] == pin(raw(prefix + "result.json")), run + ": proof result relationship")
|
||||
need(ordered_times(result["finish"], proof["verified"]), run + ": proof time after closed gate")
|
||||
comparator = proof_spec["comparator"]
|
||||
need(valid_pin(proof["comparator"]) and pin(raw(comparator)) == proof["comparator"], run + ": comparator bytes")
|
||||
need(comparator == "comparators/" + proof["comparator"]["sha256"] + ".py", run + ": comparator identity")
|
||||
need(sum(name.startswith("crates/vendor/iced_widget/") for name in inputs) == 71, run + ": vendor count")
|
||||
capsule = (logical[prefix + "inputs.json"], logical[prefix + "inputs.zip"])
|
||||
if capsule not in seen_capsules:
|
||||
with zipfile.ZipFile(io.BytesIO(raw(prefix + "inputs.zip"))) as source:
|
||||
inventory(source, inputs)
|
||||
for name, expected in inputs.items():
|
||||
need(pin(source.read(name)) == expected, run + ": source hash " + name)
|
||||
seen_capsules.add(capsule)
|
||||
artifacts = []
|
||||
for line in raw(prefix + "stdout.log").decode("utf-8", errors="replace").splitlines():
|
||||
try:
|
||||
value = json.loads(line)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
if isinstance(value, dict) and value.get("reason") == "compiler-artifact" and value.get("executable"):
|
||||
artifacts.append(value)
|
||||
need(isinstance(result["artifacts"], list), run + ": artifact records")
|
||||
need(artifacts == [record["cargo"] for record in result["artifacts"]], run + ": Cargo JSON artifact relationship")
|
||||
for record in result["artifacts"]:
|
||||
need(set(record) == {"cargo", "readback"}
|
||||
and (record["readback"] is None or valid_pin(record["readback"])), run + ": artifact readback pin")
|
||||
print(run, "recorded exit", result["exitCode"], "inputs", len(inputs), "executable records", len(artifacts))
|
||||
return {"runs": len(runs), "recordedFailures": sum(spec["exitCode"] != 0 for spec in runs.values()),
|
||||
"logicalFiles": len(logical), "storedMembers": len(stored), "sourceCapsulePairs": len(seen_capsules),
|
||||
"captureHelpers": helpers, "sourceProofs": sum("sourceProof" in spec for spec in runs.values())}
|
||||
|
||||
|
||||
def verify_package(root):
|
||||
files = parsed((root / "files.json").read_bytes())
|
||||
need(set(files) == OUTER_FILES, "outer file inventory")
|
||||
for name, expected in files.items():
|
||||
need(valid_pin(expected) and pin((root / name).read_bytes()) == expected, "outer hash: " + name)
|
||||
manifest = parsed((root / "manifest.json").read_bytes())
|
||||
with zipfile.ZipFile(root / "evidence.zip") as archive:
|
||||
inventory(archive, manifest["members"])
|
||||
return verify_data(manifest, archive.read)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("package", nargs="?", type=Path, default=Path(__file__).resolve().parent)
|
||||
args = parser.parse_args()
|
||||
print("PASS offline consistency:", verify_package(args.package))
|
||||
print("Historical receipts only; no source/binary execution or native acceptance claim.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,9 @@
|
||||
# Allocation native evidence package (review candidate)
|
||||
|
||||
Adapted from versioned Process native prepare/verify tools. Explicit completed runs only; raw bytes and historical helpers are archived without extraction or execution. Collector refuses existing output and checks raw run bytes again after collection. Run-1 remains aborted preflight, run-2 remains failed baseline verification, all other runs remain unreviewed. Archive consistency does not decide native acceptance.
|
||||
|
||||
`python -B prepare.py --root CHECKOUT --runs run-1 run-2 run-3 run-4 run-5 run-6 --check-only`
|
||||
|
||||
After review, replace `--check-only` with `--output FRESH_DIRECTORY`. Verify with `python -B PACKAGE/verify.py`. Do not include active runs. Exact raw members, source capsules, fixture/snapshot maps, build receipt/artifact relationships, logs, captures, helper versions and Allocation history/expected-path/sentinel checks are independently read back. Historical source capsules are included once for self-contained provenance; the seven source-commit proofs remain in the sibling gate package and should be reviewed with it. No new Git comparison or binary execution occurs.
|
||||
|
||||
Missing capture-executed.js is recorded as null, not reconstructed from current capture.js. Prepared capture.js bytes remain pinned; this cannot establish exact executed UI code or PID-window binding. Images/accessibility/lifetime metadata cannot prove physical gesture sequence, one-step history, held input, focus/camera, or usability. Parent observations and independent review establish those bounded claims. Prefix sentinels establish exact leading fixture comments only. Existing 18 model warnings remain advisory fixture incompleteness; inspect the acceptance report. The archive records executable hashes and Cargo artifact receipts, not executable binaries themselves.
|
||||
Binary file not shown.
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"prepare.py": {
|
||||
"bytes": 7267,
|
||||
"sha256": "ae0cd9fb7f31e004c3fcdc66666f0330944fc658d54b79ef9221056e559f9ecc"
|
||||
},
|
||||
"verify.py": {
|
||||
"bytes": 18702,
|
||||
"sha256": "23d3a705e4b6fab536d0399da9102dc535154ffe991161bcbccd4982b640e0fb"
|
||||
},
|
||||
"README.md": {
|
||||
"bytes": 1836,
|
||||
"sha256": "988e01bb8b3aadaeaa06d12f83ea670807f6448da4b6464154349fe5881ae639"
|
||||
},
|
||||
"manifest.json": {
|
||||
"bytes": 212419,
|
||||
"sha256": "11223b9ef83b9191d7feebf3f268dff11b154e2da0e635357ee3357218e41520"
|
||||
},
|
||||
"evidence.zip": {
|
||||
"bytes": 12480731,
|
||||
"sha256": "6c4fb3151d46141b56e0e5df24d8ded0c644fa01bf6b2b575952eea55ae3c61e"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,122 @@
|
||||
"""Package explicitly named closed Allocation native runs; never execute archived helpers."""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import io
|
||||
import json
|
||||
import re
|
||||
import zipfile
|
||||
from verify import pin, parsed, need, norm, relative, safe, zip_inventory, verify_data, verify_package
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
NOTES = {"run-1": "Aborted chooser preflight; fixture not opened; no history acceptance.",
|
||||
"run-2": "Aborted baseline verification: captured checker exit3221225781; no history acceptance."}
|
||||
|
||||
|
||||
def collect(root, runs, extra_helpers=()):
|
||||
native = root / ".local/allocation-multiselect-native"
|
||||
candidates = [p for p in native.iterdir() if p.is_file()]
|
||||
candidates += sorted((root / ".local/allocation-group-gates").glob("*.py"))
|
||||
candidates += [p for run in runs for p in (native / run / "helpers-original").glob("*") if p.is_file()]
|
||||
candidates += list(extra_helpers)
|
||||
helpers = [(p.suffix, p.read_bytes()) for p in candidates]
|
||||
payload, specs = {}, {}
|
||||
def add(name, data):
|
||||
need(safe(name) and (name not in payload or payload[name] == data), "member collision: " + name)
|
||||
payload[name] = data
|
||||
return name
|
||||
def helper(expected):
|
||||
matches = [(suffix, data) for suffix, data in helpers if pin(data) == expected]
|
||||
need(matches, "exact historical helper unavailable: " + str(expected))
|
||||
suffix, data = matches[0]
|
||||
return add("helpers/" + expected["sha256"] + suffix, data)
|
||||
def tree(folder, prefix):
|
||||
need(folder.is_dir(), "missing directory: " + str(folder))
|
||||
for path in sorted(folder.rglob("*")):
|
||||
need(not path.is_symlink() and not getattr(path, "is_junction", lambda: False)(), "linked evidence input")
|
||||
if path.is_file():
|
||||
add(prefix + path.relative_to(folder).as_posix(), path.read_bytes())
|
||||
for run in runs:
|
||||
folder = native / run
|
||||
need((folder / "finished.json").is_file(), "open/unlaunched run refused: " + run)
|
||||
prepared = parsed((folder / "prepared.json").read_bytes())
|
||||
tree(folder, "runs/" + run + "/raw/")
|
||||
spec = {"originalRun": norm(prepared["cwd"]) + "/.local/allocation-multiselect-native/" + run,
|
||||
"reviewStatus": "aborted" if run in NOTES else "unreviewed",
|
||||
"reviewNote": NOTES[run] if run in NOTES else "Packaging consistency does not decide native acceptance. Consult the independent parent review.",
|
||||
"helpers": {name: helper(expected) for name, expected in prepared["helpers"].items()},
|
||||
"executedCapture": pin((folder / "capture-executed.js").read_bytes()) if (folder / "capture-executed.js").is_file() else None, "builds": {}}
|
||||
for kind in ("gui", "checker"):
|
||||
recorded = prepared[kind]
|
||||
rel = relative(recorded["receipt"], prepared["cwd"])
|
||||
need(rel.startswith(".local/allocation-group-gates/") and rel.endswith("/result.json"), "unexpected receipt location")
|
||||
receipt = root / rel
|
||||
result_data = receipt.read_bytes()
|
||||
need(pin(result_data) == recorded["receiptPin"], "historical build receipt changed")
|
||||
result = parsed(result_data)
|
||||
prefix = "builds/" + pin(result_data)["sha256"] + "/"
|
||||
build = {"originalRelative": rel, "helper": helper(result["helper"])}
|
||||
for name in ("started.json", "process.json", "result.json", "stdout.log", "stderr.log", "inputs.json", "inputs.zip"):
|
||||
data = (receipt.parent / name).read_bytes()
|
||||
# Deduplicate source capsules independently of differing build receipts.
|
||||
member = ("inputs/" + pin(data)["sha256"] + Path(name).suffix) if name.startswith("inputs.") else prefix + name
|
||||
build[name] = add(member, data)
|
||||
spec["builds"][kind] = build
|
||||
if kind == "checker":
|
||||
capsule = payload[build["inputs.zip"]]
|
||||
need(pin(capsule) == result["archive"], "historical checker capsule pin")
|
||||
inputs = parsed(payload[build["inputs.json"]])
|
||||
with zipfile.ZipFile(io.BytesIO(capsule)) as archive:
|
||||
zip_inventory(archive, inputs)
|
||||
source = archive.read("crates/oa-core/examples/allocation_multiselect_native.rs")
|
||||
spec["checkerSource"] = add("checker-source/" + pin(source)["sha256"] + ".rs", source)
|
||||
specs[run] = spec
|
||||
# Re-read every raw run file to reject changes or additions during collection.
|
||||
for run in runs:
|
||||
folder = native / run
|
||||
current = {"runs/" + run + "/raw/" + f.relative_to(folder).as_posix(): f.read_bytes() for f in folder.rglob("*") if f.is_file()}
|
||||
need(current == {n: d for n, d in payload.items() if n.startswith("runs/" + run + "/raw/")}, "run changed during collection: " + run)
|
||||
manifest = {"schema": 1, "kind": "allocation-multiselect-native", "runs": specs,
|
||||
"members": {name: pin(data) for name, data in sorted(payload.items())}}
|
||||
return payload, manifest, verify_data(manifest, payload.__getitem__)
|
||||
|
||||
|
||||
def write_json(path, value):
|
||||
with path.open("xb") as stream:
|
||||
stream.write((json.dumps(value, indent=2) + "\n").encode())
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--root", type=Path, required=True)
|
||||
parser.add_argument("--runs", nargs="+", required=True)
|
||||
parser.add_argument("--output", type=Path)
|
||||
parser.add_argument("--check-only", action="store_true")
|
||||
parser.add_argument("--helper", type=Path, action="append", default=[], help="Exact historical helper bytes if current helper changed")
|
||||
args = parser.parse_args()
|
||||
need(len(args.runs) == len(set(args.runs)) and all(re.fullmatch(r"run-[0-9]+", name) for name in args.runs), "explicit unique run-N names required")
|
||||
need(args.check_only or args.output is not None, "--output or --check-only required")
|
||||
if args.output is not None:
|
||||
need(not args.output.exists(), "fresh nonexistent output directory required")
|
||||
payload, manifest, summary = collect(args.root.resolve(), args.runs, args.helper)
|
||||
if args.check_only:
|
||||
print("PASS raw consistency; no archive written:", summary)
|
||||
return
|
||||
out = args.output.resolve()
|
||||
out.mkdir(parents=True, exist_ok=False)
|
||||
for name in ("prepare.py", "verify.py", "README.md"):
|
||||
with (out / name).open("xb") as stream:
|
||||
stream.write((HERE / name).read_bytes())
|
||||
with zipfile.ZipFile(out / "evidence.zip", "x", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as archive:
|
||||
for name, data in sorted(payload.items()):
|
||||
info = zipfile.ZipInfo(name, date_time=(2026, 9, 23, 0, 0, 0))
|
||||
info.compress_type = zipfile.ZIP_DEFLATED
|
||||
info.external_attr = 0o100644 << 16
|
||||
archive.writestr(info, data)
|
||||
write_json(out / "manifest.json", manifest)
|
||||
write_json(out / "files.json", {name: pin((out / name).read_bytes()) for name in ("prepare.py", "verify.py", "README.md", "manifest.json", "evidence.zip")})
|
||||
need(verify_package(out) == summary, "written package differs from raw evidence")
|
||||
print("PASS package readback:", out, summary)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,255 @@
|
||||
"""Verify archived native receipts and bytes; do not rerun the GUI or checker."""
|
||||
from pathlib import Path, PurePosixPath
|
||||
from datetime import datetime
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import io
|
||||
import re
|
||||
import stat
|
||||
import zipfile
|
||||
|
||||
BASE = {"prepared.json", "started.json", "finished.json", "generation.json", "generation.stdout.log",
|
||||
"generation.stderr.log", "app.stdout.log", "app.stderr.log"}
|
||||
EXPECTED_CHANGED = {
|
||||
"oa-assigned": ["oa/participants.arcadia", "views/diagram_oa_assign.arcadia"],
|
||||
"sa-assigned": ["sa/participants.arcadia", "views/diagram_sa_assign.arcadia"],
|
||||
"oa-arranged": ["views/diagram_oa_arrange.arcadia"],
|
||||
"sa-arranged": ["views/diagram_sa_arrange.arcadia"],
|
||||
"oa-performers": ["views/diagram_oa_assign.arcadia"],
|
||||
}
|
||||
OUTER = {"prepare.py", "verify.py", "README.md", "manifest.json", "evidence.zip"}
|
||||
|
||||
|
||||
def need(ok, message):
|
||||
if not ok:
|
||||
raise ValueError(message)
|
||||
|
||||
|
||||
def pin(data):
|
||||
return {"bytes": len(data), "sha256": hashlib.sha256(data).hexdigest()}
|
||||
|
||||
|
||||
def safe(name):
|
||||
return (isinstance(name, str) and bool(name) and "\\" not in name and ":" not in name
|
||||
and not PurePosixPath(name).is_absolute() and all(ord(c) >= 32 for c in name)
|
||||
and all(part not in ("", ".", "..") for part in name.split("/")))
|
||||
|
||||
|
||||
def parsed(data):
|
||||
def unique(items):
|
||||
result = {}
|
||||
for key, value in items:
|
||||
need(key not in result, "duplicate JSON key: " + key)
|
||||
result[key] = value
|
||||
return result
|
||||
return json.loads(data, object_pairs_hook=unique)
|
||||
|
||||
|
||||
def norm(path):
|
||||
return path.replace("\\", "/").rstrip("/")
|
||||
|
||||
|
||||
def relative(path, root):
|
||||
prefix = norm(root) + "/"
|
||||
need(norm(path).startswith(prefix), "recorded path outside owner: " + path)
|
||||
value = norm(path)[len(prefix):]
|
||||
need(safe(value), "unsafe recorded relative path")
|
||||
return value
|
||||
|
||||
|
||||
def ordered(*values):
|
||||
times = [datetime.fromisoformat(value) for value in values]
|
||||
return all(t.utcoffset() is not None for t in times) and times == sorted(times)
|
||||
|
||||
|
||||
def zip_inventory(archive, members):
|
||||
names = archive.namelist()
|
||||
need(len(names) == len(set(names)) and set(names) == set(members), "exact archive members")
|
||||
for info in archive.infolist():
|
||||
need(safe(info.filename) and not info.is_dir() and not stat.S_ISLNK(info.external_attr >> 16), "unsafe archive member")
|
||||
need(info.file_size == members[info.filename]["bytes"], "archive member size")
|
||||
|
||||
|
||||
def verify_data(manifest, read):
|
||||
need(manifest["schema"] == 1 and manifest["kind"] == "allocation-multiselect-native", "manifest kind")
|
||||
members, runs = manifest["members"], manifest["runs"]
|
||||
need(bool(runs) and all(re.fullmatch(r"run-[0-9]+", run) for run in runs), "run names")
|
||||
need(all(safe(name) for name in members), "safe member names")
|
||||
for name, expected in members.items():
|
||||
need(pin(read(name)) == expected, "member pin: " + name)
|
||||
used, summaries = set(), {}
|
||||
def raw(name):
|
||||
need(name in members, "missing member: " + name)
|
||||
used.add(name)
|
||||
return read(name)
|
||||
def obj(name):
|
||||
return parsed(raw(name))
|
||||
def tree(prefix):
|
||||
return {name[len(prefix):]: raw(name) for name in members if name.startswith(prefix)}
|
||||
def pinned_tree(prefix, expected):
|
||||
actual = tree(prefix)
|
||||
need({name: pin(data) for name, data in actual.items()} == expected, "pinned tree: " + prefix)
|
||||
return actual
|
||||
for run, spec in runs.items():
|
||||
prefix = "runs/" + run + "/raw/"
|
||||
prepared, start, end, generation = [obj(prefix + n + ".json") for n in ("prepared", "started", "finished", "generation")]
|
||||
need(spec["reviewStatus"] in ("aborted", "unreviewed") and isinstance(spec["reviewNote"], str) and spec["reviewNote"], "explicit non-acceptance status")
|
||||
if run in ("run-1", "run-2"):
|
||||
need(spec["reviewStatus"] == "aborted", "early aborted run classification")
|
||||
origin = norm(prepared["cwd"]) + "/.local/allocation-multiselect-native/" + run
|
||||
need(spec["originalRun"] == origin and prepared["run"] == run and start["cwd"] == prepared["cwd"], "original run mapping")
|
||||
need(start["prepared"] == pin(raw(prefix + "prepared.json")), "launch preparation pin")
|
||||
need(ordered(generation["start"], generation["finish"], prepared["prepared"], start["start"], end["finish"]), "native lifetime")
|
||||
need(type(start["pid"]) is int and start["pid"] > 0 and type(end["exitCode"]) is int, "closed native process")
|
||||
for key in ("guiUnchanged", "checkerUnchanged", "fixtureUnchanged"):
|
||||
need(type(end[key]) is bool, "closure flag")
|
||||
need(start["gui"] == prepared["gui"] and start["checker"] == prepared["checker"] == generation["checker"], "binary identity")
|
||||
need(start["command"] == [prepared["gui"]["binary"]], "GUI command")
|
||||
need(generation["exitCode"] == 0 and generation["command"][:2] == [prepared["checker"]["binary"], "generate"] and len(generation["command"]) == 3 and norm(generation["command"][2]) == origin + "/fixture", "generation command")
|
||||
for stream in ("stdout", "stderr"):
|
||||
need(end[stream] == pin(raw(prefix + "app." + stream + ".log")), "GUI log pin")
|
||||
need(generation[stream] == pin(raw(prefix + "generation." + stream + ".log")), "generation log pin")
|
||||
need(set(spec["helpers"]) == set(prepared["helpers"]), "exact prepared helper mapping")
|
||||
for name, expected in prepared["helpers"].items():
|
||||
need(safe(name) and "/" not in name and pin(raw(spec["helpers"][name])) == expected, "historical helper: " + name)
|
||||
need(start["helper"] == prepared["helpers"]["native.py"], "launch helper")
|
||||
if spec["executedCapture"] is not None:
|
||||
need(pin(raw(prefix + "capture-executed.js")) == spec["executedCapture"], "actual capture function pin")
|
||||
original_helpers = tree(prefix + "helpers-original/")
|
||||
if original_helpers:
|
||||
need({n: pin(d) for n, d in original_helpers.items()} == prepared["helpers"], "original helper tree")
|
||||
fixture = pinned_tree(prefix + "fixture/", prepared["fixture"])
|
||||
fm = parsed(fixture["manifest.json"])
|
||||
need(fm["fixture"] == "allocation-multiselect-native-20260923" and fm["source_entry"] == prepared["entry"] and safe(prepared["entry"]), "fixture identity/entry")
|
||||
need(parsed(fixture["baseline.json"])["id"] == fm["ids"]["allocation_multiselect_native"], "complete Project baseline identity")
|
||||
fixture_source = {name[7:]: data for name, data in fixture.items() if name.startswith("source/")}
|
||||
need(prepared["entry"] in fixture_source and norm(prepared["workingEntry"]) == origin + "/working/" + prepared["entry"], "working entry mapping")
|
||||
working = tree(prefix + "working/")
|
||||
need(bool(working) and all(name.endswith(".arcadia") for name in working), "saved working tree")
|
||||
build_maps = {}
|
||||
for kind, target in (("gui", "arcadia-studio"), ("checker", "allocation_multiselect_native")):
|
||||
recorded, b = prepared[kind], spec["builds"][kind]
|
||||
result, began, process = [obj(b[n]) for n in ("result.json", "started.json", "process.json")]
|
||||
need(pin(raw(b["result.json"])) == recorded["receiptPin"] and relative(recorded["receipt"], prepared["cwd"]) == b["originalRelative"], "build receipt mapping")
|
||||
need(all(result.get(k) == v for k, v in began.items()) and process["command"] == result["command"] and type(process["pid"]) is int and process["pid"] > 0, "build start/result/process")
|
||||
need(result["exitCode"] == 0 and result["inputsUnchanged"] is True and ordered(result["start"], process["observed"], result["finish"], generation["start"]), "successful preceding build")
|
||||
for stream in ("stdout", "stderr"):
|
||||
need(pin(raw(b[stream + ".log"])) == result[stream], "build log pin")
|
||||
need(pin(raw(b["helper"])) == result["helper"], "build helper pin")
|
||||
need(pin(raw(b["inputs.json"])) == recorded["inputs"] == result["inputs"], "build source map pin")
|
||||
inputs = obj(b["inputs.json"])
|
||||
need(len(inputs) == result["inputCount"] and all(safe(name) for name in inputs), "source map count/paths")
|
||||
capsule = raw(b["inputs.zip"])
|
||||
need(pin(capsule) == result["archive"], "build source capsule pin")
|
||||
with zipfile.ZipFile(io.BytesIO(capsule)) as archive:
|
||||
zip_inventory(archive, inputs)
|
||||
need(all(pin(archive.read(name)) == expected for name, expected in inputs.items()), "all captured source bytes")
|
||||
if kind == "checker":
|
||||
path = "crates/oa-core/examples/allocation_multiselect_native.rs"
|
||||
need(raw(spec["checkerSource"]) == archive.read(path), "historical checker source")
|
||||
artifacts = [row for row in result["artifacts"] if row["cargo"]["target"]["name"] == target]
|
||||
need(len(artifacts) == 1 and artifacts[0]["readback"] == recorded["pin"] and artifacts[0]["cargo"]["executable"] == recorded["binary"] and artifacts[0]["cargo"]["profile"]["test"] is False, "native executable provenance")
|
||||
stdout_rows = [parsed(line) for line in raw(b["stdout.log"]).splitlines() if line.strip().startswith(b"{")]
|
||||
need(artifacts[0]["cargo"] in stdout_rows, "artifact appears in raw Cargo output")
|
||||
build_maps[kind] = inputs
|
||||
need(prepared["gui"]["inputs"] == prepared["checker"]["inputs"] and build_maps["gui"] == build_maps["checker"], "same GUI/checker source map")
|
||||
top = {name[len(prefix):] for name in members if name.startswith(prefix) and "/" not in name[len(prefix):]}
|
||||
base = BASE | (top & {"history-bytes.json", "observations.md", "capture-executed.js"})
|
||||
need(("capture-executed.js" in top) == (spec["executedCapture"] is not None), "explicit executed capture availability")
|
||||
if "observations.md" in top:
|
||||
raw(prefix + "observations.md")
|
||||
captures = sorted(name for name in top - base if name.endswith(".json"))
|
||||
need(captures, "native captures required")
|
||||
expected_top = base | set(captures)
|
||||
for name in captures:
|
||||
capture = obj(prefix + name)
|
||||
need(ordered(start["start"], capture["time"], end["finish"]), "capture lifetime")
|
||||
window = capture["window"]
|
||||
need(norm(window["app"]) == "process:" + norm(prepared["gui"]["binary"]) and type(window["id"]) is int and window["id"] > 0, "captured executable/window")
|
||||
need((capture.get("accessibility") is None or isinstance(capture["accessibility"], dict)) and isinstance(capture["images"], list), "accessibility/capture payload")
|
||||
for image in capture["images"]:
|
||||
filename = image["file"]
|
||||
need(safe(filename) and "/" not in filename and filename.startswith(name[:-5] + "-") and filename.endswith((".jpg", ".png")) and filename not in expected_top, "unique image mapping")
|
||||
need(all(type(image[k]) is int and image[k] > 0 for k in ("width", "height")), "image dimensions")
|
||||
expected_top.add(filename)
|
||||
data = raw(prefix + filename)
|
||||
need(data.startswith(b"\xff\xd8") if filename.endswith(".jpg") else data.startswith(b"\x89PNG\r\n\x1a\n"), "image signature")
|
||||
need(top == expected_top, "exact top-level run files")
|
||||
labels = {name[len(prefix + "snapshots/"):].split("/")[0] for name in members if name.startswith(prefix + "snapshots/")}
|
||||
states, results, positive = {}, {}, []
|
||||
for label in sorted(labels):
|
||||
need(re.fullmatch(r"[a-z0-9-]+", label), "snapshot label")
|
||||
folder = prefix + "snapshots/" + label + "/"
|
||||
began, result = obj(folder + "started.json"), obj(folder + "result.json")
|
||||
need(all(result.get(k) == v for k, v in began.items()), "snapshot start/result")
|
||||
need(result["prepared"] == start["prepared"] and result["checker"] == prepared["checker"] and result["helper"] == prepared["helpers"]["snapshot.py"], "snapshot provenance")
|
||||
need(ordered(start["start"], result["start"], result["finish"], end["finish"]), "snapshot lifetime")
|
||||
mode, command = result["mode"], result["command"]
|
||||
need(mode in ("unchanged", *EXPECTED_CHANGED) and len(command) == 5 and command[:3] == [prepared["checker"]["binary"], "check", mode] and norm(command[3]) == origin + "/fixture/baseline.json" and norm(command[4]) == origin + "/snapshots/" + label + "/source/" + prepared["entry"], "snapshot command")
|
||||
need(result["baseline"] == pin(fixture["baseline.json"]), "snapshot baseline")
|
||||
for stream in ("stdout", "stderr"):
|
||||
need(result[stream] == pin(raw(folder + stream + ".log")), "snapshot log pin")
|
||||
need(type(result["exitCode"]) is int and all(type(result[k]) is bool for k in ("binaryUnchanged", "workingSourceUnchanged", "snapshotUnchanged", "baselineUnchanged")), "snapshot closure")
|
||||
states[label] = pinned_tree(folder + "source/", result["source"])
|
||||
need({name for name in members if name.startswith(folder)} == {folder + n for n in ("started.json", "result.json", "stdout.log", "stderr.log")} | {folder + "source/" + n for n in states[label]}, "exact snapshot files")
|
||||
if result["exitCode"] == 0:
|
||||
report = obj(folder + "stdout.log")
|
||||
need(report["result"] == "PASS" and report["comparison"] == "complete Project equality; literal fixed record edits" and report["mode"] == mode and report["baseline"] == command[3] and report["saved"] == command[4], "positive complete Project report")
|
||||
positive.append(label)
|
||||
results[label] = result
|
||||
history_present = "history-bytes.json" in top
|
||||
if history_present:
|
||||
history = obj(prefix + "history-bytes.json")
|
||||
groups = history["equalByteGroups"]
|
||||
changed_mode = history["changedMode"]
|
||||
need(changed_mode in EXPECTED_CHANGED, "known Allocation history mode")
|
||||
need(len(groups) == 2 and groups[0][:2] == ["baseline", "undo"] and groups[1] in (["changed", "redo"], ["changed", "redo", "reopened"]), "literal Allocation history groups")
|
||||
history_labels = [label for group in groups for label in group]
|
||||
need(len(history_labels) == len(set(history_labels)) and set(history_labels) == set(history["snapshots"]) and set(history_labels) <= set(positive), "history labels/positive reports")
|
||||
need(history["status"] == "PASS" and history["prepared"] == start["prepared"] and history["helper"] == prepared["helpers"]["history-bytes.py"] and ordered(end["finish"], history["verified"]), "history provenance")
|
||||
need(end["exitCode"] == 0 and all(end[k] is True for k in ("guiUnchanged", "checkerUnchanged", "fixtureUnchanged")), "history clean closure")
|
||||
sentinels = {n: "// Native preservation sentinel: " + n + "\n" for n in fixture_source if n.endswith(".arcadia")}
|
||||
need(sentinels and history["fixtureCommentPrefixes"] == sentinels, "exact fixture sentinel inventory")
|
||||
need(all(fixture_source[n].startswith(s.encode()) for n, s in sentinels.items()), "immutable fixture sentinels")
|
||||
need(set(history["sourceFilesPerSnapshot"]) == set(history_labels), "source count labels")
|
||||
for group, mode in zip(groups, ("unchanged", changed_mode)):
|
||||
for label in group:
|
||||
need(states[label] == states[group[0]] and results[label]["mode"] == mode and history["snapshots"][label] == pin(raw(prefix + "snapshots/" + label + "/result.json")), "literal history bytes/receipt")
|
||||
need(all(results[label][k] is True for k in ("binaryUnchanged", "workingSourceUnchanged", "snapshotUnchanged", "baselineUnchanged")), "stable history snapshot")
|
||||
need(len(states[label]) == history["sourceFilesPerSnapshot"][label], "history source count")
|
||||
need(all(n in states[label] and states[label][n].startswith(s.encode()) for n, s in sentinels.items()), "snapshot prefix sentinels")
|
||||
need(states["baseline"] == fixture_source and states["baseline"] != states["changed"], "baseline exact and edit changed")
|
||||
changed = sorted(name for name in set(states["baseline"]) | set(states["changed"]) if states["baseline"].get(name) != states["changed"].get(name))
|
||||
need(changed == history["changedFilesFromBaseline"] == history["expectedChangedFiles"] == EXPECTED_CHANGED[changed_mode], "exact intended Allocation source paths")
|
||||
order = ["baseline", "changed", "undo", "redo"] + (["reopened"] if "reopened" in groups[1] else [])
|
||||
need(history["historyOrder"] == order, "history order labels")
|
||||
sequence = [results[label] for label in order]
|
||||
need(all(ordered(a["finish"], b["start"]) for a, b in zip(sequence, sequence[1:])), "history chronology")
|
||||
need(working == states[order[-1]], "final saved working bytes")
|
||||
if run == "run-1":
|
||||
need(not labels and not history_present and working == fixture_source, "aborted preflight source remains original")
|
||||
if run == "run-2":
|
||||
need(labels == {"baseline"} and not history_present and results["baseline"]["exitCode"] == 3221225781 and states["baseline"] == working == fixture_source, "aborted failed baseline preserved")
|
||||
summaries[run] = {"reviewStatus": spec["reviewStatus"], "exitCode": end["exitCode"], "captures": len(captures), "snapshots": len(labels), "positiveReports": len(positive), "historyBytes": history_present}
|
||||
need(used == set(members), "unreferenced package members: " + repr(set(members) - used))
|
||||
return summaries
|
||||
|
||||
|
||||
def verify_package(root):
|
||||
files = parsed((root / "files.json").read_bytes())
|
||||
need(set(files) == OUTER, "outer files")
|
||||
for name, expected in files.items():
|
||||
need(pin((root / name).read_bytes()) == expected, "outer pin: " + name)
|
||||
manifest = parsed((root / "manifest.json").read_bytes())
|
||||
with zipfile.ZipFile(root / "evidence.zip") as archive:
|
||||
zip_inventory(archive, manifest["members"])
|
||||
return verify_data(manifest, archive.read)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("package", nargs="?", type=Path, default=Path(__file__).resolve().parent)
|
||||
args = parser.parse_args()
|
||||
print("PASS byte/receipt consistency only:", verify_package(args.package))
|
||||
print("No GUI/checker execution or native acceptance claim; see independent parent review.")
|
||||
@@ -0,0 +1,7 @@
|
||||
# Review record scope
|
||||
|
||||
These are contemporaneous independent review notes, preserved with their earlier findings and follow-ups. Their historical pending-build wording is not the final acceptance status; the [parent checkpoint](../../allocation-group-app-20260924.md) resolves that status against final source `495464e84b5bae8ddad93e9fc69ef353e7f1c884` and the packaged receipts.
|
||||
|
||||
Source references describe that checkout. Original `.local` paths name the preparation environment, not a dependency on the reader's machine. `native-acceptance-plan.md` is the earlier proposal: its B coordinates were corrected as documented by `native-geometry-diagnosis.md`; use the committed Rust example and captured fixture for exact final values. `native-replay.md` records helper usage and observation boundaries; portable archive verification commands are in the parent checkpoint. The archive retains the actual helper bytes by hash, never silently substituting current local helpers.
|
||||
|
||||
No review note grants integration or release acceptance. The native verifier intentionally leaves successful workflows as `unreviewed`: it checks receipt/byte consistency; owner observations and independent review supply the bounded acceptance described in the checkpoint.
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
# Allocation adapter implementation review
|
||||
|
||||
2026-09-23. Independent read-only inspection while App/canvas owners were still completing their work. Reviewed `app/allocation_drag.rs`, early `app.rs` guards, selection helpers, `diagram/{work_drag,multiselect}.rs`, relevant `diagram.rs` hit/dispatch/drawing, and mounted root `allocation_input.rs`. No builds or source edits. Findings were sent promptly to root; line positions can shift during owner edits.
|
||||
|
||||
## P1 — dirty root shielding omits part of actual route-control hit area
|
||||
|
||||
`diagram/work_drag.rs::work_dirty_press` recognizes nodes, ports, edges and labels, but not selected-route attachment/pin controls. `diagram.rs::hit_edge` accepts distance≤7/zoom; actual bend and attachment control hit testing accepts≤8/zoom. Therefore a pointer 7.5/zoom perpendicular to an exposed straight-run selected pin is a clean control hit but not a dirty object hit (choose a route segment away from cards/ports/labels). For an endpoint, the control's outward diagonal hit area likewise extends beyond the endpoint segment-distance hit domain.
|
||||
|
||||
`allocation_input::Root::dirty_object` uses this predicate before dispatch to siblings specifically because capture inside Canvas cannot prevent inspector blur. The missing ring reaches ordinary children; the dirty canvas can interpret it as blank-space pan because `editable=false` disables control handling. This is a concrete focus/camera boundary gap, not merely an unsupported drag. It predates some new code but is directly relevant to the promised control-hit parity and expanded Allocation ownership.
|
||||
|
||||
Minimal fix: include selected, nonsuspended route pin and valid attachment-owner control geometry in the dirty predicate, with the same8/zoom domain, independent of editable status. Do not simply call `allocation_press_target` unchanged: its control branch is gated by editable, and the root deliberately constructs a noneditable canvas. Prefer one narrow geometric handle predicate shared by clean/dirty paths if practical. Validate a mounted focused dirty property/source field, press ring and move/release: focus/caret, drafts/source, selection and camera remain exact, with retryable refusal. Include the clean control hit as a positive discriminator and assert that ordinary edge/card/label hits are false at the chosen point.
|
||||
|
||||
## P2 — ordinary performer out-and-back snaps an off-grid original
|
||||
|
||||
At inspection, `PerformerPlan::place` unconditionally computed round(requested/10)*10−anchor. This contradicts the settled addendum's explicit requested==original zero rule for preserving ordinary performer policy. A performer authored at(103,107), dragged beyond3px and returned exactly to press, obtains delta(−3,+3), moving its visual descendants/routes and adding history despite zero final displacement.
|
||||
|
||||
Minimal fix: requested==anchor returns delta(0,0); otherwise preserve the existing snap calculation. Test both singleton and group ordinary performer movement with an off-grid anchor, irreversible crossing then exact return, and an existing redo branch. Assert complete Project/source/revision/redo unchanged and selection not collapsed. This rule is specific to preserving the chosen ordinary performer policy; do not silently change frozen pure assignment/Arrange policies in this repair.
|
||||
|
||||
## Reviewed boundaries with no further concrete defect found
|
||||
|
||||
- Bare Allocation MoveNode and saved Allocation group Move now reject before `allocation_before`, regardless of armed/active state. New ordinary performer movement uses the same Press/Active capability and core translator, closing both earlier fallback findings.
|
||||
- Press verifies clean current stamp, exact selection snapshot, visible actual target and bounds; every original selected member is validated before placement conversion. Selected-member press retains primary/draft; unselected press uses one direct replacement and captures its post-replacement snapshot. Unsupported typed sets retain a pending refused lifecycle.
|
||||
- Canvas skips generic group handling while state.work owns input. Clean attachment/pin hits now precede underlying cards and produce the exact edge for owned-group/armed admission. Ordinary unarmed edge controls remain on their existing path. This clean hit fix does not resolve the dirty-domain issue above.
|
||||
- Old token envelopes are checked before shared boundaries. Active packets also verify stamp and selection, crossed is accumulated with strict>3, outside release cancels, and stationary release collapses only before crossing. Current tests' generation-isolation and singleton bare-move additions address the previously noted discriminators.
|
||||
- Release recomputes the candidate/final command; failed Document execution takes the error path without sync or inspector refresh. Successful execution preserves legacy primary, syncs, then reconciles exact membership. No anchor promotion or filtered-subset fallback found.
|
||||
- Performer candidate uses `translate_placements` and exact final Project comparison rather than importing explicit Arrange collision/header gates. Its off-grid zero defect is the concrete exception noted above.
|
||||
|
||||
This review is not a passing test/native claim. The two findings should be repaired and exercised through their discriminators before the corresponding no-op/focus guarantees are accepted.
|
||||
|
||||
## Follow-up — fixes inspected, execution still pending
|
||||
|
||||
Read back both fixes plus `app/allocation_drag_tests/control_focus.rs`, the ordinary off-grid test in `group.rs`, and the diagram-level outer-ring test. Root reports `app-1` stopped on unrelated test compilation errors (Candidate/Arc pointer comparison and private test access); none of these new regressions is claimed passing here.
|
||||
|
||||
**P2 off-grid defect is corrected in code.** `PerformerPlan::place` now returns delta(0,0) when requested==captured anchor and otherwise uses the existing snap formula. Exact pointer return computes zero screen displacement, so adding it to the captured original reaches this branch without an epsilon interpretation. The new test uses authored(103,107), both singleton and performer-group selection, a genuinely crossed20px movement with preview, then zero-delta preview on exact return. It checks the same accepted Project Arc, source bytes, revision, retained selection and existing Undo/Redo, and executes Redo to compare its exact saved future. This is discriminating: the previous formula produces(−3,+3). No new policy regression found in the fix.
|
||||
|
||||
**P1 dirty hit-domain defect is corrected in code.** `allocation_route_control(view,routed,world)` resolves only the selected nonsuspended edge, its pins, and endpoints with valid attachment owners, all at the existing8/zoom radius. It is deliberately independent of editability. The clean hit chain retains its editable guard, while dirty shielding invokes the geometric predicate directly; therefore Root's noneditable canvas no longer disables the control-ring shield. This preserves control ownership and avoids expanding the shield to all unselected routes' hypothetical handles.
|
||||
|
||||
The mounted Name regression has a valid isolated geometry setup: real OA Interaction, explicit endpoint attachments and pin(450,547), all actual route points asserted y547, label offset away, card endpoints separated. Probe(450,554.5) is7.5px from the straight line and within8px of the pin, outside both cards. It creates an actual dirty Name field and caret state, demands Refused without ordinary selection/route/pan messages, and checks exact source, Project Arc, selection and pan. The next typed Y must yield `YXFlow`, so focus/caret evidence comes from actual subsequent input, not only a focus flag. The separate diagram test strengthens hit-domain isolation across zooms by checking that all old ordinary hit methods miss, the new dirty shield hits, and subsequent movement/release cannot become pan or selection.
|
||||
|
||||
**No remaining blocker found for these two fixes by inspection.** Keep evidence scope precise: the mounted regression covers a property Name field and click at the pin ring. It does not separately execute dirty source-editor focus, an attachment-ring mounted case or a held move/release pan attempt. The shared independent-of-editability geometric predicate supports those paths, but do not describe them as executed regressions until tested. The mounted root already consumes the refused press/release sequence, making this click test meaningful for the reported blur failure.
|
||||
@@ -0,0 +1,37 @@
|
||||
# App group test review
|
||||
|
||||
2026-09-23. Read-only review of the new, unexecuted `app/allocation_drag_tests/group.rs` against current fixture/projection helpers and the settled adapter-plan addendum. Production files were being edited by their owners. No build or source edit; compilation is not claimed.
|
||||
|
||||
## Findings sent to root
|
||||
|
||||
**P2 — ABA test does not isolate selection-snapshot admission.** `group_selection_aba_and_old_press_cannot_retire_a_new_token` sends the old Press only while a fresh Active already exists. The ordinary `active.is_none()` guard rejects it even if selection-generation validation is absent. Furthermore both routed Inspect messages retire the Allocation epoch, furnishing a second independent rejection. Keep this useful old-owner test, but do not cite it alone as generation/primary-ABA evidence. Add an idle admission discriminator with current stamp/epoch but the captured pre-ABA selection snapshot; e.g. construct the deliberately stale packet by retaining its old selection while replacing only its stamp with a newly captured current stamp. Assert no Active, no selection/draft/source change. A corresponding current-snapshot control must admit. This isolates the precise guard rather than disabling normal boundary behavior.
|
||||
|
||||
**P2 coverage — include singleton bare performer MoveNode after retirement.** Existing guard test always selects two performers, so the generic `MoveNode` multi-selection veto independently protects the model. Its epoch/arm/token assertions *do* distinguish the old post-boundary veto for armed/active cases, so the test is meaningful and should remain. A singleton performer after Cancel closes the remaining mutation discriminator; pair with a fresh authorized singleton performer movement. The present positive ordinary control is a group only.
|
||||
|
||||
**P3 — assert lifecycle completion/refusal explicitly in mixed test.** The exact3/3.25 out-and-back distinction is sound, but the crossed case only asserts retained selection and unchanged Document Arc. Also assert Active and preview retired after Release and a whole-set refusal status/feedback at crossing. Otherwise a leaked unsupported token or swallowed release can satisfy its current postconditions. Inspecting `Prepared::Refused` after admitted press (or a supported-control contrast) would make branch intent explicit without asserting internal command construction.
|
||||
|
||||
## Fixture and oracle assessment
|
||||
|
||||
- Assignment coordinates are reachable: both anchor release points land inside C's accepted authored600×900 enclosure. X/Y remain vertically separated; their new positions are literal(1540,200)/(1540,600). OA/SA have no FunctionalExchange here, so default94-height predictions are appropriate. Relation identity/header preservation is covered by cloning accepted Project and editing only Allocation bodies.
|
||||
- Explicit Arrange selects A plus Y, giving union A/X/Y. +400x preserves A/X enclosure size, while stationary B's derived enclosure grows to contain Y. A remains above B with ample separation; C at1500 stays clear. Both pressed headers are visible. Expected records correctly leave B authored geometry and every relation unchanged.
|
||||
- Unsupported A+X is an ordinary mixed work/performer set even though X is A's visual child. Both original targets are visible. Exact3 and3.25 screen-unit probes are representable and use ordered CursorMoved events before returning; these meaningfully distinguish strict threshold and irreversible crossing.
|
||||
- Ordinary performer A+C +300x legitimately moves A's visual child X once and leaves B/Y alone. C's resulting right edge2400 exceeds world viewport2200 while both pointer endpoints remain in bounds. This is a valid discriminator against accidentally adopting explicit Arrange's required-header containment policy.
|
||||
- `exact_history` compares a separately constructed complete Project, reopened source model, and exact source bytes through one Undo/Redo. It is stronger than using preview output as the expected model. Selection is checked before history navigation, appropriately avoiding a claim that Undo/Redo must restore UI membership.
|
||||
|
||||
## Integration prerequisites, not new defects
|
||||
|
||||
At read time the parent canvas fixture still had `selection: None` and the new group module had not yet been registered in its parent module. Both are explicitly assigned migration work; these tests require `Some(real selection)` and registration before they can provide evidence. `Press.target`, `Mode`, `Candidate::{Assignment,Arrange,Performers}` match settled/current adapter types; no additional definite Rust API/type error found by inspection.
|
||||
|
||||
The five families do not cover the previously identified attachment/bend-over-card hit precedence. That evidence belongs in the separately assigned canvas tests; confirm those tests exist before declaring that earlier finding resolved. No claim here that absence from this one file is an App defect.
|
||||
|
||||
## Follow-up — zero-delta fixture ordering repair after app-2
|
||||
|
||||
Read the closed app-2 log:130 passed,1 failed, with the sole failure at the zero-delta test's expected Allocation count2 versus actual0. Read back the repaired `unassign(...,inside_c=true)` fixture and the added Press/preview assertions. No rerun here; the changed fixture has not inherited the earlier gate's passing status.
|
||||
|
||||
**Diagnosis is consistent with the actual hit algorithm.** Before assignment, Work/Y and empty C have no visual parent relationship, so all have equal projected depth despite geometric containment. `projection.rs` sorts nodes by(depth,id), and `DiagramCanvas::hit` searches that order in reverse. Empty C is body-hittable because it has no visual children. Random UUID ordering can therefore make C cover the intended Work press. That selects an ordinary performer candidate, for which zero allocations is correct. The old failure log lacks the actual Press target, so this explanation is code-supported rather than a recorded hit trace.
|
||||
|
||||
**Repair is sound and scoped to the intended fixture.** Replacing C's placement ID with `Id::from_u128(1)` updates both map key and `NodePlacement.id`, and updates `g.cn`; the fixture has no saved route/selection/other record pointing to the old placement ID. Version4 UUID placement IDs are necessarily greater than1, independent of their random bits. C is therefore painted/hit below both work cards while retaining the valid geometric overlap and absence of semantic allocation. Other performers remain spatially separate, and Work/Y do not overlap one another. After assignment, work depth is greater than C's, so the later already-assigned no-op no longer relies on equal-depth tie order.
|
||||
|
||||
New assertions require Press.target==exact Work, retained group selection, Candidate::Assignment, destination C and changed_count2 before extracting generated IDs. Thus an accidental performer press now fails at the actual cause rather than being misreported as missing semantic output. Generated allocation IDs remain taken from the preview solely as nondeterministic gesture identities; expected relation bodies/headers/aliases and complete Project diff remain independently constructed.
|
||||
|
||||
No remaining UUID-order nondeterminism found for this fixture's two anchor/target geometries. The deterministic layer order is appropriate for a test that needs an exposed work press; it does not establish universal selectability of arbitrary overlapping same-depth cards. No production hit-order change is warranted by this fixture repair. The cfg(test) index getter narrowing is consistent with these tests reading cached semantic indexes while production rendering consumes the already projected candidate view; no contrary production use was required by this repair.
|
||||
@@ -0,0 +1,69 @@
|
||||
# Bounded Allocation native acceptance proposal
|
||||
|
||||
Prepared 2026-09-23 while the parent's app-2 gate runs. This is a proposed evaluator and observation plan, not a generated fixture or passing native result. No tracked source, build, or publication changes were made.
|
||||
|
||||
## Reuse the established evaluator boundary
|
||||
|
||||
Use `crates/oa-core/examples/process_multiselect_native.rs` as the small producer/checker pattern: deterministic UUIDs and aliases; create-only generation of baseline JSON, source, and manifest; checker verifies the baseline equals its own original fixture; literal intended record edits followed by complete Project equality. Its `load`, `equal`, `roundtrip`, and `write_new` functions are reusable patterns. Permit model-completeness warnings; reject source errors. Do not import App, an Allocation planner, or the production translator to calculate expected output.
|
||||
|
||||
Reuse the existing `.local/process-multiselect-native` launch/snapshot protocol with a bounded list of modes. Pin actual GUI/checker build receipts and fixture once, launch an isolated working copy, save before stable source snapshots, retain checker stdout/stderr/exit, and close normally. Extend/adapt the current helpers; no new evidence framework is needed. Native source/history checks follow its README and `snapshot.py`; complete Project checks alone do not establish exact source bytes, selection, gesture delivery, or one-step history.
|
||||
|
||||
The source authorities are `harness/multiselect-contract-20260923.md`, `harness/allocation-arrange-amendment-20260923.md`, and existing Allocation v2. Current App `allocation_drag_tests/group.rs` already exercises literal assignment, mixed Arrange, zero-delta semantic changes, stale lifetime, source failure, and ordinary off-grid no-op. Native work should establish those paths are reachable and understandable through actual controls, not reproduce every synthetic stale packet.
|
||||
|
||||
## One compact project, four independent Allocation diagrams
|
||||
|
||||
Generate layer-specific engineering records for **01 OA assign**, **02 SA assign**, **03 OA arrange**, **04 SA arrange**. Do not reuse engineering records between these diagrams: a semantic change in one must not legitimately alter another's projection. Add one small saved Architecture sentinel showing the assign X/Y at different positions; it needs no native operation. Deterministic IDs cover all elements, placements, relations, ports, and diagrams. Give allocations distinctive names/descriptions, preserved aliases, and source comments. Emit exact ID-to-label and source-path mappings in the manifest.
|
||||
|
||||
The following are *authored* positions in world units, not promised screen coordinates. Default work size224×94. OA uses Activity/Entity/Actor and Interaction; SA uses Function/System/SystemActor and FunctionalExchange. Create exchanges through real layer-compatible constructors, then deterministically reidentify generated records, including SA ports. SA exchanged work projects132 high, so never assert OA geometry for SA. No Role→Function fixture is valid.
|
||||
|
||||
### Assign diagram, repeated in OA and SA
|
||||
|
||||
| Placement | Baseline position | Other authored facts |
|
||||
| --- | --- | --- |
|
||||
| A | (43,47) | Expanded; default size |
|
||||
| X | (80,140) | Direct ax: A→X |
|
||||
| B | (43,347) | Expanded; default size |
|
||||
| Y | (80,440) | Direct by: B→Y |
|
||||
| C | (500,40) | Expanded; authored size360×600 |
|
||||
|
||||
X/Y are independent semantic roots. One X→Y exchange has an explicit nonsuspended custom route with pins[(340,260),(340,400)], plus a nondefault relative label. Use supported attachment defaults/constructors. Keep the complete route record exact except the stated pins. No allocation relation needs an explicit route. There is intentionally no OA role-only third root in this minimum native fixture: the focused tests cover its provenance, and a third root makes a compact destination/viewport trial substantially less clear.
|
||||
|
||||
Expected `oa-assigned`/`sa-assigned`: change only ax.body to Allocation{source:C,target:X} and by.body to Allocation{source:C,target:Y}; preserve relation IDs and all header metadata. Positions become X=(560,140), Y=(560,440), delta(480,0). Internal pins become[(820,260),(820,400)]. A/B/C authored records remain exact, as do every other record and diagram. No generated-ID substitution is necessary: both direct relations already exist. In projection A/B shrink and C encloses X/Y; this visual consequence is not an authored geometry edit.
|
||||
|
||||
### Arrange diagram, repeated in OA and SA
|
||||
|
||||
Use independent records P=(40,40), U=(80,140), V=(440,140), direct pu:P→U, V unassigned. P is expanded. An internal U→V exchange has pins[(360,180),(360,230)]. This diagram has no other performer that would grow into the proposed destination.
|
||||
|
||||
Expected `oa-arranged`/`sa-arranged`: select exact {P,V}; affected set is {P,U,V}. Positions become P=(40,280), U=(80,380), V=(440,380), delta(0,240); pins become[(360,420),(360,470)]. All engineering, fold, size, port-anchor, route-label/attachment, and other-diagram records stay exact. A performer-only bug omits V; a selected-only bug omits U; the full literal oracle rejects either.
|
||||
|
||||
### Ordinary performer mode, reusing a fresh assign baseline
|
||||
|
||||
Select {A,B} in **01 OA assign**, anchor A. Expected `oa-performers`: anchor becomes(120,90), so common delta is(77,43), **not**(80,40), because A starts off-grid. B=(120,390), X=(157,183), Y=(157,483); route pins[(417,303),(417,443)]. C and every allocation/engineering record remain exact. This simultaneously distinguishes ordinary performer closure movement and one complete route map. Ordinary mode must not show assignment-destination or Arrange refusal semantics.
|
||||
|
||||
Before native execution, producer tests must validate all baselines and literal expected Projects, JSON/source roundtrips, visible selected identities and usable projected spacing. Use projection only to verify fixture usability and display dimensions, not to derive expected record mutations. Inspect the complete rendered scenes once to ensure the custom route is active and the target C can actually be hit. Coordinates above are unexecuted design predictions; revise and document any correction before freezing fixture/checker, never from an observed saved result.
|
||||
|
||||
## Minimum native workflow set
|
||||
|
||||
Five successful gestures cover both layers for assignment and explicit mixed Arrange, plus ordinary performers. Start each workflow from a fresh working copy of the immutable baseline (or separately captured baseline reopen); the checker modes each mean one operation from that original baseline, never an undocumented cumulative state. Use one staged application and ordinary Open between copies if convenient.
|
||||
|
||||
1. **OA assignment and selection.** Click Y; focus a clean inspector text field, hold Shift before returning to canvas, Shift-click X. Observe exact two-card membership, primary X, readable count, and no inspector edit. Toggle X off/on once; optionally use the existing context Inspect action to make X primary without changing membership. A stationary click on selected Y must collapse to Y; reconstruct {Y,X}, primary X. Drag selected Y by(480,0) into C while X remains primary. Observe stable C outline, truthful two-work assignment feedback, both-card preview, stable camera, and unchanged primary through selected-member press/preview/release. Save/check `oa-assigned`.
|
||||
2. **SA assignment.** Select X/Y through real Shift, press the nonprimary member, and repeat the literal +480 move to C. Inspect actual port-bearing Function cards and route during preview, not only the final positions. Save/check `sa-assigned`. This is an actual SA assignment, not an SA preservation sentinel.
|
||||
3. **OA mixed selection.** Select {P,V}, primary V. An ordinary crossed drag from P must refuse the entire set with useful Arrange guidance; save/check `unchanged`. Arm Arrange next drag, press P, preview and release +240 vertically. Observe P and unselected contained U moving together with V, allocations unchanged, combined count, retained primary V, and one-shot arm consumed. Save/check `oa-arranged`.
|
||||
4. **SA mixed selection.** Repeat explicit Arrange on {P,V}, but press work V with performer P primary. This exercises both anchor kinds across real layers without another fixture. Save/check `sa-arranged`; confirm taller Function/port content remains legible and contained.
|
||||
5. **Ordinary performers and no-op history.** From fresh OA assign baseline, select A/B with B primary, drag A to authored snapped(120,90); save/check `oa-performers`. Undo exactly once, save/check `unchanged`. Without changing model or invoking another edit, press A, move clearly more than3 screen pixels, return exactly to the original pointer position, release. Both off-grid performers and their children must remain exact, selection must remain a group, and Redo must still restore `oa-performers`. Save/check unchanged after out-and-back, then Redo once/save/check the moved result. This is the off-grid regression discriminator.
|
||||
|
||||
For each successful workflow, one Undo must restore baseline and one Redo must restore the accepted result; actual physical actions are observed, not inferred from receipts. The ordinary performer workflow embeds its no-op between that Undo and Redo. A save does not count as another model edit. Reopen one accepted assignment and one accepted Arrange bundle and check the corresponding complete expected mode again. Screenshots need only capture meaningful selection/preview/refusal/result states, not every mouse packet.
|
||||
|
||||
On OA Arrange before its successful move, also perform a crossed out-and-back with work V as the on-grid anchor. Expect unchanged source/model, retained group, consumed arm, no history entry. Test Escape once during a visible preview. **Do not label an ordinary work-group out-and-back as necessarily a no-op:** its frozen performer destination can change another root's assignment even at zero delta. The App tests already establish that semantic-positive case; the native no-op discriminators use explicit Arrange and ordinary performers deliberately.
|
||||
|
||||
One additional short native refusal is worthwhile in SA assign before mutation: select X plus Y's exact visible port (Shift), cross the threshold from X, and observe whole-set refusal with no owner promotion; save/check unchanged. This verifies rendered hit identity and feedback unavailable from a pure model oracle. If native hit inspection cannot establish a port rather than its card, report that trial unproven rather than accepting unchanged bytes as proof. Remaining dirty focus, stale/ABA envelopes, source-budget failure, hidden diagnostics, and all-header viewport cases stay with the focused/full-tree suites unless native observation exposes a concrete issue.
|
||||
|
||||
## Literal oracle and small falsifier set
|
||||
|
||||
Modes: `unchanged`, `oa-assigned`, `sa-assigned`, `oa-arranged`, `sa-arranged`, `oa-performers`. Each expected Project starts from deterministic baseline and applies exactly the table edits above. Do not infer delta, target, relation IDs, scope, or source order from actual output. During GUI operation derive screen destinations from observed canvas transform and the predeclared world target; choose a point comfortably inside the intended snap cell. If the wrong snap cell is reached, retain the failed attempt and retry a fresh trial, never loosen the oracle to whichever delta happened.
|
||||
|
||||
Bounded checker tests should accept literal valid expected models/source and reject core-valid counterexamples: only primary moves; only one allocation retargets; allocations recreated with different IDs/header metadata; union-internal pins remain stationary or translate twice; P/V move but U does not; ordinary movement changes allocations; another diagram or SA/OA sentinel changes; baseline is replaced by a result. Also reject source comment drift in the independent byte-preservation check, since complete Project equality cannot detect it.
|
||||
|
||||
Source history requires full inventory and exact bytes: baseline==undo==no-op/refusal snapshots for a trial; accepted==redo==reopened where captured. For assignment, only the known relation-containing source files and active diagram file may differ between groups; for Arrange/ordinary performer movement only the active diagram file may differ. Determine those explicit paths from the generated source manifest, not a guessed source layout. Check unchanged text/comments in edited files through before/after identity-aware spans or a small literal source expectation if the writer's format makes that straightforward; allowed-file equality alone is insufficient to claim comment preservation. Do not generate the expected source by running the desktop planner being evaluated.
|
||||
|
||||
At normal1440×900 first, ensure all selected headers and destination are readable in the actual canvas, including the inspector. A compact-window check can reuse one mixed Arrange selection at1100×700: inspect count, guidance and arm control without demanding the same unscaled viewport. Do not reduce zoom beneath the ordinary work/Arrange readability limit to force the fixture to fit. Native acceptance records actual build/source identity, observed focus/selection/feedback and saved oracle results separately; no native execution or fixture validation is claimed by this plan.
|
||||
@@ -0,0 +1,34 @@
|
||||
# Allocation native producer review
|
||||
|
||||
2026-09-23. Read-only review of `.local/allocation-native-producer.rs`, [native acceptance plan](native-acceptance-plan.md), and actual core constructors/identity/route APIs. Producer is uncompiled and no fixture/preflight/test execution is claimed. No blocking concrete defect found by inspection; adopt only subject to compilation and generation/preflight.
|
||||
|
||||
## Oracle and identity assessment
|
||||
|
||||
- `expected` edits literal relation bodies, node positions and route pins from the supplied baseline; it does not call a desktop planner, translator, projector or infer actual-output deltas. `check` independently requires baseline equality to the deterministic original before complete actual Project equality. No generated-ID substitution or filtered-map comparison weakens the oracle.
|
||||
- Project::empty contributes only its random project ID, overwritten immediately. Element/diagram/placement IDs are deterministic. Actual FunctionalExchange constructor creates the exchange plus two ports; relation and both generated port IDs/aliases are reidentified, with source/target references updated by core reidentify. There are no generated items in this fixture. The fixture-equality test is an appropriate extra randomness detector.
|
||||
- SA uses only one System and other performers are SystemActor; work is Function with real typed exchange ports. OA uses supported Activity/Entity relationships. The separate baseline/source roundtrip and core validation gates should reject any mistaken constructor assumption before publication.
|
||||
- Ordinary performer literals correctly use delta(77,43) from authored A(43,47) to snapped(120,90); child and internal pin deltas match. Assignment literals use+480x; Arrange literals use+240y. Existing relation headers, ports, folds, inactive diagram and opposite-layer records are included in complete equality.
|
||||
|
||||
## Geometry and native reachability
|
||||
|
||||
The hand-calculated spacing is consistent: baseline A/B enclosures remain left of C; independent X/Y are vertically separated including SA132 height; assigned X/Y right edge784 and route pins820 remain inside C's authored right860; Arrange P/U move rigidly with V and have no second performer whose growth creates a hidden obstacle. Ordinary performer movement leaves its expanded A/B right bounds below Cleft500. No equal-depth unrelated card overlap like the repaired zero-delta App fixture is apparent here.
|
||||
|
||||
`preflight` appropriately uses production projection/routing only for usability checks, not expected mutations. It checks all placements visible, literal work extents, bounded complete scenes, unrelated overlaps and active exact pins/no routing warnings for every mode before writing bytes. This is stricter than core validity and may expose a real route/fixture problem at generation; do not weaken it merely to publish a fixture. Actual native target hits and readable viewport remain separate observations. The900×750 world envelope is not a promise that the default1440×900 window's center canvas fits at zoom1.
|
||||
|
||||
## Falsifier quality
|
||||
|
||||
`through_json` demands the corrupted model remain core-valid and `mismatch` demands the complete-Project mismatch reason, excluding vacuous parser/validation refusal. Partial geometry, one unchanged responsibility, recreated identity, metadata drift, stationary/double-translated pins, omitted closure members, changed ordinary assignment and opposite-layer/other-view sentinels are genuine independent failures. Literal expected success is supplemented by a real core Batch/Session Undo/Redo for OA/SA assignment. That Session test is explicitly core history, not Document source-history/native evidence. Arrange/ordinary positive tests are literal oracle/source roundtrip tests, not demonstrations that their App gestures execute.
|
||||
|
||||
## Qualifications for adoption and evidence wording
|
||||
|
||||
1. **Text occurrence paths are not ownership paths.** `id_text_occurrence_files` reports every source file mentioning an ID, potentially including references in otherwise unrelated diagrams. Byte-preservation tooling must identify the owning ax/by record files and the active diagram file; do not whitelist every mention file as allowed to change. This is correctly named in the producer but needs care when consuming the manifest.
|
||||
2. `check` proves semantic complete-Project equality and parses the saved source. Its `roundtrip(actual)` formats a new bundle; it cannot prove preservation of the saved source's comments or exact Undo/Redo bytes. Keep the planned independent full-inventory byte snapshots and comment/span checks. The emitted PASS limits state this accurately.
|
||||
3. Each mode is one operation from original baseline. Loading a cumulative OA+SA trial will correctly fail. Maintain the plan's fresh working copies/reopens and do not revise expected output from observed saved coordinates.
|
||||
4. Generation is create-only, performs candidate validation before output, and checks reload afterward. A later filesystem failure can leave a partial directory, which intentionally cannot be reused. Treat only the final successful generation plus pinned manifest/baseline as a complete fixture; retry in a fresh directory. No overwrite claim is overstated.
|
||||
5. Compact fixture deliberately lacks role-only selected work, external one-ended saved route, explicit dirty-field scenario, and folded diagnostic cases. Those are existing focused-suite responsibilities, not established by this native oracle. Do not broaden native claims to them. Native SA exact-port refusal still needs observed port membership; unchanged bytes alone do not prove the intended hit.
|
||||
|
||||
No definite Rust API/type error was found in the inspected constructor/load/route-warning paths. Compilation, all producer tests, create-only generation and actual rendered preflight inspection remain the immediate next gates. No release/native acceptance follows from this static review.
|
||||
|
||||
## Native input tool qualification
|
||||
|
||||
Parent reports the available Computer Use sky API documents click, complete drag and key chords, but no held-Shift/mouse-down or multipoint stroke. Therefore the proposal's physical Shift-click, held-preview Escape and crossed out-and-back native sequences cannot be claimed from this API as currently documented. Use actual context **Add to selection** / **Inspect** controls for additive membership and primary in native trials; preserve Shift, held-preview/cancel and out-and-back as mounted-test evidence. A same-endpoint drag call does not establish that its path crossed the threshold, and a complete drag cannot establish an intermediate held preview/cancellation. Record actual observed native scope; do not invent input methods or relax oracle expectations to make an unsupported trial appear completed.
|
||||
@@ -0,0 +1,13 @@
|
||||
# Native fixture route preflight correction
|
||||
|
||||
2026-09-23, before fixture freeze. Only tracked change is `crates/oa-core/examples/allocation_multiselect_native.rs`; no production routing/App/planner change.
|
||||
|
||||
The parent's native-evaluator-1 compiled but passed4/5: deterministic fixture generation failed warning-free routing preflight with `sa_assign: [NoClearPath]`. Captured native-evaluator-2 added projected ports/nodes and actual route points to the failure; it reproduced the failure with754 unchanged inputs. Logs/receipts are `.local/allocation-group-gates/native-evaluator-{1,2}`. This is a retained failed attempt, not native acceptance.
|
||||
|
||||
Actual SA projection put X's output on Bottom at(192,272), Y's input on Top at(192,440). `routing::PORT_STUB=18` requires Y's terminal stub at(192,422). The ancestor B header begins at347, is64 high, and its routing obstacle expands by `CLEARANCE=12`, ending at423. Thus the terminal stub was one unit inside the inflated ancestor header. The active authored pins were intact; this was not endpoint promotion, missing exchange identity, port-contract corruption, or a suspended custom route. `routing::edge_obstacles` treats an endpoint's ancestor body as traversable but retains its inflated header. Automatic port `clear_exit` checks connected peer owners; it does not promise a clear exit through every ancestor header. No general routing change was necessary for this acceptance fixture.
|
||||
|
||||
Correction: authored B=(43,337), previously(43,347), in both OA/SA assignment fixtures. Y remains(80,440); pins, X, A, C, and all semantic records stay as designed. The header clearance now ends413, leaving nine units before the SA terminal stub422. This also preserves the existing OA counterpart and avoids introducing manual anchors solely to work around spacing. The ordinary OA performer move retains delta(77,43), therefore B's literal final position changes from(120,390) to(120,380). All other literal expected positions and routes stay exact. Earlier ignored proposal/producer copies are historical and contain the old coordinate; the tracked formatted producer is authoritative.
|
||||
|
||||
Captured native-evaluator-3 ran `cargo test -p oa-core --example allocation_multiselect_native --message-format=json -- --test-threads=1`: PASS5/0, exit0,754 inputs unchanged. Input map SHA256 `bd15948fb069a031f225667ddfe9812b62027ff750de9898329e43ba6a6f8b25`; receipt `.local/allocation-group-gates/native-evaluator-3/result.json`. Environment: stable1.98.1 Windows gnullvm, jobs1, incremental0. All six modes pass complete source/JSON tests and generation preflight for active warning-free routes, OA94/SA132 work extents, all visible placement origins, no unrelated projected overlap, and compact bounds. The negative mutations remain core-valid and are rejected by whole-Project equality. The actual core assignment Batch still passes one-step Session Undo/Redo.
|
||||
|
||||
Warning-free preflight was not weakened. This verifies fixture/core evaluator behavior, not App group-planner admission or real native usability. Parent still owns GUI capture, source-byte history checks, current combined gates, publication, and any subsequent guide wording changes.
|
||||
@@ -0,0 +1,123 @@
|
||||
# Allocation native helper review
|
||||
|
||||
Read-only review, 2026-09-23, of `.local/allocation-multiselect-native/{common.py,native.py,snapshot.py,history-bytes.py,capture.js,README.md}` against `crates/oa-core/examples/allocation_multiselect_native.rs` at `495464e84b5bae8ddad93e9fc69ef353e7f1c884`. Working tree was clean and producer bytes matched that commit. No Cargo, fixture generation, GUI, native acceptance, or tracked/helper edits were performed. Parent owns Gitea and actual native observation.
|
||||
|
||||
No blocking false-positive path found for the intended trusted, freshly generated fixture workflow. One small Windows path guard defect and the following evidence qualifications remain.
|
||||
|
||||
## Concrete guard defect
|
||||
|
||||
`common.safe_entry('/escape.arcadia')` accepts a rooted drive-less Windows path: `is_absolute()` is false, `drive == ''`, and no `..` exists. Joining it to `C:/.../working` yields `C:/escape.arcadia`. Reject `path.root`/`path.anchor` as well (and preferably empty/dot paths) before claiming the function guarantees containment. This was reproduced using the prescribed Windows Python. The deterministic producer emits `project.arcadia`; snapshot additionally requires the entry in its relative inventory. Therefore this does not allow the reviewed normal run to produce a passing history report, but malformed manifest preparation can produce an out-of-bundle workingEntry before the later rejection. No captured/helper files were altered to demonstrate it.
|
||||
|
||||
## What the checks establish
|
||||
|
||||
- `inventory` includes every file, not only `.arcadia`; snapshot compares pre-copy, post-copy, and post-check inventories plus the copied inventory. Added non-source files and mutations were independently shown to affect this map. Fixture pinning covers baseline, manifest, all source files, and generated geometry metadata. Preparation and snapshots use create-only destinations; failed partial runs remain unusable rather than being overwritten.
|
||||
- All six Python modes exactly match the Rust literal oracle. Each changed mode is from the deterministic original, never cumulative. The checker validates original-baseline identity and complete Project equality, including preserved engineering metadata, relation IDs, typed ports, inactive diagrams, and complete routes; it does not derive expected changes from the output or desktop planner.
|
||||
- History requires literal complete file maps/bytes for fixture==baseline==undo==explicit extra-unchanged snapshots and changed==redo==optional reopened. Canonical snapshots have ordered, non-overlapping times inside the recorded GUI lifetime. Changed paths must equal the supplied intended set, not merely be a subset. Extra-unchanged labels are not chronologically tied to a particular refusal; observation supplies that association.
|
||||
- Leading comment sentinels are checked independently in every included snapshot, so changing the same comment in changed and redo cannot hide behind group equality. A bounded test accepted the exact prefix and rejected deletion, comment rewrite, and LF-to-CRLF alteration. This establishes these exact leading comments including newline; it does not establish arbitrary internal comment or whitespace preservation in edited files. The README/report accurately restrict that claim.
|
||||
|
||||
## Source and executable provenance
|
||||
|
||||
`artifact` requires successful unchanged-input receipts, the exact input-map hash, one named executable, a non-test profile, and its current recorded binary hash. Independently confirmed that native-evaluator-3 is rejected because it is a test harness. A separately captured ordinary checker build is required.
|
||||
|
||||
During this review gui-1 completed. Its executable matched its receipt, and the existing source comparator independently found its full 754-input inventory and every Git blob equal commit `495464e84b5bae8ddad93e9fc69ef353e7f1c884`. The receipt's captured HEAD is the older `16b30a6...`; use exact input-map/blob evidence for the source claim, not that HEAD field. GUI map SHA-256: `dba5bc7789a56998fe9b877af26ab8a442b31de55443aaaea23930d58c8610ed`. The forthcoming ordinary checker must match this map exactly. Review did not write a gate source-proof receipt or inspect a future checker build.
|
||||
|
||||
Native helpers themselves do not compare an input map to Git or revalidate its archived capsule/toolchain. Their matching input-map and executable checks are receipt consistency, relying on the reviewed build capture and separate source comparison. `verify_prepared` pins the selected receipts, binaries, fixture and existing helper files; launch records process exit and readback hashes, while history rechecks logs and recorded lifecycle success.
|
||||
|
||||
## Owning source paths
|
||||
|
||||
`id_text_occurrence_files` correctly means references, not ownership. The CLI allows any explicit changed-path set, so determine it before the trial. Independent prediction from formatter ownership and the literal edits:
|
||||
|
||||
| Mode | Expected changed paths |
|
||||
| --- | --- |
|
||||
| oa-assigned | `oa/participants.arcadia`, `views/diagram_oa_assign.arcadia` |
|
||||
| sa-assigned | `sa/participants.arcadia`, `views/diagram_sa_assign.arcadia` |
|
||||
| oa-arranged | `views/diagram_oa_arrange.arcadia` |
|
||||
| sa-arranged | `views/diagram_sa_arrange.arcadia` |
|
||||
| oa-performers | `views/diagram_oa_assign.arcadia` |
|
||||
|
||||
Confirm the actual owning declarations in the generated immutable baseline before gestures. Allocation relations belong to their original performer source module; unchanged ownership is retained during edits. Occurrence files containing unchanged ID references must remain byte-identical.
|
||||
|
||||
## Observation still required
|
||||
|
||||
`capture.js` uses a supplied returned window and creates real screenshot/accessibility files, but does not bind that window to started.json's PID, hash its images, or reject capture after process closure. Parent must establish the process/window identity, inspect the actual working path, and retain/pin captures in the final evidence package. A screenshot or snapshot time inside the recorded lifetime alone does not establish active-process identity or an actual Save/Open action.
|
||||
|
||||
Ordered baseline/changed/undo/redo bytes cannot prove exactly one Undo and one Redo: manually restored files or several gestures could yield identical endpoints. The intended source must be opened through the observed GUI, edited through real supported controls, saved, and one-step history observed. Reopened means a witnessed in-process reopen, not a cold restart. Exit zero and unchanged hashes do not classify warnings; inspect app logs.
|
||||
|
||||
Current complete-click/drag/key-chord API does not establish held Shift, held preview/cancellation, or an out-and-back threshold crossing. Native additive selection through observed Add to selection/Inspect is valid but a different exercised input path. Exact membership/primary, focus, stable camera, destination/Arrange feedback, actual pointer delivery, and usable layout remain observer responsibilities. These limitations are already explicit and should remain in acceptance wording.
|
||||
|
||||
## Addendum: guard correction and preflight run separation
|
||||
|
||||
Parent subsequently changed `safe_entry` to require `not path.anchor` plus no `..` components. Independent Windows Python readback accepted `views/diagram.arcadia` and rejected `/escape.arcadia`, backslash-rooted paths, `C:/absolute.arcadia`, `C:relative.arcadia`, and `../traversal.arcadia`. The identified rooted-path defect is resolved. No helper or tracked source edits were made by this reviewer.
|
||||
|
||||
Run-1 is an aborted UI preflight, not accepted Allocation evidence. Parent observed only Home/Open chooser interactions and canceled the chooser before opening the fixture. Independent readback confirms working bytes still equal the generated fixture source, no snapshots directory or history acceptance report exists, normal exit 0, unchanged GUI/checker/fixture flags, and correctly pinned empty stdout/stderr. `run-1/helpers-original` exactly equals the complete helper inventory pinned in that run's prepared.json, preserving the original reviewed helper bytes after correction. The corrected global helper intentionally no longer matches run-1's old common.py pin; do not relabel or reuse that run for acceptance.
|
||||
|
||||
Parent starts a fresh run-2 with the corrected helper. That fresh preparation must pin the new bytes and earn its own actual source snapshots, observations, complete history report and lifecycle evidence. This addendum grants no run-2 acceptance in advance.
|
||||
|
||||
## Addendum: final evaluator gates, source ownership, and packaging proposal
|
||||
|
||||
Independently verified `native-evaluator-4/source-proof.json` and `native-lint-1/source-proof.json`: named run/commit, exact result pin, comparator bytes, input-map hash/count, exit 0 and unchanged-input flags all agree. Repeated the existing comparator's read-only full 754-path inventory and Git-blob comparison against `495464e84b5bae8ddad93e9fc69ef353e7f1c884` for both. The evaluator log records five tests passed, zero failed; lint used `-D warnings` and completed normally. These are reviewed captured executions, not newly rerun Cargo tests. Existing Allocation `prepare.collect`/verifier also passed wholly in memory for these two closed gates (two proofs, one deduplicated source-capsule pair); no archive or packaging files were written.
|
||||
|
||||
The expected source-path table above is now independently confirmed against generated **immutable** run-2 fixture declarations as well as tracked formatter ownership. OA ax/by allocation declarations are in `oa/participants.arcadia`; SA ax/by are in `sa/participants.arcadia`. Each diagram declaration, placements and route reside in its own `views/diagram_<scope>.arcadia`. Producer `expected` mutates only ax/by relation bodies plus active placements/pins for assignment; Arrange and ordinary performers mutate only active placements/pins. `update_bundle` retains existing declaration ownership. Therefore the table is the complete intended changed-file set, established without reading a post-gesture diff. No live working source, GUI or snapshots were altered.
|
||||
|
||||
### Minimal versioned evidence proposal (not created)
|
||||
|
||||
Use one new `harness/allocation-native-evidence-20260923/` tree with:
|
||||
|
||||
- `checks/`: existing `.local/allocation-evidence-tools/prepare.py` output (`prepare.py`, `verify.py`, `README.md`, `manifest.json`, `files.json`, `evidence.zip`). Select explicit closed final native/app/build gates with their existing source proofs; minimally GUI/checker builds, native-evaluator-4 and native-lint-1, plus final App acceptance/lint gates required by the parent report. Retain relevant failed predecessor gates if not already durably published; link existing planner evidence instead of copying it again. This schema already preserves optional proofs and comparator bytes, so no separate source-proof package is needed.
|
||||
- `native/`: one portable archive plus member/run manifest, outer hashes and a small standard-library verifier adapted from the existing Process native packager. Preserve exact selected **closed** run trees, all successful and failed snapshots, immutable fixtures, working bytes, screenshots/accessibility, observation notes, executed capture bytes if actually recorded, logs and exact historical helper versions. Identify run-1 as `aborted-preflight`, retain its `helpers-original`, and make accepted modes/observed scope explicit per later run. Link/pin the corresponding GUI/checker build receipts in sibling checks instead of duplicating their source capsules. The adaptation must replace Process-specific fixture/mode/report checks and hardcoded run-1 failure narrative, enforce Allocation's exact expected-path map and prefix sentinel checks, and distinguish observed acceptance from archive consistency. Existing Allocation gate tools alone cannot consume native run directories.
|
||||
- `reviews/`: compact exact-byte archive of the final relevant independent App/native review notes, member hashes and short index; adapt the existing review-archive pattern. Do not copy every exploratory draft. Link the authoritative versioned acceptance report for results and limitations.
|
||||
|
||||
The parent acceptance report should be the single readable account of exercised modes, code commit, actual one-step history and in-process reopen observations, plus unsupported held-input/cancel scope. Archive indexes only navigate/pin evidence. Preserve original paths/receipt bytes inside archives; portability comes from manifest mappings, not rewritten receipts. Exclude live runs from capture. Before publication, perform create-only package write/readback and bounded negative verification (mutated member, omitted file, wrong mode/path/sentinel and swapped proof/receipt); no GUI/checker replay or broader new framework is needed.
|
||||
|
||||
## Addendum: run-3 OA assignment evidence review
|
||||
|
||||
Run-3 supports bounded OA two-work assignment acceptance when combined with the parent's observed actions. Independent read-only verification rechecked current pinned GUI/checker/helper/fixture bytes; launch/preparation binding; five snapshot start/result/report/command/log relationships and lifetime ordering; the complete 16-file inventories; leading sentinels; exact history groups; final working==reopened bytes; and normal exit 0 with correctly pinned empty app logs. All checks passed. No checker, Cargo or GUI was rerun by this reviewer.
|
||||
|
||||
The actual complete source diff is exactly the intended literal edit: oa_assign_ax/by change only source references A/B to C; X/Y positions change x80 to560; the two authored pins change x340 to820. Their y values, relation IDs/descriptions and every other byte remain unchanged. Only `oa/participants.arcadia` and `views/diagram_oa_assign.arcadia` differ. Baseline==undo==fixture, and changed==redo==reopened, across all 16 files. Each of baseline/changed/undo/redo/reopened has a correctly bound successful complete-Project checker report in its intended mode. This is stronger source-specific evidence than a permitted-file whitelist alone.
|
||||
|
||||
Visually reviewed captures 06-two-selected, 07-group-assigned, 09-one-undo and 15-reopened. Both X/Y cards have selection outlines; the strip says 2 selected and Properties edit only Y, with Y's inspector initially showing performer B. After assignment both cards are inside C, the inspector shows performer C, and the status says Assign 2 of 2 leaf activities to C. Selection/primary and the 72% zoom/layout remain consistent across the operation/undo captures; undo visibly restores A/X and B/Y. Reopen shows the assigned saved geometry, no selection, and source-open feedback. All cards and destination headers are readable at the captured 1442x932 size. These screenshots support before/after state and truthful completed-operation feedback, not held-preview or intermediate pointer behavior.
|
||||
|
||||
The UI explicitly shows 18 model checks and reports 18 model warnings retained. Empty runtime logs do not mean no model warnings. Acceptance should characterize those existing fixture/model diagnostics once and avoid claiming a warning-free model. This review does not infer their complete categories from the screenshots. Capture metadata names the expected executable and stable window ID, but has no PID binding; parent observation remains the authority for launch/window identity and exactly one physical Undo/Redo. Reopen is within the same process. The review does not grant SA, Arrange, ordinary-performer, Shift/held-input, canceled-drag, refusal or compact-window acceptance from this OA trial.
|
||||
|
||||
Run-2 remains a separate aborted baseline-verification attempt. Independently confirmed its fixture, working and copied baseline source have identical full inventories, its baseline checker receipt records exit3221225781 with empty streams, GUI closed exit0, and no history acceptance report exists. Parent identifies a missing toolchain DLL search PATH as the cause; the failed receipt itself only establishes process-start failure, not a successful semantic check. Preserve the failed snapshot and contextual diagnosis. Parent observed only X selection and no model mutation; do not promote that run to native assignment acceptance.
|
||||
|
||||
## Addendum: precise classification of the 18 fixture warnings
|
||||
|
||||
Resolved from the current tracked validator branches and independently enumerated immutable baseline records, without compiling or executing another binary. The available `arcadia.exe` predates this source snapshot, so it was not used as current-source evidence. The four warning categories sum exactly to the 18 shown by the native GUI:
|
||||
|
||||
| Code | Count | Exact affected aliases |
|
||||
| --- | --- | --- |
|
||||
| `work_without_behavior_context` | 8 | `oa_assign_x`, `oa_assign_y`, `oa_arrange_u`, `oa_arrange_v`, `sa_assign_x`, `sa_assign_y`, `sa_arrange_u`, `sa_arrange_v` |
|
||||
| `exchange_without_behavior_context` | 4 | `oa_assign_exchange`, `oa_arrange_exchange`, `sa_assign_exchange`, `sa_arrange_exchange` |
|
||||
| `unallocated_leaf` | 2 | `oa_arrange_v`, `sa_arrange_v` |
|
||||
| `empty_port_contract` | 4 | `sa_assign_exchange_input`, `sa_assign_exchange_output`, `sa_arrange_exchange_input`, `sa_arrange_exchange_output` |
|
||||
|
||||
`validation/coverage.rs:70` requests including each leaf work item in a process/chain or scenario through an action/exchange occurrence; line119 requests using each exchange in such a behavior context. This compact fixture contains no behavior sequences/occurrences, so its eight leaves and four exchanges intentionally have no such coverage. `validation.rs:344` requests allocating each unallocated leaf; the two independent V cards deliberately have no performer to exercise mixed Arrange semantics. All other six leaves have exactly one direct allocation and no role ambiguity. `ports.rs:808` warns that each typed SA function port has no declared exchange items (input field `incoming_items`, output field `outgoing_items`). The four ports are correctly typed and owned, with empty payload lists and no legacy contract; they remain valid rendered hit targets.
|
||||
|
||||
These are existing advisory engineering-completeness warnings, not malformed source, broken endpoint ownership, route suspension, a crash, or a warning introduced by group movement. `session.rs:276` makes `validate_candidate` reject any Error while permitting these Warnings; the recorded native checker calls that gate. The fixture producer independently requires no routing warnings and active declared pins in every literal candidate. Source-derived enumeration found the expected8+4+2+4 objects; this is an explanation of the captured native count, not a newly executed Rust validation receipt.
|
||||
|
||||
Every intended native mode preserves the warning conditions: assignment changes existing direct performer sources while retaining one allocation per affected leaf; Arrange/ordinary movement change placements/routes only; no mode adds behavior coverage or payload declarations. Therefore retained18 is expected throughout and does not undermine this bounded assignment/movement/source-history acceptance. It does mean the fixture is not a complete behavior or exchange-payload model; do not report complete engineering coverage or a warning-free project. No production, helper, binary or live GUI state was changed by this investigation.
|
||||
|
||||
## Addendum: run-4 SA assignment/refusal and run-5 OA Arrange
|
||||
|
||||
Independent read-only checks passed for both closed runs: exact pinned preparation/helpers/fixture/binaries, launch/lifecycle/log relationships, every snapshot start/result/checker stdout and source pin, complete16-file byte groups, sentinel prefixes, canonical history order, final working source, and unique existing image references (19 captures for run-4;26 for run-5). Each run exited0 with empty pinned app logs. Parent separately observed one Ctrl+Z and one Ctrl+Y per operation; this review did not replay inputs or execute a checker.
|
||||
|
||||
Run-4's baseline==undo==port-refusal==port-refusal-saved equals the immutable fixture; changed==redo. The only source edits are SA allocation ax/by source references to C, X/Y x80→560 and both route-pin x340→820, in the two predeclared files. All six snapshot reports pass their exact complete-Project modes. Reviewed captures07/08/13: the refusal trial visibly selects X plus Y's actual input port, the inspector says Port with alias `sa_assign_exchange_input`, and the strip says2 selected. Refusal text explicitly says movement requires placed work/performers and to remove ports/rows/connections. This removes the earlier port-hit/owner-promotion uncertainty for this observed trial. The later valid assignment visibly moves both typed Function cards and endpoint route into C while preserving Y as primary, with truthful2-of-2 feedback. No SA reopen was captured or claimed.
|
||||
|
||||
Run-5's baseline==undo==mixed-refusal equals the immutable fixture; changed==redo==reopened. Its sole changed file is `views/diagram_oa_arrange.arcadia`: P y40→280; U/V y140→380; pin y180→420 and230→470. All engineering relations/allocations and other source bytes remain exact; all six checker reports pass. Reviewed captures12/14/15: exact mixed P/V selection with V primary remains visible after ordinary refusal, whose feedback directs the user to Arrange; the control then explicitly shows armed; the result shows it consumed and reports2 selected as3 placements(1 contained). P, unselected contained U and independent V move together at stable79% zoom. The diagrams remain readable in the observed viewport.
|
||||
|
||||
These observations extend bounded native acceptance to SA assignment, exact typed-port whole-set refusal, OA mixed ordinary refusal and explicit OA Arrange, plus their recorded source/history evidence. They add no held-preview, Shift, Escape/interruption, out-and-back, compact-window or SA-reopen evidence. The18 retained completeness warnings are the previously classified unchanged fixture advisories. No new finding requires these successful runs to be repeated. No packaging work was duplicated.
|
||||
|
||||
## Addendum: final run-6/run-7 and native package consistency review
|
||||
|
||||
Run-6 contains21 captures and5 successful complete-Project snapshots (baseline, mixed-refusal, changed, undo, redo). Run-7 contains14 captures and4 successful snapshots (baseline, changed, undo, redo). Both histories, exact16-file snapshot inventories, prefix sentinels, receipt/source/build relationships and clean lifecycle records verify; both GUI exits are0 with empty app stdout/stderr. Final working bytes equal redo. Run-6 baseline==mixed-refusal==undo and changed==redo; run-7 baseline==undo and changed==redo. Neither run includes reopen.
|
||||
|
||||
Independently inspected literal source diffs: run-6 changes only `views/diagram_sa_arrange.arcadia`, exactly P/U/V y+240 and both pin y+240 (five numeric edits). Run-7 changes only `views/diagram_oa_assign.arcadia`: A(43,47)→(120,90), B(43,337)→(120,380), X(80,140)→(157,183), Y(80,440)→(157,483), pins(340,260)/(340,400)→(417,303)/(417,443). All six positions use exactly delta(77,43); all engineering allocations and remaining source bytes stay exact. This demonstrates off-grid anchored successful movement, not the separate out-and-back/no-op case.
|
||||
|
||||
Reviewed run-6 captures12/13/15/16: P/V are selected with P primary; ordinary mixed movement is refused with Arrange guidance; armed feedback appears; after the parent's Alt+A/V-anchor action, P, contained U and independent V have all moved, P remains primary, the arm is consumed, and feedback reports2 selected/3 affected placements. Ports remain visible and the72% viewport stays readable. The input mechanism and nonprimary anchor depend on the parent's observed action sequence, consistent with the images. Reviewed run-7 captures06/07: A/B selected with B primary, A-anchor movement carries X/Y and the route, B remains primary, and feedback says Move2 selected performers without assignment or Arrange semantics.
|
||||
|
||||
**No compact-window acceptance:** run-7 capture13's resize attempt did not resize; capture14 was reobserved at the original normal window extent. Its actual image metadata is1442x932 (parent reports window rectangle1442x933). The labels `compact-layout`/`compact-observed` are historical names, not measured compact geometry. Neither run establishes held Shift, interrupted drag/Escape, held preview, out-and-back or no-op history. Parent observed exactly one Undo/Redo per accepted operation; images/byte groups do not count physical actions themselves.
|
||||
|
||||
Read `allocation-native-package-tools/prepare.py` and `verify.py`, then independently collected/verified all seven closed runs **in memory only**. Exact totals:102 captures,27 snapshots,26 successful checker reports,5 history reports; run-1 has3 captures/no snapshots, run-2 has4 captures/one failed baseline snapshot, accepted-workflow runs3–7 have95 captures/26 positive snapshots. The archive layer correctly retains runs1/2 as aborted and leaves others unreviewed; parent acceptance is separate. It binds every consumed raw member, historical helper, fixture/source map, Cargo executable artifact, captured report and history group, and enforces Allocation-specific intended paths/sentinel prefixes. The collector's second raw-tree read rejects changes during collection. No concrete consistency blocker found for these captured runs.
|
||||
|
||||
Three bounded negative checks mutated only in-memory history JSON while updating its archive member hash, so rejection was semantic rather than merely a stale outer hash: incorrect expected changed paths rejected at exact intended Allocation source paths; altered sentinel map rejected at fixture sentinel inventory; wrong changed mode rejected at history mode/receipt equality. Source-commit proofs remain in sibling gate evidence; the native verifier establishes recorded capsule/binary provenance but does not perform another Git comparison or binary replay. Missing executed capture source remains explicitly null; prepared template bytes cannot prove exact executed UI code or PID/window linkage. No archive, helper, production source or binary was changed by this review.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Allocation native capture helpers
|
||||
|
||||
Prepare-only local helpers, adapted from process-multiselect-native. Their presence is not native acceptance. No build, fixture generation or GUI launch was performed while preparing them. Parent owns observation, action choice, receipt selection and evidence approval. Existing run directories, source snapshots and reports are never overwritten or removed.
|
||||
|
||||
## Matching input and create-only preparation
|
||||
|
||||
From `C:/ar-multiselect-9e8r6t`, `python` below means `C:/Users/User/.cache/codex-runtimes/codex-primary-runtime/dependencies/python/python.exe`. Obtain successful captured **build** receipts for `allocation_multiselect_native` and `arcadia-studio` from the same input map; test-harness executables are rejected. Use the same working Windows/toolchain runtime environment as the successful builds, including required DLL search paths. Do not treat a missing-DLL process exit as an observed GUI trial.
|
||||
|
||||
```text
|
||||
python -B .local/allocation-multiselect-native/native.py prepare run-1 --checker-receipt CHECKER_BUILD/result.json --gui-receipt GUI_BUILD/result.json
|
||||
python -B .local/allocation-multiselect-native/native.py launch run-1
|
||||
```
|
||||
|
||||
Replace receipt placeholders with actual passing captures. `prepare` verifies the recorded input-map/executable hashes, refuses existing run-N, runs checker `generate` into immutable `run-N/fixture`, and copies its source into `run-N/working`. It pins the complete fixture and helper files. Inspect generation logs and `fixture/manifest.json` before launching; the fixture producer's geometry preflight must pass. A failed producer run remains evidence; use a new label after fixes. Never launch/open the immutable fixture for editing.
|
||||
|
||||
`launch` prints PID and workingEntry, captures stdout/stderr, and waits for normal closure before recording finished.json/exit status and unchanged hashes. Keep that producer session alive. The GUI receives no project CLI argument: use its Open flow for the printed workingEntry. Match one actually returned Computer Use window to the recorded process using observed window/app data. Close normally and inspect logs; the helper does not decide whether warnings are acceptable.
|
||||
|
||||
## One chosen operation mode per run
|
||||
|
||||
Supported checker modes are `unchanged`, `oa-assigned`, `sa-assigned`, `oa-arranged`, `sa-arranged`, `oa-performers`. Each changed mode means one literal operation from the generated baseline, not a cumulative progression through all modes. Use fresh run directories for independent OA/SA/Arrange/ordinary-performer trials. The generated manifest records exact selection and expected deltas; consult the actual matching producer rather than inventing coordinates from this README.
|
||||
|
||||
Use observed context-menu **Add to selection** and **Inspect** for native membership/primary. Current sky offers complete clicks, drags and key chords. Do not claim held Shift, a screenshot during a held gesture, interrupted drag, or out-and-back native evidence. Those require separate mounted input/renderer tests; complete drag can establish its observed before/after behavior only. Parent records exact count, primary, stable camera/layout, target choice, refusals and source/history actions separately.
|
||||
|
||||
Before input, read the Computer Use SKILL.md and its guidance/API; use its node_repl + sky returned-window workflow. After selecting exactly one returned window, set `globalThis.allocationRun = 'run-1'` and `globalThis.allocationWindow` to that actual object, then load capture.js. `await allocationCapture('01-baseline-saved')` writes actual screenshots/accessibility create-only and updates allocationState/allocationWindow. Every capture label is unique. Observe, perform one supported action, then refresh; no guessed window handles or synthetic screenshots.
|
||||
|
||||
## Saved snapshots and exact byte history
|
||||
|
||||
Pause interaction after each completed save. Each snapshot copies **every file** under working, verifies complete stable inventories before/after copying and checking, and invokes the pinned independent complete-Project checker. Snapshots copy names/content literally, including comments and otherwise unrelated files; copying alone does not establish preservation across an edit. No id-reference path whitelist is applied.
|
||||
|
||||
Example for one OA assignment run:
|
||||
|
||||
```text
|
||||
python -B .local/allocation-multiselect-native/snapshot.py run-1 baseline unchanged
|
||||
python -B .local/allocation-multiselect-native/snapshot.py run-1 mixed-refusal unchanged
|
||||
python -B .local/allocation-multiselect-native/snapshot.py run-1 changed oa-assigned
|
||||
python -B .local/allocation-multiselect-native/snapshot.py run-1 undo unchanged
|
||||
python -B .local/allocation-multiselect-native/snapshot.py run-1 redo oa-assigned
|
||||
python -B .local/allocation-multiselect-native/snapshot.py run-1 reopened oa-assigned
|
||||
```
|
||||
|
||||
The observer performs and records exactly one Undo and one Redo between the corresponding saves. Reopened is optional and means reopening the saved working source **within the same GUI lifetime**; it is not a cold restart. A failed/partial snapshot is retained and needs a fresh label/run; do not overwrite it or repair its receipt.
|
||||
|
||||
After normal app closure and finished.json, run:
|
||||
|
||||
```text
|
||||
python -B .local/allocation-multiselect-native/history-bytes.py run-1 --changed-mode oa-assigned --expected-changed-source EXACT_PATH_1 --expected-changed-source EXACT_PATH_2 --extra-unchanged mixed-refusal --reopened
|
||||
```
|
||||
|
||||
Supply each exact expected changed bundle-relative path after inspecting generated source and the literal intended edit; repeat the flag once per path. Include only the paths that should actually change. Manifest `id_text_occurrence_files` is an identity-reference aid, **not** an allowed-change list: referencing an edited ID does not authorize rewriting that file. Do not derive the allowed set from the observed diff after the trial merely to make the gate pass. Replace the example's path count with the independently reviewed complete intended set.
|
||||
|
||||
The gate requires `fixture source == baseline == undo == every --extra-unchanged label` byte-for-byte, and `changed == redo == reopened` (when requested). The groups must differ at exactly the supplied paths. Independently of these history groups, every listed snapshot must retain every fixture `.arcadia` file's exact leading `// Native preservation sentinel: {path}\n` bytes, checked against the pinned immutable fixture, including the newline. These prefixes are all generated comments in this fixture. This detects a comment altered in both changed and redo even though their bytes match; it does not establish arbitrary comment-span preservation. The report records `fixtureCommentPrefixes` and this bounded scope. It verifies full file inventories, source/log/receipt hashes, one selected checker mode, and ordered baseline→changed→undo→redo→optional reopened snapshots within the recorded GUI lifetime. Pass `--reopened` only when that snapshot exists; otherwise omit it and the claim. history-bytes.json is create-only. Explicit extra-unchanged labels cannot duplicate canonical history labels.
|
||||
|
||||
## Limits
|
||||
|
||||
The Rust checker compares complete Projects; the history helper compares complete saved-directory bytes within each history group and separately verifies the immutable fixture prefix sentinels across all listed snapshots. It does not parse or prove preservation of arbitrary comment spans in changed files. Neither proves pointer delivery, set/primary identity, focus, usability, the number of Undo/Redo gestures, readable live feedback, or absence of serious warnings. Actual captures and the parent's observations supply those facts. Receipt/hash consistency is not external authentication. Files outside working (preferences, caches and recovery storage) are outside the byte gate. Failed or unlisted exploratory snapshot labels remain preserved but are not silently promoted to passing evidence. No helper imports App into the pure checker or changes producer geometry.
|
||||
@@ -0,0 +1,37 @@
|
||||
# Allocation integration seams for prerelease #37
|
||||
|
||||
Read-only comparison of actual fetched commits: group/App source `495464e84b5bae8ddad93e9fc69ef353e7f1c884` and drawing/Traceability tip `301126f6098286bf440c1bd1b1ed685753984723`; common ancestor `b8685416f56e53e30bd11ecf93a54cea10ac8458`. Local HEAD was reported by the parent as `57aec979f640ba1b8aa6fcbb7d9e26b29cf51596`; this review uses the explicit source commits, not uncommitted files. Existing untracked human-check documents were left untouched. No merge, index/ref/object mutation, build, GUI or Gitea action occurred. A stdout-only three-way merge of temporary file copies was used to identify text conflicts. This is not acceptance of a merged release candidate; the later union tip was not available to this review.
|
||||
|
||||
## One confirmed textual conflict
|
||||
|
||||
The branches modify five common code files. A three-way text check found **one conflicting hunk in `app.rs`, `App::update_inner`, `Message::Menu(Event::Invoke)`** (group line2412; drawing line2294). `architecture_authoring/tests.rs`, `guide.rs`, `model_menu.rs` and `views.rs` merge textually. Peer changes no `diagram.rs` canvas code.
|
||||
|
||||
Owner956 must compose the Menu hunk in this order after the existing menu token/context and pending-edit guards:
|
||||
|
||||
1. Preserve the group's early dispatch for `Message::DiagramSelection` and captured Process `TopologyCanvas` actions. Calling `select(menu.request.object)` first would collapse/rebind exact typed membership or navigate away from a qualified Process use before the intended selection action.
|
||||
2. For the remaining actions, preserve drawing's narrowly scoped `show_in_mixed_view` branch (drawing `app.rs:2319`): Traceability `PlaceSelected` sets selected/clears attachment/refreshes draft without ordinary cross-layer navigation; otherwise use `select`. Dropping it makes Show in this diagram operate on the preferred same-layer view instead of the validated captured mixed view.
|
||||
3. Preserve group's State-vs-architecture placement routing (`behavior.diagram.placement` with `selected_node=None` on Behavior canvas; otherwise `selected_node=request.placement`) and `reconcile_multiselect`, followed by the existing bend/action dispatch.
|
||||
|
||||
Choosing either conflict side wholesale loses a verified invariant. The combined behavior is directly exercised by existing group/context-menu/State tests and drawing's `model_menu_traceability_tests` plus `traceability_navigation_tests`; neither branch's separate pass proves that composition.
|
||||
|
||||
## Concrete lifecycle gap to check on the union
|
||||
|
||||
Both input families share `App::update`. At group `app.rs:1442`, `allocation_before(&message)` runs before `edit_connection`; `app/allocation_drag.rs:686` retires Allocation state for every `Message::Connection`, and `State::retire` at608 clears active token, preview and Arrange arm while advancing its epoch. Only later does `App::edit_connection` (group4611; drawing4481) reject a stale `Event::Endpoint(captured, id)` or an absent drawing session. There is no Connection envelope among the earlier stale-admission guards (`source_input_envelope` accepts unrelated messages).
|
||||
|
||||
Consequently, a delayed endpoint from a canceled drawing session can retire a newer group drag/Arrange arm even though it cannot create a relation. This is a source-demonstrated ordering risk already present in the compared branches, **not a merge-marker conflict or a newly demonstrated native failure**. Drawing's `allocation_cancel_navigation_stale_context_and_pending_edits_preserve_model` preserves a newer drawing session under old endpoints; group's `navigation_union::assert_stale_noop` preserves newer group ownership for other stale packet families. Neither inspected test covers this cross-family case. The focused integration check is: cancel drawing, establish a fresh live Allocation group press/Arrange state, deliver the old Endpoint, and require unchanged token/epoch/arm/preview/Document. If fixed, reject stale/non-owned drawing endpoint packets before shared retirement while still letting a current intentional tool change retire old drag ownership.
|
||||
|
||||
## Compatible seams that must not be conflated
|
||||
|
||||
Canvas dispatch already separates modes: `diagram/multiselect.rs::group_input` (group85, guard158) declines new group input while a connection session exists; `allocation_input_owner` (636) also requires no connection session. `DiagramCanvas::update` then routes the physical endpoint click to Connection before normal Allocation movement. Keep these guards; no replacement canvas architecture is needed to add the tool.
|
||||
|
||||
Drawing's `connection_tool::Tool::{can_connect,command,available_in}` (peer76/126/185) creates **one new Allocation relation for compatible unallocated leaf work**, preserves authored placements and view filters, and requires projections containing both performer/work categories. Group assignment deliberately retargets existing Allocation relation bodies while moving the selected closure and pins. Do not substitute either operation's planner for the other. The palette's new `available_in` call replaces an exhaustive four-tool match; retain it with the new enum variant.
|
||||
|
||||
Drawing's mixed Traceability endpoint branch in `edit_connection` intentionally bypasses ordinary `select` for the visible opposite-layer performer, and temporarily aligns `self.layer` to the captured diagram before execute, restoring it on failure. Keep that captured-view behavior together with group's outer multiselect reconciliation. The existing `allocation_mixed_view_keeps_captured_canvas_for_opposite_layer_pairs` tests exact view/camera/source history and rejects opposite-owner-layer targets; preserve those assertions on the actual union. Actual group/native results remain evidence for495464e, not automatic acceptance of drawing or the merged candidate.
|
||||
|
||||
## Parent readback of the selected candidate
|
||||
|
||||
The release owner subsequently published candidate `6ae75fb2ccaae16c29004444815ddeaeab2e75ac`. Full-branch transport stalled, so this session retrieved named files through Gitea's contents API at that explicit commit, verifying returned size and Git blob SHA-1 before inspection. This is scoped source inspection, not a local candidate checkout, combined compilation or package acceptance.
|
||||
|
||||
Candidate `app.rs` blob `646b0415a156b3a4e8074847c5b2dadfea24b27c` preserves the required Menu::Invoke composition: exact group/Process early dispatch, captured mixed-view placement, State-versus-Architecture placement assignment, and final selection reconciliation. Candidate `app/allocation_drag.rs` blob `b6c329496c1c935defee6f6cd85c281c3556e4a2` is byte-identical to the accepted495464e adapter. The old Endpoint/Restart admission still occurs after shared retirement in this candidate, confirming the identified gap applies to the selected integration source.
|
||||
|
||||
The narrow contribution adds early admission for both captured drawing packet types. A packet must match the current route context and an extant session with that exact context before shared state changes. Choose/Begin remain intentional tool boundaries, and a current Restart retains its existing reset behavior. The regression distinguishes missing session, wrong captured context, wrong live context while captured/session agree, and wrong retained session while captured/live agree. OA/SA canceled-drawing-to-Arrange tests require unchanged ownership, preview, selection and exact source, then complete the surviving action with literal model/history expectations. A positive drawing path verifies current Choose/Begin, Restart and a completed connection. The [admission report](prerelease37-connection-admission-20260924.md) distinguishes this contribution's validation from combined-source acceptance and records the subsequently identified Route context/selected-edge correction.
|
||||
@@ -0,0 +1,61 @@
|
||||
# Captured Connection and Route admission — prerelease 37
|
||||
|
||||
Branch contribution by Codex-9e8r6t; release coordination is [issue 37](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/37). This is not acceptance of the combined prerelease or its packaged executable. Linux builds are excluded from this prerelease by the user's current instruction.
|
||||
|
||||
## Behavior and source
|
||||
|
||||
Source `72b1bb7aa723128f57b407f4dc55e55c3bc34818` adds early admission for captured Connection Endpoint/Restart events before shared Allocation retirement. A captured drawing event must match both the live route context and an extant drawing session. An old event therefore cannot retire a newer Arrange arm, active group press or preview. Current Choose/Begin and Restart retain their intentional tool-boundary behavior.
|
||||
|
||||
Production successor `6f247af795b7bec1818c588b2dabf203f8f68d8a`, with test-helper qualification corrected at final tested source `e68a83959900e556768d8470640e28a7df7d8cb8`, also admits captured Route Commit/CommitAttachment before retirement. Their captured context and changed edge must match the current context and selected edge. There is no invented route-session requirement. Selecting a placement group does not necessarily advance the canvas context, so context equality alone is insufficient. Stale route commits now preserve existing feedback instead of reaching the former late cancellation diagnostic. Current route edits and unscoped route commands retain their existing handlers.
|
||||
|
||||
The three Connection and six Route regression cases protect ownership, selection, preview/Document identity, complete model/source state, and exact Undo/Redo history. Positive cases prove current drawing and route edits remain usable. Context and selected-edge predicates have separate discriminating cases. The retained group movement uses literal expected placements and saved pin `(1100, 400)`, independently of the movement planner.
|
||||
|
||||
## Initial Connection validation
|
||||
|
||||
All successful rows below captured the same 755 inputs and were compared byte-for-byte with every relevant Git blob of source `72b1bb7`. Windows GNU LLVM; Rust/Cargo 1.98.1; one build job; incremental compilation disabled. The [portable package](prerelease37-connection-evidence-20260924/checks/README.md) retains exact commands, environment, source capsules, streams and source-proof receipts.
|
||||
|
||||
| Captured run | Result |
|
||||
| --- | --- |
|
||||
| connection-focused-2 | 3 passed, 0 failed |
|
||||
| connection-regressions-1 | 23 passed, 0 failed |
|
||||
| allocation-regressions-1 | 45 passed, 0 failed |
|
||||
| connection-format-2 | `cargo fmt --all -- --check`, exit 0 |
|
||||
| connection-lint-1 | `cargo clippy -p oa-desktop --all-targets -- -D warnings`, exit 0 |
|
||||
|
||||
The first focused/format attempts at `4421730` failed because the nested test module needed an explicit relative path; no tests ran in that attempt. The first focused filter also named the wrong Rust module path. Both failures are preserved. The corrected registration and actual `connection_lifetime` filter passed in the second attempts; independent review also added a test isolating the live-context predicate.
|
||||
|
||||
Initial archive SHA-256: `30087db61aa4e045a522cf9db56d6b85b595012ebb6a1bb1d03bfb8bb213bd5c` (6,087,017 bytes). Standalone verification and selfcheck passed: seven runs, two retained failures, five source proofs, original-byte equality and eleven corruption-rejection checks. Archive consistency is not a fresh test run or an authenticated build.
|
||||
|
||||
## Final Route and combined local admission validation
|
||||
|
||||
Final tested contribution source is `e68a83959900e556768d8470640e28a7df7d8cb8`. Each final gate captured the same 756 input files unchanged, and a separate exact Git-blob comparison binds its input map to that source. Environment matches the initial Windows GNU LLVM gates above. Test filters overlap; these row counts must not be summed into a unique-test total.
|
||||
|
||||
| Captured run | Command/filter and result |
|
||||
| --- | --- |
|
||||
| route-regressions-3 | Desktop `route` filter: 31 passed, 0 failed, including all six new Route cases |
|
||||
| allocation-regressions-2 | Desktop `app::allocation_drag::tests`: 51 passed, 0 failed, including the three Connection and six Route cases |
|
||||
| route-attachments-1 | Desktop `attachment` filter: 28 passed, 0 failed |
|
||||
| route-format-2 | `cargo fmt --all -- --check`, exit 0 |
|
||||
| route-lint-1 | `cargo clippy -p oa-desktop --all-targets -- -D warnings`, exit 0 |
|
||||
|
||||
The [Route evidence package](prerelease37-connection-evidence-20260924/route-checks/README.md) retains seven completed runs: these five final gates, earlier successful format at 6f247af, and the failed route-regressions-2 compile at 6f247af. The latter failed E0425 because the new test fixture used unqualified `project_view`; no tests ran. The one-line `projection::project_view` correction at e68a839 changes only that test helper. Seven source proofs bind the five final gates to e68a839 and the two historical gates to 6f247af; a source proof on a failed gate does not convert it to a pass.
|
||||
|
||||
The failed route-regressions-2 receipt records checked-out HEAD `c655c5eeaaa32c1c21074a7543844ae99a1d6fd5`, the documentation-only descendant of 6f247af. Its separate source proof names 6f247af because all captured compiler inputs match that commit exactly; the raw HEAD receipt is preserved unchanged.
|
||||
|
||||
An earlier [interrupted route compilation](prerelease37-connection-evidence-20260924/interrupted/README.md) is retained separately with its six partial raw files. It has no result receipt, known exit code or established test outcome and is excluded from the completed-run counts.
|
||||
|
||||
Standalone archive verification and selfcheck passed: seven completed runs, one retained compiler failure, seven source proofs, exact original-byte equality, existing-output refusal and eleven corruption-rejection checks. Final Route archive SHA-256: `501f01c7279bc9d719151abe9b729769dd0078678c702448671356e91965015f` (`6,094,831` bytes). Its schema/tool name retains the existing Connection-admission evidence name; the explicit run inventory identifies its Route scope. These checks establish captured evidence consistency, not hermetic compilation or full workspace acceptance.
|
||||
|
||||
The published initial evidence ZIP was also freshly downloaded from Gitea and matched its exact 6,087,017 bytes/hash; the [transport receipt](prerelease37-connection-evidence-20260924/transport-readback.json) records 26.0 seconds. This establishes artifact availability and one successful Windows transfer, not Linux networking or package runtime behavior.
|
||||
|
||||
## Reviews and scope
|
||||
|
||||
- [Drawing/Allocation integration seams](prerelease37-allocation-seams-20260924.md): actual branch/candidate inspection, including the required Menu composition.
|
||||
- [Connection guard review](prerelease37-connection-evidence-20260924/reviews/connection-envelope-review.md): initial evaluator finding and addressed readback.
|
||||
- [Route guard review](prerelease37-connection-evidence-20260924/reviews/route-envelope-review.md): same-context selection defect and addressed readback.
|
||||
- [Candidate Text Apply review](prerelease37-connection-evidence-20260924/reviews/candidate-text-review.md): scoped source evidence, not execution.
|
||||
- [Final delivery review](prerelease37-connection-evidence-20260924/reviews/final-delivery-review.md): independent receipt, archive and source-identity readback.
|
||||
|
||||
The release owner's integrated candidate `897d9f049406d5be597543eb0a6755b796259302` was fetched and inspected. Both new regression modules are byte-identical to tested contribution source e68a839. Its code difference from the prior candidate9e99618c is exactly the test-helper qualification. This confirms that the known compiler correction was integrated; it does not substitute these contribution gates for combined-source execution.
|
||||
|
||||
This narrow contribution has no fresh GUI build, mounted native reproduction or packaged acceptance claim. Earlier [Allocation native evidence](allocation-group-app-20260924.md) remains bound to `495464e`. The release owner's final integrated source, workspace gates, native tests and separate-machine downloaded-package launch are distinct acceptance steps. Preserved Scenario group work is excluded.
|
||||
@@ -0,0 +1,2 @@
|
||||
# Preserve measured package files and historical review bytes verbatim.
|
||||
* -text whitespace=cr-at-eol
|
||||
@@ -0,0 +1,24 @@
|
||||
# Prerelease 37 Connection admission captured-gate evidence tools
|
||||
|
||||
Adapted from the reviewed Allocation prepare/verify/selfcheck tools. Read-only preparation consumes explicitly selected **closed** runs in CHECKOUT/.local/prerelease37-gates. No Cargo, source execution, checkout, GUI, network or archived helper execution occurs.
|
||||
|
||||
```powershell
|
||||
python -B prepare.py --root CHECKOUT --runs connection-focused-1 connection-format-1 --check-only
|
||||
```
|
||||
|
||||
Keep failed connection-focused-1 and connection-format-1: verification preserves their recorded failures rather than converting archive consistency into test success. The example selects those two historical failures. Later closed Connection admission runs can be explicitly listed with the same interface; the final package manifest is authoritative for its exact inventory. No implicit discovery includes active directories.
|
||||
|
||||
To create a portable package, replace --check-only with `--output NEW_DIRECTORY`. Existing output refuses. The exact seven raw files are required, with only `source-proof.json` accepted as an optional eighth file. No raw byte normalization or receipt rewriting occurs. Maps and nested input ZIPs deduplicate by SHA-256; other logical raw files keep their names. All original helper bytes, failed-run streams and optional proof bytes remain archived. The prerelease37 Connection admission manifest kind distinguishes this evidence from Allocation and Process packages.
|
||||
|
||||
The gate.py and source-proof.py helper catalog is preserved. Every gate's recorded helper must match available bytes; use repeatable `--helper PATH` for older captures. If a proof exists, its exact comparator bytes must be available at the default `.local/process-source-evidence-tools/verify.py` or a repeatable `--comparator PATH`. They are archived under their content hash and never executed.
|
||||
|
||||
An optional source proof is independently pinned in the manifest with its full named commit. Verification binds it to the enclosing run, exact result receipt, captured map/count, completed gate time, status and comparator bytes/hash. Captured HEAD is preserved as historical metadata and is not substituted for the named source commit. This packager checks historical proof consistency; it does **not** repeat Git blob comparison. A source proof on a failed gate is source-identity evidence only, never a passing-test claim.
|
||||
|
||||
```powershell
|
||||
python -B PACKAGE/verify.py
|
||||
python -B selfcheck.py --root CHECKOUT --runs connection-focused-1 connection-format-1 --output TOOLS/NEW_SELFCHECK_DIRECTORY
|
||||
```
|
||||
|
||||
The verifier checks pins, exact ZIP inventory, process/start/result relationships, streams, command/exit identities, source capsules, vendor inventory and Cargo artifact relationships. Selfchecks compare packaged raw bytes to originals, reject existing output, and mutate only in-memory copies to test stream/helper/exit/capsule/proof corruption. The package contains tools, manifest, files.json and evidence.zip; selfcheck is a preparation utility, not packaged runtime input.
|
||||
|
||||
Limits: internal consistency is not external authentication, hermetic build proof, native usability, a new source comparison or production acceptance. Review exact run results and source identity separately. Run with `python -B` to avoid incidental bytecode writes.
|
||||
Binary file not shown.
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"prepare.py": {
|
||||
"bytes": 6319,
|
||||
"sha256": "ece8199911549266ee8ff6a3cd5636534cc13f15315fdce8838e269071a14383"
|
||||
},
|
||||
"verify.py": {
|
||||
"bytes": 11451,
|
||||
"sha256": "87e29134dd04f38040ed7be0fb36954ef6c5873ff7fe844217ce72fd1aa3f92f"
|
||||
},
|
||||
"README.md": {
|
||||
"bytes": 3287,
|
||||
"sha256": "cfa094bdf820db71002e84a0617ed86f4e2de2e693809f308ff02b6a80a21d07"
|
||||
},
|
||||
"manifest.json": {
|
||||
"bytes": 18809,
|
||||
"sha256": "4b5b371bfe6761b6183932793e37999f5082ebeb13beca7e86e5f5bcd850ea3e"
|
||||
},
|
||||
"evidence.zip": {
|
||||
"bytes": 6087017,
|
||||
"sha256": "30087db61aa4e045a522cf9db56d6b85b595012ebb6a1bb1d03bfb8bb213bd5c"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,414 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"kind": "prerelease37-connection-admission-cargo-gates",
|
||||
"runs": {
|
||||
"connection-focused-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--bin",
|
||||
"arcadia-studio",
|
||||
"--message-format=json",
|
||||
"allocation_drag_tests::group::connection_lifetime",
|
||||
"--",
|
||||
"--test-threads=1"
|
||||
],
|
||||
"head": "44217307f9194682d65babf883be87fdb709e4c0",
|
||||
"exitCode": 101,
|
||||
"inputCount": 755,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py"
|
||||
},
|
||||
"connection-format-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"fmt",
|
||||
"--all",
|
||||
"--",
|
||||
"--check"
|
||||
],
|
||||
"head": "44217307f9194682d65babf883be87fdb709e4c0",
|
||||
"exitCode": 1,
|
||||
"inputCount": 755,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py"
|
||||
},
|
||||
"connection-focused-2": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--bin",
|
||||
"arcadia-studio",
|
||||
"--message-format=json",
|
||||
"connection_lifetime",
|
||||
"--",
|
||||
"--test-threads=1"
|
||||
],
|
||||
"head": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"exitCode": 0,
|
||||
"inputCount": 755,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"pin": {
|
||||
"bytes": 599,
|
||||
"sha256": "9828a732394e336fa44e2fd175ae963dcbd49fb91e40f973d12e2bdd2aef1b49"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"connection-format-2": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"fmt",
|
||||
"--all",
|
||||
"--",
|
||||
"--check"
|
||||
],
|
||||
"head": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"exitCode": 0,
|
||||
"inputCount": 755,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"pin": {
|
||||
"bytes": 598,
|
||||
"sha256": "669373dcd72740739d45b132f633b4ddb05ac50447d8dcfd32ea726d213496fd"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"allocation-regressions-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--bin",
|
||||
"arcadia-studio",
|
||||
"--message-format=json",
|
||||
"app::allocation_drag::tests",
|
||||
"--",
|
||||
"--test-threads=1"
|
||||
],
|
||||
"head": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"exitCode": 0,
|
||||
"inputCount": 755,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"pin": {
|
||||
"bytes": 603,
|
||||
"sha256": "ab424d2c2a8d557dc3c607092061458bcc5035a70aa618f3421610efec29e3ae"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"connection-regressions-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--bin",
|
||||
"arcadia-studio",
|
||||
"--message-format=json",
|
||||
"connection",
|
||||
"--",
|
||||
"--test-threads=1"
|
||||
],
|
||||
"head": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"exitCode": 0,
|
||||
"inputCount": 755,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"pin": {
|
||||
"bytes": 603,
|
||||
"sha256": "cadd10c80770f167aebd72023846c8991291979ca34139c65109de25c03714cc"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"connection-lint-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"clippy",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--all-targets",
|
||||
"--message-format=json",
|
||||
"--",
|
||||
"-D",
|
||||
"warnings"
|
||||
],
|
||||
"head": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"exitCode": 0,
|
||||
"inputCount": 755,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"pin": {
|
||||
"bytes": 596,
|
||||
"sha256": "51d5d4915efc0e272062844638f529624319b10ff5885fb330731b99af2377b9"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
}
|
||||
},
|
||||
"captureHelpers": {
|
||||
"gate.py": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"source-proof.py": "helpers/58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119.py"
|
||||
},
|
||||
"logicalFiles": {
|
||||
"cargo/allocation-regressions-1/inputs.json": "capsules/a85e81534e4db5ddf3f677e86ad875f6b1e0ae184aa962bdfae40219a2a48901.json",
|
||||
"cargo/allocation-regressions-1/inputs.zip": "capsules/14cd46c42ec90ffb7ff40898fbc06f85f0aed4d75ec164e6250c763c123c5989.zip",
|
||||
"cargo/allocation-regressions-1/process.json": "cargo/allocation-regressions-1/process.json",
|
||||
"cargo/allocation-regressions-1/result.json": "cargo/allocation-regressions-1/result.json",
|
||||
"cargo/allocation-regressions-1/source-proof.json": "cargo/allocation-regressions-1/source-proof.json",
|
||||
"cargo/allocation-regressions-1/started.json": "cargo/allocation-regressions-1/started.json",
|
||||
"cargo/allocation-regressions-1/stderr.log": "cargo/allocation-regressions-1/stderr.log",
|
||||
"cargo/allocation-regressions-1/stdout.log": "cargo/allocation-regressions-1/stdout.log",
|
||||
"cargo/connection-focused-1/inputs.json": "capsules/331c184edd48b4616a43c353fead9a0aa15b262e8ba0f1d73813bea3451e90bf.json",
|
||||
"cargo/connection-focused-1/inputs.zip": "capsules/f1505cd1396b5662d904db4e13f36d43dbc50bd7243b02973c9d5dd7a8a9799a.zip",
|
||||
"cargo/connection-focused-1/process.json": "cargo/connection-focused-1/process.json",
|
||||
"cargo/connection-focused-1/result.json": "cargo/connection-focused-1/result.json",
|
||||
"cargo/connection-focused-1/started.json": "cargo/connection-focused-1/started.json",
|
||||
"cargo/connection-focused-1/stderr.log": "cargo/connection-focused-1/stderr.log",
|
||||
"cargo/connection-focused-1/stdout.log": "cargo/connection-focused-1/stdout.log",
|
||||
"cargo/connection-focused-2/inputs.json": "capsules/a85e81534e4db5ddf3f677e86ad875f6b1e0ae184aa962bdfae40219a2a48901.json",
|
||||
"cargo/connection-focused-2/inputs.zip": "capsules/14cd46c42ec90ffb7ff40898fbc06f85f0aed4d75ec164e6250c763c123c5989.zip",
|
||||
"cargo/connection-focused-2/process.json": "cargo/connection-focused-2/process.json",
|
||||
"cargo/connection-focused-2/result.json": "cargo/connection-focused-2/result.json",
|
||||
"cargo/connection-focused-2/source-proof.json": "cargo/connection-focused-2/source-proof.json",
|
||||
"cargo/connection-focused-2/started.json": "cargo/connection-focused-2/started.json",
|
||||
"cargo/connection-focused-2/stderr.log": "cargo/connection-focused-2/stderr.log",
|
||||
"cargo/connection-focused-2/stdout.log": "cargo/connection-focused-2/stdout.log",
|
||||
"cargo/connection-format-1/inputs.json": "capsules/331c184edd48b4616a43c353fead9a0aa15b262e8ba0f1d73813bea3451e90bf.json",
|
||||
"cargo/connection-format-1/inputs.zip": "capsules/f1505cd1396b5662d904db4e13f36d43dbc50bd7243b02973c9d5dd7a8a9799a.zip",
|
||||
"cargo/connection-format-1/process.json": "cargo/connection-format-1/process.json",
|
||||
"cargo/connection-format-1/result.json": "cargo/connection-format-1/result.json",
|
||||
"cargo/connection-format-1/started.json": "cargo/connection-format-1/started.json",
|
||||
"cargo/connection-format-1/stderr.log": "cargo/connection-format-1/stderr.log",
|
||||
"cargo/connection-format-1/stdout.log": "cargo/connection-format-1/stdout.log",
|
||||
"cargo/connection-format-2/inputs.json": "capsules/a85e81534e4db5ddf3f677e86ad875f6b1e0ae184aa962bdfae40219a2a48901.json",
|
||||
"cargo/connection-format-2/inputs.zip": "capsules/14cd46c42ec90ffb7ff40898fbc06f85f0aed4d75ec164e6250c763c123c5989.zip",
|
||||
"cargo/connection-format-2/process.json": "cargo/connection-format-2/process.json",
|
||||
"cargo/connection-format-2/result.json": "cargo/connection-format-2/result.json",
|
||||
"cargo/connection-format-2/source-proof.json": "cargo/connection-format-2/source-proof.json",
|
||||
"cargo/connection-format-2/started.json": "cargo/connection-format-2/started.json",
|
||||
"cargo/connection-format-2/stderr.log": "cargo/connection-format-2/stderr.log",
|
||||
"cargo/connection-format-2/stdout.log": "cargo/connection-format-2/stdout.log",
|
||||
"cargo/connection-lint-1/inputs.json": "capsules/a85e81534e4db5ddf3f677e86ad875f6b1e0ae184aa962bdfae40219a2a48901.json",
|
||||
"cargo/connection-lint-1/inputs.zip": "capsules/14cd46c42ec90ffb7ff40898fbc06f85f0aed4d75ec164e6250c763c123c5989.zip",
|
||||
"cargo/connection-lint-1/process.json": "cargo/connection-lint-1/process.json",
|
||||
"cargo/connection-lint-1/result.json": "cargo/connection-lint-1/result.json",
|
||||
"cargo/connection-lint-1/source-proof.json": "cargo/connection-lint-1/source-proof.json",
|
||||
"cargo/connection-lint-1/started.json": "cargo/connection-lint-1/started.json",
|
||||
"cargo/connection-lint-1/stderr.log": "cargo/connection-lint-1/stderr.log",
|
||||
"cargo/connection-lint-1/stdout.log": "cargo/connection-lint-1/stdout.log",
|
||||
"cargo/connection-regressions-1/inputs.json": "capsules/a85e81534e4db5ddf3f677e86ad875f6b1e0ae184aa962bdfae40219a2a48901.json",
|
||||
"cargo/connection-regressions-1/inputs.zip": "capsules/14cd46c42ec90ffb7ff40898fbc06f85f0aed4d75ec164e6250c763c123c5989.zip",
|
||||
"cargo/connection-regressions-1/process.json": "cargo/connection-regressions-1/process.json",
|
||||
"cargo/connection-regressions-1/result.json": "cargo/connection-regressions-1/result.json",
|
||||
"cargo/connection-regressions-1/source-proof.json": "cargo/connection-regressions-1/source-proof.json",
|
||||
"cargo/connection-regressions-1/started.json": "cargo/connection-regressions-1/started.json",
|
||||
"cargo/connection-regressions-1/stderr.log": "cargo/connection-regressions-1/stderr.log",
|
||||
"cargo/connection-regressions-1/stdout.log": "cargo/connection-regressions-1/stdout.log",
|
||||
"comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py",
|
||||
"helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"helpers/58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119.py": "helpers/58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119.py"
|
||||
},
|
||||
"members": {
|
||||
"capsules/14cd46c42ec90ffb7ff40898fbc06f85f0aed4d75ec164e6250c763c123c5989.zip": {
|
||||
"bytes": 3040776,
|
||||
"sha256": "14cd46c42ec90ffb7ff40898fbc06f85f0aed4d75ec164e6250c763c123c5989"
|
||||
},
|
||||
"capsules/331c184edd48b4616a43c353fead9a0aa15b262e8ba0f1d73813bea3451e90bf.json": {
|
||||
"bytes": 126555,
|
||||
"sha256": "331c184edd48b4616a43c353fead9a0aa15b262e8ba0f1d73813bea3451e90bf"
|
||||
},
|
||||
"capsules/a85e81534e4db5ddf3f677e86ad875f6b1e0ae184aa962bdfae40219a2a48901.json": {
|
||||
"bytes": 126555,
|
||||
"sha256": "a85e81534e4db5ddf3f677e86ad875f6b1e0ae184aa962bdfae40219a2a48901"
|
||||
},
|
||||
"capsules/f1505cd1396b5662d904db4e13f36d43dbc50bd7243b02973c9d5dd7a8a9799a.zip": {
|
||||
"bytes": 3040660,
|
||||
"sha256": "f1505cd1396b5662d904db4e13f36d43dbc50bd7243b02973c9d5dd7a8a9799a"
|
||||
},
|
||||
"cargo/allocation-regressions-1/process.json": {
|
||||
"bytes": 220,
|
||||
"sha256": "248f4476bc996f5b32ea708308d13805134f95cdec2af5620275b73b6a46fc79"
|
||||
},
|
||||
"cargo/allocation-regressions-1/result.json": {
|
||||
"bytes": 3346,
|
||||
"sha256": "e44aef2b84c8c32f4345f8906a3abc17f7b8322e1acc98b01b20ee17a146972c"
|
||||
},
|
||||
"cargo/allocation-regressions-1/source-proof.json": {
|
||||
"bytes": 603,
|
||||
"sha256": "ab424d2c2a8d557dc3c607092061458bcc5035a70aa618f3421610efec29e3ae"
|
||||
},
|
||||
"cargo/allocation-regressions-1/started.json": {
|
||||
"bytes": 1612,
|
||||
"sha256": "e8d3bf4aca316e1fd070ca0f5219d46313a1dfd4c64b0b4860116e2c419a6bbc"
|
||||
},
|
||||
"cargo/allocation-regressions-1/stderr.log": {
|
||||
"bytes": 210,
|
||||
"sha256": "c39778722d7dd318acd13162c403db30bef8954168ec8ae5705a6699fcc95b46"
|
||||
},
|
||||
"cargo/allocation-regressions-1/stdout.log": {
|
||||
"bytes": 298709,
|
||||
"sha256": "511fde7af8ce47db8b5290c43eeb42863f444fba9f84aa471d5d13286270c54e"
|
||||
},
|
||||
"cargo/connection-focused-1/process.json": {
|
||||
"bytes": 242,
|
||||
"sha256": "8eb05916282ff44629bf72088ec44748ccec510ccd8f64412622bd791cc3d172"
|
||||
},
|
||||
"cargo/connection-focused-1/result.json": {
|
||||
"bytes": 2031,
|
||||
"sha256": "3377951f3175537f7010c069e0d8fa0810a8bb24082ee6416d71b47eba51fda8"
|
||||
},
|
||||
"cargo/connection-focused-1/started.json": {
|
||||
"bytes": 1634,
|
||||
"sha256": "a0427b0557de3bce3fa4371610368b5aba56d7bc77a721d9ee857df14cd61f7a"
|
||||
},
|
||||
"cargo/connection-focused-1/stderr.log": {
|
||||
"bytes": 166,
|
||||
"sha256": "ad39fc56f685f7f3c6a9b4a342abff1f19f9daad28c394673be0e3a2a1cf2523"
|
||||
},
|
||||
"cargo/connection-focused-1/stdout.log": {
|
||||
"bytes": 295416,
|
||||
"sha256": "270c8973742b7564cd8dca1a39f25c24802b53befacdc44679bd261182be3d35"
|
||||
},
|
||||
"cargo/connection-focused-2/process.json": {
|
||||
"bytes": 212,
|
||||
"sha256": "88825e3fb6ec06cfc30390f6c920c8ba138cd1cf318e7640b1cb22d28376bb97"
|
||||
},
|
||||
"cargo/connection-focused-2/result.json": {
|
||||
"bytes": 3338,
|
||||
"sha256": "68939477e4011a0de426adf0c78260ce4eed7d4618de3119b33f27f9257234ef"
|
||||
},
|
||||
"cargo/connection-focused-2/source-proof.json": {
|
||||
"bytes": 599,
|
||||
"sha256": "9828a732394e336fa44e2fd175ae963dcbd49fb91e40f973d12e2bdd2aef1b49"
|
||||
},
|
||||
"cargo/connection-focused-2/started.json": {
|
||||
"bytes": 1604,
|
||||
"sha256": "3ea15b713275aa4088f0ede9222f3e249106025ed3935c761a1e979e0810a1a6"
|
||||
},
|
||||
"cargo/connection-focused-2/stderr.log": {
|
||||
"bytes": 288,
|
||||
"sha256": "fe1a93a60511f726056a2b70353ff3d2ad9d63427b48b2c7355c5485e7a1dfb3"
|
||||
},
|
||||
"cargo/connection-focused-2/stdout.log": {
|
||||
"bytes": 293650,
|
||||
"sha256": "3c446459c6415db2cc1d9554507f278c2ad01aea6eb747525f60ed9e37f2d2e9"
|
||||
},
|
||||
"cargo/connection-format-1/process.json": {
|
||||
"bytes": 116,
|
||||
"sha256": "287fa4c950dfa20b2bd7dfc99914d07decbd7f3089ed6756aaa919bb8e26d3b7"
|
||||
},
|
||||
"cargo/connection-format-1/result.json": {
|
||||
"bytes": 1874,
|
||||
"sha256": "18ed79a8f6388bb93f199f79d77ddf3da7708054e0f41fcb9e44030911b8a087"
|
||||
},
|
||||
"cargo/connection-format-1/started.json": {
|
||||
"bytes": 1483,
|
||||
"sha256": "87bbb6011b68269fdab0472da3cfa074ccb561e63f1c9efa83ca81060f5b48dc"
|
||||
},
|
||||
"cargo/connection-format-1/stderr.log": {
|
||||
"bytes": 181,
|
||||
"sha256": "e6be01db877bb8d308543844eb96ef6c620e1b730b0313c4ab40dbd6fb859a4a"
|
||||
},
|
||||
"cargo/connection-format-1/stdout.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"cargo/connection-format-2/process.json": {
|
||||
"bytes": 116,
|
||||
"sha256": "55410bf9cc4f4b446b6aafd0782f234e756b3202952294f1ad52929de11f0646"
|
||||
},
|
||||
"cargo/connection-format-2/result.json": {
|
||||
"bytes": 1871,
|
||||
"sha256": "4f261156169ee53703ff784736792cf7ea593fb3f4f2004917aa0ad751d2e75e"
|
||||
},
|
||||
"cargo/connection-format-2/source-proof.json": {
|
||||
"bytes": 598,
|
||||
"sha256": "669373dcd72740739d45b132f633b4ddb05ac50447d8dcfd32ea726d213496fd"
|
||||
},
|
||||
"cargo/connection-format-2/started.json": {
|
||||
"bytes": 1483,
|
||||
"sha256": "6fad517a834063db7b5774bfb216f888ab76d46155fbbcc34504d7aad2188ae4"
|
||||
},
|
||||
"cargo/connection-format-2/stderr.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"cargo/connection-format-2/stdout.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"cargo/connection-lint-1/process.json": {
|
||||
"bytes": 179,
|
||||
"sha256": "ac079286f4df14c1797fab2030004b75653392b8078839d84e383158ecee3b22"
|
||||
},
|
||||
"cargo/connection-lint-1/result.json": {
|
||||
"bytes": 1962,
|
||||
"sha256": "ec759bb78dbadb99893c728dc87dac8c4f2f3b3b6548378e570b694a950563d1"
|
||||
},
|
||||
"cargo/connection-lint-1/source-proof.json": {
|
||||
"bytes": 596,
|
||||
"sha256": "51d5d4915efc0e272062844638f529624319b10ff5885fb330731b99af2377b9"
|
||||
},
|
||||
"cargo/connection-lint-1/started.json": {
|
||||
"bytes": 1566,
|
||||
"sha256": "f3863de8411828d8db60a87a31539b2966e62b2c0aee92ada8e175595627e914"
|
||||
},
|
||||
"cargo/connection-lint-1/stderr.log": {
|
||||
"bytes": 137,
|
||||
"sha256": "3a455f3ba071f27a9429f649c9cb75fce90d0b054054388470b73aadc557a18d"
|
||||
},
|
||||
"cargo/connection-lint-1/stdout.log": {
|
||||
"bytes": 269521,
|
||||
"sha256": "d316d47efa31ef26264566ce7dbb29f008adc7eb0cd4035319c42c9d066e862e"
|
||||
},
|
||||
"cargo/connection-regressions-1/process.json": {
|
||||
"bytes": 203,
|
||||
"sha256": "44e467beac6aa66ef68330d6e550fdf1df4273efbb5224f7eaf3c78a4c5ee7d9"
|
||||
},
|
||||
"cargo/connection-regressions-1/result.json": {
|
||||
"bytes": 3328,
|
||||
"sha256": "7a8de66cdcca94a5884046f4342b9267250746cec53b3ef2a63368477e082a09"
|
||||
},
|
||||
"cargo/connection-regressions-1/source-proof.json": {
|
||||
"bytes": 603,
|
||||
"sha256": "cadd10c80770f167aebd72023846c8991291979ca34139c65109de25c03714cc"
|
||||
},
|
||||
"cargo/connection-regressions-1/started.json": {
|
||||
"bytes": 1595,
|
||||
"sha256": "861f1a81ef5da8af2afe68cf2115a3ba6fb0403bdb48e4d82870e0f3841400e5"
|
||||
},
|
||||
"cargo/connection-regressions-1/stderr.log": {
|
||||
"bytes": 210,
|
||||
"sha256": "8e1c02b183f8e9812aba2413bde57010fb6eb9e6756dfaa04cad275028710a54"
|
||||
},
|
||||
"cargo/connection-regressions-1/stdout.log": {
|
||||
"bytes": 295748,
|
||||
"sha256": "953e35f1d65016e036219ea6114893c013206522f96d44b6df44de50ed35caf1"
|
||||
},
|
||||
"comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py": {
|
||||
"bytes": 10094,
|
||||
"sha256": "7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a"
|
||||
},
|
||||
"helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py": {
|
||||
"bytes": 3827,
|
||||
"sha256": "1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20"
|
||||
},
|
||||
"helpers/58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119.py": {
|
||||
"bytes": 1399,
|
||||
"sha256": "58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
"""Package explicitly selected closed gate captures without running Cargo."""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import json
|
||||
import zipfile
|
||||
from verify import RAW_FILES, pin, parsed, need, run_name, verify_data, verify_package
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def collect(root, runs, helpers, comparators=()):
|
||||
gates = root / ".local/prerelease37-gates"
|
||||
candidates = [gates / name for name in ("gate.py", "source-proof.py")] + helpers
|
||||
helper_bytes = [path.read_bytes() for path in candidates]
|
||||
comparator_paths = [root / ".local/process-source-evidence-tools/verify.py", *comparators]
|
||||
comparator_bytes = [path.read_bytes() for path in comparator_paths if path.is_file()]
|
||||
payload, logical, specs = {}, {}, {}
|
||||
def add(name, data):
|
||||
stored = ("capsules/" + pin(data)["sha256"] + Path(name).suffix
|
||||
if name.rsplit("/", 1)[1] in {"inputs.json", "inputs.zip"} else name)
|
||||
need(stored not in payload or payload[stored] == data, "deduplication collision")
|
||||
payload[stored] = data
|
||||
logical[name] = stored
|
||||
helper_catalog = {}
|
||||
for name, data in zip(("gate.py", "source-proof.py"), helper_bytes):
|
||||
helper = "helpers/" + pin(data)["sha256"] + ".py"
|
||||
add(helper, data)
|
||||
helper_catalog[name] = helper
|
||||
for run in runs:
|
||||
folder = gates / run
|
||||
need(folder.is_dir(), "run directory missing: " + run)
|
||||
paths = list(folder.iterdir())
|
||||
names = {path.name for path in paths}
|
||||
need(all(path.is_file() and not path.is_symlink() for path in paths)
|
||||
and names in (RAW_FILES, RAW_FILES | {"source-proof.json"}), "exact closed raw files required: " + run)
|
||||
raw = {path.name: path.read_bytes() for path in paths}
|
||||
result = parsed(raw["result.json"])
|
||||
need("finish" in result and result["inputsUnchanged"] is True, "unfinished/changed run refused: " + run)
|
||||
matches = [data for data in helper_bytes if pin(data) == result["helper"]]
|
||||
need(bool(matches), "exact historical helper unavailable for " + run + "; supply --helper PATH")
|
||||
helper = "helpers/" + result["helper"]["sha256"] + ".py"
|
||||
add(helper, matches[0])
|
||||
for name, data in sorted(raw.items()):
|
||||
add("cargo/" + run + "/" + name, data)
|
||||
specs[run] = {key: result[key] for key in ("command", "head", "exitCode", "inputCount")}
|
||||
specs[run]["helper"] = helper
|
||||
if "source-proof.json" in raw:
|
||||
proof = parsed(raw["source-proof.json"])
|
||||
matches = [data for data in comparator_bytes if pin(data) == proof["comparator"]]
|
||||
need(bool(matches), "exact source comparator unavailable for " + run + "; supply --comparator PATH")
|
||||
comparator = "comparators/" + proof["comparator"]["sha256"] + ".py"
|
||||
add(comparator, matches[0])
|
||||
specs[run]["sourceProof"] = {"commit": proof["commit"], "pin": pin(raw["source-proof.json"]), "comparator": comparator}
|
||||
manifest = {"schema": 1, "kind": "prerelease37-connection-admission-cargo-gates", "runs": specs,
|
||||
"captureHelpers": helper_catalog,
|
||||
"logicalFiles": dict(sorted(logical.items())),
|
||||
"members": {name: pin(data) for name, data in sorted(payload.items())}}
|
||||
summary = verify_data(manifest, payload.__getitem__)
|
||||
return payload, manifest, summary
|
||||
|
||||
|
||||
def write_json(path, value):
|
||||
with path.open("xb") as output:
|
||||
output.write((json.dumps(value, indent=2) + "\n").encode("utf-8"))
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--root", type=Path, required=True, help="checkout holding .local/prerelease37-gates")
|
||||
parser.add_argument("--runs", nargs="+", required=True, help="explicit run names, including failures to retain")
|
||||
parser.add_argument("--helper", type=Path, action="append", default=[], help="additional exact historical helper (repeatable)")
|
||||
parser.add_argument("--comparator", type=Path, action="append", default=[], help="additional exact historical source comparator (repeatable)")
|
||||
parser.add_argument("--output", type=Path, help="fresh, nonexistent package directory (required unless --check-only)")
|
||||
parser.add_argument("--check-only", action="store_true", help="validate raw captures in memory without creating an archive")
|
||||
args = parser.parse_args()
|
||||
need(all(run_name(run) for run in args.runs) and len(args.runs) == len(set(args.runs)), "unique safe run names required")
|
||||
need(args.check_only or args.output is not None, "--output is required unless --check-only")
|
||||
if not args.check_only:
|
||||
need(not args.output.exists(), "output must be a fresh directory: " + str(args.output))
|
||||
payload, manifest, summary = collect(args.root.resolve(), args.runs, args.helper, args.comparator)
|
||||
if args.check_only:
|
||||
print("PASS raw consistency; no package written:", summary)
|
||||
return
|
||||
out = args.output.resolve()
|
||||
out.mkdir(parents=True, exist_ok=False)
|
||||
for name in ("evidence.zip", "manifest.json", "files.json"):
|
||||
need(not (out / name).exists(), "existing package output refused: " + name)
|
||||
for name in ("prepare.py", "verify.py", "README.md"):
|
||||
data = (HERE / name).read_bytes()
|
||||
destination = out / name
|
||||
if destination.exists():
|
||||
need(destination.read_bytes() == data, "different existing package tool: " + name)
|
||||
else:
|
||||
with destination.open("xb") as output:
|
||||
output.write(data)
|
||||
with zipfile.ZipFile(out / "evidence.zip", "x", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as archive:
|
||||
for name, data in sorted(payload.items()):
|
||||
info = zipfile.ZipInfo(name, date_time=(2026, 9, 23, 0, 0, 0))
|
||||
info.compress_type = zipfile.ZIP_DEFLATED
|
||||
info.external_attr = 0o100644 << 16
|
||||
archive.writestr(info, data)
|
||||
write_json(out / "manifest.json", manifest)
|
||||
names = ("prepare.py", "verify.py", "README.md", "manifest.json", "evidence.zip")
|
||||
write_json(out / "files.json", {name: pin((out / name).read_bytes()) for name in names})
|
||||
need(verify_package(out) == summary, "written package differs from validated captures")
|
||||
print("PASS package readback:", out, summary)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,38 @@
|
||||
Command: C:\Users\User\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\python.exe -B .local/prerelease37-evidence-tools/selfcheck.py --root . --runs connection-focused-1 connection-format-1 connection-focused-2 connection-format-2 allocation-regressions-1 connection-regressions-1 connection-lint-1 --output .local/prerelease37-evidence-tools/selfcheck-initial-publication
|
||||
Exit: 0
|
||||
|
||||
STDOUT
|
||||
connection-focused-1 recorded exit 101 inputs 755 executable records 0
|
||||
connection-format-1 recorded exit 1 inputs 755 executable records 0
|
||||
connection-focused-2 recorded exit 0 inputs 755 executable records 1
|
||||
connection-format-2 recorded exit 0 inputs 755 executable records 0
|
||||
allocation-regressions-1 recorded exit 0 inputs 755 executable records 1
|
||||
connection-regressions-1 recorded exit 0 inputs 755 executable records 1
|
||||
connection-lint-1 recorded exit 0 inputs 755 executable records 0
|
||||
connection-focused-1 recorded exit 101 inputs 755 executable records 0
|
||||
connection-format-1 recorded exit 1 inputs 755 executable records 0
|
||||
connection-focused-2 recorded exit 0 inputs 755 executable records 1
|
||||
connection-format-2 recorded exit 0 inputs 755 executable records 0
|
||||
allocation-regressions-1 recorded exit 0 inputs 755 executable records 1
|
||||
connection-regressions-1 recorded exit 0 inputs 755 executable records 1
|
||||
connection-lint-1 recorded exit 0 inputs 755 executable records 0
|
||||
PASS package readback: C:\ar-multiselect-9e8r6t\.local\prerelease37-evidence-tools\selfcheck-initial-publication {'runs': 7, 'recordedFailures': 2, 'logicalFiles': 57, 'storedMembers': 47, 'sourceCapsulePairs': 2, 'captureHelpers': {'gate.py': 'helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py', 'source-proof.py': 'helpers/58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119.py'}, 'sourceProofs': 5}
|
||||
connection-focused-1 recorded exit 101 inputs 755 executable records 0
|
||||
connection-format-1 recorded exit 1 inputs 755 executable records 0
|
||||
connection-focused-2 recorded exit 0 inputs 755 executable records 1
|
||||
connection-format-2 recorded exit 0 inputs 755 executable records 0
|
||||
allocation-regressions-1 recorded exit 0 inputs 755 executable records 1
|
||||
connection-regressions-1 recorded exit 0 inputs 755 executable records 1
|
||||
connection-lint-1 recorded exit 0 inputs 755 executable records 0
|
||||
connection-focused-1 recorded exit 101 inputs 755 executable records 0
|
||||
connection-format-1 recorded exit 1 inputs 755 executable records 0
|
||||
connection-focused-2 recorded exit 0 inputs 755 executable records 1
|
||||
connection-format-2 recorded exit 0 inputs 755 executable records 0
|
||||
allocation-regressions-1 recorded exit 0 inputs 755 executable records 1
|
||||
connection-regressions-1 recorded exit 0 inputs 755 executable records 1
|
||||
connection-lint-1 recorded exit 0 inputs 755 executable records 0
|
||||
PASS original raw/optional-proof/helper archive equality, existing-output refusal, and corruption rejection: ['raw stream/receipt mismatch', 'helper bytes/name mismatch', 'run/result exit mismatch', 'input archive mapping mismatch', 'missing closed receipt', 'source proof run relationship', 'source proof commit relationship', 'source proof result relationship', 'source proof inputCount relationship', 'missing declared optional source proof', 'source comparator bytes mismatch']
|
||||
Historical captured failures remain failures: 2
|
||||
No Cargo, archived code, or GUI executed.
|
||||
|
||||
STDERR
|
||||
@@ -0,0 +1,194 @@
|
||||
"""Offline consistency checks only; never execute or extract archived inputs."""
|
||||
from pathlib import Path, PurePosixPath
|
||||
from datetime import datetime
|
||||
import argparse
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import math
|
||||
import re
|
||||
import stat
|
||||
import zipfile
|
||||
|
||||
RAW_FILES = {"started.json", "process.json", "result.json", "stdout.log", "stderr.log", "inputs.json", "inputs.zip"}
|
||||
OUTER_FILES = {"prepare.py", "verify.py", "README.md", "manifest.json", "evidence.zip"}
|
||||
|
||||
|
||||
def need(condition, message):
|
||||
if not condition:
|
||||
raise ValueError(message)
|
||||
|
||||
|
||||
def pin(data):
|
||||
return {"bytes": len(data), "sha256": hashlib.sha256(data).hexdigest()}
|
||||
|
||||
|
||||
def valid_pin(value):
|
||||
return (isinstance(value, dict) and set(value) == {"bytes", "sha256"}
|
||||
and type(value["bytes"]) is int and value["bytes"] >= 0
|
||||
and isinstance(value["sha256"], str)
|
||||
and re.fullmatch(r"[0-9a-f]{64}", value["sha256"]) is not None)
|
||||
|
||||
|
||||
def safe(name):
|
||||
return (isinstance(name, str) and bool(name) and "\\" not in name and ":" not in name
|
||||
and all(ord(char) >= 32 for char in name)
|
||||
and not PurePosixPath(name).is_absolute()
|
||||
and all(part not in ("", ".", "..") for part in name.split("/")))
|
||||
|
||||
|
||||
def run_name(name):
|
||||
return isinstance(name, str) and re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]*", name) is not None
|
||||
|
||||
|
||||
def parsed(data):
|
||||
def pairs(items):
|
||||
result = {}
|
||||
for key, value in items:
|
||||
need(key not in result, "duplicate JSON key: " + key)
|
||||
result[key] = value
|
||||
return result
|
||||
def constant(value):
|
||||
raise ValueError("nonfinite JSON constant: " + value)
|
||||
return json.loads(data, object_pairs_hook=pairs, parse_constant=constant)
|
||||
|
||||
|
||||
def inventory(archive, expected):
|
||||
names = archive.namelist()
|
||||
need(len(names) == len(set(names)) and set(names) == set(expected), "exact ZIP membership")
|
||||
for entry in archive.infolist():
|
||||
need(safe(entry.filename) and not entry.is_dir(), "unsafe ZIP member: " + entry.filename)
|
||||
need(not stat.S_ISLNK(entry.external_attr >> 16), "ZIP symlink: " + entry.filename)
|
||||
need(valid_pin(expected[entry.filename]), "invalid member pin: " + entry.filename)
|
||||
need(entry.file_size == expected[entry.filename]["bytes"], "ZIP recorded size: " + entry.filename)
|
||||
|
||||
|
||||
def ordered_times(*values):
|
||||
times = [datetime.fromisoformat(value) for value in values]
|
||||
return all(value.utcoffset() is not None for value in times) and times == sorted(times)
|
||||
|
||||
|
||||
def verify_data(manifest, read):
|
||||
"""Shared read-only verifier for packaged bytes or prepare's in-memory payload."""
|
||||
need(manifest["schema"] == 1 and manifest["kind"] == "prerelease37-connection-admission-cargo-gates", "manifest schema")
|
||||
runs, logical, stored = manifest["runs"], manifest["logicalFiles"], manifest["members"]
|
||||
helpers = manifest["captureHelpers"]
|
||||
need(isinstance(helpers, dict) and set(helpers) == {"gate.py", "source-proof.py"}, "capture helper names")
|
||||
need(all(isinstance(name, str) and re.fullmatch(r"helpers/[0-9a-f]{64}\.py", name)
|
||||
for name in helpers.values()), "capture helper mapping")
|
||||
need(isinstance(runs, dict) and bool(runs) and all(run_name(run) for run in runs), "explicit run inventory")
|
||||
need(all(safe(name) for name in logical) and all(safe(name) for name in stored), "unsafe manifest path")
|
||||
need(set(logical.values()) == set(stored), "unreferenced or absent stored members")
|
||||
for name, expected in stored.items():
|
||||
need(valid_pin(expected) and pin(read(name)) == expected, "stored member hash: " + name)
|
||||
expected_logical = {"cargo/" + run + "/" + name for run in runs for name in RAW_FILES}
|
||||
expected_logical.update(spec["helper"] for spec in runs.values())
|
||||
expected_logical.update(helpers.values())
|
||||
for run, spec in runs.items():
|
||||
if "sourceProof" in spec:
|
||||
proof_spec = spec["sourceProof"]
|
||||
need(set(proof_spec) == {"commit", "pin", "comparator"}, run + ": exact source proof spec")
|
||||
need(isinstance(proof_spec["commit"], str) and re.fullmatch(r"[0-9a-f]{40}", proof_spec["commit"]), run + ": named full source commit")
|
||||
need(valid_pin(proof_spec["pin"]), run + ": source proof pin")
|
||||
need(isinstance(proof_spec["comparator"], str) and re.fullmatch(r"comparators/[0-9a-f]{64}\.py", proof_spec["comparator"]), run + ": comparator path")
|
||||
expected_logical.update({"cargo/" + run + "/source-proof.json", proof_spec["comparator"]})
|
||||
need(set(logical) == expected_logical, "exact logical raw/helper inventory")
|
||||
for name, destination in logical.items():
|
||||
if name.startswith("cargo/") and name.rsplit("/", 1)[1] in {"inputs.json", "inputs.zip"}:
|
||||
expected = "capsules/" + stored[destination]["sha256"] + PurePosixPath(name).suffix
|
||||
need(destination == expected, "input capsule mapping: " + name)
|
||||
else:
|
||||
need(destination == name, "only input maps/archives may be deduplicated: " + name)
|
||||
def raw(name):
|
||||
return read(logical[name])
|
||||
for name, helper in helpers.items():
|
||||
need(helper == "helpers/" + pin(raw(helper))["sha256"] + ".py", "named helper hash: " + name)
|
||||
seen_capsules = set()
|
||||
for run, spec in runs.items():
|
||||
prefix = "cargo/" + run + "/"
|
||||
start = parsed(raw(prefix + "started.json"))
|
||||
process = parsed(raw(prefix + "process.json"))
|
||||
result = parsed(raw(prefix + "result.json"))
|
||||
required_start = {"start", "command", "cwd", "head", "scope", "inputs", "archive",
|
||||
"inputCount", "environment", "helper", "cargo", "rustc"}
|
||||
need(required_start <= start.keys(), run + ": incomplete start receipt")
|
||||
need(all(result.get(key) == value for key, value in start.items()), run + ": start/result relationship")
|
||||
command = result["command"]
|
||||
need(isinstance(command, list) and command and command[0] == "cargo"
|
||||
and all(isinstance(arg, str) for arg in command), run + ": Cargo command")
|
||||
need(command == process["command"] == spec["command"], run + ": command relationship")
|
||||
need(result["head"] == spec["head"] and re.fullmatch(r"(?:[0-9a-f]{40}|[0-9a-f]{64})", result["head"]), run + ": HEAD")
|
||||
need(type(result["exitCode"]) is int and result["exitCode"] == spec["exitCode"], run + ": recorded exit")
|
||||
need(result["inputsUnchanged"] is True, run + ": changed inputs refused")
|
||||
need(type(result["seconds"]) in (int, float) and math.isfinite(result["seconds"])
|
||||
and result["seconds"] >= 0, run + ": duration")
|
||||
need(type(process["pid"]) is int and process["pid"] > 0, run + ": process identity")
|
||||
need(ordered_times(start["start"], process["observed"], result["finish"]), run + ": completed timestamp order")
|
||||
for field, filename in [("inputs", "inputs.json"), ("archive", "inputs.zip"), ("stdout", "stdout.log"), ("stderr", "stderr.log")]:
|
||||
need(result[field] == pin(raw(prefix + filename)), run + ": " + field + " pin")
|
||||
need(valid_pin(result["helper"]) and result["helper"] == pin(raw(spec["helper"])), run + ": helper pin")
|
||||
need(spec["helper"] == "helpers/" + result["helper"]["sha256"] + ".py", run + ": helper mapping")
|
||||
inputs = parsed(raw(prefix + "inputs.json"))
|
||||
need(isinstance(inputs, dict) and all(safe(name) for name in inputs), run + ": safe input inventory")
|
||||
need(type(result["inputCount"]) is int and len(inputs) == result["inputCount"] == spec["inputCount"], run + ": input counts")
|
||||
if "sourceProof" in spec:
|
||||
proof_spec = spec["sourceProof"]
|
||||
proof_bytes = raw(prefix + "source-proof.json")
|
||||
proof = parsed(proof_bytes)
|
||||
need(pin(proof_bytes) == proof_spec["pin"], run + ": source proof pinned bytes")
|
||||
need(proof["run"] == run and proof["commit"] == proof_spec["commit"], run + ": proof run/commit identity")
|
||||
need(proof["status"] == "PASS exact captured input inventory and Git blob bytes", run + ": source proof status")
|
||||
need(proof["inputCount"] == len(inputs) and proof["inputMap"] == result["inputs"], run + ": proof input relationship")
|
||||
need(proof["result"] == pin(raw(prefix + "result.json")), run + ": proof result relationship")
|
||||
need(ordered_times(result["finish"], proof["verified"]), run + ": proof time after closed gate")
|
||||
comparator = proof_spec["comparator"]
|
||||
need(valid_pin(proof["comparator"]) and pin(raw(comparator)) == proof["comparator"], run + ": comparator bytes")
|
||||
need(comparator == "comparators/" + proof["comparator"]["sha256"] + ".py", run + ": comparator identity")
|
||||
need(sum(name.startswith("crates/vendor/iced_widget/") for name in inputs) == 71, run + ": vendor count")
|
||||
capsule = (logical[prefix + "inputs.json"], logical[prefix + "inputs.zip"])
|
||||
if capsule not in seen_capsules:
|
||||
with zipfile.ZipFile(io.BytesIO(raw(prefix + "inputs.zip"))) as source:
|
||||
inventory(source, inputs)
|
||||
for name, expected in inputs.items():
|
||||
need(pin(source.read(name)) == expected, run + ": source hash " + name)
|
||||
seen_capsules.add(capsule)
|
||||
artifacts = []
|
||||
for line in raw(prefix + "stdout.log").decode("utf-8", errors="replace").splitlines():
|
||||
try:
|
||||
value = json.loads(line)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
if isinstance(value, dict) and value.get("reason") == "compiler-artifact" and value.get("executable"):
|
||||
artifacts.append(value)
|
||||
need(isinstance(result["artifacts"], list), run + ": artifact records")
|
||||
need(artifacts == [record["cargo"] for record in result["artifacts"]], run + ": Cargo JSON artifact relationship")
|
||||
for record in result["artifacts"]:
|
||||
need(set(record) == {"cargo", "readback"}
|
||||
and (record["readback"] is None or valid_pin(record["readback"])), run + ": artifact readback pin")
|
||||
print(run, "recorded exit", result["exitCode"], "inputs", len(inputs), "executable records", len(artifacts))
|
||||
return {"runs": len(runs), "recordedFailures": sum(spec["exitCode"] != 0 for spec in runs.values()),
|
||||
"logicalFiles": len(logical), "storedMembers": len(stored), "sourceCapsulePairs": len(seen_capsules),
|
||||
"captureHelpers": helpers, "sourceProofs": sum("sourceProof" in spec for spec in runs.values())}
|
||||
|
||||
|
||||
def verify_package(root):
|
||||
files = parsed((root / "files.json").read_bytes())
|
||||
need(set(files) == OUTER_FILES, "outer file inventory")
|
||||
for name, expected in files.items():
|
||||
need(valid_pin(expected) and pin((root / name).read_bytes()) == expected, "outer hash: " + name)
|
||||
manifest = parsed((root / "manifest.json").read_bytes())
|
||||
with zipfile.ZipFile(root / "evidence.zip") as archive:
|
||||
inventory(archive, manifest["members"])
|
||||
return verify_data(manifest, archive.read)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("package", nargs="?", type=Path, default=Path(__file__).resolve().parent)
|
||||
args = parser.parse_args()
|
||||
print("PASS offline consistency:", verify_package(args.package))
|
||||
print("Historical receipts only; no source/binary execution or native acceptance claim.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,7 @@
|
||||
# Interrupted route compilation
|
||||
|
||||
This is an exact archive of the six partial files left by route-regressions-1 when the task was interrupted. No result.json, process exit or test result was observed. The external observation in receipt.json was recorded after resume; it does not rewrite the original gate receipts. The interrupted run must not be counted as passed or as a known compiler failure.
|
||||
|
||||
The input map matches the independently completed route-format-1 capture for source6f247af. A separate restarted run (route-regressions-2) subsequently exposed a missing test-helper qualifier; that later compiler failure does not retroactively establish this interrupted process's exit.
|
||||
|
||||
Archive creation read back every entry and compared it byte-for-byte with the retained original. receipt.json pins the complete member inventory and ZIP. These checks establish preserved bytes, not execution success.
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"run": "route-regressions-1",
|
||||
"observed": "2026-09-24T06:26:51.595717+00:00",
|
||||
"status": "interrupted before result receipt; no exit code or test outcome established",
|
||||
"observation": "After user interruption/resume, original unified session was unknown and Win32 process inventory contained no cargo/rustc/python/clippy driver. Raw partial files retained unchanged.",
|
||||
"files": {
|
||||
"inputs.json": {
|
||||
"bytes": 126743,
|
||||
"sha256": "db306addb9c9012a9f928210bd67615e0245ac3c59e32abba8412e8c7d5265e9"
|
||||
},
|
||||
"inputs.zip": {
|
||||
"bytes": 3043591,
|
||||
"sha256": "4a0ae23e1599f7647c5b62eedfbac7c1d714362990b957aa59553fd363e46bbe"
|
||||
},
|
||||
"process.json": {
|
||||
"bytes": 198,
|
||||
"sha256": "72a94cab687f14ec3b1783815b4c37e1d673e84eb916d6dc8cae8c9171e7e13f"
|
||||
},
|
||||
"started.json": {
|
||||
"bytes": 1590,
|
||||
"sha256": "63344e4bd93a43122bd0b5608381141321e5204945d166ca82857971a57aeb3c"
|
||||
},
|
||||
"stderr.log": {
|
||||
"bytes": 76,
|
||||
"sha256": "9ff4708671bbf79cd3ed1dee3cafa7e469f39da63407423fc8f2b42b6f4c4f73"
|
||||
},
|
||||
"stdout.log": {
|
||||
"bytes": 294729,
|
||||
"sha256": "5ffb2106f188ee05ebab10c8096f8959cef4e7010eff877b4d2591d7a9c62237"
|
||||
}
|
||||
},
|
||||
"archive": {
|
||||
"file": "route-regressions-1.zip",
|
||||
"bytes": 3004097,
|
||||
"sha256": "9eaed652053596f940406460a02d23f87045bf9c45ac5af748fb5daef59bd9c2"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
+79
@@ -0,0 +1,79 @@
|
||||
[
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "harness/prerelease-integrated-source-20260924.md",
|
||||
"gitBlob": "898bf45550a8c8c449c6b1989cabbf534575a27f",
|
||||
"bytes": 5288,
|
||||
"sha256": "8e6eb6333c47850dff04185972c8c93ce5ddf4b0f2b591bc3d4fc59a6b878e48"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "harness/reviews/prerelease37-integration-20260924.md",
|
||||
"gitBlob": "dc6e6b6036ab587d7e7cec59b8c762ac404efc1b",
|
||||
"bytes": 8866,
|
||||
"sha256": "4f13043fc653d50a18e043438d919cdf32db63454e048a4c61ab3b4a473808ea"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "crates/oa-desktop/src/app.rs",
|
||||
"gitBlob": "646b0415a156b3a4e8074847c5b2dadfea24b27c",
|
||||
"bytes": 292471,
|
||||
"sha256": "d2296118db22fc51554273a7ec84421c83793b769b87c65a85a2a6d61c7a16b5"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "crates/oa-desktop/src/app/allocation_drag.rs",
|
||||
"gitBlob": "b6c329496c1c935defee6f6cd85c281c3556e4a2",
|
||||
"bytes": 42262,
|
||||
"sha256": "26934501d8a95a6d196bdf477eed00c68545c98921744b181613c0b1f9d95c36"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "crates/oa-desktop/src/connection_tool.rs",
|
||||
"gitBlob": "10910961860ebf528edf4b159b59a62c0a500d75",
|
||||
"bytes": 28840,
|
||||
"sha256": "43cb9fb4c0da31a28c2a59ef8bbf5a66989b4ab58cb532a237e9595b34ebe5eb"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "crates/oa-desktop/src/connection_allocation_tests.rs",
|
||||
"gitBlob": "2f8c93e6154db2421b9f39b5e045866875ff7612",
|
||||
"bytes": 20135,
|
||||
"sha256": "33e8b7d238240f40be0fe64e796f8af4d8d3e6945315e07b70bc553fe45ea15b"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "crates/oa-desktop/src/diagram/multiselect.rs",
|
||||
"gitBlob": "5f95b92c50ab4b0e521b801a9f74b9ed223dbaf9",
|
||||
"bytes": 11124,
|
||||
"sha256": "8cb5d7866fa092ecbcf7355ffc41bd060666d271fccd0bdafea68ae442cde78f"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "crates/oa-desktop/src/app/multiselect_tests.rs",
|
||||
"gitBlob": "0c71dec48ce6ad5e625f941c4b2638bb557bcc72",
|
||||
"bytes": 31304,
|
||||
"sha256": "09eea1403af1e4804dcb3ab863ad110095f451d9b1957dcdbf859fe7dd833abd"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "crates/oa-desktop/src/app/allocation_drag_tests/navigation_union.rs",
|
||||
"gitBlob": "7bce1ba559289986f9c92376669571b62d490e79",
|
||||
"bytes": 7247,
|
||||
"sha256": "9962cd4e11efb586b55c9a5f78b9df26c4316b6df3a0b01fd3587ee89e4c5900"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "crates/oa-desktop/src/app/process_multiselect/tests.rs",
|
||||
"gitBlob": "dec1498584c97b38317c6397ed937d1e62512cee",
|
||||
"bytes": 42929,
|
||||
"sha256": "22ead59676c53877d58339e7fe31ba548231610f8c12dcb1f07ef550e0680d90"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "crates/oa-desktop/src/app/allocation_drag_tests/group.rs",
|
||||
"gitBlob": "eb894abbfe0db1687999be72bb10a73d26268610",
|
||||
"bytes": 34325,
|
||||
"sha256": "8bf8d57dbc07978934ab46f39058c71061059f232c6868394914a74f18f39c59"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,41 @@
|
||||
# Candidate completed-Text-Apply review
|
||||
|
||||
2026-09-24. Bounded read-only review of fetched files for candidate `6ae75fb2ccaae16c29004444815ddeaeab2e75ac`, under `.local/prerelease37/source-6ae75fb2ccaae16c29004444815ddeaeab2e75ac/`. This is a nine-file partial source retrieval with per-file commit/blob/hash receipts, not a complete checkout. Identity verification was reported by the parent; this review read the receipts and source without performing another fetch. No Cargo, format/test/build gate, Git/Gitea, GUI or production mutation was performed. This report is the only file written.
|
||||
|
||||
## Result
|
||||
|
||||
No new material production defect found in the examined completed-Text-Apply path or dispatch ordering. The ordinary/State test is a meaningful concrete integration evaluator. **The retrieved Allocation `navigation_union.rs` does not contain a completed Text Apply evaluator**, so the two files must not be described as proving that workflow across all group adapters.
|
||||
|
||||
## What the ordinary/State test establishes
|
||||
|
||||
`crates/oa-desktop/src/app/multiselect_tests.rs:197` captures the original group, navigates through the real Text page, selects the declaration's actual source file, edits the source editor using SelectAll/Paste, and dispatches `Message::ApplySource`. It asserts that the dirty edit initially leaves the committed Document unchanged, then asserts a literal full expected Project and exact edited SourceBundle after Apply. The expected change is constructed independently as one element name replacement; unrelated diagrams/routes and opposite-layer records remain part of the complete-model oracle.
|
||||
|
||||
Candidate `app.rs:4027` dispatches to `apply_source`; `app.rs:901` compiles the source workspace, calls `document.apply_compiled`, and synchronizes only after success. Thus this test does not substitute an ordinary model command or manually increment a revision for completed Text Apply.
|
||||
|
||||
The test returns to the ordinary or State surface, recreates the same exact membership and primary, verifies that the fresh snapshot is admitted, and dispatches old Move/Hide/Collapse/Refuse events. It checks complete Project/source/revision, the fresh selection snapshot, legacy inspector/placement identity, camera epochs/geometry, draft state, feedback, and the Allocation Arrange-arm sentinel. The early envelope at candidate `app.rs:1354` handles both DiagramSelection and StateDiagram::Selection before shared Allocation retirement at `app.rs:1535` and the ordinary update-inner side effects. The tests dispatch the shared DiagramSelection form even for State; this is a valid common App path, but is not a mounted State-canvas event test.
|
||||
|
||||
The literal Undo/Redo assertions at `multiselect_tests.rs:329` establish one Text Apply history entry and no hidden second edit from the stale packets. Source/model compilation is checked after Apply. The test deliberately distinguishes a clean completed source transaction from rejection caused merely by a still-dirty source draft.
|
||||
|
||||
This is not an isolated proof that the revision field alone rejects the packet: Page transitions and selection regeneration also make its lifetime stale. That is appropriate to the realistic workflow; the same file separately has field-specific envelope tests at line 438. Do not interpret the new test alone as mutation coverage for every Snapshot equality field. Also, its manually set Arrange arm is a side-effect sentinel, not evidence of retaining a real post-Text Allocation group press or preview.
|
||||
|
||||
## Allocation coverage boundary and next discriminating case
|
||||
|
||||
The fetched `app/allocation_drag_tests/navigation_union.rs` ends with two existing tests: `stale_gesture_packets_preserve_navigation_and_a_new_live_cancel_still_works` at line 104 and `old_chooser_envelopes_preserve_a_current_canvas_gesture` at line 170. They exercise actual captured Allocation packets across navigation/chooser lifetime changes, retain pending reveal/current gesture ownership, and prove that a current Cancel still works. Neither edits source nor calls ApplySource.
|
||||
|
||||
For the requested completed-Text boundary, a distinct evaluator still needs to capture old Allocation input, perform an actual source edit and successful Apply, return to the Allocation view and establish a fresh group owner/preview, then deliver old Preview/Release/Cancel/Refused packets. Require the newer token/preview/selection/model/source/history to remain exact and let the fresh group gesture complete with an independently expected whole Project and one additional history entry. The candidate's own `harness/reviews/prerelease37-integration-20260924.md` already lists this extension as pending; this review does not reopen a claimed completed test.
|
||||
|
||||
Treat old Press separately: candidate `app.rs:1448` intentionally routes a rejected Press to its diagnostic handler before shared boundaries, and `navigation_union.rs:119` expects `The view changed. Start this drag again.` while preserving ownership/navigation. A blanket unchanged-status oracle for old Press would contradict the current explicit contract. Other stale Allocation packets use the early `allocation_event_envelope` gate (`app.rs:1444`, `app/allocation_drag.rs:768`).
|
||||
|
||||
## Limits
|
||||
|
||||
The fetched set does not include all called selection/Document/source-workspace implementation modules or the complete combined test tree. This review therefore assesses the concrete fetched tests and visible dispatch order, supplemented only by the parent's stated retrieval identity. It establishes no combined compilation, passing tests, native behavior, packaged launch, full-source integration or prerelease acceptance. The separately reviewed Endpoint/Restart envelope correction is outside this task and is not re-evaluated here.
|
||||
|
||||
## Parent follow-up: additional identified Text Apply cases
|
||||
|
||||
The release owner identified two additional files in issue37 comment1194. The parent retrieved both at the same exact candidate6ae75fb and independently verified their API path/size and Git blob identities; the accompanying candidate-source-receipts.json now records eleven files. This resolves the earlier retrieval coverage boundary; it was not evidence of missing implementation.
|
||||
|
||||
`app/process_multiselect/tests.rs:148` actually edits and applies the source declaration, compares a literal complete Project and exact SourceBundle, returns to Process, establishes the same membership/primary with a fresh snapshot, and proves the intended current reorder would change the model. Replaying the old reorder must preserve the new state and leave one history entry belonging only to Text Apply. Both OA and SA are exercised.
|
||||
|
||||
`app/allocation_drag_tests/group.rs:194` captures an actual old group press, completes Text Apply with the literal model/source oracle, returns to the Allocation diagram and creates a new actual assignment preview. Old Press and Release must preserve the current press/preview identities, selection, camera, epochs, draft and exact model/source. It then undoes/redoes the Text edit; the current gesture intentionally remains a preview, so this case does not prove completing a post-Text assignment. Its old Press expectation is scoped to a newer active gesture and agrees with the diagnostic distinction above. Both OA and SA are exercised.
|
||||
|
||||
No material defect found in this additional readback. The parent reviewed source only; combined execution remains the release gate. The earlier proposed extra Preview/Cancel/Refused and surviving-gesture completion cases are possible broader coverage, not a claim that this candidate promised or passed them.
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
# Connection envelope review for prerelease 37
|
||||
|
||||
2026-09-24. Read-only review of current local source identified by the parent as `4421730`, including the early `App::update` guard and three new tests in `app/allocation_drag_tests/group/connection_lifetime.rs`. No Cargo, Git, Gitea, GUI or production changes. Build results are outside this review. This local report is the only artifact written.
|
||||
|
||||
## One material evaluator correction
|
||||
|
||||
**The tests do not independently exercise the live-route-context comparison.** In `crates/oa-desktop/src/app/allocation_drag_tests/group/connection_lifetime.rs:175`, each captured-field mismatch leaves `session.context == current` and changes `captured`. Therefore both `route_context() != Some(captured)` and `session.context != captured` are true. All these cases would still reject if the first guard predicate in `crates/oa-desktop/src/app.rs:1315` were removed. The missing-session tests also reject through the second predicate. The converse case at test line 201 correctly isolates the session-context predicate, because captured equals the current route context while the retained session differs.
|
||||
|
||||
Add one complementary case: retain a drawing session and its captured context, advance the current canvas epoch without rewriting the retained session, then deliver Endpoint and Restart with that old captured context. Explicitly assert `captured == connection.context` and `Some(captured) != route_context()` before using `assert_captured_noop`. This tests the first predicate independently and requires the full newer state to remain unchanged. The existing captured-field matrix remains useful for checking all Context fields, but is not a substitute for this isolated relationship.
|
||||
|
||||
## Code assessment
|
||||
|
||||
No material implementation defect found in the inspected guard. It returns before `reconcile_pending_diagram_reveal`, `allocation_before`, `source_interaction`, connection-session taking, or selection reconciliation. The two preceding computations (`process_preview_primary` and `multiselect_boundary`) read state. Endpoint and Restart must match both the live route context and the extant drawing session, including its document, diagram, revision and epoch.
|
||||
|
||||
Choose and Begin do not match the early guard. They still cross the intentional Allocation retirement boundary; Begin invokes the existing current-context Choose/Endpoint flow inside `edit_connection`. A current Restart passes and delegates to Choose, creating a fresh canvas epoch/session. The test at `connection_lifetime.rs:209` exercises Choose and Begin from a live Allocation preview, current Restart, and a completed endpoint pair.
|
||||
|
||||
The no-op helper compares Allocation epoch/arm, active press identity, preview and routed identity, feedback, exact selection, drawing session, camera/context, source-interaction counter, status, Document snapshot identity and source bundle. The OA/SA cancellation test then completes the retained group action and uses a literal whole-Project geometry expectation. The positive connection test checks relation kind/endpoints and whole-Project preservation except the new generated relation. Both use `group.rs:171` to verify reopen and exact source/model Undo/Redo with one history step. Those checks are appropriate to this bounded fix; this review does not claim broader prerelease integration or native acceptance.
|
||||
|
||||
## Follow-up readback: 72b1bb7
|
||||
|
||||
The parent reports correction source `72b1bb7`. Read back the two scoped edits without running any build/test/format gates. The earlier material evaluator finding is addressed: the test now advances `canvas_epoch`, explicitly asserts that the live route differs while the retained session equals the captured context, and sends both Endpoint and Restart through the full no-op assertion helper. Restoring `canvas_epoch = current.epoch` before the converse case keeps that case independent. Removing either guard predicate would now expose its corresponding isolated case.
|
||||
|
||||
The test module declaration now explicitly names `#[path = "group/connection_lifetime.rs"]`, matching the existing source file relative to `app/allocation_drag_tests/group.rs`. This resolves the inspected source-path ambiguity; actual module resolution/compilation and test success remain for the running gates. The parent reports retained initial Rust/format module-resolution failures; this follow-up does not erase them or claim fresh gates passed. No further material correction found in these two edits.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Final admission delivery review — prerelease 37
|
||||
|
||||
2026-09-24. Independent bounded read-only source/evidence review at contribution HEAD `e68a83959900e556768d8470640e28a7df7d8cb8` in `C:/ar-multiselect-9e8r6t`. This report is the reviewer's only written artifact. No Cargo, formatter, compiler, GUI, network request, Git mutation or archived-source execution was performed.
|
||||
|
||||
## Result
|
||||
|
||||
No material finding in the final contribution report, completed-run package, source pins or preserved failure/interruption boundaries. This is acceptance of the scoped evidence delivery, not the combined prerelease, a native workflow, a packaged executable or publication availability.
|
||||
|
||||
## Source readback
|
||||
|
||||
- The final `e68a839` commit changes exactly one test-helper reference, from `project_view` to `projection::project_view`. Production admission guards remain those reviewed at `6f247af`: Connection Endpoint/Restart requires current context and matching extant drawing-session context; Route Commit/CommitAttachment requires current context and the selected edge. Both reject before shared Allocation retirement.
|
||||
- Read back the prior independent reviews and their addressed evaluator/selected-edge findings. The six Route cases retain separate context and selected-edge discriminators, successful current edits and literal route/placement/history expectations. This review found no additional scoped correction and does not substitute for broader integration review.
|
||||
|
||||
## Independently verified evidence
|
||||
|
||||
- Read all seven completed result receipts, stream/capsule hashes and source proofs. Five final gates bind to `e68a839`; the historical successful format and failed compile bind to `6f247af`. Every capture has 756 unchanged inputs.
|
||||
- Direct independent `git cat-file --batch` comparison of both distinct source capsules verified all 756 archived inputs byte-for-byte against each named source, including each manifest size/hash. These comparisons did not execute the archived comparator.
|
||||
- Ran the final package's read-only standalone verifier with Python `-B`: PASS, seven runs, one recorded failure, seven source proofs, 59 logical files, 49 stored members and two distinct source capsule pairs.
|
||||
- Independently compared every packaged logical raw file to its retained original. The final archive is exactly 6,094,831 bytes, SHA-256 `501f01c7279bc9d719151abe9b729769dd0078678c702448671356e91965015f`.
|
||||
- Read the actual test streams: Route 31, Allocation 51, attachment 28 passed, each with zero failures. The Allocation output names all nine new cases (three Connection, six Route) as passing. Final format and strict all-targets desktop Clippy receipts have exit 0. The report correctly warns that overlapping filters cannot be summed into a unique-test total.
|
||||
- The historical `route-regressions-2` stream contains E0425 for unqualified `project_view`, exit 101 and no test result. Its raw HEAD remains `c655c5eeaaa32c1c21074a7543844ae99a1d6fd5`, the documentation-only descendant of `6f247af`; the report explicitly distinguishes that HEAD from the equal-input source proof.
|
||||
- Independently verified the separate interrupted archive's complete six-member inventory, hashes and byte equality with retained partial originals. Its input map equals historical `route-format-1`; no final receipt or exit is inferred. It remains outside the seven completed runs.
|
||||
- Read the retained `route-checks/selfcheck-final.txt`: command, stdout, empty stderr and exit 0 record original-byte equality, existing-output refusal and eleven named corruption rejections. This supplemental transcript is separate from the package's standard pinned file inventory, as in the initial evidence delivery.
|
||||
|
||||
## Claim boundaries
|
||||
|
||||
The updated top-level report keeps initial 755-input Connection evidence at `72b1bb7` separate from final 756-input evidence, preserves the earlier failures and interrupted run, and identifies the one-line test correction. It does not promote archive consistency into fresh tests, authenticated/hermetic compilation, full workspace acceptance or native/package acceptance. The recorded initial Gitea transport receipt matches the stated 6,087,017-byte initial archive hash and 26.032-second transfer; this reviewer did not independently repeat that network transfer. Earlier Allocation native evidence remains pinned to its earlier source. Combined-source checks, Windows native verification and separate-machine downloaded-package launch remain the release owner's distinct acceptance work.
|
||||
@@ -0,0 +1,38 @@
|
||||
# Route envelope independent review
|
||||
|
||||
2026-09-24. Read-only review of the parent's initially frozen, uncommitted Route guard and four `group/route_lifetime.rs` tests in `C:/ar-multiselect-9e8r6t`. Production scope is the extra captured Commit/CommitAttachment guard in `app.rs`; test registration uses the explicit `group/route_lifetime.rs` path in `app/allocation_drag_tests/group.rs`. No Cargo, formatting, Git/Gitea, GUI or production edits were performed by this reviewer. This report is the only file written. Compilation and native behavior remain outside the review.
|
||||
|
||||
## Initial material finding: same context, different selected edge
|
||||
|
||||
The initial guard at `crates/oa-desktop/src/app.rs:1323` rejects only a Route Context mismatch. A change in selected target is a separate ownership boundary that does not necessarily change that Context.
|
||||
|
||||
Concrete counterexample for both Commit and CommitAttachment:
|
||||
|
||||
1. Use the routed fixture; select its valid edge and capture the proposed route packet and current Context.
|
||||
2. Without Escape or a model/camera mutation, select the two group placements and arm Arrange. `inspect_multiselect_primary` (`app/multiselect.rs:286`) changes `self.selected` directly and does not advance `canvas_epoch`; therefore the captured Context can still equal `route_context()`.
|
||||
3. Deliver the old edge packet, first while Arrange is armed, and separately after a fresh group press/motion establishes an active preview.
|
||||
4. The initial context-only guard admits it. `allocation_before` (`app/allocation_drag.rs:686`) retires the newer Allocation arm/token/preview. The later `edit_route` then finds no projected edge matching the now-selected work object (`app.rs:4745`) and returns, after the damage.
|
||||
|
||||
The original negative helper `stale_route_preserves_new_group` calls Escape before selecting the group, so its explicit epoch mismatch masks this case. This is a source-demonstrated control-flow defect; it was not runtime-tested by this reviewer.
|
||||
|
||||
Recommended bounded correction: bind the route payload in the early pattern and reject if **Context differs OR payload.edge differs from self.selected**, before shared boundaries. No drawing session or selected-attachment requirement belongs here: current route edits are valid without them. Add the same-context changed-selection case for both route event variants, explicitly asserting captured Context equality and changed selected identity, then verify both the armed and live-preview stages remain intact and the current group still completes.
|
||||
|
||||
The parent acknowledged this finding and requested the correction. Follow-up readback and gate status must be recorded separately rather than treating the initial guard as accepted.
|
||||
|
||||
## Other reviewed behavior
|
||||
|
||||
- Guard placement is correct for rejected envelopes: it precedes pending-reveal reconciliation, Allocation retirement, source interaction, legacy route handlers and later selection reconciliation. The two preceding App computations read state.
|
||||
- The context field tests independently vary document/diagram/revision/epoch, advance the live epoch with a fixed capture, and test absent active diagram. They do not rely on a missing route session to reject; Route has no equivalent to Connection session ownership.
|
||||
- The positive test exercises both current Commit and current CommitAttachment with no Connection session and no armed attachment. It requires a revision increment, preserved selected edge, the expected bend selection, and a literal complete expected Project. Commit changes only the pin to `(750,450)`; CommitAttachment changes only the source attachment to Top/0.25. Existing label, target attachment, unrelated routes/model records remain protected by whole-Project equality.
|
||||
- The completed Arrange oracle is independently authored: selected performer position becomes `(500,100)`, its carried work becomes `(540,200)`, and the other selected work becomes `(540,600)`. Both route endpoint placements move by `(400,0)`, so the original saved pin `(700,400)` must become `(1100,400)` exactly once. The expected label and endpoint attachment fields remain inherited from the unchanged baseline. This is the correct literal pin oracle; leaving `(700,400)` would contradict simultaneous two-endpoint movement.
|
||||
- `exact_history` verifies the whole expected Project, source reopen, one Undo to exact original Project/source, and one Redo to exact completed Project/source with no additional undo/redo entries. The positive expected Project is not cloned from the observed output or computed by the route planner.
|
||||
- The no-op helper checks exact selection/primary, context/camera, Allocation epoch/arm, active press Arc, preview Arc, feedback, handle state, source-interaction counter, status, Document snapshot Arc and source bundle. Together with successful completion/history, these are meaningful checks for the intended early-rejection fix.
|
||||
|
||||
No other material correction found in the initial bounded review. This does not establish broader route-session ABA semantics, complete prerelease integration, passing gates or native acceptance.
|
||||
## Follow-up: corrected frozen source readback
|
||||
|
||||
Read back the corrected, still-uncommitted guard and six registered tests. The material finding above is addressed: the early pattern binds `changed` for both Commit variants and rejects `route_context() != Some(captured) || changed.edge != self.selected`. It retains current route semantics without inventing a session/armed-handle requirement.
|
||||
|
||||
The two separately named `same_context_route_commit_after_selection_preserves_arrange_arm_and_live_group` and `same_context_route_attachment_after_selection_preserves_arrange_arm_and_live_group` tests execute the helper without Escape. Before delivering the old packet at both the armed and live-preview stages, they explicitly assert that the captured Context still equals the live Context and that the selected object differs from the packet edge. They then use the full no-op checks and complete the retained Arrange with the literal whole-Project pin/placement and history oracle. These cases discriminate the new edge predicate; the existing Context cases keep the edge selected and discriminate Context separately. Positive session-free route edits remain covered with independently authored expected route fields.
|
||||
|
||||
No further material correction found in this bounded readback. The initial finding is source-addressed, not a claim that compilation, tests, formatting or native gates passed. This reviewer ran none of those gates.
|
||||
@@ -0,0 +1,24 @@
|
||||
# Prerelease 37 Connection admission captured-gate evidence tools
|
||||
|
||||
Adapted from the reviewed Allocation prepare/verify/selfcheck tools. Read-only preparation consumes explicitly selected **closed** runs in CHECKOUT/.local/prerelease37-gates. No Cargo, source execution, checkout, GUI, network or archived helper execution occurs.
|
||||
|
||||
```powershell
|
||||
python -B prepare.py --root CHECKOUT --runs connection-focused-1 connection-format-1 --check-only
|
||||
```
|
||||
|
||||
Keep failed connection-focused-1 and connection-format-1: verification preserves their recorded failures rather than converting archive consistency into test success. The example selects those two historical failures. Later closed Connection admission runs can be explicitly listed with the same interface; the final package manifest is authoritative for its exact inventory. No implicit discovery includes active directories.
|
||||
|
||||
To create a portable package, replace --check-only with `--output NEW_DIRECTORY`. Existing output refuses. The exact seven raw files are required, with only `source-proof.json` accepted as an optional eighth file. No raw byte normalization or receipt rewriting occurs. Maps and nested input ZIPs deduplicate by SHA-256; other logical raw files keep their names. All original helper bytes, failed-run streams and optional proof bytes remain archived. The prerelease37 Connection admission manifest kind distinguishes this evidence from Allocation and Process packages.
|
||||
|
||||
The gate.py and source-proof.py helper catalog is preserved. Every gate's recorded helper must match available bytes; use repeatable `--helper PATH` for older captures. If a proof exists, its exact comparator bytes must be available at the default `.local/process-source-evidence-tools/verify.py` or a repeatable `--comparator PATH`. They are archived under their content hash and never executed.
|
||||
|
||||
An optional source proof is independently pinned in the manifest with its full named commit. Verification binds it to the enclosing run, exact result receipt, captured map/count, completed gate time, status and comparator bytes/hash. Captured HEAD is preserved as historical metadata and is not substituted for the named source commit. This packager checks historical proof consistency; it does **not** repeat Git blob comparison. A source proof on a failed gate is source-identity evidence only, never a passing-test claim.
|
||||
|
||||
```powershell
|
||||
python -B PACKAGE/verify.py
|
||||
python -B selfcheck.py --root CHECKOUT --runs connection-focused-1 connection-format-1 --output TOOLS/NEW_SELFCHECK_DIRECTORY
|
||||
```
|
||||
|
||||
The verifier checks pins, exact ZIP inventory, process/start/result relationships, streams, command/exit identities, source capsules, vendor inventory and Cargo artifact relationships. Selfchecks compare packaged raw bytes to originals, reject existing output, and mutate only in-memory copies to test stream/helper/exit/capsule/proof corruption. The package contains tools, manifest, files.json and evidence.zip; selfcheck is a preparation utility, not packaged runtime input.
|
||||
|
||||
Limits: internal consistency is not external authentication, hermetic build proof, native usability, a new source comparison or production acceptance. Review exact run results and source identity separately. Run with `python -B` to avoid incidental bytecode writes.
|
||||
Binary file not shown.
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"prepare.py": {
|
||||
"bytes": 6319,
|
||||
"sha256": "ece8199911549266ee8ff6a3cd5636534cc13f15315fdce8838e269071a14383"
|
||||
},
|
||||
"verify.py": {
|
||||
"bytes": 11451,
|
||||
"sha256": "87e29134dd04f38040ed7be0fb36954ef6c5873ff7fe844217ce72fd1aa3f92f"
|
||||
},
|
||||
"README.md": {
|
||||
"bytes": 3287,
|
||||
"sha256": "cfa094bdf820db71002e84a0617ed86f4e2de2e693809f308ff02b6a80a21d07"
|
||||
},
|
||||
"manifest.json": {
|
||||
"bytes": 19486,
|
||||
"sha256": "16bdf6f8ce82e701a82bc232ae4d19c8e08ea4f21a71d42276d95a9b8ff04438"
|
||||
},
|
||||
"evidence.zip": {
|
||||
"bytes": 6094831,
|
||||
"sha256": "501f01c7279bc9d719151abe9b729769dd0078678c702448671356e91965015f"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,440 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"kind": "prerelease37-connection-admission-cargo-gates",
|
||||
"runs": {
|
||||
"route-format-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"fmt",
|
||||
"--all",
|
||||
"--",
|
||||
"--check"
|
||||
],
|
||||
"head": "6f247af795b7bec1818c588b2dabf203f8f68d8a",
|
||||
"exitCode": 0,
|
||||
"inputCount": 756,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "6f247af795b7bec1818c588b2dabf203f8f68d8a",
|
||||
"pin": {
|
||||
"bytes": 593,
|
||||
"sha256": "3d1b4749654f7182ea5a59b402aac7de6863872df256a2876bc182ff295df60d"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"route-regressions-2": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--bin",
|
||||
"arcadia-studio",
|
||||
"--message-format=json",
|
||||
"route",
|
||||
"--",
|
||||
"--test-threads=1"
|
||||
],
|
||||
"head": "c655c5eeaaa32c1c21074a7543844ae99a1d6fd5",
|
||||
"exitCode": 101,
|
||||
"inputCount": 756,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "6f247af795b7bec1818c588b2dabf203f8f68d8a",
|
||||
"pin": {
|
||||
"bytes": 598,
|
||||
"sha256": "5f217f1318566ce1aa2637e848d324fe8933205c6740f48ff15532b8956d5411"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"route-format-2": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"fmt",
|
||||
"--all",
|
||||
"--",
|
||||
"--check"
|
||||
],
|
||||
"head": "e68a83959900e556768d8470640e28a7df7d8cb8",
|
||||
"exitCode": 0,
|
||||
"inputCount": 756,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "e68a83959900e556768d8470640e28a7df7d8cb8",
|
||||
"pin": {
|
||||
"bytes": 593,
|
||||
"sha256": "d14829233926a809d061b031194f19541bea0203c56beb441f4c596d585f699b"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"route-regressions-3": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--bin",
|
||||
"arcadia-studio",
|
||||
"--message-format=json",
|
||||
"route",
|
||||
"--",
|
||||
"--test-threads=1"
|
||||
],
|
||||
"head": "e68a83959900e556768d8470640e28a7df7d8cb8",
|
||||
"exitCode": 0,
|
||||
"inputCount": 756,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "e68a83959900e556768d8470640e28a7df7d8cb8",
|
||||
"pin": {
|
||||
"bytes": 598,
|
||||
"sha256": "dce6830e0fa49bcbf4437d090ade5114b5a86d4a9fe6ad2015fc51fb1f63c54f"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"allocation-regressions-2": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--bin",
|
||||
"arcadia-studio",
|
||||
"--message-format=json",
|
||||
"app::allocation_drag::tests",
|
||||
"--",
|
||||
"--test-threads=1"
|
||||
],
|
||||
"head": "e68a83959900e556768d8470640e28a7df7d8cb8",
|
||||
"exitCode": 0,
|
||||
"inputCount": 756,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "e68a83959900e556768d8470640e28a7df7d8cb8",
|
||||
"pin": {
|
||||
"bytes": 603,
|
||||
"sha256": "25c573fa3c37474de2a44b9e298daddc01aa84717435ee8bad080fd4ad5db594"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"route-attachments-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--bin",
|
||||
"arcadia-studio",
|
||||
"--message-format=json",
|
||||
"attachment",
|
||||
"--",
|
||||
"--test-threads=1"
|
||||
],
|
||||
"head": "e68a83959900e556768d8470640e28a7df7d8cb8",
|
||||
"exitCode": 0,
|
||||
"inputCount": 756,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "e68a83959900e556768d8470640e28a7df7d8cb8",
|
||||
"pin": {
|
||||
"bytes": 598,
|
||||
"sha256": "6cf60c7f472b27928a19573a5f558e42176a8f77a8e76462f4f54226b58578a6"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"route-lint-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"clippy",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--all-targets",
|
||||
"--message-format=json",
|
||||
"--",
|
||||
"-D",
|
||||
"warnings"
|
||||
],
|
||||
"head": "e68a83959900e556768d8470640e28a7df7d8cb8",
|
||||
"exitCode": 0,
|
||||
"inputCount": 756,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "e68a83959900e556768d8470640e28a7df7d8cb8",
|
||||
"pin": {
|
||||
"bytes": 591,
|
||||
"sha256": "6b643a140f408ce706aef87b31c716cb8b6b7ea1dd632ee6b60d96fc27d26a52"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
}
|
||||
},
|
||||
"captureHelpers": {
|
||||
"gate.py": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"source-proof.py": "helpers/58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119.py"
|
||||
},
|
||||
"logicalFiles": {
|
||||
"cargo/allocation-regressions-2/inputs.json": "capsules/e8d0acc2de87827cc220143aafcd4963122cfa6529cc679f886f9870954f30f5.json",
|
||||
"cargo/allocation-regressions-2/inputs.zip": "capsules/cb78349d30b9b3828711f50a700e0db8e46b31977d5909e775a6abbf57a2dd35.zip",
|
||||
"cargo/allocation-regressions-2/process.json": "cargo/allocation-regressions-2/process.json",
|
||||
"cargo/allocation-regressions-2/result.json": "cargo/allocation-regressions-2/result.json",
|
||||
"cargo/allocation-regressions-2/source-proof.json": "cargo/allocation-regressions-2/source-proof.json",
|
||||
"cargo/allocation-regressions-2/started.json": "cargo/allocation-regressions-2/started.json",
|
||||
"cargo/allocation-regressions-2/stderr.log": "cargo/allocation-regressions-2/stderr.log",
|
||||
"cargo/allocation-regressions-2/stdout.log": "cargo/allocation-regressions-2/stdout.log",
|
||||
"cargo/route-attachments-1/inputs.json": "capsules/e8d0acc2de87827cc220143aafcd4963122cfa6529cc679f886f9870954f30f5.json",
|
||||
"cargo/route-attachments-1/inputs.zip": "capsules/cb78349d30b9b3828711f50a700e0db8e46b31977d5909e775a6abbf57a2dd35.zip",
|
||||
"cargo/route-attachments-1/process.json": "cargo/route-attachments-1/process.json",
|
||||
"cargo/route-attachments-1/result.json": "cargo/route-attachments-1/result.json",
|
||||
"cargo/route-attachments-1/source-proof.json": "cargo/route-attachments-1/source-proof.json",
|
||||
"cargo/route-attachments-1/started.json": "cargo/route-attachments-1/started.json",
|
||||
"cargo/route-attachments-1/stderr.log": "cargo/route-attachments-1/stderr.log",
|
||||
"cargo/route-attachments-1/stdout.log": "cargo/route-attachments-1/stdout.log",
|
||||
"cargo/route-format-1/inputs.json": "capsules/db306addb9c9012a9f928210bd67615e0245ac3c59e32abba8412e8c7d5265e9.json",
|
||||
"cargo/route-format-1/inputs.zip": "capsules/4a0ae23e1599f7647c5b62eedfbac7c1d714362990b957aa59553fd363e46bbe.zip",
|
||||
"cargo/route-format-1/process.json": "cargo/route-format-1/process.json",
|
||||
"cargo/route-format-1/result.json": "cargo/route-format-1/result.json",
|
||||
"cargo/route-format-1/source-proof.json": "cargo/route-format-1/source-proof.json",
|
||||
"cargo/route-format-1/started.json": "cargo/route-format-1/started.json",
|
||||
"cargo/route-format-1/stderr.log": "cargo/route-format-1/stderr.log",
|
||||
"cargo/route-format-1/stdout.log": "cargo/route-format-1/stdout.log",
|
||||
"cargo/route-format-2/inputs.json": "capsules/e8d0acc2de87827cc220143aafcd4963122cfa6529cc679f886f9870954f30f5.json",
|
||||
"cargo/route-format-2/inputs.zip": "capsules/cb78349d30b9b3828711f50a700e0db8e46b31977d5909e775a6abbf57a2dd35.zip",
|
||||
"cargo/route-format-2/process.json": "cargo/route-format-2/process.json",
|
||||
"cargo/route-format-2/result.json": "cargo/route-format-2/result.json",
|
||||
"cargo/route-format-2/source-proof.json": "cargo/route-format-2/source-proof.json",
|
||||
"cargo/route-format-2/started.json": "cargo/route-format-2/started.json",
|
||||
"cargo/route-format-2/stderr.log": "cargo/route-format-2/stderr.log",
|
||||
"cargo/route-format-2/stdout.log": "cargo/route-format-2/stdout.log",
|
||||
"cargo/route-lint-1/inputs.json": "capsules/e8d0acc2de87827cc220143aafcd4963122cfa6529cc679f886f9870954f30f5.json",
|
||||
"cargo/route-lint-1/inputs.zip": "capsules/cb78349d30b9b3828711f50a700e0db8e46b31977d5909e775a6abbf57a2dd35.zip",
|
||||
"cargo/route-lint-1/process.json": "cargo/route-lint-1/process.json",
|
||||
"cargo/route-lint-1/result.json": "cargo/route-lint-1/result.json",
|
||||
"cargo/route-lint-1/source-proof.json": "cargo/route-lint-1/source-proof.json",
|
||||
"cargo/route-lint-1/started.json": "cargo/route-lint-1/started.json",
|
||||
"cargo/route-lint-1/stderr.log": "cargo/route-lint-1/stderr.log",
|
||||
"cargo/route-lint-1/stdout.log": "cargo/route-lint-1/stdout.log",
|
||||
"cargo/route-regressions-2/inputs.json": "capsules/db306addb9c9012a9f928210bd67615e0245ac3c59e32abba8412e8c7d5265e9.json",
|
||||
"cargo/route-regressions-2/inputs.zip": "capsules/4a0ae23e1599f7647c5b62eedfbac7c1d714362990b957aa59553fd363e46bbe.zip",
|
||||
"cargo/route-regressions-2/process.json": "cargo/route-regressions-2/process.json",
|
||||
"cargo/route-regressions-2/result.json": "cargo/route-regressions-2/result.json",
|
||||
"cargo/route-regressions-2/source-proof.json": "cargo/route-regressions-2/source-proof.json",
|
||||
"cargo/route-regressions-2/started.json": "cargo/route-regressions-2/started.json",
|
||||
"cargo/route-regressions-2/stderr.log": "cargo/route-regressions-2/stderr.log",
|
||||
"cargo/route-regressions-2/stdout.log": "cargo/route-regressions-2/stdout.log",
|
||||
"cargo/route-regressions-3/inputs.json": "capsules/e8d0acc2de87827cc220143aafcd4963122cfa6529cc679f886f9870954f30f5.json",
|
||||
"cargo/route-regressions-3/inputs.zip": "capsules/cb78349d30b9b3828711f50a700e0db8e46b31977d5909e775a6abbf57a2dd35.zip",
|
||||
"cargo/route-regressions-3/process.json": "cargo/route-regressions-3/process.json",
|
||||
"cargo/route-regressions-3/result.json": "cargo/route-regressions-3/result.json",
|
||||
"cargo/route-regressions-3/source-proof.json": "cargo/route-regressions-3/source-proof.json",
|
||||
"cargo/route-regressions-3/started.json": "cargo/route-regressions-3/started.json",
|
||||
"cargo/route-regressions-3/stderr.log": "cargo/route-regressions-3/stderr.log",
|
||||
"cargo/route-regressions-3/stdout.log": "cargo/route-regressions-3/stdout.log",
|
||||
"comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py",
|
||||
"helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"helpers/58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119.py": "helpers/58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119.py"
|
||||
},
|
||||
"members": {
|
||||
"capsules/4a0ae23e1599f7647c5b62eedfbac7c1d714362990b957aa59553fd363e46bbe.zip": {
|
||||
"bytes": 3043591,
|
||||
"sha256": "4a0ae23e1599f7647c5b62eedfbac7c1d714362990b957aa59553fd363e46bbe"
|
||||
},
|
||||
"capsules/cb78349d30b9b3828711f50a700e0db8e46b31977d5909e775a6abbf57a2dd35.zip": {
|
||||
"bytes": 3043596,
|
||||
"sha256": "cb78349d30b9b3828711f50a700e0db8e46b31977d5909e775a6abbf57a2dd35"
|
||||
},
|
||||
"capsules/db306addb9c9012a9f928210bd67615e0245ac3c59e32abba8412e8c7d5265e9.json": {
|
||||
"bytes": 126743,
|
||||
"sha256": "db306addb9c9012a9f928210bd67615e0245ac3c59e32abba8412e8c7d5265e9"
|
||||
},
|
||||
"capsules/e8d0acc2de87827cc220143aafcd4963122cfa6529cc679f886f9870954f30f5.json": {
|
||||
"bytes": 126743,
|
||||
"sha256": "e8d0acc2de87827cc220143aafcd4963122cfa6529cc679f886f9870954f30f5"
|
||||
},
|
||||
"cargo/allocation-regressions-2/process.json": {
|
||||
"bytes": 220,
|
||||
"sha256": "8e65fce3dd862b78a7c5070711822af2e4099ee56dee15f4559e0adda7f65abb"
|
||||
},
|
||||
"cargo/allocation-regressions-2/result.json": {
|
||||
"bytes": 3345,
|
||||
"sha256": "9aeea52a19472c7cd6274af330ba611382c7031b1a3cfa3728f13ba4353c1a95"
|
||||
},
|
||||
"cargo/allocation-regressions-2/source-proof.json": {
|
||||
"bytes": 603,
|
||||
"sha256": "25c573fa3c37474de2a44b9e298daddc01aa84717435ee8bad080fd4ad5db594"
|
||||
},
|
||||
"cargo/allocation-regressions-2/started.json": {
|
||||
"bytes": 1612,
|
||||
"sha256": "b8b5e728cc5758d7225f17a0878880c39a6e879f03e2107b6df261c9a2b6598b"
|
||||
},
|
||||
"cargo/allocation-regressions-2/stderr.log": {
|
||||
"bytes": 210,
|
||||
"sha256": "1a13b97ab1dfa891ef6fa0e981e0b8bc4e17bc115bb6b2bce0e168a47c369981"
|
||||
},
|
||||
"cargo/allocation-regressions-2/stdout.log": {
|
||||
"bytes": 299506,
|
||||
"sha256": "5553cfb160507bcfeba793a9336d60526e9849b89a11685de2410af742501443"
|
||||
},
|
||||
"cargo/route-attachments-1/process.json": {
|
||||
"bytes": 203,
|
||||
"sha256": "cc6531c8d12842f011c909960547cbfd80f8b467435d0338971e8fea7d1e2e67"
|
||||
},
|
||||
"cargo/route-attachments-1/result.json": {
|
||||
"bytes": 3328,
|
||||
"sha256": "ee3c271a97e9b6938c8e33cbb35a17b53bc8a6c7cd814dbf01c5897a08f4042d"
|
||||
},
|
||||
"cargo/route-attachments-1/source-proof.json": {
|
||||
"bytes": 598,
|
||||
"sha256": "6cf60c7f472b27928a19573a5f558e42176a8f77a8e76462f4f54226b58578a6"
|
||||
},
|
||||
"cargo/route-attachments-1/started.json": {
|
||||
"bytes": 1595,
|
||||
"sha256": "d47d26d311036a3f60c0c526d581744bcb8c089254bdbb4d9fdee9f5eed121d4"
|
||||
},
|
||||
"cargo/route-attachments-1/stderr.log": {
|
||||
"bytes": 210,
|
||||
"sha256": "3e213115378cb561ca1805b9ac76d6ced710a1c9bc05358237ef2dc0aa5afa25"
|
||||
},
|
||||
"cargo/route-attachments-1/stdout.log": {
|
||||
"bytes": 296666,
|
||||
"sha256": "1ee3c68790b0bbb3a6114d4a06f80866abdd75cd85105ed1e06b38e8486164e1"
|
||||
},
|
||||
"cargo/route-format-1/process.json": {
|
||||
"bytes": 116,
|
||||
"sha256": "7a53591a1e2b9b70f4bfa1cac5e99b8f655ad197bc77f85e6b41bdde9c478632"
|
||||
},
|
||||
"cargo/route-format-1/result.json": {
|
||||
"bytes": 1872,
|
||||
"sha256": "098a5b5002922967dddc30330555ab4b34574c91763171adad7732f620f70780"
|
||||
},
|
||||
"cargo/route-format-1/source-proof.json": {
|
||||
"bytes": 593,
|
||||
"sha256": "3d1b4749654f7182ea5a59b402aac7de6863872df256a2876bc182ff295df60d"
|
||||
},
|
||||
"cargo/route-format-1/started.json": {
|
||||
"bytes": 1484,
|
||||
"sha256": "e05f56bcd42f4c1faeace30b1090e13253d53c58c56c4be55bcabb05d3d12ba1"
|
||||
},
|
||||
"cargo/route-format-1/stderr.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"cargo/route-format-1/stdout.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"cargo/route-format-2/process.json": {
|
||||
"bytes": 116,
|
||||
"sha256": "896d65f9ccef7389e768bb47cfee5e660cd3fcb9fc4e5d7539bfeabd0a1186c3"
|
||||
},
|
||||
"cargo/route-format-2/result.json": {
|
||||
"bytes": 1872,
|
||||
"sha256": "d73e18d2ed84ac8030e8e1b896f4a09367d27f2f0c43a242df1c02f7b1cd61c8"
|
||||
},
|
||||
"cargo/route-format-2/source-proof.json": {
|
||||
"bytes": 593,
|
||||
"sha256": "d14829233926a809d061b031194f19541bea0203c56beb441f4c596d585f699b"
|
||||
},
|
||||
"cargo/route-format-2/started.json": {
|
||||
"bytes": 1484,
|
||||
"sha256": "5319f5aad6f40b648701d52640f3eec07ee47bcde7a343bcd735cd7a2ec68d6d"
|
||||
},
|
||||
"cargo/route-format-2/stderr.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"cargo/route-format-2/stdout.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"cargo/route-lint-1/process.json": {
|
||||
"bytes": 179,
|
||||
"sha256": "19449fa3c3005ab019daa4f8dc4ad032195dc3192983a0f05aab56bb38f7deaa"
|
||||
},
|
||||
"cargo/route-lint-1/result.json": {
|
||||
"bytes": 1961,
|
||||
"sha256": "5cc9b045529ca458e7980ccceeb6f7ae98ef3ac86f9d7b8aedeae9065a81e6ce"
|
||||
},
|
||||
"cargo/route-lint-1/source-proof.json": {
|
||||
"bytes": 591,
|
||||
"sha256": "6b643a140f408ce706aef87b31c716cb8b6b7ea1dd632ee6b60d96fc27d26a52"
|
||||
},
|
||||
"cargo/route-lint-1/started.json": {
|
||||
"bytes": 1566,
|
||||
"sha256": "9acc933713a5c8171ea379812548f1d51ace982bc29bda1546f321066f864784"
|
||||
},
|
||||
"cargo/route-lint-1/stderr.log": {
|
||||
"bytes": 137,
|
||||
"sha256": "2233336da05e39aa60c45cae62339002a904cf342d551ca5e68869c206c3ed23"
|
||||
},
|
||||
"cargo/route-lint-1/stdout.log": {
|
||||
"bytes": 269521,
|
||||
"sha256": "ff8a21497364f30c012ca736f2f46dc73ea5a7ec46f1ee4ee534e50f38244a91"
|
||||
},
|
||||
"cargo/route-regressions-2/process.json": {
|
||||
"bytes": 198,
|
||||
"sha256": "89f42020b064a65a6979a975e05f02e091cf939bce4a0be29220b75d7e023213"
|
||||
},
|
||||
"cargo/route-regressions-2/result.json": {
|
||||
"bytes": 1988,
|
||||
"sha256": "5e437ac0f2c4db94352f8199ac53172b3af60a709de922cdf81361a46b21a838"
|
||||
},
|
||||
"cargo/route-regressions-2/source-proof.json": {
|
||||
"bytes": 598,
|
||||
"sha256": "5f217f1318566ce1aa2637e848d324fe8933205c6740f48ff15532b8956d5411"
|
||||
},
|
||||
"cargo/route-regressions-2/started.json": {
|
||||
"bytes": 1590,
|
||||
"sha256": "5f7cbe5936440e1eb91300babe5b64489880ed65129d5c1ac1bf5e6632a332a1"
|
||||
},
|
||||
"cargo/route-regressions-2/stderr.log": {
|
||||
"bytes": 166,
|
||||
"sha256": "ad39fc56f685f7f3c6a9b4a342abff1f19f9daad28c394673be0e3a2a1cf2523"
|
||||
},
|
||||
"cargo/route-regressions-2/stdout.log": {
|
||||
"bytes": 295421,
|
||||
"sha256": "8d488ecf03a78a1bee6f419ab1f8ea8d60d8b082d78df3ca9d1f1053898c09b3"
|
||||
},
|
||||
"cargo/route-regressions-3/process.json": {
|
||||
"bytes": 198,
|
||||
"sha256": "f0f320268fb9038a69794983635473518c80729678ad2e5367e882f54f2a7ab6"
|
||||
},
|
||||
"cargo/route-regressions-3/result.json": {
|
||||
"bytes": 3325,
|
||||
"sha256": "6ea156b2e27520e99a5d6cae234cae38bc421cdf33bfb9176431120f6537823b"
|
||||
},
|
||||
"cargo/route-regressions-3/source-proof.json": {
|
||||
"bytes": 598,
|
||||
"sha256": "dce6830e0fa49bcbf4437d090ade5114b5a86d4a9fe6ad2015fc51fb1f63c54f"
|
||||
},
|
||||
"cargo/route-regressions-3/started.json": {
|
||||
"bytes": 1590,
|
||||
"sha256": "93bbffadfaca7fac771f2f34822075415208d82119350021a4c097f29c69bf3e"
|
||||
},
|
||||
"cargo/route-regressions-3/stderr.log": {
|
||||
"bytes": 287,
|
||||
"sha256": "d7ddcdea80dcdb2594a40a694ef81956d5f8a18fed026b6262a6fefe2966f012"
|
||||
},
|
||||
"cargo/route-regressions-3/stdout.log": {
|
||||
"bytes": 297050,
|
||||
"sha256": "f7c21752b8d414e07e32c148b8aefa9977515fc8f1411fa59a9724d4ae092d6b"
|
||||
},
|
||||
"comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py": {
|
||||
"bytes": 10094,
|
||||
"sha256": "7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a"
|
||||
},
|
||||
"helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py": {
|
||||
"bytes": 3827,
|
||||
"sha256": "1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20"
|
||||
},
|
||||
"helpers/58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119.py": {
|
||||
"bytes": 1399,
|
||||
"sha256": "58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
"""Package explicitly selected closed gate captures without running Cargo."""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import json
|
||||
import zipfile
|
||||
from verify import RAW_FILES, pin, parsed, need, run_name, verify_data, verify_package
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def collect(root, runs, helpers, comparators=()):
|
||||
gates = root / ".local/prerelease37-gates"
|
||||
candidates = [gates / name for name in ("gate.py", "source-proof.py")] + helpers
|
||||
helper_bytes = [path.read_bytes() for path in candidates]
|
||||
comparator_paths = [root / ".local/process-source-evidence-tools/verify.py", *comparators]
|
||||
comparator_bytes = [path.read_bytes() for path in comparator_paths if path.is_file()]
|
||||
payload, logical, specs = {}, {}, {}
|
||||
def add(name, data):
|
||||
stored = ("capsules/" + pin(data)["sha256"] + Path(name).suffix
|
||||
if name.rsplit("/", 1)[1] in {"inputs.json", "inputs.zip"} else name)
|
||||
need(stored not in payload or payload[stored] == data, "deduplication collision")
|
||||
payload[stored] = data
|
||||
logical[name] = stored
|
||||
helper_catalog = {}
|
||||
for name, data in zip(("gate.py", "source-proof.py"), helper_bytes):
|
||||
helper = "helpers/" + pin(data)["sha256"] + ".py"
|
||||
add(helper, data)
|
||||
helper_catalog[name] = helper
|
||||
for run in runs:
|
||||
folder = gates / run
|
||||
need(folder.is_dir(), "run directory missing: " + run)
|
||||
paths = list(folder.iterdir())
|
||||
names = {path.name for path in paths}
|
||||
need(all(path.is_file() and not path.is_symlink() for path in paths)
|
||||
and names in (RAW_FILES, RAW_FILES | {"source-proof.json"}), "exact closed raw files required: " + run)
|
||||
raw = {path.name: path.read_bytes() for path in paths}
|
||||
result = parsed(raw["result.json"])
|
||||
need("finish" in result and result["inputsUnchanged"] is True, "unfinished/changed run refused: " + run)
|
||||
matches = [data for data in helper_bytes if pin(data) == result["helper"]]
|
||||
need(bool(matches), "exact historical helper unavailable for " + run + "; supply --helper PATH")
|
||||
helper = "helpers/" + result["helper"]["sha256"] + ".py"
|
||||
add(helper, matches[0])
|
||||
for name, data in sorted(raw.items()):
|
||||
add("cargo/" + run + "/" + name, data)
|
||||
specs[run] = {key: result[key] for key in ("command", "head", "exitCode", "inputCount")}
|
||||
specs[run]["helper"] = helper
|
||||
if "source-proof.json" in raw:
|
||||
proof = parsed(raw["source-proof.json"])
|
||||
matches = [data for data in comparator_bytes if pin(data) == proof["comparator"]]
|
||||
need(bool(matches), "exact source comparator unavailable for " + run + "; supply --comparator PATH")
|
||||
comparator = "comparators/" + proof["comparator"]["sha256"] + ".py"
|
||||
add(comparator, matches[0])
|
||||
specs[run]["sourceProof"] = {"commit": proof["commit"], "pin": pin(raw["source-proof.json"]), "comparator": comparator}
|
||||
manifest = {"schema": 1, "kind": "prerelease37-connection-admission-cargo-gates", "runs": specs,
|
||||
"captureHelpers": helper_catalog,
|
||||
"logicalFiles": dict(sorted(logical.items())),
|
||||
"members": {name: pin(data) for name, data in sorted(payload.items())}}
|
||||
summary = verify_data(manifest, payload.__getitem__)
|
||||
return payload, manifest, summary
|
||||
|
||||
|
||||
def write_json(path, value):
|
||||
with path.open("xb") as output:
|
||||
output.write((json.dumps(value, indent=2) + "\n").encode("utf-8"))
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--root", type=Path, required=True, help="checkout holding .local/prerelease37-gates")
|
||||
parser.add_argument("--runs", nargs="+", required=True, help="explicit run names, including failures to retain")
|
||||
parser.add_argument("--helper", type=Path, action="append", default=[], help="additional exact historical helper (repeatable)")
|
||||
parser.add_argument("--comparator", type=Path, action="append", default=[], help="additional exact historical source comparator (repeatable)")
|
||||
parser.add_argument("--output", type=Path, help="fresh, nonexistent package directory (required unless --check-only)")
|
||||
parser.add_argument("--check-only", action="store_true", help="validate raw captures in memory without creating an archive")
|
||||
args = parser.parse_args()
|
||||
need(all(run_name(run) for run in args.runs) and len(args.runs) == len(set(args.runs)), "unique safe run names required")
|
||||
need(args.check_only or args.output is not None, "--output is required unless --check-only")
|
||||
if not args.check_only:
|
||||
need(not args.output.exists(), "output must be a fresh directory: " + str(args.output))
|
||||
payload, manifest, summary = collect(args.root.resolve(), args.runs, args.helper, args.comparator)
|
||||
if args.check_only:
|
||||
print("PASS raw consistency; no package written:", summary)
|
||||
return
|
||||
out = args.output.resolve()
|
||||
out.mkdir(parents=True, exist_ok=False)
|
||||
for name in ("evidence.zip", "manifest.json", "files.json"):
|
||||
need(not (out / name).exists(), "existing package output refused: " + name)
|
||||
for name in ("prepare.py", "verify.py", "README.md"):
|
||||
data = (HERE / name).read_bytes()
|
||||
destination = out / name
|
||||
if destination.exists():
|
||||
need(destination.read_bytes() == data, "different existing package tool: " + name)
|
||||
else:
|
||||
with destination.open("xb") as output:
|
||||
output.write(data)
|
||||
with zipfile.ZipFile(out / "evidence.zip", "x", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as archive:
|
||||
for name, data in sorted(payload.items()):
|
||||
info = zipfile.ZipInfo(name, date_time=(2026, 9, 23, 0, 0, 0))
|
||||
info.compress_type = zipfile.ZIP_DEFLATED
|
||||
info.external_attr = 0o100644 << 16
|
||||
archive.writestr(info, data)
|
||||
write_json(out / "manifest.json", manifest)
|
||||
names = ("prepare.py", "verify.py", "README.md", "manifest.json", "evidence.zip")
|
||||
write_json(out / "files.json", {name: pin((out / name).read_bytes()) for name in names})
|
||||
need(verify_package(out) == summary, "written package differs from validated captures")
|
||||
print("PASS package readback:", out, summary)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,37 @@
|
||||
Command: ['C:\\Users\\User\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\python\\python.exe', '-B', '.local/prerelease37-evidence-tools/selfcheck.py', '--root', 'C:/ar-multiselect-9e8r6t', '--runs', 'route-format-1', 'route-regressions-2', 'route-format-2', 'route-regressions-3', 'allocation-regressions-2', 'route-attachments-1', 'route-lint-1', '--output', '.local/prerelease37-evidence-tools/selfcheck-route-delivery']
|
||||
route-format-1 recorded exit 0 inputs 756 executable records 0
|
||||
route-regressions-2 recorded exit 101 inputs 756 executable records 0
|
||||
route-format-2 recorded exit 0 inputs 756 executable records 0
|
||||
route-regressions-3 recorded exit 0 inputs 756 executable records 1
|
||||
allocation-regressions-2 recorded exit 0 inputs 756 executable records 1
|
||||
route-attachments-1 recorded exit 0 inputs 756 executable records 1
|
||||
route-lint-1 recorded exit 0 inputs 756 executable records 0
|
||||
route-format-1 recorded exit 0 inputs 756 executable records 0
|
||||
route-regressions-2 recorded exit 101 inputs 756 executable records 0
|
||||
route-format-2 recorded exit 0 inputs 756 executable records 0
|
||||
route-regressions-3 recorded exit 0 inputs 756 executable records 1
|
||||
allocation-regressions-2 recorded exit 0 inputs 756 executable records 1
|
||||
route-attachments-1 recorded exit 0 inputs 756 executable records 1
|
||||
route-lint-1 recorded exit 0 inputs 756 executable records 0
|
||||
PASS package readback: C:\ar-multiselect-9e8r6t\.local\prerelease37-evidence-tools\selfcheck-route-delivery {'runs': 7, 'recordedFailures': 1, 'logicalFiles': 59, 'storedMembers': 49, 'sourceCapsulePairs': 2, 'captureHelpers': {'gate.py': 'helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py', 'source-proof.py': 'helpers/58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119.py'}, 'sourceProofs': 7}
|
||||
route-format-1 recorded exit 0 inputs 756 executable records 0
|
||||
route-regressions-2 recorded exit 101 inputs 756 executable records 0
|
||||
route-format-2 recorded exit 0 inputs 756 executable records 0
|
||||
route-regressions-3 recorded exit 0 inputs 756 executable records 1
|
||||
allocation-regressions-2 recorded exit 0 inputs 756 executable records 1
|
||||
route-attachments-1 recorded exit 0 inputs 756 executable records 1
|
||||
route-lint-1 recorded exit 0 inputs 756 executable records 0
|
||||
route-format-1 recorded exit 0 inputs 756 executable records 0
|
||||
route-regressions-2 recorded exit 101 inputs 756 executable records 0
|
||||
route-format-2 recorded exit 0 inputs 756 executable records 0
|
||||
route-regressions-3 recorded exit 0 inputs 756 executable records 1
|
||||
allocation-regressions-2 recorded exit 0 inputs 756 executable records 1
|
||||
route-attachments-1 recorded exit 0 inputs 756 executable records 1
|
||||
route-lint-1 recorded exit 0 inputs 756 executable records 0
|
||||
PASS original raw/optional-proof/helper archive equality, existing-output refusal, and corruption rejection: ['raw stream/receipt mismatch', 'helper bytes/name mismatch', 'run/result exit mismatch', 'input archive mapping mismatch', 'missing closed receipt', 'source proof run relationship', 'source proof commit relationship', 'source proof result relationship', 'source proof inputCount relationship', 'missing declared optional source proof', 'source comparator bytes mismatch']
|
||||
Historical captured failures remain failures: 1
|
||||
No Cargo, archived code, or GUI executed.
|
||||
|
||||
STDERR:
|
||||
|
||||
Exit: 0
|
||||
@@ -0,0 +1,194 @@
|
||||
"""Offline consistency checks only; never execute or extract archived inputs."""
|
||||
from pathlib import Path, PurePosixPath
|
||||
from datetime import datetime
|
||||
import argparse
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import math
|
||||
import re
|
||||
import stat
|
||||
import zipfile
|
||||
|
||||
RAW_FILES = {"started.json", "process.json", "result.json", "stdout.log", "stderr.log", "inputs.json", "inputs.zip"}
|
||||
OUTER_FILES = {"prepare.py", "verify.py", "README.md", "manifest.json", "evidence.zip"}
|
||||
|
||||
|
||||
def need(condition, message):
|
||||
if not condition:
|
||||
raise ValueError(message)
|
||||
|
||||
|
||||
def pin(data):
|
||||
return {"bytes": len(data), "sha256": hashlib.sha256(data).hexdigest()}
|
||||
|
||||
|
||||
def valid_pin(value):
|
||||
return (isinstance(value, dict) and set(value) == {"bytes", "sha256"}
|
||||
and type(value["bytes"]) is int and value["bytes"] >= 0
|
||||
and isinstance(value["sha256"], str)
|
||||
and re.fullmatch(r"[0-9a-f]{64}", value["sha256"]) is not None)
|
||||
|
||||
|
||||
def safe(name):
|
||||
return (isinstance(name, str) and bool(name) and "\\" not in name and ":" not in name
|
||||
and all(ord(char) >= 32 for char in name)
|
||||
and not PurePosixPath(name).is_absolute()
|
||||
and all(part not in ("", ".", "..") for part in name.split("/")))
|
||||
|
||||
|
||||
def run_name(name):
|
||||
return isinstance(name, str) and re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]*", name) is not None
|
||||
|
||||
|
||||
def parsed(data):
|
||||
def pairs(items):
|
||||
result = {}
|
||||
for key, value in items:
|
||||
need(key not in result, "duplicate JSON key: " + key)
|
||||
result[key] = value
|
||||
return result
|
||||
def constant(value):
|
||||
raise ValueError("nonfinite JSON constant: " + value)
|
||||
return json.loads(data, object_pairs_hook=pairs, parse_constant=constant)
|
||||
|
||||
|
||||
def inventory(archive, expected):
|
||||
names = archive.namelist()
|
||||
need(len(names) == len(set(names)) and set(names) == set(expected), "exact ZIP membership")
|
||||
for entry in archive.infolist():
|
||||
need(safe(entry.filename) and not entry.is_dir(), "unsafe ZIP member: " + entry.filename)
|
||||
need(not stat.S_ISLNK(entry.external_attr >> 16), "ZIP symlink: " + entry.filename)
|
||||
need(valid_pin(expected[entry.filename]), "invalid member pin: " + entry.filename)
|
||||
need(entry.file_size == expected[entry.filename]["bytes"], "ZIP recorded size: " + entry.filename)
|
||||
|
||||
|
||||
def ordered_times(*values):
|
||||
times = [datetime.fromisoformat(value) for value in values]
|
||||
return all(value.utcoffset() is not None for value in times) and times == sorted(times)
|
||||
|
||||
|
||||
def verify_data(manifest, read):
|
||||
"""Shared read-only verifier for packaged bytes or prepare's in-memory payload."""
|
||||
need(manifest["schema"] == 1 and manifest["kind"] == "prerelease37-connection-admission-cargo-gates", "manifest schema")
|
||||
runs, logical, stored = manifest["runs"], manifest["logicalFiles"], manifest["members"]
|
||||
helpers = manifest["captureHelpers"]
|
||||
need(isinstance(helpers, dict) and set(helpers) == {"gate.py", "source-proof.py"}, "capture helper names")
|
||||
need(all(isinstance(name, str) and re.fullmatch(r"helpers/[0-9a-f]{64}\.py", name)
|
||||
for name in helpers.values()), "capture helper mapping")
|
||||
need(isinstance(runs, dict) and bool(runs) and all(run_name(run) for run in runs), "explicit run inventory")
|
||||
need(all(safe(name) for name in logical) and all(safe(name) for name in stored), "unsafe manifest path")
|
||||
need(set(logical.values()) == set(stored), "unreferenced or absent stored members")
|
||||
for name, expected in stored.items():
|
||||
need(valid_pin(expected) and pin(read(name)) == expected, "stored member hash: " + name)
|
||||
expected_logical = {"cargo/" + run + "/" + name for run in runs for name in RAW_FILES}
|
||||
expected_logical.update(spec["helper"] for spec in runs.values())
|
||||
expected_logical.update(helpers.values())
|
||||
for run, spec in runs.items():
|
||||
if "sourceProof" in spec:
|
||||
proof_spec = spec["sourceProof"]
|
||||
need(set(proof_spec) == {"commit", "pin", "comparator"}, run + ": exact source proof spec")
|
||||
need(isinstance(proof_spec["commit"], str) and re.fullmatch(r"[0-9a-f]{40}", proof_spec["commit"]), run + ": named full source commit")
|
||||
need(valid_pin(proof_spec["pin"]), run + ": source proof pin")
|
||||
need(isinstance(proof_spec["comparator"], str) and re.fullmatch(r"comparators/[0-9a-f]{64}\.py", proof_spec["comparator"]), run + ": comparator path")
|
||||
expected_logical.update({"cargo/" + run + "/source-proof.json", proof_spec["comparator"]})
|
||||
need(set(logical) == expected_logical, "exact logical raw/helper inventory")
|
||||
for name, destination in logical.items():
|
||||
if name.startswith("cargo/") and name.rsplit("/", 1)[1] in {"inputs.json", "inputs.zip"}:
|
||||
expected = "capsules/" + stored[destination]["sha256"] + PurePosixPath(name).suffix
|
||||
need(destination == expected, "input capsule mapping: " + name)
|
||||
else:
|
||||
need(destination == name, "only input maps/archives may be deduplicated: " + name)
|
||||
def raw(name):
|
||||
return read(logical[name])
|
||||
for name, helper in helpers.items():
|
||||
need(helper == "helpers/" + pin(raw(helper))["sha256"] + ".py", "named helper hash: " + name)
|
||||
seen_capsules = set()
|
||||
for run, spec in runs.items():
|
||||
prefix = "cargo/" + run + "/"
|
||||
start = parsed(raw(prefix + "started.json"))
|
||||
process = parsed(raw(prefix + "process.json"))
|
||||
result = parsed(raw(prefix + "result.json"))
|
||||
required_start = {"start", "command", "cwd", "head", "scope", "inputs", "archive",
|
||||
"inputCount", "environment", "helper", "cargo", "rustc"}
|
||||
need(required_start <= start.keys(), run + ": incomplete start receipt")
|
||||
need(all(result.get(key) == value for key, value in start.items()), run + ": start/result relationship")
|
||||
command = result["command"]
|
||||
need(isinstance(command, list) and command and command[0] == "cargo"
|
||||
and all(isinstance(arg, str) for arg in command), run + ": Cargo command")
|
||||
need(command == process["command"] == spec["command"], run + ": command relationship")
|
||||
need(result["head"] == spec["head"] and re.fullmatch(r"(?:[0-9a-f]{40}|[0-9a-f]{64})", result["head"]), run + ": HEAD")
|
||||
need(type(result["exitCode"]) is int and result["exitCode"] == spec["exitCode"], run + ": recorded exit")
|
||||
need(result["inputsUnchanged"] is True, run + ": changed inputs refused")
|
||||
need(type(result["seconds"]) in (int, float) and math.isfinite(result["seconds"])
|
||||
and result["seconds"] >= 0, run + ": duration")
|
||||
need(type(process["pid"]) is int and process["pid"] > 0, run + ": process identity")
|
||||
need(ordered_times(start["start"], process["observed"], result["finish"]), run + ": completed timestamp order")
|
||||
for field, filename in [("inputs", "inputs.json"), ("archive", "inputs.zip"), ("stdout", "stdout.log"), ("stderr", "stderr.log")]:
|
||||
need(result[field] == pin(raw(prefix + filename)), run + ": " + field + " pin")
|
||||
need(valid_pin(result["helper"]) and result["helper"] == pin(raw(spec["helper"])), run + ": helper pin")
|
||||
need(spec["helper"] == "helpers/" + result["helper"]["sha256"] + ".py", run + ": helper mapping")
|
||||
inputs = parsed(raw(prefix + "inputs.json"))
|
||||
need(isinstance(inputs, dict) and all(safe(name) for name in inputs), run + ": safe input inventory")
|
||||
need(type(result["inputCount"]) is int and len(inputs) == result["inputCount"] == spec["inputCount"], run + ": input counts")
|
||||
if "sourceProof" in spec:
|
||||
proof_spec = spec["sourceProof"]
|
||||
proof_bytes = raw(prefix + "source-proof.json")
|
||||
proof = parsed(proof_bytes)
|
||||
need(pin(proof_bytes) == proof_spec["pin"], run + ": source proof pinned bytes")
|
||||
need(proof["run"] == run and proof["commit"] == proof_spec["commit"], run + ": proof run/commit identity")
|
||||
need(proof["status"] == "PASS exact captured input inventory and Git blob bytes", run + ": source proof status")
|
||||
need(proof["inputCount"] == len(inputs) and proof["inputMap"] == result["inputs"], run + ": proof input relationship")
|
||||
need(proof["result"] == pin(raw(prefix + "result.json")), run + ": proof result relationship")
|
||||
need(ordered_times(result["finish"], proof["verified"]), run + ": proof time after closed gate")
|
||||
comparator = proof_spec["comparator"]
|
||||
need(valid_pin(proof["comparator"]) and pin(raw(comparator)) == proof["comparator"], run + ": comparator bytes")
|
||||
need(comparator == "comparators/" + proof["comparator"]["sha256"] + ".py", run + ": comparator identity")
|
||||
need(sum(name.startswith("crates/vendor/iced_widget/") for name in inputs) == 71, run + ": vendor count")
|
||||
capsule = (logical[prefix + "inputs.json"], logical[prefix + "inputs.zip"])
|
||||
if capsule not in seen_capsules:
|
||||
with zipfile.ZipFile(io.BytesIO(raw(prefix + "inputs.zip"))) as source:
|
||||
inventory(source, inputs)
|
||||
for name, expected in inputs.items():
|
||||
need(pin(source.read(name)) == expected, run + ": source hash " + name)
|
||||
seen_capsules.add(capsule)
|
||||
artifacts = []
|
||||
for line in raw(prefix + "stdout.log").decode("utf-8", errors="replace").splitlines():
|
||||
try:
|
||||
value = json.loads(line)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
if isinstance(value, dict) and value.get("reason") == "compiler-artifact" and value.get("executable"):
|
||||
artifacts.append(value)
|
||||
need(isinstance(result["artifacts"], list), run + ": artifact records")
|
||||
need(artifacts == [record["cargo"] for record in result["artifacts"]], run + ": Cargo JSON artifact relationship")
|
||||
for record in result["artifacts"]:
|
||||
need(set(record) == {"cargo", "readback"}
|
||||
and (record["readback"] is None or valid_pin(record["readback"])), run + ": artifact readback pin")
|
||||
print(run, "recorded exit", result["exitCode"], "inputs", len(inputs), "executable records", len(artifacts))
|
||||
return {"runs": len(runs), "recordedFailures": sum(spec["exitCode"] != 0 for spec in runs.values()),
|
||||
"logicalFiles": len(logical), "storedMembers": len(stored), "sourceCapsulePairs": len(seen_capsules),
|
||||
"captureHelpers": helpers, "sourceProofs": sum("sourceProof" in spec for spec in runs.values())}
|
||||
|
||||
|
||||
def verify_package(root):
|
||||
files = parsed((root / "files.json").read_bytes())
|
||||
need(set(files) == OUTER_FILES, "outer file inventory")
|
||||
for name, expected in files.items():
|
||||
need(valid_pin(expected) and pin((root / name).read_bytes()) == expected, "outer hash: " + name)
|
||||
manifest = parsed((root / "manifest.json").read_bytes())
|
||||
with zipfile.ZipFile(root / "evidence.zip") as archive:
|
||||
inventory(archive, manifest["members"])
|
||||
return verify_data(manifest, archive.read)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("package", nargs="?", type=Path, default=Path(__file__).resolve().parent)
|
||||
args = parser.parse_args()
|
||||
print("PASS offline consistency:", verify_package(args.package))
|
||||
print("Historical receipts only; no source/binary execution or native acceptance claim.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"url": "http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/raw/commit/c655c5eeaaa32c1c21074a7543844ae99a1d6fd5/harness/prerelease37-connection-evidence-20260924/checks/evidence.zip",
|
||||
"bytes": 6087017,
|
||||
"sha256": "30087db61aa4e045a522cf9db56d6b85b595012ebb6a1bb1d03bfb8bb213bd5c",
|
||||
"seconds": 26.032,
|
||||
"status": "PASS exact downloaded published artifact bytes"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
* -text whitespace=cr-at-eol
|
||||
@@ -0,0 +1,38 @@
|
||||
# Prerelease 37: quick manual checks
|
||||
|
||||
Extract `test-models.zip`. The `allocation` and `process` folders each contain a complete project. Use a fresh writable copy of each supplied test folder and open its `project.arcadia`; keep the included subfolders together. Use a normal desktop-sized window. These are checks to perform on this prerelease, not a claim that physical Shift+click was covered by earlier automated native trials.
|
||||
|
||||
## Allocation: select and move the whole group
|
||||
|
||||
1. Open **Allocation group native**, then **01 OA assign**. Click **X - receive**, hold Shift and click **Y - assess**, then release Shift. Both cards should stay outlined and the selection strip should say **2 selected**. Properties should show only the primary card, Y. Shift-click Y again to check removal, then add it again.
|
||||
2. If Shift selection fails, record that failure. To continue checking movement separately, right-click the second card and choose **Add to selection**. This fallback does not make the Shift check pass.
|
||||
3. Drag X (the selected nonprimary card) into **C - destination**, keeping the group inside C. Both X and Y should move together, their connection should follow, and both should now be assigned to C. Y should remain primary; feedback should describe assigning both work items.
|
||||
4. Press **Save all**, then **Ctrl+Z once**: both cards and their assignments should return together. Press **Ctrl+Y once**: both should return to C together. Save, go **Home**, reopen this saved project from recent models, and confirm the saved result. Selection may clear and the view may refit after reopening.
|
||||
5. Repeat steps 1–4 in **02 SA assign**. Click the Function card bodies, avoiding the small ports. This manual SA reopen is a new check; earlier native evidence did not cover it.
|
||||
|
||||
## Allocation: refuse the whole unsupported set
|
||||
|
||||
In a fresh **02 SA assign**, select X, then add Y's small input port to the selection. Confirm **2 selected** and that Properties identifies a **Port**, not Y's Function. Attempt to drag X. Nothing should move or become reassigned; feedback should tell you that ports/rows/connections cannot participate in this movement. It must not silently move only X. If the second item is the Function rather than the Port, correct the selection before judging the result.
|
||||
|
||||
## Allocation: explicit Arrange and ordinary performers
|
||||
|
||||
1. Open **03 OA arrange**. Select **P - team** and **V - independent work**. An ordinary drag should refuse this mixed selection and direct you to **Arrange**.
|
||||
2. Choose **Arrange** (or **Alt+A**), then drag the selected nonprimary member downward into clear space while keeping the headers visible. P, its unselected child **U - contained work**, and V should move together. The arm should be consumed; feedback should describe **2 selected / 3 placements**. U must remain assigned to P and V must remain unallocated: Arrange changes layout, not responsibility.
|
||||
3. Check one Undo/Redo and Save/reopen as above. Repeat in **04 SA arrange**.
|
||||
4. In a fresh **01 OA assign**, select the **A - origin team** and **B - other team** headers. Leave Arrange off. Drag A with B primary. Both performers, their contained X/Y cards and the connecting route should move together; X must stay assigned to A and Y to B. Check one Undo/Redo.
|
||||
|
||||
The Allocation fixture starts with **18 advisory model warnings** about omitted behavior coverage, unallocated independent leaves and empty port payload contracts. They are expected; a new source error, crash or unexpected additional warning is not.
|
||||
|
||||
## Process: reorder selected occurrences as one operation
|
||||
|
||||
Use the separate **Process group native** `process` folder included in `test-models.zip`.
|
||||
|
||||
1. Open its `project.arcadia`, choose **Behavior & scenarios**, then **01 OA group order**. One column contains **Card A**, **Card B**, **Card C**; the other contains **Downstream U/V**.
|
||||
2. Click C, then Shift-click A. Confirm exactly two selected; A is primary. Drag nonprimary C onto the **lower half of Card B**. The insertion feedback should mean **After B**.
|
||||
3. On release, the first column should read **B, A, C**. A/C keep their original relative order despite reverse selection, and Downstream U/V remain in their column. A singleton move of C would leave A/B/C unchanged, so verify that A actually moved too.
|
||||
4. Check **one Ctrl+Z**, **one Ctrl+Y**, **Save**, and reopen. Both selected occurrences must undo/redo together; connection guards and reusable Activity details must stay unchanged.
|
||||
5. On a fresh copy, select a card from each column (for example A and Downstream U) and attempt a reorder. The whole set should refuse; it must not silently move an eligible subset. If desired, repeat the Shift-selection check using **Add to selection** only as the separately recorded fallback.
|
||||
|
||||
For a failure, record the diagram/process, exact selected items, primary item, drag start/drop target, feedback and whether one Undo recovered it. A screenshot before and after helps.
|
||||
|
||||
These are test inputs, not an application build. The combined prerelease must still open them and pass these checks. Earlier fixture acceptance does not establish the behavior of the new executable.
|
||||
@@ -0,0 +1,131 @@
|
||||
{
|
||||
"purpose": "Immutable test inputs for new manual prerelease checks; no combined-build acceptance",
|
||||
"origins": {
|
||||
"allocation": {
|
||||
"source": "495464e84b5bae8ddad93e9fc69ef353e7f1c884",
|
||||
"archive": "harness/allocation-group-app-evidence-20260924/native/evidence.zip",
|
||||
"archivePin": {
|
||||
"bytes": 12480731,
|
||||
"sha256": "6c4fb3151d46141b56e0e5df24d8ded0c644fa01bf6b2b575952eea55ae3c61e"
|
||||
},
|
||||
"prefix": "runs/run-3/raw/fixture/source/",
|
||||
"files": {
|
||||
"oa/activities/activity_oa_arrange_u.arcadia": {
|
||||
"bytes": 470,
|
||||
"sha256": "eb2733307ff3d48878e591df7e6421e0c73525ab8cc2f97ad7aef628dc3042a3"
|
||||
},
|
||||
"oa/activities/activity_oa_arrange_v.arcadia": {
|
||||
"bytes": 233,
|
||||
"sha256": "678f487ec27ce397e87e63eb85797be385aca67c15ecff709fa267acacb9c1ce"
|
||||
},
|
||||
"oa/activities/activity_oa_assign_x.arcadia": {
|
||||
"bytes": 455,
|
||||
"sha256": "6235e0229ffa2758f75db3332a70e12def6e71a494d6a8a986268b3177b1b39f"
|
||||
},
|
||||
"oa/activities/activity_oa_assign_y.arcadia": {
|
||||
"bytes": 220,
|
||||
"sha256": "d4ec5c10773e602f04d1c6c99f6b732232b565dd84a9e1dec4eba5e0b571be0e"
|
||||
},
|
||||
"oa/participants.arcadia": {
|
||||
"bytes": 1289,
|
||||
"sha256": "796778a58c4224389440ca4d0be8baaa0333606737fd3b5c35d2330507cc71b2"
|
||||
},
|
||||
"project.arcadia": {
|
||||
"bytes": 1081,
|
||||
"sha256": "93d37fc154dd998df5a2f970885fd6242c215d1835fbeec7eafdbc9facb95f67"
|
||||
},
|
||||
"sa/functions/function_sa_arrange_u.arcadia": {
|
||||
"bytes": 855,
|
||||
"sha256": "5aacf5fce5fe4764e4a852f9b9cdef87fe8bf72215c3efcfae07e19bee400c6a"
|
||||
},
|
||||
"sa/functions/function_sa_arrange_v.arcadia": {
|
||||
"bytes": 517,
|
||||
"sha256": "954192058737353a9877c6dee6ef6b506cddd437752080011e4e60963326fd86"
|
||||
},
|
||||
"sa/functions/function_sa_assign_x.arcadia": {
|
||||
"bytes": 834,
|
||||
"sha256": "837f7bf6166f426e19b369bbeabd755fdd69643778e29634ac0f3c803635eae0"
|
||||
},
|
||||
"sa/functions/function_sa_assign_y.arcadia": {
|
||||
"bytes": 500,
|
||||
"sha256": "7215d5c7014f26096f89e2eb2ede624c34b27c0a88c730a7d5bf0d8d1f64aed0"
|
||||
},
|
||||
"sa/participants.arcadia": {
|
||||
"bytes": 1286,
|
||||
"sha256": "bef29a13814e146ea99669471facb99f155715db9baec1cca29f6c7f66db54fe"
|
||||
},
|
||||
"views/diagram_oa_arrange.arcadia": {
|
||||
"bytes": 1123,
|
||||
"sha256": "9de36df685c6136871f9295fd429e8f41205c5d6a78b207ef206708ab510615b"
|
||||
},
|
||||
"views/diagram_oa_assign.arcadia": {
|
||||
"bytes": 1518,
|
||||
"sha256": "2ba92dd75c1b76b639fbdf4b66aad5cf6fb97a3efefad42ceabea594f8d5a5a3"
|
||||
},
|
||||
"views/diagram_other_view.arcadia": {
|
||||
"bytes": 584,
|
||||
"sha256": "5205dd9be203acdf5a15e98882d26e0370daad9bb5e84e10e15f74b4d6c31b7d"
|
||||
},
|
||||
"views/diagram_sa_arrange.arcadia": {
|
||||
"bytes": 1137,
|
||||
"sha256": "4ab02e6082ef638b5f583d5b9a12cc3ba440a44c58dc2b80d5c27e122eae7d3c"
|
||||
},
|
||||
"views/diagram_sa_assign.arcadia": {
|
||||
"bytes": 1532,
|
||||
"sha256": "5319a4c31a558b53c7b2d87d902287d999fe76fd84fcfce9f80a7610f4020488"
|
||||
}
|
||||
}
|
||||
},
|
||||
"process": {
|
||||
"source": "b743a10d51032dc1655b94fed5e49fbbbcee83f4",
|
||||
"archive": "harness/multiselect-process-evidence-20260923/native/evidence.zip",
|
||||
"archivePin": {
|
||||
"bytes": 12355442,
|
||||
"sha256": "e94c3e9f2fb644c8e415e09b75eaf973d9c2a93534ad9e41d69793a3c11e8f88"
|
||||
},
|
||||
"prefix": "runs/run-3/raw/fixture/source/",
|
||||
"files": {
|
||||
"oa/activities/activity_oa_downstream_work.arcadia": {
|
||||
"bytes": 171,
|
||||
"sha256": "7ebf9516f9f016139a4504649cd41e327b59b6798581bf9f65af2d30cfcd5a0e"
|
||||
},
|
||||
"oa/activities/activity_oa_shared_work.arcadia": {
|
||||
"bytes": 159,
|
||||
"sha256": "56139bf120e41b752d50aa85955b96eddc0c7e573fe493248deed1b49933a95d"
|
||||
},
|
||||
"oa/behavior/process_oa_process.arcadia": {
|
||||
"bytes": 1737,
|
||||
"sha256": "2f39cdf59c59bf56099ce5f07e339fa3f4fd0c8e964545f19c995a2341aba2e0"
|
||||
},
|
||||
"oa/behavior/process_reusable.arcadia": {
|
||||
"bytes": 429,
|
||||
"sha256": "21d216c39d0990a3ebc2d40b02d00c1cc126185def1fb5342c3205ca890ae04a"
|
||||
},
|
||||
"oa/behavior/process_reuse_trial.arcadia": {
|
||||
"bytes": 730,
|
||||
"sha256": "2384e5b81dc64ec229a0b1664333ee48a8a251b7ac8138584215a8611472fe2a"
|
||||
},
|
||||
"project.arcadia": {
|
||||
"bytes": 753,
|
||||
"sha256": "a942705a988f5bfa35f2bdae273bce3fc6dd9afff8267a96483df598dd5a13a6"
|
||||
},
|
||||
"sa/behavior/process_sa_chain.arcadia": {
|
||||
"bytes": 1734,
|
||||
"sha256": "b6aa1d90a113dacffdbc980f1fa6c6b2b85050921622c57492c80a1602e11a16"
|
||||
},
|
||||
"sa/functions/function_sa_downstream_work.arcadia": {
|
||||
"bytes": 171,
|
||||
"sha256": "95ee9f35a41a4580c51bbf56b1b00717969adb77a0c05a1f30740c4c23788a02"
|
||||
},
|
||||
"sa/functions/function_sa_shared_work.arcadia": {
|
||||
"bytes": 159,
|
||||
"sha256": "0117bcfe32321b63d6b05b745f67aa57a565026eed68eb5a98300b7265941e9a"
|
||||
},
|
||||
"traceability.arcadia": {
|
||||
"bytes": 528,
|
||||
"sha256": "c5eaf8dc3c09dc56129a73da9630bb3fda99a0cfc9a8fa266e3a6ee32f53deb6"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
17327e563af333fd9913f6d8b8a7e6ad0e095cea33393a5cad7a8b3397ffb185 test-models.zip
|
||||
Reference in New Issue
Block a user