Skip to content

feat: a beat settles two ways — an objective's limit, its expiry world, and a bot that waits it out (spec-0133) - #1345

Draft
stellarfeline wants to merge 15 commits into
mainfrom
feat/timed-beat
Draft

stellarfeline wants to merge 15 commits into
mainfrom
feat/timed-beat

Conversation

@stellarfeline

Copy link
Copy Markdown
Owner

What it is for

A festival day whose daytime tasks are soft failures: a task done in time, or lost, and the day goes on either way while the night reads which tasks were done. spec-0133 gives an objective a second settlement, expired, which the proof enumerates as a story outcome and the bot proves by waiting.

What lands

  • DSL 0.72.0. Every objective kind takes limit {ticks, from?, on_expire?}; from is activation (default), quest-start or {objective}. on_expire is effect root R12 (EffectRootKind::ObjectiveExpire), run from the schedule with no acting player. New verb despawn-wave{wave}.

  • Refusals. DW1085: a limit on an objective that is not announced. DW1086: a from that is not one of the objective's after-ancestors. DW1084 (nav::limit): a window shorter than the critical path's proven walk at 4 t/block. DW0100 covers the tick bounds and an empty on_expire. DW0913 gains a despawn-wave arm. DW1087 is returned to the planner.

  • Emission. The clock is schedule … replace, started in the function its start event runs: activate_<obj>, the new activate_q_<quest>, or a named ancestor's activate_<obj>. complete_<obj> marks the objective settled (dw.os_<obj>) and clears the clock. expire_<obj> → settle_expired_<obj>, guarded by the objective's own pending_guard: it sets dw.x_<obj>, broadcasts [dw:expired …], prints the limit.expired chrome line with the bass cue, runs cleanup, runs on_expire, then checks the quest. Every guard of a limited objective reads its expiry. Every after and quest-completion term on it reads the settled score. The countdown is a bossbar named with the limit.countdown chrome string. The two chrome strings are in all 34 language tables. despawn-wave is the re-seat's own removal, written by one function.

  • Born expired. A clock that starts before its objective opens marks it run out (#lx_<obj>). When the objective opens, it settles expired that same tick and its on_expire fires. It is never left open with no time limit.

  • First arrival. A quest-start clock on a quest that is active when the world loads starts when the first player arrives: @a[tag=dw_joined], the join_place first-join hook, or any player online in a campaign with no spawn anchor.

  • Worlds. Expiry is an elective outcome that is never forced. Each limited objective gets one expiry witness per selection: the all world with that objective expired. A limit makes the campaign export its worlds.

  • Contract format 7. A step may be expire {objective, wait_ticks, stand}. The harness walks to stand, does nothing, and passes on the expiry marker. A completion marker that arrives first fails the step by name.

  • PackTest. limit_<obj>_expires and limit_<obj>_done, each in its own test environment.

  • Gallery. obj/climb-the-loft has 700 ticks; its on_expire narrates, sets flag/loft-went-dark and despawns wave/lane. The far hall's epilogue reads that flag. The primary states from: activation, and the valley overlay binds quest-start and {objective}. Three probes are refused by name, as DW1084, DW1085 and DW1086. Three more DW1086 probes write the limit of a kill, a collect and an interact.

  • Pages. compiler.md, dsl/quest/check.md, delvec/compiler/nav/limit.md, dsl/diagnostic.md, flow.md, i18n.md, and the /new-delve pages quest-capabilities.md, story.md and ladder.md.

  • Release notes. The delvec 1.16.0 and delvewright-dsl 0.71.0 entries are exactly as on main. The timed beat's lines are in a separate delvewright-dsl 0.72.0 entry. check-release-notes fails this file with 2 findings (one entry per line; an entry must name the version the tree states). No form of the file passes while 0.71.0 is untagged and the tree states 0.72.0. The 0.71.0 entry is dropped once that release is published.

Measurements

  • Suites: clippy -D warnings is clean. delvewright-dsl: 228 + 674 passed. delvec --lib: 1413 passed. delvec --test it --no-fail-fast: 3073 passed, 0 failed, 12 ignored. The harness: tsc clean, 91 + 226 tests passed over the touched files. The docs job's 31 checks pass.

  • Gallery primary: limit binding (DW1084): 1 objective(s) carry a limit; walk 204 of 700 tick(s) (29%) — obj/climb-the-loft; 10 expiry world(s) exported, 7000 tick(s) of waiting.

  • Story worlds: 10 before, 20 after.

  • PackTest: 295 required tests passed, including the 2 new limit templates (remote, at a041edd).

  • Bot critical path: PASSED, 34 steps.

  • Branch run of the expiry witness world/2: PASSED in 40 steps. [expire] obj/climb-the-loft: expired after 34781ms.

Fixes nothing recorded as an issue.

🤖 Generated with Claude Code

https://claude.ai/code/session_019qiQFhKtTpTbYnn9NZBbSd

stellarfeline and others added 15 commits November 10, 2026 15:35
The spec as approved on its own branch, and its index row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qiQFhKtTpTbYnn9NZBbSd
…ave (spec-0133 §2, §7)

Every objective kind takes an optional `limit {ticks, from, on_expire}`;
`on_expire` is the twelfth effect root (`EffectRootKind::ObjectiveExpire`),
visited by the one enumeration, its producers dated at the objective and
credited only in a world that expires it. `despawn-wave` emits the removal
the wave's re-seat performs, from one function. `DW1085` refuses a limit on
an unannounced objective, `DW1086` a clock started by a non-ancestor;
`DW0100` restates the tick bounds and an empty `on_expire`; `DW0913` gains
the `despawn-wave` arm. The flow model gains the expiry outcome: an
objective a story world expires settles at its step, firing `on_expire`
in place of its completion bundle, and every limited objective is one
expiry witness world.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qiQFhKtTpTbYnn9NZBbSd
…countdown (spec-0133 §3, §8)

The clock is `schedule … replace`, started by the function the limit's
start event runs: the objective's own `activate_<obj>`, the quest's new
`activate_q_<quest>` (driven once from the tick while the quest is
active), or a named ancestor's `activate_<obj>`. `complete_<obj>` marks
the objective settled and clears the clock; `expire_<obj>` runs
`settle_expired_<obj>` only under the objective's own pending guard,
which marks it expired and settled, broadcasts `[dw:expired …]` before
`on_expire`, tells an announced objective with the new chrome line and
the bass cue, takes its affordance and highlight away, and checks the
quest. Every guard of a limited objective gains its expired term, and
every `after` term and quest check on one reads its settled score, so
either settlement lets the line go on. The countdown is a bossbar named
with the new `limit.countdown` chrome string, redrawn once a second
through a replace-mode schedule, removed on either settlement. Two
chrome strings enter every language table. The PackTest gains
`limit_<obj>_expires` and `limit_<obj>_done`, each alone in its own test
environment; the campaign template's progression baseline clears a
limited objective's two scores (DW0807 refused the build until it did).

`cargo test -p delvec --test it limit::`: 8 passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qiQFhKtTpTbYnn9NZBbSd
…w is measured against its walk (spec-0133 §5, §6, §8)

`critical-path.json` moves to format 7: a step may be `expire`
`{objective, wait_ticks, stand}`, standing where the all world's step for
the objective stands, its `stand` a cell of the proven route outside the
objective's own volume (which the step keeps out of), every volume armed
at the step, a wave's notice and every body a talk walks to. Every limit
makes the campaign export its worlds; each expiry witness is a row of
`branch-plan.json` and a `world-path-<k>.json`. `DW1084` (`nav::limit`)
refuses a window shorter than the critical path's proven walk from the
clock's start step to the objective at 4 t/block; `validation/limit.json`
and the `limit binding (DW1084)` line state every limit, its walk, the
expiry worlds and their summed wait.

Measured on hello-world with a 1200-tick window on the door: `walk 16 of
1200 tick(s) (1%)` from the keeper; `from: quest-start` walks 7 blocks,
28 ticks, from the spawn; 1 expiry world, 1200 ticks of waiting.
`cargo test -p delvec --test it limit`: 15 passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qiQFhKtTpTbYnn9NZBbSd
An objective's `limit` and the `despawn-wave` verb change what a stage-5
document says. Set through the sites tools/lib/version_sites.py names: by
hand crates/dsl/Cargo.toml (the authority), Cargo.toml
[workspace.dependencies], versions.toml dsl_crate_version and
dsl_crate_req; cargo update -p delvewright-dsl for Cargo.lock and the
prefab generator's Cargo.lock; tools/ci/check-reference-versions.py
--write (5 bound claims); delvec fmt over every tracked document stating
0.71.0 outside the decision records: 349 examined, 349 rewritten, 0
unparseable, each a one-line diff. version_sites.py verify: 349 of 350
dsl_version statements equal 0.72.0, the one other in OLDER_DOCUMENTS.

Also lands `crates/dsl/tests/it/limit.rs` (criterion 1's schema export,
criterion 7's DW1085 and DW1086, DW0100's window bounds and empty
`on_expire`, DW0913's `despawn-wave` arm): 7 passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qiQFhKtTpTbYnn9NZBbSd
… what a lost task is (spec-0133 §8, §10)

The harness takes `critical-path.json` format 7 and refuses 6: an
`expire` step parses with `objective`, `wait_ticks` and `stand` (and
`pos`, the same cell), each required. The executor walks to `stand`,
does nothing, and passes on the anchored `[dw:expired …]` marker
(`markers::parseExpiredMarker`), within `wait_ticks` at 20 a second plus
the objective grace; a `[dw:complete …]` for the same objective first
fails the step by name. Harness: 91 + 226 tests pass over
critical-path, markers, executor, aftermath, watch and sequencer; tsc
clean.

Pages: compiler.md carries the `limit` surface row, the
`despawn-wave` effect and emission rows, the `limit` emission row, the
format-7 contract and the chrome count; `dsl/quest/check.md` DW1085 and
DW1086; `delvec/compiler/nav/limit.md` DW1084 (its declaring module's
page); `dsl/diagnostic.md` DW0913's third arm; `flow.md` the outcome
class and the expiry witness; `i18n.md` the two chrome keys. The
`/new-delve` pages: *A task that can be lost*, the soft failure among
the line shapes, and an expiry world's wait in *Branch runs*.
check-dw-codes, check-diagnostic-messages, check-effect-roots,
check-capability-ownership and check-skill-page are green. The settled
score is `dw.os_<obj>`: `dw.s_` is a datum's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qiQFhKtTpTbYnn9NZBbSd
…hree clocks refused (spec-0133 §9)

The primary limits `obj/climb-the-loft` (announced `reach-anchor` of the
mandatory far-hall line) to 700 ticks from its activation; its
`on_expire` narrates, sets `flag/loft-went-dark` and despawns
`wave/lane`, the wave the same line seats and no `kill` requires. The
far hall's final scene gains an epilogue line on that flag, so the
expiry witness is also an ending witness; zh-cn carries both new lines.
The valley-site overlay titles and announces its warden and its far
corner and gives them `from: quest-start` and `from: {objective}`.
Probes: `a-clock-on-a-silent-objective` (DW1085),
`a-clock-that-starts-after-its-objective` (DW1086),
`a-window-shorter-than-the-walk` (DW1084).

A generated PackTest that opens an objective writes each `after`
prerequisite's settled score where the prerequisite carries a limit
(`packtest_guards`, the dialogue mask, the join baseline, the trigger
dispatch): the valley point's `verb_interact` read `dw.os_far_corner`
without writing it and DW0807 refused the build until it did.

Measured (`delvec build`, local): primary `limit binding (DW1084): 1
objective(s) carry a limit; walk 204 of 400 tick(s) (51%) —
obj/climb-the-loft` at a 400-tick window (raised to 700: the step
carries an 11 s en-route cutscene allowance); story worlds 10 -> 20, 10
of them expiry worlds; valley: walk 40 of 2400 (quest-start), 112 of
2400 (from obj/ask-the-warden), 2 expiry worlds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qiQFhKtTpTbYnn9NZBbSd
…lfth root, `despawn-wave` and format 7

The full `cargo test -p delvec --test it --no-fail-fast` on the remote
build machine at a041edd ran 3085 tests: 3066 passed, 12 ignored, 7
failed, each an enumeration this change moved — the effect-root sweep's
count and its every-root control (now binding R12 through a titled
window on `obj/exit`), the verb censuses of `v35_perception` and
`happening_subject_tests`, three `format_version` literals (now the
constant `plan::CRITICAL_PATH_FORMAT_VERSION`), and the placements
audit, which classes `settle_expired_<obj>` with the bodies only a
played world holds. Clippy refused an unused binding in `limit.rs`.

The gallery: the DW1029 probe's declared count moves from 6 to 7 — the
far hall's endings read six seeded inputs now, `flag/loft-went-dark`
among them — and the README names the timed climb.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qiQFhKtTpTbYnn9NZBbSd
…ave` among the world changes

`cargo test -p delvewright-dsl` at 3f6b49c: 672 of 674 passed, the two
reds the verb count (46 -> 47) and the aftermath classes, which now name
`despawn-wave` as a verb that changes the world. After: 228 + 674 passed.
The same remote run: clippy clean, `cargo test -p delvec --test it
--no-fail-fast` 3073 passed, 0 failed, 12 ignored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qiQFhKtTpTbYnn9NZBbSd
…art are written

The coverage gate at 3f6b49c (local, `check-gallery-coverage.py`)
reported five units nowhere: `LimitFrom::activation` (the primary left
the default unwritten) and `Objective::collect|interact|kill.limit`.
The primary's window on the mezzanine now states `from: activation`;
three probes give the muster's fight, its bone and the laid emerald a
clock started by the objective itself, each refused as `DW1086` and
each declaring its kind's `limit` unit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qiQFhKtTpTbYnn9NZBbSd
The primary writes `from: activation`, so the probe edit is a replace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qiQFhKtTpTbYnn9NZBbSd
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qiQFhKtTpTbYnn9NZBbSd
…nd the timed beat

Merging main brought the 1.16.0 release notes, whose format entry is
headed `delvewright-dsl 0.71.0`; this branch states 0.72.0, and
check-release-notes refuses a heading the tree has moved past. The
entry is headed 0.72.0 and gains the `limit` and `despawn-wave` lines;
the delvec entry gains the timed beat. check-release-notes: 0 findings;
version_sites.py verify exits 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qiQFhKtTpTbYnn9NZBbSd
… expired, and a world nobody entered runs no clock (spec-0133 §3)

A clock started before its objective opens (`quest-start`, or an
ancestor's activation) could run out while the objective was closed;
`expire_<obj>` then did nothing and the objective opened later with no
window at all. Now `expire_<obj>` marks a not-done objective run out
(`#lx_<obj> dw.sys`), and the tick settles it expired the tick its own
pending guard opens (`born_expired_drivers`), `on_expire` firing and the
story going on; `setup` and the limit templates clear the mark. The test
`an_objective_whose_clock_ran_out_before_it_opened_is_born_expired` reds
with the driver withheld (measured: 0 passed, 1 failed; restored from a
scratch copy).

A `quest-start` clock on a quest active at world load started at the
first tick of an empty world. Its driver now also requires the first
player's arrival: `@a[tag=dw_joined]`, the first-join hook `join_place`
keys on, or any player online in a campaign with no spawn anchor
(`a_quest_start_clock_at_world_load_waits_for_the_first_arrival`).

spec-0133 §3 says both in its own words; compiler.md's `limit` row and
the `/new-delve` page follow. `cargo test -p delvec --test it limit`:
16 passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qiQFhKtTpTbYnn9NZBbSd
…right-dsl 0.72.0 beside them

The `delvewright-dsl 0.71.0` and `delvec 1.16.0` entries are restored
byte for byte to origin/main's (the record of the release being
published); the timed beat's lines move to a separate
`delvewright-dsl 0.72.0` entry. check-release-notes refuses this file
with two findings, and no form of it passes while 0.71.0 is untagged and
the tree states 0.72.0: the file holds one entry per line, and an entry
must name the version the tree states (or one a tag names).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qiQFhKtTpTbYnn9NZBbSd

This branch has not been deployed

No deployments
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