Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eda2d0d162 | ||
|
|
2a80e1699b | ||
|
|
8307891085 | ||
|
|
5ae9137276 | ||
|
|
72827ccefa | ||
|
|
8e0df9797d | ||
|
|
5fb9770df3 | ||
|
|
110a34d994 | ||
|
|
4aef6b0cb7 | ||
|
|
2b0b9d4df1 | ||
|
|
b9f14716b7 | ||
|
|
fee30f9b40 | ||
|
|
1d7f916083 | ||
|
|
3d60fd5226 | ||
|
|
e7f5bdff67 | ||
|
|
2e8a70aa05 | ||
|
|
0337f57087 | ||
|
|
e4336d11db | ||
|
|
ff615d0ee0 |
@@ -4,12 +4,12 @@
|
||||
|
||||
- Follow `PROJECT_GOAL.md` and the user's current instructions. Work autonomously within scope. Resolve discoverable questions yourself; record, test, and revise reversible assumptions. Ask only when a consequential decision needs information or authorization not already available. Continue independent useful work while blocked elsewhere.
|
||||
- Map unfamiliar domains through primary sources, comparable systems, and real user workflows. Identify underlying needs, valuable concepts, and avoidable friction; evaluate improvements rather than merely reproducing features. Synthesize findings into decisions or experiments, and stop research when another action offers greater value.
|
||||
- Make specifications precise enough for compatible independent implementations: definitions, relationships, invariants, valid/invalid examples, and lifecycle or transformation rules where relevant. Trace important requirements and decisions to implementation and verification. Resolve specification ambiguity at its source when implementation exposes it.
|
||||
- Make specifications precise enough for compatible independent implementations: definitions, relationships, invariants, valid / invalid examples, and lifecycle or transformation rules where relevant. Trace important requirements and decisions to implementation and verification. Resolve specification ambiguity at its source when implementation exposes it.
|
||||
- For consequential uncertain choices, compare materially different candidates against explicit user-derived criteria. Ask independent critics to challenge assumptions and attempt to falsify proposals; refine or replace candidates as evidence warrants. Record the choice, evidence, tradeoffs, rejected approaches, and reasons to revisit it. Use proportionate effort for reversible local choices.
|
||||
- Plan milestones that resolve uncertainty or deliver useful capability, with explicit dependencies. Test foundational assumptions before substantial downstream work. At checkpoints, assess progress toward the actual objective and replan when evidence invalidates the strategy or progress stalls.
|
||||
- Define observable success and suitable evaluators before repeated improvement: invariants, examples, round trips, transformations, performance checks, realistic user tasks, or explicit review rubrics. Ensure the evaluator measures the intended outcome. For experiments, record the question, expectations, method, result, and implications; isolate exploratory prototypes from production architecture.
|
||||
- Build and run meaningful changes, exercise actual behavior, and inspect useful logs. Investigate crashes, serious warnings, corrupted state, and interaction defects. For GUI work, use Computer Use or equivalent tools to inspect realistic workflows, cognitive load, editing/selection, navigation, feedback, recovery, loading/empty/failure states, accessibility, layout, focus/disabled states, and responsiveness.
|
||||
- Match independent reviews to the artifact and risks. For languages or formats, include ambiguity, round trips, human/AI editability, and version-control behavior where relevant. Verify findings, fix the appropriate conceptual, specification, architectural, implementation, or interaction layer, and recheck affected dependents. Reuse valid evidence; repeat checks for concrete risks, changes, findings, or required gates. Compilation, screenshots, and worker reports alone do not establish correctness or usability.
|
||||
- Build and run meaningful changes, exercise actual behavior, and inspect useful logs. Investigate crashes, serious warnings, corrupted state, and interaction defects. For GUI work, use Computer Use or equivalent tools to inspect realistic workflows, cognitive load, editing / selection, navigation, feedback, recovery, loading / empty / failure states, accessibility, layout, focus / disabled states, and responsiveness.
|
||||
- Match independent reviews to the artifact and risks. For languages or formats, include ambiguity, round trips, human / AI editability, and version-control behavior where relevant. Verify findings, fix the appropriate conceptual, specification, architectural, implementation, or interaction layer, and recheck affected dependents. Reuse valid evidence; repeat checks for concrete risks, changes, findings, or required gates. Compilation, screenshots, and worker reports alone do not establish correctness or usability.
|
||||
- Favor the simplest maintainable design satisfying known requirements and justified extensibility. Avoid speculative abstraction, unrelated refactoring, and repeating approaches already invalidated by evidence.
|
||||
|
||||
## User input
|
||||
@@ -24,10 +24,10 @@ Keep one authoritative home for each kind of information. Use links and brief sc
|
||||
|
||||
| Information | Authoritative home |
|
||||
| --- | --- |
|
||||
| Live plans, priorities, dependencies, ownership, status, blockers, and handoffs | Gitea issues/milestones; linked PRs hold change review and validation. |
|
||||
| Specifications, architecture, interfaces, and build/run documentation | Established version-controlled project documents. |
|
||||
| Live plans, priorities, dependencies, ownership, status, blockers, and handoffs | Gitea issues / milestones; linked PRs hold change review and validation. |
|
||||
| Specifications, architecture, interfaces, and build / run documentation | Established version-controlled project documents. |
|
||||
| Durable decisions and reusable evidence | `harness/DECISIONS.md` and relevant topic notes, or existing equivalents. |
|
||||
| Navigation and integrated-state checkpoint | `harness/STATE.md`: links, verified code commit/release, capabilities, and material limitations. No live task ledger. |
|
||||
| Navigation and integrated-state checkpoint | `harness/STATE.md`: links, verified code commit / release, capabilities, and material limitations. No live task ledger. |
|
||||
| Shared entrypoint | Gitea Wiki: overview and links, without duplicating versioned documentation. |
|
||||
| Published deliverables | Gitea releases, tied to source commits and verification. |
|
||||
|
||||
@@ -38,9 +38,9 @@ Use Gitea for live coordination and the relevant code version for implementation
|
||||
All independent agents coordinating through Gitea run on completely separate machines, each with its own clone, filesystem, tools, processes, and runtime. Gitea and pushed Git history are the shared communication and delivery channels. Local files (including `harness`), unpushed commits, worktrees, test results, and localhost services are not accessible to peers. Matching branch names or paths do not imply synchronized contents.
|
||||
|
||||
1. On starting or resuming, including after compaction, inspect the working branch, local changes, applicable instructions, relevant shared records, and existing implementation. Recover context from authoritative records. Check for newer shared instructions on the actual remote default branch without overwriting local work. Read what is relevant; avoid repeated full-history scans and constant polling.
|
||||
2. Search before creating tasks. Claim a meaningful outcome in its Gitea issue with a stable session identity, scope, remote repository/branch, and acceptance criteria. Keep small steps within that task. Recheck after claiming; comments are not atomic locks. Respect existing ownership and resolve overlapping claims before overlapping work.
|
||||
3. Independent sessions are peers. Each owns its contribution and delegated work on its own machine. Record one integration/release owner when a milestone needs shared coordination. Use distinct remote work branches, partition files/interfaces, and coordinate changes to shared specifications and documents. Worktrees provide local isolation only; they do not share work between machines.
|
||||
4. At meaningful checkpoints or handoffs, record changed results, commit/PR links, verification commands and environment, blockers, and next actions. Identify the tested code version and label uncommitted or unpublished work as local-only. Before asking peers to consume results, push the required code and versioned documentation, including relevant `harness` updates; publish other required artifacts in Gitea and link them. Use repository-relative paths with commit references, reproducible commands, and necessary environment details; local paths and localhost URLs alone are not handoffs. Do not assume another agent is running or has read a message; silence alone does not establish abandonment. Record evidence and preserve existing work when reassigning a task.
|
||||
2. Search before creating tasks. Claim a meaningful outcome in its Gitea issue with a stable session identity, scope, remote repository / branch, and acceptance criteria. Keep small steps within that task. Recheck after claiming; comments are not atomic locks. Respect existing ownership and resolve overlapping claims before overlapping work.
|
||||
3. Independent sessions are peers. Each owns its contribution and delegated work on its own machine. Record one integration / release owner when a milestone needs shared coordination. Use distinct remote work branches, partition files / interfaces, and coordinate changes to shared specifications and documents. Worktrees provide local isolation only; they do not share work between machines.
|
||||
4. At meaningful checkpoints or handoffs, record changed results, commit / PR links, verification commands and environment, blockers, and next actions. Identify the tested code version and label uncommitted or unpublished work as local-only. Before asking peers to consume results, push the required code and versioned documentation, including relevant `harness` updates; publish other required artifacts in Gitea and link them. Use repository-relative paths with commit references, reproducible commands, and necessary environment details; local paths and localhost URLs alone are not handoffs. Do not assume another agent is running or has read a message; silence alone does not establish abandonment. Record evidence and preserve existing work when reassigning a task.
|
||||
5. Follow project review and merge conventions. Fetch the relevant remote branches and inspect the identified commits before reviewing or integrating another machine's work. Resolve conflicts while preserving others' edits, and verify the combined result locally; report material environment differences. Local instruction-file edits reach peers only after publication and retrieval. Do not overwrite others' work or force-push shared history to resolve coordination problems.
|
||||
6. Close work when its acceptance criteria are met and the result is integrated or delivered as appropriate. Mark duplicates and superseded tasks explicitly with replacement links. Then choose further useful unowned work while the requested objective remains incomplete.
|
||||
|
||||
@@ -64,10 +64,10 @@ Preserve concise records of decisions and rationale, useful experiments, failed
|
||||
|
||||
Consolidate duplicates, repair stale guidance, and remove obsolete temporary files alongside useful project work. Before removing material, check references, active ownership, and ongoing use; preserve unique knowledge and update affected links. Prefer closing, superseding, or archiving history over deletion. User input is excluded from autonomous cleanup.
|
||||
|
||||
If legacy local task ledgers or plans duplicate Gitea, migrate their live content once into existing or appropriately scoped issues, preserve useful decisions/evidence, and mark the old records superseded. Record the migration so later sessions do not repeat it. Do not create empty documents or maintain records for ceremony.
|
||||
If legacy local task ledgers or plans duplicate Gitea, migrate their live content once into existing or appropriately scoped issues, preserve useful decisions / evidence, and mark the old records superseded. Record the migration so later sessions do not repeat it. Do not create empty documents or maintain records for ceremony.
|
||||
|
||||
## Delivery
|
||||
|
||||
Publish versioned Gitea releases at meaningful working milestones; mark unfinished versions as prereleases. Check existing releases/tags and coordinate one owner and version before publishing. Build from an identified integrated commit and verify the packaged result and launch steps.
|
||||
Publish versioned Gitea releases at meaningful working milestones; mark unfinished versions as prereleases. Check existing releases / tags and coordinate one owner and version before publishing. Build from an identified integrated commit and verify the packaged result and launch steps.
|
||||
|
||||
Include runnable builds or appropriate artifacts, concise release notes, build/run/deployment instructions as applicable, known limitations, tested platforms, and verification evidence. Confirm publication and artifact availability. Use existing authorized API access or project tooling when MCP lacks a needed release/upload operation. Do not silently replace published versions with different contents.
|
||||
Include runnable builds or appropriate artifacts, concise release notes, build / run / deployment instructions as applicable, known limitations, tested platforms, and verification evidence. Confirm publication and artifact availability. Use existing authorized API access or project tooling when MCP lacks a needed release / upload operation. Do not silently replace published versions with different contents.
|
||||
|
||||
+2
-2
@@ -17,14 +17,14 @@ Apply these criteria to the scope requested by the user:
|
||||
- Consequential decisions have evidence and rationale. Major assumptions have been validated, explicitly accepted, or isolated without compromising required behavior.
|
||||
- The result builds, runs, and operates on the required platforms. Relevant tests and evaluators pass; integrations, edge cases, and failure states have been exercised against the actual result.
|
||||
- User-facing behavior and usability have been exercised directly, using representative data and workflows.
|
||||
- A fresh independent review covers requirements, domain/semantic consistency, usability, architecture, correctness, regressions, runtime behavior, documentation, and remaining risks. Valid material findings are addressed, blocking and high-impact issues resolved, and affected behavior reverified; no known blocker or high-severity defect remains.
|
||||
- A fresh independent review covers requirements, domain / semantic consistency, usability, architecture, correctness, regressions, runtime behavior, documentation, and remaining risks. Valid material findings are addressed, blocking and high-impact issues resolved, and affected behavior reverified; no known blocker or high-severity defect remains.
|
||||
- Deliverables are maintainable, appropriately extensible, documented, and published as required by `AGENTS.md`. Obsolete scaffolding is removed; documentation and shared state accurately describe the result. Another engineer or agent can reproduce the verification and continue the work.
|
||||
|
||||
If required work or verification is blocked, record the specific gap and continue independent useful work without claiming completion.
|
||||
|
||||
## Final handoff
|
||||
|
||||
Link the delivered result and summarize capabilities, discoveries, architecture and decisions, relevant rejected approaches, verification and independent review, remaining limitations, and exact build/run/deployment/verification instructions. Refer to authoritative records instead of repeating the project history.
|
||||
Link the delivered result and summarize capabilities, discoveries, architecture and decisions, relevant rejected approaches, verification and independent review, remaining limitations, and exact build / run / deployment / verification instructions. Refer to authoritative records instead of repeating the project history.
|
||||
|
||||
## USER-OWNED APPLICATION SPECIFICATION
|
||||
|
||||
|
||||
@@ -1019,7 +1019,6 @@ impl App {
|
||||
iced::time::every(std::time::Duration::from_secs(10)).map(|_| Message::CheckpointTick),
|
||||
window::close_requests().map(|id| Message::Request(Pending::Close(id))),
|
||||
window::resize_events().map(|(_, size)| Message::WindowResized(size)),
|
||||
architecture_authoring::subscription(self),
|
||||
iced::event::listen_with(|event, status, _| {
|
||||
let iced::Event::Keyboard(keyboard::Event::KeyPressed {
|
||||
key,
|
||||
@@ -1077,7 +1076,9 @@ impl App {
|
||||
// navigation and close retain raw input until explicit completion/cancel.
|
||||
if self.architecture.is_some() {
|
||||
if matches!(message, Message::Escape) {
|
||||
return self.architecture_event(architecture_authoring::Event::Cancel);
|
||||
self.architecture = None;
|
||||
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
|
||||
return Task::none();
|
||||
}
|
||||
let placing = self.architecture.as_ref().is_some_and(|s| s.saved.placing);
|
||||
if !matches!(
|
||||
|
||||
@@ -3,8 +3,6 @@ use super::*;
|
||||
use iced::widget::{button, column, pick_list, row, scrollable, text, text_input};
|
||||
use serde::{Deserialize, Serialize};
|
||||
mod palette;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub use palette::view as palette;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -23,35 +21,9 @@ pub enum Event {
|
||||
Filter(Filter),
|
||||
ShowPlaced(bool),
|
||||
Page(usize),
|
||||
CapturedEscape,
|
||||
Edit,
|
||||
Cancel,
|
||||
}
|
||||
|
||||
pub fn subscription(app: &App) -> Subscription<Message> {
|
||||
if app.architecture.as_ref().is_some_and(State::inline) {
|
||||
iced::event::listen_with(captured_escape)
|
||||
} else {
|
||||
Subscription::none()
|
||||
}
|
||||
}
|
||||
|
||||
fn captured_escape(
|
||||
event: iced::Event,
|
||||
status: iced::event::Status,
|
||||
_: iced::window::Id,
|
||||
) -> Option<Message> {
|
||||
// A focused Iced text input captures Escape while dropping its focus. Only
|
||||
// inline architecture naming opts into also cancelling that same keypress.
|
||||
(status == iced::event::Status::Captured
|
||||
&& matches!(
|
||||
event,
|
||||
iced::Event::Keyboard(iced::keyboard::Event::KeyPressed {
|
||||
key: iced::keyboard::Key::Named(iced::keyboard::key::Named::Escape),
|
||||
..
|
||||
})
|
||||
))
|
||||
.then_some(Message::Architecture(Event::CapturedEscape))
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub enum Filter {
|
||||
@@ -364,20 +336,14 @@ impl App {
|
||||
}
|
||||
return iced::widget::operation::focus("architecture-name");
|
||||
}
|
||||
Event::CapturedEscape => {
|
||||
if self.architecture.as_ref().is_some_and(State::inline) {
|
||||
return self.architecture_event(Event::Cancel);
|
||||
}
|
||||
}
|
||||
Event::Cancel => {
|
||||
self.architecture = None;
|
||||
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
|
||||
self.status = "Cancelled · Select / move".into();
|
||||
}
|
||||
Event::Name(value) => {
|
||||
if value.len() <= 16384
|
||||
&& let Some(s) = &mut self.architecture
|
||||
&& let Intent::Create { name, .. } | Intent::Rename { name, .. } =
|
||||
&& let (Intent::Create { name, .. } | Intent::Rename { name, .. }) =
|
||||
&mut s.saved.intent
|
||||
{
|
||||
*name = value;
|
||||
@@ -409,6 +375,13 @@ impl App {
|
||||
s.saved.page = page.min(1_000_000);
|
||||
}
|
||||
}
|
||||
Event::Edit => {
|
||||
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
|
||||
if let Some(s) = &mut self.architecture {
|
||||
s.saved.placing = false;
|
||||
}
|
||||
return iced::widget::operation::focus("architecture-name");
|
||||
}
|
||||
Event::Pick(id) => {
|
||||
if !candidates(self)
|
||||
.iter()
|
||||
@@ -449,10 +422,7 @@ impl App {
|
||||
}
|
||||
}
|
||||
Event::Click(context, point) => {
|
||||
if self.route_context() != Some(context)
|
||||
|| !point.x.is_finite()
|
||||
|| !point.y.is_finite()
|
||||
{
|
||||
if self.route_context() != Some(context) {
|
||||
return Task::none();
|
||||
}
|
||||
if self.architecture.as_ref().is_some_and(|s| s.saved.placing) {
|
||||
@@ -490,10 +460,6 @@ impl App {
|
||||
self.status =
|
||||
"The diagram changed. The name is retained; cancel and restart this placement."
|
||||
.into();
|
||||
if let Some(s) = &mut self.architecture {
|
||||
s.saved.placing = false;
|
||||
s.error = Some(self.status.clone());
|
||||
}
|
||||
return;
|
||||
}
|
||||
let diagram = s.saved.diagram;
|
||||
@@ -587,10 +553,9 @@ pub fn inline_editor(app: &App) -> Option<Element<'_, Message>> {
|
||||
row![
|
||||
iced::widget::Space::new().width(x),
|
||||
iced::widget::opaque(
|
||||
iced::widget::container(scrollable(name_editor(app)))
|
||||
iced::widget::container(name_editor(app))
|
||||
.padding(10)
|
||||
.width(width)
|
||||
.max_height((size.height - y - 8.).max(0.))
|
||||
.style(crate::style::card)
|
||||
)
|
||||
]
|
||||
|
||||
@@ -165,7 +165,7 @@ fn tool<'a>(
|
||||
.into()
|
||||
}
|
||||
pub fn view(app: &App) -> Element<'_, Message> {
|
||||
let mut content = iced::widget::column![tool(
|
||||
let mut content = column![tool(
|
||||
app,
|
||||
Icon::Collapse(app.architecture_palette_expanded),
|
||||
if app.architecture_palette_expanded {
|
||||
|
||||
@@ -1,378 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
fn setup(layer: Layer) -> App {
|
||||
let mut app = App::with_project(Project::empty());
|
||||
app.layer = layer;
|
||||
app.active_diagram = None;
|
||||
let _ = app.update(Message::AddDiagram);
|
||||
app.zoom = 0.75;
|
||||
app.pan = iced::Vector::new(37., -19.);
|
||||
app
|
||||
}
|
||||
fn send(app: &mut App, event: Event) {
|
||||
let _ = app.update(Message::Architecture(event));
|
||||
}
|
||||
fn create(app: &mut App, kind: Kind, name: &str) -> Id {
|
||||
send(app, Event::Start(kind, Some(Position { x: 120., y: 160. })));
|
||||
send(app, Event::Name(name.into()));
|
||||
send(app, Event::Submit);
|
||||
assert!(app.architecture.is_none(), "{}", app.status);
|
||||
app.selected
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn architecture_allocated_work_is_root_owned_and_one_exact_history_change() {
|
||||
for (layer, participant, work) in [
|
||||
(Layer::Oa, Kind::Entity, Kind::Activity),
|
||||
(Layer::Sa, Kind::System, Kind::Function),
|
||||
] {
|
||||
let mut app = setup(layer);
|
||||
let performer = create(&mut app, participant, "Control team");
|
||||
let before = app.document.bundle().clone();
|
||||
let camera = (app.zoom, app.pan);
|
||||
let point = Position { x: 151., y: 221. };
|
||||
send(&mut app, Event::Allocate(work, performer, Some(point)));
|
||||
send(&mut app, Event::Name(" Report 雨 ".into()));
|
||||
assert_eq!(app.document.bundle(), &before);
|
||||
send(&mut app, Event::Submit);
|
||||
assert!(app.architecture.is_none(), "{}", app.status);
|
||||
let created = app.selected;
|
||||
let element = &app.project().elements[&created];
|
||||
assert_eq!(element.kind(), work);
|
||||
assert_eq!(element.name, " Report 雨 ");
|
||||
assert_eq!(element.parent, None);
|
||||
assert!(app.project().relations.values().any(|r| {
|
||||
r.kind() == RelationKind::Allocation && r.source() == performer && r.target() == created
|
||||
}));
|
||||
let d = &app.project().diagrams[&app.active_diagram.unwrap()];
|
||||
assert_eq!(d.nodes[&app.selected_node.unwrap()].position, point);
|
||||
assert_eq!((app.zoom, app.pan), camera);
|
||||
let accepted = app.document.bundle().clone();
|
||||
let _ = app.update(Message::Undo);
|
||||
assert_eq!(app.document.bundle(), &before);
|
||||
let _ = app.update(Message::Redo);
|
||||
assert_eq!(app.document.bundle(), &accepted);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn architecture_f2_routes_selected_node_to_inline_rename_and_exact_undo() {
|
||||
use iced::widget::canvas::{Event as CanvasEvent, Program};
|
||||
use iced::{keyboard, mouse};
|
||||
let mut app = setup(Layer::Oa);
|
||||
let element = create(&mut app, Kind::Entity, "Original team");
|
||||
let before = app.document.bundle().clone();
|
||||
let diagrams = app.project().diagrams.clone();
|
||||
let camera = (app.zoom, app.pan);
|
||||
let bounds = iced::Rectangle::new(iced::Point::ORIGIN, iced::Size::new(600., 380.));
|
||||
let canvas = crate::diagram::DiagramCanvas {
|
||||
project: app.project(),
|
||||
view_index: None,
|
||||
diagram: app.active_diagram.unwrap(),
|
||||
selected: element,
|
||||
zoom: app.zoom,
|
||||
pan: app.pan,
|
||||
context: app.route_context().unwrap(),
|
||||
selected_bend: None,
|
||||
editable: true,
|
||||
placing: false,
|
||||
connection: None,
|
||||
};
|
||||
let mut state = crate::diagram::State::default();
|
||||
let key = CanvasEvent::Keyboard(keyboard::Event::KeyPressed {
|
||||
key: keyboard::Key::Named(keyboard::key::Named::F2),
|
||||
modified_key: keyboard::Key::Named(keyboard::key::Named::F2),
|
||||
physical_key: keyboard::key::Physical::Code(keyboard::key::Code::F2),
|
||||
location: keyboard::Location::Standard,
|
||||
modifiers: keyboard::Modifiers::empty(),
|
||||
text: None,
|
||||
repeat: false,
|
||||
});
|
||||
assert!(
|
||||
canvas
|
||||
.update(&mut state, &key, bounds, mouse::Cursor::Unavailable)
|
||||
.is_none()
|
||||
);
|
||||
let _ = canvas.update(
|
||||
&mut state,
|
||||
&CanvasEvent::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)),
|
||||
bounds,
|
||||
mouse::Cursor::Available(iced::Point::new(150., 120.)),
|
||||
);
|
||||
let action = canvas
|
||||
.update(&mut state, &key, bounds, mouse::Cursor::Unavailable)
|
||||
.unwrap();
|
||||
let message = action.into_inner().0.unwrap();
|
||||
let _ = app.update(message);
|
||||
assert!(app.architecture.as_ref().unwrap().inline());
|
||||
assert!(matches!(&app.architecture.as_ref().unwrap().saved.intent,
|
||||
Intent::Rename { element: id, name } if *id == element && name == "Original team"));
|
||||
send(&mut app, Event::Name(" Renamed team 雨 ".into()));
|
||||
assert_eq!(app.document.bundle(), &before);
|
||||
send(&mut app, Event::Submit);
|
||||
assert!(app.architecture.is_none(), "{}", app.status);
|
||||
assert_eq!(app.project().elements[&element].name, " Renamed team 雨 ");
|
||||
assert_eq!(app.project().diagrams, diagrams);
|
||||
assert_eq!((app.zoom, app.pan), camera);
|
||||
let accepted = app.document.bundle().clone();
|
||||
let _ = app.update(Message::Undo);
|
||||
assert_eq!(app.document.bundle(), &before);
|
||||
let _ = app.update(Message::Redo);
|
||||
assert_eq!(app.document.bundle(), &accepted);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn architecture_blank_names_and_escape_never_accept_placeholder_or_rename() {
|
||||
let mut app = setup(Layer::Oa);
|
||||
let element = create(&mut app, Kind::Actor, "Dispatcher");
|
||||
let before = app.document.bundle().clone();
|
||||
let context = app.route_context().unwrap();
|
||||
for event in [
|
||||
Event::Start(Kind::Activity, Some(Position { x: 220., y: 330. })),
|
||||
Event::Rename(context, element, Position { x: 150., y: 200. }),
|
||||
] {
|
||||
// Every cancel changes the gesture context; use the current context for F2.
|
||||
let event = match event {
|
||||
Event::Rename(_, element, point) => {
|
||||
Event::Rename(app.route_context().unwrap(), element, point)
|
||||
}
|
||||
event => event,
|
||||
};
|
||||
send(&mut app, event);
|
||||
send(&mut app, Event::Name(" \t ".into()));
|
||||
send(&mut app, Event::Submit);
|
||||
assert!(app.architecture.as_ref().unwrap().inline());
|
||||
assert!(app.architecture.as_ref().unwrap().error.is_some());
|
||||
assert_eq!(app.document.bundle(), &before);
|
||||
let _ = app.update(Message::Escape);
|
||||
assert!(app.architecture.is_none());
|
||||
assert_eq!(app.document.bundle(), &before);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn architecture_invalid_click_cannot_poison_inline_or_checkpoint_geometry() {
|
||||
let mut app = setup(Layer::Oa);
|
||||
send(&mut app, Event::Start(Kind::Entity, None));
|
||||
let before = app.document.bundle().clone();
|
||||
let context = app.route_context().unwrap();
|
||||
for point in [
|
||||
Position { x: f32::NAN, y: 0. },
|
||||
Position {
|
||||
x: 0.,
|
||||
y: f32::INFINITY,
|
||||
},
|
||||
] {
|
||||
send(&mut app, Event::Click(context, point));
|
||||
let draft = app.architecture.as_ref().unwrap();
|
||||
assert!(draft.unnamed_tool());
|
||||
assert!(validate_saved(app.project(), &draft.saved).is_ok());
|
||||
assert_eq!(app.document.bundle(), &before);
|
||||
}
|
||||
send(&mut app, Event::Click(context, Position { x: 25., y: 30. }));
|
||||
assert!(app.architecture.as_ref().unwrap().inline());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn architecture_stale_submit_keeps_create_and_rename_visible_and_exact() {
|
||||
for rename in [false, true] {
|
||||
for stale_document in [false, true] {
|
||||
let mut app = setup(Layer::Oa);
|
||||
let element = create(&mut app, Kind::Entity, "Accepted team");
|
||||
let point = Position {
|
||||
x: 151.5,
|
||||
y: 227.25,
|
||||
};
|
||||
let event = if rename {
|
||||
Event::Rename(app.route_context().unwrap(), element, point)
|
||||
} else {
|
||||
Event::Start(Kind::Activity, Some(point))
|
||||
};
|
||||
send(&mut app, event);
|
||||
send(
|
||||
&mut app,
|
||||
Event::Name(" Retained engineering name 雨 ".into()),
|
||||
);
|
||||
// Change only the binding. The accepted model/source must not be
|
||||
// rewritten or a rejected draft silently rebound to the new context.
|
||||
if stale_document {
|
||||
app.architecture.as_mut().unwrap().document = Id::new_v4();
|
||||
} else {
|
||||
app.architecture.as_mut().unwrap().revision = app.document.revision() + 1;
|
||||
}
|
||||
let draft = app.architecture.as_ref().unwrap();
|
||||
let saved = serde_json::to_value(&draft.saved).unwrap();
|
||||
let binding = (draft.document, draft.revision);
|
||||
let model = app.project().clone();
|
||||
let source = app.document.bundle().clone();
|
||||
let revision = app.document.revision();
|
||||
let camera = (app.zoom, app.pan);
|
||||
|
||||
for _ in 0..2 {
|
||||
send(&mut app, Event::Submit);
|
||||
let draft = app.architecture.as_ref().unwrap();
|
||||
assert!(
|
||||
draft.inline(),
|
||||
"rename={rename}, stale_document={stale_document}"
|
||||
);
|
||||
assert!(inline_editor(&app).is_some());
|
||||
assert_eq!(draft.error.as_deref(), Some(app.status.as_str()));
|
||||
assert!(app.status.contains("cancel and restart"));
|
||||
assert_eq!(serde_json::to_value(&draft.saved).unwrap(), saved);
|
||||
assert_eq!((draft.document, draft.revision), binding);
|
||||
assert_eq!(app.project(), &model);
|
||||
assert_eq!(app.document.bundle(), &source);
|
||||
assert_eq!(app.document.revision(), revision);
|
||||
assert_eq!((app.zoom, app.pan), camera);
|
||||
}
|
||||
let _ = app.update(Message::Escape);
|
||||
assert!(app.architecture.is_none());
|
||||
assert_eq!(app.project(), &model);
|
||||
assert_eq!(app.document.bundle(), &source);
|
||||
let fresh = create(&mut app, Kind::Actor, "Fresh context");
|
||||
assert_eq!(app.project().elements[&fresh].name, "Fresh context");
|
||||
let _ = app.update(Message::Undo);
|
||||
assert_eq!(app.project(), &model);
|
||||
assert_eq!(app.document.bundle(), &source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn key_event(named: iced::keyboard::key::Named) -> iced::Event {
|
||||
use iced::keyboard;
|
||||
iced::Event::Keyboard(keyboard::Event::KeyPressed {
|
||||
key: keyboard::Key::Named(named),
|
||||
modified_key: keyboard::Key::Named(named),
|
||||
physical_key: keyboard::key::Physical::Code(keyboard::key::Code::Escape),
|
||||
location: keyboard::Location::Standard,
|
||||
modifiers: keyboard::Modifiers::empty(),
|
||||
text: None,
|
||||
repeat: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn focused_text_input_escape(name: &str) -> Message {
|
||||
use iced::advanced::{Layout, Shell, layout, widget};
|
||||
let mut field: iced::Element<'_, (), iced::Theme, ()> = text_input("Engineering name", name)
|
||||
.id("architecture-name")
|
||||
.on_input(|_| ())
|
||||
.into();
|
||||
let mut tree = widget::Tree::new(field.as_widget());
|
||||
let size = iced::Size::new(300., 40.);
|
||||
let node =
|
||||
field
|
||||
.as_widget_mut()
|
||||
.layout(&mut tree, &(), &layout::Limits::new(iced::Size::ZERO, size));
|
||||
tree.state
|
||||
.downcast_mut::<iced::widget::text_input::State<()>>()
|
||||
.focus();
|
||||
let escape = key_event(iced::keyboard::key::Named::Escape);
|
||||
let mut messages = Vec::new();
|
||||
let mut shell = Shell::new(&mut messages);
|
||||
field.as_widget_mut().update(
|
||||
&mut tree,
|
||||
&escape,
|
||||
Layout::new(&node),
|
||||
iced::mouse::Cursor::Unavailable,
|
||||
&(),
|
||||
&mut iced::advanced::clipboard::Null,
|
||||
&mut shell,
|
||||
&iced::Rectangle::new(iced::Point::ORIGIN, size),
|
||||
);
|
||||
assert!(
|
||||
shell.is_event_captured(),
|
||||
"Iced consumes focused-input Escape"
|
||||
);
|
||||
assert!(
|
||||
!tree
|
||||
.state
|
||||
.downcast_ref::<iced::widget::text_input::State<()>>()
|
||||
.is_focused()
|
||||
);
|
||||
captured_escape(
|
||||
escape,
|
||||
iced::event::Status::Captured,
|
||||
iced::window::Id::unique(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn architecture_first_focused_input_escape_cancels_create_and_rename() {
|
||||
for rename in [false, true] {
|
||||
let mut app = setup(Layer::Oa);
|
||||
let element = create(&mut app, Kind::Actor, "Accepted dispatcher");
|
||||
let model = app.project().clone();
|
||||
let source = app.document.bundle().clone();
|
||||
let revision = app.document.revision();
|
||||
let camera = (app.zoom, app.pan);
|
||||
let point = Position { x: 439., y: 328.2 };
|
||||
let event = if rename {
|
||||
Event::Rename(app.route_context().unwrap(), element, point)
|
||||
} else {
|
||||
Event::Start(Kind::Actor, Some(point))
|
||||
};
|
||||
send(&mut app, event);
|
||||
let name = " Unaccepted long engineering name 雨 ";
|
||||
send(&mut app, Event::Name(name.into()));
|
||||
assert!(app.architecture.as_ref().unwrap().inline());
|
||||
let message = focused_text_input_escape(name);
|
||||
let _ = app.update(message);
|
||||
assert!(app.architecture.is_none());
|
||||
assert!(inline_editor(&app).is_none());
|
||||
assert_eq!(app.status, "Cancelled · Select / move");
|
||||
assert_eq!(app.project(), &model);
|
||||
assert_eq!(app.document.bundle(), &source);
|
||||
assert_eq!(app.document.revision(), revision);
|
||||
assert_eq!((app.zoom, app.pan), camera);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn architecture_captured_escape_does_not_cancel_other_controls_or_placement() {
|
||||
let mut app = setup(Layer::Oa);
|
||||
for event in [
|
||||
None,
|
||||
Some(Event::Start(Kind::Entity, None)),
|
||||
Some(Event::Existing(None)),
|
||||
] {
|
||||
send(&mut app, Event::Cancel);
|
||||
if let Some(event) = event {
|
||||
send(&mut app, event);
|
||||
}
|
||||
let saved = app
|
||||
.architecture
|
||||
.as_ref()
|
||||
.map(|s| serde_json::to_value(&s.saved).unwrap());
|
||||
let before = app.document.bundle().clone();
|
||||
let status = app.status.clone();
|
||||
let epoch = app.canvas_epoch;
|
||||
let message = focused_text_input_escape("Other field or existing-element search");
|
||||
let _ = app.update(message);
|
||||
assert_eq!(
|
||||
app.architecture
|
||||
.as_ref()
|
||||
.map(|s| serde_json::to_value(&s.saved).unwrap()),
|
||||
saved
|
||||
);
|
||||
assert_eq!(app.document.bundle(), &before);
|
||||
assert_eq!(app.status, status);
|
||||
assert_eq!(app.canvas_epoch, epoch);
|
||||
}
|
||||
assert!(
|
||||
captured_escape(
|
||||
key_event(iced::keyboard::key::Named::Escape),
|
||||
iced::event::Status::Ignored,
|
||||
iced::window::Id::unique(),
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
assert!(
|
||||
captured_escape(
|
||||
key_event(iced::keyboard::key::Named::Enter),
|
||||
iced::event::Status::Captured,
|
||||
iced::window::Id::unique(),
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
@@ -621,9 +621,8 @@ impl canvas::Program<Message> for DiagramCanvas<'_> {
|
||||
Key::Named(Named::F2)
|
||||
if self.editable && self.connection.is_none() && modifiers.is_empty() =>
|
||||
{
|
||||
let scene = self.scene(state);
|
||||
let node = scene
|
||||
.view
|
||||
let view = self.scene(state).view;
|
||||
let node = view
|
||||
.nodes
|
||||
.iter()
|
||||
.find(|n| n.element == self.selected || n.id == self.selected)?;
|
||||
|
||||
@@ -187,8 +187,8 @@ fn article(topic: Topic) -> Article {
|
||||
&[
|
||||
"Choose New in the top bar. Use Project properties to name the model and describe its purpose. Apply changes commits the property draft.",
|
||||
"On Architecture, choose Create architecture view. Architecture · allocated work shows work inside its performer.",
|
||||
"Expand the diagram's side palette to see tool names. Choose Entity, click its position in the diagram, enter Rescue coordinator and press Enter. Escape cancels the draft. Use Actor for an external participant such as a Vessel in distress. Right-clicking a blank area also offers Create Entity here.",
|
||||
"Right-click Rescue coordinator and choose Add activity. Enter Assess incident and press Enter to create the activity and its performer allocation together. Ordinary Activity creation in the palette leaves work unallocated; use + Relationship with Allocation to assign existing work. The allocated-work view shows work inside its performer.",
|
||||
"Choose Entity in the diagram's Create toolbar. Enter Rescue coordinator, choose Choose position, then click the diagram. Use Actor for an external participant such as a Vessel in distress. You can also right-click a blank area, choose Create Entity here, enter its name and choose Create here.",
|
||||
"Choose Activity in the same toolbar, name it Assess incident and place it. Select Rescue coordinator and use + Relationship with Allocation to assign Assess incident to it. The allocated-work view then shows the activity inside its performer.",
|
||||
"Use Add existing to place an element that is already in the model. The initial choices match this view; no search is required. Include already shown offers Locate for elements already placed here.",
|
||||
"Add a second activity for another participant. Select an activity, choose + Relationship, select Interaction and choose the compatible target. Name the exchange after what is conveyed.",
|
||||
"Visit Model checks to review unfinished work. Save all writes the accepted model and applied drafts to your chosen file.",
|
||||
@@ -262,8 +262,8 @@ fn article(topic: Topic) -> Article {
|
||||
"Create and allocate work",
|
||||
&[
|
||||
"Choose the Operational or System perspective. Create participants using Entity/Actor in OA or System/System actor in SA.",
|
||||
"In the diagram's side palette, choose Activity in OA or Function in SA, click its position, enter a name and press Enter. Escape cancels without creating an element. Select a card and press F2 to rename it in place. For semantic decomposition, use Model explorer, select existing work and use + In selected to create smaller work units.",
|
||||
"Right-click a participant and choose Add activity or Add function to create new work and its allocation together. To assign existing leaf work, use + Relationship with Allocation from the participant to the work. Model explorer also offers + Allocated when a participant and the appropriate work kind are selected.",
|
||||
"In the diagram's Create toolbar, choose Activity in OA or Function in SA, enter a name and click to place it. For semantic decomposition, use Model explorer, select existing work and use + In selected to create smaller work units.",
|
||||
"To assign work to a participant, use + Relationship with Allocation from the participant to the leaf work. Model explorer also offers + Allocated when a participant and the appropriate work kind are selected.",
|
||||
"Choose Architecture · allocated work to review responsibilities. Choose Functional dataflow to review the activity/function hierarchy and its exchanges independently.",
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
# Compact palette implementation verification — 2026-09-10
|
||||
|
||||
Owner: Codex `/root/palette_finish`, assigned by root. Base branch
|
||||
`codex/release1-continuation`, starting HEAD `1797516`. No commit or push is
|
||||
performed by this worker. Root owns native QA, guide changes, integration and the
|
||||
shared release ledgers.
|
||||
|
||||
Read repository AGENTS.md, Gitea issue #1/comments, Wiki Home, open PR inventory
|
||||
(empty), and `harness/release-capella-basic-editing-20260907.md` before editing.
|
||||
Ownership and results are recorded in issue #1.
|
||||
|
||||
## Changes
|
||||
|
||||
* Qualify the Iced `column!` macro in the palette to remove E0659.
|
||||
* Borrow the projected scene when F2 finds its selected node instead of moving
|
||||
its view out of `Arc`, removing E0507.
|
||||
* Remove redundant or-pattern parentheses and the unused old `Edit` event.
|
||||
* Reject non-finite placement coordinates before they can become inline-editor
|
||||
or recovery-draft geometry. Existing context/epoch guards continue rejecting
|
||||
cancelled or stale canvas gestures.
|
||||
* Bound the inline name card to the remaining canvas height and make its content
|
||||
scrollable, so a wrapped performer title or validation error can remain inside
|
||||
the canvas. The card stays in the canvas stack and does not add a toolbar row.
|
||||
|
||||
Four new regression tests live in
|
||||
`crates/oa-desktop/src/app/architecture_authoring/tests.rs`:
|
||||
|
||||
1. OA and SA contextual creation preserves root ownership while adding the
|
||||
allocation and placement atomically; exact source bundle Undo/Redo and camera
|
||||
values are checked.
|
||||
2. A real Iced canvas F2 event requires canvas keyboard focus, dispatches the
|
||||
selected node to inline rename, retains its exact engineering name, and
|
||||
preserves placements/camera and exact source bundle Undo/Redo.
|
||||
3. Blank creation/rename submission and Escape never accept a placeholder or
|
||||
change the accepted source bundle.
|
||||
4. NaN/infinite canvas coordinates cannot poison draft geometry or recovery
|
||||
validation; the next valid click still opens naming.
|
||||
|
||||
## Verification
|
||||
|
||||
All Cargo commands use `.local/toolchain/env.ps1`,
|
||||
`CARGO_BUILD_JOBS=1`, `--locked`, and the worker's exclusive Cargo lease.
|
||||
|
||||
The first repaired build passed 15 existing `architecture_` filtered tests,
|
||||
with 569 filtered out. Log: `.local/palette-focused-initial-20260910.log`.
|
||||
It exposed the unused `Edit` warning, subsequently removed. An initial new-test
|
||||
compile caught an incorrect SourceBundle assertion; it was corrected before
|
||||
the final tests.
|
||||
|
||||
The full current desktop suite passed **588 tests, 0 failures, 0 ignored**:
|
||||
`cargo test --locked -p oa-desktop --bin arcadia-studio -- --test-threads=1`.
|
||||
Compilation/link took 44.58 s and test execution 113.14 s. This includes the
|
||||
four new regressions and existing placement/recovery/connection/transition
|
||||
coverage. Log: `.local/palette-desktop-tests-20260910.log`.
|
||||
|
||||
`cargo build --locked -p oa-desktop --bin arcadia-studio` passed in 2m 03s,
|
||||
including root's updated guide text. Log: `.local/palette-gui-build-20260910.log`.
|
||||
`cargo fmt --all -- --check` passed; its empty success log is
|
||||
`.local/palette-fmt-20260910.log`.
|
||||
|
||||
Built executable:
|
||||
`C:\Users\User\Documents\Git\ARCADIA-Studio\.local\target-gnullvm\debug\arcadia-studio.exe`
|
||||
(109,516,288 bytes), modified `2026-09-10T17:42:00.6365121Z`.
|
||||
SHA-256: `1B47C096624BB1101BEC8B18E63CD601A259DFF8C1BEC24581C31C6B511F9411`.
|
||||
Root received this path/hash for an isolated native-QA staging copy.
|
||||
|
||||
`cargo clippy --locked -p oa-desktop --all-targets -- -D warnings` passed in
|
||||
47.56 s. Log: `.local/palette-clippy-20260910.log`.
|
||||
|
||||
All assigned gates are complete. The worker released its Cargo lease and source
|
||||
freeze to root; subsequent workers must obtain the next lease from root. Source
|
||||
and report changes remain uncommitted for root's integration. No native UI was
|
||||
operated by this worker.
|
||||
|
||||
## Scope and remaining acceptance
|
||||
|
||||
Automated application/canvas tests establish state transitions and exact history;
|
||||
they do not establish native focus, visual compactness or pointer ergonomics.
|
||||
Root must inspect the newly built executable at wide/minimum sizes, especially
|
||||
bottom/right-edge naming, long names, wrapped errors, expanded palette labels,
|
||||
Enter/Escape, F2, existing insertion/Locate and contextual allocation.
|
||||
|
||||
Canvas origin must remain stable through arming, naming, validation, commit and
|
||||
cancel; only the explicit palette width toggle may resize the canvas. Continue
|
||||
the independent basic-editing oracle, Save/reopen/recovery and OA/SA transition
|
||||
destination checks. No Windows release acceptance or Linux runtime acceptance
|
||||
is inferred from this implementation report.
|
||||
|
||||
## Follow-up: stale submission rejection
|
||||
|
||||
Independent local review and the separate Codex-956atf host's issue #1 comment
|
||||
#12 identified a remaining stale-submit defect after the first verification:
|
||||
Submit set `placing=true`, then a stale document/revision guard returned with
|
||||
only a status string. That hid the retained inline name card. Earlier passing
|
||||
gates above did not cover this rejected-submit path.
|
||||
|
||||
Codex-9e8r6t `/root/palette_finish` resumed the assigned architecture scope and
|
||||
sole Cargo lease. Guard rejection now restores `placing=false` and copies the
|
||||
explanation into the card's error, without rebinding its document/revision or
|
||||
changing its name, point or intent.
|
||||
|
||||
A new regression covers Create/Rename × stale document/revision (four cases),
|
||||
repeated Submit without rebinding, actual inline-view presence, exact serialized
|
||||
saved draft, accepted Project/source/revision/camera preservation, Escape, and
|
||||
successful fresh creation with exact Undo after cancellation. The focused
|
||||
`architecture_` gate passed 20 tests, 569 filtered out, in 2.22 s after 48.63 s
|
||||
compilation. Log: `.local/palette-stale-submit-tests-20260910.log`.
|
||||
|
||||
Root's native `1B47C096…` check then found that the first Escape was consumed by
|
||||
the focused Iced name field and only unfocused it. The original global keyboard
|
||||
subscription intentionally ignores captured Escape; that policy remains intact.
|
||||
A separate subscription is now present only while architecture naming is inline,
|
||||
and dispatches a narrowly rechecked `CapturedEscape` event. The Cancel handler
|
||||
also restores `Cancelled · Select / move` status; ordinary architecture Escape
|
||||
delegates to that handler. The only app.rs edits are this subscription entry and
|
||||
the pre-existing architecture Escape guard delegation.
|
||||
|
||||
Two additional regressions exercise actual Iced TextInput update/capture followed
|
||||
by the subscription mapper and App update: first-keypress Create/Rename cancel
|
||||
preserves exact accepted model/source/revision/camera, while absent architecture,
|
||||
armed placement and the existing-element picker do not react to captured Escape.
|
||||
Ignored Escape and captured Enter are not mapped by the new subscription.
|
||||
|
||||
The final `architecture_` gate passed **22 tests**, 569 filtered out, in 4.12 s
|
||||
after 2m 19s compilation. Log: `.local/palette-escape-tests-20260910.log`.
|
||||
The same locked desktop build command passed in 1m 12s; log:
|
||||
`.local/palette-candidate2-build-20260910.log`. Workspace formatting check passed;
|
||||
log: `.local/palette-candidate2-fmt-20260910.log`.
|
||||
|
||||
Second candidate at the same local build path: 109,518,848 bytes, modified
|
||||
`2026-09-10T17:59:06.6655664Z`.
|
||||
SHA-256: `E6D6B1D975A11F242D288039E8F07D20E5474EA9C7CFCDE93F1318046EA548D4`.
|
||||
Root received this identity for a new staging copy. Strict desktop Clippy
|
||||
(`--locked -p oa-desktop --all-targets -- -D warnings`) passed in 1m 07s;
|
||||
log: `.local/palette-candidate2-clippy-20260910.log`. Cargo lease is released to
|
||||
root again. No worker-owned Cargo process remains.
|
||||
|
||||
Root's existing native stage remains the earlier `1B47C096…` binary and is not
|
||||
overwritten. The full integrated suite is deferred until the other-host behavior
|
||||
and forms changes are integrated; the earlier 588-test scope is preserved above.
|
||||
@@ -1,30 +0,0 @@
|
||||
# Independent palette review — 2026-09-10
|
||||
|
||||
Reviewer: **Codex-9e8r6t /root/behavior_audit**. Root requested this bounded read-only review while the separate-host scenario patch is being coordinated. Reviewed the current uncommitted architecture delta over `1797516`, its new/existing tests, guide wording, [implementation evidence](release-palette-implementation-20260910.md), and [original native acceptance](release-capella-basic-editing-20260907.md). Read Gitea issue #1, including Codex-956atf's stale-submission finding in comment #12. No production files, Cargo commands or native input were used in this review.
|
||||
|
||||
## Confirmed finding
|
||||
|
||||
**P2 — stale submission hides the retained inline name.**
|
||||
|
||||
`crates/oa-desktop/src/app/architecture_authoring.rs:400–416` validates nonempty input, then sets `saved.placing = true` before calling `commit_architecture`. The commit guard at `447–460` rejects stale document/revision/diagram, dirty source/properties/behavior or invalid point, but it only sets the status string and returns. It does not restore `placing = false` or set the draft error. `State::inline` at `95–99` requires `!placing`, and `inline_editor` at `540–541` filters on that predicate.
|
||||
|
||||
Consequently the model remains unchanged and the bytes of the name remain in memory, but the name editor disappears on this rejection. The status says the name is retained while asking the user to cancel/restart. The old `Edit` action was removed in this delta; no direct repair action restores the editor. A later canvas click can reopen naming, but cannot repair the stale document/revision binding. This is a defect in the stale-context recovery path, **not an observed ordinary native workflow failure or accepted-model corruption**. Most normal navigation/model edits are already blocked during naming by `app.rs:1075–1097`.
|
||||
|
||||
**Minimal correction:** on the guarded rejection, retain all raw fields and set `saved.placing = false` plus `error = Some(status.clone())`, as the actual execution-failure branches already do at `474–477` and `534–536`. Keep rejection of the stale commit; do not silently refresh its document/revision or accept it against another model. Ensure the retained editor can be read/copied/cancelled. This mirrors the separate-host finding but was independently confirmed in our current implementation.
|
||||
|
||||
**Focused falsifier to add:** for both named Create-at-point and F2 Rename, capture the raw name, accepted bundle and camera; invalidate the saved document/revision binding in the test fixture; send `Event::Submit`. Assert the bundle and camera are exact, no new history is created, the same raw name/point remains, `inline()` is true, and `error` is populated. Repeat Submit without repair and assert it still rejects. Cancel must remove only the draft. Restore ordinary naming afterward to ensure the failed draft does not poison the next operation. The existing stale test changes a **Click event**'s revision (`architecture_authoring_tests.rs:65–86`), so it does not cover this Submit path. The four new tests cover blank Submit and invalid Click, not stale Submit.
|
||||
|
||||
## Areas reviewed without another confirmed source defect
|
||||
|
||||
- The new F2 test drives a real Iced canvas event, requires canvas focus and verifies exact source/history plus unchanged placements/camera. The production Arc borrow fix is correct and does not alter selection identity.
|
||||
- Named OA/SA allocated work is root-owned with a distinct allocation and placement in one Batch. New tests compare exact source bundles before/after Undo/Redo; no semantic-parent/allocation conflation was found.
|
||||
- Nonfinite Click rejection precedes saved geometry, and State restoration also validates point finiteness, compatible type/performer and accepted fingerprint. Existing checkpoint tests retain raw names, phase and point with a fresh process binding. These safeguards should stay intact.
|
||||
- Name length is bounded, whitespace-only Submit keeps the editor/error, and Escape cancels without accepting a placeholder. Choosing another creation tool is allowed only while the first tool is unnamed; named inputs are protected by the App gate.
|
||||
- The new card height bound and scrollable wrapper directly address a long title/error overflowing the canvas. Source review does not prove native edge placement, focus, scrolling or expanded-label readability. There is no new measured layout assertion for this wrapper; retain root's minimum/wide native acceptance.
|
||||
- Current guide text teaches side-palette creation, point then inline name/Enter, Escape, F2, explicit allocated work and palette connections. Those statements match the inspected architecture implementation. Scenario guide labels will need synchronization after the separate clicked-slot/layout work; that is outside this palette review.
|
||||
|
||||
## Validation boundary
|
||||
|
||||
The upstream palette report records desktop588, GUI build, formatting and strict desktop Clippy. This reviewer did not rerun them. Root reports actual tool→point→inline→Enter with stable screen origin and one exact source Undo; that evidence belongs in root's native report. It does not exercise stale submission, long names at the lower/right edge, existing insertion/Locate, recovered inline focus or all OA/SA allocation cases.
|
||||
|
||||
Fix and run the focused stale-submission falsifier before claiming that rejection paths retain a usable inline editor. No other blocking finding was established in this bounded review. Scenario production remains unchanged pending root's source-coordination clearance.
|
||||
@@ -1,118 +0,0 @@
|
||||
# Palette native acceptance — 2026-09-10
|
||||
|
||||
Codex-9e8r6t `/root`, actual Windows Computer Use through the bundled `@oai/sky`
|
||||
API. This is ongoing acceptance, not a release verdict.
|
||||
|
||||
## Candidate and isolation
|
||||
|
||||
Initial binary SHA256
|
||||
`1B47C096624BB1101BEC8B18E63CD601A259DFF8C1BEC24581C31C6B511F9411`,
|
||||
109,516,288 bytes, staged with the exact LLVM-MinGW libunwind DLL and examples
|
||||
in `.local/native-qa/palette-20260910-v1/bin`. Process4852 started at
|
||||
17:42:58UTC with fresh task-local APPDATA/LOCALAPPDATA and a clean Windows PATH.
|
||||
`process.json` records hashes, paths and process identity; stdout/stderr are
|
||||
retained there. No pre-existing Arcadia GUI was touched. Native window525310
|
||||
was selected from actual returned windows.
|
||||
|
||||
The frozen independent fixture was copied to the run's `work` directory.
|
||||
Checker self-check and baseline passed before UI work using the local toolchain
|
||||
runtime on PATH. The native Open dialog loaded this copy. UIA value setting in
|
||||
the Windows file dialog was unavailable (`CacheRequest`,0x80070057); actual
|
||||
focused filename typing succeeded. This was a test-tool issue, not an app error.
|
||||
|
||||
## Observed and verified
|
||||
|
||||
- At1442×932 outer dimensions, expanding the palette reveals Entity/Actor,
|
||||
Activity, compatible connections and Add existing. Incompatible drawing tools
|
||||
explain their missing source. Expansion changes canvas width only.
|
||||
- Entity tool → point(381,435) → focused inline field → `Rescue team` → Enter
|
||||
worked. Canvas origin remained at approximately(261,315) throughout all phases;
|
||||
the accepted card appeared at the chosen screen point. No placeholder model
|
||||
element was accepted before Enter. Opening a project had retained a camera
|
||||
pan, so this screen point was not world(120,120).
|
||||
- Ctrl+S succeeded first submission. The initial fixed-coordinate entity oracle
|
||||
intentionally failed because the operator had not yet set its required world
|
||||
position: saved(90,90.20001220703125), expected(120,120). This is not evidence
|
||||
of a click-transform defect: the observed screen placement matched the click.
|
||||
The complete click-state bundle is preserved in `checkpoint-entity-click`.
|
||||
- One Ctrl+Z removed the whole creation. Ctrl+S then passed the complete baseline
|
||||
model and both exact source files. Preserved in `checkpoint-entity-undo`.
|
||||
Ctrl+Y restored the same Entity/placement identity visually.
|
||||
- Through the actual node context menu's Placement properties, the operator
|
||||
explicitly set X=120,Y=120 and applied. Ctrl+S passed the independent complete
|
||||
entity model and all **three** source files. Preserved in `checkpoint-entity`.
|
||||
The existing oracle was not changed to fit the observed result.
|
||||
- Native border resizing reached1102×732 outer dimensions, corresponding to
|
||||
the configured1100×700 client minimum. Both expanded156px and compact36px
|
||||
palettes remained usable. The canvas origin stayed stable; the existing
|
||||
entity retained its world coordinates and visible camera position.
|
||||
- Compact Actor tool → lower-right canvas point(730,673) opened the inline
|
||||
editor clamped wholly within the available canvas. A long Unicode name was
|
||||
horizontally editable without card overflow; Create/Cancel remained visible.
|
||||
|
||||
## Confirmed defect and next verification
|
||||
|
||||
**Escape from the focused inline text input does not cancel on the first key.**
|
||||
It unfocuses the input while retaining the inline card and pending title state.
|
||||
A separate screenshot observation confirmed the result. `app.rs`'s keyboard
|
||||
subscription discards captured Escape, and Iced's text input captures it.
|
||||
Palette worker is implementing narrowly scoped inline cancellation, preserving
|
||||
other widgets' captured-Escape behavior. This was missed by tests that directly
|
||||
sent the Cancel event. Stale-Submit visibility was independently found in source
|
||||
and is being fixed in the same next candidate.
|
||||
|
||||
Explicit Cancel subsequently closed the draft; Ctrl+S then passed the complete
|
||||
entity model and all three exact sources (`checkpoint-cancelled`). The captured
|
||||
pending recovery snapshot is separately preserved in
|
||||
`checkpoint-long-draft-recovery`: exact Actor name, point(439,328.2), placing=false.
|
||||
It is a retained checkpoint for a recovery test, not a claimed crash test.
|
||||
The Cancel footer still gave the old positioning instruction; that feedback is
|
||||
also corrected in the next candidate. `escape-retains-inline.png` records the
|
||||
actual first-Escape failure.
|
||||
|
||||
## Scenario layout baseline
|
||||
|
||||
Opened the staged teaching example from Home at the same1102×732 outer size.
|
||||
Its process view uses most of the center pane for the graph. Selected Scenario
|
||||
`Repair request and measured reply`: its heading/tools pushed the idle diagram
|
||||
top to approximately483px, leaving only its upper part visible. Choosing
|
||||
`+ Message` shrank its diagram to approximately110px: only the Controller and
|
||||
Technician headings fit; the exchanges were below the viewport. The form also
|
||||
had a small independently scrolling field area. Saved evidence:
|
||||
`scenario-minimum-before.png` and `scenario-minimum-pending-before.png`.
|
||||
This confirms the source audit's scenario workspace problem. Explicit Cancel
|
||||
restored the clean teaching model; no semantic edit was committed.
|
||||
|
||||
The first candidate closed cleanly through its native title-bar button. The
|
||||
subsequent process check found process4852 absent and its stderr log empty.
|
||||
Next: verify corrected first Escape and restored checkpoint focus, then F2,
|
||||
named/existing placement, allocation, connection, transitions and history/reopen.
|
||||
Minimum-size observations alone do not complete those remaining workflows.
|
||||
|
||||
## Candidate 2: focused Escape and recovery
|
||||
|
||||
The isolated `palette-20260910-v2` stage contains the rebuilt candidate with
|
||||
SHA-256 `E6D6B1D975A11F242D288039E8F07D20E5474EA9C7CFCDE93F1318046EA548D4`.
|
||||
Its `process.json` records process 648, the executable and runtime DLL hashes,
|
||||
and its separate profile/work directories. The candidate 1 stage was preserved.
|
||||
|
||||
Home's Recover action restored the retained long Actor-name checkpoint. Both
|
||||
the complete accepted bundle and serialized architecture draft matched the
|
||||
preserved checkpoint; the comparison is retained in `checkpoint-restored-recovery`.
|
||||
The inline card was visible with its full name and position, but recovery did
|
||||
not automatically focus its name field (`recovered-inline-before-focus.png`).
|
||||
After clicking that field, **one Escape cancelled the draft**, returned the
|
||||
palette to Select, and showed `Cancelled · Select / move`. This verifies the
|
||||
actual captured-key fix, not merely a directly injected Cancel event.
|
||||
|
||||
Ctrl+S opened Save As for the recovered copy. Submitting the full path to
|
||||
`recovered-work/project.arcadia` saved successfully on the first submission.
|
||||
The independent checker passed the complete Entity model and all three exact
|
||||
source files; `checkpoint-recovered-save` preserves the result. A read-only
|
||||
repeat after the desktop-control stop also passed the same oracle.
|
||||
|
||||
The user stopped Computer Use with physical Escape during the next observation.
|
||||
No rename action was performed by that stopped call. Native control is paused;
|
||||
F2 rename, the remaining complete basic-editing sequence, transitions and final
|
||||
reopen/runtime acceptance remain pending. Source integration and packaging
|
||||
continue. Recovery autofocus remains a small confirmed usability follow-up.
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,79 @@
|
||||
param(
|
||||
[string]$Remote = "gitea",
|
||||
[string]$MainBranch = "main"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# Verify remote exists.
|
||||
$remotes = git remote
|
||||
if ($remotes -notcontains $Remote) {
|
||||
throw "Git remote '$Remote' does not exist. Available remotes: $($remotes -join ', ')"
|
||||
}
|
||||
|
||||
Write-Host "Using remote: $Remote"
|
||||
Write-Host "Canonical branch: $MainBranch"
|
||||
|
||||
# Update remote references.
|
||||
git fetch $Remote --prune
|
||||
|
||||
# Update local main from the selected remote.
|
||||
git switch $MainBranch
|
||||
git pull --ff-only $Remote $MainBranch
|
||||
|
||||
# Get all branches on this specific remote.
|
||||
$remotePrefix = "$Remote/"
|
||||
|
||||
$branches = git for-each-ref `
|
||||
--format="%(refname:short)" `
|
||||
"refs/remotes/$Remote" |
|
||||
Where-Object {
|
||||
$_ -ne "$Remote/$MainBranch" -and
|
||||
$_ -ne "$Remote/HEAD"
|
||||
}
|
||||
|
||||
foreach ($remoteBranch in $branches) {
|
||||
# Remove "<remote>/" prefix while preserving branch names such as
|
||||
# agent/foo/bar.
|
||||
$branch = $remoteBranch.Substring($remotePrefix.Length)
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Updating $branch..."
|
||||
|
||||
# Check whether the local branch already exists.
|
||||
git show-ref --verify --quiet "refs/heads/$branch"
|
||||
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
git switch $branch
|
||||
|
||||
# Make sure the local branch reflects the latest remote branch
|
||||
# before modifying the instruction files.
|
||||
git pull --ff-only $Remote $branch
|
||||
}
|
||||
else {
|
||||
git switch --track -c $branch "$Remote/$branch"
|
||||
}
|
||||
|
||||
# Force these files to exactly match canonical main.
|
||||
git restore `
|
||||
--source "$MainBranch" `
|
||||
-- AGENTS.md PROJECT_GOAL.md
|
||||
|
||||
git add AGENTS.md PROJECT_GOAL.md
|
||||
|
||||
# Commit only when one of the files actually changed.
|
||||
git diff --cached --quiet
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
git commit -m "Sync agent instructions from $MainBranch"
|
||||
git push $Remote $branch
|
||||
}
|
||||
else {
|
||||
Write-Host "Already up to date."
|
||||
}
|
||||
}
|
||||
|
||||
git switch $MainBranch
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Finished synchronizing AGENTS.md and PROJECT_GOAL.md."
|
||||
Reference in New Issue
Block a user