# Milestone #38 — The control surface: what the board can do

Milestone **#33** fixed what the Discord board *shows*. This one is about what a person or
an agent can **do** from a surface, and about that surface telling the truth afterwards.

Six Requests, GREENLIT 2026-07-28 after a grill session with the Operator. Scope, exclusions
and the platform constraint are on issue **#38**; this file is the build path across it.
Per-wave detail stays JIT — a `W<n>-PLAN.md` lands when its wave starts, not now.

## The one constraint, stated once

**serenity 0.12.5 — the latest published version — cannot put a select menu in a modal.**
Its `ComponentType` knows types 1–8; Discord's modal-select needs the `Label` wrapper
(type 18), and a hand-rolled payload fails on the **return** leg too — `edit_modal_submit`
reads values by matching `ActionRowComponent::InputText`, and 0.12.5 deserializes an unknown
component as `Unknown(u8)`. So we could send it and never read the answer back.

Consequence, and it is architectural rather than cosmetic: **every choose-from-a-vocabulary
interaction on this board is a select in an ephemeral message, never a dropdown in a form.**
#11's State control and #10's `/pick-milestone` Finalize are both built that way. Modals keep
what they are good at — free text (`/new-milestone`'s title and body).

## Waves

Ordered so the surface-free work ships first: each board wave ends in the Operator's read of
Discord, and those queue on a human. Nothing here is blocked on another Project.

### W31 — the shell says where it is · #37, #36, `daemon project` arity
Agent-facing only, provable by `cargo test` plus a drive of the installed binary. No board
read owed. Full detail in `W31-PLAN.md`.
Activates `REQ-ACK-NAMES-REPO`, `REQ-BIND-SELF-ALIAS`, `REQ-BIND-FOLLOWUP`,
`REQ-DAEMON-PROJECT-BOUND-REPO`.

### W32 — the card acts · #11, #18
The Change State control (button → ephemeral select, Greenlit omitted, Cut/Shelved take a
second click) and release posts as cards (footer key, dual-shape dedup, in-place conversion,
own color, drift-corrected). Two card surfaces, one wave, one board read.
Activates `REQ-CARD-STATE-CONTROL`, `REQ-RELEASE-CARD`.

**Checkpoint:** run `req-confusion-audit` at the top of this wave. Thirteen requirement ids
landed at once and several are near neighbours by construction — `REQ-BIND-SELF-ALIAS` vs
`REQ-BIND-FOLLOWUP`, `REQ-TARGET-REACTION` vs `REQ-MILESTONE-STAGING`, `REQ-CARD-IMAGE` vs
`REQ-CARD-MILESTONE-LINE`. Cheaper to discriminate them before the tags are spread across
the code than after.

### W33 — the tripwire · #26
Announce-and-advance on the tag cursor, delivered over `spt send --active-only`. Red-first
against a **rig-constructed** fixture: the shape is unreachable on a live node, so no
specimen is to be manufactured on a live endpoint. The `KNOWN-HAZARDS` entry lands with it.
Activates `REQ-TAG-CURSOR-JUMP-ANNOUNCE`.
**Precedence:** must not be prioritised against Request **#25**, which is live on a path
agents use today. If #25 is open when this wave comes up, #25 goes first.

### W34 — Targets · #10, first half
The 🎯 reaction seeded on Backlog and Eval cards, per-Alchemist, inert for non-Alchemists,
carried across relocation (relocation posts a new card and deletes the old, so reactions die
with it — the carry is the same within-operation move as `REQ-SEED-ATTACHMENT-CARRY`). The
staging post: one per Alchemist, pinned in Backlog, edited rather than duplicated on
re-invoke, cycle-refreshed.
Activates `REQ-TARGET-REACTION`, `REQ-MILESTONE-STAGING`,
`REQ-HAZARD-TARGET-RELOCATION-CARRY`.

### W35 — Finalize · #10, second half
Assignment from Targets **read at submit**, `/new-milestone`'s modal and `/pick-milestone`'s
ephemeral select over Backlog/Eval Milestones only, Target consumption with a loud failure
path, one Updates note per Finalize, the milestone line on Request cards, and the `targets`
verb reading the daemon's cycle-written index.
Activates `REQ-MILESTONE-FINALIZE`, `REQ-CARD-MILESTONE-LINE`, `REQ-TARGETS-VERB`.

`REQ-TARGETS-VERB` adds a capability, so `manifest.toml`, `runtime.rs`'s arg table and
`CONTEXT.md`'s vocabulary line move together or `REQ-VOCAB-PARITY` fails — which is the
check working. The `CONTEXT.md` vocabulary line does **not** name `targets` until this wave
ships: the glossary says a verb named there and missing from the manifest is a defect.

## Gate, every wave without exception

`cargo test` green, `./traceable-reqs.exe check` exit 0, registry entries landing **before**
the code that satisfies them, and the behavior verified against the **installed binary or
the live board** — never a green assertion alone. v0.7.0 → v0.7.2 are the standing evidence:
three releases to close one Request because each was proved by a test that asserted the
wrong half.

Release authority is the DRI's (Operator ruling 2026-07-26): a green gate ships, no
authorization needed. What stays Operator-blocked is narrower — a live proof needing a human
acting in Discord, taking another owner's instance down, and the Done transition.

## Acceptance

W31 is closed by the installed binary. W32–W35 each end in the Operator's read of the board;
a Request whose unit tests pass and whose board is wrong is not done, and this project has
shipped that mistake three times (v0.4.2, W26, #30).

## Carried, not scheduled

- **#23** — routed out, rests in Cut, lives as `BigscreenVR/spt-bs-releases#2` under doyle's
  ruling with a persistent Watch. When seamless update lands, the alias-freed-by-teardown
  argument behind #37 decays; #37 stays right anyway.
- **No verb edits a Request's or Milestone's body from the shell.** #38's own body was
  written with `gh` because `milestone create` is single-line and there is no `edit` verb.
  Raise with the Operator before it becomes a habit.
- **Type is set at mint and no verb changes it.** #18 and #36 are typed `IDEA` and are now
  greenlit build work, which reads oddly on the board. Not a defect today; worth a ruling if
  it recurs.
