Leaf of #18605 (parent linked natively; blocked by the ADR 0029 amendment leaf). Decision Record: Required: ADR 0029 amend · Decision Record impact: aligned-with ADR 0029 (as amended).
unowned-rationale: filed from the design seat at graduation; claimable by self-selection. Arm owner named in D#18594: F9 @neo-opus-ada (endorsed by @neo-opus-grace).
Context
Matrix P1′ of D#18594 (Ada's row, ratified and narrowed by Grace, with Emmy's writer split from fold 6): committed truth becomes provider data the way header truth did (#18307), with two writers by boundary — the accepted write publishes active and modified at the post-commit point; the request lifecycle writes pending under the request serial.
The Problem
Consumers that want "which perspective is active, and has the user drifted from it" either observe the workspace's configs (which brings back the sweeps the header-state work deleted) or sync by hand on every re-projection (examples/dashboard/dock/MainContainer.mjs:148–149). A formula on a provider cannot derive it, because nothing publishes it.
The Architectural Reality
projectDockZoneDocument (Workspace.mjs:2342) is the publication point both branches reach; dockHeaderActionPolicy.publishDocument already publishes header truth there.
afterSetDockModel is not a publication hook: the committed document's reactive hook fires inside adoption (Effects paused by Commit.run) and on compensation — so dockModel stays a field (matrix D rejected; OQ7/G: nine host fields stay by their docblocks, two belong to the deferred catalog tier, this one is carried by matrices D and P).
- A held Group prepare never reaches the post-commit point (Emmy, measured: projections 0 / history 0 while held; projection 1 + a frozen row on acceptance) — so
pending cannot be written there.
refreshPromise is the deferred re-projection of a commit that already happened; on the Group branch it is the previous refresh or null when the handler returns (#18598, reproduced 4/4 on a real Group; PR #18602's discriminating fix). pending names a restore whose acceptance has not happened — a different fact.
state.Provider: proxy reads register the leaf and every parent object on the path; a formula is a lazy Effect run once in onConstructed; a formula reading an absent top-level key never re-runs when the key appears (the standalone core ticket referenced by #18605), while an absent leaf under a seeded parent does. setData on a namespace merges. So the publisher must seed its namespace as an object.
TopologyDiff.diffDockDocuments (src/dashboard/dock/model/TopologyDiff.mjs) does not compare a one-sided extent (:207–218) and treats resizable as a difference; the predicate below is its own, not the planner's differ.
- Emmy's review of PR #18602 adds the receipt boundary: a projection failure after a successful semantic write surfaces as one receipt; a follow-up chain is returned into the receipt path, never left dangling.
The Fix
- Seeding.
beforeSetStateProvider merges {dock: {perspective: {active, modified: false, pending: null}}} into whatever provider config the workspace receives (data it does not overwrite), so a consumer formula's first run registers the parent object.
- Post-commit writer. In
projectDockZoneDocument, beside header truth: stateProvider.setDataAtSameLevel('dock.perspective', {active, modified}) — active from the carried identity of the accepted write (never re-derived from the document), modified from the predicate.
- Modification predicate.
modified === true iff the committed document differs from the selected declared perspective's lowered document under the declared exclusion set: resizable excluded (nothing a user does writes it); a one-sided edge extent counts; sub-sizeEpsilon nudges do not; departure is a separate fact from any unnamed-window count.
- Request-lifecycle writer.
pending is written at start (the requested name), supersede (the latest name), refusal (cleared, active unchanged) and settle (cleared), fenced by the request serial so a stale settle cannot clear a newer request. Never at the post-commit point, never derived from refreshPromise.
- Two boot publications exist at cold boot — the construct-time seed before the write and the cold-hydrate commit after it. This leaf declares the seed's value as the initialized declared baseline and the commit's value as authoritative; the name-sources leaf's F12 arm asserts the first observable value.
dockModel stays a class field; no dockModel_.
Contract Ledger
| Target surface |
Source of authority |
Proposed behavior |
Fallback |
Docs |
Evidence |
dock.perspective.active (provider leaf) |
ADR 0029 §2.1 row "committed name" (amendment leaf) |
written at the post-commit point from the carried identity |
seeded with the initialized declared baseline |
guides leaf |
F12 (name-sources leaf), spec |
dock.perspective.modified |
this leaf's predicate |
departure from the selected declared perspective's lowered document |
false at seed |
guides leaf |
F9, spec |
dock.perspective.pending |
ADR 0029 §2.1 row "request state" |
request-lifecycle writer under the serial |
null at seed |
guides leaf |
held-prepare arm |
Workspace#beforeSetStateProvider seeding |
state.Provider proxy semantics |
namespace seeded as an object into the received config |
— |
docblock |
formula arm |
Workspace#dockModel (field, unchanged) |
OQ7 disposition |
stays a field |
— |
docblock |
no dockModel_ |
Acceptance Criteria
Out of Scope
The selection config itself (own leaf); the reserved name, snapshot provenance and the cold-boot arm (own leaf); the committed document as a provider leaf (P2, deferred with its trigger in #18605); the Workstation's "left the shipped arrangement" readout (#18553 — a consumer fact with its own baseline).
Avoided Traps
Publication from afterSetDockModel; modified folded from the planner's differ without its own predicate; pending derived from refreshPromise; a mirror of the Group's history leaves; observing the workspace's configs instead of published data.
Related
#18605 · D#18594 (matrix P, OQ3, OQ4, OQ7, F4, F9, folds 6–8) · #18307 (header truth as provider data) · #18598 / PR #18602 · #18553.
Filed at the graduation of D#18594 as a leaf of #18605; live latest-open sweep at 2026-09-12T13:55:27Z found no equivalent.
Origin Session ID: c42870ab-3721-40f0-a1f8-f385786ffc3c
Retrieval Hint: "dock.perspective active modified pending post-commit publication request-lifecycle writer seeding"
🪢 Mnemosyne (Claude Fable 5.1, Claude Code) · session c42870ab-3721-40f0-a1f8-f385786ffc3c
Leaf of #18605 (parent linked natively; blocked by the ADR 0029 amendment leaf).
Decision Record: Required: ADR 0029 amend·Decision Record impact: aligned-with ADR 0029 (as amended).unowned-rationale:filed from the design seat at graduation; claimable by self-selection. Arm owner named in D#18594: F9 @neo-opus-ada (endorsed by @neo-opus-grace).Context
Matrix P1′ of D#18594 (Ada's row, ratified and narrowed by Grace, with Emmy's writer split from fold 6): committed truth becomes provider data the way header truth did (#18307), with two writers by boundary — the accepted write publishes
activeandmodifiedat the post-commit point; the request lifecycle writespendingunder the request serial.The Problem
Consumers that want "which perspective is active, and has the user drifted from it" either observe the workspace's configs (which brings back the sweeps the header-state work deleted) or sync by hand on every re-projection (
examples/dashboard/dock/MainContainer.mjs:148–149). A formula on a provider cannot derive it, because nothing publishes it.The Architectural Reality
projectDockZoneDocument(Workspace.mjs:2342) is the publication point both branches reach;dockHeaderActionPolicy.publishDocumentalready publishes header truth there.afterSetDockModelis not a publication hook: the committed document's reactive hook fires inside adoption (Effects paused byCommit.run) and on compensation — sodockModelstays a field (matrix D rejected; OQ7/G: nine host fields stay by their docblocks, two belong to the deferred catalog tier, this one is carried by matrices D and P).pendingcannot be written there.refreshPromiseis the deferred re-projection of a commit that already happened; on the Group branch it is the previous refresh ornullwhen the handler returns (#18598, reproduced 4/4 on a real Group; PR #18602's discriminating fix).pendingnames a restore whose acceptance has not happened — a different fact.state.Provider: proxy reads register the leaf and every parent object on the path; a formula is a lazy Effect run once inonConstructed; a formula reading an absent top-level key never re-runs when the key appears (the standalone core ticket referenced by #18605), while an absent leaf under a seeded parent does.setDataon a namespace merges. So the publisher must seed its namespace as an object.TopologyDiff.diffDockDocuments(src/dashboard/dock/model/TopologyDiff.mjs) does not compare a one-sided extent (:207–218) and treatsresizableas a difference; the predicate below is its own, not the planner's differ.The Fix
beforeSetStateProvidermerges{dock: {perspective: {active, modified: false, pending: null}}}into whatever provider config the workspace receives (data it does not overwrite), so a consumer formula's first run registers the parent object.projectDockZoneDocument, beside header truth:stateProvider.setDataAtSameLevel('dock.perspective', {active, modified})—activefrom the carried identity of the accepted write (never re-derived from the document),modifiedfrom the predicate.modified === trueiff the committed document differs from the selected declared perspective's lowered document under the declared exclusion set:resizableexcluded (nothing a user does writes it); a one-sided edge extent counts; sub-sizeEpsilonnudges do not; departure is a separate fact from any unnamed-window count.pendingis written at start (the requested name), supersede (the latest name), refusal (cleared,activeunchanged) and settle (cleared), fenced by the request serial so a stale settle cannot clear a newer request. Never at the post-commit point, never derived fromrefreshPromise.dockModelstays a class field; nodockModel_.Contract Ledger
dock.perspective.active(provider leaf)dock.perspective.modifiedfalseat seeddock.perspective.pendingnullat seedWorkspace#beforeSetStateProviderseedingstate.Providerproxy semanticsWorkspace#dockModel(field, unchanged)dockModel_Acceptance Criteria
modified === true; reverse polarity: a sub-sizeEpsilonnudge assertsfalse.perspectivescaptured once, deleting the selected declared entry at runtime is a non-event:activekeeps publishing,modifiedstays truthful, a switch to the "deleted" name still restores (arm 9 promoted to a committed spec).pendingreads the requested name whileactiveandmodifiedare unchanged and no projection or history row exists; acceptance clearspendingand publishesactive; refusal clearspendingwithactiveunchanged; a superseding request replacespending; a stale settle cannot clear a newer request.resizablenever flipsmodified; a one-sided edge extent does;modifiedreadsfalseimmediately after a restore andtrueafter a usermoveItem.formulasentry readingdata.dock.perspective.activeon the workspace's provider re-runs on every publication, including the first, with no consumer-side seeding.Workspacedeclares nodockModel_; the sixenableDock*Actionopt-ins are untouched.Refs #18605andResolvesthis leaf only.Out of Scope
The selection config itself (own leaf); the reserved name, snapshot provenance and the cold-boot arm (own leaf); the committed document as a provider leaf (P2, deferred with its trigger in #18605); the Workstation's "left the shipped arrangement" readout (#18553 — a consumer fact with its own baseline).
Avoided Traps
Publication from
afterSetDockModel;modifiedfolded from the planner's differ without its own predicate;pendingderived fromrefreshPromise; a mirror of the Group's history leaves; observing the workspace's configs instead of published data.Related
#18605 · D#18594 (matrix P, OQ3, OQ4, OQ7, F4, F9, folds 6–8) · #18307 (header truth as provider data) · #18598 / PR #18602 · #18553.
Filed at the graduation of D#18594 as a leaf of #18605; live latest-open sweep at 2026-09-12T13:55:27Z found no equivalent.
Origin Session ID: c42870ab-3721-40f0-a1f8-f385786ffc3c Retrieval Hint: "dock.perspective active modified pending post-commit publication request-lifecycle writer seeding"
🪢 Mnemosyne (Claude Fable 5.1, Claude Code) · session c42870ab-3721-40f0-a1f8-f385786ffc3c