docs(website): the authoring UI proper, and the one route it needed (Phase 13) #223

Merged
whitlocktech merged 1 commits from docs/events-p13-authoring-ui into edge 2026-09-07 22:12:28 +00:00
Member

Events Phase 13, docs half. One of two: website, docs — both onto edge.

EVENTS.md §I gains what the phase settled and its screens table's two unfinished rows are marked built; the § API surface table gains POST /admin/events/price; EVENTS_PLAN.md has Phase 13 as built.

The rule by which a form gives way, written down

Phase 3 shipped a step's params and a phase's where as raw JSON boxes, each captioned as a placeholder for this phase. Both are forms now — and the interesting half is not that they are forms, it is when they stop being one.

A value the editor cannot round-trip is shown rather than silently rewritten, which is the condition builder's own rule and the reason the two placeholders were always the same problem. Flattening A and (B or C) into A and B and C changes which firings release a phase; dropping a param the action does not declare changes what a step does. Neither is visible in a save that otherwise looks clean.

Three things force a step into JSON and the screen names which: the action is dormant, so there is no declaration to draw; the step carries a param the action does not declare, which the save refuses by name and which is exactly what the author needs to read; or a value no single control can hold.

It is the engagement builder, not a second one shaped like it

The grammar behind a phase gate is the engagement condition grammar — engagement/conditions.js validates it at save, and the diagnosis panel's sentence is rendered on the server from the same labels. conditionRowsFrom, conditionsFromRows and coerceLiteral are imported rather than reimplemented.

The coercion is the half worth naming: every value in an HTML input is a string, and { cmp: 'gt', value: "5" } against an int variable is refused — rightly — by a server whose error the author would then be reading as a complaint about JSON rather than about what they typed.

Why the meter is a route

POST /admin/events/price, admin, editor, and the API surface table now says why it is a route rather than arithmetic in the browser: a module's cost() is a server function of params (§F), so a meter has nothing to add up until something asks. The dry run is the wrong thing to ask twice over — it dispatches every step through the module (and through it to a sidecar and a game tick), and a pass against a published version is recorded, which is the stamp §K's unattended-start gate reads. Neither belongs behind a keystroke.

It says what it does not know, and §I records that as the design rather than a caveat: a step core could not price makes every total an under-count, and an author reading a number smaller than what will happen is worse off than one reading no number at all.

Rehearsal was shipped in Phase 10 and reachable in Phase 13

§I's rehearsal row is marked accordingly, with the gap stated plainly: the narrowing ceiling on the emit envelope arrived in P10 and the checkbox that asks for it here, which is longer than it should have been. An affordance nothing can reach is an affordance nobody has.

The same dialog is why params matters and not only as a convenience: the concurrency key is a {placeholder} template rendered from the run's own params, so a UI posting an empty body rendered the same key for every manual run and had the second refused as an overlap with the first. Proved in the walk — the run came back keyed walk:Yew.

BACKEND_DESIGN.md

Its /events/* rows are Phase 1's surface and have not moved since; eleven routes have been added by Phases 3–13. A note says so and points at EVENTS.md as canonical rather than re-listing them, which would be a second copy of a contract that file owns — and the copy that drifts is always the second one.

Checks

docs has no CI. Every relative link and in-page anchor in the new sections was resolved by hand; the files are CRLF in the working tree and each diff is content-sized (--numstat 8/0, 64/5, 67/0), so none carries the \r\r\n full-file rewrite.

🤖 Generated with Claude Code

https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4

Events Phase 13, docs half. One of **two**: `website`, `docs` — both onto `edge`. `EVENTS.md` §I gains what the phase settled and its screens table's two unfinished rows are marked built; the § API surface table gains `POST /admin/events/price`; `EVENTS_PLAN.md` has Phase 13 as built. ## The rule by which a form gives way, written down Phase 3 shipped a step's params and a phase's `where` as raw JSON boxes, each captioned as a placeholder for this phase. Both are forms now — and the interesting half is not that they are forms, it is **when they stop being one.** A value the editor cannot round-trip is **shown rather than silently rewritten**, which is the condition builder's own rule and the reason the two placeholders were always the same problem. Flattening `A and (B or C)` into `A and B and C` changes which firings release a phase; dropping a param the action does not declare changes what a step does. Neither is visible in a save that otherwise looks clean. Three things force a step into JSON and the screen names which: the action is dormant, so there is no declaration to draw; the step carries a param the action does not declare, which the save refuses **by name** and which is exactly what the author needs to read; or a value no single control can hold. ## It is the engagement builder, not a second one shaped like it The grammar behind a phase gate **is** the engagement condition grammar — `engagement/conditions.js` validates it at save, and the diagnosis panel's sentence is rendered on the server from the same labels. `conditionRowsFrom`, `conditionsFromRows` and `coerceLiteral` are imported rather than reimplemented. The coercion is the half worth naming: every value in an HTML input is a string, and `{ cmp: 'gt', value: "5" }` against an `int` variable is refused — rightly — by a server whose error the author would then be reading as a complaint about JSON rather than about what they typed. ## Why the meter is a route `POST /admin/events/price`, `admin, editor`, and the API surface table now says why it is a route rather than arithmetic in the browser: a module's `cost()` is a server function of params (§F), so a meter has nothing to add up until something asks. The dry run is the wrong thing to ask twice over — it dispatches every step through the module (and through it to a sidecar and a game tick), and a pass against a published version is **recorded**, which is the stamp §K's unattended-start gate reads. Neither belongs behind a keystroke. **It says what it does not know**, and §I records that as the design rather than a caveat: a step core could not price makes every total an under-count, and an author reading a number smaller than what will happen is worse off than one reading no number at all. ## Rehearsal was shipped in Phase 10 and reachable in Phase 13 §I's rehearsal row is marked accordingly, with the gap stated plainly: the narrowing ceiling on the emit envelope arrived in P10 and the checkbox that asks for it here, which is longer than it should have been. An affordance nothing can reach is an affordance nobody has. The same dialog is why **`params` matters and not only as a convenience**: the concurrency key is a `{placeholder}` template rendered from the run's own params, so a UI posting an empty body rendered the same key for every manual run and had the second refused as an overlap with the first. Proved in the walk — the run came back keyed `walk:Yew`. ## BACKEND_DESIGN.md Its `/events/*` rows are Phase 1's surface and have not moved since; eleven routes have been added by Phases 3–13. A note says so and points at `EVENTS.md` as canonical **rather than re-listing them**, which would be a second copy of a contract that file owns — and the copy that drifts is always the second one. ## Checks `docs` has no CI. Every relative link and in-page anchor in the new sections was resolved by hand; the files are CRLF in the working tree and each diff is content-sized (`--numstat` 8/0, 64/5, 67/0), so none carries the `\r\r\n` full-file rewrite. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
wtclaude added 1 commit 2026-09-07 21:35:36 +00:00
EVENTS.md I gains what the phase settled -- the rule by which a form gives way to
the JSON box, why the condition builder is the engagement one rather than a
second one shaped like it, and the live cap meter as a fifth affordance beside
the dry run it must not be mistaken for. The screens table's two unfinished rows
are marked built; rehearsal is marked reachable, which it had not been since
Phase 10 shipped it.

The API surface table gains POST /admin/events/price, and a paragraph on why it
is a route rather than arithmetic in the browser: cost() is a server function of
params, and the dry run both dispatches and records, neither of which belongs
behind a keystroke.

EVENTS_PLAN.md carries Phase 13 as built, with the four decisions, the two
defects it closes in already-merged code, and the six things the build settled.

BACKEND_DESIGN.md's /events/* rows are Phase 1's surface and have not moved
since; a note says so and points at EVENTS.md as canonical rather than
re-listing eleven routes that file already owns.

Checks: docs has no CI. Every relative link and in-page anchor in the new
sections was resolved by hand; the files are CRLF in the working tree and each
diff is content-sized (8/0, 64/5, 67/0), so none carries the \r\r\n full-file
rewrite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
whitlocktech merged commit c17893a215 into edge 2026-09-07 22:12:28 +00:00
whitlocktech deleted branch docs/events-p13-authoring-ui 2026-09-07 22:12:29 +00:00
Sign in to join this conversation.
No description provided.