Plan: Add the clock provider seam to the stdlib time module (7.1.1) - #696
Draft
leynos wants to merge 4 commits into
Draft
Plan: Add the clock provider seam to the stdlib time module (7.1.1)#696leynos wants to merge 4 commits into
leynos wants to merge 4 commits into
Conversation
Contributor
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueComment |
Contributor
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
|
Plan the injectable `ClockProvider` seam specified in the Netsukefile testing framework technical design section 5.2, so `now()` can be made deterministic without changing behaviour for manifest authors. The plan records the port shape, its ownership by `StdlibConfig`, the verification obligations with their negative controls, and the seam classification work ADR-008 and roadmap 7.1.1 require. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Run the repository's Markdown formatter over the new plan and split an over-long trait declaration onto separate lines so the line-length lint passes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Use "handwritten" rather than "hand-written" as the typos gate requires, and rename the axiom identifiers from AX-n to AXIOM-n so the gate stops reading the prefix as a misspelling. The longer identifier reflows one paragraph. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Six-lens design review found two errors of substance and three build-blockers in the prescribed code. Corrections of substance: - OBL-5's non-vacuity argument was false. The refusing `now` stub is registered after the permissive query helpers, and MiniJinja's `add_function` is last-write-wins, so a clock leaked into `register_query_functions` would be masked by the stub and the obligation would still pass. The obligation now needs two tests, the second asserting `now` is undefined after the permissive half alone. - Nothing pinned the offset of the injected path. An arbitrary provider may return a non-UTC instant, which would make the harness assert behaviour production never exhibits. `WallClock::read` now normalizes to UTC and OBL-1 gains a non-UTC-provider case. D10's rejection of the resolved-value enum rested on a circular claim that the enum makes the per-call negative control unwriteable; it does not. The withdrawn claim is replaced by the cohesion argument, and the design document's normativity is demoted to a tiebreak because D2 adds a container the design does not name. Rename the container to `WallClock`: `Clock` already names a monotonic clock generic in `src/runner/process/mod.rs` alongside two other `MonotonicClock` spellings. Build-blockers fixed: the accessor must be `const fn` without `#[must_use]`; the sequenced fixture violated the denied `indexing_slicing` lint and underflowed on an empty vector; and the `src/stdlib/mod.rs` re-export must land in EP-M1 or its doctests leave the milestone failing to compile. Also add `fixed_clock()`, a `ClockInstant` re-export, and an `is_system()` discriminant so a leaked clock is observable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
leynos
force-pushed
the
7-1-1-clock-provider-seam
branch
from
September 8, 2026 15:20
d91ee5f to
1e6c93e
Compare
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
Draft ExecPlan for roadmap item 7.1.1, which makes the Netsuke stdlib
now()Jinja function testable by reading its instant through an injected clock
provider instead of calling
OffsetDateTime::now_utc()directly.This PR contains the plan only — no implementation. The plan must be
approved before any code is written.
Plan:
docs/execplans/7-1-1-clock-provider-seam.mdWhat the plan 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.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, following the existing
CommandEnvprecedent. It is namedWallClockrather thanClockbecauseClockalready names a monotonic clock generic insrc/runner/process/mod.rs, alongside two otherMonotonicClockspellings.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).
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, and the addendum must say so.
Verification approach
Seven obligations, each paired with the concrete mutation it must reject. The
plan requires a one-off mutation exercise before sign-off, of which the most
important is: store the clock on
StdlibConfigbut never pass it totime::register_functions. That mutation compiles cleanly and passes everyunit test, failing only the integration and behavioural layers — which is why
those layers are mandatory rather than optional.
A six-lens design review corrected two errors of substance before this PR was
opened. Both are called out because both would have shipped as false assurance:
nowstub is registered after the permissive query helpers, andMiniJinja's
add_functionis last-write-wins — so a clock leaked intoregister_query_functionswould be silently masked by the stub, and a testasserting only "query mode refuses
now" would still pass. The obligationnow carries a second test asserting
nowis undefined after the permissivehalf alone.
system_clock()yieldsUTC, but an arbitrary provider need not; a fixture built from a local-time
literal would render a non-
Ztimestamp, making the harness assert behaviourproduction never exhibits.
WallClock::readnow normalizes to UTC, with anon-UTC-provider case guarding it.
Two pre-existing gaps the planning surfaced, both now closed by the plan:
now()is refused inmanifest-query mode, despite
docs/users-guide.mdpromising it.no roadmap item cross-referenced. Answering it is now a plan deliverable.
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.Roadmap
Item 7.1.1 is marked done by the implementor on completion, not by this PR.
References
🤖 Generated with Claude Code
Summary by Sourcery
Approve an execution plan for introducing a testable clock seam to the stdlib time helpers without implementing the seam itself.
Enhancements:
now()helper deterministic through an injected clock provider while preserving ambient-clock and manifest-query behaviour.Documentation: