fix(harness): dsl_version allowlist covers 0.9.0 + compiler-sync CI gate (task #157) - #263
Merged
Merged
Conversation
…ate (task #157) The hollow-vigil ladder run found the bot tier refusing every dsl_version 0.9.0 campaign at the version gate — server booted, bot connected, then `critical-path/version unsupported version "0.9.0"` — because harness/src/critical-path.ts's SUPPORTED_DSL_VERSIONS allowlist still ended at 0.8.0 while spec-0026 raises the compiler's ceiling to 0.9.0. v0.9 only adds stage-1 (world-generation) surface the harness never parses, so every consumer gated on this one array (waypoints, combat plan) needed no other change. Durable guard: tools/check-harness-dsl-version.py asserts the compiler's SUPPORTED_DSL_VERSION (crates/dsl/src/envelope.rs) is a member of the harness's SUPPORTED_DSL_VERSIONS, wired into the docs CI job next to check-dw-codes.py. Nothing else in the repo relates these two files, so a future compiler version bump now fails loud instead of silently stranding the bot tier. Validator-only: no crates/ change, player-facing output is byte-identical.
stellarfeline
added a commit
that referenced
this pull request
Aug 4, 2026
…ene-rect moat fill (task #157 round 3) 1. SNAPSHOT PALETTE. block_color had no cherry entries, so 2044 cherry cells painted oak-green through the `_leaves` suffix family — the whole point of cherry-valley is that these read pink in every render. Explicit 1.21.11 texture-average entries: cherry_leaves [229,158,187], cherry_log [92,58,58] (bark-dominant), pink_petals [224,168,190] (was falling to the magenta UNKNOWN fallback). Sweep: those three were the only gaps in the surround emission id set; a test pins the pinks against the suffix families and the whole emission palette off-fallback. 2. VISTA FOV (planner ruling). A spawn eye 40 blocks from a 48-high rim subtends ~52 degrees — no fixed FOV frames floor + crest + sky. The vista camera now aims at MID-rim on the nearest crest line and carries a per-shot vertical FOV derived from the geometry: frame bottom <= -15 degrees elevation (gap floor in frame), top >= crest-top + 8 degrees of sky, clamped <= 110. Plumbing verified end to end: render_plan emits camera.fov (the existing pov_camera channel) and delve-render's Chunky scene emission honors it (scene.rs, default 70 when absent) — a new scene.rs test proves the field is never silently dropped, and a surround test asserts the 94x27 spawn-eye case frames floor AND crest-top arithmetically. 3. SCENE-RECT MOAT (planner design ruling). The #157 walls-down probe found DW0322 void exposure INSIDE the scene rect: the rect is the keep blob's bounding box, and columns outside every piece footprint were void. Scene-rect columns with ZERO piece-authored blocks now receive ambient gap-floor ground at floor_top_y with the gap floor's own surface dapple (world-coordinate noise - the pattern runs seamlessly from the annulus through the interior); columns with any authored block are untouched (pieces own their columns; authored overhangs/voids are intentional). Emitted as row-strip tiles (horizon/valley/m<n>) so no tile ever carries an air cell over a piece column; attached at the read_structures choke point (the authored-column set needs the piece bytes), so build, snapshot, blocking and the render plan all see the same completed surround; the un-climbability flood seeds on moat cells too. Fixtures with full plates emit zero moat tiles - existing emissions byte-identical. Proofs (all green): snapshot palette test; scene.rs per-shot fov test; vista_fov_frames_floor_and_crest_on_hollow_proportions; the_moat_fills_only_unauthored_scene_columns (L-blob, authored columns untouched, every unauthored column surfaced, byte-deterministic); v09_blob_scene_with_void_moat_builds_green (walls-down irregular blob in a 94x27 rect - no DW0322 at the rect edge or interior). Spec-0026 amendment text (short) + compiler.md same PR. Note: the bot tier on this branch is stale-red only because the branch predates merged #263; the batch integration branch carries the fix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
stellarfeline
added a commit
that referenced
this pull request
Aug 5, 2026
… kill tally (task #178) (#268) * chore(harness): pick #263 (dsl_version allowlist covers 0.9.0) onto this base worker/spec-0026-valley predates the fix, so the bot tier refuses every 0.9.0 campaign from this base and no ladder run is possible here. Identical patch to the one already on main; it drops out on rebase/merge. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnrGdWS7ADsjCoJfiJc6GC * fix(compiler,harness): the wave countdown is a liveness census, not a kill tally (task #178) A `kill` objective asks for the wave to be DOWN. The emitted countdown asked for something else — one player-credited kill per mob — and a mob has many ways to die that credit nobody: burning in daylight, falling out of the world, drowning, another mob. Every such death took a mob out of the world and left the countdown above zero, asking the party to kill something that no longer existed. No rest, no retry and no route recovers from that. hollow-vigil shipped it (owner playtest 2026-08-05). Its opening beat's roof and two walls come off for the open-air rule, the world is pinned at `time set noon`, and the first wave walked out through the carved wall and burned — `空洞步卒 burned to death`, reproduced live on the shipped datapack. Countdown stuck at 3 with nothing alive, so `obj/purge` never completed, `flag/halls-quiet` never set, `quest/the-barrow-key` never armed, and the barrow key's chest was never placed anywhere in the world. Design change, in two halves. **Emission.** `tick` now reconciles the countdown against the world, once per wave a `kill` objective adjudicates: execute if score #<w> dw.wave matches 1.. store result score #<w> dw.wave \ if entity @e[tag=dw_wave_<w>] Vanilla's own answer to "how many are left", so no new primitive and no bookkeeping to keep honest. `matches 1..` does both guard jobs: before the spawn the counter is unset, so an empty selector can never read as "cleared"; after a legitimate zero the line stops firing, so a finished wave stays finished and a `respawns_on_rest` re-seat re-arms it cleanly. The `player_killed_entity` decrement stays as a fast path — a mob the player just killed is still matched by `@e` through its death animation. **Harness.** `kill` was the one objective-bearing step that never called `requireObjective`; it graded itself on mob-side evidence and returned. That is why an all-green ladder shipped a softlock: `passed: true`, both encounters census-cleared, and the delve considered the objective open forever. The census still decides when to stop swinging; it no longer decides whether the step is proven. `critical-path.json` format 2 already DOCUMENTED this contract — `kill` simply did not implement it. Ladder proof (same world both sides, `hollow-vigil` @ ce82515): * generated PackTest `kill_uncredited` — `verb_kill` minus the hand-fed `k_reward_<wave>` calls. Shipped datapack: 19/20, `Expected #party dw.o_warden to match 1, but got 0`. Fixed datapack: 20/20. * bot tier on the fixed build: `critical path 'hollow-vigil' PASSED`. * live rcon on both builds: countdown 3→3→3 (stuck, 0 alive, no chest) vs 3→1→0 tracking the burn exactly. Determinism: two builds byte-identical; the datapack diff against the staged build is exactly the two census lines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnrGdWS7ADsjCoJfiJc6GC --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.
Summary
critical-path/version unsupported version "0.9.0"; harness supports 0.2.0 … 0.8.0.harness/src/critical-path.ts'sSUPPORTED_DSL_VERSIONSallowlist lagged spec-0026's compiler bump to 0.9.0.0.9.0. Checked for version-conditional harness behavior first: there is none — every consumer (critical-path.ts,waypoints.ts,combat.ts) gates on membership in this one array, and v0.9 (spec-0026) only adds stage-1 world-generation surface (horizonobject form, new base/shorthand names) the harness never parses. No other harness file needed a change.mainthe compiler's ownSUPPORTED_DSL_VERSIONis still0.8.0(spec-0026's compiler-side bump to0.9.0lives on the still-openworker/spec-0026-foundation/ PR feat(dsl,compiler): spec-0026 foundation — horizon library surface, per-area walk_y datum, DW0364/DW0366/DW0367 (dsl 0.9.0) #260) — this PR only moves the harness ceiling ahead of that landing, per the version-adoption discipline (CLAUDE.md: adoption is scheduled within the milestone, never left to lag).Durable guard (debug doctrine: automate the pitfall out of existence)
Added
tools/check-harness-dsl-version.py, wired into thedocs (local link check)CI job next totools/check-dw-codes.py(same class: deterministic, offline, stdlib-only Python 3 scanning source text — no build required). It asserts the compiler's singleSUPPORTED_DSL_VERSIONidentity constant (crates/dsl/src/envelope.rs) is a member of the harness'sSUPPORTED_DSL_VERSIONSallowlist (harness/src/critical-path.ts).Design choice and why: the compiler already names its own ceiling in exactly one place (
SUPPORTED_DSL_VERSION, distinct from the full accepted-setSUPPORTED_DSL_VERSIONSlist) — that's the authoritative source of truth, no compiler change needed to expose it. The harness already has one array every JSON-artifact consumer gates on. A text-scanning script comparing the two, run in the existing offlinedocsjob, is the smallest mechanically-enforced form: no new plumbing, no artifact the compiler would need to start emitting, no cross-job dependency. It checks membership of the ceiling, not set equality, so the harness may still carry older versions if the compiler ever drops one from its own list.Red -> green proof
Added a harness unit test (
accepts the 0.7.0, 0.8.0, and 0.9.0 dsl versions) before the fix — it reproduced the exact production error:After adding
"0.9.0"toSUPPORTED_DSL_VERSIONS: full harness suite green (320/320),tsc --noEmitclean.The sync guard itself is proven both ways by
tools/tests/test_check_harness_dsl_version.py(test_harness_lagging_the_compiler_ceiling_failsreproduces the task #157 shape over a synthetic source pair — red —test_harness_covering_the_compiler_ceiling_passes— green), and against the live repo:python3 tools/check-harness-dsl-version.pypasses today (compiler0.8.0⊆ harness{…, 0.9.0}).Scope
Validator-only: no
crates/change, player-facing output is byte-identical. Files touched:harness/src/critical-path.ts,harness/test/critical-path.test.ts,tools/check-harness-dsl-version.py(new),tools/tests/test_check_harness_dsl_version.py(new),.github/workflows/ci.yml,docs/reference/tools.md.Test plan
node --test test/critical-path.test.ts— red before, green after (see above)npm run test(harness, full suite) — 320/320 greennpm run typecheck— cleanpython3 -m pytest -q tools/tests— 92/92 green (incl. new sync-gate tests)python3 tools/check-harness-dsl-version.py,check-dw-codes.py,check-doc-dupes.py,check-worker-override.py— all green locally