Guide: Entity Workbench App

Status: Active Source: A workbench-go four-pass UI-conventions reconciliation review; a three-pass workbench-go response (with §13 synthesis); an entity-browser-rust-side UI-alignment review; the Godot app architecture doc; and workbench-go Stage 5 implementation feedback (three findings — §5.3 reframed, §5.4 schema refocused, new §10.1).


What this guide covers

This is a general-purpose application namespace convention built around app/{app-id}/.... The entity workbench — the application class that makes the entity system visible and operable to a human user (tree browsing, handler dispatch, peer/identity management, knowledge-base authoring, and so on) — is the first concrete instance and the reason the convention exists. Future apps using app/{app-id}/... (a calculator, an expanded knowledge-base, third-party apps) inherit the conventions for free.

In scope:

Out of scope:

Cross-links: GUIDE-PEER-CONCERNS-AND-NAMESPACES.md, SDK-OPERATIONS.md (especially §6.4 generation-counter, §6.5 notification access levels, §11.6 handler registration, §15 configuration directory), GUIDE-SDK-PATTERNS.md, GUIDE-PERSISTENCE.md, ENTITY-NATIVE-TYPE-SYSTEM.md §11.1 (type entity path convention).


1. Conformance philosophy

This is an application-layer convention, not an SDK contract. Wire-format invariants live in V7; SDK invariants live in SDK-OPERATIONS. This guide describes how applications under the app/{app-id}/... namespace pattern compose well — three impls of the workbench should be able to read each other's app/workbench-{id}/... state without reverse-engineering language-native struct names; future apps using the same pattern should pick up the same conventions for free.

TierItems
MUSTapp/{app-id}/workspace/... namespace prefix; bundled per-window state as CBOR maps.
SHOULDSchema field names per content-type; canonical action vocabulary; action wire shape with (window_id, event, value) semantics (§6 owns this surface and scopes it to wire persistence/replay — read §6 before treating it as a runtime requirement); per-presentation-context selection propagation.
Idiomatic per implWindow abstraction; renderer; layout primitives; in-memory action shape; change-detection style; renderer-specific decoration.

App state vs app runtime: "everything under app/{app-id}/..." means state, not the app's binary or runtime image. Future "transferable apps" addresses runtime as a separate concern; this guide is about state.


2. Architectural pattern: model → output → renderer

The load-bearing convention. Each window/panel/view is composed of three layers:

The pattern is TEA-shaped (The Elm Architecture). The renderer-neutrality of the output struct is what makes the same model run under multiple renderers — the Go workbench's workbench/ is shared between raylib (canvas/) and tview (console/); Rust's Knowledge Base similarly.

Why the output struct is the convention (not the model interface): the model's shape is per-impl idiomatic (Go uses struct + interface, Rust uses trait + associated type, Godot uses Node + signal); the model's output is data, and data crosses idiom boundaries cleanly.


3. Path namespace

The app/{app-id}/... namespace is general-purpose. {app-id} is a per-instance identifier that the app claims by writing to it — there is no central registry, no reservation mechanism. Apps coexist by claiming different {app-id}s. Multiple instances of the same app type are also supported by giving each instance its own {app-id}.

Concrete examples on a single peer's tree:

/{peer_id}/app/workbench-01/workspace/...        first workbench instance
/{peer_id}/app/workbench-02/workspace/...        second workbench instance
/{peer_id}/app/workbench-dev/...                 development instance
/{peer_id}/app/calculator/workspace/...          a calculator app using the same convention
/{peer_id}/app/calculator-02/workspace/...       second calculator instance
/{peer_id}/app/knowledge-base/...                a KB app using the same convention

Within an app's namespace, the binding decisions on path shape:

app/{app-id}/
    workspace/
        windows/{window_id}/state              bundled per-window state (CBOR map)
        window-index                           the live window set (§4.2a; OPTIONAL, see §8)
        screens/{screen_id}/...                per-presentation-context state (optional; flat apps omit)
        selection                              propagated selection (§5)
    settings/{key}                             global app settings
    manifest                                   OPTIONAL — app metadata, if needed

App-side path helpers SHOULD parameterize {app-id} per GUIDE-PEER-CONCERNS-AND-NAMESPACES.md §4.3. App-id is permissive — claimed labels, renameable, no registration.

