Context
T2.9 of the #13349 map — NON-VIEW, unblocked NOW under the design-frame boundary (app-lifecycle is pipeline-side).
The Problem
Created widgets must be first-class objects the whole module reasons over — the pane chrome, follow-up mutation targeting ("make THE GRID bigger"), lifecycle controls, and T4 persistence all need one authoritative registry of live created instances. Without it, each surface re-derives "what exists" from the component tree — the reach-around the #14642 boundary contract bans.
The Fix
A create-module store (apps/agentos/view/create/store/ per convention): one record per created instance — {instanceId, blueprintSchema, title, createdAt, state (live|disposed), paneRef, blueprintSnapshot} — written on accept+instantiate, updated on mutation/dispose. The store is the T3 context-binding's resolution source and T4's serialization source. Explicit-import consumption only.
Acceptance Criteria
Contract Ledger
The consumed store API (backfilled at review request — PR review cycle 1):
| Surface |
Contract |
Consumer |
| Record fields |
instanceId (key) · blueprintSchema (string id, e.g. grid@1) · title · createdAt (ISO, display-only) · creationIndex (Int — the ordering authority) · state (live|disposed) · paneRef (null until pane mounts) · blueprintSnapshot (registry-owned object) |
pane chrome (render), T4 (serialization) |
registerCreated({instanceId, blueprintSchema, title, blueprintSnapshot, paneRef?}) |
fail-closed {accepted, reason, record}; refuses missing identity, non-object snapshot, duplicate id; stamps createdAt/creationIndex/state:'live' |
#14689's insert registrar |
markMutated(instanceId, {title?, blueprintSnapshot?}) |
only those two fields; disposed records never mutate; unknown keys refused |
#14689's mutation path |
markDisposed(instanceId) |
flips to disposed, record KEPT (history-complete); double-dispose refused |
#14689's dispose path |
resolveTarget({instanceId?, title?}) |
by-id → any state (callers see disposed honestly); by-title → latest LIVE match; no selector → latest live overall |
T3 context binding ("make THE GRID bigger") |
| Snapshot ownership rule |
blueprintSnapshot is REGISTRY-OWNED: structured-cloned on register and on mutate, never aliased to caller state; non-cloneable content (functions — the executable class) is refused, so it cannot even be stored; consumers treat resolved records as read-only |
every consumer |
Out of Scope
Persistence to disk/git (T4) · pane chrome (SSOT-gated views) · the dock wiring (T2.7) · the stage-insert hook wiring (#14689).
Related
Parent #13349 (T2.9) · #14642 (convention) · #13361 (the mutation mechanism whose product face consumes this) · #14689 (the hook-wiring leaf consuming this API).
Origin Session ID: a5af7cf6-45a3-42db-8a30-f04f4241a55c
Retrieval Hint: "created instance registry store first-class live widgets create module"
Context
T2.9 of the #13349 map — NON-VIEW, unblocked NOW under the design-frame boundary (app-lifecycle is pipeline-side).
The Problem
Created widgets must be first-class objects the whole module reasons over — the pane chrome, follow-up mutation targeting ("make THE GRID bigger"), lifecycle controls, and T4 persistence all need one authoritative registry of live created instances. Without it, each surface re-derives "what exists" from the component tree — the reach-around the #14642 boundary contract bans.
The Fix
A create-module store (
apps/agentos/view/create/store/per convention): one record per created instance —{instanceId, blueprintSchema, title, createdAt, state (live|disposed), paneRef, blueprintSnapshot}— written on accept+instantiate, updated on mutation/dispose. The store is the T3 context-binding's resolution source and T4's serialization source. Explicit-import consumption only.Acceptance Criteria
registerCreated/markMutated/markDisposed) — the hook WIRING into the stage-insert mechanism is #14689's scope (filed as the decomposition's next leaf before this ticket's PR review; its ACs carry the wiring clause).Contract Ledger
The consumed store API (backfilled at review request — PR review cycle 1):
instanceId(key) ·blueprintSchema(string id, e.g.grid@1) ·title·createdAt(ISO, display-only) ·creationIndex(Int — the ordering authority) ·state(live|disposed) ·paneRef(null until pane mounts) ·blueprintSnapshot(registry-owned object)registerCreated({instanceId, blueprintSchema, title, blueprintSnapshot, paneRef?}){accepted, reason, record}; refuses missing identity, non-object snapshot, duplicate id; stampscreatedAt/creationIndex/state:'live'markMutated(instanceId, {title?, blueprintSnapshot?})markDisposed(instanceId)disposed, record KEPT (history-complete); double-dispose refusedresolveTarget({instanceId?, title?})disposedhonestly); by-title → latest LIVE match; no selector → latest live overallblueprintSnapshotis REGISTRY-OWNED: structured-cloned on register and on mutate, never aliased to caller state; non-cloneable content (functions — the executable class) is refused, so it cannot even be stored; consumers treat resolved records as read-onlyOut of Scope
Persistence to disk/git (T4) · pane chrome (SSOT-gated views) · the dock wiring (T2.7) · the stage-insert hook wiring (#14689).
Related
Parent #13349 (T2.9) · #14642 (convention) · #13361 (the mutation mechanism whose product face consumes this) · #14689 (the hook-wiring leaf consuming this API).
Origin Session ID: a5af7cf6-45a3-42db-8a30-f04f4241a55c Retrieval Hint: "created instance registry store first-class live widgets create module"