Sequence |
Process → FunctionalChain, or paired occurrence contributions directly owned by Process / FunctionalChain |
@@ -2261,7 +2271,7 @@ deeply nested drafts produce a diagnostic instead of exhausting the parser stack
6.1. Modular source projects
The pure source_project core API compiles a virtual source bundle, containing
entry and a files map from logical paths to UTF-8 strings. Its entry is a
-root-level .arcadia file with the source5 project header above. A project entry
+root-level .arcadia file with the source7 project header above. A project entry
MAY also be include string ";". An included fragment consists only of ordinary
declarations and include statements; it MUST NOT contain another version header,
project wrapper, or project description. Includes MUST occur at document level,
@@ -2324,15 +2334,15 @@ the source index. Columns remain one-based Unicode scalars within the real file;
spans never refer to concatenated or synthetic source.
Single-string parse/lint APIs remain filesystem-free and reject include
statements with an explicit source-bundle requirement. V1/V2 single-file imports
-continue through explicit migration. Modular projects accept source3, source4 and
-source5. Source3 is upgraded as one project, preserving file paths, includes, nested
+continue through explicit migration. Modular projects accept source3 through
+source7. Older bundles are upgraded as one project, preserving file paths, includes, nested
ownership and comments through targeted token edits. Generated allocation
declarations stay in their interface's existing file. Individual include fragments
are not independently migrated. Missing UUIDs stabilize before migration so new
allocation identities are deterministic on subsequent compilation.
An authored source4 bundle upgrades through the strict prose-only baseline
adapter; after any missing UUID stabilization, the version token alone changes
-to 5. Existing prose, comments, includes and nested ownership remain intact.
+to 7. Existing prose, comments, includes and nested ownership remain intact.
Source5 semantic facet comments follow their closed selector identity through
row reordering and reidentification; UUID-looking text does not act as an Id.
@@ -2654,7 +2664,7 @@ defaults without changing any prior identity, name, reference, order, geometry,
artifact or trace field. The original V2 golden source/JSON in
crates/oa-core/tests/fixtures MUST NOT be regenerated from the newer demo;
they verify that every old persistent field survives. Canonical output writes
-the added defaults through the explicit schema-3, schema-4 and schema-5 migrations.
+the added defaults through explicit migration to the current schema 7.
The V2 adapter preserves every original identity, alias, prose, parent,
placement, trace and artifact. Generic ports become Legacy with their original
direction/interface record. Existing FE endpoint IDs remain unchanged. For
@@ -2671,13 +2681,13 @@ IDs, required resolutions, generated endpoint IDs/aliases/owners, and boundary
orientation defaults. REST import, replacement, candidate validation and
project snapshots surface the same report. Opening an old server envelope
preserves its resource ID/revision and bytes; the next explicit edit writes
-schema 5. Model/source exports contain only the canonical model. Migration
+schema 7. Model/source exports contain only the canonical model. Migration
reports are transient response metadata, not persisted model facts.
Admission checks account for generated objects and their references before
-construction. Both the old V2 contract and the resulting V5 graph are
+construction. Both the old V2 contract and the resulting current graph are
validated. Migration itself does not save or increase a stored revision.
Canonical second saves are stable. V1 normalization produces the frozen V2
-model and then uses these same V2-to-3-to-4-to-5 stages.
+model and then uses the same explicit adapters to reach schema 7.
Save validates a snapshot, writes a sibling temporary file, flushes it, and
atomically replaces the destination. Failed serialization or validation must
not truncate a previous file. External concurrent-writer conflict resolution
diff --git a/docs/language.rst b/docs/language.rst
index bfd98d3f..c90fbffa 100644
--- a/docs/language.rst
+++ b/docs/language.rst
@@ -103,6 +103,9 @@ lists all additional fields. ``ref`` means ``@alias``; ``ref?`` also accepts
* - ``oa_exchange_item``, ``sa_exchange_item``
- ``kind``: Information, Material, Energy; ``mechanism``: ExchangeMechanism; ``supertype``: item ref?
- Information; Unspecified; none
+ * - ``oa_exchange_category``, ``sa_exchange_category``
+ - ``family``: Work or Participant; ``exchanges``: same-layer exchange refs
+ - Work; ``[]``; root-level only
* - ``oa_data_type``, ``sa_data_type``
- ``definition``: DataTypeDefinition; ``default``: DataValue?; ``unit``: string
- String with minimum_length 0 / maximum_length none; none; ``""``
@@ -223,7 +226,9 @@ The present Studio profile does not decompose SA Actors.
All relations have a header identity, alias, display name, optional
``description`` (default ``""``), and required ``source`` and ``target``
-references. Their kind is the declaration keyword:
+endpoints. Ordinary endpoints are semantic references; ContextualLink endpoints
+are qualified OccurrenceEndpoint records resolved from its required ``owner``.
+Their kind is the declaration keyword:
.. list-table:: Relation catalog
:header-rows: 1
@@ -286,6 +291,9 @@ references. Their kind is the declaration keyword:
* - ``sequence_link``
- Preceding contribution → following contribution in one process/chain
- ``guard`` string / ""
+ * - ``contextual_link``
+ - Qualified occurrence endpoints resolved from an explicit owner Process/FunctionalChain; section 4.2.1
+ - Required ``owner``, ``source`` and ``target`` OccurrenceEndpoint records, and typed ``purpose``: Precedence, Exchange or CommonWork
* - ``state_transition``
- State vertices in one machine, subject to section 4.4
- ``trigger``, ``guard``, ``effect`` strings / ""; ``transition_kind`` External; ``triggers``, ``effects`` reference lists / []
@@ -1190,7 +1198,7 @@ are invalid.
* - Mission
- Operational Mission → System Mission
* - Exchange
- - Interaction relation → FunctionalExchange relation
+ - Interaction → FunctionalExchange, or CommunicationMean → BoundaryExchange
* - Sequence
- Process → FunctionalChain, or paired occurrence contributions directly owned by Process / FunctionalChain
* - Item
diff --git a/docs/scope.md b/docs/scope.md
index 7dc13be6..d5dcf3a4 100644
--- a/docs/scope.md
+++ b/docs/scope.md
@@ -13,7 +13,7 @@ Arcadia Studio implements a systems-engineering workbench spanning Operational A
| States and conditions | Participant-owned state machines, nested regions, states/modes and typed pseudostates, signal/time/change events, trigger/guard/effect transitions, linked entry/do/exit work, availability and constraints |
| Perspectives and evidence | Bidirectional counterpart creation/linking, reviewed descriptive and typed semantic changes, explicit ordered collections and combined multi-source review, many-to-many typed/manual traces, arbitrary artifact URI/revision/fragment references |
| Views and editing | Independent and duplicated diagrams, decomposition/allocation nesting, collapse, pan/zoom/Fit/Arrange, editable connection routes and labels, four-side port positioning, direct interaction/communication/functional/boundary exchange tools, named exchange categories, saved line-kind/category/coverage/context/hide/label settings, contextual menus, SVG export, model explorer and typed properties |
-| Text and storage | `arcadia 6` source projects with relative includes and nested declarations, stable aliases/UUIDs, a source-file tree, per-file drafts and diagnostics, comment-preserving graphical updates, exact model/source Undo/Redo, legacy migration, per-file atomic replacement with interrupted source-save recovery, Home/recents and private unsaved-work checkpoints |
+| Text and storage | `arcadia 7` source projects with relative includes and nested declarations, stable aliases/UUIDs, a source-file tree, per-file drafts and diagnostics, comment-preserving graphical updates, exact model/source Undo/Redo, legacy migration, per-file atomic replacement with interrupted source-save recovery, Home/recents and private unsaved-work checkpoints |
| Automation and hosted editing | Rust REST service for project/source-bundle/query/validation/transaction/transition/artifact workflows; optional desktop catalog/open/publish, explicit conditional Save, server review and local Save as; ETag preconditions, bounded requests and atomic current-revision storage |
Graphical and textual parity applies to the persistent fields of this profile. Diagram placements are model data; camera, selection, source cursor and undo history are session state. Canvas gestures need not match textual operations one for one: typed inspectors and explicit commands expose precise semantics. Full source replacement must repair dependencies in its candidate, while graphical delete/reidentify commands maintain references automatically.
diff --git a/harness/DECISIONS.md b/harness/DECISIONS.md
index f45f7821..7859f950 100644
--- a/harness/DECISIONS.md
+++ b/harness/DECISIONS.md
@@ -211,6 +211,57 @@ Decision: retain same-layer admission for ordinary diagrams, but allow both-laye
Alternatives rejected: filtering every view by its labelled layer loses existing Traceability content; ignoring layer validation everywhere hides mistaken references; serializing temporary GUI filters breaks source/API parity. Revisit if native workflows or cross-layer regression evidence exposes ambiguity. Current implementation is pending integrated tests and native acceptance, not an established release result.
+## Continuous behavior authoring — 2026-09-10
+
+Problem: adding the first Exchange to an empty process changed its interactive
+Graph into an ordered sequence Scene without gesture tools. Scenario non-message
+creation discarded the visible operand and insertion point, defaulting to root
+append. These are verified source paths; new-binary native acceptance is pending.
+
+Select one interactive process renderer. Project legacy ordered adjacency as
+endpoint pairs without fabricated semantic relation IDs. Loading and selecting
+remain read-only. The first topology mutation uses the existing validated batch
+to materialize unaffected adjacency and splice the chosen insertion atomically.
+Reject the alternative of a special 0/1-element renderer exception, which would
+reintroduce the discontinuity at a later element count; also reject automatic
+load-time migration and artificial self-links. Core legacy/explicit semantics
+and the source version are unchanged. Revisit if actual imported-path editing
+cannot preserve ordering, rejection and exact source/Undo behavior.
+
+Scenario Action, State, Reuse and empty Fragment creation capture the clicked
+owner, operand, before-occurrence and optional lifeline before starting the
+existing typed draft. Keep current validation, recovery and atomic history.
+Fragment range wrapping and cross-operand movement require separate explicit
+transactions. Do not infer persisted lifeline geometry from a click: the current
+model does not store horizontal lifeline order. Reused-scenario navigation uses
+bounded transient caller/use context, never an extra model history entry.
+
+Evidence and acceptance: `release-behavior-audit-20260910.md`, Gitea #1, new
+production-pointer regression paths and independent source reviews. Compilation,
+automated execution and native validation are still pending at this decision.
+
+## Typed stored observations — 2026-09-10
+
+Problem: generic trace-baseline Add inserted a UUID string into the closed
+FacetReview list, producing an invalid draft. Hiding Add and routing only to
+current review/reset cannot fulfill the normative contract: historical values
+and references may be valid even when their live definitions no longer exist.
+
+Select an explicit advanced typed history editor over the existing OpenAPI
+TraceBaseline/FacetReview descriptors. They provide the finite shape catalog
+already required for REST parity; final authority remains core typed decoding,
+historical admission and one EditTrace through Document. Preserve invalid raw
+drafts, exact historical UUID text, optional/Observed alternatives and explicit
+constructor selection. Current observations may be copied only by a deliberate
+action. Cache the descriptor and bound recursive rendering; unsupported shapes
+must fail visibly instead of generating an invalid default.
+
+Reject a separate handwritten GUI metamodel or a new duplicate core type-shape
+catalog, and reject raw JSON as the sole graphical editing surface. Revisit if
+descriptor/core consistency or native usability cannot be verified. This choice
+is under implementation; it does not yet close the parity finding in
+`release-end-state-audit-20260910.md`.
+
## Basic canvas authoring — 2026-09-07
The user rejected the large horizontal creation row. Actual installed Capella 7.1
diff --git a/harness/PLAN.md b/harness/PLAN.md
index d46bb43b..183f27dd 100644
--- a/harness/PLAN.md
+++ b/harness/PLAN.md
@@ -1,5 +1,22 @@
# First release execution plan
+## Active execution — 2026-09-10
+
+1. Restore a reproducible local toolchain; repair the interrupted compact palette;
+ verify focused history/draft/placement regressions and the actual native workflow.
+2. Independently reassess current behavior/scenario ergonomics and OA/SA semantic,
+ source/API and documentation completeness. Resolve verified high-impact gaps
+ with bounded changes and cross-representation acceptance criteria.
+3. Complete realistic graphical OA/SA teaching, transition, behavior, recovery and
+ hosted workflows. Exercise minimum and wide layouts, rejected edits and history.
+4. Run current Windows/Linux automated and native acceptance, fresh independent
+ end-state reviews, optimized builds and package verification. Publish accurate
+ build/run evidence and handoff only when the original release criteria hold.
+
+This ordering supersedes historical stopped-session next actions below. Preserve
+their scoped evidence; no test or runtime result transfers to a new binary without
+verification. Root holds the serialized build/native lease; Gitea #1 tracks workers.
+
**Latest refinement:** consult actual installed Capella 7.1, replace the user-rejected horizontal creation/connection rows with a compact contextual palette and inline authoring, independently critique interaction risks, then build and verify the real workflow at wide/minimum sizes. [Benchmark and concrete acceptance](release-capella-basic-editing-20260907.md). Do not ship the rejected toolbar as the usability correction.
**Current priority (7 September):** the user's usability correction takes precedence over the advanced feature sequence below. Establish contextual named diagram creation, existing-element placement, basic editing and clear layer transitions, then verify them natively. Desktop checks were explicitly reauthorized. See [priority reset and acceptance](release-basic-editing-reset-20260907.md).
diff --git a/harness/STATE.md b/harness/STATE.md
index b92805a1..ac689e82 100644
--- a/harness/STATE.md
+++ b/harness/STATE.md
@@ -1,5 +1,85 @@
# Progress Ledger — first release
+## Active continuation — 2026-09-10
+
+The renewed user request is preserved in `PROJECT_GOAL.md`. Root is integrating
+the first release, prioritizing direct diagram authoring and behavior/scenario
+usability. The checkout began clean at `1797516` on `codex/release1-continuation`.
+Gitea issue #1 and Wiki Home were read; ownership and progress are coordinated there.
+
+- Root owns integration, serialized builds, native Computer Use and these ledgers.
+ `palette_repair` owns architecture authoring and `diagram.rs`; `behavior_audit`
+ and `release_audit` independently inspect ergonomics and release coverage.
+- The recorded palette compilation errors have local corrections pending a fresh
+ build. Earlier 584-test evidence does not cover those corrections.
+- This checkout began without the historical `.local` toolchain/cache/packages.
+ Project-local Rust/Cargo 1.98.1 GNU/LLVM and verified LLVM-MinGW 20260908
+ (clang 23.1.1) are installed. Locked dependencies are being fetched.
+ Host: four logical processors, 8 GiB RAM; builds remain serialized.
+- Computer Use initialized successfully. The renewed request authorizes native
+ evaluation; old session Escape holds and process IDs are historical. No Arcadia
+ or Capella window is currently running.
+- WSL lists no installed distribution. Linux runtime/build acceptance remains an
+ open gate; no historical denied guest workaround is being retried.
+
+**Current branch is `codex/behavior-authoring-20260910`.** A distinct Gitea user
+`Codex-9e8r6t` is running another root/team on a separate environment with the old
+build cache. Our identity is **Codex-956atf**. Gitea #25 and #38 acknowledge the
+scope split: their palette/platform/package and subsequent scenario layout work;
+our process/scenario adapters and typed-history work. We will publish a clearly
+untested coherent snapshot on this distinct branch for their warm-cache checks.
+No shared branch-tip push or merge has occurred here.
+Their reports/results must not be claimed as local validation or overwritten.
+
+Implemented locally, awaiting compilation:
+
+- Palette compile repair, retained inline name after rejected stale Submit,
+ nonfinite click rejection and rename/allocation/history regressions.
+- One interactive process renderer with presentation-only inferred ordered
+ paths; first editing batch preserves adjacency atomically. Independent source
+ review found no confirmed defect. Root added 3+ step/before-first insertion
+ and incompatible imported conversion tests after the reviewer hit model capacity.
+- Scenario Action/State/Reuse/empty Fragment tools capture clicked operand/slot
+ and optional lifeline. Pointer-to-App, recovery, stale-event and history tests
+ are written. A bounded exact caller/use return path and scoped mounted-scroll
+ restoration are implemented with regressions, also untested.
+- Advanced typed stored-history editor uses existing OpenAPI type descriptors;
+ ten focused tests cover typed selectors, historical IDs, invalid raw input,
+ recovery and source/history. Core validation remains authoritative.
+- Normative catalogs now include ExchangeCategory, ContextualLink and both
+ Exchange trace pairs. Manual trace help and scope source version corrected;
+ strict Docutils 0.22.2 HTML regeneration passed.
+
+Reference evidence: actual Capella 7.1 scenario/lifeline/LOOP creation and saved
+XML are recorded in `release-capella-scenario-benchmark-20260910.md`; the isolated
+Capella process closed normally and is absent. This is not Studio acceptance.
+
+Toolchain bootstrap exec session **20812** completed successfully: verified
+LLVM-MinGW 20260908 ZIP SHA256
+`1bcf74d06b724aeecaa6412ca85f5b26fb1da770e7cdcefa9263c9c5c3ad34b6`, extracted
+successfully; stable **1.98.1** GNU/LLVM, rustfmt and Clippy installed.
+`.local/toolchain/env.ps1` configures task-local Cargo/Rustup/PATH and one build
+job. Locked dependency fetch is local exec session **13108**. No build/test pass
+is established; the target cache is still cold.
+
+The coherent candidate passed `cargo fmt --all` and `git diff --check`. It is
+published as an **UNTESTED integration snapshot** so the other host can use its
+warm build cache; publication is not acceptance. Their broader architecture
+palette/Escape repair should be reconciled with our overlapping compile/stale
+repairs while retaining the additional invariant tests. Their measured minimum
+scenario canvas is too small; they own the subsequent layout implementation.
+
+Next: focused `architecture_authoring_tests`,
+`behavior::topology`, `direct_clicked`, caller-navigation and `history_fields::tests`,
+then full relevant gates and a fresh native binary. Root owns all Cargo on this
+host. Worker output or tests existing in source are not passes.
+
+Gitea Actions run16 is queued for both ubuntu-latest and windows-latest with no
+started steps, as are older listed runs. No usable current Linux runner result
+is established and no duplicate dispatch was made. Progress is concrete;
+release acceptance remains incomplete. Older checkpoint paragraphs below retain
+their original revision scope.
+
**Checkpoint publication — 2026-09-10:** the user requested committing all current changes and pushing `codex/release1-continuation` to both `origin` and `gitea`. This is a work-in-progress snapshot. The interrupted palette-focused compilation failed with E0659 (ambiguous `column!` in `app/architecture_authoring/palette.rs:168`) and E0507 (moving the view out of an Arc in `diagram.rs:624`); an unnecessary-pattern-parentheses warning also remains in `architecture_authoring.rs:346`. The prior 584-test pass does not cover this newer palette revision. No Cargo/rustc process or local subagent was active at the publication preflight. Fix these errors and complete automated/native acceptance before packaging the redesign. Gitea coordination starts at issue #1 and the project Wiki; older evidence below retains its original scope.
**Latest priority checkpoint — Capella consultation and redesign:** the user rejected the large creation buttons and explicitly requested consulting the installed Capella. Native control resumed for that consultation in a disposable Capella 7.1 workspace. Actual palette, F2 rename and activity allocation were exercised; saved XML confirms allocation distinct from ownership. The horizontal creation/connection rows are being replaced with a compact expandable palette and inline naming. This supersedes the Escape hold and toolbar-fix proposal below. New implementation, native acceptance and a fresh user build remain pending. [Observed benchmark, alternatives and acceptance criteria](release-capella-basic-editing-20260907.md). The first-release goal remains active.
diff --git a/harness/TASK_LEDGER.md b/harness/TASK_LEDGER.md
index 4cfa5f45..d6080c95 100644
--- a/harness/TASK_LEDGER.md
+++ b/harness/TASK_LEDGER.md
@@ -1,5 +1,33 @@
# Task Ledger — first release
+## Current knowledge — 2026-09-10
+
+Given: the renewed objective in `PROJECT_GOAL.md` requires a feature-complete,
+usable OA/SA first release and prioritizes direct behavior/scenario authoring.
+Graphical/source/REST parity, explicit reST semantics, traceability, offline
+guidance, Windows/Linux acceptance and independent review remain required.
+Older scope deferrals must be checked against this request.
+
+Verified: the checkout began clean at `1797516`; Gitea contains one active
+continuation issue and no open PR. Current code includes direct behavior editors,
+so old blanket descriptions of static diagrams need workflow-level reassessment.
+The machine began without the ignored historical toolchain/cache/distributions.
+Local Rust/Cargo 1.98.1 and verified LLVM-MinGW are now installed; dependency
+fetch is pending and no current binary has been built. Native Computer Use is
+available. Isolated Capella activity/lifeline/LOOP creation was saved and checked;
+no Arcadia window is running. Candidate B1/B2/B3 and F1 edits are source-complete,
+formatted and whitespace-clean, but have no fresh compile/test/runtime pass.
+
+Assumption: the selected compact architecture palette remains a promising design,
+pending real authoring evidence. Decision: repair and evaluate it before further
+redesign. Parallel independent audits identify current behavior and semantic gaps
+instead of reimplementing integrated work.
+
+Open questions: which remaining menu-only behavior operations prevent a coherent
+direct workflow; which first-release semantic/parity gaps remain; and how to
+complete current Linux runtime acceptance. Root owns integration and native
+control; worker ownership is disjoint. See `STATE.md` and Gitea #1.
+
**Superseding user direction:** large horizontal creation buttons were rejected as intrusive and unprofessional. The user explicitly authorized consulting the installed Capella; that consultation has resumed and established actual compact palette, inline rename and explicit activity-allocation behavior. See [Capella 7.1 evidence and selected redesign](release-capella-basic-editing-20260907.md). The current candidate replaces creation/connection rows with one compact expandable palette and an in-place name editor. Native acceptance of the redesigned executable is pending; compactness is not assumed to prove usability. The earlier Escape hold and proposed toolbar-only correction below are superseded.
**Latest evidence:** basic editing full desktop584/584, bins, formatting and strictClippy passed. Native named Entity creation works at100%, but changing pending-toolbar height moves the screen placement22px. User Escape stopped the attempted Undo; native checks are paused again. Code-only toolbar correction proceeds. [Current findings](release-basic-editing-reset-20260907.md). The previous goal turn made concrete progress through implementation, integration gates and a native-discovered defect; this is not a no-progress blocker turn.
diff --git a/harness/release-behavior-audit-20260910.md b/harness/release-behavior-audit-20260910.md
new file mode 100644
index 00000000..27d59ddb
--- /dev/null
+++ b/harness/release-behavior-audit-20260910.md
@@ -0,0 +1,280 @@
+# Behavior authoring audit — 2026-09-10
+
+Author: Codex-956atf `/root/behavior_audit`. Initial bounded read-only source and
+domain review, followed by root's separately assigned B2 implementation below.
+The audit locations/findings describe the pre-edit snapshot. No Cargo,
+application input, or new runtime acceptance was performed. Read repository
+AGENTS.md, Gitea issue #1 and
+its comments, Wiki Home, and open PR inventory (none). Coordination is in issue
+#1. Source was inspected in the shared continuation checkout while other workers
+handled architecture authoring/builds; HEAD at the final inspection was
+`1797516f460ce27f7bc92e50ea2478ec51dd49b9`.
+
+The current implementation is substantially beyond static behavior diagrams.
+The remaining high-value work is making direct authoring consistent across the
+first process contribution and the non-message scenario tools. Historical
+absence claims in the early semantics audit are superseded by source7 code and
+its scoped evidence.
+
+## Ordered findings
+
+### B1 — A first exchange switches a new process into a less editable surface
+
+**High workflow impact; source-confirmed, fresh native reproduction required.**
+
+An empty Process/FunctionalChain opens the editable topology Graph. Draw its
+first Exchange contribution and it becomes a nonempty exchange-only ordered
+path. `uses_explicit_topology` now returns false, so the view changes to a
+sequence Scene without topology interaction. Tools for drawing contributions,
+connecting, reconnecting, zooming and reordering disappear. A one-contribution
+path has no link to convert, so the user must use Append exchange or separate
+Add work/control/reuse form buttons to return to the graph. Existing multi-step
+ordered paths likewise require the conversion/menu route.
+
+Evidence: `behavior.rs:753` defines the inferred legacy/explicit distinction;
+`behavior/topology/view.rs:89` selects the explicit surface, `:380` begins the
+legacy branch, and `:421` builds `Scene::sequence` without an interaction
+context. `behavior.rs:887` initializes `interaction: None`.
+`app/process_canvas.rs:316` already tests first/second exchange creation and
+exact history, but that test does not assert a continuous editable surface.
+
+**Reproduce:** Create an empty OA process (repeat in SA), choose Exchange and
+click the canvas, select an existing exchange and Create. Try to draw a Work
+contribution or connect from the newly created contribution without entering a
+different workflow. Repeat with an old ordered exchange-only process.
+
+**Bounded next fix:** retain one interactive process surface for empty, single
+and legacy ordered paths. Preserve the legacy implied order; when a pointer
+mutation needs explicit links, include the existing `preserved_order` proposal
+in that same atomic command. Do not invent a self-link merely to force the view
+predicate, and do not migrate a document merely by opening it.
+
+**Acceptance:** first and subsequent contributions remain directly editable;
+opening/selecting remains read-only; OA and SA preserve all old exchange IDs
+and implied order; insertion/connection uses one exact source/model Undo step;
+redo and save/reopen retain the result. Verify the actual viewport and visible
+tools at 1100×700 and a normal desktop size.
+
+### B2 — Most scenario objects still require detached append forms
+
+**High workflow impact; source-confirmed, fresh native evaluation required.**
+
+Scenario canvas tools are only Select and Message. Lifeline, reuse, fragment,
+action and state-observation buttons directly start a form. Every such Start
+sets the parent to the main scenario and the insertion position to append,
+even when an operand is selected. Consequently adding an action at a visible
+operand position or inserting a reference/fragment between existing messages
+requires manually choosing an operand, creating at its end, then reordering.
+Fragments cannot be drawn around a visible sequence range. The semantics and
+generic property editing exist; the missing part is contextual canvas authoring.
+
+Evidence: `behavior/scenario.rs:69` (`CanvasTool`), `:327` (Start initializer),
+`:1436` (form-launch controls), `:1565` (manual destination), `:1777` (fragment
+form). `behavior.rs:1564` handles only message drawing and selected-occurrence
+reordering. Reordering deliberately refuses operand changes at
+`behavior/scenario.rs:636`; this invariant should remain explicit.
+
+The scenario shell also retains three permanent control rows, an outer page
+scroll and a separately scrolled canvas; pending forms add another scroller.
+The canvas is capped at 340 px normally and 190 px while pending (`:1535`),
+unlike the newer process remaining-height layout. This is a source-established
+space allocation; clipping or interaction failure on the current binary is
+not claimed without native observation.
+
+**Reproduce:** In the teaching scenario select an ALT operand and a middle
+message. Add Action or Reuse. The form says Main scenario sequence / append,
+rather than retaining the selected operand and visible insertion point. Try
+the same task by selecting a tool and clicking the intended canvas position.
+
+**Bounded next fix:** extend the existing scenario gesture/Slot adapter for
+Action, State, Reuse and Fragment; capture the exact owner, operand, before-ID
+and optional lifeline before opening its compact contextual editor. Reuse the
+existing core commands, validation, draft recovery and atomic history. Give
+scenario controls the compact, stable remaining-height shell used by processes.
+Range wrapping and cross-operand movement need a distinct explicit transaction;
+they should not be silently smuggled into ordinary reorder.
+
+**Acceptance:** create each supported non-message occurrence at the clicked
+root/operand slot; select its semantic identity afterwards; Escape, scroll,
+resize, document change and rejected Apply cannot commit a stale slot; invalid
+draft text survives repair/recovery; Undo/Redo restore complete source bundles.
+Native users must complete an ALT + action + reuse task at 1100×700 without
+losing the target region or searching for offscreen submission controls.
+
+### B3 — Opening a reused scenario does not retain an explicit caller return
+
+**Medium workflow impact; source-confirmed.**
+
+Scenario references have real identity and bindings, and an Open action works.
+However it is placed in the full sequence listing below the canvas and sends
+only `Message::Select(target)`. Selection replaces the active scenario. There
+is no scenario caller stack/Back action to restore the particular use and its
+viewport, unlike process reference navigation. The Connections inspector can
+find users manually; that does not preserve which of several callers was open.
+
+Evidence: `behavior/scenario.rs:1841` / `:1893` (listing/Open),
+`app.rs:961` / `:982` (selection changes active behavior),
+`behavior/scenario.rs:155` (State has no return context). Compare existing
+`behavior/topology/direct.rs:191` (remember/back) and
+`app/process_canvas.rs:161` / `:183` (guarded Open/Back).
+
+**Reproduce:** Open caller A containing two uses of the same scenario, select
+its second use in the canvas, open the definition through the listing, then
+return to that exact use and viewport. The user must reconstruct the return
+through owner selection/Connections.
+
+**Bounded next fix:** selected-reference Open and Back to caller controls with
+document/owner/use identity and viewport context, using the same draft guards
+as process navigation. This is transient navigation state, not model history.
+
+**Acceptance:** A→B→C and two uses of B return in order to the exact initiating
+use; renamed/deleted callers are handled safely; pending invalid edits block
+navigation without loss; no model/source/Undo entry is added by navigation.
+
+### B4 — Create/Delete message semantics are not reflected in lifeline geometry
+
+**Medium visual-semantic impact; source-confirmed.**
+
+The core validates creation/destruction ordering, and message names distinguish
+Create and Delete. The renderer nevertheless draws every lifeline from its
+top header to the bottom of the whole scenario; lifecycle kinds only affect
+the message label. There is no birth anchor or destruction mark, so a reader
+sees the same apparent existence interval for an ordinary participant and one
+that is explicitly created and destroyed during the interaction.
+
+Evidence: `behavior.rs:1016` always builds the full-height lifeline;
+`:1165` selects only a textual message-kind prefix. Core lifecycle coverage is
+in `oa-core/tests/release_scenarios.rs:459` and its loop/branch variants. This
+finding is about visualization, not missing core lifecycle validation.
+
+**Reproduce:** Use a valid root sequence with Create(B), a call/reply involving
+B, and Delete(B). Inspect B before Create and after Delete; its lifeline still
+runs through both regions.
+
+**Bounded next fix:** derive birth/death anchors and a destruction glyph from
+the existing occurrence identities. Handle branch-local lifetime explicitly;
+an event in one ALT operand must not falsely terminate all other alternatives.
+No persistent execution engine is needed.
+
+**Acceptance:** root-sequence Create/Delete, ordinary lifelong participants,
+ALT-specific events and LOOP lifecycle warnings are distinguishable; hit tests
+still return the actual occurrence; direct insertion continues to reject invalid
+before-birth/after-death uses through the shared core boundary.
+
+## Semantics and history findings that must not be reported as missing
+
+- Scenario occurrences and referenced engineering definitions are distinct;
+ repeated exchange uses have distinct names/IDs. Direct Call+Reply creates
+ correlated occurrences in one transaction. Execution bars are derived from
+ correlation and operand boundaries (`behavior.rs:1304`), not persisted proof
+ of simulated execution. `scenario.rs:1026` and direct tests cover the adapter;
+ the earlier scoped native results are in `release-scenario-editing.md`.
+- Source7 already provides caller-qualified process addresses, distinct repeated
+ reference uses, precedence versus exchange/common-work incidence, and explicit
+ interface-operation allocation identity. See `oa-core/src/model.rs:534`,
+ `behavior/topology/contextual.rs`, `scenario_operation_tests.rs`, and
+ `release-source7-native-acceptance-20260907.md`. The original S1/S2 absence
+ findings are stale. Remaining purpose/native cases are acceptance work.
+- Shallow/deep history vertices, scope validation, creation tools and documented
+ semantics already exist (`oa-core/src/state_validation.rs:187`,
+ `oa-core/tests/release_states.rs:466`, `behavior/state_view.rs:111`,
+ `docs/language.rst:860`). Shallow history restores the immediate substate;
+ deep history restores nested configuration. Studio explicitly uses history
+ per Region and does not persist running execution state. The primary Capella
+ metamodel describes the corresponding containing-state behavior; this is a
+ documented profile distinction, not grounds to require a simulator.
+ [CapellaCommon schema](https://raw.githubusercontent.com/eclipse-capella/capella/master/m2/plugins/org.polarsys.capella.core.data.def/model/CapellaCommon.ecore).
+- Capella has distinct start/finish duration constraints and execution objects.
+ Studio's local timer is not an equivalent named duration relation. It does
+ have maintained generic constraints over semantic IDs. This audit did not
+ falsify that alternative for a required user workflow, so it does not reopen
+ a metamodel expansion already assessed in `release-scope-reconciliation-20260906.md`.
+ [Capella Interaction schema](https://raw.githubusercontent.com/eclipse-capella/capella/master/m2/plugins/org.polarsys.capella.core.data.def/model/Interaction.ecore).
+
+## Comparison and next action
+
+The existing native Capella benchmark actually exercised palette→canvas chain
+reference/control/sequence-link creation, a scenario reference region and Open,
+and call/return insertion reusing an existing exchange. It separates observed
+controls from executed actions (`release-capella-workflow-benchmark.md:51–69`).
+These support B1–B3 as engineering-workflow comparisons rather than a demand to
+copy the Eclipse UI. Capella's official overview independently distinguishes
+functional, exchange and interface scenario perspectives, all now represented
+by Studio's typed scenario viewpoints.
+[Official feature overview](https://mbse-capella.org/features.html).
+
+Prioritize B1 and the bounded slot-based part of B2; native-check those exact
+first-use tasks after root's current compilation/runtime gates. B3 is a small
+navigation follow-up. B4 should receive branch-aware visual acceptance. None
+of this report certifies current Windows/Linux behavior or transfers historical
+test/native evidence to a changed executable.
+
+## Authorized B2 implementation follow-up
+
+Root subsequently assigned `behavior.rs`, `behavior/scenario.rs` and only the
+ScenarioCanvas event arm in `app.rs`; Gitea #18 records this ownership under
+Codex-956atf. The separate topology worker owns B1. Current changes:
+
+- A compact selector replaces the three permanent scenario tool rows. Action,
+ Observe state, Reuse and empty Fragment use the same choose-tool/click-position
+ workflow as messages; lifeline creation remains explicit because persistent
+ horizontal placement is outside the existing schema.
+- Press captures the exact root/operand/before identity and an optional clicked
+ lifeline for Action/State. Release opens the existing draft; no model mutation
+ occurs until Create. The selected position remains marked in the pending
+ canvas. Clicking away from a lifeline leaves it unresolved for explicit choice.
+- Existing context, scroll/bounds, Escape and pending-edit guards apply to this
+ new press variant. The draft adapter validates slot and lifeline ownership
+ before starting. Existing command admission handles the complete semantics;
+ no separate model, schema, fragment wrapping or cross-operand reorder was added.
+- Added `direct_clicked` tests: real Scene callbacks through App for all four
+ tools, root and nested slots, on/off-lifeline clicks, exact full source/model
+ Undo/Redo; invalid/cross-scenario capture, all context dimensions, scroll
+ invalidation, incomplete raw-draft recovery/repair; all four tools against
+ nine pointer cancellation cases. These tests are written, not yet run.
+- Applied root's coordinated process-header predicate change so every
+ Process/FunctionalChain uses the compact header with the topology worker's
+ unified renderer. The semantic legacy/explicit predicate is unchanged.
+
+Whitespace verification passed for this worker's source changes. Another worker
+read the clicked-slot adapter and reported no confirmed blocker, while requesting
+the now-added off-lifeline/cancellation cases. Formatting/build/tests await root's
+toolchain and serialized Cargo lease. No native pass is claimed: test actual
+root/operand selection, current tool visibility, pending marker/viewport and
+Create/Cancel reachability at 1100×700 and normal size, including crash recovery,
+save/reopen and exact Undo/Redo. B2's larger remaining-height shell, fragment
+range wrapping and B4 lifecycle rendering are not part of this bounded
+implementation. B3 followed as the separately assigned slice below.
+
+## B3 implementation follow-up — candidate ready, execution pending
+
+The selected ScenarioUse exposes **Open definition**; the target scenario exposes
+**Back to caller**. Detailed sequence-list Open uses the same guarded adapter.
+Navigation retains document, caller scenario, exact use occurrence, target,
+selected tool and independent canvas/page offsets in at most 32 transient frames.
+Nested A → B → C and distinct uses of B return to their initiating occurrence.
+Renaming retains identity; deleted or repointed uses are rejected on return.
+Property, source and behavior drafts block both directions without applying them.
+
+The existing scroll observer restores only its matching document/scenario/token,
+directly in the mounted root Scrollable. It observes natural viewport clamping,
+consumes a request once and sends a correlated receipt to the App. It does not
+use a delayed global scroll task or traverse into nested scrollables. This adds
+one passive Restored variant beside the existing Scrolled early App handler; all
+other App changes remain in ScenarioCanvas. Caller navigation is neither model
+content nor recovery data and creates no model/source/history command.
+
+Added regressions cover nested and distinct-use App navigation, exact unchanged
+model/source/revision/history, invalid pending drafts, four stale context axes,
+non-use targets, renamed/deleted/repointed caller references, and real mounted
+Scrollable → App flow for both scroll areas including clamping, subsequent wheel
+input, repeated receipts and changed page. Foreign document/owner and process
+views reject restoration. These tests have not run yet; root will format and
+compile the coherent candidate before acceptance.
+
+Native acceptance remains: select a root or operand reference, Open definition,
+open a second reference there, return twice and inspect exact selected occurrence,
+tool and both scroll positions. Repeat from the detailed list with two uses of
+one target, a resized window and an invalid draft. Verify no dirty/history change
+from navigation. The separate remaining-height layout slice must retain these
+events and observer restoration when moving the scenario workspace.
diff --git a/harness/release-capella-scenario-benchmark-20260910.md b/harness/release-capella-scenario-benchmark-20260910.md
new file mode 100644
index 00000000..bffe0870
--- /dev/null
+++ b/harness/release-capella-scenario-benchmark-20260910.md
@@ -0,0 +1,44 @@
+# Native Capella scenario benchmark — 2026-09-10
+
+Codex-956atf /root used the installed Capella 7.1 executable through Computer
+Use in an isolated `.local/capella-reference-20260910` workspace. The generated
+project is `Behavior workflow benchmark`; no existing user model was opened.
+This evaluates reference-product behavior, not the current Studio binary.
+
+At the observed 1010×760 window size:
+
+- Operational Analysis → Define Operational Activities and describe Interactions
+ exposes creation of activity breakdown, interaction and activity-scenario views.
+ The scenario workflow created `[OAS] Response workflow` and its capability.
+- The right scenario palette exposes Activity, Activities, Interaction,
+ Interaction with return branch, Reference, LOOP and Operand. Activity is a
+ direct point-placement tool: clicking the canvas created a represented activity
+ and a lifeline. The lifeline is a separate InstanceRole, not the work definition.
+- Selecting LOOP and dragging a region across the lifeline created a visible
+ combined fragment. Its tooltip explains the repeated scenario part and covered
+ lifelines. The actual drag created one operand and explicit start/end records.
+- Selecting the new object expanded formatting/context controls and moved the
+ canvas vertically. Preserve direct interaction and coverage meaning in Studio
+ without copying this unstable canvas origin or the clipped palette labels.
+
+Ctrl+S saved the model, and the saved XML independently confirms:
+
+| Record | Identity / relationship |
+|---|---|
+| Activity | `04b58488-e33b-4693-ae8d-6f830cbc0cf3` |
+| Scenario | `49f702e0-1e0d-45c2-a0e8-e40545ce5b6c`, kind INTERACTION |
+| InstanceRole | `29115069-194d-4c1a-ac2c-ee86486a04fb`, represents the Activity |
+| LOOP | `1b4a102e-4c20-4e92-bd4f-71e6b677d2fa` |
+| Operand | `ac05a0af-60d1-48f2-9548-17214d3ebdc1`, covers the InstanceRole |
+| Fragment interval | start `8d988d2b-c191-46b2-8c92-1ff1e62251cb`, end `a8c26302-a5bb-49c0-8e7c-d2fe8026fe1a` |
+
+These observations support scenario tool/point authoring and explicit occurrence
+versus definition identity. They do not demonstrate wrapping existing messages,
+cross-operand movement, lifecycle drawing or successful scenario reuse; those
+operations were not exercised. Studio's current clicked-slot improvement creates
+an empty typed fragment using its existing occurrence/branch model. Persisted
+horizontal lifeline arrangement and drag-range wrapping need separate design and
+acceptance if adopted; this benchmark does not silently introduce either.
+
+After saving, Alt+F4 requested a clean Capella close. Root checks process absence
+before reclaiming memory for compilation; no forced termination was used.
diff --git a/harness/release-end-state-audit-20260910.md b/harness/release-end-state-audit-20260910.md
new file mode 100644
index 00000000..e5250da1
--- /dev/null
+++ b/harness/release-end-state-audit-20260910.md
@@ -0,0 +1,88 @@
+# Independent release end-state audit — 2026-09-10
+
+Reviewer: **Codex /root/release_audit**. Base HEAD `1797516f460ce27f7bc92e50ea2478ec51dd49b9`; concurrent palette/integration work is excluded from acceptance. Read PROJECT_GOAL.md, the supplied request, AGENTS.md, Gitea issue #1/comments, Wiki Home and the empty open-PR inventory before auditing. Coordination is recorded in issue #1.
+
+**Verdict: first-release completion is not supported yet.** Current source supplies substantial OA/SA semantics and shared source/GUI/REST infrastructure. The earlier missing communication means, selective views, coverage diagnostics, contextual assembly and operation identity are implemented; historical absence findings must not be repeated as current defects. A concrete trace-inspector defect remains below, and current native/platform/package acceptance is still incomplete.
+
+This was read-only implementation/specification inspection. No Cargo, native automation, deployment or runtime test ran. Test names below identify evaluators that exist, not fresh passing results. Root owns current builds/runtime and the separate behavior audit owns process/scenario ergonomics.
+
+## Requirements matrix
+
+| User outcome | Current source evidence | Acceptance assessment |
+|---|---|---|
+| Rust/Iced desktop on Windows and Linux | Three Rust crates; desktop Cargo.toml selects Iced 0.14, WGPU, X11 and Wayland. README gives native build paths. | Implemented architecture; current Windows palette rebuild/native acceptance and current Linux runtime remain gates. An ELF cross-build is not a Linux GUI trial. |
+| Useful OA and SA engineering semantics | model.rs has operational roles/activities/missions/processes, system boundary/functions/chains, typed data/interfaces/ports, states, scenarios, constraints and 25 relation kinds. validation.rs, ports.rs and data.rs enforce common rules. | No new fundamental absence established outside the separate behavior audit. Do not infer full Capella conformance. LA/PA and execution remain outside the user's current layer scope. |
+| Equivalent persistent graphical/text models | Project, validated Command boundary, source_project updater and desktop Document couple accepted source to the same model. forms.rs exposes typed records; dedicated workflows handle richer constructs. | Strong implementation coverage; **F1 exposes a broken typed-history inspector path**. `every_demo_record_has_a_lossless_graphical_draft` checks existing demo values, not every exposed constructor/add operation. |
+| Strict, unambiguous reST contract | docs/language.rst defines grammar, identity, invariants, lifecycle, migration and graphical obligations; docs/api.rst specifies REST. | Substantial contract; **F2 catalog inconsistencies** need correction. Source/header and package documentation should agree at final freeze. |
+| Human/AI/Git-friendly source with highlighting and lint | Modular includes, nested declarations, stable aliases/UUIDs, source-preserving updates and exact source history; highlight.rs adapts core tokenizer; source_workspace/source_navigation retain and locate drafts. | Implemented, with source7 source/lifecycle tests. No new text-loss counterexample found in this bounded audit. LSP, completion and semantic Git merge were not explicit requirements. |
+| Easy bidirectional OA/SA transition | transitions.rs maps dependencies/counterparts in either direction; semantic review supports scalar, constructor, keyed/ordered, attached and artifact decisions. Communication boundary ports and technical-operation omission are explicit. | Core/API paths and tests exist; actual complete engineering transition and revision/history workflows still require their remaining native cases. **F2 help wording misstates Manual-trace behavior.** |
+| Trace arbitrary development artifacts | ArtifactLink stores owner/relation/URI/revision/fragment; shared identity/reference maintenance, forms and searchable traceability inventory; server artifact/trace queries plus ordinary transactions. | Required reference model is present. No artifact fetching, executable adapter or plugin ABI is necessary to establish this requirement. Native final-tree evidence must include save/reopen and a cross-tool REST edit. |
+| Deployable service and clear REST for automation | routes.rs exposes project/model/source-bundle, lists/query/validation, commands, transitions and semantic review; store persists coupled representations with ETags. Dockerfile and systemd recipe exist. | Source7 HTTP test explicitly exercises instance/operation edits, parity and restart. Current Linux server suite and exact release build/deployment verification remain outstanding; recipes do not prove deployed execution. |
+| Intuitive nested diagrams and consistent editing | Distinct semantic ownership, allocation and placement; independent saved views, ports/routes, filters and duplication; current compact palette under repair. | Cannot accept from source. Root's native authoring evidence and separate behavior audit must cover selection, draft/cancel/history and wide/minimum size. |
+| Built-in concise navigable guide | guide.rs has searchable topics, related links, destination links and worked source7/transition/evidence material; bundled teaching source exists. | Implemented; guide still contains older control wording. Remaining complete teaching workflow and current control-label/native checks must close. |
+
+## Material findings
+
+### F1 — P2: the trace-history inspector offers an Add action that cannot create a valid fact
+
+The generic trace inspector recursively displays `baseline.semantic.facets`. Its array control always offers **+ Add** (`crates/oa-desktop/src/forms.rs:1916–1930`). `Message::FieldArray` forwards directly to `Draft::array` (`app.rs:1720`). `edit_array` has special constructors for bindings, values, routes, waypoints and port anchors, but none for facets: its fallback appends the nil UUID **string** (`forms.rs:622–650`). `SemanticBaseline.facets` instead requires `Vec` (`crates/oa-core/src/semantic/types.rs:258`), a closed typed enum. `Draft::command` deserializes the trace as-is (`forms.rs:600`). The generated draft therefore cannot become a valid EditTrace.
+
+Small reproduction for root's serialized test gate: select a trace with `baseline.semantic = Some(SemanticBaseline::default())`, call `Draft::from_project`, then `array("/baseline/semantic/facets", None, 0)` and `command()`. The Add produces a UUID rather than a facet; command must reject. Removing the bad row or reverting recovers the draft, so this is not an established stored-data corruption finding.
+
+The specialized semantic-review workflow can create reviewed current observations and reset exact history; that does not make this separate advertised Add control valid. Optional semantic history is also omitted when absent, so the ordinary inspector cannot initialize it through that path. The normative obligation explicitly includes typed trace baselines (`docs/language.rst:1959–1963`).
+
+**Next action:** choose one coherent history-editing surface. Replace the generic invalid constructors with an intentional typed workflow, or route the inspector to the existing review/reset tools while defining which history operations they support. Verify all displayed history controls against absent, empty and nonempty histories, including historical references whose live objects were deleted. Do not claim that a generic recursive form proves full constructor parity.
+
+### F2 — P2: the normative catalogs and guide give inconsistent transition information
+
+- `docs/language.rst:1192–1193` lists Exchange traces only as Interaction → FunctionalExchange, although its communication section (`:341`) and `transitions.rs::exchange_counterparts` admit CommunicationMean → BoundaryExchange. The reference table should describe both admitted pairs.
+- The same document's element catalog (`:94–145`) omits ExchangeCategory, and the relation catalog (`:232–303`) omits ContextualLink. Both have detailed definitions later, so their semantics are implemented/documented rather than absent; the purported catalogs are incomplete entry points for an independent implementation.
+- `guide.rs:658` says a Manual trace records correspondence “without propagating properties.” Existing-counterpart transition logic finds any endpoint-matching trace and applies reviewed name/description changes without a Manual-kind exemption (`transitions.rs:1055–1153`). Explicit Capability ↔ Mission Manual mappings are deliberately supported (`language.rst:1218`). Clarify that ordinary edits never synchronize automatically, while an explicit supported review can propagate selected properties; do not imply Manual is a no-update policy.
+- `docs/scope.md:16` still labels the implemented source profile `arcadia 6`; core/schema and the normative contract are version 7. README also retains older review labels and pending claims that newer scoped STATE evidence supersedes. Existing guide Activity/Function steps (`guide.rs:265`) describe the older Create-toolbar workflow while palette work is actively changing that surface.
+
+**Next action:** synchronize the tables, scope and task instructions against the accepted controls; regenerate the checked-in HTML from corrected reST and rerun the existing strict documentation/OpenAPI checks. Do not change transition semantics merely to match stale wording.
+
+### F3 — P1 release gate: current platform, deployment and full-workflow evidence is incomplete
+
+`harness/STATE.md` explicitly records a frozen Linux GUI cross-build with no actual Linux GUI runtime (`:57`), an earlier Linux server HTTP 50/51 result with a subsequently corrected evaluator (`:44`), and pending native purpose/teaching/hosted/compact cases. The Windows development preview (`:46`) predates current palette changes and is not the optimized final release. README correctly calls final suites, optimized/MSRV builds and current packages outstanding (`:156`). Root's new checks may supersede particular rows after this audit.
+
+This is missing release evidence, not proof that Linux, REST or the implemented workflows are broken. It still prevents meeting the user's explicit build/launch, real-workflow, current-platform and final-review criteria. The Docker builder pins Rust 1.90 while normal rust-toolchain.toml selects stable; final compatibility must be checked against the actual locked tree and intended package, rather than inferred from a different frozen snapshot.
+
+**Next action:** finish the current Windows authoring slice, then freeze one candidate revision for appropriate complete tests, strict checks, optimized desktop/server builds, current Linux runtime and representative cross-representation/REST restart cases. Assemble packages from that accepted tree and verify examples, reST/HTML/OpenAPI, notices, runtime dependencies, clean launch and checksums. Respect prior runtime/approval boundaries; a stopped guest or denied workaround is not successful acceptance.
+
+## Highest-value completion sequence
+
+1. Reproduce and correct F1; retain focused failure/repair evidence. Finish root's current palette compile/native gate and address the separate behavior audit's verified findings.
+2. Close the already documented complete teaching story: OA responsibility/communication → reviewed SA contribution and interfaces → behavior/degraded state → artifact evidence → source edit → conditional REST edit/reload and stale-write rejection → save/reopen/history. Use exact whole-model and authored-source comparisons, not screen appearance alone.
+3. Correct F2 and prune misleading current-status wording without deleting historical evidence. Keep the normative contract explicit about intentional asymmetry: semantic ownership versus allocation, OA abstraction versus SA technical ports, and reviewed counterpart differences.
+4. Complete F3 on a frozen candidate and obtain fresh independent runtime/UX plus final requirements review. No additional broad metamodel subsystem is justified by this audit alone.
+
+At the end of the initial audit, only this report had been written. The subsequently authorized F1 implementation is recorded below; no fresh runtime/test pass is claimed here.
+
+## Bounded F1 remediation recommendation
+
+Root requested a decision on generated review history versus arbitrary facet constructors after receiving this audit.
+
+**Use the existing semantic-review/history surface, with a distinct typed stored-observation editor.** Removing generic Add and routing to inspection/reset safely removes the immediate trap, but inspection/reset alone does not discharge the current parity contract. Section 5.1 deliberately admits historical observations independently of current object applicability, including references whose live objects are gone; section 8 explicitly requires graphical editing of trace baselines. A workflow that can only acknowledge current live facts cannot express every admitted source-authored historical value. Changing the spec to declare those properties uneditable would narrow the requested equivalence.
+
+The bounded implementation need not build another reconciliation engine:
+
+- Choose a closed facet selector and its exact typed row keys, using the existing shared catalog. Never append an arbitrary default FacetReview.
+- Edit canonical OA and SA Observed branches with their selector-specific types. NotApplicable, Value(none), and absence of the facet remain distinct. Current observations can be presented as a deliberate starting choice, not silently acknowledged on Add.
+- Keep stored-observation editing separate from Keep/Take/live candidate edits. It changes the trace history only and must say so. Existing inspect/reset remains the ordinary workflow.
+- Historical UUID input must retain deleted identities and allow syntactically valid exact keys; live-object-only pickers are insufficient. Use the existing historical validator, not live-reference applicability, for historical admission.
+- Stage a complete typed candidate; validate and apply one EditTrace through the existing Document/source/history boundary. An incomplete selector, key or observation keeps Apply disabled or fails with a located editable draft. Source writeback and Undo/Redo remain shared.
+
+Acceptance should cover absent history, an empty facet list, one scalar, one optional value, NotApplicable, a keyed attachment/artifact observation with deleted UUIDs, changing an observation's constructor, duplicate/overlapping-invalid history, and exact source/JSON Save/Undo/Redo. Prove the original nil-UUID Add cannot occur. This is a finite history-authoring slice over existing types, not a new metamodel or automatic synchronization feature.
+
+## F1 implementation candidate — Codex-956atf /root/release_audit
+
+Root authorized the bounded implementation in `crates/oa-desktop/src/forms.rs`, `forms/history_fields.rs`, and `forms/history_fields/tests.rs`. No core API or app handler was changed. The trace inspector replaces its recursive baseline form with a collapsed **Edit stored history (advanced)** surface. It initializes absent prose/semantic baselines, chooses a facet family and leaf explicitly, edits keyed selectors and both typed observations, and removes/reorders rows. Optional values, NotApplicable, booleans, integers/reals, enum and compound constructors, exact strings and historical UUIDs are separate controls. Current counterpart observations are an explicit opt-in action using `capture_facet`; the editor changes no counterpart.
+
+**Design decision:** reuse the existing checked-in OpenAPI TraceBaseline/FacetReview descriptor as finite historical field-shape metadata, cached once. This already documents the admitted serialized constructors, so using it here avoids a second manually maintained semantic catalog or new core metadata API. This is not an all-product REST-schema form generator. The supported local refs, oneOf/anyOf, fixed objects, arrays and primitive shapes are bounded by depth 32; list rendering pages 12 rows. Unsupported metadata produces a visible unavailable-field message. The real Rust types and `validate_semantic_baseline` remain authoritative for admission; the descriptor supplies no replacement validator.
+
+**Deliberate draft behavior:** a newly chosen facet has both observations NotApplicable and cannot be committed until the engineer supplies an admitted observation. New UUID slots are empty, never a made-up nil identity. Historical typed Add replaces the generic UUID-array fallback. Scoped shape/list events preserve unrelated incomplete inputs. Constructor caches follow row movement/removal and recovery; exact-string mode also converts inactive textual buffers transactionally. Replacing or clearing a whole history subtree discards its inactive constructor buffers so later new facts cannot revive old data. Ordinary Apply remains the existing EditTrace/Document/source transaction.
+
+Ten targeted tests are present. They cover absent/empty/existing history; descriptor-derived complete witnesses for every offered scalar/keyed fact against actual FacetReview deserialization and historical validation; rejected NA/NA and duplicates with unchanged Document snapshots; deleted UUID keys/observations; numeric limits and invalid raw repair; compound values and constructor switching; TypeRef's nested primitive picker; exact Unicode/quoted strings and literal keys; cache relocation/recovery; clear/recreate isolation; source/JSON round-trip and exact one-step Undo/Redo including retained source comments.
+
+**Verification status:** `git diff --check` passes for the candidate (an unrelated generated HTML line-ending warning is present). No Cargo, rustfmt, native GUI or integration suite was run by this worker; root owns the serialized build/toolchain lease. F1 is therefore a ready source candidate, not an accepted fix yet. Root should run targeted history tests, the relevant desktop suite and strict lint, then exercise expanded history controls at normal/minimum width, typed Add/repair, paging, apply/reopen and Undo in the actual desktop. F2 and shared decision/wiki synthesis remain root-owned. F3 remains open.
From b6b76efc0d6092d96e793feadf529e281017c5a9 Mon Sep 17 00:00:00 2001
From: Codex
Date: Thu, 10 Sep 2026 11:13:27 -0700
Subject: [PATCH 02/14] Fix nullable stored-history constructor switching
---
crates/oa-desktop/src/forms.rs | 3 +
crates/oa-desktop/src/forms/history_fields.rs | 39 ++++
.../src/forms/history_fields/tests.rs | 178 ++++++++++++++++++
harness/STATE.md | 21 ++-
harness/release-end-state-audit-20260910.md | 2 +
5 files changed, 239 insertions(+), 4 deletions(-)
diff --git a/crates/oa-desktop/src/forms.rs b/crates/oa-desktop/src/forms.rs
index e09a176b..3661b709 100644
--- a/crates/oa-desktop/src/forms.rs
+++ b/crates/oa-desktop/src/forms.rs
@@ -449,6 +449,9 @@ impl Draft {
None
};
let tag = |value: &Value| {
+ if self.kind == RecordKind::Trace && history_fields::is_path(path) {
+ return history_fields::constructor_tag(path, value);
+ }
value
.as_str()
.or_else(|| value.as_object()?.keys().next().map(String::as_str))
diff --git a/crates/oa-desktop/src/forms/history_fields.rs b/crates/oa-desktop/src/forms/history_fields.rs
index 10b5ccef..54805bdd 100644
--- a/crates/oa-desktop/src/forms/history_fields.rs
+++ b/crates/oa-desktop/src/forms/history_fields.rs
@@ -382,6 +382,45 @@ fn branch_has_fields(schema: &'static Value) -> bool {
.is_none_or(|values| values.len() > 1)
}
+/// Constructor identity is independent of a primitive's edited content. In
+/// particular, absent, an empty UUID/text, a number and a list are different
+/// branches. String enum tags and externally tagged objects retain their variant.
+pub(super) fn constructor_tag(path: &str, value: &Value) -> String {
+ fn enum_value(schema: &'static Value, value: &Value, depth: usize) -> bool {
+ if depth > MAX_DEPTH {
+ return false;
+ }
+ let Ok(schema) = resolve(schema) else {
+ return false;
+ };
+ if let Some(options) = alternatives(schema) {
+ return options
+ .iter()
+ .any(|option| enum_value(option, value, depth + 1));
+ }
+ schema
+ .get("enum")
+ .and_then(Value::as_array)
+ .is_some_and(|values| values.contains(value))
+ }
+ match value {
+ Value::Null => "null".into(),
+ Value::Bool(_) => "boolean".into(),
+ Value::Number(_) => "number".into(),
+ Value::Array(_) => "array".into(),
+ Value::String(word)
+ if schema_at(path).is_some_and(|schema| enum_value(schema, value, 0)) =>
+ {
+ format!("enum:{word}")
+ }
+ Value::String(_) => "string".into(),
+ Value::Object(fields) => format!(
+ "object:{}",
+ fields.keys().cloned().collect::>().join("/")
+ ),
+ }
+}
+
fn paging<'a>(draft: &'a Draft, path: &str, count: usize) -> (usize, View<'a, Message>) {
let last = count.saturating_sub(1) / PAGE_SIZE;
let page = draft
diff --git a/crates/oa-desktop/src/forms/history_fields/tests.rs b/crates/oa-desktop/src/forms/history_fields/tests.rs
index ddfe61d3..23b40072 100644
--- a/crates/oa-desktop/src/forms/history_fields/tests.rs
+++ b/crates/oa-desktop/src/forms/history_fields/tests.rs
@@ -731,3 +731,181 @@ fn history_clear_discards_inactive_invalid_constructors_before_reusing_the_same_
});
assert_history_transaction(&mut document(&project), &draft, &expected);
}
+
+/// Exercise the same descriptor choice and FieldShape event emitted by the
+/// nested picker. Injecting an already complete Observed value skips this path.
+fn choose_history_constructor(draft: &mut Draft, path: &str, label: &str) {
+ let schema = resolve(schema_at(path).expect("typed history field")).unwrap();
+ let branch = alternatives(schema)
+ .expect("constructor choices")
+ .iter()
+ .find(|branch| branch_label(branch) == label)
+ .expect("visible constructor choice");
+ let event = variant_message(
+ path,
+ branch_seed(branch).unwrap(),
+ draft.value.pointer(path).is_some(),
+ );
+ let Message::FieldShape(path, value) = event else {
+ panic!("constructor picker must emit FieldShape");
+ };
+ draft.shape(&path, value).unwrap();
+}
+
+#[test]
+fn history_nested_optional_number_events_switch_absent_present_and_recover_invalid_raw() {
+ let (project, id) = fixture(Some(baseline(vec![])));
+ let mut draft = Draft::from_project(&project, id).unwrap();
+ draft
+ .append(
+ FACETS,
+ choice(FacetSelector::Data(DataFacet::IntegerMinimum)),
+ )
+ .unwrap();
+ let side = format!("{FACETS}/0/Data/IntegerMinimum/oa");
+ let path = format!("{side}/Value");
+ choose_history_constructor(&mut draft, &side, "Observed value");
+ assert_eq!(draft.value.pointer(&path), Some(&Value::Null));
+ choose_history_constructor(&mut draft, &path, "Set number");
+ assert_eq!(
+ draft.value.pointer(&path),
+ Some(&json!(0)),
+ "null and numeric constructor identities must differ"
+ );
+ draft.input(&path, "-".into());
+ choose_history_constructor(&mut draft, &path, "Absent (none)");
+ assert_eq!(draft.value.pointer(&path), Some(&Value::Null));
+ let mut draft = Draft::from_recovery(&project, draft.recovery_snapshot()).unwrap();
+ choose_history_constructor(&mut draft, &path, "Set number");
+ assert_eq!(draft.inputs[&path].raw, "-");
+ assert!(draft.command().is_err());
+ draft.input(&path, "7".into());
+ let mut expected = project.clone();
+ expected
+ .traces
+ .get_mut(&id)
+ .unwrap()
+ .baseline
+ .as_mut()
+ .unwrap()
+ .semantic = Some(SemanticBaseline {
+ facets: vec![FacetReview::Data(DataReview::IntegerMinimum(Reviewed {
+ oa: Observed::Value(Some(7)),
+ sa: Observed::NotApplicable,
+ }))],
+ });
+ assert_history_transaction(&mut document(&project), &draft, &expected);
+ choose_history_constructor(&mut draft, &path, "Absent (none)");
+ expected
+ .traces
+ .get_mut(&id)
+ .unwrap()
+ .baseline
+ .as_mut()
+ .unwrap()
+ .semantic = Some(SemanticBaseline {
+ facets: vec![FacetReview::Data(DataReview::IntegerMinimum(Reviewed {
+ oa: Observed::Value(None),
+ sa: Observed::NotApplicable,
+ }))],
+ });
+ assert_history_transaction(&mut document(&project), &draft, &expected);
+ choose_history_constructor(&mut draft, &path, "Set number");
+ assert_eq!(
+ draft.inputs[&path].raw, "7",
+ "returning to Value restores its edited contents"
+ );
+}
+
+#[test]
+fn history_nested_uuid_and_list_events_preserve_present_values_and_tagged_item_branches() {
+ let (project, id) = fixture(Some(baseline(vec![])));
+ let mut draft = Draft::from_project(&project, id).unwrap();
+ draft
+ .append(FACETS, choice(FacetSelector::Header(HeaderFacet::Parent)))
+ .unwrap();
+ draft
+ .append(FACETS, choice(FacetSelector::Field(FieldFacet::Default)))
+ .unwrap();
+ let side = format!("{FACETS}/0/Header/Parent/oa");
+ let uuid = format!("{side}/Value");
+ choose_history_constructor(&mut draft, &side, "Observed value");
+ choose_history_constructor(&mut draft, &uuid, "Set historical UUID");
+ assert_eq!(
+ draft.value.pointer(&uuid),
+ Some(&json!("")),
+ "empty UUID input is distinct from absent"
+ );
+ assert!(draft.command().is_err());
+ draft.input(&uuid, "incomplete-uuid".into());
+ choose_history_constructor(&mut draft, &uuid, "Absent (none)");
+ choose_history_constructor(&mut draft, &uuid, "Set historical UUID");
+ assert_eq!(draft.inputs[&uuid].raw, "incomplete-uuid");
+ let historical = Id::from_u128(8765);
+ draft.input(&uuid, historical.to_string());
+ choose_history_constructor(&mut draft, &uuid, "Absent (none)");
+ choose_history_constructor(&mut draft, &uuid, "Set historical UUID");
+ assert_eq!(draft.inputs[&uuid].raw, historical.to_string());
+
+ let side = format!("{FACETS}/1/Field/Default/oa");
+ let list = format!("{side}/Value");
+ choose_history_constructor(&mut draft, &side, "Observed value");
+ choose_history_constructor(&mut draft, &list, "Set list");
+ assert_eq!(
+ draft.value.pointer(&list),
+ Some(&json!([])),
+ "empty list is distinct from absent"
+ );
+ let array_schema = alternatives(resolve(schema_at(&list).unwrap()).unwrap())
+ .unwrap()
+ .iter()
+ .map(|branch| resolve(branch).unwrap())
+ .find(|branch| branch["type"] == "array")
+ .unwrap();
+ let event = Message::FieldAppend(list.clone(), seed(&array_schema["items"], 0).unwrap());
+ let Message::FieldAppend(path, value) = event else {
+ unreachable!()
+ };
+ draft.append(&path, value).unwrap();
+ let item = format!("{list}/0");
+ choose_history_constructor(&mut draft, &item, "Integer");
+ draft.input(&format!("{item}/Integer"), "-".into());
+ choose_history_constructor(&mut draft, &item, "String");
+ draft.input(&format!("{item}/String"), "retained textual branch".into());
+ choose_history_constructor(&mut draft, &item, "Integer");
+ assert_eq!(draft.inputs[&format!("{item}/Integer")].raw, "-");
+ draft.input(&format!("{item}/Integer"), "21".into());
+ choose_history_constructor(&mut draft, &list, "Absent (none)");
+ assert_eq!(draft.value.pointer(&list), Some(&Value::Null));
+ let mut draft = Draft::from_recovery(&project, draft.recovery_snapshot()).unwrap();
+ choose_history_constructor(&mut draft, &list, "Set list");
+ assert_eq!(draft.inputs[&format!("{item}/Integer")].raw, "21");
+ choose_history_constructor(&mut draft, &item, "String");
+ assert_eq!(
+ draft.inputs[&format!("{item}/String")].raw,
+ "retained textual branch"
+ );
+ let mut expected = project.clone();
+ expected
+ .traces
+ .get_mut(&id)
+ .unwrap()
+ .baseline
+ .as_mut()
+ .unwrap()
+ .semantic = Some(SemanticBaseline {
+ facets: vec![
+ FacetReview::Header(HeaderReview::Parent(Reviewed {
+ oa: Observed::Value(Some(historical)),
+ sa: Observed::NotApplicable,
+ })),
+ FacetReview::Field(FieldReview::Default(Reviewed {
+ oa: Observed::Value(Some(vec![DataValue::String(
+ "retained textual branch".into(),
+ )])),
+ sa: Observed::NotApplicable,
+ })),
+ ],
+ });
+ assert_history_transaction(&mut document(&project), &draft, &expected);
+}
diff --git a/harness/STATE.md b/harness/STATE.md
index ac689e82..5c889cc1 100644
--- a/harness/STATE.md
+++ b/harness/STATE.md
@@ -44,7 +44,7 @@ Implemented locally, awaiting compilation:
are written. A bounded exact caller/use return path and scoped mounted-scroll
restoration are implemented with regressions, also untested.
- Advanced typed stored-history editor uses existing OpenAPI type descriptors;
- ten focused tests cover typed selectors, historical IDs, invalid raw input,
+ twelve focused tests cover typed selectors, historical IDs, invalid raw input,
recovery and source/history. Core validation remains authoritative.
- Normative catalogs now include ExchangeCategory, ContextualLink and both
Exchange trace pairs. Manual trace help and scope source version corrected;
@@ -62,12 +62,25 @@ successfully; stable **1.98.1** GNU/LLVM, rustfmt and Clippy installed.
job. Locked dependency fetch is local exec session **13108**. No build/test pass
is established; the target cache is still cold.
-The coherent candidate passed `cargo fmt --all` and `git diff --check`. It is
-published as an **UNTESTED integration snapshot** so the other host can use its
-warm build cache; publication is not acceptance. Their broader architecture
+The coherent candidate `01bb52a` passed `cargo fmt --all` and `git diff --check`.
+Direct push failed because this fresh host has no Git credentials. Authenticated
+Gitea MCP transferred its exact verified Git bundle through
+`codex/behavior-transfer-20260910` (`harness/transfers/behavior-01bb52a.bundle.b64`),
+prerequisite `1797516`, decoded SHA256
+`85A38B00207036DCB8DB24ED4A7E80B2BE670275EEB7B00413D36153322ADA5D`.
+This **UNTESTED integration snapshot** lets the other host use its warm cache;
+transfer is not acceptance. Their broader architecture
palette/Escape repair should be reconciled with our overlapping compile/stale
repairs while retaining the additional invariant tests. Their measured minimum
scenario canvas is too small; they own the subsequent layout implementation.
+Their isolated palette commit is `7d1df4f`, fetched locally for reconciliation.
+
+Independent F1 review found a real optional-value cache collision in `01bb52a`.
+The follow-up distinguishes null/scalar/list/tagged constructor identities and
+adds actual inner selector-event tests. Independent source recheck found no
+remaining collision; execution is still pending. The original full-platform
+fetch was stopped after network timeouts; current Windows desktop test build
+is exec session **51036**, compiling dependencies. Root owns this Cargo lease.
Next: focused `architecture_authoring_tests`,
`behavior::topology`, `direct_clicked`, caller-navigation and `history_fields::tests`,
diff --git a/harness/release-end-state-audit-20260910.md b/harness/release-end-state-audit-20260910.md
index e5250da1..81593770 100644
--- a/harness/release-end-state-audit-20260910.md
+++ b/harness/release-end-state-audit-20260910.md
@@ -86,3 +86,5 @@ Root authorized the bounded implementation in `crates/oa-desktop/src/forms.rs`,
Ten targeted tests are present. They cover absent/empty/existing history; descriptor-derived complete witnesses for every offered scalar/keyed fact against actual FacetReview deserialization and historical validation; rejected NA/NA and duplicates with unchanged Document snapshots; deleted UUID keys/observations; numeric limits and invalid raw repair; compound values and constructor switching; TypeRef's nested primitive picker; exact Unicode/quoted strings and literal keys; cache relocation/recovery; clear/recreate isolation; source/JSON round-trip and exact one-step Undo/Redo including retained source comments.
**Verification status:** `git diff --check` passes for the candidate (an unrelated generated HTML line-ending warning is present). No Cargo, rustfmt, native GUI or integration suite was run by this worker; root owns the serialized build/toolchain lease. F1 is therefore a ready source candidate, not an accepted fix yet. Root should run targeted history tests, the relevant desktop suite and strict lint, then exercise expanded history controls at normal/minimum width, typed Add/repair, paging, apply/reopen and Undo in the actual desktop. F2 and shared decision/wiki synthesis remain root-owned. F3 remains open.
+
+**Independent-review follow-up:** behavior_audit found a real defect in snapshot `01bb52a`: the reused constructor-cache key mapped null, numbers, booleans and arrays to the same empty tag, also colliding with empty UUID input. The nested optional picker consequently restored the old branch immediately instead of switching; earlier tests that injected complete Observed values skipped that event. The bounded follow-up adds history-only typed constructor identities (including distinct enum and tagged-object variants) while retaining other property-editor behavior. Two additional tests derive the visible nested picker choices and dispatch their actual FieldShape events: absent/present integer with invalid raw recovery and exact Value(None); empty UUID/absence with deleted identity; optional list/absence with nested Integer/String constructor retention and recovery. The resulting accepted models still receive exact Document/source/Undo checks. Twelve history tests are now present. Whitespace check passes; compilation, execution and native acceptance remain pending with root.
From aed40d74647ce3e1861f24deaf2d1fb7a56d9d7a Mon Sep 17 00:00:00 2001
From: "sorokin.oleg.edu"
Date: Thu, 10 Sep 2026 11:10:24 -0700
Subject: [PATCH 03/14] Fix inline architecture naming and captured Escape
cancellation
---
crates/oa-desktop/src/app.rs | 5 +-
.../src/app/architecture_authoring.rs | 46 ++-
.../src/app/architecture_authoring/palette.rs | 2 +-
.../src/app/architecture_authoring/tests.rs | 378 ++++++++++++++++++
crates/oa-desktop/src/guide.rs | 8 +-
...release-palette-implementation-20260910.md | 140 +++++++
...ase-palette-independent-review-20260910.md | 30 ++
harness/release-palette-native-20260910.md | 118 ++++++
8 files changed, 710 insertions(+), 17 deletions(-)
create mode 100644 crates/oa-desktop/src/app/architecture_authoring/tests.rs
create mode 100644 harness/release-palette-implementation-20260910.md
create mode 100644 harness/release-palette-independent-review-20260910.md
create mode 100644 harness/release-palette-native-20260910.md
diff --git a/crates/oa-desktop/src/app.rs b/crates/oa-desktop/src/app.rs
index de931069..44bf6d85 100644
--- a/crates/oa-desktop/src/app.rs
+++ b/crates/oa-desktop/src/app.rs
@@ -1019,6 +1019,7 @@ 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,
@@ -1076,9 +1077,7 @@ impl App {
// navigation and close retain raw input until explicit completion/cancel.
if self.architecture.is_some() {
if matches!(message, Message::Escape) {
- self.architecture = None;
- self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
- return Task::none();
+ return self.architecture_event(architecture_authoring::Event::Cancel);
}
let placing = self.architecture.as_ref().is_some_and(|s| s.saved.placing);
if !matches!(
diff --git a/crates/oa-desktop/src/app/architecture_authoring.rs b/crates/oa-desktop/src/app/architecture_authoring.rs
index 03b98e01..5685c611 100644
--- a/crates/oa-desktop/src/app/architecture_authoring.rs
+++ b/crates/oa-desktop/src/app/architecture_authoring.rs
@@ -3,6 +3,8 @@ 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)]
@@ -21,9 +23,35 @@ pub enum Event {
Filter(Filter),
ShowPlaced(bool),
Page(usize),
- Edit,
+ CapturedEscape,
Cancel,
}
+
+pub fn subscription(app: &App) -> Subscription {
+ 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 {
+ // 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 {
@@ -336,9 +364,15 @@ 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
@@ -375,13 +409,6 @@ 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()
@@ -560,9 +587,10 @@ pub fn inline_editor(app: &App) -> Option> {
row![
iced::widget::Space::new().width(x),
iced::widget::opaque(
- iced::widget::container(name_editor(app))
+ iced::widget::container(scrollable(name_editor(app)))
.padding(10)
.width(width)
+ .max_height((size.height - y - 8.).max(0.))
.style(crate::style::card)
)
]
diff --git a/crates/oa-desktop/src/app/architecture_authoring/palette.rs b/crates/oa-desktop/src/app/architecture_authoring/palette.rs
index 64f8cea8..f73a82f4 100644
--- a/crates/oa-desktop/src/app/architecture_authoring/palette.rs
+++ b/crates/oa-desktop/src/app/architecture_authoring/palette.rs
@@ -165,7 +165,7 @@ fn tool<'a>(
.into()
}
pub fn view(app: &App) -> Element<'_, Message> {
- let mut content = column![tool(
+ let mut content = iced::widget::column![tool(
app,
Icon::Collapse(app.architecture_palette_expanded),
if app.architecture_palette_expanded {
diff --git a/crates/oa-desktop/src/app/architecture_authoring/tests.rs b/crates/oa-desktop/src/app/architecture_authoring/tests.rs
new file mode 100644
index 00000000..9ac234ec
--- /dev/null
+++ b/crates/oa-desktop/src/app/architecture_authoring/tests.rs
@@ -0,0 +1,378 @@
+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::>()
+ .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::>()
+ .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()
+ );
+}
diff --git a/crates/oa-desktop/src/guide.rs b/crates/oa-desktop/src/guide.rs
index 32c4e0b1..f1219c84 100644
--- a/crates/oa-desktop/src/guide.rs
+++ b/crates/oa-desktop/src/guide.rs
@@ -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.",
- "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.",
+ "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.",
"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 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.",
+ "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.",
"Choose Architecture · allocated work to review responsibilities. Choose Functional dataflow to review the activity/function hierarchy and its exchanges independently.",
],
),
diff --git a/harness/release-palette-implementation-20260910.md b/harness/release-palette-implementation-20260910.md
new file mode 100644
index 00000000..f0001d76
--- /dev/null
+++ b/harness/release-palette-implementation-20260910.md
@@ -0,0 +1,140 @@
+# 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.
diff --git a/harness/release-palette-independent-review-20260910.md b/harness/release-palette-independent-review-20260910.md
new file mode 100644
index 00000000..ebbfc402
--- /dev/null
+++ b/harness/release-palette-independent-review-20260910.md
@@ -0,0 +1,30 @@
+# 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.
diff --git a/harness/release-palette-native-20260910.md b/harness/release-palette-native-20260910.md
new file mode 100644
index 00000000..1feb3e1a
--- /dev/null
+++ b/harness/release-palette-native-20260910.md
@@ -0,0 +1,118 @@
+# 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.
From eb5161b207f69c5dd110dd1dace827e45e6a7c0c Mon Sep 17 00:00:00 2001
From: Codex
Date: Thu, 10 Sep 2026 11:24:18 -0700
Subject: [PATCH 04/14] Prepare scoped lifecycle projection and align authoring
guide
---
crates/oa-desktop/src/behavior.rs | 3 +
.../src/behavior/scenario_lifecycle.rs | 283 +++++++++++
.../src/behavior/scenario_lifecycle/tests.rs | 470 ++++++++++++++++++
crates/oa-desktop/src/guide.rs | 16 +-
crates/oa-server/tests/semantic_schema.py | 3 +-
...ease-scenario-lifecycle-design-20260910.md | 86 ++++
6 files changed, 854 insertions(+), 7 deletions(-)
create mode 100644 crates/oa-desktop/src/behavior/scenario_lifecycle.rs
create mode 100644 crates/oa-desktop/src/behavior/scenario_lifecycle/tests.rs
create mode 100644 harness/release-scenario-lifecycle-design-20260910.md
diff --git a/crates/oa-desktop/src/behavior.rs b/crates/oa-desktop/src/behavior.rs
index 1012b0e5..8726fd75 100644
--- a/crates/oa-desktop/src/behavior.rs
+++ b/crates/oa-desktop/src/behavior.rs
@@ -15,6 +15,9 @@ use iced::{
use oa_core::*;
use std::collections::BTreeMap;
pub(crate) mod scenario;
+// Projection acceptance precedes the coordinated scenario renderer integration.
+#[cfg(test)]
+mod scenario_lifecycle;
pub(crate) mod state_canvas;
pub(crate) mod state_edit;
pub(crate) mod state_menu;
diff --git a/crates/oa-desktop/src/behavior/scenario_lifecycle.rs b/crates/oa-desktop/src/behavior/scenario_lifecycle.rs
new file mode 100644
index 00000000..18a0ba9c
--- /dev/null
+++ b/crates/oa-desktop/src/behavior/scenario_lifecycle.rs
@@ -0,0 +1,283 @@
+//! Read-only lifecycle geometry. Operand screen order is never execution order.
+use oa_core::{FragmentOperator, Id, Kind, MessageKind, OccurrenceBody, Project, SharedBody};
+use std::collections::{BTreeMap, BTreeSet};
+
+/// Maps already recorded by the sequence renderer. Rows carry their owning sequence.
+pub(crate) struct Geometry<'a> {
+ pub rows: &'a BTreeMap,
+ pub scopes: &'a BTreeMap,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub(crate) struct Span {
+ pub start: f32,
+ pub end: f32,
+}
+impl Span {
+ fn valid(self) -> bool {
+ self.start.is_finite() && self.end.is_finite() && self.start <= self.end
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) enum Life {
+ Alive,
+ Absent,
+ Unresolved,
+}
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub(crate) struct Interval {
+ pub scope: Id,
+ pub span: Span,
+ pub life: Life,
+}
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) enum MarkerKind {
+ Birth,
+ Death,
+}
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub(crate) struct Marker {
+ pub occurrence: Id,
+ pub scope: Id,
+ pub y: f32,
+ pub kind: MarkerKind,
+}
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) enum UncertaintyReason {
+ Conditional,
+ Repeated,
+ Parallel,
+ MissingGeometry,
+}
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) struct Uncertainty {
+ pub scope: Id,
+ pub reason: UncertaintyReason,
+}
+#[derive(Debug, Clone, PartialEq)]
+pub(crate) struct LaneLifetime {
+ pub lifeline: Id,
+ pub intervals: Vec,
+ pub markers: Vec,
+ pub uncertainties: Vec,
+}
+
+/// Stable lane-ID order; source/model/history and caller-owned geometry are untouched.
+/// An opaque ScenarioUse is a local use, not a caller birth/death contract.
+pub(crate) fn project_lifetimes(
+ project: &Project,
+ scenario: Id,
+ geometry: &Geometry<'_>,
+) -> Vec {
+ if project
+ .elements
+ .get(&scenario)
+ .is_none_or(|e| e.kind() != Kind::Scenario)
+ {
+ return vec![];
+ }
+ project
+ .elements
+ .values()
+ .filter(|e| e.kind() == Kind::Lifeline && e.parent == Some(scenario))
+ .map(|e| {
+ let mut projection = Projection {
+ project,
+ geometry,
+ lane: e.id,
+ visiting: BTreeSet::new(),
+ result: LaneLifetime {
+ lifeline: e.id,
+ intervals: vec![],
+ markers: vec![],
+ uncertainties: vec![],
+ },
+ };
+ let initial = if projection.contains(scenario, Some(MarkerKind::Birth)) {
+ Life::Unresolved
+ } else {
+ Life::Alive
+ };
+ projection.sequence(scenario, initial);
+ projection.result
+ })
+ .collect()
+}
+
+struct Projection<'a, 'g> {
+ project: &'a Project,
+ geometry: &'a Geometry<'g>,
+ lane: Id,
+ visiting: BTreeSet,
+ result: LaneLifetime,
+}
+impl Projection<'_, '_> {
+ fn body(&self, id: Id) -> Option<&OccurrenceBody> {
+ match self.project.elements.get(&id)?.shared()? {
+ SharedBody::Occurrence { body, .. } => Some(body),
+ _ => None,
+ }
+ }
+ fn event(&self, id: Id) -> Option {
+ match self.body(id)? {
+ OccurrenceBody::Exchange {
+ receiver: Some(line),
+ message_kind,
+ ..
+ } if *line == self.lane => match message_kind {
+ MessageKind::Create => Some(MarkerKind::Birth),
+ MessageKind::Delete => Some(MarkerKind::Death),
+ _ => None,
+ },
+ _ => None,
+ }
+ }
+ fn contains(&self, scope: Id, kind: Option) -> bool {
+ // Local structural walk only: no guards, iteration simulation or reuse expansion.
+ let mut pending = vec![scope];
+ let mut seen = BTreeSet::new();
+ while let Some(id) = pending.pop() {
+ if !seen.insert(id) {
+ continue;
+ }
+ if self
+ .event(id)
+ .is_some_and(|event| kind.is_none_or(|k| event == k))
+ {
+ return true;
+ }
+ if let Some(steps) = self.project.elements.get(&id).and_then(|e| e.steps()) {
+ pending.extend(steps);
+ }
+ if let Some(OccurrenceBody::Fragment { branches, .. }) = self.body(id) {
+ pending.extend(branches);
+ }
+ }
+ false
+ }
+ fn span(&self, scope: Id) -> Option {
+ let &(start, end) = self.geometry.scopes.get(&scope)?;
+ let span = Span { start, end };
+ span.valid().then_some(span)
+ }
+ fn uncertain(&mut self, scope: Id, reason: UncertaintyReason) {
+ let value = Uncertainty { scope, reason };
+ if !self.result.uncertainties.contains(&value) {
+ self.result.uncertainties.push(value);
+ }
+ }
+ fn interval(&mut self, scope: Id, start: f32, end: f32, life: Life) {
+ if start < end && start.is_finite() && end.is_finite() {
+ self.result.intervals.push(Interval {
+ scope,
+ span: Span { start, end },
+ life,
+ });
+ }
+ }
+ fn sequence(&mut self, scope: Id, entry: Life) -> Life {
+ let Some(span) = self.span(scope) else {
+ self.uncertain(scope, UncertaintyReason::MissingGeometry);
+ return Life::Unresolved;
+ };
+ if !self.visiting.insert(scope) {
+ self.uncertain(scope, UncertaintyReason::MissingGeometry);
+ return Life::Unresolved;
+ }
+ let steps = self
+ .project
+ .elements
+ .get(&scope)
+ .and_then(|e| e.steps())
+ .cloned()
+ .unwrap_or_default();
+ // A creation directly in this sequence fixes its local birth boundary.
+ // A creation only in a nested operand cannot fix its ancestor's entry.
+ let mut life = if steps
+ .iter()
+ .any(|&id| self.event(id) == Some(MarkerKind::Birth))
+ {
+ Life::Absent
+ } else {
+ entry
+ };
+ let mut cursor = span.start;
+ for id in steps {
+ let event = self.event(id);
+ let fragment = match self.body(id) {
+ Some(OccurrenceBody::Fragment { operator, branches }) => {
+ Some((*operator, branches.clone()))
+ }
+ _ => None,
+ };
+ if event.is_none() && fragment.is_none() {
+ continue;
+ }
+ let y = self
+ .geometry
+ .rows
+ .get(&id)
+ .filter(|(owner, y)| {
+ *owner == scope && y.is_finite() && *y >= cursor && *y <= span.end
+ })
+ .map(|(_, y)| *y);
+ let Some(y) = y else {
+ self.uncertain(id, UncertaintyReason::MissingGeometry);
+ life = Life::Unresolved;
+ continue;
+ };
+ self.interval(scope, cursor, y, life);
+ cursor = y;
+ if let Some(kind) = event {
+ self.result.markers.push(Marker {
+ occurrence: id,
+ scope,
+ y,
+ kind,
+ });
+ life = match kind {
+ MarkerKind::Birth => Life::Alive,
+ MarkerKind::Death => Life::Absent,
+ };
+ }
+ if let Some((operator, branches)) = fragment {
+ let entry = life;
+ let mut missing = false;
+ for branch in branches {
+ let bounds = self
+ .span(branch)
+ .filter(|s| s.start >= cursor && s.end <= span.end);
+ if let Some(bounds) = bounds {
+ // Every sibling inherits the same entry, never its displayed predecessor's exit.
+ self.sequence(branch, entry);
+ cursor = bounds.end;
+ } else {
+ self.uncertain(branch, UncertaintyReason::MissingGeometry);
+ missing = true;
+ }
+ }
+ if self.contains(id, None) {
+ let reason = match operator {
+ FragmentOperator::Alternative | FragmentOperator::Optional => {
+ UncertaintyReason::Conditional
+ }
+ FragmentOperator::Loop => UncertaintyReason::Repeated,
+ FragmentOperator::Parallel => UncertaintyReason::Parallel,
+ };
+ self.uncertain(id, reason);
+ life = Life::Unresolved;
+ } else if missing {
+ life = Life::Unresolved;
+ }
+ }
+ }
+ self.interval(scope, cursor, span.end, life);
+ self.visiting.remove(&scope);
+ life
+ }
+}
+
+#[cfg(test)]
+#[path = "scenario_lifecycle/tests.rs"]
+mod tests;
diff --git a/crates/oa-desktop/src/behavior/scenario_lifecycle/tests.rs b/crates/oa-desktop/src/behavior/scenario_lifecycle/tests.rs
new file mode 100644
index 00000000..7821a5d7
--- /dev/null
+++ b/crates/oa-desktop/src/behavior/scenario_lifecycle/tests.rs
@@ -0,0 +1,470 @@
+use super::*;
+use oa_core::*;
+
+struct Fixture {
+ p: Project,
+ owner: Id,
+ a: Id,
+ b: Id,
+ exchange: Id,
+ rows: BTreeMap,
+ scopes: BTreeMap,
+}
+impl Fixture {
+ fn new(layer: Layer) -> Self {
+ let mut p = Project::empty();
+ let kind = if layer == Layer::Oa {
+ Kind::Activity
+ } else {
+ Kind::Function
+ };
+ let a = p.add_element(kind, layer, "Request", None).unwrap();
+ let b = p.add_element(kind, layer, "Provide", None).unwrap();
+ let owner = p
+ .add_element(Kind::Scenario, layer, "Lifecycle", None)
+ .unwrap();
+ let exchange = p.relate(
+ if layer == Layer::Oa {
+ RelationKind::Interaction
+ } else {
+ RelationKind::FunctionalExchange
+ },
+ "Service",
+ a,
+ b,
+ );
+ let line = |p: &mut Project, represented| {
+ let id = p
+ .add_element(Kind::Lifeline, layer, "Participant", Some(owner))
+ .unwrap();
+ *p.elements.get_mut(&id).unwrap().shared_mut().unwrap() = SharedBody::Lifeline {
+ represented: Some(represented),
+ };
+ id
+ };
+ let a = line(&mut p, a);
+ let b = line(&mut p, b);
+ Self {
+ p,
+ owner,
+ a,
+ b,
+ exchange,
+ rows: BTreeMap::new(),
+ scopes: [(owner, (0., 600.))].into(),
+ }
+ }
+ fn occurrence(&mut self, owner: Id, y: f32, body: OccurrenceBody) -> Id {
+ let mut e = self
+ .p
+ .make_element(
+ Kind::Occurrence,
+ self.p.elements[&owner].layer(),
+ "Occurrence",
+ Some(owner),
+ )
+ .unwrap();
+ let id = e.id;
+ *e.shared_mut().unwrap() = SharedBody::Occurrence {
+ body,
+ precondition: String::new(),
+ postcondition: String::new(),
+ };
+ self.p.elements.insert(id, e);
+ if let Some(steps) = self.p.elements.get_mut(&owner).unwrap().steps_mut() {
+ steps.push(id);
+ } else if let Some(SharedBody::Occurrence {
+ body: OccurrenceBody::Fragment { branches, .. },
+ ..
+ }) = self.p.elements.get_mut(&owner).unwrap().shared_mut()
+ {
+ branches.push(id);
+ }
+ self.rows.insert(id, (owner, y));
+ id
+ }
+ fn message(&mut self, owner: Id, y: f32, kind: MessageKind) -> Id {
+ self.occurrence(
+ owner,
+ y,
+ OccurrenceBody::Exchange {
+ exchange: Some(self.exchange),
+ sender: Some(self.a),
+ receiver: Some(self.b),
+ message_kind: kind,
+ operation: None,
+ reply_to: None,
+ values: vec![],
+ },
+ )
+ }
+ fn fragment(&mut self, owner: Id, y: f32, operator: FragmentOperator) -> Id {
+ self.occurrence(
+ owner,
+ y,
+ OccurrenceBody::Fragment {
+ operator,
+ branches: vec![],
+ },
+ )
+ }
+ fn branch(&mut self, fragment: Id, start: f32, end: f32) -> Id {
+ let id = self.occurrence(
+ fragment,
+ start,
+ OccurrenceBody::Branch {
+ guard: "condition".into(),
+ occurrences: vec![],
+ },
+ );
+ self.scopes.insert(id, (start, end));
+ id
+ }
+ fn project(&self) -> Vec {
+ let errors: Vec<_> = validation::validate(&self.p)
+ .into_iter()
+ .filter(|i| i.severity == validation::Severity::Error)
+ .collect();
+ assert!(
+ errors.is_empty(),
+ "fixture must be ordinarily admitted: {errors:?}"
+ );
+ let before = self.p.clone();
+ let source = source_project::format_bundle(&self.p);
+ let geometry = Geometry {
+ rows: &self.rows,
+ scopes: &self.scopes,
+ };
+ let result = project_lifetimes(&self.p, self.owner, &geometry);
+ assert_eq!(result, project_lifetimes(&self.p, self.owner, &geometry));
+ assert_eq!(self.p, before);
+ assert_eq!(source_project::format_bundle(&self.p), source);
+ assert!(
+ result
+ .windows(2)
+ .all(|pair| pair[0].lifeline < pair[1].lifeline)
+ );
+ result
+ }
+ fn lane(&self) -> LaneLifetime {
+ self.project()
+ .into_iter()
+ .find(|lane| lane.lifeline == self.b)
+ .unwrap()
+ }
+}
+fn intervals(lane: &LaneLifetime, scope: Id) -> Vec<(f32, f32, Life)> {
+ lane.intervals
+ .iter()
+ .filter(|i| i.scope == scope)
+ .map(|i| (i.span.start, i.span.end, i.life))
+ .collect()
+}
+
+#[test]
+fn root_lifetime_has_exact_birth_death_boundaries_without_source_mutation() {
+ for layer in [Layer::Oa, Layer::Sa] {
+ let mut f = Fixture::new(layer);
+ let create = f.message(f.owner, 100., MessageKind::Create);
+ f.message(f.owner, 200., MessageKind::Asynchronous);
+ let delete = f.message(f.owner, 300., MessageKind::Delete);
+ let lane = f.lane();
+ assert_eq!(
+ intervals(&lane, f.owner),
+ vec![
+ (0., 100., Life::Absent),
+ (100., 300., Life::Alive),
+ (300., 600., Life::Absent)
+ ]
+ );
+ assert_eq!(
+ lane.markers,
+ vec![
+ Marker {
+ occurrence: create,
+ scope: f.owner,
+ y: 100.,
+ kind: MarkerKind::Birth
+ },
+ Marker {
+ occurrence: delete,
+ scope: f.owner,
+ y: 300.,
+ kind: MarkerKind::Death
+ },
+ ]
+ );
+ let sender = f
+ .project()
+ .into_iter()
+ .find(|lane| lane.lifeline == f.a)
+ .unwrap();
+ assert_eq!(intervals(&sender, f.owner), vec![(0., 600., Life::Alive)]);
+ }
+}
+
+#[test]
+fn alternative_death_does_not_kill_its_displayed_sibling() {
+ let mut f = Fixture::new(Layer::Sa);
+ let fragment = f.fragment(f.owner, 100., FragmentOperator::Alternative);
+ let left = f.branch(fragment, 120., 250.);
+ let right = f.branch(fragment, 300., 450.);
+ f.message(left, 200., MessageKind::Delete);
+ f.message(right, 350., MessageKind::Asynchronous);
+ let lane = f.lane();
+ assert_eq!(
+ intervals(&lane, left),
+ vec![(120., 200., Life::Alive), (200., 250., Life::Absent)]
+ );
+ assert_eq!(intervals(&lane, right), vec![(300., 450., Life::Alive)]);
+ assert_eq!(
+ intervals(&lane, f.owner),
+ vec![(0., 100., Life::Alive), (450., 600., Life::Unresolved)]
+ );
+ assert_eq!(
+ lane.uncertainties,
+ vec![Uncertainty {
+ scope: fragment,
+ reason: UncertaintyReason::Conditional
+ }]
+ );
+}
+
+#[test]
+fn conditional_birth_does_not_infer_absence_in_siblings_or_ancestors() {
+ for operator in [FragmentOperator::Alternative, FragmentOperator::Optional] {
+ let mut f = Fixture::new(Layer::Oa);
+ let fragment = f.fragment(f.owner, 100., operator);
+ let left = f.branch(fragment, 120., 250.);
+ let right = f.branch(fragment, 300., 450.);
+ f.message(left, 200., MessageKind::Create);
+ let lane = f.lane();
+ assert_eq!(
+ intervals(&lane, left),
+ vec![(120., 200., Life::Absent), (200., 250., Life::Alive)]
+ );
+ assert_eq!(
+ intervals(&lane, right),
+ vec![(300., 450., Life::Unresolved)]
+ );
+ assert_eq!(
+ intervals(&lane, f.owner),
+ vec![(0., 100., Life::Unresolved), (450., 600., Life::Unresolved)]
+ );
+ }
+}
+
+#[test]
+fn loop_lifetime_is_local_and_warning_bearing_loops_remain_drawable() {
+ for balanced in [true, false] {
+ let mut f = Fixture::new(Layer::Sa);
+ let fragment = f.fragment(f.owner, 100., FragmentOperator::Loop);
+ let iteration = f.branch(fragment, 120., 450.);
+ let create = f.message(iteration, 160., MessageKind::Create);
+ f.message(iteration, 250., MessageKind::Asynchronous);
+ if balanced {
+ f.message(iteration, 350., MessageKind::Delete);
+ }
+ let lane = f.lane();
+ assert_eq!(lane.markers.len(), if balanced { 2 } else { 1 });
+ assert_eq!(lane.markers[0].occurrence, create);
+ assert!(lane.uncertainties.contains(&Uncertainty {
+ scope: fragment,
+ reason: UncertaintyReason::Repeated
+ }));
+ assert_eq!(
+ intervals(&lane, f.owner).last().unwrap().2,
+ Life::Unresolved
+ );
+ assert_eq!(
+ validation::validate(&f.p)
+ .iter()
+ .any(|i| i.code == "lifeline_repetition"),
+ !balanced
+ );
+ }
+}
+
+#[test]
+fn optional_cleanup_inside_loop_does_not_claim_balanced_or_definite_exit() {
+ let mut f = Fixture::new(Layer::Sa);
+ let repeated = f.fragment(f.owner, 100., FragmentOperator::Loop);
+ let iteration = f.branch(repeated, 120., 500.);
+ f.message(iteration, 160., MessageKind::Create);
+ let optional = f.fragment(iteration, 280., FragmentOperator::Optional);
+ let cleanup = f.branch(optional, 320., 400.);
+ f.message(cleanup, 350., MessageKind::Delete);
+ let lane = f.lane();
+ assert_eq!(
+ intervals(&lane, cleanup),
+ vec![(320., 350., Life::Alive), (350., 400., Life::Absent)]
+ );
+ assert_eq!(
+ intervals(&lane, iteration),
+ vec![
+ (120., 160., Life::Absent),
+ (160., 280., Life::Alive),
+ (400., 500., Life::Unresolved)
+ ]
+ );
+ assert!(
+ validation::validate(&f.p)
+ .iter()
+ .any(|i| i.code == "lifeline_repetition")
+ );
+ assert!(lane.uncertainties.contains(&Uncertainty {
+ scope: optional,
+ reason: UncertaintyReason::Conditional
+ }));
+ assert!(lane.uncertainties.contains(&Uncertainty {
+ scope: repeated,
+ reason: UncertaintyReason::Repeated
+ }));
+}
+
+#[test]
+fn lifecycle_free_fragments_do_not_poison_other_lanes() {
+ let mut f = Fixture::new(Layer::Oa);
+ let fragment = f.fragment(f.owner, 100., FragmentOperator::Parallel);
+ let left = f.branch(fragment, 120., 250.);
+ let right = f.branch(fragment, 300., 450.);
+ f.message(left, 200., MessageKind::Delete);
+ let result = f.project();
+ let sender = result.iter().find(|lane| lane.lifeline == f.a).unwrap();
+ assert!(sender.intervals.iter().all(|i| i.life == Life::Alive));
+ assert!(sender.uncertainties.is_empty());
+ let target = result.iter().find(|lane| lane.lifeline == f.b).unwrap();
+ assert!(target.uncertainties.contains(&Uncertainty {
+ scope: fragment,
+ reason: UncertaintyReason::Parallel
+ }));
+ assert_eq!(intervals(target, right), vec![(300., 450., Life::Alive)]);
+}
+
+#[test]
+fn explicit_receiver_is_authoritative_even_with_missing_exchange() {
+ let mut f = Fixture::new(Layer::Oa);
+ let create = f.message(f.owner, 100., MessageKind::Create);
+ if let Some(SharedBody::Occurrence {
+ body: OccurrenceBody::Exchange { exchange, .. },
+ ..
+ }) = f.p.elements.get_mut(&create).unwrap().shared_mut()
+ {
+ *exchange = None;
+ }
+ assert_eq!(f.lane().markers[0].occurrence, create);
+ if let Some(SharedBody::Occurrence {
+ body: OccurrenceBody::Exchange {
+ receiver, exchange, ..
+ },
+ ..
+ }) = f.p.elements.get_mut(&create).unwrap().shared_mut()
+ {
+ *receiver = None;
+ *exchange = Some(f.exchange);
+ }
+ assert!(
+ f.lane().markers.is_empty(),
+ "exchange endpoints never invent a receiver"
+ );
+}
+
+#[test]
+fn missing_geometry_is_reported_without_nonfinite_or_guessed_markers() {
+ let mut f = Fixture::new(Layer::Oa);
+ let create = f.message(f.owner, 100., MessageKind::Create);
+ f.rows.insert(create, (f.owner, f32::NAN));
+ let lane = f.lane();
+ assert!(lane.markers.is_empty());
+ assert_eq!(
+ lane.uncertainties,
+ vec![Uncertainty {
+ scope: create,
+ reason: UncertaintyReason::MissingGeometry
+ }]
+ );
+ assert_eq!(
+ intervals(&lane, f.owner),
+ vec![(0., 600., Life::Unresolved)]
+ );
+ f.scopes.remove(&f.owner);
+ let lane = f.lane();
+ assert!(lane.intervals.is_empty());
+ assert_eq!(lane.uncertainties[0].scope, f.owner);
+}
+
+#[test]
+fn scenario_use_is_opaque_and_never_imports_callee_lifecycle_events() {
+ let mut f = Fixture::new(Layer::Oa);
+ let callee =
+ f.p.add_element(Kind::Scenario, Layer::Oa, "Callee", None)
+ .unwrap();
+ let remote_a =
+ f.p.add_element(Kind::Lifeline, Layer::Oa, "Remote A", Some(callee))
+ .unwrap();
+ let remote_b =
+ f.p.add_element(Kind::Lifeline, Layer::Oa, "Remote B", Some(callee))
+ .unwrap();
+ for (remote, local) in [(remote_a, f.a), (remote_b, f.b)] {
+ let body = f.p.elements[&local].shared().unwrap().clone();
+ *f.p.elements.get_mut(&remote).unwrap().shared_mut().unwrap() = body;
+ }
+ f.occurrence(
+ callee,
+ 200.,
+ OccurrenceBody::Exchange {
+ exchange: Some(f.exchange),
+ sender: Some(remote_a),
+ receiver: Some(remote_b),
+ message_kind: MessageKind::Create,
+ operation: None,
+ reply_to: None,
+ values: vec![],
+ },
+ );
+ f.occurrence(
+ f.owner,
+ 100.,
+ OccurrenceBody::ScenarioUse {
+ scenario: Some(callee),
+ bindings: vec![
+ LifelineBinding {
+ referenced: remote_a,
+ local: f.a,
+ },
+ LifelineBinding {
+ referenced: remote_b,
+ local: f.b,
+ },
+ ],
+ },
+ );
+ let lane = f.lane();
+ assert_eq!(intervals(&lane, f.owner), vec![(0., 600., Life::Alive)]);
+ assert!(lane.markers.is_empty());
+ assert!(lane.uncertainties.is_empty());
+}
+
+#[test]
+fn projection_retains_exact_authored_source_and_does_not_enter_history() {
+ use crate::document::Document;
+ let mut f = Fixture::new(Layer::Oa);
+ f.message(f.owner, 100., MessageKind::Create);
+ f.message(f.owner, 300., MessageKind::Delete);
+ let mut source = source_project::format_bundle(&f.p);
+ let text = source.files.get_mut(&source.entry).unwrap();
+ *text = format!("// Retained author note — 雨\r\n{text}\n");
+ let document =
+ Document::from_bundle(source.clone(), source_project::ProjectLimits::default()).unwrap();
+ let accepted = document.project().clone();
+ let geometry = Geometry {
+ rows: &f.rows,
+ scopes: &f.scopes,
+ };
+ let result = project_lifetimes(document.project(), f.owner, &geometry);
+ assert_eq!(result, f.project());
+ assert_eq!(document.bundle(), &source);
+ assert_eq!(document.project(), &accepted);
+ assert!(!document.can_undo());
+ assert!(!document.can_redo());
+}
diff --git a/crates/oa-desktop/src/guide.rs b/crates/oa-desktop/src/guide.rs
index f1219c84..31c24c26 100644
--- a/crates/oa-desktop/src/guide.rs
+++ b/crates/oa-desktop/src/guide.rs
@@ -491,18 +491,18 @@ fn article(topic: Topic) -> Article {
&[
"Open Behavior & scenarios, choose Scenario under Create element and add it at a legal root/owner. Select the scenario in the behavior selector.",
"Use + Lifeline and choose the participant or work it represents. Choices follow the scenario's viewpoint: operational activities, operational participants, system functions or system boundary participants.",
- "Choose + Message and its style. Call + correlated reply creates both occurrences in one edit. Select the exchange and compatible sending/receiving lifelines. Reply to existing call offers valid unanswered calls.",
- "Use Action, Observe state or Fragment for other behavior. A local timer has an expression and a lifeline, with no global exchange. Create and delete messages describe participant lifetime; Model checks also flag risks inside repeated loops.",
- "Choose a message style and Draw message. Start at a highlighted sequence position on a lifeline, then click or drag to the receiving lifeline. The form retains the chosen position while you select the exchange and details. Escape cancels the gesture.",
+ "Choose a message style in the tool selector. Start at a highlighted sequence position on a lifeline, then click or drag to the receiving lifeline. Complete the exchange and details in the form, which retains that sequence position. Escape cancels the gesture.",
+ "Call + correlated reply creates both occurrences in one edit. Reply to existing call offers valid unanswered calls. A local timer has an expression and a lifeline, with no global exchange. Create and delete messages describe participant lifetime; Model checks also flag risks inside repeated loops.",
+ "Choose Add action, Observe state, Reuse scenario or Add fragment and click the intended sequence position. Clicking an action or observation on a lifeline also selects that lifeline. Complete any remaining choices before Create.",
"In Select / reorder, click an occurrence and drag its ↕ handle to another position within the same sequence. The ordered list's up/down controls provide another way to reorder. Select an occurrence to inspect its particular use of the exchange, including its message values.",
],
),
walkthrough(
"Describe alternatives",
&[
- "Choose Fragment and select ALT, PAR, LOOP or OPT. Enter the branch conditions; the tool creates the fragment and its operands together.",
- "Use the Destination selector in subsequent message or action forms to choose the intended operand. New occurrences append to that operand's ordered behavior.",
- "Use Reuse scenario to refer to existing behavior. Bind every referenced lifeline to a compatible local lifeline explicitly. Open source follows the original scenario without copying its contents.",
+ "Choose Add fragment, click its sequence position and select ALT, PAR, LOOP or OPT. Enter the branch conditions; the tool creates the fragment and its operands together.",
+ "Click the highlighted position inside an operand to place the next message, action or reference there. The form shows its destination and insertion position. Append to this sequence instead explicitly moves the pending insertion to that sequence's end.",
+ "Choose Reuse scenario and click its position, then bind every referenced lifeline to a compatible local lifeline explicitly. Select the resulting use and choose Open definition. Back to caller returns to that exact use and its viewport after you apply or cancel pending edits.",
"Inspect the scenario projection and Model checks. State exactly what each guard means, including units and assumptions where relevant.",
],
),
@@ -696,6 +696,10 @@ fn article(topic: Topic) -> Article {
"Map references by meaning",
"Reference mapping connect exact OA and SA identities used by the proposed change. Similar names are not sufficient. Map the records that play the same engineering role, including enclosing fields in qualified message-value paths. Mapping a referenced record does not authorize creating, moving or editing that record. If it needs a counterpart, establish that correspondence first.",
),
+ explain(
+ "Edit stored observations deliberately",
+ "In a trace's Properties, Edit stored history (advanced)… edits the observations retained by that trace. Choose the fact and enter its Operational and System observations explicitly. Not applicable, an observed absent value, and no stored fact have different meanings. Historical references may retain the exact UUID of a deleted record. Apply changes validates the complete history; Undo restores it. These history edits leave both counterpart records unchanged. Use Review counterpart changes… for Keep/Take decisions about their current engineering content.",
+ ),
walkthrough(
"Review one message value or lifeline binding",
&[
diff --git a/crates/oa-server/tests/semantic_schema.py b/crates/oa-server/tests/semantic_schema.py
index 18087f00..861b83c0 100644
--- a/crates/oa-server/tests/semantic_schema.py
+++ b/crates/oa-server/tests/semantic_schema.py
@@ -2,7 +2,8 @@
Run from any directory with Python 3: semantic_schema.py [--check] [--validate].
--validate additionally runs source-7 structural fixtures with jsonschema.
-This generates documentation only, never runtime selectors or model mutations.
+The desktop stored-history editor reads the checked-in descriptor for field
+shapes. Core types and validation remain authoritative for model mutations.
"""
import copy
import json
diff --git a/harness/release-scenario-lifecycle-design-20260910.md b/harness/release-scenario-lifecycle-design-20260910.md
new file mode 100644
index 00000000..92d32b5b
--- /dev/null
+++ b/harness/release-scenario-lifecycle-design-20260910.md
@@ -0,0 +1,86 @@
+# Scenario lifecycle geometry proposal — 2026-09-10
+
+Author: Codex-956atf /root/palette_repair. Status: source/design review only; not implemented, compiled or native-tested. Ownership is this report only. Current scenario changes belong to behavior_audit; the subsequent scenario layout work belongs to the separately coordinated Codex-9e8r6t session.
+
+## Decision
+
+Render Create/Delete as lifecycle events with scope-aware live segments, separate from neutral lane guides. Keep every existing lifeline's identity and editing target available. Do not shorten one full-height line using the earliest creation and latest deletion in screen coordinates: stacked ALT operands are alternatives, not successive execution.
+
+The first complete candidate should support exact root lifetime boundaries, operand-local boundaries, explicit unresolved continuation at conditional merges, and a source-navigation route for lifecycle hidden inside references. This is a derived desktop projection. No Project, occurrence, source, REST, recovery or history schema changes are needed. Rendering must never create/delete lifeline elements or infer new semantic lifecycle events.
+
+## Source evidence and limits
+
+- `crates/oa-desktop/src/behavior.rs`, `Scene::sequence`, currently draws every lane from y=85 to the scene bottom and puts its header at y=15. `Scene::occurrences` labels Create/Delete but renders ordinary message endpoints. `rows` and `sequence_bounds` already retain semantic sequence identities and row extents. `activation_sequence` provides a useful example of independent branch traversal, but lifecycle is a different analysis.
+- `crates/oa-core/src/validation.rs`, `possibly_before`, distinguishes exclusive ALT/Optional operands, possible parallel ordering, and ordered sibling contributions. `guaranteed_before` deliberately does not promote a conditional branch event into an unconditional fact after its fragment.
+- The same file's `validate_message` rejects a use possibly before Create or after Delete, including local actions/states and ScenarioUse bindings. It emits `lifeline_repetition` warnings for unbalanced loop lifetime events; these warnings are accepted model states, not rendering failures.
+- `validate_scenario_use` validates target/viewpoint/binding identities and recursion. A binding counts as a local lifeline use. It does not define a caller-visible birth/death contract, or inline a callee lifetime into the caller. A presentation change must not invent that contract.
+- Fragment guards are strings. The renderer cannot prove guard coverage, numeric loop count, or a branch's execution merely from its displayed position. In particular, do not treat an `else` spelling as an execution proof that the core does not supply.
+
+## Geometry and selection rules
+
+Maintain a neutral, low-contrast dotted lane guide for navigation and insertion, distinct from the stronger active-lifetime spine. A small legend or selected-lifeline description must explain that the guide is an editing axis, not evidence of existence. Without this distinction a shortened lifetime would make future Create placement undiscoverable or make unresolved paths appear definitely alive.
+
+Keep the existing top identity header as an explicitly neutral lane label. At a Create receiver row draw a compact birth capsule carrying the lifeline's short name and start its active spine there. Route the Create message into the capsule's sender-facing boundary; do not end the arrow at an unrelated center behind the label. At Delete draw an X centered on the receiver lane and stop the active spine there. An ordinary message keeps its existing arrow convention. For a self-Create/Delete use a local elbow with sufficient clearance so the event marker and arrowhead remain distinguishable.
+
+Each birth/death marker carries the original lifecycle occurrence ID for selection and properties. The lane header/guide carries the lifeline ID. Hit-test markers before guides; never reuse a fake element/relation ID. `lane_at` remains based on the lane map and visible canvas bounds rather than active spine segments, so users can insert a Create before the current birth or inspect an unused lane. Core validation remains the acceptance boundary for those edits.
+
+Do not let ordinary guide lines receive message arrowheads. Separate guide/lifetime geometry from `Line` message records, or introduce an explicit render role; overloading `dashed` would confuse reply dashes with lane semantics. Keep call activation geometry unchanged except for clipping/overlap QA. Lifetime is not call activation.
+
+## Conservative structural projection
+
+Use a pure per-lifeline traversal over the scenario/operand tree. Track a derived presentation state `Alive`, `Absent` or `Unresolved`. `Unresolved` means the stored local structure does not establish one lifetime across every represented path; it is not an invalid model state. This state exists only in the renderer.
+
+Initial state: a root-owned Create establishes absence before that event. Lifecycle classification is per exact local receiver lifeline: unrelated lane events and opaque ScenarioUse records do not change it. A lane with no Create anywhere in the local tree uses the ordinary pre-existing-lifeline convention. A lane whose birth is conditional/repeated starts unresolved outside that scope. When evidence is insufficient, retain the guide and local markers instead of asserting global absence or presence. Do not turn missing/partial receiver data into a new boundary. Conversely, an explicit receiver with an unresolved exchange still has its authored lifecycle event; relation completeness is a separate validation concern.
+
+| Structure | Concrete rule |
+| --- | --- |
+| Root ordered sequence | Traverse by stored steps. Create changes that local path to Alive at its row; Delete changes it to Absent. Draw only Alive intervals with the active spine; Absent intervals retain the neutral guide. Later explicit root events may establish a definite state again. A simple root Create → use → Delete therefore has exactly one live interval. |
+| ALT | Give each operand the same entry state and project each independently. A deletion in the upper operand never removes the lower operand's spine. If any operand affects lifetime, the enclosing continuation is Unresolved unless unconditional knowledge is separately established by a later root event. This conservative merge also avoids assuming guards are exhaustive. |
+| Optional | Draw its operand as one possible path and retain the bypass possibility. A lifetime change inside it cannot globally create/delete the lane after the frame. The continuation becomes Unresolved. |
+| Loop | Show one labeled iteration's local lifetime. A same-operand Create → use → Delete is a local interval, not a global one spanning every iteration. After a lifecycle-bearing loop, use Unresolved continuation because zero/exact iteration count and loop entry lifetime are not modeled. An unbalanced event stays visible and retains the existing repetition warning; do not silently repair it or suppress it. Optional cleanup inside a loop does not prove balance. |
+| Parallel | Draw each operand independently from the entry context. Screen order supplies no interleaving. Any lifecycle-affecting operand makes aggregate continuation Unresolved; do not choose last screen event as final state. No lifecycle events means ordinary continuation. Existing core validation still rejects lifecycle/use conflicts across parallel operands. |
+| Nested fragments | Apply the same rules recursively. An event clips only its current structural path; unresolved effects propagate outward. Each sibling starts from its enclosing entry context, not from its preceding displayed sibling's exit. Never connect a dead segment in one operand directly to the next operand's active segment as if it were resurrection. |
+| ScenarioUse | Keep the reference frame opaque. Show its binding usage/identity and the existing Open/Back navigation. Do not map the callee's internal row Y or births/deaths onto the caller. If indicating referenced lifecycle effects is useful, use a scope label such as “Lifecycle inside reference”; it must not claim the caller lane was created/deleted. The callee renders its own lifetimes when opened. |
+
+At unresolved intervals draw only the neutral guide and a concise scope/selection explanation, such as “Lifetime depends on operand.” Do not use a continuous active spine that suggests unconditional survival. Within an operand, a first local Create establishes its birth boundary; geometry before it in that operand is absent/unresolved as appropriate, without retroactively changing sibling or ancestor geometry.
+
+This proposal intentionally underclaims rather than inventing full execution analysis. A later optimization may prove additional definite joins only if it shares an authoritative semantic rule with core. It should not be a second private simulation engine in the desktop.
+
+## Minimum implementation partition
+
+1. Add a small pure module under `behavior/scenario/` (for example `lifecycle.rs`) returning scoped intervals and marker descriptors keyed by lifeline/occurrence/sequence IDs. Inputs: Project, visible scenario and row/scope geometry. It performs no commands, UUID generation, serialization writes or source formatting.
+2. After `Scene::occurrences` records positions, call this projection from `Scene::sequence`. If current `sequence_bounds` omit a needed fragment/operand frame bound, record that bound during the existing recursion; do not derive nesting from screen overlap.
+3. Extend Scene with explicit lane guides/lifetime markers, or a typed rendering role. Adapt message endpoint geometry only for resolved local Create receiver capsules. Paint guide → lifetime spine → messages → markers/labels, with deliberate marker hit priority.
+4. Preserve `lane_at`, slots, zoom, scroll, pending placement, and pointer-context cancellation behavior. Add selected-marker explanations through existing selection/properties affordances. Coordinate this with the separately owned scenario pane/layout change before editing shared `behavior.rs`.
+5. No core edit should be required for this conservative candidate. If definite post-reference or exhaustive-ALT effects are requested, stop treating them as geometry and propose the semantic contract explicitly first.
+
+## Exact fixtures and assertions
+
+Use ordinary Command/Document fixtures; assert projection leaves both Project and exact SourceBundle unchanged. For mutations followed by reprojection, one Undo/Redo must restore exact source plus prior geometry. Reuse existing core fixtures rather than weakening admission to make drawings easier.
+
+1. Root: sender A, receiver B; Create B, Action on B, Delete B. Assert one active B interval bounded by the two occurrence rows, one birth capsule, one X, guides before/after, no change to A lifetime. No-Create pre-existing B + Delete ends at Delete. Root Create without Delete continues to scenario bottom.
+2. ALT: delete B in left operand, ordinary use B in right. This is valid in `conditional_lifetime_changes_conflict_with_later_or_parallel_use_not_exclusive_use`. Assert right remains independently active; no active connection between operand layouts; continuation is unresolved. Swap displayed operands and assert identical semantic per-operand intervals.
+3. Optional: delete B only inside operand. Assert bypass remains represented and post-frame lifetime is unresolved. A later B use must still be rejected by core exactly as before; projection does not legitimize it.
+4. Conditional birth: Create B in one ALT/Optional operand, no creation in another/bypass. Assert only the creating path gets a birth marker; no global header relocation or claimed birth before the whole fragment.
+5. Loop: Create B → use B → Delete B in one iteration. Assert local interval and iteration scope. Unbalanced Create and unbalanced Delete remain drawable with `lifeline_repetition`. Nest Optional Delete after loop Create; warning and unresolved continuation remain, matching `optional_cleanup_does_not_claim_a_repeated_lifetime_is_balanced`.
+6. Parallel: lifecycle in one operand and conflicting use in another remains core-invalid. For a valid non-conflicting model, geometry never takes the later screen operand as a happens-after statement. Nested ALT inside PAR and PAR inside ALT terminate deterministically.
+7. Reuse: two distinct ScenarioUse occurrences bind the same referenced lifeline to different local lifelines. Assert no caller birth/death is inferred; caller use and binding IDs remain distinct; opening either reference shows its own source projection and Back restores the exact use. A caller Delete followed by a bound use remains core-invalid.
+8. Optional/incomplete records: unresolved receiver, absent target relation, empty fragment, empty operand, lifeline without represented element. An explicit receiver keeps its Create/Delete marker even when exchange=None; receiver=None never borrows an endpoint from the exchange. Retain existing placeholders, no panic, no guessed lifecycle boundary. Test accepted warning-bearing models; do not bypass core errors in native acceptance.
+9. Interaction: birth/X clicks select lifecycle occurrence; top header/guide selects lifeline; Add Action/Message/Create still reaches slots outside the active interval and core decides validity. At 0.5×/1×/2×, hit radius stays screen-sized. Scroll/resize/tool change between press/release must cancel stale gestures.
+10. Projection repeatability: unchanged model produces identical descriptors; rename only changes captions; alternate UUID insertion/map iteration order cannot reorder semantic paths. Read-only projection does not touch source bytes/history/camera.
+
+Useful existing core tests: `create_delete_and_local_timer_messages_enforce_lifetime_rules`, `conditional_lifetime_changes_conflict_with_later_or_parallel_use_not_exclusive_use`, `repeated_lifecycle_requires_per_iteration_balance_without_prohibiting_balanced_loops`, `optional_cleanup_does_not_claim_a_repeated_lifetime_is_balanced`, and scenario reuse binding/deletion tests in `crates/oa-core/tests/release_scenarios.rs`.
+
+## Native questions and acceptance boundary
+
+- At 1100×700 and 1440×900, can a new user distinguish an editing guide from the active spine and find the relevant operand? Does a birth capsule fit without covering the preceding slot, message label or pending insertion marker?
+- Check self-Create/Delete, long Unicode names, adjacent births on neighboring lanes, nested fragments and selected activation bars at minimum zoom. Marker/capsule collision needs actual rendering QA, not just coordinate tests.
+- Confirm neutral top lane labels are clearly identities, not birth boxes. If users still read them as existence, use a compact lane-label style and the row birth capsule; moving the only identity header into a conditional branch is not acceptable.
+- Check that post-fragment “unresolved” is understandable without making the diagram appear broken. Keep detail in selection help rather than repeating large warning text on every lane.
+- Current core does not export exhaustive-choice or caller lifecycle contracts. This is an explicit semantic limit, not something to guess from guard text or reference expansion.
+
+No B4 release acceptance is claimed by this document. Implement only after the scenario owner yields its snapshot; then run focused projection/core tests and native checks against the exact resulting binary.
+
+## Pure module implementation handoff
+
+The initial pure candidate is now in `crates/oa-desktop/src/behavior/scenario_lifecycle.rs` with nested tests. It has not been wired, compiled, formatted or runtime-tested. `Geometry` borrows the existing row map `Id -> (owning sequence Id, y)` and sequence bound map `Id -> (start, end)`; no extra fragment bounds are required. `project_lifetimes` returns deterministic lane-ID ordered `LaneLifetime` records with scoped intervals, occurrence-identified birth/death markers, and conditional/repeated/parallel/missing-geometry uncertainty. Opaque ScenarioUse does not change local state or inspect callees. Call the visualization 'local occurrence lifetime' when explaining this boundary. Missing or nonfinite geometry creates uncertainty descriptors, never guessed marker positions. Root owns module declaration and Scene integration after the concurrent scenario snapshot is stable.
From 4ecad78433bdfcf20792f073924c80d8ba37c251 Mon Sep 17 00:00:00 2001
From: Codex
Date: Thu, 10 Sep 2026 11:38:44 -0700
Subject: [PATCH 05/14] Seed optional history values from explicit constructors
---
crates/oa-desktop/src/forms/history_fields.rs | 4 ++-
.../src/forms/history_fields/tests.rs | 32 +++++++++++++++++++
2 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/crates/oa-desktop/src/forms/history_fields.rs b/crates/oa-desktop/src/forms/history_fields.rs
index 54805bdd..cd2b37fe 100644
--- a/crates/oa-desktop/src/forms/history_fields.rs
+++ b/crates/oa-desktop/src/forms/history_fields.rs
@@ -120,7 +120,9 @@ fn seed(schema: &'static Value, depth: usize) -> Result {
if let Some(options) = alternatives(schema) {
if options
.iter()
- .any(|s| matches_value(s, &json!("NotApplicable"), 0))
+ .filter_map(|s| resolve(s).ok())
+ .filter_map(|s| s.get("enum").and_then(Value::as_array))
+ .any(|values| values.contains(&json!("NotApplicable")))
{
return Ok(json!("NotApplicable"));
}
diff --git a/crates/oa-desktop/src/forms/history_fields/tests.rs b/crates/oa-desktop/src/forms/history_fields/tests.rs
index 23b40072..10af0691 100644
--- a/crates/oa-desktop/src/forms/history_fields/tests.rs
+++ b/crates/oa-desktop/src/forms/history_fields/tests.rs
@@ -752,6 +752,38 @@ fn choose_history_constructor(draft: &mut Draft, path: &str, label: &str) {
draft.shape(&path, value).unwrap();
}
+#[test]
+fn history_optional_uuid_and_text_seed_absence_without_inventing_observations() {
+ for fact in ["Header/Parent", "Artifact/review/Revision"] {
+ let side = format!("{FACETS}/0/{fact}/oa");
+ let schema = resolve(schema_at(&side).unwrap()).unwrap();
+ assert_eq!(seed(schema, 0).unwrap(), json!("NotApplicable"));
+ let observed_value = alternatives(schema)
+ .unwrap()
+ .iter()
+ .find(|branch| branch_label(branch) == "Observed value")
+ .unwrap();
+ let value = branch_seed(observed_value).unwrap();
+ assert_eq!(value, json!({"Value": null}));
+ assert_eq!(
+ seed(schema_at(&format!("{side}/Value")).unwrap(), 0).unwrap(),
+ Value::Null,
+ "a plain text/UUID shape is not the NotApplicable enum constructor"
+ );
+ if fact == "Header/Parent" {
+ assert_eq!(
+ serde_json::from_value::>>(value).unwrap(),
+ Observed::Value(None)
+ );
+ } else {
+ assert_eq!(
+ serde_json::from_value::>>(value).unwrap(),
+ Observed::Value(None)
+ );
+ }
+ }
+}
+
#[test]
fn history_nested_optional_number_events_switch_absent_present_and_recover_invalid_raw() {
let (project, id) = fixture(Some(baseline(vec![])));
From 8547b8582585bf732f74a0baae1f06fc0e36b31b Mon Sep 17 00:00:00 2001
From: Codex
Date: Thu, 10 Sep 2026 11:44:35 -0700
Subject: [PATCH 06/14] Record revised release requirements and native scenario
oracle
---
PROJECT_GOAL.md | 87 ++-
harness/PLAN.md | 4 +
harness/STATE.md | 62 +-
harness/TASK_LEDGER.md | 6 +
harness/fixtures/scenario-authoring/README.md | 96 +++
harness/scenario-authoring-contract.rs | 672 ++++++++++++++++++
6 files changed, 901 insertions(+), 26 deletions(-)
create mode 100644 harness/fixtures/scenario-authoring/README.md
create mode 100644 harness/scenario-authoring-contract.rs
diff --git a/PROJECT_GOAL.md b/PROJECT_GOAL.md
index 799056d0..bc536a56 100644
--- a/PROJECT_GOAL.md
+++ b/PROJECT_GOAL.md
@@ -13,6 +13,7 @@ Deliver the product described by the repository, specification, requirements, re
Persist until the intended outcome is genuinely achieved.
Do not stop at:
+
- research,
- planning,
- a plausible conceptual design,
@@ -26,6 +27,7 @@ The final result must be conceptually coherent, technically sound, usable, imple
Bias toward action and completion.
Resolve discoverable questions yourself through:
+
- repository exploration,
- web and literature research,
- primary documentation,
@@ -76,27 +78,35 @@ Maintain a concise durable Task Ledger in `harness/TASK_LEDGER.md`, unless an eq
It should distinguish:
### Goal and success criteria
+
What outcome must ultimately exist.
### Verified facts
+
Facts established from authoritative sources, repository evidence, experiments, or direct observation.
### Given facts
+
Requirements or constraints explicitly supplied by me.
### Assumptions
+
Things currently treated as true but not yet verified.
### Open questions
+
Important unknowns.
### Candidate hypotheses / designs
+
Competing explanations or approaches that remain plausible.
### Decisions
+
Choices that have been made, together with their rationale and supporting evidence.
### Current strategy
+
The current high-level route toward the goal.
Do not silently convert an assumption into a fact.
@@ -179,6 +189,7 @@ Possible roles include:
- integration reviewer.
Give every subagent:
+
- one bounded question or responsibility,
- relevant context,
- clear boundaries,
@@ -215,15 +226,19 @@ For consequential, uncertain, difficult-to-reverse design problems, do not accep
Use an idea-tournament process.
### Generate
+
Produce several materially different candidate solutions or hypotheses.
Aim for diversity rather than cosmetic variations of one idea.
### Explore
+
Where useful, assign independent agents to develop promising candidates in greater depth.
### Reflect
+
Have independent critics examine candidates for:
+
- correctness,
- hidden assumptions,
- internal consistency,
@@ -238,6 +253,7 @@ Have independent critics examine candidates for:
Critics should actively attempt to falsify proposals rather than merely improve them.
### Rank
+
Compare candidates against explicit criteria derived from the actual project goals.
Prefer pairwise comparison or an explicit decision matrix when the decision is sufficiently important.
@@ -245,6 +261,7 @@ Prefer pairwise comparison or an explicit decision matrix when the decision is s
Do not rank based on eloquence.
### Evolve
+
Improve the strongest candidates.
Combine complementary ideas when a hybrid is genuinely better.
@@ -252,6 +269,7 @@ Combine complementary ideas when a hybrid is genuinely better.
Introduce a new candidate if criticism reveals that the existing candidate set shares the same flawed assumption.
### Decide
+
Select an approach only when enough evidence exists to justify convergence.
Record the decision and why alternatives lost.
@@ -456,6 +474,7 @@ Do not equate technical functionality with usability.
Use independent reviewers throughout the project, but select reviewers according to the artifact being evaluated.
A conceptual or semantic design may need:
+
- domain review,
- consistency review,
- adversarial / falsification review,
@@ -463,6 +482,7 @@ A conceptual or semantic design may need:
- and user-workflow review.
A language may need:
+
- ambiguity review,
- grammar / round-trip review,
- human-editability review,
@@ -470,6 +490,7 @@ A language may need:
- and version-control review.
An implementation may need:
+
- correctness review,
- architecture review,
- requirements review,
@@ -481,6 +502,7 @@ A final release should receive a cross-disciplinary review rather than only a co
Require concrete findings and evidence.
For material findings:
+
1. verify the finding,
2. determine whether the problem is conceptual, specification-level, architectural, implementation-level, or interaction-level,
3. fix it at the correct layer,
@@ -562,11 +584,11 @@ Do not automatically use maximum reasoning for every delegated task.
Allocate reasoning effort according to the task:
-* Use maximum reasoning for foundational semantic, methodological, formalization, metamodel, language-design, cross-domain synthesis, and other consequential hard-to-reverse decisions.
-* Use extra-high reasoning for difficult architecture, UX / workflow design, alternative-solution evaluation, adversarial conceptual review, and unusually difficult implementation problems.
-* Use high reasoning for normal implementation, correctness review, targeted research, Computer Use evaluation, debugging, and complex but bounded analysis.
-* Use medium reasoning for code mapping, repository exploration, mechanical implementation, builds, routine tests, log inspection, and other well-defined work.
-* Use lower effort only for genuinely trivial mechanical tasks.
+- Use maximum reasoning for foundational semantic, methodological, formalization, metamodel, language-design, cross-domain synthesis, and other consequential hard-to-reverse decisions.
+- Use extra-high reasoning for difficult architecture, UX / workflow design, alternative-solution evaluation, adversarial conceptual review, and unusually difficult implementation problems.
+- Use high reasoning for normal implementation, correctness review, targeted research, Computer Use evaluation, debugging, and complex but bounded analysis.
+- Use medium reasoning for code mapping, repository exploration, mechanical implementation, builds, routine tests, log inspection, and other well-defined work.
+- Use lower effort only for genuinely trivial mechanical tasks.
Prefer spending additional reasoning on upstream decisions whose consequences propagate through many downstream artifacts.
@@ -578,12 +600,18 @@ Use GPT-6 Astra model exclusively.
## Application specification
-Create a modern clone of ARCADIA Capella using Rust Iced framework. It should be cross platform (supporting Windows and Linux at least). Use Rust exclusively (wherever possible & practical). Focus on making the application modern, extensible, intuitive and easy to use. Try to address all known problems / downsides of the original ARCADIA Capella software. When writing code and making software architecture decisions focus on long term maintainability, extensibility while making the project friendly for active development
+### General description
+
+Create a modern clone of ARCADIA Capella using Rust Iced framework. It should be cross platform (supporting Windows and Linux at least). Use Rust exclusively (wherever possible & practical).
+
+Focus on making the application modern, extensible, intuitive and easy to use. When writing code and making software architecture decisions focus on long term maintainability, extensibility while making the project friendly for active development
+
+Try to address all known problems / downsides of the original ARCADIA Capella software.
Version 1 MVP in the form of working Operational Analysis layer is done.
Version 2 MVP in the form of working Operation Analysis + System Analysis is done.
-You should make the 1-st release version of the project with fully feature complete Operation Analysis + System Architecture, according to this application specification.
-And the remaining ARCADIA layers will be added in future releases.
+You should make the 1st release version of the project with fully feature complete Operation Analysis + System Architecture, according to this application specification.
+Remaining ARCADIA layers will be added in future releases.
Transitioning of model elements between layers (in both directions) should be easy and intuitive.
@@ -591,33 +619,52 @@ Keep in mind that the target audience for the application are system engineers.
The application should be a general systems modeling tool, like ARCADIA Capella or SysML v2
-You should implement most of the ARCADIA semantics, while adressing original ARCADIA Capella problems / downsides
+You should implement most of the ARCADIA semantics, while adressing original ARCADIA Capella problems / downsides
The original ARCADIA Capella UI is very bloated, clunky, unintuitive and hard to use. In addition, it lacks a number of quality of life features. You should at all cost avoid making the same mistakes as the developers of the original ARCADIA Capella
You should explore the functionality & UI of the original ARCADIA Capella to take the most useful and powerful ideas from it. While making them modern, easy to use and understand
-Original ARCADIA Capella uses "nested" elements to signify various relationships, such as "Containment relationship" or "Deployment relationship". Instead of using visually noisy arrows / graph edges. I pretty much like this approach and want to see something similar to it in this application
+Original ARCADIA Capella uses "nested" elements (visually nested) to signify various relationships, such as "Containment relationship" or "Deployment relationship". Instead of using visually noisy arrows / graph edges. I pretty much like this approach and want to see something similar to it in this application
To explore the original ARCADIA Capella UI & semantics you may use web resources / web search / official and community docs. Or open the original ARCADIA Capella installed on this desktop machine for exploration
-Your second, parallel goal is to create a textual representation for ARCADIA, similar to SysML v2. So a user can either edit a system model using UI (graphical representation) or text. Both graphical and textual representations should be fully equivalent and have full parity. You may take inspiration from SysML v2. Both graphical and textual representations should have one overarching, concrete, unambiguous spec in reST format, similar to SysML v2 strictness
-The application should support both forms of editing
-The textual representation should be easily editable and understandble for both humans and AI
-The textual representation should be easily handled by version control tools such as Git
+Your second, parallel, goal is to create a textual representation for ARCADIA, similar to SysML v2. So a user can either edit a system model using UI (graphical representation) or text. Both graphical and textual representations should be fully equivalent and have full parity. You may take inspiration from SysML v2. Both graphical and textual representations should have one overarching, concrete, unambiguous spec in reST format, similar to SysML v2 strictness
+The application should support both forms of editing
+The textual representation should be easily editable and understandble for both humans and AI
+The textual representation should be easily handled by version control tools such as Git
The underlying semantics of the textual / graphical representation should support traceability of various development artifacts (for example - code, CAD files, electrical schematics, Docx files and anything in between)
The textual representation editor should have syntax highlighting and linting
-The project should also have a backend server / service that you can deploy. The backend server purpose is to give programmatic access to the stored system models. So, there also should be a clear REST API for querying, editing and working with models. The purpose of the REST API is to allow automation, scripting and AI-driven workflows
-An example could be - writing custom plugins for various domain specific software (version control software, CAD software etc...) to establish traceability between development artifacts and a system model
+The project should also have a backend server / service that you can deploy. The backend server purpose is to give programmatic access to stored system models. So, there also should be a clear REST API for querying, editing and working with models. The purpose of the REST API is to allow automation, scripting and AI-driven workflows
+An example could be - writing custom plugins for various domain specific software (version control software, CAD software etc...) to establish traceability between development artifacts and a system model
The desktop application should contain built-in guide / help / tutorial with clear and concise info on modeling concepts, underlying semantics, common modeling scenarios and application features / how to use the app. It should be easy to navigate and explore
-@Computer
+@Computer
+
+### Semantic model / metamodel
+
+Requirements:
+
+- Textual notation.
+- Graphical notation.
+- Constraints and calculations.
+- Quantities and units.
+- State and behavior modeling.
+- Reusable model libraries.
+- Strong, standardized specification.
+- Strong, standardized API.
+
+### UI
+
+The focus should be on reducing system engineer's cognitive load when creating / working with a system model. Regardless of the model size (big or small).
+
+Long, homogeneous lists in the UI that require user to scroll far or search for a desired option for too long - is a bad design.
## Active issues / current guidance
-Right now, the behavior & scenarios UI is hard to use. It contains static / uneditable diagrams, which is highly dubious. In addition, user workflow with the behavior & scenarios UI differ significantly from the other UIs - you can't edit diagrams and instead suppose to click on menus for model development
+Right now, the behavior & scenarios UI is hard to use. It contains static / uneditable diagrams, which is highly dubious. In addition, user workflow with the behavior & scenarios UI differ significantly from the other UIs - you can't edit diagrams and instead suppose to click on menus for model development
----
+---
-The current project state is nowhere near the usability of the original ARCADIA Capella (despite its downfalls) or its functionality / semantics. You should focus on that by studying original ARCADIA Capella very closely and implementing its features in a modern, ergonomic and easy to use way
+The current project state is nowhere near the usability of the original ARCADIA Capella (despite its downfalls) or its functionality / semantics. You should focus on that by studying original ARCADIA Capella very closely and implementing its features in a modern, ergonomic and easy to use way
\ No newline at end of file
diff --git a/harness/PLAN.md b/harness/PLAN.md
index 183f27dd..94e1e1c1 100644
--- a/harness/PLAN.md
+++ b/harness/PLAN.md
@@ -7,6 +7,10 @@
2. Independently reassess current behavior/scenario ergonomics and OA/SA semantic,
source/API and documentation completeness. Resolve verified high-impact gaps
with bounded changes and cross-representation acceptance criteria.
+ The updated goal explicitly requires calculations, quantities/units and
+ reusable model libraries. Audit actual semantics, compare coherent alternatives
+ and establish observable acceptance before implementing foundational extensions.
+ Correct concrete unbounded selectors without replacing existing useful search.
3. Complete realistic graphical OA/SA teaching, transition, behavior, recovery and
hosted workflows. Exercise minimum and wide layouts, rejected edits and history.
4. Run current Windows/Linux automated and native acceptance, fresh independent
diff --git a/harness/STATE.md b/harness/STATE.md
index 5c889cc1..8c56648c 100644
--- a/harness/STATE.md
+++ b/harness/STATE.md
@@ -2,7 +2,10 @@
## Active continuation — 2026-09-10
-The renewed user request is preserved in `PROJECT_GOAL.md`. Root is integrating
+The updated user goal from attachment `2f1f0002-e43c-45ba-80a8-b359f05f99d0`
+was read in full and copied exactly to `PROJECT_GOAL.md` on 2026-09-10. It now
+explicitly requires calculations, quantities/units and reusable model libraries,
+and rejects long homogeneous choice lists. Root is integrating
the first release, prioritizing direct diagram authoring and behavior/scenario
usability. The checkout began clean at `1797516` on `codex/release1-continuation`.
Gitea issue #1 and Wiki Home were read; ownership and progress are coordinated there.
@@ -14,7 +17,7 @@ Gitea issue #1 and Wiki Home were read; ownership and progress are coordinated t
build. Earlier 584-test evidence does not cover those corrections.
- This checkout began without the historical `.local` toolchain/cache/packages.
Project-local Rust/Cargo 1.98.1 GNU/LLVM and verified LLVM-MinGW 20260908
- (clang 23.1.1) are installed. Locked dependencies are being fetched.
+ (clang 23.1.1) are installed. Windows desktop dependencies are compiling.
Host: four logical processors, 8 GiB RAM; builds remain serialized.
- Computer Use initialized successfully. The renewed request authorizes native
evaluation; old session Escape holds and process IDs are historical. No Arcadia
@@ -48,7 +51,8 @@ Implemented locally, awaiting compilation:
recovery and source/history. Core validation remains authoritative.
- Normative catalogs now include ExchangeCategory, ContextualLink and both
Exchange trace pairs. Manual trace help and scope source version corrected;
- strict Docutils 0.22.2 HTML regeneration passed.
+ strict Docutils 0.22.2 HTML regeneration passed. OpenAPI `semantic_schema.py
+ --check --validate` passed 72 fixtures and all 204 component schemas.
Reference evidence: actual Capella 7.1 scenario/lifeline/LOOP creation and saved
XML are recorded in `release-capella-scenario-benchmark-20260910.md`; the isolated
@@ -59,8 +63,9 @@ LLVM-MinGW 20260908 ZIP SHA256
`1bcf74d06b724aeecaa6412ca85f5b26fb1da770e7cdcefa9263c9c5c3ad34b6`, extracted
successfully; stable **1.98.1** GNU/LLVM, rustfmt and Clippy installed.
`.local/toolchain/env.ps1` configures task-local Cargo/Rustup/PATH and one build
-job. Locked dependency fetch is local exec session **13108**. No build/test pass
-is established; the target cache is still cold.
+job. The original full-platform dependency fetch **13108** was stopped after
+network timeouts; Windows-only compilation fetched its remaining dependencies.
+No current Rust build/test pass is established yet.
The coherent candidate `01bb52a` passed `cargo fmt --all` and `git diff --check`.
Direct push failed because this fresh host has no Git credentials. Authenticated
@@ -73,7 +78,9 @@ transfer is not acceptance. Their broader architecture
palette/Escape repair should be reconciled with our overlapping compile/stale
repairs while retaining the additional invariant tests. Their measured minimum
scenario canvas is too small; they own the subsequent layout implementation.
-Their isolated palette commit is `7d1df4f`, fetched locally for reconciliation.
+Their isolated palette commit `7d1df4f` was cherry-picked cleanly as `aed40d7`.
+Our additional authoring tests and scenario handlers are retained. External
+native/test results remain scoped to their exact source and executable.
Independent F1 review found a real optional-value cache collision in `01bb52a`.
The follow-up distinguishes null/scalar/list/tagged constructor identities and
@@ -82,6 +89,49 @@ remaining collision; execution is still pending. The original full-platform
fetch was stopped after network timeouts; current Windows desktop test build
is exec session **51036**, compiling dependencies. Root owns this Cargo lease.
+Commit `eb5161b` adds the pure B4 lifecycle projection, ten admitted-fixture
+tests and current scenario/history guide wording. The module is declared only
+under `cfg(test)` pending coordinated Scene integration after the other host's
+scenario workspace refactor. Independent source reviews found no confirmed B3
+navigation or B4 projection blocker; synthetic geometry tests do not establish
+actual Scene/marker/hit-test acceptance. No new Studio binary has launched here.
+
+Latest transfer: `harness/transfers/behavior-eb5161b.bundle.b64` on the transfer
+branch, decoded 91,268 bytes, SHA256
+`87F3FBA862AE3751E60E18B39C7BB02B147E4E66079F501050A709D8ADDADE6D`.
+Full MCP readback matched local bytes. It contains all four local commits from
+`1797516`; the other host can cherry-pick `b6b76ef` and `eb5161b` onto its existing
+integration. Gitea #65 records exact handoff and the Wiki title-encoding problem.
+Root is preparing an independent native scenario fixture/oracle through the
+release auditor; only harness/scenario-authoring-contract.rs and its README are
+owned by that worker. Generated fixtures require a later direct-rustc lease.
+
+Actual cross-host test evidence supersedes source-only confidence: the other
+host's immutable pre-`b6b76ef` integrated artifact ran 618 tests, with 615 passing
+and three failures (Gitea #68). One B3 deleted/changed-caller regression is being
+diagnosed by their scenario/layout owner. Two F1 failures came from seeding
+optional UUID/text as `NotApplicable` via broad string-shape matching. Local
+`4ecad78` now recognizes only an explicit enum constructor and adds the nullable
+UUID/text regression; independent descriptor review clears it for execution.
+No assertion was weakened. Thirteen history tests now await local execution.
+Incremental transfer `behavior-4ecad78.bundle.b64` requires `eb5161b`, decoded
+1,464 bytes, SHA256
+`3A93A99AB9C727A4B685D7436C9E23FB262144580E8524623B75D2D676FDF033`.
+Exact local and MCP readback checks pass; Gitea #76 requests warm-host reruns.
+
+The native scenario oracle now requires both independently predicted semantic
+checkpoint equality and exact saved/restored Project+SourceBundle identity.
+Independent review caught that rediscovering fresh role IDs at each checkpoint
+alone could falsely accept recreated IDs on Redo; `same` and its adversarial
+self-check close that evaluator gap. It remains uncompiled/unexecuted.
+
+Fresh `requirements_delta` owns only its new audit report. Early source evidence
+shows constraints are explicitly unexecuted expressions and units are strings
+without dimensional conversion; the newly explicit calculation/quantity goals
+must not be declared complete from this metadata. Reusable library contracts
+and concrete unbounded selectors are under domain/requirements review before
+foundational design decisions. Current behavior/native work remains useful.
+
Next: focused `architecture_authoring_tests`,
`behavior::topology`, `direct_clicked`, caller-navigation and `history_fields::tests`,
then full relevant gates and a fresh native binary. Root owns all Cargo on this
diff --git a/harness/TASK_LEDGER.md b/harness/TASK_LEDGER.md
index d6080c95..cf9f6086 100644
--- a/harness/TASK_LEDGER.md
+++ b/harness/TASK_LEDGER.md
@@ -8,6 +8,12 @@ Graphical/source/REST parity, explicit reST semantics, traceability, offline
guidance, Windows/Linux acceptance and independent review remain required.
Older scope deferrals must be checked against this request.
+Updated given requirements (2026-09-10 goal edit): calculations, quantities and
+units, reusable model libraries, and avoiding long homogeneous selection lists
+are explicit. `PROJECT_GOAL.md` preserves the exact new attachment. A fresh
+independent domain audit maps them to actual behavior before design convergence;
+unexecuted constraints and unit strings alone are not assumed to satisfy them.
+
Verified: the checkout began clean at `1797516`; Gitea contains one active
continuation issue and no open PR. Current code includes direct behavior editors,
so old blanket descriptions of static diagrams need workflow-level reassessment.
diff --git a/harness/fixtures/scenario-authoring/README.md b/harness/fixtures/scenario-authoring/README.md
new file mode 100644
index 00000000..00fe63bb
--- /dev/null
+++ b/harness/fixtures/scenario-authoring/README.md
@@ -0,0 +1,96 @@
+# Scenario authoring native contract
+
+Prepared by Codex-956atf /root/release_audit on 2026-09-10. **Source candidate only: compilation, self-check, generation and native execution have not run.** This fixture/oracle is preparation for B2 clicked-slot authoring and B3 caller return, not acceptance evidence.
+
+`harness/scenario-authoring-contract.rs` generates deterministic OA and SA versions of the same small scenario tree. Each has Activity/Function choices, a Ready state, explicit lifelines, two nested ALT fragments, and fixed root/operand insertion anchors. A caller contains two distinct uses of B verification; B uses C detail. Each use explicitly maps both referenced lifelines to distinct local lifelines representing the same work. No native action/state/reuse/fragment role exists in the baseline.
+
+Only this new Rust source and this README belong to the preparation slice. Generate fixture files after root grants the direct-rustc lease and the checker self-check passes.
+
+## Compile and generate
+
+Use the current matching `oa_core` and `serde_json` rlibs from root's accepted Cargo output. Do not build another Cargo tree or select an arbitrary stale rlib. With root's configured Rust environment and clang linker, substitute the exact recorded paths below:
+
+```powershell
+. .local/toolchain/env.ps1
+$scenarioCore = 'ABSOLUTE_PATH_TO_CURRENT_liboa_core-HASH.rlib'
+$scenarioJson = 'ABSOLUTE_PATH_TO_MATCHING_libserde_json-HASH.rlib'
+$scenarioDeps = Split-Path -LiteralPath $scenarioCore -Parent
+$scenarioLinker = 'ABSOLUTE_PATH_TO_TOOLCHAIN_clang.exe'
+New-Item -ItemType Directory -Force -Path .local/native-qa | Out-Null
+rustc --edition 2024 -D warnings harness/scenario-authoring-contract.rs --extern "oa_core=$scenarioCore" --extern "serde_json=$scenarioJson" -L "dependency=$scenarioDeps" -C "linker=$scenarioLinker" -o .local/native-qa/scenario-authoring-contract.exe
+.local/native-qa/scenario-authoring-contract.exe self-check
+.local/native-qa/scenario-authoring-contract.exe generate harness/fixtures/scenario-authoring/base
+.local/native-qa/scenario-authoring-contract.exe check baseline harness/fixtures/scenario-authoring/base/project.arcadia
+```
+
+The compiler target must match those rlibs; include root's explicit `--target` if its toolchain setup requires one. Record the exact command, compiler/rlib identities, checker hash and result. Generation refuses any existing output directory. It writes `project.arcadia`, an included Unicode/CRLF `rationale.arcadia`, and `expected-baseline.json`. The JSON is a reference artifact, not an additional source document.
+
+Copy the **whole** generated base into separate fresh OA and SA native working directories. Preserve the base. Each layer's four checkpoints start from that same baseline; `sa-action` does not include the OA creations. Preserve the whole working directory after each declared Save/Undo/Redo checkpoint. Do not modify fixture sources or adjust expected fields to match observed output.
+
+## Fixed locations and choices
+
+The UUID namespace is `5ceaa100-0000-4000-8000-000000000000` plus a deterministic integer. Stable aliases make the intended records easy to confirm in the inspector/source. All labels below have `OA ` or `SA ` prepended.
+
+| Role | OA alias / UUID suffix | SA alias / UUID suffix |
+| --- | --- | --- |
+| A caller | `scenario_oa_30` / `000000000082` | `scenario_sa_30` / `0000000000e6` |
+| B verification | `scenario_oa_31` / `000000000083` | `scenario_sa_31` / `0000000000e7` |
+| C detail | `scenario_oa_32` / `000000000084` | `scenario_sa_32` / `0000000000e8` |
+| Inner chosen operand | `scenario_oa_55` / `00000000009b` | `scenario_sa_55` / `0000000000ff` |
+| Inner anchor | `scenario_oa_57` / `00000000009d` | `scenario_sa_57` / `000000000101` |
+| Root entry / Root exit | `scenario_oa_50` / `scenario_oa_62` | `scenario_sa_50` / `scenario_sa_62` |
+| First use of B / Second use of B | `scenario_oa_60` / `scenario_oa_61` | `scenario_sa_60` / `scenario_sa_61` |
+| B use of C | `scenario_oa_64` | `scenario_sa_64` |
+
+The target operand is **Outer ALT → Outer chosen operand → Inner ALT → Inner chosen operand**. The other inner operand and outer fallback are deliberate nearby decoys. Every sibling's records and order must remain unchanged.
+
+## B3 navigation before editing
+
+Open `project.arcadia`, select **OA A caller**, and open **Behavior**. At normal size and at 1100 × 700, record the actual diagram visibility, selected occurrence, selected tool, canvas scroll and outer page scroll. Native screenshots and observations must establish these UI properties; the source checker cannot observe them.
+
+1. Select **OA Second use of B**, then **Open definition**. In **OA B verification**, select **OA B use of C** and open its definition. **Back to caller** must return first to the exact B use, then to the exact **Second use of B** in A, retaining the caller's tool and canvas/page offsets subject to viewport clamping. The first use is a distinct occurrence, not an interchangeable return address.
+2. Repeat through **OA First use of B**. Also exercise the detailed-list **Open OA B verification →** action for one of those uses; it must retain that initiating occurrence. After Back, ordinary scrolling must continue naturally rather than snapping repeatedly to the restored offset.
+3. Save without edits and run `check baseline ENTRY`. There must be no new model Undo entry from navigation. Repeat the two-use return once in **SA A caller**. No new model or source checkpoint is needed: both layers still equal baseline.
+
+For one blocked navigation case, arm Add action, click a valid slot and type `unfinished native action` without creating it. Attempt Open/Back only if those controls are reachable in that pending state. The draft must remain visible/recoverable or the navigation control must be unavailable. Cancel, Save, and check baseline. Record the actual control availability; do not count an unperformed attempt as a pass.
+
+## B2 four creations per layer
+
+Run the same four steps on the separate OA and SA copies. Replace `XX` below with the literal `OA` or `SA`, and lowercase `xx` in checker modes. Select **XX A caller → Behavior**. The compact selector's currently authored labels are **Add action**, **Observe state**, **Reuse scenario**, and **Add fragment**. Choose the tool and click the prescribed visible insertion slot; do not substitute a Tools append action or repair the destination afterward through the inspector.
+
+Before pressing Create, record the form's destination/before-slot label and selected lifeline. This observation proves which native gesture was performed. Full model/source equality then checks the resulting semantics independently.
+
+| Step / checkpoint | Prescribed click and literal form values | Expected insertion |
+| --- | --- | --- |
+| 1. `xx-action` | **Add action**; click on **XX A inspector** at the root slot before **XX Root entry**. Name `XX native action`; work **XX Inspect ticket**; observed lifeline **XX A inspector**. Create. | New Action is the first A root step, before the original root entry. |
+| 2. `xx-state` | **Observe state**; click in **Inner chosen operand** before **XX Inner anchor**, on **XX A inspector**. Name `XX native state`; state **XX Ready**; observed lifeline **XX A inspector**. Create. | The inner chosen operand becomes `[native state, Inner anchor]`. Both enclosing ALT structures retain their original identities/guards. |
+| 3. `xx-reuse` | **Reuse scenario**; click in that same inner operand before **XX Inner anchor**. Name `XX native reuse`; definition **XX B verification**. Explicitly bind **XX B inspector → XX A inspector** and **XX B recorder → XX A recorder**. Create. | Inner operand becomes `[native state, native reuse, Inner anchor]`. No scenario/lifeline definition is cloned. |
+| 4. `xx-fragment` | **Add fragment**; click the root slot before **XX Root exit**. Name `XX native alternative`; operator **ALT · alternatives**; first guard `ticket.priority > 2`; second guard `ticket.priority <= 2`. Create. | One new root Fragment followed by the original Root exit. Its two new operands are empty and named `XX native alternative · operand 1` and `XX native alternative · operand 2`. Existing occurrences are not wrapped or moved into them. |
+
+Before each Create, retain the previously verified stage's whole-directory snapshot (the generated baseline for step 1). After Create: Save, preserve a new whole-directory snapshot and check the declared checkpoint. Undo once, Save and preserve another copy: check the preceding checkpoint **and require `same PREVIOUS_VERIFIED_ENTRY UNDO_ENTRY`**. Redo once, Save and preserve another copy: check the current checkpoint **and require `same CURRENT_VERIFIED_ENTRY REDO_ENTRY`**. The `same` mode compares every Project identity/field and every SourceBundle byte; rerunning a semantic checkpoint alone permits a new set of fresh UUIDs and cannot prove exact history restoration. Never replace the preserved verified snapshots with their restored copies.
+
+In particular, a fragment's creation and both operands must disappear/reappear in one Undo/Redo step with their original IDs. Reopen the final saved entry, Save and preserve the reopened copy, check `xx-fragment`, and require `same FINAL_VERIFIED_ENTRY REOPENED_ENTRY`. Use the normal Save operation in the working directory; save copies by preserving the whole directory so the included source stays attached.
+
+Example checks for an OA run:
+
+```powershell
+.local/native-qa/scenario-authoring-contract.exe check oa-action PATH_TO_SAVED_project.arcadia
+.local/native-qa/scenario-authoring-contract.exe check baseline PATH_TO_ACTION_UNDO_project.arcadia
+.local/native-qa/scenario-authoring-contract.exe same PATH_TO_VERIFIED_BASELINE_project.arcadia PATH_TO_ACTION_UNDO_project.arcadia
+.local/native-qa/scenario-authoring-contract.exe check oa-action PATH_TO_ACTION_REDO_project.arcadia
+.local/native-qa/scenario-authoring-contract.exe same PATH_TO_VERIFIED_ACTION_project.arcadia PATH_TO_ACTION_REDO_project.arcadia
+.local/native-qa/scenario-authoring-contract.exe check oa-state PATH_TO_SAVED_project.arcadia
+.local/native-qa/scenario-authoring-contract.exe check oa-reuse PATH_TO_SAVED_project.arcadia
+.local/native-qa/scenario-authoring-contract.exe check oa-fragment PATH_TO_REOPENED_project.arcadia
+.local/native-qa/scenario-authoring-contract.exe same PATH_TO_VERIFIED_FRAGMENT_project.arcadia PATH_TO_REOPENED_project.arcadia
+```
+
+One cancellation can be inserted before step 1: arm Observe state and click away from a lifeline. The form must require an explicit observed lifeline; it must not silently choose an unrelated lane. Enter an incomplete name, use the form's **Cancel** control, Save, and check baseline. Exercise Escape separately: current behavior cancels the gesture while preserving the pending scenario form/raw draft; it is not form cancellation. Focus, pending-marker visibility, stable canvas origin, Escape delivery, minimum-size reachability and clicked-slot geometry require separate native observations. Do not infer those from a passing file check.
+
+## Independence and limits
+
+The checker uses only unique **Kind::Occurrence + layer + predetermined literal name** roles to discover fresh UUIDv4 IDs. IDs must be distinct and absent from the deterministic baseline. It independently constructs every other field: body, parent, reference targets, binding order and identities, guards, empty operands, pre/postconditions, description, alias and insertion order. It does not read observed fields into the prediction. Any unclaimed object, sibling edit, wrong destination, binding, name/value or source byte causes full equality to fail.
+
+Expected transactions use core typed commands and source writeback, independently of the desktop planners, then compare the whole Project and the complete reachable two-file SourceBundle. The source oracle deliberately shares the core language engine; this fixture validates native GUI-to-model/source parity rather than supplying an independent language implementation. Semantic checkpoint checks plus required exact saved/restored snapshot comparisons prove state and identity restoration only for the captured Undo/Redo/reopen sequence, not arbitrary history internals.
+
+The self-check covers deterministic seeds, all eight OA/SA checkpoint fixed points, carriers with deliberately false parent/body/alias/description, rejected unrelated edits, wrong insertion order/parent/bindings, and a model-neutral change to the retained note. It also constructs two equivalent valid checkpoints with different fresh action IDs: each passes semantic prediction, while exact `same` snapshot comparison rejects their identity difference. It is not a native UI test. Current compile, self-check, generation, native action/return/scroll/recovery, screenshot and release evidence remain pending until recorded by root.
diff --git a/harness/scenario-authoring-contract.rs b/harness/scenario-authoring-contract.rs
new file mode 100644
index 00000000..e8383ac5
--- /dev/null
+++ b/harness/scenario-authoring-contract.rs
@@ -0,0 +1,672 @@
+//! Independent B2/B3 native fixture; no desktop implementation is linked.
+//! generate DIR | check CHECKPOINT ENTRY | same VERIFIED_ENTRY RESTORED_ENTRY | self-check
+use oa_core::{source_project::*, *};
+use std::{
+ collections::{BTreeMap, BTreeSet},
+ fs,
+ path::Path,
+};
+
+fn id(n: u128) -> Id {
+ Id::from_u128(0x5ceaa100000040008000000000000000 + n)
+}
+#[derive(Clone, Copy)]
+struct Side {
+ layer: Layer,
+ word: &'static str,
+ base: u128,
+}
+const SIDES: [Side; 2] = [
+ Side {
+ layer: Layer::Oa,
+ word: "OA",
+ base: 100,
+ },
+ Side {
+ layer: Layer::Sa,
+ word: "SA",
+ base: 200,
+ },
+];
+impl Side {
+ fn id(self, n: u128) -> Id {
+ id(self.base + n)
+ }
+ fn name(self, text: &str) -> String {
+ format!("{} {text}", self.word)
+ }
+}
+fn seed(p: &mut Project, side: Side, n: u128, kind: Kind, text: &str, parent: Option) {
+ let mut element = p
+ .make_element(kind, side.layer, &side.name(text), parent)
+ .unwrap();
+ element.id = side.id(n);
+ element.alias = format!("scenario_{}_{}", side.word.to_lowercase(), n);
+ p.elements.insert(element.id, element);
+}
+fn seed_occurrence(
+ p: &mut Project,
+ side: Side,
+ n: u128,
+ text: &str,
+ parent: u128,
+ body: OccurrenceBody,
+) {
+ seed(p, side, n, Kind::Occurrence, text, Some(side.id(parent)));
+ *p.elements
+ .get_mut(&side.id(n))
+ .unwrap()
+ .shared_mut()
+ .unwrap() = SharedBody::Occurrence {
+ body,
+ precondition: String::new(),
+ postcondition: String::new(),
+ };
+}
+fn action(side: Side, work: u128, line: u128) -> OccurrenceBody {
+ OccurrenceBody::Action {
+ action: Some(side.id(work)),
+ lifeline: Some(side.id(line)),
+ }
+}
+fn reuse(side: Side, target: u128, remote: [u128; 2], local: [u128; 2]) -> OccurrenceBody {
+ OccurrenceBody::ScenarioUse {
+ scenario: Some(side.id(target)),
+ bindings: remote
+ .into_iter()
+ .zip(local)
+ .map(|(referenced, local)| LifelineBinding {
+ referenced: side.id(referenced),
+ local: side.id(local),
+ })
+ .collect(),
+ }
+}
+fn fixture() -> Project {
+ let mut p = Project::empty();
+ p.id = id(1);
+ p.alias = "scenario_authoring".into();
+ p.name = "Scenario authoring contract".into();
+ p.description = "Native clicked-slot and caller-return contract.".into();
+ for side in SIDES {
+ let work = if side.layer == Layer::Oa {
+ Kind::Activity
+ } else {
+ Kind::Function
+ };
+ seed(&mut p, side, 10, work, "Inspect ticket", None);
+ seed(&mut p, side, 11, work, "Record result", None);
+ seed(&mut p, side, 20, Kind::StateMachine, "Ticket modes", None);
+ seed(
+ &mut p,
+ side,
+ 21,
+ Kind::Region,
+ "Ticket region",
+ Some(side.id(20)),
+ );
+ seed(&mut p, side, 22, Kind::State, "Ready", Some(side.id(21)));
+ for (n, text) in [(30, "A caller"), (31, "B verification"), (32, "C detail")] {
+ seed(&mut p, side, n, Kind::Scenario, text, None);
+ }
+ for (n, owner, represented, text) in [
+ (40, 30, 10, "A inspector"),
+ (41, 30, 11, "A recorder"),
+ (42, 31, 10, "B inspector"),
+ (43, 31, 11, "B recorder"),
+ (44, 32, 10, "C inspector"),
+ (45, 32, 11, "C recorder"),
+ ] {
+ seed(&mut p, side, n, Kind::Lifeline, text, Some(side.id(owner)));
+ *p.elements
+ .get_mut(&side.id(n))
+ .unwrap()
+ .shared_mut()
+ .unwrap() = SharedBody::Lifeline {
+ represented: Some(side.id(represented)),
+ };
+ }
+ seed_occurrence(&mut p, side, 50, "Root entry", 30, action(side, 10, 40));
+ seed_occurrence(
+ &mut p,
+ side,
+ 51,
+ "Outer ALT",
+ 30,
+ OccurrenceBody::Fragment {
+ operator: FragmentOperator::Alternative,
+ branches: vec![side.id(52), side.id(53)],
+ },
+ );
+ seed_occurrence(
+ &mut p,
+ side,
+ 52,
+ "Outer chosen operand",
+ 51,
+ OccurrenceBody::Branch {
+ guard: "ticket.present".into(),
+ occurrences: vec![side.id(54)],
+ },
+ );
+ seed_occurrence(
+ &mut p,
+ side,
+ 53,
+ "Outer fallback operand",
+ 51,
+ OccurrenceBody::Branch {
+ guard: "else".into(),
+ occurrences: vec![side.id(59)],
+ },
+ );
+ seed_occurrence(
+ &mut p,
+ side,
+ 54,
+ "Inner ALT",
+ 52,
+ OccurrenceBody::Fragment {
+ operator: FragmentOperator::Alternative,
+ branches: vec![side.id(55), side.id(56)],
+ },
+ );
+ seed_occurrence(
+ &mut p,
+ side,
+ 55,
+ "Inner chosen operand",
+ 54,
+ OccurrenceBody::Branch {
+ guard: "ticket.ready".into(),
+ occurrences: vec![side.id(57)],
+ },
+ );
+ seed_occurrence(
+ &mut p,
+ side,
+ 56,
+ "Inner other operand",
+ 54,
+ OccurrenceBody::Branch {
+ guard: "else".into(),
+ occurrences: vec![side.id(58)],
+ },
+ );
+ for (n, text, parent, line) in [
+ (57, "Inner anchor", 55, 40),
+ (58, "Inner other anchor", 56, 40),
+ (59, "Fallback anchor", 53, 41),
+ (62, "Root exit", 30, 41),
+ (63, "B entry", 31, 42),
+ (65, "B exit", 31, 43),
+ (66, "C entry", 32, 44),
+ ] {
+ seed_occurrence(
+ &mut p,
+ side,
+ n,
+ text,
+ parent,
+ action(side, if line % 2 == 0 { 10 } else { 11 }, line),
+ );
+ }
+ seed_occurrence(
+ &mut p,
+ side,
+ 60,
+ "First use of B",
+ 30,
+ reuse(side, 31, [42, 43], [40, 41]),
+ );
+ seed_occurrence(
+ &mut p,
+ side,
+ 61,
+ "Second use of B",
+ 30,
+ reuse(side, 31, [42, 43], [40, 41]),
+ );
+ seed_occurrence(
+ &mut p,
+ side,
+ 64,
+ "B use of C",
+ 31,
+ reuse(side, 32, [44, 45], [42, 43]),
+ );
+ for (owner, steps) in [
+ (30, vec![50, 51, 60, 61, 62]),
+ (31, vec![63, 64, 65]),
+ (32, vec![66]),
+ ] {
+ *p.elements
+ .get_mut(&side.id(owner))
+ .unwrap()
+ .steps_mut()
+ .unwrap() = steps.into_iter().map(|n| side.id(n)).collect();
+ }
+ }
+ execute_batch(&p, &[]).expect("deterministic fixture must satisfy core admission")
+}
+fn bundle(p: &Project) -> CompiledSourceProject {
+ let entry = language::format(p).replacen("{\n", "{\n include \"rationale.arcadia\";\n", 1);
+ let raw = SourceBundle {
+ entry: "project.arcadia".into(),
+ files: BTreeMap::from([
+ ("project.arcadia".into(), entry),
+ ("rationale.arcadia".into(), "// Retain caller/operand evidence: ticket 雨.\r\n// Navigation never edits this note.\r\n".into()),
+ ]),
+ };
+ let compiled = compile_bundle(&raw, ProjectLimits::default()).unwrap();
+ assert_eq!(compiled.decoded.project, *p);
+ assert_eq!(compiled.bundle, raw);
+ compiled
+}
+fn load(entry: &Path) -> CompiledSourceProject {
+ let files = source_files::load_project(entry, ProjectLimits::default()).unwrap();
+ let raw = files.reachable_bundle();
+ let compiled = compile_bundle(&raw, ProjectLimits::default()).unwrap();
+ assert_eq!(
+ compiled.bundle, raw,
+ "No migration or source normalization permitted"
+ );
+ execute_batch(&compiled.decoded.project, &[]).unwrap();
+ compiled
+}
+fn advance(base: &mut CompiledSourceProject, commands: Vec) {
+ let expected = execute_batch(&base.decoded.project, &[Command::Batch(commands)]).unwrap();
+ *base = update_bundle(base, &expected, ProjectLimits::default()).unwrap();
+}
+struct Roles<'a> {
+ actual: &'a Project,
+ baseline: Project,
+ used: BTreeSet,
+}
+impl Roles<'_> {
+ fn occurrence(&mut self, side: Side, text: &str) -> Id {
+ let name = side.name(text);
+ let found: Vec<_> = self
+ .actual
+ .elements
+ .values()
+ .filter(|e| e.kind() == Kind::Occurrence && e.layer() == side.layer && e.name == name)
+ .map(|e| e.id)
+ .collect();
+ assert_eq!(
+ found.len(),
+ 1,
+ "Exactly one fresh occurrence for role {name}"
+ );
+ let id = found[0];
+ assert_eq!(
+ id.get_version_num(),
+ 4,
+ "Native occurrence needs UUIDv4: {name}"
+ );
+ assert!(!self.baseline.contains(id), "Role reused a seed: {name}");
+ assert!(
+ self.used.insert(id),
+ "Role identities must be distinct: {name}"
+ );
+ id
+ }
+}
+fn authored(
+ p: &Project,
+ side: Side,
+ id: Id,
+ text: &str,
+ parent: Id,
+ body: OccurrenceBody,
+) -> Element {
+ let name = side.name(text);
+ Element {
+ id,
+ alias: p.fresh_alias(&name, "element", id),
+ name,
+ description: String::new(),
+ parent: Some(parent),
+ body: ElementBody::shared_in(
+ side.layer,
+ SharedBody::Occurrence {
+ body,
+ precondition: String::new(),
+ postcondition: String::new(),
+ },
+ ),
+ }
+}
+fn insert_before(base: &mut CompiledSourceProject, parent: Id, anchor: Id, elements: Vec) {
+ let mut steps = base.decoded.project.elements[&parent]
+ .steps()
+ .unwrap()
+ .clone();
+ let position = steps
+ .iter()
+ .position(|id| *id == anchor)
+ .expect("declared insertion anchor");
+ let inserted: Vec<_> = elements
+ .iter()
+ .filter(|e| e.parent == Some(parent))
+ .map(|e| e.id)
+ .collect();
+ steps.splice(position..position, inserted);
+ let mut commands: Vec<_> = elements.into_iter().map(Command::CreateElement).collect();
+ commands.push(Command::SetSteps(parent, steps));
+ advance(base, commands);
+}
+fn checkpoint(mode: &str) -> Option<(Side, usize)> {
+ if mode == "baseline" {
+ return None;
+ }
+ let (prefix, stage) = mode
+ .split_once('-')
+ .expect("checkpoint is baseline or oa/sa-stage");
+ let side = match prefix {
+ "oa" => SIDES[0],
+ "sa" => SIDES[1],
+ _ => panic!("Unknown layer"),
+ };
+ let count = match stage {
+ "action" => 1,
+ "state" => 2,
+ "reuse" => 3,
+ "fragment" => 4,
+ _ => panic!("Unknown checkpoint"),
+ };
+ Some((side, count))
+}
+fn expected(mode: &str, actual: &Project) -> CompiledSourceProject {
+ let baseline = fixture();
+ let mut base = bundle(&baseline);
+ let Some((side, count)) = checkpoint(mode) else {
+ return base;
+ };
+ let mut roles = Roles {
+ actual,
+ baseline,
+ used: BTreeSet::new(),
+ };
+ let key = roles.occurrence(side, "native action");
+ let e = authored(
+ &base.decoded.project,
+ side,
+ key,
+ "native action",
+ side.id(30),
+ action(side, 10, 40),
+ );
+ insert_before(&mut base, side.id(30), side.id(50), vec![e]);
+ if count == 1 {
+ return base;
+ }
+ let key = roles.occurrence(side, "native state");
+ let e = authored(
+ &base.decoded.project,
+ side,
+ key,
+ "native state",
+ side.id(55),
+ OccurrenceBody::State {
+ state: Some(side.id(22)),
+ lifeline: Some(side.id(40)),
+ },
+ );
+ insert_before(&mut base, side.id(55), side.id(57), vec![e]);
+ if count == 2 {
+ return base;
+ }
+ let key = roles.occurrence(side, "native reuse");
+ let e = authored(
+ &base.decoded.project,
+ side,
+ key,
+ "native reuse",
+ side.id(55),
+ reuse(side, 31, [42, 43], [40, 41]),
+ );
+ insert_before(&mut base, side.id(55), side.id(57), vec![e]);
+ if count == 3 {
+ return base;
+ }
+ let fragment = roles.occurrence(side, "native alternative");
+ let branch1 = roles.occurrence(side, "native alternative · operand 1");
+ let branch2 = roles.occurrence(side, "native alternative · operand 2");
+ let p = &base.decoded.project;
+ let elements = vec![
+ authored(
+ p,
+ side,
+ fragment,
+ "native alternative",
+ side.id(30),
+ OccurrenceBody::Fragment {
+ operator: FragmentOperator::Alternative,
+ branches: vec![],
+ },
+ ),
+ authored(
+ p,
+ side,
+ branch1,
+ "native alternative · operand 1",
+ fragment,
+ OccurrenceBody::Branch {
+ guard: "ticket.priority > 2".into(),
+ occurrences: vec![],
+ },
+ ),
+ authored(
+ p,
+ side,
+ branch2,
+ "native alternative · operand 2",
+ fragment,
+ OccurrenceBody::Branch {
+ guard: "ticket.priority <= 2".into(),
+ occurrences: vec![],
+ },
+ ),
+ ];
+ insert_before(&mut base, side.id(30), side.id(62), elements);
+ base
+}
+fn same_snapshot(verified: &CompiledSourceProject, restored: &CompiledSourceProject) -> bool {
+ verified.decoded.project == restored.decoded.project && verified.bundle == restored.bundle
+}
+fn self_check() {
+ let baseline = fixture();
+ assert_eq!(
+ baseline,
+ fixture(),
+ "seed identities and fields must be deterministic"
+ );
+ let base = bundle(&baseline);
+ assert_eq!(expected("baseline", &baseline).bundle, base.bundle);
+ for side in SIDES {
+ // Role carrier intentionally has wrong parent, alias and occurrence body.
+ // Only its fresh IDs may survive in any prediction.
+ let mut carrier = baseline.clone();
+ for text in [
+ "native action",
+ "native state",
+ "native reuse",
+ "native alternative",
+ "native alternative · operand 1",
+ "native alternative · operand 2",
+ ] {
+ let mut e = carrier
+ .make_element(Kind::Occurrence, side.layer, &side.name(text), None)
+ .unwrap();
+ e.alias = "wrong_carrier_alias".into();
+ e.description = "must never enter the prediction".into();
+ carrier.elements.insert(e.id, e);
+ }
+ for stage in ["action", "state", "reuse", "fragment"] {
+ let mode = format!("{}-{stage}", side.word.to_lowercase());
+ let correct = expected(&mode, &carrier);
+ assert_ne!(correct.decoded.project, carrier);
+ let again = expected(&mode, &correct.decoded.project);
+ assert_eq!(again.decoded.project, correct.decoded.project);
+ assert_eq!(
+ again.bundle, correct.bundle,
+ "full source checkpoint fixed point"
+ );
+ let mut wrong = correct.decoded.project.clone();
+ wrong.elements.get_mut(&side.id(10)).unwrap().description =
+ "unrequested seed edit".into();
+ assert_ne!(expected(&mode, &wrong).decoded.project, wrong);
+ let native = wrong
+ .elements
+ .values()
+ .find(|e| e.name == side.name("native action"))
+ .unwrap()
+ .id;
+ let mut wrong = correct.decoded.project.clone();
+ wrong.elements.get_mut(&native).unwrap().parent = Some(side.id(55));
+ assert_ne!(
+ expected(&mode, &wrong).decoded.project,
+ wrong,
+ "parent is predicted, not read back"
+ );
+ let mut wrong = correct.decoded.project.clone();
+ wrong
+ .elements
+ .get_mut(&side.id(30))
+ .unwrap()
+ .steps_mut()
+ .unwrap()
+ .swap(0, 1);
+ assert_ne!(
+ expected(&mode, &wrong).decoded.project,
+ wrong,
+ "clicked order is independently fixed"
+ );
+ }
+ let mode = format!("{}-fragment", side.word.to_lowercase());
+ let correct = expected(&mode, &carrier);
+ // Both semantic checks may legitimately discover fresh native IDs. An
+ // Undo/Redo check must additionally pin them to the earlier snapshot.
+ let mut other_carrier = carrier.clone();
+ let old = other_carrier
+ .elements
+ .values()
+ .find(|e| e.name == side.name("native action"))
+ .unwrap()
+ .id;
+ let mut replaced = other_carrier.elements.remove(&old).unwrap();
+ replaced.id = Id::new_v4();
+ other_carrier.elements.insert(replaced.id, replaced);
+ let recreated = expected(&mode, &other_carrier);
+ let correct_prediction = expected(&mode, &correct.decoded.project);
+ let recreated_prediction = expected(&mode, &recreated.decoded.project);
+ assert!(same_snapshot(&correct_prediction, &correct));
+ assert!(same_snapshot(&recreated_prediction, &recreated));
+ assert!(
+ !same_snapshot(&correct, &recreated),
+ "recreated native IDs pass semantic roles but must fail exact history restoration"
+ );
+ let mut wrong = correct.decoded.project.clone();
+ let e = wrong
+ .elements
+ .values_mut()
+ .find(|e| e.name == side.name("native reuse"))
+ .unwrap();
+ let Some(SharedBody::Occurrence {
+ body: OccurrenceBody::ScenarioUse { bindings, .. },
+ ..
+ }) = e.shared_mut()
+ else {
+ unreachable!()
+ };
+ bindings[0].local = side.id(41);
+ assert_ne!(
+ expected(&mode, &wrong).decoded.project,
+ wrong,
+ "bindings are never copied from actual"
+ );
+ let mut changed_source = correct.bundle.clone();
+ changed_source
+ .files
+ .get_mut("rationale.arcadia")
+ .unwrap()
+ .push_str("// unrequested note\n");
+ let changed = compile_bundle(&changed_source, ProjectLimits::default()).unwrap();
+ assert_eq!(changed.decoded.project, correct.decoded.project);
+ assert_ne!(
+ changed.bundle,
+ expected(&mode, &changed.decoded.project).bundle
+ );
+ }
+ println!(
+ "PASS self-check: deterministic OA/SA baseline, eight semantic checkpoints, independent fields/source bytes and exact history identity discrimination"
+ );
+}
+fn main() {
+ assert_eq!(SCHEMA_VERSION, 7);
+ let args: Vec<_> = std::env::args().collect();
+ if args.get(1).is_some_and(|s| s == "self-check") {
+ assert_eq!(args.len(), 2);
+ self_check();
+ return;
+ }
+ if args.get(1).is_some_and(|s| s == "generate") {
+ assert_eq!(args.len(), 3);
+ let dir = Path::new(&args[2]);
+ assert!(
+ !dir.exists(),
+ "Generate only into a fresh directory; preserve existing evidence"
+ );
+ let p = fixture();
+ let base = bundle(&p);
+ fs::create_dir_all(dir).unwrap();
+ for (name, bytes) in base.bundle.files {
+ fs::write(dir.join(name), bytes).unwrap();
+ }
+ fs::write(
+ dir.join("expected-baseline.json"),
+ serde_json::to_vec_pretty(&p).unwrap(),
+ )
+ .unwrap();
+ println!(
+ "PASS generated deterministic OA/SA fixture: {} elements, 2 sources; native roles absent",
+ p.elements.len()
+ );
+ return;
+ }
+ if args.get(1).is_some_and(|s| s == "same") {
+ assert_eq!(args.len(), 4, "same VERIFIED_SAVED_ENTRY RESTORED_ENTRY");
+ let verified = load(Path::new(&args[2]));
+ let restored = load(Path::new(&args[3]));
+ assert!(
+ same_snapshot(&verified, &restored),
+ "Restored snapshot must preserve every Project identity/field and every SourceBundle byte"
+ );
+ println!(
+ "PASS same: exact saved/restored Project and SourceBundle, including all native identities"
+ );
+ return;
+ }
+ assert_eq!(
+ args.len(),
+ 4,
+ "check baseline|oa-action|oa-state|oa-reuse|oa-fragment|sa-action|sa-state|sa-reuse|sa-fragment ENTRY"
+ );
+ assert_eq!(args[1], "check");
+ let actual = load(Path::new(&args[3]));
+ let prediction = expected(&args[2], &actual.decoded.project);
+ assert_eq!(
+ actual.decoded.project, prediction.decoded.project,
+ "Complete Project differs from independent checkpoint"
+ );
+ assert_eq!(
+ actual.bundle, prediction.bundle,
+ "Complete source bundle differs, including retained note bytes"
+ );
+ println!(
+ "PASS {}: whole model and {} exact reachable sources; only unique fresh role IDs borrowed",
+ args[2],
+ actual.bundle.files.len()
+ );
+}
From aa40345281e6ae8f203063840f1766ba9ce8381c Mon Sep 17 00:00:00 2001
From: Codex
Date: Thu, 10 Sep 2026 11:57:29 -0700
Subject: [PATCH 07/14] Record semantic release gaps and remove unused palette
import
---
.../src/app/architecture_authoring/palette.rs | 2 +-
.../release-requirements-delta-20260910.md | 101 ++++++++++++++++++
2 files changed, 102 insertions(+), 1 deletion(-)
create mode 100644 harness/release-requirements-delta-20260910.md
diff --git a/crates/oa-desktop/src/app/architecture_authoring/palette.rs b/crates/oa-desktop/src/app/architecture_authoring/palette.rs
index f73a82f4..16880120 100644
--- a/crates/oa-desktop/src/app/architecture_authoring/palette.rs
+++ b/crates/oa-desktop/src/app/architecture_authoring/palette.rs
@@ -4,7 +4,7 @@ use iced::{
Point, Rectangle, Size, Theme, mouse,
widget::{
canvas::{self, Frame, Geometry, Path, Stroke},
- column, container, tooltip,
+ container, tooltip,
},
};
diff --git a/harness/release-requirements-delta-20260910.md b/harness/release-requirements-delta-20260910.md
new file mode 100644
index 00000000..5647f966
--- /dev/null
+++ b/harness/release-requirements-delta-20260910.md
@@ -0,0 +1,101 @@
+# Updated requirements: semantic and workflow delta
+
+Codex-956atf `/root/requirements_delta`, 2026-09-10. Independent, bounded audit for the root integrator. Read the new authoritative attachment in full, repository `PROJECT_GOAL.md`, Gitea issue #1 through comment #73, the open PR list (empty), and Wiki Home/release-readiness material. Ownership was announced in issue #1 comment #74. Other-host scenario layout, platform and packaging work remains outside this audit.
+
+The attachment and synchronized `PROJECT_GOAL.md` have identical SHA256 `7C1BDBD077E31439501C04DC3613E6BE47A389704DDC3A44EF8BD9AD98EBB513`. Source inspection began at `eb5161b`; root advanced to `4ecad78433bdfcf20792f073924c80d8ba37c251` during the audit. That advance does not introduce the absent metamodel capabilities identified here. No production files, Cargo invocation, native application or runtime test were part of this audit. Existing test source is evidence of its assertions, not a new passing result.
+
+## Disposition
+
+The updated objective makes **calculations, quantities/units, reusable model libraries, and scalable selection workflows explicit release requirements**. Three material semantic gaps remain: the current model preserves expression and unit annotations without evaluating them, and modular source/reusable sequences do not provide a maintained cross-project library mechanism. The current specification accurately documents those limits; documenting a limit does not satisfy the newly explicit requirement.
+
+The graphical/textual model, state/behavior semantics and REST service already have substantial implementations. This audit does not revive pre-source7 absence claims. Their remaining runtime acceptance is owned by the current release work. Strong specification/API foundations also exist; the question is extending their precise contracts and establishing conformance, not starting another metamodel.
+
+## Requirement-to-evidence map
+
+| Explicit requirement | Implemented capability and concrete evidence | Remaining delta / evidence limit |
+| --- | --- | --- |
+| Textual and graphical notation, equivalent editing | `docs/language.rst:9–43` defines one model and graphical conformance obligations. Source7 provides typed records, stable identities, nested declarations, modular source, validation, exact source/history updates. `forms.rs`, specialized behavior editors and the diagram authoring work adapt the same typed core. | Every new semantic feature below must enter both representations and the existing history/recovery boundary. Current behavior redesign has pending execution/native acceptance; this audit supplies none. |
+| Constraints | `model.rs:651` stores `Constraint { expression: String, constrained: Vec }`. `validation.rs:560–586` checks text, reference membership/layer and empty-expression completeness. Forms treat `expression` as multiline text and `constrained` as references (`forms.rs:819–910,1034`). OpenAPI exposes the same closed string/reference structure (`docs/openapi.json:3089`). | Structural constraints are checked, but authored constraint expressions are not parsed or evaluated. There is no result expressing whether an authored predicate is satisfied. This is expressly stated in `docs/language.rst:202–205`. |
+| Calculations | Numeric type bounds and typed defaults are validated; `DataValue` contains Boolean, i64 Integer, f64 Real, String and Enumeration (`model.rs:298`). `MessageValue.expression` is also a string (`model.rs:520`). | No calculation definition/application, typed expression tree, input binding, dependency evaluation or result model is present in the core module/kind/project catalogs. Arithmetic-looking message text is not computation: `guide.rs:606`, `language.rst:933`, and the HTTP data-contract tests preserve it as text. |
+| Quantities and units | DataType/DataField carry `unit: String` (`model.rs:603–617`). `data.rs:608` inherits a named type's unit when field text is empty. `data.rs:1013–1038` warns for nonnumeric units and differing field/type strings. `r6_data.rs:209–281` contains an exact unit-inheritance/default/bounds assertion. Forms expose free unit text (`forms.rs:910`); REST/source round-trip it. | No quantity-kind identity, unit definition, dimension algebra, unit-bearing value or conversion. `language.rst:929–933` explicitly disclaims dimensional compatibility. A string equality warning cannot distinguish an equivalent scaled unit from an incompatible physical quantity. Existing scalar default checks do not close this gap. |
+| State and behavior modeling | Current `OccurrenceBody`, state/event/transition models, reuse and source7 caller-qualified links/operation identity are implemented. `language.rst:517–876` describes these semantics; process/scenario authoring and lifecycle projection work is active. | Retain the current behavior/native gates. Lack of a simulator is not evidence that state/behavior *modeling* is absent; calculations need not turn these diagrams into executable simulations. |
+| Reusable model libraries | Named data types, record/item/interface specialization, SequenceReference, ScenarioUse/bindings and modular source provide real reuse within a project. `language.rst:731`, `929–957`, `1646–1725`; `source_project.rs:17,286`. | Included files share **one global alias/UUID namespace** and add no namespace or library identity (`language.rst:1690`). The pure compiler admits an explicit local bundle and resolves no external project. `Project` has elements/relations/diagrams/artifact-links/traces only (`model.rs:1621`). No library package/version/dependency declaration, imported definition provenance, instance identity mapping or library upgrade/reconciliation workflow exists. Copying a directory or opening an example does not establish these semantics. |
+| Strong standardized specification | Normative reST exists, with closed syntax, semantics, invariants, migration and graphical editing obligations. `docs/language.rst` explicitly separates its contract from native acceptance and external Arcadia/Capella/SysML conformance. | Extend the single specification with new evaluation/library semantics and valid/invalid examples before downstream implementation. Publish a release-scoped conformance matrix. Some supporting documents are stale: `docs/acceptance.md:8` still inventories 27 kinds while `model.rs:69` inventories 39. Do not use those old acceptance claims as current coverage. |
+| Strong standardized API | `docs/api.rst:4–43` and `routes.rs:29–85` define versioned REST, a shared Project/Command/parser/validation boundary, ETags and rejected-write semantics. OpenAPI 3.1 is published. Root records schema validation separately. | No analysis/evaluation or library workflow can currently be exposed because the core lacks it. OpenAPI conformance is distinct from conforming to the OMG Systems Modeling API. The user's wording does not alone establish that a full OMG adapter is required; record the interpretation explicitly instead of calling a proprietary API OMG-standardized. |
+| Avoid long homogeneous UI lists | Explorer has kind groups, containment, breadcrumbs, search and a 100-item paged catalog (`explorer_view.rs:17–149,249–388`). Existing contextual authoring narrows many choices. | A concrete remaining risk is `forms.rs:1034`: Constraint targets include every same-layer semantic identity; `:920–964` sorts the complete list; `:1952` renders an ordinary `pick_list` without a search/grouping control. Generic arrays (`:2020–2067`) render every full editor row. Compatibility filtering helps correctness but does not bound human search effort. Native dense-model evaluation remains required; this is a verified design path, not a measured task-time claim. |
+
+Paths without a prefix in the table refer to `crates/oa-core/src` for model/data/validation/source_project, `crates/oa-desktop/src` for forms/guide/explorer_view, `crates/oa-server/src` for routes, and `crates/oa-core/tests` for r6_data.
+
+## Domain observations, separate from the proposed design
+
+Capella's official feature description defines reusable collections and replicas that can be packaged in libraries and shared by projects. It also identifies mass, cost and latency viewpoints. This supports a requirement for maintained reuse and engineering analysis; it does not imply that ordinary includes or presentation copying provide those functions. [Capella features](https://mbse-capella.org/features.html)
+
+The official Arcadia/Capella comparison describes constraints, opaque/parseable expressions, properties and physical dimensions, with analysis often supplied through viewpoints. It does not require Studio to clone a dedicated parametric diagram or to become a behavioral simulator. The user has nevertheless explicitly required calculations, so Capella's extension architecture cannot justify implementing only prose. [Capella's parametric comparison](https://mbse-capella.org/arcadia_capella_sysml_tool.html#parametric)
+
+SysML 2.0 distinguishes reusable calculations with results (§7.19), Boolean-valued constraints (§7.20), and quantity kinds/units with conversion (§7.8 and §9.8). These are useful reference distinctions, not a claim that Studio implements KerML or SysML. The proposed subset below is Studio's own design. [OMG SysML 2.0 language specification](https://www.omg.org/spec/SysML/2.0/Language/PDF)
+
+The OMG Systems Modeling API publishes a separate normative specification, OpenAPI document and schemas. Merely using JSON, REST or an OpenAPI document does not implement that domain standard. [OMG Systems Modeling API 1.0](https://www.omg.org/spec/SystemsModelingAPI/1.0)
+
+## Alternatives and a recommended minimum coherent design
+
+These are candidates for root synthesis and independent semantic criticism, **not ratified scope changes or implemented capability**. A completed small engineering workflow is the useful first milestone; adding new text boxes is not.
+
+| Candidate | Benefit | Principal cost or shortfall | Assessment |
+| --- | --- | --- | --- |
+| Keep expression/unit metadata; distribute source snippets | Very small change and preserves all current behavior. | Cannot compute a budget, reject a dimensionally invalid expression, or identify/update an imported library instance. | Fails the explicit requirements as a final release interpretation. |
+| Add a bounded pure analysis model plus versioned local library packages | Delivers useful OA/SA analysis and cross-project reuse through the current Rust/core/source/API architecture. Evaluation and package resolution remain explicit and reproducible. | Requires foundational value/reference/identity decisions, one schema migration and complete editing adapters. | Recommended first-release direction, subject to the acceptance cases below. |
+| Make external analysis tools/plugins the principal implementation | Domain solvers can remain specialized; artifact traceability is useful. | A link or export alone still leaves the required Studio workflow incomplete. Reproducible bindings, invocation, typed results, unit checking and failure handling need substantial contracts. | Useful later integration; acceptable as a primary route only if that full end-to-end workflow is implemented and accepted. |
+| Adopt full KerML/SysML evaluation and OMG repository services | Strongest direct standards interoperability. | Broad semantics and repository lifecycle well beyond the current OA/SA model; large migration and UX burden. | Do not infer this commitment from “similar to SysML v2.” Evaluate separately if full conformance becomes an explicit target. |
+
+### Analysis and quantities
+
+The smallest useful core needs an **identified engineering property/value attached to an explicit semantic owner**, a reusable calculation with typed parameters/result, and a constraint with explicit parameter bindings and a Boolean result. A system's measured or assumed property must not be silently represented by an ExchangeItem field's *type default*: those are different domain facts. Root should compare a general owned-property record against a dedicated analysis context before selecting concrete Rust variants. Analysis properties can remain layer-owned; shared quantity/unit definitions need an explicit layer policy rather than accidental exceptions to existing data/reference rules.
+
+Use a closed, versioned expression language with literals, exact reference identities, parameter names, arithmetic, comparisons, Boolean operations and bounded reusable calculation calls. Provide sum over an explicitly modeled finite collection so engineers can express budgets without manually expanding every term. Pin precedence, type inference, coercions, result type and diagnostics in reST. Reference spelling may use stable source aliases, but name matching must not establish identity. Cycles and wrong reference kinds require located diagnostics; an unevaluable dependency must not silently supply zero.
+
+Evaluation should be a pure core operation on a model revision plus explicit inputs, shared by desktop and server. Distinguish a Boolean false result (an unmet engineering constraint) from invalid model structure, missing inputs and evaluation failure. Engineers must be able to save a model with an unsatisfied requirement. Results should identify the evaluated revision, input bindings and dependency path; mere evaluation must not dirty source or create an Undo step. Do not store an implicitly authoritative cached number that survives changed inputs.
+
+Quantities need named quantity-kind and unit identities, dimension information, explicit scale/conversion rules and a unit-bearing value. Quantity-kind identity and dimensional compatibility are related but must not be conflated: equal dimension alone is not universal interchangeability. Preserve the entered magnitude/unit while deriving a comparison representation. Support base and derived scalar units plus user-defined scaled units; built-in definitions should be versioned like other library content. Specify absolute/relative quantities and affine-unit rules before admitting offset units. A unit outside the supported model must produce a precise unsupported/invalid diagnostic, never a pretend conversion.
+
+Numeric behavior is a consequential open choice: compare exact decimal/rational arithmetic for predictable engineering budgets against bounded finite binary64 with explicit tolerance/comparison rules. Existing i64 bounds must not be weakened by converting all integers to binary64. Decide overflow, division by zero, nonfinite results and computation budgets before implementation. General equation solving, differential simulation, vectors/tensors and arbitrary embedded scripts are materially different candidate expansions; they are not implicitly delivered by a scalar calculation feature and must remain explicitly tracked if required by further scope evaluation.
+
+### Libraries
+
+Use an explicit **versioned package with a self-contained, validated source bundle, stable package/export identities and a digest**. Resolve/pin the complete dependency closure outside the pure compiler and retain the pinned bytes for offline reopen and hosted reproducibility. Local packages are sufficient to prove the workflow; a package registry or hidden network fetch is not required.
+
+The first coherent library workflow should support two distinct uses: immutable imported definitions such as data types/units/calculations, and editable instantiation of an exported OA/SA model pattern. Imported definitions retain package provenance; an architectural instance receives fresh local identities with an explicit source-to-instance map. Two uses must not alias one another accidentally. Pattern closure must cover actual owned records, internal relations, sequence order/bindings and relevant traces; external references require explicit binding or a reported unresolved dependency. Do not clone a second System of Interest or bypass existing OA/SA admission rules.
+
+An explicit namespace/export mechanism is needed; current global aliases and source include paths must not be repurposed as if they already provide it. Decide how shared immutable definitions participate in core references and commands, including prevention of accidental edits/deletes, and how layer-neutral vocabulary relates to OA/SA-owned elements. Pinning avoids silent changes, but a useful maintained library also needs an explicit update path: compare previous package revision, proposed revision and current local instance; show local changes/deletions and apply chosen updates atomically. Existing semantic-review concepts are relevant, but the current OA/SA counterpart reviewer is not automatically a cross-project library engine.
+
+A package's content being present in the source bundle does not make it ordinary editable project source. Its provenance and immutable/editable role must survive text, JSON, REST, history and reopen. An explicit detach operation can produce ordinary local content while preserving traceable origin. Graphical presentation copying should be a separately declared choice; it must not be confused with semantic instantiation.
+
+### Specification, API and selection workflows
+
+Keep one versioned Studio language/semantic specification and one OpenAPI contract with explicit conformance obligations and an external standards mapping. Extend the core command boundary for analysis/library changes; do not put separate evaluators or library rules in Iced and HTTP handlers. Analysis responses need revision/input/result identity and structured diagnostics; library mutations need the same ETag and atomic rejection guarantees as current model writes. A read response must not imply an analysis result for a different revision.
+
+For the proven long-list path, design one reusable contextual reference chooser: show the current value, appropriate scope/kind groups and containment context; support name/alias search and keyboard selection; reveal additional results explicitly with counts. Selected and invalid/missing values must remain visible. Search is helpful but cannot be the only organizing principle. A large collection editor should provide a compact overview and focused item editor instead of expanding every item into a full form. Apply this first to Constraint targets and similarly broad semantic/reference fields, preserving specialized caller/operation/port selectors already implemented.
+
+## Acceptance before calling the delta satisfied
+
+These are proposed evaluators. None has been executed in this audit.
+
+| Case | Required observable result |
+| --- | --- |
+| A1 — Real engineering budget | On an OA activity or SA function, author two duration inputs, e.g. 750 ms and 1 s, calculate total duration and constrain it to at most 2 s. Obtain 1.75 s and satisfied. Change one input to make the constraint fail. Show input identities, units, dependencies and exact evaluated revision. Use a separately computed oracle, not the evaluator's own output as the expected answer. |
+| A2 — Unit and numeric falsifiers | Equivalent scaled values compare correctly; adding duration to mass rejects; incompatible quantity kinds with equal dimensions are not silently substituted; division by zero, overflow, missing inputs and dependency cycles are distinguished. i64 boundary values retain precision. Verify whichever affine/relative-unit policy the spec actually admits. |
+| A3 — Genuine representation parity | Build the same calculation/property/constraint through GUI, source and REST and compare the full resulting model. Change units/expression/bindings through each path. Verify named references survive rename, and invalid drafts preserve the accepted model/source. Graphical editing exposes the expression and result meaning rather than only an opaque payload. |
+| A4 — Exact lifecycle and recovery | Save/reopen and GUI Undo/Redo restore full accepted model plus exact authored source/IDs for analysis and library edits. Crash recovery retains raw invalid expressions and unresolved binding drafts. Compare saved snapshots directly so a semantically similar reconstruction with new UUIDs cannot pass. Evaluation alone leaves revision/source/history unchanged. |
+| L1 — Cross-project reuse | Publish one package with a reusable data definition, calculation and OA/SA pattern. Consume it in two fresh projects, instantiate the pattern twice with different bindings in one project, and verify distinct instance IDs and one clear source map. Reopen offline with the same pinned definitions/results. |
+| L2 — Maintained library change | Publish an updated package, make a local instance edit, and preview update. Show retained/taken/deleted/conflicting content and affected bindings before one atomic apply. Rejection leaves all state unchanged. The untouched second project stays on its pinned revision; duplicate names, transitive package conflicts, missing dependencies and attempted edits to imported immutable content have explicit outcomes. |
+| S1 — Independent contract consumption | A client created from the published schema can create/read/query/edit/evaluate and perform package/instance operations without relying on undocumented Rust layout. ReST/OpenAPI examples, invalid fixtures, schema migration, no-op behavior and stale-ETag rejection agree with the implementation. No unproved OMG or complete Capella-conformance claim is made. |
+| U1 — Large-model human task | In the real minimum-size and wide application, select a desired Constraint target among at least 1,000 compatible records with repeated names, and edit one member of a large collection. The user can narrow by context/group, distinguish identity, reach keyboard actions and recover a no-match/invalid current choice without traversing the whole catalog. Establish a task-time/action budget before comparison; record observed results rather than treating widget count or pagination as usability proof. |
+
+## Integration consequences and next actions
+
+Root should record these as open first-release requirements in the durable ledgers now. Current behavior authoring, shared validation, source preservation and release-platform work remain useful and should continue. Their completion does not close A1–L2.
+
+Before a production subsystem, run a bounded semantic design/criticism step on owned engineering properties versus analysis contexts, numeric policy, quantity-kind/unit compatibility, and immutable definitions versus instantiated patterns. The recommendation above makes those choices concrete enough to evaluate; it does not authorize silently substituting metadata or templates for the user's requested capabilities. If a materially stronger interpretation of “standardized” or “libraries” is selected, preserve that scope explicitly.
+
+Any accepted extension needs a deliberate schema/source migration. Existing source7 Constraint expressions, guard/event/message text and arbitrary unit strings must remain authored annotations unless the engineer explicitly converts them to a checked expression or binds a unit identity. Do not infer execution from punctuation or recognize units heuristically during migration. Preserve complete identity/history, source ownership/comments, reference/deletion behavior, OA/SA transitions, semantic-review compatibility, JSON/OpenAPI, graphical drafts and recovery. Exact bundled definition versions must accompany hosted and file-based interchange.
+
+Only after the new workflow passes its semantic, API/source and native acceptance should the specification's current non-execution/non-conversion statements be replaced. Updating those sentences alone would create a false conformance claim.
From 632c8feb36ff44e17fa6a6f7bdb59da2017909aa Mon Sep 17 00:00:00 2001
From: Codex
Date: Thu, 10 Sep 2026 12:02:47 -0700
Subject: [PATCH 08/14] Consolidate active release ledgers and preserve exact
historical evidence
---
.gitattributes | 2 +
harness/PLAN.md | 166 ++++------
harness/STATE.md | 306 ++++++------------
harness/TASK_LEDGER.md | 225 ++++++++-----
.../PLAN.md | 121 +++++++
.../STATE.md | 247 ++++++++++++++
.../TASK_LEDGER.md | 102 ++++++
7 files changed, 781 insertions(+), 388 deletions(-)
create mode 100644 harness/history/2026-09-10-ledgers-before-continuation-refresh/PLAN.md
create mode 100644 harness/history/2026-09-10-ledgers-before-continuation-refresh/STATE.md
create mode 100644 harness/history/2026-09-10-ledgers-before-continuation-refresh/TASK_LEDGER.md
diff --git a/.gitattributes b/.gitattributes
index 5f9a2625..9d1ccf1e 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -18,6 +18,8 @@
harness/fixtures/release1-native/** -text
harness/fixtures/interface-operation-probe/** -text
harness/history/2026-09-06-ledgers-before-consolidation/** -text
+harness/history/2026-09-10-ledgers-before-continuation-refresh/** -text
crates/oa-desktop/src/recovery/fixtures/*.json -text
harness/fixtures/source7-native/** -text
harness/fixtures/basic-editing/** -text
+harness/fixtures/scenario-authoring/base/** -text
diff --git a/harness/PLAN.md b/harness/PLAN.md
index 94e1e1c1..f04c8ea4 100644
--- a/harness/PLAN.md
+++ b/harness/PLAN.md
@@ -1,121 +1,71 @@
# First release execution plan
-## Active execution — 2026-09-10
+Updated 2026-09-10. [PROJECT_GOAL.md](../PROJECT_GOAL.md) is the authoritative
+current objective. [STATE.md](STATE.md) records exact execution/evidence and leases;
+[TASK_LEDGER.md](TASK_LEDGER.md) separates facts, assumptions and decisions.
+The complete previous plan is [preserved byte-for-byte](history/2026-09-10-ledgers-before-continuation-refresh/PLAN.md).
+Its earlier goal paths, active-process claims and Escape pauses are historical.
-1. Restore a reproducible local toolchain; repair the interrupted compact palette;
- verify focused history/draft/placement regressions and the actual native workflow.
-2. Independently reassess current behavior/scenario ergonomics and OA/SA semantic,
- source/API and documentation completeness. Resolve verified high-impact gaps
- with bounded changes and cross-representation acceptance criteria.
- The updated goal explicitly requires calculations, quantities/units and
- reusable model libraries. Audit actual semantics, compare coherent alternatives
- and establish observable acceptance before implementing foundational extensions.
- Correct concrete unbounded selectors without replacing existing useful search.
-3. Complete realistic graphical OA/SA teaching, transition, behavior, recovery and
- hosted workflows. Exercise minimum and wide layouts, rejected edits and history.
-4. Run current Windows/Linux automated and native acceptance, fresh independent
- end-state reviews, optimized builds and package verification. Publish accurate
- build/run evidence and handoff only when the original release criteria hold.
+## Outcome and working principles
-This ordering supersedes historical stopped-session next actions below. Preserve
-their scoped evidence; no test or runtime result transfers to a new binary without
-verification. Root holds the serialized build/native lease; Gitea #1 tracks workers.
+Deliver usable, complete OA/SA engineering workflows on Windows and Linux, with
+one precise reST contract and equivalent graphical, textual and REST editing.
+Constraints/calculations, quantities/units and reusable libraries are explicit
+requirements alongside behavior/state modeling, traceability and navigable help.
+Later Arcadia layers remain future scope. Compilation, metadata fields and polished
+screens alone do not satisfy this outcome.
-**Latest refinement:** consult actual installed Capella 7.1, replace the user-rejected horizontal creation/connection rows with a compact contextual palette and inline authoring, independently critique interaction risks, then build and verify the real workflow at wide/minimum sizes. [Benchmark and concrete acceptance](release-capella-basic-editing-20260907.md). Do not ship the rejected toolbar as the usability correction.
+Retain the shared typed core, stable identity, semantic ownership/allocation distinct
+from geometry, validated atomic commands and exact source/history. Persistent
+extensions need coherent references/deletion, transition, source/migration,
+API/schema and reachable graphical authoring. Define observable acceptance before
+broad downstream changes. Do not silently promote a design proposal to a decision.
-**Current priority (7 September):** the user's usability correction takes precedence over the advanced feature sequence below. Establish contextual named diagram creation, existing-element placement, basic editing and clear layer transitions, then verify them natively. Desktop checks were explicitly reauthorized. See [priority reset and acceptance](release-basic-editing-reset-20260907.md).
+## Capability milestones
-## Outcome and baseline
+| Milestone | Work and acceptance | Dependency/current limit |
+| --- | --- | --- |
+| 1. Reliable direct authoring | Finish cold compilation and meaningful regressions; integrate compact named architecture/behavior creation and scenario workspace. Exercise actual minimum/wide native creation, allocation, editing, rejection, cancellation and history without implicit draft application or placement drift. | Root owns this host's Cargo/native lease. External host owns scenario layout/B3/local creation/palette; integrate coherent checkpoints first. No current local Rust/native Studio pass. |
+| 2. Coherent process/scenario work | Interactive ordered/explicit process editing; clicked root/nested scenario insertion; exact reuse bindings and caller return; measured canvas/zoom/scroll; visible scoped Create/Delete lifetimes. Validate model/source/identity and no unintended edit during navigation, including stale/deleted/retargeted references and recovery. | B1/B2/B3 adapters are source-integrated; B3 has a known externally owned correction. B4 is cfg(test) projection only, awaiting Scene integration. Compile/self-check the independent oracle before using native checkpoints. |
+| 3. Precise engineering analysis | Compare owned properties, analysis contexts and existing-data/external alternatives; settle quantity kind/unit/value, numerical policy, calculation/binding and Boolean/error/unknown semantics through falsifying examples. Implement mixed-unit budgets and reusable calculation/constraint definitions with full UI/source/REST parity and exact history/recovery. | New requirement audit confirms absence. Maximum-reasoning proposal tracks precede root synthesis and independent criticism; no semantic contract is ratified yet. Strings are not evaluation. |
+| 4. Maintained reusable libraries | Compare package/definition/instance alternatives. Establish pinned identity/version/dependency closure, local edits, import bindings, detach and explicit three-way updates. Demonstrate two projects, two independent instances, offline reopen, conflicting updates and stable unaffected identities. | Coordinate with milestone 3 definition references and migration. Same-project includes/reuse do not satisfy maintained cross-project libraries. No package contract implemented yet. |
+| 5. Understandable complete engineering tasks | Close teaching, OA↔SA transition/reconciliation, typed state/data/interface, artifact, saved-view and hosted workflows. Replace concrete long homogeneous selectors with contextual/searchable bounded choices; verify representative large models, keyboard access, minimum sizes and failure/recovery. Keep offline guide and exact worked examples aligned. | Existing implementations and historical evidence are a coverage starting point. Reassess against current goal instead of reviving stale absence claims or carrying old scope exclusions forward. |
+| 6. Release confidence and delivery | Fresh current Windows/Linux suites, strict Clippy/format, normative reST/OpenAPI/migration/examples; actual GUI/service transport, optimized builds, runtime/notices/archive/provenance/checksums and clean-environment launch. Fresh independent requirements/domain/parity/architecture/UX/runtime review; resolve material findings, then final handoff. | Integrates all preceding capabilities. Linux runtime remains open; use an approved supported environment, preserving historical failed/denied experiments. Historical previews are not final distributions. |
-Deliver the first release of Arcadia Studio for system engineers: complete Operational Analysis and System Analysis/System Architecture workflows, equivalent graphical and textual models, clear artifact traceability and a deployable/programmatically accessible Rust service. Logical/Physical Architecture and later Arcadia layers are future releases, as the new objective explicitly states.
+Milestones are capability gates, not a rigid serial schedule. Root can continue
+build/native acceptance while independent foundational investigations compare
+alternatives. Libraries and analysis share definition-resolution assumptions and
+must converge coherently before broad model/schema edits. User workflows and
+cross-representation evaluators guide implementation boundaries.
-The authoritative objective is attachment `10b0f701-1743-4de7-9ee1-225920e19649/goal-objective.md`. It retains first-release scope and strengthens domain coherence, alternative evaluation and target-user task evidence. TASK_LEDGER.md separates given/verified facts, assumptions and open candidates; STATE.md records progress. The accepted 0.2.0 prototype is a historical baseline, not proof of release completeness. Its old D:/CodexBuilds archives are unavailable on this machine; existing dist archives must not be replaced until new acceptance passes.
+## Immediate execution sequence
-The user's cross-branch reminder led to an ancestry/patch/tree audit. Continue from integrated `c091f80` on `codex/release1-continuation`; direct state/scenario/process, data, hosted and semantic review work already exists here. `release-branch-integration-20260906.md` records the audit. `release-current-coverage-20260906.md` is the current measurable acceptance map and supersedes stale absence claims in older narratives below.
+1. Observe the existing one-job desktop build to completion; preserve failures and
+ fix causes. Run focused corrected cases, then proportionate full gates and GUI.
+2. Exchange exact verified commits with the separate host, preserving its workspace
+ ownership. Do not merge the bundle-transfer branch or assume remote publication.
+3. Compile and self-check [scenario oracle](scenario-authoring-contract.rs), follow
+ its [native procedure](fixtures/scenario-authoring/README.md), and compare exact
+ whole snapshots for Undo/Redo/reopen at minimum/wide sizes.
+4. Synthesize the [requirement delta](release-requirements-delta-20260910.md) and
+ independent semantic proposals; subject materially different candidates to
+ concrete numerical, identity, migration and engineering-workflow falsifiers.
+5. Update current ledgers and durable decisions when evidence changes the plan.
+ Reassess unresolved assumptions before expanding downstream implementation.
-## Existing architecture and retained decisions
+## Coordination and evidence rules
-Retain three Rust crates and one typed Project/validated atomic Command boundary. oa-core owns semantics, references, history, transitions, source and persistence; oa-desktop owns Iced interaction/drafts/projections; oa-server owns REST/ETag/current-model storage. Keep identity, semantic containment, allocation and per-diagram placement separate. No runtime plugin framework or later-layer skeleton.
+Use Gitea issue #1 and Wiki for current ownership/handoffs; read before resuming.
+Codex-956atf and Codex-9e8r6t are distinct hosts. Cargo/native leases are host-local;
+shared source ownership is coordinated. Root owns local integration and final
+responsibility. Both hosts currently lack direct push authentication; authenticated
+MCP and anonymous fetch support verified bundle exchange without shared-ref overwrite.
-The current forms are a closed typed-record adapter, not a second metamodel. Expand their real creation/reference/variant controls with every semantic addition. Core choices must drive graphical choices. New persistent fields participate in text round trips, identity/removal visitors, transitions, API schemas and graphical authoring tests.
-
-## Milestones and measurable acceptance
-
-Nested source ownership and persistent REST bundles are implemented and verified: Windows minimum-size source-to-graphics, nested reparent/source/Undo/Redo/Save/reopen, recovery, rebuilt Linux source/history/Save/reopen, independent review and both platform suites passed. Preserve the existing grammar/updater and separate allocation/order semantics.
-
-Explorer/port discovery, custom routing, direct palette connections, four-side port
-anchors, contextual menus and source folders have scoped Windows/Linux acceptance.
-See release-explorer-usability.md, release-routing-verification.md,
-release-routing-linux.md and release-port-drawing.md for exact evidence and limits.
-
-Direct state-machine editing is accepted through316dc55: typed persisted views,
-explicit-owner creation, movement, direct transitions/reconnection, typed detail
-forms, folding, placement and correct camera/tool lifetime. Windows default/minimum
-and Linux native acceptance plus independent reviews are recorded in
-release-state-editing.md. Direct scenario/process gestures remain required.
-
-R6 data contracts and direct scenario/process gestures are integrated; native
-acceptance continues on both platforms. Typed R3 core/source5/REST and scalar review
-milestones1–2 are integrated and pass full platform checks. Close independent scalar
-fixes and native review, then finish constructor/keyed/order/reset/artifact and
-multi-source graphical authoring using the existing shared core planner. Source5
-migration retains lexical ownership/comments; no flat source rewrite is allowed.
-See release-data-contract.md, release-semantic-integration.md and release-semantic-gui.md.
-
-Continue source authoring improvements only through the production parser/updater.
-The23-test experiment is evidence, not architecture: independent probes exposed
-comment misattachment and unsupported edits. Additional shorthand must preserve
-complete edit/delete behavior and exact source history, and justify itself in real
-human authoring tasks. Shared state placements are now validated; scenario/chain
-interaction must preserve its own occurrence and ordering semantics.
-
-1. **Release coverage audit and interfaces.** Compare actual core/UI/API with official Capella OA/SA semantics and the new objective. Freeze a coverage table and concrete variant/reference/transition contracts before editing shared interfaces. Distinguish unsupported later layers from missing OA/SA workflows; V2 exclusions are reconsidered. Native audit exercises onboarding/help/authoring at supported sizes. Independent audits have separate file ownership.
-2. **Complete engineering semantics.** Fill verified gaps in capability relationships and mission involvement; operational process/functional-chain function/control topology; scenario/state/data constructs needed by OA/SA engineers. Add typed semantic-content drift and reviewed existing-counterpart reconciliation, including reference mapping and explicit dependency/ownership decisions. Every addition has a usable graphical authoring workflow, equivalent source/API representation, meaningful reject/undo/delete/reidentify/transition tests and a worked example. Exact additions and invariants are recorded in the coverage/interface artifact from milestone1, avoiding speculative LA/PA concepts.
-3. **Approachable authoring and help.** Replace static help with searchable topic navigation, concise concepts, numbered OA→SA tutorials, related topics and direct workspace actions. Add a focused starting workspace, recent models and practical creation guidance. Improve source authoring with asynchronous versioned lint, semantic warnings, diagnostic navigation and find/go-to tools. Make selection/navigation and hidden/placed model distinctions clear. Add recoverable local checkpoints for unsaved accepted models and drafts; preserve explicit file-save/conflict behavior.
-4. **Connected automation workflow.** Keep headless deployment and shared core REST contract. Add direct optional desktop open/publish/save of hosted models using explicit connection details and conditional ETags, preserving drafts on failure/conflict and keeping credentials in memory. Verify actual desktop→API→desktop edits and artifact integration locally. Update OpenAPI, deployment and source contracts for the expanded release semantics.
-5. **Release integration and polish.** Guided examples cover OA goals/roles/process alternatives, system missions/capabilities/boundary contracts, chains/scenarios/states and evidence. Review default/minimum/wide layouts, practical keyboard paths, empty/loading/error/recovery states and performance on representative models. Build/run the actual native app after each meaningful UI milestone, fix from evidence, then reverify.
-6. **Independent final acceptance and packaging.** Fresh requirements, correctness, native UX and simplicity reviews in parallel; fix all material findings and rerun affected checks. Full Windows/Linux locked tests, formatting, strict Clippy and optimized builds. Operate primary GUI workflows and actual service transport. Verify current docs/reST rendering, examples/migration, reproducible source workflow, package binary equality/archive contents and checksums. Only then replace 0.2.0 bundles and mark this goal complete.
-
-Dependencies: milestone1 freezes shared interfaces for2; guide/lifecycle work in3 can proceed independently once UI ownership is partitioned. Structural counterpart work depends on2. Hosted client4 depends on final core/API types but transport/lifecycle can be developed independently. Milestones5–6 integrate all preceding work.
-
-## UI and state flow
-
-Keep task navigation, engineering perspective and context-sensitive properties. A Home/start workspace offers projects, recovery and tutorials without obscuring active modeling. Help uses local Rust-owned content and search/category navigation; workspace links pass explicit events and respect draft/modal guards.
-
-Text edits update a snapshot/version; parsing/semantic lint runs on a blocking worker and only the latest result is adopted. Syntax errors and semantic warnings have distinct severity/location and navigate to source. Applying text remains one atomic model transaction. Invalid text and property input are recoverable drafts.
-
-Recovery writes separate per-session checkpoints in user-local application data, never silently overwrites a model file, and offers explicit restore. Recent paths are convenience metadata. Hosted models use a separate connection/resource/revision/ETag handle; ordinary core editing and Undo stay local until explicit conditional Save. Network/file failures retain the same draft. Tokens are not persisted or included in diagnostics.
-
-## Failure and verification strategy
-
-Cover wrong-layer/reference/kind input, ownership/reference cycles, duplicate or ambiguous counterpart matches, stale plans/ETags, typed semantic drift, sequence control topology and repeated occurrences, invalid source/property drafts, external file edits, failed storage/network operations, oversized models and unavailable graphics. Keep incomplete engineering work editable through warnings while rejecting structural corruption.
-
-Tests must cover user-visible meaning and cross-representation invariants, not just serialize the same value twice. Use actual HTTP and native CUA for end-to-end behaviors. Independent review targets current artifacts and the original release objective, not only implementation summaries. Record unsupported/unavailable environments without turning missing required functionality into a limitation.
-
-## Definition of done
-
-Every concrete requirement in the new objective and release coverage table has authoritative current evidence. Primary OA/SA engineering workflows work graphically, textually and through the API; help is navigable and useful; native Windows/Linux UI is intentionally designed and exercised; no blocking/high-impact defect remains; strict checks pass; independent findings are closed; durable docs accurately reflect the release; another engineer can build/run/test and use the supplied packages. V2 completion alone does not satisfy this goal.
-
-## Historical milestone progress — hosted integration
-
-Milestone4 desktop adapter is implemented on the existing Document/REST boundary.
-Windows native publish, graphical/text save, rejected invalid source, API artifact
-reload and stale-revision recovery are demonstrated. Offline/retry and exact local-copy/catalog Open native acceptance passed on Windows.
-Destination and catalog defects found afterward were fixed/reviewed/integrated; remaining platform/native cases retain their report scope. Constructor/keyed/order/Child/Incident/artifact and multi-source review, Home/recents and checkpoint recovery are now implemented. Their scoped native and automated results are in STATE.md; this older milestone is not a current absence list.
-
-## Current execution refinement — 2026-09-07
-
-First-release scope above is unchanged. The user-requested Windows development preview is packaged with565files, including all37 teaching-folder files, and is linked from `DEVELOPMENT_SUMMARY.md`. Latest Fit6 and two existing scroll/branch regressions pass; full desktop570/570, workspace bins, formatting and strict workspace/all-target Clippy pass. Corrected full server56/56 retains its unchanged-server scope. Exact build status and provenance are recorded in STATE.md. This is not an optimized final release.
-
-Native6BAB operation/recovery/history passes and BCC1 corrected OA/SA nested routing passes retain their separate binary scopes. The latest DC50 preview launched with a clean PATH, and its four-card OA Fit overview was visible at1444×932 and1102×932. Minimum-height, pending-form and remaining process-purpose acceptance are still open. Computer Use was stopped by the user's Escape; leave both live GUI processes untouched and continue independent work.
-
-Native Linux core436passed/2ignored is complete on its frozen455-file snapshot. The original server compile passed, unit2/2 and HTTP50/51 passed, and the single504-versus412 timing fixture failed twice. Its private preparation-gate replacement passes on Windows but still requires Linux execution. Preserve the original failures and exact final-state assertions; do not alter the production30-second deadline to make the fixture pass.
-
-The fresh Linux GUI cross-build passed on immutable458-file snapshot `source7-20260907T102528493108Z`, including the Fit constructor fix. Run `20260907T1025486926611Z` completed in2m20s Cargo time with pre/post checks, ELF requirements and all37 lesson files verified; the compiler lease is released. The subsequent headless guest failed its early IO-APIC timer check on three bounded boots, before SSH/tests. Exact matching boot assets were extracted, but automatic approval review rejected the proposed transient boot workaround before launch. All task VMs are stopped; no application failure or current Linux server pass is inferred. Latest guide-label changes and the validated Exchange/CommonWork native oracles follow the frozen binary snapshot.
-
-Next actions:
-
-1. Retain all three guest boot records and exact-kernel proof; the proposed direct-boot action was denied by automatic approval review and must not be retried or split to bypass it. Resolve the runtime gate through an approved route or changed environment. Verify actual readiness and remaining monotonic budget before running the corrected Linux server gate. Do not restart on observation timeout alone.
-2. Preserve the reviewed guide corrections and validated expected-model/source Exchange/CommonWork oracles; the updated guide now builds on Windows and belongs in the next packaged/native snapshot. Complete native purpose authoring/history once control is available.
-3. Once native control is authorized again, complete current-layout minimum-height/Focus/pending-form checks, remaining engineering/hosted walkthroughs and actual Linux GUI acceptance using exact model/source/history evidence.
-4. Execute the corrected Linux server suite; complete final Windows/Linux optimized builds, package provenance, documentation/contracts, and fresh independent cross-disciplinary end-state review. Successful slices do not complete the goal.
+Record exact source/binary, command exit, test scope and native model/source oracle.
+Do not add focused reruns to full-suite totals or transfer old binary acceptance.
+Older denied Linux workaround instructions remain applicable to that action; do
+not bypass them. Older stopped native sessions do not prevent currently authorized
+native work. The [archived plan](history/2026-09-10-ledgers-before-continuation-refresh/PLAN.md)
+and [archived progress](history/2026-09-10-ledgers-before-continuation-refresh/STATE.md)
+preserve all previous strategy and scoped evidence; archive report links resolve
+from their original `harness/` directory.
diff --git a/harness/STATE.md b/harness/STATE.md
index 8c56648c..4c24887f 100644
--- a/harness/STATE.md
+++ b/harness/STATE.md
@@ -1,220 +1,124 @@
# Progress Ledger — first release
-## Active continuation — 2026-09-10
+Updated 2026-09-10, coordination through Gitea #92. This ledger describes the
+Codex-956atf checkout; external-host evidence is labeled separately. The entire
+previous ledger is preserved [byte-for-byte](history/2026-09-10-ledgers-before-continuation-refresh/STATE.md).
+Historical dates, binaries, process IDs and stopped-session instructions in that
+archive are evidence history, not current status or authorization.
-The updated user goal from attachment `2f1f0002-e43c-45ba-80a8-b359f05f99d0`
-was read in full and copied exactly to `PROJECT_GOAL.md` on 2026-09-10. It now
-explicitly requires calculations, quantities/units and reusable model libraries,
-and rejects long homogeneous choice lists. Root is integrating
-the first release, prioritizing direct diagram authoring and behavior/scenario
-usability. The checkout began clean at `1797516` on `codex/release1-continuation`.
-Gitea issue #1 and Wiki Home were read; ownership and progress are coordinated there.
+## Current objective and source
-- Root owns integration, serialized builds, native Computer Use and these ledgers.
- `palette_repair` owns architecture authoring and `diagram.rs`; `behavior_audit`
- and `release_audit` independently inspect ergonomics and release coverage.
-- The recorded palette compilation errors have local corrections pending a fresh
- build. Earlier 584-test evidence does not cover those corrections.
-- This checkout began without the historical `.local` toolchain/cache/packages.
- Project-local Rust/Cargo 1.98.1 GNU/LLVM and verified LLVM-MinGW 20260908
- (clang 23.1.1) are installed. Windows desktop dependencies are compiling.
- Host: four logical processors, 8 GiB RAM; builds remain serialized.
-- Computer Use initialized successfully. The renewed request authorizes native
- evaluation; old session Escape holds and process IDs are historical. No Arcadia
- or Capella window is currently running.
-- WSL lists no installed distribution. Linux runtime/build acceptance remains an
- open gate; no historical denied guest workaround is being retried.
+Deliver the complete, usable OA/SA first release in [PROJECT_GOAL.md](../PROJECT_GOAL.md),
+including graphical/source/REST parity, constraints/calculations, quantities/units,
+reusable model libraries, traceability, offline guidance and Windows/Linux release
+acceptance. Direct behavior/scenario authoring and reduced cognitive load remain
+immediate priorities. The release is incomplete.
-**Current branch is `codex/behavior-authoring-20260910`.** A distinct Gitea user
-`Codex-9e8r6t` is running another root/team on a separate environment with the old
-build cache. Our identity is **Codex-956atf**. Gitea #25 and #38 acknowledge the
-scope split: their palette/platform/package and subsequent scenario layout work;
-our process/scenario adapters and typed-history work. We will publish a clearly
-untested coherent snapshot on this distinct branch for their warm-cache checks.
-No shared branch-tip push or merge has occurred here.
-Their reports/results must not be claimed as local validation or overwritten.
+Local branch: `codex/behavior-authoring-20260910`; source checkpoint `aa40345281e6ae8f203063840f1766ba9ce8381c`.
+The authoritative goal was copied byte-identically from attachment
+`2f1f0002-e43c-45ba-80a8-b359f05f99d0/goal-objective.md`; SHA256
+`7C1BDBD077E31439501C04DC3613E6BE47A389704DDC3A44EF8BD9AD98EBB513`.
+See [Task Ledger](TASK_LEDGER.md) for knowledge/decisions and [Plan](PLAN.md)
+for capability acceptance. Older goal paths are superseded.
-Implemented locally, awaiting compilation:
+## Completed work and evidence
-- Palette compile repair, retained inline name after rejected stale Submit,
- nonfinite click rejection and rename/allocation/history regressions.
-- One interactive process renderer with presentation-only inferred ordered
- paths; first editing batch preserves adjacency atomically. Independent source
- review found no confirmed defect. Root added 3+ step/before-first insertion
- and incompatible imported conversion tests after the reviewer hit model capacity.
-- Scenario Action/State/Reuse/empty Fragment tools capture clicked operand/slot
- and optional lifeline. Pointer-to-App, recovery, stale-event and history tests
- are written. A bounded exact caller/use return path and scoped mounted-scroll
- restoration are implemented with regressions, also untested.
-- Advanced typed stored-history editor uses existing OpenAPI type descriptors;
- twelve focused tests cover typed selectors, historical IDs, invalid raw input,
- recovery and source/history. Core validation remains authoritative.
-- Normative catalogs now include ExchangeCategory, ContextualLink and both
- Exchange trace pairs. Manual trace help and scope source version corrected;
- strict Docutils 0.22.2 HTML regeneration passed. OpenAPI `semantic_schema.py
- --check --validate` passed 72 fixtures and all 204 component schemas.
+| Scope | Current evidence and limits |
+| --- | --- |
+| Reproducible local toolchain | Ignored `.local/toolchain/env.ps1` selects Rust/Cargo 1.98.1 GNU/LLVM and verified LLVM-MinGW 20260908 (clang 23.1.1), with one Cargo job. The cold build is still active; this is setup evidence only. |
+| B1 process authoring | `01bb52a` includes interactive empty/ordered/explicit topology and presentation-only inferred paths; first conversion preserves ordered adjacency atomically. Source review and added boundary tests exist; current local Rust/native acceptance is pending. |
+| B2 scenario authoring | Clicked root/operand/slot and optional lifeline feed Action/State/Reuse/Fragment adapters. Pointer-to-App, cancellation, stale-event, recovery and history tests are written, not yet locally executed. |
+| B3 caller navigation | Bounded caller/use frames and scoped viewport restoration are implemented. External tests exposed a real same-selected-ID/visible-owner defect, detailed below; this feature is not accepted. |
+| Architecture palette | External palette commit `7d1df4f` was cherry-picked as `aed40d7`, retaining local invariant tests. Captured Escape, stale Submit/raw retention and bounded card fixes are source-integrated. External native results do not transfer to this checkout. |
+| Typed stored-history F1 | `b6b76ef` fixes nullable constructor cache discrimination; `4ecad78` fixes NotApplicable seeding by checking actual enum alternatives. Thirteen focused history tests await local execution; core validation remains authoritative. |
+| Lifeline lifecycle B4 | `eb5161b` adds pure local/scope-aware lifecycle projection with ten tests, declared under `cfg(test)` only. It is not production Scene wiring or native acceptance. [Design](release-scenario-lifecycle-design-20260910.md). |
+| Native scenario oracle | `8547b85` includes [independent checker](scenario-authoring-contract.rs) and [instructions](fixtures/scenario-authoring/README.md). Whole Project/SourceBundle comparison preserves exact identities for Undo/Redo. Compile, self-check, fixture generation and native use are pending. |
+| Normative documentation | Strict Docutils 0.22.2 language HTML regeneration passed. `semantic_schema.py --check --validate` passed 72 source7 fixtures, 204 schemas, 14 families/90 scalar leaves. These are documentation/schema gates, not a Rust build or runtime pass. |
+| Comparable-product study | Actual isolated Capella 7.1 scenario/activity/lifeline/LOOP creation was saved and XML inspected, then closed normally. [Report](release-capella-scenario-benchmark-20260910.md). No Studio native workflow has run on this fresh host. |
+| New requirement audit | [Independent delta report](release-requirements-delta-20260910.md) establishes that current expressions/unit labels are descriptive and source includes are not maintained cross-project libraries. Separate foundational design proposals remain under investigation, not ratified or implemented. |
-Reference evidence: actual Capella 7.1 scenario/lifeline/LOOP creation and saved
-XML are recorded in `release-capella-scenario-benchmark-20260910.md`; the isolated
-Capella process closed normally and is absent. This is not Studio acceptance.
+## Active work, ownership and unresolved findings
-Toolchain bootstrap exec session **20812** completed successfully: verified
-LLVM-MinGW 20260908 ZIP SHA256
-`1bcf74d06b724aeecaa6412ca85f5b26fb1da770e7cdcefa9263c9c5c3ad34b6`, extracted
-successfully; stable **1.98.1** GNU/LLVM, rustfmt and Clippy installed.
-`.local/toolchain/env.ps1` configures task-local Cargo/Rustup/PATH and one build
-job. The original full-platform dependency fetch **13108** was stopped after
-network timeouts; Windows-only compilation fetched its remaining dependencies.
-No current Rust build/test pass is established yet.
+Root Codex-956atf owns this host's integration, serialized Cargo and native UI.
+Session `51036` runs `cargo test --locked -p oa-desktop --bin arcadia-studio --no-run`
+with log `.local/logs/desktop-test-build.log`; at 11:59 Pacific it has reached
+`oa-desktop` but has not completed. No local Rust test/build pass or current Studio
+binary acceptance is claimed. Source the local toolchain environment before later
+commands; do not start a concurrent Cargo writer. Confirm process state on resume.
-The coherent candidate `01bb52a` passed `cargo fmt --all` and `git diff --check`.
-Direct push failed because this fresh host has no Git credentials. Authenticated
-Gitea MCP transferred its exact verified Git bundle through
-`codex/behavior-transfer-20260910` (`harness/transfers/behavior-01bb52a.bundle.b64`),
-prerequisite `1797516`, decoded SHA256
-`85A38B00207036DCB8DB24ED4A7E80B2BE670275EEB7B00413D36153322ADA5D`.
-This **UNTESTED integration snapshot** lets the other host use its warm cache;
-transfer is not acceptance. Their broader architecture
-palette/Escape repair should be reconciled with our overlapping compile/stale
-repairs while retaining the additional invariant tests. Their measured minimum
-scenario canvas is too small; they own the subsequent layout implementation.
-Their isolated palette commit `7d1df4f` was cherry-picked cleanly as `aed40d7`.
-Our additional authoring tests and scenario handlers are retained. External
-native/test results remain scoped to their exact source and executable.
+Codex-9e8r6t is a separate host/team with a warm cache. It owns scenario workspace
+layout/B3 correction, local named behavior creation, palette and platform/packaging.
+Its latest reported mounted-layout slice passed 8/8 focused tests (#82); subsequent
+raw-draft guard, B3 owner correction and integrated seed fix await its focused gate.
+Its source/commit is not yet imported here. Earlier immutable integrated tests
+passed 615/618, exposing the seed and navigation cases; that is external evidence,
+not a current local suite result. Its creation module is prepared but not declared,
+compiled or natively accepted (#81). Packaging `b98f135` is likewise not imported.
-Independent F1 review found a real optional-value cache collision in `01bb52a`.
-The follow-up distinguishes null/scalar/list/tagged constructor identities and
-adds actual inner selector-event tests. Independent source recheck found no
-remaining collision; execution is still pending. The original full-platform
-fetch was stopped after network timeouts; current Windows desktop test build
-is exec session **51036**, compiling dependencies. Root owns this Cargo lease.
+B3 diagnosis (#79): command execution may preselect an occurrence while another
+scenario remains visible; `select(same_id)` then returns without restoring the
+caller owner. Back/OpenUse must establish visible owner and selection together.
+Retarget rejection also needs a correctly established pre-Back test state. Preserve
+exact source/history and mounted viewport checks. External ownership includes
+explicit guards against tool selection silently applying a pending property draft.
-Commit `eb5161b` adds the pure B4 lifecycle projection, ten admitted-fixture
-tests and current scenario/history guide wording. The module is declared only
-under `cfg(test)` pending coordinated Scene integration after the other host's
-scenario workspace refactor. Independent source reviews found no confirmed B3
-navigation or B4 projection blocker; synthetic geometry tests do not establish
-actual Scene/marker/hit-test acceptance. No new Studio binary has launched here.
+Maximum-reasoning analysis/quantity and library tracks own separate proposal
+reports only (#83/#86). Missing calculation evaluation, dimensional/unit semantics,
+maintained library reuse and concrete unbounded selectors are release gaps. No
+broad shared core/schema changes are justified until synthesis and criticism.
-Latest transfer: `harness/transfers/behavior-eb5161b.bundle.b64` on the transfer
-branch, decoded 91,268 bytes, SHA256
-`87F3FBA862AE3751E60E18B39C7BB02B147E4E66079F501050A709D8ADDADE6D`.
-Full MCP readback matched local bytes. It contains all four local commits from
-`1797516`; the other host can cherry-pick `b6b76ef` and `eb5161b` onto its existing
-integration. Gitea #65 records exact handoff and the Wiki title-encoding problem.
-Root is preparing an independent native scenario fixture/oracle through the
-release auditor; only harness/scenario-authoring-contract.rs and its README are
-owned by that worker. Generated fixtures require a later direct-rustc lease.
+Both hosts report direct Git push authentication unavailable. Anonymous fetch and
+authenticated Gitea MCP remain usable. Exact verified bundles are exchanged on
+`codex/behavior-transfer-20260910`; do not merge that transfer branch. Existing
+bundles cover `01bb52a`, `b6b76ef`, `eb5161b` and incremental `4ecad78`; exact hashes,
+prerequisites and readback are preserved in the [archived ledger](history/2026-09-10-ledgers-before-continuation-refresh/STATE.md)
+and Gitea #65/#76. Latest verified transfer is `harness/transfers/behavior-aa40345.bundle.b64` on that branch (Gitea #92): 24,926 decoded bytes, prerequisite `4ecad78`, SHA256 `21B36D5016600953D95F064AE56569B6C8E7D3EA2B3B316C97222AD122FECA4F`. It includes the requirements audit and removal of an unused palette import after an actual compiler warning; the build is still incomplete. Root owns subsequent transfer and integration.
-Actual cross-host test evidence supersedes source-only confidence: the other
-host's immutable pre-`b6b76ef` integrated artifact ran 618 tests, with 615 passing
-and three failures (Gitea #68). One B3 deleted/changed-caller regression is being
-diagnosed by their scenario/layout owner. Two F1 failures came from seeding
-optional UUID/text as `NotApplicable` via broad string-shape matching. Local
-`4ecad78` now recognizes only an explicit enum constructor and adds the nullable
-UUID/text regression; independent descriptor review clears it for execution.
-No assertion was weakened. Thirteen history tests now await local execution.
-Incremental transfer `behavior-4ecad78.bundle.b64` requires `eb5161b`, decoded
-1,464 bytes, SHA256
-`3A93A99AB9C727A4B685D7436C9E23FB262144580E8524623B75D2D676FDF033`.
-Exact local and MCP readback checks pass; Gitea #76 requests warm-host reruns.
+## Remaining gates and highest-value next actions
-The native scenario oracle now requires both independently predicted semantic
-checkpoint equality and exact saved/restored Project+SourceBundle identity.
-Independent review caught that rediscovering fresh role IDs at each checkpoint
-alone could falsely accept recreated IDs on Redo; `same` and its adversarial
-self-check close that evaluator gap. It remains uncompiled/unexecuted.
+1. Finish the active build; diagnose actual failures. Run focused history,
+ architecture, topology, clicked scenario, caller navigation and lifecycle tests,
+ then relevant full suites, strict Clippy and current GUI build. Record exact tree.
+2. Integrate the externally verified workspace/B3/local creation/palette/package
+ checkpoints without overwriting ownership or treating their earlier tests as new
+ local acceptance. Wire B4 only after coordinating actual Scene geometry.
+3. Compile/self-check the independent oracle, then exercise current Studio at
+ minimum/wide sizes: named creation, direct process/scenario edits, nested reuse,
+ caller return, lifeline lifecycle, invalid/cancel/recovery, Save/Undo/Redo/reopen.
+ Compare complete model/source and exact identities, not screenshots alone.
+4. Resolve analysis/quantity/library alternatives against real engineering budgets,
+ unit/kind failures and two-project reuse/update examples. Then implement precise
+ shared-core/source/API/graphical contracts and large-model selection workflows.
+5. Complete current teaching, transitions, semantic review, state/data, saved-view,
+ artifact, hosted transport and migration acceptance. Historical scoped passes
+ remain useful references, never blanket current acceptance.
+6. Complete Windows/Linux build/test/native/runtime and optimized package gates,
+ notices, provenance and clean-environment launch. WSL has no installed distro
+ here; Linux acceptance remains open. Historical denied guest workarounds must
+ not be retried or split to bypass review. No host restart is authorized.
+7. Run fresh independent cross-disciplinary end-state review, resolve material
+ findings and reconcile specification/help/coverage before final handoff.
-Fresh `requirements_delta` owns only its new audit report. Early source evidence
-shows constraints are explicitly unexecuted expressions and units are strings
-without dimensional conversion; the newly explicit calculation/quantity goals
-must not be declared complete from this metadata. Reusable library contracts
-and concrete unbounded selectors are under domain/requirements review before
-foundational design decisions. Current behavior/native work remains useful.
+## Reassessment
-Next: focused `architecture_authoring_tests`,
-`behavior::topology`, `direct_clicked`, caller-navigation and `history_fields::tests`,
-then full relevant gates and a fresh native binary. Root owns all Cargo on this
-host. Worker output or tests existing in source are not passes.
+Meaningful progress continues: concrete interaction defects have been found and
+corrected, the cold toolchain is advancing, exact native evaluation is prepared,
+and the revised semantic requirements are no longer hidden behind metadata.
+Passing older suites or accumulating prose would not close the user problem.
+Current highest information gain comes from actual build/native results and
+independently criticized foundational examples, while preserving existing identity,
+source and atomicity contracts. Replan when those results falsify a proposal.
-Gitea Actions run16 is queued for both ubuntu-latest and windows-latest with no
-started steps, as are older listed runs. No usable current Linux runner result
-is established and no duplicate dispatch was made. Progress is concrete;
-release acceptance remains incomplete. Older checkpoint paragraphs below retain
-their original revision scope.
+## Historical evidence access
-**Checkpoint publication — 2026-09-10:** the user requested committing all current changes and pushing `codex/release1-continuation` to both `origin` and `gitea`. This is a work-in-progress snapshot. The interrupted palette-focused compilation failed with E0659 (ambiguous `column!` in `app/architecture_authoring/palette.rs:168`) and E0507 (moving the view out of an Arc in `diagram.rs:624`); an unnecessary-pattern-parentheses warning also remains in `architecture_authoring.rs:346`. The prior 584-test pass does not cover this newer palette revision. No Cargo/rustc process or local subagent was active at the publication preflight. Fix these errors and complete automated/native acceptance before packaging the redesign. Gitea coordination starts at issue #1 and the project Wiki; older evidence below retains its original scope.
-
-**Latest priority checkpoint — Capella consultation and redesign:** the user rejected the large creation buttons and explicitly requested consulting the installed Capella. Native control resumed for that consultation in a disposable Capella 7.1 workspace. Actual palette, F2 rename and activity allocation were exercised; saved XML confirms allocation distinct from ownership. The horizontal creation/connection rows are being replaced with a compact expandable palette and inline naming. This supersedes the Escape hold and toolbar-fix proposal below. New implementation, native acceptance and a fresh user build remain pending. [Observed benchmark, alternatives and acceptance criteria](release-capella-basic-editing-20260907.md). The first-release goal remains active.
-
-**Latest checkpoint:** basic-editing v1 passed full desktop584/584, Windows binaries, format and strict all-target Clippy. Native v1 D8CFAA confirmed direct contextual named Entity creation at100%, and found a22px placement shift caused by the pending toolbar changing canvas height. The user pressed Escape at the attempted Undo; that action did not execute. **Desktop control is paused again.** Process9600 remains the isolated unsaved QA application; do not overwrite its binary or presume a clean close. Code-only placement fix is continuing. [Exact v1 evidence and remaining acceptance](release-basic-editing-reset-20260907.md). This supersedes the resumed-control state below; the first-release goal remains incomplete.
-
-**Superseding priority checkpoint:** the user explicitly said “Resume desktop checks” and rejected the focus on advanced review before basic diagram editing. Isolated native checks resumed and reproduced the global creation catalog, placeholder creation/camera jump and noisy review. Contextual named creation and compatible layer-transition destinations are now being implemented and tested. Their new executable has not yet been accepted. [Current priority and scoped evidence](release-basic-editing-reset-20260907.md). The older Escape hold and next-step priorities below are superseded; historical binary/test results retain their original scope.
-
-Current checkpoint: 2026-09-07. The requested Windows development preview is built and packaged with its runtime, documentation, notices and complete examples. Latest Fit6, two existing scroll/branch regressions, full desktop570/570, workspace bins, formatting and strict workspace/all-target Clippy pass. Corrected server56/56 retains its earlier unchanged-server scope. Packaged DC50 GUI launched and the OA graph Fit overview was observed at1444×932 and1102×932; minimum-height and pending-form native acceptance remain open. The user stopped Computer Use with Escape; do not resume native control without renewed authorization. Native Linux core436passed/2ignored remains verified; corrected Linux server and full Linux GUI build/runtime remain open. The fresh Linux desktop cross-build passed on Rust1.90 in2m20s using the preserved cache; ELF and all37 teaching files are verified. Three later guest boots failed the early IO-APIC timer check before SSH/tests; the exact-kernel workaround was then rejected by automatic approval review before launch. All task VM processes are stopped. **Goal active; release incomplete.** The authoritative objective is `C:\Users\User\.codex\attachments\10b0f701-1743-4de7-9ee1-225920e19649\goal-objective.md`: a usable, feature-complete OA/SA engineering tool for Windows and Linux with graphical/source/REST parity, artifact traceability, offline guidance and independent end-state acceptance.
-
-## Workspace and ownership
-
-- Current branch `codex/release1-continuation`, checkpoint **4b6b5fc1977523e8cf52fdd5503a0a71c87a3f63** commits290files of source6 work and evidence. [Bounded checkpoint audit](release-checkpoint-integration-review-20260906.md) verified65pinned manifest entries and145exact staged/working fixture/archive files; `.gitattributes` protects their measured line endings. Integrated ancestry starts at `c091f80`; [branch audit](release-branch-integration-20260906.md) prevents reimplementing already integrated work. New source7 implementation is authorized but not yet accepted.
-- Historical6BAB native scope (superseded in rendering by BCC1 and Fit by DC50): acceptance binary `.local/native-qa/bin-source7-acceptance/arcadia-studio.exe` SHA256 `6BAB6CDE7D31326F3C3CF7342D9B25FB6ADA032864BBE8104625E5EC9736283B` follows the Windows433/55/561 and strict/build gates below. Reset and ReadStatus each passed exact complete model/source Save/Undo/Redo and reopening with the correct derived Reply allocation30/31. OA caller→First.B passed exact persistence/history/reopen; routing remains open. Process11060 closed cleanly; isolated old6 recovery process11240 also closed after its complete source/JSON/raw-draft pass. Old5 process5244 subsequently passed exact30-source/full-JSON recovery and raw-workspace comparison. Current7 process8224→10420 passed invalid nested-link draft crash recovery; completing the recovered SA X→[R3,Inner]/B link passed exact model/two-source Save/Undo/Redo and saved reopening. Those historical native processes closed; the later BCC1 routing and DC50 Fit evidence is recorded separately below. [Native evidence](release-source7-native-acceptance-20260907.md).
-- Completed bounded90minute Linux gate: one-job Rust1.90,455-file immutable snapshot SHA256 `7104946c89b2be7a1a030fdff47cb4405bacf05357d68cf35d181e860f55c279`, launched07:02:43UTC, deadline08:32:43UTC. Server compilation passed17m10s; unit2/2 and HTTP50/51 passed, with one504-vs-412 test assertion reproduced in an isolated rerun. Its two-second read/PATCH checks passed, but later persisted-state assertions were not reached. This establishes a timing-dependent fixture/deadline mismatch, not a production locking defect. Native core completed436passed/0failed/2ignored across40targets, Rust1.90, compile23m03 and1992seconds total; all455 source pre/post checks passed. The two ignored cases are optional inheritance scaling and explicit strace fault injection (earlier separately scoped injection passes remain historical). All artifacts were copied before sync/poweroff; QEMU6280, launcher5000 and seed816 were absent08:13:56UTC. [Current Linux report](release-source7-linux-gate-20260907.md). Earlier Linux evidence retains its older snapshot scope. WSL remains pending restart; no host restart is authorized.
-- Windows session5735 completed all six gates with exit0: focused prepared-HTTP and cancellation, full server56/56 in68.78s, format10.44s, strict workspace/all-target Clippy99.60s, bins102.22s. Status/logs: `.local/source7-post-routing-gates-status.json`. Distinct GUI BCC1FC6DB68123C9D3A43CE53AA8EF59E33C54080632C104C99ED13270D5333F process8568 passed the scoped corrected routing checks and closed cleanly. [HTTP evaluator correction](release-http-preparation-phase-regression-20260907.md), [routing correction](release-contextual-route-native-fix-20260907.md), [native details](release-source7-native-acceptance-20260907.md). These passes precede the now-tested [Fit/layout correction](release-process-fit-layout-audit-20260907.md).
-- The requested Windows preview is `dist/ArcadiaStudio-preview-source7-20260907-x86_64-pc-windows-gnullvm/arcadia-studio.exe`; its565-file ZIP includes all37 teaching-folder files and has SHA256 `1fac10b30daf2f8e1dac29e9469485e4bea142e77ff5a13e4c9d1e41ff03abb2`. The adjacent provenance records actual source/binary hashes. This is a development profile, not an optimized final release. Latest gates are in `.local/source7-latest-preview-status.json`; full desktop570/570 completed in198.82s test time,200.63s command time, exit0 in `.local/source7-latest-full-desktop-status.json`. Initial Fit4/2 failures exposed real zero-size-slot remounting; corrected constructors pass unchanged expectations. [Fit review](release-process-fit-independent-review-20260907.md), [development summary](../DEVELOPMENT_SUMMARY.md).
-- Computer Use is stopped. Read-only inspection initially found preview9148 and second GUI8044 live; later9148 exited without agent intervention, while8044 remained untouched. No GUI control/closure is authorized for background verification. The cross-compiler lease ended after the successful142.766s monotonic run; no cross processes remain. The Linux worker completed a bounded headless guest experiment and released the lease. Three boots (same configuration twice, then Normal boot priority) reached terminal IO-APIC timer panic before SSH/Cargo; all task VM trees stopped and exact evidence was preserved. Exact matching kernel/microcode extraction passed without changing base/overlay hashes; automatic approval review rejected preparation/launch of the transient no_timer_check experiment as “blocked by policy.” No fourth boot or corrected tests ran. No task VM is live; see [the preserved boot evidence](release-source7-linux-server-corrected-gate-20260907.md). [Cross-build report](release-source7-linux-gui-cross-build-20260907.md).
-
-## Current source build and stopped runtime gate
-
-The guide-only followup compiled successfully with locked Windows workspace bins in12.70s Cargo/12.82s command, exit0. Status `.local/source7-guide-current-build-status.json` records guide SHA256 `80A6797051FB28E767F2AB5DD2F335ABCD9F3E5F3A75C731CDE46D8B90D5C92E`; resulting GUI SHA256 `327FBDF734716A7F1AF2874183D46A531A8F429BA1A478E83A3E7F2EB1D6248B`. Formatting and independent actual-label review pass. This later debug artifact does not replace the user-facing packaged DC50 preview, its archive or earlier native evidence. No full570 rerun is claimed for the text-only followup.
-
-Previous goal turn made progress by confirming full570 tests and handing off the package/summary. This continuation produced the successful Linux GUI build/ELF evidence, validated purpose oracles, corrected/built guide text, and concrete guest-boot evidence. Goal remains active/incomplete. The denied direct-boot action must not be split or retried to bypass automatic review; no new Linux job is running or scheduled.
-
-## Latest Linux build and purpose-oracle evidence
-
-Linux GUI snapshot458files `source7-20260907T102528493108Z` has archive SHA256 `9b10aa8457a5371367f7e24a0b2260b63c19fed69873d98a3972062a744503d7`. Run `20260907T1025486926611Z` passed locked/offline Rust1.90 zigbuild, both source checks and independent ELF/package readback. GUI72,953,296bytes SHA256 `330A3872D48EBF24AD21E6D4FA13CB6098BEBB3CB7FF82A2E082925CCF3DD346`; ELF64PIE, glibc requirements≤2.28, all37 lesson files exact. Actual Linux GUI runtime is still unverified. Subsequent guide-label text is excluded from both published Windows and this frozen Linux preview.
-
-Extended native checker `source7-native-contract-incidence.exe` SHA256 `16994DC437F5CCC5E5F6FBB4212C03449F2389AE4615FA98B2930B3637468EE3` compiled with warnings denied in4.05s during a coordinated short lease. OA Exchange and SA CommonWork self-checks pass: exact contextual assembly, unchanged precedence, wrong direction/work rejection, caller-only exchange coverage and stable source update. Separate43-element/10-relation incidence fixture baseline passes; all original golden hashes and preserved OA-link/Reset accepted cases remain exact. Independent review found no material issue. [Cases and reproducible native steps](fixtures/source7-native/README.md). Native purpose authoring/history remains pending.
-
-## Completed and verified at this checkpoint
-
-| Scope | Evidence and limit |
-|---|---|
-| Historical source6 diagnostics and guide | Semantic filter **108 desktop + 2 core** passed; full desktop **527/527** passed; strict all-target Clippy and GUI/server build passed (`.local/semantic-diagnostics-final-tests.log`, `desktop-diagnostics-final-tests.log`, `diagnostics-final-clippy.log`, `diagnostics-final-build.log`). Subsequent guide **3/3** focused tests, strict Clippy and build passed (`.local/guide-navigation-{tests,clippy,build}.log`). The guide test increases compiled desktop inventory to528; no full528 run or final full-workspace total is claimed. |
-| Current artifact implementation checkpoint | Full desktop **518/518**, corrected fresh artifact picker **6/6**, strict all-target Clippy passed. Logs `.local/source6-artifact-inspector-desktop-tests2.log`, `.local/source6-artifacts-final-tests2.log`, `.local/source6-artifacts-final-clippy3.log`. Four subsequent artifact recovery falsifiers and two core absence oracles passed separately. This predates latest display corrections. |
-| Native artifact MOVE / CREATE / DELETE | All three complete source Apply/Save, one Undo/Save and one Redo/Save oracles passed; **nine saves succeeded first attempt**. MOVE invalid Owner and disabled optional text, and CREATE excluded row/all eight decisions/reserved UUID survived controlled-stop recovery exactly. DELETE removed destination from identified moved-source absence while retaining the source link. [Exact native report](release-artifacts-native-20260906.md), [fixture/generator instructions](fixtures/release1-native/artifacts/README.md). |
-| Source-6 atomic batch review | Included valid and excluded invalid raw rows survived forced-stop recovery; repaired two-source Apply and complete Save/Undo/Redo source comparisons passed. [Native report](release-current-native-verification.md). |
-| Scenario ordered-member variant | At1100×700, Take rejected ambiguous insertion; explicit final order retained the local self-test. Complete Apply/Save, Undo/Save and Redo/Save matched the independent source oracles with only the schema5→6 header token upgrade. All three saves succeeded first attempt. Branch steps/fragment branches and this variant's crash recovery remain separate cases. [Exact native report](release-current-native-verification.md). |
-| Native guide navigation | On current guide binary at1100×700: related Scenarios-bottom→Exchanges opens the new heading; filtering `exchange` retains the current topic's bottom position; `Git` auto-selects Build first and opens its heading. No dirty model. Root's actual native observations close these bounded scroll cases; [implementation/tests](release-semantic-conflict-diagnostics-20260906.md) remain separately scoped. |
-| Communication means | Drawing/create/reuse and legacy reverse full-model/source/history passes are retained. Corrected reverse Apply now opens the explicitly requested OA scenario in Behavior, preserving the original four-object Architecture. Distinct oracle predicts10UUIDs/zero placements; accepted model/all3sources, Undo baseline, Redo and reopen pass. Every Save succeeded first submission. Navigation5focused tests, strict Clippy and bins build pass; compiled desktop535 is not a full535 run. [Exact report](release-communication-native-20260906.md), [implementation](release-behavior-transition-context-20260906.md). |
-| Teaching project and Home hook | Corrected94element/62relation/5view/68trace/30source example passes4expanded direct-rustc tests. Minimum-size Home/guide, raw-source guard, reviewed NameTake and exact source/history/reopen pass. Navigation fix is reverified. Additional data/interface/type drill-down, saved-view comparisons, invalid-view guard and typed Entry action selection pass. Fold/unfold and function rename/Undo/Redo each match complete model/all30source oracles, five first-submission saves; saved reopen retains the Entry target identity. Complete process/scenario/reuse/evidence lesson remains open. [Native report](release-teaching-native-20260906.md). |
-| Child/Incident and source-5 recovery | Two-row attachment recovery and complete Apply/Save/Undo/Redo passed, every save first attempt. Exact old source-5 raw/base/include/path retention, manual repair and two-file Save passed. [Attachments](release-attachments-native-20260906.md), [source-5 native](release-source5-native-recovery-20260906.md), [independent review](release-source5-checkpoint-independent-review-20260906.md). |
-| Dense saved views | Forty-label baseline and selected real relation identities inspected; filter, duplicate/hide, original10/copy9 persistence across restart and exact model oracle passed. Additional coverage/context intersections, empty selections and Names-and-items preview/Apply/Undo observed. Minimum-size overview requires working zoom/pan. Limits remain in [native evidence](release-current-native-verification.md). |
-| Core/source/REST integration | Fresh `--all-targets` core run: **433 passed/1 ignored**,44 target summaries, exit0 in138.72s; fresh server run: **55/55**,4 summaries, exit0 in64.74s. Logs `.local/source7-core-all-targets.log`, `.local/source7-server-all-targets.log`; exits/timings in `.local/source7-current-gates-status.json`. These supersede the earlier combined core count and pre-budget-fix HTTP51 checkpoint. OpenAPI **72fixtures/204schemas/90leaves** and both strict reST renders pass. [Core checkpoint](release-source7-core-checkpoint-20260906.md), [API integration](release-source7-api-integration-20260906.md). |
-| Source7 desktop authoring and recovery | Latest routing3/3 and full desktop **564/564 pass**; [routing checkpoint](release-contextual-route-native-fix-20260907.md) records exact logs. Earlier full desktop **561/561 passed**, zero failed/ignored,263.39s in `.local/source7-desktop-full-tests2.log`. The initial560/1 run exposed canvas scroll-state loss when a new conditional navigation control shifted its widget; a fixed navigation container corrected it, and the unchanged regression passed. Coverage includes qualified5, operation3 (selected Async request fields), semantic source7 facets/omission3, recovery8, legacy source5 13, artifact search2 and focus guard1. Exact old-version enum/cache-path intent rejection and raw text retention are covered. The complete-scope endpoint picker preserves access beyond512; canvas preview remains bounded. Guide now accurately describes attachment/artifact reconciliation and artifact filtering. [Desktop checkpoint](release-source7-desktop-authoring-20260906.md), [recovery](release-source7-recovery-20260906.md). |
-| Windows strict/build checkpoint and native7 | Strict workspace/all-target Clippy passed2m50s; root observed final format exit0/empty log. Three affected semantic tests passed3.46s after equivalent lint edits; native build exit0 recorded in `.local/source7-current-gates-status.json`. These earlier results scope the6BAB binary, not the later routing test binary or pending new strict/build sequence. Reset/ReadStatus full model/all-source Save/Undo/Redo/reopen pass. OA First.B persistence passes but routing acceptance remains open. Old6 Recover preserved the complete30-file bundle/workspace/Ports DTO, exact spaces/Unicode/fingerprint/reserved identity; Cancel/Save as and JSON export passed full independent source/model equality. Original seed unchanged. Old5 complete accepted/raw recovery, exact repair/30-source Save and full-model export pass. Current7 invalid nested draft crash recovery plus completed link Save/Undo/Redo/reopen pass; rendering still uses the older binary. [Native report](release-source7-native-acceptance-20260907.md). |
-| Current Linux source7 and cross-built probe | Frozen native server compilation passed17m10s; unit2/2 and HTTP50/51, with one timing-dependent504-vs-412 assertion reproduced twice. No complete server pass; later targets not claimed. Native core436passed/0failed/2ignored; VM cleanly off08:13:56UTC. Separately, the complete Windows macro/build-script cross-build probe's hash-verified ELF executed successfully on Linux; this is neither a full desktop build nor GUI acceptance. [Native Linux gate](release-source7-linux-gate-20260907.md), [cross-build probe](release-linux-cross-build-probe-20260906.md). |
-| Historical Linux/MSRV core and server | Ubuntu24.04.4/x86_64/glibc2.39/ext4/Rust1.90: core38/38, patched journal4focused/42full and3explicit syscall fault cases pass on recorded snapshots. Actual server build passes34m42s including3m24s pause. Real HTTP modular import, invalid PUT unchanged source/ETag, clean SIGTERM and exact model/source/ETag restart pass on the older28-source lesson snapshot. Full server test command timed out while compiling; zero tests executed. That gate shut down cleanly21:53:56; the separate current gate has the scoped results above. Current desktop/Linux package unverified. [Linux report](release-linux-validation-options.md), [journal contract](release-linux-journal-publication.md). |
-| Packaging foundation | Portable Windows QA staging, dependency/runtime/font/Rust notices, archive readback and clean-PATH smoke passed at earlier binaries. [Package report](release-windows-package-review.md). QA archives are not current final release artifacts. |
-
-Historical full platform/native milestones retain their original revision scope; [complete prior ledger](history/2026-09-06-ledgers-before-consolidation/STATE.md) preserves all evidence links and previous environments. A test or native pass never transfers automatically to a changed executable.
-
-## Active findings and checks
-
-Native artifact review exposed same-row conflict wording and reserved identities shown as deleted. Corrections retain typed actual-slot equality, row-addressed reservations and accepted-identity precedence; independent re-review found no further material defect. The completed108-semantic/527-desktop checks supersede the earlier interrupted run and include reserved-label/partial-creation regressions. Revised artifact wording still needs its own native observation on the current binary. Guide-scroll implementation/tests/build and the bounded native cases above passed. [Diagnostic/guide report](release-semantic-conflict-diagnostics-20260906.md).
-
-**F1** teaching onboarding/review/history has current-binary native evidence; remaining tutorial work is scoped in its report. **F2**, faithful behavior-canvas SVG export, is deferred by root's scope decision: export remains architecture-only and must be described that way; no faithful scenario/process/state SVG acceptance is claimed.
-
-Reverse scenario's first-dependency focus/overlapping architecture placements and teaching NameTake's separate layer/view mismatch are fixed and natively reverified. Latest NameTake4focused tests, strict alltargetClippy28.26s and binsbuild50.50s pass; no full539desktop pass is claimed. [Navigation evidence](release-semantic-review-navigation-20260906.md). Minimum-size process working space is cramped; native lesson evidence records23% overview versus100% scrolling rather than claiming readability at Fit.
-
-Independent scope reconciliation verified **S1** missing contextual assembly into repeated/nested process references and **S2** missing maintained interface-operation identity in parameterless messages. Isolated existing-core probes falsify the proposed workarounds. Root ratified the jointly critiqued [source7 integration decision](release-source7-integration-decision-20260906.md), including deterministic cache-independent admission, whole-link deletion, purpose-specific analysis, exact Call-derived operation/Reply identity and one frozen old5/6 checkpoint migration. These contracts now have implemented core/source/REST/desktop/recovery paths and the current automated gates above; remaining native7 and current Linux acceptance remain incomplete. [Scope reconciliation](release-scope-reconciliation-20260906.md).
-
-Linux ordinary rename's empty-target replacement was independently reproduced and corrected using `renameat2(RENAME_NOREPLACE)` without fallback. Exact errno/no-write assertions now pass under actual syscall injection. This establishes the ext4 publication invariant, not power-loss durability, hostile ancestor protection or model-file compare-and-swap. Windows retains its no-replace API and bounded scanner retries.
-
-## Remaining gates and next actions
-
-1. Fit implementation/review and automated gates are complete, including full570 desktop tests. Finish its native minimum-height/Focus/pending-form checks when native control resumes. Preserve completed BCC1 routing and old5/old6/current7 recovery/source/history oracles. Complete remaining current-binary S1/S2 purpose workflows and teaching cases; operation allocation deletion remains subject to independent channel validation. Native-check revised reserved labels and same-row conflicts. The later guide-only control-label correction passed formatting, independent review and the fresh workspace-binary build; it awaits the next packaged/native release snapshot.
-2. Close uncovered selective-view lifecycle cases; advanced ordered/process/scenario/fragment variants beyond the accepted Scenario case; typed state actions and pending Linux data/hosted/compact workflows. Implemented semantics must not be called missing merely because acceptance is outstanding; [coverage audit](release-current-coverage-20260906.md) is the baseline map, newer scoped reports supersede its old gap claims.
-3. Verify complete current Linux integration suites and desktop/server build/runtime, then supported-size native workflows. The successful core/MSRV pilot is a reusable starting point, not application acceptance.
-4. Preserve current strict Clippy/format/reST/example/source/OpenAPI results and rerun affected checks after changes. Complete optimized/MSRV/final platform gates, actual hosted transport variants and final migration/package checks. Rebuild portable distributions from the accepted exact tree, validate notices/runtime/content/checksums and clean-environment launch before replacing old bundles.
-5. Conduct fresh independent requirements/domain/parity/architecture/runtime/UX/end-state reviews; resolve material findings. Commit/integrate authorized source with reviewable scope; remote publication is not requested. Only complete the goal when its applicable criteria are met.
-
-Meaningful progress continues: native lifecycle and Linux no-replace uncertainty have been resolved with exact oracles. The highest-value work is current-binary validation and closing uncovered engineering workflows, not accumulating repeated historical headings or treating aggregate test counts as release completion.
+The [complete prior STATE](history/2026-09-10-ledgers-before-continuation-refresh/STATE.md)
+retains exact historical binary hashes, counts, logs, process closure, bundle hashes
+and all report links. Links inside the byte-preserved archive retain their original
+`harness/` context; resolve those relative report paths from `harness/`.
+Useful direct entries: [source7 native](release-source7-native-acceptance-20260907.md),
+[teaching](release-teaching-native-20260906.md),
+[artifact native](release-artifacts-native-20260906.md),
+[coverage baseline](release-current-coverage-20260906.md),
+[Linux gate](release-source7-linux-gate-20260907.md),
+[Linux cross-build](release-source7-linux-gui-cross-build-20260907.md),
+[denied boot experiment](release-source7-linux-server-corrected-gate-20260907.md).
diff --git a/harness/TASK_LEDGER.md b/harness/TASK_LEDGER.md
index cf9f6086..c72e4353 100644
--- a/harness/TASK_LEDGER.md
+++ b/harness/TASK_LEDGER.md
@@ -1,102 +1,169 @@
# Task Ledger — first release
-## Current knowledge — 2026-09-10
-
-Given: the renewed objective in `PROJECT_GOAL.md` requires a feature-complete,
-usable OA/SA first release and prioritizes direct behavior/scenario authoring.
-Graphical/source/REST parity, explicit reST semantics, traceability, offline
-guidance, Windows/Linux acceptance and independent review remain required.
-Older scope deferrals must be checked against this request.
-
-Updated given requirements (2026-09-10 goal edit): calculations, quantities and
-units, reusable model libraries, and avoiding long homogeneous selection lists
-are explicit. `PROJECT_GOAL.md` preserves the exact new attachment. A fresh
-independent domain audit maps them to actual behavior before design convergence;
-unexecuted constraints and unit strings alone are not assumed to satisfy them.
-
-Verified: the checkout began clean at `1797516`; Gitea contains one active
-continuation issue and no open PR. Current code includes direct behavior editors,
-so old blanket descriptions of static diagrams need workflow-level reassessment.
-The machine began without the ignored historical toolchain/cache/distributions.
-Local Rust/Cargo 1.98.1 and verified LLVM-MinGW are now installed; dependency
-fetch is pending and no current binary has been built. Native Computer Use is
-available. Isolated Capella activity/lifeline/LOOP creation was saved and checked;
-no Arcadia window is running. Candidate B1/B2/B3 and F1 edits are source-complete,
-formatted and whitespace-clean, but have no fresh compile/test/runtime pass.
-
-Assumption: the selected compact architecture palette remains a promising design,
-pending real authoring evidence. Decision: repair and evaluate it before further
-redesign. Parallel independent audits identify current behavior and semantic gaps
-instead of reimplementing integrated work.
-
-Open questions: which remaining menu-only behavior operations prevent a coherent
-direct workflow; which first-release semantic/parity gaps remain; and how to
-complete current Linux runtime acceptance. Root owns integration and native
-control; worker ownership is disjoint. See `STATE.md` and Gitea #1.
-
-**Superseding user direction:** large horizontal creation buttons were rejected as intrusive and unprofessional. The user explicitly authorized consulting the installed Capella; that consultation has resumed and established actual compact palette, inline rename and explicit activity-allocation behavior. See [Capella 7.1 evidence and selected redesign](release-capella-basic-editing-20260907.md). The current candidate replaces creation/connection rows with one compact expandable palette and an in-place name editor. Native acceptance of the redesigned executable is pending; compactness is not assumed to prove usability. The earlier Escape hold and proposed toolbar-only correction below are superseded.
-
-**Latest evidence:** basic editing full desktop584/584, bins, formatting and strictClippy passed. Native named Entity creation works at100%, but changing pending-toolbar height moves the screen placement22px. User Escape stopped the attempted Undo; native checks are paused again. Code-only toolbar correction proceeds. [Current findings](release-basic-editing-reset-20260907.md). The previous goal turn made concrete progress through implementation, integration gates and a native-discovered defect; this is not a no-progress blocker turn.
-
-**Current steering:** basic diagram authoring and understandable OA/SA transitions come first. The user explicitly reauthorized desktop checks; root resumed the isolated test build and reproduced the reported usability problems. Creation, transition and independent fixture work is underway. No new basic-editing native pass is claimed. See [priority reset](release-basic-editing-reset-20260907.md); older execution priorities and Escape hold are superseded.
-
-Updated 2026-09-07 after source7 Reset/ReadStatus and old6 recovery native passes, OA contextual persistence with rendering open, and reproducible Linux server test deadline evidence. Native Linux core now passed436/0failed/2ignored and its guest shut down cleanly. Old5 full recovery and source7 pending nested-link crash recovery/history/reopen also passed; routing3/3 and full desktop564/564 now pass. Root's private-server/full-server/format/strict-Clippy/bins sequence is running with results pending. [STATE.md](STATE.md) owns current execution/evidence and leases. [PLAN.md](PLAN.md) owns the release plan; [DECISIONS.md](DECISIONS.md) preserves architectural rationale. Full pre-consolidation contents, including superseded requirements interpretations and evidence, remain [archived byte-for-byte](history/2026-09-06-ledgers-before-consolidation/TASK_LEDGER.md).
+Updated 2026-09-10. [STATE.md](STATE.md) owns current execution/evidence;
+[PLAN.md](PLAN.md) owns capability milestones. The entire former Task Ledger is
+[preserved byte-for-byte](history/2026-09-10-ledgers-before-continuation-refresh/TASK_LEDGER.md).
+Its old goal references, aggregate test claims and stopped-session directives retain
+historical scope. This ledger does not add new semantic design decisions.
## Goal and success criteria
-The authoritative user objective is `C:\Users\User\.codex\attachments\10b0f701-1743-4de7-9ee1-225920e19649\goal-objective.md`. Deliver a usable, substantially complete OA/SA engineering tool in Rust/Iced on Windows and Linux; equivalent graphical, strict textual/reST and REST workflows; practical artifact traceability; navigable offline guidance; recoverable drafts; reproducible deployment. Completion requires native engineering task evidence and fresh independent end-state review, not implementation or passing tests alone. Later Arcadia layers remain future scope.
+Deliver the usable, feature-complete OA/SA first release specified in
+[PROJECT_GOAL.md](../PROJECT_GOAL.md). It is the exact copy of the user's revised
+`2f1f0002-e43c-45ba-80a8-b359f05f99d0/goal-objective.md`, SHA256
+`7C1BDBD077E31439501C04DC3613E6BE47A389704DDC3A44EF8BD9AD98EBB513`.
-## Given facts and constraints
+Success requires coherent explicit semantics, practical engineering workflows,
+equivalent graphical/textual/REST behavior, real Windows/Linux runtime evidence,
+recoverable authoring, relevant automated evaluators, precise documentation,
+reproducible packages and fresh independent cross-disciplinary end-state review.
+No known blocker or high-impact finding may remain unresolved. Later Arcadia
+layers remain future scope. Code, a passing slice or an attractive screen alone
+cannot establish completion.
-- User prioritizes Capella domain fidelity, useful containment/nesting and explicit typed ports, while avoiding clutter, persistent action overload and awkward navigation. Study real domain workflows rather than mechanically copying Capella classes.
-- Both-layer transitions and reviewed counterpart reconciliation must be intuitive and precise. Behavior/scenario diagrams must be directly editable and preserve selection/camera/task context.
-- Human/AI/Git-friendly modular source, comments/includes, complete graphical/source/API parity and clear rejection are required. One validated model/history boundary must govern every representation.
-- User permits autonomous reversible engineering, experiments, independent agents and authorized native work. Root is the sole native operator in this collaboration. Do not run concurrent shared Cargo writers. No remote push, usage reset, broad target deletion or host restart is implied by current task authorization.
-- User permits isolated Linux work. WSL remains unavailable pending restart; earlier task-local QEMU core and server gates completed and shut down cleanly; the separate source7 core gate also completed and shut down cleanly, as recorded below. The server built and passed real HTTP/restart checks; its full test command timed out during compilation before any test executed. Historical external worktrees/toolchains are not present on this machine.
+## Given facts
+
+- Rust/Iced desktop and a deployable REST service serve system engineers. OA/SA
+ transitions in both directions must be understandable, explicit and useful.
+- The revised goal explicitly requires constraints/calculations, quantities/units,
+ reusable model libraries, state/behavior modeling and strong standardized
+ specification/API, as well as textual/graphical notation and artifact traceability.
+- Human/AI/Git-friendly source and a concrete unambiguous reST specification must
+ match graphical editing. The source editor needs highlighting/linting; help must
+ be offline, concise and navigable.
+- Direct behavior/scenario diagram authoring and reduced cognitive load are current
+ priorities. Long homogeneous selectors and bloated persistent creation controls
+ are rejected. Study actual Capella's useful domain workflows and nesting without
+ mechanically copying its interaction problems.
+- The user authorizes autonomous bounded engineering, native Capella/Studio
+ evaluation and explicit independent delegation, exclusively with GPT-6 Astra
+ at effort appropriate to the question. Root owns synthesis/integration.
+- Project instructions require Gitea coordination, ownership preservation and
+ durable handoffs. Current root reserves one Cargo/native operator per host.
+ Do not infer restart, usage reset or credential disclosure authorization.
## Verified facts
-- Latest superseding checkpoint: Fit6 and both existing scroll/branch regressions, full desktop570/570, workspace bins, format and strict workspace/all-target Clippy pass. Windows preview565-file ZIP and complete examples are verified; current server56/56 also passed. Native BCC1 OA/SA nested routing and unchanged-source oracles passed, followed by bounded DC50 packaged Fit overview. Earlier pending-build/routing claims below retain their historical scope and are superseded by these exact current results in STATE.md.
+- Current local source is `aa40345` on `codex/behavior-authoring-20260910`, following
+ baseline `1797516`. Local commits cover B1/B2/B3 authoring, nullable history cache
+ (`b6b76ef`), external palette integration (`aed40d7`), cfg(test) lifecycle projection
+ (`eb5161b`), history seed correction (`4ecad78`) and exact-identity native oracle. Latest `aa40345` commits the requirements audit
+ and an unused-import correction prompted by the active compiler.
+- This fresh host has task-local Rust/Cargo 1.98.1 GNU/LLVM and verified LLVM-MinGW.
+ The one-job desktop test no-run build is active, with no local Rust build/test or
+ Studio native pass yet. The history suite has thirteen tests; lifecycle has ten,
+ both awaiting local execution. [Progress and evidence](STATE.md).
+- Strict language HTML regeneration and the source7 OpenAPI checker passed locally:
+ 72 fixtures, 204 schemas and 90 scalar leaves. Actual isolated Capella scenario,
+ activity/lifeline and LOOP creation was saved/inspected; this is reference study,
+ not Studio acceptance. [Native benchmark](release-capella-scenario-benchmark-20260910.md).
+- Core/source7 already implements substantial direct behavior, typed data/ports,
+ contextual chain links, maintained message-operation identity, semantic reviews,
+ artifacts, hosted source envelopes and recovery. Historical absence claims must
+ be checked against code and newer scoped evidence, not copied forward.
+- [Requirements audit](release-requirements-delta-20260910.md): Constraint and other
+ expression strings are descriptive, with no calculation/binding/result evaluator;
+ unit strings persist/inherit without dimensional compatibility or conversion;
+ named types/sequence/scenario reuse and includes exist, but there is no maintained
+ cross-project package/version/instance/update contract. These are explicit gaps.
+- The same audit identifies concrete unbounded Constraint target choices and generic
+ expanded collection editors. Explorer already has hierarchy/search/paging; the
+ problem is targeted, not a claim that all selection is flat.
+- Gitea #79 distinguishes a B3 retarget evaluator setup error from the real
+ same-selected-ID/visible-owner navigation defect. The external scenario owner
+ corrects owner restoration and pending-draft guards. External layout 8/8 passes
+ precede subsequent edits and do not establish current local/native acceptance.
+- Codex-9e8r6t is a separate host with independent Cargo/native leases. It owns
+ scenario workspace/B3/local behavior creation, palette and platform/packaging;
+ current integration is not yet imported. Both hosts report direct push auth
+ unavailable. MCP/anonymous fetch and verified bundle exchange remain usable.
+- Current Linux acceptance is open; this host has no installed WSL distribution.
+ Historical Linux core/server/cross-build results have exact older snapshot scope.
+ The earlier denied guest workaround did not run and must not be bypassed.
-- Branch/patch/tree audit established that direct state/scenario/process, data contracts, hosted editing and earlier semantic review are already integrated; no required production transplant was found. [Branch evidence](release-branch-integration-20260906.md).
-- Capella's function input/output exchange endpoints and component Standard/Flow ports are distinct; explicit allocation connects them. Preserve that domain distinction. [Native benchmark](release-capella-workflow-benchmark.md).
-- Source6 introduced participant CommunicationMean, typed explicit transition endpoint decisions, saved exchange categories/view selectors, completeness warnings and presentation duplication; current source7 retains them. Older audit absence claims are superseded; native acceptance remains scoped by reports.
-- Semantic review includes independent scalar/constructor/keyed/order/Child/Incident/artifact facets and multiple source rows. One frozen accepted base, explicit mappings, unique merged candidate slots and one atomic command preserve raw excluded/rejected drafts. Recovery replans; it never restores an unchecked serialized command. [Batch design](release-semantic-batch-foundation-design.md), [artifact contract](release-semantic-artifacts.md).
-- Native Windows artifact MOVE/CREATE/DELETE passed exact complete Save/Undo/Redo oracles, nine first-attempt saves. MOVE invalid raw optional state and CREATE exclusion/reserved identity survived controlled-stop recovery exactly. Destination-only retention is automated evidence, not separately native-applied in that run. [Native report](release-artifacts-native-20260906.md).
-- Native batch, Child/Incident and source-5 checkpoint workflows have scoped exact source/recovery/history evidence. Dense views have persistence/duplicate/hide and bounded readability evidence. Current counts, binaries and remaining cases are in STATE.md; these are not universal usability claims.
-- Native communication means drawing, explicit create/reuse ports and reverse Call/Reply pass independent complete model/source and exact Undo/Redo checks. The reverse navigation/overlapping-placement defect is fixed and reverified against a distinct oracle:10newUUIDs,zero implicit placements, correct Behavior owner and preserved Architecture. [Exact evidence](release-communication-native-20260906.md).
-- Windows semantic108desktop+2core/full527desktop passes remain scoped. Subsequent guide/home and behavior navigation slices passed focused tests/strict Clippy/build; semantic navigation is also natively reverified. Fresh source7 core all-targets433passed/1ignored across44 summaries and server55/55 across4 summaries have recorded exit0. Full desktop561/561 passed263.39s after a real scroll-state regression was fixed with a stable navigation Column; the unchanged test passed. Strict workspace/all-target Clippy and root-observed final format exit0 pass;3 affected semantic tests passed after equivalent lint edits. Subsequent routing3/3 and full desktop564/564 pass; new standalone build and strict/server results remain pending. Logs and exact scope are in STATE.md; do not add affected reruns to full-suite totals.
-- Native Scenario ordered review passed ambiguity rejection, explicit retained-local final order and complete Save/Undo/Redo source oracles at1100×700. Current guide binary passes related-topic heading reset, same-topic search scroll retention and Git auto-selected-topic reset without dirtying the model. Branch/fragment order variants and revised artifact diagnostic rendering remain separate gates. [Native ordered evidence](release-current-native-verification.md), [guide/diagnostic implementation](release-semantic-conflict-diagnostics-20260906.md).
-- Windows user-local GNU/LLVM builds and isolated clean-PATH native launch work. Actual Ubuntu24.04.4/Rust1.90 core/journal tests pass on hashed snapshots. Actual Linux server build and real HTTP modular import/invalidPUT/cleanrestart exact source/model/ETag pass on the older28-source lesson snapshot. Its full server test command timed out during compilation with zero tests executed, then that gate shut down cleanly. The current frozen source7 gate compiled the server in17m10s and passed unit2/2 plus HTTP50/51; the sole504-vs-412 timing assertion failed again alone. Its final persisted-state checks were not reached. Native core436passed/0failed/2ignored and the VM shut down cleanly08:13:56UTC; full desktop/Linux package remain unverified. [Linux evidence](release-linux-validation-options.md).
-- Corrected30-source/37-file teaching project passes minimum-size Home/guide/dirty-source guard, located initial warning, reviewed NameTake and complete exact source/Undo/Redo/reopen checks. The layer/canvas mismatch is corrected and natively reverified. Data/interface/type navigation, saved-view distinctions, invalid-view draft guard and typed state Entry fold/rename/history/reopen pass. The process workspace is cramped at minimum size and artifact navigation required excessive scrolling; targeted source7 UI improvements await native observation. [Native evidence](release-teaching-native-20260906.md).
-- Scope reconciliation established caller-context links into repeated/nested reusable chains (**S1**) and maintained interface-operation selection for parameterless messages (**S2**) as real missing semantics. Their ratified source7 implementation now has fresh full core/server/desktop gates, OpenAPI72 fixtures and strict reST evidence. Old5/6 compatibility and current7 raw recovery tests pass. Root's native Reset and ReadStatus cases passed complete Save/Undo/Redo oracles and reopened with explicit derived allocation30/31. OA First.B persistence/history/reopen passed but rendering is open; old6 recovery passed full30-source/model JSON and exact raw workspace/Ports DTO comparison. Old5 complete raw/accepted recovery, exact repaired30-source save/full-model export pass. Current7 invalid nested draft crash recovery and completed link Save/Undo/Redo/reopen also pass; corrected rendering and remaining native7/Linux workflows remain open. [Native report](release-source7-native-acceptance-20260907.md). [Scope reconciliation](release-scope-reconciliation-20260906.md), [integration decision](release-source7-integration-decision-20260906.md).
+## Assumptions
-## Decisions and candidate designs
+- Compact contextual tools and an adequately sized scenario workspace can reduce
+ friction; only actual engineering tasks at minimum/wide sizes can validate this.
+- A bounded pure analysis model and pinned reusable packages may satisfy the new
+ requirements without full language/runtime adoption. This is a candidate, not a
+ selected architecture or a permission to treat metadata as semantics.
+- Existing shared core/source/history boundaries remain suitable for additions;
+ foundational examples and independent criticism must test that assumption.
-Retain three Rust crates, one typed Project and validated atomic Command. Keep semantic ownership, reference/allocation/order relationships and diagram placement separate. Forms remain a closed typed adapter, not a second metamodel. Persistent additions require model validation, references/deletion, transition, parser/updater, API/schema and reachable graphical editing parity.
+## Open questions
-Semantic reconciliation uses finite typed facets and separate member/presence/order decisions. Compare actual destination slots before constructing the core's complete candidate; two rows never become sequential Apply. Missing identities are not inferred by names. Raw constructor/numeric/keyed/optional drafts survive rejection, exclusion and checkpoint recovery. Artifact Presence means attachment to the reviewed owner; an identified source moved away can delete the destination, whereas a source key of None preserves destination-only membership. No invented tombstone or silent seed acknowledgment.
+- What precise owned-value, calculation, constraint-result, quantity-kind, unit and
+ numerical contracts meet mass/power/latency and affine/dimensional edge cases?
+- Which library definition/instance/pinning/binding/update design preserves stable
+ identities, local engineering edits and offline reproducibility across projects?
+- What migration and OA↔SA treatment follows from those contracts without guessing
+ executable meaning from old strings or silently changing old models?
+- Do current real gestures, exact caller return, nested scopes and lifecycle markers
+ remain usable and correct under minimum sizing, scrolling, zoom and dirty drafts?
+- Which uncovered teaching, traceability, transition, saved-view, state/data, hosted
+ and platform cases remain after current-binary acceptance? What supported Linux
+ environment can complete the remaining actual runtime gate?
+- Does "standardized" require any external conformance beyond the project's precise
+ interoperable spec/API? Full OMG conformance is not inferred merely from comparison
+ to SysML v2; explicit claims require a mapped and verified contract.
-Source updates preserve lexical ownership/comments/includes and exact history. Checkpoint source5/6→7 upgrade admits only the established token/typed migration contract, validates old base fingerprints and resets transient approvals; it does not silently rebase authored drafts. Hosted saves remain explicit and conditional on the resource/ETag; credentials stay in memory.
+## Candidate hypotheses / designs
-For Linux journal publication, ordinary rename lost the empty-target ownership invariant in a real ext4 probe. Selected `renameat2(RENAME_NOREPLACE)` through existing locked libc with no fallback; syscall errors fail before model writes. Alternative overwrite fallback was rejected by that falsifier. Windows native no-replace/retry behavior is retained; other OS behavior is explicit. [Contract, alternatives and measured limits](release-linux-journal-publication.md).
+The [requirements delta](release-requirements-delta-20260910.md) compares metadata/
+snippets, bounded pure engineering analysis with maintained pinned libraries,
+external evaluators, and full standards adoption. Metadata alone fails the explicit
+new semantics; external tools alone still need binding/result contracts. The report
+recommends the bounded route for root synthesis, not as a ratified decision.
-Root selected **F1**, a complete source-6 teaching-project example. Corrected model/replay tests and bounded native onboarding/review/history pass; the full process/scenario/state/data/evidence lesson remains open. **F2**, faithful behavior-canvas SVG export, is deferred: current export scope is architecture-only, not a claim of faithful scenario/process/state export. Record this as an explicit scope decision rather than an accepted behavior-export result.
+Independent maximum-reasoning tracks now compare owned engineering properties
+versus analysis contexts; calculation definitions/applications; numeric/unit rules;
+and copy, linked-definition, live-instance or pinned-definition/editable-instance
+library architectures. Require concrete falsifiers, source/API migration effects,
+identity/history/recovery behavior and independent critique before convergence.
-Behavior-root transition Apply should focus the explicitly requested counterpart and its nearest behavior owner. Without an explicit destination diagram it must preserve unrelated architecture presentation. Ordinary architecture transitions retain their existing path; explicit destination remains honored. This decision follows the independently reproduced reverse scenario placement/navigation failure, not a semantic change to the core transition.
+## Decisions retained
-## Assumptions and open questions
-
-- Nested source and contextual editors should reduce authoring effort; additional shorthand/grouping must justify grammar or interaction complexity through real tasks. Dense-view path overlap is not itself a failure when every actual identity remains accessible; arbitrary crossing-free layout is not promised.
-- Remaining native variants must establish useful engineering workflows: CM transitions, advanced OA process/SA chain/scenario reuse/fragment/order, typed state actions, compact/Linux/hosted cases, and uncovered saved-view lifecycle combinations. Do not expand or shrink required OA/SA scope merely to fit implementation convenience.
-- Latest diagnostic/reserved-label corrections have independent review and completed semantic/full-desktop/Clippy/build checks; their revised artifact rendering still needs native verification. The historical source6 process3012 closed cleanly. The6BAB source7 native build passed the scoped cases above;11060 and old6 recovery11240 closed cleanly. Old5 recovery/repair/save/model and current7 nested draft recovery/history/reopen pass on that binary. Later routing/hit-priority changes passed3focused/full564 desktop tests; root is running the server/format/strict/build sequence with results pending, and no corrected-render native acceptance is transferred. Exact binary scope is in STATE.md.
-- Historical Linux evidence includes x86_64/glibc2.39/ext4 core/MSRV and server/HTTP runtime. Current455-file snapshot SHA256 `7104946c89b2be7a1a030fdff47cb4405bacf05357d68cf35d181e860f55c279` is frozen; core completed436passes/2ignored before clean VM shutdown08:13:56UTC; its original08:32:43UTC deadline was not reached. The hash-verified cross-built probe executed successfully on Linux, including its Windows macro/build-script build path, but no complete Linux GUI has been built. Syscall failure tests do not establish power-loss durability, network-filesystem behavior or cross-process model CAS. [Current Linux gate](release-source7-linux-gate-20260907.md).
-- Earlier Windows QA packages include runtime/notices and verified archive contents; final accepted-tree optimized distributions and exact provenance/checksums remain a release gate. [Packaging evidence](release-windows-package-review.md).
+- Keep three Rust crates and one authoritative typed Project with validated atomic
+ Commands. Separate semantic identity/ownership, allocation/reference/order and
+ diagram geometry. Forms are closed adapters, not a second metamodel. New persistent
+ semantics require core/source/UI/API/reference/transition parity. [Decisions](DECISIONS.md).
+- Preserve authored source ownership/comments/includes, exact identity and accepted
+ model/source history. Source writeback must succeed before adoption. Raw invalid,
+ excluded or rejected drafts survive; navigation/tools must not implicitly accept
+ another editor. Recovery validates/replans rather than trusting serialized commands.
+- Semantic reconciliation uses finite typed facets, frozen baselines, explicit
+ mappings, actual destination slots and a single atomic candidate. Do not infer
+ identity by name or apply reviewed rows as sequential independent edits.
+ [Foundation](release-semantic-batch-foundation-design.md),
+ [artifact contract](release-semantic-artifacts.md).
+- Layer transitions are explicit engineering decisions, not automatic propagation.
+ Behavior-root navigation must establish the requested owner while preserving
+ unrelated architecture presentation. Ports/allocation and contextual occurrences
+ retain their distinct domain meaning. [Source7 contract](release-source7-integration-decision-20260906.md).
+- Hosted revision/source envelopes and ETag checks remain atomic; credentials stay
+ out of persisted models and repository. Linux journal publication retains the
+ selected no-replace invariant, with measured limits, not an overwrite fallback.
+ [Journal decision](release-linux-journal-publication.md).
+- Repair/evaluate the compact palette informed by native Capella evidence; do not
+ return to the rejected horizontal toolbar. Lifecycle projection remains isolated
+ under cfg(test) until coordinated Scene integration and actual acceptance.
+- Preserve historical scope decisions as history. In particular, architecture-only
+ SVG is implemented and behavior SVG was deferred; that is not proof of satisfying
+ the revised release objective. Reassess older exclusions against current scope.
## Current strategy
-The requested Windows preview and whole-run development summary are delivered; the summary is being refreshed with the confirmed package, full570/570 desktop result and final Fit/strict/build gates. Preview DC50 has bounded native launch/overview evidence, not complete minimum-height/pending-form acceptance. Computer Use stopped on user Escape, so leave live GUI processes untouched and advance independent verification.
+Finish the active local build and exact regression/native loop while separate
+agents investigate uncertain foundations. Integrate only coherent, verified
+external checkpoints respecting ownership. Use independently constructed complete
+model/source oracles and exact snapshot comparison for identity/history. Ratify
+analysis/library contracts through alternatives and falsifying examples, then
+implement shared semantics and ergonomic authoring together. Close current
+cross-platform/service/package and independent end-state gates before handoff.
-Preserve the successful immutable458-file Linux GUI cross-build with the Fit fix, exact ELF/package evidence and released compiler lease. Three subsequent guest boots reached terminal IO-APIC timer panic before any tests; exact-kernel extraction succeeded but automatic approval review denied the proposed transient workaround. All task VMs are stopped. Preserve the denied action and evidence without bypassing review. Its snapshot/run/hashes and actual process status belong in STATE.md and the cross-build report. Preserve the original interrupted runs, cache, native core436/2ignored and failed Linux timed-server fixture. The replacement private HTTP preparation test and full server56 pass on Windows; Linux execution remains required.
-
-Guide steps now name Tools/Selection details/Restore panels and the exact Reply label; formatting, independent label review and a fresh Windows workspace-binary build pass. The user-facing preview retains its original tested bytes. Independent full-model/source purpose oracles compiled and passed both self-checks and preserved baseline cases; complete their native authoring/history steps when control resumes. Complete current native OA/SA teaching, artifact, saved-view, process/scenario and hosted cases once control is available. Retain caller-specific budgets, actual port proof, independent channel validation and exact old5/6 recovery bytes. Final optimized packages, current platform gates and fresh independent end-state review remain open; architecture-only SVG and passing slices do not redefine completion.
+The [archived Task Ledger](history/2026-09-10-ledgers-before-continuation-refresh/TASK_LEDGER.md)
+and [archived STATE](history/2026-09-10-ledgers-before-continuation-refresh/STATE.md)
+preserve all older claims, decisions and evidence links exactly. Relative report
+links inside those byte-preserved files resolve from their original `harness/`
+context. Their historical test totals and process holds are not live status.
diff --git a/harness/history/2026-09-10-ledgers-before-continuation-refresh/PLAN.md b/harness/history/2026-09-10-ledgers-before-continuation-refresh/PLAN.md
new file mode 100644
index 00000000..94e1e1c1
--- /dev/null
+++ b/harness/history/2026-09-10-ledgers-before-continuation-refresh/PLAN.md
@@ -0,0 +1,121 @@
+# First release execution plan
+
+## Active execution — 2026-09-10
+
+1. Restore a reproducible local toolchain; repair the interrupted compact palette;
+ verify focused history/draft/placement regressions and the actual native workflow.
+2. Independently reassess current behavior/scenario ergonomics and OA/SA semantic,
+ source/API and documentation completeness. Resolve verified high-impact gaps
+ with bounded changes and cross-representation acceptance criteria.
+ The updated goal explicitly requires calculations, quantities/units and
+ reusable model libraries. Audit actual semantics, compare coherent alternatives
+ and establish observable acceptance before implementing foundational extensions.
+ Correct concrete unbounded selectors without replacing existing useful search.
+3. Complete realistic graphical OA/SA teaching, transition, behavior, recovery and
+ hosted workflows. Exercise minimum and wide layouts, rejected edits and history.
+4. Run current Windows/Linux automated and native acceptance, fresh independent
+ end-state reviews, optimized builds and package verification. Publish accurate
+ build/run evidence and handoff only when the original release criteria hold.
+
+This ordering supersedes historical stopped-session next actions below. Preserve
+their scoped evidence; no test or runtime result transfers to a new binary without
+verification. Root holds the serialized build/native lease; Gitea #1 tracks workers.
+
+**Latest refinement:** consult actual installed Capella 7.1, replace the user-rejected horizontal creation/connection rows with a compact contextual palette and inline authoring, independently critique interaction risks, then build and verify the real workflow at wide/minimum sizes. [Benchmark and concrete acceptance](release-capella-basic-editing-20260907.md). Do not ship the rejected toolbar as the usability correction.
+
+**Current priority (7 September):** the user's usability correction takes precedence over the advanced feature sequence below. Establish contextual named diagram creation, existing-element placement, basic editing and clear layer transitions, then verify them natively. Desktop checks were explicitly reauthorized. See [priority reset and acceptance](release-basic-editing-reset-20260907.md).
+
+## Outcome and baseline
+
+Deliver the first release of Arcadia Studio for system engineers: complete Operational Analysis and System Analysis/System Architecture workflows, equivalent graphical and textual models, clear artifact traceability and a deployable/programmatically accessible Rust service. Logical/Physical Architecture and later Arcadia layers are future releases, as the new objective explicitly states.
+
+The authoritative objective is attachment `10b0f701-1743-4de7-9ee1-225920e19649/goal-objective.md`. It retains first-release scope and strengthens domain coherence, alternative evaluation and target-user task evidence. TASK_LEDGER.md separates given/verified facts, assumptions and open candidates; STATE.md records progress. The accepted 0.2.0 prototype is a historical baseline, not proof of release completeness. Its old D:/CodexBuilds archives are unavailable on this machine; existing dist archives must not be replaced until new acceptance passes.
+
+The user's cross-branch reminder led to an ancestry/patch/tree audit. Continue from integrated `c091f80` on `codex/release1-continuation`; direct state/scenario/process, data, hosted and semantic review work already exists here. `release-branch-integration-20260906.md` records the audit. `release-current-coverage-20260906.md` is the current measurable acceptance map and supersedes stale absence claims in older narratives below.
+
+## Existing architecture and retained decisions
+
+Retain three Rust crates and one typed Project/validated atomic Command boundary. oa-core owns semantics, references, history, transitions, source and persistence; oa-desktop owns Iced interaction/drafts/projections; oa-server owns REST/ETag/current-model storage. Keep identity, semantic containment, allocation and per-diagram placement separate. No runtime plugin framework or later-layer skeleton.
+
+The current forms are a closed typed-record adapter, not a second metamodel. Expand their real creation/reference/variant controls with every semantic addition. Core choices must drive graphical choices. New persistent fields participate in text round trips, identity/removal visitors, transitions, API schemas and graphical authoring tests.
+
+## Milestones and measurable acceptance
+
+Nested source ownership and persistent REST bundles are implemented and verified: Windows minimum-size source-to-graphics, nested reparent/source/Undo/Redo/Save/reopen, recovery, rebuilt Linux source/history/Save/reopen, independent review and both platform suites passed. Preserve the existing grammar/updater and separate allocation/order semantics.
+
+Explorer/port discovery, custom routing, direct palette connections, four-side port
+anchors, contextual menus and source folders have scoped Windows/Linux acceptance.
+See release-explorer-usability.md, release-routing-verification.md,
+release-routing-linux.md and release-port-drawing.md for exact evidence and limits.
+
+Direct state-machine editing is accepted through316dc55: typed persisted views,
+explicit-owner creation, movement, direct transitions/reconnection, typed detail
+forms, folding, placement and correct camera/tool lifetime. Windows default/minimum
+and Linux native acceptance plus independent reviews are recorded in
+release-state-editing.md. Direct scenario/process gestures remain required.
+
+R6 data contracts and direct scenario/process gestures are integrated; native
+acceptance continues on both platforms. Typed R3 core/source5/REST and scalar review
+milestones1–2 are integrated and pass full platform checks. Close independent scalar
+fixes and native review, then finish constructor/keyed/order/reset/artifact and
+multi-source graphical authoring using the existing shared core planner. Source5
+migration retains lexical ownership/comments; no flat source rewrite is allowed.
+See release-data-contract.md, release-semantic-integration.md and release-semantic-gui.md.
+
+Continue source authoring improvements only through the production parser/updater.
+The23-test experiment is evidence, not architecture: independent probes exposed
+comment misattachment and unsupported edits. Additional shorthand must preserve
+complete edit/delete behavior and exact source history, and justify itself in real
+human authoring tasks. Shared state placements are now validated; scenario/chain
+interaction must preserve its own occurrence and ordering semantics.
+
+1. **Release coverage audit and interfaces.** Compare actual core/UI/API with official Capella OA/SA semantics and the new objective. Freeze a coverage table and concrete variant/reference/transition contracts before editing shared interfaces. Distinguish unsupported later layers from missing OA/SA workflows; V2 exclusions are reconsidered. Native audit exercises onboarding/help/authoring at supported sizes. Independent audits have separate file ownership.
+2. **Complete engineering semantics.** Fill verified gaps in capability relationships and mission involvement; operational process/functional-chain function/control topology; scenario/state/data constructs needed by OA/SA engineers. Add typed semantic-content drift and reviewed existing-counterpart reconciliation, including reference mapping and explicit dependency/ownership decisions. Every addition has a usable graphical authoring workflow, equivalent source/API representation, meaningful reject/undo/delete/reidentify/transition tests and a worked example. Exact additions and invariants are recorded in the coverage/interface artifact from milestone1, avoiding speculative LA/PA concepts.
+3. **Approachable authoring and help.** Replace static help with searchable topic navigation, concise concepts, numbered OA→SA tutorials, related topics and direct workspace actions. Add a focused starting workspace, recent models and practical creation guidance. Improve source authoring with asynchronous versioned lint, semantic warnings, diagnostic navigation and find/go-to tools. Make selection/navigation and hidden/placed model distinctions clear. Add recoverable local checkpoints for unsaved accepted models and drafts; preserve explicit file-save/conflict behavior.
+4. **Connected automation workflow.** Keep headless deployment and shared core REST contract. Add direct optional desktop open/publish/save of hosted models using explicit connection details and conditional ETags, preserving drafts on failure/conflict and keeping credentials in memory. Verify actual desktop→API→desktop edits and artifact integration locally. Update OpenAPI, deployment and source contracts for the expanded release semantics.
+5. **Release integration and polish.** Guided examples cover OA goals/roles/process alternatives, system missions/capabilities/boundary contracts, chains/scenarios/states and evidence. Review default/minimum/wide layouts, practical keyboard paths, empty/loading/error/recovery states and performance on representative models. Build/run the actual native app after each meaningful UI milestone, fix from evidence, then reverify.
+6. **Independent final acceptance and packaging.** Fresh requirements, correctness, native UX and simplicity reviews in parallel; fix all material findings and rerun affected checks. Full Windows/Linux locked tests, formatting, strict Clippy and optimized builds. Operate primary GUI workflows and actual service transport. Verify current docs/reST rendering, examples/migration, reproducible source workflow, package binary equality/archive contents and checksums. Only then replace 0.2.0 bundles and mark this goal complete.
+
+Dependencies: milestone1 freezes shared interfaces for2; guide/lifecycle work in3 can proceed independently once UI ownership is partitioned. Structural counterpart work depends on2. Hosted client4 depends on final core/API types but transport/lifecycle can be developed independently. Milestones5–6 integrate all preceding work.
+
+## UI and state flow
+
+Keep task navigation, engineering perspective and context-sensitive properties. A Home/start workspace offers projects, recovery and tutorials without obscuring active modeling. Help uses local Rust-owned content and search/category navigation; workspace links pass explicit events and respect draft/modal guards.
+
+Text edits update a snapshot/version; parsing/semantic lint runs on a blocking worker and only the latest result is adopted. Syntax errors and semantic warnings have distinct severity/location and navigate to source. Applying text remains one atomic model transaction. Invalid text and property input are recoverable drafts.
+
+Recovery writes separate per-session checkpoints in user-local application data, never silently overwrites a model file, and offers explicit restore. Recent paths are convenience metadata. Hosted models use a separate connection/resource/revision/ETag handle; ordinary core editing and Undo stay local until explicit conditional Save. Network/file failures retain the same draft. Tokens are not persisted or included in diagnostics.
+
+## Failure and verification strategy
+
+Cover wrong-layer/reference/kind input, ownership/reference cycles, duplicate or ambiguous counterpart matches, stale plans/ETags, typed semantic drift, sequence control topology and repeated occurrences, invalid source/property drafts, external file edits, failed storage/network operations, oversized models and unavailable graphics. Keep incomplete engineering work editable through warnings while rejecting structural corruption.
+
+Tests must cover user-visible meaning and cross-representation invariants, not just serialize the same value twice. Use actual HTTP and native CUA for end-to-end behaviors. Independent review targets current artifacts and the original release objective, not only implementation summaries. Record unsupported/unavailable environments without turning missing required functionality into a limitation.
+
+## Definition of done
+
+Every concrete requirement in the new objective and release coverage table has authoritative current evidence. Primary OA/SA engineering workflows work graphically, textually and through the API; help is navigable and useful; native Windows/Linux UI is intentionally designed and exercised; no blocking/high-impact defect remains; strict checks pass; independent findings are closed; durable docs accurately reflect the release; another engineer can build/run/test and use the supplied packages. V2 completion alone does not satisfy this goal.
+
+## Historical milestone progress — hosted integration
+
+Milestone4 desktop adapter is implemented on the existing Document/REST boundary.
+Windows native publish, graphical/text save, rejected invalid source, API artifact
+reload and stale-revision recovery are demonstrated. Offline/retry and exact local-copy/catalog Open native acceptance passed on Windows.
+Destination and catalog defects found afterward were fixed/reviewed/integrated; remaining platform/native cases retain their report scope. Constructor/keyed/order/Child/Incident/artifact and multi-source review, Home/recents and checkpoint recovery are now implemented. Their scoped native and automated results are in STATE.md; this older milestone is not a current absence list.
+
+## Current execution refinement — 2026-09-07
+
+First-release scope above is unchanged. The user-requested Windows development preview is packaged with565files, including all37 teaching-folder files, and is linked from `DEVELOPMENT_SUMMARY.md`. Latest Fit6 and two existing scroll/branch regressions pass; full desktop570/570, workspace bins, formatting and strict workspace/all-target Clippy pass. Corrected full server56/56 retains its unchanged-server scope. Exact build status and provenance are recorded in STATE.md. This is not an optimized final release.
+
+Native6BAB operation/recovery/history passes and BCC1 corrected OA/SA nested routing passes retain their separate binary scopes. The latest DC50 preview launched with a clean PATH, and its four-card OA Fit overview was visible at1444×932 and1102×932. Minimum-height, pending-form and remaining process-purpose acceptance are still open. Computer Use was stopped by the user's Escape; leave both live GUI processes untouched and continue independent work.
+
+Native Linux core436passed/2ignored is complete on its frozen455-file snapshot. The original server compile passed, unit2/2 and HTTP50/51 passed, and the single504-versus412 timing fixture failed twice. Its private preparation-gate replacement passes on Windows but still requires Linux execution. Preserve the original failures and exact final-state assertions; do not alter the production30-second deadline to make the fixture pass.
+
+The fresh Linux GUI cross-build passed on immutable458-file snapshot `source7-20260907T102528493108Z`, including the Fit constructor fix. Run `20260907T1025486926611Z` completed in2m20s Cargo time with pre/post checks, ELF requirements and all37 lesson files verified; the compiler lease is released. The subsequent headless guest failed its early IO-APIC timer check on three bounded boots, before SSH/tests. Exact matching boot assets were extracted, but automatic approval review rejected the proposed transient boot workaround before launch. All task VMs are stopped; no application failure or current Linux server pass is inferred. Latest guide-label changes and the validated Exchange/CommonWork native oracles follow the frozen binary snapshot.
+
+Next actions:
+
+1. Retain all three guest boot records and exact-kernel proof; the proposed direct-boot action was denied by automatic approval review and must not be retried or split to bypass it. Resolve the runtime gate through an approved route or changed environment. Verify actual readiness and remaining monotonic budget before running the corrected Linux server gate. Do not restart on observation timeout alone.
+2. Preserve the reviewed guide corrections and validated expected-model/source Exchange/CommonWork oracles; the updated guide now builds on Windows and belongs in the next packaged/native snapshot. Complete native purpose authoring/history once control is available.
+3. Once native control is authorized again, complete current-layout minimum-height/Focus/pending-form checks, remaining engineering/hosted walkthroughs and actual Linux GUI acceptance using exact model/source/history evidence.
+4. Execute the corrected Linux server suite; complete final Windows/Linux optimized builds, package provenance, documentation/contracts, and fresh independent cross-disciplinary end-state review. Successful slices do not complete the goal.
diff --git a/harness/history/2026-09-10-ledgers-before-continuation-refresh/STATE.md b/harness/history/2026-09-10-ledgers-before-continuation-refresh/STATE.md
new file mode 100644
index 00000000..13b5a9c3
--- /dev/null
+++ b/harness/history/2026-09-10-ledgers-before-continuation-refresh/STATE.md
@@ -0,0 +1,247 @@
+# Progress Ledger — first release
+
+## Active continuation — 2026-09-10
+
+Latest source checkpoint is `8547b85` on `codex/behavior-authoring-20260910`.
+The cold one-job desktop test build has reached `oa-desktop`; it has not yet
+completed. Root retains the only local Cargo/native lease. Exact native scenario
+fixture/checker preparation is committed, including whole-snapshot identity
+comparison for Undo/Redo; compilation and native use remain pending.
+
+The independent revised-requirements audit is
+`release-requirements-delta-20260910.md`. Current constraint expressions and unit
+labels are descriptive, and source includes are not maintained cross-project
+libraries. These remain explicit first-release gaps. Separate maximum-reasoning
+analysis/quantity and library design tracks are comparing precise contracts and
+falsifiers before production changes; their recommendations are not decisions.
+
+External integrated tests exposed actual defects, superseding the earlier
+source-only B3 review below. Root corrected typed-history `NotApplicable`
+constructor seeding in `4ecad78`; thirteen focused history tests await execution
+here. Gitea #79 distinguishes a retarget test setup error from a real same-ID
+selection/visible-owner navigation defect. The external scenario owner is fixing
+Back/OpenUse owner restoration, mounted viewport coverage and pending-draft
+guards alongside their workspace layout. No overlapping scenario edits here.
+
+Both hosts now report unavailable direct Git push authentication. Anonymous
+fetch and authenticated Gitea MCP remain usable; exact verified Git bundles are
+exchanged through the separate transfer branch. No credentials are copied into
+the repository or conversation. External checkpoints and evidence remain scoped
+to their source/binary, and their current integration is not yet imported here.
+
+The updated user goal from attachment `2f1f0002-e43c-45ba-80a8-b359f05f99d0`
+was read in full and copied exactly to `PROJECT_GOAL.md` on 2026-09-10. It now
+explicitly requires calculations, quantities/units and reusable model libraries,
+and rejects long homogeneous choice lists. Root is integrating
+the first release, prioritizing direct diagram authoring and behavior/scenario
+usability. The checkout began clean at `1797516` on `codex/release1-continuation`.
+Gitea issue #1 and Wiki Home were read; ownership and progress are coordinated there.
+
+- Root owns integration, serialized builds, native Computer Use and these ledgers.
+ `palette_repair` owns architecture authoring and `diagram.rs`; `behavior_audit`
+ and `release_audit` independently inspect ergonomics and release coverage.
+- The recorded palette compilation errors have local corrections pending a fresh
+ build. Earlier 584-test evidence does not cover those corrections.
+- This checkout began without the historical `.local` toolchain/cache/packages.
+ Project-local Rust/Cargo 1.98.1 GNU/LLVM and verified LLVM-MinGW 20260908
+ (clang 23.1.1) are installed. Windows desktop dependencies are compiling.
+ Host: four logical processors, 8 GiB RAM; builds remain serialized.
+- Computer Use initialized successfully. The renewed request authorizes native
+ evaluation; old session Escape holds and process IDs are historical. No Arcadia
+ or Capella window is currently running.
+- WSL lists no installed distribution. Linux runtime/build acceptance remains an
+ open gate; no historical denied guest workaround is being retried.
+
+**Current branch is `codex/behavior-authoring-20260910`.** A distinct Gitea user
+`Codex-9e8r6t` is running another root/team on a separate environment with the old
+build cache. Our identity is **Codex-956atf**. Gitea #25 and #38 acknowledge the
+scope split: their palette/platform/package and subsequent scenario layout work;
+our process/scenario adapters and typed-history work. We will publish a clearly
+untested coherent snapshot on this distinct branch for their warm-cache checks.
+No shared branch-tip push or merge has occurred here.
+Their reports/results must not be claimed as local validation or overwritten.
+
+Implemented locally, awaiting compilation:
+
+- Palette compile repair, retained inline name after rejected stale Submit,
+ nonfinite click rejection and rename/allocation/history regressions.
+- One interactive process renderer with presentation-only inferred ordered
+ paths; first editing batch preserves adjacency atomically. Independent source
+ review found no confirmed defect. Root added 3+ step/before-first insertion
+ and incompatible imported conversion tests after the reviewer hit model capacity.
+- Scenario Action/State/Reuse/empty Fragment tools capture clicked operand/slot
+ and optional lifeline. Pointer-to-App, recovery, stale-event and history tests
+ are written. A bounded exact caller/use return path and scoped mounted-scroll
+ restoration are implemented with regressions, also untested.
+- Advanced typed stored-history editor uses existing OpenAPI type descriptors;
+ twelve focused tests cover typed selectors, historical IDs, invalid raw input,
+ recovery and source/history. Core validation remains authoritative.
+- Normative catalogs now include ExchangeCategory, ContextualLink and both
+ Exchange trace pairs. Manual trace help and scope source version corrected;
+ strict Docutils 0.22.2 HTML regeneration passed. OpenAPI `semantic_schema.py
+ --check --validate` passed 72 fixtures and all 204 component schemas.
+
+Reference evidence: actual Capella 7.1 scenario/lifeline/LOOP creation and saved
+XML are recorded in `release-capella-scenario-benchmark-20260910.md`; the isolated
+Capella process closed normally and is absent. This is not Studio acceptance.
+
+Toolchain bootstrap exec session **20812** completed successfully: verified
+LLVM-MinGW 20260908 ZIP SHA256
+`1bcf74d06b724aeecaa6412ca85f5b26fb1da770e7cdcefa9263c9c5c3ad34b6`, extracted
+successfully; stable **1.98.1** GNU/LLVM, rustfmt and Clippy installed.
+`.local/toolchain/env.ps1` configures task-local Cargo/Rustup/PATH and one build
+job. The original full-platform dependency fetch **13108** was stopped after
+network timeouts; Windows-only compilation fetched its remaining dependencies.
+No current Rust build/test pass is established yet.
+
+The coherent candidate `01bb52a` passed `cargo fmt --all` and `git diff --check`.
+Direct push failed because this fresh host has no Git credentials. Authenticated
+Gitea MCP transferred its exact verified Git bundle through
+`codex/behavior-transfer-20260910` (`harness/transfers/behavior-01bb52a.bundle.b64`),
+prerequisite `1797516`, decoded SHA256
+`85A38B00207036DCB8DB24ED4A7E80B2BE670275EEB7B00413D36153322ADA5D`.
+This **UNTESTED integration snapshot** lets the other host use its warm cache;
+transfer is not acceptance. Their broader architecture
+palette/Escape repair should be reconciled with our overlapping compile/stale
+repairs while retaining the additional invariant tests. Their measured minimum
+scenario canvas is too small; they own the subsequent layout implementation.
+Their isolated palette commit `7d1df4f` was cherry-picked cleanly as `aed40d7`.
+Our additional authoring tests and scenario handlers are retained. External
+native/test results remain scoped to their exact source and executable.
+
+Independent F1 review found a real optional-value cache collision in `01bb52a`.
+The follow-up distinguishes null/scalar/list/tagged constructor identities and
+adds actual inner selector-event tests. Independent source recheck found no
+remaining collision; execution is still pending. The original full-platform
+fetch was stopped after network timeouts; current Windows desktop test build
+is exec session **51036**, compiling dependencies. Root owns this Cargo lease.
+
+Commit `eb5161b` adds the pure B4 lifecycle projection, ten admitted-fixture
+tests and current scenario/history guide wording. The module is declared only
+under `cfg(test)` pending coordinated Scene integration after the other host's
+scenario workspace refactor. Independent source reviews found no confirmed B3
+navigation or B4 projection blocker; synthetic geometry tests do not establish
+actual Scene/marker/hit-test acceptance. No new Studio binary has launched here.
+
+Latest transfer: `harness/transfers/behavior-eb5161b.bundle.b64` on the transfer
+branch, decoded 91,268 bytes, SHA256
+`87F3FBA862AE3751E60E18B39C7BB02B147E4E66079F501050A709D8ADDADE6D`.
+Full MCP readback matched local bytes. It contains all four local commits from
+`1797516`; the other host can cherry-pick `b6b76ef` and `eb5161b` onto its existing
+integration. Gitea #65 records exact handoff and the Wiki title-encoding problem.
+Root is preparing an independent native scenario fixture/oracle through the
+release auditor; only harness/scenario-authoring-contract.rs and its README are
+owned by that worker. Generated fixtures require a later direct-rustc lease.
+
+Actual cross-host test evidence supersedes source-only confidence: the other
+host's immutable pre-`b6b76ef` integrated artifact ran 618 tests, with 615 passing
+and three failures (Gitea #68). One B3 deleted/changed-caller regression is being
+diagnosed by their scenario/layout owner. Two F1 failures came from seeding
+optional UUID/text as `NotApplicable` via broad string-shape matching. Local
+`4ecad78` now recognizes only an explicit enum constructor and adds the nullable
+UUID/text regression; independent descriptor review clears it for execution.
+No assertion was weakened. Thirteen history tests now await local execution.
+Incremental transfer `behavior-4ecad78.bundle.b64` requires `eb5161b`, decoded
+1,464 bytes, SHA256
+`3A93A99AB9C727A4B685D7436C9E23FB262144580E8524623B75D2D676FDF033`.
+Exact local and MCP readback checks pass; Gitea #76 requests warm-host reruns.
+
+The native scenario oracle now requires both independently predicted semantic
+checkpoint equality and exact saved/restored Project+SourceBundle identity.
+Independent review caught that rediscovering fresh role IDs at each checkpoint
+alone could falsely accept recreated IDs on Redo; `same` and its adversarial
+self-check close that evaluator gap. It remains uncompiled/unexecuted.
+
+Fresh `requirements_delta` owns only its new audit report. Early source evidence
+shows constraints are explicitly unexecuted expressions and units are strings
+without dimensional conversion; the newly explicit calculation/quantity goals
+must not be declared complete from this metadata. Reusable library contracts
+and concrete unbounded selectors are under domain/requirements review before
+foundational design decisions. Current behavior/native work remains useful.
+
+Next: focused `architecture_authoring_tests`,
+`behavior::topology`, `direct_clicked`, caller-navigation and `history_fields::tests`,
+then full relevant gates and a fresh native binary. Root owns all Cargo on this
+host. Worker output or tests existing in source are not passes.
+
+Gitea Actions run16 is queued for both ubuntu-latest and windows-latest with no
+started steps, as are older listed runs. No usable current Linux runner result
+is established and no duplicate dispatch was made. Progress is concrete;
+release acceptance remains incomplete. Older checkpoint paragraphs below retain
+their original revision scope.
+
+**Checkpoint publication — 2026-09-10:** the user requested committing all current changes and pushing `codex/release1-continuation` to both `origin` and `gitea`. This is a work-in-progress snapshot. The interrupted palette-focused compilation failed with E0659 (ambiguous `column!` in `app/architecture_authoring/palette.rs:168`) and E0507 (moving the view out of an Arc in `diagram.rs:624`); an unnecessary-pattern-parentheses warning also remains in `architecture_authoring.rs:346`. The prior 584-test pass does not cover this newer palette revision. No Cargo/rustc process or local subagent was active at the publication preflight. Fix these errors and complete automated/native acceptance before packaging the redesign. Gitea coordination starts at issue #1 and the project Wiki; older evidence below retains its original scope.
+
+**Latest priority checkpoint — Capella consultation and redesign:** the user rejected the large creation buttons and explicitly requested consulting the installed Capella. Native control resumed for that consultation in a disposable Capella 7.1 workspace. Actual palette, F2 rename and activity allocation were exercised; saved XML confirms allocation distinct from ownership. The horizontal creation/connection rows are being replaced with a compact expandable palette and inline naming. This supersedes the Escape hold and toolbar-fix proposal below. New implementation, native acceptance and a fresh user build remain pending. [Observed benchmark, alternatives and acceptance criteria](release-capella-basic-editing-20260907.md). The first-release goal remains active.
+
+**Latest checkpoint:** basic-editing v1 passed full desktop584/584, Windows binaries, format and strict all-target Clippy. Native v1 D8CFAA confirmed direct contextual named Entity creation at100%, and found a22px placement shift caused by the pending toolbar changing canvas height. The user pressed Escape at the attempted Undo; that action did not execute. **Desktop control is paused again.** Process9600 remains the isolated unsaved QA application; do not overwrite its binary or presume a clean close. Code-only placement fix is continuing. [Exact v1 evidence and remaining acceptance](release-basic-editing-reset-20260907.md). This supersedes the resumed-control state below; the first-release goal remains incomplete.
+
+**Superseding priority checkpoint:** the user explicitly said “Resume desktop checks” and rejected the focus on advanced review before basic diagram editing. Isolated native checks resumed and reproduced the global creation catalog, placeholder creation/camera jump and noisy review. Contextual named creation and compatible layer-transition destinations are now being implemented and tested. Their new executable has not yet been accepted. [Current priority and scoped evidence](release-basic-editing-reset-20260907.md). The older Escape hold and next-step priorities below are superseded; historical binary/test results retain their original scope.
+
+Current checkpoint: 2026-09-07. The requested Windows development preview is built and packaged with its runtime, documentation, notices and complete examples. Latest Fit6, two existing scroll/branch regressions, full desktop570/570, workspace bins, formatting and strict workspace/all-target Clippy pass. Corrected server56/56 retains its earlier unchanged-server scope. Packaged DC50 GUI launched and the OA graph Fit overview was observed at1444×932 and1102×932; minimum-height and pending-form native acceptance remain open. The user stopped Computer Use with Escape; do not resume native control without renewed authorization. Native Linux core436passed/2ignored remains verified; corrected Linux server and full Linux GUI build/runtime remain open. The fresh Linux desktop cross-build passed on Rust1.90 in2m20s using the preserved cache; ELF and all37 teaching files are verified. Three later guest boots failed the early IO-APIC timer check before SSH/tests; the exact-kernel workaround was then rejected by automatic approval review before launch. All task VM processes are stopped. **Goal active; release incomplete.** The authoritative objective is `C:\Users\User\.codex\attachments\10b0f701-1743-4de7-9ee1-225920e19649\goal-objective.md`: a usable, feature-complete OA/SA engineering tool for Windows and Linux with graphical/source/REST parity, artifact traceability, offline guidance and independent end-state acceptance.
+
+## Workspace and ownership
+
+- Current branch `codex/release1-continuation`, checkpoint **4b6b5fc1977523e8cf52fdd5503a0a71c87a3f63** commits290files of source6 work and evidence. [Bounded checkpoint audit](release-checkpoint-integration-review-20260906.md) verified65pinned manifest entries and145exact staged/working fixture/archive files; `.gitattributes` protects their measured line endings. Integrated ancestry starts at `c091f80`; [branch audit](release-branch-integration-20260906.md) prevents reimplementing already integrated work. New source7 implementation is authorized but not yet accepted.
+- Historical6BAB native scope (superseded in rendering by BCC1 and Fit by DC50): acceptance binary `.local/native-qa/bin-source7-acceptance/arcadia-studio.exe` SHA256 `6BAB6CDE7D31326F3C3CF7342D9B25FB6ADA032864BBE8104625E5EC9736283B` follows the Windows433/55/561 and strict/build gates below. Reset and ReadStatus each passed exact complete model/source Save/Undo/Redo and reopening with the correct derived Reply allocation30/31. OA caller→First.B passed exact persistence/history/reopen; routing remains open. Process11060 closed cleanly; isolated old6 recovery process11240 also closed after its complete source/JSON/raw-draft pass. Old5 process5244 subsequently passed exact30-source/full-JSON recovery and raw-workspace comparison. Current7 process8224→10420 passed invalid nested-link draft crash recovery; completing the recovered SA X→[R3,Inner]/B link passed exact model/two-source Save/Undo/Redo and saved reopening. Those historical native processes closed; the later BCC1 routing and DC50 Fit evidence is recorded separately below. [Native evidence](release-source7-native-acceptance-20260907.md).
+- Completed bounded90minute Linux gate: one-job Rust1.90,455-file immutable snapshot SHA256 `7104946c89b2be7a1a030fdff47cb4405bacf05357d68cf35d181e860f55c279`, launched07:02:43UTC, deadline08:32:43UTC. Server compilation passed17m10s; unit2/2 and HTTP50/51 passed, with one504-vs-412 test assertion reproduced in an isolated rerun. Its two-second read/PATCH checks passed, but later persisted-state assertions were not reached. This establishes a timing-dependent fixture/deadline mismatch, not a production locking defect. Native core completed436passed/0failed/2ignored across40targets, Rust1.90, compile23m03 and1992seconds total; all455 source pre/post checks passed. The two ignored cases are optional inheritance scaling and explicit strace fault injection (earlier separately scoped injection passes remain historical). All artifacts were copied before sync/poweroff; QEMU6280, launcher5000 and seed816 were absent08:13:56UTC. [Current Linux report](release-source7-linux-gate-20260907.md). Earlier Linux evidence retains its older snapshot scope. WSL remains pending restart; no host restart is authorized.
+- Windows session5735 completed all six gates with exit0: focused prepared-HTTP and cancellation, full server56/56 in68.78s, format10.44s, strict workspace/all-target Clippy99.60s, bins102.22s. Status/logs: `.local/source7-post-routing-gates-status.json`. Distinct GUI BCC1FC6DB68123C9D3A43CE53AA8EF59E33C54080632C104C99ED13270D5333F process8568 passed the scoped corrected routing checks and closed cleanly. [HTTP evaluator correction](release-http-preparation-phase-regression-20260907.md), [routing correction](release-contextual-route-native-fix-20260907.md), [native details](release-source7-native-acceptance-20260907.md). These passes precede the now-tested [Fit/layout correction](release-process-fit-layout-audit-20260907.md).
+- The requested Windows preview is `dist/ArcadiaStudio-preview-source7-20260907-x86_64-pc-windows-gnullvm/arcadia-studio.exe`; its565-file ZIP includes all37 teaching-folder files and has SHA256 `1fac10b30daf2f8e1dac29e9469485e4bea142e77ff5a13e4c9d1e41ff03abb2`. The adjacent provenance records actual source/binary hashes. This is a development profile, not an optimized final release. Latest gates are in `.local/source7-latest-preview-status.json`; full desktop570/570 completed in198.82s test time,200.63s command time, exit0 in `.local/source7-latest-full-desktop-status.json`. Initial Fit4/2 failures exposed real zero-size-slot remounting; corrected constructors pass unchanged expectations. [Fit review](release-process-fit-independent-review-20260907.md), [development summary](../DEVELOPMENT_SUMMARY.md).
+- Computer Use is stopped. Read-only inspection initially found preview9148 and second GUI8044 live; later9148 exited without agent intervention, while8044 remained untouched. No GUI control/closure is authorized for background verification. The cross-compiler lease ended after the successful142.766s monotonic run; no cross processes remain. The Linux worker completed a bounded headless guest experiment and released the lease. Three boots (same configuration twice, then Normal boot priority) reached terminal IO-APIC timer panic before SSH/Cargo; all task VM trees stopped and exact evidence was preserved. Exact matching kernel/microcode extraction passed without changing base/overlay hashes; automatic approval review rejected preparation/launch of the transient no_timer_check experiment as “blocked by policy.” No fourth boot or corrected tests ran. No task VM is live; see [the preserved boot evidence](release-source7-linux-server-corrected-gate-20260907.md). [Cross-build report](release-source7-linux-gui-cross-build-20260907.md).
+
+## Current source build and stopped runtime gate
+
+The guide-only followup compiled successfully with locked Windows workspace bins in12.70s Cargo/12.82s command, exit0. Status `.local/source7-guide-current-build-status.json` records guide SHA256 `80A6797051FB28E767F2AB5DD2F335ABCD9F3E5F3A75C731CDE46D8B90D5C92E`; resulting GUI SHA256 `327FBDF734716A7F1AF2874183D46A531A8F429BA1A478E83A3E7F2EB1D6248B`. Formatting and independent actual-label review pass. This later debug artifact does not replace the user-facing packaged DC50 preview, its archive or earlier native evidence. No full570 rerun is claimed for the text-only followup.
+
+Previous goal turn made progress by confirming full570 tests and handing off the package/summary. This continuation produced the successful Linux GUI build/ELF evidence, validated purpose oracles, corrected/built guide text, and concrete guest-boot evidence. Goal remains active/incomplete. The denied direct-boot action must not be split or retried to bypass automatic review; no new Linux job is running or scheduled.
+
+## Latest Linux build and purpose-oracle evidence
+
+Linux GUI snapshot458files `source7-20260907T102528493108Z` has archive SHA256 `9b10aa8457a5371367f7e24a0b2260b63c19fed69873d98a3972062a744503d7`. Run `20260907T1025486926611Z` passed locked/offline Rust1.90 zigbuild, both source checks and independent ELF/package readback. GUI72,953,296bytes SHA256 `330A3872D48EBF24AD21E6D4FA13CB6098BEBB3CB7FF82A2E082925CCF3DD346`; ELF64PIE, glibc requirements≤2.28, all37 lesson files exact. Actual Linux GUI runtime is still unverified. Subsequent guide-label text is excluded from both published Windows and this frozen Linux preview.
+
+Extended native checker `source7-native-contract-incidence.exe` SHA256 `16994DC437F5CCC5E5F6FBB4212C03449F2389AE4615FA98B2930B3637468EE3` compiled with warnings denied in4.05s during a coordinated short lease. OA Exchange and SA CommonWork self-checks pass: exact contextual assembly, unchanged precedence, wrong direction/work rejection, caller-only exchange coverage and stable source update. Separate43-element/10-relation incidence fixture baseline passes; all original golden hashes and preserved OA-link/Reset accepted cases remain exact. Independent review found no material issue. [Cases and reproducible native steps](fixtures/source7-native/README.md). Native purpose authoring/history remains pending.
+
+## Completed and verified at this checkpoint
+
+| Scope | Evidence and limit |
+|---|---|
+| Historical source6 diagnostics and guide | Semantic filter **108 desktop + 2 core** passed; full desktop **527/527** passed; strict all-target Clippy and GUI/server build passed (`.local/semantic-diagnostics-final-tests.log`, `desktop-diagnostics-final-tests.log`, `diagnostics-final-clippy.log`, `diagnostics-final-build.log`). Subsequent guide **3/3** focused tests, strict Clippy and build passed (`.local/guide-navigation-{tests,clippy,build}.log`). The guide test increases compiled desktop inventory to528; no full528 run or final full-workspace total is claimed. |
+| Current artifact implementation checkpoint | Full desktop **518/518**, corrected fresh artifact picker **6/6**, strict all-target Clippy passed. Logs `.local/source6-artifact-inspector-desktop-tests2.log`, `.local/source6-artifacts-final-tests2.log`, `.local/source6-artifacts-final-clippy3.log`. Four subsequent artifact recovery falsifiers and two core absence oracles passed separately. This predates latest display corrections. |
+| Native artifact MOVE / CREATE / DELETE | All three complete source Apply/Save, one Undo/Save and one Redo/Save oracles passed; **nine saves succeeded first attempt**. MOVE invalid Owner and disabled optional text, and CREATE excluded row/all eight decisions/reserved UUID survived controlled-stop recovery exactly. DELETE removed destination from identified moved-source absence while retaining the source link. [Exact native report](release-artifacts-native-20260906.md), [fixture/generator instructions](fixtures/release1-native/artifacts/README.md). |
+| Source-6 atomic batch review | Included valid and excluded invalid raw rows survived forced-stop recovery; repaired two-source Apply and complete Save/Undo/Redo source comparisons passed. [Native report](release-current-native-verification.md). |
+| Scenario ordered-member variant | At1100×700, Take rejected ambiguous insertion; explicit final order retained the local self-test. Complete Apply/Save, Undo/Save and Redo/Save matched the independent source oracles with only the schema5→6 header token upgrade. All three saves succeeded first attempt. Branch steps/fragment branches and this variant's crash recovery remain separate cases. [Exact native report](release-current-native-verification.md). |
+| Native guide navigation | On current guide binary at1100×700: related Scenarios-bottom→Exchanges opens the new heading; filtering `exchange` retains the current topic's bottom position; `Git` auto-selects Build first and opens its heading. No dirty model. Root's actual native observations close these bounded scroll cases; [implementation/tests](release-semantic-conflict-diagnostics-20260906.md) remain separately scoped. |
+| Communication means | Drawing/create/reuse and legacy reverse full-model/source/history passes are retained. Corrected reverse Apply now opens the explicitly requested OA scenario in Behavior, preserving the original four-object Architecture. Distinct oracle predicts10UUIDs/zero placements; accepted model/all3sources, Undo baseline, Redo and reopen pass. Every Save succeeded first submission. Navigation5focused tests, strict Clippy and bins build pass; compiled desktop535 is not a full535 run. [Exact report](release-communication-native-20260906.md), [implementation](release-behavior-transition-context-20260906.md). |
+| Teaching project and Home hook | Corrected94element/62relation/5view/68trace/30source example passes4expanded direct-rustc tests. Minimum-size Home/guide, raw-source guard, reviewed NameTake and exact source/history/reopen pass. Navigation fix is reverified. Additional data/interface/type drill-down, saved-view comparisons, invalid-view guard and typed Entry action selection pass. Fold/unfold and function rename/Undo/Redo each match complete model/all30source oracles, five first-submission saves; saved reopen retains the Entry target identity. Complete process/scenario/reuse/evidence lesson remains open. [Native report](release-teaching-native-20260906.md). |
+| Child/Incident and source-5 recovery | Two-row attachment recovery and complete Apply/Save/Undo/Redo passed, every save first attempt. Exact old source-5 raw/base/include/path retention, manual repair and two-file Save passed. [Attachments](release-attachments-native-20260906.md), [source-5 native](release-source5-native-recovery-20260906.md), [independent review](release-source5-checkpoint-independent-review-20260906.md). |
+| Dense saved views | Forty-label baseline and selected real relation identities inspected; filter, duplicate/hide, original10/copy9 persistence across restart and exact model oracle passed. Additional coverage/context intersections, empty selections and Names-and-items preview/Apply/Undo observed. Minimum-size overview requires working zoom/pan. Limits remain in [native evidence](release-current-native-verification.md). |
+| Core/source/REST integration | Fresh `--all-targets` core run: **433 passed/1 ignored**,44 target summaries, exit0 in138.72s; fresh server run: **55/55**,4 summaries, exit0 in64.74s. Logs `.local/source7-core-all-targets.log`, `.local/source7-server-all-targets.log`; exits/timings in `.local/source7-current-gates-status.json`. These supersede the earlier combined core count and pre-budget-fix HTTP51 checkpoint. OpenAPI **72fixtures/204schemas/90leaves** and both strict reST renders pass. [Core checkpoint](release-source7-core-checkpoint-20260906.md), [API integration](release-source7-api-integration-20260906.md). |
+| Source7 desktop authoring and recovery | Latest routing3/3 and full desktop **564/564 pass**; [routing checkpoint](release-contextual-route-native-fix-20260907.md) records exact logs. Earlier full desktop **561/561 passed**, zero failed/ignored,263.39s in `.local/source7-desktop-full-tests2.log`. The initial560/1 run exposed canvas scroll-state loss when a new conditional navigation control shifted its widget; a fixed navigation container corrected it, and the unchanged regression passed. Coverage includes qualified5, operation3 (selected Async request fields), semantic source7 facets/omission3, recovery8, legacy source5 13, artifact search2 and focus guard1. Exact old-version enum/cache-path intent rejection and raw text retention are covered. The complete-scope endpoint picker preserves access beyond512; canvas preview remains bounded. Guide now accurately describes attachment/artifact reconciliation and artifact filtering. [Desktop checkpoint](release-source7-desktop-authoring-20260906.md), [recovery](release-source7-recovery-20260906.md). |
+| Windows strict/build checkpoint and native7 | Strict workspace/all-target Clippy passed2m50s; root observed final format exit0/empty log. Three affected semantic tests passed3.46s after equivalent lint edits; native build exit0 recorded in `.local/source7-current-gates-status.json`. These earlier results scope the6BAB binary, not the later routing test binary or pending new strict/build sequence. Reset/ReadStatus full model/all-source Save/Undo/Redo/reopen pass. OA First.B persistence passes but routing acceptance remains open. Old6 Recover preserved the complete30-file bundle/workspace/Ports DTO, exact spaces/Unicode/fingerprint/reserved identity; Cancel/Save as and JSON export passed full independent source/model equality. Original seed unchanged. Old5 complete accepted/raw recovery, exact repair/30-source Save and full-model export pass. Current7 invalid nested draft crash recovery plus completed link Save/Undo/Redo/reopen pass; rendering still uses the older binary. [Native report](release-source7-native-acceptance-20260907.md). |
+| Current Linux source7 and cross-built probe | Frozen native server compilation passed17m10s; unit2/2 and HTTP50/51, with one timing-dependent504-vs-412 assertion reproduced twice. No complete server pass; later targets not claimed. Native core436passed/0failed/2ignored; VM cleanly off08:13:56UTC. Separately, the complete Windows macro/build-script cross-build probe's hash-verified ELF executed successfully on Linux; this is neither a full desktop build nor GUI acceptance. [Native Linux gate](release-source7-linux-gate-20260907.md), [cross-build probe](release-linux-cross-build-probe-20260906.md). |
+| Historical Linux/MSRV core and server | Ubuntu24.04.4/x86_64/glibc2.39/ext4/Rust1.90: core38/38, patched journal4focused/42full and3explicit syscall fault cases pass on recorded snapshots. Actual server build passes34m42s including3m24s pause. Real HTTP modular import, invalid PUT unchanged source/ETag, clean SIGTERM and exact model/source/ETag restart pass on the older28-source lesson snapshot. Full server test command timed out while compiling; zero tests executed. That gate shut down cleanly21:53:56; the separate current gate has the scoped results above. Current desktop/Linux package unverified. [Linux report](release-linux-validation-options.md), [journal contract](release-linux-journal-publication.md). |
+| Packaging foundation | Portable Windows QA staging, dependency/runtime/font/Rust notices, archive readback and clean-PATH smoke passed at earlier binaries. [Package report](release-windows-package-review.md). QA archives are not current final release artifacts. |
+
+Historical full platform/native milestones retain their original revision scope; [complete prior ledger](history/2026-09-06-ledgers-before-consolidation/STATE.md) preserves all evidence links and previous environments. A test or native pass never transfers automatically to a changed executable.
+
+## Active findings and checks
+
+Native artifact review exposed same-row conflict wording and reserved identities shown as deleted. Corrections retain typed actual-slot equality, row-addressed reservations and accepted-identity precedence; independent re-review found no further material defect. The completed108-semantic/527-desktop checks supersede the earlier interrupted run and include reserved-label/partial-creation regressions. Revised artifact wording still needs its own native observation on the current binary. Guide-scroll implementation/tests/build and the bounded native cases above passed. [Diagnostic/guide report](release-semantic-conflict-diagnostics-20260906.md).
+
+**F1** teaching onboarding/review/history has current-binary native evidence; remaining tutorial work is scoped in its report. **F2**, faithful behavior-canvas SVG export, is deferred by root's scope decision: export remains architecture-only and must be described that way; no faithful scenario/process/state SVG acceptance is claimed.
+
+Reverse scenario's first-dependency focus/overlapping architecture placements and teaching NameTake's separate layer/view mismatch are fixed and natively reverified. Latest NameTake4focused tests, strict alltargetClippy28.26s and binsbuild50.50s pass; no full539desktop pass is claimed. [Navigation evidence](release-semantic-review-navigation-20260906.md). Minimum-size process working space is cramped; native lesson evidence records23% overview versus100% scrolling rather than claiming readability at Fit.
+
+Independent scope reconciliation verified **S1** missing contextual assembly into repeated/nested process references and **S2** missing maintained interface-operation identity in parameterless messages. Isolated existing-core probes falsify the proposed workarounds. Root ratified the jointly critiqued [source7 integration decision](release-source7-integration-decision-20260906.md), including deterministic cache-independent admission, whole-link deletion, purpose-specific analysis, exact Call-derived operation/Reply identity and one frozen old5/6 checkpoint migration. These contracts now have implemented core/source/REST/desktop/recovery paths and the current automated gates above; remaining native7 and current Linux acceptance remain incomplete. [Scope reconciliation](release-scope-reconciliation-20260906.md).
+
+Linux ordinary rename's empty-target replacement was independently reproduced and corrected using `renameat2(RENAME_NOREPLACE)` without fallback. Exact errno/no-write assertions now pass under actual syscall injection. This establishes the ext4 publication invariant, not power-loss durability, hostile ancestor protection or model-file compare-and-swap. Windows retains its no-replace API and bounded scanner retries.
+
+## Remaining gates and next actions
+
+1. Fit implementation/review and automated gates are complete, including full570 desktop tests. Finish its native minimum-height/Focus/pending-form checks when native control resumes. Preserve completed BCC1 routing and old5/old6/current7 recovery/source/history oracles. Complete remaining current-binary S1/S2 purpose workflows and teaching cases; operation allocation deletion remains subject to independent channel validation. Native-check revised reserved labels and same-row conflicts. The later guide-only control-label correction passed formatting, independent review and the fresh workspace-binary build; it awaits the next packaged/native release snapshot.
+2. Close uncovered selective-view lifecycle cases; advanced ordered/process/scenario/fragment variants beyond the accepted Scenario case; typed state actions and pending Linux data/hosted/compact workflows. Implemented semantics must not be called missing merely because acceptance is outstanding; [coverage audit](release-current-coverage-20260906.md) is the baseline map, newer scoped reports supersede its old gap claims.
+3. Verify complete current Linux integration suites and desktop/server build/runtime, then supported-size native workflows. The successful core/MSRV pilot is a reusable starting point, not application acceptance.
+4. Preserve current strict Clippy/format/reST/example/source/OpenAPI results and rerun affected checks after changes. Complete optimized/MSRV/final platform gates, actual hosted transport variants and final migration/package checks. Rebuild portable distributions from the accepted exact tree, validate notices/runtime/content/checksums and clean-environment launch before replacing old bundles.
+5. Conduct fresh independent requirements/domain/parity/architecture/runtime/UX/end-state reviews; resolve material findings. Commit/integrate authorized source with reviewable scope; remote publication is not requested. Only complete the goal when its applicable criteria are met.
+
+Meaningful progress continues: native lifecycle and Linux no-replace uncertainty have been resolved with exact oracles. The highest-value work is current-binary validation and closing uncovered engineering workflows, not accumulating repeated historical headings or treating aggregate test counts as release completion.
diff --git a/harness/history/2026-09-10-ledgers-before-continuation-refresh/TASK_LEDGER.md b/harness/history/2026-09-10-ledgers-before-continuation-refresh/TASK_LEDGER.md
new file mode 100644
index 00000000..cf9f6086
--- /dev/null
+++ b/harness/history/2026-09-10-ledgers-before-continuation-refresh/TASK_LEDGER.md
@@ -0,0 +1,102 @@
+# Task Ledger — first release
+
+## Current knowledge — 2026-09-10
+
+Given: the renewed objective in `PROJECT_GOAL.md` requires a feature-complete,
+usable OA/SA first release and prioritizes direct behavior/scenario authoring.
+Graphical/source/REST parity, explicit reST semantics, traceability, offline
+guidance, Windows/Linux acceptance and independent review remain required.
+Older scope deferrals must be checked against this request.
+
+Updated given requirements (2026-09-10 goal edit): calculations, quantities and
+units, reusable model libraries, and avoiding long homogeneous selection lists
+are explicit. `PROJECT_GOAL.md` preserves the exact new attachment. A fresh
+independent domain audit maps them to actual behavior before design convergence;
+unexecuted constraints and unit strings alone are not assumed to satisfy them.
+
+Verified: the checkout began clean at `1797516`; Gitea contains one active
+continuation issue and no open PR. Current code includes direct behavior editors,
+so old blanket descriptions of static diagrams need workflow-level reassessment.
+The machine began without the ignored historical toolchain/cache/distributions.
+Local Rust/Cargo 1.98.1 and verified LLVM-MinGW are now installed; dependency
+fetch is pending and no current binary has been built. Native Computer Use is
+available. Isolated Capella activity/lifeline/LOOP creation was saved and checked;
+no Arcadia window is running. Candidate B1/B2/B3 and F1 edits are source-complete,
+formatted and whitespace-clean, but have no fresh compile/test/runtime pass.
+
+Assumption: the selected compact architecture palette remains a promising design,
+pending real authoring evidence. Decision: repair and evaluate it before further
+redesign. Parallel independent audits identify current behavior and semantic gaps
+instead of reimplementing integrated work.
+
+Open questions: which remaining menu-only behavior operations prevent a coherent
+direct workflow; which first-release semantic/parity gaps remain; and how to
+complete current Linux runtime acceptance. Root owns integration and native
+control; worker ownership is disjoint. See `STATE.md` and Gitea #1.
+
+**Superseding user direction:** large horizontal creation buttons were rejected as intrusive and unprofessional. The user explicitly authorized consulting the installed Capella; that consultation has resumed and established actual compact palette, inline rename and explicit activity-allocation behavior. See [Capella 7.1 evidence and selected redesign](release-capella-basic-editing-20260907.md). The current candidate replaces creation/connection rows with one compact expandable palette and an in-place name editor. Native acceptance of the redesigned executable is pending; compactness is not assumed to prove usability. The earlier Escape hold and proposed toolbar-only correction below are superseded.
+
+**Latest evidence:** basic editing full desktop584/584, bins, formatting and strictClippy passed. Native named Entity creation works at100%, but changing pending-toolbar height moves the screen placement22px. User Escape stopped the attempted Undo; native checks are paused again. Code-only toolbar correction proceeds. [Current findings](release-basic-editing-reset-20260907.md). The previous goal turn made concrete progress through implementation, integration gates and a native-discovered defect; this is not a no-progress blocker turn.
+
+**Current steering:** basic diagram authoring and understandable OA/SA transitions come first. The user explicitly reauthorized desktop checks; root resumed the isolated test build and reproduced the reported usability problems. Creation, transition and independent fixture work is underway. No new basic-editing native pass is claimed. See [priority reset](release-basic-editing-reset-20260907.md); older execution priorities and Escape hold are superseded.
+
+Updated 2026-09-07 after source7 Reset/ReadStatus and old6 recovery native passes, OA contextual persistence with rendering open, and reproducible Linux server test deadline evidence. Native Linux core now passed436/0failed/2ignored and its guest shut down cleanly. Old5 full recovery and source7 pending nested-link crash recovery/history/reopen also passed; routing3/3 and full desktop564/564 now pass. Root's private-server/full-server/format/strict-Clippy/bins sequence is running with results pending. [STATE.md](STATE.md) owns current execution/evidence and leases. [PLAN.md](PLAN.md) owns the release plan; [DECISIONS.md](DECISIONS.md) preserves architectural rationale. Full pre-consolidation contents, including superseded requirements interpretations and evidence, remain [archived byte-for-byte](history/2026-09-06-ledgers-before-consolidation/TASK_LEDGER.md).
+
+## Goal and success criteria
+
+The authoritative user objective is `C:\Users\User\.codex\attachments\10b0f701-1743-4de7-9ee1-225920e19649\goal-objective.md`. Deliver a usable, substantially complete OA/SA engineering tool in Rust/Iced on Windows and Linux; equivalent graphical, strict textual/reST and REST workflows; practical artifact traceability; navigable offline guidance; recoverable drafts; reproducible deployment. Completion requires native engineering task evidence and fresh independent end-state review, not implementation or passing tests alone. Later Arcadia layers remain future scope.
+
+## Given facts and constraints
+
+- User prioritizes Capella domain fidelity, useful containment/nesting and explicit typed ports, while avoiding clutter, persistent action overload and awkward navigation. Study real domain workflows rather than mechanically copying Capella classes.
+- Both-layer transitions and reviewed counterpart reconciliation must be intuitive and precise. Behavior/scenario diagrams must be directly editable and preserve selection/camera/task context.
+- Human/AI/Git-friendly modular source, comments/includes, complete graphical/source/API parity and clear rejection are required. One validated model/history boundary must govern every representation.
+- User permits autonomous reversible engineering, experiments, independent agents and authorized native work. Root is the sole native operator in this collaboration. Do not run concurrent shared Cargo writers. No remote push, usage reset, broad target deletion or host restart is implied by current task authorization.
+- User permits isolated Linux work. WSL remains unavailable pending restart; earlier task-local QEMU core and server gates completed and shut down cleanly; the separate source7 core gate also completed and shut down cleanly, as recorded below. The server built and passed real HTTP/restart checks; its full test command timed out during compilation before any test executed. Historical external worktrees/toolchains are not present on this machine.
+
+## Verified facts
+
+- Latest superseding checkpoint: Fit6 and both existing scroll/branch regressions, full desktop570/570, workspace bins, format and strict workspace/all-target Clippy pass. Windows preview565-file ZIP and complete examples are verified; current server56/56 also passed. Native BCC1 OA/SA nested routing and unchanged-source oracles passed, followed by bounded DC50 packaged Fit overview. Earlier pending-build/routing claims below retain their historical scope and are superseded by these exact current results in STATE.md.
+
+- Branch/patch/tree audit established that direct state/scenario/process, data contracts, hosted editing and earlier semantic review are already integrated; no required production transplant was found. [Branch evidence](release-branch-integration-20260906.md).
+- Capella's function input/output exchange endpoints and component Standard/Flow ports are distinct; explicit allocation connects them. Preserve that domain distinction. [Native benchmark](release-capella-workflow-benchmark.md).
+- Source6 introduced participant CommunicationMean, typed explicit transition endpoint decisions, saved exchange categories/view selectors, completeness warnings and presentation duplication; current source7 retains them. Older audit absence claims are superseded; native acceptance remains scoped by reports.
+- Semantic review includes independent scalar/constructor/keyed/order/Child/Incident/artifact facets and multiple source rows. One frozen accepted base, explicit mappings, unique merged candidate slots and one atomic command preserve raw excluded/rejected drafts. Recovery replans; it never restores an unchecked serialized command. [Batch design](release-semantic-batch-foundation-design.md), [artifact contract](release-semantic-artifacts.md).
+- Native Windows artifact MOVE/CREATE/DELETE passed exact complete Save/Undo/Redo oracles, nine first-attempt saves. MOVE invalid raw optional state and CREATE exclusion/reserved identity survived controlled-stop recovery exactly. Destination-only retention is automated evidence, not separately native-applied in that run. [Native report](release-artifacts-native-20260906.md).
+- Native batch, Child/Incident and source-5 checkpoint workflows have scoped exact source/recovery/history evidence. Dense views have persistence/duplicate/hide and bounded readability evidence. Current counts, binaries and remaining cases are in STATE.md; these are not universal usability claims.
+- Native communication means drawing, explicit create/reuse ports and reverse Call/Reply pass independent complete model/source and exact Undo/Redo checks. The reverse navigation/overlapping-placement defect is fixed and reverified against a distinct oracle:10newUUIDs,zero implicit placements, correct Behavior owner and preserved Architecture. [Exact evidence](release-communication-native-20260906.md).
+- Windows semantic108desktop+2core/full527desktop passes remain scoped. Subsequent guide/home and behavior navigation slices passed focused tests/strict Clippy/build; semantic navigation is also natively reverified. Fresh source7 core all-targets433passed/1ignored across44 summaries and server55/55 across4 summaries have recorded exit0. Full desktop561/561 passed263.39s after a real scroll-state regression was fixed with a stable navigation Column; the unchanged test passed. Strict workspace/all-target Clippy and root-observed final format exit0 pass;3 affected semantic tests passed after equivalent lint edits. Subsequent routing3/3 and full desktop564/564 pass; new standalone build and strict/server results remain pending. Logs and exact scope are in STATE.md; do not add affected reruns to full-suite totals.
+- Native Scenario ordered review passed ambiguity rejection, explicit retained-local final order and complete Save/Undo/Redo source oracles at1100×700. Current guide binary passes related-topic heading reset, same-topic search scroll retention and Git auto-selected-topic reset without dirtying the model. Branch/fragment order variants and revised artifact diagnostic rendering remain separate gates. [Native ordered evidence](release-current-native-verification.md), [guide/diagnostic implementation](release-semantic-conflict-diagnostics-20260906.md).
+- Windows user-local GNU/LLVM builds and isolated clean-PATH native launch work. Actual Ubuntu24.04.4/Rust1.90 core/journal tests pass on hashed snapshots. Actual Linux server build and real HTTP modular import/invalidPUT/cleanrestart exact source/model/ETag pass on the older28-source lesson snapshot. Its full server test command timed out during compilation with zero tests executed, then that gate shut down cleanly. The current frozen source7 gate compiled the server in17m10s and passed unit2/2 plus HTTP50/51; the sole504-vs-412 timing assertion failed again alone. Its final persisted-state checks were not reached. Native core436passed/0failed/2ignored and the VM shut down cleanly08:13:56UTC; full desktop/Linux package remain unverified. [Linux evidence](release-linux-validation-options.md).
+- Corrected30-source/37-file teaching project passes minimum-size Home/guide/dirty-source guard, located initial warning, reviewed NameTake and complete exact source/Undo/Redo/reopen checks. The layer/canvas mismatch is corrected and natively reverified. Data/interface/type navigation, saved-view distinctions, invalid-view draft guard and typed state Entry fold/rename/history/reopen pass. The process workspace is cramped at minimum size and artifact navigation required excessive scrolling; targeted source7 UI improvements await native observation. [Native evidence](release-teaching-native-20260906.md).
+- Scope reconciliation established caller-context links into repeated/nested reusable chains (**S1**) and maintained interface-operation selection for parameterless messages (**S2**) as real missing semantics. Their ratified source7 implementation now has fresh full core/server/desktop gates, OpenAPI72 fixtures and strict reST evidence. Old5/6 compatibility and current7 raw recovery tests pass. Root's native Reset and ReadStatus cases passed complete Save/Undo/Redo oracles and reopened with explicit derived allocation30/31. OA First.B persistence/history/reopen passed but rendering is open; old6 recovery passed full30-source/model JSON and exact raw workspace/Ports DTO comparison. Old5 complete raw/accepted recovery, exact repaired30-source save/full-model export pass. Current7 invalid nested draft crash recovery and completed link Save/Undo/Redo/reopen also pass; corrected rendering and remaining native7/Linux workflows remain open. [Native report](release-source7-native-acceptance-20260907.md). [Scope reconciliation](release-scope-reconciliation-20260906.md), [integration decision](release-source7-integration-decision-20260906.md).
+
+## Decisions and candidate designs
+
+Retain three Rust crates, one typed Project and validated atomic Command. Keep semantic ownership, reference/allocation/order relationships and diagram placement separate. Forms remain a closed typed adapter, not a second metamodel. Persistent additions require model validation, references/deletion, transition, parser/updater, API/schema and reachable graphical editing parity.
+
+Semantic reconciliation uses finite typed facets and separate member/presence/order decisions. Compare actual destination slots before constructing the core's complete candidate; two rows never become sequential Apply. Missing identities are not inferred by names. Raw constructor/numeric/keyed/optional drafts survive rejection, exclusion and checkpoint recovery. Artifact Presence means attachment to the reviewed owner; an identified source moved away can delete the destination, whereas a source key of None preserves destination-only membership. No invented tombstone or silent seed acknowledgment.
+
+Source updates preserve lexical ownership/comments/includes and exact history. Checkpoint source5/6→7 upgrade admits only the established token/typed migration contract, validates old base fingerprints and resets transient approvals; it does not silently rebase authored drafts. Hosted saves remain explicit and conditional on the resource/ETag; credentials stay in memory.
+
+For Linux journal publication, ordinary rename lost the empty-target ownership invariant in a real ext4 probe. Selected `renameat2(RENAME_NOREPLACE)` through existing locked libc with no fallback; syscall errors fail before model writes. Alternative overwrite fallback was rejected by that falsifier. Windows native no-replace/retry behavior is retained; other OS behavior is explicit. [Contract, alternatives and measured limits](release-linux-journal-publication.md).
+
+Root selected **F1**, a complete source-6 teaching-project example. Corrected model/replay tests and bounded native onboarding/review/history pass; the full process/scenario/state/data/evidence lesson remains open. **F2**, faithful behavior-canvas SVG export, is deferred: current export scope is architecture-only, not a claim of faithful scenario/process/state export. Record this as an explicit scope decision rather than an accepted behavior-export result.
+
+Behavior-root transition Apply should focus the explicitly requested counterpart and its nearest behavior owner. Without an explicit destination diagram it must preserve unrelated architecture presentation. Ordinary architecture transitions retain their existing path; explicit destination remains honored. This decision follows the independently reproduced reverse scenario placement/navigation failure, not a semantic change to the core transition.
+
+## Assumptions and open questions
+
+- Nested source and contextual editors should reduce authoring effort; additional shorthand/grouping must justify grammar or interaction complexity through real tasks. Dense-view path overlap is not itself a failure when every actual identity remains accessible; arbitrary crossing-free layout is not promised.
+- Remaining native variants must establish useful engineering workflows: CM transitions, advanced OA process/SA chain/scenario reuse/fragment/order, typed state actions, compact/Linux/hosted cases, and uncovered saved-view lifecycle combinations. Do not expand or shrink required OA/SA scope merely to fit implementation convenience.
+- Latest diagnostic/reserved-label corrections have independent review and completed semantic/full-desktop/Clippy/build checks; their revised artifact rendering still needs native verification. The historical source6 process3012 closed cleanly. The6BAB source7 native build passed the scoped cases above;11060 and old6 recovery11240 closed cleanly. Old5 recovery/repair/save/model and current7 nested draft recovery/history/reopen pass on that binary. Later routing/hit-priority changes passed3focused/full564 desktop tests; root is running the server/format/strict/build sequence with results pending, and no corrected-render native acceptance is transferred. Exact binary scope is in STATE.md.
+- Historical Linux evidence includes x86_64/glibc2.39/ext4 core/MSRV and server/HTTP runtime. Current455-file snapshot SHA256 `7104946c89b2be7a1a030fdff47cb4405bacf05357d68cf35d181e860f55c279` is frozen; core completed436passes/2ignored before clean VM shutdown08:13:56UTC; its original08:32:43UTC deadline was not reached. The hash-verified cross-built probe executed successfully on Linux, including its Windows macro/build-script build path, but no complete Linux GUI has been built. Syscall failure tests do not establish power-loss durability, network-filesystem behavior or cross-process model CAS. [Current Linux gate](release-source7-linux-gate-20260907.md).
+- Earlier Windows QA packages include runtime/notices and verified archive contents; final accepted-tree optimized distributions and exact provenance/checksums remain a release gate. [Packaging evidence](release-windows-package-review.md).
+
+## Current strategy
+
+The requested Windows preview and whole-run development summary are delivered; the summary is being refreshed with the confirmed package, full570/570 desktop result and final Fit/strict/build gates. Preview DC50 has bounded native launch/overview evidence, not complete minimum-height/pending-form acceptance. Computer Use stopped on user Escape, so leave live GUI processes untouched and advance independent verification.
+
+Preserve the successful immutable458-file Linux GUI cross-build with the Fit fix, exact ELF/package evidence and released compiler lease. Three subsequent guest boots reached terminal IO-APIC timer panic before any tests; exact-kernel extraction succeeded but automatic approval review denied the proposed transient workaround. All task VMs are stopped. Preserve the denied action and evidence without bypassing review. Its snapshot/run/hashes and actual process status belong in STATE.md and the cross-build report. Preserve the original interrupted runs, cache, native core436/2ignored and failed Linux timed-server fixture. The replacement private HTTP preparation test and full server56 pass on Windows; Linux execution remains required.
+
+Guide steps now name Tools/Selection details/Restore panels and the exact Reply label; formatting, independent label review and a fresh Windows workspace-binary build pass. The user-facing preview retains its original tested bytes. Independent full-model/source purpose oracles compiled and passed both self-checks and preserved baseline cases; complete their native authoring/history steps when control resumes. Complete current native OA/SA teaching, artifact, saved-view, process/scenario and hosted cases once control is available. Retain caller-specific budgets, actual port proof, independent channel validation and exact old5/6 recovery bytes. Final optimized packages, current platform gates and fresh independent end-state review remain open; architecture-only SVG and passing slices do not redefine completion.
From ef1a4478c55e635ca4a21156d363b82174ff3772 Mon Sep 17 00:00:00 2001
From: Codex
Date: Thu, 10 Sep 2026 12:10:59 -0700
Subject: [PATCH 09/14] Validate scenario oracle and record first local
behavior test results
---
.gitattributes | 2 +-
harness/PLAN.md | 4 +-
harness/STATE.md | 29 +-
harness/TASK_LEDGER.md | 6 +
harness/fixtures/scenario-authoring/README.md | 30 +-
.../base/expected-baseline.json | 1339 +++++++++++++++++
.../scenario-authoring/base/project.arcadia | 674 +++++++++
.../scenario-authoring/base/rationale.arcadia | 2 +
8 files changed, 2068 insertions(+), 18 deletions(-)
create mode 100644 harness/fixtures/scenario-authoring/base/expected-baseline.json
create mode 100644 harness/fixtures/scenario-authoring/base/project.arcadia
create mode 100644 harness/fixtures/scenario-authoring/base/rationale.arcadia
diff --git a/.gitattributes b/.gitattributes
index 9d1ccf1e..7250a748 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -22,4 +22,4 @@ harness/history/2026-09-10-ledgers-before-continuation-refresh/** -text
crates/oa-desktop/src/recovery/fixtures/*.json -text
harness/fixtures/source7-native/** -text
harness/fixtures/basic-editing/** -text
-harness/fixtures/scenario-authoring/base/** -text
+harness/fixtures/scenario-authoring/base/** -text whitespace=cr-at-eol
diff --git a/harness/PLAN.md b/harness/PLAN.md
index f04c8ea4..c7515507 100644
--- a/harness/PLAN.md
+++ b/harness/PLAN.md
@@ -26,7 +26,7 @@ broad downstream changes. Do not silently promote a design proposal to a decisio
| Milestone | Work and acceptance | Dependency/current limit |
| --- | --- | --- |
| 1. Reliable direct authoring | Finish cold compilation and meaningful regressions; integrate compact named architecture/behavior creation and scenario workspace. Exercise actual minimum/wide native creation, allocation, editing, rejection, cancellation and history without implicit draft application or placement drift. | Root owns this host's Cargo/native lease. External host owns scenario layout/B3/local creation/palette; integrate coherent checkpoints first. No current local Rust/native Studio pass. |
-| 2. Coherent process/scenario work | Interactive ordered/explicit process editing; clicked root/nested scenario insertion; exact reuse bindings and caller return; measured canvas/zoom/scroll; visible scoped Create/Delete lifetimes. Validate model/source/identity and no unintended edit during navigation, including stale/deleted/retargeted references and recovery. | B1/B2/B3 adapters are source-integrated; B3 has a known externally owned correction. B4 is cfg(test) projection only, awaiting Scene integration. Compile/self-check the independent oracle before using native checkpoints. |
+| 2. Coherent process/scenario work | Interactive ordered/explicit process editing; clicked root/nested scenario insertion; exact reuse bindings and caller return; measured canvas/zoom/scroll; visible scoped Create/Delete lifetimes. Validate model/source/identity and no unintended edit during navigation, including stale/deleted/retargeted references and recovery. | B1/B2/B3 adapters are source-integrated; B3 has a known externally owned correction. B4 is cfg(test) projection only, awaiting Scene integration. The independent oracle compiled/self-checked and its generated baseline passed; use it for native checkpoints. |
| 3. Precise engineering analysis | Compare owned properties, analysis contexts and existing-data/external alternatives; settle quantity kind/unit/value, numerical policy, calculation/binding and Boolean/error/unknown semantics through falsifying examples. Implement mixed-unit budgets and reusable calculation/constraint definitions with full UI/source/REST parity and exact history/recovery. | New requirement audit confirms absence. Maximum-reasoning proposal tracks precede root synthesis and independent criticism; no semantic contract is ratified yet. Strings are not evaluation. |
| 4. Maintained reusable libraries | Compare package/definition/instance alternatives. Establish pinned identity/version/dependency closure, local edits, import bindings, detach and explicit three-way updates. Demonstrate two projects, two independent instances, offline reopen, conflicting updates and stable unaffected identities. | Coordinate with milestone 3 definition references and migration. Same-project includes/reuse do not satisfy maintained cross-project libraries. No package contract implemented yet. |
| 5. Understandable complete engineering tasks | Close teaching, OA↔SA transition/reconciliation, typed state/data/interface, artifact, saved-view and hosted workflows. Replace concrete long homogeneous selectors with contextual/searchable bounded choices; verify representative large models, keyboard access, minimum sizes and failure/recovery. Keep offline guide and exact worked examples aligned. | Existing implementations and historical evidence are a coverage starting point. Reassess against current goal instead of reviving stale absence claims or carrying old scope exclusions forward. |
@@ -44,7 +44,7 @@ cross-representation evaluators guide implementation boundaries.
fix causes. Run focused corrected cases, then proportionate full gates and GUI.
2. Exchange exact verified commits with the separate host, preserving its workspace
ownership. Do not merge the bundle-transfer branch or assume remote publication.
-3. Compile and self-check [scenario oracle](scenario-authoring-contract.rs), follow
+3. Use the self-checked [scenario oracle](scenario-authoring-contract.rs), follow
its [native procedure](fixtures/scenario-authoring/README.md), and compare exact
whole snapshots for Undo/Redo/reopen at minimum/wide sizes.
4. Synthesize the [requirement delta](release-requirements-delta-20260910.md) and
diff --git a/harness/STATE.md b/harness/STATE.md
index 4c24887f..f6192a90 100644
--- a/harness/STATE.md
+++ b/harness/STATE.md
@@ -25,14 +25,14 @@ for capability acceptance. Older goal paths are superseded.
| Scope | Current evidence and limits |
| --- | --- |
-| Reproducible local toolchain | Ignored `.local/toolchain/env.ps1` selects Rust/Cargo 1.98.1 GNU/LLVM and verified LLVM-MinGW 20260908 (clang 23.1.1), with one Cargo job. The cold build is still active; this is setup evidence only. |
+| Reproducible local toolchain | Ignored `.local/toolchain/env.ps1` selects Rust/Cargo 1.98.1 GNU/LLVM and verified LLVM-MinGW 20260908 (clang 23.1.1), with one Cargo job. First cold desktop test build completed in 58m03s; one unused import warning was corrected separately. |
| B1 process authoring | `01bb52a` includes interactive empty/ordered/explicit topology and presentation-only inferred paths; first conversion preserves ordered adjacency atomically. Source review and added boundary tests exist; current local Rust/native acceptance is pending. |
| B2 scenario authoring | Clicked root/operand/slot and optional lifeline feed Action/State/Reuse/Fragment adapters. Pointer-to-App, cancellation, stale-event, recovery and history tests are written, not yet locally executed. |
| B3 caller navigation | Bounded caller/use frames and scoped viewport restoration are implemented. External tests exposed a real same-selected-ID/visible-owner defect, detailed below; this feature is not accepted. |
| Architecture palette | External palette commit `7d1df4f` was cherry-picked as `aed40d7`, retaining local invariant tests. Captured Escape, stale Submit/raw retention and bounded card fixes are source-integrated. External native results do not transfer to this checkout. |
| Typed stored-history F1 | `b6b76ef` fixes nullable constructor cache discrimination; `4ecad78` fixes NotApplicable seeding by checking actual enum alternatives. Thirteen focused history tests await local execution; core validation remains authoritative. |
| Lifeline lifecycle B4 | `eb5161b` adds pure local/scope-aware lifecycle projection with ten tests, declared under `cfg(test)` only. It is not production Scene wiring or native acceptance. [Design](release-scenario-lifecycle-design-20260910.md). |
-| Native scenario oracle | `8547b85` includes [independent checker](scenario-authoring-contract.rs) and [instructions](fixtures/scenario-authoring/README.md). Whole Project/SourceBundle comparison preserves exact identities for Undo/Redo. Compile, self-check, fixture generation and native use are pending. |
+| Native scenario oracle | [Independent checker](scenario-authoring-contract.rs) compiled with warnings denied using completed matching core/JSON rlibs; self-check, generation and whole-baseline check passed (exit 0). The checked-in fixture has 62 elements/two sources, eight OA/SA semantic checkpoints and exact identity discrimination for history. [Inputs/hashes/instructions](fixtures/scenario-authoring/README.md). Studio native use remains pending. |
| Normative documentation | Strict Docutils 0.22.2 language HTML regeneration passed. `semantic_schema.py --check --validate` passed 72 source7 fixtures, 204 schemas, 14 families/90 scalar leaves. These are documentation/schema gates, not a Rust build or runtime pass. |
| Comparable-product study | Actual isolated Capella 7.1 scenario/activity/lifeline/LOOP creation was saved and XML inspected, then closed normally. [Report](release-capella-scenario-benchmark-20260910.md). No Studio native workflow has run on this fresh host. |
| New requirement audit | [Independent delta report](release-requirements-delta-20260910.md) establishes that current expressions/unit labels are descriptive and source includes are not maintained cross-project libraries. Separate foundational design proposals remain under investigation, not ratified or implemented. |
@@ -40,11 +40,24 @@ for capability acceptance. Older goal paths are superseded.
## Active work, ownership and unresolved findings
Root Codex-956atf owns this host's integration, serialized Cargo and native UI.
-Session `51036` runs `cargo test --locked -p oa-desktop --bin arcadia-studio --no-run`
-with log `.local/logs/desktop-test-build.log`; at 11:59 Pacific it has reached
-`oa-desktop` but has not completed. No local Rust test/build pass or current Studio
-binary acceptance is claimed. Source the local toolchain environment before later
-commands; do not start a concurrent Cargo writer. Confirm process state on resume.
+Session `51036` completed `cargo test --locked -p oa-desktop --bin arcadia-studio --no-run`
+with exit 0 (log `.local/logs/desktop-test-build.log`). The executable is
+`target/debug/deps/arcadia_studio-58e30cee1fd51a8b.exe`, SHA256
+`95A3B78E5A61AB5E9750C2FC077138247E538D9360720A7A8E0CBB47B882ECC1`,
+629-test inventory. Rust implementation inputs match `8547b85`; the unused palette
+import correction in `aa40345` is later than compiler consumption and requires a
+fresh build for warning-free acceptance. No Studio GUI has launched here.
+
+Direct executable runs with `--test-threads=1` passed history 13/13, pure lifecycle
+10/10, topology 45/45, architecture authoring 18/18 and mounted scroll 5/5. Scenario
+direct tests passed 13/14 and reproduced the known caller-return assertion failure
+at `scenario.rs:2473`; do not count that filter as passing. Logs are
+`.local/logs/history-fields-focused.log` and `.local/logs/focused-*.log`. The full
+suite has not run. Gitea #97's exact integration bundle was anonymously fetched,
+decoded, SHA256-verified and admitted with `git bundle verify`; source integration
+and a new build follow. Source the local toolchain before later commands and keep
+one Cargo writer. The independent checker used completed rlibs without another
+Cargo writer; its evidence is separately scoped above.
Codex-9e8r6t is a separate host/team with a warm cache. It owns scenario workspace
layout/B3 correction, local named behavior creation, palette and platform/packaging.
@@ -82,7 +95,7 @@ and Gitea #65/#76. Latest verified transfer is `harness/transfers/behavior-aa403
2. Integrate the externally verified workspace/B3/local creation/palette/package
checkpoints without overwriting ownership or treating their earlier tests as new
local acceptance. Wire B4 only after coordinating actual Scene geometry.
-3. Compile/self-check the independent oracle, then exercise current Studio at
+3. Use the self-checked independent oracle to exercise current Studio at
minimum/wide sizes: named creation, direct process/scenario edits, nested reuse,
caller return, lifeline lifecycle, invalid/cancel/recovery, Save/Undo/Redo/reopen.
Compare complete model/source and exact identities, not screenshots alone.
diff --git a/harness/TASK_LEDGER.md b/harness/TASK_LEDGER.md
index c72e4353..29aa125c 100644
--- a/harness/TASK_LEDGER.md
+++ b/harness/TASK_LEDGER.md
@@ -44,6 +44,12 @@ cannot establish completion.
## Verified facts
+- The independent scenario checker compiled with warnings denied and passed its
+ self-check, deterministic fixture generation and complete baseline check on this
+ host. Its eight OA/SA checkpoints and exact UUID/source restoration comparisons
+ are evaluator evidence, not native Studio acceptance. Exact inputs/hashes are in
+ [the fixture instructions](fixtures/scenario-authoring/README.md).
+
- Current local source is `aa40345` on `codex/behavior-authoring-20260910`, following
baseline `1797516`. Local commits cover B1/B2/B3 authoring, nullable history cache
(`b6b76ef`), external palette integration (`aed40d7`), cfg(test) lifecycle projection
diff --git a/harness/fixtures/scenario-authoring/README.md b/harness/fixtures/scenario-authoring/README.md
index 00fe63bb..3c87facb 100644
--- a/harness/fixtures/scenario-authoring/README.md
+++ b/harness/fixtures/scenario-authoring/README.md
@@ -1,10 +1,10 @@
# Scenario authoring native contract
-Prepared by Codex-956atf /root/release_audit on 2026-09-10. **Source candidate only: compilation, self-check, generation and native execution have not run.** This fixture/oracle is preparation for B2 clicked-slot authoring and B3 caller return, not acceptance evidence.
+Prepared by Codex-956atf /root/release_audit on 2026-09-10. Root compiled the checker with warnings denied, passed its self-check, generated the deterministic baseline and verified the complete model/source on Windows. **Native Studio execution remains pending.** This fixture/oracle prepares B2 clicked-slot authoring and B3 caller return; it does not establish GUI acceptance.
`harness/scenario-authoring-contract.rs` generates deterministic OA and SA versions of the same small scenario tree. Each has Activity/Function choices, a Ready state, explicit lifelines, two nested ALT fragments, and fixed root/operand insertion anchors. A caller contains two distinct uses of B verification; B uses C detail. Each use explicitly maps both referenced lifelines to distinct local lifelines representing the same work. No native action/state/reuse/fragment role exists in the baseline.
-Only this new Rust source and this README belong to the preparation slice. Generate fixture files after root grants the direct-rustc lease and the checker self-check passes.
+The checked-in `base` directory is the verified fixture. Preserve it and copy the whole directory for native work. To reproduce generation, use a fresh output directory after compiling and self-checking the checker.
## Compile and generate
@@ -14,16 +14,16 @@ Use the current matching `oa_core` and `serde_json` rlibs from root's accepted C
. .local/toolchain/env.ps1
$scenarioCore = 'ABSOLUTE_PATH_TO_CURRENT_liboa_core-HASH.rlib'
$scenarioJson = 'ABSOLUTE_PATH_TO_MATCHING_libserde_json-HASH.rlib'
-$scenarioDeps = Split-Path -LiteralPath $scenarioCore -Parent
+$scenarioDeps = [IO.Path]::GetDirectoryName($scenarioCore)
$scenarioLinker = 'ABSOLUTE_PATH_TO_TOOLCHAIN_clang.exe'
New-Item -ItemType Directory -Force -Path .local/native-qa | Out-Null
rustc --edition 2024 -D warnings harness/scenario-authoring-contract.rs --extern "oa_core=$scenarioCore" --extern "serde_json=$scenarioJson" -L "dependency=$scenarioDeps" -C "linker=$scenarioLinker" -o .local/native-qa/scenario-authoring-contract.exe
.local/native-qa/scenario-authoring-contract.exe self-check
-.local/native-qa/scenario-authoring-contract.exe generate harness/fixtures/scenario-authoring/base
-.local/native-qa/scenario-authoring-contract.exe check baseline harness/fixtures/scenario-authoring/base/project.arcadia
+.local/native-qa/scenario-authoring-contract.exe generate .local/native-qa/scenario-generated
+.local/native-qa/scenario-authoring-contract.exe check baseline .local/native-qa/scenario-generated/project.arcadia
```
-The compiler target must match those rlibs; include root's explicit `--target` if its toolchain setup requires one. Record the exact command, compiler/rlib identities, checker hash and result. Generation refuses any existing output directory. It writes `project.arcadia`, an included Unicode/CRLF `rationale.arcadia`, and `expected-baseline.json`. The JSON is a reference artifact, not an additional source document.
+The compiler target must match those rlibs; include root's explicit `--target` if its toolchain setup requires one. Record the exact command, compiler/rlib identities, checker hash and result. Generation refuses any existing output directory. It writes `project.arcadia`, an included Unicode/CRLF `rationale.arcadia`, and `expected-baseline.json`. The JSON is a reference artifact, not an additional source document. The root validation record below supplies the exact first-run identities.
Copy the **whole** generated base into separate fresh OA and SA native working directories. Preserve the base. Each layer's four checkpoints start from that same baseline; `sa-action` does not include the OA creations. Preserve the whole working directory after each declared Save/Undo/Redo checkpoint. Do not modify fixture sources or adjust expected fields to match observed output.
@@ -93,4 +93,20 @@ The checker uses only unique **Kind::Occurrence + layer + predetermined literal
Expected transactions use core typed commands and source writeback, independently of the desktop planners, then compare the whole Project and the complete reachable two-file SourceBundle. The source oracle deliberately shares the core language engine; this fixture validates native GUI-to-model/source parity rather than supplying an independent language implementation. Semantic checkpoint checks plus required exact saved/restored snapshot comparisons prove state and identity restoration only for the captured Undo/Redo/reopen sequence, not arbitrary history internals.
-The self-check covers deterministic seeds, all eight OA/SA checkpoint fixed points, carriers with deliberately false parent/body/alias/description, rejected unrelated edits, wrong insertion order/parent/bindings, and a model-neutral change to the retained note. It also constructs two equivalent valid checkpoints with different fresh action IDs: each passes semantic prediction, while exact `same` snapshot comparison rejects their identity difference. It is not a native UI test. Current compile, self-check, generation, native action/return/scroll/recovery, screenshot and release evidence remain pending until recorded by root.
+The self-check covers deterministic seeds, all eight OA/SA checkpoint fixed points, carriers with deliberately false parent/body/alias/description, rejected unrelated edits, wrong insertion order/parent/bindings, and a model-neutral change to the retained note. It also constructs two equivalent valid checkpoints with different fresh action IDs: each passes semantic prediction, while exact `same` snapshot comparison rejects their identity difference. It is not a native UI test. Native action/return/scroll/recovery, screenshot and release evidence remain pending.
+
+## Root validation — 2026-09-10
+
+Source `632c8fe` (checker unchanged from `8547b85`), Rust 1.98.1 target `x86_64-pc-windows-gnullvm`, LLVM-MinGW 20260908 clang 23.1.1. The direct `rustc` command above used the already completed core/JSON rlibs from the active cold desktop build, with no second Cargo writer. Compile with `-D warnings`, `self-check`, `generate`, and `check baseline` all exited 0. Generation reported 62 elements and two reachable sources. The initial PowerShell recipe's incompatible `Split-Path -LiteralPath -Parent` was corrected to `IO.Path.GetDirectoryName` before compilation.
+
+| Artifact | SHA256 |
+| --- | --- |
+| Checker executable `.local/native-qa/scenario-authoring-contract.exe` | `407318D5C7CAE6B237F261A21B0FDA55F6FEF655CB9DFCFA3153A7E372638498` |
+| Checker source working bytes | `3BDA3EBC38E99BB658AD3EB9A5889E9C1CC77E8B1F8609079580AE27A4E8E789` |
+| `liboa_core-6c8da57367eec642.rlib` | `D7EFB5F3C423FF8AF13637F53CC481502C7F317BF01E9D072F9EFD64CD954C13` |
+| `libserde_json-da2c4f333fc47818.rlib` | `485300D4125A69DE7DA5413F832EB4B9CDE547E60F103F3B284E49AEE4194D05` |
+| `base/project.arcadia` | `9B1AFBE96B0BF8FA613451B00929492F05A59B9EB2CA74C2C374A8B7BB7CBA9E` |
+| `base/rationale.arcadia` | `82DF210F22F6AA336A3E7E00240977D671C7C3923DB7BC58FB29E6D98B67EF49` |
+| `base/expected-baseline.json` | `ABD8246C939EBB8A2162EF82E23D0FB322EE1A8B4D0D7C28A34037E42E86E841` |
+
+Logs are `.local/logs/scenario-oracle-{compile,self-check,generate,baseline}.log`. These results validate the independent evaluator and fixture only; the desktop test build was still running and no Studio native pass is transferred from them.
diff --git a/harness/fixtures/scenario-authoring/base/expected-baseline.json b/harness/fixtures/scenario-authoring/base/expected-baseline.json
new file mode 100644
index 00000000..b679ffef
--- /dev/null
+++ b/harness/fixtures/scenario-authoring/base/expected-baseline.json
@@ -0,0 +1,1339 @@
+{
+ "schema_version": 7,
+ "id": "5ceaa100-0000-4000-8000-000000000001",
+ "alias": "scenario_authoring",
+ "name": "Scenario authoring contract",
+ "description": "Native clicked-slot and caller-return contract.",
+ "elements": {
+ "5ceaa100-0000-4000-8000-00000000006e": {
+ "id": "5ceaa100-0000-4000-8000-00000000006e",
+ "alias": "scenario_oa_10",
+ "name": "OA Inspect ticket",
+ "description": "",
+ "parent": null,
+ "body": {
+ "Oa": "Activity"
+ }
+ },
+ "5ceaa100-0000-4000-8000-00000000006f": {
+ "id": "5ceaa100-0000-4000-8000-00000000006f",
+ "alias": "scenario_oa_11",
+ "name": "OA Record result",
+ "description": "",
+ "parent": null,
+ "body": {
+ "Oa": "Activity"
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000078": {
+ "id": "5ceaa100-0000-4000-8000-000000000078",
+ "alias": "scenario_oa_20",
+ "name": "OA Ticket modes",
+ "description": "",
+ "parent": null,
+ "body": {
+ "Oa": {
+ "Shared": "StateMachine"
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000079": {
+ "id": "5ceaa100-0000-4000-8000-000000000079",
+ "alias": "scenario_oa_21",
+ "name": "OA Ticket region",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000078",
+ "body": {
+ "Oa": {
+ "Shared": "Region"
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-00000000007a": {
+ "id": "5ceaa100-0000-4000-8000-00000000007a",
+ "alias": "scenario_oa_22",
+ "name": "OA Ready",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000079",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "State": {
+ "kind": "State",
+ "entry": "",
+ "exit": "",
+ "do_activity": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000082": {
+ "id": "5ceaa100-0000-4000-8000-000000000082",
+ "alias": "scenario_oa_30",
+ "name": "OA A caller",
+ "description": "",
+ "parent": null,
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Scenario": {
+ "viewpoint": "OperationalActivity",
+ "context": null,
+ "precondition": "",
+ "postcondition": "",
+ "steps": [
+ "5ceaa100-0000-4000-8000-000000000096",
+ "5ceaa100-0000-4000-8000-000000000097",
+ "5ceaa100-0000-4000-8000-0000000000a0",
+ "5ceaa100-0000-4000-8000-0000000000a1",
+ "5ceaa100-0000-4000-8000-0000000000a2"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000083": {
+ "id": "5ceaa100-0000-4000-8000-000000000083",
+ "alias": "scenario_oa_31",
+ "name": "OA B verification",
+ "description": "",
+ "parent": null,
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Scenario": {
+ "viewpoint": "OperationalActivity",
+ "context": null,
+ "precondition": "",
+ "postcondition": "",
+ "steps": [
+ "5ceaa100-0000-4000-8000-0000000000a3",
+ "5ceaa100-0000-4000-8000-0000000000a4",
+ "5ceaa100-0000-4000-8000-0000000000a5"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000084": {
+ "id": "5ceaa100-0000-4000-8000-000000000084",
+ "alias": "scenario_oa_32",
+ "name": "OA C detail",
+ "description": "",
+ "parent": null,
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Scenario": {
+ "viewpoint": "OperationalActivity",
+ "context": null,
+ "precondition": "",
+ "postcondition": "",
+ "steps": [
+ "5ceaa100-0000-4000-8000-0000000000a6"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-00000000008c": {
+ "id": "5ceaa100-0000-4000-8000-00000000008c",
+ "alias": "scenario_oa_40",
+ "name": "OA A inspector",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000082",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Lifeline": {
+ "represented": "5ceaa100-0000-4000-8000-00000000006e"
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-00000000008d": {
+ "id": "5ceaa100-0000-4000-8000-00000000008d",
+ "alias": "scenario_oa_41",
+ "name": "OA A recorder",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000082",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Lifeline": {
+ "represented": "5ceaa100-0000-4000-8000-00000000006f"
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-00000000008e": {
+ "id": "5ceaa100-0000-4000-8000-00000000008e",
+ "alias": "scenario_oa_42",
+ "name": "OA B inspector",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000083",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Lifeline": {
+ "represented": "5ceaa100-0000-4000-8000-00000000006e"
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-00000000008f": {
+ "id": "5ceaa100-0000-4000-8000-00000000008f",
+ "alias": "scenario_oa_43",
+ "name": "OA B recorder",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000083",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Lifeline": {
+ "represented": "5ceaa100-0000-4000-8000-00000000006f"
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000090": {
+ "id": "5ceaa100-0000-4000-8000-000000000090",
+ "alias": "scenario_oa_44",
+ "name": "OA C inspector",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000084",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Lifeline": {
+ "represented": "5ceaa100-0000-4000-8000-00000000006e"
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000091": {
+ "id": "5ceaa100-0000-4000-8000-000000000091",
+ "alias": "scenario_oa_45",
+ "name": "OA C recorder",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000084",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Lifeline": {
+ "represented": "5ceaa100-0000-4000-8000-00000000006f"
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000096": {
+ "id": "5ceaa100-0000-4000-8000-000000000096",
+ "alias": "scenario_oa_50",
+ "name": "OA Root entry",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000082",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Action": {
+ "action": "5ceaa100-0000-4000-8000-00000000006e",
+ "lifeline": "5ceaa100-0000-4000-8000-00000000008c"
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000097": {
+ "id": "5ceaa100-0000-4000-8000-000000000097",
+ "alias": "scenario_oa_51",
+ "name": "OA Outer ALT",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000082",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Fragment": {
+ "operator": "Alternative",
+ "branches": [
+ "5ceaa100-0000-4000-8000-000000000098",
+ "5ceaa100-0000-4000-8000-000000000099"
+ ]
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000098": {
+ "id": "5ceaa100-0000-4000-8000-000000000098",
+ "alias": "scenario_oa_52",
+ "name": "OA Outer chosen operand",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000097",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Branch": {
+ "guard": "ticket.present",
+ "occurrences": [
+ "5ceaa100-0000-4000-8000-00000000009a"
+ ]
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000099": {
+ "id": "5ceaa100-0000-4000-8000-000000000099",
+ "alias": "scenario_oa_53",
+ "name": "OA Outer fallback operand",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000097",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Branch": {
+ "guard": "else",
+ "occurrences": [
+ "5ceaa100-0000-4000-8000-00000000009f"
+ ]
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-00000000009a": {
+ "id": "5ceaa100-0000-4000-8000-00000000009a",
+ "alias": "scenario_oa_54",
+ "name": "OA Inner ALT",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000098",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Fragment": {
+ "operator": "Alternative",
+ "branches": [
+ "5ceaa100-0000-4000-8000-00000000009b",
+ "5ceaa100-0000-4000-8000-00000000009c"
+ ]
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-00000000009b": {
+ "id": "5ceaa100-0000-4000-8000-00000000009b",
+ "alias": "scenario_oa_55",
+ "name": "OA Inner chosen operand",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-00000000009a",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Branch": {
+ "guard": "ticket.ready",
+ "occurrences": [
+ "5ceaa100-0000-4000-8000-00000000009d"
+ ]
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-00000000009c": {
+ "id": "5ceaa100-0000-4000-8000-00000000009c",
+ "alias": "scenario_oa_56",
+ "name": "OA Inner other operand",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-00000000009a",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Branch": {
+ "guard": "else",
+ "occurrences": [
+ "5ceaa100-0000-4000-8000-00000000009e"
+ ]
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-00000000009d": {
+ "id": "5ceaa100-0000-4000-8000-00000000009d",
+ "alias": "scenario_oa_57",
+ "name": "OA Inner anchor",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-00000000009b",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Action": {
+ "action": "5ceaa100-0000-4000-8000-00000000006e",
+ "lifeline": "5ceaa100-0000-4000-8000-00000000008c"
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-00000000009e": {
+ "id": "5ceaa100-0000-4000-8000-00000000009e",
+ "alias": "scenario_oa_58",
+ "name": "OA Inner other anchor",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-00000000009c",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Action": {
+ "action": "5ceaa100-0000-4000-8000-00000000006e",
+ "lifeline": "5ceaa100-0000-4000-8000-00000000008c"
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-00000000009f": {
+ "id": "5ceaa100-0000-4000-8000-00000000009f",
+ "alias": "scenario_oa_59",
+ "name": "OA Fallback anchor",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000099",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Action": {
+ "action": "5ceaa100-0000-4000-8000-00000000006f",
+ "lifeline": "5ceaa100-0000-4000-8000-00000000008d"
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000a0": {
+ "id": "5ceaa100-0000-4000-8000-0000000000a0",
+ "alias": "scenario_oa_60",
+ "name": "OA First use of B",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000082",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "ScenarioUse": {
+ "scenario": "5ceaa100-0000-4000-8000-000000000083",
+ "bindings": [
+ {
+ "referenced": "5ceaa100-0000-4000-8000-00000000008e",
+ "local": "5ceaa100-0000-4000-8000-00000000008c"
+ },
+ {
+ "referenced": "5ceaa100-0000-4000-8000-00000000008f",
+ "local": "5ceaa100-0000-4000-8000-00000000008d"
+ }
+ ]
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000a1": {
+ "id": "5ceaa100-0000-4000-8000-0000000000a1",
+ "alias": "scenario_oa_61",
+ "name": "OA Second use of B",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000082",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "ScenarioUse": {
+ "scenario": "5ceaa100-0000-4000-8000-000000000083",
+ "bindings": [
+ {
+ "referenced": "5ceaa100-0000-4000-8000-00000000008e",
+ "local": "5ceaa100-0000-4000-8000-00000000008c"
+ },
+ {
+ "referenced": "5ceaa100-0000-4000-8000-00000000008f",
+ "local": "5ceaa100-0000-4000-8000-00000000008d"
+ }
+ ]
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000a2": {
+ "id": "5ceaa100-0000-4000-8000-0000000000a2",
+ "alias": "scenario_oa_62",
+ "name": "OA Root exit",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000082",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Action": {
+ "action": "5ceaa100-0000-4000-8000-00000000006f",
+ "lifeline": "5ceaa100-0000-4000-8000-00000000008d"
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000a3": {
+ "id": "5ceaa100-0000-4000-8000-0000000000a3",
+ "alias": "scenario_oa_63",
+ "name": "OA B entry",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000083",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Action": {
+ "action": "5ceaa100-0000-4000-8000-00000000006e",
+ "lifeline": "5ceaa100-0000-4000-8000-00000000008e"
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000a4": {
+ "id": "5ceaa100-0000-4000-8000-0000000000a4",
+ "alias": "scenario_oa_64",
+ "name": "OA B use of C",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000083",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "ScenarioUse": {
+ "scenario": "5ceaa100-0000-4000-8000-000000000084",
+ "bindings": [
+ {
+ "referenced": "5ceaa100-0000-4000-8000-000000000090",
+ "local": "5ceaa100-0000-4000-8000-00000000008e"
+ },
+ {
+ "referenced": "5ceaa100-0000-4000-8000-000000000091",
+ "local": "5ceaa100-0000-4000-8000-00000000008f"
+ }
+ ]
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000a5": {
+ "id": "5ceaa100-0000-4000-8000-0000000000a5",
+ "alias": "scenario_oa_65",
+ "name": "OA B exit",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000083",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Action": {
+ "action": "5ceaa100-0000-4000-8000-00000000006f",
+ "lifeline": "5ceaa100-0000-4000-8000-00000000008f"
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000a6": {
+ "id": "5ceaa100-0000-4000-8000-0000000000a6",
+ "alias": "scenario_oa_66",
+ "name": "OA C entry",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000084",
+ "body": {
+ "Oa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Action": {
+ "action": "5ceaa100-0000-4000-8000-00000000006e",
+ "lifeline": "5ceaa100-0000-4000-8000-000000000090"
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000d2": {
+ "id": "5ceaa100-0000-4000-8000-0000000000d2",
+ "alias": "scenario_sa_10",
+ "name": "SA Inspect ticket",
+ "description": "",
+ "parent": null,
+ "body": {
+ "Sa": "Function"
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000d3": {
+ "id": "5ceaa100-0000-4000-8000-0000000000d3",
+ "alias": "scenario_sa_11",
+ "name": "SA Record result",
+ "description": "",
+ "parent": null,
+ "body": {
+ "Sa": "Function"
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000dc": {
+ "id": "5ceaa100-0000-4000-8000-0000000000dc",
+ "alias": "scenario_sa_20",
+ "name": "SA Ticket modes",
+ "description": "",
+ "parent": null,
+ "body": {
+ "Sa": {
+ "Shared": "StateMachine"
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000dd": {
+ "id": "5ceaa100-0000-4000-8000-0000000000dd",
+ "alias": "scenario_sa_21",
+ "name": "SA Ticket region",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000dc",
+ "body": {
+ "Sa": {
+ "Shared": "Region"
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000de": {
+ "id": "5ceaa100-0000-4000-8000-0000000000de",
+ "alias": "scenario_sa_22",
+ "name": "SA Ready",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000dd",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "State": {
+ "kind": "State",
+ "entry": "",
+ "exit": "",
+ "do_activity": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000e6": {
+ "id": "5ceaa100-0000-4000-8000-0000000000e6",
+ "alias": "scenario_sa_30",
+ "name": "SA A caller",
+ "description": "",
+ "parent": null,
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Scenario": {
+ "viewpoint": "Functional",
+ "context": null,
+ "precondition": "",
+ "postcondition": "",
+ "steps": [
+ "5ceaa100-0000-4000-8000-0000000000fa",
+ "5ceaa100-0000-4000-8000-0000000000fb",
+ "5ceaa100-0000-4000-8000-000000000104",
+ "5ceaa100-0000-4000-8000-000000000105",
+ "5ceaa100-0000-4000-8000-000000000106"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000e7": {
+ "id": "5ceaa100-0000-4000-8000-0000000000e7",
+ "alias": "scenario_sa_31",
+ "name": "SA B verification",
+ "description": "",
+ "parent": null,
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Scenario": {
+ "viewpoint": "Functional",
+ "context": null,
+ "precondition": "",
+ "postcondition": "",
+ "steps": [
+ "5ceaa100-0000-4000-8000-000000000107",
+ "5ceaa100-0000-4000-8000-000000000108",
+ "5ceaa100-0000-4000-8000-000000000109"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000e8": {
+ "id": "5ceaa100-0000-4000-8000-0000000000e8",
+ "alias": "scenario_sa_32",
+ "name": "SA C detail",
+ "description": "",
+ "parent": null,
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Scenario": {
+ "viewpoint": "Functional",
+ "context": null,
+ "precondition": "",
+ "postcondition": "",
+ "steps": [
+ "5ceaa100-0000-4000-8000-00000000010a"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000f0": {
+ "id": "5ceaa100-0000-4000-8000-0000000000f0",
+ "alias": "scenario_sa_40",
+ "name": "SA A inspector",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000e6",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Lifeline": {
+ "represented": "5ceaa100-0000-4000-8000-0000000000d2"
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000f1": {
+ "id": "5ceaa100-0000-4000-8000-0000000000f1",
+ "alias": "scenario_sa_41",
+ "name": "SA A recorder",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000e6",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Lifeline": {
+ "represented": "5ceaa100-0000-4000-8000-0000000000d3"
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000f2": {
+ "id": "5ceaa100-0000-4000-8000-0000000000f2",
+ "alias": "scenario_sa_42",
+ "name": "SA B inspector",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000e7",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Lifeline": {
+ "represented": "5ceaa100-0000-4000-8000-0000000000d2"
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000f3": {
+ "id": "5ceaa100-0000-4000-8000-0000000000f3",
+ "alias": "scenario_sa_43",
+ "name": "SA B recorder",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000e7",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Lifeline": {
+ "represented": "5ceaa100-0000-4000-8000-0000000000d3"
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000f4": {
+ "id": "5ceaa100-0000-4000-8000-0000000000f4",
+ "alias": "scenario_sa_44",
+ "name": "SA C inspector",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000e8",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Lifeline": {
+ "represented": "5ceaa100-0000-4000-8000-0000000000d2"
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000f5": {
+ "id": "5ceaa100-0000-4000-8000-0000000000f5",
+ "alias": "scenario_sa_45",
+ "name": "SA C recorder",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000e8",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Lifeline": {
+ "represented": "5ceaa100-0000-4000-8000-0000000000d3"
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000fa": {
+ "id": "5ceaa100-0000-4000-8000-0000000000fa",
+ "alias": "scenario_sa_50",
+ "name": "SA Root entry",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000e6",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Action": {
+ "action": "5ceaa100-0000-4000-8000-0000000000d2",
+ "lifeline": "5ceaa100-0000-4000-8000-0000000000f0"
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000fb": {
+ "id": "5ceaa100-0000-4000-8000-0000000000fb",
+ "alias": "scenario_sa_51",
+ "name": "SA Outer ALT",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000e6",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Fragment": {
+ "operator": "Alternative",
+ "branches": [
+ "5ceaa100-0000-4000-8000-0000000000fc",
+ "5ceaa100-0000-4000-8000-0000000000fd"
+ ]
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000fc": {
+ "id": "5ceaa100-0000-4000-8000-0000000000fc",
+ "alias": "scenario_sa_52",
+ "name": "SA Outer chosen operand",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000fb",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Branch": {
+ "guard": "ticket.present",
+ "occurrences": [
+ "5ceaa100-0000-4000-8000-0000000000fe"
+ ]
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000fd": {
+ "id": "5ceaa100-0000-4000-8000-0000000000fd",
+ "alias": "scenario_sa_53",
+ "name": "SA Outer fallback operand",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000fb",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Branch": {
+ "guard": "else",
+ "occurrences": [
+ "5ceaa100-0000-4000-8000-000000000103"
+ ]
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000fe": {
+ "id": "5ceaa100-0000-4000-8000-0000000000fe",
+ "alias": "scenario_sa_54",
+ "name": "SA Inner ALT",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000fc",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Fragment": {
+ "operator": "Alternative",
+ "branches": [
+ "5ceaa100-0000-4000-8000-0000000000ff",
+ "5ceaa100-0000-4000-8000-000000000100"
+ ]
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-0000000000ff": {
+ "id": "5ceaa100-0000-4000-8000-0000000000ff",
+ "alias": "scenario_sa_55",
+ "name": "SA Inner chosen operand",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000fe",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Branch": {
+ "guard": "ticket.ready",
+ "occurrences": [
+ "5ceaa100-0000-4000-8000-000000000101"
+ ]
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000100": {
+ "id": "5ceaa100-0000-4000-8000-000000000100",
+ "alias": "scenario_sa_56",
+ "name": "SA Inner other operand",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000fe",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Branch": {
+ "guard": "else",
+ "occurrences": [
+ "5ceaa100-0000-4000-8000-000000000102"
+ ]
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000101": {
+ "id": "5ceaa100-0000-4000-8000-000000000101",
+ "alias": "scenario_sa_57",
+ "name": "SA Inner anchor",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000ff",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Action": {
+ "action": "5ceaa100-0000-4000-8000-0000000000d2",
+ "lifeline": "5ceaa100-0000-4000-8000-0000000000f0"
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000102": {
+ "id": "5ceaa100-0000-4000-8000-000000000102",
+ "alias": "scenario_sa_58",
+ "name": "SA Inner other anchor",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-000000000100",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Action": {
+ "action": "5ceaa100-0000-4000-8000-0000000000d2",
+ "lifeline": "5ceaa100-0000-4000-8000-0000000000f0"
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000103": {
+ "id": "5ceaa100-0000-4000-8000-000000000103",
+ "alias": "scenario_sa_59",
+ "name": "SA Fallback anchor",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000fd",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Action": {
+ "action": "5ceaa100-0000-4000-8000-0000000000d3",
+ "lifeline": "5ceaa100-0000-4000-8000-0000000000f1"
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000104": {
+ "id": "5ceaa100-0000-4000-8000-000000000104",
+ "alias": "scenario_sa_60",
+ "name": "SA First use of B",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000e6",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "ScenarioUse": {
+ "scenario": "5ceaa100-0000-4000-8000-0000000000e7",
+ "bindings": [
+ {
+ "referenced": "5ceaa100-0000-4000-8000-0000000000f2",
+ "local": "5ceaa100-0000-4000-8000-0000000000f0"
+ },
+ {
+ "referenced": "5ceaa100-0000-4000-8000-0000000000f3",
+ "local": "5ceaa100-0000-4000-8000-0000000000f1"
+ }
+ ]
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000105": {
+ "id": "5ceaa100-0000-4000-8000-000000000105",
+ "alias": "scenario_sa_61",
+ "name": "SA Second use of B",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000e6",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "ScenarioUse": {
+ "scenario": "5ceaa100-0000-4000-8000-0000000000e7",
+ "bindings": [
+ {
+ "referenced": "5ceaa100-0000-4000-8000-0000000000f2",
+ "local": "5ceaa100-0000-4000-8000-0000000000f0"
+ },
+ {
+ "referenced": "5ceaa100-0000-4000-8000-0000000000f3",
+ "local": "5ceaa100-0000-4000-8000-0000000000f1"
+ }
+ ]
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000106": {
+ "id": "5ceaa100-0000-4000-8000-000000000106",
+ "alias": "scenario_sa_62",
+ "name": "SA Root exit",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000e6",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Action": {
+ "action": "5ceaa100-0000-4000-8000-0000000000d3",
+ "lifeline": "5ceaa100-0000-4000-8000-0000000000f1"
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000107": {
+ "id": "5ceaa100-0000-4000-8000-000000000107",
+ "alias": "scenario_sa_63",
+ "name": "SA B entry",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000e7",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Action": {
+ "action": "5ceaa100-0000-4000-8000-0000000000d2",
+ "lifeline": "5ceaa100-0000-4000-8000-0000000000f2"
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000108": {
+ "id": "5ceaa100-0000-4000-8000-000000000108",
+ "alias": "scenario_sa_64",
+ "name": "SA B use of C",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000e7",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "ScenarioUse": {
+ "scenario": "5ceaa100-0000-4000-8000-0000000000e8",
+ "bindings": [
+ {
+ "referenced": "5ceaa100-0000-4000-8000-0000000000f4",
+ "local": "5ceaa100-0000-4000-8000-0000000000f2"
+ },
+ {
+ "referenced": "5ceaa100-0000-4000-8000-0000000000f5",
+ "local": "5ceaa100-0000-4000-8000-0000000000f3"
+ }
+ ]
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-000000000109": {
+ "id": "5ceaa100-0000-4000-8000-000000000109",
+ "alias": "scenario_sa_65",
+ "name": "SA B exit",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000e7",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Action": {
+ "action": "5ceaa100-0000-4000-8000-0000000000d3",
+ "lifeline": "5ceaa100-0000-4000-8000-0000000000f3"
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ },
+ "5ceaa100-0000-4000-8000-00000000010a": {
+ "id": "5ceaa100-0000-4000-8000-00000000010a",
+ "alias": "scenario_sa_66",
+ "name": "SA C entry",
+ "description": "",
+ "parent": "5ceaa100-0000-4000-8000-0000000000e8",
+ "body": {
+ "Sa": {
+ "Shared": {
+ "Occurrence": {
+ "body": {
+ "Action": {
+ "action": "5ceaa100-0000-4000-8000-0000000000d2",
+ "lifeline": "5ceaa100-0000-4000-8000-0000000000f4"
+ }
+ },
+ "precondition": "",
+ "postcondition": ""
+ }
+ }
+ }
+ }
+ }
+ },
+ "relations": {},
+ "diagrams": {},
+ "artifact_links": {},
+ "traces": {}
+}
\ No newline at end of file
diff --git a/harness/fixtures/scenario-authoring/base/project.arcadia b/harness/fixtures/scenario-authoring/base/project.arcadia
new file mode 100644
index 00000000..b7fee00e
--- /dev/null
+++ b/harness/fixtures/scenario-authoring/base/project.arcadia
@@ -0,0 +1,674 @@
+arcadia 7;
+project scenario_authoring "Scenario authoring contract" id "5ceaa100-0000-4000-8000-000000000001" {
+ include "rationale.arcadia";
+ description "Native clicked-slot and caller-return contract.";
+
+ oa_activity scenario_oa_10 "OA Inspect ticket" id "5ceaa100-0000-4000-8000-00000000006e" {
+ description "";
+ parent none;
+ }
+
+ oa_activity scenario_oa_11 "OA Record result" id "5ceaa100-0000-4000-8000-00000000006f" {
+ description "";
+ parent none;
+ }
+
+ oa_state_machine scenario_oa_20 "OA Ticket modes" id "5ceaa100-0000-4000-8000-000000000078" {
+ description "";
+ parent none;
+ }
+
+ oa_region scenario_oa_21 "OA Ticket region" id "5ceaa100-0000-4000-8000-000000000079" {
+ description "";
+ parent @scenario_oa_20;
+ }
+
+ oa_state scenario_oa_22 "OA Ready" id "5ceaa100-0000-4000-8000-00000000007a" {
+ description "";
+ do_activity "";
+ entry "";
+ exit "";
+ kind State;
+ parent @scenario_oa_21;
+ }
+
+ oa_scenario scenario_oa_30 "OA A caller" id "5ceaa100-0000-4000-8000-000000000082" {
+ context none;
+ description "";
+ parent none;
+ postcondition "";
+ precondition "";
+ steps [@scenario_oa_50, @scenario_oa_51, @scenario_oa_60, @scenario_oa_61, @scenario_oa_62];
+ viewpoint OperationalActivity;
+ }
+
+ oa_scenario scenario_oa_31 "OA B verification" id "5ceaa100-0000-4000-8000-000000000083" {
+ context none;
+ description "";
+ parent none;
+ postcondition "";
+ precondition "";
+ steps [@scenario_oa_63, @scenario_oa_64, @scenario_oa_65];
+ viewpoint OperationalActivity;
+ }
+
+ oa_scenario scenario_oa_32 "OA C detail" id "5ceaa100-0000-4000-8000-000000000084" {
+ context none;
+ description "";
+ parent none;
+ postcondition "";
+ precondition "";
+ steps [@scenario_oa_66];
+ viewpoint OperationalActivity;
+ }
+
+ oa_lifeline scenario_oa_40 "OA A inspector" id "5ceaa100-0000-4000-8000-00000000008c" {
+ description "";
+ parent @scenario_oa_30;
+ represented @scenario_oa_10;
+ }
+
+ oa_lifeline scenario_oa_41 "OA A recorder" id "5ceaa100-0000-4000-8000-00000000008d" {
+ description "";
+ parent @scenario_oa_30;
+ represented @scenario_oa_11;
+ }
+
+ oa_lifeline scenario_oa_42 "OA B inspector" id "5ceaa100-0000-4000-8000-00000000008e" {
+ description "";
+ parent @scenario_oa_31;
+ represented @scenario_oa_10;
+ }
+
+ oa_lifeline scenario_oa_43 "OA B recorder" id "5ceaa100-0000-4000-8000-00000000008f" {
+ description "";
+ parent @scenario_oa_31;
+ represented @scenario_oa_11;
+ }
+
+ oa_lifeline scenario_oa_44 "OA C inspector" id "5ceaa100-0000-4000-8000-000000000090" {
+ description "";
+ parent @scenario_oa_32;
+ represented @scenario_oa_10;
+ }
+
+ oa_lifeline scenario_oa_45 "OA C recorder" id "5ceaa100-0000-4000-8000-000000000091" {
+ description "";
+ parent @scenario_oa_32;
+ represented @scenario_oa_11;
+ }
+
+ oa_occurrence scenario_oa_50 "OA Root entry" id "5ceaa100-0000-4000-8000-000000000096" {
+ body {
+ Action {
+ action @scenario_oa_10;
+ lifeline @scenario_oa_40;
+ };
+ };
+ description "";
+ parent @scenario_oa_30;
+ postcondition "";
+ precondition "";
+ }
+
+ oa_occurrence scenario_oa_51 "OA Outer ALT" id "5ceaa100-0000-4000-8000-000000000097" {
+ body {
+ Fragment {
+ branches [@scenario_oa_52, @scenario_oa_53];
+ operator Alternative;
+ };
+ };
+ description "";
+ parent @scenario_oa_30;
+ postcondition "";
+ precondition "";
+ }
+
+ oa_occurrence scenario_oa_52 "OA Outer chosen operand" id "5ceaa100-0000-4000-8000-000000000098" {
+ body {
+ Branch {
+ guard "ticket.present";
+ occurrences [@scenario_oa_54];
+ };
+ };
+ description "";
+ parent @scenario_oa_51;
+ postcondition "";
+ precondition "";
+ }
+
+ oa_occurrence scenario_oa_53 "OA Outer fallback operand" id "5ceaa100-0000-4000-8000-000000000099" {
+ body {
+ Branch {
+ guard "else";
+ occurrences [@scenario_oa_59];
+ };
+ };
+ description "";
+ parent @scenario_oa_51;
+ postcondition "";
+ precondition "";
+ }
+
+ oa_occurrence scenario_oa_54 "OA Inner ALT" id "5ceaa100-0000-4000-8000-00000000009a" {
+ body {
+ Fragment {
+ branches [@scenario_oa_55, @scenario_oa_56];
+ operator Alternative;
+ };
+ };
+ description "";
+ parent @scenario_oa_52;
+ postcondition "";
+ precondition "";
+ }
+
+ oa_occurrence scenario_oa_55 "OA Inner chosen operand" id "5ceaa100-0000-4000-8000-00000000009b" {
+ body {
+ Branch {
+ guard "ticket.ready";
+ occurrences [@scenario_oa_57];
+ };
+ };
+ description "";
+ parent @scenario_oa_54;
+ postcondition "";
+ precondition "";
+ }
+
+ oa_occurrence scenario_oa_56 "OA Inner other operand" id "5ceaa100-0000-4000-8000-00000000009c" {
+ body {
+ Branch {
+ guard "else";
+ occurrences [@scenario_oa_58];
+ };
+ };
+ description "";
+ parent @scenario_oa_54;
+ postcondition "";
+ precondition "";
+ }
+
+ oa_occurrence scenario_oa_57 "OA Inner anchor" id "5ceaa100-0000-4000-8000-00000000009d" {
+ body {
+ Action {
+ action @scenario_oa_10;
+ lifeline @scenario_oa_40;
+ };
+ };
+ description "";
+ parent @scenario_oa_55;
+ postcondition "";
+ precondition "";
+ }
+
+ oa_occurrence scenario_oa_58 "OA Inner other anchor" id "5ceaa100-0000-4000-8000-00000000009e" {
+ body {
+ Action {
+ action @scenario_oa_10;
+ lifeline @scenario_oa_40;
+ };
+ };
+ description "";
+ parent @scenario_oa_56;
+ postcondition "";
+ precondition "";
+ }
+
+ oa_occurrence scenario_oa_59 "OA Fallback anchor" id "5ceaa100-0000-4000-8000-00000000009f" {
+ body {
+ Action {
+ action @scenario_oa_11;
+ lifeline @scenario_oa_41;
+ };
+ };
+ description "";
+ parent @scenario_oa_53;
+ postcondition "";
+ precondition "";
+ }
+
+ oa_occurrence scenario_oa_60 "OA First use of B" id "5ceaa100-0000-4000-8000-0000000000a0" {
+ body {
+ ScenarioUse {
+ bindings [{
+ local @scenario_oa_40;
+ referenced @scenario_oa_42;
+ }, {
+ local @scenario_oa_41;
+ referenced @scenario_oa_43;
+ }];
+ scenario @scenario_oa_31;
+ };
+ };
+ description "";
+ parent @scenario_oa_30;
+ postcondition "";
+ precondition "";
+ }
+
+ oa_occurrence scenario_oa_61 "OA Second use of B" id "5ceaa100-0000-4000-8000-0000000000a1" {
+ body {
+ ScenarioUse {
+ bindings [{
+ local @scenario_oa_40;
+ referenced @scenario_oa_42;
+ }, {
+ local @scenario_oa_41;
+ referenced @scenario_oa_43;
+ }];
+ scenario @scenario_oa_31;
+ };
+ };
+ description "";
+ parent @scenario_oa_30;
+ postcondition "";
+ precondition "";
+ }
+
+ oa_occurrence scenario_oa_62 "OA Root exit" id "5ceaa100-0000-4000-8000-0000000000a2" {
+ body {
+ Action {
+ action @scenario_oa_11;
+ lifeline @scenario_oa_41;
+ };
+ };
+ description "";
+ parent @scenario_oa_30;
+ postcondition "";
+ precondition "";
+ }
+
+ oa_occurrence scenario_oa_63 "OA B entry" id "5ceaa100-0000-4000-8000-0000000000a3" {
+ body {
+ Action {
+ action @scenario_oa_10;
+ lifeline @scenario_oa_42;
+ };
+ };
+ description "";
+ parent @scenario_oa_31;
+ postcondition "";
+ precondition "";
+ }
+
+ oa_occurrence scenario_oa_64 "OA B use of C" id "5ceaa100-0000-4000-8000-0000000000a4" {
+ body {
+ ScenarioUse {
+ bindings [{
+ local @scenario_oa_42;
+ referenced @scenario_oa_44;
+ }, {
+ local @scenario_oa_43;
+ referenced @scenario_oa_45;
+ }];
+ scenario @scenario_oa_32;
+ };
+ };
+ description "";
+ parent @scenario_oa_31;
+ postcondition "";
+ precondition "";
+ }
+
+ oa_occurrence scenario_oa_65 "OA B exit" id "5ceaa100-0000-4000-8000-0000000000a5" {
+ body {
+ Action {
+ action @scenario_oa_11;
+ lifeline @scenario_oa_43;
+ };
+ };
+ description "";
+ parent @scenario_oa_31;
+ postcondition "";
+ precondition "";
+ }
+
+ oa_occurrence scenario_oa_66 "OA C entry" id "5ceaa100-0000-4000-8000-0000000000a6" {
+ body {
+ Action {
+ action @scenario_oa_10;
+ lifeline @scenario_oa_44;
+ };
+ };
+ description "";
+ parent @scenario_oa_32;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_function scenario_sa_10 "SA Inspect ticket" id "5ceaa100-0000-4000-8000-0000000000d2" {
+ description "";
+ parent none;
+ }
+
+ sa_function scenario_sa_11 "SA Record result" id "5ceaa100-0000-4000-8000-0000000000d3" {
+ description "";
+ parent none;
+ }
+
+ sa_state_machine scenario_sa_20 "SA Ticket modes" id "5ceaa100-0000-4000-8000-0000000000dc" {
+ description "";
+ parent none;
+ }
+
+ sa_region scenario_sa_21 "SA Ticket region" id "5ceaa100-0000-4000-8000-0000000000dd" {
+ description "";
+ parent @scenario_sa_20;
+ }
+
+ sa_state scenario_sa_22 "SA Ready" id "5ceaa100-0000-4000-8000-0000000000de" {
+ description "";
+ do_activity "";
+ entry "";
+ exit "";
+ kind State;
+ parent @scenario_sa_21;
+ }
+
+ sa_scenario scenario_sa_30 "SA A caller" id "5ceaa100-0000-4000-8000-0000000000e6" {
+ context none;
+ description "";
+ parent none;
+ postcondition "";
+ precondition "";
+ steps [@scenario_sa_50, @scenario_sa_51, @scenario_sa_60, @scenario_sa_61, @scenario_sa_62];
+ viewpoint Functional;
+ }
+
+ sa_scenario scenario_sa_31 "SA B verification" id "5ceaa100-0000-4000-8000-0000000000e7" {
+ context none;
+ description "";
+ parent none;
+ postcondition "";
+ precondition "";
+ steps [@scenario_sa_63, @scenario_sa_64, @scenario_sa_65];
+ viewpoint Functional;
+ }
+
+ sa_scenario scenario_sa_32 "SA C detail" id "5ceaa100-0000-4000-8000-0000000000e8" {
+ context none;
+ description "";
+ parent none;
+ postcondition "";
+ precondition "";
+ steps [@scenario_sa_66];
+ viewpoint Functional;
+ }
+
+ sa_lifeline scenario_sa_40 "SA A inspector" id "5ceaa100-0000-4000-8000-0000000000f0" {
+ description "";
+ parent @scenario_sa_30;
+ represented @scenario_sa_10;
+ }
+
+ sa_lifeline scenario_sa_41 "SA A recorder" id "5ceaa100-0000-4000-8000-0000000000f1" {
+ description "";
+ parent @scenario_sa_30;
+ represented @scenario_sa_11;
+ }
+
+ sa_lifeline scenario_sa_42 "SA B inspector" id "5ceaa100-0000-4000-8000-0000000000f2" {
+ description "";
+ parent @scenario_sa_31;
+ represented @scenario_sa_10;
+ }
+
+ sa_lifeline scenario_sa_43 "SA B recorder" id "5ceaa100-0000-4000-8000-0000000000f3" {
+ description "";
+ parent @scenario_sa_31;
+ represented @scenario_sa_11;
+ }
+
+ sa_lifeline scenario_sa_44 "SA C inspector" id "5ceaa100-0000-4000-8000-0000000000f4" {
+ description "";
+ parent @scenario_sa_32;
+ represented @scenario_sa_10;
+ }
+
+ sa_lifeline scenario_sa_45 "SA C recorder" id "5ceaa100-0000-4000-8000-0000000000f5" {
+ description "";
+ parent @scenario_sa_32;
+ represented @scenario_sa_11;
+ }
+
+ sa_occurrence scenario_sa_50 "SA Root entry" id "5ceaa100-0000-4000-8000-0000000000fa" {
+ body {
+ Action {
+ action @scenario_sa_10;
+ lifeline @scenario_sa_40;
+ };
+ };
+ description "";
+ parent @scenario_sa_30;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_occurrence scenario_sa_51 "SA Outer ALT" id "5ceaa100-0000-4000-8000-0000000000fb" {
+ body {
+ Fragment {
+ branches [@scenario_sa_52, @scenario_sa_53];
+ operator Alternative;
+ };
+ };
+ description "";
+ parent @scenario_sa_30;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_occurrence scenario_sa_52 "SA Outer chosen operand" id "5ceaa100-0000-4000-8000-0000000000fc" {
+ body {
+ Branch {
+ guard "ticket.present";
+ occurrences [@scenario_sa_54];
+ };
+ };
+ description "";
+ parent @scenario_sa_51;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_occurrence scenario_sa_53 "SA Outer fallback operand" id "5ceaa100-0000-4000-8000-0000000000fd" {
+ body {
+ Branch {
+ guard "else";
+ occurrences [@scenario_sa_59];
+ };
+ };
+ description "";
+ parent @scenario_sa_51;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_occurrence scenario_sa_54 "SA Inner ALT" id "5ceaa100-0000-4000-8000-0000000000fe" {
+ body {
+ Fragment {
+ branches [@scenario_sa_55, @scenario_sa_56];
+ operator Alternative;
+ };
+ };
+ description "";
+ parent @scenario_sa_52;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_occurrence scenario_sa_55 "SA Inner chosen operand" id "5ceaa100-0000-4000-8000-0000000000ff" {
+ body {
+ Branch {
+ guard "ticket.ready";
+ occurrences [@scenario_sa_57];
+ };
+ };
+ description "";
+ parent @scenario_sa_54;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_occurrence scenario_sa_56 "SA Inner other operand" id "5ceaa100-0000-4000-8000-000000000100" {
+ body {
+ Branch {
+ guard "else";
+ occurrences [@scenario_sa_58];
+ };
+ };
+ description "";
+ parent @scenario_sa_54;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_occurrence scenario_sa_57 "SA Inner anchor" id "5ceaa100-0000-4000-8000-000000000101" {
+ body {
+ Action {
+ action @scenario_sa_10;
+ lifeline @scenario_sa_40;
+ };
+ };
+ description "";
+ parent @scenario_sa_55;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_occurrence scenario_sa_58 "SA Inner other anchor" id "5ceaa100-0000-4000-8000-000000000102" {
+ body {
+ Action {
+ action @scenario_sa_10;
+ lifeline @scenario_sa_40;
+ };
+ };
+ description "";
+ parent @scenario_sa_56;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_occurrence scenario_sa_59 "SA Fallback anchor" id "5ceaa100-0000-4000-8000-000000000103" {
+ body {
+ Action {
+ action @scenario_sa_11;
+ lifeline @scenario_sa_41;
+ };
+ };
+ description "";
+ parent @scenario_sa_53;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_occurrence scenario_sa_60 "SA First use of B" id "5ceaa100-0000-4000-8000-000000000104" {
+ body {
+ ScenarioUse {
+ bindings [{
+ local @scenario_sa_40;
+ referenced @scenario_sa_42;
+ }, {
+ local @scenario_sa_41;
+ referenced @scenario_sa_43;
+ }];
+ scenario @scenario_sa_31;
+ };
+ };
+ description "";
+ parent @scenario_sa_30;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_occurrence scenario_sa_61 "SA Second use of B" id "5ceaa100-0000-4000-8000-000000000105" {
+ body {
+ ScenarioUse {
+ bindings [{
+ local @scenario_sa_40;
+ referenced @scenario_sa_42;
+ }, {
+ local @scenario_sa_41;
+ referenced @scenario_sa_43;
+ }];
+ scenario @scenario_sa_31;
+ };
+ };
+ description "";
+ parent @scenario_sa_30;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_occurrence scenario_sa_62 "SA Root exit" id "5ceaa100-0000-4000-8000-000000000106" {
+ body {
+ Action {
+ action @scenario_sa_11;
+ lifeline @scenario_sa_41;
+ };
+ };
+ description "";
+ parent @scenario_sa_30;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_occurrence scenario_sa_63 "SA B entry" id "5ceaa100-0000-4000-8000-000000000107" {
+ body {
+ Action {
+ action @scenario_sa_10;
+ lifeline @scenario_sa_42;
+ };
+ };
+ description "";
+ parent @scenario_sa_31;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_occurrence scenario_sa_64 "SA B use of C" id "5ceaa100-0000-4000-8000-000000000108" {
+ body {
+ ScenarioUse {
+ bindings [{
+ local @scenario_sa_42;
+ referenced @scenario_sa_44;
+ }, {
+ local @scenario_sa_43;
+ referenced @scenario_sa_45;
+ }];
+ scenario @scenario_sa_32;
+ };
+ };
+ description "";
+ parent @scenario_sa_31;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_occurrence scenario_sa_65 "SA B exit" id "5ceaa100-0000-4000-8000-000000000109" {
+ body {
+ Action {
+ action @scenario_sa_11;
+ lifeline @scenario_sa_43;
+ };
+ };
+ description "";
+ parent @scenario_sa_31;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_occurrence scenario_sa_66 "SA C entry" id "5ceaa100-0000-4000-8000-00000000010a" {
+ body {
+ Action {
+ action @scenario_sa_10;
+ lifeline @scenario_sa_44;
+ };
+ };
+ description "";
+ parent @scenario_sa_32;
+ postcondition "";
+ precondition "";
+ }
+
+}
diff --git a/harness/fixtures/scenario-authoring/base/rationale.arcadia b/harness/fixtures/scenario-authoring/base/rationale.arcadia
new file mode 100644
index 00000000..64b58187
--- /dev/null
+++ b/harness/fixtures/scenario-authoring/base/rationale.arcadia
@@ -0,0 +1,2 @@
+// Retain caller/operand evidence: ticket 雨.
+// Navigation never edits this note.
From 65a1fef17c2fe7b4f86cd18bed2d02a4806bc412 Mon Sep 17 00:00:00 2001
From: "sorokin.oleg.edu"
Date: Thu, 10 Sep 2026 11:25:06 -0700
Subject: [PATCH 10/14] Restore inline naming focus after recovery
---
crates/oa-desktop/src/app/checkpoints.rs | 182 +++++++++++++++++-
...release-palette-implementation-20260910.md | 24 +++
harness/release-palette-native-20260910.md | 23 +++
3 files changed, 228 insertions(+), 1 deletion(-)
diff --git a/crates/oa-desktop/src/app/checkpoints.rs b/crates/oa-desktop/src/app/checkpoints.rs
index 9f5525cd..4aa9a5ca 100644
--- a/crates/oa-desktop/src/app/checkpoints.rs
+++ b/crates/oa-desktop/src/app/checkpoints.rs
@@ -202,7 +202,16 @@ impl App {
} else {
"Recovered an unsaved local copy. Choose Save as to save it; the original file and hosted model are unchanged.".into()
};
- self.request_lint()
+ let lint = self.request_lint();
+ if self
+ .architecture
+ .as_ref()
+ .is_some_and(architecture_authoring::State::inline)
+ {
+ Task::batch([lint, iced::widget::operation::focus("architecture-name")])
+ } else {
+ lint
+ }
}
}
@@ -331,6 +340,177 @@ mod tests {
.unwrap()
}
+ async fn run_recovery_tasks(app: &mut App, task: Task) -> (usize, bool, bool) {
+ use iced::advanced::{Layout, layout, widget};
+ use iced::futures::StreamExt;
+ // Mount real inputs, with focus initially in another control. This
+ // verifies the returned operation targets the naming field by its ID.
+ let mut fields: iced::Element<'_, (), iced::Theme, ()> = iced::widget::column![
+ iced::widget::text_input("Engineering name", "Retained name")
+ .id("architecture-name")
+ .on_input(|_| ()),
+ iced::widget::text_input("Other control", "Other value")
+ .id("unrelated-field")
+ .on_input(|_| ())
+ ]
+ .into();
+ let mut tree = widget::Tree::new(fields.as_widget());
+ let node = fields.as_widget_mut().layout(
+ &mut tree,
+ &(),
+ &layout::Limits::new(iced::Size::ZERO, iced::Size::new(320., 100.)),
+ );
+ tree.children[1]
+ .state
+ .downcast_mut::>()
+ .focus();
+ let mut operations = 0;
+ let mut lint_completions = 0;
+ let mut stream = iced_runtime::task::into_stream(task).expect("Recovery must still lint");
+ while let Some(action) = stream.next().await {
+ match action {
+ iced_runtime::Action::Widget(mut operation) => {
+ operations += 1;
+ fields.as_widget_mut().operate(
+ &mut tree,
+ Layout::new(&node),
+ &(),
+ operation.as_mut(),
+ );
+ }
+ iced_runtime::Action::Output(message @ Message::Linted(_, _)) => {
+ lint_completions += 1;
+ let _ = app.update(message);
+ }
+ _ => panic!("Unexpected recovery task action"),
+ }
+ }
+ assert_eq!(
+ lint_completions, 1,
+ "Autofocus must not replace request_lint"
+ );
+ assert!(app.lint_running.is_none());
+ let focused = |index: usize| {
+ tree.children[index]
+ .state
+ .downcast_ref::>()
+ .is_focused()
+ };
+ (operations, focused(0), focused(1))
+ }
+
+ fn architecture_recovery_app() -> App {
+ let mut app = App::with_project(Project::empty());
+ let _ = app.update(Message::AddDiagram);
+ let _ = app.update(Message::Architecture(architecture_authoring::Event::Start(
+ Kind::Entity,
+ Some(Position { x: 80., y: 90. }),
+ )));
+ let _ = app.update(Message::Architecture(architecture_authoring::Event::Name(
+ "Accepted team".into(),
+ )));
+ let _ = app.update(Message::Architecture(architecture_authoring::Event::Submit));
+ assert!(app.architecture.is_none());
+ app
+ }
+
+ #[tokio::test]
+ async fn checkpoint_inline_create_and_rename_restore_focus_without_changing_drafts_or_history()
+ {
+ use architecture_authoring::Event;
+ for rename in [false, true] {
+ let mut app = architecture_recovery_app();
+ let point = Position { x: 439., y: 328.2 };
+ let event = if rename {
+ Event::Rename(app.route_context().unwrap(), app.selected, point)
+ } else {
+ Event::Start(Kind::Actor, Some(point))
+ };
+ let _ = app.update(Message::Architecture(event));
+ let _ = app.update(Message::Architecture(Event::Name(
+ " Recovered engineering name 雨 ".into(),
+ )));
+ let saved = serde_json::to_value(&app.architecture.as_ref().unwrap().saved).unwrap();
+ let snapshot = retained_snapshot(&app).with_architecture(app.architecture.as_ref());
+ let expected = snapshot.clone().restore().unwrap();
+ let checkpoint = snapshot.id;
+ let mut recovered = App::with_project(Project::empty());
+ let task = recovered.checkpoint_loaded(Ok(Arc::new(snapshot)));
+ assert_eq!(
+ run_recovery_tasks(&mut recovered, task).await,
+ (1, true, false)
+ );
+ assert_eq!(recovered.page, Page::Diagram);
+ assert!(recovered.architecture.as_ref().unwrap().inline());
+ assert_eq!(
+ serde_json::to_value(&recovered.architecture.as_ref().unwrap().saved).unwrap(),
+ saved
+ );
+ assert_eq!(recovered.project(), expected.document.project());
+ assert_eq!(recovered.document.bundle(), expected.document.bundle());
+ assert_eq!(recovered.document.revision(), expected.document.revision());
+ assert_eq!(recovered.document.can_undo(), expected.document.can_undo());
+ assert_eq!(recovered.document.can_redo(), expected.document.can_redo());
+ assert_eq!(
+ recovered.source_workspace.bundle(),
+ expected.workspace.bundle()
+ );
+ assert_eq!(recovered.recovery.restored, Some(checkpoint));
+ assert!(recovered.path.is_none());
+ }
+ }
+
+ #[tokio::test]
+ async fn checkpoint_noninline_contexts_keep_other_focus_and_still_lint() {
+ use architecture_authoring::Event;
+ for context in ["accepted", "armed", "picker", "property", "source"] {
+ let mut app = architecture_recovery_app();
+ match context {
+ "armed" => {
+ let _ = app.update(Message::Architecture(Event::Start(Kind::Actor, None)));
+ }
+ "picker" => {
+ let _ = app.update(Message::Architecture(Event::Existing(None)));
+ }
+ "property" => {
+ app.draft
+ .as_mut()
+ .unwrap()
+ .input("/name", "Unapplied property".into());
+ assert!(app.draft_dirty());
+ }
+ "source" => {
+ app.source_workspace
+ .edit("incomplete source 雨".into(), app.source_editor.cursor());
+ assert!(app.source_dirty());
+ }
+ _ => {}
+ }
+ let snapshot = retained_snapshot(&app).with_architecture(app.architecture.as_ref());
+ let expected = snapshot.clone().restore().unwrap();
+ let mut recovered = App::with_project(Project::empty());
+ let task = recovered.checkpoint_loaded(Ok(Arc::new(snapshot)));
+ assert_eq!(
+ run_recovery_tasks(&mut recovered, task).await,
+ (0, false, true),
+ "{context}"
+ );
+ assert!(
+ !recovered
+ .architecture
+ .as_ref()
+ .is_some_and(architecture_authoring::State::inline)
+ );
+ assert_eq!(recovered.project(), expected.document.project());
+ assert_eq!(recovered.document.bundle(), expected.document.bundle());
+ assert_eq!(recovered.document.revision(), expected.document.revision());
+ assert_eq!(
+ recovered.source_workspace.bundle(),
+ expected.workspace.bundle()
+ );
+ }
+ }
+
#[test]
fn saving_an_older_snapshot_keeps_recovery_for_newer_unsaved_work() {
let directory = tempfile::tempdir().unwrap();
diff --git a/harness/release-palette-implementation-20260910.md b/harness/release-palette-implementation-20260910.md
index f0001d76..c4e945d0 100644
--- a/harness/release-palette-implementation-20260910.md
+++ b/harness/release-palette-implementation-20260910.md
@@ -138,3 +138,27 @@ 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.
+
+## Recovery autofocus follow-up on integrated source
+
+Root's native candidate 2 confirmed that recovered inline naming was visible but
+needed a click to focus. In `app/checkpoints.rs`, recovery now batches the
+unchanged lint task with focus of `architecture-name` only when the restored
+architecture state is inline. Armed tools, existing-element pickers and other
+recovery contexts retain their existing focus behavior.
+
+Two tests consume the actual returned Task stream, operate on mounted Iced name
+and unrelated TextInputs, require lint completion and compare exact accepted
+source/model, raw draft, revision and history. Seven recovery contexts include
+Create/Rename and the non-inline/property/source controls. The integrated source
+at `057eddd` plus this checkpoint-only patch compiled in 4m 42s; all 13 checkpoint
+tests passed in 4.84s, with 605 filtered. Scoped rustfmt and diff check passed.
+Log: `.local/checkpoint-autofocus-tests-20260910.log`.
+
+Test artifact SHA256:
+`D504C59BC2A5853F4FBE54A9DFA841DA0AC8DD2475342E45A91DBAA6533372B4`.
+Root copied this immutable artifact into
+`.local/integration-20260910/tests-057eddd/` and is executing its full 618-test
+inventory separately while later scenario edits proceed. That result is pending;
+it does not include the later F1 nullable-constructor correction or workspace
+layout. No new GUI executable or native autofocus pass is claimed.
diff --git a/harness/release-palette-native-20260910.md b/harness/release-palette-native-20260910.md
index 1feb3e1a..d67116ed 100644
--- a/harness/release-palette-native-20260910.md
+++ b/harness/release-palette-native-20260910.md
@@ -116,3 +116,26 @@ 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.
+
+## Resumed native F2 check
+
+The user subsequently explicitly reauthorized Computer Use. Continued on the
+same separately staged candidate 2 (`E6D6B1D9…`), not the newly integrated source.
+Selecting Rescue team and pressing F2 opened the anchored, focused name editor.
+Before applying, an independent expected bundle was copied from the already
+oracle-verified Entity snapshot and changed only the unique quoted name in
+`oa/participants.arcadia` to `Rescue coordination team`; all other bytes and the
+complete file set were retained. Enter accepted the visible name, retained the
+same alias/UUID and geometry, and first-submit Ctrl+S saved it. All three saved
+files matched that expectation byte-for-byte. One Undo followed by Save passed
+the complete Entity model/three-source oracle. One Redo followed by Save matched
+the same name-only expectation exactly. Evidence is retained as
+`expected-entity-rename`, `checkpoint-entity-rename` and `rename-source-result.json`.
+The last Undo/Save returned the working fixture to the original Entity baseline.
+
+Input limitation observed: after Ctrl+A selected the original name, the first
+Computer Use type_text call left an empty field. A fresh observation confirmed
+that state; explicit refocus and a second type_text produced the intended name.
+The cause is not established, so this is not recorded as a proven application
+typing defect or as uninterrupted first-attempt typing. Applying, history and
+source persistence were tested only after the correct text was visible.
From 6d2b9619e316b14f08291258637500b202468db6 Mon Sep 17 00:00:00 2001
From: "sorokin.oleg.edu"
Date: Thu, 10 Sep 2026 11:38:10 -0700
Subject: [PATCH 11/14] Add portable Linux packaging and verify distribution
notices
---
.gitattributes | 5 +
.gitignore | 1 +
README.md | 4 +
deployment/Dockerfile | 11 +
deployment/Dockerfile.dockerignore | 29 +-
docs/linux-packaging.md | 65 +++
docs/server.md | 8 +-
docs/windows-packaging.md | 12 +-
...elease-cantarell-notice-review-20260910.md | 70 ++++
...lease-packaging-implementation-20260910.md | 51 +++
...e-packaging-independent-review-20260910.md | 59 +++
scripts/package-artifacts.py | 369 ++++++++++++++++++
scripts/package-common.ps1 | 76 ++++
scripts/package-linux.ps1 | 98 +++++
scripts/package-readme.md | 11 +
scripts/package-windows.ps1 | 37 +-
scripts/test-package-artifacts.py | 211 ++++++++++
scripts/test-windows-notices.ps1 | 24 +-
scripts/windows-notices.ps1 | 36 +-
.../Cantarell-Regular-NOTICE.txt | 31 ++
scripts/windows-notices/Fira-OFL.txt | 186 ++++-----
.../FiraSans-Regular-NOTICE.txt | 8 +-
.../GNOME-Cantarell-61362600e104-COPYING | 94 +++++
scripts/windows-notices/manifest.json | 19 +
24 files changed, 1374 insertions(+), 141 deletions(-)
create mode 100644 docs/linux-packaging.md
create mode 100644 harness/release-cantarell-notice-review-20260910.md
create mode 100644 harness/release-packaging-implementation-20260910.md
create mode 100644 harness/release-packaging-independent-review-20260910.md
create mode 100644 scripts/package-artifacts.py
create mode 100644 scripts/package-common.ps1
create mode 100644 scripts/package-linux.ps1
create mode 100644 scripts/package-readme.md
create mode 100644 scripts/test-package-artifacts.py
create mode 100644 scripts/windows-notices/Cantarell-Regular-NOTICE.txt
create mode 100644 scripts/windows-notices/GNOME-Cantarell-61362600e104-COPYING
diff --git a/.gitattributes b/.gitattributes
index 7250a748..e01692e4 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -23,3 +23,8 @@ crates/oa-desktop/src/recovery/fixtures/*.json -text
harness/fixtures/source7-native/** -text
harness/fixtures/basic-editing/** -text
harness/fixtures/scenario-authoring/base/** -text whitespace=cr-at-eol
+
+# Preserve checksum-bound font notices verbatim through Git round trips.
+scripts/windows-notices/GNOME-Cantarell-61362600e104-COPYING -text
+scripts/windows-notices/Fira-OFL.txt -text
+scripts/windows-notices/FiraSans-Regular-NOTICE.txt -text
diff --git a/.gitignore b/.gitignore
index 7b884746..7bb80b00 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,4 @@
/model-store/
/.local/
.arcadia-save-*/
+__pycache__/
diff --git a/README.md b/README.md
index 7b8bdb86..f091d7e5 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,8 @@ Build both desktop and server release executables:
cargo build --release --workspace --locked
```
+Stage the complete teaching examples beside these executables so **Home → Open water-pressure walkthrough** works in an optimized source build. From the repository root, use `Copy-Item -Recurse -Force examples target/release/` in PowerShell or `cp -R examples target/release/` on Linux. If you set a different Cargo target directory or pass `--target`, use that build's executable directory instead of `target/release`. The packaging scripts include these examples automatically.
+
Run `target/release/arcadia-studio.exe` on Windows or `./target/release/arcadia-studio` on Linux. No task-specific cache directory is required. A graphical desktop and compatible graphics driver are required; the application uses Iced 0.14 with WGPU and X11/Wayland support. It does not require Java, Eclipse, a browser, an account or a running server.
An optional Windows path uses the `stable-x86_64-pc-windows-gnullvm` Rust toolchain with LLVM-MinGW. Put LLVM-MinGW's `bin` directory on `PATH`, including `clang.exe` and `llvm-windres.exe`, then run:
@@ -55,6 +57,8 @@ The desktop starts at **Home**, where you can create a model, open a local proje
The example opens in an architecture view with work nested inside its performers. Each layer also has dedicated functional dataflow and mission/capability views. Regenerate both example formats with matching identities using `cargo run --locked -p oa-core --example generate_demo`; the command verifies both saved models. Historical migration fixtures remain separate.
+The architecture diagram has a compact side palette; expand it to see tool names. Choose an element tool, click its position, enter its name in place and press **Enter**. **Escape** cancels the draft. Select a card and press **F2** to rename it. Right-click a participant and choose **Add activity** or **Add function** to create work with its performer allocation in one undoable edit. Ordinary palette creation leaves work unallocated. **Add existing** places a compatible model element in the current view; **Include already shown** offers **Locate** for existing placements.
+
1. In **Operational Analysis**, create entities, actors, activities and capabilities. Use **In selected** for semantic decomposition and **Allocated** for new leaf work performed by a participant. Activities can decompose independently of their performer; roles capture shared operational responsibility. **Communication means** connects operational Entities or Actors directly, with direction and carried items; **Exchange allocation** associates the Interactions it carries. Operational communication requires no system ports.
2. Switch to **System Analysis**. Define one System of Interest and external Actors, then functions, missions, capabilities and functional chains. The system boundary has no internal LA/PA components in this version.
3. Select a System or System Actor and choose **Add port** in the browser or Properties. Choose Standard or Flow, then **Create port**. Use **Boundary exchange** in the diagram palette, or **Draw boundary exchange** on a selected visible source port, to connect compatible component ports directly. Create the other participant's matching port first if none exists. Functions expose separate input/output port actions; the **Functional exchange** palette tool connects them. **Actions** exposes other engineering relationships and off-diagram configuration; **Data & interfaces** holds reusable payloads and contracts.
diff --git a/deployment/Dockerfile b/deployment/Dockerfile
index 3bdf39d2..198c5c45 100644
--- a/deployment/Dockerfile
+++ b/deployment/Dockerfile
@@ -1,9 +1,19 @@
+# syntax=docker/dockerfile:1
+# Requires --build-context server_notices=.
+# See docs/linux-packaging.md. Missing/stale notices fail before application build.
FROM rust:1.90-bookworm AS builder
+RUN apt-get update && apt-get install -y --no-install-recommends python3 \
+ && rm -rf /var/lib/apt/lists/*
WORKDIR /src
COPY Cargo.toml Cargo.lock ./
+COPY LICENSE ./LICENSE
COPY build ./build
COPY crates ./crates
COPY docs/openapi.json ./docs/openapi.json
+COPY scripts/package-artifacts.py ./scripts/package-artifacts.py
+COPY --from=server_notices / /tmp/server-notices/
+RUN python3 scripts/package-artifacts.py verify-notices \
+ --directory /tmp/server-notices --repository /src --target x86_64-unknown-linux-gnu
RUN cargo build --release --locked -p oa-server
FROM debian:bookworm-slim
@@ -12,6 +22,7 @@ RUN groupadd --gid 10001 arcadia \
&& mkdir /data \
&& chown 10001:10001 /data
COPY --from=builder /src/target/release/arcadia-server /usr/local/bin/arcadia-server
+COPY --from=builder /tmp/server-notices/ /usr/share/doc/arcadia-server/
USER 10001:10001
VOLUME ["/data"]
EXPOSE 8787
diff --git a/deployment/Dockerfile.dockerignore b/deployment/Dockerfile.dockerignore
index d4695eba..0c32edaf 100644
--- a/deployment/Dockerfile.dockerignore
+++ b/deployment/Dockerfile.dockerignore
@@ -1,9 +1,24 @@
-.git
-target
-dist
-harness
+# Only inputs consumed by this Dockerfile enter the ordinary repository context.
+# The server_notices named context is supplied separately and verified in builder.
+**
+!Cargo.toml
+!Cargo.lock
+!LICENSE
+!build/
+!build/**
+!crates/
+!crates/**
+!docs/
+!docs/openapi.json
+!scripts/
+!scripts/package-artifacts.py
+!deployment/
+!deployment/Dockerfile
+!deployment/Dockerfile.dockerignore
+# Do not admit generated/private files even inside an allowed source tree.
**/target
-**/*.sqlite3
+**/.git
+**/.env
+**/.env.*
**/*.log
-.codex
-.openai
+**/*.sqlite3
diff --git a/docs/linux-packaging.md b/docs/linux-packaging.md
new file mode 100644
index 00000000..26847e1e
--- /dev/null
+++ b/docs/linux-packaging.md
@@ -0,0 +1,65 @@
+# Linux packaging and deployment inputs
+
+The packaging command stages **already-built** desktop/server executables for `x86_64-unknown-linux-gnu`. It runs on Windows PowerShell 5.1 or PowerShell 7 (including Linux) with Python 3.11+ standard library, Git, and the matching Rust/Cargo dependency cache. It does not build, download, install runtime libraries or launch the application. Python creates and reads back the tar archive; end users do not need Python, PowerShell or Rust.
+
+Build and validate first in a source checkout. The current runtime validation baseline is Ubuntu 24.04 x86-64. A glibc symbol inventory is not a claim that every distribution with that glibc version supports the renderer and dialogs.
+
+```powershell
+./scripts/package-linux.ps1 -Target x86_64-unknown-linux-gnu `
+ -Version 0.2.0-rc1 -BinaryDirectory ./target/release -Python python3
+```
+
+For an explicit-target build, use `target/x86_64-unknown-linux-gnu/release`. A cross-build may supply another directory containing both `arcadia-studio` and `arcadia-server`. Select the Rust compiler used for the intended build before packaging; its Linux target libraries, copyright reports and locked source/archive cache must be installed. Read-only `cargo metadata --locked --offline --filter-platform x86_64-unknown-linux-gnu` and Rust version/sysroot queries are the only Rust commands run by packaging. When packaging from Windows, `-Python C:/path/to/python.exe` selects Python explicitly.
+
+The script refuses existing output/claim names. It creates `dist/ArcadiaStudio--x86_64-unknown-linux-gnu/`, a `.tar.gz`, and an adjacent `.sha256`. A failed attempt leaves its claim and any partial outputs for diagnosis; choose a fresh version or output root rather than overwriting them. Archives use sorted paths, zero timestamps and uid/gid, 0755 for the two Linux executables and 0644 for other files. Every entry is read back and compared against staging, including permissions. The same unchanged inputs on the same Python/zlib implementation produce the same bytes; this does not promise reproducible compiler output.
+
+## Contents and provenance
+
+Both executables, the MIT license, a package-specific README, complete tracked examples (including all water-pressure teaching/evidence files), deployment references and the reachable documentation closure are included. Relative Markdown/reST/HTML file links are checked before archiving; upstream license texts are preserved verbatim and their internal source-tree hyperlinks are outside that documentation check. Untracked private files, caches and unrelated harness history are not included merely because they exist on disk.
+
+`package.json` records input hashes and static ELF dependencies. `SHA256SUMS.txt` inventories every file except itself; the archive checksum covers that inventory too. `dependency-notices.json`, `THIRD-PARTY-NOTICES.txt` and `licenses/` use the same strict collector as Windows: exact target-filtered normal/build dependency graph, locked crate archives, actual source hashes, checksum-bound supplements and embedded fonts, Rust distribution notices and target-library hashes. Unknown sources, absent notices or changed supplements stop packaging. No Linux system libraries are bundled by this pipeline; their runtime distribution supplies them and its notices.
+
+ELF preflight requires little-endian x86-64 executables/PIE, the glibc dynamic loader, no RPATH/RUNPATH and a reviewed set of direct libc/compiler-runtime imports. Unknown libraries require an explicit packaging design instead of being silently copied from a developer machine. Graphics/window/portal libraries loaded at runtime are outside that static import list.
+
+Optionally pass `-BuildEvidence `, containing nonempty `source_identity`, `profile`, `compiler`, exact `target`, and `binary_sha256` keyed by `arcadia-studio` and `arcadia-server`. Hash strings must be lowercase SHA256. Target and binary hashes are checked; other fields remain explicit caller assertions. Omission records build provenance as unknown. Never label a development binary an optimized release because of its directory or package version.
+
+## Native extracted-bundle checks
+
+Extract into a fresh user-writable Linux directory and run from an unrelated working directory. Verify SHA256SUMS, file permissions, `file`/`ldd` and actual desktop/runtime libraries. A typical Ubuntu runtime needs X11/XKB and Wayland clients, Vulkan loader/Mesa Vulkan drivers, and working session DBus plus an xdg-desktop-portal backend for file dialogs. A successful `ldd` alone is insufficient because these libraries can be loaded dynamically.
+
+For a software Vulkan trial, first locate the installed lavapipe ICD; only if it exists, use:
+
+```sh
+WGPU_BACKEND=vulkan VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.x86_64.json ./arcadia-studio
+```
+
+A compatible native GPU may run without these variables. WSLg uses real Linux user space with a particular display environment; record the actual X11/Wayland backend and do not infer support for all Linux sessions from one trial. Exercise Home walkthrough, Help/search, native Open/Save dialogs, a multi-file project, diagram/source edits, Undo/Redo and reopening at normal/minimum window sizes. Compare complete saved model/source and inspect stderr, not merely whether a window appeared.
+
+Run the server against a separate local data directory and unused loopback port. Follow [server configuration](server.md) for health, authentication, ETags, restart and backup/restore. Test create/import, a changed conditional update, unchanged no-op, stale/invalid rejection, and persistence after a clean restart. Record exact binary/archive/source/compiler hashes and platform. Packaging verification is separate from current-tree release acceptance.
+
+## Container server notices and context
+
+The Dockerfile is x86-64 Linux only and requires BuildKit named build contexts. Before its application build it verifies a prepared server-only notice bundle against Cargo.lock, Rust compiler release/commit, exact Linux target-library hashes, notice-file checksums and workspace source hashes. This prevents using Windows dependency notices or a stale source/compiler inventory as the container's notice claim.
+
+In the source checkout, select **Rust 1.90.0** and its `x86_64-unknown-linux-gnu` target libraries to match `rust:1.90-bookworm`. Populate the locked dependency cache through your ordinary documented build/setup first; notice collection itself remains offline. On a Windows host, a Rust 1.90 GNU-host toolchain with installed Linux target libraries can provide this read-only inventory. Prepare a fresh output root:
+
+```powershell
+./scripts/package-linux.ps1 -Target x86_64-unknown-linux-gnu `
+ -ServerNoticesOnly -OutputRoot ./.local/container-notices-rc1
+```
+
+This writes `.local/container-notices-rc1/server-notices` and a sibling claim file. Pass only the notice directory as the named context:
+
+```sh
+docker buildx build --platform linux/amd64 --load \
+ --build-context server_notices=./.local/container-notices-rc1/server-notices \
+ -f deployment/Dockerfile -t arcadia-server:0.2.0-rc1 .
+```
+
+The Dockerfile-specific ignore file allows only its ordinary source/build inputs; `.local`, model data, `.env` files and arbitrary caches are excluded from that root context. The separate named context includes only the explicitly prepared notices. The image retains them at `/usr/share/doc/arcadia-server/`, alongside Debian's own distribution notices. Do not pass a broad cache or home directory as the named context. Updating Cargo.lock, workspace sources or the compiler requires regenerating the bundle under a fresh output name.
+
+The image runs as UID/GID 10001 and expects its data volume and token as described in [server configuration](server.md). Verify the actual image build, service health/authentication, writable volume and restart on a Docker-capable Linux runner. A Dockerfile review and successful local package checks do not establish container build/runtime acceptance.
+
+## Runner-independent validation commands
+
+Run `cargo fmt --all -- --check`, `cargo clippy --locked --workspace --all-targets -- -D warnings`, `cargo test --locked --workspace --all-targets`, and `cargo build --release --locked --workspace --bins` with the explicit supported compiler and native prerequisites. Run `python3 scripts/test-package-artifacts.py` and the PowerShell notice contract checks. Then package the resulting binaries and perform the native checks above. These commands are usable on a suitably provisioned runner; this document does not assert that GitLab, Gitea or GitHub has an active runner for this repository.
diff --git a/docs/server.md b/docs/server.md
index 6a32ecb7..a1eb4c11 100644
--- a/docs/server.md
+++ b/docs/server.md
@@ -1,6 +1,6 @@
# Arcadia Studio server
-`arcadia-server` is a headless Rust REST service for the same schema-5 model, commands, source language, validation, artifacts and OA/SA traces used by the desktop. It stores each model and its authored source project together in an atomic JSON envelope. The service provides a deployment artifact and configuration examples; this repository does not assert that a remote instance has been deployed.
+`arcadia-server` is a headless Rust REST service for the same schema-7 model, commands, source language, validation, artifacts and OA/SA traces used by the desktop. It stores each model and its authored source project together in an atomic JSON envelope. The service provides a deployment artifact and configuration examples; this repository does not assert that a remote instance has been deployed.
The complete HTTP contract is [api.rst](api.rst); machine-readable schemas are in [openapi.json](openapi.json), also served at `/api/v1/openapi.json`. The original [service research](research-v2-service.md) discusses broader alternatives; retained history, SQLite, idempotency receipts and stored transition plans from that research are **not** the implemented storage contract.
@@ -68,10 +68,12 @@ Errors use `application/problem+json` with stable `code`, HTTP `status`, human `
## Linux deployment
-`deployment/Dockerfile` builds a headless image from the repository root:
+Prepare the server-only dependency notices using the pinned Rust toolchain as described in [Linux packaging](linux-packaging.md), then build the headless image from the repository root. BuildKit supplies this checksum-bound notice bundle as a separate context; the ordinary build context excludes local caches and model data.
```sh
-docker build -f deployment/Dockerfile -t arcadia-server:local .
+docker buildx build --platform linux/amd64 --load \
+ --build-context server_notices=./.local/container-notices-rc1/server-notices \
+ -f deployment/Dockerfile -t arcadia-server:local .
docker run --rm --name arcadia-server \
-p 127.0.0.1:8787:8787 \
-v arcadia-data:/data \
diff --git a/docs/windows-packaging.md b/docs/windows-packaging.md
index 5255a9d1..d18bcfa2 100644
--- a/docs/windows-packaging.md
+++ b/docs/windows-packaging.md
@@ -2,12 +2,12 @@
The standard build target is x86_64 MSVC. The optional gnullvm build uses LLVM-MinGW UCRT and currently needs `libunwind.dll` alongside both executables. Packaging is a separate operation on already-built inputs; it neither builds nor launches applications and does not confer release acceptance.
-Run from a Git checkout with Windows PowerShell 5.1 or PowerShell 7. Build and test first using the [README](../README.md). Select the actual output directory explicitly; a native-host Cargo build normally uses `target/release`, while an explicit `--target` uses `target//release`.
+Run from a Git checkout with Windows PowerShell 5.1 or PowerShell 7 and Python 3.11+ (standard library only). Build and test first using the [README](../README.md). Select the actual output directory explicitly; a native-host Cargo build normally uses `target/release`, while an explicit `--target` uses `target//release`. `-Python ` selects Python when it is not named `python` on PATH; it collects/checks the package-local documentation closure without running binaries.
Packaging also needs the build's matching `cargo` and `rustc` on the packaging process PATH, the locked dependency sources/archive cache, and installed Rust target libraries and copyright reports. The script invokes only read-only `cargo metadata --locked --offline --filter-platform ` and Rust version/sysroot queries. It does not download dependencies or build. These tools are packaging prerequisites; the extracted application does not require them.
```powershell
-# Standard MSVC outputs, already built:
+# MSVC outputs, already built with a verified runtime strategy:
./scripts/package-windows.ps1 -Target x86_64-pc-windows-msvc `
-Version 0.2.0-rc1 -BinaryDirectory ./target/release
@@ -17,11 +17,13 @@ Packaging also needs the build's matching `cargo` and `rustc` on the packaging p
-LlvmMinGWRoot C:/Tools/llvm-mingw-20260826-ucrt-x86_64
```
-The caller must supply the matching build target, version, and final binaries. PE headers establish x86_64 architecture, but do not prove a source revision or Cargo profile. `package.json` records input hashes and inspected imports; retain build/test evidence tied to those hashes separately. The version labels the package and does not change the embedded application version.
+The caller must supply the matching build target, version, and final binaries. PE headers establish x86_64 architecture, but do not prove a source revision or Cargo profile. `package.json` records input hashes and inspected imports; retain build/test evidence tied to those hashes separately. The version labels the package and does not change the embedded application version. Optional `-BuildEvidence ` requires nonempty `source_identity`, `profile`, `compiler`, exact `target`, and `binary_sha256` keyed by both executable filenames. Target and lowercase SHA256 values are checked; source/compiler/profile remain explicitly supplied assertions, never inferred from a directory name.
+
+Ordinary dynamic-CRT MSVC output can import VC redistributables and will be refused by the strict runtime check. One candidate recipe is an explicit-target build with `CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS=-C target-feature=+crt-static`; validate its actual imports and runtime on the intended platform before packaging. This guide does not assert that such a build has been performed. The existing GNU/LLVM recipe has its own exact libunwind staging below. Do not silence an unknown import or copy arbitrary DLLs to make packaging pass.
The script creates a fresh `dist/ArcadiaStudio--/`, matching ZIP, and ZIP `.sha256` file. `-OutputRoot` selects another location. Existing output names are refused; no directories are deleted or overwritten. A `.packaging.lock` claim and partial outputs remain after failure for inspection; retry under a fresh version/output location. ZIP file order and timestamps are fixed so unchanged inputs can be compared across runs; this is not a reproducible compiler-build guarantee.
-Each package includes desktop and server executables, README, MIT license, built-in Help in the application, tracked docs (including language/API specifications and OpenAPI), tracked examples and deployment recipes, current coverage/runtime notes, START-HERE, provenance, and SHA-256 inventory. It includes current working-tree contents of tracked files, so review uncommitted documentation edits before packaging. Untracked sample/user files are excluded. Repository sources are needed separately for the documented Cargo and Docker build recipes.
+Each package includes both executables, a package-specific README, MIT license, built-in Help, language/API specifications and OpenAPI, server/platform guides and their reachable documentation closure, all tracked examples and deployment references, START-HERE, provenance, and SHA256 inventory. Relative Markdown/reST/HTML file links are checked before archiving; upstream license texts retain their own source-tree links unchanged. Current working-tree contents of tracked inputs are copied, so review uncommitted documentation edits before packaging. Untracked sample/user files are excluded. Repository sources and tools are needed separately for Cargo/Docker recipes; the archive README makes this distinction explicit.
For gnullvm, the exact selected toolchain's x86_64 `libunwind.dll` and full distribution `LICENSE.TXT` are included; both are hashed. Standard MSVC packaging has no LLVM-MinGW coupling. The script reads ordinary PE imports recursively for packaged runtime DLLs, accepts a bounded list of Windows system components/API sets, and fails on unknown imports. For example, a build importing a VC redistributable DLL must have its redistribution and dependency handling resolved before packaging; it is not silently treated as part of Windows. Do not copy arbitrary DLLs from System32. Dynamically loaded graphics libraries and delay-load imports require the runtime check below.
@@ -64,6 +66,6 @@ try {
}
```
-This snippet only starts processes; it does not verify readiness. Check the server's health route documented in [server setup](server.md), import/read/update a model, restart with the same temporary store, and verify persistence. Close only the processes started for this smoke. Exercise the desktop's Home/example/open/save/reopen and Help, inspect real diagrams, and perform the [Save-as Undo/Redo recheck](../harness/release-save-as-runtime-packaging.md). Close normally and check stderr/exit state. A process that survives a few seconds alone is not adequate GUI evidence.
+This snippet only starts processes; it does not verify readiness. Check the server's health route documented in [server setup](server.md), import/read/update a model, restart with the same temporary store, and verify persistence. Close only the processes started for this smoke. Exercise Home/example/open/save/reopen and Help, inspect real diagrams, and verify a graphical edit → Save as → Undo → Redo → Save → reopen against the complete intended model/source. Close normally and check stderr/exit state. A process that survives a few seconds alone is not adequate GUI evidence.
Acceptance is pending until these checks are actually performed against the packaged final binaries. Historical V2 bundles, this machine's Cargo checks, and a successful development launch with compiler PATH are separate evidence.
diff --git a/harness/release-cantarell-notice-review-20260910.md b/harness/release-cantarell-notice-review-20260910.md
new file mode 100644
index 00000000..ba5a6604
--- /dev/null
+++ b/harness/release-cantarell-notice-review-20260910.md
@@ -0,0 +1,70 @@
+# Cantarell notice: independent review — 2026-09-10
+
+Reviewer: Codex-9e8r6t `/root/palette_finish`. Author: `/root/release_audit`.
+Scope: read-only font/notice provenance and proposed mapping review. Only this
+report is authored by the reviewer; no production, Cargo or native changes.
+The existing packaging reviewer was notified of this separate report ownership.
+
+## Verified evidence
+
+* Cached `sctk-adwaita-0.10.1/src/title/Cantarell-Regular.ttf` is 31,236 bytes,
+ SHA-256 `d15f25bcdaba2e25f54c32d4e29e68fedeb1b7a1d8fbc83f7c4475916e93b55f`.
+ Independent Python stdlib SFNT parsing confirms matching Mac and Windows name
+ records: version 0.05, FontForge date 21 March 2011, Dave Crossland, copyrights
+ Understanding Limited 2009–2011 and Jakub Steiner 2010–2011, and OFL 1.1.
+* The font inside the cached crate archive matches byte for byte. Archive SHA-256
+ `b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec`
+ matches Cargo.lock. The font also matches the raw font fetched independently
+ from the crate's recorded [upstream commit](https://github.com/PolyMeilex/sctk-adwaita/tree/d5f343c45c04d4a026db57f76760819b7325db00/src/title).
+ Cached production source embeds that font and explicitly identifies OFL 1.1;
+ the crate's separate MIT licence describes its code.
+* Independently resolving GNOME's annotated `0.0.5` tag gives tag object
+ `ce24531ad0bfd8b17d49fa5aa9e995cbd4abbf74`, then commit
+ `61362600e104b96c88a2fdc068e1e2c68aef4f16`. Its
+ [historical COPYING](https://raw.githubusercontent.com/GNOME/cantarell-fonts/61362600e104b96c88a2fdc068e1e2c68aef4f16/COPYING)
+ matches the embedded attribution and supplies full OFL 1.1 text. Local
+ `GNOME-Cantarell-61362600e104-COPYING` matches those upstream bytes exactly:
+ 4,534 bytes, SHA-256
+ `7bb92a19f8f5485713f6e96a8331289b3aae49e8e44e7cad58b567e03be16d76`.
+
+OFL 1.1 permits bundling and embedding with software while requiring inclusion
+of its copyright notice and licence; the font remains under OFL. The historical
+attribution is appropriate to these exact embedded bytes. This review does not
+claim a reproducible font build from the GNOME tag or identity with arbitrary
+Cantarell releases. [Official OFL text](https://openfontlicense.org/open-font-license-official-text/).
+
+## Proposed mapping and independent defect
+
+The author added exactly one asset mapping for package `sctk-adwaita`, version
+`0.10.1`, path `src/title/Cantarell-Regular.ttf`, the exact font SHA above and
+`OFL-1.1`. Both referenced notice hashes pass independent readback. The second
+file, `Cantarell-Regular-NOTICE.txt`, documents the extraction/provenance without
+claiming an upstream rebuild; SHA-256
+`2936eaab48b3b4ac2fa216a02f1bb0ccbbd8fe2227e9b930e526056f639def80`.
+
+The initial proposal had a confirmed byte-preservation defect: Git's configured
+text filters changed the upstream COPYING before staging. `git hash-object
+--no-filters` returned `1c603c58746e8f83f2cc112dc17765ace7e3af2e`, while
+`--path` returned `954cd7c4816a4e72287bc5eb75f98eb86cf3167f`. A fresh checkout
+would therefore fail the manifest's exact notice checksum. The reviewer
+reported this to root and author and recommended a targeted `-text` attribute
+for the unmodified upstream COPYING.
+
+The author applied the targeted `-text` rule with root's authorization.
+Independent post-fix `git check-attr` reports text unset; both raw and filtered
+Git hashes now equal `1c603c58746e8f83f2cc112dc17765ace7e3af2e`, while SHA-256
+remains the manifest's `7bb92a19…be16d76`. The extracted provenance notice was
+already stable: both Git hashes equal
+`c70e0fcdbf6846c1730052c9efddbd7c31389d08`. This resolves the reviewer's confirmed
+Cantarell blocker without changing the upstream notice or relaxing checks.
+
+**Verdict:** the exact Cantarell asset/notice mapping and its Git preservation
+rule pass this bounded independent review. Include the attribute change with
+the supplement files when committing. The author's broader Fira-byte repairs
+and all-supplement Git roundtrip are outside this Cantarell-specific verdict.
+
+The strict collector still validates the locked registry archive and the exact
+asset package/version/path/hash, requires nonempty matching notices, hashes
+their actual files and records their pinned HTTPS origins. No MIT fallback or
+relaxed embedded-font check is proposed. Full Linux notice preflight and final
+archive readback remain the author's/root's gates; this reviewer ran no Cargo.
diff --git a/harness/release-packaging-implementation-20260910.md b/harness/release-packaging-implementation-20260910.md
new file mode 100644
index 00000000..6c4f304c
--- /dev/null
+++ b/harness/release-packaging-implementation-20260910.md
@@ -0,0 +1,51 @@
+# Portable packaging implementation — 2026-09-10
+
+Codex-9e8r6t `/root/release_audit`, separate host from Codex-956atf's behavior/forms work. Root authorized only the packaging/helper/notice/Docker/platform-guide files and this report; root keeps README, server guide, built-in guide and shared ledgers. No commits or pushes by this agent. Existing runtime acceptance is not transferred to new artifacts.
+
+## Implemented contract
+
+* `scripts/package-linux.ps1` packages prebuilt x86-64 glibc desktop/server binaries or exports a server-only notice directory. No build, download, installation, server or GUI launch occurs. It retains explicit target, fresh-output claims, exact copies/hashes and strict notice preflight. ELF imports are inspected without execution; unknown dependencies, wrong architecture/loader and RPATH/RUNPATH require explicit resolution.
+* `scripts/package-common.ps1` shares documentation collection, notice writing, checksum staging and optional caller-supplied build evidence. Evidence must name source/profile/compiler/target and actual binary hashes; the packager checks target/hashes and labels the remaining statements as assertions, not inferred build provenance.
+* `scripts/package-artifacts.py` uses Python 3.11+ standard library for local document closure/link verification, bounded ELF table inspection, deterministic tar/gzip creation with Linux executable modes, archive readback, and container notice verification. Archive claims refuse overwriting. Direct imports and observed GLIBC strings are explicitly distinct from dynamically loaded graphics/portal dependencies and native compatibility.
+* Windows retains its original PE/runtime-DLL allowlist, exact libunwind provenance and recursive import checks. It now shares the package-specific README and a bounded reachable documentation closure; no wholesale harness copying. Both platforms include all 37 teaching/evidence files and complete tracked examples. License texts remain verbatim; their internal upstream source-tree hyperlinks are outside the product-document link check.
+* `Get-ArcadiaNotices` is the existing reviewed collector with selectable root packages and shared compiler-input hashes; the old Windows entry point remains a compatibility wrapper. The exact locked normal/build graph, registry archives, real source hashes, Rust target libraries, embedded font mapping and checksum-bound notice supplements retain their existing refusal behavior. Server-only output excludes the desktop root.
+* Docker's ordinary repository context is an allowlist. A separate BuildKit named `server_notices` context contains only the explicitly prepared notice directory. Builder verification checks exact Linux target/server root, Cargo.lock, Rust release/commit and target-library hashes, common build inputs, exact local crate source file sets, notice manifest and inventory references. The final image retains the bundle under `/usr/share/doc/arcadia-server`. Missing/stale notices fail before application compilation. No actual Docker build/runtime was possible on this host.
+
+Platform guides describe package prerequisites, extraction/runtime acceptance, provenance limits, the container notice preparation/build command, and runner-independent validation commands. Root owns the coordinating Docker-command update in `docs/server.md`.
+
+## Independent criticism addressed
+
+The independent packaging reviewer reproduced acceptance of changed root Cargo.toml, shared build script, embedded OpenAPI and newly added crate files against an old notice inventory. Fixed at the collector/verifier boundary by recording shared build inputs and reconciling exact local file sets. The reviewer also demonstrated that whitespace/case in HTML href syntax bypassed the regular expression; replaced that parsing with stdlib HTMLParser and added positive/negative coverage. An HTML base URL now fails explicitly because changing relative-link resolution needs a deliberate contract. Two further findings were corrected and independently reverified: ancestor Windows junctions could escape documentation roots, and a file added during tar readback escaped the final stage comparison. Python and PowerShell now reject reparse ancestors, and readback compares the final file set as well as hashes. See `harness/release-packaging-independent-review-20260910.md` and Gitea issue 1 comment 49.
+
+A real Linux desktop graph subsequently exposed an existing scanner false positive: ab_glyph's Rustdoc font examples reference development fonts outside its distributed crate. The collector now excludes full-line Rust `//` comments before scanning literal font macros; production macros still require an in-crate file and exact notice mapping. Containment is checked before relative-path slicing, with platform-appropriate path comparison. Synthetic comment/production regressions pass and the independent reviewer found no actionable defect in this narrow follow-up.
+
+The subsequent full Linux preflight correctly refused an unmapped actual `sctk-adwaita 0.10.1/src/title/Cantarell-Regular.ttf`. Added an exact asset mapping, unchanged historical OFL text and readable name-table attribution. The 31,236 font bytes have SHA256 `d15f25bcdaba2e25f54c32d4e29e68fedeb1b7a1d8fbc83f7c4475916e93b55f`; cached crate and [pinned crate-upstream font](https://raw.githubusercontent.com/PolyMeilex/sctk-adwaita/d5f343c45c04d4a026db57f76760819b7325db00/src/title/Cantarell-Regular.ttf) match exactly. The embedded Mac/Windows name records explicitly identify OFL 1.1, Version 0.05 and Understanding Limited/Jakub Steiner copyright years matching the [historical GNOME COPYING](https://raw.githubusercontent.com/GNOME/cantarell-fonts/61362600e104b96c88a2fdc068e1e2c68aef4f16/COPYING). The latter is 4,534 bytes, SHA256 `7bb92a19f8f5485713f6e96a8331289b3aae49e8e44e7cad58b567e03be16d76`, from peeled GNOME tag 0.0.5. Attribution notice SHA256: `2936eaab48b3b4ac2fa216a02f1bb0ccbbd8fe2227e9b930e526056f639def80`. This does not infer which GNOME source built sctk's font. Exact mapping success and changed-font-byte refusal were added to the synthetic contract. `/root/palette_finish` independently reviewed the upstream bytes/embedded records/historical notice in `harness/release-cantarell-notice-review-20260910.md`.
+
+That review found Git newline normalization would corrupt the new upstream notice's checksum. A read-only audit of all 25 supplement references also found the existing Fira OFL and extracted Fira notice had the same problem. Added three exact `-text` attributes. An isolated alternate Git index staged and checked out every referenced notice: all **25 raw/staged/restored SHA256 hashes equal their manifest values**. The root index was untouched. Existing Fira HEAD blobs are normalized (`5e4608f24a4b050232daa61b2d0d13cfe10317b6`, `0988a708ca1ce6534e1e94628bb47b3f7466d5ba`), while the manifest-matching raw files have Git hashes `b82722716de236b6d2c771e89a1c907006a677d5`, `0ab1f7af399b149c9d273f094ac84ec640cf3c24`. **Root must stage those two exact raw files with `git add --renormalize -- scripts/windows-notices/Fira-OFL.txt scripts/windows-notices/FiraSans-Regular-NOTICE.txt`**, alongside attributes; Git's existing stat cache can otherwise make them appear unchanged. This is a byte-preservation correction, not license text rewriting.
+
+## Verification and exact scopes
+
+* Python synthetic suite passes **11/11** (final rerun 0.365 seconds): deterministic bytes/modes/path ordering, overwrite refusal, late stage additions, ancestor reparse refusal, local-link completeness/traversal/fenced examples, HTML attribute variants, ELF acceptance/rejection/table bounds and container notice/compiler/library/source mismatch checks.
+* Existing PowerShell notice tests pass, including normal/build graph selection and prior archive/font/source/traversal/supplement refusals; added Linux server-only selection, target-specific runtime refusal and shared build-input inventory checks. All five changed/new PowerShell script parser checks passed.
+* Actual independent documentation closure: Windows **63 files / 15 local links**, Linux **62 files / 13 local links**, both **49 examples** including all **37** water-pressure teaching files, **0** harness files or sources escaping the checkout. Final package-local link checks run after copying actual inputs.
+* Preserved real Linux GUI package (38 files: old GUI plus its 37 teaching files) was archived without mutation or execution. Archive `.local/packaging-20260910/preserved-gui-archive-a.tar.gz`, **18,842,613 bytes**, SHA256 **`63170eea59c485430fd23f9eb19f8a161f40787f16df6c97db162354c2062187`**. Every entry was read back, content/mode/timestamp/uid/gid checked, and input stage hashes retained. Attempting to reuse that path failed; original archive hash stayed unchanged. This is an archive-helper/environment-fixture check, not a complete Linux distribution: no extracted real Linux server binary exists here outside preserved guest storage.
+* Linux command with a missing ELF directory refused before creating its output directory/claim. This exercised the actual PowerShell→Python error propagation.
+* Real frozen Windows source7 inputs were packaged twice in fresh ignored outputs. Final post-review package: `.local/packaging-20260910/windows-b/ArcadiaStudio-packaging-check-20260910-b-x86_64-pc-windows-gnullvm.zip`, **557 files**, SHA256 **`bc27e398d7268a4ee6e6c52ccc89f32bb0a4a970020ca198963501c49146e8c8`**. The strict current target graph contained 266 packages. It records `NativeAcceptance: Pending`; current docs/notices do not establish the old binaries' build source or compiler. The later comment-scanner-only adjustment is covered by its targeted regressions and does not transfer acceptance.
+* Actual Rust 1.90.0 Linux server-only notices: `.local/packaging-20260910/linux-server-notices-a/server-notices`, **102 packages / 178 files**, inventory SHA256 **`b196d64cf79b4b5a20c2145bccca4356e7af5f744f70ebb3d45c4d43470b899a`**. Local container-verifier execution passed against the source/compiler snapshot at collection. Subsequent integrated source changes require regeneration, as designed. This checks notices without claiming a server executable or Docker build.
+* **Final full Linux PowerShell adapter passed**, using two explicitly inert 1,024-byte ELF fixtures plus the actual **339-package** locked Linux dependency graph, strict Cantarell notices, documentation closure, caller-evidence validation and full tar readback. Archive `.local/packaging-20260910/linux-synthetic-b/ArcadiaStudio-synthetic-adapter-test-20260910-b-x86_64-unknown-linux-gnu.tar.gz`: **681 files / 1,575,171 bytes**, SHA256 **`b49362c15e740639e6c2890c354ab86db270ec813384db288a3d1b4a5dc8f6ca`**. Its package metadata explicitly identifies the inert fixtures, `compiler: none; constructed ELF headers only`, and `NativeAcceptance: Pending`. Package-local verification checked 13 links. This establishes the complete adapter path, not a runnable Linux desktop/server distribution. Evidence: `linux-synthetic-integration-b.log` and `linux-synthetic-summary.json`; the initial strict Cantarell refusal remains in `linux-synthetic-integration.log`.
+
+Logs and subsequent real package results are under `.local/packaging-20260910/`. Real notice runs are serialized after the local palette build's Cargo lease; they invoke only locked offline metadata and Rust identity/sysroot queries. No application compile or Linux provisioning is performed by this work.
+
+Retained evidence: `python-contracts.log`, `powershell-parser.log`, `notice-contracts.log`, `windows-package-b.log`, `linux-server-notices-a.log`, `linux-server-notices-verify.log`, `linux-server-notices-summary.json`, `preserved-gui-archive-a.log`, and `preserved-gui-overwrite-refusal.log`. Independent adversarial probes and actual closure manifests are under `.local/packaging-independent-review-20260910/`. Root explicitly authorized adding `__pycache__/` to `.gitignore`; generated helper bytecode is confirmed ignored.
+
+## File handoff
+
+Owned production changes: `.gitignore` (one bytecode-ignore line); `scripts/package-linux.ps1`, `scripts/package-common.ps1`, `scripts/package-readme.md`, `scripts/package-artifacts.py`, `scripts/test-package-artifacts.py`, `scripts/package-windows.ps1`, `scripts/windows-notices.ps1`, `scripts/test-windows-notices.ps1`; `deployment/Dockerfile`, `deployment/Dockerfile.dockerignore`; `docs/linux-packaging.md`, `docs/windows-packaging.md`. This implementation report is owned here; the separate independent review is owned by `/root/behavior_audit`. Root controls staging/commit and its README/server/guide/common-ledger edits. Final production file hashes are retained in `.local/packaging-20260910/implementation-input-hashes.json`.
+
+Root additionally authorized the exact notice-integrity additions: `.gitattributes` (three `-text` paths), `scripts/windows-notices/manifest.json`, `GNOME-Cantarell-61362600e104-COPYING`, `Cantarell-Regular-NOTICE.txt`, and the raw-byte restoration of `Fira-OFL.txt` and `FiraSans-Regular-NOTICE.txt`. The separate Cantarell review is owned by `/root/palette_finish`. New evidence paths: `.local/packaging-20260910/cantarell-name-table.json`, `notice-git-roundtrip-preflight.json`, `notice-roundtrip.log`, `notice-roundtrip/results.json`; retained upstream download and extraction script are in the same ignored task directory.
+
+Reproduction commands: `python scripts/test-package-artifacts.py`; `pwsh -NoProfile -File scripts/test-windows-notices.ps1`; the platform packager invocations in the owned guides, with a fresh output root and the exact prebuilt binary directory. The Windows check used preserved `dist/ArcadiaStudio-preview-source7-20260907-x86_64-pc-windows-gnullvm` binaries and `.local/toolchain/llvm-mingw-20260826-ucrt-x86_64`; it never implies a current optimized build. All real metadata queries were serialized under `CARGO_BUILD_JOBS=1`, locked/offline, with the selected installed target compiler. The only runtime provisioning was the earlier separately authorized failed WSL import, recorded in its own report.
+
+## Open release gates
+
+Current optimized Windows/Linux binaries and their native acceptance, a full Linux archive containing the real matching desktop/server inputs, actual container build/runtime and a usable Linux runner remain separate release gates. An old GUI helper archive or strict notice manifest is never presented as current application acceptance. Windows MSVC runtime/CRT strategy still requires an actual verified build; the import allowlist was not weakened to make an unverified recipe pass.
diff --git a/harness/release-packaging-independent-review-20260910.md b/harness/release-packaging-independent-review-20260910.md
new file mode 100644
index 00000000..9b726f84
--- /dev/null
+++ b/harness/release-packaging-independent-review-20260910.md
@@ -0,0 +1,59 @@
+# Independent packaging review — 2026-09-10
+
+Codex-9e8r6t `/root/behavior_audit`. This is the same local host as `/root/release_audit`, and a separate session from Codex-956atf. Root assigned a bounded independent review while cross-host scenario work remains pending. Reviewed current scripts, their delta/tests, deployment files, the implementation report, issue #1 and the release-readiness Wiki. Production files were read-only; findings were sent directly to the owner. No Cargo, Docker, application build or native application execution occurred. All probes used inert files under `.local/packaging-independent-review-20260910/` or the standalone synthetic notice test's reported temporary directory.
+
+Final result: four concrete defects reproduced, corrected by `/root/release_audit`, and independently reverified. No remaining actionable defect was established within this bounded review. This closes the findings below, not the separate native Linux/container release gates.
+
+## Ranked verified findings
+
+### P2 — Docker notice verification omitted shared build inputs and newly added crate files
+
+Locations: `scripts/package-artifacts.py::verify_notices`, `scripts/windows-notices.ps1::Get-ArcadiaNotices`, `deployment/Dockerfile` COPY/build steps. Real consumers are `crates/oa-server/build.rs:1` (`../../build/windows_manifest.rs`) and `crates/oa-server/src/routes.rs:110` (embedded `docs/openapi.json`).
+
+Before: a valid notice fixture still passed after changing root Cargo.toml, shared build script, embedded OpenAPI, or adding `crates/oa-server/src/bin/new_tool.rs`. The control mutation of an already listed `src/main.rs` correctly failed. Verification compared only listed local crate hashes, although Docker consumed additional files. This contradicted the stated refusal of stale source inventories; it was not a runtime or license-interpretation finding.
+
+Owner's correction: inventory root Cargo.toml/Cargo.lock/LICENSE, `build/**` and embedded OpenAPI, then compare the complete shared and local-crate file sets and hashes using the same exclusions as collection. **Independently reverified:** unchanged fixture passes; all five source mutations/additions refuse. Evidence: `probe.py` records the original hole; `probe_after.py` uses the expanded inventory and confirms refusal.
+
+Acceptance: the prepared notice bundle must cease to validate whenever a shared consumed file changes, a local source is added/removed, or an existing recorded file changes. A regenerated matching inventory must validate.
+
+### P2 — Documentation root inputs could follow an ancestor junction outside the checkout
+
+Locations: `scripts/package-artifacts.py::ordinary` and `document_inputs`; `scripts/package-common.ps1::Copy-PackageInputs`. Initial README/root collection applied a leaf symlink check but skipped the resolved repository-containment check used for recursively linked files. A leaf inside a Windows directory junction is not itself a symlink/reparse point.
+
+Before: an actual Windows junction from an inert fixture's `repository/examples` to sibling `external-sentinel` caused the mocked tracked `examples/sentinel.txt` to be returned as a package input even though its resolved path was outside that fixture repository. This is a concrete unintended-file inclusion boundary; no real private file was opened or included. The real repository documentation closure did not contain an escaping input in this review.
+
+Owner added ancestor reparse checks plus uniform root/README/linked-file containment, including both PowerShell copy paths. **Independently reverified with the actual Windows junction:** the input now refuses at `repository/examples`; the ordinary actual product closure still passes unchanged.
+
+Acceptance: the same tracked-path/junction fixture must refuse before copying; ordinary in-repository README, example and linked-document files must retain their current closure. Windows and Linux entry points must share the ordinary-input rule.
+
+### P2 — Valid HTML attributes bypassed the documentation link check
+
+Location: `scripts/package-artifacts.py::local_links`.
+
+Before: `Missing` produced `checked_local_document_links: 0` and passed. The original regex required exact lowercase, adjacent, quoted attribute syntax. This was a parser-contract defect demonstrated synthetically, not an observed broken link in today's bundle.
+
+Owner replaced HTML parsing with stdlib HTMLParser, including inline HTML in Markdown, and added attribute-variant coverage; unsupported HTML base URLs now refuse explicitly. **Independently reverified:** the same missing-file input now refuses. Current real documentation closures still pass.
+
+Acceptance: equivalent whitespace/case/quoting forms must resolve the same local target; a missing target must fail regardless of its valid HTML attribute spelling.
+
+### P3 — Tar readback could report unchanged staging after a file addition
+
+Location: `scripts/package-artifacts.py::make_archive`, final stage verification.
+
+Before: an isolated hook added `late-file.txt` to staging at archive readback. The helper returned one archived file and claimed `stage unchanged`, although the stage now held two files. Existing file hashes were rechecked, but the file set was not enumerated again. The archive's existing bytes/modes were correct; the specific unchanged-stage assertion was too strong.
+
+Owner added a final file-set comparison. **Independently reverified:** the same late-addition hook now refuses with `Stage changed while archiving or archive incomplete`; unchanged archive/determinism tests pass.
+
+Acceptance: an added or removed file during archiving/readback must fail unchanged-stage verification; unchanged inputs must keep deterministic bytes and refusal to overwrite existing archives.
+
+## Independent evidence and limits
+
+- Final standalone Python suite passed **11/11** (0.586 s); the PowerShell notice contract script passed all normal/build and server-only selection, runtime, archive, embedded-font, actual-source, unsupported-source, traversal and supplement checks again. Its final inert fixture was retained at `C:/Temp/arcadia-notices-1257d0d322b54d3e8431544c9267acc9`. The shell lacked a `python` PATH alias, so Python was explicitly selected from the bundled workspace runtime; this was an environment selection issue, not a packaging failure.
+- Final independent refusal outputs are retained in `.local/packaging-independent-review-20260910/final-probes.json`; actual documentation results are in `final-closure.json`. Baseline notices pass; changed root manifest, shared build script, OpenAPI, recorded source, added source, missing HTML target, escaping junction and late archive addition all refuse as intended.
+- `closure.py` independently copied and checked the actual product documentation. Windows: 63 files, 15 local file links. Linux: 62 files, 13 local file links. Both include all 49 example files, including 37 teaching files, with zero harness files and zero input paths resolving outside this repository. Their stages remain under the review's `.local` directory. This establishes the current reachable file closure; fragment anchors and third-party license source-tree links remain outside the stated check.
+- Independently ran the helper's ELF inspection on the preserved historical GUI at `.local/cross-linux-qa/gui-runs/20260907T1025486926611Z/package/arcadia-studio`: 72,953,296 bytes, SHA256 `330a3872d48ebf24ad21e6d4fa13cb6098bebb3cb7ff82a2e082925ccf3dd346`, expected x86-64 glibc interpreter, five allowed direct imports, no RPATH/RUNPATH. Observed GLIBC strings agree with the old report's 2.28 maximum. No additional concrete ELF defect was established within the helper's explicitly static scope.
+- Reviewed fresh-output checks, exclusive claim creation, `CreateNew`/`xb` archive creation, input hash checks, deterministic tar metadata and archive readback. Existing-path refusal is tested without changing the existing archive. Partial outputs/claims intentionally remain, as the platform guides state.
+- BuildEvidence checks actual binary hashes and target and explicitly labels source/profile/compiler as caller assertions. Package metadata and guides do not turn an old/development binary, a notice inventory, static ELF inspection, or successful archiving into current optimized/native release acceptance. These qualifications are material and should remain.
+- Docker's ordinary context is substantially narrowed and excludes root `.local` and unrelated directories; the named context is explicitly prepared notices. A real Docker build/runtime and current full Linux desktop/server package remain unexecuted release gates, as the implementation report states. This review did not invent native evidence from source inspection.
+
+No scenario, palette, root ledger, README or guide changes were made by this reviewer. The packaging owner owns the corrections and their implementation report; root owns native acceptance and integration.
diff --git a/scripts/package-artifacts.py b/scripts/package-artifacts.py
new file mode 100644
index 00000000..6d5cd20c
--- /dev/null
+++ b/scripts/package-artifacts.py
@@ -0,0 +1,369 @@
+#!/usr/bin/env python3
+"""Stdlib-only prebuilt artifact inspection, document closure and tar verification.
+
+Never builds, downloads, launches an input executable, or overwrites an archive.
+"""
+import argparse
+import gzip
+import hashlib
+import html
+from html.parser import HTMLParser
+import json
+from pathlib import Path, PurePosixPath
+import re
+import stat
+import struct
+import subprocess
+import sys
+import tarfile
+from urllib.parse import unquote, urlsplit
+
+
+def digest(path):
+ with Path(path).open('rb') as stream:
+ return hashlib.file_digest(stream, 'sha256').hexdigest()
+
+
+def ordinary(path):
+ path = Path(path).absolute()
+ if not path.is_file():
+ raise ValueError(f'Expected ordinary file: {path}')
+ reject_reparse(path)
+ return path
+
+
+def reject_reparse(path):
+ for candidate in (path, *path.parents):
+ info = candidate.lstat()
+ if stat.S_ISLNK(info.st_mode) or getattr(info, 'st_file_attributes', 0) & getattr(stat, 'FILE_ATTRIBUTE_REPARSE_POINT', 0):
+ raise ValueError(f'Symlink/reparse ancestor not permitted in package input: {candidate}')
+
+
+def local_links(path):
+ text = path.read_text(encoding='utf-8-sig')
+ if path.suffix.lower() == '.md':
+ # Commands/example syntax are not rendered Markdown links.
+ text = re.sub(r'(?ms)^\s*(`{3,}|~{3,}).*?^\s*\1\s*$', '', text)
+ text = re.sub(r'`[^`\n]*`', '', text)
+ links = re.findall(r'!?\[[^\]\n]*\]\(\s*(<[^>]*>|[^\s)]+)(?:\s+[^)]*)?\)', text)
+ links += re.findall(r'(?m)^\s*\[[^]\n]+\]:\s*(<[^>]*>|\S+)', text)
+ elif path.suffix.lower() == '.rst':
+ links = re.findall(r'`[^`\n]*<([^>]+)>`_', text)
+ links += re.findall(r'(?m)^\.\.\s+_[^:\n]+:\s*(\S+)', text)
+ elif path.suffix.lower() in ('.html', '.htm'):
+ links = []
+ else:
+ return []
+ class Links(HTMLParser):
+ def handle_starttag(self, tag, attrs):
+ if tag == 'base' and any(name == 'href' for name, _ in attrs):
+ raise ValueError(f'HTML base URL requires explicit document-closure support: {path}')
+ links.extend(value for name, value in attrs if name in ('href', 'src') and value)
+ Links().feed(text)
+ return [html.unescape(link.strip('<>')) for link in links]
+
+
+def linked_path(origin, link):
+ parsed = urlsplit(link)
+ if parsed.scheme or parsed.netloc or not parsed.path:
+ return None
+ decoded = unquote(parsed.path)
+ if '\\' in decoded or decoded.startswith('/'):
+ raise ValueError(f'Nonportable document link: {origin}: {link}')
+ parts = []
+ for part in (PurePosixPath(origin).parent / decoded).parts:
+ if part == '..':
+ if not parts:
+ raise ValueError(f'Document link escapes package: {origin}: {link}')
+ parts.pop()
+ elif part not in ('', '.'):
+ parts.append(part)
+ return '/'.join(parts)
+
+
+def document_inputs(repository, platform):
+ repository = Path(repository).resolve()
+ raw = subprocess.check_output(['git', '-C', str(repository), 'ls-files', '-z'])
+ tracked = set(raw.decode('utf-8').split('\0')) - {''}
+ roots = ['LICENSE', 'docs/server.md', 'docs/api.rst', 'docs/api.html',
+ 'docs/language.rst', 'docs/language.html', f'docs/{platform}-packaging.md',
+ 'deployment/Dockerfile', 'deployment/Dockerfile.dockerignore',
+ 'deployment/arcadia-server.service']
+ roots += sorted(name for name in tracked if name.startswith('examples/'))
+ # Two explicitly authored inputs can be packaged before their first commit.
+ permitted = tracked | set(roots) | {'scripts/package-readme.md', 'docs/linux-packaging.md', 'docs/windows-packaging.md'}
+ def repository_file(name):
+ candidate = ordinary(repository / name)
+ candidate.resolve().relative_to(repository)
+ return candidate
+ sources = {'README.md': repository_file('scripts/package-readme.md')}
+ for name in roots:
+ sources[name] = repository_file(name)
+ queue = list(sources)
+ while queue:
+ origin = queue.pop()
+ for link in local_links(sources[origin]):
+ relative = linked_path(origin, link)
+ if relative is None or relative in sources:
+ continue
+ if relative not in permitted:
+ raise ValueError(f'Untracked/missing package document link: {origin}: {link}')
+ candidate = repository_file(relative)
+ sources[relative] = candidate
+ queue.append(relative)
+ return {'files': [{'path': name, 'source': str(source)}
+ for name, source in sorted(sources.items())]}
+
+
+def check_documents(stage):
+ stage = Path(stage).resolve()
+ count = 0
+ for source in sorted(stage.rglob('*')):
+ if not source.is_file() or source.suffix.lower() not in ('.md', '.rst', '.html', '.htm'):
+ continue
+ relative = source.relative_to(stage).as_posix()
+ # Third-party/Rust source notices can contain their own source-tree links;
+ # preserve those upstream texts verbatim, rather than rewriting licenses.
+ if relative.startswith('licenses/'):
+ continue
+ for link in local_links(source):
+ target = linked_path(relative, link)
+ if target is not None:
+ ordinary(stage / target)
+ count += 1
+ return {'checked_local_document_links': count}
+
+
+def inspect_elf(path):
+ path = ordinary(path)
+ with path.open('rb') as stream:
+ data = stream.read()
+ if len(data) < 64 or data[:6] != b'\x7fELF\x02\x01':
+ raise ValueError(f'Expected ELF64 little-endian input: {path}')
+ kind, machine = struct.unpack_from(' len(data):
+ raise ValueError('Invalid/bounded ELF program table')
+ programs = [struct.unpack_from(' len(data):
+ raise ValueError('ELF segment escapes file')
+
+ def string(offset, end):
+ if not 0 <= offset < end <= len(data):
+ raise ValueError('Invalid ELF string range')
+ stop = data.find(b'\0', offset, min(end, offset + 4096))
+ if stop < 0:
+ raise ValueError('Unterminated/bounded ELF string')
+ return data[offset:stop].decode('ascii')
+
+ interpreter = None
+ dynamic = []
+ for ptype, _, offset, _, _, size, _, _ in programs:
+ if ptype == 3:
+ interpreter = string(offset, offset + size)
+ if ptype == 2:
+ if size // 16 > 65536 or size % 16:
+ raise ValueError('Invalid/bounded ELF dynamic table')
+ for entry in range(offset, offset + size, 16):
+ tag, value = struct.unpack_from(' 1 and args[1] == 'r:gz':
+ self.file('stage/late.txt', b'late')
+ return result
+ with patch.object(packaging.tarfile, 'open', side_effect=open_and_add):
+ with self.assertRaisesRegex(ValueError, 'Stage changed'):
+ packaging.make_archive(self.root / 'stage', self.root / 'archive.tar.gz', 'fixture')
+
+ def test_input_rejects_a_reparse_ancestor(self):
+ source = self.file('ordinary/file.txt')
+ original = Path.lstat
+ def reparse(path, **kwargs):
+ if path == source.parent:
+ result = original(path, **kwargs)
+ return type('ReparseStat', (), {'st_mode': result.st_mode, 'st_file_attributes': 1024})()
+ return original(path, **kwargs)
+ with patch.object(Path, 'lstat', reparse), patch.object(packaging.stat, 'FILE_ATTRIBUTE_REPARSE_POINT', 1024, create=True):
+ with self.assertRaisesRegex(ValueError, 'reparse ancestor'):
+ packaging.ordinary(source)
+
+ def test_local_document_links_are_complete_and_cannot_escape(self):
+ self.file('docs/README.md', b'[guide](guide%20one.md#section)\n[web](https://example.com)\n')
+ self.file('docs/guide one.md', b'# section\n')
+ self.assertEqual(packaging.check_documents(self.root / 'docs')['checked_local_document_links'], 1)
+ self.file('docs/README.md', b'[missing](missing.md)')
+ with self.assertRaises(ValueError):
+ packaging.check_documents(self.root / 'docs')
+ self.file('docs/README.md', b'[escape](../outside.md)')
+ with self.assertRaises(ValueError):
+ packaging.check_documents(self.root / 'docs')
+
+ def test_rst_directives_and_fenced_examples_are_not_links(self):
+ self.file('docs/a.rst', b'.. code-block:: rust\n\n sample\n')
+ self.file('docs/a.md', b'```text\n[example](absent.md)\n```\n`[sample](absent.md)`\n')
+ self.assertEqual(packaging.check_documents(self.root / 'docs')['checked_local_document_links'], 0)
+
+ def test_html_attribute_case_spacing_and_unquoted_links_are_checked(self):
+ self.file('docs/a.html', b'Missing')
+ with self.assertRaises(ValueError):
+ packaging.check_documents(self.root / 'docs')
+ self.file('docs/missing.html', b'ok')
+ self.file('docs/a.html', b'Present')
+ self.assertEqual(packaging.check_documents(self.root / 'docs')['checked_local_document_links'], 1)
+
+ def test_elf_accepts_reviewed_glibc_and_refuses_unknown_runtime(self):
+ self.assertEqual(packaging.inspect_elf(self.elf())['needed'], ['libc.so.6'])
+ with self.assertRaisesRegex(ValueError, 'Unreviewed'):
+ packaging.inspect_elf(self.elf(needed=b'private-runtime.so'))
+ with self.assertRaisesRegex(ValueError, 'RPATH'):
+ packaging.inspect_elf(self.elf(runpath=True))
+
+ def test_elf_rejects_wrong_architecture_truncation_and_loader(self):
+ with self.assertRaisesRegex(ValueError, 'glibc dynamic loader'):
+ packaging.inspect_elf(self.elf(interpreter=b'/lib/ld-musl-x86_64.so.1'))
+ elf = self.elf()
+ data = bytearray(elf.read_bytes())
+ struct.pack_into('
Date: Thu, 10 Sep 2026 12:03:17 -0700
Subject: [PATCH 12/14] Keep scenario authoring visible and restore exact
caller context
---
crates/oa-desktop/src/app.rs | 52 +-
crates/oa-desktop/src/behavior.rs | 19 +-
crates/oa-desktop/src/behavior/scenario.rs | 553 ++----------
.../src/behavior/scenario/workspace.rs | 748 ++++++++++++++++
.../src/behavior/scenario/workspace/tests.rs | 822 ++++++++++++++++++
crates/oa-desktop/src/views.rs | 12 +-
.../release-scenario-workspace-20260910.md | 46 +
7 files changed, 1744 insertions(+), 508 deletions(-)
create mode 100644 crates/oa-desktop/src/behavior/scenario/workspace.rs
create mode 100644 crates/oa-desktop/src/behavior/scenario/workspace/tests.rs
create mode 100644 harness/release-scenario-workspace-20260910.md
diff --git a/crates/oa-desktop/src/app.rs b/crates/oa-desktop/src/app.rs
index 44bf6d85..82135f1b 100644
--- a/crates/oa-desktop/src/app.rs
+++ b/crates/oa-desktop/src/app.rs
@@ -1103,6 +1103,7 @@ impl App {
Message::ScenarioCanvas(
crate::behavior::scenario::CanvasEvent::Scrolled(..)
| crate::behavior::scenario::CanvasEvent::Restored(..)
+ | crate::behavior::scenario::CanvasEvent::Viewport(..)
) | Message::TopologyCanvas(
crate::behavior::topology::CanvasEvent::Scrolled(..)
| crate::behavior::topology::CanvasEvent::Viewport(..)
@@ -1294,6 +1295,11 @@ impl App {
.topology
.enter(context.document, context.owner);
}
+ if let Some(context) = self.scenario_context() {
+ self.behavior
+ .scenario
+ .enter(context.document, context.scenario);
+ }
if self.connection.as_ref().is_some_and(|session| {
Some(session.context) != self.route_context()
|| self.page != Page::Diagram
@@ -1364,6 +1370,17 @@ impl App {
Message::ScenarioCanvas(event) => {
use crate::behavior::scenario::{self, CanvasEvent};
let context = event.context();
+ if let CanvasEvent::Viewport(_, size) = event {
+ if self.scenario_context().is_some_and(|current| {
+ current.document == context.document
+ && current.scenario == context.scenario
+ && current.revision == context.revision
+ }) && self.behavior.scenario.viewport(size)
+ {
+ self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
+ }
+ return Task::none();
+ }
if let CanvasEvent::Restored(_, token, area, offset) = event {
if self.scenario_context().is_some_and(|current| {
current.document == context.document && current.scenario == context.scenario
@@ -1377,7 +1394,7 @@ impl App {
return Task::none();
}
if let CanvasEvent::Scrolled(_, area, offset) = event {
- // Inner and outer scroll notifications may come from the
+ // Canvas and outline notifications may come from the
// same frame. Accept both across the first epoch bump,
// while still rejecting another owner/document/revision.
if self.scenario_context().is_some_and(|current| {
@@ -1394,6 +1411,19 @@ impl App {
return Task::none();
}
match event {
+ CanvasEvent::Focus(_) => {
+ self.behavior.scenario.toggle_focus();
+ self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
+ }
+ CanvasEvent::Shelf(_, shelf) => {
+ self.behavior.scenario.toggle_shelf(shelf);
+ self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
+ }
+ CanvasEvent::Zoom(_, scale) => {
+ if self.behavior.scenario.zoom(scale) {
+ self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
+ }
+ }
CanvasEvent::OpenUse(_, occurrence) => {
if self.behavior.has_pending() || self.draft_dirty() || self.source_dirty()
{
@@ -1405,6 +1435,9 @@ impl App {
self.behavior.scenario = state;
match target {
Ok(target) => {
+ // A command can preselect this target while the
+ // caller remains visible, bypassing select below.
+ self.behavior.active_owner = Some(target);
self.select(target);
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
}
@@ -1423,12 +1456,23 @@ impl App {
let caller = state.back(self.project(), context);
self.behavior.scenario = state;
if let Some(caller) = caller {
+ // A command may already have selected this UUID while
+ // leaving the referenced definition open. select's
+ // same-UUID fast path must not keep that old owner.
+ self.behavior.active_owner = Some(caller.owner);
self.select(caller.occurrence);
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
if let Some(context) = self.scenario_context() {
self.behavior.scenario.restore_caller(context, caller);
}
} else {
+ // An edit can select a retargeted caller outside the
+ // definition that remains visible after rejected Back.
+ if crate::behavior::selected_behavior(self.project(), self.selected)
+ != Some(context.scenario)
+ {
+ self.select(context.scenario);
+ }
self.status =
"The calling scenario reference is no longer available.".into();
}
@@ -1443,7 +1487,11 @@ impl App {
}
}
CanvasEvent::Choose(_, tool) => {
- if self.behavior.has_pending() || !self.apply() {
+ if self.behavior.has_pending() || self.draft_dirty() || self.source_dirty()
+ {
+ self.status =
+ "Apply or cancel pending edits before choosing a scenario tool."
+ .into();
return Task::none();
}
self.behavior.scenario.choose_tool(context.scenario, tool);
diff --git a/crates/oa-desktop/src/behavior.rs b/crates/oa-desktop/src/behavior.rs
index 8726fd75..c3605f65 100644
--- a/crates/oa-desktop/src/behavior.rs
+++ b/crates/oa-desktop/src/behavior.rs
@@ -32,12 +32,12 @@ mod scenario_scroll {
use iced::advanced::{Clipboard, Layout, Shell, Widget, layout, renderer, widget};
use iced::{Length, Vector, overlay};
- pub(super) fn observe<'a>(
+ pub(super) fn observe<'a, R: renderer::Renderer + 'a>(
context: scenario::Context,
area: scenario::ScrollArea,
restore: Option,
- content: impl Into>,
- ) -> Element<'a, Message> {
+ content: impl Into>,
+ ) -> Element<'a, Message, Theme, R> {
Element::new(Observer {
content: content.into(),
context,
@@ -922,20 +922,21 @@ pub fn view(app: &App) -> Element<'_, Message> {
return out.push(label("Start with a Process, Scenario or State machine using Create element. Add occurrences or regions inside it to describe behavior.")).padding(24).into();
};
let e = &p.elements[&selected.id];
- let compact_process = matches!(e.kind(), Kind::Process | Kind::FunctionalChain);
+ let compact_canvas = matches!(
+ e.kind(),
+ Kind::Process | Kind::FunctionalChain | Kind::Scenario
+ );
if e.kind() == Kind::StateMachine {
out = column![text("States & modes").size(20)].spacing(8);
- } else if compact_process {
- // The process canvas owns its compact header and remaining-height layout.
+ } else if compact_canvas {
+ // The process/scenario canvas owns its compact remaining-height layout.
out = column![].spacing(6);
- } else if matches!(e.kind(), Kind::Process | Kind::FunctionalChain) {
- out = column![text("Processes & functional chains").size(20)].spacing(8);
}
out = out.push(
pick_list(choices, Some(selected), Message::BehaviorOwner)
.width(Fill)
.text_size(13)
- .padding(if compact_process { 6 } else { 10 }),
+ .padding(if compact_canvas { 6 } else { 10 }),
);
if matches!(e.kind(), Kind::Process | Kind::FunctionalChain) {
return topology::view(app, e.id, out);
diff --git a/crates/oa-desktop/src/behavior/scenario.rs b/crates/oa-desktop/src/behavior/scenario.rs
index 0d7f86d7..82f37e95 100644
--- a/crates/oa-desktop/src/behavior/scenario.rs
+++ b/crates/oa-desktop/src/behavior/scenario.rs
@@ -4,6 +4,8 @@ use super::topology::{occurrence, unique_batch};
use super::*;
use iced::widget::{Column, column, text_input};
use std::{cell::RefCell, collections::BTreeSet};
+mod workspace;
+pub use workspace::{Shelf, view};
#[cfg(test)]
#[path = "scenario_operation_tests.rs"]
@@ -94,6 +96,10 @@ impl std::fmt::Display for CanvasTool {
pub enum CanvasEvent {
Scrolled(Context, ScrollArea, iced::Vector),
Restored(Context, Id, ScrollArea, iced::Vector),
+ Viewport(Context, Size),
+ Zoom(Context, Option),
+ Focus(Context),
+ Shelf(Context, Shelf),
OpenUse(Context, Id),
Back(Context),
Choose(Context, CanvasTool),
@@ -120,6 +126,7 @@ pub enum CanvasEvent {
#[derive(Debug, Clone, Copy)]
pub enum ScrollArea {
Canvas,
+ // Retained navigation channel, now the independent Selection/outline shelf.
Page,
}
impl CanvasEvent {
@@ -127,6 +134,10 @@ impl CanvasEvent {
match self {
Self::Scrolled(context, _, _)
| Self::Restored(context, ..)
+ | Self::Viewport(context, _)
+ | Self::Zoom(context, _)
+ | Self::Focus(context)
+ | Self::Shelf(context, _)
| Self::OpenUse(context, _)
| Self::Back(context)
| Self::Choose(context, _)
@@ -191,6 +202,7 @@ pub struct State {
scroll_offsets: [iced::Vector; 2],
return_to: Vec,
restore: Option,
+ presentation: workspace::Presentation,
}
const CALLER_LIMIT: usize = 32;
pub(crate) struct Caller {
@@ -200,6 +212,7 @@ pub(crate) struct Caller {
target: Id,
tool: CanvasTool,
offsets: [iced::Vector; 2],
+ presentation: workspace::Presentation,
}
struct ViewportRestore {
document: Id,
@@ -259,6 +272,32 @@ struct Draft {
choice_page: usize,
}
impl State {
+ pub(crate) fn enter(&mut self, document: Id, owner: Id) {
+ if self.presentation.identity != Some((document, owner)) {
+ self.presentation = workspace::Presentation {
+ identity: Some((document, owner)),
+ focused: self.presentation.focused,
+ ..Default::default()
+ };
+ self.scroll_offsets = [iced::Vector::ZERO; 2];
+ self.restore = None;
+ }
+ }
+ pub(crate) fn viewport(&mut self, size: Size) -> bool {
+ self.presentation.viewport(size)
+ }
+ pub(crate) fn zoom(&mut self, scale: Option) -> bool {
+ self.presentation.zoom(scale)
+ }
+ pub(crate) fn focused(&self) -> bool {
+ self.presentation.focused
+ }
+ pub(crate) fn toggle_focus(&mut self) {
+ self.presentation.focused = !self.presentation.focused;
+ }
+ pub(crate) fn toggle_shelf(&mut self, shelf: Shelf) {
+ self.presentation.shelf = (self.presentation.shelf != Some(shelf)).then_some(shelf);
+ }
pub(crate) fn open_use(
&mut self,
p: &Project,
@@ -279,6 +318,7 @@ impl State {
target,
tool: self.canvas_tool(context.scenario),
offsets: self.scroll_offsets,
+ presentation: self.presentation.clone(),
});
self.canvas_tool = Some((target, CanvasTool::Select));
self.scroll_offsets = [iced::Vector::ZERO; 2];
@@ -309,6 +349,8 @@ impl State {
}
self.canvas_tool = Some((frame.owner, frame.tool));
self.scroll_offsets = frame.offsets;
+ self.presentation = frame.presentation;
+ self.presentation.identity = Some((context.document, context.scenario));
self.restore = Some(ViewportRestore {
document: context.document,
owner: frame.owner,
@@ -378,6 +420,9 @@ impl State {
}
pub(crate) fn scrolled(&mut self, area: ScrollArea, offset: iced::Vector) -> bool {
+ if !offset.x.is_finite() || !offset.y.is_finite() || offset.x < 0. || offset.y < 0. {
+ return false;
+ }
let index = match area {
ScrollArea::Canvas => 0,
ScrollArea::Page => 1,
@@ -387,7 +432,8 @@ impl State {
changed
}
fn scroll_offset(&self) -> iced::Vector {
- self.scroll_offsets[0] + self.scroll_offsets[1]
+ // The former page scroll now belongs to a sibling outline shelf.
+ self.scroll_offsets[0]
}
fn call_choices(&self, p: &Project, context: Context, draft: &Draft) -> Vec {
let key = CallChoiceContext {
@@ -1634,493 +1680,6 @@ fn bounded_picker<'a>(
}
out.into()
}
-pub fn view<'a>(app: &'a App, owner: Id, mut out: Column<'a, Message>) -> Element<'a, Message> {
- let p = app.project();
- out = out.push(label(match viewpoint(p, owner) {
- ScenarioViewpoint::OperationalActivity => {
- "Operational activity scenario · lifelines represent activities"
- }
- ScenarioViewpoint::Functional => {
- "Functional scenario · lifelines represent system functions"
- }
- ScenarioViewpoint::ParticipantInteraction => {
- "Participant scenario · lifelines represent entities, actors or roles"
- }
- ScenarioViewpoint::BoundaryInterface => {
- "Interface scenario · lifelines represent the system and external actors"
- }
- ScenarioViewpoint::MixedLegacy => {
- "Mixed scenario · lifelines may represent work or participants"
- }
- }));
- let pending = app.behavior.has_pending();
- let context = app.scenario_context().expect("visible scenario context");
- let tool = app.behavior.scenario.canvas_tool(owner);
- // Keep the canvas at a stable widget-tree position when a form replaces
- // the palette, so Iced retains the user's current sequence viewport.
- let mut controls = column![].spacing(6);
- if !pending {
- controls = controls.push(
- row![
- button("Select / reorder")
- .on_press(Message::ScenarioCanvas(CanvasEvent::Choose(
- context,
- CanvasTool::Select
- )))
- .style(if tool == CanvasTool::Select {
- style::selected
- } else {
- style::nav
- }),
- pick_list(
- vec![
- CanvasTool::Select,
- CanvasTool::Message(MessageStyle::CallReply),
- CanvasTool::Message(MessageStyle::Asynchronous),
- CanvasTool::Message(MessageStyle::Call),
- CanvasTool::Message(MessageStyle::Reply),
- CanvasTool::Message(MessageStyle::Create),
- CanvasTool::Message(MessageStyle::Delete),
- CanvasTool::Message(MessageStyle::Timer),
- CanvasTool::Add(Tool::Action),
- CanvasTool::Add(Tool::Observation),
- CanvasTool::Add(Tool::Reuse),
- CanvasTool::Add(Tool::Fragment),
- ],
- Some(tool),
- move |tool| Message::ScenarioCanvas(CanvasEvent::Choose(context, tool))
- )
- .text_size(12)
- .padding(8)
- .width(240),
- action("+ Lifeline", event(Event::Start(owner, Tool::Lifeline))),
- action("Properties", Message::EditProperties(owner)),
- ]
- .spacing(6)
- .wrap(),
- );
- controls = controls.push(label(match tool {
- CanvasTool::Message(_) => "Draw message · choose a sequence position on a lifeline, then click or drag to another lifeline · Escape cancels",
- CanvasTool::Add(Tool::Action | Tool::Observation) => "Click a highlighted sequence position · click on a lifeline to preselect it · Escape cancels",
- CanvasTool::Add(Tool::Reuse) => "Click a highlighted sequence position to reuse a scenario and bind its lifelines · Escape cancels",
- CanvasTool::Add(Tool::Fragment) => "Click a highlighted sequence position to insert an empty fragment with operands · Escape cancels",
- _ => "Select a lifeline or occurrence · drag its ↕ handle to reorder within its sequence",
- }));
- } else {
- controls = controls.push(label(
- "Pending form · the canvas remains visible · apply or cancel below",
- ));
- }
- let clean = !pending && !app.draft_dirty() && !app.source_dirty();
- let mut navigation = row![].spacing(6);
- if referenced_scenario(p, owner, app.selected).is_some() {
- navigation = navigation.push(button("Open definition").on_press_maybe(clean.then_some(
- Message::ScenarioCanvas(CanvasEvent::OpenUse(context, app.selected)),
- )));
- }
- if app.behavior.scenario.can_back(p, context) {
- navigation =
- navigation.push(button("Back to caller").on_press_maybe(
- clean.then_some(Message::ScenarioCanvas(CanvasEvent::Back(context))),
- ));
- }
- controls = controls.push(navigation.wrap());
- out = out.push(controls);
- let mut scene = Scene::sequence(p, owner, app.selected);
- scene.interaction = Some((
- context,
- tool,
- !app.behavior.has_pending() && !app.draft_dirty() && !app.source_dirty(),
- ));
- scene.scroll_offset = app.behavior.scenario.scroll_offset();
- scene.pending_placement = app.behavior.scenario.pending_placement(owner);
- let size = scene.size;
- out = out.push(
- container(super::scenario_scroll::observe(
- context,
- ScrollArea::Canvas,
- app.behavior
- .scenario
- .scroll_restore(context, ScrollArea::Canvas),
- scrollable(
- canvas::Canvas::new(scene)
- .width(size.width)
- .height(size.height),
- )
- .direction(scrollable::Direction::Both {
- vertical: scrollable::Scrollbar::default(),
- horizontal: scrollable::Scrollbar::default(),
- })
- .height(if pending {
- (app.window_size.height - 660.).clamp(110., 190.)
- } else {
- (app.window_size.height - 410.).clamp(180., 340.)
- }),
- ))
- .padding(8)
- .style(style::card),
- );
- if let Some(d) = app
- .behavior
- .scenario
- .draft
- .as_ref()
- .filter(|d| d.owner == owner)
- {
- let mut form = column![
- text(d.tool.label()).size(16),
- text_input("Name (defaults to selected source)", &d.name)
- .on_input(|s| event(Event::Name(s)))
- .padding(9)
- .size(12)
- ]
- .spacing(9);
- if d.tool != Tool::Lifeline {
- form = form.push(label(format!(
- "Insert: {}",
- slot_label(
- p,
- Slot {
- parent: d.parent,
- before: d.before
- }
- )
- )));
- if d.before.is_some() {
- form = form.push(
- button("Append to this sequence instead").on_press(event(Event::AppendInstead)),
- );
- }
- form = form.push(picker(
- parents(p, owner),
- Some(d.parent),
- "Destination sequence / operand…",
- Event::Parent,
- ));
- }
- if matches!(
- d.tool,
- Tool::Lifeline | Tool::Reuse | Tool::Action | Tool::Observation
- ) {
- form = form.push(picker(
- sources(p, d),
- d.target,
- "Choose a named source…",
- Event::Target,
- ));
- }
- if d.tool == Tool::Message {
- form = form.push(
- pick_list(
- vec![
- MessageStyle::Asynchronous,
- MessageStyle::Call,
- MessageStyle::CallReply,
- MessageStyle::Reply,
- MessageStyle::Create,
- MessageStyle::Delete,
- MessageStyle::Timer,
- ],
- Some(d.style),
- |style| event(Event::Style(style)),
- )
- .width(Fill)
- .padding(9)
- .text_size(12),
- );
- if d.style == MessageStyle::Reply {
- form = form.push(bounded_picker(
- app.behavior.scenario.call_choices(p, context, d),
- d,
- d.call,
- "Choose the earlier call…",
- Event::Call,
- ));
- if d.call.is_some() {
- form = form.push(label(
- "Reply reuses the call exchange in reverse and retains its correlation.",
- ));
- }
- } else if d.style != MessageStyle::Timer {
- form = form.push(bounded_picker(
- exchanges(p, d),
- d,
- d.exchange,
- "Choose the exchange…",
- Event::Exchange,
- ));
- form = form.push(label(
- "Apply checks that the message is valid at this insertion position.",
- ));
- }
- form = form.push(picker(
- d.line_choices(p, true),
- d.sender,
- "Sending lifeline…",
- Event::Sender,
- ));
- if d.style != MessageStyle::Timer {
- form = form.push(picker(
- d.line_choices(p, false),
- d.receiver,
- "Receiving lifeline…",
- Event::Receiver,
- ));
- }
- if d.style == MessageStyle::Timer {
- form = form.push(
- text_input("Duration / deadline, e.g. 30 s", &d.expression)
- .on_input(|s| event(Event::Expression(s)))
- .padding(9)
- .size(12),
- );
- }
- if p.layer_of(owner) == Some(Layer::Sa)
- && viewpoint(p, owner) == ScenarioViewpoint::BoundaryInterface
- && matches!(
- d.style,
- MessageStyle::Call | MessageStyle::CallReply | MessageStyle::Asynchronous
- )
- {
- form = form.push(
- iced::widget::checkbox(d.operation.is_some())
- .label("Invoke a specific interface operation")
- .on_toggle(|enabled| event(Event::OperationIntent(enabled))),
- );
- if let Some(operation) = &d.operation {
- form = form
- .push(bounded_picker(
- crate::data_values::operation_choices(p, &d.proposed_message(p)),
- d,
- operation.allocation,
- "Choose the exact interface operation allocation…",
- Event::Operation,
- ))
- .push(
- button("Keep operation unresolved")
- .on_press(event(Event::ClearOperation)),
- );
- if let Some(id) = operation.allocation {
- form = form.push(label(crate::data_values::operation_identity(p, id)));
- }
- }
- }
- if let Some(description) =
- crate::data_values::operation_label(p, &d.proposed_message(p))
- {
- form = form.push(label(description));
- }
- match d.signature(p) {
- Ok(Some(exchange)) => form = form.push(value_panel(p, d, &exchange)),
- Err(problem) => {
- form = form.push(label(problem));
- for (key, expression) in d.authored_values() {
- form = form
- .push(
- text(format!(
- "Retained value · {} / {}",
- key.path
- .iter()
- .map(|id| p.named(*id))
- .collect::>()
- .join(" → "),
- p.named(key.field)
- ))
- .size(11),
- )
- .push(
- text_input("Value expression", expression)
- .on_input({
- let key = key.clone();
- move |raw| event(Event::Value(key.clone(), raw))
- })
- .padding(8)
- .size(12),
- );
- }
- }
- Ok(None) => {}
- }
- if d.style == MessageStyle::CallReply {
- form=form.push(label("Creates the call and reversed, correlated reply together in one undoable operation."));
- }
- }
- if matches!(d.tool, Tool::Action | Tool::Observation) {
- form = form.push(picker(
- lines(p, owner),
- d.sender,
- "Observed lifeline…",
- Event::Sender,
- ));
- }
- if d.tool == Tool::Reuse
- && let Some(target) = d.target
- {
- for line in lines(p, target) {
- let referenced = line.id;
- let compatible: Vec<_> = lines(p, owner)
- .into_iter()
- .filter(|c| {
- compatible_binding(p, referenced, c.id)
- && !d
- .bindings
- .iter()
- .any(|(r, l)| *r != referenced && *l == c.id)
- })
- .collect();
- if compatible.is_empty() {
- form = form.push(label(format!(
- "Add a compatible local lifeline before binding '{}'.",
- p.named(referenced)
- )));
- }
- form = form
- .push(label(format!("Bind {}", p.named(referenced))))
- .push(picker(
- compatible,
- d.bindings.get(&referenced).copied(),
- "Compatible local lifeline…",
- move |c| Event::Binding(referenced, c),
- ));
- }
- form = form.push(label(
- "Bindings are explicit, one-to-one and refer to existing lifelines.",
- ));
- }
- if d.tool == Tool::Fragment {
- form = form.push(
- pick_list(
- vec![
- Operator(FragmentOperator::Alternative),
- Operator(FragmentOperator::Parallel),
- Operator(FragmentOperator::Loop),
- Operator(FragmentOperator::Optional),
- ],
- Some(Operator(d.operator)),
- |o| event(Event::Operator(o)),
- )
- .padding(9)
- .text_size(12)
- .width(Fill),
- );
- if d.operator != FragmentOperator::Parallel {
- form = form.push(
- text_input("Condition / loop guard", &d.expression)
- .on_input(|s| event(Event::Expression(s)))
- .padding(9)
- .size(12),
- );
- }
- if d.operator == FragmentOperator::Alternative {
- form = form.push(
- text_input("Second alternative guard", &d.second_guard)
- .on_input(|s| event(Event::SecondGuard(s)))
- .padding(9)
- .size(12),
- );
- }
- form=form.push(label("Creates the fragment with its operands. Choose an operand as destination when adding messages or actions."));
- }
- let readiness = d.command(p);
- if let Err(error) = &readiness {
- form = form.push(label(error.clone()));
- }
- let footer = row![
- button("Create")
- .padding(9)
- .on_press_maybe(readiness.is_ok().then_some(event(Event::Submit))),
- action("Cancel", event(Event::Cancel))
- ]
- .spacing(7);
- out = out.push(
- container(
- column![
- scrollable(form).height((app.window_size.height - 650.).clamp(110., 240.)),
- footer
- ]
- .spacing(9),
- )
- .padding(14)
- .style(style::card),
- );
- }
- if lines(p, owner).is_empty() && p.elements[&owner].steps().is_none_or(Vec::is_empty) {
- out = out.push(container(label("Add lifelines for the participating work or actors, then create messages between them.")).padding(16).width(Fill).style(style::card));
- } else if !pending {
- out = sequence_rows(p, context, owner, out);
- }
- super::scenario_scroll::observe(
- context,
- ScrollArea::Page,
- app.behavior
- .scenario
- .scroll_restore(context, ScrollArea::Page),
- scrollable(out.padding(24)),
- )
-}
-fn sequence_rows<'a>(
- p: &'a Project,
- context: Context,
- owner: Id,
- mut out: Column<'a, Message>,
-) -> Column<'a, Message> {
- if let Some(SharedBody::Occurrence {
- body: OccurrenceBody::Fragment { branches, .. },
- ..
- }) = p.elements.get(&owner).and_then(|e| e.shared())
- {
- for branch in branches {
- out = out.push(action(
- &format!("Operand: {}", p.named(*branch)),
- Message::Select(*branch),
- ));
- out = sequence_rows(p, context, *branch, out);
- }
- }
- if let Some(steps) = p.elements.get(&owner).and_then(|e| e.steps()) {
- for (i, id) in steps.iter().enumerate() {
- out =
- out.push(
- row![
- action(
- &format!("{}. {}", i + 1, p.named(*id)),
- Message::Select(*id)
- )
- .width(Fill),
- button("↑")
- .padding(8)
- .on_press_maybe((i > 0).then_some(Message::Command(
- Command::MoveStep(owner, i, i.saturating_sub(1))
- ))),
- button("↓").padding(8).on_press_maybe(
- (i + 1 < steps.len()).then_some(Message::Command(Command::MoveStep(
- owner,
- i,
- i + 1
- )))
- )
- ]
- .spacing(6),
- );
- if let Some(SharedBody::Occurrence {
- body:
- OccurrenceBody::ScenarioUse {
- scenario: Some(target),
- ..
- },
- ..
- }) = p.elements[id].shared()
- {
- out = out.push(action(
- &format!("Open {} →", p.named(*target)),
- Message::ScenarioCanvas(CanvasEvent::OpenUse(context, *id)),
- ));
- }
- out = sequence_rows(p, context, *id, out);
- }
- }
- out
-}
#[cfg(test)]
pub(crate) mod direct_tests {
@@ -2180,7 +1739,7 @@ pub(crate) mod direct_tests {
epoch: 0,
}
}
- fn placement_fixture() -> (Fixture, Id, Id, Id, Id, Id) {
+ pub(super) fn placement_fixture() -> (Fixture, Id, Id, Id, Id, Id) {
let mut f = fixture();
let p = &mut f.project;
let machine = p
@@ -2334,7 +1893,12 @@ pub(crate) mod direct_tests {
);
assert_eq!(
app.behavior.scenario.scroll_offset(),
- iced::Vector::new(25., 250.)
+ iced::Vector::new(25., 170.)
+ );
+ assert_eq!(
+ app.behavior.scenario.scroll_offsets[1],
+ iced::Vector::new(0., 80.),
+ "The caller's independent outline scroll is restored separately"
);
assert!(
!app.behavior
@@ -2471,6 +2035,11 @@ pub(crate) mod direct_tests {
assert!(app.document.can_undo(), "{}", app.status);
navigation_event(&mut app, CanvasEvent::Back);
assert_eq!(app.selected, if edit == 0 { first } else { b });
+ assert_eq!(
+ app.scenario_context().unwrap().scenario,
+ if edit == 0 { a } else { b },
+ "Back must restore the caller workspace even if a rename already selected its UUID"
+ );
assert_eq!(app.project(), &project);
assert_eq!(app.document.bundle(), &source);
assert_eq!(app.document.revision(), revision);
@@ -2909,7 +2478,7 @@ pub(crate) mod direct_tests {
assert!(app.busy);
assert_eq!(
app.behavior.scenario.scroll_offset(),
- iced::Vector::new(25., 140.)
+ iced::Vector::new(25., 100.)
);
assert_ne!(
app.scenario_context(),
@@ -2925,7 +2494,7 @@ pub(crate) mod direct_tests {
)));
assert_eq!(
app.behavior.scenario.scroll_offset(),
- iced::Vector::new(25., 140.)
+ iced::Vector::new(25., 100.)
);
let root = std::path::PathBuf::from("scenario-scroll-save-fixture");
let receipt = crate::app::FileSnapshot {
diff --git a/crates/oa-desktop/src/behavior/scenario/workspace.rs b/crates/oa-desktop/src/behavior/scenario/workspace.rs
new file mode 100644
index 00000000..ea63b579
--- /dev/null
+++ b/crates/oa-desktop/src/behavior/scenario/workspace.rs
@@ -0,0 +1,748 @@
+//! Scenario presentation. Layout/camera controls never construct model commands.
+use super::super::scenario_scroll;
+use super::*;
+use iced::widget::{Space, column, sensor};
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Shelf {
+ Tools,
+ Selection,
+}
+
+#[derive(Clone, Default)]
+pub(super) struct Presentation {
+ pub(super) identity: Option<(Id, Id)>,
+ pub(super) viewport: Option,
+ pub(super) focused: bool,
+ pub(super) shelf: Option,
+ pub(super) zoom: Option,
+ pub(super) fit: bool,
+}
+const MIN_SCALE: f32 = 0.05;
+impl Presentation {
+ pub(super) fn viewport(&mut self, size: Size) -> bool {
+ if !size.width.is_finite()
+ || !size.height.is_finite()
+ || size.width <= 0.
+ || size.height <= 0.
+ || self.viewport == Some(size)
+ {
+ return false;
+ }
+ self.viewport = Some(size);
+ true
+ }
+ pub(super) fn zoom(&mut self, scale: Option) -> bool {
+ if scale.is_some_and(|value| !value.is_finite() || value <= 0.) {
+ return false;
+ }
+ let next = scale.map(|value| value.clamp(MIN_SCALE, 3.));
+ let changed = self.zoom != next || self.fit != scale.is_none();
+ self.zoom = next;
+ self.fit = scale.is_none();
+ changed
+ }
+ fn fit_scale(&self, scene: Size) -> Option {
+ let viewport = self.viewport?;
+ // This is the measured inner Scrollable, never a window-height estimate.
+ // Leave room for the overlay scrollbars on both axes.
+ Some(
+ ((viewport.width - 16.).max(1.) / scene.width.max(1.))
+ .min((viewport.height - 16.).max(1.) / scene.height.max(1.)),
+ )
+ }
+ fn scale(&self, scene: Size) -> f32 {
+ if self.fit {
+ self.fit_scale(scene).unwrap_or(1.).clamp(MIN_SCALE, 1.)
+ } else {
+ self.zoom.unwrap_or(1.)
+ }
+ }
+ fn fit_limited(&self, scene: Size) -> bool {
+ self.fit && self.fit_scale(scene).is_some_and(|scale| scale < MIN_SCALE)
+ }
+}
+
+pub fn view<'a>(app: &'a App, owner: Id, out: Column<'a, Message>) -> Element<'a, Message> {
+ let p = app.project();
+ let state = &app.behavior.scenario;
+ let presentation = &state.presentation;
+ let pending = state
+ .draft
+ .as_ref()
+ .is_some_and(|draft| draft.owner == owner);
+ let context = app.scenario_context().expect("visible scenario context");
+ let tool = state.canvas_tool(owner);
+ let clean = !app.behavior.has_pending() && !app.draft_dirty() && !app.source_dirty();
+ let mut scene = Scene::sequence(p, owner, app.selected);
+ let scale = presentation.scale(scene.size);
+ let limited = presentation.fit_limited(scene.size);
+ scene.scale = scale;
+ scene.interaction = Some((context, tool, clean));
+ // The outline is a sibling shelf, not an ancestor of this canvas.
+ scene.scroll_offset = state.scroll_offset();
+ scene.pending_placement = state.pending_placement(owner);
+ let size = Size::new(scene.size.width * scale, scene.size.height * scale);
+ let canvas = canvas_pane(
+ context,
+ state.scroll_restore(context, ScrollArea::Canvas),
+ canvas::Canvas::new(scene)
+ .width(size.width)
+ .height(size.height)
+ .into(),
+ );
+ let mut navigation = row![
+ out.width(Fill),
+ button(
+ text(if presentation.focused {
+ "Restore panels"
+ } else {
+ "Focus canvas"
+ })
+ .size(12)
+ )
+ .padding(6)
+ .on_press(Message::ScenarioCanvas(CanvasEvent::Focus(context)))
+ ]
+ .spacing(6)
+ .align_y(iced::Center);
+ if state.can_back(p, context) {
+ navigation = navigation.push(
+ button(text("Back to caller").size(12))
+ .padding(6)
+ .on_press_maybe(
+ clean.then_some(Message::ScenarioCanvas(CanvasEvent::Back(context))),
+ ),
+ );
+ }
+ let mut header = column![
+ navigation,
+ row![
+ button(text("Tools").size(12))
+ .padding(6)
+ .style(if presentation.shelf == Some(Shelf::Tools) {
+ button::primary
+ } else {
+ style::nav
+ })
+ .on_press(Message::ScenarioCanvas(CanvasEvent::Shelf(
+ context,
+ Shelf::Tools
+ ))),
+ button(text("Selection").size(12))
+ .padding(6)
+ .style(if presentation.shelf == Some(Shelf::Selection) {
+ button::primary
+ } else {
+ style::nav
+ })
+ .on_press(Message::ScenarioCanvas(CanvasEvent::Shelf(
+ context,
+ Shelf::Selection
+ ))),
+ button(text("Fit overview").size(12))
+ .padding(6)
+ .on_press(Message::ScenarioCanvas(CanvasEvent::Zoom(context, None))),
+ button(text("−").size(12))
+ .padding(6)
+ .on_press(Message::ScenarioCanvas(CanvasEvent::Zoom(
+ context,
+ Some(scale / 1.25)
+ ))),
+ text(format!("{:.0}%", scale * 100.)).size(12),
+ button(text("+").size(12))
+ .padding(6)
+ .on_press(Message::ScenarioCanvas(CanvasEvent::Zoom(
+ context,
+ Some(scale * 1.25)
+ ))),
+ button(text("100%").size(12))
+ .padding(6)
+ .on_press(Message::ScenarioCanvas(CanvasEvent::Zoom(
+ context,
+ Some(1.)
+ )))
+ ]
+ .spacing(6)
+ .align_y(iced::Center)
+ .wrap(),
+ text(if pending {
+ "Pending form · the highlighted sequence position is retained".to_owned()
+ } else {
+ let guidance = match tool {
+ CanvasTool::Message(_) => {
+ "Click a source position, then click or drag to a target · Esc cancels"
+ }
+ CanvasTool::Add(Tool::Action | Tool::Observation) => {
+ "Click a highlighted position · a lifeline click preselects that lifeline"
+ }
+ CanvasTool::Add(Tool::Reuse | Tool::Fragment) => {
+ "Click a highlighted sequence position to open its form"
+ }
+ _ => "Select a lifeline or occurrence · drag its ↕ handle to reorder",
+ };
+ format!("{tool} · {guidance}")
+ })
+ .size(11)
+ ]
+ .spacing(6);
+ if limited {
+ header = header
+ .push(text("Minimum zoom reached · scroll to see the remaining scenario.").size(11));
+ }
+ let details = match presentation.shelf {
+ Some(Shelf::Tools) => {
+ let choose = move |tool| Message::ScenarioCanvas(CanvasEvent::Choose(context, tool));
+ let tool_picker: Element<'_, Message> = if clean {
+ pick_list(
+ vec![
+ CanvasTool::Select,
+ CanvasTool::Message(MessageStyle::CallReply),
+ CanvasTool::Message(MessageStyle::Asynchronous),
+ CanvasTool::Message(MessageStyle::Call),
+ CanvasTool::Message(MessageStyle::Reply),
+ CanvasTool::Message(MessageStyle::Create),
+ CanvasTool::Message(MessageStyle::Delete),
+ CanvasTool::Message(MessageStyle::Timer),
+ CanvasTool::Add(Tool::Action),
+ CanvasTool::Add(Tool::Observation),
+ CanvasTool::Add(Tool::Reuse),
+ CanvasTool::Add(Tool::Fragment),
+ ],
+ Some(tool),
+ choose,
+ )
+ .text_size(12)
+ .padding(6)
+ .width(Fill)
+ .into()
+ } else {
+ button(text(format!("{tool} · finish pending edits to change tools")).size(12))
+ .padding(6)
+ .width(Fill)
+ .into()
+ };
+ column![
+ tool_picker,
+ row![
+ button(text("+ Lifeline").size(12))
+ .padding(6)
+ .on_press_maybe(
+ clean.then_some(event(Event::Start(owner, Tool::Lifeline)))
+ ),
+ button(text("Scenario properties").size(12))
+ .padding(6)
+ .on_press_maybe(clean.then_some(Message::EditProperties(owner))),
+ ]
+ .spacing(6)
+ .wrap(),
+ text(match viewpoint(p, owner) {
+ ScenarioViewpoint::OperationalActivity =>
+ "Activity scenario · lifelines represent activities",
+ ScenarioViewpoint::Functional =>
+ "Functional scenario · lifelines represent system functions",
+ ScenarioViewpoint::ParticipantInteraction =>
+ "Participant scenario · entities, actors or roles",
+ ScenarioViewpoint::BoundaryInterface =>
+ "Interface scenario · system and external actors",
+ ScenarioViewpoint::MixedLegacy => "Mixed scenario · work or participants",
+ })
+ .size(11),
+ ]
+ .spacing(6)
+ }
+ _ => {
+ let mut details =
+ column![text(format!("Selected: {}", p.named(app.selected))).size(12)].spacing(6);
+ let mut navigation = row![].spacing(6);
+ if referenced_scenario(p, owner, app.selected).is_some() {
+ navigation = navigation.push(
+ button(text("Open definition").size(12))
+ .padding(6)
+ .on_press_maybe(clean.then_some(Message::ScenarioCanvas(
+ CanvasEvent::OpenUse(context, app.selected),
+ ))),
+ );
+ }
+ navigation = navigation.push(
+ button(text("Properties").size(12))
+ .padding(6)
+ .on_press_maybe(clean.then_some(Message::EditProperties(app.selected))),
+ );
+ details = details.push(navigation.wrap());
+ if lines(p, owner).is_empty() && p.elements[&owner].steps().is_none_or(Vec::is_empty) {
+ details.push(
+ text("Use Tools to add lifelines, then draw messages or insert occurrences.")
+ .size(12),
+ )
+ } else {
+ sequence_rows(p, context, owner, details, clean)
+ }
+ }
+ };
+ let details = details_pane(
+ context,
+ state.scroll_restore(context, ScrollArea::Page),
+ details.into(),
+ presentation.shelf.is_some(),
+ pending,
+ );
+ let mut form_panel = editor_panel(Space::new().height(0).into(), Space::new().height(0).into());
+ if let Some(d) = app
+ .behavior
+ .scenario
+ .draft
+ .as_ref()
+ .filter(|d| d.owner == owner)
+ {
+ let mut form = column![
+ text(d.tool.label()).size(16),
+ text_input("Name (defaults to selected source)", &d.name)
+ .on_input(|s| event(Event::Name(s)))
+ .padding(9)
+ .size(12)
+ ]
+ .spacing(9);
+ if d.tool != Tool::Lifeline {
+ form = form.push(label(format!(
+ "Insert: {}",
+ slot_label(
+ p,
+ Slot {
+ parent: d.parent,
+ before: d.before
+ }
+ )
+ )));
+ if d.before.is_some() {
+ form = form.push(
+ button("Append to this sequence instead").on_press(event(Event::AppendInstead)),
+ );
+ }
+ form = form.push(picker(
+ parents(p, owner),
+ Some(d.parent),
+ "Destination sequence / operand…",
+ Event::Parent,
+ ));
+ }
+ if matches!(
+ d.tool,
+ Tool::Lifeline | Tool::Reuse | Tool::Action | Tool::Observation
+ ) {
+ form = form.push(picker(
+ sources(p, d),
+ d.target,
+ "Choose a named source…",
+ Event::Target,
+ ));
+ }
+ if d.tool == Tool::Message {
+ form = form.push(
+ pick_list(
+ vec![
+ MessageStyle::Asynchronous,
+ MessageStyle::Call,
+ MessageStyle::CallReply,
+ MessageStyle::Reply,
+ MessageStyle::Create,
+ MessageStyle::Delete,
+ MessageStyle::Timer,
+ ],
+ Some(d.style),
+ |style| event(Event::Style(style)),
+ )
+ .width(Fill)
+ .padding(9)
+ .text_size(12),
+ );
+ if d.style == MessageStyle::Reply {
+ form = form.push(bounded_picker(
+ app.behavior.scenario.call_choices(p, context, d),
+ d,
+ d.call,
+ "Choose the earlier call…",
+ Event::Call,
+ ));
+ if d.call.is_some() {
+ form = form.push(label(
+ "Reply reuses the call exchange in reverse and retains its correlation.",
+ ));
+ }
+ } else if d.style != MessageStyle::Timer {
+ form = form.push(bounded_picker(
+ exchanges(p, d),
+ d,
+ d.exchange,
+ "Choose the exchange…",
+ Event::Exchange,
+ ));
+ form = form.push(label(
+ "Apply checks that the message is valid at this insertion position.",
+ ));
+ }
+ form = form.push(picker(
+ d.line_choices(p, true),
+ d.sender,
+ "Sending lifeline…",
+ Event::Sender,
+ ));
+ if d.style != MessageStyle::Timer {
+ form = form.push(picker(
+ d.line_choices(p, false),
+ d.receiver,
+ "Receiving lifeline…",
+ Event::Receiver,
+ ));
+ }
+ if d.style == MessageStyle::Timer {
+ form = form.push(
+ text_input("Duration / deadline, e.g. 30 s", &d.expression)
+ .on_input(|s| event(Event::Expression(s)))
+ .padding(9)
+ .size(12),
+ );
+ }
+ if p.layer_of(owner) == Some(Layer::Sa)
+ && viewpoint(p, owner) == ScenarioViewpoint::BoundaryInterface
+ && matches!(
+ d.style,
+ MessageStyle::Call | MessageStyle::CallReply | MessageStyle::Asynchronous
+ )
+ {
+ form = form.push(
+ iced::widget::checkbox(d.operation.is_some())
+ .label("Invoke a specific interface operation")
+ .on_toggle(|enabled| event(Event::OperationIntent(enabled))),
+ );
+ if let Some(operation) = &d.operation {
+ form = form
+ .push(bounded_picker(
+ crate::data_values::operation_choices(p, &d.proposed_message(p)),
+ d,
+ operation.allocation,
+ "Choose the exact interface operation allocation…",
+ Event::Operation,
+ ))
+ .push(
+ button("Keep operation unresolved")
+ .on_press(event(Event::ClearOperation)),
+ );
+ if let Some(id) = operation.allocation {
+ form = form.push(label(crate::data_values::operation_identity(p, id)));
+ }
+ }
+ }
+ if let Some(description) =
+ crate::data_values::operation_label(p, &d.proposed_message(p))
+ {
+ form = form.push(label(description));
+ }
+ match d.signature(p) {
+ Ok(Some(exchange)) => form = form.push(value_panel(p, d, &exchange)),
+ Err(problem) => {
+ form = form.push(label(problem));
+ for (key, expression) in d.authored_values() {
+ form = form
+ .push(
+ text(format!(
+ "Retained value · {} / {}",
+ key.path
+ .iter()
+ .map(|id| p.named(*id))
+ .collect::>()
+ .join(" → "),
+ p.named(key.field)
+ ))
+ .size(11),
+ )
+ .push(
+ text_input("Value expression", expression)
+ .on_input({
+ let key = key.clone();
+ move |raw| event(Event::Value(key.clone(), raw))
+ })
+ .padding(8)
+ .size(12),
+ );
+ }
+ }
+ Ok(None) => {}
+ }
+ if d.style == MessageStyle::CallReply {
+ form=form.push(label("Creates the call and reversed, correlated reply together in one undoable operation."));
+ }
+ }
+ if matches!(d.tool, Tool::Action | Tool::Observation) {
+ form = form.push(picker(
+ lines(p, owner),
+ d.sender,
+ "Observed lifeline…",
+ Event::Sender,
+ ));
+ }
+ if d.tool == Tool::Reuse
+ && let Some(target) = d.target
+ {
+ for line in lines(p, target) {
+ let referenced = line.id;
+ let compatible: Vec<_> = lines(p, owner)
+ .into_iter()
+ .filter(|c| {
+ compatible_binding(p, referenced, c.id)
+ && !d
+ .bindings
+ .iter()
+ .any(|(r, l)| *r != referenced && *l == c.id)
+ })
+ .collect();
+ if compatible.is_empty() {
+ form = form.push(label(format!(
+ "Add a compatible local lifeline before binding '{}'.",
+ p.named(referenced)
+ )));
+ }
+ form = form
+ .push(label(format!("Bind {}", p.named(referenced))))
+ .push(picker(
+ compatible,
+ d.bindings.get(&referenced).copied(),
+ "Compatible local lifeline…",
+ move |c| Event::Binding(referenced, c),
+ ));
+ }
+ form = form.push(label(
+ "Bindings are explicit, one-to-one and refer to existing lifelines.",
+ ));
+ }
+ if d.tool == Tool::Fragment {
+ form = form.push(
+ pick_list(
+ vec![
+ Operator(FragmentOperator::Alternative),
+ Operator(FragmentOperator::Parallel),
+ Operator(FragmentOperator::Loop),
+ Operator(FragmentOperator::Optional),
+ ],
+ Some(Operator(d.operator)),
+ |o| event(Event::Operator(o)),
+ )
+ .padding(9)
+ .text_size(12)
+ .width(Fill),
+ );
+ if d.operator != FragmentOperator::Parallel {
+ form = form.push(
+ text_input("Condition / loop guard", &d.expression)
+ .on_input(|s| event(Event::Expression(s)))
+ .padding(9)
+ .size(12),
+ );
+ }
+ if d.operator == FragmentOperator::Alternative {
+ form = form.push(
+ text_input("Second alternative guard", &d.second_guard)
+ .on_input(|s| event(Event::SecondGuard(s)))
+ .padding(9)
+ .size(12),
+ );
+ }
+ form=form.push(label("Creates the fragment with its operands. Choose an operand as destination when adding messages or actions."));
+ }
+ let readiness = d.command(p);
+ if let Err(error) = &readiness {
+ form = form.push(label(error.clone()));
+ }
+ let footer = row![
+ button("Create")
+ .padding(9)
+ .on_press_maybe(readiness.is_ok().then_some(event(Event::Submit))),
+ action("Cancel", event(Event::Cancel))
+ ]
+ .spacing(7);
+ form_panel = editor_panel(footer.into(), form.into());
+ }
+ workspace_layout(header.into(), details, canvas, form_panel, pending)
+}
+
+fn sequence_action<'a>(
+ title: &str,
+ message: Message,
+ enabled: bool,
+) -> iced::widget::Button<'a, Message> {
+ button(text(title.to_owned()).size(12))
+ .padding(8)
+ .style(style::nav)
+ .on_press_maybe(enabled.then_some(message))
+}
+
+// Permanent header/shelf/body and canvas/form slots. Zero-sized disclosures
+// must remain in the tree: Column::push otherwise drops them and remounts siblings.
+fn workspace_layout<'a, R: iced::advanced::text::Renderer + 'a>(
+ header: iced::Element<'a, Message, Theme, R>,
+ details: iced::Element<'a, Message, Theme, R>,
+ canvas: iced::Element<'a, Message, Theme, R>,
+ form: iced::Element<'a, Message, Theme, R>,
+ pending: bool,
+) -> iced::Element<'a, Message, Theme, R> {
+ let panes = Column::from_vec(vec![
+ container(canvas).width(Fill).height(Fill).into(),
+ container(form)
+ .width(Fill)
+ .height(if pending {
+ Fill
+ } else {
+ iced::Length::Fixed(0.)
+ })
+ .clip(true)
+ .into(),
+ ])
+ .spacing(if pending { 6 } else { 0 })
+ .width(Fill)
+ .height(Fill);
+ Column::from_vec(vec![header, details, panes.into()])
+ .spacing(6)
+ .padding(8)
+ .width(Fill)
+ .height(Fill)
+ .into()
+}
+
+fn details_pane<'a, R: iced::advanced::text::Renderer + 'a>(
+ context: Context,
+ restore: Option,
+ details: iced::Element<'a, Message, Theme, R>,
+ open: bool,
+ pending: bool,
+) -> iced::Element<'a, Message, Theme, R> {
+ scenario_scroll::observe(
+ context,
+ ScrollArea::Page,
+ restore,
+ scrollable(details)
+ .id("scenario-details")
+ .width(Fill)
+ .height(if open {
+ if pending { 72. } else { 100. }
+ } else {
+ 0.
+ }),
+ )
+}
+
+fn canvas_pane<'a, R: iced::advanced::text::Renderer + 'a>(
+ context: Context,
+ restore: Option,
+ content: iced::Element<'a, Message, Theme, R>,
+) -> iced::Element<'a, Message, Theme, R> {
+ container(
+ sensor(scenario_scroll::observe(
+ context,
+ ScrollArea::Canvas,
+ restore,
+ scrollable(content)
+ .id("scenario-canvas")
+ .direction(scrollable::Direction::Both {
+ vertical: scrollable::Scrollbar::default(),
+ horizontal: scrollable::Scrollbar::default(),
+ })
+ .width(Fill)
+ .height(Fill),
+ ))
+ .on_show(move |size| Message::ScenarioCanvas(CanvasEvent::Viewport(context, size)))
+ .on_resize(move |size| Message::ScenarioCanvas(CanvasEvent::Viewport(context, size))),
+ )
+ .padding(8)
+ .width(Fill)
+ .height(Fill)
+ .style(style::card)
+ .into()
+}
+
+fn editor_panel<'a, R: iced::advanced::text::Renderer + 'a>(
+ actions: iced::Element<'a, Message, Theme, R>,
+ fields: iced::Element<'a, Message, Theme, R>,
+) -> iced::Element<'a, Message, Theme, R> {
+ container(
+ column![
+ container(actions).id("scenario-actions").width(Fill),
+ scrollable(fields)
+ .id("scenario-form")
+ .width(Fill)
+ .height(Fill),
+ ]
+ .spacing(6),
+ )
+ .padding(10)
+ .width(Fill)
+ .height(Fill)
+ .style(style::card)
+ .into()
+}
+
+#[cfg(all(test, debug_assertions))]
+mod tests;
+
+fn sequence_rows<'a>(
+ p: &'a Project,
+ context: Context,
+ owner: Id,
+ mut out: Column<'a, Message>,
+ enabled: bool,
+) -> Column<'a, Message> {
+ if let Some(SharedBody::Occurrence {
+ body: OccurrenceBody::Fragment { branches, .. },
+ ..
+ }) = p.elements.get(&owner).and_then(|e| e.shared())
+ {
+ for branch in branches {
+ out = out.push(sequence_action(
+ &format!("Operand: {}", p.named(*branch)),
+ Message::Select(*branch),
+ enabled,
+ ));
+ out = sequence_rows(p, context, *branch, out, enabled);
+ }
+ }
+ if let Some(steps) = p.elements.get(&owner).and_then(|e| e.steps()) {
+ for (i, id) in steps.iter().enumerate() {
+ out = out.push(
+ row![
+ sequence_action(
+ &format!("{}. {}", i + 1, p.named(*id)),
+ Message::Select(*id),
+ enabled
+ )
+ .width(Fill),
+ button("↑")
+ .padding(8)
+ .on_press_maybe((enabled && i > 0).then_some(Message::Command(
+ Command::MoveStep(owner, i, i.saturating_sub(1))
+ ))),
+ button("↓").padding(8).on_press_maybe(
+ (enabled && i + 1 < steps.len())
+ .then_some(Message::Command(Command::MoveStep(owner, i, i + 1)))
+ )
+ ]
+ .spacing(6),
+ );
+ if let Some(SharedBody::Occurrence {
+ body:
+ OccurrenceBody::ScenarioUse {
+ scenario: Some(target),
+ ..
+ },
+ ..
+ }) = p.elements[id].shared()
+ {
+ out = out.push(sequence_action(
+ &format!("Open {} →", p.named(*target)),
+ Message::ScenarioCanvas(CanvasEvent::OpenUse(context, *id)),
+ enabled,
+ ));
+ }
+ out = sequence_rows(p, context, *id, out, enabled);
+ }
+ }
+ out
+}
diff --git a/crates/oa-desktop/src/behavior/scenario/workspace/tests.rs b/crates/oa-desktop/src/behavior/scenario/workspace/tests.rs
new file mode 100644
index 00000000..8829a024
--- /dev/null
+++ b/crates/oa-desktop/src/behavior/scenario/workspace/tests.rs
@@ -0,0 +1,822 @@
+use super::*;
+use iced::Vector;
+use iced::advanced::{Layout, Shell, layout, widget};
+
+#[derive(Default)]
+struct Geometry {
+ scrolls: BTreeMap,
+ actions: Option,
+}
+impl widget::Operation for Geometry {
+ fn traverse(&mut self, visit: &mut dyn FnMut(&mut dyn widget::Operation)) {
+ visit(self);
+ }
+ fn container(&mut self, id: Option<&widget::Id>, bounds: Rectangle) {
+ if id == Some(&widget::Id::new("scenario-actions")) {
+ self.actions = Some(bounds);
+ }
+ }
+ fn scrollable(
+ &mut self,
+ id: Option<&widget::Id>,
+ bounds: Rectangle,
+ _: Rectangle,
+ translation: Vector,
+ _: &mut dyn widget::operation::Scrollable,
+ ) {
+ for name in ["scenario-canvas", "scenario-details", "scenario-form"] {
+ if id == Some(&widget::Id::new(name)) {
+ assert!(
+ self.scrolls
+ .insert(name.into(), (bounds, translation))
+ .is_none()
+ );
+ }
+ }
+ }
+}
+type TestView = iced::Element<'static, Message, Theme, ()>;
+fn fixture_view(state: &State, context: Context, pending: bool, header: f32) -> TestView {
+ workspace_layout(
+ Space::new().height(header).into(),
+ details_pane(
+ context,
+ state.scroll_restore(context, ScrollArea::Page),
+ Space::new().height(1800.).into(),
+ state.presentation.shelf.is_some(),
+ pending,
+ ),
+ canvas_pane(
+ context,
+ state.scroll_restore(context, ScrollArea::Canvas),
+ Space::new().width(1800.).height(2400.).into(),
+ ),
+ editor_panel(
+ Space::new().height(32.).into(),
+ Space::new().height(2000.).into(),
+ ),
+ pending,
+ )
+}
+fn geometry(view: &mut TestView, tree: &mut widget::Tree, size: Size) -> (layout::Node, Geometry) {
+ let node = view
+ .as_widget_mut()
+ .layout(tree, &(), &layout::Limits::new(Size::ZERO, size));
+ let mut geometry = Geometry::default();
+ view.as_widget_mut()
+ .operate(tree, Layout::new(&node), &(), &mut geometry);
+ (node, geometry)
+}
+fn scroll(
+ view: &mut TestView,
+ tree: &mut widget::Tree,
+ node: &layout::Node,
+ name: &'static str,
+ offset: Vector,
+) {
+ let mut operation = widget::operation::scrollable::scroll_to::<()>(
+ widget::Id::new(name),
+ widget::operation::scrollable::AbsoluteOffset {
+ x: Some(offset.x),
+ y: Some(offset.y),
+ },
+ );
+ view.as_widget_mut()
+ .operate(tree, Layout::new(node), &(), &mut operation);
+}
+fn redraw(view: &mut TestView, tree: &mut widget::Tree, size: Size, app: &mut App) {
+ let (node, _) = geometry(view, tree, size);
+ let mut messages = vec![];
+ view.as_widget_mut().update(
+ tree,
+ &iced::Event::Window(iced::window::Event::RedrawRequested(
+ std::time::Instant::now(),
+ )),
+ Layout::new(&node),
+ mouse::Cursor::Unavailable,
+ &(),
+ &mut iced::advanced::clipboard::Null,
+ &mut Shell::new(&mut messages),
+ &Rectangle::with_size(size),
+ );
+ for message in messages {
+ let _ = app.update(message);
+ }
+}
+
+#[test]
+fn scenario_workspace_allocates_remaining_height_and_pins_actions_at_minimum_and_wide_sizes() {
+ for size in [
+ Size::new(500., 570.),
+ Size::new(1050., 570.),
+ Size::new(840., 770.),
+ Size::new(1394., 802.),
+ ] {
+ for pending in [false, true] {
+ for shelf in [None, Some(Shelf::Tools), Some(Shelf::Selection)] {
+ for header in [68., 100., 132.] {
+ let mut state = State::default();
+ state.presentation.shelf = shelf;
+ let mut view = fixture_view(
+ &state,
+ super::super::direct_tests::context(Id::new_v4()),
+ pending,
+ header,
+ );
+ let mut tree = widget::Tree::new(view.as_widget());
+ let (node, actual) = geometry(&mut view, &mut tree, size);
+ assert_eq!(node.size(), size);
+ let canvas = actual.scrolls["scenario-canvas"].0;
+ assert!(canvas.x >= 0. && canvas.y >= header);
+ assert!(
+ canvas.x + canvas.width <= size.width
+ && canvas.y + canvas.height <= size.height
+ );
+ assert!(
+ canvas.height >= if pending { 145. } else { 290. },
+ "{size:?} header={header} pending={pending} shelf={shelf:?}: {canvas:?}"
+ );
+ if pending {
+ let form = actual.scrolls["scenario-form"].0;
+ let actions = actual.actions.unwrap();
+ assert!(form.height >= 100. && form.y + form.height <= size.height);
+ assert!(actions.y >= canvas.y + canvas.height);
+ assert!(actions.y + actions.height <= form.y);
+ scroll(
+ &mut view,
+ &mut tree,
+ &node,
+ "scenario-form",
+ Vector::new(0., 600.),
+ );
+ let (_, after) = geometry(&mut view, &mut tree, size);
+ assert_eq!(after.actions, Some(actions));
+ assert_eq!(after.scrolls["scenario-form"].1.y, 600.);
+ assert_eq!(after.scrolls["scenario-canvas"].1, Vector::ZERO);
+ }
+ }
+ }
+ }
+ }
+}
+
+#[test]
+fn scenario_workspace_canvas_and_form_scroll_survive_disclosures_focus_width_and_resize() {
+ let context = super::super::direct_tests::context(Id::new_v4());
+ let mut state = State::default();
+ let mut view = fixture_view(&state, context, false, 68.);
+ let mut tree = widget::Tree::new(view.as_widget());
+ let (node, _) = geometry(&mut view, &mut tree, Size::new(500., 570.));
+ let offset = Vector::new(90., 180.);
+ scroll(&mut view, &mut tree, &node, "scenario-canvas", offset);
+ let mut prior_form = false;
+ for (pending, shelf, header, size) in [
+ (false, Some(Shelf::Tools), 100., Size::new(500., 570.)),
+ (true, Some(Shelf::Selection), 68., Size::new(1050., 570.)),
+ (true, None, 68., Size::new(1394., 802.)),
+ (true, Some(Shelf::Tools), 100., Size::new(500., 570.)),
+ (false, None, 100., Size::new(500., 570.)),
+ ] {
+ state.presentation.shelf = shelf;
+ view = fixture_view(&state, context, pending, header);
+ tree.diff(view.as_widget());
+ let (node, actual) = geometry(&mut view, &mut tree, size);
+ assert_eq!(actual.scrolls["scenario-canvas"].1, offset);
+ if pending {
+ if prior_form {
+ assert_eq!(actual.scrolls["scenario-form"].1.y, 600.);
+ }
+ scroll(
+ &mut view,
+ &mut tree,
+ &node,
+ "scenario-form",
+ Vector::new(0., 600.),
+ );
+ let (_, after) = geometry(&mut view, &mut tree, size);
+ assert_eq!(after.actions, actual.actions);
+ assert_eq!(after.scrolls["scenario-canvas"].1, offset);
+ }
+ prior_form = pending;
+ }
+}
+
+#[test]
+fn scenario_workspace_caller_restores_actual_mounted_canvas_and_shelf_with_camera() {
+ use super::super::direct_tests::{navigation_event, navigation_fixture};
+ let (p, [a, b, _], [reference, _, _]) = navigation_fixture();
+ let mut app = App::with_project(p);
+ let _ = app.update(Message::Select(a));
+ let _ = app.update(Message::Page(crate::app::Page::Behavior));
+ navigation_event(&mut app, |ctx| CanvasEvent::Shelf(ctx, Shelf::Selection));
+ navigation_event(&mut app, CanvasEvent::Focus);
+ navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, Some(0.75)));
+ let source = app.document.snapshot();
+ let size = Size::new(1050., 570.);
+ let mut view = fixture_view(
+ &app.behavior.scenario,
+ app.scenario_context().unwrap(),
+ false,
+ 68.,
+ );
+ let mut tree = widget::Tree::new(view.as_widget());
+ redraw(&mut view, &mut tree, size, &mut app);
+ let (node, _) = geometry(&mut view, &mut tree, size);
+ let canvas_offset = Vector::new(90., 180.);
+ scroll(
+ &mut view,
+ &mut tree,
+ &node,
+ "scenario-canvas",
+ canvas_offset,
+ );
+ scroll(
+ &mut view,
+ &mut tree,
+ &node,
+ "scenario-details",
+ Vector::new(0., 80.),
+ );
+ redraw(&mut view, &mut tree, size, &mut app);
+ assert_eq!(
+ app.behavior.scenario.scroll_offset(),
+ canvas_offset,
+ "shelf is not a canvas ancestor"
+ );
+ navigation_event(&mut app, |ctx| CanvasEvent::OpenUse(ctx, reference));
+ assert_eq!(app.selected, b);
+ view = fixture_view(
+ &app.behavior.scenario,
+ app.scenario_context().unwrap(),
+ false,
+ 68.,
+ );
+ tree.diff(view.as_widget());
+ redraw(&mut view, &mut tree, size, &mut app);
+ let (_, opened) = geometry(&mut view, &mut tree, size);
+ assert_eq!(opened.scrolls["scenario-canvas"].1, Vector::ZERO);
+ navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, Some(2.)));
+ navigation_event(&mut app, CanvasEvent::Focus);
+ navigation_event(&mut app, CanvasEvent::Back);
+ assert_eq!(app.selected, reference);
+ assert!(app.behavior.scenario.focused());
+ assert_eq!(
+ app.behavior.scenario.presentation.shelf,
+ Some(Shelf::Selection)
+ );
+ assert_eq!(
+ app.behavior
+ .scenario
+ .presentation
+ .scale(Size::new(800., 1200.)),
+ 0.75
+ );
+ view = fixture_view(
+ &app.behavior.scenario,
+ app.scenario_context().unwrap(),
+ false,
+ 68.,
+ );
+ tree.diff(view.as_widget());
+ redraw(&mut view, &mut tree, size, &mut app);
+ let (node, returned) = geometry(&mut view, &mut tree, size);
+ assert_eq!(returned.scrolls["scenario-canvas"].1, canvas_offset);
+ assert_eq!(returned.scrolls["scenario-details"].1, Vector::new(0., 80.));
+ for area in [ScrollArea::Canvas, ScrollArea::Page] {
+ assert!(
+ app.behavior
+ .scenario
+ .scroll_restore(app.scenario_context().unwrap(), area)
+ .is_none()
+ );
+ }
+ scroll(
+ &mut view,
+ &mut tree,
+ &node,
+ "scenario-canvas",
+ Vector::new(90., 220.),
+ );
+ redraw(&mut view, &mut tree, size, &mut app);
+ assert_eq!(
+ geometry(&mut view, &mut tree, size).1.scrolls["scenario-canvas"]
+ .1
+ .y,
+ 220.,
+ "receipt cannot snap a later gesture back"
+ );
+ assert!(std::sync::Arc::ptr_eq(&source, &app.document.snapshot()));
+ assert!(!app.document.can_undo());
+}
+
+#[test]
+fn scenario_workspace_fit_uses_measured_inner_viewport_and_rejects_nonfinite_inputs() {
+ let graph = Size::new(900., 1400.);
+ let mut presentation = Presentation::default();
+ assert_eq!(presentation.scale(graph), 1.);
+ presentation.zoom(None);
+ assert_eq!(
+ presentation.scale(graph),
+ 1.,
+ "no invented window-height fallback"
+ );
+ assert!(presentation.viewport(Size::new(484., 204.)));
+ assert!(!presentation.viewport(Size::new(484., 204.)));
+ let scale = presentation.scale(graph);
+ assert!(graph.width * scale <= 468. && graph.height * scale <= 188.);
+ assert!(!presentation.fit_limited(graph));
+ for bad in [f32::NAN, f32::INFINITY, 0., -1.] {
+ assert!(!presentation.zoom(Some(bad)));
+ assert!(!presentation.viewport(Size::new(bad, 100.)));
+ assert!(!presentation.viewport(Size::new(100., bad)));
+ assert_eq!(presentation.scale(graph), scale);
+ }
+ assert!(presentation.fit_limited(Size::new(900., 10000.)));
+ assert!(presentation.zoom(Some(1.25)));
+ presentation.viewport(Size::new(200., 100.));
+ assert_eq!(
+ presentation.scale(graph),
+ 1.25,
+ "manual zoom survives form/viewport resize"
+ );
+}
+
+#[test]
+fn scenario_workspace_open_and_back_restore_owner_and_mounted_scroll_after_preselection() {
+ use super::super::direct_tests::{navigation_event, navigation_fixture};
+ let (p, [a, b, _], [reference, _, _]) = navigation_fixture();
+ let mut app = App::with_project(p);
+ let _ = app.update(Message::Select(a));
+ let _ = app.update(Message::Page(crate::app::Page::Behavior));
+ navigation_event(&mut app, |ctx| {
+ CanvasEvent::Choose(ctx, CanvasTool::Message(MessageStyle::Timer))
+ });
+ navigation_event(&mut app, |ctx| CanvasEvent::Shelf(ctx, Shelf::Selection));
+ navigation_event(&mut app, CanvasEvent::Focus);
+ navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, Some(0.75)));
+ let size = Size::new(1050., 570.);
+ let mut view = fixture_view(
+ &app.behavior.scenario,
+ app.scenario_context().unwrap(),
+ false,
+ 68.,
+ );
+ let mut tree = widget::Tree::new(view.as_widget());
+ redraw(&mut view, &mut tree, size, &mut app);
+ let (node, _) = geometry(&mut view, &mut tree, size);
+ let offset = Vector::new(90., 180.);
+ scroll(&mut view, &mut tree, &node, "scenario-canvas", offset);
+ scroll(
+ &mut view,
+ &mut tree,
+ &node,
+ "scenario-details",
+ Vector::new(0., 80.),
+ );
+ redraw(&mut view, &mut tree, size, &mut app);
+
+ let _ = app.update(Message::Command(Command::Rename(
+ b,
+ "Renamed definition".into(),
+ )));
+ assert_eq!(app.selected, b);
+ assert_eq!(app.scenario_context().unwrap().scenario, a);
+ let before_open = app.document.snapshot();
+ navigation_event(&mut app, |ctx| CanvasEvent::OpenUse(ctx, reference));
+ assert_eq!(app.selected, b);
+ assert_eq!(app.scenario_context().unwrap().scenario, b);
+ assert!(std::sync::Arc::ptr_eq(
+ &before_open,
+ &app.document.snapshot()
+ ));
+ view = fixture_view(
+ &app.behavior.scenario,
+ app.scenario_context().unwrap(),
+ false,
+ 68.,
+ );
+ tree.diff(view.as_widget());
+ redraw(&mut view, &mut tree, size, &mut app);
+ let (_, opened) = geometry(&mut view, &mut tree, size);
+ assert_eq!(opened.scrolls["scenario-canvas"].1, Vector::ZERO);
+
+ let _ = app.update(Message::Command(Command::Rename(
+ reference,
+ "Renamed caller use".into(),
+ )));
+ assert_eq!(app.selected, reference);
+ assert_eq!(app.scenario_context().unwrap().scenario, b);
+ let before_back = app.document.snapshot();
+ let revision = app.document.revision();
+ navigation_event(&mut app, CanvasEvent::Back);
+ assert_eq!(app.selected, reference);
+ assert_eq!(app.scenario_context().unwrap().scenario, a);
+ assert_eq!(
+ app.behavior.scenario.canvas_tool(a),
+ CanvasTool::Message(MessageStyle::Timer)
+ );
+ assert!(app.behavior.scenario.focused());
+ assert_eq!(
+ app.behavior.scenario.presentation.shelf,
+ Some(Shelf::Selection)
+ );
+ assert_eq!(app.behavior.scenario.presentation.zoom, Some(0.75));
+ view = fixture_view(
+ &app.behavior.scenario,
+ app.scenario_context().unwrap(),
+ false,
+ 68.,
+ );
+ tree.diff(view.as_widget());
+ redraw(&mut view, &mut tree, size, &mut app);
+ let (_, restored) = geometry(&mut view, &mut tree, size);
+ assert_eq!(restored.scrolls["scenario-canvas"].1, offset);
+ assert_eq!(restored.scrolls["scenario-details"].1, Vector::new(0., 80.));
+ assert_eq!(app.behavior.scenario.scroll_offset(), offset);
+ assert!(app.behavior.scenario.restore.is_none());
+ assert!(std::sync::Arc::ptr_eq(
+ &before_back,
+ &app.document.snapshot()
+ ));
+ assert_eq!(app.document.revision(), revision);
+ assert!(app.document.can_undo());
+ assert!(!app.document.can_redo());
+}
+
+#[test]
+fn scenario_workspace_viewport_events_are_passive_guarded_and_do_not_apply_raw_forms() {
+ use super::super::direct_tests::{fixture, navigation_event};
+ let f = fixture();
+ let mut app = App::with_project(f.project);
+ let _ = app.update(Message::Select(f.owner));
+ let _ = app.update(Message::Page(crate::app::Page::Behavior));
+ let _ = app.update(event(Event::Start(f.owner, Tool::Message)));
+ let _ = app.update(event(Event::Name(" raw 雨 ".into())));
+ let raw = app.behavior.recovery_snapshot().unwrap();
+ let accepted = app.document.snapshot();
+ let context = app.scenario_context().unwrap();
+ app.busy = true;
+ let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Viewport(
+ context,
+ Size::new(500., 200.),
+ )));
+ assert_ne!(app.scenario_context(), Some(context));
+ let epoch = app.canvas_epoch;
+ let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Viewport(
+ context,
+ Size::new(500., 200.),
+ )));
+ assert_eq!(app.canvas_epoch, epoch);
+ for mismatch in 0..3 {
+ let mut stale = context;
+ match mismatch {
+ 0 => stale.document = Id::new_v4(),
+ 1 => stale.scenario = Id::new_v4(),
+ _ => stale.revision += 1,
+ }
+ let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Viewport(
+ stale,
+ Size::new(900., 400.),
+ )));
+ assert_eq!(app.canvas_epoch, epoch);
+ }
+ app.busy = false;
+ navigation_event(&mut app, |ctx| CanvasEvent::Shelf(ctx, Shelf::Tools));
+ navigation_event(&mut app, CanvasEvent::Focus);
+ navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, None));
+ assert_eq!(app.behavior.recovery_snapshot().unwrap(), raw);
+ assert!(std::sync::Arc::ptr_eq(&accepted, &app.document.snapshot()));
+ assert!(!app.document.can_undo());
+}
+
+#[test]
+fn scenario_workspace_choosing_tools_preserves_valid_and_invalid_pending_editors() {
+ use super::super::direct_tests::{fixture, navigation_event};
+ for editor in 0..3 {
+ for valid in [false, true] {
+ let f = fixture();
+ let mut app = App::with_project(f.project);
+ let _ = app.update(Message::Select(f.owner));
+ let _ = app.update(Message::Page(crate::app::Page::Behavior));
+ match editor {
+ 0 => {
+ let _ = app.update(Message::Field(
+ if valid { "/name" } else { "/id" }.into(),
+ if valid {
+ " Retained property 雨 "
+ } else {
+ "not-a-uuid"
+ }
+ .into(),
+ ));
+ assert!(app.draft_dirty());
+ assert_eq!(app.draft.as_ref().unwrap().command().is_ok(), valid);
+ }
+ 1 => {
+ let raw = if valid {
+ format!("// Retained source 雨\n{}", app.source_editor.text())
+ } else {
+ "? invalid source 雨".into()
+ };
+ let _ = app.update(Message::SourceEdit(
+ iced::widget::text_editor::Action::SelectAll,
+ ));
+ let _ = app.update(Message::SourceEdit(
+ iced::widget::text_editor::Action::Edit(
+ iced::widget::text_editor::Edit::Paste(std::sync::Arc::new(raw)),
+ ),
+ ));
+ assert!(app.source_dirty());
+ assert_eq!(
+ crate::source_workspace::compile(&app.source_workspace.bundle()).is_ok(),
+ valid
+ );
+ }
+ _ => {
+ let _ = app.update(event(Event::Start(
+ f.owner,
+ if valid { Tool::Fragment } else { Tool::Action },
+ )));
+ let _ = app.update(event(Event::Name(" Retained behavior 雨 ".into())));
+ let _ = app.update(event(Event::Expression("ready".into())));
+ let _ = app.update(event(Event::SecondGuard("otherwise".into())));
+ assert!(app.behavior.has_pending());
+ assert_eq!(
+ app.behavior
+ .scenario
+ .draft
+ .as_ref()
+ .unwrap()
+ .command(app.project())
+ .is_ok(),
+ valid
+ );
+ }
+ }
+ let source = serde_json::to_value(app.source_workspace.recovery_snapshot()).unwrap();
+ let properties = app
+ .draft
+ .as_ref()
+ .map(|draft| serde_json::to_value(draft.recovery_snapshot()).unwrap());
+ let behavior = app.behavior.recovery_snapshot().unwrap();
+ let text = app.source_editor.text();
+ let accepted = app.document.snapshot();
+ let selected = app.selected;
+ let context = app.scenario_context();
+ let tool = app.behavior.scenario.canvas_tool(f.owner);
+ navigation_event(&mut app, |ctx| {
+ CanvasEvent::Choose(ctx, CanvasTool::Message(MessageStyle::Timer))
+ });
+ assert!(
+ app.status.contains("Apply or cancel"),
+ "editor={editor} valid={valid}: {}",
+ app.status
+ );
+ assert_eq!(app.source_editor.text(), text);
+ assert_eq!(
+ serde_json::to_value(app.source_workspace.recovery_snapshot()).unwrap(),
+ source
+ );
+ assert_eq!(
+ app.draft
+ .as_ref()
+ .map(|draft| serde_json::to_value(draft.recovery_snapshot()).unwrap()),
+ properties
+ );
+ assert_eq!(app.behavior.recovery_snapshot().unwrap(), behavior);
+ assert_eq!(app.selected, selected);
+ assert_eq!(app.scenario_context(), context);
+ assert_eq!(app.behavior.scenario.canvas_tool(f.owner), tool);
+ assert!(std::sync::Arc::ptr_eq(&accepted, &app.document.snapshot()));
+ assert!(!app.document.can_undo());
+ assert!(!app.document.can_redo());
+ }
+ }
+}
+
+#[test]
+fn scenario_workspace_whole_app_canvas_tree_survives_pending_shelves_and_focus() {
+ use iced::advanced::widget::tree;
+ use std::rc::Rc;
+ fn path_to(tree: &widget::Tree, tag: tree::Tag) -> Option> {
+ if tree.tag == tag {
+ return Some(vec![]);
+ }
+ tree.children.iter().enumerate().find_map(|(i, child)| {
+ let mut path = path_to(child, tag)?;
+ path.insert(0, i);
+ Some(path)
+ })
+ }
+ fn at<'a>(mut tree: &'a mut widget::Tree, path: &[usize]) -> &'a mut widget::Tree {
+ for &index in path {
+ tree = &mut tree.children[index];
+ }
+ tree
+ }
+ let f = super::super::direct_tests::fixture();
+ let mut app = App::with_project(f.project);
+ let _ = app.update(Message::Select(f.call));
+ let _ = app.update(Message::Page(crate::app::Page::Behavior));
+ let canvas: Element<'_, Message> =
+ canvas::Canvas::new(Scene::sequence(app.project(), f.owner, f.call)).into();
+ let tag = widget::Tree::new(canvas.as_widget()).tag;
+ drop(canvas);
+ let mut tree = widget::Tree::new(crate::views::view(&app).as_widget());
+ let mut path = path_to(&tree, tag).unwrap();
+ path.pop();
+ let marker = Rc::new(());
+ at(&mut tree, &path).state = tree::State::new(Rc::clone(&marker));
+ let _ = app.update(event(Event::Start(f.owner, Tool::Message)));
+ let _ = app.update(event(Event::Name(" untouched 雨 ".into())));
+ let raw = app.behavior.recovery_snapshot().unwrap();
+ let accepted = app.document.snapshot();
+ for shelf in [Shelf::Tools, Shelf::Selection, Shelf::Selection] {
+ super::super::direct_tests::navigation_event(&mut app, |ctx| {
+ CanvasEvent::Shelf(ctx, shelf)
+ });
+ super::super::direct_tests::navigation_event(&mut app, CanvasEvent::Focus);
+ tree.diff(crate::views::view(&app).as_widget());
+ let mut current = path_to(&tree, tag).unwrap();
+ current.pop();
+ assert_eq!(current, path);
+ assert_eq!(Rc::strong_count(&marker), 2);
+ assert!(Rc::ptr_eq(
+ at(&mut tree, &path).state.downcast_ref::>(),
+ &marker
+ ));
+ assert_eq!(app.behavior.recovery_snapshot().unwrap(), raw);
+ assert_eq!(app.selected, f.call);
+ assert!(std::sync::Arc::ptr_eq(&accepted, &app.document.snapshot()));
+ }
+}
+
+#[test]
+fn scenario_workspace_scaled_and_scrolled_scene_keeps_clicked_slots_and_message_endpoints() {
+ let f = super::super::direct_tests::fixture();
+ for scale in [0.25, 0.5, 1., 2.] {
+ for drag in [false, true] {
+ let mut scene = Scene::sequence(&f.project, f.owner, f.call);
+ let context = super::super::direct_tests::context(f.owner);
+ scene.scale = scale;
+ scene.scroll_offset = Vector::new(35., 40.);
+ scene.interaction = Some((context, CanvasTool::Message(MessageStyle::CallReply), true));
+ let slot = scene.slots.last().unwrap();
+ let expected = slot.slot;
+ let bounds = Rectangle::new(
+ Point::new(20., 80.),
+ Size::new(scene.size.width * scale, scene.size.height * scale),
+ );
+ let screen = |world: Point| {
+ Point::new(bounds.x + world.x * scale, bounds.y + world.y * scale)
+ - scene.scroll_offset
+ };
+ let a = screen(Point::new(scene.lanes[&f.sender], slot.y));
+ let b = screen(Point::new(scene.lanes[&f.receiver], slot.y));
+ let mut input = super::super::super::SequenceInput::default();
+ let mut messages = vec![];
+ let mut events = vec![
+ (mouse::Event::CursorMoved { position: a }, a),
+ (mouse::Event::ButtonPressed(mouse::Button::Left), a),
+ ];
+ if !drag {
+ events.push((mouse::Event::ButtonReleased(mouse::Button::Left), a));
+ }
+ events.push((mouse::Event::CursorMoved { position: b }, b));
+ if !drag {
+ events.push((mouse::Event::ButtonPressed(mouse::Button::Left), b));
+ }
+ events.push((mouse::Event::ButtonReleased(mouse::Button::Left), b));
+ for (event, cursor) in events {
+ if let Some(action) = canvas::Program::update(
+ &scene,
+ &mut input,
+ &canvas::Event::Mouse(event),
+ bounds,
+ mouse::Cursor::Available(cursor + scene.scroll_offset),
+ ) {
+ if let Some(message) = action.into_inner().0 {
+ messages.push(message);
+ }
+ }
+ }
+ assert!(
+ matches!(messages.as_slice(), [Message::ScenarioCanvas(CanvasEvent::Begin { sender, receiver, slot, .. })]
+ if *sender == f.sender && *receiver == f.receiver && *slot == expected),
+ "scale={scale} drag={drag}"
+ );
+ }
+ }
+}
+
+#[test]
+fn scenario_workspace_scaled_nested_occurrences_and_reorder_keep_exact_sequence_identity() {
+ let (f, branch, anchor, _, _, _) = super::super::direct_tests::placement_fixture();
+ for scale in [0.25, 0.5, 1., 2.] {
+ let mut scene = Scene::sequence(&f.project, f.owner, f.call);
+ let context = super::super::direct_tests::context(f.owner);
+ scene.scale = scale;
+ scene.scroll_offset = Vector::new(35., 40.);
+ let bounds = Rectangle::new(
+ Point::new(20., 80.),
+ Size::new(scene.size.width * scale, scene.size.height * scale),
+ );
+ let screen = |world: Point| {
+ Point::new(bounds.x + world.x * scale, bounds.y + world.y * scale)
+ - Vector::new(35., 40.)
+ };
+ let send =
+ |scene: &Scene, input: &mut super::super::super::SequenceInput, event, cursor| {
+ canvas::Program::update(
+ scene,
+ input,
+ &canvas::Event::Mouse(event),
+ bounds,
+ mouse::Cursor::Available(cursor + scene.scroll_offset),
+ )
+ .and_then(|action| action.into_inner().0)
+ };
+ for tool in [Tool::Action, Tool::Observation, Tool::Reuse, Tool::Fragment] {
+ scene.interaction = Some((context, CanvasTool::Add(tool), true));
+ for expected in [
+ Slot {
+ parent: f.owner,
+ before: Some(f.reply),
+ },
+ Slot {
+ parent: branch,
+ before: Some(anchor),
+ },
+ ] {
+ let slot = scene
+ .slots
+ .iter()
+ .find(|slot| slot.slot == expected)
+ .unwrap();
+ let point = screen(Point::new(scene.lanes[&f.sender], slot.y));
+ let mut input = super::super::super::SequenceInput::default();
+ send(
+ &scene,
+ &mut input,
+ mouse::Event::CursorMoved { position: point },
+ point,
+ );
+ assert!(
+ send(
+ &scene,
+ &mut input,
+ mouse::Event::ButtonPressed(mouse::Button::Left),
+ point
+ )
+ .is_none()
+ );
+ let message = send(
+ &scene,
+ &mut input,
+ mouse::Event::ButtonReleased(mouse::Button::Left),
+ point,
+ )
+ .unwrap();
+ assert!(
+ matches!(message, Message::ScenarioCanvas(CanvasEvent::BeginOccurrence { tool: captured, slot, lifeline, .. })
+ if captured == tool && slot == expected && lifeline == tool.uses_lifeline().then_some(f.sender)),
+ "scale={scale} tool={tool:?} slot={expected:?}"
+ );
+ }
+ }
+ scene.interaction = Some((context, CanvasTool::Select, true));
+ let mut input = super::super::super::SequenceInput::default();
+ let expected = scene
+ .slots
+ .iter()
+ .rev()
+ .find(|slot| slot.slot.parent == f.owner)
+ .unwrap();
+ let start = screen(scene.reorder_handle().unwrap().center());
+ let end = screen(Point::new(17., expected.y));
+ send(
+ &scene,
+ &mut input,
+ mouse::Event::CursorMoved { position: start },
+ start,
+ );
+ send(
+ &scene,
+ &mut input,
+ mouse::Event::ButtonPressed(mouse::Button::Left),
+ start,
+ );
+ send(
+ &scene,
+ &mut input,
+ mouse::Event::CursorMoved { position: end },
+ end,
+ );
+ assert!(
+ matches!(send(&scene, &mut input, mouse::Event::ButtonReleased(mouse::Button::Left), end),
+ Some(Message::ScenarioCanvas(CanvasEvent::Reorder { occurrence, slot, .. }))
+ if occurrence == f.call && slot == expected.slot),
+ "scale={scale}"
+ );
+ }
+}
diff --git a/crates/oa-desktop/src/views.rs b/crates/oa-desktop/src/views.rs
index ff6b762e..54608744 100644
--- a/crates/oa-desktop/src/views.rs
+++ b/crates/oa-desktop/src/views.rs
@@ -228,13 +228,15 @@ pub fn view(app: &App) -> View<'_, Message> {
Page::Guide => crate::guide::view(&app.guide),
};
let process_layout = app.topology_context().is_some();
- let focus_process = process_layout && app.behavior.topology.focused;
+ let scenario_layout = app.scenario_context().is_some();
+ let focus_behavior = (process_layout && app.behavior.topology.focused)
+ || (scenario_layout && app.behavior.scenario.focused());
let mut body = if app.page == Page::Home {
row![container(main).width(Fill).height(Fill)].height(Fill)
- } else if process_layout {
+ } else if process_layout || scenario_layout {
// Keep main at the same widget-tree path across Focus toggles. The
// collapsed slot has no controls, so it cannot paint over or hit the canvas.
- let process_sidebar: View<'_, Message> = if focus_process {
+ let process_sidebar: View<'_, Message> = if focus_behavior {
Space::new().width(0).into()
} else {
sidebar.into()
@@ -242,7 +244,7 @@ pub fn view(app: &App) -> View<'_, Message> {
// Row::push would discard the zero-width slot and shift main again.
widget::Row::from_vec(vec![
container(process_sidebar)
- .width(if focus_process { 0 } else { 260 })
+ .width(if focus_behavior { 0 } else { 260 })
.height(Fill)
.clip(true)
.into(),
@@ -253,7 +255,7 @@ pub fn view(app: &App) -> View<'_, Message> {
} else {
row![sidebar, container(main).width(Fill).height(Fill)].height(Fill)
};
- if !focus_process && !matches!(app.page, Page::Home | Page::Text | Page::Guide) {
+ if !focus_behavior && !matches!(app.page, Page::Home | Page::Text | Page::Guide) {
body = body.push(inspector(app));
}
let footer = container(
diff --git a/harness/release-scenario-workspace-20260910.md b/harness/release-scenario-workspace-20260910.md
new file mode 100644
index 00000000..44468041
--- /dev/null
+++ b/harness/release-scenario-workspace-20260910.md
@@ -0,0 +1,46 @@
+# Scenario workspace — 2026-09-10
+
+Owner: **Codex-9e8r6t /root/behavior_audit**. Root assigned the bounded layout slice after [the source audit](release-behavior-gap-audit-20260910.md). Scope is the scenario canvas, compact shelves, focus and independently scrollable form. Existing semantics, source grammar, REST commands and raw-draft recovery remain authoritative. No owner creation, missing-source creation, reuse semantics, state naming or commits/pushes are included.
+
+## Coordination and current status
+
+**Implementation in progress after integrated `057eddd` and F1 follow-up `a70de55`.** Root released the Rust source hold after palette_finish's integrated checkpoint compile, then assigned this worker the next sole Cargo lease after release_audit's metadata run. Codex-956atf's clicked-slot Action/Observation/Reuse/Fragment and caller-navigation work is integrated and retained; its separate lifetime projection remains owned on the other host in standalone `scenario_lifecycle.rs`. Root owns real native QA, integration and common ledgers/Wiki. No commits/pushes by this worker.
+
+## Bounded implementation design
+
+1. Integrate the other host's clicked-slot work first. Then move the existing scenario view/outline to `behavior/scenario/workspace.rs`, leaving its updated draft/planner/event semantics in `scenario.rs`. Keep the extracted fields/forms behavior unchanged before replacing its outer layout.
+2. Use a compact stable header for owner, focus and shelf/camera actions. Tools and Selection are disclosures; place the existing sequence outline inside a scrolling shelf rather than beneath the diagram. Default shelves closed. Active-tool guidance is concise and always visible.
+3. Keep a fixed widget-tree slot for the closed shelf (`Column::from_vec`; a zero-height child must not disappear). Allocate header and shelf once, then divide actual remaining height between the canvas and pending editor. Keep both canvas and form slots stable when the form opens/closes. Fields scroll independently; Create/Cancel remain outside the field scroller. No enclosing page scrollable.
+4. Extend the existing process focus layout slot in `views.rs` to scenarios without moving the main workspace in the widget tree. Focus must retain selection, canvas state and raw draft. Coordinate any App change with root.
+5. Evaluate measured viewport Fit/manual scale against actual Scene geometry. Existing Scene draws with `scale` and transforms pointer positions by that scale. If included, get viewport size from a sensor around the actual canvas scrollable, accept passive viewport changes under document/owner/revision guards, and invalidate old pointer epochs on geometry/scale changes. Use a readable default of 100%; tool changes must not force another scale. A Fit overview must be explicit, and limited overview scale must be disclosed. Do not infer available height from total window size.
+
+## Planned verification
+
+- Use the exact generic production pane builders with real Iced layout and scroll operations at minimum/wide center-pane sizes, with short/wrapped headers, open/closed shelves, pending forms and focused widths. Assert all canvas/action/field rectangles lie within the actual parent allocation; assert remaining space grows with the viewport.
+- Establish real canvas/form scroll offsets, then diff across shelves, form open/cancel, focus and resize. Verify stable widget state and independent scrolling, not just widget counts. Full App tree-diff tests also preserve selected UUID, raw behavior recovery snapshot and the accepted document Arc.
+- If camera controls are included, test default/manual/Fit against the scene bounds and measured viewport; finite inputs only; correct same-size notifications; stale context rejection; production click-click, drag and reorder at multiple scales with nonzero scroll origins.
+- Retain existing scenario paired-call/branch/reorder/source/Undo/Redo/recovery tests. New geometry events remain passive during async saves and modal guards; drawing continues to reject pending source/properties/behavior edits.
+- Root-owned native before/after task at 1440×900 and 1100×700: draw into a visible ALT operand, open a message/value form, scroll fields, reach Create/Cancel, open/close shelves and Focus, then Save/Undo/Redo/reopen. Actual Windows and Linux acceptance remain separate from unit/build results.
+
+## Implemented slice
+
+- Extracted the current scenario form/sequence outline into `behavior/scenario/workspace.rs`. Existing field choices, clicked slots, command planning, operation identity, correlation, raw recovery and ordered occurrence semantics remain in the original scenario module. Scene row/scope projection maps are unchanged for the separately owned lifetime renderer work.
+- Replaced window-height subtraction/clamps and the enclosing page scroller with a permanent header/shelf/body tree. The canvas and form split the actual remaining height; opening/cancelling a form does not change the canvas tree path. Create/Cancel remain pinned above independently scrolling form fields. Tools and Selection are compact disclosures, the current tool remains visible, and Back to caller is reachable in the compact header.
+- Added scenario presentation-only Focus, shelf and zoom events with exact context checks, plus passive measured viewport messages guarded by document/owner/revision. Default scale is 100%; explicit Fit uses the actual inner Scrollable with scrollbar allowance, with a disclosed 5% lower bound. Manual scale is retained across viewport changes. Nonfinite camera/scroll values are rejected.
+- Caller navigation now retains scale/shelf/focus alongside both actual scroll offsets. The old Page scroll channel becomes the independent outline shelf; only the canvas channel contributes to canvas hit coordinates. Existing mounted observer restoration/receipt handling is retained, with a renderer-generic entry point for real Iced layout tests.
+- Root's immutable integrated 618-test run independently exposed an existing Back failure after editing a caller reference. Diagnosed two related cases: a retarget edit can select a UUID outside the visible definition, and a rename can preselect the caller UUID so `select` skips restoring its active owner. The narrow Back handler now explicitly restores a valid caller's owner and normalizes a rejected Back's cross-owner selection to the still-visible definition. The existing test's source/history assertions are retained and its active-owner assertion is strengthened. The other host's independent [review #79](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/1#issuecomment-79) identified the mirrored OpenUse preselection case; it now explicitly establishes the target owner. A dedicated mounted regression exercises both cases and their actual scroll restoration, tool/camera state, unchanged accepted Arc and history.
+- Found and fixed an existing tool-selection inconsistency during the layout review: `CanvasEvent::Choose` called `App::apply`, which could commit a valid property draft when merely changing a drawing tool. Root authorized an explicit dirty-editor guard. The Tools shelf now disables its tool control and + Lifeline while source, property or behavior inputs are pending. An actual App-path regression covers valid and invalid drafts from all three editors, checking their validity and exact raw/accepted-state retention. Generic behavior Start handling remains a separately coordinated boundary for the new owner-creation work.
+
+## Verification in progress
+
+New `behavior/scenario/workspace/tests.rs` exercises the production pane builders with Iced's null renderer: minimum/wide geometry, pinned actions, canvas/form scroll retention across disclosures and resizing, mounted caller canvas/shelf restoration with camera state, measured Fit bounds and nonfinite rejection, passive/stale viewport events with raw drafts, full App tree identity, and scaled click-click/drag endpoints, root/ALT occurrences and reordering with nonzero scroll offsets.
+
+The first focused compile reported an ambiguous `column!` import and an invariant Button lifetime introduced by extraction; both were corrected. The second compile passed six of seven tests, exposing an idle shelf allocation that left 282 px instead of the required 290 px canvas in the smallest, tallest-header case. Reducing the idle disclosure from 112 to 100 px resolved this without imposing a synthetic canvas-height clamp.
+
+The third focused compile passed **8/8 workspace tests, 620 filtered**, after 5m27s compilation and 1.63s test execution. Across the full matrix, real Iced layout establishes at least 290 px idle canvas, 145 px pending canvas and 100 px pending form fields, with pinned actions. The tightest 500×570 / 132 px header / open-shelf allocation calculates to 294 px idle and 150 px pending canvas. These pane tests do not establish whole-window native acceptance. Logs/status: `.local/scenario-workspace-20260910/workspace-tests{,-2,-3}.log` and matching `-status.json`.
+
+After root integrated the upstream explicit-enum F1 seed repair as `81668f9`, the complete scenario filter passed **52/52**, 579 filtered, in 29.60s after a 3m20s compile. This includes all ten new workspace tests and the unchanged original failing Back assertion with the added active-owner assertion. The history filter then passed **42/42**, 589 filtered, in 25.50s, including both formerly failing exhaustive/UUID-prose cases and the explicit-enum seed regression. Logs and exit-status JSON: `.local/scenario-workspace-20260910/{scenario-tests,history-tests}{.log,-status.json}`.
+
+Root received an immutable **631-test** executable in `.local/scenario-workspace-20260910/tests-81668f9/arcadia-studio-tests.exe`, 104,007,680 bytes, SHA256 `FF5999466032E4FBDD615969E349720EC6B29A5945F7E3A57CCF976D8A2736E7`. The sibling `libunwind.dll` is 190,976 bytes, SHA256 `F45604A12E8BCD839FD86F667C1BE1CC7C4F9DA2B3C69FF381AC708F0E1AE7D4`. `manifest.json` records the source HEAD and 252 working-tree input hashes (including clearly marked present but undeclared creation modules), SHA256 `D06C72EBBE9B39A7CE2CCCFF888D2DE7892DFD07EA166FC308155642D37708EE`. Root owns the full run of this immutable artifact while this worker builds the GUI and performs fmt/strict Clippy under the coordinated source freeze.
+
+Scoped rustfmt and diff checks pass. No current GUI binary or native acceptance is claimed at this checkpoint. Root's earlier immutable artifact had 615 passes and three failures; all three failing cases now pass the focused current gates above.
From 80cf197be6f1d400bf0b9c84bf8557f88c895aa6 Mon Sep 17 00:00:00 2001
From: Codex
Date: Fri, 11 Sep 2026 01:27:48 -0700
Subject: [PATCH 13/14] Remeasure scenario Fit viewport after owner or document
changes
---
.../src/behavior/scenario/workspace.rs | 1 +
.../src/behavior/scenario/workspace/tests.rs | 66 +++++++++++++++++++
2 files changed, 67 insertions(+)
diff --git a/crates/oa-desktop/src/behavior/scenario/workspace.rs b/crates/oa-desktop/src/behavior/scenario/workspace.rs
index ea63b579..121136ce 100644
--- a/crates/oa-desktop/src/behavior/scenario/workspace.rs
+++ b/crates/oa-desktop/src/behavior/scenario/workspace.rs
@@ -649,6 +649,7 @@ fn canvas_pane<'a, R: iced::advanced::text::Renderer + 'a>(
.width(Fill)
.height(Fill),
))
+ .key((context.document, context.scenario))
.on_show(move |size| Message::ScenarioCanvas(CanvasEvent::Viewport(context, size)))
.on_resize(move |size| Message::ScenarioCanvas(CanvasEvent::Viewport(context, size))),
)
diff --git a/crates/oa-desktop/src/behavior/scenario/workspace/tests.rs b/crates/oa-desktop/src/behavior/scenario/workspace/tests.rs
index 8829a024..8c32bfa7 100644
--- a/crates/oa-desktop/src/behavior/scenario/workspace/tests.rs
+++ b/crates/oa-desktop/src/behavior/scenario/workspace/tests.rs
@@ -341,6 +341,72 @@ fn scenario_workspace_fit_uses_measured_inner_viewport_and_rejects_nonfinite_inp
);
}
+#[test]
+fn scenario_workspace_same_size_owner_and_document_changes_remeasure_fit_viewport() {
+ use super::super::direct_tests::{navigation_event, navigation_fixture};
+ let (project, [a, b, _], [reference, _, _]) = navigation_fixture();
+ let mut app = App::with_project(project.clone());
+ let _ = app.update(Message::Select(a));
+ let _ = app.update(Message::Page(crate::app::Page::Behavior));
+ let size = Size::new(500., 570.);
+ let graph = Size::new(1800., 2400.);
+ let mut view = fixture_view(
+ &app.behavior.scenario,
+ app.scenario_context().unwrap(),
+ false,
+ 68.,
+ );
+ let mut tree = widget::Tree::new(view.as_widget());
+ redraw(&mut view, &mut tree, size, &mut app);
+ let first_viewport = app.behavior.scenario.presentation.viewport.unwrap();
+ navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, None));
+ assert!(app.behavior.scenario.presentation.scale(graph) < 1.);
+
+ navigation_event(&mut app, |ctx| CanvasEvent::OpenUse(ctx, reference));
+ assert_eq!(app.scenario_context().unwrap().scenario, b);
+ assert!(app.behavior.scenario.presentation.viewport.is_none());
+ view = fixture_view(
+ &app.behavior.scenario,
+ app.scenario_context().unwrap(),
+ false,
+ 68.,
+ );
+ tree.diff(view.as_widget());
+ redraw(&mut view, &mut tree, size, &mut app);
+ assert_eq!(
+ app.behavior.scenario.presentation.viewport,
+ Some(first_viewport)
+ );
+ navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, None));
+ let fitted = app.behavior.scenario.presentation.scale(graph);
+ assert!(graph.width * fitted <= first_viewport.width - 16.);
+ assert!(graph.height * fitted <= first_viewport.height - 16.);
+
+ let prior_document = app.scenario_context().unwrap().document;
+ app = App::with_project(project);
+ let _ = app.update(Message::Select(b));
+ let _ = app.update(Message::Page(crate::app::Page::Behavior));
+ let context = app.scenario_context().unwrap();
+ assert_ne!(context.document, prior_document);
+ assert_eq!(
+ context.scenario, b,
+ "semantic UUID is retained across documents"
+ );
+ assert!(app.behavior.scenario.presentation.viewport.is_none());
+ let accepted = app.document.snapshot();
+ view = fixture_view(&app.behavior.scenario, context, false, 68.);
+ tree.diff(view.as_widget());
+ redraw(&mut view, &mut tree, size, &mut app);
+ assert_eq!(
+ app.behavior.scenario.presentation.viewport,
+ Some(first_viewport)
+ );
+ navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, None));
+ assert_eq!(app.behavior.scenario.presentation.scale(graph), fitted);
+ assert!(std::sync::Arc::ptr_eq(&accepted, &app.document.snapshot()));
+ assert!(!app.document.can_undo());
+}
+
#[test]
fn scenario_workspace_open_and_back_restore_owner_and_mounted_scroll_after_preselection() {
use super::super::direct_tests::{navigation_event, navigation_fixture};
From c6d296a7e22c8f126834a1e217b88551634dd5cf Mon Sep 17 00:00:00 2001
From: Codex
Date: Fri, 11 Sep 2026 02:04:38 -0700
Subject: [PATCH 14/14] Preserve native authoring evidence and compared
semantic contracts
---
.gitattributes | 4 +
PROJECT_GOAL.md | 29 +-
harness/DECISIONS.md | 41 +
harness/PLAN.md | 22 +-
harness/STATE.md | 104 +-
harness/TASK_LEDGER.md | 56 +-
.../analysis-context-20260911/README.md | 29 +
.../B-factored-cleared-override.study | 107 ++
.../generated/B-factored-cycle.rejected-study | 108 +++
.../generated/B-factored-fixed-all.study | 108 +++
.../generated/B-factored-frozen-case20.study | 55 ++
.../generated/B-factored-missing.study | 108 +++
.../generated/B-factored-provenance.md | 31 +
.../generated/B-factored-threshold.study | 108 +++
.../generated/B-factored.study | 108 +++
.../generated/B-inline-cleared-override.study | 105 ++
.../generated/B-inline-cycle.rejected-study | 106 ++
.../generated/B-inline-fixed-all.study | 106 ++
.../generated/B-inline-frozen-case20.study | 53 +
.../generated/B-inline-missing.study | 106 ++
.../generated/B-inline-provenance.md | 31 +
.../generated/B-inline-threshold.study | 106 ++
.../generated/B-inline.study | 106 ++
.../generated/D-factored-cycle.rejected-study | 524 ++++++++++
.../D-factored-deleted-row.rejected-study | 523 ++++++++++
.../generated/D-factored-fixed-all.study | 524 ++++++++++
.../generated/D-factored-frozen-case20.study | 55 ++
.../generated/D-factored-missing.study | 524 ++++++++++
.../generated/D-factored-provenance.md | 31 +
.../generated/D-factored-threshold.study | 524 ++++++++++
.../generated/D-factored.study | 524 ++++++++++
.../generated/D-inline-cycle.rejected-study | 522 ++++++++++
.../D-inline-deleted-row.rejected-study | 521 ++++++++++
.../generated/D-inline-fixed-all.study | 522 ++++++++++
.../generated/D-inline-fixed-one-only.study | 522 ++++++++++
.../generated/D-inline-frozen-case20.study | 53 +
.../generated/D-inline-missing.study | 522 ++++++++++
.../generated/D-inline-provenance.md | 31 +
.../generated/D-inline-threshold.study | 522 ++++++++++
.../generated/D-inline.study | 522 ++++++++++
.../generated/EVIDENCE.json | 166 ++++
.../generated/RESULTS.md | 14 +
.../analysis-context-20260911/main.rs | 913 ++++++++++++++++++
.../textual-authoring-20260911/README.md | 24 +
.../alarm-response.arcadia | 110 +++
.../observed/canonical.arcadia | 344 +++++++
.../observed/participants.arcadia | 1 +
.../observed/renamed.arcadia | 110 +++
.../observed/reparented.arcadia | 114 +++
.../observed/split-entry.arcadia | 111 +++
.../observed/stabilized.arcadia | 110 +++
.../probe-output.txt | 4 +
.../textual-authoring-20260911/probe.rs | 107 ++
...se-analysis-context-experiment-20260911.md | 94 ++
...ease-analysis-semantics-design-20260910.md | 366 +++++++
...ase-libraries-semantics-design-20260910.md | 292 ++++++
harness/release-scenario-native-20260911.md | 107 ++
...ease-semantic-proposals-review-20260910.md | 124 +++
...lease-textual-authoring-design-20260911.md | 214 ++++
59 files changed, 11952 insertions(+), 76 deletions(-)
create mode 100644 harness/experiments/analysis-context-20260911/README.md
create mode 100644 harness/experiments/analysis-context-20260911/generated/B-factored-cleared-override.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/B-factored-cycle.rejected-study
create mode 100644 harness/experiments/analysis-context-20260911/generated/B-factored-fixed-all.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/B-factored-frozen-case20.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/B-factored-missing.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/B-factored-provenance.md
create mode 100644 harness/experiments/analysis-context-20260911/generated/B-factored-threshold.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/B-factored.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/B-inline-cleared-override.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/B-inline-cycle.rejected-study
create mode 100644 harness/experiments/analysis-context-20260911/generated/B-inline-fixed-all.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/B-inline-frozen-case20.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/B-inline-missing.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/B-inline-provenance.md
create mode 100644 harness/experiments/analysis-context-20260911/generated/B-inline-threshold.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/B-inline.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/D-factored-cycle.rejected-study
create mode 100644 harness/experiments/analysis-context-20260911/generated/D-factored-deleted-row.rejected-study
create mode 100644 harness/experiments/analysis-context-20260911/generated/D-factored-fixed-all.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/D-factored-frozen-case20.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/D-factored-missing.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/D-factored-provenance.md
create mode 100644 harness/experiments/analysis-context-20260911/generated/D-factored-threshold.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/D-factored.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/D-inline-cycle.rejected-study
create mode 100644 harness/experiments/analysis-context-20260911/generated/D-inline-deleted-row.rejected-study
create mode 100644 harness/experiments/analysis-context-20260911/generated/D-inline-fixed-all.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/D-inline-fixed-one-only.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/D-inline-frozen-case20.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/D-inline-missing.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/D-inline-provenance.md
create mode 100644 harness/experiments/analysis-context-20260911/generated/D-inline-threshold.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/D-inline.study
create mode 100644 harness/experiments/analysis-context-20260911/generated/EVIDENCE.json
create mode 100644 harness/experiments/analysis-context-20260911/generated/RESULTS.md
create mode 100644 harness/experiments/analysis-context-20260911/main.rs
create mode 100644 harness/experiments/textual-authoring-20260911/README.md
create mode 100644 harness/experiments/textual-authoring-20260911/alarm-response.arcadia
create mode 100644 harness/experiments/textual-authoring-20260911/observed/canonical.arcadia
create mode 100644 harness/experiments/textual-authoring-20260911/observed/participants.arcadia
create mode 100644 harness/experiments/textual-authoring-20260911/observed/renamed.arcadia
create mode 100644 harness/experiments/textual-authoring-20260911/observed/reparented.arcadia
create mode 100644 harness/experiments/textual-authoring-20260911/observed/split-entry.arcadia
create mode 100644 harness/experiments/textual-authoring-20260911/observed/stabilized.arcadia
create mode 100644 harness/experiments/textual-authoring-20260911/probe-output.txt
create mode 100644 harness/experiments/textual-authoring-20260911/probe.rs
create mode 100644 harness/release-analysis-context-experiment-20260911.md
create mode 100644 harness/release-analysis-semantics-design-20260910.md
create mode 100644 harness/release-libraries-semantics-design-20260910.md
create mode 100644 harness/release-scenario-native-20260911.md
create mode 100644 harness/release-semantic-proposals-review-20260910.md
create mode 100644 harness/release-textual-authoring-design-20260911.md
diff --git a/.gitattributes b/.gitattributes
index e01692e4..02db8402 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -15,6 +15,7 @@
# Exact source/recovery oracles and archived ledgers retain their measured bytes.
# Keep ordinary text diffs; only disable checkout/check-in newline conversion.
+PROJECT_GOAL.md -text whitespace=cr-at-eol
harness/fixtures/release1-native/** -text
harness/fixtures/interface-operation-probe/** -text
harness/history/2026-09-06-ledgers-before-consolidation/** -text
@@ -23,6 +24,9 @@ crates/oa-desktop/src/recovery/fixtures/*.json -text
harness/fixtures/source7-native/** -text
harness/fixtures/basic-editing/** -text
harness/fixtures/scenario-authoring/base/** -text whitespace=cr-at-eol
+harness/experiments/textual-authoring-20260911/** -text whitespace=cr-at-eol
+harness/experiments/textual-authoring-20260911/observed/split-entry.arcadia whitespace=-blank-at-eol,cr-at-eol
+harness/experiments/analysis-context-20260911/** -text whitespace=cr-at-eol
# Preserve checksum-bound font notices verbatim through Git round trips.
scripts/windows-notices/GNOME-Cantarell-61362600e104-COPYING -text
diff --git a/PROJECT_GOAL.md b/PROJECT_GOAL.md
index bc536a56..fb9d8c7b 100644
--- a/PROJECT_GOAL.md
+++ b/PROJECT_GOAL.md
@@ -608,9 +608,9 @@ Focus on making the application modern, extensible, intuitive and easy to use. W
Try to address all known problems / downsides of the original ARCADIA Capella software.
-Version 1 MVP in the form of working Operational Analysis layer is done.
-Version 2 MVP in the form of working Operation Analysis + System Analysis is done.
-You should make the 1st release version of the project with fully feature complete Operation Analysis + System Architecture, according to this application specification.
+Version 1 MVP in the form of working Operational Analysis layer is done.
+Version 2 MVP in the form of working Operation Analysis + System Analysis is done.
+You should make the 1st release version of the project with fully feature complete Operation Analysis + System Architecture, according to this application specification.
Remaining ARCADIA layers will be added in future releases.
Transitioning of model elements between layers (in both directions) should be easy and intuitive.
@@ -623,19 +623,19 @@ You should implement most of the ARCADIA semantics, while adressing original ARC
The original ARCADIA Capella UI is very bloated, clunky, unintuitive and hard to use. In addition, it lacks a number of quality of life features. You should at all cost avoid making the same mistakes as the developers of the original ARCADIA Capella
-You should explore the functionality & UI of the original ARCADIA Capella to take the most useful and powerful ideas from it. While making them modern, easy to use and understand
+You should explore the functionality & UI of the original ARCADIA Capella to take the most useful and powerful ideas from it. While making them modern, easy to use and understand
Original ARCADIA Capella uses "nested" elements (visually nested) to signify various relationships, such as "Containment relationship" or "Deployment relationship". Instead of using visually noisy arrows / graph edges. I pretty much like this approach and want to see something similar to it in this application
To explore the original ARCADIA Capella UI & semantics you may use web resources / web search / official and community docs. Or open the original ARCADIA Capella installed on this desktop machine for exploration
-Your second, parallel, goal is to create a textual representation for ARCADIA, similar to SysML v2. So a user can either edit a system model using UI (graphical representation) or text. Both graphical and textual representations should be fully equivalent and have full parity. You may take inspiration from SysML v2. Both graphical and textual representations should have one overarching, concrete, unambiguous spec in reST format, similar to SysML v2 strictness
-The application should support both forms of editing
-The textual representation should be easily editable and understandble for both humans and AI
-The textual representation should be easily handled by version control tools such as Git
-The underlying semantics of the textual / graphical representation should support traceability of various development artifacts (for example - code, CAD files, electrical schematics, Docx files and anything in between)
+Your second, parallel, goal is to create a textual representation for ARCADIA, similar to SysML v2. So a user can either edit a system model using UI (graphical representation) or text. Both graphical and textual representations should be fully equivalent and have full parity. You may take inspiration from SysML v2. Both graphical and textual representations should have one overarching, concrete, unambiguous spec in reST format, similar to SysML v2 strictness
+The application should support both forms of editing
+The textual representation should be easily editable and understandble for both humans and AI
+The textual representation should be easily handled by version control tools such as Git
+The underlying semantics of the textual / graphical representation should support traceability of various development artifacts (for example - code, CAD files, electrical schematics, Docx files and anything in between)
The textual representation editor should have syntax highlighting and linting
-The project should also have a backend server / service that you can deploy. The backend server purpose is to give programmatic access to stored system models. So, there also should be a clear REST API for querying, editing and working with models. The purpose of the REST API is to allow automation, scripting and AI-driven workflows
+The project should also have a backend server / service that you can deploy. The backend server purpose is to give programmatic access to stored system models. So, there also should be a clear REST API for querying, editing and working with models. The purpose of the REST API is to allow automation, scripting and AI-driven workflows
An example could be - writing custom plugins for various domain specific software (version control software, CAD software etc...) to establish traceability between development artifacts and a system model
The desktop application should contain built-in guide / help / tutorial with clear and concise info on modeling concepts, underlying semantics, common modeling scenarios and application features / how to use the app. It should be easy to navigate and explore
@@ -655,6 +655,15 @@ Requirements:
- Strong, standardized specification.
- Strong, standardized API.
+#### Textual notation
+
+Requirements:
+
+- Should not have visual noise and clutter.
+- Should have means to reduce cognitive load when developing system models.
+- Should be easily editable by humans. And AI agents.
+- System engineers should be able to easily develop system models (both big and small) using textual notation only.
+
### UI
The focus should be on reducing system engineer's cognitive load when creating / working with a system model. Regardless of the model size (big or small).
diff --git a/harness/DECISIONS.md b/harness/DECISIONS.md
index 7859f950..fed9dca7 100644
--- a/harness/DECISIONS.md
+++ b/harness/DECISIONS.md
@@ -278,3 +278,44 @@ This is a selected design under implementation, not a native acceptance claim.
Native reverse scenario Apply selected a dependency and inserted five overlapping behavior placements into an unrelated architecture view. Core transition correctness did not make that presentation useful. Select the counterpart of the explicit request root and resolve its nearest scenario/process/chain/state owner. Without an explicit destination diagram, keep the exact core candidate and open Behavior; preserve unrelated architecture views. Retain ordinary architecture transition placement and honor explicit diagram choices.
Automatically creating another architecture view would retain the wrong editing workspace and introduce unrequested model state. Merely navigating to Behavior after adding placements would conceal the presentation damage. Both alternatives are rejected. The selected correction retains one source transaction and Undo operation and guards stale canvas events. Revisit if mixed-root requests or realistic explicit destination workflows require a separate user choice; do not infer such a choice from dependency ordering. Test/build/native acceptance is recorded separately in release-behavior-transition-context-20260906.md.
+
+## Property baselines and explicit alternative cases — 2026-09-11
+
+Problem: complete independent bindings repeat shared engineering assumptions in
+every ordinary study case. The user requires practical text-only development and
+reduced cognitive load for small and large models. Evaluator implementation
+simplicity alone cannot justify that authoring cost.
+
+Select property-owned base values/formulas and one-level case overrides as the next
+active-study contract to formalize. Every evaluator caller explicitly requests
+ModelBaseline or a particular Case identity; no active UI selection, unnamed
+default case or case-to-case inheritance determines meaning. The baseline is the
+model's explicit property values, not a mandatory extra record for entering the
+first value. Inherited formulas read the requested effective case. An explicit
+Unbound override shadows a known base; clearing an override uses the base.
+
+The [executed comparison](release-analysis-context-experiment-20260911.md) passed
+218 assertions. Twenty cases with20 fixed/two varying inputs used62 binding rows
+under B versus480 under D, with identical results. Updating one shared assumption
+required one B binding versus20 D bindings. Reusable predicates eliminated D's
+threshold-edit disadvantage, so repeated formulas are not the reason for selection.
+
+Retain an explicit complete independent freeze/export for intentionally separate
+assumptions. It must capture effective values/formulas and the full exact definition
+environment, including package pins; copying only case rows leaves hidden live
+dependencies. Captured revisions and per-input origins must make propagation
+visible. Effective dependency/cycle validation and stale result handling must cover
+the whole requested context.
+
+Reject compulsory complete cases for ordinary shared-assumption modeling; retain
+their useful isolation as an explicit independent snapshot. Reject arbitrary
+inheritance and UI-dependent evaluation. Do not import the tiny experiment
+interpreter as production architecture. Exact check identity, type/operator
+algebra, source syntax, library closure/compatibility, transitions and public
+snapshot formats still require the coordinated precise contract. Current source7
+does not implement this decision.
+
+Revisit if complete text/native tasks expose unacceptable hidden propagation,
+wrong-context edits or inability to preserve reproducible independent studies.
+The comparison establishes semantic/text maintenance evidence, not human task
+timings or full quantity/library integration acceptance.
diff --git a/harness/PLAN.md b/harness/PLAN.md
index c7515507..28f6e594 100644
--- a/harness/PLAN.md
+++ b/harness/PLAN.md
@@ -1,6 +1,6 @@
# First release execution plan
-Updated 2026-09-10. [PROJECT_GOAL.md](../PROJECT_GOAL.md) is the authoritative
+Updated 2026-09-11. [PROJECT_GOAL.md](../PROJECT_GOAL.md) is the authoritative
current objective. [STATE.md](STATE.md) records exact execution/evidence and leases;
[TASK_LEDGER.md](TASK_LEDGER.md) separates facts, assumptions and decisions.
The complete previous plan is [preserved byte-for-byte](history/2026-09-10-ledgers-before-continuation-refresh/PLAN.md).
@@ -12,6 +12,8 @@ Deliver usable, complete OA/SA engineering workflows on Windows and Linux, with
one precise reST contract and equivalent graphical, textual and REST editing.
Constraints/calculations, quantities/units and reusable libraries are explicit
requirements alongside behavior/state modeling, traceability and navigable help.
+Small and large models must be practical to author entirely in uncluttered text;
+evaluate complete authored and stabilized models, not abbreviated grammar sketches.
Later Arcadia layers remain future scope. Compilation, metadata fields and polished
screens alone do not satisfy this outcome.
@@ -25,11 +27,11 @@ broad downstream changes. Do not silently promote a design proposal to a decisio
| Milestone | Work and acceptance | Dependency/current limit |
| --- | --- | --- |
-| 1. Reliable direct authoring | Finish cold compilation and meaningful regressions; integrate compact named architecture/behavior creation and scenario workspace. Exercise actual minimum/wide native creation, allocation, editing, rejection, cancellation and history without implicit draft application or placement drift. | Root owns this host's Cargo/native lease. External host owns scenario layout/B3/local creation/palette; integrate coherent checkpoints first. No current local Rust/native Studio pass. |
-| 2. Coherent process/scenario work | Interactive ordered/explicit process editing; clicked root/nested scenario insertion; exact reuse bindings and caller return; measured canvas/zoom/scroll; visible scoped Create/Delete lifetimes. Validate model/source/identity and no unintended edit during navigation, including stale/deleted/retargeted references and recovery. | B1/B2/B3 adapters are source-integrated; B3 has a known externally owned correction. B4 is cfg(test) projection only, awaiting Scene integration. The independent oracle compiled/self-checked and its generated baseline passed; use it for native checkpoints. |
+| 1. Reliable direct authoring | Exercise actual minimum/wide native creation, allocation, editing, rejection, cancellation and history without implicit draft application or placement drift. Integrate compact named behavior creation after its coherent external gate. | Full local desktop suite passes 642/642 at80cf197; GUI build/native and strict lint remain open. Workspace/B3/palette/recovery/packages are imported. Root owns this host's Cargo/native lease. |
+| 2. Coherent process/scenario work | Interactive ordered/explicit process editing; clicked root/nested scenario insertion; exact reuse bindings and caller return; measured canvas/zoom/scroll; visible scoped Create/Delete lifetimes. Validate model/source/identity and no unintended edit during navigation, including stale/deleted/retargeted references and recovery. | B1/B2/B3 regressions pass in the full suite. B4 is cfg(test) projection only; real lifetime extents, branch-local activation clipping, incomplete lanes and self-arrow endpoints require integration. Use the independently self-checked oracle for native checkpoints. |
| 3. Precise engineering analysis | Compare owned properties, analysis contexts and existing-data/external alternatives; settle quantity kind/unit/value, numerical policy, calculation/binding and Boolean/error/unknown semantics through falsifying examples. Implement mixed-unit budgets and reusable calculation/constraint definitions with full UI/source/REST parity and exact history/recovery. | New requirement audit confirms absence. Maximum-reasoning proposal tracks precede root synthesis and independent criticism; no semantic contract is ratified yet. Strings are not evaluation. |
| 4. Maintained reusable libraries | Compare package/definition/instance alternatives. Establish pinned identity/version/dependency closure, local edits, import bindings, detach and explicit three-way updates. Demonstrate two projects, two independent instances, offline reopen, conflicting updates and stable unaffected identities. | Coordinate with milestone 3 definition references and migration. Same-project includes/reuse do not satisfy maintained cross-project libraries. No package contract implemented yet. |
-| 5. Understandable complete engineering tasks | Close teaching, OA↔SA transition/reconciliation, typed state/data/interface, artifact, saved-view and hosted workflows. Replace concrete long homogeneous selectors with contextual/searchable bounded choices; verify representative large models, keyboard access, minimum sizes and failure/recovery. Keep offline guide and exact worked examples aligned. | Existing implementations and historical evidence are a coverage starting point. Reassess against current goal instead of reviving stale absence claims or carrying old scope exclusions forward. |
+| 5. Understandable complete engineering tasks | Close teaching, OA↔SA transition/reconciliation, typed state/data/interface, artifact, saved-view and hosted workflows. Establish complete text-only creation/refactoring/modular maintenance with stable identity. Replace concrete long homogeneous selectors with contextual/searchable bounded choices; verify large models, keyboard access, minimum sizes and failure/recovery. Keep offline guide/examples aligned. | A complete warning-free text-only model probe passes source/identity/rename/reparent/file-move checks; measure usability separately. Anchored chooser is an undeclared widget experiment. Reassess current goal rather than copying historical scope exclusions. |
| 6. Release confidence and delivery | Fresh current Windows/Linux suites, strict Clippy/format, normative reST/OpenAPI/migration/examples; actual GUI/service transport, optimized builds, runtime/notices/archive/provenance/checksums and clean-environment launch. Fresh independent requirements/domain/parity/architecture/UX/runtime review; resolve material findings, then final handoff. | Integrates all preceding capabilities. Linux runtime remains open; use an approved supported environment, preserving historical failed/denied experiments. Historical previews are not final distributions. |
Milestones are capability gates, not a rigid serial schedule. Root can continue
@@ -40,16 +42,18 @@ cross-representation evaluators guide implementation boundaries.
## Immediate execution sequence
-1. Observe the existing one-job desktop build to completion; preserve failures and
- fix causes. Run focused corrected cases, then proportionate full gates and GUI.
+1. Finish the current GUI build and format/strict Clippy checks, then run native
+ engineering tasks. The current 642-test desktop suite has passed; rerun for new
+ code or unresolved failures, not as a substitute for runtime evaluation.
2. Exchange exact verified commits with the separate host, preserving its workspace
ownership. Do not merge the bundle-transfer branch or assume remote publication.
3. Use the self-checked [scenario oracle](scenario-authoring-contract.rs), follow
its [native procedure](fixtures/scenario-authoring/README.md), and compare exact
whole snapshots for Undo/Redo/reopen at minimum/wide sizes.
-4. Synthesize the [requirement delta](release-requirements-delta-20260910.md) and
- independent semantic proposals; subject materially different candidates to
- concrete numerical, identity, migration and engineering-workflow falsifiers.
+4. Resolve the paired semantic criticism with concrete text-only package normalization,
+ cosmetic revision/type compatibility, subjectless analysis closure, OA/SA mounted
+ data transitions, baseline-versus-case maintenance and closed numeric algebra
+ experiments. Select coherent contracts before production metamodel expansion.
5. Update current ledgers and durable decisions when evidence changes the plan.
Reassess unresolved assumptions before expanding downstream implementation.
diff --git a/harness/STATE.md b/harness/STATE.md
index f6192a90..a3c32f37 100644
--- a/harness/STATE.md
+++ b/harness/STATE.md
@@ -1,6 +1,6 @@
# Progress Ledger — first release
-Updated 2026-09-10, coordination through Gitea #92. This ledger describes the
+Updated 2026-09-11, coordination through Gitea #116. This ledger describes the
Codex-956atf checkout; external-host evidence is labeled separately. The entire
previous ledger is preserved [byte-for-byte](history/2026-09-10-ledgers-before-continuation-refresh/STATE.md).
Historical dates, binaries, process IDs and stopped-session instructions in that
@@ -12,12 +12,13 @@ Deliver the complete, usable OA/SA first release in [PROJECT_GOAL.md](../PROJECT
including graphical/source/REST parity, constraints/calculations, quantities/units,
reusable model libraries, traceability, offline guidance and Windows/Linux release
acceptance. Direct behavior/scenario authoring and reduced cognitive load remain
-immediate priorities. The release is incomplete.
+immediate priorities. Complete small and large models must be practical to develop
+through uncluttered text alone. The release is incomplete.
-Local branch: `codex/behavior-authoring-20260910`; source checkpoint `aa40345281e6ae8f203063840f1766ba9ce8381c`.
+Local branch: `codex/behavior-authoring-20260910`; implementation checkpoint `80cf197be6f1d400bf0b9c84bf8557f88c895aa6`.
The authoritative goal was copied byte-identically from attachment
-`2f1f0002-e43c-45ba-80a8-b359f05f99d0/goal-objective.md`; SHA256
-`7C1BDBD077E31439501C04DC3613E6BE47A389704DDC3A44EF8BD9AD98EBB513`.
+`a49d1780-25d5-4778-ad68-6140c60e4207/goal-objective.md`; SHA256
+`DD58F75C93D55FE16ED141A23D834898FB6EBC625AE7E7AE379DA552A8312435`.
See [Task Ledger](TASK_LEDGER.md) for knowledge/decisions and [Plan](PLAN.md)
for capability acceptance. Older goal paths are superseded.
@@ -26,72 +27,93 @@ for capability acceptance. Older goal paths are superseded.
| Scope | Current evidence and limits |
| --- | --- |
| Reproducible local toolchain | Ignored `.local/toolchain/env.ps1` selects Rust/Cargo 1.98.1 GNU/LLVM and verified LLVM-MinGW 20260908 (clang 23.1.1), with one Cargo job. First cold desktop test build completed in 58m03s; one unused import warning was corrected separately. |
-| B1 process authoring | `01bb52a` includes interactive empty/ordered/explicit topology and presentation-only inferred paths; first conversion preserves ordered adjacency atomically. Source review and added boundary tests exist; current local Rust/native acceptance is pending. |
-| B2 scenario authoring | Clicked root/operand/slot and optional lifeline feed Action/State/Reuse/Fragment adapters. Pointer-to-App, cancellation, stale-event, recovery and history tests are written, not yet locally executed. |
-| B3 caller navigation | Bounded caller/use frames and scoped viewport restoration are implemented. External tests exposed a real same-selected-ID/visible-owner defect, detailed below; this feature is not accepted. |
+| B1 process authoring | `01bb52a` includes interactive empty/ordered/explicit topology and presentation-only inferred paths; first conversion preserves ordered adjacency atomically. All topology regressions pass within the current full desktop suite. Native acceptance is pending. |
+| B2 scenario authoring | Clicked root/operand/slot and optional lifeline feed Action/State/Reuse/Fragment adapters. Pointer-to-App, cancellation, stale-event, recovery and history tests pass within the current full desktop suite. |
+| B3 caller navigation and workspace | External `3978bbe` imported as `865cd55` fixes visible-owner restoration, pending-draft guards and remaining-height workspace. Root `80cf197` keys the viewport Sensor by document/scenario; a real mounted regression failed before the fix and now passes. Current full desktop suite is green; native acceptance remains pending. |
| Architecture palette | External palette commit `7d1df4f` was cherry-picked as `aed40d7`, retaining local invariant tests. Captured Escape, stale Submit/raw retention and bounded card fixes are source-integrated. External native results do not transfer to this checkout. |
-| Typed stored-history F1 | `b6b76ef` fixes nullable constructor cache discrimination; `4ecad78` fixes NotApplicable seeding by checking actual enum alternatives. Thirteen focused history tests await local execution; core validation remains authoritative. |
+| Typed stored-history F1 | `b6b76ef` fixes nullable constructor cache discrimination; `4ecad78` fixes NotApplicable seeding by checking actual enum alternatives. Thirteen focused tests passed and are included in the current full suite; core validation remains authoritative. |
| Lifeline lifecycle B4 | `eb5161b` adds pure local/scope-aware lifecycle projection with ten tests, declared under `cfg(test)` only. It is not production Scene wiring or native acceptance. [Design](release-scenario-lifecycle-design-20260910.md). |
-| Native scenario oracle | [Independent checker](scenario-authoring-contract.rs) compiled with warnings denied using completed matching core/JSON rlibs; self-check, generation and whole-baseline check passed (exit 0). The checked-in fixture has 62 elements/two sources, eight OA/SA semantic checkpoints and exact identity discrimination for history. [Inputs/hashes/instructions](fixtures/scenario-authoring/README.md). Studio native use remains pending. |
+| Native scenario oracle | [Independent checker](scenario-authoring-contract.rs) compiled/self-checked and verified the generated62-element/two-source baseline. Native OA root Action and exact Save/Undo/Redo pass at minimum size; native caller identity/scroll return and source-neutral navigation pass. Remaining prescribed checkpoints are open. [Native scope](release-scenario-native-20260911.md), [oracle inputs](fixtures/scenario-authoring/README.md). |
| Normative documentation | Strict Docutils 0.22.2 language HTML regeneration passed. `semantic_schema.py --check --validate` passed 72 source7 fixtures, 204 schemas, 14 families/90 scalar leaves. These are documentation/schema gates, not a Rust build or runtime pass. |
-| Comparable-product study | Actual isolated Capella 7.1 scenario/activity/lifeline/LOOP creation was saved and XML inspected, then closed normally. [Report](release-capella-scenario-benchmark-20260910.md). No Studio native workflow has run on this fresh host. |
+| Comparable-product study | Actual isolated Capella 7.1 scenario/activity/lifeline/LOOP creation was saved and XML inspected, then closed normally. [Report](release-capella-scenario-benchmark-20260910.md). Current Studio native evidence is recorded separately. |
| New requirement audit | [Independent delta report](release-requirements-delta-20260910.md) establishes that current expressions/unit labels are descriptive and source includes are not maintained cross-project libraries. Separate foundational design proposals remain under investigation, not ratified or implemented. |
+| Packaging integration | External `b98f135` imported as `6d2b961`; all 25 checked-in exact notices match the manifest. `scripts/test-package-artifacts.py` passes 11/11 and `scripts/test-windows-notices.ps1` passes synthetic package/runtime/source/font checks. Actual optimized distributions and clean-environment launches remain open. |
## Active work, ownership and unresolved findings
Root Codex-956atf owns this host's integration, serialized Cargo and native UI.
-Session `51036` completed `cargo test --locked -p oa-desktop --bin arcadia-studio --no-run`
-with exit 0 (log `.local/logs/desktop-test-build.log`). The executable is
+The integrated command `cargo test --locked -p oa-desktop --bin arcadia-studio -- --test-threads=1`
+completed with exit 0: **642 passed, zero failed/ignored**, tests 141.25 seconds,
+warning-free build 36.31 seconds. Log `.local/logs/desktop-integrated-20260911.log`.
+Implementation inputs match `80cf197`. The executable is
`target/debug/deps/arcadia_studio-58e30cee1fd51a8b.exe`, SHA256
-`95A3B78E5A61AB5E9750C2FC077138247E538D9360720A7A8E0CBB47B882ECC1`,
-629-test inventory. Rust implementation inputs match `8547b85`; the unused palette
-import correction in `aa40345` is later than compiler consumption and requires a
-fresh build for warning-free acceptance. No Studio GUI has launched here.
+`DF122CAF7CFB63A3A9D0D2F2098944CF88F53C4ADA2C43B576B2836F6FD42BE8`.
+The GUI build passed in3m28s; executable SHA256
+`A5ED6DF334B1BFB620425AFDF3E863F0B522990E6FB47C6C401A71CF4443954A`.
+Studio is running from immutable `.local/native-qa/studio-80cf197/`. Actual caller
+identity/scroll return, source-neutral navigation and minimum-size OA root Action
+creation with exact Save/Undo/Redo comparisons pass. [Native evidence and remaining
+gates](release-scenario-native-20260911.md). Formatting passed. Strict Clippy found
+two collapsible-if cases; root corrected them. The isolated chooser is now declared
+under cfg(test). Its warning-free first compile completed;9/12 focused tests pass.
+Three failures trace to test inputs that sent whole strings as single physical key
+events; the worker is correcting realistic typing and adding paste/IME coverage.
+No production chooser integration or current passing whole-suite claim follows.
-Direct executable runs with `--test-threads=1` passed history 13/13, pure lifecycle
+Historical first-binary runs with `--test-threads=1` passed history 13/13, pure lifecycle
10/10, topology 45/45, architecture authoring 18/18 and mounted scroll 5/5. Scenario
direct tests passed 13/14 and reproduced the known caller-return assertion failure
at `scenario.rs:2473`; do not count that filter as passing. Logs are
-`.local/logs/history-fields-focused.log` and `.local/logs/focused-*.log`. The full
-suite has not run. Gitea #97's exact integration bundle was anonymously fetched,
-decoded, SHA256-verified and admitted with `git bundle verify`; source integration
-and a new build follow. Source the local toolchain before later commands and keep
+`.local/logs/history-fields-focused.log` and `.local/logs/focused-*.log`. The current
+642/642 suite supersedes that known failure after its correction; do not add filter
+counts to the full total. Gitea #97's exact integration bundle was anonymously fetched,
+decoded, SHA256-verified and admitted with `git bundle verify`; selected source and
+packaging commits are integrated. Source the local toolchain before commands and keep
one Cargo writer. The independent checker used completed rlibs without another
Cargo writer; its evidence is separately scoped above.
Codex-9e8r6t is a separate host/team with a warm cache. It owns scenario workspace
layout/B3 correction, local named behavior creation, palette and platform/packaging.
-Its latest reported mounted-layout slice passed 8/8 focused tests (#82); subsequent
-raw-draft guard, B3 owner correction and integrated seed fix await its focused gate.
-Its source/commit is not yet imported here. Earlier immutable integrated tests
-passed 615/618, exposing the seed and navigation cases; that is external evidence,
-not a current local suite result. Its creation module is prepared but not declared,
-compiled or natively accepted (#81). Packaging `b98f135` is likewise not imported.
+Its coherent workspace and packaging checkpoint is imported. New local named
+behavior creation passed its 20 focused tests (#115), with broader checks/review
+and a verified transfer still pending. Its current native basic-editing checkpoint
+passed its independent model/four-source oracle; that is external evidence, not
+this host's acceptance. It owns docs/language.rst source7/default corrections,
+examples/release1 README schema correction and local-creation README guidance (#116).
B3 diagnosis (#79): command execution may preselect an occurrence while another
scenario remains visible; `select(same_id)` then returns without restoring the
-caller owner. Back/OpenUse must establish visible owner and selection together.
-Retarget rejection also needs a correctly established pre-Back test state. Preserve
-exact source/history and mounted viewport checks. External ownership includes
-explicit guards against tool selection silently applying a pending property draft.
+caller owner. The imported correction now establishes owner and selection together.
+Root's additional Sensor regression proves same-size owner/document changes must
+remeasure Fit; see `.local/logs/scenario-sensor-before-fix.log` for the red result.
+Exact source/history and mounted viewport checks pass in the integrated suite.
-Maximum-reasoning analysis/quantity and library tracks own separate proposal
-reports only (#83/#86). Missing calculation evaluation, dimensional/unit semantics,
-maintained library reuse and concrete unbounded selectors are release gaps. No
-broad shared core/schema changes are justified until synthesis and criticism.
+Analysis/quantity and library proposals plus paired independent criticism are ready
+for synthesis. Remaining material questions: text-only instance base/map creation,
+cosmetic package updates versus quantity/type compatibility, subjectless case export
+closure, mounted data OA/SA transition policy, repeated case assumptions and closure
+of anonymous dimension operations. No production contract is ratified. Isolated
+text-only source experiments, baseline-versus-complete-case experiments and an
+undeclared anchored reference chooser widget are active; their passing claims must
+remain separate from the 642-test implementation. Root owns their integration.
+
+B4 independent Scene review (#114) identifies real integration boundaries: insertion
+sequence bounds are not lifetime extents; activation tails must not continue past
+branch-local Delete; incomplete represented=None lifelines still need geometry;
+self Create/Delete arrives at row+24. The pure projection has not yet been wired.
Both hosts report direct Git push authentication unavailable. Anonymous fetch and
authenticated Gitea MCP remain usable. Exact verified bundles are exchanged on
`codex/behavior-transfer-20260910`; do not merge that transfer branch. Existing
bundles cover `01bb52a`, `b6b76ef`, `eb5161b` and incremental `4ecad78`; exact hashes,
prerequisites and readback are preserved in the [archived ledger](history/2026-09-10-ledgers-before-continuation-refresh/STATE.md)
-and Gitea #65/#76. Latest verified transfer is `harness/transfers/behavior-aa40345.bundle.b64` on that branch (Gitea #92): 24,926 decoded bytes, prerequisite `4ecad78`, SHA256 `21B36D5016600953D95F064AE56569B6C8E7D3EA2B3B316C97222AD122FECA4F`. It includes the requirements audit and removal of an unused palette import after an actual compiler warning; the build is still incomplete. Root owns subsequent transfer and integration.
+and Gitea #65/#76. Latest verified outgoing transfer is `harness/transfers/behavior-aa40345.bundle.b64` on that branch (Gitea #92): 24,926 decoded bytes, prerequisite `4ecad78`, SHA256 `21B36D5016600953D95F064AE56569B6C8E7D3EA2B3B316C97222AD122FECA4F`. Later native oracle, proposals, ledgers and Sensor correction still need publication. Root owns subsequent transfer and integration.
## Remaining gates and highest-value next actions
-1. Finish the active build; diagnose actual failures. Run focused history,
- architecture, topology, clicked scenario, caller navigation and lifecycle tests,
- then relevant full suites, strict Clippy and current GUI build. Record exact tree.
+1. Finish the active GUI build and current format/strict Clippy gates. The full
+ desktop suite has passed; repeat only for new source changes or unresolved risk.
+ Record exact implementation/binary and run the real application.
2. Integrate the externally verified workspace/B3/local creation/palette/package
checkpoints without overwriting ownership or treating their earlier tests as new
local acceptance. Wire B4 only after coordinating actual Scene geometry.
@@ -115,7 +137,7 @@ and Gitea #65/#76. Latest verified transfer is `harness/transfers/behavior-aa403
## Reassessment
Meaningful progress continues: concrete interaction defects have been found and
-corrected, the cold toolchain is advancing, exact native evaluation is prepared,
+corrected, all 642 desktop tests pass, exact native evaluation is prepared,
and the revised semantic requirements are no longer hidden behind metadata.
Passing older suites or accumulating prose would not close the user problem.
Current highest information gain comes from actual build/native results and
diff --git a/harness/TASK_LEDGER.md b/harness/TASK_LEDGER.md
index 29aa125c..839889bc 100644
--- a/harness/TASK_LEDGER.md
+++ b/harness/TASK_LEDGER.md
@@ -1,17 +1,18 @@
# Task Ledger — first release
-Updated 2026-09-10. [STATE.md](STATE.md) owns current execution/evidence;
+Updated 2026-09-11. [STATE.md](STATE.md) owns current execution/evidence;
[PLAN.md](PLAN.md) owns capability milestones. The entire former Task Ledger is
[preserved byte-for-byte](history/2026-09-10-ledgers-before-continuation-refresh/TASK_LEDGER.md).
Its old goal references, aggregate test claims and stopped-session directives retain
-historical scope. This ledger does not add new semantic design decisions.
+historical scope. New semantic decisions are recorded explicitly in DECISIONS.md;
+prototype results retain their separate scope.
## Goal and success criteria
Deliver the usable, feature-complete OA/SA first release specified in
[PROJECT_GOAL.md](../PROJECT_GOAL.md). It is the exact copy of the user's revised
-`2f1f0002-e43c-45ba-80a8-b359f05f99d0/goal-objective.md`, SHA256
-`7C1BDBD077E31439501C04DC3613E6BE47A389704DDC3A44EF8BD9AD98EBB513`.
+`a49d1780-25d5-4778-ad68-6140c60e4207/goal-objective.md`, SHA256
+`DD58F75C93D55FE16ED141A23D834898FB6EBC625AE7E7AE379DA552A8312435`.
Success requires coherent explicit semantics, practical engineering workflows,
equivalent graphical/textual/REST behavior, real Windows/Linux runtime evidence,
@@ -31,6 +32,8 @@ cannot establish completion.
- Human/AI/Git-friendly source and a concrete unambiguous reST specification must
match graphical editing. The source editor needs highlighting/linting; help must
be offline, concise and navigable.
+- The newest explicit goal requires uncluttered notation that reduces cognitive
+ load and supports development of complete small and large models using text alone.
- Direct behavior/scenario diagram authoring and reduced cognitive load are current
priorities. Long homogeneous selectors and bloated persistent creation controls
are rejected. Study actual Capella's useful domain workflows and nesting without
@@ -50,15 +53,17 @@ cannot establish completion.
are evaluator evidence, not native Studio acceptance. Exact inputs/hashes are in
[the fixture instructions](fixtures/scenario-authoring/README.md).
-- Current local source is `aa40345` on `codex/behavior-authoring-20260910`, following
+- Current local implementation is `80cf197` on `codex/behavior-authoring-20260910`, following
baseline `1797516`. Local commits cover B1/B2/B3 authoring, nullable history cache
(`b6b76ef`), external palette integration (`aed40d7`), cfg(test) lifecycle projection
- (`eb5161b`), history seed correction (`4ecad78`) and exact-identity native oracle. Latest `aa40345` commits the requirements audit
- and an unused-import correction prompted by the active compiler.
+ (`eb5161b`), history seed correction (`4ecad78`) and exact-identity native oracle.
+ Later imports add focus recovery, exact packaging notices and scenario workspace/
+ owner restoration. Root80cf197 fixes document/scenario viewport Sensor identity
+ with a real mounted red-before/green-after regression.
- This fresh host has task-local Rust/Cargo 1.98.1 GNU/LLVM and verified LLVM-MinGW.
- The one-job desktop test no-run build is active, with no local Rust build/test or
- Studio native pass yet. The history suite has thirteen tests; lifecycle has ten,
- both awaiting local execution. [Progress and evidence](STATE.md).
+ The integrated full desktop suite passes642/642, zero failed/ignored, with a
+ warning-free build. History and pure lifecycle tests are included. Current GUI
+ build/native and strict lint remain open. [Exact hashes/logs](STATE.md).
- Strict language HTML regeneration and the source7 OpenAPI checker passed locally:
72 fixtures, 204 schemas and 90 scalar leaves. Actual isolated Capella scenario,
activity/lifeline and LOOP creation was saved/inspected; this is reference study,
@@ -75,13 +80,14 @@ cannot establish completion.
- The same audit identifies concrete unbounded Constraint target choices and generic
expanded collection editors. Explorer already has hierarchy/search/paging; the
problem is targeted, not a claim that all selection is flat.
-- Gitea #79 distinguishes a B3 retarget evaluator setup error from the real
- same-selected-ID/visible-owner navigation defect. The external scenario owner
- corrects owner restoration and pending-draft guards. External layout 8/8 passes
- precede subsequent edits and do not establish current local/native acceptance.
+- Gitea #79's same-selected-ID/visible-owner defect is fixed in the imported
+ scenario checkpoint; owner restoration and pending-draft guards pass current
+ tests. B4 independent review #114 adds Scene integration requirements for actual
+ lifetime extents, branch-local activation clipping, incomplete lanes and self arrows.
- Codex-9e8r6t is a separate host with independent Cargo/native leases. It owns
scenario workspace/B3/local behavior creation, palette and platform/packaging;
- current integration is not yet imported. Both hosts report direct push auth
+ coherent workspace/package integration is imported, while newer named behavior
+ creation and docs changes await transfer. Both hosts report direct push auth
unavailable. MCP/anonymous fetch and verified bundle exchange remain usable.
- Current Linux acceptance is open; this host has no installed WSL distribution.
Historical Linux core/server/cross-build results have exact older snapshot scope.
@@ -105,6 +111,13 @@ cannot establish completion.
identities, local engineering edits and offline reproducibility across projects?
- What migration and OA↔SA treatment follows from those contracts without guessing
executable meaning from old strings or silently changing old models?
+- How can text-only creation establish library instance bases/maps, preserve
+ compatibility across cosmetic package revisions, export subjectless cases, and
+ transition mounted OA/SA data without hidden GUI normalization or silent forks?
+- Do explicit baseline values with one-level case overrides or complete per-case
+ bindings better support repeated study maintenance and reproducible evaluation?
+- Which source-preserving refactoring/navigation and grammar changes reduce actual
+ engineering effort while retaining stable identity and cross-file ownership?
- Do current real gestures, exact caller return, nested scopes and lifecycle markers
remain usable and correct under minimum sizing, scrolling, zoom and dirty drafts?
- Which uncovered teaching, traceability, transition, saved-view, state/data, hosted
@@ -127,6 +140,17 @@ versus analysis contexts; calculation definitions/applications; numeric/unit rul
and copy, linked-definition, live-instance or pinned-definition/editable-instance
library architectures. Require concrete falsifiers, source/API migration effects,
identity/history/recovery behavior and independent critique before convergence.
+The completed analysis/library proposals and paired review remain unratified.
+An isolated executable text-only fixture admits without warnings and passes exact
+source/identity/rename/reparent/file-move/default checks. Separate baseline-versus-
+complete-case and anchored chooser experiments are active, with no production
+metamodel or selector integration implied.
+
+The subsequent context experiment passed218 assertions: shared property baselines
+and one-level overrides reduce repeated input maintenance while retaining explicit
+independent freeze. Root selected that B direction for precise contract refinement;
+quantity/library/source details remain open. [Decision](DECISIONS.md),
+[experiment and limits](release-analysis-context-experiment-20260911.md).
## Decisions retained
@@ -160,7 +184,7 @@ identity/history/recovery behavior and independent critique before convergence.
## Current strategy
-Finish the active local build and exact regression/native loop while separate
+Finish the GUI/native loop after the green full desktop suite while separate
agents investigate uncertain foundations. Integrate only coherent, verified
external checkpoints respecting ownership. Use independently constructed complete
model/source oracles and exact snapshot comparison for identity/history. Ratify
diff --git a/harness/experiments/analysis-context-20260911/README.md b/harness/experiments/analysis-context-20260911/README.md
new file mode 100644
index 00000000..611314ba
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/README.md
@@ -0,0 +1,29 @@
+# Analysis context experiment
+
+This standalone Rust program compares explicit baseline/override studies (B) with complete independent case bindings (D). It is an isolated experiment, **not Studio syntax or a production model implementation**. Its expression language is deliberately limited to integer magnitudes in milliseconds, addition/sum, comparison, missing inputs and pure reusable functions.
+
+The source owns the fixture generator, parser, explicit context resolver, dependency-cycle check, evaluator, frozen-snapshot operation, provenance observations and line-diff counter. All comparisons use the same 20 fixed inputs, 2 varying inputs and 20 named cases. The formula/check variants are either inline or factored into reusable pure definitions.
+
+From the repository root, using the installed local toolchain:
+
+```powershell
+. .local/toolchain/env.ps1
+New-Item -ItemType Directory -Force .local/analysis-context-20260911 | Out-Null
+rustc --edition=2024 -D warnings -C linker=clang.exe harness/experiments/analysis-context-20260911/main.rs -o .local/analysis-context-20260911/analysis-context.exe
+if ($LASTEXITCODE -ne 0) { throw 'Experiment compilation failed' }
+& .local/analysis-context-20260911/analysis-context.exe harness/experiments/analysis-context-20260911/generated
+if ($LASTEXITCODE -ne 0) { throw 'Experiment assertions failed' }
+```
+
+The program overwrites its named generated fixtures/results in the output directory. It does not invoke Cargo, modify `target`, or import Studio modules. `EVIDENCE.json` was recorded separately after the verified run and contains exact compiler, program/executable/result hashes and every generated source fixture hash. Rerunning after editing the program requires refreshing that evidence rather than trusting the old manifest.
+
+- [Results](generated/RESULTS.md): executed counts and assertions.
+- [Evidence](generated/EVIDENCE.json): final run identity and 29 fixture hashes.
+- [B with factored calculations](generated/B-factored.study) and [D equivalent](generated/D-factored.study).
+- [B case provenance](generated/B-factored-provenance.md).
+- [Frozen B case](generated/B-factored-frozen-case20.study): complete independent snapshot, copied definitions and explicit captured row origins.
+- [Interpretation and limitations](../../release-analysis-context-experiment-20260911.md).
+
+The small dialect has explicit `study B/D revision`, a shared property catalog, optional pure `function` declarations, an explicit named `baseline` for B, and named cases declaring either `base engineering` or `independent`. `missing` is an entire binding hole. Removing a B override resolves the baseline; removing a required D row rejects. Function parameters cannot capture project properties. No case-to-case inheritance or implicit selected/default case exists.
+
+Generated studies do not have OA/SA subjects, UUID identity, quantity kinds, persistent source editing, JSON/REST, library import resolution or native UI integration. The experiment compares context semantics and the actual emitted text size/edit count, not human completion time or end-to-end product conformance.
diff --git a/harness/experiments/analysis-context-20260911/generated/B-factored-cleared-override.study b/harness/experiments/analysis-context-20260911/generated/B-factored-cleared-override.study
new file mode 100644
index 00000000..a6c5da98
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/B-factored-cleared-override.study
@@ -0,0 +1,107 @@
+study B revision 1
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+function budget_formula(result) = le(result, 22000)
+function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+baseline engineering revision 1
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case01 base engineering
+end
+case case02 base engineering
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 base engineering
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 base engineering
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 base engineering
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 base engineering
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 base engineering
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 base engineering
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 base engineering
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 base engineering
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 base engineering
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 base engineering
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 base engineering
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 base engineering
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 base engineering
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 base engineering
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 base engineering
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 base engineering
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 base engineering
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 base engineering
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/B-factored-cycle.rejected-study b/harness/experiments/analysis-context-20260911/generated/B-factored-cycle.rejected-study
new file mode 100644
index 00000000..f590771d
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/B-factored-cycle.rejected-study
@@ -0,0 +1,108 @@
+study B revision 1
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+function budget_formula(result) = le(result, 22000)
+function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+baseline engineering revision 1
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case01 base engineering
+end
+case case02 base engineering
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 base engineering
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 base engineering
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 base engineering
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 base engineering
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 base engineering
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 base engineering
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 base engineering
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 base engineering
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 base engineering
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 base engineering
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 base engineering
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 base engineering
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 base engineering
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 base engineering
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 base engineering
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 base engineering
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 base engineering
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 base engineering
+ varying01 = total
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/B-factored-fixed-all.study b/harness/experiments/analysis-context-20260911/generated/B-factored-fixed-all.study
new file mode 100644
index 00000000..31b17393
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/B-factored-fixed-all.study
@@ -0,0 +1,108 @@
+study B revision 2
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+function budget_formula(result) = le(result, 22000)
+function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+baseline engineering revision 2
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case01 base engineering
+end
+case case02 base engineering
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 base engineering
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 base engineering
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 base engineering
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 base engineering
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 base engineering
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 base engineering
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 base engineering
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 base engineering
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 base engineering
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 base engineering
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 base engineering
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 base engineering
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 base engineering
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 base engineering
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 base engineering
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 base engineering
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 base engineering
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 base engineering
+ varying01 = 1700
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/B-factored-frozen-case20.study b/harness/experiments/analysis-context-20260911/generated/B-factored-frozen-case20.study
new file mode 100644
index 00000000..aac5be0c
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/B-factored-frozen-case20.study
@@ -0,0 +1,55 @@
+study D revision 1
+origin B:case20:document1:engineering@1
+captured budget = baseline:engineering@1 via case20
+captured fixed01 = baseline:engineering@1 via case20
+captured fixed02 = baseline:engineering@1 via case20
+captured fixed03 = baseline:engineering@1 via case20
+captured fixed04 = baseline:engineering@1 via case20
+captured fixed05 = baseline:engineering@1 via case20
+captured fixed06 = baseline:engineering@1 via case20
+captured fixed07 = baseline:engineering@1 via case20
+captured fixed08 = baseline:engineering@1 via case20
+captured fixed09 = baseline:engineering@1 via case20
+captured fixed10 = baseline:engineering@1 via case20
+captured fixed11 = baseline:engineering@1 via case20
+captured fixed12 = baseline:engineering@1 via case20
+captured fixed13 = baseline:engineering@1 via case20
+captured fixed14 = baseline:engineering@1 via case20
+captured fixed15 = baseline:engineering@1 via case20
+captured fixed16 = baseline:engineering@1 via case20
+captured fixed17 = baseline:engineering@1 via case20
+captured fixed18 = baseline:engineering@1 via case20
+captured fixed19 = baseline:engineering@1 via case20
+captured fixed20 = baseline:engineering@1 via case20
+captured total = baseline:engineering@1 via case20
+captured varying01 = case:case20
+captured varying02 = case:case20
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+function budget_formula(result) = le(result, 22000)
+function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+case case20 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1700
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/B-factored-missing.study b/harness/experiments/analysis-context-20260911/generated/B-factored-missing.study
new file mode 100644
index 00000000..f7588979
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/B-factored-missing.study
@@ -0,0 +1,108 @@
+study B revision 1
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+function budget_formula(result) = le(result, 22000)
+function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+baseline engineering revision 1
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case01 base engineering
+end
+case case02 base engineering
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 base engineering
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 base engineering
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 base engineering
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 base engineering
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 base engineering
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 base engineering
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 base engineering
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 base engineering
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 base engineering
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 base engineering
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 base engineering
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 base engineering
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 base engineering
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 base engineering
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 base engineering
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 base engineering
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 base engineering
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 base engineering
+ varying01 = missing
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/B-factored-provenance.md b/harness/experiments/analysis-context-20260911/generated/B-factored-provenance.md
new file mode 100644
index 00000000..b452d2fc
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/B-factored-provenance.md
@@ -0,0 +1,31 @@
+# B-factored: case20 evaluation
+
+Value: Bool(false)
+Document revision: 1
+
+- budget: baseline:engineering@1 via case20
+- fixed01: baseline:engineering@1 via case20
+- fixed02: baseline:engineering@1 via case20
+- fixed03: baseline:engineering@1 via case20
+- fixed04: baseline:engineering@1 via case20
+- fixed05: baseline:engineering@1 via case20
+- fixed06: baseline:engineering@1 via case20
+- fixed07: baseline:engineering@1 via case20
+- fixed08: baseline:engineering@1 via case20
+- fixed09: baseline:engineering@1 via case20
+- fixed10: baseline:engineering@1 via case20
+- fixed11: baseline:engineering@1 via case20
+- fixed12: baseline:engineering@1 via case20
+- fixed13: baseline:engineering@1 via case20
+- fixed14: baseline:engineering@1 via case20
+- fixed15: baseline:engineering@1 via case20
+- fixed16: baseline:engineering@1 via case20
+- fixed17: baseline:engineering@1 via case20
+- fixed18: baseline:engineering@1 via case20
+- fixed19: baseline:engineering@1 via case20
+- fixed20: baseline:engineering@1 via case20
+- total: baseline:engineering@1 via case20
+- varying01: case:case20
+- varying02: case:case20
+
+Rejected cycle: case20: cycle: budget -> total -> varying01 -> total
diff --git a/harness/experiments/analysis-context-20260911/generated/B-factored-threshold.study b/harness/experiments/analysis-context-20260911/generated/B-factored-threshold.study
new file mode 100644
index 00000000..28d73ee7
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/B-factored-threshold.study
@@ -0,0 +1,108 @@
+study B revision 2
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+function budget_formula(result) = le(result, 22500)
+function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+baseline engineering revision 1
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case01 base engineering
+end
+case case02 base engineering
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 base engineering
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 base engineering
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 base engineering
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 base engineering
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 base engineering
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 base engineering
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 base engineering
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 base engineering
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 base engineering
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 base engineering
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 base engineering
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 base engineering
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 base engineering
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 base engineering
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 base engineering
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 base engineering
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 base engineering
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 base engineering
+ varying01 = 1700
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/B-factored.study b/harness/experiments/analysis-context-20260911/generated/B-factored.study
new file mode 100644
index 00000000..018ab22b
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/B-factored.study
@@ -0,0 +1,108 @@
+study B revision 1
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+function budget_formula(result) = le(result, 22000)
+function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+baseline engineering revision 1
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case01 base engineering
+end
+case case02 base engineering
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 base engineering
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 base engineering
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 base engineering
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 base engineering
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 base engineering
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 base engineering
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 base engineering
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 base engineering
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 base engineering
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 base engineering
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 base engineering
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 base engineering
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 base engineering
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 base engineering
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 base engineering
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 base engineering
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 base engineering
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 base engineering
+ varying01 = 1700
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/B-inline-cleared-override.study b/harness/experiments/analysis-context-20260911/generated/B-inline-cleared-override.study
new file mode 100644
index 00000000..3ef62e7d
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/B-inline-cleared-override.study
@@ -0,0 +1,105 @@
+study B revision 1
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+baseline engineering revision 1
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case01 base engineering
+end
+case case02 base engineering
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 base engineering
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 base engineering
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 base engineering
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 base engineering
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 base engineering
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 base engineering
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 base engineering
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 base engineering
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 base engineering
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 base engineering
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 base engineering
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 base engineering
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 base engineering
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 base engineering
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 base engineering
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 base engineering
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 base engineering
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 base engineering
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/B-inline-cycle.rejected-study b/harness/experiments/analysis-context-20260911/generated/B-inline-cycle.rejected-study
new file mode 100644
index 00000000..2f04435f
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/B-inline-cycle.rejected-study
@@ -0,0 +1,106 @@
+study B revision 1
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+baseline engineering revision 1
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case01 base engineering
+end
+case case02 base engineering
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 base engineering
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 base engineering
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 base engineering
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 base engineering
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 base engineering
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 base engineering
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 base engineering
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 base engineering
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 base engineering
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 base engineering
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 base engineering
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 base engineering
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 base engineering
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 base engineering
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 base engineering
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 base engineering
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 base engineering
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 base engineering
+ varying01 = total
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/B-inline-fixed-all.study b/harness/experiments/analysis-context-20260911/generated/B-inline-fixed-all.study
new file mode 100644
index 00000000..e7775036
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/B-inline-fixed-all.study
@@ -0,0 +1,106 @@
+study B revision 2
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+baseline engineering revision 2
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case01 base engineering
+end
+case case02 base engineering
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 base engineering
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 base engineering
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 base engineering
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 base engineering
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 base engineering
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 base engineering
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 base engineering
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 base engineering
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 base engineering
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 base engineering
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 base engineering
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 base engineering
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 base engineering
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 base engineering
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 base engineering
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 base engineering
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 base engineering
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 base engineering
+ varying01 = 1700
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/B-inline-frozen-case20.study b/harness/experiments/analysis-context-20260911/generated/B-inline-frozen-case20.study
new file mode 100644
index 00000000..84b6591f
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/B-inline-frozen-case20.study
@@ -0,0 +1,53 @@
+study D revision 1
+origin B:case20:document1:engineering@1
+captured budget = baseline:engineering@1 via case20
+captured fixed01 = baseline:engineering@1 via case20
+captured fixed02 = baseline:engineering@1 via case20
+captured fixed03 = baseline:engineering@1 via case20
+captured fixed04 = baseline:engineering@1 via case20
+captured fixed05 = baseline:engineering@1 via case20
+captured fixed06 = baseline:engineering@1 via case20
+captured fixed07 = baseline:engineering@1 via case20
+captured fixed08 = baseline:engineering@1 via case20
+captured fixed09 = baseline:engineering@1 via case20
+captured fixed10 = baseline:engineering@1 via case20
+captured fixed11 = baseline:engineering@1 via case20
+captured fixed12 = baseline:engineering@1 via case20
+captured fixed13 = baseline:engineering@1 via case20
+captured fixed14 = baseline:engineering@1 via case20
+captured fixed15 = baseline:engineering@1 via case20
+captured fixed16 = baseline:engineering@1 via case20
+captured fixed17 = baseline:engineering@1 via case20
+captured fixed18 = baseline:engineering@1 via case20
+captured fixed19 = baseline:engineering@1 via case20
+captured fixed20 = baseline:engineering@1 via case20
+captured total = baseline:engineering@1 via case20
+captured varying01 = case:case20
+captured varying02 = case:case20
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+case case20 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1700
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/B-inline-missing.study b/harness/experiments/analysis-context-20260911/generated/B-inline-missing.study
new file mode 100644
index 00000000..94ef6255
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/B-inline-missing.study
@@ -0,0 +1,106 @@
+study B revision 1
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+baseline engineering revision 1
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case01 base engineering
+end
+case case02 base engineering
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 base engineering
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 base engineering
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 base engineering
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 base engineering
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 base engineering
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 base engineering
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 base engineering
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 base engineering
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 base engineering
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 base engineering
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 base engineering
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 base engineering
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 base engineering
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 base engineering
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 base engineering
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 base engineering
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 base engineering
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 base engineering
+ varying01 = missing
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/B-inline-provenance.md b/harness/experiments/analysis-context-20260911/generated/B-inline-provenance.md
new file mode 100644
index 00000000..c6400afc
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/B-inline-provenance.md
@@ -0,0 +1,31 @@
+# B-inline: case20 evaluation
+
+Value: Bool(false)
+Document revision: 1
+
+- budget: baseline:engineering@1 via case20
+- fixed01: baseline:engineering@1 via case20
+- fixed02: baseline:engineering@1 via case20
+- fixed03: baseline:engineering@1 via case20
+- fixed04: baseline:engineering@1 via case20
+- fixed05: baseline:engineering@1 via case20
+- fixed06: baseline:engineering@1 via case20
+- fixed07: baseline:engineering@1 via case20
+- fixed08: baseline:engineering@1 via case20
+- fixed09: baseline:engineering@1 via case20
+- fixed10: baseline:engineering@1 via case20
+- fixed11: baseline:engineering@1 via case20
+- fixed12: baseline:engineering@1 via case20
+- fixed13: baseline:engineering@1 via case20
+- fixed14: baseline:engineering@1 via case20
+- fixed15: baseline:engineering@1 via case20
+- fixed16: baseline:engineering@1 via case20
+- fixed17: baseline:engineering@1 via case20
+- fixed18: baseline:engineering@1 via case20
+- fixed19: baseline:engineering@1 via case20
+- fixed20: baseline:engineering@1 via case20
+- total: baseline:engineering@1 via case20
+- varying01: case:case20
+- varying02: case:case20
+
+Rejected cycle: case20: cycle: budget -> total -> varying01 -> total
diff --git a/harness/experiments/analysis-context-20260911/generated/B-inline-threshold.study b/harness/experiments/analysis-context-20260911/generated/B-inline-threshold.study
new file mode 100644
index 00000000..88b06f42
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/B-inline-threshold.study
@@ -0,0 +1,106 @@
+study B revision 2
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+baseline engineering revision 2
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case01 base engineering
+end
+case case02 base engineering
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 base engineering
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 base engineering
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 base engineering
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 base engineering
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 base engineering
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 base engineering
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 base engineering
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 base engineering
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 base engineering
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 base engineering
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 base engineering
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 base engineering
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 base engineering
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 base engineering
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 base engineering
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 base engineering
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 base engineering
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 base engineering
+ varying01 = 1700
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/B-inline.study b/harness/experiments/analysis-context-20260911/generated/B-inline.study
new file mode 100644
index 00000000..2e37facb
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/B-inline.study
@@ -0,0 +1,106 @@
+study B revision 1
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+baseline engineering revision 1
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case01 base engineering
+end
+case case02 base engineering
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 base engineering
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 base engineering
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 base engineering
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 base engineering
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 base engineering
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 base engineering
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 base engineering
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 base engineering
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 base engineering
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 base engineering
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 base engineering
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 base engineering
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 base engineering
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 base engineering
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 base engineering
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 base engineering
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 base engineering
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 base engineering
+ varying01 = 1700
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/D-factored-cycle.rejected-study b/harness/experiments/analysis-context-20260911/generated/D-factored-cycle.rejected-study
new file mode 100644
index 00000000..284cd5f0
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/D-factored-cycle.rejected-study
@@ -0,0 +1,524 @@
+study D revision 1
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+function budget_formula(result) = le(result, 22000)
+function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+case case01 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case02 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = total
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/D-factored-deleted-row.rejected-study b/harness/experiments/analysis-context-20260911/generated/D-factored-deleted-row.rejected-study
new file mode 100644
index 00000000..970a7c9e
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/D-factored-deleted-row.rejected-study
@@ -0,0 +1,523 @@
+study D revision 1
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+function budget_formula(result) = le(result, 22000)
+function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+case case01 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case02 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/D-factored-fixed-all.study b/harness/experiments/analysis-context-20260911/generated/D-factored-fixed-all.study
new file mode 100644
index 00000000..f9fa4001
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/D-factored-fixed-all.study
@@ -0,0 +1,524 @@
+study D revision 2
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+function budget_formula(result) = le(result, 22000)
+function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+case case01 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case02 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 independent
+ budget = budget_formula(total)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1700
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/D-factored-frozen-case20.study b/harness/experiments/analysis-context-20260911/generated/D-factored-frozen-case20.study
new file mode 100644
index 00000000..a932652a
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/D-factored-frozen-case20.study
@@ -0,0 +1,55 @@
+study D revision 1
+origin D:case20:document1
+captured budget = case:case20
+captured fixed01 = case:case20
+captured fixed02 = case:case20
+captured fixed03 = case:case20
+captured fixed04 = case:case20
+captured fixed05 = case:case20
+captured fixed06 = case:case20
+captured fixed07 = case:case20
+captured fixed08 = case:case20
+captured fixed09 = case:case20
+captured fixed10 = case:case20
+captured fixed11 = case:case20
+captured fixed12 = case:case20
+captured fixed13 = case:case20
+captured fixed14 = case:case20
+captured fixed15 = case:case20
+captured fixed16 = case:case20
+captured fixed17 = case:case20
+captured fixed18 = case:case20
+captured fixed19 = case:case20
+captured fixed20 = case:case20
+captured total = case:case20
+captured varying01 = case:case20
+captured varying02 = case:case20
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+function budget_formula(result) = le(result, 22000)
+function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+case case20 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1700
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/D-factored-missing.study b/harness/experiments/analysis-context-20260911/generated/D-factored-missing.study
new file mode 100644
index 00000000..89c04c39
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/D-factored-missing.study
@@ -0,0 +1,524 @@
+study D revision 1
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+function budget_formula(result) = le(result, 22000)
+function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+case case01 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case02 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = missing
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/D-factored-provenance.md b/harness/experiments/analysis-context-20260911/generated/D-factored-provenance.md
new file mode 100644
index 00000000..a960a5e8
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/D-factored-provenance.md
@@ -0,0 +1,31 @@
+# D-factored: case20 evaluation
+
+Value: Bool(false)
+Document revision: 1
+
+- budget: case:case20
+- fixed01: case:case20
+- fixed02: case:case20
+- fixed03: case:case20
+- fixed04: case:case20
+- fixed05: case:case20
+- fixed06: case:case20
+- fixed07: case:case20
+- fixed08: case:case20
+- fixed09: case:case20
+- fixed10: case:case20
+- fixed11: case:case20
+- fixed12: case:case20
+- fixed13: case:case20
+- fixed14: case:case20
+- fixed15: case:case20
+- fixed16: case:case20
+- fixed17: case:case20
+- fixed18: case:case20
+- fixed19: case:case20
+- fixed20: case:case20
+- total: case:case20
+- varying01: case:case20
+- varying02: case:case20
+
+Rejected cycle: case20: cycle: budget -> total -> varying01 -> total
diff --git a/harness/experiments/analysis-context-20260911/generated/D-factored-threshold.study b/harness/experiments/analysis-context-20260911/generated/D-factored-threshold.study
new file mode 100644
index 00000000..2bb0d8da
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/D-factored-threshold.study
@@ -0,0 +1,524 @@
+study D revision 2
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+function budget_formula(result) = le(result, 22500)
+function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+case case01 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case02 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1700
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/D-factored.study b/harness/experiments/analysis-context-20260911/generated/D-factored.study
new file mode 100644
index 00000000..a6e0a487
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/D-factored.study
@@ -0,0 +1,524 @@
+study D revision 1
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+function budget_formula(result) = le(result, 22000)
+function total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02) = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+case case01 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case02 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 independent
+ budget = budget_formula(total)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = total_formula(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1700
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/D-inline-cycle.rejected-study b/harness/experiments/analysis-context-20260911/generated/D-inline-cycle.rejected-study
new file mode 100644
index 00000000..b8323a9a
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/D-inline-cycle.rejected-study
@@ -0,0 +1,522 @@
+study D revision 1
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+case case01 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case02 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = total
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/D-inline-deleted-row.rejected-study b/harness/experiments/analysis-context-20260911/generated/D-inline-deleted-row.rejected-study
new file mode 100644
index 00000000..458e9e6f
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/D-inline-deleted-row.rejected-study
@@ -0,0 +1,521 @@
+study D revision 1
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+case case01 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case02 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/D-inline-fixed-all.study b/harness/experiments/analysis-context-20260911/generated/D-inline-fixed-all.study
new file mode 100644
index 00000000..f9bdd47e
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/D-inline-fixed-all.study
@@ -0,0 +1,522 @@
+study D revision 2
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+case case01 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case02 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1700
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/D-inline-fixed-one-only.study b/harness/experiments/analysis-context-20260911/generated/D-inline-fixed-one-only.study
new file mode 100644
index 00000000..61116469
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/D-inline-fixed-one-only.study
@@ -0,0 +1,522 @@
+study D revision 2
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+case case01 independent
+ budget = le(total, 22000)
+ fixed01 = 1200
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case02 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1700
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/D-inline-frozen-case20.study b/harness/experiments/analysis-context-20260911/generated/D-inline-frozen-case20.study
new file mode 100644
index 00000000..34f73f58
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/D-inline-frozen-case20.study
@@ -0,0 +1,53 @@
+study D revision 1
+origin D:case20:document1
+captured budget = case:case20
+captured fixed01 = case:case20
+captured fixed02 = case:case20
+captured fixed03 = case:case20
+captured fixed04 = case:case20
+captured fixed05 = case:case20
+captured fixed06 = case:case20
+captured fixed07 = case:case20
+captured fixed08 = case:case20
+captured fixed09 = case:case20
+captured fixed10 = case:case20
+captured fixed11 = case:case20
+captured fixed12 = case:case20
+captured fixed13 = case:case20
+captured fixed14 = case:case20
+captured fixed15 = case:case20
+captured fixed16 = case:case20
+captured fixed17 = case:case20
+captured fixed18 = case:case20
+captured fixed19 = case:case20
+captured fixed20 = case:case20
+captured total = case:case20
+captured varying01 = case:case20
+captured varying02 = case:case20
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+case case20 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1700
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/D-inline-missing.study b/harness/experiments/analysis-context-20260911/generated/D-inline-missing.study
new file mode 100644
index 00000000..6675f405
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/D-inline-missing.study
@@ -0,0 +1,522 @@
+study D revision 1
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+case case01 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case02 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = missing
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/D-inline-provenance.md b/harness/experiments/analysis-context-20260911/generated/D-inline-provenance.md
new file mode 100644
index 00000000..db18226f
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/D-inline-provenance.md
@@ -0,0 +1,31 @@
+# D-inline: case20 evaluation
+
+Value: Bool(false)
+Document revision: 1
+
+- budget: case:case20
+- fixed01: case:case20
+- fixed02: case:case20
+- fixed03: case:case20
+- fixed04: case:case20
+- fixed05: case:case20
+- fixed06: case:case20
+- fixed07: case:case20
+- fixed08: case:case20
+- fixed09: case:case20
+- fixed10: case:case20
+- fixed11: case:case20
+- fixed12: case:case20
+- fixed13: case:case20
+- fixed14: case:case20
+- fixed15: case:case20
+- fixed16: case:case20
+- fixed17: case:case20
+- fixed18: case:case20
+- fixed19: case:case20
+- fixed20: case:case20
+- total: case:case20
+- varying01: case:case20
+- varying02: case:case20
+
+Rejected cycle: case20: cycle: budget -> total -> varying01 -> total
diff --git a/harness/experiments/analysis-context-20260911/generated/D-inline-threshold.study b/harness/experiments/analysis-context-20260911/generated/D-inline-threshold.study
new file mode 100644
index 00000000..11c65cb6
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/D-inline-threshold.study
@@ -0,0 +1,522 @@
+study D revision 2
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+case case01 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case02 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 independent
+ budget = le(total, 22500)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1700
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/D-inline.study b/harness/experiments/analysis-context-20260911/generated/D-inline.study
new file mode 100644
index 00000000..e6741394
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/D-inline.study
@@ -0,0 +1,522 @@
+study D revision 1
+properties fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02, total, budget
+case case01 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 750
+ varying02 = 1000
+end
+case case02 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 800
+ varying02 = 1025
+end
+case case03 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 850
+ varying02 = 1050
+end
+case case04 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 900
+ varying02 = 1075
+end
+case case05 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 950
+ varying02 = 1100
+end
+case case06 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1000
+ varying02 = 1125
+end
+case case07 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1050
+ varying02 = 1150
+end
+case case08 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1100
+ varying02 = 1175
+end
+case case09 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1150
+ varying02 = 1200
+end
+case case10 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1200
+ varying02 = 1225
+end
+case case11 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1250
+ varying02 = 1250
+end
+case case12 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1300
+ varying02 = 1275
+end
+case case13 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1350
+ varying02 = 1300
+end
+case case14 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1400
+ varying02 = 1325
+end
+case case15 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1450
+ varying02 = 1350
+end
+case case16 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1500
+ varying02 = 1375
+end
+case case17 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1550
+ varying02 = 1400
+end
+case case18 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1600
+ varying02 = 1425
+end
+case case19 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1650
+ varying02 = 1450
+end
+case case20 independent
+ budget = le(total, 22000)
+ fixed01 = 1000
+ fixed02 = 1000
+ fixed03 = 1000
+ fixed04 = 1000
+ fixed05 = 1000
+ fixed06 = 1000
+ fixed07 = 1000
+ fixed08 = 1000
+ fixed09 = 1000
+ fixed10 = 1000
+ fixed11 = 1000
+ fixed12 = 1000
+ fixed13 = 1000
+ fixed14 = 1000
+ fixed15 = 1000
+ fixed16 = 1000
+ fixed17 = 1000
+ fixed18 = 1000
+ fixed19 = 1000
+ fixed20 = 1000
+ total = sum(fixed01, fixed02, fixed03, fixed04, fixed05, fixed06, fixed07, fixed08, fixed09, fixed10, fixed11, fixed12, fixed13, fixed14, fixed15, fixed16, fixed17, fixed18, fixed19, fixed20, varying01, varying02)
+ varying01 = 1700
+ varying02 = 1475
+end
diff --git a/harness/experiments/analysis-context-20260911/generated/EVIDENCE.json b/harness/experiments/analysis-context-20260911/generated/EVIDENCE.json
new file mode 100644
index 00000000..350a4c0e
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/EVIDENCE.json
@@ -0,0 +1,166 @@
+{
+ "recorded_utc": "2026-09-11T08:50:38.6429749Z",
+ "compiler": [
+ "rustc 1.98.1 (48a229cea 2026-09-01)",
+ "binary: rustc",
+ "commit-hash: 48a229ceaefd4985c50990b14116b6d856af0985",
+ "commit-date: 2026-09-01",
+ "host: x86_64-pc-windows-gnullvm",
+ "release: 1.98.1",
+ "LLVM version: 22.1.8"
+ ],
+ "compile_command": "rustc --edition=2024 -D warnings -C linker=clang.exe harness/experiments/analysis-context-20260911/main.rs -o .local/analysis-context-20260911/analysis-context.exe",
+ "run_command": ".local/analysis-context-20260911/analysis-context.exe harness/experiments/analysis-context-20260911/generated",
+ "process_exit_code": 0,
+ "assertions_passed": 218,
+ "program_sha256": "5632ACAFD06413DCC9961DDC1ADE60C7556FB56B43ED3C667F5106E7559A3733",
+ "executable_sha256": "36E2D5A7F0683CC19F2EB50CA4840F3A793A8350498E3E17EDD75710A78FC675",
+ "results_sha256": "248419751E9B836CDE6D80F97202843DD5DC085D1BEC67CB2FCC5046960C143B",
+ "fixture_hashes": [
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/B-factored-cleared-override.study",
+ "sha256": "DD740295FB0F849A63815AE3174EB067616687A5D46BD63089A6AA279F9D48B8",
+ "bytes": 2762
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/B-factored-cycle.rejected-study",
+ "sha256": "4FF294ACA6EDBAD60CB6C899D2B1D93B8E07337A295DBC63876A4497DC9D58EA",
+ "bytes": 2782
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/B-factored-fixed-all.study",
+ "sha256": "A51BD1F3EF7153D67FB63EAB2B9906E9FFB0049A2179B47C632ABB3E9B01A0F4",
+ "bytes": 2781
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/B-factored-frozen-case20.study",
+ "sha256": "05370CCB21E0B0AB8F26F9704FBECA1BE44FA1156FBF6C8420E09C436D016476",
+ "bytes": 2665
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/B-factored-missing.study",
+ "sha256": "D6D1B53B307836275A417C3CB094881F91755D1FAD0D521CE9B90FE1FD8F039D",
+ "bytes": 2784
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/B-factored-threshold.study",
+ "sha256": "86C5DC5FCA452A9CD7FC13318D72BE81973802C13F878D488561551D27F62C67",
+ "bytes": 2781
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/B-factored.study",
+ "sha256": "0C2B9F055E4E6993A14688A0F58BD6629F724687DC694D7213166197B197B878",
+ "bytes": 2781
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/B-inline-cleared-override.study",
+ "sha256": "8FC77924459D892D1E91CF1756C708ED34005ED840F3A38B98474037BBEC4FA1",
+ "bytes": 2262
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/B-inline-cycle.rejected-study",
+ "sha256": "3D8249B3DDB21F36C765AF440F6DE0E9E7B125E6504A07BDD3BA83E7B94CE9E1",
+ "bytes": 2282
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/B-inline-fixed-all.study",
+ "sha256": "CD32AE6CDE2E21AFBB536109A9D2911ECC3572A9BE368C2D877EE3EB8F2D2303",
+ "bytes": 2281
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/B-inline-frozen-case20.study",
+ "sha256": "B09AF2449DD8E2DAEB5F0B4AE0FF9AC914227B6FE619B4B0139FEF0778B7FB7A",
+ "bytes": 2165
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/B-inline-missing.study",
+ "sha256": "0886DE0C602CCD06A7F7AC5C248C7EE9C5670C567880DCD3ED6D37E14C5AA524",
+ "bytes": 2284
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/B-inline-threshold.study",
+ "sha256": "54F518BCBB166264F763BC14AFA1437D4E95A3244DFE9DF0B0331591E72C85DD",
+ "bytes": 2281
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/B-inline.study",
+ "sha256": "87A97378550BB79DA9A0CAD453700352D9E24C289E6865BA9D8CF554C62FEE36",
+ "bytes": 2281
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/D-factored-cycle.rejected-study",
+ "sha256": "DAC04A19992313549D322D70DF564EF9026FBD5F30402DDA591AF579B7F70E26",
+ "bytes": 14027
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/D-factored-deleted-row.rejected-study",
+ "sha256": "C00EF4080857E18ADCC309FE57D512CA681E28A46E259B44A34D3852A334E973",
+ "bytes": 14007
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/D-factored-fixed-all.study",
+ "sha256": "D746830E424702ACE35B5A26EFE8D2E96D4D2CC028DC2B7E207E6326D0F22597",
+ "bytes": 14026
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/D-factored-frozen-case20.study",
+ "sha256": "804F0D3DB792659BFAA1CFB0CF96BF243C379F6971EC1FE357FE98CDF3269ED6",
+ "bytes": 2167
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/D-factored-missing.study",
+ "sha256": "32351A6F8BA499AAAD644FABFB08F7D6A067365394B2B6AD33EE7581A9761889",
+ "bytes": 14029
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/D-factored-threshold.study",
+ "sha256": "20EC6C0EB3EB599BE6354F2BED4BB0EF07747B4EE0B9CB80B757C59633A4579C",
+ "bytes": 14026
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/D-factored.study",
+ "sha256": "17C9CEB39E9B39D867EF732D4DE8BA36C00546BE1BC987547A52B99EEA83BE99",
+ "bytes": 14026
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/D-inline-cycle.rejected-study",
+ "sha256": "2C7443440051B7DD31733B7814C86FFC53BE5F7EBDFFE56925B9F9E60693FD84",
+ "bytes": 13242
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/D-inline-deleted-row.rejected-study",
+ "sha256": "26970B159D67A5E858298EC77C6B7FFA233EC3EF0FB903E9E860795DE829F28C",
+ "bytes": 13222
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/D-inline-fixed-all.study",
+ "sha256": "39AEC6A7457B4A8D9D9518B4B29A8185048811329B7614794FF69E122A972D6C",
+ "bytes": 13241
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/D-inline-fixed-one-only.study",
+ "sha256": "83E9F73E7E8CE4AC300979CE1D120707C4B7DD4A1D076C8D9BAB4BC8CF8230E8",
+ "bytes": 13241
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/D-inline-frozen-case20.study",
+ "sha256": "88A1A971651E827399775A7D9F6AAD32B7CE1C3E0F78D57E7AC72F207495E2B6",
+ "bytes": 1667
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/D-inline-missing.study",
+ "sha256": "8F44E9524A1BFCC96102C2C91CE15C4A87E698509225FD84D9A860EF8F7A23E6",
+ "bytes": 13244
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/D-inline-threshold.study",
+ "sha256": "59C77A7DF1488E96B994FE04A32A505B8DFC5F3F92C45A8BD774A043C1C2F5A0",
+ "bytes": 13241
+ },
+ {
+ "path": "harness/experiments/analysis-context-20260911/generated/D-inline.study",
+ "sha256": "7A0FF1A4CCAD6B41344331182820AB6C6C7990DA09B842F8D04C60008A8D5232",
+ "bytes": 13241
+ }
+ ]
+}
diff --git a/harness/experiments/analysis-context-20260911/generated/RESULTS.md b/harness/experiments/analysis-context-20260911/generated/RESULTS.md
new file mode 100644
index 00000000..783c8abe
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/generated/RESULTS.md
@@ -0,0 +1,14 @@
+# Executed context experiment
+
+All input magnitudes are integer milliseconds. This tiny dialect is not Studio source.
+
+| Variant | Initial lines | Initial bytes | Binding rows | Fixed-all edit removed/added lines | Threshold edit removed/added lines | Initial passing cases | After fixed-all change |
+| --- | ---: | ---: | ---: | --- | --- | ---: | ---: |
+| B-inline | 106 | 2281 | 62 | 3/3 | 3/3 | 4 | 1 |
+| B-factored | 108 | 2781 | 62 | 3/3 | 2/2 | 4 | 1 |
+| D-inline | 522 | 13241 | 480 | 21/21 | 21/21 | 4 | 1 |
+| D-factored | 524 | 14026 | 480 | 21/21 | 2/2 | 4 | 1 |
+
+Executed assertions: 218. All passed. Every accepted fixture was parsed, validated, rendered and parsed back with exact experiment AST equality. Rejected cases were checked separately.
+
+Negative controls: D changing one repeated fixed input changes only that case; B evaluating inherited formulas against baseline inputs instead of effective case inputs gives 21,750 instead of case20's 23,175. A shallow B case copy is not frozen; factored D also needs its definition environment snapshotted. Clearing a B override restores its named baseline value, including an explicitly missing baseline. A deleted D binding row rejects. Baseline chains, recursive definitions and project captures inside pure functions reject.
diff --git a/harness/experiments/analysis-context-20260911/main.rs b/harness/experiments/analysis-context-20260911/main.rs
new file mode 100644
index 00000000..0fb2a8a1
--- /dev/null
+++ b/harness/experiments/analysis-context-20260911/main.rs
@@ -0,0 +1,913 @@
+//! Isolated context experiment. This is deliberately NOT Studio syntax or production code.
+use std::collections::{BTreeMap, BTreeSet};
+use std::fmt::Write as _;
+use std::path::Path;
+
+type Bindings = BTreeMap;
+type Functions = BTreeMap;
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+enum Expr {
+ Int(i64),
+ Missing,
+ Ref(String),
+ Sum(Vec),
+ Le(Box, Box),
+ Call(String, Vec),
+}
+#[derive(Clone, Debug, PartialEq, Eq)]
+struct Function {
+ parameters: Vec,
+ body: Expr,
+}
+#[derive(Clone, Debug, PartialEq, Eq)]
+struct Baseline {
+ name: String,
+ revision: u64,
+ bindings: Bindings,
+}
+#[derive(Clone, Debug, PartialEq, Eq)]
+struct Case {
+ name: String,
+ base: Option,
+ bindings: Bindings,
+}
+#[derive(Clone, Debug, PartialEq, Eq)]
+struct Study {
+ mode: char,
+ revision: u64,
+ properties: Vec,
+ functions: Functions,
+ baseline: Option,
+ cases: Vec,
+ origin: Option,
+ captured_origins: BTreeMap,
+}
+#[derive(Clone, Debug, PartialEq, Eq)]
+enum Value {
+ Int(i64),
+ Bool(bool),
+ Missing(BTreeSet),
+}
+#[derive(Clone, Debug, PartialEq, Eq)]
+struct Observation {
+ value: Value,
+ origins: BTreeMap,
+ document_revision: u64,
+}
+
+fn split_args(s: &str) -> Result, String> {
+ if s.trim().is_empty() {
+ return Ok(vec![]);
+ }
+ let mut depth = 0_i32;
+ let mut start = 0;
+ let mut out = vec![];
+ for (i, ch) in s.char_indices() {
+ match ch {
+ '(' => depth += 1,
+ ')' => {
+ depth -= 1;
+ if depth < 0 {
+ return Err("unbalanced expression".into());
+ }
+ }
+ ',' if depth == 0 => {
+ out.push(s[start..i].trim());
+ start = i + 1;
+ }
+ _ => {}
+ }
+ }
+ if depth != 0 {
+ return Err("unbalanced expression".into());
+ }
+ out.push(s[start..].trim());
+ Ok(out)
+}
+fn expr(s: &str) -> Result {
+ let s = s.trim();
+ if s == "missing" {
+ return Ok(Expr::Missing);
+ }
+ if let Ok(n) = s.parse() {
+ return Ok(Expr::Int(n));
+ }
+ if let Some(at) = s.find('(') {
+ if !s.ends_with(')') {
+ return Err("missing closing parenthesis".into());
+ }
+ let args = split_args(&s[at + 1..s.len() - 1])?
+ .into_iter()
+ .map(expr)
+ .collect::, _>>()?;
+ return match &s[..at] {
+ "sum" => Ok(Expr::Sum(args)),
+ "le" if args.len() == 2 => Ok(Expr::Le(
+ Box::new(args[0].clone()),
+ Box::new(args[1].clone()),
+ )),
+ "le" => Err("le requires two arguments".into()),
+ name => Ok(Expr::Call(name.into(), args)),
+ };
+ }
+ if s.is_empty() || !s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
+ return Err(format!("invalid symbol {s}"));
+ }
+ Ok(Expr::Ref(s.into()))
+}
+fn spelling(e: &Expr) -> String {
+ let list = |xs: &[Expr]| xs.iter().map(spelling).collect::>().join(", ");
+ match e {
+ Expr::Int(v) => v.to_string(),
+ Expr::Missing => "missing".into(),
+ Expr::Ref(s) => s.clone(),
+ Expr::Sum(xs) => format!("sum({})", list(xs)),
+ Expr::Le(a, b) => format!("le({}, {})", spelling(a), spelling(b)),
+ Expr::Call(name, xs) => format!("{name}({})", list(xs)),
+ }
+}
+fn parse(text: &str) -> Result {
+ let mut lines = text.lines().filter(|l| !l.trim().is_empty());
+ let head = lines
+ .next()
+ .ok_or("empty study")?
+ .split_whitespace()
+ .collect::>();
+ if head.len() != 4
+ || head[0] != "study"
+ || head[2] != "revision"
+ || !matches!(head[1], "B" | "D")
+ {
+ return Err("invalid study header".into());
+ }
+ let mut study = Study {
+ mode: head[1].chars().next().unwrap(),
+ revision: head[3].parse().map_err(|_| "bad revision")?,
+ properties: vec![],
+ functions: BTreeMap::new(),
+ baseline: None,
+ cases: vec![],
+ origin: None,
+ captured_origins: BTreeMap::new(),
+ };
+ let mut current: Option<(bool, Case, u64)> = None;
+ for line in lines {
+ let line = line.trim();
+ if line == "end" {
+ let (base, item, revision) = current.take().ok_or("unmatched end")?;
+ if base {
+ if study.baseline.is_some() {
+ return Err("multiple baselines forbidden".into());
+ }
+ study.baseline = Some(Baseline {
+ name: item.name,
+ revision,
+ bindings: item.bindings,
+ });
+ } else {
+ if study.cases.iter().any(|c| c.name == item.name) {
+ return Err("duplicate case".into());
+ }
+ study.cases.push(item);
+ }
+ } else if let Some((_, case, _)) = current.as_mut() {
+ let (name, value) = line.split_once(" = ").ok_or("expected binding")?;
+ if case.bindings.insert(name.into(), expr(value)?).is_some() {
+ return Err("duplicate binding".into());
+ }
+ } else if let Some(properties) = line.strip_prefix("properties ") {
+ if !study.properties.is_empty() {
+ return Err("duplicate properties".into());
+ }
+ study.properties = properties.split(", ").map(String::from).collect();
+ } else if let Some(origin) = line.strip_prefix("origin ") {
+ study.origin = Some(origin.into());
+ } else if let Some(captured) = line.strip_prefix("captured ") {
+ let (property, origin) = captured
+ .split_once(" = ")
+ .ok_or("invalid captured origin")?;
+ if study
+ .captured_origins
+ .insert(property.into(), origin.into())
+ .is_some()
+ {
+ return Err("duplicate captured origin".into());
+ }
+ } else if let Some(def) = line.strip_prefix("function ") {
+ let (left, right) = def.split_once(" = ").ok_or("invalid function")?;
+ let at = left.find('(').ok_or("function parameters required")?;
+ if !left.ends_with(')') {
+ return Err("bad parameters".into());
+ }
+ let f = Function {
+ parameters: split_args(&left[at + 1..left.len() - 1])?
+ .into_iter()
+ .map(String::from)
+ .collect(),
+ body: expr(right)?,
+ };
+ if study.functions.insert(left[..at].into(), f).is_some() {
+ return Err("duplicate function".into());
+ }
+ } else {
+ let words = line.split_whitespace().collect::>();
+ let (base, name, parent, revision) = match words.as_slice() {
+ ["baseline", name, "revision", revision] => (
+ true,
+ *name,
+ None,
+ revision.parse().map_err(|_| "bad baseline revision")?,
+ ),
+ ["case", name, "base", parent] => (false, *name, Some((*parent).to_owned()), 0),
+ ["case", name, "independent"] => (false, *name, None, 0),
+ _ => return Err(format!("invalid declaration: {line}")),
+ };
+ current = Some((
+ base,
+ Case {
+ name: name.into(),
+ base: parent,
+ bindings: BTreeMap::new(),
+ },
+ revision,
+ ));
+ }
+ }
+ if current.is_some() {
+ return Err("unterminated block".into());
+ }
+ validate(&study)?;
+ Ok(study)
+}
+fn render(s: &Study) -> String {
+ let mut text = format!("study {} revision {}\n", s.mode, s.revision);
+ if let Some(origin) = &s.origin {
+ writeln!(text, "origin {origin}").unwrap();
+ }
+ for (property, origin) in &s.captured_origins {
+ writeln!(text, "captured {property} = {origin}").unwrap();
+ }
+ writeln!(text, "properties {}", s.properties.join(", ")).unwrap();
+ for (name, f) in &s.functions {
+ writeln!(
+ text,
+ "function {name}({}) = {}",
+ f.parameters.join(", "),
+ spelling(&f.body)
+ )
+ .unwrap();
+ }
+ let write_bindings = |text: &mut String, bindings: &Bindings| {
+ for (p, e) in bindings {
+ writeln!(text, " {p} = {}", spelling(e)).unwrap();
+ }
+ text.push_str("end\n");
+ };
+ if let Some(b) = &s.baseline {
+ writeln!(text, "baseline {} revision {}", b.name, b.revision).unwrap();
+ write_bindings(&mut text, &b.bindings);
+ }
+ for c in &s.cases {
+ writeln!(
+ text,
+ "case {} {}",
+ c.name,
+ c.base
+ .as_ref()
+ .map_or("independent".into(), |b| format!("base {b}"))
+ )
+ .unwrap();
+ write_bindings(&mut text, &c.bindings);
+ }
+ text
+}
+fn refs(e: &Expr, names: &mut BTreeSet, calls: &mut BTreeSet) {
+ match e {
+ Expr::Ref(p) => {
+ names.insert(p.clone());
+ }
+ Expr::Sum(xs) | Expr::Call(_, xs) => {
+ if let Expr::Call(f, _) = e {
+ calls.insert(f.clone());
+ }
+ for x in xs {
+ refs(x, names, calls);
+ }
+ }
+ Expr::Le(a, b) => {
+ refs(a, names, calls);
+ refs(b, names, calls);
+ }
+ _ => {}
+ }
+}
+fn acyclic(graph: &BTreeMap>) -> Result<(), String> {
+ fn visit(
+ n: &str,
+ graph: &BTreeMap>,
+ path: &mut Vec,
+ done: &mut BTreeSet,
+ ) -> Result<(), String> {
+ if path.iter().any(|p| p == n) {
+ path.push(n.into());
+ return Err(format!("cycle: {}", path.join(" -> ")));
+ }
+ if done.contains(n) {
+ return Ok(());
+ }
+ path.push(n.into());
+ for next in graph
+ .get(n)
+ .ok_or_else(|| format!("unknown dependency {n}"))?
+ {
+ visit(next, graph, path, done)?;
+ }
+ path.pop();
+ done.insert(n.into());
+ Ok(())
+ }
+ let mut done = BTreeSet::new();
+ for n in graph.keys() {
+ visit(n, graph, &mut vec![], &mut done)?;
+ }
+ Ok(())
+}
+fn resolved<'a>(s: &'a Study, c: &'a Case, property: &str) -> Result<(&'a Expr, String), String> {
+ if let Some(e) = c.bindings.get(property) {
+ return Ok((e, format!("case:{}", c.name)));
+ }
+ if let Some(base) = &c.base {
+ let b = s
+ .baseline
+ .as_ref()
+ .filter(|b| &b.name == base)
+ .ok_or("unknown baseline")?;
+ if let Some(e) = b.bindings.get(property) {
+ return Ok((
+ e,
+ format!("baseline:{}@{} via {}", b.name, b.revision, c.name),
+ ));
+ }
+ }
+ Err(format!("missing binding row: {}.{property}", c.name))
+}
+fn validate(s: &Study) -> Result<(), String> {
+ let properties = s.properties.iter().cloned().collect::>();
+ if properties.len() != s.properties.len() || properties.is_empty() {
+ return Err("invalid property catalog".into());
+ }
+ if (s.mode == 'B') != s.baseline.is_some() {
+ return Err("B requires one explicit baseline; D forbids baselines".into());
+ }
+ let mut function_graph = BTreeMap::new();
+ for (name, f) in &s.functions {
+ let mut names = BTreeSet::new();
+ let mut calls = BTreeSet::new();
+ refs(&f.body, &mut names, &mut calls);
+ let parameters = f.parameters.iter().cloned().collect::>();
+ if parameters.len() != f.parameters.len() || !names.is_subset(¶meters) {
+ return Err(format!("function {name} captures unknown/project property"));
+ }
+ function_graph.insert(name.clone(), calls);
+ }
+ acyclic(&function_graph)?;
+ let mut contexts = s.cases.clone();
+ if let Some(b) = &s.baseline {
+ contexts.push(Case {
+ name: format!("baseline_{}", b.name),
+ base: None,
+ bindings: b.bindings.clone(),
+ });
+ }
+ for c in &contexts {
+ if !c.bindings.keys().all(|p| properties.contains(p)) {
+ return Err("unknown binding property".into());
+ }
+ if s.mode == 'D' && c.base.is_some() {
+ return Err("D case cannot inherit".into());
+ }
+ if let Some(name) = &c.base {
+ if s.baseline.as_ref().is_none_or(|b| &b.name != name) {
+ return Err("unknown baseline; case-to-case inheritance forbidden".into());
+ }
+ }
+ let mut graph = BTreeMap::new();
+ for p in &properties {
+ let (e, _) = resolved(s, c, p)?;
+ let mut names = BTreeSet::new();
+ let mut calls = BTreeSet::new();
+ refs(e, &mut names, &mut calls);
+ if !names.is_subset(&properties) || !calls.iter().all(|f| s.functions.contains_key(f)) {
+ return Err("unknown reference".into());
+ }
+ graph.insert(p.clone(), names);
+ }
+ acyclic(&graph).map_err(|e| format!("{}: {e}", c.name))?;
+ for p in &properties {
+ evaluate(s, c, p)?;
+ }
+ }
+ Ok(())
+}
+fn evaluate(s: &Study, c: &Case, property: &str) -> Result {
+ let mut origins = BTreeMap::new();
+ let mut cache = BTreeMap::new();
+ fn property_value(
+ s: &Study,
+ c: &Case,
+ p: &str,
+ origins: &mut BTreeMap,
+ cache: &mut BTreeMap,
+ ) -> Result {
+ if let Some(v) = cache.get(p) {
+ return Ok(v.clone());
+ }
+ let (e, origin) = resolved(s, c, p)?;
+ origins.insert(
+ p.into(),
+ s.captured_origins
+ .get(p)
+ .map_or(origin, |old| format!("frozen:{old}")),
+ );
+ let value = run(s, c, e, &BTreeMap::new(), Some(p), origins, cache)?;
+ if !matches!(value, Value::Missing(_)) && (p == "budget") != matches!(value, Value::Bool(_))
+ {
+ return Err(format!("wrong type for {p}"));
+ }
+ cache.insert(p.into(), value.clone());
+ Ok(value)
+ }
+ fn run(
+ s: &Study,
+ c: &Case,
+ e: &Expr,
+ params: &BTreeMap,
+ missing_property: Option<&str>,
+ origins: &mut BTreeMap,
+ cache: &mut BTreeMap,
+ ) -> Result {
+ match e {
+ Expr::Int(n) => Ok(Value::Int(*n)),
+ Expr::Missing => Ok(Value::Missing(BTreeSet::from([missing_property
+ .unwrap_or("function-body")
+ .into()]))),
+ Expr::Ref(p) => params
+ .get(p)
+ .cloned()
+ .map_or_else(|| property_value(s, c, p, origins, cache), Ok),
+ Expr::Call(name, args) => {
+ let f = s.functions.get(name).ok_or("unknown function")?;
+ if f.parameters.len() != args.len() {
+ return Err("wrong function arity".into());
+ }
+ let mut bound = BTreeMap::new();
+ for (parameter, arg) in f.parameters.iter().zip(args) {
+ bound.insert(
+ parameter.clone(),
+ run(s, c, arg, params, missing_property, origins, cache)?,
+ );
+ }
+ run(s, c, &f.body, &bound, missing_property, origins, cache)
+ }
+ Expr::Sum(xs) => {
+ let mut total = 0_i64;
+ let mut missing = BTreeSet::new();
+ for x in xs {
+ match run(s, c, x, params, missing_property, origins, cache)? {
+ Value::Int(n) => total = total.checked_add(n).ok_or("integer overflow")?,
+ Value::Missing(m) => missing.extend(m),
+ Value::Bool(_) => return Err("sum requires numeric inputs".into()),
+ }
+ }
+ Ok(if missing.is_empty() {
+ Value::Int(total)
+ } else {
+ Value::Missing(missing)
+ })
+ }
+ Expr::Le(a, b) => {
+ let a = run(s, c, a, params, missing_property, origins, cache)?;
+ let b = run(s, c, b, params, missing_property, origins, cache)?;
+ match (a, b) {
+ (Value::Int(a), Value::Int(b)) => Ok(Value::Bool(a <= b)),
+ (Value::Missing(mut a), Value::Missing(b)) => {
+ a.extend(b);
+ Ok(Value::Missing(a))
+ }
+ (Value::Missing(a), Value::Int(_)) | (Value::Int(_), Value::Missing(a)) => {
+ Ok(Value::Missing(a))
+ }
+ _ => Err("le requires numeric inputs".into()),
+ }
+ }
+ }
+ }
+ let value = property_value(s, c, property, &mut origins, &mut cache)?;
+ Ok(Observation {
+ value,
+ origins,
+ document_revision: s.revision,
+ })
+}
+fn fixture(mode: char, factored: bool) -> Study {
+ let inputs = (1..=20)
+ .map(|i| format!("fixed{i:02}"))
+ .chain(["varying01".into(), "varying02".into()])
+ .collect::>();
+ let mut all = inputs.clone();
+ all.extend(["total".into(), "budget".into()]);
+ let mut bindings = (1..=20)
+ .map(|i| (format!("fixed{i:02}"), Expr::Int(1000)))
+ .collect::();
+ bindings.insert("varying01".into(), Expr::Int(750));
+ bindings.insert("varying02".into(), Expr::Int(1000));
+ let total = Expr::Sum(inputs.iter().cloned().map(Expr::Ref).collect());
+ let check = Expr::Le(
+ Box::new(Expr::Ref("total".into())),
+ Box::new(Expr::Int(22000)),
+ );
+ let mut functions = BTreeMap::new();
+ if factored {
+ functions.insert(
+ "total_formula".into(),
+ Function {
+ parameters: inputs.clone(),
+ body: total,
+ },
+ );
+ functions.insert(
+ "budget_formula".into(),
+ Function {
+ parameters: vec!["result".into()],
+ body: Expr::Le(
+ Box::new(Expr::Ref("result".into())),
+ Box::new(Expr::Int(22000)),
+ ),
+ },
+ );
+ bindings.insert(
+ "total".into(),
+ Expr::Call(
+ "total_formula".into(),
+ inputs.into_iter().map(Expr::Ref).collect(),
+ ),
+ );
+ bindings.insert(
+ "budget".into(),
+ Expr::Call("budget_formula".into(), vec![Expr::Ref("total".into())]),
+ );
+ } else {
+ bindings.insert("total".into(), total);
+ bindings.insert("budget".into(), check);
+ }
+ let cases = (0..20)
+ .map(|i| {
+ let mut rows = if mode == 'D' {
+ bindings.clone()
+ } else {
+ BTreeMap::new()
+ };
+ if mode == 'D' || i != 0 {
+ rows.insert("varying01".into(), Expr::Int(750 + 50 * i));
+ rows.insert("varying02".into(), Expr::Int(1000 + 25 * i));
+ }
+ Case {
+ name: format!("case{:02}", i + 1),
+ base: (mode == 'B').then(|| "engineering".into()),
+ bindings: rows,
+ }
+ })
+ .collect();
+ Study {
+ mode,
+ revision: 1,
+ properties: all,
+ functions,
+ baseline: (mode == 'B').then(|| Baseline {
+ name: "engineering".into(),
+ revision: 1,
+ bindings,
+ }),
+ cases,
+ origin: None,
+ captured_origins: BTreeMap::new(),
+ }
+}
+fn freeze(s: &Study, case: usize) -> Study {
+ let c = &s.cases[case];
+ Study {
+ mode: 'D',
+ revision: 1,
+ properties: s.properties.clone(),
+ functions: s.functions.clone(),
+ baseline: None,
+ cases: vec![Case {
+ name: c.name.clone(),
+ base: None,
+ bindings: s
+ .properties
+ .iter()
+ .map(|p| (p.clone(), resolved(s, c, p).unwrap().0.clone()))
+ .collect(),
+ }],
+ origin: Some(format!(
+ "{}:{}:document{}{}",
+ s.mode,
+ c.name,
+ s.revision,
+ s.baseline
+ .as_ref()
+ .map_or(String::new(), |b| format!(":{}@{}", b.name, b.revision))
+ )),
+ captured_origins: s
+ .properties
+ .iter()
+ .map(|p| (p.clone(), resolved(s, c, p).unwrap().1))
+ .collect(),
+ }
+}
+fn write_fixture(dir: &Path, name: &str, s: &Study) -> Result {
+ let source = render(s);
+ let back = parse(&source)?;
+ assert_eq!(&back, s, "exact experiment AST round trip");
+ std::fs::write(dir.join(format!("{name}.study")), &source).map_err(|e| e.to_string())?;
+ Ok(source)
+}
+fn diff(before: &str, after: &str) -> (usize, usize) {
+ let a = before.lines().collect::>();
+ let b = after.lines().collect::>();
+ let mut row = vec![0; b.len() + 1];
+ for x in &a {
+ let mut diagonal = 0;
+ for (j, y) in b.iter().enumerate() {
+ let old = row[j + 1];
+ row[j + 1] = if x == y {
+ diagonal + 1
+ } else {
+ row[j + 1].max(row[j])
+ };
+ diagonal = old;
+ }
+ }
+ (a.len() - row[b.len()], b.len() - row[b.len()])
+}
+fn assert_total(s: &Study, index: usize, expected: i64) {
+ assert_eq!(
+ evaluate(s, &s.cases[index], "total").unwrap().value,
+ Value::Int(expected)
+ );
+}
+fn change_fixed(s: &mut Study, all: bool) {
+ s.revision += 1;
+ if let Some(b) = s.baseline.as_mut() {
+ b.revision += 1;
+ b.bindings.insert("fixed01".into(), Expr::Int(1200));
+ } else {
+ for c in s.cases.iter_mut().take(if all { 20 } else { 1 }) {
+ c.bindings.insert("fixed01".into(), Expr::Int(1200));
+ }
+ }
+}
+fn change_threshold(s: &mut Study, value: i64) {
+ s.revision += 1;
+ if let Some(f) = s.functions.get_mut("budget_formula") {
+ f.body = Expr::Le(
+ Box::new(Expr::Ref("result".into())),
+ Box::new(Expr::Int(value)),
+ );
+ } else {
+ let e = Expr::Le(
+ Box::new(Expr::Ref("total".into())),
+ Box::new(Expr::Int(value)),
+ );
+ if let Some(b) = s.baseline.as_mut() {
+ b.revision += 1;
+ b.bindings.insert("budget".into(), e);
+ } else {
+ for c in &mut s.cases {
+ c.bindings.insert("budget".into(), e.clone());
+ }
+ }
+ }
+}
+fn pass_count(s: &Study) -> usize {
+ s.cases
+ .iter()
+ .filter(|c| evaluate(s, c, "budget").unwrap().value == Value::Bool(true))
+ .count()
+}
+
+fn main() -> Result<(), String> {
+ let out = std::env::args()
+ .nth(1)
+ .ok_or("usage: analysis-context.exe OUTPUT_DIRECTORY")?;
+ let dir = Path::new(&out);
+ std::fs::create_dir_all(dir).map_err(|e| e.to_string())?;
+ let mut report = String::from(
+ "# Executed context experiment\n\nAll input magnitudes are integer milliseconds. This tiny dialect is not Studio source.\n\n| Variant | Initial lines | Initial bytes | Binding rows | Fixed-all edit removed/added lines | Threshold edit removed/added lines | Initial passing cases | After fixed-all change |\n| --- | ---: | ---: | ---: | --- | --- | ---: | ---: |\n",
+ );
+ let mut checks = 0;
+ for mode in ['B', 'D'] {
+ for factored in [false, true] {
+ let name = format!("{mode}-{}", if factored { "factored" } else { "inline" });
+ let original = fixture(mode, factored);
+ validate(&original)?;
+ for i in 0..20 {
+ assert_total(&original, i, 21750 + 75 * i as i64);
+ checks += 1;
+ }
+ assert_eq!(pass_count(&original), 4);
+ checks += 1;
+ let initial = write_fixture(dir, &name, &original)?;
+ let mut changed = original.clone();
+ change_fixed(&mut changed, true);
+ validate(&changed)?;
+ for i in 0..20 {
+ assert_total(&changed, i, 21950 + 75 * i as i64);
+ checks += 1;
+ }
+ assert_eq!(pass_count(&changed), 1);
+ checks += 1;
+ let fixed_source = write_fixture(dir, &format!("{name}-fixed-all"), &changed)?;
+ let mut threshold = original.clone();
+ change_threshold(&mut threshold, 22500);
+ validate(&threshold)?;
+ assert_eq!(pass_count(&threshold), 11);
+ checks += 1;
+ let threshold_source = write_fixture(dir, &format!("{name}-threshold"), &threshold)?;
+ let frozen = freeze(&original, 19);
+ let frozen_before = evaluate(&frozen, &frozen.cases[0], "budget")?;
+ assert_total(&frozen, 0, 23175);
+ checks += 1;
+ change_threshold(&mut changed, 25000);
+ assert_eq!(
+ evaluate(&frozen, &frozen.cases[0], "budget")?,
+ frozen_before
+ );
+ checks += 1;
+ write_fixture(dir, &format!("{name}-frozen-case20"), &frozen)?;
+ // A shallow case copy still follows its baseline and/or shared function environment.
+ let shallow = original.cases[19].clone();
+ if mode == 'B' {
+ assert_eq!(
+ evaluate(&changed, &shallow, "total")?.value,
+ Value::Int(23375)
+ );
+ checks += 1;
+ }
+ if factored {
+ assert_eq!(
+ evaluate(&changed, &shallow, "budget")?.value,
+ Value::Bool(true)
+ );
+ checks += 1;
+ }
+ if mode == 'D' && !factored {
+ assert_eq!(
+ evaluate(&changed, &shallow, "total")?.value,
+ Value::Int(23175)
+ );
+ assert_eq!(
+ evaluate(&changed, &shallow, "budget")?.value,
+ Value::Bool(false)
+ );
+ checks += 2;
+ }
+ assert_eq!(frozen.captured_origins.len(), 24);
+ assert!(
+ evaluate(&frozen, &frozen.cases[0], "total")?
+ .origins
+ .values()
+ .all(|origin| origin.starts_with("frozen:"))
+ );
+ checks += 2;
+ let mut missing = original.clone();
+ missing.cases[19]
+ .bindings
+ .insert("varying01".into(), Expr::Missing);
+ validate(&missing)?;
+ assert_eq!(
+ evaluate(&missing, &missing.cases[19], "budget")?.value,
+ Value::Missing(BTreeSet::from(["varying01".into()]))
+ );
+ checks += 1;
+ write_fixture(dir, &format!("{name}-missing"), &missing)?;
+ let frozen_missing = freeze(&missing, 19);
+ assert_eq!(
+ evaluate(&frozen_missing, &frozen_missing.cases[0], "budget")?.value,
+ Value::Missing(BTreeSet::from(["varying01".into()]))
+ );
+ checks += 1;
+ missing.cases[19].bindings.remove("varying01");
+ if mode == 'B' {
+ validate(&missing)?;
+ assert_total(&missing, 19, 22225);
+ checks += 1;
+ write_fixture(dir, &format!("{name}-cleared-override"), &missing)?;
+ } else {
+ assert!(
+ validate(&missing)
+ .unwrap_err()
+ .contains("missing binding row")
+ );
+ checks += 1;
+ std::fs::write(
+ dir.join(format!("{name}-deleted-row.rejected-study")),
+ render(&missing),
+ )
+ .map_err(|e| e.to_string())?;
+ }
+ let mut cyclic = original.clone();
+ cyclic.cases[19]
+ .bindings
+ .insert("varying01".into(), Expr::Ref("total".into()));
+ let cycle = validate(&cyclic).unwrap_err();
+ assert!(cycle.contains("cycle"));
+ checks += 1;
+ std::fs::write(
+ dir.join(format!("{name}-cycle.rejected-study")),
+ render(&cyclic),
+ )
+ .map_err(|e| e.to_string())?;
+ let observation = evaluate(&original, &original.cases[19], "budget")?;
+ let mut detail = format!(
+ "# {name}: case20 evaluation\n\nValue: {:?}\nDocument revision: {}\n\n",
+ observation.value, observation.document_revision
+ );
+ for (p, origin) in &observation.origins {
+ writeln!(detail, "- {p}: {origin}").unwrap();
+ }
+ writeln!(detail, "\nRejected cycle: {cycle}").unwrap();
+ std::fs::write(dir.join(format!("{name}-provenance.md")), detail)
+ .map_err(|e| e.to_string())?;
+ let rows = original.baseline.as_ref().map_or(0, |b| b.bindings.len())
+ + original
+ .cases
+ .iter()
+ .map(|c| c.bindings.len())
+ .sum::();
+ let (a, b) = diff(&initial, &fixed_source);
+ let (c, d) = diff(&initial, &threshold_source);
+ writeln!(
+ report,
+ "| {name} | {} | {} | {rows} | {a}/{b} | {c}/{d} | 4 | 1 |",
+ initial.lines().count(),
+ initial.len()
+ )
+ .unwrap();
+ }
+ }
+ // Negative controls establish that source locality and isolation differ.
+ let mut d = fixture('D', false);
+ change_fixed(&mut d, false);
+ validate(&d)?;
+ assert_total(&d, 0, 21950);
+ assert_total(&d, 19, 23175);
+ checks += 2;
+ write_fixture(dir, "D-inline-fixed-one-only", &d)?;
+ let b = fixture('B', false);
+ let baseline_case = Case {
+ name: "wrong_baseline_context".into(),
+ base: None,
+ bindings: b.baseline.as_ref().unwrap().bindings.clone(),
+ };
+ assert_eq!(
+ evaluate(&b, &baseline_case, "total")?.value,
+ Value::Int(21750)
+ );
+ assert_total(&b, 19, 23175);
+ checks += 2;
+ let mut explicit = fixture('B', false);
+ explicit
+ .baseline
+ .as_mut()
+ .unwrap()
+ .bindings
+ .insert("varying01".into(), Expr::Missing);
+ explicit.cases[19].bindings.remove("varying01");
+ validate(&explicit)?;
+ assert!(matches!(
+ evaluate(&explicit, &explicit.cases[19], "total")?.value,
+ Value::Missing(_)
+ ));
+ checks += 1;
+ let mut chained = fixture('B', false);
+ chained.cases[1].base = Some("case01".into());
+ assert!(validate(&chained).unwrap_err().contains("case-to-case"));
+ checks += 1;
+ let mut capture = fixture('D', true);
+ capture.functions.get_mut("budget_formula").unwrap().body = Expr::Ref("fixed01".into());
+ assert!(validate(&capture).unwrap_err().contains("captures"));
+ checks += 1;
+ let mut recursive = fixture('D', true);
+ recursive.functions.get_mut("budget_formula").unwrap().body =
+ Expr::Call("budget_formula".into(), vec![Expr::Ref("result".into())]);
+ assert!(validate(&recursive).unwrap_err().contains("cycle"));
+ checks += 1;
+ writeln!(report,"\nExecuted assertions: {checks}. All passed. Every accepted fixture was parsed, validated, rendered and parsed back with exact experiment AST equality. Rejected cases were checked separately.\n").unwrap();
+ report.push_str("Negative controls: D changing one repeated fixed input changes only that case; B evaluating inherited formulas against baseline inputs instead of effective case inputs gives 21,750 instead of case20's 23,175. A shallow B case copy is not frozen; factored D also needs its definition environment snapshotted. Clearing a B override restores its named baseline value, including an explicitly missing baseline. A deleted D binding row rejects. Baseline chains, recursive definitions and project captures inside pure functions reject.\n");
+ std::fs::write(dir.join("RESULTS.md"), &report).map_err(|e| e.to_string())?;
+ println!("{report}");
+ Ok(())
+}
diff --git a/harness/experiments/textual-authoring-20260911/README.md b/harness/experiments/textual-authoring-20260911/README.md
new file mode 100644
index 00000000..c00cd65e
--- /dev/null
+++ b/harness/experiments/textual-authoring-20260911/README.md
@@ -0,0 +1,24 @@
+# Current-source7 authoring probe
+
+Isolated experiment owned by Codex-956atf `/root/requirements_delta`. The complete initial example is `alarm-response.arcadia`; `probe.rs` calls the existing public core. No proposed new syntax is implemented here.
+
+The final run passed with no model warnings on 2026-09-11. See `probe-output.txt` and the parent design report. Generated source alternatives live in `observed/`: open `stabilized.arcadia`, `canonical.arcadia`, `renamed.arcadia` or `reparented.arcadia` individually. The split example consists of `split-entry.arcadia` plus `participants.arcadia`. The six alternatives are not one SourceBundle.
+
+The split-entry snapshot retains the indentation on the blank line left by moving its participant declaration. That trailing whitespace is intentional observed source, not a hand-authored formatting requirement; preserve the snapshot's bytes when using it as evidence.
+
+The reader lease used Rust 1.98.1 gnullvm and core artifact `liboa_core-6c8da57367eec642.rlib`, SHA-256 `D7EFB5F3C423FF8AF13637F53CC481502C7F317BF01E9D072F9EFD64CD954C13`. Root retains Cargo/build ownership. The temporary probe executable was removed; no target artifact was changed.
+
+From the repository root, after the build owner identifies the matching artifact and grants a reader lease:
+
+```powershell
+. .local\toolchain\env.ps1
+$authoringCore = (Get-Item -LiteralPath 'target\debug\deps\liboa_core-6c8da57367eec642.rlib').FullName
+$authoringDeps = Join-Path (Get-Location) 'target\debug\deps'
+$authoringProbe = Join-Path (Get-Location) 'harness\experiments\textual-authoring-20260911\probe.exe'
+rustc --edition=2024 'harness\experiments\textual-authoring-20260911\probe.rs' --extern "oa_core=$authoringCore" -L "dependency=$authoringDeps" -C linker=clang.exe -o $authoringProbe
+if ($LASTEXITCODE -ne 0) { throw 'Probe compilation failed' }
+& $authoringProbe 'harness\experiments\textual-authoring-20260911\alarm-response.arcadia' 'harness\experiments\textual-authoring-20260911\observed-rerun'
+if ($LASTEXITCODE -ne 0) { throw 'Probe assertions failed' }
+```
+
+Each run intentionally authors a new model from omitted IDs, so regenerated UUID bytes differ across runs. Assertions test stable identity only after adopting that run's accepted bundle. The retained `observed/` snapshots are the exact final run, not deterministic UUID fixtures. The probe does not test HTTP/native UI, save recovery, performance or a new grammar.
diff --git a/harness/experiments/textual-authoring-20260911/alarm-response.arcadia b/harness/experiments/textual-authoring-20260911/alarm-response.arcadia
new file mode 100644
index 00000000..f401ca54
--- /dev/null
+++ b/harness/experiments/textual-authoring-20260911/alarm-response.arcadia
@@ -0,0 +1,110 @@
+arcadia 7;
+project alarm_response "Alarm response" {
+ description "A complete small OA/SA source-authoring example. Numerical annotations are descriptive.";
+
+ oa_actor plant_operator "Plant operator" {}
+ oa_entity maintenance "Maintenance team" {}
+ oa_activity manage_alarm "Manage a pressure alarm" {
+ oa_activity detect_alarm "Detect alarm" {}
+ // Assessing the alarm is work decomposition, independently allocated below.
+ oa_activity assess_alarm "Assess alarm" {}
+ }
+ oa_capability safe_response "Respond safely" {}
+ oa_exchange_item alarm_report "Alarm report" {
+ mechanism Flow;
+ oa_data_field reported_pressure "Reported pressure" {
+ type_ref Real;
+ unit "kPa";
+ }
+ }
+ allocation operator_detects "Operator detects" {
+ source @plant_operator; target @detect_alarm;
+ }
+ allocation team_assesses "Team assesses" {
+ source @maintenance; target @assess_alarm;
+ }
+ interaction alarm_message "Pressure alarm report" {
+ source @detect_alarm; target @assess_alarm; items [@alarm_report];
+ }
+ capability_involvement assessment_supports_response "Assessment supports safe response" {
+ source @assess_alarm; target @safe_response;
+ }
+
+ oa_process triage "Alarm triage" {
+ steps [@detect_once, @assess_once];
+ oa_occurrence detect_once "Detect this alarm" {
+ body { Action { action @detect_alarm; lifeline none; }; };
+ }
+ oa_occurrence assess_once "Assess this alarm" {
+ body { Action { action @assess_alarm; lifeline none; }; };
+ }
+ }
+ sequence_link assessment_after_detection "Assess after detecting" {
+ source @detect_once; target @assess_once;
+ }
+ chain_involvement triage_supports_response "Triage supports safe response" {
+ source @triage; target @safe_response;
+ }
+
+ oa_scenario notification "Alarm notification" {
+ viewpoint OperationalActivity;
+ context @safe_response;
+ steps [@notify_once];
+ oa_lifeline detector_line "Detector" { represented @detect_alarm; }
+ oa_lifeline assessor_line "Assessor" { represented @assess_alarm; }
+ oa_occurrence notify_once "Report this pressure" {
+ body { Exchange {
+ exchange @alarm_message; sender @detector_line; receiver @assessor_line;
+ message_kind Asynchronous; reply_to none;
+ values [{ field @reported_pressure; path []; expression "420"; }];
+ }; };
+ }
+ }
+
+ sa_system alarm_console "Alarm console" {
+ sa_state_machine console_status "Console status" {
+ sa_region console_region "Main region" {
+ sa_initial console_start "Start" {}
+ sa_state console_ready "Ready" {}
+ sa_state console_alerting "Alerting" {}
+ }
+ }
+ }
+ sa_function classify_alarm "Classify alarm" {}
+ sa_functional_chain classification_flow "Alarm classification" {
+ steps [@classify_once];
+ sa_occurrence classify_once "Classify this alarm" {
+ body { Action { action @classify_alarm; lifeline none; }; };
+ }
+ }
+ allocation console_classifies "Console classifies" {
+ source @alarm_console; target @classify_alarm;
+ }
+ sa_event alarm_received "Alarm received" {}
+ state_transition initialize_console "Initialize" {
+ source @console_start; target @console_ready;
+ }
+ state_transition display_alarm "Display alarm" {
+ source @console_ready; target @console_alerting; triggers [@alarm_received];
+ }
+ trace assessment_refinement "Alarm classification refines assessment" {
+ oa @assess_alarm; sa @classify_alarm;
+ baseline {
+ oa { name "Assess alarm"; description ""; };
+ sa { name "Classify alarm"; description ""; };
+ };
+ }
+ artifact response_procedure "Response procedure" {
+ owner @assess_alarm; uri "procedures/alarm-response.md"; revision "review-1";
+ }
+
+ diagram response_view "Operational work" { projection FunctionalDataflow; }
+ node detector_card {
+ diagram @response_view; element @detect_alarm;
+ position { x 40; y 40; };
+ }
+ node assessor_card {
+ diagram @response_view; element @assess_alarm;
+ position { x 400; y 40; };
+ }
+}
diff --git a/harness/experiments/textual-authoring-20260911/observed/canonical.arcadia b/harness/experiments/textual-authoring-20260911/observed/canonical.arcadia
new file mode 100644
index 00000000..d4c5ff26
--- /dev/null
+++ b/harness/experiments/textual-authoring-20260911/observed/canonical.arcadia
@@ -0,0 +1,344 @@
+arcadia 7;
+project alarm_response "Alarm response" id "a0f5c4c2-f39c-4095-9816-71f1862e24d8" {
+ description "A complete small OA/SA source-authoring example. Numerical annotations are descriptive.";
+
+ oa_exchange_item alarm_report "Alarm report" id "0a48a460-964a-4578-97c4-428707e8b201" {
+ description "";
+ kind Information;
+ mechanism Flow;
+ parent none;
+ supertype none;
+ }
+
+ oa_data_field reported_pressure "Reported pressure" id "0bf7f442-cc8d-4782-ba65-f357e91fd477" {
+ default none;
+ description "";
+ direction Unspecified;
+ maximum 1;
+ minimum 1;
+ parent @alarm_report;
+ storage Value;
+ type_ref Real;
+ unit "kPa";
+ }
+
+ oa_lifeline assessor_line "Assessor" id "1b421446-9d17-4c8b-90f1-6fbe8b513a04" {
+ description "";
+ parent @notification;
+ represented @assess_alarm;
+ }
+
+ oa_activity manage_alarm "Manage a pressure alarm" id "4154bf39-9fd5-490e-aebf-fba3d67b697c" {
+ description "";
+ parent none;
+ }
+
+ sa_state console_alerting "Alerting" id "4d167878-2bbe-4acd-abd6-ee7dcd42ec50" {
+ description "";
+ do_activity "";
+ entry "";
+ exit "";
+ kind State;
+ parent @console_region;
+ }
+
+ oa_occurrence assess_once "Assess this alarm" id "528d1c78-62d9-4e45-bb4a-cf529d8ed4a9" {
+ body {
+ Action {
+ action @assess_alarm;
+ lifeline none;
+ };
+ };
+ description "";
+ parent @triage;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_functional_chain classification_flow "Alarm classification" id "578dbcd5-3c28-43d8-913e-af27bc51ebde" {
+ description "";
+ parent none;
+ steps [@classify_once];
+ }
+
+ oa_lifeline detector_line "Detector" id "57c62024-6d19-4818-ac8c-66fb4a9b3da7" {
+ description "";
+ parent @notification;
+ represented @detect_alarm;
+ }
+
+ sa_initial console_start "Start" id "5a92c3a8-6af5-452a-849e-c2a17dc33260" {
+ description "";
+ do_activity "";
+ entry "";
+ exit "";
+ kind Initial;
+ parent @console_region;
+ }
+
+ oa_actor plant_operator "Plant operator" id "60539571-2921-4e7d-ab83-bae670d0cb15" {
+ description "";
+ parent none;
+ }
+
+ oa_scenario notification "Alarm notification" id "69e23f04-7bb3-4db1-a2a3-d8e65101c546" {
+ context @safe_response;
+ description "";
+ parent none;
+ postcondition "";
+ precondition "";
+ steps [@notify_once];
+ viewpoint OperationalActivity;
+ }
+
+ oa_entity maintenance "Maintenance team" id "6af96577-1b04-4ba1-b91b-6cef61adc6d6" {
+ description "";
+ parent none;
+ }
+
+ oa_occurrence detect_once "Detect this alarm" id "6ca87cbe-9579-49b3-884e-e6ee63ba8c5b" {
+ body {
+ Action {
+ action @detect_alarm;
+ lifeline none;
+ };
+ };
+ description "";
+ parent @triage;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_event alarm_received "Alarm received" id "7fba860a-721f-4da2-8315-010de2e9c69e" {
+ body {
+ Signal {
+ items [];
+ };
+ };
+ description "";
+ parent none;
+ }
+
+ oa_activity assess_alarm "Assess alarm" id "8cbd5a4f-1c31-49c9-8a01-987144ce962e" {
+ description "";
+ parent @manage_alarm;
+ }
+
+ sa_state console_ready "Ready" id "9064a197-96ea-4bdf-9dd6-2e9ef66856d8" {
+ description "";
+ do_activity "";
+ entry "";
+ exit "";
+ kind State;
+ parent @console_region;
+ }
+
+ oa_process triage "Alarm triage" id "9888d781-09f3-4861-b9dc-7a97080d24d6" {
+ description "";
+ parent none;
+ steps [@detect_once, @assess_once];
+ }
+
+ sa_system alarm_console "Alarm console" id "9ea585b5-be12-4204-8909-02ea9bed49f0" {
+ description "";
+ parent none;
+ }
+
+ oa_occurrence notify_once "Report this pressure" id "a1c37b31-41ed-4e16-a243-a50702ff1bc1" {
+ body {
+ Exchange {
+ exchange @alarm_message;
+ message_kind Asynchronous;
+ receiver @assessor_line;
+ reply_to none;
+ sender @detector_line;
+ values [{
+ expression "420";
+ field @reported_pressure;
+ path [];
+ }];
+ };
+ };
+ description "";
+ parent @notification;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_occurrence classify_once "Classify this alarm" id "a6c37863-8e72-44f9-a8a3-fea06341c714" {
+ body {
+ Action {
+ action @classify_alarm;
+ lifeline none;
+ };
+ };
+ description "";
+ parent @classification_flow;
+ postcondition "";
+ precondition "";
+ }
+
+ sa_state_machine console_status "Console status" id "a6c6f463-5c80-4b02-a2e6-67cd0088b2f2" {
+ description "";
+ parent @alarm_console;
+ }
+
+ sa_function classify_alarm "Classify alarm" id "ad6941ee-f900-4a2b-a368-d8879f1ffece" {
+ description "";
+ parent none;
+ }
+
+ oa_activity detect_alarm "Detect alarm" id "bf9e9d8a-edb2-47f4-93f9-7ae373c42160" {
+ description "";
+ parent @manage_alarm;
+ }
+
+ oa_capability safe_response "Respond safely" id "c097b1b5-45dc-49de-bcf6-89b5ccb752d1" {
+ description "";
+ parent none;
+ }
+
+ sa_region console_region "Main region" id "e8b62edc-2233-4ec4-a811-2e8baf81a6e9" {
+ description "";
+ parent @console_status;
+ }
+
+ interaction alarm_message "Pressure alarm report" id "269e5bdd-2478-4a43-9c77-84d99ac4e8a6" {
+ description "";
+ items [@alarm_report];
+ source @detect_alarm;
+ target @assess_alarm;
+ }
+
+ capability_involvement assessment_supports_response "Assessment supports safe response" id "27633a6d-d962-4d19-9367-9ca3f00de0b0" {
+ description "";
+ source @assess_alarm;
+ target @safe_response;
+ }
+
+ allocation console_classifies "Console classifies" id "294d4262-303f-41db-b01e-fe47c1647693" {
+ description "";
+ source @alarm_console;
+ target @classify_alarm;
+ }
+
+ chain_involvement triage_supports_response "Triage supports safe response" id "2aeba184-bf69-49c7-87ff-cc2da82b006c" {
+ description "";
+ source @triage;
+ target @safe_response;
+ }
+
+ state_transition initialize_console "Initialize" id "839d4359-c78f-414b-867e-5b40b1f145cc" {
+ description "";
+ effect "";
+ effects [];
+ guard "";
+ source @console_start;
+ target @console_ready;
+ transition_kind External;
+ trigger "";
+ triggers [];
+ }
+
+ allocation operator_detects "Operator detects" id "8653fb7d-529d-495d-abde-897dee36b4bb" {
+ description "";
+ source @plant_operator;
+ target @detect_alarm;
+ }
+
+ allocation team_assesses "Team assesses" id "bb36afbf-9878-401b-b172-d04b08ecf603" {
+ description "";
+ source @maintenance;
+ target @assess_alarm;
+ }
+
+ sequence_link assessment_after_detection "Assess after detecting" id "bfd9c0e7-832b-4983-90aa-fc37d23193e2" {
+ description "";
+ guard "";
+ source @detect_once;
+ target @assess_once;
+ }
+
+ state_transition display_alarm "Display alarm" id "dd383165-1b89-46b9-8ae3-80a6b6406a4e" {
+ description "";
+ effect "";
+ effects [];
+ guard "";
+ source @console_ready;
+ target @console_alerting;
+ transition_kind External;
+ trigger "";
+ triggers [@alarm_received];
+ }
+
+ diagram response_view "Operational work" id "a44d9d69-78a0-49de-8eb8-3757aa6c0919" {
+ description "";
+ layer Oa;
+ projection FunctionalDataflow;
+ routes [];
+ view_options {
+ coverage "All";
+ exchange_labels "Name";
+ hidden_edges [];
+ participant_categories "All";
+ relation_kinds "All";
+ work_categories "All";
+ work_contexts "All";
+ };
+ }
+
+ node detector_card id "45f016f4-3558-45b9-9559-da8ef9d5dcad" {
+ collapsed false;
+ diagram @response_view;
+ element @detect_alarm;
+ position {
+ x 40.0;
+ y 40.0;
+ };
+ size {
+ height 94.0;
+ width 224.0;
+ };
+ }
+
+ node assessor_card id "6ee7a0cc-013b-4a0f-8cff-513872e028b7" {
+ collapsed false;
+ diagram @response_view;
+ element @assess_alarm;
+ position {
+ x 400.0;
+ y 40.0;
+ };
+ size {
+ height 94.0;
+ width 224.0;
+ };
+ }
+
+ artifact response_procedure "Response procedure" id "6ba54037-c6f1-495b-ba96-6f2e910a218c" {
+ description "";
+ fragment none;
+ owner @assess_alarm;
+ relation References;
+ revision "review-1";
+ uri "procedures/alarm-response.md";
+ }
+
+ trace assessment_refinement "Alarm classification refines assessment" id "34142333-67c2-4972-a64f-47e140ed93ca" {
+ baseline {
+ oa {
+ description "";
+ name "Assess alarm";
+ };
+ sa {
+ description "";
+ name "Classify alarm";
+ };
+ };
+ description "";
+ kind Refinement;
+ oa @assess_alarm;
+ sa @classify_alarm;
+ }
+
+}
diff --git a/harness/experiments/textual-authoring-20260911/observed/participants.arcadia b/harness/experiments/textual-authoring-20260911/observed/participants.arcadia
new file mode 100644
index 00000000..2d350787
--- /dev/null
+++ b/harness/experiments/textual-authoring-20260911/observed/participants.arcadia
@@ -0,0 +1 @@
+oa_actor plant_operator "Plant operator" id "60539571-2921-4e7d-ab83-bae670d0cb15" {}
diff --git a/harness/experiments/textual-authoring-20260911/observed/renamed.arcadia b/harness/experiments/textual-authoring-20260911/observed/renamed.arcadia
new file mode 100644
index 00000000..71b2aa4c
--- /dev/null
+++ b/harness/experiments/textual-authoring-20260911/observed/renamed.arcadia
@@ -0,0 +1,110 @@
+arcadia 7;
+project alarm_response "Alarm response" id "a0f5c4c2-f39c-4095-9816-71f1862e24d8" {
+ description "A complete small OA/SA source-authoring example. Numerical annotations are descriptive.";
+
+ oa_actor plant_operator "Plant operator" id "60539571-2921-4e7d-ab83-bae670d0cb15" {}
+ oa_entity maintenance "Maintenance team" id "6af96577-1b04-4ba1-b91b-6cef61adc6d6" {}
+ oa_activity manage_alarm "Manage a pressure alarm" id "4154bf39-9fd5-490e-aebf-fba3d67b697c" {
+ oa_activity detect_alarm "Detect alarm" id "bf9e9d8a-edb2-47f4-93f9-7ae373c42160" {}
+ // Assessing the alarm is work decomposition, independently allocated below.
+ oa_activity review_alarm "Assess alarm" id "8cbd5a4f-1c31-49c9-8a01-987144ce962e" {}
+ }
+ oa_capability safe_response "Respond safely" id "c097b1b5-45dc-49de-bcf6-89b5ccb752d1" {}
+ oa_exchange_item alarm_report "Alarm report" id "0a48a460-964a-4578-97c4-428707e8b201" {
+ mechanism Flow;
+ oa_data_field reported_pressure "Reported pressure" id "0bf7f442-cc8d-4782-ba65-f357e91fd477" {
+ type_ref Real;
+ unit "kPa";
+ }
+ }
+ allocation operator_detects "Operator detects" id "8653fb7d-529d-495d-abde-897dee36b4bb" {
+ source @plant_operator; target @detect_alarm;
+ }
+ allocation team_assesses "Team assesses" id "bb36afbf-9878-401b-b172-d04b08ecf603" {
+ source @maintenance; target @review_alarm;
+ }
+ interaction alarm_message "Pressure alarm report" id "269e5bdd-2478-4a43-9c77-84d99ac4e8a6" {
+ source @detect_alarm; target @review_alarm; items [@alarm_report];
+ }
+ capability_involvement assessment_supports_response "Assessment supports safe response" id "27633a6d-d962-4d19-9367-9ca3f00de0b0" {
+ source @review_alarm; target @safe_response;
+ }
+
+ oa_process triage "Alarm triage" id "9888d781-09f3-4861-b9dc-7a97080d24d6" {
+ steps [@detect_once, @assess_once];
+ oa_occurrence detect_once "Detect this alarm" id "6ca87cbe-9579-49b3-884e-e6ee63ba8c5b" {
+ body { Action { action @detect_alarm; lifeline none; }; };
+ }
+ oa_occurrence assess_once "Assess this alarm" id "528d1c78-62d9-4e45-bb4a-cf529d8ed4a9" {
+ body { Action { action @review_alarm; lifeline none; }; };
+ }
+ }
+ sequence_link assessment_after_detection "Assess after detecting" id "bfd9c0e7-832b-4983-90aa-fc37d23193e2" {
+ source @detect_once; target @assess_once;
+ }
+ chain_involvement triage_supports_response "Triage supports safe response" id "2aeba184-bf69-49c7-87ff-cc2da82b006c" {
+ source @triage; target @safe_response;
+ }
+
+ oa_scenario notification "Alarm notification" id "69e23f04-7bb3-4db1-a2a3-d8e65101c546" {
+ viewpoint OperationalActivity;
+ context @safe_response;
+ steps [@notify_once];
+ oa_lifeline detector_line "Detector" id "57c62024-6d19-4818-ac8c-66fb4a9b3da7" { represented @detect_alarm; }
+ oa_lifeline assessor_line "Assessor" id "1b421446-9d17-4c8b-90f1-6fbe8b513a04" { represented @review_alarm; }
+ oa_occurrence notify_once "Report this pressure" id "a1c37b31-41ed-4e16-a243-a50702ff1bc1" {
+ body { Exchange {
+ exchange @alarm_message; sender @detector_line; receiver @assessor_line;
+ message_kind Asynchronous; reply_to none;
+ values [{ field @reported_pressure; path []; expression "420"; }];
+ }; };
+ }
+ }
+
+ sa_system alarm_console "Alarm console" id "9ea585b5-be12-4204-8909-02ea9bed49f0" {
+ sa_state_machine console_status "Console status" id "a6c6f463-5c80-4b02-a2e6-67cd0088b2f2" {
+ sa_region console_region "Main region" id "e8b62edc-2233-4ec4-a811-2e8baf81a6e9" {
+ sa_initial console_start "Start" id "5a92c3a8-6af5-452a-849e-c2a17dc33260" {}
+ sa_state console_ready "Ready" id "9064a197-96ea-4bdf-9dd6-2e9ef66856d8" {}
+ sa_state console_alerting "Alerting" id "4d167878-2bbe-4acd-abd6-ee7dcd42ec50" {}
+ }
+ }
+ }
+ sa_function classify_alarm "Classify alarm" id "ad6941ee-f900-4a2b-a368-d8879f1ffece" {}
+ sa_functional_chain classification_flow "Alarm classification" id "578dbcd5-3c28-43d8-913e-af27bc51ebde" {
+ steps [@classify_once];
+ sa_occurrence classify_once "Classify this alarm" id "a6c37863-8e72-44f9-a8a3-fea06341c714" {
+ body { Action { action @classify_alarm; lifeline none; }; };
+ }
+ }
+ allocation console_classifies "Console classifies" id "294d4262-303f-41db-b01e-fe47c1647693" {
+ source @alarm_console; target @classify_alarm;
+ }
+ sa_event alarm_received "Alarm received" id "7fba860a-721f-4da2-8315-010de2e9c69e" {}
+ state_transition initialize_console "Initialize" id "839d4359-c78f-414b-867e-5b40b1f145cc" {
+ source @console_start; target @console_ready;
+ }
+ state_transition display_alarm "Display alarm" id "dd383165-1b89-46b9-8ae3-80a6b6406a4e" {
+ source @console_ready; target @console_alerting; triggers [@alarm_received];
+ }
+ trace assessment_refinement "Alarm classification refines assessment" id "34142333-67c2-4972-a64f-47e140ed93ca" {
+ oa @review_alarm; sa @classify_alarm;
+ baseline {
+ oa { name "Assess alarm"; description ""; };
+ sa { name "Classify alarm"; description ""; };
+ };
+ }
+ artifact response_procedure "Response procedure" id "6ba54037-c6f1-495b-ba96-6f2e910a218c" {
+ owner @review_alarm; uri "procedures/alarm-response.md"; revision "review-1";
+ }
+
+ diagram response_view "Operational work" id "a44d9d69-78a0-49de-8eb8-3757aa6c0919" { projection FunctionalDataflow; }
+ node detector_card id "45f016f4-3558-45b9-9559-da8ef9d5dcad" {
+ diagram @response_view; element @detect_alarm;
+ position { x 40; y 40; };
+ }
+ node assessor_card id "6ee7a0cc-013b-4a0f-8cff-513872e028b7" {
+ diagram @response_view; element @review_alarm;
+ position { x 400; y 40; };
+ }
+}
diff --git a/harness/experiments/textual-authoring-20260911/observed/reparented.arcadia b/harness/experiments/textual-authoring-20260911/observed/reparented.arcadia
new file mode 100644
index 00000000..428ec642
--- /dev/null
+++ b/harness/experiments/textual-authoring-20260911/observed/reparented.arcadia
@@ -0,0 +1,114 @@
+arcadia 7;
+project alarm_response "Alarm response" id "a0f5c4c2-f39c-4095-9816-71f1862e24d8" {
+ description "A complete small OA/SA source-authoring example. Numerical annotations are descriptive.";
+
+ oa_actor plant_operator "Plant operator" id "60539571-2921-4e7d-ab83-bae670d0cb15" {}
+ oa_entity maintenance "Maintenance team" id "6af96577-1b04-4ba1-b91b-6cef61adc6d6" {}
+ oa_activity manage_alarm "Manage a pressure alarm" id "4154bf39-9fd5-490e-aebf-fba3d67b697c" {
+ oa_activity detect_alarm "Detect alarm" id "bf9e9d8a-edb2-47f4-93f9-7ae373c42160" {}
+
+
+
+ }
+ oa_capability safe_response "Respond safely" id "c097b1b5-45dc-49de-bcf6-89b5ccb752d1" {}
+ oa_exchange_item alarm_report "Alarm report" id "0a48a460-964a-4578-97c4-428707e8b201" {
+ mechanism Flow;
+ oa_data_field reported_pressure "Reported pressure" id "0bf7f442-cc8d-4782-ba65-f357e91fd477" {
+ type_ref Real;
+ unit "kPa";
+ }
+ }
+ allocation operator_detects "Operator detects" id "8653fb7d-529d-495d-abde-897dee36b4bb" {
+ source @plant_operator; target @detect_alarm;
+ }
+ allocation team_assesses "Team assesses" id "bb36afbf-9878-401b-b172-d04b08ecf603" {
+ source @maintenance; target @assess_alarm;
+ }
+ interaction alarm_message "Pressure alarm report" id "269e5bdd-2478-4a43-9c77-84d99ac4e8a6" {
+ source @detect_alarm; target @assess_alarm; items [@alarm_report];
+ }
+ capability_involvement assessment_supports_response "Assessment supports safe response" id "27633a6d-d962-4d19-9367-9ca3f00de0b0" {
+ source @assess_alarm; target @safe_response;
+ }
+
+ oa_process triage "Alarm triage" id "9888d781-09f3-4861-b9dc-7a97080d24d6" {
+ steps [@detect_once, @assess_once];
+ oa_occurrence detect_once "Detect this alarm" id "6ca87cbe-9579-49b3-884e-e6ee63ba8c5b" {
+ body { Action { action @detect_alarm; lifeline none; }; };
+ }
+ oa_occurrence assess_once "Assess this alarm" id "528d1c78-62d9-4e45-bb4a-cf529d8ed4a9" {
+ body { Action { action @assess_alarm; lifeline none; }; };
+ }
+ }
+ sequence_link assessment_after_detection "Assess after detecting" id "bfd9c0e7-832b-4983-90aa-fc37d23193e2" {
+ source @detect_once; target @assess_once;
+ }
+ chain_involvement triage_supports_response "Triage supports safe response" id "2aeba184-bf69-49c7-87ff-cc2da82b006c" {
+ source @triage; target @safe_response;
+ }
+
+ oa_scenario notification "Alarm notification" id "69e23f04-7bb3-4db1-a2a3-d8e65101c546" {
+ viewpoint OperationalActivity;
+ context @safe_response;
+ steps [@notify_once];
+ oa_lifeline detector_line "Detector" id "57c62024-6d19-4818-ac8c-66fb4a9b3da7" { represented @detect_alarm; }
+ oa_lifeline assessor_line "Assessor" id "1b421446-9d17-4c8b-90f1-6fbe8b513a04" { represented @assess_alarm; }
+ oa_occurrence notify_once "Report this pressure" id "a1c37b31-41ed-4e16-a243-a50702ff1bc1" {
+ body { Exchange {
+ exchange @alarm_message; sender @detector_line; receiver @assessor_line;
+ message_kind Asynchronous; reply_to none;
+ values [{ field @reported_pressure; path []; expression "420"; }];
+ }; };
+ }
+ }
+
+ sa_system alarm_console "Alarm console" id "9ea585b5-be12-4204-8909-02ea9bed49f0" {
+ sa_state_machine console_status "Console status" id "a6c6f463-5c80-4b02-a2e6-67cd0088b2f2" {
+ sa_region console_region "Main region" id "e8b62edc-2233-4ec4-a811-2e8baf81a6e9" {
+ sa_initial console_start "Start" id "5a92c3a8-6af5-452a-849e-c2a17dc33260" {}
+ sa_state console_ready "Ready" id "9064a197-96ea-4bdf-9dd6-2e9ef66856d8" {}
+ sa_state console_alerting "Alerting" id "4d167878-2bbe-4acd-abd6-ee7dcd42ec50" {}
+ }
+ }
+ }
+ sa_function classify_alarm "Classify alarm" id "ad6941ee-f900-4a2b-a368-d8879f1ffece" {}
+ sa_functional_chain classification_flow "Alarm classification" id "578dbcd5-3c28-43d8-913e-af27bc51ebde" {
+ steps [@classify_once];
+ sa_occurrence classify_once "Classify this alarm" id "a6c37863-8e72-44f9-a8a3-fea06341c714" {
+ body { Action { action @classify_alarm; lifeline none; }; };
+ }
+ }
+ allocation console_classifies "Console classifies" id "294d4262-303f-41db-b01e-fe47c1647693" {
+ source @alarm_console; target @classify_alarm;
+ }
+ sa_event alarm_received "Alarm received" id "7fba860a-721f-4da2-8315-010de2e9c69e" {}
+ state_transition initialize_console "Initialize" id "839d4359-c78f-414b-867e-5b40b1f145cc" {
+ source @console_start; target @console_ready;
+ }
+ state_transition display_alarm "Display alarm" id "dd383165-1b89-46b9-8ae3-80a6b6406a4e" {
+ source @console_ready; target @console_alerting; triggers [@alarm_received];
+ }
+ trace assessment_refinement "Alarm classification refines assessment" id "34142333-67c2-4972-a64f-47e140ed93ca" {
+ oa @assess_alarm; sa @classify_alarm;
+ baseline {
+ oa { name "Assess alarm"; description ""; };
+ sa { name "Classify alarm"; description ""; };
+ };
+ }
+ artifact response_procedure "Response procedure" id "6ba54037-c6f1-495b-ba96-6f2e910a218c" {
+ owner @assess_alarm; uri "procedures/alarm-response.md"; revision "review-1";
+ }
+
+ diagram response_view "Operational work" id "a44d9d69-78a0-49de-8eb8-3757aa6c0919" { projection FunctionalDataflow; }
+ node detector_card id "45f016f4-3558-45b9-9559-da8ef9d5dcad" {
+ diagram @response_view; element @detect_alarm;
+ position { x 40; y 40; };
+ }
+ node assessor_card id "6ee7a0cc-013b-4a0f-8cff-513872e028b7" {
+ diagram @response_view; element @assess_alarm;
+ position { x 400; y 40; };
+ }
+
+ // Assessing the alarm is work decomposition, independently allocated below.
+ oa_activity assess_alarm "Assess alarm" id "8cbd5a4f-1c31-49c9-8a01-987144ce962e" {}
+}
diff --git a/harness/experiments/textual-authoring-20260911/observed/split-entry.arcadia b/harness/experiments/textual-authoring-20260911/observed/split-entry.arcadia
new file mode 100644
index 00000000..77c05082
--- /dev/null
+++ b/harness/experiments/textual-authoring-20260911/observed/split-entry.arcadia
@@ -0,0 +1,111 @@
+arcadia 7;
+project alarm_response "Alarm response" id "a0f5c4c2-f39c-4095-9816-71f1862e24d8" {
+ description "A complete small OA/SA source-authoring example. Numerical annotations are descriptive.";
+
+
+ oa_entity maintenance "Maintenance team" id "6af96577-1b04-4ba1-b91b-6cef61adc6d6" {}
+ oa_activity manage_alarm "Manage a pressure alarm" id "4154bf39-9fd5-490e-aebf-fba3d67b697c" {
+ oa_activity detect_alarm "Detect alarm" id "bf9e9d8a-edb2-47f4-93f9-7ae373c42160" {}
+ // Assessing the alarm is work decomposition, independently allocated below.
+ oa_activity assess_alarm "Assess alarm" id "8cbd5a4f-1c31-49c9-8a01-987144ce962e" {}
+ }
+ oa_capability safe_response "Respond safely" id "c097b1b5-45dc-49de-bcf6-89b5ccb752d1" {}
+ oa_exchange_item alarm_report "Alarm report" id "0a48a460-964a-4578-97c4-428707e8b201" {
+ mechanism Flow;
+ oa_data_field reported_pressure "Reported pressure" id "0bf7f442-cc8d-4782-ba65-f357e91fd477" {
+ type_ref Real;
+ unit "kPa";
+ }
+ }
+ allocation operator_detects "Operator detects" id "8653fb7d-529d-495d-abde-897dee36b4bb" {
+ source @plant_operator; target @detect_alarm;
+ }
+ allocation team_assesses "Team assesses" id "bb36afbf-9878-401b-b172-d04b08ecf603" {
+ source @maintenance; target @assess_alarm;
+ }
+ interaction alarm_message "Pressure alarm report" id "269e5bdd-2478-4a43-9c77-84d99ac4e8a6" {
+ source @detect_alarm; target @assess_alarm; items [@alarm_report];
+ }
+ capability_involvement assessment_supports_response "Assessment supports safe response" id "27633a6d-d962-4d19-9367-9ca3f00de0b0" {
+ source @assess_alarm; target @safe_response;
+ }
+
+ oa_process triage "Alarm triage" id "9888d781-09f3-4861-b9dc-7a97080d24d6" {
+ steps [@detect_once, @assess_once];
+ oa_occurrence detect_once "Detect this alarm" id "6ca87cbe-9579-49b3-884e-e6ee63ba8c5b" {
+ body { Action { action @detect_alarm; lifeline none; }; };
+ }
+ oa_occurrence assess_once "Assess this alarm" id "528d1c78-62d9-4e45-bb4a-cf529d8ed4a9" {
+ body { Action { action @assess_alarm; lifeline none; }; };
+ }
+ }
+ sequence_link assessment_after_detection "Assess after detecting" id "bfd9c0e7-832b-4983-90aa-fc37d23193e2" {
+ source @detect_once; target @assess_once;
+ }
+ chain_involvement triage_supports_response "Triage supports safe response" id "2aeba184-bf69-49c7-87ff-cc2da82b006c" {
+ source @triage; target @safe_response;
+ }
+
+ oa_scenario notification "Alarm notification" id "69e23f04-7bb3-4db1-a2a3-d8e65101c546" {
+ viewpoint OperationalActivity;
+ context @safe_response;
+ steps [@notify_once];
+ oa_lifeline detector_line "Detector" id "57c62024-6d19-4818-ac8c-66fb4a9b3da7" { represented @detect_alarm; }
+ oa_lifeline assessor_line "Assessor" id "1b421446-9d17-4c8b-90f1-6fbe8b513a04" { represented @assess_alarm; }
+ oa_occurrence notify_once "Report this pressure" id "a1c37b31-41ed-4e16-a243-a50702ff1bc1" {
+ body { Exchange {
+ exchange @alarm_message; sender @detector_line; receiver @assessor_line;
+ message_kind Asynchronous; reply_to none;
+ values [{ field @reported_pressure; path []; expression "420"; }];
+ }; };
+ }
+ }
+
+ sa_system alarm_console "Alarm console" id "9ea585b5-be12-4204-8909-02ea9bed49f0" {
+ sa_state_machine console_status "Console status" id "a6c6f463-5c80-4b02-a2e6-67cd0088b2f2" {
+ sa_region console_region "Main region" id "e8b62edc-2233-4ec4-a811-2e8baf81a6e9" {
+ sa_initial console_start "Start" id "5a92c3a8-6af5-452a-849e-c2a17dc33260" {}
+ sa_state console_ready "Ready" id "9064a197-96ea-4bdf-9dd6-2e9ef66856d8" {}
+ sa_state console_alerting "Alerting" id "4d167878-2bbe-4acd-abd6-ee7dcd42ec50" {}
+ }
+ }
+ }
+ sa_function classify_alarm "Classify alarm" id "ad6941ee-f900-4a2b-a368-d8879f1ffece" {}
+ sa_functional_chain classification_flow "Alarm classification" id "578dbcd5-3c28-43d8-913e-af27bc51ebde" {
+ steps [@classify_once];
+ sa_occurrence classify_once "Classify this alarm" id "a6c37863-8e72-44f9-a8a3-fea06341c714" {
+ body { Action { action @classify_alarm; lifeline none; }; };
+ }
+ }
+ allocation console_classifies "Console classifies" id "294d4262-303f-41db-b01e-fe47c1647693" {
+ source @alarm_console; target @classify_alarm;
+ }
+ sa_event alarm_received "Alarm received" id "7fba860a-721f-4da2-8315-010de2e9c69e" {}
+ state_transition initialize_console "Initialize" id "839d4359-c78f-414b-867e-5b40b1f145cc" {
+ source @console_start; target @console_ready;
+ }
+ state_transition display_alarm "Display alarm" id "dd383165-1b89-46b9-8ae3-80a6b6406a4e" {
+ source @console_ready; target @console_alerting; triggers [@alarm_received];
+ }
+ trace assessment_refinement "Alarm classification refines assessment" id "34142333-67c2-4972-a64f-47e140ed93ca" {
+ oa @assess_alarm; sa @classify_alarm;
+ baseline {
+ oa { name "Assess alarm"; description ""; };
+ sa { name "Classify alarm"; description ""; };
+ };
+ }
+ artifact response_procedure "Response procedure" id "6ba54037-c6f1-495b-ba96-6f2e910a218c" {
+ owner @assess_alarm; uri "procedures/alarm-response.md"; revision "review-1";
+ }
+
+ diagram response_view "Operational work" id "a44d9d69-78a0-49de-8eb8-3757aa6c0919" { projection FunctionalDataflow; }
+ node detector_card id "45f016f4-3558-45b9-9559-da8ef9d5dcad" {
+ diagram @response_view; element @detect_alarm;
+ position { x 40; y 40; };
+ }
+ node assessor_card id "6ee7a0cc-013b-4a0f-8cff-513872e028b7" {
+ diagram @response_view; element @assess_alarm;
+ position { x 400; y 40; };
+ }
+ include "participants.arcadia";
+}
diff --git a/harness/experiments/textual-authoring-20260911/observed/stabilized.arcadia b/harness/experiments/textual-authoring-20260911/observed/stabilized.arcadia
new file mode 100644
index 00000000..277a663e
--- /dev/null
+++ b/harness/experiments/textual-authoring-20260911/observed/stabilized.arcadia
@@ -0,0 +1,110 @@
+arcadia 7;
+project alarm_response "Alarm response" id "a0f5c4c2-f39c-4095-9816-71f1862e24d8" {
+ description "A complete small OA/SA source-authoring example. Numerical annotations are descriptive.";
+
+ oa_actor plant_operator "Plant operator" id "60539571-2921-4e7d-ab83-bae670d0cb15" {}
+ oa_entity maintenance "Maintenance team" id "6af96577-1b04-4ba1-b91b-6cef61adc6d6" {}
+ oa_activity manage_alarm "Manage a pressure alarm" id "4154bf39-9fd5-490e-aebf-fba3d67b697c" {
+ oa_activity detect_alarm "Detect alarm" id "bf9e9d8a-edb2-47f4-93f9-7ae373c42160" {}
+ // Assessing the alarm is work decomposition, independently allocated below.
+ oa_activity assess_alarm "Assess alarm" id "8cbd5a4f-1c31-49c9-8a01-987144ce962e" {}
+ }
+ oa_capability safe_response "Respond safely" id "c097b1b5-45dc-49de-bcf6-89b5ccb752d1" {}
+ oa_exchange_item alarm_report "Alarm report" id "0a48a460-964a-4578-97c4-428707e8b201" {
+ mechanism Flow;
+ oa_data_field reported_pressure "Reported pressure" id "0bf7f442-cc8d-4782-ba65-f357e91fd477" {
+ type_ref Real;
+ unit "kPa";
+ }
+ }
+ allocation operator_detects "Operator detects" id "8653fb7d-529d-495d-abde-897dee36b4bb" {
+ source @plant_operator; target @detect_alarm;
+ }
+ allocation team_assesses "Team assesses" id "bb36afbf-9878-401b-b172-d04b08ecf603" {
+ source @maintenance; target @assess_alarm;
+ }
+ interaction alarm_message "Pressure alarm report" id "269e5bdd-2478-4a43-9c77-84d99ac4e8a6" {
+ source @detect_alarm; target @assess_alarm; items [@alarm_report];
+ }
+ capability_involvement assessment_supports_response "Assessment supports safe response" id "27633a6d-d962-4d19-9367-9ca3f00de0b0" {
+ source @assess_alarm; target @safe_response;
+ }
+
+ oa_process triage "Alarm triage" id "9888d781-09f3-4861-b9dc-7a97080d24d6" {
+ steps [@detect_once, @assess_once];
+ oa_occurrence detect_once "Detect this alarm" id "6ca87cbe-9579-49b3-884e-e6ee63ba8c5b" {
+ body { Action { action @detect_alarm; lifeline none; }; };
+ }
+ oa_occurrence assess_once "Assess this alarm" id "528d1c78-62d9-4e45-bb4a-cf529d8ed4a9" {
+ body { Action { action @assess_alarm; lifeline none; }; };
+ }
+ }
+ sequence_link assessment_after_detection "Assess after detecting" id "bfd9c0e7-832b-4983-90aa-fc37d23193e2" {
+ source @detect_once; target @assess_once;
+ }
+ chain_involvement triage_supports_response "Triage supports safe response" id "2aeba184-bf69-49c7-87ff-cc2da82b006c" {
+ source @triage; target @safe_response;
+ }
+
+ oa_scenario notification "Alarm notification" id "69e23f04-7bb3-4db1-a2a3-d8e65101c546" {
+ viewpoint OperationalActivity;
+ context @safe_response;
+ steps [@notify_once];
+ oa_lifeline detector_line "Detector" id "57c62024-6d19-4818-ac8c-66fb4a9b3da7" { represented @detect_alarm; }
+ oa_lifeline assessor_line "Assessor" id "1b421446-9d17-4c8b-90f1-6fbe8b513a04" { represented @assess_alarm; }
+ oa_occurrence notify_once "Report this pressure" id "a1c37b31-41ed-4e16-a243-a50702ff1bc1" {
+ body { Exchange {
+ exchange @alarm_message; sender @detector_line; receiver @assessor_line;
+ message_kind Asynchronous; reply_to none;
+ values [{ field @reported_pressure; path []; expression "420"; }];
+ }; };
+ }
+ }
+
+ sa_system alarm_console "Alarm console" id "9ea585b5-be12-4204-8909-02ea9bed49f0" {
+ sa_state_machine console_status "Console status" id "a6c6f463-5c80-4b02-a2e6-67cd0088b2f2" {
+ sa_region console_region "Main region" id "e8b62edc-2233-4ec4-a811-2e8baf81a6e9" {
+ sa_initial console_start "Start" id "5a92c3a8-6af5-452a-849e-c2a17dc33260" {}
+ sa_state console_ready "Ready" id "9064a197-96ea-4bdf-9dd6-2e9ef66856d8" {}
+ sa_state console_alerting "Alerting" id "4d167878-2bbe-4acd-abd6-ee7dcd42ec50" {}
+ }
+ }
+ }
+ sa_function classify_alarm "Classify alarm" id "ad6941ee-f900-4a2b-a368-d8879f1ffece" {}
+ sa_functional_chain classification_flow "Alarm classification" id "578dbcd5-3c28-43d8-913e-af27bc51ebde" {
+ steps [@classify_once];
+ sa_occurrence classify_once "Classify this alarm" id "a6c37863-8e72-44f9-a8a3-fea06341c714" {
+ body { Action { action @classify_alarm; lifeline none; }; };
+ }
+ }
+ allocation console_classifies "Console classifies" id "294d4262-303f-41db-b01e-fe47c1647693" {
+ source @alarm_console; target @classify_alarm;
+ }
+ sa_event alarm_received "Alarm received" id "7fba860a-721f-4da2-8315-010de2e9c69e" {}
+ state_transition initialize_console "Initialize" id "839d4359-c78f-414b-867e-5b40b1f145cc" {
+ source @console_start; target @console_ready;
+ }
+ state_transition display_alarm "Display alarm" id "dd383165-1b89-46b9-8ae3-80a6b6406a4e" {
+ source @console_ready; target @console_alerting; triggers [@alarm_received];
+ }
+ trace assessment_refinement "Alarm classification refines assessment" id "34142333-67c2-4972-a64f-47e140ed93ca" {
+ oa @assess_alarm; sa @classify_alarm;
+ baseline {
+ oa { name "Assess alarm"; description ""; };
+ sa { name "Classify alarm"; description ""; };
+ };
+ }
+ artifact response_procedure "Response procedure" id "6ba54037-c6f1-495b-ba96-6f2e910a218c" {
+ owner @assess_alarm; uri "procedures/alarm-response.md"; revision "review-1";
+ }
+
+ diagram response_view "Operational work" id "a44d9d69-78a0-49de-8eb8-3757aa6c0919" { projection FunctionalDataflow; }
+ node detector_card id "45f016f4-3558-45b9-9559-da8ef9d5dcad" {
+ diagram @response_view; element @detect_alarm;
+ position { x 40; y 40; };
+ }
+ node assessor_card id "6ee7a0cc-013b-4a0f-8cff-513872e028b7" {
+ diagram @response_view; element @assess_alarm;
+ position { x 400; y 40; };
+ }
+}
diff --git a/harness/experiments/textual-authoring-20260911/probe-output.txt b/harness/experiments/textual-authoring-20260911/probe-output.txt
new file mode 100644
index 00000000..941a40fc
--- /dev/null
+++ b/harness/experiments/textual-authoring-20260911/probe-output.txt
@@ -0,0 +1,4 @@
+PASS: admission; stable and canonical round-trip; fresh/stable identity; raw alias rejection; command alias rewrite; nested reparent; cross-file move; source7 default probes
+objects=40 elements=25 relations=9 diagrams=1 artifacts=1 traces=1
+authored_bytes=4373 stable_bytes=6053 canonical_bytes=9710 authored_lines=110 stable_lines=110 canonical_lines=344
+warnings=[]
diff --git a/harness/experiments/textual-authoring-20260911/probe.rs b/harness/experiments/textual-authoring-20260911/probe.rs
new file mode 100644
index 00000000..3588e6b2
--- /dev/null
+++ b/harness/experiments/textual-authoring-20260911/probe.rs
@@ -0,0 +1,107 @@
+//! Isolated reader probe for root's build lease; no production dependencies added.
+//! Usage: probe.exe INPUT.arcadia OUTPUT_DIRECTORY
+use oa_core::{language, source_project::*, *};
+use std::{collections::BTreeMap, path::PathBuf};
+
+fn compile(bundle: &SourceBundle) -> CompiledSourceProject {
+ compile_bundle(bundle, ProjectLimits::default()).unwrap_or_else(|issues| {
+ panic!("source admission failed: {issues:#?}")
+ })
+}
+
+fn identity(project: &Project, alias: &str) -> Id {
+ project.identities().into_iter().find(|(_, name)| *name == alias).unwrap().0
+}
+
+fn main() {
+ let arguments: Vec<_> = std::env::args_os().collect();
+ assert_eq!(arguments.len(), 3, "expected input and isolated output directory");
+ let input = std::fs::read_to_string(&arguments[1]).unwrap();
+ let output = PathBuf::from(&arguments[2]);
+ std::fs::create_dir_all(&output).unwrap();
+ let bundle = SourceBundle {
+ entry: "project.arcadia".into(),
+ files: BTreeMap::from([("project.arcadia".into(), input.clone())]),
+ };
+ let compiled = compile(&bundle);
+ let project = &compiled.decoded.project;
+ assert_eq!(project.schema_version, 7);
+ assert_eq!(project.identities().len(), 40);
+ assert_eq!(compile(&compiled.bundle).decoded.project, *project);
+ assert_eq!(compile(&compiled.bundle).bundle, compiled.bundle);
+ assert_ne!(compile(&bundle).decoded.project.id, project.id);
+
+ let stable = &compiled.bundle.files["project.arcadia"];
+ let canonical = language::format(project);
+ assert_eq!(language::parse(&canonical).unwrap(), *project);
+ assert_eq!(language::format(&language::parse(&canonical).unwrap()), canonical);
+ assert_eq!(stable.matches(" id \"").count(), 40);
+ assert!(stable.contains("// Assessing the alarm is work decomposition"));
+
+ let assess = identity(project, "assess_alarm");
+ let mut header_only = compiled.bundle.clone();
+ header_only.files.get_mut("project.arcadia").unwrap().replace_range(
+ stable.find("oa_activity assess_alarm ").unwrap()
+ ..stable.find("oa_activity assess_alarm ").unwrap() + "oa_activity assess_alarm ".len(),
+ "oa_activity review_alarm ",
+ );
+ assert!(compile_bundle(&header_only, ProjectLimits::default()).is_err());
+
+ let alias_plan = prepare_command(project, &Command::SetAlias(assess, "review_alarm".into())).unwrap();
+ let aliased = update_bundle_with_identity_map(
+ &compiled, &alias_plan.project, &alias_plan.identity_map, ProjectLimits::default(),
+ ).unwrap();
+ assert_eq!(identity(&aliased.decoded.project, "review_alarm"), assess);
+ assert!(!aliased.bundle.files["project.arcadia"].contains("@assess_alarm"));
+ assert!(aliased.bundle.files["project.arcadia"].contains("// Assessing the alarm is work decomposition"));
+
+ let moved_plan = prepare_command(project, &Command::Reparent { element: assess, parent: None }).unwrap();
+ let moved = update_bundle_with_identity_map(
+ &compiled, &moved_plan.project, &moved_plan.identity_map, ProjectLimits::default(),
+ ).unwrap();
+ assert_eq!(moved.decoded.project.elements[&assess].parent, None);
+ assert!(moved.bundle.files["project.arcadia"].contains("// Assessing the alarm is work decomposition"));
+
+ let actor = identity(project, "plant_operator");
+ let location = &compiled.index.declarations[&actor];
+ let actor_text = &stable[location.span.start..location.span.end];
+ let mut split_entry = stable.replacen(actor_text, "", 1);
+ let closing = split_entry.rfind('}').unwrap();
+ split_entry.insert_str(closing, " include \"participants.arcadia\";\n");
+ let split = SourceBundle {
+ entry: "project.arcadia".into(),
+ files: BTreeMap::from([
+ ("project.arcadia".into(), split_entry),
+ ("participants.arcadia".into(), format!("{actor_text}\n")),
+ ]),
+ };
+ assert_eq!(compile(&split).decoded.project, *project);
+
+ let mut missing_viewpoint = compiled.bundle.clone();
+ missing_viewpoint.files.insert("project.arcadia".into(), stable.replace("viewpoint OperationalActivity;", ""));
+ let implicit = compile(&missing_viewpoint);
+ let scenario = identity(project, "notification");
+ assert!(matches!(implicit.decoded.project.elements[&scenario].body.shared(),
+ Some(SharedBody::Scenario { viewpoint: ScenarioViewpoint::MixedLegacy, .. })));
+
+ let mut reduced_message = compiled.bundle.clone();
+ reduced_message.files.insert("project.arcadia".into(), stable
+ .replace("message_kind Asynchronous; reply_to none;", "")
+ .replace("lifeline none;", ""));
+ assert_eq!(compile(&reduced_message).decoded.project, *project);
+
+ std::fs::write(output.join("stabilized.arcadia"), stable).unwrap();
+ std::fs::write(output.join("canonical.arcadia"), &canonical).unwrap();
+ std::fs::write(output.join("renamed.arcadia"), &aliased.bundle.files["project.arcadia"]).unwrap();
+ std::fs::write(output.join("reparented.arcadia"), &moved.bundle.files["project.arcadia"]).unwrap();
+ std::fs::write(output.join("split-entry.arcadia"), &split.files["project.arcadia"]).unwrap();
+ std::fs::write(output.join("participants.arcadia"), &split.files["participants.arcadia"]).unwrap();
+
+ println!("PASS: admission; stable and canonical round-trip; fresh/stable identity; raw alias rejection; command alias rewrite; nested reparent; cross-file move; source7 default probes");
+ println!("objects={} elements={} relations={} diagrams={} artifacts={} traces={}",
+ project.identities().len(), project.elements.len(), project.relations.len(),
+ project.diagrams.len(), project.artifact_links.len(), project.traces.len());
+ println!("authored_bytes={} stable_bytes={} canonical_bytes={} authored_lines={} stable_lines={} canonical_lines={}",
+ input.len(), stable.len(), canonical.len(), input.lines().count(), stable.lines().count(), canonical.lines().count());
+ println!("warnings={:#?}", compiled.diagnostics);
+}
diff --git a/harness/release-analysis-context-experiment-20260911.md b/harness/release-analysis-context-experiment-20260911.md
new file mode 100644
index 00000000..a40a9229
--- /dev/null
+++ b/harness/release-analysis-context-experiment-20260911.md
@@ -0,0 +1,94 @@
+# Executed comparison of baseline values and complete analysis cases
+
+Codex-956atf `/root/semantics_critic`, 2026-09-11. Root assigned this bounded follow-up to the paired semantic review. Read current Gitea issue #1 through #117; announced ownership in #120. Owned only `harness/experiments/analysis-context-20260911/` and this report. Existing proposals, review, production and ledgers were untouched. Root approved the isolated std-only direct-rustc command; no Cargo, `target`, native application or package changes occurred.
+
+## Result and recommendation
+
+**Prefer B for the next engineering-study prototype:** explicit baseline property values/formulas plus one level of named case overrides. In this controlled maintenance task it produces identical results with materially less repeated authored state. Preserve an explicit complete frozen snapshot operation for studies that must stop following the baseline. D remains useful as that independent snapshot form and for studies deliberately maintaining unrelated assumptions.
+
+This recommendation is based on executed semantic examples and text counts, not a measured human usability study or a final metamodel decision. A visible baseline, effective-case formula evaluation, distinguishable Missing/Clear operations, captured revision/provenance and complete freeze semantics are necessary conditions. Removing any of those to make B look simpler reintroduces concrete ambiguity demonstrated below.
+
+## Method
+
+The [standalone program](experiments/analysis-context-20260911/main.rs) generates and parses a small closed experiment dialect. B and D use the same property identities represented by stable symbols, same expression AST, pure function definitions, dependency checks and evaluator. B resolves an explicitly named baseline plus local overrides; D requires each selected case's complete bindings. There is no UI-selected or unnamed default case and no case-to-case inheritance. Both can evaluate unknown inputs without inventing zero.
+
+Twenty fixed inputs are each 1,000 ms. Cases 1–20 have two varying inputs:
+
+```text
+varying01 = 750 + 50 * (case_number - 1) ms
+varying02 = 1000 + 25 * (case_number - 1) ms
+total = sum(all 22 inputs)
+budget = total <= 22000 ms
+```
+
+The independent arithmetic expectation is `total = 21750 + 75 * (case_number - 1)` ms. Cases 1–4 satisfy the check; case 20 gives 23,175 ms and violates it. B's first case explicitly names the baseline and overrides nothing; the other 19 override the two varying inputs. D contains all 22 input bindings in every case. Both also have result and check bindings.
+
+Each context model is tested with inline expressions and with **shared pure reusable calculations**, so D is not penalized merely for repeating the threshold expression. Factored calls bind all arguments explicitly; definitions do not capture project properties. All accepted emitted source is parsed/validated and round-tripped to exact experiment AST equality. Deliberately invalid source is emitted separately as `*.rejected-study`.
+
+Changes are made programmatically to the experimental AST and emitted as complete text, then parsed again. The experiment counts literal changed source lines using longest common subsequence. It does **not** count those changes as human actions, claim the syntax is Studio's, or pretend a generated source edit is an observed engineering task.
+
+## Executed measurements
+
+Final [results](experiments/analysis-context-20260911/generated/RESULTS.md): **218 assertions passed, process exit 0**.
+
+| Variant | Initial lines | UTF-8 bytes | Binding rows | Change one shared fixed assumption: removed/added lines | Change threshold: removed/added lines |
+| --- | ---: | ---: | ---: | ---: | ---: |
+| B, inline | 106 | 2,281 | 62 | 3 / 3 | 3 / 3 |
+| B, factored | 108 | 2,781 | 62 | 3 / 3 | 2 / 2 |
+| D, inline | 522 | 13,241 | 480 | 21 / 21 | 21 / 21 |
+| D, factored | 524 | 14,026 | 480 | 21 / 21 | 2 / 2 |
+
+The fixed-assumption task changes `fixed01` from 1,000 to 1,200 ms across the active study. B edits one binding plus baseline/document revision lines. D edits twenty bindings plus its document revision line. Both correctly add 200 ms to every case and reduce the passing cases from four to one.
+
+For the threshold task, changing 22,000 to 22,500 ms yields eleven passing cases in every variant. B-inline changes one baseline predicate. D-inline changes twenty predicates. **Factoring the predicate removes that difference:** B-factored and D-factored each change one function body and one document revision. This falsifies a blanket claim that D necessarily repeats every formula edit. It does not remove D's repeated fixed input values; factoring also increases initial text because the calls and parameter lists remain explicit.
+
+Binding counts include the result and check. B has 24 baseline bindings and 38 override bindings; D has 20 × 24 = 480. The common property catalog is counted in emitted line/byte size. OA/SA subjects, labels, UUIDs and full type declarations are outside the experiment; these numbers cannot be substituted for complete product-source size.
+
+## Concrete semantic counterexamples
+
+| Operation or mistake | Executed B result | Executed D result | Implication |
+| --- | --- | --- | --- |
+| Change one fixed assumption for the whole active study | One baseline binding affects all twenty cases. | All twenty bindings must change; a negative control editing only the first changes only that case. | B reduces maintenance duplication. D's local isolation is useful when the cases intentionally represent independent assumptions. An atomic bulk editor could reduce D's actions, but not the duplicated source state. |
+| Evaluate an inherited formula in its definition context | Wrong baseline context gives 21,750 ms for case 20; correct effective-case context gives 23,175 ms. | Complete case expressions use their selected case's inputs directly. | B must resolve all property reads in the requested case, even when the formula originated in the baseline. “Inherit the already evaluated baseline result” is incorrect. |
+| Set the varying input override to `missing` | It shadows the known baseline input. Dependent total/check become Missing, with `varying01` as cause. | An explicit missing row also propagates Missing. | Missing is a real authored state, not an absent override or a false predicate. |
+| Remove that row | B clears the override and restores the baseline's 750 ms. Case 20 total becomes 22,225 ms. | A required missing binding row is a structural rejection. | Commands and text must distinguish Clear/Use baseline from Set missing. The same absent-map representation must not mean both. |
+| Clear an override whose baseline itself is missing | Result stays Missing; no fallback value is invented. | Not an inheritance operation. | B resolution terminates at one explicit baseline. It must not search another case or a payload default. |
+| Override `varying01 = total` | Effective case graph rejects a cycle, even though the baseline graph was valid. | The equivalent complete case also rejects. | B cannot validate only the baseline and override fragments independently. Dependency checks must see the fully resolved case. |
+| Point a case at another case | Rejects case-to-case inheritance. | Inheritance is forbidden. | This experiment's B benefit does not require a general inheritance graph. |
+| Copy a case and call it frozen | A shallow case copy still follows later baseline changes. | Inline D's complete copy remains independent; factored D still follows changed shared function bodies. | A freeze contract must capture the definition environment as well as values/formulas. D's complete input rows alone are insufficient once reusable definitions exist. |
+| Use a project property inside a supposedly pure function | Rejected. | Rejected. | Reuse does not secretly capture a current case, which would change the comparison. Recursive definitions also reject. |
+
+The program explicitly calculates both the correct effective-case result and the wrong baseline-context value as a negative control. It does not implement the wrong behavior as an alternative acceptable evaluator.
+
+## Frozen studies and provenance
+
+The experiment's freeze operation creates a complete independent D-style document containing the selected case's effective bindings, expression ASTs, the copied pure definition environment, captured input/formula row origins and original document/baseline revision identity. It keeps explicit Missing inputs. Changes to the original baseline and threshold functions leave the frozen observation unchanged. The frozen file parses and reproduces its exact experimental state without access to the original active study.
+
+See [frozen B case 20](experiments/analysis-context-20260911/generated/B-factored-frozen-case20.study) and [active B provenance](experiments/analysis-context-20260911/generated/B-factored-provenance.md). The latter identifies fixed inputs and result/check formulas as `baseline:engineering@1 via case20`, and varying inputs as `case:case20`. Frozen observations retain their captured origins with an explicit `frozen:` marker. All 24 effective bindings retain an origin.
+
+This is intentionally a larger snapshot than an active B case. Freezing removes future propagation by capturing actual dependencies; it cannot honestly be reduced to a copied case name or cached result. Both the fixed input and pure definition changes were applied after capture. Inline D already has isolated values/formulas, which is its clearest advantage; shared calculation definitions introduce the same capture obligation for D.
+
+The experiment's provenance strings are observations produced by the fixture/evaluator, not a secure import or history authority. Production provenance still needs stable typed identities, exact accepted document/source references and validated snapshot origin. The program does not test UUID renaming, package trust, artifact identity or historical remapping.
+
+## Remaining counterexamples and conditions on the recommendation
+
+1. **Propagation is a visible engineering decision.** In B, the source of an unchanged case can remain byte-identical while its result changes after a baseline edit. Before adopting B, the native/source workflows must expose the named base and show the affected cases or result revision. A cache keyed only by a case's own text would be invalid. The experiment supplies document/baseline provenance but does not test a real asynchronous UI cache.
+2. **Frozen evidence must capture external definitions.** The experiment copies its complete small function environment. Real units/calculations imported from packages need exact pins and retained bytes. A frozen binding graph referring to mutable ambient definitions remains live, whichever context model is chosen.
+3. **Thresholds can be factored in D.** The comparison supports B's reduction of repeated assumptions; it does not establish that D always repeats computation definitions. Avoid justifying B with that stronger, falsified claim.
+4. **Independent studies may prefer D.** If the twenty fixed assumptions are intentionally independent measurements rather than one shared design assumption, B's propagation is unwanted. Explicit independent/frozen studies cover that use case; silently turning every case into a live override would be misleading.
+5. **Actual authoring remains unmeasured.** A visible “Use baseline” control might be clearer than twenty copied values, or engineers may misunderstand inherited formulas. The next native/text task must measure wrong-context changes, recovery and comprehension. This experiment cannot provide human timings, UI clarity, large-project latency or accessibility evidence.
+6. **Metamodel integration is still open.** Subject ownership, OA/SA transitions, library pattern closure, check identity, quantity compatibility and source normalization require root's paired-contract decisions. The tiny interpreter is not a substitute for those contracts. Do not import it into production merely because its examples pass.
+
+The narrow conclusion is that **one-level explicit B is a stronger active-study candidate than compulsory complete-case D for shared-assumption maintenance**. A complete independent snapshot remains useful. This yields a concrete next prototype, not an argument for arbitrary inheritance or hidden defaults.
+
+## Exact execution evidence and reproduction
+
+- Compiler: `rustc 1.98.1 (48a229cea 2026-09-01)`, commit `48a229ceaefd4985c50990b14116b6d856af0985`, host `x86_64-pc-windows-gnullvm`, LLVM 22.1.8.
+- Compile: std-only `rustc --edition=2024 -D warnings -C linker=clang.exe`; no warnings or errors in the completed final build.
+- Final program SHA256: `5632ACAFD06413DCC9961DDC1ADE60C7556FB56B43ED3C667F5106E7559A3733`.
+- Executable: `.local/analysis-context-20260911/analysis-context.exe`; SHA256 `36E2D5A7F0683CC19F2EB50CA4840F3A793A8350498E3E17EDD75710A78FC675`.
+- Results SHA256: `248419751E9B836CDE6D80F97202843DD5DC085D1BEC67CB2FCC5046960C143B`.
+- Final process exit: **0**; **218 assertions passed**. Each assertion count is an experiment check, not a count of independent production tests.
+- [Evidence manifest](experiments/analysis-context-20260911/generated/EVIDENCE.json) records all 29 emitted source fixture hashes, commands and UTC recording time. [README](experiments/analysis-context-20260911/README.md) gives the complete repeatable commands.
+
+All runtime outputs are confined to the owned experiment directory, with the executable isolated under `.local`. Existing Studio tests and release acceptance were not rerun or claimed by this task. Root retains the final semantic choice and implementation ownership.
diff --git a/harness/release-analysis-semantics-design-20260910.md b/harness/release-analysis-semantics-design-20260910.md
new file mode 100644
index 00000000..77b0eada
--- /dev/null
+++ b/harness/release-analysis-semantics-design-20260910.md
@@ -0,0 +1,366 @@
+# Analysis semantics: competing designs and a bounded proposed contract
+
+Codex-956atf `/root/requirements_delta`, 2026-09-10, finalized 2026-09-11. Design proposal for root synthesis and independent criticism, **not a ratified decision, implementation, or release acceptance**. Sole output of this assignment is this file. No production edits, Cargo or native execution. Read Gitea coordination through #81 before starting and through #101 on resuming; announced this work in #83. Coordinated neutral-definition identity and immutable import assumptions with `/root/libraries_design`; incorporated the paired critic's numeric serialization counterexample and library compatibility concern. Root retains build/native ownership.
+
+The authoritative user objective and the delta evidence are recorded in `PROJECT_GOAL.md` and `release-requirements-delta-20260910.md`. This report examines the actual core at `8547b85` and its unchanged analysis-relevant predecessors. The current Constraint/MessageValue expressions remain prose; DataType/DataField units remain strings. This proposal must not reinterpret those existing records during migration.
+
+On resuming, read the full replacement objective at `C:/Users/User/.codex/attachments/a49d1780-25d5-4778-ad68-6140c60e4207/goal-objective.md` (SHA-256 `DD58F75C93D55FE16ED141A23D834898FB6EBC625AE7E7AE379DA552A8312435`). Its explicit requirements for low-clutter notation, reduced cognitive load, human/AI editing and complete small/large model development through text strengthen the authoring gates in sections 3.4 and 7. This design update is not a fresh production audit of the concurrently changing checkout.
+
+## 1. The decision and its evidence
+
+The key question is whether a numerical value belongs directly to a modeled subject, to one explicit analysis case, or to a subject declaration plus case-specific assignment. A second question is how much mathematics the first release must execute. Neither question is settled by having a text field called `expression`.
+
+Capella's official material identifies properties, expressions and dimensions and describes analysis through viewpoints. Its mass/cost/latency examples support useful architectural analysis without requiring behavioral simulation. [Capella comparison](https://mbse-capella.org/arcadia_capella_sysml_tool.html#parametric), [Capella features](https://mbse-capella.org/features.html)
+
+SysML 2.0 separates calculation definitions/applications, Boolean constraints and quantity/unit concepts. Its calculation semantics include pure reusable computations; its quantity treatment distinguishes a quantity kind from a particular unit. Those distinctions inform this proposal; they do not make the proposed Studio language SysML-conformant. [OMG language specification, §§7.8, 7.19, 7.20 and 9.8](https://www.omg.org/spec/SysML/2.0/Language/PDF)
+
+For the temperature falsifier below, BIPM defines the Celsius/Kelvin numerical relationship with the exact offset 273.15 and distinguishes temperature values from intervals. The proposed affine algebra is a Studio design interpretation of that distinction. [BIPM SI Brochure, ninth edition, temperature discussion](https://www.bipm.org/documents/20126/41483022/SI-Brochure-9-EN.pdf/2d2b50bf-f2b4-9661-f402-5f9d66e4b507?version=4.0)
+
+The implementation constraints are concrete:
+
+- `model.rs:603–617,651` contains current unit/expression metadata; `parent_permits` (`validation.rs:92`) permits DataFields only in data/item structures. A payload type default must not become a system property value accidentally.
+- `Project::layer_of` follows an Element's layer or a Relation's source (`model.rs:1803`). There is no neutral modeling layer. New reusable definitions must not masquerade as OA or relax all existing same-layer checks.
+- `prepare_command` clones, applies a complete batch, finishes deletion handling, validates, then returns the candidate (`session.rs:185`). This is the mutation boundary to retain.
+- `visit_element_refs` currently visits live UUID references; expression text has no visited references (`references.rs:17–152`). Actual expression references require a typed visitor, not text search.
+- Transition closure follows references and selected ownership (`transitions.rs:447–606`); `inferred_kind` has a closed catalog. New property/case/check semantics will not transition automatically merely because they use SharedBody.
+- Historical review is a closed typed vocabulary (`semantic/types.rs:88–155`). New live references and historical observations require different validation rules, as in existing source5–7 history.
+
+Paths above are under `crates/oa-core/src`.
+
+## 2. Alternatives and provisional ranking
+
+Ratings below are engineering judgments to be falsified by examples and native tasks, not measurements.
+
+| Architecture | Simple property/budget | Alternative assumptions | Meaning and automation | Main cost |
+| --- | --- | --- | --- | --- |
+| A. Directly owned value/expression; no cases | Best initial simplicity. One property contains its value or derived expression. | Requires editing the one model state or copying declarations/projects. | Clear single-state meaning; weak support for simultaneous alternatives. | Adding cases later may reinterpret every property read. |
+| B. Directly owned baseline value plus explicit override cases | Good initial simplicity; ordinary properties work without a case. | Case chooses a base and stores local overrides. Unchanged properties follow the base. | Viable if every resolved input reports its origin and inheritance rules are explicit. | Two value locations, inherited-change effects, override clearing versus unbound values, and derived-value lookup across scopes. An explicit `UseBase(property)` binding avoids implicit fallback but adds records/actions. |
+| C. Case-owned values with optional subject annotations; no reusable property declarations | A case can be a compact typed worksheet. | Each case owns independent columns/values. | All inputs are explicit and isolated. | “The same system property” in two cases has no stable shared identity without another correspondence mechanism. |
+| D. Subject property declarations plus complete explicit case bindings | One extra named case concept; no value is stored on the property declaration. Inline formulas suffice for small tasks. | The same declaration can have different, visibly separate bindings in nominal/degraded cases. No inheritance or fallback. | Stable property identity, explicit assumptions, one value location per case/property, simple pure evaluation. | More semantic records and source declarations; UI must make the common task one coherent action instead of several catalog detours. |
+
+**Provisional preference: D**, because stable property identity and explicit assumptions support engineering studies and cross-project reuse without implementing override inheritance. This preference is conditional on the low-overhead workflows below. B is a credible competitor, not a straw man. If native comparison shows that engineers mostly enter context-independent facts and D forces repeated case creation or input copying, prefer B with explicit base provenance and a precisely defined override contract, or revisit a visible `UseBase` form. Do not add an invisible “default case” to rescue D; that would change the chosen semantics.
+
+Reusable calculation definitions are orthogonal to A–D. A small budget must support an inline expression. Requiring a reusable function definition, parameter list and application before adding two durations would be unnecessary authoring overhead. “Make reusable” can be a later explicit transformation of a selected expression.
+
+### Numerical alternatives
+
+| Domain | Exact decimal budgets and large integers | Degrees/radians and ordinary functions | Reproducibility / cost | Disposition |
+| --- | --- | --- | --- | --- |
+| Q. Bounded exact rational scalar algebra | Exact within stated size limits; `0.1 + 0.2 == 0.3` and rational divisions are unambiguous. | Cannot generally evaluate π-based conversion, sine, cosine, logarithms or irrational roots. Must report Unsupported. | Small, precise arithmetic contract; bounded big-integer implementation required. | Good **first implementation slice**, insufficient evidence for broad common-unit/calculation usability. |
+| F. Finite binary64 throughout | Decimal rounding and large-integer loss require care. Existing i64 rules must stay independent. | Familiar engineering functions and approximate angular conversion are practical. | Must fix conversion, rounding, math function behavior and comparison policy; CPU/library differences need measured bounds. | Plausible simpler release choice if ordinary numerical breadth dominates exactness. |
+| Q+F. Explicit exact and approximate domains | Exact calculations stay exact; approximation occurs at a visible domain boundary. | An implemented approximate domain handles angles and transcendental functions with explicit result labeling/policy. | More type/evaluation rules and two conformance suites. No implicit approximate-to-exact conversion. | Preferred **target for further release design**, conditional on a justified, independently verified approximate profile. |
+
+This report formalizes Q as a bounded foundation. It does **not** claim that Q alone satisfies the user's complete calculations/quantities requirement. The approximate profile remains a consequential design/acceptance item, not a harmless future convenience. In particular, an everyday `30 deg → rad` or `sin(30 deg)` request must either run under an implemented approximate domain or receive a precise Unsupported result. A decimal constant pretending to be exact π is unacceptable.
+
+Before implementing F, compare a portable pinned Rust math implementation against platform intrinsics using reference values and documented error limits. Select rounding, supported function domains, signed-zero behavior, overflow/underflow and comparison semantics explicitly. Approximation labeling is not an error bound; a user-supplied engineering tolerance is not automatically a proof of numerical accuracy. Do not implement approximate equality by an undocumented global epsilon. That work is outside this exact first slice and must remain open in the release ledger.
+
+## 3. Proposed model and low-overhead authoring
+
+### 3.1 Meanings
+
+An **EngineeringProperty** declares what scalar or finite scalar collection is associated with an existing semantic subject and its type. It stores no value/default. Its subject is an existing Element or Relation in the same layer, excluding analysis records themselves. Using an explicit typed subject reference supports an exchange's latency as well as a function's duration without pretending a Relation can be an Element parent.
+
+An **AnalysisCase** names one set of input assumptions and derived bindings in one layer. It maps property identity to an expression or an explicit unbound value. A property read always means that property's binding in the named case. There is no lookup in another case, payload default, selected diagram, active UI state or external file. Case subject is optional explanatory focus, not an implicit namespace or lookup scope.
+
+An **AnalysisCheck** is an identified, case-owned Boolean predicate. It may link an existing descriptive Constraint as its documented requirement. The old Constraint's prose remains prose; the new check supplies the formal assertion in an explicit case. Several checks/cases may reference the same documented Constraint. A check without that link still expresses a formal engineering constraint.
+
+A neutral **CalculationDefinition** has stable typed parameters, a declared result and an optional pure expression body. A Boolean-valued reusable calculation can supply a predicate to a check. Definitions may call other definitions but cannot capture project properties, cases, UI selection, files, clocks or mutable external state.
+
+### 3.2 Concrete Rust sketch
+
+The names below are proposed, not existing public APIs. Closed Serde records and duplicate-key rejection follow current conventions.
+
+```rust
+// Proposal: three new Kind/SharedBody variants, using existing Element identity.
+enum AnalysisSubject { Element(Id), Relation(Id) }
+
+enum ScalarType {
+ Boolean,
+ Number, // Q1: exact rational
+ Quantity(DefinitionRef), // must resolve to a QuantityKind
+}
+enum AnalysisType { Scalar(ScalarType), List(ScalarType) } // no nested lists
+
+// SharedBody additions:
+EngineeringProperty {
+ subject: AnalysisSubject,
+ value_type: AnalysisType,
+ display_unit: Option, // compatible quantity kind; no value/default
+},
+AnalysisCase {
+ subject: Option,
+ bindings: BTreeMap>, // key is EngineeringProperty identity
+},
+AnalysisCheck { documented_constraint: Option, predicate: Option },
+
+// Project gets a separate neutral definition catalog, coordinated with libraries.
+enum DefinitionRef {
+ Local(Id),
+ Imported { import: Id, package: PackageId, member: Id },
+}
+struct Parameter { id: ParameterId, name: String, value_type: AnalysisType }
+struct CalculationDefinition {
+ parameters: Vec,
+ result: AnalysisType,
+ body: Option,
+}
+
+struct Exact { numerator: CanonicalInteger, denominator: CanonicalPositiveInteger }
+// Wire representation is canonical decimal strings, not JSON floating numbers.
+struct Dimension([i16; 7]); // length, mass, time, current, temperature, amount, luminous intensity
+enum QuantityAlgebra { Linear, Affine { difference_kind: DefinitionRef } }
+struct QuantityKindDefinition { dimension: Dimension, algebra: QuantityAlgebra }
+struct UnitDefinition {
+ kind: DefinitionRef,
+ scale: Exact, // strictly positive in Q1
+ offset: Exact, // zero for a Linear kind
+ symbol: String, // presentation; never identity
+}
+
+enum Expr {
+ Boolean(bool), Number(Exact),
+ Quantity { magnitude: Exact, unit: DefinitionRef },
+ Property(Id), Parameter(ParameterId), List(Vec),
+ Unary { op: UnaryOp, operand: Box },
+ Binary { op: BinaryOp, left: Box, right: Box },
+ If { condition: Box, when_true: Box, when_false: Box },
+ Call { definition: DefinitionRef, arguments: BTreeMap },
+ Sum { values: Box, initial: Box },
+ Power { base: Box, exponent: i8 },
+ AsKind { kind: DefinitionRef, value: Box },
+}
+```
+
+EngineeringProperty and AnalysisCase are layer-root Elements. AnalysisCheck requires parent AnalysisCase. The subject relationship is explicit reference association, not an extra containment rule. Property ownership/attachment is therefore visible in the Properties/Analysis presentation even though it is not an Element child of its subject. If user testing instead requires the property to participate in semantic containment, revisit this choice explicitly; do not give `parent` two meanings.
+
+`display_unit` is permitted only for a quantity scalar or quantity list and must have the property's resolved kind. It chooses result presentation, not input interpretation or a default value. A source literal retains its own magnitude/unit. Without a requested display unit, the wire result remains a canonical coordinate plus resolved kind; the UI must not invent a unit symbol. The Add value/budget forms can explicitly set this field from the visible unit choice.
+
+Parameter IDs are scoped to the defining CalculationDefinition and remain stable through rename. They are not ordinary project Element references. A call's argument keys must exactly match the definition's parameter IDs. Wrong/missing/duplicate argument keys reject; an input whose value is not known is represented by a case binding set to `None`, not an invalid argument map. Empty calculation bodies/check predicates are admitted incomplete records with warnings.
+
+### 3.3 Minimum GUI and source work
+
+For one new property/value, the proposed minimum GUI operation is: select the modeled subject, choose **Add analysis value**, enter name/value/unit, and choose or visibly name the case. One final Create applies a batch creating the property, a new named case when needed, and its binding. The form states the case name and resulting records. Creating “Nominal” is explicit model creation, not implicit fallback. Existing cases are chosen by context, with alternatives visible. No separate global-catalog visit is required.
+
+For a two-duration budget, one compact **New budget** form can show case name, two named input rows with subjects/units, the result expression and threshold. One Create reserves all new IDs and applies one reviewed batch: three properties, one case with three bindings and one check. A reusable definition is not mandatory. Subsequent editing shows nominal and alternative case values side by side or under an explicit case selector; the result always bears its case name. Outside an analysis workspace, a property inspector lists its case values with names rather than silently displaying one “current” value.
+
+Local neutral definitions can make the source self-contained; library imports may supply the same definitions instead. Proposed syntax, not accepted by source7:
+
+```text
+quantity_kind duration "Duration" {
+ dimension [0, 0, 1, 0, 0, 0, 0]; algebra Linear;
+}
+unit s "Second" { kind @duration; scale 1; offset 0; symbol "s"; }
+unit ms "Millisecond" { kind @duration; scale 0.001; offset 0; symbol "ms"; }
+```
+
+The source cost for one property/value is two declarations, using those unit definitions:
+
+```text
+oa_property receive_time "Receive time" { subject @receive; type Quantity(@duration); display_unit @ms; }
+oa_analysis nominal "Nominal" { bindings { @receive_time = 750 [@ms]; } }
+```
+
+The two-case budget can remain concise without inventing a reusable function:
+
+```text
+oa_property receive_time "Receive time" { subject @receive; type Quantity(@duration); }
+oa_property dispatch_time "Dispatch time" { subject @dispatch; type Quantity(@duration); }
+oa_property total_time "Total time" { subject @respond; type Quantity(@duration); display_unit @s; }
+oa_analysis nominal "Nominal" {
+ bindings {
+ @receive_time = 750 [@ms];
+ @dispatch_time = 1 [@s];
+ @total_time = @receive_time + @dispatch_time;
+ }
+}
+oa_analysis degraded "Degraded radio" {
+ bindings {
+ @receive_time = 1250 [@ms];
+ @dispatch_time = 1 [@s];
+ @total_time = @receive_time + @dispatch_time;
+ }
+}
+oa_check nominal_budget "Nominal budget" {
+ parent @nominal; predicate @total_time <= 2 [@s];
+}
+oa_check degraded_budget "Degraded budget" {
+ parent @degraded; predicate @total_time <= 2 [@s];
+}
+```
+
+Optional explicit reuse adds a neutral definition and changes a binding expression:
+
+```text
+calculation combine "Combine durations" {
+ input a : Quantity(@duration);
+ input b : Quantity(@duration);
+ result Quantity(@duration);
+ expression $a + $b;
+}
+// In one case: @total_time = @combine(a: @receive_time, b: @dispatch_time);
+```
+
+Ordinary declaration UUID stabilization follows the existing source identity rule. The proposed authoring form extends optional initial IDs to calculation parameters: accepting a new parameter allocates its ID once, and the accepted source retains it, for example `input a id "10000000-0000-4000-8000-000000000001" : Quantity(@duration);`. This extension is a proposal, not existing source7 behavior. The short examples show initial authored text, not the more verbose stabilized file. Retaining established IDs is required for rename and history; deleting an ID is not a rename mechanism. Stabilization must be available through the shared source admission service without creating records in a GUI. Missing-ID diagnostics and the inserted IDs must be reviewable by a text-only client.
+
+`$name` resolves only a parameter of the current definition. `@name` in value position resolves a case property; `@name(...)` resolves a calculation; `[@name]` resolves a unit. Imported qualified spelling is delegated to the library grammar. Duplicate binding keys reject. Names are parsed to stable IDs, and an accepted rename updates the relevant tokens. `none` is permitted as an entire binding/body/predicate hole, never as an arithmetic scalar. Parentheses, unary operators, multiply/divide, add/subtract, comparisons, `&&`, `||`, and conditional expressions require explicit precedence in the normative extension; comparisons are non-associative, so chained comparisons reject. Quoted prose is never implicitly parsed as this expression language.
+
+To test D against B, compare first-property creation, a two-input budget and a nominal/degraded change in both a small and a dense model. Proposed acceptance: D needs no additional page change and at most one additional context-choice action for the first case; editing an existing case needs no extra case-creation action. If D repeatedly requires recreating declarations or reentering unchanged inputs where B would show clear inheritance, its claimed simplicity is falsified. Record task completion/time and wrong-context errors, not just click counts.
+
+### 3.4 Text-only development and cognitive load
+
+The source examples are a minimum analysis fragment assuming existing subjects `receive`, `dispatch` and `respond`; they do not prove that an entire model can be authored through text. The acceptance fixture must include those OA/SA subjects and all other referenced records, local definitions or explicit supplied package imports, with no prerequisite GUI-created records. Adding a case, defining/reusing a calculation, declaring a quantity/unit, inspecting a missing input and correcting a constraint must all be possible from the published language and API alone.
+
+| Text-authoring concern | Proposed treatment and falsification gate |
+| --- | --- |
+| First useful calculation | Inline arithmetic and local definitions are sufficient; a reusable definition or library publication is optional. An engineer can create the one-value and two-duration examples from a blank source bundle using documented admission, with no form-only operation. |
+| Identity noise | Initial authoring may omit newly allocated IDs; references use readable aliases. Stable IDs remain in accepted declarations and must round-trip. Evaluate both initial and stabilized files; omitting UUIDs from this report is not evidence that the real files are uncluttered. Folding is supplementary, not a requirement for understanding an ordinary editor's text. |
+| Hidden context | Every binding has one explicit case. Property names, unit references and case blocks carry meaning; they must not be removed merely to shorten text. A property read cannot fall back to another case or UI selection. A diagnostics client can locate the case, property and expression token responsible. |
+| Repetition across cases | D repeats unchanged inputs and derived expressions. With 1,000 applicable properties and 20 complete cases, that can mean 20,000 binding rows. This is a material design cost, not solved by a search box or collapsed list. Compare B's baseline plus explicit overrides using the same task and provenance display; reverse D's preference if repetition dominates comprehension or causes divergent copied formulas. |
+| Locality and file organization | Existing explicit source bundles/includes should let authors group properties/cases/definitions into files. Analysis extensions must retain exact authored bytes and targeted token edits, with stable references after a declaration moves between supplied files. Splitting files alone does not solve the current global alias namespace's cognitive cost; scoped naming would need a separate deliberate language decision. |
+| Reuse without expanded clutter | Local calculations and pinned imported definitions need one documented reference form each, with no duplicated editable expansion of sealed definitions. Package transport and import spelling remain shared library-design obligations. A text-only engineer must be able to supply the declared package bytes and pins, not rely on an invisible desktop cache. |
+| Meaningful notation | The seven-entry dimension vector is compact but opaque without its fixed order. Compare it with named nonzero dimensions such as `dimension { time: 1; }` before freezing syntax. The latter is an alternative spelling proposal, not a second admitted grammar. Similarly, repeated `bindings` wrappers and prefix-heavy declarations warrant user comparison; do not add shorthand whose case or type resolution becomes implicit. |
+| Human and AI edits | The normative extension must define precedence, name resolution, ID retention, canonical exact literals and located diagnostics, and provide complete examples. A client following that specification must predict admission and evaluation without private Rust structures or GUI state. A one-input edit should leave unrelated source bytes and accepted identities unchanged. |
+
+Use two authoring evaluations after the semantic choice: a complete small OA/SA example and a generated dense source bundle containing at least 1,000 properties, 20 cases and 100 reusable definitions. Those are proposed stress fixtures, not claims about current capacity. Record source size, changed lines for one assumption, task completion, wrong-case edits, diagnostic locality, and parse/apply/evaluate latency under stated hardware. Include moving a definition between files, a parameter rename retaining identity, adding an alternative, a deliberately invalid unit change followed by repair, and portable save/reopen. Compare D with B before treating D's source ergonomics as settled. A larger standalone textual workflow audit is outside this assignment.
+
+## 4. Bounded Q1 semantics
+
+### 4.1 Admission and expression typing
+
+An accepted case may contain explicit unbound inputs, empty definition bodies and empty checks with completeness warnings. Every live property/unit/kind/calculation/constraint/subject reference must resolve to the correct kind. Every property reference reachable from a case expression must have a binding row in that case; its row may be `None`. Missing rows are a structural error, which a graphical planner can resolve by explicitly adding visible unbound input rows in the same batch. Unknown identities, wrong types, recursive definitions and cyclic case-property dependencies reject the whole candidate. A definition may use parameters and other definitions, never `Property` nodes.
+
+Calculation call graphs and case dependency graphs must be acyclic even if a syntactic branch is currently unused. Both branches/operands are statically type checked. Dynamic `if`, `&&` and `||` use ordinary left-to-right conditional evaluation: only a known condition/left Boolean can select or short-circuit. A missing/error condition propagates; it is not converted to false. Thus `true || unknown` is true because the unknown branch is unnecessary, while `unknown || true` remains unresolved under this explicitly ordered evaluation contract. Reports identify the dependencies actually consulted. That distinction needs source/UI guidance and a falsifier test.
+
+Lists are finite, flat and homogeneous. Empty list typing comes from a declared target/parameter type. Sum takes a homogeneous numeric or linear-quantity list and an explicit typed initial value; empty sum returns that initial value. There is no implicit “sum all children,” open-ended query or dynamic collection discovery. Property aggregation over architecture topology is a separate future design, not an assumed behavior of `sum`.
+
+### 4.2 Exact numbers
+
+Q1 represents a rational as normalized integers `n/d`, `d > 0`, gcd 1, with zero represented only as `0/1`. Canonical wire integers have no plus sign or leading zeroes. Decimal source literals are converted arithmetically to rationals; they never pass through f64. The canonical writer can always emit an integer when `d == 1` or `ratio(n, d)` otherwise; `ratio` is a literal constructor, and division is a separate expression operation. Authored decimal spelling is preserved by ordinary source editing. A terminating decimal may be longer than the allowed literal token, so canonical serialization must not insist on decimal expansion. Noncanonical wire rationals reject instead of being silently rounded or ambiguously compared.
+
+Proposed reference profile: at most 4,096 bits for each reduced numerator/denominator, 1,234 decimal digits in a numeric token's mantissa or canonical ratio component, absolute decimal exponent at most 128, expression depth at most 128, at most 65,536 expression nodes per case/definition closure, list length at most 1,024 and call depth at most 32. Signs and punctuation are not decimal digits. The actual 4,096-bit bound still applies; not every 1,234-digit integer fits. Integer powers are limited to -16 through 16. These are reviewable engineering limits, not measured performance claims. Operations use bounded intermediates, normalize and then enforce the stated result bound; inputs and temporary allocation are bounded before multiplication/division. Crossing a computational bound reports NumericLimit, never a truncated value.
+
+Source, wire and model admission must share these value limits. The earlier 256-digit source proposal was inconsistent with a 4,096-bit admitted literal and is rejected: an accepted `Expr::Number` containing `10^300` must serialize back to the same literal AST. The integer/ratio fallback avoids the exponent bound and also handles, for example, `1 / 2^4095` without emitting 4,095 fractional digits. Exact values at both 4,096-bit boundaries must survive model-to-source-to-model and JSON round-trips; a formatter may not rewrite a literal into an arithmetic AST to evade its literal limits.
+
+Addition/subtraction/multiplication/division and bounded integer powers are exact. Division by zero and zero to a negative power fail evaluation. `0^0` is explicitly rejected in Q1. Comparisons use rational arithmetic; no global epsilon exists. Existing DataValue::Integer remains i64 and retains its current precision/boundary rules. There is no automatic binding to payload type defaults and no implicit conversion of an analysis result into DataValue; a future adapter must check integrality/range/type exactly.
+
+Unary minus/absolute value accept Number or linear quantity; logical negation accepts Boolean. Arithmetic and ordering reject Booleans/lists. Equality/inequality accepts matching Boolean types or compatible numerical scalar types; list equality is outside Q1. Number plus quantity and implicit conversion from a named dimensionless kind to Number reject. Quantity divided by Number preserves the quantity kind; Number divided by a linear quantity yields an anonymous inverse dimension. A quantity power of one preserves its kind, power zero gives Number one when defined, and other powers yield anonymous dimensions. These typing rules apply before evaluation; their numeric zero-domain failures are evaluated separately.
+
+### 4.3 Quantities and units
+
+Quantity kind identity is the resolved definition identity, not its name, symbol or seven-dimensional vector. Two different kinds may share a dimension and still be incompatible. A unit's declared kind must match the value's type. In Q1, canonical coordinate is `magnitude * scale + offset`. Linear units require zero offset. Affine units belong to an affine kind with a declared linear difference kind of the same dimension; both coordinates use the same coherent scale basis. Type checking verifies dimension components and arithmetic exponent bounds.
+
+| Operation | Q1 rule |
+| --- | --- |
+| Linear add/subtract/compare | Same resolved kind required; convert coordinates exactly. |
+| Affine point minus point | Same affine kind; result has its declared linear difference kind. |
+| Point plus/minus interval | Interval must have that affine kind's difference kind; result is a point. Interval plus point is allowed. |
+| Point plus point, interval minus point, point multiply/divide/power | Reject as an invalid operation. Comparisons between points of the same affine kind are allowed. |
+| Number scaling a linear quantity | Preserve kind, with exact scalar scaling. |
+| Product/quotient of different linear quantities | Produce an anonymous derived dimension. Do not guess a named physical kind. |
+| Linear quantity divided by the same kind | Produce an exact dimensionless Number. |
+| `AsKind(kind, value)` | Only an anonymous derived result with matching dimension can acquire the explicitly chosen linear kind. It cannot silently convert an already named kind into another named kind. |
+| Conversion between units | Same resolved kind; exact scale/offset rules. Changing display unit does not mutate the authored literal or its magnitude. |
+
+`Dimension` exponents are ordered exactly as in the Rust sketch and each admitted component is bounded to -32 through 32. An anonymous derived quantity is an intermediate result, not a declared property/parameter type; assigning it to a named result requires explicit AsKind. This is deliberately more explicit than using dimensional equality to label force-times-distance automatically as either energy or torque. Revisit this authoring burden if a catalog of unambiguous registered kind operations can be independently specified and justified.
+
+Q1 does not admit logarithmic units or nonrational scale factors. A prospective π-based UnitDefinition must not be represented by a rational approximation with an “exact” label. An approximate-domain extension needs a distinct scale/expression representation and a corresponding profile; its source/JSON form and numerical guarantees remain an open design item. Until then degree/radian conversion and transcendental functions receive Unsupported in the capability/workflow layer; unknown syntax in strict source remains a located rejection. This limit is material to release scope.
+
+### 4.4 Outcomes and revision identity
+
+Admission and evaluation are different operations. An unmet engineering requirement does not make a structurally valid model unsavable.
+
+```rust
+enum ValueOutcome {
+ Value(AnalysisValue),
+ Missing { inputs: Vec },
+ Unsupported { operation: String, required_domain: String },
+ Failed { diagnostics: Vec },
+}
+enum CheckOutcome {
+ Satisfied,
+ Violated,
+ Unresolved { causes: Vec },
+}
+struct EvaluationReport {
+ case: Id,
+ model_snapshot: SnapshotIdentity,
+ definition_environment: EnvironmentDigest,
+ numeric_profile: ProfileId,
+ values: BTreeMap,
+ checks: BTreeMap,
+}
+```
+
+Public `AnalysisValue` is a Boolean, an Exact Number, a quantity `{resolved_kind, canonical_coordinate: Exact}`, or a homogeneous flat list carrying its resolved element type. Anonymous derived dimensions are internal intermediates only: a declared result/property must acquire its explicit kind before it can produce a public value. Quantity display conversions accompany that result as derived presentation and never replace the authored literal. JSON represents every Exact numerator/denominator as canonical decimal strings, including converted display values.
+
+Cause collections are nonempty for every nonvalue outcome, deduplicated and stably ordered. Missing includes absent input values or admitted empty definition/check bodies. Unsupported is distinct from a failed supported operation such as division by zero. A check returns Satisfied/Violated only for an evaluated true/false Boolean; the other value outcomes become Unresolved with their original classified causes and dependency path. No-check cases report “No checks,” not vacuous whole-model success. A case summary of passed checks is not a claim that every unrelated input is populated.
+
+Q1's admitted AST contains only its supported operations. Consequently an unknown function is a source/admission error, not an accepted Q1 node fabricated solely to produce `ValueOutcome::Unsupported`. In the first slice, Unsupported belongs to capability or explicit conversion/evaluation requests outside Q1; the value outcome variant is reserved for a future multi-profile contract that can admit the requested operation. That extension must define when admission versus evaluation reports unsupported capability. Neither path implements the missing mathematics.
+
+The core evaluator consumes an immutable model plus fully resolved immutable definition environment and explicit case ID. It performs no command, history operation, filesystem/network access or source rewrite. Dependency plans and results are derived data. A client supplies its captured document/server revision stamp; server code takes that stamp from the captured stored envelope, never from an untrusted claim in a request. Results retain the full environment digest and numerical profile. A result can describe only that snapshot. Async desktop results for superseded snapshots must be discarded or visibly marked stale. An accepted source/comment-only revision still gets the current document/service stamp even if numerical values are identical.
+
+Proposed UI behavior: Evaluate never applies another pending editor. Either evaluate the clearly labeled accepted snapshot or require explicit Apply/Cancel of the analysis draft; select one behavior and test it. API evaluation likewise cannot write model state. Proposed route shape can follow current validation endpoints, returning the captured ETag/revision and structured report; exact route naming belongs to the shared API design.
+
+## 5. Falsifying worked examples
+
+| Example | Expected result; what it can falsify |
+| --- | --- |
+| Nominal/degraded duration | Nominal `750 ms + 1 s = 7/4 s`, check `<= 2 s` is Satisfied. Degraded uses the **same property IDs**, binds `1250 ms + 1 s = 9/4 s`, and is Violated. Evaluating either cannot change or read the other's bindings. |
+| Decimal and fractional arithmetic | `0.1 + 0.2 == 0.3` is true; `ratio(1,3) + ratio(1,6) = ratio(1,2)`. Normalized results and exact wire strings are independently predicted, without consulting the evaluator. |
+| Literal serialization closure | An admitted exact literal `10^300`, maximum permitted 4,096-bit numerator, and `ratio(1, 2^4095)` with the denominator expanded as its integer digits all survive canonical source and JSON round-trips with the same literal AST. Reject an actual 4,097-bit component. A long terminating decimal must use the bounded ratio spelling when needed. |
+| Existing large integer | Importing an i64 maximum as an explicitly exact analysis literal preserves 9223372036854775807. Adding one produces the exact rational integer 9223372036854775808, but conversion back into DataValue::Integer must reject. Existing i64 admission must not be changed. |
+| Unit mismatch | `1000 ms == 1 s` is true. `1 s + 1 kg` rejects type checking. A free legacy unit string `"s"` creates no typed unit identity or conversion. |
+| Equal dimension, different kind | Comparing a Torque value with Energy rejects even though dimensions agree. `AsKind(Energy, force * length)` is an explicit dimension-checked assignment; directly `AsKind(Energy, torque)` rejects. |
+| Affine temperature | With explicit point/difference kinds and rational units: 20 Celsius-point converts to 293.15 Kelvin-point; 30 Celsius-point minus 20 Celsius-point gives 10 Kelvin-interval; adding that interval to 20 Celsius-point gives 30 Celsius-point. Adding two temperature points rejects. |
+| Missing/error/false | A declared duration input bound to `none` makes its dependent budget Unresolved/Missing. A known denominator zero gives Unresolved/Failed(DivisionByZero). A computed 9/4 s against 2 s is Violated. None may be displayed as a successful structural validation or the others' outcome. |
+| Everyday nonrational math | `30 deg → rad` and `sin(30 deg)` are Unsupported under Q1, not an “exact” decimal π conversion. A later approximate profile must independently demonstrate results near π/6 and 1/2 with its promised numerical guarantees. This is an open release gate. |
+| Identity and purity | Rename a calculation parameter and its unit alias: stable parameter/definition identity and results remain. A calculation definition containing a project Property reference rejects. A recursive call rejects, including inside an unused branch. |
+| Conditional evaluation | A well-typed `true || missing_boolean` is Satisfied without consulting the right operand; `missing_boolean || true` is Unresolved. An invalid unit/type expression on either side still rejects static validation. |
+| Library revisions | Two cosmetic import aliases resolving to the same digest/member/dependency closure yield the same definition identity. A changed pinned revision is not automatically the same quantity type because a member name/UUID survived. Rebinding is explicit and invalidates results. |
+| Cosmetic package update | Under digest-based kind identity, changing only a package comment still makes its unchanged Duration definition a different kind. Adding old/new Duration values rejects and AsKind cannot bridge them. If the intended selective-update workflow requires these values to mix, the conservative identity rule fails that workflow and an explicit compatibility design is required. |
+| No automatic pruning | Deleting a referenced engineering property or definition without removing/rebinding dependent formulas rejects the whole batch. No term is silently deleted from a sum or constraint. An explicit complete replacement batch may succeed even if its intermediate command sequence is temporarily incomplete. |
+
+## 6. Integration and the library boundary
+
+The sibling library proposal uses `Local(Id)` or import/package/member references. An ImportId's pinned complete dependency closure is immutable: upgrade creates a new import and explicit selected-use rebindings. A resolved definition key includes exact package digest, member identity and dependency closure; alias spelling does not participate. Stable logical origin `(PackageId, MemberId)` remains useful for correspondence, but is not sufficient for type/evaluation equality across revisions. Definitions referenced by Q1 never fetch or resolve “latest.”
+
+Exact evaluation provenance and quantity type compatibility need not ultimately be the same equivalence relation. Treating the full package digest as kind identity is conservative but makes even comment-only repackaging incompatible. Before library release, compare (1) explicit versioned compatibility declarations with checked conversion bridges, (2) a specified semantic definition fingerprint including its semantic dependency closure, and (3) retaining strict identity and requiring all interacting uses to be rebound together. Option 1 needs a trust and validation contract; option 2 needs a normative canonical semantic form and deliberate treatment of labels/metadata; option 3 can defeat selective updates. No alternative is ratified here. Results must retain the exact package/environment digest whichever compatibility policy is selected, and same names or dimensions alone remain insufficient. The bounded Q1 slice has no cross-revision bridge.
+
+The evaluator's minimum interface is a typed resolver returning a validated QuantityKind, Unit or Calculation plus its resolved identity/environment evidence. Root-owned library code chooses package transport/catalog envelopes. Neutral definitions may reference each other through that resolver. Current layer-owned DataType/Interface library mounts, if selected by the sibling design, remain an explicit compatibility adapter; analysis must not assume that `TypeRef::Named(Id)` is already neutral or that mounted architecture instances are pure definitions.
+
+| Existing boundary | Required integration behavior |
+| --- | --- |
+| Model identity | New analysis Elements enter the current global IDs/aliases. Local neutral definitions need identity/name/alias services and collision rules in their catalog. Parameter IDs stay scoped to their definition. Imported definition identity must never be confused with a local Element UUID. |
+| References and deletion | Add typed Model/Definition/Parameter reference traversal for Expr. Existing UUID visitors may wrap Model references but cannot blindly rewrite all UUID-shaped data. Analysis subject deletion either rebinds/removes the declaration and affected bindings/checks in an explicit batch or refuses with impact. Do not reuse existing list-pruning rules to drop an expression operand. |
+| Layer and ownership | Property subjects, case bindings and documented constraints stay in the Element's OA/SA layer. Checks belong to a same-layer case. Neutral definitions cannot capture model records, so permitting a DefinitionRef does not weaken ordinary same-layer references. |
+| OA↔SA transition | Map subject/property/case/check identities and all Expr::Property/documented-constraint references through explicit counterparts. Calculation/unit/kind pins remain the same neutral definitions. Never remap ParameterIds as model IDs or copy a neutral definition into OA/SA. Cases/checks require deliberate new catalog/closure rules and exact forward/reverse tests. |
+| Source and JSON | Introduce a deliberate next schema/language version with closed records, AST grammar, located diagnostics and schema fixtures. Source token ownership must preserve comments and rewrite stable property/definition/parameter references correctly. Generic raw-string replacement is insufficient. |
+| History and recovery | Exact Project+SourceBundle snapshots include accepted definitions/import pins and analysis ASTs. Raw invalid expressions remain drafts. Add closed typed analysis facets/binding keys to semantic review rather than arbitrary JSON patches. Historical imported references must retain enough original pin/origin information to be readable after a live import is removed, without validating them as live references. |
+| Publication and API | Analysis requests operate on captured ETag-qualified snapshots. State-changing analysis/library commands retain current atomic admission and no-op semantics. Schema clients must see real definitions, binding types, numerical representation and classified results; an opaque expression string is not the contract. |
+| Legacy migration | Preserve source7 Constraint/guard/event/message prose and unit strings exactly. Add no heuristic parsing, unit guessing or default numerical values. Structured quantity use in payload DataValue/TypeRef is a further integration slice; these engineering-property examples alone do not prove typed payload parity. |
+
+## 7. Exact first slice, remaining decisions and revisit evidence
+
+The first production slice proposed for root approval is one **Q1 duration budget workflow in both OA and SA**, including exact numbers, local neutral Duration/second/millisecond definitions, two explicit cases sharing typed properties, inline expression, reusable two-parameter calculation, formal check, GUI/source/REST authoring and exact history/recovery. It must not be released as the entire quantity/calculation capability merely because it passes.
+
+Acceptance must establish all of the following:
+
+1. Independently construct the complete nominal/degraded models and sources and obtain exactly 7/4 and 9/4 seconds, with different true/false checks and stable shared property identities. Demonstrate reusable call parameter identity independently of names.
+2. Drive the **same** creation/change through graphical controls, source and real HTTP. Compare full models, accepted source bytes, references and result stamps. Evaluate alone changes no model, revision or Undo/Redo state.
+3. Reject wrong layer/kind/units, duplicate/missing parameter arguments, missing binding rows, definition capture and cycles atomically. Retain admitted explicit `none` inputs and show Missing distinctly from False and DivisionByZero.
+4. Exercise exact decimal/fraction/i64-boundary examples and numerical limits, including literal AST/source/JSON closure at 4,096-bit boundaries and the long terminating-decimal counterexample. Check read-only unit conversion versus changing an authored unit literal. No f64 parse/round-trip is permitted in the Q1 evaluator or exact wire adapter.
+5. Save, Undo, Redo, reopen and recover a raw invalid expression using independently retained complete snapshots. Changing valid UUIDs while preserving names/numbers must fail identity assertions. Pending other editors must not be implicitly applied by Evaluate/navigation.
+6. Exercise creation/editing at minimum and wide native sizes, with a dense model and duplicate names. Test D against the direct-baseline/explicit-override alternative using the action/context-error criteria above. All property/case/function/parameter/unit choices must be reachable without traversing global homogeneous lists.
+7. Demonstrate honest Unsupported behavior for degree/radian/trigonometric requests; keep their approximate-domain acceptance and full structured payload/library integration visibly open. Unsupported is a successful refusal test, not evidence that the requested operation is implemented.
+8. Complete the small source-only workflow from a blank supplied bundle using only the published specification and admission API. Include subject declarations, definitions, both cases, inline and reusable calculations, checks, stable parameter rename, split-file edit, invalid-input repair and save/reopen. Inspect the real stabilized text for identity/boilerplate cost. Run the section 3.4 dense fixture and compare D with B before claiming that the analysis notation supports large-model authoring comfortably.
+
+| Consequential unresolved choice | Evidence that should decide or reopen it |
+| --- | --- |
+| D versus baseline values plus overrides (B) | Native and text-only task comparison including ordinary context-independent properties, creation overhead, repeated assumptions/formulas, stabilized-source noise, propagation surprises and wrong-context edits. Reverse preference if explicit complete case bindings cause more confusion/work than clearly labeled baseline inheritance. |
+| Q1 alone versus F versus Q+F release scope | Representative engineering formulas must include angular conversion and transcendental cases, not only rational budgets. Q1 cannot win a claim of broad capability by excluding these tasks. Freeze a verified approximate profile before claiming their support. |
+| Typed subject association versus true semantic ownership | Deletion, navigation and property discoverability tests on both Element and Relation subjects. Avoid changing parent semantics merely to reuse an existing tree renderer. |
+| Explicit anonymous-dimension AsKind versus registered kind operations | Force/acceleration/energy/torque examples and authoring measurements. A shortcut is acceptable only if the kind algebra is unambiguous and versioned, never inferred from display names. |
+| Structured quantity types in existing data/payload contracts | Real message/default/type workflows need a deliberate shared QuantityValue and type-reference design. Retaining old annotations is compatibility, not completion of that integration. |
+| Definition/package compatibility and historical evidence | Library revision/mount design and independent review must prove pin provenance, selective updates and historical readability. Resolve the unchanged-Duration/comment-only-repackaging falsifier explicitly; exact evaluation provenance must survive any reviewed cross-revision compatibility policy. Do not silently accept same-name or same-member-version changes as type equivalence. |
+
+The root should record the selected alternatives, rejected options, open approximation/payload/library work and resulting release criteria in the durable ledgers. The next highest-information step is independent criticism of these examples and the simple-property authoring flow, followed by one agreed vertical implementation. No statement in this report closes the user's broader goal.
diff --git a/harness/release-libraries-semantics-design-20260910.md b/harness/release-libraries-semantics-design-20260910.md
new file mode 100644
index 00000000..d2df68be
--- /dev/null
+++ b/harness/release-libraries-semantics-design-20260910.md
@@ -0,0 +1,292 @@
+# Reusable libraries: candidate semantics and falsifiers
+
+Codex-956atf `/root/libraries_design`, 2026-09-10. This is a bounded foundational proposal for root synthesis and independent criticism. **No candidate in this report is a ratified decision or implemented capability.** Sole owned file: this report. No production edits, Cargo invocation, native application operation, or new passing test result. Initial source checkpoint was `8547b85`; later root changes through `aa40345` do not change the inspected library/reference architecture.
+
+Read the complete updated `PROJECT_GOAL.md`, the requirements delta, Gitea issue #1 through comment #92, the open PR list (empty), and shared Wiki Home. Ownership is recorded in comments #86 and #93. Coordinated definition interfaces with the separate analysis/quantity designer. Codex-9e8r6t's independent criticism in #90 is incorporated below; that host's scenario/layout/creation/platform work is outside this task. Wiki Home contains older checkpoint evidence, not current release acceptance.
+
+## Proposed outcome and decision boundary
+
+An engineer can publish a reusable library from ordinary modeled content, use its definitions in multiple projects, instantiate an OA/SA pattern more than once with explicit contextual bindings, and selectively update an instance without losing local work. The saved project carries the exact required library content and reopens offline. GUI, textual source and REST expose the same imports, bindings, instance identities and update choices.
+
+The leading candidate is **immutable content-pinned definition imports plus editable, materialized pattern instances with explicit origin maps and three-way update**. It fits the current architecture better than making every architectural object a virtual instance of an external definition. It also provides maintained reuse that source copying alone cannot provide.
+
+The most consequential remaining choice is how existing layer-owned data/interface definitions participate. This report prefers an explicit immutable local mount adapter for those existing records, while new layer-neutral quantity/calculation definitions use a qualified `DefRef`. That is a compatibility boundary requiring criticism and tests, not permission to create two inconsistent library systems. If the adapter cannot satisfy the common identity, source and update rules below, it loses to the more invasive uniform definition-reference alternative.
+
+Local package publication and explicit server upload are sufficient to prove maintained cross-project reuse. A registry, automatic discovery, remote writeback from an instance, arbitrary executable plugins and full SysML/KerML instantiation are separate capabilities; none is assumed to exist under this proposal. This is a proposed bounded implementation strategy for the user's requirement, not a reduction of the acceptance obligations.
+
+## Evidence that constrains the design
+
+| Verified repository fact | Consequence |
+| --- | --- |
+| `model.rs:13` has only `Layer::Oa` and `Layer::Sa`; `Project` at `:1621` has local element/relation/diagram/artifact/trace maps. `identities()` at `:1727` builds one UUID/alias namespace. | A package cannot be another Project merged into the maps without an explicit identity translation and ownership policy. A third neutral layer must not be invented just to store units. |
+| `SharedBody` at `model.rs:593` includes DataType, DataField, ExchangeItem and Interface; `TypeRef::Named(Id)` at `:347` and ordinary semantic references use local UUIDs. `validation.rs:126` expects an existing compatible element in the owner's layer. | Neutral library references are not currently accepted by these paths. Making data definitions neutral requires a deliberate metamodel/refactoring decision. |
+| `parent_permits` at `validation.rs:92` distinguishes semantic containment. Processes/scenarios are layer roots; occurrences, lifelines, ports, data fields and state regions have specific owner kinds. SA admits at most one System of Interest (`language.rst:31`). | Instantiation cannot place every selected object under an arbitrary container or clone another System of Interest. Binding and graphical placement are different operations. |
+| `OccurrenceBody` contains ordered branches, sequence references, ScenarioUse lifeline bindings, reply identities, operation allocations and message-field paths (`model.rs:550`). `ContextualLink` endpoints have occurrence-reference paths. | A pattern is a graph of typed records and references, not a collection of named boxes. Repeated uses, paths and order must retain their distinctions. |
+| `references.rs:5,11` enumerates typed live element/relation references; `:206` also remaps views, placements, artifacts, traces and explicitly typed semantic-history references. Deletion at `:270` can cascade into other records. | Reuse these authoritative classifications where applicable, but do not run one UUID replacement over package metadata, text or historical origins. A library update must preview cross-instance deletion impact. |
+| `semantic/historical.rs:305` deliberately distinguishes historical UUID remapping from deletion; historical facts need not refer to currently live objects. OA/SA review has its own baseline and transition semantics. | A package source member, an old local snapshot identity and a live local reference are different categories. OA/SA counterpart review is not a library merge engine. |
+| `SourceBundle` is an explicit UTF-8 file map (`source_project.rs:17`). The compiler reads no filesystem/network. Includes share the project's global namespace (`language.rst:1692`). | Imports need separate syntax and explicit supplied content. Neither relative include paths nor an ambient cache establish library identity. |
+| `prepare_command` at `session.rs:170` validates a private candidate and returns exact identity correspondence. `update_bundle_with_identity_map` at `source_project.rs:780` checks the old index, preserves authored bytes and recompiles. Server admission at `store.rs:144` uses that boundary. | Library operations must extend this preparation/admission path. An adapter may not accept a model before package resolution and the corresponding source update both succeed. |
+| `Session` stores Project history; desktop/source and server add document-level responsibilities. `Command::Replace` and nested Batch are available (`session.rs:375`). | Read-only widgets are insufficient for immutability. Replace, source Apply, REST writes, nested batches, deletion, identity changes, migration and recovery all need the same semantic guards. |
+
+Capella's official feature page describes reusable collections/replicas packaged in libraries and shared between projects. Its official comparison explains why architectural functions/components are usually treated as distinct usages and why replication/synchronization supports that workflow. These observations support both shared definitions and distinguishable editable architectural occurrences. They do not specify the content-addressing, offline, merge or API rules proposed here. [Capella features](https://mbse-capella.org/features.html), [Capella instance and definition rationale](https://mbse-capella.org/arcadia_capella_sysml_tool.html)
+
+## Materially different candidates
+
+Criteria come from the requested engineering workflow: maintained reuse, separate instances, exact offline reproducibility, complete source/API parity, understandable local editing, and a tractable extension of current semantics. Scores below are qualitative design judgments, not measured performance.
+
+| Candidate | Maintained reuse / local editing | Identity and offline behavior | Existing-core impact | Main falsifier |
+| --- | --- | --- | --- | --- |
+| A. Copy source or model fragments; record a URL in an artifact link | Local editing is simple; comparison/update is manual and correspondence is not authoritative. | Fresh IDs can be generated, but provenance, exact dependency versions and subsequent merge are unspecified. | Small. | Cannot reliably update two differently edited instances of the same pattern. Fails as the final library implementation. |
+| B. Compose immutable external model namespaces; all uses remain qualified references | Excellent shared definitions; architectural local edits require a separate override or specialization model. | Qualified identities and pinned packages can be exact. | Every local lookup, containment/reference validator, query, diagram, transition and command must understand an external model environment. | A supposedly simple rename or deletion of one architecture occurrence modifies the shared definition or requires an unexplained override language. |
+| C. Virtual template instances plus an override log; expand effective models on demand | Strong separation of template and instance; updates can replay or rebase overrides. | Instance identity must remain stable as template shape changes; overridden lists/deletions need their own algebra. | Adds an effective-model layer below nearly all current commands, validation, projection, source and API queries. | Current explicit source records cease to be the editable authoritative model, or effective expansion yields different IDs after reopen. |
+| D. Pinned shared definitions plus materialized editable patterns and explicit three-way update | Ordinary local modeling remains available; origin maps make later comparison possible. | Exact package closure and local maps survive reopen/history; updates are explicit. | New package/document subsystem, reference adapter and merge planner; ordinary architectural records remain local. | Immutability or provenance can be bypassed through Replace/source/REST, or local edits are silently lost during update. Leading candidate if these are disproved. |
+
+D wins over A because it maintains correspondence and update semantics. It wins over B/C for the first release because the existing ordinary local graph remains the engineering workspace, while shared definitions still have their proper distinct role. B is the stronger fallback if uniform qualified references prove simpler than maintaining the proposed legacy-definition mount boundary. C is not selected merely because template expansion is convenient for implementation; its override language would itself need a substantial specification and user workflow.
+
+## Common package, identity and resolution contract
+
+The following rules apply to every export category and both representations of imported definitions.
+
+1. **Logical identity:** a library author allocates one `PackageId` UUID and preserves it across revisions. Each exported item, prototype record, neutral definition and external role has a persistent UUID. Renaming does not change identity. The publisher must create a new identity when a record changes semantic record class. Names and source aliases never establish correspondence.
+2. **Revision identity:** a `PackagePin` contains `package_id`, an exact nonempty version label, a package-format version and a SHA-256 digest. The label is human-facing and is not an implicit compatibility range. No ordering or automatic upgrade follows from a label. A changed digest is a different immutable revision even if all names are unchanged. Two contents claiming the same package ID/version with different digests produce a conflict when compared or brought into the same import graph; there is no last-writer-wins choice.
+3. **Sealing:** a package contains a manifest and one prototype `SourceBundle`. The prototype is an ordinary admitted Project in its own private namespace, including any local neutral definition catalog introduced by the analysis design. The manifest identifies the source entry, exports, member selection, roles and exact dependencies. All package record IDs must already be explicit. Importing sealed content must never insert missing IDs or migrate/reformat bytes silently; an older package requiring migration is republished as a new revision.
+4. **Digest preimage:** define a portable framed byte stream rather than hashing ZIP metadata. Proposal: ASCII `ARCADIA-LIBRARY-1` followed by a NUL; then the manifest UTF-8 byte length as unsigned 64-bit big-endian and its exact bytes; then the entry-path length/bytes; then the file count; then each canonical logical path and source text, each separately length-prefixed, in ascending UTF-8 path-byte order. Counts/lengths are unsigned 64-bit big-endian. The manifest has no self-digest field; it includes dependency pins. Hash exact source bytes, including comments and line endings. The lowercase 64-hex-character digest identifies those bytes. This rule is a Studio proposal, not a claim of conformance to another package format.
+5. **Dependencies:** every package dependency names another exact pin, never a URL to resolve during compilation. The complete transitive graph must be supplied and acyclic. Prototype imports must agree with the manifest's declared dependencies; hidden prototype dependencies are errors. Within one root import closure, each PackageId resolves to exactly one pin. A diamond with two different revisions of the same PackageId is an explicit conflict, even if the author expects them to be compatible. An identical repeated dependency is deduplicated by digest. Report the two requiring paths.
+6. **Concurrent versions:** two separate named imports in one project may have distinct closures and pin different versions of the same package. This permits one instance to advance while another remains pinned. Aliases qualify lookup; they do not make the definitions equal. Identical digest/member/dependency identity reached through two aliases denotes the same imported definition; changed digest is not automatic type equivalence. This matches the analysis designer's proposed exact type identity.
+7. **Import immutability:** once referenced, an ImportId's root pin and closure cannot change. Updating creates a new import and explicitly rebinds selected uses/instances. Renaming the import alias changes source spelling only. Removing an import is refused while a live definition use, mount or managed instance still requires it. Old package bytes remain available to history and recovery even when the current document no longer uses them.
+8. **Resolution:** a pure `VerifiedPackageStore` is derived from explicitly supplied package bytes by digest checking, manifest parsing, bounded graph resolution and prototype validation. No deserialized boolean such as `verified: true` is trusted. Missing content, unknown export/member, forbidden private member, duplicate manifest key/UUID, dependency cycle, wrong digest, unsupported package format and source/admission limit failures return located diagnostics and no candidate. They never substitute a cached different version or an empty definition.
+9. **Budgets:** apply total document/package limits to the deduplicated closure, not fresh limits per package. Initial benchmark candidates are 64 packages, graph depth 16, 256 dependency edges, and 64 MiB combined project/package source, in addition to existing record/parse budgets. These numeric choices require realistic-library measurements before becoming normative; finite configurable refusal is mandatory. A rejected import retains the raw draft and accepted document unchanged.
+
+Digest verification establishes exact content identity; publisher identity or compatibility promises are separate metadata. Registry access, if later added, only supplies bytes to this same admission boundary.
+
+## Export categories and the definition-reference tradeoff
+
+The proposed manifest has three explicit export categories. An engineer sees their purpose, not Rust variant names.
+
+| Export category | Consumer semantics | Layer policy |
+| --- | --- | --- |
+| `Definition` | An immutable reusable quantity kind, unit, calculation or other neutral catalog definition. `DefRef::Imported { import, package, member }` resolves through the supplied closure. Only stable formal parameter IDs and other definitions may be captured; no particular project's property/case/participant identity. | Neutral. Applicability of an analysis property/case remains OA/SA-owned and is checked by the analysis model. |
+| `LayeredDefinition` | An immutable data/interface contract graph mounted once into a consuming project, with an explicit source-member-to-local-ID map. Many local uses point to that one mounted graph. Initial allowed records: DataType, DataField, ExchangeItem, Interface and their data-association/interface-item-allocation closure. | Exactly the layer declared in the prototype export. Mounting does not translate OA to SA or relax same-layer references. A package may publish separate OA/SA exports. |
+| `Pattern` | A set of ordinary OA/SA semantic records materialized with fresh local identities and explicit external bindings. The resulting architectural records are editable, with instance provenance maintained separately. | Original layer of each member is preserved. An export may deliberately span OA/SA and carry explicit mapped traces. An independent layer-transition command remains a different operation. |
+
+The layered-definition adapter preserves current `TypeRef::Named(Id)` and same-layer validators. Mounted records are semantically read-only; they are not editable pattern instances. Their payload must exactly equal the admitted export transformed only by the stored identity/alias map. The mount's revision and member identity/alias map are immutable once created; editing that map in source is not a bypass for renaming/reidentifying an imported payload. The outer import alias remains local spelling. A user can fork/detach definitions explicitly. A new package revision creates a new mount with new local IDs; selected referencing uses are rebound in one reviewed transaction. The old mount can coexist for unchanged uses. A layered definition has no project-specific roles: its complete data/interface dependency graph is included or resolved through other explicitly mounted exact definition exports.
+
+This avoids silently neutralizing current data semantics or converting all architectural references into external references. Its cost is real: local mounted IDs and qualified neutral definition references have different storage shapes. The common resolver must expose one origin descriptor `(pin, export, member)`, one import lifecycle and one read-only/error policy to UI/API clients. The origin descriptor must be discoverable from a mounted local ID and from a `DefRef`. An API must never report the mounted record as ordinary editable content merely because its model field is a UUID.
+
+The competing uniform alternative introduces qualified references for all reusable data/interface/member positions and a neutral definition catalog for those records. It removes materialized mounts, but touches DataField ownership, inherited field lookup, port/interface contracts, operation allocation identities, message field paths, diagrams, transitions and semantic-history source forms. It should win if a focused prototype shows fewer special cases or if the intended engineering workflow requires one exact data identity to span OA and SA. That requirement is not inferred by redefining the existing same-layer rule. Root should resolve this adapter choice before implementing the catalog/reference migration.
+
+## Pattern publication, closure and binding
+
+Use an admitted prototype Project as the authoring context, rather than allowing arbitrary ill-typed placeholder JSON. An export selects prototype members and declares the prototype identities at its boundary as named roles. Those prototype role records demonstrate a valid context, but are not cloned into consumers.
+
+An export's closure is explicitly reviewable and then sealed:
+
+- Start from selected semantic roots. Include all semantically owned descendants required by those roots: ordered occurrences/branches, lifelines, ports, fields, machine regions/states and newly modeled analysis records where their declared subject is included.
+- Include internally owned relation records, not only relations whose endpoint names happen to match. This includes sequence/contextual links, state transitions/actions, interface-item allocations and other relations needed for the selected behavior/contracts. An omitted essential relation is an export error; an intentionally partial external relation requires a declared boundary role.
+- Classify every typed reference as another exported member, an imported definition with an exact pin, or a declared external role. No dangling package-private UUID may escape. Forward references are valid; unresolved references are not converted to `None` to make the export succeed. Existing deliberately incomplete optional fields may remain incomplete and retain their engineering warnings.
+- Give each role a stable ID, label, required reference category, allowed kinds/layer, and context predicates inferred from all its use sites. Parent roles must satisfy `parent_permits`. Scenario roles must respect viewpoint/represented-source rules; operation/port bindings must satisfy their existing ownership contracts. The final materialized candidate still passes complete core validation, which is stronger than matching a kind label.
+- A required role has exactly one explicit binding; an optional role can be explicitly absent only where every use site permits absence. Two roles may bind the same host identity only when their contracts and all resulting relations remain valid. This is shown in preview. There is no bind-by-name fallback.
+- A package System of Interest is a context role, never a cloned member. An SA pattern may require binding to the host's System or leave that separately optional if its contents permit. The operation never creates a second System, translates it into another kind, or silently creates one to satisfy a binding.
+- A prototype Project ID is not an architectural member. Layer roots stay roots unless their kind permits a chosen semantic parent. Importing into a diagram adds placements only through an explicit presentation choice; the diagram is not their semantic owner.
+
+The package author can publish a whole coherent two-layer pattern; the manifest must state which cross-layer trace links are included. New instances may copy those links when both endpoints are resolved. **They do not copy prior semantic-review acknowledgments as though the consumer reviewed the instance.** The initial policy clears copied trace baselines and reports that review is pending. Ordinary accepted local baselines on an existing instance are not automatically refreshed during a library update. Explicitly supplied artifact links preserve URI/revision/fragment literals; UUID-shaped strings are never remapped. A relative artifact target must have an explicit package or consumer base policy before it can be exported portably; no silent rebasing against the user's current directory.
+
+Presentation export is an explicit independent option: include a named diagram with its placements/routes/options only when every referenced semantic identity can be mapped or bound. Allocate fresh diagram and placement UUIDs, remap actual typed references, and preserve geometry. Semantic-only instantiation creates no arbitrary default arrangement; the initiating graphical workflow may supply reviewed placement commands. Updating semantic content does not overwrite local layout unless the user selects a separate presentation change set.
+
+## Proposed persistent model and document shapes
+
+These are type-level sketches, not production Rust or finalized source grammar. Identity wrapper types should prevent passing a foreign member UUID to a local-ID remapper.
+
+```text
+PackagePin { package_id: PackageId, version: String, format: u32, digest: Digest }
+PackageArchive { manifest_source: String, prototype_source: SourceBundle }
+LibraryImport { id: ImportId, alias: Alias, root: PackagePin }
+DefRef = Local(DefinitionId)
+ | Imported { import: ImportId, package: PackageId, member: MemberId }
+
+DefinitionMount {
+ id: MountId, alias: Alias, import: ImportId, export: ExportId,
+ member_map: Map, aliases: Map
+}
+
+LibraryInstance {
+ id: InstanceId, alias: Alias, name: String,
+ base: { import: ImportId, export: ExportId, bindings: RoleBindings,
+ materialization_policy: Policy, original_local_map: MemberMap,
+ snapshot: TypedInstanceGraph },
+ correspondence: Map,
+ identity_lineage: ExplicitLocalIdentityCorrespondence,
+ proposed_binding_defaults: RoleBindings,
+ detached_origins: List
+}
+
+Project additions: imports, definition_mounts, library_instances,
+ local neutral definitions (owned by the analysis design)
+
+PortableDocument {
+ model: Project,
+ source: SourceBundle,
+ packages: Map
+}
+```
+
+The package archive map is authoritative input to pure admission, not an ambient resolver cache. Its parsed model/index can be derived and shared immutably in memory. An implementation may deduplicate immutable bytes between snapshots, but it must not omit them from portable export/recovery or drop a pin required by Undo.
+
+`Project` includes import/mount/instance intent and local editable records. The document compiler derives/verifies mounted payload against exact package content, checks every origin map and definition reference, and rejects forged mismatches. Public JSON that contains mounted records is checked against the same materialization. No special trust attaches to an API caller's supplied project or plan. A core model-only convenience API can use an empty store for projects without imports; it must return `missing_library_content` for imported content unless the required store is explicitly supplied.
+
+The baseline records the actual last adopted package, bindings and materialized typed graph, not a regenerated guess from current local data. Its snapshot includes the exact chosen local aliases/names, identity map, copied presentation policy and constructor payloads. Regenerating aliases with `fresh_alias` against today's project would create a false baseline after unrelated names are added; that is prohibited. Baseline references can be historical and absent, so validate their closed shape, mapping and derivation separately from current live-reference admission. Deleted tracked members retain a reserved local identity so a later import cannot silently reuse that UUID for a different object. The active correspondence is injective, record-class-preserving and disjoint between instances and mounts. Separate instances always allocate separate local members, even with identical bindings and names. Shared immutable definitions can deliberately be common to both.
+
+Explicit local `Reidentify` must update current correspondence and a typed lineage map used during comparison; it does not rewrite the sealed package or the immutable original snapshot. Deletion marks a mapped member absent instead of forgetting its origin. A subsequent new record with a similar name is a local addition, not the deleted member's replacement. A retained upstream-deleted member can be explicitly detached; a later upstream reintroduction cannot silently adopt that local extension.
+
+## Textual source and portable admission
+
+Text must represent the complete library intent. Merely attaching packages to a desktop save dialog would break parity with source Apply and REST.
+
+Proposed source forms illustrate the required information; keyword spelling/version numbers remain subject to grammar review:
+
+```text
+library_import controls_v1 id "" {
+ package "";
+ version "1.0.0";
+ digest "";
+}
+
+definition_mount oa_telemetry id "" {
+ import @controls_v1;
+ export "";
+ members { "" -> "" alias telemetry; }
+}
+
+library_instance left_monitor id "" {
+ import @controls_v1;
+ export "";
+ bindings { "" -> @operator_left; }
+ members { "" -> @monitor_left; }
+ // Exact baseline/correspondence records are typed source values.
+}
+```
+
+For neutral definition use, a readable qualified alias can resolve to the exact imported member (`controls_v1::duration`); canonical output must retain the stable import/member identity, and alias changes must use the source index rather than text replacement. Existing local alias spelling remains globally unique for local records. Member aliases are scoped inside their package; two packages may both export `duration`. Missing/ambiguous export lookup is an error, never a first-result match. Avoid widening current reserved words until the actual grammar is selected.
+
+The pure source compiler accepts **an explicit portable source envelope consisting of the ordinary SourceBundle plus package archives**, resolves imports from that envelope, and returns the entire admitted document. Existing include semantics remain unchanged. Package archives are nested typed source bundles in JSON/REST. A directory export places them at digest-addressed paths and an explicit manifest maps pins to those logical entries; file loading builds the same envelope before calling the compiler. The directory loader may read only those declared bounded paths, just as current includes are explicit. It must never search another open project's cache for a convenient revision.
+
+Canonical source export must offer a self-contained envelope/directory containing every required package, with the invariant `compile_source_document(format_source_document(document)) == document.model` plus exact package bytes. A single source string that contains imports but lacks the explicit package envelope returns missing-content diagnostics. Existing no-library string APIs continue working. A plain model JSON export that needs imports must likewise use the published portable document representation; it must not masquerade as independently reopenable when its required bytes are absent.
+
+`SourceIndex` needs locations for imports, mounts, instance mappings, bindings, definition references and lineage fields, with typed classification of local versus foreign IDs. Derived mounted payload does not receive a second ordinary editable declaration: canonical formatting emits its mount directive/map and the supplied package envelope, and compilation reconstructs/verifies it. The source updater must classify derived mounted records explicitly rather than treating a missing ordinary declaration as a reason to append one. Graphical operations update only affected local tokens and metadata; package bytes are immutable. Editable pattern instances, their diagrams and their source declarations remain ordinary authored records. Exact source/history tests must include comments, duplicate display names, non-ASCII text, mixed file ownership and CRLF content.
+
+## Command boundary, visitor audit and immutability
+
+The shared core should expose pure prepare operations, with no Iced or HTTP-specific library rules:
+
+```text
+inspect_package(archive, supplied_closure, limits) -> PackageInspection
+prepare_import(document, archive_closure, import_identity) -> PreparedDocument
+prepare_mount(document, import, export, reserved_map) -> PreparedDocument
+prepare_instantiate(document, import, export, bindings, presentation, reserved_map)
+ -> PreparedDocument
+preview_instance_update(document, request) -> LibraryUpdatePlan
+prepare_instance_update(document, request, choices, expected_plan_fingerprint)
+ -> PreparedDocument
+prepare_detach_instance / prepare_fork_definition / prepare_remove_import
+```
+
+Preparation resolves exact bytes, reserves IDs once, validates package/mapping/role rules, builds an ordinary candidate command batch, checks complete model semantics, updates the source envelope, recompiles, and compares the resulting full model and pinned content before adoption. The returned selected identity is optional metadata, not proof of acceptance. A preview exposes its base revision/source/closure fingerprint and reserved IDs. Commit recomputes/checks the request and choices against the current document; it never trusts arbitrary serialized commands supplied in a plan. Stale revision, changed source, changed package bytes, lost binding target, unresolved conflict or source-budget failure rejects the whole operation.
+
+Mandatory reference categories and their behavior:
+
+| Category | Example | Rename/reidentify/delete behavior |
+| --- | --- | --- |
+| Live local semantic reference | Parent, endpoint, field path, scenario binding, analysis property subject, local DefRef | Existing typed local visitors apply; same-layer/kind rules remain. Include new analysis and library use sites in impact queries. |
+| Live local library bookkeeping | Instance current correspondence, current binding defaults, imported/mounted object handles | Update through explicit typed library visitors. Reidentify composes actual identity correspondence. Deletion records tombstones or refuses removal of required managed objects as specified. |
+| Qualified definition reference | ImportId plus PackageId/MemberId | Import alias changes source spelling only. Explicit reference rebinding changes revision. A local element reidentify never changes PackageId or MemberId. |
+| Foreign origin | PackageId, export/member/role IDs, dependency IDs and sealed prototype references | Never visited as local identity. Never renamed/deleted because an equal UUID bit pattern happens to occur locally. |
+| Historical baseline/original-map reference | Last-adopted role bindings and original local map | Retain exact stored observations. Use explicit identity-lineage data during comparison rather than rewriting sealed or historical origins. Absence from the current model is allowed. Never include these in deletion cascades. |
+| Existing OA/SA semantic-history reference | Typed SemanticBaseline fields | Preserve the current explicit-remap policy in `semantic/historical.rs`; do not broaden it to package history. No generic scan of UUID-shaped strings. |
+| Literal content | Artifact URI/revision/fragment, descriptions, expression text, comments | Preserve bytes except an explicitly edited literal. A UUID-looking substring is not a reference. |
+
+Audit every affected boundary: `Project.identities/contains/fresh_alias/layer_of/named`, typed reference enumeration/remapping, reserved/tombstone identity checks, deletion impact and finish-deletions, inheritance pruning, kind/reference admission, source normalization/index/update, JSON decoding, migration, semantic baselines and transition preparation, projection/view options, server query/edit endpoints, recovery and both forms of history. An allowlist of editable imported fields in a form is not this audit.
+
+Mounted records reject ordinary Edit/Rename/Reparent/SetAlias/Reidentify/Delete and any transitive command that would mutate their verified payload. If a local deletion would cascade into a mount, the whole operation requires a reviewed rebind/detach/removal instead. Replace and source Apply may propose another **valid immutable revision/import/mount**, but cannot alter bytes while retaining an old pin or forge a mounted payload that does not match the export. The first implementation should centralize this as before/after document admission, so nested Batch and all adapters are covered.
+
+## Three-way update: exact observable rules
+
+For a managed instance, define `B` as its exact stored last-adopted materialized snapshot, validated against the pinned export and stored policy/bindings; `L` is the current local graph and `N` is the proposed new export with explicit proposed bindings and reserved identity/alias map. Match members by stable package member identity, then use the stored correspondence/explicit local identity lineage to compare their local roles. Do not match by name, diagram position or graph similarity, or reconstruct `B` using current local names/values.
+
+Fresh members in `N` reserve new local IDs once. Surviving members keep their current local IDs. A kind/class change under the same source member ID is a conflict, not an identity-preserving edit. A package update may not silently convert an Activity into a Function, an element into a relation, or a definition into an occurrence.
+
+For a typed facet with values `b`, `l`, `n`:
+
+- If `l == b`, the upstream change to `n` is an eligible update.
+- If `n == b`, retain the local change `l`.
+- If `l == n`, the edits have converged.
+- Otherwise, require an explicit local/upstream/valid authored resolution.
+
+Equality is typed and includes exact reference identities. Constructor changes and their dependent fields are one coupled decision. In the first coherent slice, ordered member arrays are compared as complete ordered values; simultaneous local reorder and upstream insertion is a conflict requiring an explicit final order. This conservative rule is preferable to silently inventing an insertion anchor. Truly set-valued collections can compare by identity membership where the existing specification says order is irrelevant.
+
+Deletion is not a special case that bypasses review:
+
+| Situation | Required outcome |
+| --- | --- |
+| Upstream deletes a member; local member is unchanged | Eligible deletion only after all external references and current deletion cascades are shown. Any affected object outside the instance requires explicit resolution. |
+| Upstream deletes a locally edited or locally referenced member | Conflict: keep as detached local content, or explicitly delete/rebind the full reviewed impact. No hidden cascade. |
+| Local member was deleted; upstream changes it | Conflict: preserve local deletion or recreate from upstream using its reserved identity if still admissible. Never resurrect it just because its name appears in the new package. |
+| Upstream and local both delete | Converged absence; provenance remains sufficient for the next update. |
+| Local addition is absent from both package revisions | Preserve it as local extension. Its relations/containment can still make another proposed change invalid. |
+| Upstream changes or removes a role | Review the role/binding facet and all affected use sites. Wrong-layer or missing targets are errors. No fallback to a different same-named participant. |
+
+An ordinary local field edit changes the actual model; it does not retroactively change the package baseline. An explicit rebind action records intended role defaults and changes the selected current use sites, with a preview of any divergent local field. Future additions use the proposed bindings shown in update preview. The tool must not imply that every ordinary reference remains a live parameter substitution after materialization.
+
+The plan covers all choices and the complete candidate, including effects outside the instance, before Apply becomes available. Apply is one document/history transaction. On success the base advances to the exact new package/materialization, even when selected local overrides or deletions remain; they remain differences from that new base. A rejected or cancelled preview leaves the old base unchanged. Evaluating a preview and inspecting changes create no history step.
+
+Semantic and presentation updates have separate choices. Existing OA/SA traces stay ordinary local artifacts: neither updating a package nor changing a binding implies the engineer has reviewed OA/SA consistency. The current semantic reviewer can report resulting changes, but its acknowledgment state is not a substitute for the library baseline.
+
+## Detach, fork and removal
+
+`Detach instance` retains the current local semantic records, IDs, relationships, source spelling and local layout. It removes active update management and reservations for absent members, records a non-live origin note with package digest/export/mapping, and clearly states that future package updates will not be offered for those records. Detaching must not detach another instance sharing an immutable definition.
+
+`Fork imported definition` creates ordinary editable local neutral definitions or a local data graph, with fresh IDs and an explicit reference-rebind selection. It copies the dependency closure needed for the fork or retains explicit pinned references; no dangling private member escapes. The original imported definition and unselected uses remain pinned. This operation is different from changing a package's sealed bytes.
+
+Removing a whole instance uses a reviewed deletion impact and one atomic command. Removing an unused mount/import is explicit; it cannot remove another current use's dependency. Garbage collection may reclaim package bytes only when no current document, history, pending accepted recovery snapshot or retained baseline needs them. Closing the project does not evict information required by its saved portable form.
+
+## Concrete two-project example
+
+Publish package `ControlPatterns`, PackageId `P`, version `1.0.0`, digest `D1`. It exports neutral Duration/ms/s and a reusable calculation, one OA data contract, and a monitor pattern. The OA pattern contains Activity `A`, an owned behavior/scenario with two distinct occurrences and a typed link; external participant role `R` is excluded from cloned membership. A separate SA export can carry Function/port structure and bind the host System. The IDs `P/A/R` are symbolic stable UUIDs for this example, not name-based resolution.
+
+1. Project Alpha imports `D1` as import `I1`; Beta independently imports the exact same package. Both persist all required package bytes.
+2. Alpha instantiates the pattern twice. Maps are `A -> alpha_left` and `A -> alpha_right`, with distinct UUIDs for every copied occurrence/link/placement. Role `R` binds to different existing participants. Both can deliberately use the same immutable Duration definition and OA data mount. Beta's map has entirely different local IDs.
+3. Alpha changes the left activity's description and reorders its two occurrences. The right instance stays unchanged. Beta closes its project and its application.
+4. Publish `1.1.0` with digest `D2`, preserving `A` and other surviving member IDs. It changes the same description, adds a third occurrence, and removes another referenced member. Alpha imports `D2` as `I2`; `I1` is untouched.
+5. Update only Alpha's left instance. Preview shows the description conflict, the complete order conflict, the member addition and outside-reference deletion impact. The engineer keeps the local description, explicitly chooses a valid three-occurrence order and keeps the removed referenced member as detached local content. One Apply advances only this instance to `I2`; its surviving UUIDs remain stable. The right instance remains on `I1`.
+6. Undo restores Alpha's exact model/source/import/instance state and prior accepted package references; Redo restores the same reserved IDs and choices. Save and close Alpha, clear only disposable resolver caches, disconnect the network, and reopen Alpha/Beta from their portable files. Alpha has its selected `D2` instance plus the pinned `D1` instance; Beta still has `D1`. Their type/evaluation identities and results are derived from those exact bytes.
+
+A variation binds a role to an invalid SA actor for the OA export, supplies two different pins of a shared dependency within one closure, modifies one package source byte without updating its digest, or removes a required archived package. Each variation rejects with a precise diagnostic and preserves the accepted project. A variation uses two package versions under distinct imports; it succeeds without conflating their definitions by alias or stable member ID alone.
+
+## REST, migration and release acceptance
+
+Extend the existing published Studio API, not an undocumented desktop-only endpoint. Read/query responses expose imports, origin, definition mutability, instance status and exact pins. Package inspection/upload accepts bounded explicit archives; import/instantiate/rebind/detach/update use the same pure core preparation as the GUI. A read-only preview response includes revision/closure identity and structured changes/conflicts; commit requires the current ETag and complete choices. A stale ETag or changed dependency rejects atomically. Independent clients must not need private Rust enum knowledge beyond the published schemas/examples. This is not a claim to implement the OMG Systems Modeling API.
+
+A deliberate next schema/source version must add the chosen forms. Existing source7 includes remain ordinary project modules. Existing ScenarioUse/SequenceReference remain within-project reuse. Existing annotations, artifact links or filenames that look like library references do not become imports by heuristic. The default migration adds empty library state and preserves all accepted source/comments/IDs; neutral analysis migration is coordinated with its own design. Package-format evolution is independent and explicit: old immutable bytes keep their old digest, and supported readers either interpret that format correctly or request a republished/migrated revision.
+
+The first vertical slice is complete only when it proves the following through core, source, REST and the actual native application. None has been executed in this report.
+
+| Acceptance / falsifier | Required independent observation |
+| --- | --- |
+| L1. Shared definitions plus distinct architecture | Publish a package containing a usable data definition, neutral unit/calculation and OA/SA pattern exports; consume in two fresh projects and instantiate twice in one. Compare complete models, maps, typed references and exact package bytes. Two instances must not alias accidentally. |
+| L2. Real maintained update | Execute the description/order/deletion example above. Check local changes and outside references, explicit choices, base advancement, untouched old-version uses and stable surviving IDs. Compare against a separately constructed expected graph, not the planner's own candidate. |
+| L3. Exact history/recovery | Save/reopen, Undo/Redo and crash recovery retain exact authored source, all pins/maps, reserved IDs and raw invalid bindings/update drafts. Compare saved snapshots byte-for-byte and full models; semantically similar regenerated IDs fail. Preview/cancel creates no revision/history step. |
+| L4. Offline and missing content | Close both applications/projects, discard disposable caches, reopen offline. Exact pinned definitions/results remain available. Remove a required archive and observe a clear unresolved import with no mutation or fallback, then supply the exact bytes and recover. |
+| L5. Dependency and namespace failures | Test a diamond with conflicting dependency versions, a dependency cycle, same package/version with different content, duplicate manifest keys and IDs, case-colliding logical paths, unknown/private exports, alias collisions and aggregate-budget exhaustion. Test two legitimate concurrent versions under different imports. |
+| L6. Immutability on every adapter | Attempt direct/multi-level-Batch/Replace/source/REST mutations, reidentification and cascaded deletion of mounted/imported content. Every path either rejects unchanged or performs an explicit valid fork/rebind/removal. Changing an origin UUID or digest cannot make forged payload authoritative. |
+| L7. Typed visitor traps | Put identical UUID bits in a live local ID, package member, historical original map, artifact URI, description and expression text. Reidentify/delete the local object. Only the specified typed categories change; package bytes/literal content remain exact. Include current OA/SA historical semantics and a deleted managed member. |
+| L8. Domain closure | Pattern contains nested ALT/LOOP occurrences, ScenarioUse repeated bindings, operation allocation and message-field paths, contextual links, state machine transitions, OA/SA traces and data inheritance. Instantiate with valid/invalid bindings. Full core admission passes/rejects appropriately; no required relation is dropped to force success. |
+| L9. Layer and singleton fidelity | OA cannot bind an SA role; importing an SA pattern never clones a second System of Interest. Explicit parent and graphical placement choices preserve existing containment semantics. New neutral definitions do not bypass architectural layer checks. |
+| L10. Graphical task usability | At minimum and wide windows, publish/import, choose an export, bind the intended participant among repeated names, instantiate, inspect origin and resolve update conflicts. Use scope/kind groups, contextual search and a focused detail editor. A package browser or change review must not expand every member into one long homogeneous list. Record an action/time budget before the comparison and observe real task completion. |
+| L11. Independent API/source client | A client using the published OpenAPI and reST can supply package bytes, create imports/instances, query origin, preview/apply update, handle stale ETag, detach and export a portable document. Build equivalent accepted models through GUI/source/API and compare them fully. |
+
+## Highest-value next experiment and reasons to revisit
+
+Before a large implementation, run a small pure-core prototype against one existing DataType/Interface graph and the proposed neutral Duration/calculation definition. Implement only import resolution, one immutable mount, one pattern map and the exact document round-trip in an isolated experiment. Falsify the two-reference-shape bridge with source rename, local Reidentify, deletion, Replace, duplicate UUID bits and concurrent versions. If special cases spread into ordinary projection/command code instead of a confined resolver/admission adapter, switch to the uniform definition-reference design before proceeding.
+
+Then implement the three-way update example with a conservative typed-facet merge. A complete valid candidate plus exact source history is the success criterion; a visually plausible diff is not. Native workflow design can use real inspection data from that prototype, but the package/reference contract must be reviewed before downstream UI/API/schema work.
+
+Reasons to reopen the leading choice include a demonstrated need for virtual instances/overrides, exact shared data identity across layers, registry-driven dependency solving, collaborative package authoring, package signatures as a workflow requirement, or unacceptable measured size/performance of preserved package/history content. These would justify explicit design expansion, not hidden changes to pin or identity semantics.
diff --git a/harness/release-scenario-native-20260911.md b/harness/release-scenario-native-20260911.md
new file mode 100644
index 00000000..d9744949
--- /dev/null
+++ b/harness/release-scenario-native-20260911.md
@@ -0,0 +1,107 @@
+# Scenario authoring: native Windows evidence, 2026-09-11
+
+Codex-956atf `/root`. This is a partial capability checkpoint, not first-release
+acceptance. Actual Studio was operated through the Computer Use `sky` API against
+copies of the independently generated [fixture](fixtures/scenario-authoring/README.md).
+No fixture fields or expected transactions were changed to match the observed UI.
+
+## Build and execution identity
+
+- Rust implementation: `80cf197be6f1d400bf0b9c84bf8557f88c895aa6`.
+- `cargo build --locked -p oa-desktop --bin arcadia-studio`: exit0, 3m28s,
+ warning-free dev build. Log `.local/logs/desktop-gui-integrated-20260911.log`.
+- Executable SHA256: `A5ED6DF334B1BFB620425AFDF3E863F0B522990E6FB47C6C401A71CF4443954A`.
+- Immutable native staging directory: `.local/native-qa/studio-80cf197/`.
+- Bundled `libunwind.dll` SHA256:
+ `AA282FA47BB2C4E210CEA5E470CDBDCA58568F0BE0BC73E404D86AB9CD95EF6A`.
+- Process4416 opened the real Iced workspace. The launch helper initially reported
+ no targetable window; a fresh window listing found the completed workspace.
+ No second instance was launched. The debug console window was not automated.
+- Full desktop tests passed642/642 before this native run. Their executable/hash
+ differ from the GUI; see [STATE](STATE.md). Formatting passed. Subsequent strict
+ Clippy found two collapsible-if statements, corrected after this immutable build;
+ the current lint gate is not yet claimed passing.
+
+The native checker requires the task-local runtime on PATH; an initial invocation
+without the toolchain environment exited `-1073741515`. Sourcing
+`.local/toolchain/env.ps1` resolved that launch dependency. Both new OA/SA working
+copies then passed the baseline checker. This was a checker-launch error, not a
+semantic fixture failure. The GUI stage includes its runtime beside the executable.
+
+## Observed navigation and layout
+
+Opened `.local/native-qa/scenario-20260911/oa/project.arcadia` through Studio's
+normal Open dialog. The initial window captured1442×932 pixels, corresponding to
+the1440×900 client. The project contains62 elements, zero relations and11 fixture
+engineering warnings; that matches the independently constructed baseline.
+
+At normal size, selected OA A caller and Behavior & scenarios. The new workspace
+keeps the scenario selector and compact controls above the remaining-height canvas.
+Fit displayed the whole nested caller at42%; opening Selection reduced available
+height and recomputed35%. These overview scales are for navigation, not readable
+detailed editing of every nested label.
+
+1. Selected **OA Second use of B**, verified its name and alias
+ `scenario_oa_61`, then Selection → Open definition. The canvas changed to
+ **OA B verification** with Back to caller available.
+2. Selected **OA B use of C** (`scenario_oa_64`), opened C, then Back twice.
+ First return restored B's exact use; second restored A's **Second use of B**,
+ its Selection panel and35% overview. The initiating use was not replaced by the
+ similarly targeted first use.
+3. Repeated A→B→A through **OA First use of B** (`scenario_oa_60`). Return selected
+ that first occurrence and preserved its presentation.
+4. Switched A to100%, scrolled the actual canvas until the two reuse blocks were
+ visible, opened B and returned. The first-use block returned to the same visible
+ y-position (top646, label666 in the captured window) and remained selected.
+ A subsequent upward scroll moved naturally; Save did not snap it back.
+5. Save all completed. Independent `check baseline` passed the whole Project and
+ both reachable source files, including the retained note. Navigation introduced
+ no accepted source/model difference in this checkpoint.
+
+Resized through the Windows system-menu Size command, using Right/Down then a
+pointer placement. The resulting capture was1102×732 pixels:1100×700 client, the
+application's configured minimum. The initial corner drag did not resize; it is
+not counted as a sizing result. At minimum size, Fit recomputed21% with Selection
+open and28% after closing Selection. Focus canvas hid the side panels and Restore
+panels remained accessible. Normal-scale editing retained a real scrollable canvas.
+
+## OA action: creation and exact history pass
+
+Performed the fixture's prescribed `oa-action` task at the minimum1100×700 client,
+with focused canvas and100% scale. Tools → Add action exposed an explicit root
+insertion line labeled before **OA Root entry**. Clicked that line at the
+**OA A inspector** lifeline, not the Tools append action.
+
+The pending marker retained the chosen owner/before-slot/lifeline. The bottom form
+kept **Create** and **Cancel** pinned while its fields scrolled independently.
+Entered literal name **OA native action**, selected work **OA Inspect ticket**,
+and confirmed the preselected observed lifeline **OA A inspector**. Create added
+one action before Root entry; the canvas showed63 objects and selected the new
+action. Save all completed.
+
+| Native stage | Independent result | Preserved whole-directory snapshot |
+| --- | --- | --- |
+| Before Create | Previously checked baseline | `.local/native-qa/scenario-20260911/oa-baseline-verified/` |
+| Create, Save | `check oa-action` PASS | `oa-action-verified/` under the same run directory |
+| Ctrl+Z, Save | `check baseline` PASS; `same baseline-verified working` PASS | `oa-action-undo/` |
+| Ctrl+Y, Save | `check oa-action` PASS; `same action-verified working` PASS | `oa-action-redo/` |
+
+Every `same` comparison covers all Project identities/fields and every SourceBundle
+byte. The semantic checker borrows only the unique fresh role UUID and independently
+predicts the remaining graph. The verified snapshots were preserved before Undo;
+restored snapshots did not replace them. All checker commands exited0. Tool outputs
+record both semantic and exact equality passes.
+
+## Open native gates
+
+OA nested state observation/reuse/fragment and all four SA creation checkpoints,
+final reopen, blocked navigation/cancellation/recovery, detailed-list definition
+entry, minimum-size nested caller return, and B4 lifecycle rendering remain open.
+This report does not infer them from the passing root action. B4 is still a pure
+cfg(test) projection; independent Scene review identified lifetime bounds,
+branch-local activation clipping, incomplete-lane geometry and self-arrow endpoint
+work before production integration.
+
+Continue from the saved OA action state in the run directory, following the exact
+fixture procedure. Root retains native input; concurrent source builds use the
+separate target tree and must not overwrite the running staged executable.
diff --git a/harness/release-semantic-proposals-review-20260910.md b/harness/release-semantic-proposals-review-20260910.md
new file mode 100644
index 00000000..7e125fec
--- /dev/null
+++ b/harness/release-semantic-proposals-review-20260910.md
@@ -0,0 +1,124 @@
+# Independent review of the paired analysis and library proposals
+
+Codex-956atf `/root/semantics_critic`, resumed 2026-09-11. This is a design review, not an implementation test or release acceptance. Read the full revised objective in attachment `a49d1780-25d5-4778-ad68-6140c60e4207`, both proposals, the requirements delta, Gitea issue #1 through #102, the empty open-PR list and Wiki Home. Ownership is recorded in #103. Wiki Home describes an older checkpoint. Another host's earlier comments do not establish current activity.
+
+Owned output is this file only. No production or ledger edits, Cargo, native application input, commits or pushes. Root was integrating unrelated behavior/packaging changes while this review ran; inspected semantic boundaries remained unchanged. Source HEAD observed during review: `865cd55e21e622985322d711b23e6d653b563d3d`.
+
+References below use **A** for [analysis proposal](release-analysis-semantics-design-20260910.md) and **L** for [library proposal](release-libraries-semantics-design-20260910.md). Line numbers identify the versions initially reviewed; section names and quoted concepts identify the locations if the authors add follow-up text. Both explicitly remain proposals. A deliberate exclusion or unfinished experiment is not reported as a shipped defect. P1 means a contract decision or required workflow must be resolved before the relevant production foundation is ratified; P2 means a concrete design weakness or incompleteness to resolve before acceptance.
+
+The analysis author revised and froze A during review. Re-read its new section 3.4, exact literal rules and compatibility discussion. Final observed SHA256: A `BF631450F46FF19AA09D142B964B60FA380309E4A728F895D1ECEC3ECBF6EF41`; L `29687171ED01FE4A5A8C2F3B3493008C144189D44349524CDFF825862D41BE72`. R7 is corrected at proposal level; R2/R4 are now explicitly acknowledged consequential choices in A. That acknowledgment improves the proposal without resolving the choices.
+
+## Recommendation
+
+Keep the pure evaluator, typed expression references, exact package pinning, editable materialized architecture, explicit bindings, complete old instance baseline and atomic document admission. These are compatible foundations. Do not ratify the entire paired design yet. In particular, byte-level package identity must not accidentally become the only available engineering compatibility policy; and a source export with omitted management records does not demonstrate text-only authoring.
+
+The strongest immediate comparison is a compact direct-value/baseline design with one explicitly based alternative case against A's complete-case design, using both authored text and the native workflow. For libraries, retain materialized patterns, but decide the cross-revision definition policy and source creation/update normalization before committing to the hybrid mount implementation.
+
+## Findings
+
+### R1 — P1: the source example omits the information needed to author a managed instance
+
+**Locations:** L, “Textual source and portable admission,” lines 145–175, especially the comment at 165; “Proposed persistent model,” lines 112–139. A, “Minimum GUI and source work,” lines 192–203. Existing contract: `docs/language.rst:1772–1781`; `crates/oa-core/src/source_project.rs:286`; `crates/oa-server/src/store.rs:116–130`.
+
+The library example names an import, export, binding and member, then replaces the exact baseline/correspondence with a comment. Those records are authoritative for future updates, not optional display details. The proposed portable envelope solves transport and offline availability, but does not specify how an engineer creates, updates or repairs those records using text alone. A full serializer grammar would establish expressiveness; requiring users to hand-copy the old typed graph, identity map and lineage still fails the new low-clutter authoring criterion.
+
+**Minimal falsifier:** start with an empty project directory and an available package, without a GUI or preexisting instance. Write a readable declaration for two instances; normalize/compile it; edit one local occurrence; upgrade that instance to a new package while retaining the edit. The proposal currently has no specified text operation that creates the complete baseline and no complete example of the update. Supplying a GUI-generated snapshot as the starting point would evade this task.
+
+**Smallest convergence:** distinguish a concise authored creation/update request from its accepted, fully explicit normalized state. A Rust source command may allocate/stabilize IDs once and emit a declared, version-controlled management module containing the exact base/map/lineage. Specify when this happens, how repeated compilation stays inert, how conflicts are expressed and committed from text, and how diagnostics point to authored input or generated metadata. That module is part of the portable source envelope and published grammar, never an invisible desktop cache. An alternative is a fully authored management syntax, but it must win the same task without manually rebuilding snapshots. Existing missing-UUID stabilization is a useful precedent, not proof that instance normalization already exists.
+
+**Acceptance:** a human-readable tutorial performs initial publication/import/two instantiations/local edit/selective update/detach entirely through text plus the published Rust tooling. Reopen from only the directory and packages. Compare the full model and exact accepted metadata to independently constructed expectations. Measure relevant text edits and incidental identity/bookkeeping edits separately. A smaller displayed snippet must not hide mandatory work.
+
+### R2 — P1: cosmetic package changes can make selective updates unusable across unchanged definitions
+
+**Locations:** L common contract lines 51–56, layered mounts line 72, two-project example 253–260; A unit rules 227–241 and library boundary 293–300. Existing layered compatibility: `crates/oa-core/src/data.rs:129–145` checks local identity ancestry.
+
+L hashes exact comments/line endings and assigns new revision identity to changed bytes. A makes that whole pinned revision part of quantity-kind equality and rejects named-kind-to-named-kind `AsKind`. This is a deliberate conservative policy, but its interaction with selective instance updates is not closed by the worked example.
+
+**Minimal falsifier:** package P contains Duration, second and two-duration calculation plus a monitor pattern. Alpha has two P@D1 instances and a total budget over both. Publish D2 changing only a pattern description or a source comment; all Duration/unit definitions and stable member IDs are identical. Update only Alpha's left instance as required by L2. If its imported references advance, left Duration@D2 and right Duration@D1 cannot be added. The supported unit conversion requires equal kind and `AsKind` refuses this named-to-named conversion. If references stay on D1 instead, the proposal needs a defined mixed-version materialization/base policy and must not claim the instance now derives wholly from D2.
+
+The analogous mount problem is concrete: a new mount receives new local IDs. An unchanged item/interface in D2 is not in D1's local ancestry, so existing item/interface satisfaction does not consider it compatible. Rebinding only the chosen pattern can require edits to unchanged outside ports, allocations and message paths. An explicit rejection protects integrity but does not by itself complete a usable selective update.
+
+**Choose one policy before ratification:**
+
+- Keep exact byte pins for reproducibility and additionally define validated semantic identity/compatibility over a typed definition and its relevant dependency closure. Cosmetic package bytes may differ while the verified definition remains compatible. Do not substitute name matching or a publisher's unverified flag.
+- Keep strict revision types and provide a first-class, checked compatibility bridge or selected-use migration that works for genuinely unchanged definitions and shows all affected consumers. Specify bridge scope and composition.
+- Deliberately require stable shared vocabulary packages, so pattern-only packages can change while unit/data dependencies stay pinned. This is the smallest initial experiment, but it imposes a package-authoring constraint and does not solve updates to the vocabulary itself. Revise the bundled-definition example accordingly and retain the wider gate.
+
+Do not silently choose structural equivalence for all equal-dimensional kinds: Energy/Torque still require their intended distinction. The needed distinction is between archive reproducibility, definition identity, and permitted use-site compatibility.
+
+### R3 — P1: a valid subjectless analysis case falls out of the proposed pattern closure
+
+**Locations:** A meanings 59–63, root placement 132 and integration 305–307; L publication closure 84–87. Existing transition closure: `crates/oa-core/src/transitions.rs:504–571`; existing kind mapping `:203–235`.
+
+A deliberately makes property/case records layer roots, gives a property an outgoing subject reference, and allows a case's explanatory subject to be absent. L says to include new analysis records whose subject is included. Those rules do not find a subjectless case that binds the included properties. Checks have a case parent and no subject. Following ordinary ownership or outgoing references from the activity cannot recover this inverse relationship.
+
+**Minimal falsifier:** Activity A has property p. Subjectless case C binds p to 750 ms and owns check K. Publish A as a reusable pattern with its analysis. The stated closure can include p because its subject is A, but neither C nor K qualifies by subject. The resulting instance has the property declaration and loses its value/check. Conversely, blindly including every case that mentions p can pull an unrelated project-wide case, its other properties and their architectural subjects into this small pattern.
+
+The same topology matters to OA→SA: selecting A with Subtree currently follows children and outgoing references. New kind mappings alone do not make root-associated p/C/K travel with A. A acknowledges that transition rules need work; this example identifies the decision those rules must make.
+
+**Smallest convergence:** specify analysis selection as its own reviewed export/transition facet: explicitly selected whole cases, their checks, the properties they use, and declared external property/subject bindings. Show omitted analysis on the selected subject instead of silently treating its absence as complete. Do not automatically slice a case's formula graph or copy all mentioning cases. Decide how reusable patterns store shared formulas versus per-instance assumptions. Add a prototype-local neutral-definition reference rule: a pattern's `Local(Duration)` must become the intended pinned imported reference or explicitly copied local definition; it cannot be sent through the ordinary local architectural ID remapper without a category decision.
+
+**Acceptance:** use one subjectless local case and one project-wide case sharing p; publish/instantiate and forward/reverse-transition the small subject with explicit choices. Assert retained/omitted values, checks, external bindings, neutral pins and untouched outside analysis. No graph traversal may choose the scope by accident.
+
+### R4 — P2: complete independent cases duplicate the very formulas and fixed inputs engineers need to maintain once
+
+**Locations:** A candidate ranking 34–41, examples 164–198 and comparison 203. The report already makes D conditional; the current evidence does not justify promoting it over B.
+
+**Minimal falsifier:** two cases use the same inputs/formula/check except one duration. The provided example repeats the unchanged dispatch value, total expression and predicate. Change the requirement threshold once. Unless the engineer locates every separate check, the two cases now test different requirements while both can still reference the same descriptive Constraint. Factoring a calculation helps the formula but still adds parameters/calls/bindings and does not automatically share the check's threshold.
+
+At a larger, explicit scale, take 20 fixed inputs and 2 varying inputs in each of 20 cases. D needs 440 input assignments before repeated results/checks; baseline plus overrides needs 22 baseline assignments and 38 changed input assignments for 19 alternatives. These are representation counts, not measured human task times. Some independent studies truly need frozen separate assumptions; ordinary engineering properties do not all need that semantics.
+
+**Alternative worth prototyping:** a property contains its baseline value or derived formula; each optional case explicitly names that baseline and stores only deviations, with a single inheritance level. Evaluation reports each input's origin and the captured baseline/case revision. Distinguish “clear override/use baseline” from explicit unbound. No dependence on active UI selection and no unnamed hidden default case. If frozen assumptions are needed, make snapshotting a deliberate operation. This introduces resolution rules but removes routine duplicate authoring; compare the costs, rather than treating record-count simplicity inside the evaluator as user simplicity.
+
+**Decision test:** first single property, two-input budget, 20-case single-threshold change, and intentionally frozen study. Measure source lines/edits, wrong-context changes and native task completion. D can still win if its complete-case isolation proves materially clearer, but the comparison must include maintenance rather than only first creation.
+
+### R5 — P1: crossing OA/SA with mounted data needs an explicit origin and update disposition
+
+**Locations:** L layered-definition policy 69–76, pattern layer policy 70 and acceptance L9; A transition rule 307. Existing transition mapping includes DataType/DataField/Interface/ExchangeItem (`transitions.rs:213–233`) and follows the required contracts (`:522–539,568–571`).
+
+**Minimal falsifier:** an OA activity exchanges a mounted library item. Transition the activity/exchange to SA. Same-layer validation requires a compatible SA item. L forbids treating the OA mount as an SA mount, and a package need not provide a matching SA export. Current transition behavior can copy the shared data body into ordinary local SA records; retaining a mount marker would instead violate the immutable mapped payload/layer. Neither outcome has a specified library lifecycle in the proposal.
+
+**Smallest convergence:** offer an explicit choice in the transition plan: bind a compatible exact-pinned SA export, or create a local SA derivative with preserved origin/trace and clearly defined independence from library updates. If neither is valid, return a located unresolved decision. Do not silently fork, silently retain an invalid mount, or require the engineer to abandon the whole normal transition workflow. The reverse transition and later source-library update must follow the same policy. An ordinary OA/SA semantic trace may record correspondence, but it cannot pretend that package update management was preserved.
+
+This does not require globally neutralizing existing data semantics. It is the concrete test that should decide whether the hybrid mount adapter is simpler than uniform qualified data definitions for this product.
+
+### R6 — P2: anonymous derived dimensions need a closed intermediate operator algebra
+
+**Locations:** A ScalarType 75–80, Expr 118–129, numeric rules 223, unit table 229–241 and public value 271.
+
+**Minimal falsifier:** evaluate `AsKind(Energy, f1 * length1 + f2 * length2)`. Each product is specified to become an anonymous dimension. Addition is specified for equal resolved kinds; anonymous values have no such kind. The sketch contains no intermediate type, so accepting, rejecting or requiring two inner `AsKind` operations is currently an implementation choice. Similarly examine `(mass * length) / time`, comparison of two anonymous products, and anonymous dimensionless intermediates.
+
+**Smallest convergence:** define a separate closed internal type algebra including anonymous dimensions, then specify every permitted unary/binary/conditional/Sum combination and when the result retains or loses a named kind. It is reasonable to require explicit kind assertion at a declared output; it is unnecessary to force extra casts solely because the internal type sketch is incomplete. A worked force/energy expression should compile to an unambiguous typed tree before GUI/schema work. The conservative affine point/difference table itself does not supply a counterexample and can be retained.
+
+### R7 — corrected during review: source numeric limits must admit every accepted exact literal AST
+
+**Locations:** A exact-number contract 217–221. Existing round-trip obligation: `docs/language.rst:1772–1781`; the server explicitly compares the formatted/recompiled model (`store.rs:116–122`).
+
+Initial proposal allows a 4,096-bit canonical wire numerator/denominator, but source numeric tokens have at most 256 decimal digits and exponents at most 128. `Expr::Number(10^300/1)` is within 4,096 bits, yet its integer spelling has 301 digits and its compact decimal exponent exceeds 128. Rewriting it as arithmetic changes the AST; full model parity is not merely numerical equality. A read-only .NET BigInteger calculation confirmed the 301-digit spelling; no evaluator was run.
+
+The analysis author acknowledged and corrected this in the frozen section 4.2, now lines 238–244: up to 1,234 decimal component digits, actual 4,096-bit admission bounds, canonical integer/ratio output, and explicit literal AST/source/JSON closure. I read back that correction. The added `1/2^4095` example also avoids demanding an excessively long terminating decimal spelling. This closes the identified proposal contradiction; runtime conformance remains untested. Boundary tests must construct an accepted literal via JSON and round-trip the exact AST through source; testing only arithmetic results misses the problem.
+
+## Choices that can converge now
+
+| Product criterion | Candidate comparison | Recommended next decision |
+| --- | --- | --- |
+| Small-model text and graphical authoring | Baseline properties avoid a compulsory separate case/binding for each first value; complete cases make assumptions explicit but add authored records. | Prototype B and D against the same maintenance tasks. Do not ratify D from evaluator simplicity alone. |
+| Maintained reuse with ordinary local editing | Materialized patterns preserve the current authored graph; virtual instances introduce another effective-model/override system. | Keep materialized patterns and exact typed three-way comparison. |
+| Offline reproducibility | Exact pins, supplied package bytes and immutable revision imports are consistent across both designs. | Keep them; separate type compatibility from archive identity under R2. |
+| Source-only engineering | Full-state serialization is necessary but does not define initial authoring or update resolution. | Require concise authored requests plus explicit normalized management state, or demonstrate an equally usable complete alternative. |
+| OA/SA fidelity | Hybrid mounts preserve current layer checks; uniform references may simplify shared definition identity. | Decide using the mounted-item transition and selective-update falsifiers, not the number of touched Rust files. |
+| Numeric correctness and breadth | Q1 gives a precise rational foundation; approximate engineering functions remain openly unresolved. | A bounded exact experiment is reasonable. It cannot stand in for the declared broader release capability; freeze operator closure and source/JSON limits first. |
+
+No new factual standards claim was needed for these findings. They follow from the proposals, revised user requirements and inspected repository code. Primary standards cited by the authors can inform the later capability boundary but do not resolve Studio's identity or source-authoring choices automatically.
+
+## Deliberately not reported as defects
+
+The exact stored old baseline, immutable original map plus explicit current lineage, typed foreign/live/historical reference categories, no UUID-shaped string rewriting, complete source/package admission, stale-preview rejection, explicit order conflicts and no hidden deletion cascade are coherent requirements. The prototypes must prove them; merely noting that current code does not yet implement new proposals would not be an independent finding. Existing `vacant`/Project identity checks and deletion visitors do require the already-planned reservation/category changes, but that is acknowledged integration work.
+
+The affine point/difference separation, exact rational budget, distinct false/missing/failed outcomes and evaluation purity are compatible as specified. Unsupported trigonometry is openly a scope/acceptance decision rather than a secret arithmetic defect. A future approximate domain needs a justified profile; this review does not prescribe a full solver or all of SysML.
+
+## Focused experiment handoff
+
+Before broad production changes, build one disposable pure model/source experiment that can be driven without Iced: two properties and a budget; a subjectless case; one package containing units, an item/interface graph and a pattern; two consumers and two instances. Exercise R1–R3/R5 together: authored creation, cosmetic package revision, selected update with an outside budget/contract, explicit analysis selection, and OA/SA transition. Add the exact literal and anonymous-dimension expressions from R6/R7. Independently construct expected accepted graphs and rejected decisions.
+
+The useful output is a contract choice supported by the complete small workflow, including the final authored text and normalized metadata. Passing a resolver-only prototype or showing a shorter source excerpt would not answer these questions. Root owns ratification, implementation and all remaining release gates.
diff --git a/harness/release-textual-authoring-design-20260911.md b/harness/release-textual-authoring-design-20260911.md
new file mode 100644
index 00000000..ea559e88
--- /dev/null
+++ b/harness/release-textual-authoring-design-20260911.md
@@ -0,0 +1,214 @@
+# Text-only systems modeling: current source7 and competing authoring designs
+
+Codex-956atf `/root/requirements_delta`, 2026-09-11. Bounded language-design investigation for root synthesis and independent criticism. **No candidate below is ratified, and this report does not accept the release.** Owned files are this report and the isolated `harness/experiments/textual-authoring-20260911/` experiment. No production parser, specification, model, GUI or API edits; no Cargo or native application input.
+
+Read the complete current `PROJECT_GOAL.md`, SHA-256 `DD58F75C93D55FE16ED141A23D834898FB6EBC625AE7E7AE379DA552A8312435`, AGENTS.md, issue #1 through #117, the empty open-PR list and Wiki Home. The Wiki records an older checkpoint. Coordination is in #112. Codex-9e8r6t `/root/palette_finish` independently owns the task-based usability audit and narrow documentation corrections (#113/#116); this report concentrates on language coverage, identity, source/API behavior and architecture alternatives. Their separate small-recipe execution is #117. Root owns integration and all release decisions.
+
+Inspected source began at `865cd55`; the completed probe ran while HEAD was `80cf197be6f1d400bf0b9c84bf8557f88c895aa6`. Root granted a bounded reader lease after its desktop suite, then continued its GUI build. The probe used the completed core artifact identified below and wrote only isolated experiment files. The lease is finished. Source line references identify the inspected local version; the other host's pending documentation corrections can shift them.
+
+## 1. What the evidence establishes
+
+Source7 can express the current persistent OA/SA model through readable declarations, typed values and explicit references. It already supports useful omission of ordinary defaults, nested ownership, forward references, multiple files and identity-preserving updates. A complete engineering example authored without existing GUI records passes the real core. Calling this merely a JSON export, or claiming that text-only modeling is absent, would be incorrect.
+
+The principal weaknesses are the amount of identity and constructor scaffolding exposed during ordinary work, a project-wide alias namespace even across nested modules, limited source-oriented refactoring/navigation, and the absence of an ordinary local check/stabilize/refactor command-line tool. REST can support a source-only workflow today, but engineers must manage the bundle, returned stable identities and revision protocol. Canonical export makes default values explicit and expands small models substantially; it should not be used as a proxy for the best current authored syntax.
+
+**Leading candidate for an experiment:** preserve the current authoritative model and exact source envelope, provide a real source-only tooling path, and trial domain-specific syntax that lowers to the existing model with explicit identity and order rules. Compare that approach against a redesigned declaration/namespace language and against separating identity/history metadata. Keeping today's UUID clauses is a conservative trial boundary, not a conclusion that the new low-clutter requirement has been satisfied.
+
+The newly required calculations, quantities and maintained libraries remain separate semantic work. Better spelling for source7 cannot supply their missing behavior; their unratified designs and paired criticism are in the adjacent analysis/library reports. Conversely, adding those concepts with raw implementation-shaped records would deepen the present authoring problem.
+
+## 2. Complete small example and executable evidence
+
+The complete initial source is [alarm-response.arcadia](experiments/textual-authoring-20260911/alarm-response.arcadia). It has no external includes, libraries, missing GUI-created subjects or unexplained UUID prerequisites. Starting with an empty editor, an engineer can author this file and submit it as one source bundle.
+
+It models a plant operator detecting an alarm and a maintenance team assessing it, explicit responsibility allocation, a pressure-report payload, a capability, a two-contribution operational process and a typed notification scenario. SA adds a console, classification function and chain, an owned state machine, a signal-triggered transition and a reviewed OA/SA refinement. An artifact link identifies procedure evidence, and a saved view places both operational activities. The pressure text is a descriptive annotation under current semantics; it is not an evaluated quantity. The URI is opaque evidence metadata, not a claim that a procedure was fetched.
+
+There are 40 persistent identities: project, 25 elements, 9 relations, one diagram, two placements, one artifact and one trace. The final example produces **no structural errors and no engineering warnings**. That establishes a complete admissible small source example, not the engineering adequacy of every imaginable alarm system or the usability of its rendered diagram.
+
+| Form | UTF-8 bytes | Lines | What was measured |
+| --- | ---: | ---: | --- |
+| Initially authored, new IDs omitted | 4,373 | 110 | Exact initial fixture, already omitting ordinary default fields and nesting owned elements. |
+| Accepted/stabilized | 6,053 | 110 | Actual `compile_bundle` result; 40 explicit UUID clauses inserted, existing source retained. |
+| Canonical single-file export | 9,710 | 344 | Actual `language::format` result from that same model; defaults and flat records emitted. |
+
+Stabilization adds exactly 1,680 characters here: 40 clauses of 42 characters each. That is 38.4% growth over the initially authored text. Canonical export is 2.22 times the initial byte count and 3.13 times its line count. These are representation measurements, not measured task times or proof that fewer characters always improve comprehension. The initial source has 41 reference occurrences, 78 semicolons, 108 brace characters, 10 empty declaration bodies, four `none` tokens and one empty-list token. Display names and aliases are separately required for every named declaration; the two placement declarations omit display names by design.
+
+The saved canonical result contains 39 empty description fields, 18 `none` tokens and seven empty-list tokens. Some of those fields represent required reviewed history, so these counts must not be converted into a blanket deletion rule. The authored example intentionally retains the two empty descriptions in its reviewed trace baseline because historical observations and live defaults have different meanings.
+
+### Probe scope and reproducibility
+
+[probe.rs](experiments/textual-authoring-20260911/probe.rs) was compiled directly with Rust 1.98.1, host `x86_64-pc-windows-gnullvm`, without Cargo. Root designated `target/debug/deps/liboa_core-6c8da57367eec642.rlib` as the completed suite's core artifact; SHA-256 before and after was `D7EFB5F3C423FF8AF13637F53CC481502C7F317BF01E9D072F9EFD64CD954C13`. The successful output is [probe-output.txt](experiments/textual-authoring-20260911/probe-output.txt). The temporary executable was removed after use.
+
+Executed assertions and reported diagnostics establish:
+
+1. Initial source7 admission, all 40 expected identities and no warnings; canonical parse equality and canonical idempotence.
+2. Recompiling the returned stabilized source preserves the exact model and bundle. Recompiling the original missing-ID source allocates a different project identity.
+3. Renaming only a raw declaration alias rejects because its old uses remain unresolved. A prepared `SetAlias` command keeps the UUID and rewrites every live reference while retaining the attached comment.
+4. Reparenting the nested assessment activity to the root through the shared prepared command/updater retains its identity and attached comment. Moving a root participant into an included file preserves the entire model.
+5. Omission of a fresh source7 scenario's viewpoint resolves to `MixedLegacy`. Removing `Action.lifeline none` and `Exchange.message_kind Asynchronous`/`reply_to none` from this fixture leaves the admitted model unchanged.
+
+Actual source snapshots are retained under [observed](experiments/textual-authoring-20260911/observed/stabilized.arcadia): stabilized, canonical, renamed, reparented, and a split entry plus participant fragment. They are separate experiment outputs, not one bundle containing all six alternatives. The initial fixture SHA-256 is `D5E75F60081A7D99B0E10387C5D722064ABBE022A022F376D7BF742AC0D69B0B`; the final probe source is `F62AD8C55DD06B15F59E7A86AC11A7A77505456BE09F5AA96DB7F681280CC18C`.
+
+The probe is deliberately bounded. It did not run HTTP, the native editor, save-journal fault injection, a new language prototype, a performance benchmark or a comprehensive comment-preservation suite. Existing test source supplies additional contract evidence but is not a newly executed result in this report. The first probe had three honest completeness warnings; the final source resolved them with an explicit Flow mechanism, reviewed trace prose and SA chain rather than suppressing validation.
+
+To reproduce after root grants access to a matching built artifact, initialize `.local/toolchain/env.ps1`, compile the probe with `rustc --edition=2024`, an explicit `--extern oa_core=`, `-L dependency=` and `-C linker=clang.exe`, then pass the initial fixture path and a new isolated output directory. Do not use a different toolchain's or target's rlib and infer a semantic failure from linker incompatibility. A portable product CLI is a proposed outcome, not this developer-only command.
+
+## 3. Current semantic-family reachability
+
+The declaration dispatcher is generated from all supported `Kind::ALL`/layer pairs and all `RelationKind::ALL`, plus diagrams, placements, artifacts and traces (`language.rs:295–330`; `model.rs:69–109,900–925`). The typed resolver then validates closed records (`language.rs:1594–1808`). Thus the following is a current representational coverage map, not a claim that each authoring task has been timed or independently exercised through all interfaces.
+
+All source paths below are under `crates/oa-core/src` unless otherwise stated.
+
+| Semantic family | Current text-only path and evidence | Important authoring cost or boundary |
+| --- | --- | --- |
+| OA participants, roles, work, missions and capabilities | `oa_entity`, `oa_actor`, `oa_role`, `oa_activity`, `oa_mission`, `oa_capability`; nested decomposition plus explicit allocation/role/mission/capability relations. `docs/language.rst:94–102,210–224,240–290`; `examples/source-project/oa.arcadia`. | Nesting is containment only. Allocation must remain explicit. Global aliases are needed even for locally nested work. |
+| SA boundary, actors, functions and services | `sa_system`, `sa_actor`, `sa_function`, `sa_mission`, `sa_capability`, `sa_functional_chain`; corresponding relations and explicit OA/SA trace. Same declaration catalog and `examples/source-project/sa/`. | There is at most one System. A function nested inside its performer would be an ownership mistake, not helpful syntax. Source must express functional decomposition separately. |
+| Exchanges, contracts and interfaces | Items, categories, data types/fields, interface allocation, associations, typed ports, communication means, functional/boundary exchanges, exchange/port allocations, interface provision/requirement. `docs/language.rst:103–120,149–165,240–263,303–307`; `examples/release1/oa/types.arcadia` and SA types. | `type_ref { Named @type; };` and nested typed contracts expose constructor wrappers. Functional exchange endpoints require explicit function and port references. GUI convenience may create several records; raw text must supply their full valid graph. |
+| Process/chain contributions and reuse | `steps`, Action/Exchange/Control/SequenceReference occurrences, SequenceLink, ContextualLink with owner/reference path/leaf/purpose. `docs/language.rst:167–205,548–614`; `examples/release1/oa/behavior/process_oa_assessment_procedure.arcadia`. | Owning an occurrence does not add it to `steps`; declaration order does not create precedence. A reusable-call path identifies an occurrence instance, not just a named callee or work element. Simplification must preserve those distinctions. |
+| Scenarios, fragments, calls/replies and reuse | Explicit viewpoint/context; lifelines; Exchange/Action/State/Fragment/Branch/ScenarioUse; ordered membership, bindings, operation identity and qualified MessageValues. `docs/language.rst:617–743`; `model.rs:550–589`; `examples/release1/oa/behavior/scenario_oa_full_scenario.arcadia`. | A repeated message needs a new occurrence identity. Nested Branch/Fragment/ScenarioUse constructors plus explicit membership/binding lists are among the most verbose ordinary authoring forms. No syntax sugar may flatten alternatives or erase caller qualification. |
+| State and mode behavior | Machines, regions, all 13 StateKind vertices, events, transitions, state actions and availability. `docs/language.rst:130–141,745–873`; `examples/release1/sa/behavior/state_machine_sa_monitor.arcadia`. | Hierarchical nesting already removes most `parent` repetition. Event constructors and typed/prose action alternatives still require clear guidance. State geometry and transition topology are separate records. |
+| Constraint and value annotations | `oa_constraint`/`sa_constraint`, message expressions, entry/exit/do/guard/event strings; existing bounded data defaults. `docs/language.rst:142–165,201–205,658–670`. | Existing strings are fully editable but not evaluated calculations or unit consistency. The requirements delta and analysis proposal remain authoritative about the missing numerical semantics. |
+| Saved presentation | `diagram`, `node`, projection constructors, routes/labels/anchors, view options and categories; `docs/language.rst:1009–1176`; `examples/release1/views/`. | Every persistent view field is representable. Typing coordinates is possible, but default overlapping placements do not make a good diagram. Derived camera/selection/viewport data must stay outside the semantic source. |
+| External artifacts | `artifact` with owner, relation, URI, revision and fragment; `language.rs:1778–1788`; the complete example above. | Opaque URI/revision strings are usable from text; their existence is not evidence of retrieval, synchronization or file execution. |
+| OA/SA trace and review history | `trace`, endpoints, kind, prose baseline and typed semantic facets; `docs/language.rst:1179–1490`; `language/semantic_source.rs`; `harness/fixtures/release1-native/semantic-keyed.arcadia:392–430`. | Historical `uuid` literals may refer to deleted records. Their keys/review observations are meaning-bearing state, not optional comments or live `@alias` links. Manual history authoring is expressible but burdensome; review APIs can generate the accepted facts. |
+| Whole-document lifecycle and modularity | SourceBundle, explicit includes, source migration, exact updater; `docs/language.rst:1645–1885`; `source_project.rs:286,780`; `source_files.rs:329,819,1041`. | Pure compilation reads supplied files only. Current bundles reject unreachable supplied files; desktop draft buffers may additionally retain detached files before selecting the reachable bundle. No package namespaces or maintained library imports are implied by includes. |
+
+This covers all major current semantic families, including the less visible historical and presentation data. The general parse/format contract represents all persistent fields, but it does not prove comfortable authoring. Maintained cross-project library semantics and evaluated analysis remain unimplemented extensions; text/GUI/API designs for them must enter the same coverage matrix before the intended full end state can be accepted.
+
+## 4. Identity, references, partial edits and source-only workflow
+
+### What current identity actually means
+
+`Project`, elements, relations, views, placements, artifacts and traces share one UUID and alias namespace (`docs/language.rst:48–56`). Display names may duplicate. An alias is a stored source label, not identity derived from a name. Aliases are ASCII identifiers; strings may carry Unicode and JSON escapes (`:1578–1590`).
+
+`Parser::optional_id` allocates a fresh random UUID whenever the clause is absent (`language.rs:1226–1228`). `compile_bundle` inserts those IDs and recompiles the actual returned bytes before migration/indexing (`source_project.rs:286–300,660–675`). A plain `language::parse` returns a model but no stabilized source bundle. Lint is read-only and addresses raw bytes; accepting lint output does not preserve allocated identities. These distinctions matter to every external editor or AI client.
+
+| Operation | Correct current source-only behavior | Failure or cognitive trap |
+| --- | --- | --- |
+| Create from empty input | Author a complete header and declarations with new IDs omitted; compile/import the bundle, adopt its returned stabilized files. | Repeatedly submitting the original missing-ID buffers creates new identities even if names and text look unchanged. |
+| Change a display name | Retain alias and UUID; edit the quoted name. | Recomputing the alias from that name changes references unnecessarily. Trace drift may properly require a later review. |
+| Rename an alias | Change its declaration and all live uses in one complete source candidate, or use the documented SetAlias command and retrieve updated source. The probe verifies both rejection and safe rewrite paths. | Editing only the header does not trigger automatic refactoring during parse. Blind text replacement can corrupt prose, comments and historical values. |
+| Reidentify | Use explicit identity-change commands when retaining original-record provenance is intended. | Raw replacement of a UUID is removal/creation correspondence, not an inferred rename. Same name or alias is insufficient to preserve history/comment ownership through identity swaps. |
+| Add one new declaration to an existing file | Keep established IDs; only new headers may omit them. | Reformatting the whole file without old IDs can replace the entire modeled graph. A safe client must retain the returned source after every accepted change. |
+| Reparent a nested element | Move the declaration into its new valid owner's body or to root, retaining IDs; remove/update an explicit parent if needed. Prepared Reparent performs that relocation with source trivia. | Lexical nesting and an explicit parent cannot disagree. Moving a work element must not silently alter allocation. |
+| Move a declaration between files | Retain IDs and semantic parent; update includes and supply all reachable files atomically. The probe verifies a root participant move. | An include does not create a namespace or implicit owner. Moving a nested declaration to a top-level fragment without an explicit parent changes its model meaning. |
+| Delete a referenced record | Supply a complete repaired candidate or use a typed command with the current deletion policy. | Raw source deletion does not automatically remove every dependent relationship or ordered reference. Structural rejection is correct; copying a GUI gesture's text deletion alone is insufficient. |
+| Work with incomplete input | Keep raw drafts in the external editor; lint without adopting them; submit a structurally valid complete bundle when ready. Explicit permitted holes warn. | Parser admission may stop at the first syntax/conversion error. Partial declaration indexing is not a partially accepted model or per-file transaction. |
+| Preserve review/history | Keep full accepted source plus semantic model snapshots; retain historical `uuid` forms exactly. | Replacing historical UUID observations with current alias lookup loses facts after deletion or reidentification. Server revision storage is not desktop Undo history. |
+
+`update_bundle_with_identity_map` checks the old compiled index/model/bundle and requires valid correspondence before writing (`source_project.rs:780–849`). The updater retains omitted defaults, literal spelling, comments between `@` and alias, list-entry comments and CRLF/LF choices under defined ownership rules (`docs/language.rst:1812–1876`). Existing tests target identity swaps, nested cross-file moves, orphan comments, constructors and keyed lists (`tests/source_identity_updates.rs:37,81`; `tests/source_nested_updates.rs:49,77,244,345`; `tests/source_project.rs:900,966,1030,1097`). These are valuable obligations for a new grammar, not incidental old-editor behavior to discard.
+
+### Genuine source-only access today
+
+The REST service can import an authored complete SourceBundle through `POST /api/v1/projects/import-source-project`, retrieve it with `GET .../source-project`, and accept a conditional complete `PUT` (`docs/api.rst:85–99,164–204`; `oa-server/src/routes.rs:339,505,519`). A client can prepare ordinary text files, encode them as the documented `entry`/`files` JSON transport, import, read back stable files, edit, validate and submit using the returned ETag. JSON encoding of transport strings is not intended to be the engineer's modeling notation.
+
+Candidate validation and typed command transactions are available without graphical controls (`docs/api.rst:115–120,288–302,445–464`). SetAlias/Reparent commands use the same admission and source updater. Transition and semantic-review query/preview/apply endpoints mean text-only clients need not hand-construct every counterpart baseline. The operation still requires an explicit command/decision; ordinary source does not contain executable transition instructions.
+
+The service currently requires an existing project resource for candidate validation, so it is not a standalone offline compiler endpoint. It retains only the current revision, and writes require the captured ETag; a source-only client must retain draft and accepted snapshots itself (`docs/api.rst:13–49`). `/source` is canonical model interchange, whereas `/source-project` preserves authored text. Substituting the former can defeat a client's goal of retaining its files and comments.
+
+The shipped desktop command-line entry has only `--write-example` before launching Iced (`oa-desktop/src/main.rs:36–48`). The server binary exposes service configuration, not check/stabilize/refactor commands (`oa-server/src/main.rs:31–78`). Core has useful filesystem loading/saving and pure APIs, but writing a custom Rust client is not a comfortable systems-engineer workflow. There is no inspected ordinary local language CLI or LSP route providing source-position definition/references/rename. Existing desktop `source_navigation.rs` is literal per-buffer search and line navigation; model-to-source selection and file-tree navigation are useful but different facilities.
+
+## 5. Where noise is avoidable, and where explicitness carries meaning
+
+The current source7 file authored above is already much smaller than generated release examples. Across the actual 30 `examples/release1` Arcadia files, the summed UTF-8 size is 78,414 bytes with 258 identity clauses, 357 empty-description occurrences, 34 `parent none` fields, 29 empty lists and 69 `none` tokens. These lexical counts include historical records; they do not prove that all matching text is redundant. The independent other-host audit in [Gitea #113](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/1#issuecomment-113) reports the same 30 files/258 UUID clauses and complementary process/reuse counts. Neither corpus is a performance benchmark.
+
+Four distinct problems should not be conflated:
+
+1. **Default-heavy presentation.** Empty descriptions, root parent-none fields and ordinary defaults can often be omitted in authored declarations already. Current canonical export intentionally writes them, and graphical creation uses canonical declaration text. A minimal authoring writer could improve newly created material without changing semantics. It must compare against versioned parser defaults, preserve explicitly authored values and retain required historical observations.
+2. **Structural wrapper noise.** `body { Action { action @work; lifeline none; }; };`, `type_ref { Named @type; };` and Fragment/Branch/ScenarioUse records make engineers read metamodel serialization structure. These need a domain-focused surface syntax if an authoring trial finds the wrappers impair work. They are typed constructors today, not embedded arbitrary JSON, but their shape still reflects implementation records.
+3. **Repeated relationship authoring.** A sequence owner has a `steps` list and separate owned occurrences. Fragment/branch membership and reuse binding maps have related duplication. Some of this can be generated from one explicit authored block, but contribution order, precedence, containment, exchange occurrence and reuse identity must remain distinct. Simply interpreting all nested declarations as an execution order would change semantics.
+4. **Identity, history and modular context.** UUIDs, reviewed fact records and global aliases create cost that ordinary whitespace formatting cannot remove. Moving these facts elsewhere requires an exact portable representation and conflict rules; hiding them in a desktop cache would break the new text-only requirement.
+
+The specification also has concrete inconsistencies, independently checked before any redesign:
+
+| Finding | Evidence and current disposition |
+| --- | --- |
+| EBNF version is stale | `docs/language.rst:1599` says `arcadia 5`; its header and actual parser support current 7. The other host owns the correction in #116. |
+| Fresh-source viewpoint default is misleadingly documented as legacy-only | Catalog `:123` and scenario discussion `:631` frame omission as schema-2 hydration; `language.rs:1668–1671` sets MixedLegacy whenever the field is absent. The probe confirms fresh source7 behavior. #116 owns explicit guidance, with no semantic default change ratified. |
+| Constructor-required-field rule overstates current admission | `docs/language.rst:157–165,173–176` says supplied constructors require all fields and frames new message-field defaults as schema-2 exceptions. `model.rs:550–566` and the probe show fresh source7 accepts omitted Action.lifeline and Exchange.message_kind/reply_to while producing the same values. Root should reconcile the normative rule and actual supported defaults deliberately, preserving accepted source, rather than silently tighten the parser. |
+
+This last inconsistency matters directly to authoring counts: a reader following the general specification may type more fields than the implementation needs. It also shows why a new syntax must have executable normative examples and versioned default rules instead of relying on implicit Serde behavior.
+
+### Larger-model needs
+
+All includes and nested declarations share one global alias namespace (`docs/language.rst:1692–1696,1759–1762`). There is no current package-level scope or import alias resolution. Engineers must invent globally unique labels such as `oa_left_controller_pressure` even when the file tree already communicates much of that context. Splitting a project into files reduces local display size; it does not provide scoped names, cross-file reference navigation or independently compilable semantic modules.
+
+Core bundles allow 256 files, depth 32 and 1,024 include edges by default. Desktop policy permits 64 MiB/100,000 graph objects/250,000 records/500,000 ordered references; REST currently limits model/source-bundle JSON to 8 MiB and 5,000 graph objects/20,000 records/50,000 ordered references (`oa-desktop/src/source_workspace.rs:9–18`; `docs/api.rst:137–153`). These different capacity policies must be visible in a large-model workflow. A model that opens locally may not fit the hosted service. Neither raising the cap nor claiming core has an unlimited parse entry establishes responsive editing.
+
+SysML 2.0 distinguishes namespaces, owned/non-owned memberships, imports and qualified names rather than equating a physical file with semantic scope. That is a useful candidate vocabulary for Studio's unresolved naming design; importing the entire SysML metamodel is not required by this evidence. [OMG SysML 2.0, §7.5 and §8.2.2.5](https://www.omg.org/spec/SysML/2.0/Language/PDF)
+
+A Studio namespace choice must define whether scope is a model record or an authoring-only name environment; how it relates to current globally stored aliases; how moving a declaration affects references; what happens on local/imported name collision; and how GUI/REST display and rename the same identity. Secretly encoding qualified source names into generated global aliases would merely move the mismatch unless both representations and their mapping are specified.
+
+## 6. Materially different candidates
+
+Ratings are provisional judgments, not user-test measurements. Low implementation cost alone cannot win against the user's full authoring requirement.
+
+| Candidate | Small-model effort | Large-model context | Identity/comment/history continuity | Main failure mode | Provisional treatment |
+| --- | --- | --- | --- | --- | --- |
+| A. Current grammar with an authoring writer, source CLI and semantic navigation/refactoring | Removes generated defaults; allows ordinary editors to check, stabilize and rename safely. Existing short declarations remain. | Better tools and file navigation, but still global aliases and constructor/list burden. | Strongest reuse of current invariants and updater. | Mistaking tooling and hidden/folded UUIDs for a sufficiently clear language. | Useful baseline and immediate vertical infrastructure; insufficient alone if the measured wrapper/context burden persists. |
+| B. Domain-specific syntax over the same authoritative model, with an explicit CST-to-model mapping | Can replace constructor wrappers and duplicated membership with coherent domain statements/blocks. | Reuse syntax and selective qualified references can improve context, but namespace policy still requires a decision. | Existing UUIDs and model commands remain; updater must preserve the chosen syntax and each generated record's stable identity. | Sugar becomes a one-way macro that canonicalizes after GUI edits or recreates hidden occurrence/link IDs. | Leading bounded experiment, paired with A's real source-only tools. Must lose if lossless bidirectional mapping is too fragile. |
+| C. Redesigned declarative grammar with explicit semantic namespaces and integrated domain notation | Potentially most coherent whole-model authoring rather than accumulated keywords. | Can supply real local/imported naming and domain modules. | Highest migration cost; must map every accepted record, historical fact, source comment and ID, and preserve raw invalid drafts with their original language context. | Large new language designed before identity, library and analysis semantics are settled; coercing current ownership into namespace ownership. | Serious competitor for the full end state; require one complete alternative model and adversarial migration prototype before selection. |
+| D. Separate engineering text from explicit portable identity/layout/history management modules | The ordinary model file can become much cleaner. | Management data can be localized, but cross-file consistency and conflict review become essential. | Possible only if all modules are version-controlled, portable, typed and atomically admitted; no ambient cache or hidden database authority. | Renaming/moving text breaks sidecar identity, Git conflicts silently remap records, or text-only users must manually rebuild baselines. | Evaluate independently from surface grammar. Could complement B/C, but does not get assumed merely because the sample looks cleaner. |
+
+A and B are materially different: A changes authoring tools and generated output without new syntax; B changes where an engineer states semantic facts and therefore requires a new lossless parser/updater mapping. C changes the overall naming/declaration model. D changes the location and authority of persistent identity/history metadata. Combining selected parts must be justified by the same complete workflow, not by accumulating every feature.
+
+### Identity alternatives that must be tested explicitly
+
+| Identity mechanism | Benefit | Non-negotiable problem to solve |
+| --- | --- | --- |
+| Current inline UUID clauses | Self-contained and stable through rename/move; exact existing model/API compatibility. | Visually long; initial missing-ID buffers must be replaced by stabilized output. Folding cannot be the sole means of understanding ordinary text. |
+| Short stable authoring anchors with an explicit UUID map | References and headers can be shorter while public model UUIDs survive. | Define map authority, duplicate/absent anchors, merge conflicts, copy/fork, reidentify and baseline references. Anchor allocation cannot depend on current file position or display name. |
+| Separate identity management module keyed by a stable declared anchor | Keeps normal engineering declarations compact and portable metadata explicit. | Ordinary rename/move must keep the anchor/map coherent without invisible GUI state; partial commits and hand edits need precise diagnostics. An alias-only map is not rename-stable. |
+| IDs derived from names, file paths, AST positions or full content | Looks convenient for a fresh example. | Routine rename/move/edit changes identity and destroys required continuity. Reject as the durable identity mechanism unless an independent stable anchor is introduced, which returns to another candidate. |
+
+None of the alternatives may reinterpret historical UUID observations as live aliases. A management module also cannot store only a hash of a required old baseline if the actual old facts are needed for review or library updates. Exact data and corresponding source must travel in the portable model envelope.
+
+## 7. Preferred experiment and its falsifiers
+
+Trial B against A and one representative C/D form, retaining current UUID identity and the current model for the first prototype. Keep parser language version and semantic schema version conceptually distinct: a syntax-only form need not pretend that its mathematical or Arcadia semantics changed. The repository currently couples them through `Project.schema_version`; separating them is itself a public-format decision, not an implementation shortcut.
+
+Current bundle migration already stabilizes missing IDs first, then upgrades valid older source using targeted token changes, preserving file placement, nesting and comments (`source_project.rs:286–300`; `source_project/source_migration.rs:4–105`). Earlier readers explicitly reject later fields rather than silently absorbing new semantics. A new grammar must retain frozen source7 interpretation, including its actual default behavior, and provide an explicit migration report and reviewable diff for accepted documents. Invalid raw drafts cannot be semantically migrated as if they were valid ASTs: retain their exact bytes and original language context beside the accepted snapshot. Desktop recovery already distinguishes clean accepted buffers from dirty/unapplied ones (`oa-desktop/src/source_workspace.rs:34–48`). A successful accepted-source migration must not erase such a draft, rewrite a historical observation into a live reference, or regenerate occurrence identities from new syntax order.
+
+For example, a **proposed, currently invalid** process authoring block could be:
+
+```text
+oa_process triage "Alarm triage" id "...existing process UUID..." {
+ contributions {
+ action detect_once "Detect this alarm" id "...existing occurrence UUID..." do @detect_alarm;
+ action assess_once "Assess this alarm" id "...existing occurrence UUID..." do @assess_alarm;
+ }
+}
+sequence_link assessment_after_detection "Assess after detecting" id "...existing link UUID..." {
+ source @detect_once; target @assess_once;
+}
+```
+
+This spelling is not a promise of a finished grammar. In the candidate, each `action` explicitly owns one ordinary Action occurrence with the displayed identity and derives the process's ordered `steps` membership from block order. The existing SequenceLink remains explicit: contribution order is not an invented execution edge. The block cannot coexist with a conflicting `steps` field; it cannot absorb independently owned callee contributions, silently create a reuse clone, or allocate fresh IDs on every parse. The three UUID placeholders deliberately expose the unresolved real header cost; they must be replaced by actual accepted IDs in a measured prototype.
+
+A message/fragment/reuse form needs similarly explicit ownership, occurrence identity, viewpoint and bindings. A blanket `send A -> B` shortcut is inadequate if it conflates work exchanges, participant boundaries, operation allocations, receiver lifelines and repeated occurrences. A data-field shorthand needs exact optionality/multiplicity/type/default semantics. A state-transition shorthand must preserve trigger alternatives, local/internal/external kind, typed work effects and descriptive text. These are required next representatives, not assumed consequences of implementing `contributions`.
+
+Required implementation laws for any selected form:
+
+- Parsing and lowering produce the same complete Project as the explicit current form, including all IDs, list order, views, artifacts and historical facts.
+- Missing identities normalize once into the accepted portable source. Repeated admission is inert. All generated persistent records have explicit stable source identity; position-based IDs or unrecorded macro expansions fail.
+- GUI/REST edits update the original selected notation, preserving unaffected token bytes and comments. A parser that can import the new form but an updater that rewrites it into canonical source7 fails bidirectionality.
+- Source locations identify the actual authoring token responsible for each lowered property/reference. There are no synthetic error spans into an invisible expansion.
+- A reference edit can preserve comments and historical facts even when two model records share a source construct. Constructor changes, reorder, nested move, UUID swap, delete/reuse and removal of an optional field have specified trivia ownership.
+- A full accepted source bundle and model share one atomic admission/history/revision boundary. Invalid drafts remain drafts. Applying a semantic command cannot silently resolve an unrelated source draft.
+
+The main falsifiers are concrete: two repeated uses of one work item receive the same occurrence identity; moving a nested declaration changes its performer; reordering a contribution block invents SequenceLinks; GUI Rename destroys the authored short syntax or comments; changing a file alias remaps historical facts; or a source-only update requires opening a GUI form. Any one invalidates that candidate until its representation changes.
+
+## 8. Minimum vertical slice and full end-state obligations
+
+The first coherent production slice should make **one complete OA/SA model developable and maintainable through ordinary text tools**, while establishing reusable lossless language infrastructure. It should not consist only of a new keyword.
+
+1. Publish an exact source-only check/stabilize/refactor path. A local CLI over existing core/source-files APIs is the strongest initial candidate: read an explicit root file, diagnose raw bytes, write accepted stabilization only on request, preserve external-change detection, and expose explicit semantic alias rename/reparent with a reviewable file diff. Reuse the same protocol in an editor/LSP adapter later; do not make a network service mandatory for local modeling.
+2. Reconcile the proven current version/default documentation inconsistencies with executable fixtures. Respect the other host's ownership of EBNF/MixedLegacy corrections. Specify constructor omissions by version and keep existing accepted source meaningful.
+3. Implement one lossless domain-syntax representative only after the comparison: operational process/SA chain contributions plus their ordering and existing explicit relationships, with the complete alarm model retained. Extend the syntax-to-model location/update mechanism so a later scenario/data/analysis form does not require another ad hoc text-rewrite subsystem.
+4. Drive initial creation, display-name and alias rename, reparent, duplicate-name reference selection, file split/move, raw invalid edit and repair, counterpart review and an ordinary graphical edit through that same model. Preserve full models and exact unrelated source bytes; use API ETags and desktop accepted history where appropriate. A text-only engineer must not need to manufacture private Rust enums or edit GUI-only metadata.
+5. Compare the real initial **and stabilized** A/B/C/D texts, including all identity/history management modules. Measure completion and error recovery on the small model and on a generated large model with repeated local names, nested reuse, multiple files and 5,000 graph objects under the service cap. Also characterize the desktop's larger declared policy separately; do not extrapolate service measurements to 100,000 objects.
+
+For that comparison record source bytes, required fact entry, edited lines for a one-record change, wrong-reference/ownership/order errors, completion time, diagnostic locality and measured parse/lint/apply latency. Include an engineer and an independent AI client working only from the published contract. Count total portable source, not just the pretty primary file. Establish hardware and practical latency thresholds before judging outcomes; no performance number is supplied by this report.
+
+The broader release still requires a coherent notation for every current family in section 3 plus the selected calculation/quantity/library semantics, a deliberate naming/namespace decision for large models, practical source navigation/refactoring and accurate reST/API contracts. It must carry exact library pins and update bases, support real calculation results and formal constraints, and preserve graphic/text/API equivalence. Intermediate compatibility forms can be retained when explicit, but an unbounded collection of implementation-shaped escape hatches cannot stand in for the requested usable text-only language.
+
+Root should record the candidate comparison, selected identity/default/namespace policies and migration obligations before major parser work. The current evidence justifies a complete source-only workflow and a focused bidirectional syntax experiment; it does not yet justify a wholesale rewrite, a hidden sidecar, or a claim that current source7 already meets the new usability end state.