Repository navigation
Add the clock provider seam to the stdlib time module (7.1.1) - #696
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
Summary
WalkthroughThe stdlib now supports injectable system and fixed clocks for ChangesStdlib clock seam
Sequence Diagram(s)sequenceDiagram
participant Template
participant StdlibConfig
participant register_with_config
participant now
participant ClockProvider
Template->>StdlibConfig: configure clock
StdlibConfig->>register_with_config: provide WallClock
register_with_config->>now: capture clock
now->>ClockProvider: read instant
ClockProvider-->>now: return timestamp
now-->>Template: render formatted time
Suggested labels: Priority: ⬇️ Low Change: Refactor Merge Risk: 🔵 Low · up to The guides currently describe the clock lifecycle incorrectly, which can mislead users configuring stateful test clocks. Correct the wording before merge; runtime behavior and timestamp parsing are otherwise supported. 🚥 Pre-merge checks | ✅ 14 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (14 passed)
Full details: ObservabilityExplanation The pull request changes runtime output: Resolution Add a structured debug field at the clock-registration decision point, using the bounded values A fixed clock ticks on cue Comment |
Reviewer's GuideThis documentation-only PR adds a draft ExecPlan for making the stdlib Sequence diagram for configured now() renderingsequenceDiagram
participant Caller
participant Config as StdlibConfig
participant Registration
participant Time as time module
participant Provider as ClockProvider
Caller->>Config: with_clock(provider)
Caller->>Registration: register_with_config(config)
Registration->>Config: clock()
Registration->>Time: register_functions(WallClock)
Caller->>Time: Render template containing now()
Time->>Provider: read provider()
Provider-->>Time: OffsetDateTime instant
Time->>Time: to_offset(parsed)
Time-->>Caller: Rendered timestamp
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
d91ee5f to
1e6c93e
Compare
PR #696 still described itself as plan-only. The description now covers the delivered seam, and the plan records that swap plus the fact that the draft flag was left alone on purpose: whether to mark the PR ready before or after CodeRabbit's PR-level review is the maintainer's call. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Ensure that this is validated against the current version of the codegraph. If further refinement to address this finding would be deleterious, please supply a clear explanatory one to two paragraph markdown message in a code block that I can paste into the CodeScene web ui's diagnostic suppression function so this diagnostic can be silenced. Complex Methodtests/bdd/steps/stdlib/rendering.rs: render_template_with_context What lead to degradation?render_template_with_context has a cyclomatic complexity of 9, threshold = 9 Why does this problem occur?A Complex Method has a high cyclomatic complexity. The recommended threshold for the Rust language is a cyclomatic complexity lower than 9. How to fix it?There are many reasons for Complex Method. Sometimes, another design approach is beneficial such as a) modeling state using an explicit state machine rather than conditionals, or b) using table lookup rather than long chains of logic. In other scenarios, the function can be split using EXTRACT FUNCTION. Just make sure you extract natural and cohesive functions. Complex Methods can also be addressed by identifying complex conditional expressions and then using the DECOMPOSE CONDITIONAL refactoring. Helpful refactoring examplesTo get a general understanding of what this code health issue looks like - and how it might be addressed - we have prepared some diffs for illustrative purposes. SAMPLE# complex_method.js
function postItem(item) {
if (!item.id) {
- if (item.x != null && item.y != null) {
- post(item);
- } else {
- throw Error("Item must have x and y");
- }
+ // extract a separate function for creating new item
+ postNew(item);
} else {
- if (item.x < 10 && item.y > 25) {
- put(item);
- } else {
- throw Error("Item must have an x and y value between 10 and 25");
- }
+ // and one for updating existing items
+ updateItem(item);
}
}
+
+function postNew(item) {
+ validateNew(item);
+ post(item);
+}
+
+function updateItem(item) {
+ validateUpdate(item);
+ put(item);
+}
+ |
This comment was marked as resolved.
This comment was marked as resolved.
PR #696 still described itself as plan-only. The description now covers the delivered seam, and the plan records that swap plus the fact that the draft flag was left alone on purpose: whether to mark the PR ready before or after CodeRabbit's PR-level review is the maintainer's call. Co-Authored-By: Claude Code <noreply@anthropic.com>
d93e55e to
1b87a1e
Compare
Recording a total changes it: every plan-only commit that maintains this record adds its own length to the diff, so a head-specific total is stale the moment it is written. Replace the quoted total with the two quantities that do not move — changed-file count and the non-plan remainder — and make the attribution section attribute that remainder rather than a total.
Every substantive check passes and the pull request is approved; the only red check is the non-required CodeScene review of the base branch. Name the four checks the ruleset actually requires, so the distinction is on the record rather than left to be re-derived.
Three edits, each verified against current source before repair.
`tests/documentation_examples_tests.rs`: the CodeScene duplication
thread on `clock_snippet_mirrors_the_doctest` was valid — that test,
`env_reader_snippet_mirrors_the_doctest` and
`ninja_request_snippet_names_both_request_types` were three copies of
one shape. Extract `assert_snippet_names`, which carries the shared
"Rust fence, then each needle" contract and takes the example id, the
label used in failure messages, and the needles. Behaviour is
unchanged: all 32 tests in the target still pass.
`docs/adr-008-environment-seam-taxonomy.md`: the `with_clock`
injection-point link still named `src/stdlib/config/mod.rs`. That is
stale because the clock seam was split into its own
`src/stdlib/config/clock.rs` earlier in this branch, so the ADR
pointed at a file that no longer holds the function.
`docs/execplans/7-1-1-clock-provider-seam.md`: three corrections to the
living document — remove the duplicated `use std::{fmt, sync::Arc};`
and `time::OffsetDateTime` import lines from the implementation sketch;
record in D4 that the first review upheld a "User-Facing Documentation"
warning against the decision's rationale, since `with_clock` is a
public Rust API and not only a manifest-author concern; and correct the
recovery instructions, which described `git reset --hard` and
`git checkout --` as though they were scoped to the mutation when both
discard uncommitted work more broadly.
Gates: `make check-fmt` (including mdtablefix under the Makefile's own
flag set), `make markdownlint`, `make lint`, and the
`documentation_examples_tests` target.
`Windows / build-test-windows` fails at this head, which looks alarming next to a green branch history. It is an estate-wide breakage: the same job fails on `origin/main` (`ef7ed760`) and on every unrelated branch tested, and it last passed anywhere at 09:38Z on `36e03c7f`. The failing case is `stdlib::network::redirect::error_tests::protocol_failures_are_classified_from_a_live_response`, which lives on `origin/main` in `src/stdlib/network/redirect_error_tests.rs` (via #667). This branch's diff against its merge base `a273fad3` adds zero bytes under `src/stdlib/network/`. The error is a Windows socket race (`WSAECONNABORTED`, `os error 10053`) against the test's own loopback listener. Also recorded: the job share is not a required check. The ruleset `main-required-checks` requires only `build-test`, `kani-smoke`, `netsukefile` and `release / metadata`; `build-test` is a different job and passes at this head, so the required set is green. Gates: `make check-fmt` and `make markdownlint`.
The review asked for in this round is a posted *request*, not a completed review, and the plan should not read as though the two are the same. The entry names the queued head, the queue id, the quoted delay, and the fact that a comment body does not pin a revision — so whichever commit CodeRabbit inspects has to be read back afterwards. Gates: `make check-fmt` and `make markdownlint`.
The first rebase landed at 07248a3; origin/main has since advanced to 79545e1 (the 19-update GitHub-actions group bump). Replay the 40 branch-owned commits above the new merge base with the same explicit options used before. The re-target is byte-for-byte identity-preserving: every commit is `=` under range-diff, there are no merges and no conflicts, and the net diff is unchanged at 27 files / 3603 insertions / 161 deletions. Cargo.toml and Cargo.lock are byte-identical to origin/main, so no regeneration was needed. The new commit is a workflows-and-contract-test delta with zero file overlap with this branch, and `make test` runs only Rust targets, so it lies outside this branch's gate surface. It does not move the Windows job's line anchors, so the recorded Windows diagnosis still holds. Weave again did not participate: the driver is registered globally but merge attributes are `unspecified` for every branch-owned path.
CodeRabbit reviewed 8de3c96 and raised one inline finding plus an Observability pre-merge warning. Both are valid against the current source; neither was present when the branch was last reviewed. The inline finding is a real wording defect in both guides. They said the provider is read "rather than captured at registration", but `register_functions` moves a `WallClock` into the registered closure, so the clock *is* captured while the *instant* is not. The crate's own docs, ADR-008 and the technical design all state this correctly, which leaves the two guides as the outliers. Both passages now say that registration captures the adapter and each call invokes it afresh. The Observability warning asks for a bounded debug field at the clock-registration decision point. PR #669 added exactly such an event for the file filters one line below, so the gap is genuine and the shape is settled. `WallClock::source_label` now names the provenance from a closed set, and `register_with_config` records `clock_source` alongside "registered stdlib time helpers". `Debug` reuses the same accessor, so the label has one definition. `registration_reports_the_clock_source` covers both provenances and asserts the event never carries a provider's instant. It lives in the integration suite because `register_with_config` is public and the event is emitted there, not in `time::register_functions`. Removing the label mutation turns the injected case red, so the assertion has teeth.
`make check-fmt` rejected two spots: the `source_label` if-else on one line, and an over-long `assert!` in the new integration test. Both are formatting only; no behaviour changed.
The queued review is closed as a *completed* review rather than a pending request: its read-back shows CodeRabbit inspected 8de3c96, not the pre-rebase head the queue comment named, and returned CHANGES_REQUESTED with one inline finding and an Observability pre-merge warning. Both findings are disposed of with evidence. The inline wording finding is valid — register_functions moves a WallClock into the registered closure, so the clock is captured while the instant is not — and both guides now say so. The Observability warning is valid and answered with source_label plus the clock_source debug field, covered by a mutation-tested integration case. Adds two evidence entries: check-fmt is two gates behind one name, and the re-target boundary is the current merge base rather than an earlier one. Co-Authored-By: Claude Code <noreply@anthropic.com>
The registration event case returned Result while asserting with assert!, which clippy::panic_in_result_fn rejects under -D warnings; make lint aborted at lint-clippy on the std_filter_tests target. The assertion is replaced by a contextual `?`, so a registration failure propagates as the error the signature already promises. The test still fails on the same condition and the closed-set assertion is untouched: mutating source_label to report "system" for both provenances turns case_2_injected red, and the source is restored byte-identically. Co-Authored-By: Claude Code <noreply@anthropic.com>
The -8 gate run failed on one test outside the change surface that timed out at the 300 s per-test allowance. Recorded as artefact entry 21 with the measurement that settles it: raising the ceiling shows the test completing in 292.1 s, of which 291.6 s is its nested cold cargo build, matching the 688.6 s figure the developers' guide already records for that build under contention. Also records that my first explanation -- heavy load -- was refuted by two isolated re-runs that timed out at load 9.3 and 7.0, while its conclusion was right. The mechanism is a cold build behind a shared package-cache lock, which bites at moderate load; the entry keeps the measurement and drops the story. Co-Authored-By: Claude Code <noreply@anthropic.com>
The `-9` gate run on `25787722` reproduced the single-test signature of `-8`: `harness_compiles_under_a_split_build_dir` timed out at its 300 s allowance with 3245 of 3250 passing, and no other test failed. Issue #732 already describes this mechanism, names this test, and states that the harnesses' repeated compilation "is what puts these tests near the 300 s per-test allowance". Recording it turns "not this branch's defect" from an assertion into a citation, and keeps the plan from re-deriving the diagnosis a third time. Folded into entry 21's body rather than added as a sibling list item: a new marker at that position restarts markdown's ordered list, and mdtablefix's `--renumber` rewrites it to `1.`, which is not canonical. Co-Authored-By: Claude Code <noreply@anthropic.com>
The re-target push advanced the branch to `f81f2f98` on base `79545e12`, under a lease bound to the previously recorded remote head `8de3c963` so a concurrent rewrite would fail the push rather than be overwritten. Records the gate state at that head and, specifically, why the `lint` re-run mattered: the `panic_in_result_fn` error was only confirmed fixed by running the gate at a head containing the fix, since the earlier `-8` log predates it. Also records that both review replies were posted against this head, and that the CI run the push triggered is not yet claimed as green. Co-Authored-By: Claude Code <noreply@anthropic.com>
All four required checks pass on `74822cc2`: `build-test` and `kani-smoke` (CI run 35454416505), `netsukefile` (35454416354), and `release / metadata` (35454416662). The sole red job is the non-required `Windows / build-test-windows`, failing for the already-recorded pre-existing reason, re-verified here against `main` at `ef7ed760` rather than assumed. Also corrects a wrong premise I supplied while briefing the monitor: I described that job as failing in `git submodule` before project code runs. It does not — those lines are post-job cleanup from a successful checkout, and the real failure is the loopback race at the test step. The conclusion held, which is why the premise had to be checked rather than inherited. Co-Authored-By: Claude Code <noreply@anthropic.com>
CodeRabbit's separate answer to the pre-merge reconciliation confirms the implementation by static inspection and instructs that the observability warning be marked resolved, with no follow-up issue and no further code work. Records two facts separately rather than merging them: both review threads are resolved and the queue is empty, while the `CHANGES_REQUESTED` decision persists and is anchored to `8de3c963`, which is no longer an ancestor of the branch. A stale anchor is not an approval, and clearing it would mean dismissing a review or approving on the bot's behalf — so the decision is left with its designated owner, and the four required checks are recorded as `SUCCESS` on the published head. Co-Authored-By: Claude Code <noreply@anthropic.com>
Recording "CI is green at head H" is itself a commit, and that commit moves the head, invalidating the verdict it records. The plan is not a passive record here: `build-test` runs `make check-fmt`, markdownlint over `**/*.md`, `make spelling`, and the workspace test suite, and `tests/execplan_status_contract_tests.rs` reads `docs/execplans/` — so an edit to this document is an input to the same required checks whose result it reports. Three pushes were spent rediscovering that. The fix is not to keep re-verifying but to name the head that was verified rather than implying the newest one is, so this is the last plan commit for the re-target. Co-Authored-By: Claude Code <noreply@anthropic.com>
The reconciliation entry recorded that the walkthrough's Observability row persists because the comment "has not been re-edited". That is wrong: the comment's updated_at moved to 2026-09-19T17:25:12Z, after CodeRabbit's confirmation at 16:13:51Z, so it was touched. What did not happen is any change to what it says. Three captures of the body taken after that edit (17:55, 18:51, 20:06) are byte-identical to the live body, so the touch was content-preserving and left the row standing. The distinction is load-bearing. "The table is stale" would justify asking for another pass; "the table was refreshed and the finding still stands" would not. Only the first reading fits the evidence, and the second is the one the recorded reason implied. Dispositions are unchanged: both threads isResolved, both fixes verified present at current source, all four required checks SUCCESS on 74822cc. The row remains CodeRabbit's to flip; ticking its Ignore checkbox or dismissing the review to force the table green is not done here. Gates: make check-fmt, make markdownlint, make spelling all exit 0, and execplan_status_contract_tests 9/9 passed -- the one test reading docs/execplans/. Co-Authored-By: Claude Code <noreply@anthropic.com>
Two entries, both about replacing a record that no longer describes reality. The Progress section gains the re-rebase onto the current origin/main: boundary 79545e1, target 397fb58, 52 commits replayed, exactly one conflict, in docs/v0-1-0-migration-guide.md where main's #737 rewrote the at-a-glance table while this branch appended a row to it. Both intents were additive, so the resolution keeps main's table and appends the row; range-diff reports 51 of 52 pairs identical and the one divergence is padding. The substantive fixes are shown to survive by whole-file patch-id comparison rather than by reading subjects. The timeout disposition in entry 21 is marked superseded rather than left to read as current. It cited issue 732 as a live tracker and concluded there was nothing to fix here. The rebase adopted #752, which closed 732 and replaced that test's live private rebuild with a recorded Cargo JSON fixture, so the test no longer spawns Cargo and was dropped from the nested-cargo-builds group. The first rebased gate run confirms it: make test is 3394 of 3394 passing, 0 failed, 5 skipped in 276 s, with the formerly-timing-out test green and no longer slow. Co-Authored-By: Claude Code <noreply@anthropic.com>
1ef9859 to
cabb7c6
Compare
|
@coderabbitai Please reconcile the Observability row against the current head. The branch has been rebased onto the current
I read your 2026-09-24T20:42:22Z reply, which confirmed this warning at
You closed that reply with "Treat the observability warning as resolved" and "No The two checks you explicitly did not treat as resolvedYou were careful to add that your assessment was not a resolution of the failed Windows This was a superseded head, not a live defect. The fix, #749
Scope of this reconciliation, stated plainlyThe walkthrough's pre-merge row still renders as a warning, because the row is Gates on the rebased head: One operational note so the record is accurate: the |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- revision ---'
git rev-parse HEAD
echo '--- file discovery ---'
fd -a '^(clock|register|time_functions)\.rs$' src tests
echo '--- outlines ---'
ast-grep outline src/stdlib/time/clock.rs --items all --match 'WallClock|source_label' --view expanded
ast-grep outline src/stdlib/register.rs --items all --match 'register_with_config' --view expanded
ast-grep outline tests/std_filter_tests/time_functions.rs --items all --match 'registration_reports_the_clock_source' --view expanded
echo '--- accessor and debug ---'
rg -n -A20 -B9 'source_label|impl\s+Debug\s+for\s+WallClock' src/stdlib/time/clock.rs
echo '--- registration ---'
rg -n -A22 -B15 'registered stdlib time helpers|time::register_functions' src/stdlib/register.rs
echo '--- trace test ---'
rg -n -A105 -B18 'registration_reports_the_clock_source' tests/std_filter_tests/time_functions.rs
echo '--- relevant ancestry ---'
for sha in 061182b1 8de3c963; do
if git merge-base --is-ancestor "$sha" HEAD; then echo "$sha ancestor"; else echo "$sha not ancestor or unavailable"; fi
done
echo '--- PR head and checks ---'
gh pr view 696 --repo leynos/netsuke --json headRefOid,statusCheckRollup --jq '{headRefOid,checks:[.statusCheckRollup[]? | {name:(.name // .context),status:(.status // .state),conclusion:.conclusion,description:.description}]}'Length of output: 9054 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- relevant test locations ---'
rg -n -A35 -B15 'fn\s+protocol_failures_are_classified_from_a_live_response\b|shutdown\(Shutdown::Write\)|shutdown\(Write\)' --type rust src tests | head -180
echo '--- PR 749 ---'
gh pr view 749 --repo leynos/netsuke --json number,title,state,mergedAt,mergeCommit,url --jq '{number,title,state,mergedAt,mergeCommit,url}'
echo '--- current commit check ---'
gh pr view 696 --repo leynos/netsuke --json headRefOid,statusCheckRollup --jq '{headRefOid,checks:[.statusCheckRollup[]? | select((.name // .context) == "Windows / build-test-windows" or (.name // .context) == "CodeScene Code Coverage (main)" or (.name // .context) == "CodeRabbit") | {name:(.name // .context),status:(.status // .state),conclusion:.conclusion}]}'Length of output: 6142
Keep the Observability row resolved at 🧠 Learnings usedYou are interacting with an AI system. |
The rebased head was published with a force-with-lease bound to the previously recorded remote head, and the PR base now reads the same SHA as the replay target. Records how CodeRabbit's two explicitly-unresolved checks were disposed of. The Windows close-abort failure is superseded by this rebase, proved by ancestry: the fix was absent from the head that failed and is present afterwards. The CodeScene coverage timeout is trunk-only by design and not in the required set. Also records that the new head's green CodeRabbit status carries the description "Review paused" rather than "Review completed", so it is a pause stamp and not evidence of a review. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
@coderabbitai approve |
✅ Action performedComments resolved and changes approved. |
Summary
Implements roadmap item 7.1.1, which makes the Netsuke stdlib
now()Jinjafunction testable by reading its instant through an injected clock provider
instead of calling
OffsetDateTime::now_utc()directly.Plan and delivery record:
docs/execplans/7-1-1-clock-provider-seam.mdWhat this delivers
A caller that builds a
StdlibConfigmay supply aClockProvider, and everynow()call in that Jinja environment returns exactly that instant. A callerthat supplies nothing keeps today's behaviour precisely. There is no
user-visible change: a manifest author sees identical
now()behaviour beforeand after.
The seam is a prerequisite refactor for the Netsukefile testing framework
(roadmap phase 7), specified by
technical design §5.2,
and is deliberately scoped as a deliverable in its own right — no
netsuke testcommand, no test dialect, nosrc/testingmodule.Where it landed:
src/stdlib/time/clock.rs—ClockProvider,system_clock(),fixed_clock(), and the privateWallClockcontainer that normalizes everyread to UTC and labels the clock in
Debugoutput.src/stdlib/time/mod.rs—now()reads through the injected clock andre-expresses the instant for an explicit
offset=.src/stdlib/config/mod.rs— the clock's single owner;with_clockis theinjection point.
src/stdlib/register.rs—register_functionscaptures the provider (theprovider, not an instant) at registration;
register_manifest_queryreceivesno clock and keeps its refusing
nowstub.src/stdlib/time/clock_tests.rs, thestdlib::timeunit tests,tests/std_filter_tests/time_functions.rs(through the real registrationpath), and the
stdlib_time.featureBDD scenarios.One follow-on extraction sits outside the seam.
tests/bdd/steps/stdlib/rendering.rsnow builds itsStdlibConfigin a privateconfigure_stdlibhelper: applying the clock insiderender_template_with_contexttripped CodeScene'sComplex Methodrule(10.00 → 9.69 on the file), and the split takes the function from cyclomatic
complexity 10 to 3, with the helper's 8 under the threshold. Option order, the
four error-context strings, and rendering semantics are unchanged.
Design decisions worth a reviewer's attention
ClockProvider = Arc<dyn Fn() -> OffsetDateTime + Send + Sync>,the
EnvReadershape. ADR-008 justifies that shape by MiniJinja'sSend + Syncrequirement, but that argument is necessary and not sufficient — aBoxsatisfies it too. The decisive constraint is thatStdlibConfigderives
Clone, whichBox<dyn Fn>cannot provide (D1).HomeDirectoryshape (Ambient/Fixed(OffsetDateTime)), which wouldderive
DebugandClonefor free. It is rejected in D10 — though note thefirst draft rejected it for the wrong reason, claiming the enum made the
per-call negative control unwriteable. It does not; an enum could carry a
Sequencevariant. The surviving argument is cohesion: preserving thatcontrol under an enum means adding a test-only variant to a production type,
forcing every match site to service a case production never takes.
WallClocknewtype absorbs theDebugproblem.StdlibConfigderivesDebugandArc<dyn Fn>does not implement it; a one-field newtype with ahandwritten impl confines the boilerplate. It is named
WallClockrather thanClockbecauseClockalready names a monotonic clock generic insrc/runner/process/mod.rs, alongside two otherMonotonicClockspellings.The design document did not name this container; §5.2 now records it.
mockable::Clockischrono-typed and sits behind a feature this workspacedoes not enable;
monotonyabstracts monotonic elapsed time only and has nowall-clock type. Both were checked against published API docs (D3).
section is scoped to environment variables, and no lint forbids reading the
clock. D11 records that applying the taxonomy to a clock is a decision, not an
inheritance.
time::OffsetDateTimeis now on netsuke's public surface throughClockProvider, so atime0.4 bump becomes a breaking library-API change.The plan states the consequence rather than inheriting it silently.
Verification
All commit gates pass:
make check-fmt,make typecheck,make lint,make test,make doc-coverage,make markdownlint, andmake nixie.The mutation exercise the plan required was run and all six designated
mutations were rejected by the test the plan nominated:
clock.read()→OffsetDateTime::now_utc()now_uses_injected_clockcases failnow_reads_the_provider_on_every_callfailsnow_offset_preserves_the_instantfails at+00:00:01WallClock::default()nowstub deletedWallClock::readMutation 3 produced a finding worth carrying: taken literally as
timestamp + Duration::seconds(offset)it is a no-op, becausereplace_offsetpreserves the wall-clock time rather than the instant, so theoffset shift is exactly cancelled by the added duration. The mutation was
re-run in a form that moves the instant while still setting the offset. The
to_offset/replace_offsetnear-miss is now documented, since the seam'scontract depends on the former.
Two pre-existing gaps the planning surfaced:
now()is refused inmanifest-query mode, despite
docs/users-guide.mdpromising it. There is now,in both halves: one case asserting the refusal, one asserting
nowisundefined after the permissive query registration alone.
no roadmap item cross-referenced. Both are now closed.
Property testing covers the offset invariant (the same instant, re-expressed);
Kani and Verus are explicitly ruled out with reasons, since the only arithmetic
involved belongs to the
timecrate and is treated as an axiom.Documentation
Implementation referencesentry forsrc/stdlib/time/clock.rs.docs/developers-guide.md's "Environment and template ports" documents theclock's ownership and module boundary, in the same commit as the ADR.
supplied.
Roadmap
Item 7.1.1 and its four sub-bullets are marked done in
docs/roadmap.md, eachmapped to a named artefact in the exec plan's
Outcomes & retrospective.References
🤖 Generated with Claude Code