The app/{app-id}/workspace/... prefix is the MUST (§1); the segments beneath it are not closed. §3.1 blesses two different sub-shapes, and applications add their own (per-panel selection slots, connection aliases) without ceremony. What is fixed is the prefix, the bundled-CBOR-map shape of per-window state, and the meaning of the slots §4.2 names.

{window_id} is a session-scoped slot address. It identifies which pane is on screen now; it is not required to be stable across sessions and MUST NOT be relied on as a durable key for finding state again. Contrast {app-id} above, which an app claims by writing to it: the guide says so there and deliberately does not say it here. Implementations typically allocate window ids as a per-session counter from 1, which is a correct reading of this sentence.

The consequence is the one every implementer needs before taking §8's persist arm: on a later session, the state entity at windows/3/state was written by whatever window held ordinal 3 last time. A discriminator on entity_type turns wrong-adoption into no-adoption but never makes the right state findable — with ordinals alone, whether a window recovers its state depends on the order the user re-opens windows in. §4.2a is where the durable half lives; §8 makes the choice explicit.

3.1 Path hierarchies — flat or nested

Both shapes are permitted:

flat:    app/{app-id}/workspace/windows/{id}/state
nested:  app/{app-id}/workspace/screens/{id}/windows/{id}/state

Per-presentation-context state (the screens/ layer in entity-browser-rust terminology, "workspaces" in Godot terminology) is optional. Apps that don't differentiate use the flat form. Both impls' state schemas share fields.

3.2 Path-position-invariance

