Skip to content

feat(dsl,compiler): spec-0026 foundation — horizon library surface, per-area walk_y datum, DW0364/DW0366/DW0367 (dsl 0.9.0) - #260

Open
stellarfeline wants to merge 4 commits into
mainfrom
worker/spec-0026-foundation
Open

feat(dsl,compiler): spec-0026 foundation — horizon library surface, per-area walk_y datum, DW0364/DW0366/DW0367 (dsl 0.9.0)#260
stellarfeline wants to merge 4 commits into
mainfrom
worker/spec-0026-foundation

Conversation

@stellarfeline

@stellarfeline stellarfeline commented Aug 4, 2026

Copy link
Copy Markdown
Owner

spec-0026 foundation (task #155 slice W-A): horizon library surface, per-area walk_y datum, DW0364/DW0366/DW0367

Merge gate: owner playtest batch (engine emission = player-experienceable). Do not merge on green. Machine red→green on the motivating scenario is below and admits this into the batch.

Pairs with content PR stellarfeline/delvewright-campaigns#21 (worker/spec-0026-walk-y-backfill) — versions.toml re-pins to that branch's SHA (a63d1a0); re-pin to content main tip after the pair merges.

What changed (design level)

  • DSL 0.9.0 (stage 1): horizon accepts the existing strings (byte-identical for "void"/"ocean") OR the object form {base, …params} (spec-0026 §1). All six bases parse with their param schemas; "cherry-valley" desugars exactly to {base:"valley", flora:"cherry", palette:"stone-petal"} (parameter row, not a base — machine-asserted). Fences: the new surface is DW0141 below 0.9.0; valley/cherry-valley/summit/sky are additionally reserved at 0.9.0 (DW0141, the npc vendor/boss precedent) until the W-B/W-C surround slices land — never silently mis-emitted. DW0320 generalizes per §5: every non-void base requires boundary.
  • Flatland lands end-to-end at the ambient level (§1/§3): pinned bedrock/dirt/grass superflat (grass top y=63 = walk plane − 1 by datum equation), minecraft:plains biome, bare by design; Ambient::Flatland branch in the DW0322 boundary model (vacuously safe: ground everywhere, flush). The §3 seam material-dither band + acceptance criterion 4 belong to the surround-generator slices.
  • Per-area placement datum (§2), superseding plan::OCEAN_BASE_Y (deleted): area base y = walk_ref_y − walk_y; walk_y is required prefab metadata for every piece placed in a non-void horizon (DW0367, build/exit 3; pools additionally require entry-role agreement). Island walk_y=3 → base 60 — the datum change does not move the island (asserted against the real content island-beach-camp).
  • DW0364 (build/exit 3): empirical every-piece walk-cell-vs-flood-level proof over the assembled (or stage-7-edited) model, no waterline_y exemption — closes the DW0344 gap; DW0344 retained for waterline-declaring shore pieces (sea-mating), message rewritten to name the datum-equation term. Supersedes interim task feat(dsl,compiler): map editor L2 massing verbs — swap/insert/remove/rewire/reseed-piece (spec-0017 PR 3) #149.
  • DW0366 (validation/exit 1): horizon params out of range (ratio 2..=3, min_drop ≥ 100, build-range bounds on plateau_y/float_y) and params foreign to the declared base.
  • New module compiler::horizon: the one place horizon → physical facts (walk_ref_y, flood_level, generator-settings literals, ambient, slice status). docs/reference/compiler.md updated in this PR (rows DW0364/DW0366/DW0367 added; DW0141/DW0320/DW0322/DW0344 rows + the world-output/stage-1 sections updated); tools/check-dw-codes.py green (190 codes, all test-covered).

Plug-in contract for W-B (valley/cherry surround)

Stated in crates/compiler/src/horizon.rs module docs. A surround generator plugs in with three one-line seams outside its own new module:

  1. implement the generator in its own module (e.g. compiler::valley), consuming delvewright_dsl::ResolvedHorizon + the campaign seed, emitting placed prefab tiles like any other piece;
  2. flip that base's arm in horizon::base_implemented to true, and delete the DSL-layer reserved-base arm for it in dsl::validate::horizon_rules (same PR);
  3. fill the base's horizon::walk_ref_y / generator_settings / nav::Ambient arms with its ambient facts.

Everything else (per-area datum, DW0364/DW0367, server-properties plumbing, check-world-settings.sh parity — which asserts derivation, not values, so it is already horizon-agnostic) is base-generic. ResolvedHorizon carries ratio/rim_height/flora/palette with pinned defaults; HorizonFlora/HorizonPalette are typed enums.

Machine red→green: the #149 tide-mill scenario

Permanent fixture spec0026_horizon::flooded_interior_is_dw0364_and_corrected_datum_is_green_and_dry, plus the live demonstration:

run result
delvec @ origin/main (8a544d8), interior piece (walk plane local 1, no waterline_y) on horizon: ocean exit 0, silently GREEN — placed at base 60, walk plane world 61, one block under sea level; floods on first boot while every proof derives from a dry model
this branch, same content with the folklore datum stated honestly (walk_y: 3) exit 3, DW0364 — "74 standable cell(s) … at or below the ambient flood level (y=62)"
this branch, corrected datum (walk_y: 1, what the piece really authors) exit 0, placed at 0 62 0 (asserted), walk plane 63 — dry

Fixture verdicts (acceptance criteria 1/2/3)

  • Criterion 1: double-build byte-identity for flatland (new fixture) + ocean (existing v0.6 fixture, still green); {base:"ocean"} object form emits byte-identical to "ocean" (new fixture). Old-engine vs new-engine on the void hello-world: datapack/, server/, critical-path.json byte-identical; only manifest.json (content-pin provenance) and the unshipped creator-datapack/layout.json (version: 0.8.0→0.9.0, the standard consequence of every SUPPORTED_DSL_VERSION bump) differ — the shipped delve image (Dockerfile.delve COPY list) carries neither beyond the manifest pin.
  • Criterion 2: negative fixtures assert DW0364, DW0366 (5 range + 3 foreign-param cases), DW0367 by code; the feat(dsl,compiler): map editor L2 massing verbs — swap/insert/remove/rewire/reseed-piece (spec-0017 PR 3) #149-shaped fixture above is red with DW0364. (DW0365 belongs to the sky slice.)
  • Criterion 3: island_walk_y_3_keeps_base_60 — real content island-beach-camp (walk_y: 3) plans at [0, 60, 0], exactly the retired global datum.
  • The drowned-bell (owner's local WIP content, tidal-keep pool, entry walk_y: 3 → base 60): old and new engines produce identical diagnostic sets on it (DW0331 ×5 / DW0465 / DW0476 — pre-existing version-adoption debt, red on origin/main too); the datum change alters nothing about it.
  • One fixture adapted, check unweakened: edit_ocean_breach_strands_the_player_dw0322 relied on the mis-datumed placement (walk plane under the sea) that this PR fixes — its breach is now a canonical beach, correctly green. The stranding fixture now uses a genuinely lipped tileset (walk_y: 0, walk plane two over the sea) and still proves DW0322 fires.
  • Full workspace: 1206 tests green; fmt + clippy -D warnings clean; check-dw-codes / check-doc-dupes / check-versions / check-world-settings green.

Content pairing & adoption notes

  • Content branch backfills walk_y across all 36 GitHub-main prefabs (keep 1, cave 2, island 3, hello-room/hero 1–2), verified empirically from the .nbt geometry (standable-cell histogram + socket-floor probe), and regenerating the keep/cave tilesets from the updated generators reproduces the backfilled files byte-identically. All five generators now emit walk_y.
  • The in-flight tidal-keep/drowned-bell content (local, unpushed) needs its walk_y adoption when it lands: shore pieces 3, plinth pieces 11 (the tk generator in this PR already derives them from its door list) — same-milestone adoption round per the version-adoption discipline.
  • The pin also advances over content docs: spec-0005 — infra images on GHCR + version manifest (owner review requested) #11/docs: M1 complete — owner QA findings (lighting rule, environment sealing) #12 (island set-pieces/terrain), which the old pin (f5766f7) predated.

Ambiguities → reported, not invented

  1. vista_radius vs its own default: spec says DW0366 on vista_radius < view-distance×16, but summit ships view-distance 12 (§6) → floor 192 > the default 176. Contradiction left unenforced (summit is reserved anyway); needs a ruling in the summit slice.
  2. blend_width range: none stated; unvalidated (criterion 4's no-material-wall assertion is the real gate, W-B).
  3. Sky backdrop/placement params: deliberately not on HorizonSpec yet — the schema rejects the fields rather than freezing a wire shape before the sky slice picks one (§4 states no wire syntax).
  4. cave-shore authors an ankle-deep beach (walks flush at its own waterline); under its declared walk_y: 2 its walk plane lands dry at 63 but its authored pond would sit one block above the world sea. No tracked ocean campaign binds it; documented in cave-tileset.md.
  5. Pre-existing island-generator drift vs committed content (merged beach camp, waterline_y) is untouched.

🤖 Generated with Claude Code


Addendum 2026-08-06 — merged main (66 commits) into this branch

The branch sat while main moved 66 commits; six files conflicted. Resolved by
merge (no rebase, no force-push — PR #259 is based on this branch). Everything
below is either conflict resolution or an adaptation forced by what main
landed meanwhile. No player-facing behaviour of this slice changed:
dsl::validate::horizon_rules, horizon_defaults, compiler::horizon,
compiler::plan's datum and DW0364/DW0366/DW0367 are byte-identical to
370a45b.

The two dangerous conflicts

crates/dsl/src/envelope.rsmain had independently taken dsl 0.9.0.
Both sides bumped SUPPORTED_DSL_VERSION to 0.9.0 and both added is_v09,
for different features: main for task #179's declared drops (stage 5), this
branch for the spec-0026 horizon library (stage 1). Resolved as one version
carrying both surfaces
— the established precedent (v0.8 carries spec-0025
and spec-0016 §1), and correct because the fences are per stage: raising
quests to 0.9.0 opens drops and still DW0141s the horizon object form, and
vice versa. SUPPORTED_DSL_VERSIONS and the ordinal arm were identical on
both sides and auto-merged; only the doc contract needed writing, and it now
names both halves and the per-stage consequence.

versions.toml — the content pin. main had advanced [content].sha to
87a8ade (island storybook marker), and content main has since moved to
885fd5d. This branch's pin (a63d1a0) was a content-branch commit sitting on
4992b4f, four commits behind — reinstating it would have rolled back the
storybook marker, the v1.0.0 engine re-pin and both release fixes. Taking
main's pin was equally impossible: content main carries walk_y on zero
of its 36 prefabs
, and this engine's DW0367 hard-errors (exit 3) on any piece
placed in a non-void horizon without it.

Resolved by bringing the paired content branch forward instead: content
worker/spec-0026-walk-y-backfill (PR #21) now merges content main 885fd5d,
and the pin is that merge, 5a23d90. The merge result is exactly content
main plus 36 one-line additions
— the same shape as the original backfill,
re-derived over main's regenerated island prefabs (which changed geometry —
island-beach-camp 21×8×17 → 21×15×44 — and switched to 1-space indent). The
island values were re-derived, not copied: each regenerated file still declares
waterline_y: 2, and the island convention is walk_y = waterline_y + 1 = 3.
5a23d90 is a strict descendant of both 87a8ade and 885fd5d, so nothing is
rolled back. Still a branch sha, still re-pinned to content main the moment
content #21 merges.

Semantic collisions the conflict markers never flagged

  1. crates/compiler/src/render_plan.rs (new on main, PR feat(render): first-class panorama emission + Chunky adoption (task #29) #295) matched the
    old Horizon enum shape
    and stopped compiling against this branch's
    Horizon::Name/Horizon::Spec. Rewritten onto horizon::base_of, exhaustive
    on HorizonBase. flatland deliberately emits no horizon key: the
    render side's Horizon is an externally-tagged enum that knows only ocean,
    so a {"kind":"flatland"} invented here would be an unparseable plan rather
    than a better picture. The wire shape belongs to spec-0026 §6's render work,
    which lands with the surround slices. Ocean and void are byte-identical.
  2. main amended spec-0026 itself (PR docs(specs): spec-0026 amendment — vista_radius definition + blend_width range #261, 2026-08-04), resolving the two
    ambiguities this PR had reported rather than invented — vista_radius
    measured from the scene bounding box, floor 192, default 208 (the old 176
    sat below its own floor); blend_width 1..=16. The DW0366 doc row's claim
    that the spec "contradicts its own default" was therefore false and is
    corrected. Enforcement is deliberately not added here: both params
    configure surrounds this slice does not emit, and PR feat(dsl,compiler): valley + cherry-valley horizon — surround generator, biome layer, tree layer (spec-0026 W-B, task #155) #259 (based on this
    branch) already implements both verbatim. Implementing them twice only put a
    duplicate in the dependent branch's path — it was written, then backed out for
    exactly that reason, and the row now says who owes the check.
  3. Per-row rather than per-file docs resolution: pass-order row 6 is main's
    (compiler::pool/DW0498), row 7 + the new 7b are this branch's (walk_y
    datum, DW0364). The waterline_y bullet keeps this branch's general
    walk_y − 1 statement and main's concrete island fact, which agree
    (walk_y 3 → waterline 2). The DW0141 row is main's fuller pre-0.8
    enumeration plus a pre-0.9 clause naming both v0.9 surfaces.
  4. crates/compiler/tests/cli.rs asserted delvec 0.1.0; main made
    DELVEC_VERSION derive from CARGO_PKG_VERSION (PR fix(compiler): DELVEC_VERSION derives from the crate manifest, not a hand-typed copy #293) and the crate is
    1.0.0. Took main's assertion — the branch's literal is stale, not a design
    disagreement.

Verification

  • 1662 workspace tests green (was 1206 pre-merge); cargo fmt + clippy -D warnings clean.
  • All five spec-0026 fixtures still green over the regenerated content,
    including island_walk_y_3_keeps_base_60 — the datum survives main's island
    regen — and the feat(dsl,compiler): map editor L2 massing verbs — swap/insert/remove/rewire/reseed-piece (spec-0017 PR 3) #149 red→green pair.
  • check-versions / check-world-settings / check-dw-codes (210 codes, all
    test-covered, 1 allowlisted) / check-doc-dupes (68 files) /
    check-required-contexts (10 ci.yml jobs ↔ 10 required contexts) /
    check-harness-dsl-version (0.9.0 in the allowlist) / check-skill-version /
    check-storybook-version (1 campaign, 1 allowlisted) /
    check-shell-pipe-shortcircuit (15 scripts) / check-compose-isolation (4
    services) — all green. No job renamed, so .github/required-status-checks.txt
    is untouched.

Effect on PR #259

main alone conflicts with #259 in 8 files. Against this updated branch it
conflicts in 3: crates/compiler/src/main.rs (main's DW0498 advisory
changed read_structures from &mut plan to &plan), docs/reference/tools.md
(main deleted check-worker-override.py and added five checker rows), and
docs/reference/compiler.md (5 blocks — four are #259's task-#178 wave-countdown
content vs main's container-adoption/drops content, plus the DW0141 row,
which conflicts against main regardless). Exactly one block is attributable
to this PR: the DW0366 doc row, whose resolution is to take #259's side, since
#259 enforces what that row describes.

stellarfeline and others added 2 commits August 4, 2026 00:32
…er-area walk_y datum, DW0364/DW0366/DW0367 (WIP: tests/docs follow)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…er-area walk_y datum, DW0364/DW0366/DW0367 (dsl 0.9.0)

Stage-1 horizon generalizes into the spec-0026 composition: string shorthands
(byte-identical for void/ocean) OR the object form {base, ...params}, all six
bases parsed and param-validated; flatland lands end-to-end (grass superflat
ambient one block under the scene walk plane, plains biome, bare);
valley/cherry-valley/summit/sky are reserved (DW0141) until their surround
slices. Placement moves to the per-area datum walk_ref_y - walk_y (DW0367),
retiring plan::OCEAN_BASE_Y; DW0364 proves every placed piece's standable
cells sit above the flood level empirically, with no waterline_y exemption —
the #149 flooded-interior class is now impossible by construction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
stellarfeline and others added 2 commits August 6, 2026 01:57
…dation

# Conflicts:
#	crates/compiler/src/lib.rs
#	crates/compiler/tests/cli.rs
#	crates/dsl/src/envelope.rs
#	crates/dsl/src/lib.rs
#	docs/reference/compiler.md
#	versions.toml
…am, not by this slice

The DW0366 row claimed `summit.vista_radius`'s floor was unenforceable because
the spec contradicted itself (floor 192 vs default 176). That was true when the
foundation slice reported it and stopped being true on 2026-08-04: the spec-0026
amendment (PR #261, on `main` for 66 commits now) resolved both open params —
`vista_radius` measured from the scene bounding box, floor 192, default 208; and
`blend_width` 1..=16.

Enforcement is deliberately NOT added here. Both params configure surrounds this
slice does not emit, and the valley slice (task #155 W-B, PR #259, based on this
branch) already carries the checks and the corrected default verbatim.
Implementing them twice would put a duplicate in the dependent branch's path for
no behaviour gained. The row now says what is true: the ambiguity is resolved,
the enforcement is owed, and which slice owes it.

No behaviour change: `dsl::validate::horizon_rules` and `horizon_defaults` are
byte-identical to this branch's pre-merge state.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AjQ5p1Kv5MrkGPumi7yXWL
stellarfeline added a commit that referenced this pull request Aug 7, 2026
…he board is red (task #44) (#322)

Every gate in CI built a FIXTURE. Nothing ever built a real campaign, so the
product could stop compiling with the whole board green — PR #260 reached 10/10
while stopping the flagship released campaign `nobodys-cave-island` from
building at all (26 x DW0364 on standable cells at the ocean line).

New required status check `campaign builds (every campaign in the content repo)`
builds every campaign the pinned content checkout carries, in every language its
world.json declares, on every push.

Campaigns are DISCOVERED, never listed, so the next content re-pin gates a new
campaign with nobody remembering. A campaign that cannot build today is recorded
in .github/campaign-build-exclusions.toml, which INVERTS the assertion instead of
removing it: still built, must still fail, and must fail with exactly the
recorded codes — an extra code is a new break that was hiding behind the
exclusion, and a success is an expired exclusion. Both red. One entry today:
hollow-vigil, DW0331 (task #34).

Validator-only: no crate, no emission path and no prefab is touched, so
player-facing output is byte-identical.


Claude-Session: https://claude.ai/code/session_01AjQ5p1Kv5MrkGPumi7yXWL

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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