feat(book)!: half-hour vocabulary grid - #43
Merged
Conversation
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
BOOK_GRANULARITY_MINS15 → 30, in the TS package and its Rust twin, so appointment starts land on:00and:30only.Why it is staged, and why this half is second
required_assetsfoldsvocabulary_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:
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
requiredAssetsexpectation and both runners assert exact-set equality against it. Exact, unlike theerrorsexpectation 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 --workspace60 passing, clippy andcargo fmt --checkclean;pnpm -r test138 passing. Each affected grid case was re-derived rather than renumbered.🤖 Generated with Claude Code
https://claude.ai/code/session_018stjkZJ4kxDViq1ENnVDmH