Skip to content

feat(book)!: half-hour vocabulary grid - #43

Merged
wavekat-eason merged 3 commits into
mainfrom
feat/half-hour-grid
Aug 10, 2026
Merged

feat(book)!: half-hour vocabulary grid#43
wavekat-eason merged 3 commits into
mainfrom
feat/half-hour-grid

Conversation

@wavekat-eason

Copy link
Copy Markdown
Contributor

Draft on purpose — do not merge yet. Merging releases @wavekat/flow-schema@0.0.11, and adopting that release before the daemon fleet is updated takes booking flows off the air. The gate is stated below and in the platform's docs/34 §2.

What

BOOK_GRANULARITY_MINS 15 → 30, in the TS package and its Rust twin, so appointment starts land on :00 and :30 only.

before after
universe per voice 111 63
Mon–Fri 09:00–17:00, 30-min appointments 44 29

Why it is staged, and why this half is second

required_assets folds vocabulary_refs(node) into the set a daemon must have on disk before it will arm a flow — and it computes that set from the constant compiled into its own build, not from anything the version carries.

So a daemon still on the quarter hour, handed a version published after this change, asks for bktime_0915, does not find it, and refuses to arm the flow. Not the booking step — the whole flow, silently.

The safe order is therefore:

  1. Platforms narrow what they offer first (wavekat-platform #243, already open), leaving the frozen vocabulary a strict superset of what every daemon asks for and every caller hears. Callers get half-hour slots immediately, and no device is affected.
  2. Fleets update to a build carrying this constant.
  3. Then this merges, and the platform bumps its dependency.

Step 3 is the only one that can break anything, which is why this PR is a draft rather than a queue item.

The cross-language pin

Second commit, and worth reading independently of the grid. The vocabulary is computed twice — TypeScript and Rust — from two copies of one constant, and nothing compared them. Both languages' own tests pass happily while the two disagree, and the symptom of disagreement is not a red build; it's a phone line going quiet on the devices that updated late.

The corpus now carries an optional requiredAssets expectation and both runners assert exact-set equality against it. Exact, unlike the errors expectation beside it, because this set is the contract rather than a description of one. The Rust side also asserts that at least one case pinned something, so a corpus that quietly stopped pinning can't pass by doing nothing.

Verified by reverting the Rust constant to 15 on its own — the pin fails with required assets differ for v2/valid/clinic, which is exactly the drift it exists to catch.

Tests

cargo test --workspace 60 passing, clippy and cargo fmt --check clean; pnpm -r test 138 passing. Each affected grid case was re-derived rather than renumbered.

🤖 Generated with Claude Code

https://claude.ai/code/session_018stjkZJ4kxDViq1ENnVDmH

wavekat-eason and others added 3 commits August 9, 2026 22:00
BREAKING CHANGE: `BOOK_GRANULARITY_MINS` moves from 15 to 30, so
`bookVocabularyRefs` / `vocabulary_refs` return a smaller set for the
same node — 63 clips per voice rather than 111, and 29 rather than 44
for a Mon-Fri 09:00-17:00 step with 30-minute appointments.

Consumers must not adopt this blind. `required_assets` is computed from
the constant compiled into each build, not from anything a version
carries, so a daemon still on the quarter hour that receives a version
published after this change asks for `bktime_0915`, does not find it,
and refuses to arm the flow — silently, and for the whole flow, not
just its booking step. The safe order is: platforms narrow what they
*offer* first, leaving the frozen set a superset of what every daemon
asks for; fleets update; only then does this land. The platform side of
that argument is its docs/34 §2.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018stjkZJ4kxDViq1ENnVDmH
The `book` vocabulary is computed twice — once in TypeScript, once in
Rust — from two copies of the same constant, and nothing compared them.
Both languages' own tests pass happily while the two disagree, and the
symptom of a disagreement is not a red build: it is a daemon refusing
to arm a flow whose assets it cannot find, on whichever devices updated
late.

So the corpus gains an optional `requiredAssets` expectation, and both
runners assert exact-set equality against it — exact, unlike the
`errors` expectation beside it, because this set *is* the contract
rather than a description of one. Only cases that pin a set take part,
and the Rust side asserts at least one did, so a corpus that quietly
stopped pinning cannot make the test pass by doing nothing.

Verified by reverting the Rust constant to 15 on its own: the pin fails
with "required assets differ for v2/valid/clinic", which is the drift
it exists to catch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018stjkZJ4kxDViq1ENnVDmH
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018stjkZJ4kxDViq1ENnVDmH
@wavekat-eason
wavekat-eason marked this pull request as ready for review August 10, 2026 08:17
@wavekat-eason
wavekat-eason merged commit 988d7a4 into main Aug 10, 2026
3 checks passed
@wavekat-eason
wavekat-eason deleted the feat/half-hour-grid branch August 10, 2026 08:17
@github-actions github-actions Bot mentioned this pull request Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant