clay-borg/workplans/CB-WP-0046-the-rule-the-placement-encodes.md
tegwick 1d3f1bfe60 Sync hub state for CB-WP-0046
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 19:16:39 +02:00

5.7 KiB

id kind title status state_hub_workstream_id
CB-WP-0046 product The rule the placement encodes done 169242ac-024d-4001-aae2-d9bbf710fe2a

Purpose

structural tier  S   (one new edition reader, one rendered passage, one
                      marker attribute -- no rule, no dependency moved)
declared tier    S

Closes the two gaps CB-WP-0045 named as not done.

The gap, and the wrong reason given for it

CB-WP-0045 recorded:

"Nothing explains what a scope does … it is a ground-game question first: the scope rule is ours (Variant), not the edition's, so there is no vendored sentence to render."

The premise was false. editions/experiments/h2-scoped-problem-stress/ ships Rules_Text.csv, and its Problems / Stress scope passage is the rule verbatim:

"Each Problem has stress_scope. global: every seat. personal: the assigned owner only. bond: the owner and every seat connected to the owner through one or more Bonds (the Bond network). If the owner has no Bonds, bond scope behaves as personal. Rivalries do not expand bond scope. Multiple unclaimed Problems stack (+1 each) for seats in their scopes."

Nothing in clay-borg had ever read the file. Twenty-two passages of player-facing rules, in the repo, unreachable by a player — a whole rulebook, where CB-WP-0045 found single columns.

Third instance of F18's shape in two passes, and the largest. The pattern is now specific enough to name: when a package adds a file rather than a column, nothing notices. edition-check verifies the digest and freshness of files it knows; a file no reader mentions is not stale, it is invisible. Recorded as a finding for ground-game rather than fixed here.

The escalation is the part worth keeping. CB-WP-0045 did not just leave a gap; it explained the gap with an argument that would have stopped the next reader from looking. A wrong reason for an open item is worse than an open item, because it retires the question.

The log said nothing about which rules were played

2026-08-08-1734: "Well, i did not notice any difference, maybe i did not start the H2 variation?" The log cannot answer that — and neither could we, a week later, from the file.

The variant rides the begin marker, not an eighth column:

<!-- trial-log:begin variant=h2-scoped-problem-stress -->

It is a property of the session (fixed at startup, constant across play again), so a per-row column would repeat one fact on every line and force a third accepted row width on a parser whose docstring is about exactly that hazard (CB-WP-0032). The table shape is untouched.

Stamped from state.variant, never from the --variant flag. Three call sites once wrote state.variant = v directly instead of with_variant(), which leaves H2 inert; a log stamped from the flag would have recorded h2 for a session that played the baseline. A false provenance on real player words is worse than no provenance — which is also why an unstamped log reports unrecorded and not ground-darvo-r0.

Task: render the rule, stamp the log

id: CB-WP-0046-T01
status: done
priority: high
state_hub_task_id: "64b9e9bb-bcaa-45e5-975f-3527e4c95c05"

Controls, all mutation-proven:

mutation what went red
the scope rule never renders "Problems are placed by scope and nothing says what a scope does"
it renders always, baseline included "the baseline table explains a scope distinction that is not in play"
our paraphrase instead of the passage "the scope explanation is not the edition's own words"
the writer drops the attribute "the log does not say which rules were played"
unstamped parses as ground-darvo-r0 "an unstamped log says NOTHING, not 'baseline'"
BEGIN back to an exact match "the bytes hotseat.rs writes are the bytes this parses"

The last mutation is the finding. It went green the first time. Every trials.py fixture composed its marker out of BEGIN, so all of them followed BEGIN wherever it went — including away from what hotseat.rs actually writes. Nine checks about the marker, and reverting the marker broke every real stamped log with all nine still green.

The format is shared across two languages, so the control has to be a literal: the exact bytes the writer emits, asserted from the reading side, with the writer's own test asserting the same string from the other. A fixture built from the constant under test cannot test the constant.

stress_scope_rule() matches on the heading, not on row 20 — a row number is a property of today's file, a heading is a property of the rule — and returns None if the edition stops carrying it (ADR-0018: an absent explanation is reported absent, never replaced by ours).

Done 2026-08-08. make all green; cargo test: 80 render + 35 play; 24 trials.py checks. Verified on a live make ground VARIANT=h2 page.

Not done here

  • The other 21 passages are still unread. h2_rules_text() returns the whole rulebook and one caller uses one row of it. The Round-step passages in particular are the account of the sequence the player is standing in, and there is nowhere on the page that says it.
  • The baseline has no Rules_Text.csv at all, so this explains only the experiment. A baseline player still gets no rulebook — the reverse of the usual gap, and a ground-game question.
  • The 11 existing notes stay unrecorded forever. Their variant was never written down; inventing one is the defect this pass exists to avoid. 2026-08-08-1818's three notes are almost certainly H2 on the internal evidence of what they say, and almost certainly is not a provenance.