The state schema for a given content-type is invariant under repositioning. The state entity at app/workbench-01/workspace/windows/3/state of type app/state/tree-browser carries the same fields as the state entity at app/workbench-01/workspace/screens/0/windows/3/state of the same type. Impls migrating between flat and nested hierarchies (or composing deeper, like Godot's workspace → layer → slot → panel) do not have schema-rewrite work — only path-rewrite.

Bundled-vs-per-key state: bundled for persisted state (one entity per window, multi-field CBOR map). Per-field outputs may apply at T3 for high-frequency outputs (deferred — see §10).


4. Structural schema vocabulary

4.1 Type names vs paths

A clarifying frame. app/state/tree-browser is a type name. The corresponding type entity lives at system/type/app/state/tree-browser per ENTITY-NATIVE-TYPE-SYSTEM.md §11.1 ("type paths shown without the system/type/ prefix for brevity; resolution prepends this prefix").

4.1.1 Namespace split (normative; absorbed from the cross-impl alignment cycle)

SHOULD (cross-impl portability rule for the entity_type field). Type names written into the entity_type field of entities follow this split:

Disambiguation (load-bearing — entity-browser-rust + workbench-go cosigned). This rule scopes the entity_type field (the type-name string written into entities), NOT the instance-path prefix. The two namespaces parallel each other but apply to different concepts:

Example: an instance of the portable type app/state/selection lives at app/workbench-01/workspace/selection (per-app path, portable type). An instance of the app-internal type app/entity-browser/connection lives at app/entity-browser/connections/{id} (per-app path, app-internal type).

The slot table (§4.2) describes type-name → instance-path conventions; this rule (§4.1.1) describes the type-name conventions independently.

Implementations writing app-specific types SHOULD place them under app/{app-id}/{type_name} rather than appending app-suffixes within app/state/{type_name}. The canonical pattern from entity-browser-rust: app/entity-browser/connection, app/entity-browser/listener-state, etc. (verified cross-impl).

Type-name promotion — when a previously-app-specific type proves convergeable across impls (e.g., connection shape stabilizes), promote it from app/{app-id}/{type} to app/state/{type} and update the slot table. Pre-publication, migration is natural-overwrite acceptable; in-process parsing unaffected. Post-publication this requires a spec amendment with a published cutover schedule (peers running older codebases would not recognize a renamed type without coordination).

4.2 Slot table

Three-impl consensus across Go + entity-browser-rust + Godot.

SlotType name (full type entity at system/type/{name})Status
Per-content-type window stateapp/state/{content_type} (e.g. app/state/tree-browser)Long-term shape. Schemas defined per content-type at T2. Describes portable content-type state only — what the content-type is, not how a particular renderer decorates it.
Generic per-window fallbackapp/state/windowTransitional. For windows whose content-type isn't yet schema'd; lets new windows land before T2 ratifies their schema. Track usage rather than pin a sunset — landing a per-content-type schema graduates that content-type out of the fallback. MUST carry a content_type field naming what the window is showing — see the note below, this is the one slot where the type name cannot supply it.
The live window setapp/state/window-indexMembership, not arrangement. One entity at app/{app-id}/workspace/window-index recording which windows exist. Schema in §4.2a. OPTIONAL — but §8 requires an application persisting per-window state to maintain one or sweep at startup.
Per-screen stateapp/state/screenOptional. Multi-screen / multi-workspace apps use it; flat-window apps omit. Schema TBD.
Selectionapp/state/selectionTwo-layer model per §5; schema in §5.
Settings (key/value)app/state/settingExisting convention. Generic {key, value} pair under app/{app-id}/settings/{key}.
App manifestapp/state/manifestOptional, if needed. Lives at app/{app-id}/manifest under the app's own namespace. Speculative; not reserved unless an impl needs it.
Top-level entity outside the namespacenoneNothing top-level outside app/{app-id}/.... The "AppState" working name was dropped during consensus.
T3 outputsapp/ui/output/{content_type}Reserved namespace. Deferred until SDK affordances (S1 local-only namespace, S2 watch-with-diffs) land. Runtime path is app/{app-id}/ui/output/....
Renderer-specific decorationper-impl, not portableTheme overrides, mouse filters, layer z-index, slot/split ratios, etc. Lives in per-impl runtime/view state. NOT in the cross-impl app/state/{content_type} schema.

Why the fallback needs content_type when the per-content-type slots do not. Under app/state/{content_type} a window's state entity carries a distinct entity_type, so a reader can check it before adopting a payload and refuse a stranger's. Under app/state/window every window's state entity carries the identical type name, so that check does not exist to be made — the fallback is the one slot where a reader has no way to tell what it is holding. An application on the fallback that persists state and cannot name what each slot was has nothing to reconstruct from. The field is cheap and impls reach for it unprompted; requiring it keeps the transitional on-ramp from being more dangerous than the shape it is a stepping stone to.

4.2a The window index — which windows exist

app/state/window-index, one entity at app/{app-id}/workspace/window-index:

windows        [ { id, content_type, peer_id } ]     the live window set

id             uint          the window's session-scoped slot address (§3)
content_type   text          what the window is showing (see "opaque" below)
peer_id        text          which peer's tree the window is bound to; empty = the host peer

What it is for. windows/{id}/state answers "what was window 3's state" and cannot answer "which windows were there, and which of these saved slots is whose." Those are the facts an application needs to restore a session, and without them the only available cleanup is deleting workspace/windows/ wholesale — which is correct, and destroys everything a user would want back. With an index, three things become possible: a window can claim the id it held last session so its own state path resolves to its own state whatever order windows are opened in; fresh ids can be allocated above every id the index knows, so an unclaimed window never lands on an occupied slot; and an exact sweep can delete only state that is unreachable from the index.

Membership is portable; arrangement is not. Which windows exist, of what content type, on whose peer is application state and belongs in the tree. How they are split, sized, stacked, or themed is renderer decoration and stays per-impl per the slot table's last row. This slot carries the first and MUST NOT grow the second.

content_type is opaque. It is a string the writing application uses to find its own factory. Two applications need not agree on its values, because reconstructing another implementation's windows is not a goal — so this slot does not wait on the content-type catalog (§11 item 1) and asks nobody to agree on window names. (This is unrelated to the content_type field §5.4 retires from app/state/selection: that one was source attribution; this one names the window's own content type, which is the fact the index exists to record. A reader gating on retired selection fields scopes that gate to app/state/selection.)

A known bound, stated rather than hidden. (content_type, peer_id) does not distinguish two windows of the same content type on the same peer. Implementations claiming by that pair should let the first window claim and the next allocate fresh. This is a deliberate limit; a richer key is available if a real configuration demands one.

Three implementation warnings, earned in a built implementation:

4.3 Guidance the slot table commits to

The slot table commits to what each slot means. Per-content-type field-by-field alignment happens through ongoing T2 alignment with concrete impl reference points (Go's DetailOutput / ArticleViewOutput, Rust's KnowledgeBaseOutput, Godot's day-one panels). T2 schemas may also ship as types in entity-sdk crate exports, reducing re-derivation cost across impls.


5. Selection scope: per-panel local + per-presentation-context propagated

Two layers exist; the convention accommodates both.

5.1 Per-panel selection (local truth)

Each panel's model holds whatever selection state it needs for its own purposes — tree-browser has a current path cursor, event-log has a current event row, entity-detail has a viewed entity. This is internal to the panel and not propagated unless the panel chooses to.

5.2 Per-presentation-context selection (system-level "what is being attended to")

A higher-level signal at app/{app-id}/workspace/screens/{screen_id}/selection (or app/{app-id}/workspace/selection for flat apps) carries the current focus the user has expressed at that level — typically the most recent action from any panel that opted in to propagate. Other panels in the same context MAY subscribe to this and co-orient (entity-detail follows the focused tree-browser's current path, for instance).

Two tree-browser panels in the same screen viewing different peer trees: each holds local selection; the most recently interacted with also writes to the per-context selection; entity-detail co-orients on the per-context one. Subscription-and-notify is the propagation mechanism.

5.3 Recommended publish-to-aggregate defaults per action

ActionRecommended publish-to-aggregate
navigateyes (context)
selectyes (context)
submitno (panel)
clearno (panel)
set_filterno (panel)
toggle_rawno (panel)

Convention for content-type authors, not runtime dispatch metadata. Implementation experience across the three reference impls (workbench-go Stage 5, entity-browser-rust, Godot) converged on a per-panel-slot + per-context-aggregate model where the publisher already knows at publish time whether to write to the aggregate. The publisher writes:

Under this model, the per-panel decision collapses to a single boolean per publisher panel (typical name: publishesToAggregate). The action-name vocabulary above is still useful for typing the kind of selection on inspection and for guiding new content-type authors picking sensible defaults — it is not a runtime registry consulted at dispatch time. Future impls needing runtime-overridable propagation (e.g. tree-browser configured as "scratchpad navigator" that suppresses context propagation) express it via the publisher's own config — no registry needed.

5.4 Selection schema

Type app/state/selection (full type entity at system/type/app/state/selection). Fields:

path           text?         # focused path the user is attending to
paths          [text]?       # multi-select forward-compat hook (optional)
peer_id        text?         # which peer's tree (multi-peer apps)
type           text?         # type of the selected thing (e.g. "entity", forward-compat for "query-row", "event-row")
updated_at     uint          # epoch ms; staleness signal

Note (absorbed from workbench-go feedback and tightened per the cross-impl alignment cycle). Earlier drafts of this schema included content_type and source_window fields for source attribution ("which content-type / which window emitted this aggregate-level selection"). Implementation experience surfaced that under the per-panel-slot model (§5.2 / §5.3), source attribution is unnecessary: per-panel slots are single-publisher by construction (the slot path identifies the publisher), and per-context aggregate consumers care about the current value rather than the most-recent writer (updated_at handles staleness/tie-breaking).

Normative (absorbed from the cross-impl alignment cycle; cross-impl-aligned posture). Pre-publication stance — no legacy tolerance.

  1. Emit-side (MUST). Implementations MUST NOT emit source_window, source_panel, or source_* fields in app/state/selection entity payloads.
  2. Field rename (MUST). Implementations writing this schema MUST use type (not legacy content_type). The semantic was refocused: type names the type of the selected thing (e.g., "entity"), not the panel that emitted the selection.
  3. Read-side (MUST log violation; MAY reject). Pre-publication, implementations MUST log a violation (severity WARN minimum) when encountering legacy source_window / source_panel / content_type fields in received entities. Implementations MAY reject (return error / refuse to decode) such entities. Silent tolerance is NON-CONFORMANT pre-publication — the entire ecosystem is owned peers, and silent tolerance hides non-conformant emitters that should be fixed.
  4. Re-emit-on-write (MUST). Readers that load a legacy entity and re-publish MUST NOT re-emit legacy fields. Drop them on the round trip.
  5. Post-publication policy (deferred). Once the protocol publishes to uncontrolled peers, a "MUST tolerate (skip-unknown-fields per V7 §2.6 open-types)" rule with a published cutover date will apply. Until that cutover lands, pre-publication strictness is the canonical posture.

6. Action wire shape vs in-memory shape

Status note (absorbed from the cross-impl alignment cycle). The named action-event vocabulary below and the wire shape (window_id, event_name, value) are speculative / schema-anchor for cross-impl wire persistence and replay — NOT a load-bearing runtime requirement today.

Verified cross-impl reality: no implementation in the workbench ecosystem (entity-browser-rust, workbench-go, Godot) currently emits named action events as distinct wire-shape triples. Cross-panel co-orientation rides on selection state updates (writes to app/{app_id}/workspace/selection) in all three impls. The vocabulary below exists as schema documentation — entity-browser-rust's src/action_event.rs explicitly notes "zero in-repo call sites by design... no Rust callers until wire-shape persistence/replay lands."

Normative position:

6.1 Initial canonical events

The list is not closed — content-types add events as needed; new events should be named and registered, not absorbed into a generic catch-all.

EventValue shapeTypical targetsDefault propagation
navigatetext (path)tree-browsercontext
selecttext (path)tree-browser, query-consolecontext
submitemptyexecute-console, query-consolepanel
clearemptyevent-log, execute-consolepanel
set_filtertext (level name)event-logpanel
toggle_rawemptyentity-detailpanel

A set_field catch-all was considered and deprecated. Force every action to be named; entity-typed action history (T3) treats two named actions cleanly while a generic set_field would produce ambiguous replay.


7. Change-detection styles

A panel declares which change-detection style it uses (factory-time content-type metadata). Three styles, all SDK-supported (SDK-OPERATIONS §6.4 + §6.5):

  1. Counter pollinggeneration() checked each frame/tick. May suit immediate-mode UI with simple state where a per-frame check is already happening (raylib loops, egui native).
  2. Path-targeted observationstore().watch(prefix) returning a local event stream. May suit retained-mode UI with many panels watching disjoint subtrees (Godot signal bridge, DOM with retained state).
  3. Dispatched subscriptionsubscribe(pattern, callback) via system/subscription. Required for cross-peer reactivity. May suit any panel watching another peer's tree.

None is the default; panels pick per their rendering paradigm and observation needs. Panels watching their own peer's app/{app-id}/workspace/... state typically use (1) or (2). Panels watching network peer trees typically use (3). The convention names the styles; the SDK supports all three.


8. Persistence interaction

Forward to GUIDE-PERSISTENCE.md, which reads SDK-OPERATIONS.md §15 (Configuration Directory) + SDK-IDENTITY-INFRASTRUCTURE.md §8.4 (identity bundle layout) into developer-readable form.

App state lives in the host peer's tree. There is no separate "app store" on disk. When an application writes to app/{app-id}/workspace/windows/3/state, that's a write into the peer's tree; the on-disk effect is whatever the peer's storage backend does. Apps don't manage filesystem layout — peers do.

Persistent vs ephemeral line, per state kind:

The line between persistent and ephemeral is per-namespace, not per-extension. This guide names the line; GUIDE-PERSISTENCE.md owns the configuration-directory layout.

The persist arm's obligation. Because {window_id} is a session-scoped slot address (§3), persisting per-window state without a way to tell which window a persisted entity belongs to does not preserve a session — it hands the next session's window at that ordinal a stranger's state. An application persisting per-window state MUST be able to determine, at startup, which window each persisted state entity belongs to. It satisfies this by either:

An application willing to do neither MUST NOT persist per-window state. The ephemeral arm is a correct and conformant choice, not a lesser one — an application that does not offer session resumption was never promising the state back, and losing it is bounded and legible. The failure this rule exists to prevent is the third case: persisting, restoring, and silently restoring the wrong thing.

Dispatch-index re-registration (SDK-OPERATIONS.md §11.6.6, callout in GUIDE-PERSISTENCE.md §3.6) — applications MUST re-register their handlers in startup code. The handler manifest in the tree survives restart; the in-memory callable does not.


9. Multi-peer-per-app and multi-app composition

9.1 Multi-peer-per-app

Forward to GUIDE-PEER-CONCERNS-AND-NAMESPACES.md §3 multi-peer-per-app variant.

This composition lets UI ephemera (window state, selection, draft inputs) stay local without leaking to the network.

9.2 Multi-app composition on one peer

A single peer MAY host multiple apps in its tree, each under its own app/{app-id}/... namespace. This is the same mechanism that supports multiple instances of the same app — the convention doesn't distinguish "different instances of one app type" from "different apps."

/{peer_id}/
    app/workbench-01/...                        first workbench
    app/workbench-02/...                        second workbench
    app/calculator/...                          a calculator app
    app/knowledge-base/...                      a KB app

Each app's content-types use the same global type names; each app's instances live at per-app paths. Apps coexist by claiming different {app-id}s.

Future-tracked, not specified here: capability boundary between apps; discovery between apps; rendering primitive sharing across apps. The convention permits coexistence today; richer multi-app stories belong to a future capability/multi-app guide.


10. CLI / shell as a render context

The workbench application is one application; CLI / TUI / GUI / canvas / web / game-engine are render contexts of that application, varying in interaction affordances and interaction model. A CLI workbench uses the same app/{app-id}/... namespace, the same content-types, the same action vocabulary, the same model→output pattern — it's a thinner renderer over the same models.

What the convention commits to (for any render context):

What the convention explicitly does not commit to:

The interaction model is in active discovery — long-term we expect CLI surfaces to vary the way bash, zsh, and fish vary on top of POSIX.

10.1 Action events vs commands — two vocabularies, one render context

A CLI render context surfaces two distinct vocabularies that coexist. The application's action vocabulary (§5.3, §6) is the pub-sub plane shared by every render context; a CLI additionally exposes a command vocabulary for direct invocation. They are not the same vocabulary:

DomainWhat it isScope
Action events (§5.3, §6)Pub-sub: "user attended to X", "user submitted." Wire shape (window_id, event, value). Named vocabulary (navigate, select, submit, clear, ...).Application-agnostic. Two distinct apps use the same action names with the same semantics.
Commands (CLI-only)Imperative ops: cd, ls, connect, mount, exec, revision config put. Args + typed Result. Dispatched through a registry.Application-directed. Each app's CLI render context has its own command vocabulary.

How they intersect:

Implications for impls:


11. What's not in this guide (yet)

Tracked as future work:

  1. Content-type catalog. The current direction is shifting toward extension-driven panels — one canonical window per advanced-extension surface (revision, history, attestation, role, group, etc.) — with knowledge-base becoming a view over the revision system rather than a top-level content-type. The catalog stabilizes when the extension-driven design lands. Until then, the four content-types whose schemas are stable across the transition (tree-browser, entity-detail, execute-console, event-log) progress at T2.
  2. T3 entity-typed outputs (app/ui/output/{content_type}). Deferred until SDK affordances S1 (local-only namespace) and S2 (watch-with-diffs) are designed.
  3. Cross-peer rendering. Three latency regimes (pull-on-demand, watch-with-diffs, shared-output) likely; SDK affordances and capability scoping for cross-peer outputs are unsettled.
  4. Compute-backed UIs / handlers. T3 + reactive-recompute territory; flag as future.
  5. Multi-app capability boundary. Future capability/multi-app guide.

11A. Self-Assessment Checklist for Workbench Implementations

Status: tool, not normative. Per the cross-impl alignment cycle (workbench-go feedback §2.E): use this for surfacing demand-driven roadmap candidates; not a SHOULD-level expectation. Useful for first-pass coverage audit by any workbench impl.

Workbench applications cover varying portions of the 14 application-architecture primitives enumerated in the paper corpus's application-architectures note (internal, not published):

#PrimitiveShort description
1Pg — PaginationHow the app surfaces large result sets in bounded views
2Ch — Change detectionHow the app notices state changes (polling / watch / subscription)
3Da — DataHow data flows between substrate and app state
4Sh — ShapeType structure and field-spec conformance
5Ac — AccessCapability-scoped access patterns
6Mu — MutationWrite patterns (L0 / L1 / handler op dispatch)
7Pr — PropagationCross-panel / cross-context state propagation
8Co — CoherenceConvergence across replicas / merges / rollback
9Hi — HistoryAudit, transition chains, rollback surfaces
10Ev — Evaluation/computeEntity-native computation, expression evaluation, reactive subgraphs
11Pe — PerceptionSubscription, observation, watch surfaces
12Pn — PresentationRenderer / view-state / decoration surfaces
13Bn — BoundaryCapability boundaries, namespace boundaries, app boundaries
14Au — AuthorityIdentity stack, role assignment, attestation surfaces

Self-assessment pattern: for each primitive, score current coverage on this scale:

ScoreMeaning
1Absent — no surface in this impl
2Ad-hoc — incidental code, no canonical pattern
3Present but incomplete — partial surface, known gaps
4Complete but untested — full surface, no real-app validation
5Production-tested — full surface, validated under real workloads

Identify the lowest-scoring primitives as candidates for the next demand-driven panel/feature work. The first impl to run this self-assessment (Godot) identified Ev=1/5 (Evaluation/compute) as their biggest gap and named "compute-graph viewer" as their highest-value gap-closing panel.

Cross-impl visibility: per CROSS-IMPL-PARITY-MATRIX.md, surface gaps that cluster across multiple impls become candidates for cross-impl coordinated work; gaps unique to one impl are that impl's roadmap.


12. References

External:

Internal (this repo):

Adjacent priors: