Repository navigation
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
Draft
stellarfeline wants to merge 15 commits into
stellarfeline wants to merge 15 commits into
Conversation
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
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.
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?};fromisactivation(default),quest-startor{objective}.on_expireis effect root R12 (EffectRootKind::ObjectiveExpire), run from the schedule with no acting player. New verbdespawn-wave{wave}.Refusals.
DW1085: a limit on an objective that is not announced.DW1086: afromthat is not one of the objective'safter-ancestors.DW1084(nav::limit): a window shorter than the critical path's proven walk at 4 t/block.DW0100covers the tick bounds and an emptyon_expire.DW0913gains adespawn-wavearm.DW1087is returned to the planner.Emission. The clock is
schedule … replace, started in the function its start event runs:activate_<obj>, the newactivate_q_<quest>, or a named ancestor'sactivate_<obj>.complete_<obj>marks the objective settled (dw.os_<obj>) and clears the clock.expire_<obj>→settle_expired_<obj>, guarded by the objective's ownpending_guard: it setsdw.x_<obj>, broadcasts[dw:expired …], prints thelimit.expiredchrome line with the bass cue, runs cleanup, runson_expire, then checks the quest. Every guard of a limited objective reads its expiry. Everyafterand quest-completion term on it reads the settled score. The countdown is a bossbar named with thelimit.countdownchrome string. The two chrome strings are in all 34 language tables.despawn-waveis 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 itson_expirefires. It is never left open with no time limit.First arrival. A
quest-startclock on a quest that is active when the world loads starts when the first player arrives:@a[tag=dw_joined], thejoin_placefirst-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 tostand, does nothing, and passes on the expiry marker. A completion marker that arrives first fails the step by name.PackTest.
limit_<obj>_expiresandlimit_<obj>_done, each in its own test environment.Gallery.
obj/climb-the-lofthas 700 ticks; itson_expirenarrates, setsflag/loft-went-darkand despawnswave/lane. The far hall's epilogue reads that flag. The primary statesfrom: activation, and the valley overlay bindsquest-startand{objective}. Three probes are refused by name, as DW1084, DW1085 and DW1086. Three more DW1086 probes write thelimitof akill, acollectand aninteract.Pages.
compiler.md,dsl/quest/check.md,delvec/compiler/nav/limit.md,dsl/diagnostic.md,flow.md,i18n.md, and the/new-delvepagesquest-capabilities.md,story.mdandladder.md.Release notes. The
delvec 1.16.0anddelvewright-dsl 0.71.0entries are exactly as onmain. The timed beat's lines are in a separatedelvewright-dsl 0.72.0entry.check-release-notesfails 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 warningsis 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