Widget Contract Reconciliation¶
Status: living document · updated alongside each spec / engine / UI release
Companion to: STORYTANGL_WIDGET_VOCAB.md v1.5
Companion to: bundles/<name>/EXTENSIONS.md (Tier P3 genre layers)
This document tracks implementation status across the three layers named in spec §0.7:
Layer |
Document |
Role |
|---|---|---|
L1 — UI Vocabulary |
|
Target-truth. What data shapes the player-facing client needs. The spec evolves here; everything else chases. |
L2 — API Transport |
|
REST endpoints (and other wire transport) routing L1 needs to L3 capabilities. Optional layer — CLI ports skip it. |
L3 — Engine Capabilities |
|
Python callables that produce the data L1 wants. The current engine’s actual surface. |
The spec is target-truth. This document is the honest reality check. The two are intentionally allowed to disagree during a settling phase — that’s how the inversion (UI-led design, backend chases) works.
How to read this doc¶
Each surface in the spec gets a row in one of the §-numbered tables below. The four status columns per row:
Column |
What it tracks |
|---|---|
Spec tier (L1) |
|
Reference clients |
What |
Engine backend (L3) |
What |
Plan |
One-line forward direction. Empty when the row is settled across columns. |
A row is settled when all three implementation columns match the spec’s tier commitment. A row with mismatched columns is in negotiation — that’s fine; the doc just makes the gap visible.
Sequence of work, per the inversion strategy. UI vocabulary leads (commit the target shape in the spec), the reference UI catches up next (against fixtures, even with dummy data), then the engine + API expose capabilities that match. The CLI port skips L2 entirely and consumes L3 directly via an in-process shim.
§A — Tier S surfaces (core contract)¶
These are the surfaces v1.5 marks Tier S — committed as stable target contract. Most are already shipping in the reference UI and engine; the negotiation is mostly about typed-shape graduations.
Surface |
Spec tier |
Reference UI |
Engine backend |
Plan |
|---|---|---|---|---|
|
S |
done |
done |
— |
|
S |
done |
done |
— |
|
S |
done |
done |
— |
|
S |
done |
done |
— |
|
S |
done ( |
done ( |
tuple-row fixtures migrated in typed accepts / KvRow PR |
|
S |
done |
done |
— |
|
S |
done |
done |
— |
|
S |
done |
done |
— |
|
S |
done |
done |
— |
|
S |
done (web + CLI |
done (typed; gathered through service-info dispatch) |
sandbox and credentials fixtures cover advertised channels |
|
S |
done |
done |
bundles choose descriptor contents; clients hand it back |
|
S |
done (nested type + dirty-kind cache hints) |
partial (typed; v1 conservatively marks advertised kinds dirty) |
add finer dirty tracking only when a client needs caching |
|
S |
done client-side |
done (same endpoint; singular/plural kind filters + query descriptor routing) |
add bundle-specific providers as worlds need them |
§1.6 Info channels |
S |
done (info pills + CLI floor) |
done |
keep rich renderers optional; |
|
S |
partial (basic style/icon fields) |
done |
audit aliases and long-tail hints before treating complete |
Bundle customization / presentation profiles |
S target |
partial (docs + older world |
not_started |
keep advisory; requires world-info catalog before conformance |
§5.1 Decision Legibility Contract |
S |
partial (JSON harness covers fixtures/sequences) |
n/a (contract; not a capability) |
expand field coverage as new decision surfaces promote |
§5.2 Time Parity Rule (visual ritual skip, media advance) |
S |
partial (JSON harness covers readable fallbacks) |
n/a |
browser timing/skip E2E remains later |
§5.3 Input Parity Rule (drag fallback, hotkey numbers) |
S |
partial (JSON harness covers fixture submit floors) |
n/a |
extend toward renderer-specific drag/keyboard fallbacks |
§0.2 CLI Floor Rule |
S |
n/a |
n/a (gating rule on PRs) |
wire into CI for Tier S graduations |
§B — Tier P1 surfaces (target for next engine epoch)¶
These are committed target contract but require additive engine work. Each row should land as a single PR-shaped change unless the surface is small enough to settle with its immediate neighbors.
Surface |
Spec tier |
Reference UI |
Engine backend |
Plan |
|---|---|---|---|---|
Typed |
P1 |
done (kind |
done ( |
expand conformance cases only as new widgets land |
Typed |
P1 |
done |
done ( |
keep legacy web |
Typed |
P1 |
partial ( |
done ( |
add |
Typed |
P1 |
partial (web nested renderer + CLI/Tk inspection fixture) |
done ( |
harden layout and add broader part combinations as worlds emit them |
Typed |
P1 |
partial (documented + ad-hoc keys) |
done ( |
tighten named fields when more worlds use them |
Typed |
P1 |
done (typed + rendered) |
untyped |
engine PR |
Typed |
P1 |
done (renders |
untyped |
engine PR |
|
P1 |
done (choice display) |
not_started |
engine PR |
|
P1 |
done |
partial (string-keyed dict) |
engine PR for |
HTTP body field |
P1 |
done |
done |
— |
§1.5 Cursors and journal channels (per-channel envelopes) |
P1 |
n/a (single cursor) |
n/a (single cursor) |
wait for MVP author needing multi-cursor (Discord-bot bundle, Lost Worlds gamebook) |
Block on Tier P1 graduation to Tier S: the CLI reference port
(engine/contrib/conformance/cli_reference_port.py) must implement
each surface before its row can promote. Until then they stay P1.
§C — Tier P2 surfaces (interactive surface vocabulary)¶
These are committed target contract but larger, and several depend on the §7.4 predicate-registration protocol (an explicit open question in the spec).
Surface |
Spec tier |
Reference UI |
Engine backend |
Plan |
|---|---|---|---|---|
|
P2 |
partial (basic web widget + carwars wireframes) |
partial (untyped equivalent) |
engine PR: typed |
|
P2 |
done in carwars catalog fixtures |
not_started |
engine PR for typed lifecycle; bundle MVP needed for shop semantics |
|
P2 (proposal fixture) |
not_started |
not_started |
wait for multi-cursor MVP |
|
P2 (proposal fixture) |
not_started |
not_started |
wait for grid/hex bundle MVP (Patchwork, Carcassonne) |
|
P2 |
done |
not_started |
engine PR with |
|
P2 |
partial (basic web widget + carwars wireframes) |
not_started |
engine PR: typed |
|
P2 |
partial (orientation/grid/fan) |
not_started |
engine PR; render-port catches up as needed |
|
P2 (proposal fixture) |
not_started |
not_started |
wait for network/route bundle MVP (Ticket-to-Ride-shaped) |
|
P2 pressure fixture |
not_started |
not_started |
P1 typed field exists in the target; graph-route fixture remains gated on a route/network MVP |
|
P2 |
partial (basic rendering in carwars) |
not_started |
engine PR; CLI reference port renders outcome word + narrative |
|
P2 |
partial |
not_started |
engine PR with |
|
§7.4 OPEN |
n/a |
n/a |
highest-leverage open question; awaiting MVP author |
|
P2 |
partial |
not_started |
engine PR — single-cursor today, audience-list extension when multi-cursor lands |
|
P2 (proposal fixture) |
not_started |
not_started |
wait for team-game MVP |
§D — Tier P3 genre extension surfaces¶
These are advisory genre conventions layered on top of v1.5. They do not create new core fragment types, accepts kinds, or value types. A generic client remains conforming when it renders the underlying v1.5 surfaces and ignores these enrichments.
Surface |
Spec tier |
Reference UI |
Engine backend |
Plan |
|---|---|---|---|---|
|
P3 |
docs + v1.5 wireframe |
n/a |
keep drag optional; no garage-specific core widgets |
|
P3 |
docs + v1.5 wireframe |
n/a |
packet / finding / disposition treatments remain genre enrichments |
|
P3 |
docs + v1.5 wireframe |
n/a |
study previews, stat checks, and inventory unlocks remain genre enrichments |
|
P3 |
docs + v1.5 wireframe |
n/a |
journal-as-story transcript proof; no new surface beyond graph zones / rolls |
|
P3 |
docs + v1.5 wireframe |
bundle-specific |
optional badge or CLI suffix; backend remains authoritative |
|
P3 |
docs + v1.5 wireframe |
bundle-specific |
optional preview over mediation choices; fallback to ordinary choice text |
|
P3 |
docs + v1.5 wireframe |
bundle-specific |
optional risk badge / suffix; fallback to |
|
P3 |
docs + v1.5 wireframe |
n/a |
optional enrichment; click-pick fallback remains required by §5.3 |
Genre transcript examples |
P3 diagnostic |
v1.5 wireframe samples |
n/a |
add executable transcripts when demo worlds stabilize; diagnostic, not gating |
|
deferred |
n/a |
n/a |
do not create until a fourth cross-genre pattern is not already covered by core v1.5 |
§E — API transport (Layer 2)¶
The API surface is intentionally underspecified at the contract level — the spec commits to the data shapes (L1), and the API chooses how to route them. This table is what the engine team implements.
Endpoint |
Method |
Spec target |
Current engine |
Plan |
|---|---|---|---|---|
|
POST |
accepts |
accepts |
type response |
|
GET |
returns |
partial |
type response |
|
GET |
accepts |
done |
add bundle-specific providers |
|
GET |
public; rate-limited |
done |
— |
|
GET |
returns current |
not_started |
see auth thread |
|
various |
API key lifecycle |
not_started |
see auth thread |
|
POST |
revoke a key |
not_started |
see auth thread |
CLI port and L2. The CLI port does not call any of these
endpoints. It consumes engine/’s Python surface directly via an
in-process shim — typically a thin wrapper around
ServiceManager.do_action() / get_envelope() / get_projected_state().
The L2 column is therefore not blocking for CLI conformance.
§F — Conformance harness¶
These tests verify spec contracts against the reference implementations. They serve as the gating mechanism for tier graduations and as the regression suite for cross-port parity.
Harness |
What it checks |
Status |
Plan |
|---|---|---|---|
|
Tier S CLI rendering for every fragment / value_type |
partial (Tier S + current P1 widgets) |
extend coverage as Tier P1 → S graduations happen |
|
§5.1 — referenced UIDs are rendered |
initial |
covers fixture and sequence choices; expand as new P1/P2 fields promote |
|
§5.3 input parity |
initial |
covers fixture and sequence submit floors; time parity remains next harness |
|
§5.2 time parity |
initial |
covers JSON-readable media/roll fallbacks; browser skip timing remains later |
|
pytest harness binding fixtures + ports |
not_started |
wire into CI |
|
canonical envelopes per surface |
partial (Tier S + current P1 fixtures) |
keep promoting proposal fixtures as implementation lands |
|
forward-compatible proposal envelopes |
partial |
current set covers carwars garage, piece realization, place accepts, record KvRow, roll fragment, and one UUID-shaped v1.5 wireframe interpretation sample; CLI/Tk can inspect them without promotion |
webapp Vitest conformance suite |
webapp DOM matches expected for each fixture |
not_started |
written from fixtures; webapp regression mechanism |
§G — Recent reconciliation events¶
A short log of what changed across recent spec / UI / engine releases. Each entry names which layer moved and what the consequence is for the others.
2026-05 — spec v1.5 (L1 update only)¶
Adopted the v1.4 genre-audit additions: journal-as-narrative, per-cursor projection of shared state, and the genre extension index. Impact: no new top-level vocabulary surfaces; improves authoring discipline and cross-demo traceability.
Reconciled v1.4 language with repo-current implementation status. Impact: typed
AcceptsandUIHintsare described as implemented;Blocker,InterpretationFragment, typed info metadata, and Tier P2 widgets remain pending or partial.Updated the fixture inventory to match the current repository, including
compose_payload.jsonand the existing proposal fixture set.Imported and vetted Tier P3 extension docs for CarWars, credentials, training, and elefant_hunt, plus
GENRE_AUDIT_NOTES.md. Impact: genre enrichments are advisory over core v1.5; no_common/EXTENSIONS.mduntil a repeated cross-genre pattern is not already covered by the core vocabulary.Archived the v1.5 wireframe package under
docs/src/design/story/wireframes/v1_5/. Impact: visual coverage now matches the reconciled v1.5 vocabulary and Tier P3 extension docs; most wireframe fixtures remain design fixtures until their symbolic ids are translated for engine conformance.Translated one v1.5 wireframe interpretation sample into
engine/contrib/conformance/proposals/with UUID-shaped ids. Impact: future ports have a concrete command-feedback fixture without promoting the whole wireframe bundle to gating conformance.
2026-05 — spec v1.3 (L1 update only)¶
Reverted
accepts.kind="select"rename →pieces. Impact: L2/L3 may continue usingpieces; no migration needed.Demoted §1.5 cursors and §1.6 info channels to Tier P1. Impact: none on L2/L3 directly; clarifies that current single-cursor behavior is settled, multi-cursor is target.
Replaced
GET /story/info/{kind}with query-descriptor model. Impact: L2 evolves the/story/infoendpoint; no L1 break since the v1.2 URL form was never shipped.Added §0.7 three-layer architecture. Impact: this document exists.
EXTENSIONS.md swept
tokens → pieces. Impact: carwars Tier P3 conventions now consistent with main spec.
2026-05 — webapp v1.3 reference UI update¶
Reference webapp uses
accepts.kind="pieces"throughout (15+ call sites).Reference webapp implements
InfoAffordance.queryas an opaque JSON descriptor on/story/info?kind=...&query=....Reference webapp accepts nested
metadata.info_state. This row is superseded by the v1.5 update, wheredirty_kindsbecame an implemented cache hint in the reference client.Reference webapp ships
placeaccepts kind withoutedge_ref(proposal fixture not yet exercised).
2026-05 — webapp v1.5 reference UI reconciliation¶
Reference webapp renders
InterpretationFragmentwith the spec’sresult,text,message,blocked_reason,hint, andcandidatesfields. Impact: command-resolution feedback no longer falls through to the unknown-fragment fallback.Reference webapp renders typed
blockers[]and pluralcost_previews[]as choice decision details. Impact: locked choices expose more of the decision-legibility contract in the current shell.Reference webapp posts
edge_idto/story/do. Impact: client and endpoint server now use the same choice-edge identifier name.Reference webapp treats
metadata.info_state.dirty_kindsas cache hints for/story/info. Impact: the sidebar keeps old refresh behavior when no hint is provided, but skips clean side-channel refreshes when the backend sends explicit dirty-kind metadata. The tests cover both default status and selected affordance kinds.Reference CLI/Tk ports render or inspect blockers, cost previews, typed accepts prompts, and v1.5 interpretation fields. Impact: new client ports can compare against portable JSON fixtures and proposal fixtures instead of copying the Vue shell.
2026-05 — engine current (L3 baseline)¶
Engine emits typed
AcceptsandUIHintsmodels fromtangl.journal.intent; blockers remain dictionary-shaped pending the next intent pass.Engine HTTP body uses
edge_id.Engine
KvFragment.content, projectedkv_listvalues, web fixtures, and conformance fixtures use the unifiedKvRowrecord shape.Engine has no
/story/info/{kind}(consistent with v1.5 spec).
§H — How to use this doc¶
Spec authors. When you commit a vocabulary change, update the appropriate row’s “Spec tier” column and add a one-paragraph entry to §G. The implementation columns lag deliberately.
Reference UI authors. When the webapp implements a surface, update the “Reference UI” column to
doneorpartial. If you’re implementing against a fixture (not against a live engine), usepartialand note the fixture in the Plan column.Engine authors. When the engine ships a typed shape or endpoint, update the “Engine backend” column. If you’re shipping the engine capability but not the API endpoint, that’s still progress — the capabilities table in
ENGINE_CAPABILITIES.md(forthcoming) tracks Python surface separately.CLI port authors. Watch §A and §B; you don’t care about §E. Your conformance is against
cli_reference_port.pyand the §F harness.
A row becomes a candidate for the §G log when any column moves.
A surface becomes a candidate for Tier S graduation when all three
implementation columns read done and the CLI reference port
renders it.
§I — Forward-looking sequence¶
This is the inversion plan the project committed to: UI leads, API + engine chase. Each step’s deliverable is a single PR-shaped change to the named layer.
Phase 1 — Stabilize spec (current).
✅ Spec v1.5 with the v1.4 genre-audit additions reconciled to repo state.
✅ EXTENSIONS.md swept to
pieces.✅ Tier P3 extension docs imported and vetted for current demo genres.
✅ This reconciliation doc as the three-layer spine.
Phase 2 — Reference UI catches up. Wireframes and webapp align to v1.5.
✅ Wireframes resync: v1.5 bundle is archived under
docs/src/design/story/wireframes/v1_5/, with v1.2/v1.3 treated as visual precedent rather than contract truth.✅ The v1.5 wireframe annotates core vocabulary separately from Tier P3 enrichments (
stat_check,drag,validity_check,encounter_check), keeping contract conformance and genre flavor separable.Webapp PR sequence:
✅
composeaccepts rendering as nested ChoiceInputView.✅
InterpretationFragmentrenderer with the spec’sresult/textfield names.✅
metadata.info_statebehavior pass: use nesteddirty_kinds/available_kindsas cache hints once the backend emits them.Keep
info_affordancesopaque; clients passquerydescriptors through rather than parsing bundle-specific fields.
Phase 3 — Engine + API catch up. Backend declares capabilities, API maps them.
Engine PR sequence:
Typed
Blockermodel intangl/journal/intent.py.Typed
InterpretationFragmentwithresult/textfields.Typed
metadata.grammarmodel.HTTP API: type responses on
/story/do//story/update.Conformance harness: ✅ initial
legibility.py; ✅ initial input-parityparity.py; ✅ initialtime_parity.py; ✅ CLI-floor info-channel reachability; next add browser timing E2E only after promoted surfaces stabilize.
Phase 4 — Tier P1 → S graduation. Surfaces in §B promote one at a time, gated on the CLI reference port and the conformance harness having coverage for each.
Phase 5 — Tier P2 surfaces gate on bundle MVPs. Each Tier P2 surface (multi-cursor, pieces with position, edge_ref, predicate registration) waits for a real bundle author who needs it. We do not pre-build these.
End of v0.2.