docs(spec-0031): fold back what implementation proved the spec got wrong - #350
Merged
Conversation
Three PRs implemented against this spec and each found the spec inaccurate in a way a later reader would have re-derived. Recorded at the criteria they belong to, not as a changelog. AC2: a branch declaration is NOT a gate consumer — BranchDecl.flags is a pinning declaration, not a condition. And the gate could not be a flattened shared struct: serde rejects flatten with deny_unknown_fields, which all 76 stage structs carry and which is what turns an author's typo into DW0100 instead of silence. Taking the tidy struct would have meant deleting that from 25 sites — weakening a check to obtain a nicer type. The criterion demands a property; the struct was only one way to get it. AC8: the criterion never said through which CHANNEL the death message is stated, and the obvious reading is blocked by spec-0029 §3 — vanilla's death component carries no fallback, so a player who declines the resource pack sees a raw translation key and DW0185 cannot catch it. It also omitted the completability half entirely, and 'kills whatever enters it' is not literally implementable, since engine machinery lives in the world. on_death: records why it is campaign-wide rather than per-checkpoint, the general desugar-vs-root rule the sixth blind spot yielded, and — most importantly — that its RUNTIME behaviour is unproven, with the obligation that the first campaign declaring it carries a bot-tier proof of the corpse-side fire. A generated PackTest cannot prove it (a fake player is alive), and adding one today would bind to zero campaigns and be vacuous. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AjQ5p1Kv5MrkGPumi7yXWL
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.
Three PRs implemented against spec-0031 and each found the spec inaccurate in a way the next reader would have had to re-derive. Corrections sit at the criteria they belong to, not in a changelog nobody reads.
AC2 — two corrections
A branch declaration is not a gate consumer. The criterion listed it.
BranchDecl.flagsis a pinning declaration — "these flags are SET on this branch" — read by the chronicle and the branch proofs. It is not a condition;purse == 500has no meaning in it. Worth stating because the two read alike in JSON and only one gates anything. Measured: six consumer classes over 28 declaring sites.The gate could not be a flattened shared struct, and the reason is a rule. serde rejects
flattenin combination withdeny_unknown_fields, which all 76 stage structs carry and which is what turns an author's typo intoDW0100instead of silence. The tidy struct would have cost deletingdeny_unknown_fieldsfrom 25 sites — weakening an existing check to obtain a nicer type, which the debug doctrine forbids. So the fields sit on all 28 sites and a from-the-type test makes "every consumer carries the whole gate" a property rather than a habit. The criterion demands the property; the struct was only ever one way to get it.AC8 — three corrections
message_id— is blocked by spec-0029 §3: vanilla builds that component with nofallback, so a player who declines the resource pack sees a rawdeath.attack.…key, andDW0185cannot catch it because the literal in the emission is the key, not the authored string.on_death— why it is shaped as it is, and what is still unprovenCampaign-wide, one bundle, not a field on a checkpoint: where you come back is a checkpoint's property, that you died is true everywhere. Per-checkpoint would be N copies of one fact with N chances to forget one.
The desugar-vs-root rule, which is the reusable part: desugar when the sugar's whole meaning is the general construct; add a root when the bundle hangs off an object with runtime machinery of its own. An ambush is a trigger, so it desugars. A shortcut's unlock is not — its detection is a once-only sentinel poll that in the same function clears the gate region, retires the affordance and kills the wrong-side bodies. Desugaring would have put two independent detectors on one event, the exact defect the death edge exists to avoid.
And the honest part.
on_death's compile-time shape is proven; its runtime behaviour is not. A generated PackTest cannot prove the corpse-side positive — it drives a fake player, and a fake player is alive — and none was added because no campaign declareson_deathyet, so it would bind to zero campaigns and be a green that means nothing. The spec now carries the resulting obligation explicitly: the first campaign to declareon_deathmust carry a bot-tier proof of the corpse-side fire. Not optional, not the author's discretion.What CI proves
Doc-only.
check-doc-dupesgreen;check-reference-versionsgreen. No behaviour changes.