Work Packages: kernel.clock — the single door to time

Mission: kernel-clock-single-door · Spec: spec.md (rev 1) · Plan: plan.md (rev 3) · Tasks: rev 2 (post-tasks squad)

Critical path is a serial chain on clock.py/the gate (WP00→WP01a→WP01b→WP02→WP03→WP04); then a parallel per-package remediation lane where each WP ratchets its own _exemptions/<owner>.txt to empty; then terminal gate hardening. Delivered as stacked PRs; the gate is fail-closed from WP01b, so every intermediate PR is green and monotonically more-enforced. The specify_cli subpackage split (WP09–WP13) is confirmed against the WP00 census (census-measured: sync 7, status+merge 5, core 7, cli 13, auth+compat 12 files — all ≤~15); any lane exceeding ~15 owned files splits and triggers a finalize-tasks re-run before the fan-out.

Dependency graph

WP00 ─► WP01a ─► WP01b ─► WP02 ─► WP03 ─► WP04 ─┬─► WP05 doctrine ─────────┐
(census+ (relocate (engine+  (clock)(family)(helpers)│  WP06 glossary          │
 owner-  +91-file  gate                              │  WP07 charter           │
 map)    repoint)  fail-closed)                      │  WP08 runtime (+D-1)    ├─► WP15 ─► WP16 (opt)
                                                     │  WP09 specify_cli/sync  │  (terminal
                                                     │  WP10 status+merge      │   gate harden)
                                                     │  WP11 core              │
                                                     │  WP12 cli               │
                                                     │  WP13 auth+compat       │
                                                     │  WP14 scripts/ + tests/-shared + pyproject │
                                                     └─────────────────────────┘

Package-WP Done template (applies to WP05–WP14 and any census-split WP)

Every per-package remediation WP's Done = its package-specific line plus all of: 1. Exemption emptied: tests/architectural/_exemptions/<owner>.txt → empty; ruff+mypy clean; gate green. 2. Byte-identity registered: every site this WP migrates is registered in the per-site mapping harness (WP03) with its WP00 census prior-signature (precision/sep/suffix); the harness is green for those sites. A site whose prior contract has no exact producer match is flagged for adjudication, not auto-migrated. 3. Persisted goldens from pre-mission bytes: goldens for writers this WP owns use the WP00-captured pre-mission bytes, not a re-capture of the new producer (fails if the producer diverges from them). 4. Naive adjudicated with evidence: every naive site in this owner (per census) is enumerated in research/migration-notes.md with a behaviour test (aware value) or a pinning test (justified-naive); if the census shows none, Done records naive=∅ for this owner. 5. Test-file hygiene + C-009: new test_*.py declare a pytest marker and join the test_pytest_marker_convention + arch shard/module-inventory baselines in this WP (pytest tests/architectural/test_pytest_marker_convention.py green); each added test records its non-vacuity mutation (symbol-deletion/raise for fires-tests; over-fire for negatives; flip-a-literal-vs-captured-baseline for no-drift) and the reviewer verifies it reproduces red.

Work packages

WP00 — Census, ownership map & guards (hard prerequisite)

call sites with prior serialization signature (precision/sep/suffix) and captured pre-mission bytes for persisted writers; naive sites; time.time() sites; datetime.fromtimestamp sites (→ from_epoch); date.today() sites; parse/format sites; _internal_runtime wall-clock sites; the freshness-bounds test idiom sites (before/after = datetime.now(UTC) — migrate to now_utc()). Emit a disjoint path → owning WP map covering every gate-scoped path in src/+tests/+scripts/ — including tests/architectural/, tests/_support/, conftests, and pyproject.toml; every path owned by exactly one WP, none double-claimed. Confirm each specify_cli lane ≤~15 owned files (split + re-run finalize-tasks if not). Decide FR-011(b) ((b)=∅ or specify now_naive_local()). Emit draft _exemptions/<owner>.txt seeds (WP01b regenerates them from the real engine). Run the C-006 duplicate-application guard (base's #3288 commits vs origin/main).

FR-011(b) decided; guard result recorded.

  • Deps: none · Lane: critical · Discharges: C-006; feeds all.
  • Do: write research/census.yaml — per package AND per contract: datetime importers; each contract's
  • Done: census.yaml + ownership map reviewed (no orphan/double-claimed path); lane sizes confirmed;

WP01a — Door skeleton + relocation + mechanical repoint

canonical format constants); repoint all ~91 now_utc_iso importers (pure mechanical, no logic change); delete sibling ISO duplicates; retire/repoint the seed gate tests/specify_cli/test_clock_consolidation.py and update the time_utils.py/door docstring. (Large-but-trivial diff, reviewed as a mechanical repoint.)

test_shared_package_boundary green; seed gate retired; ruff+mypy clean.

  • Deps: WP00 · Lane: critical · FR/NFR: FR-001/002/003, NFR-003/007.
  • Do: create src/kernel/clock.py (skeleton, now_utc_iso relocated, type re-exports via __all__,
  • Done: now_utc_iso byte-identity golden green; clean-install-verification green; test_layer_rules +

WP01b — Dual gate stood up fail-closed

reusing the shared alias/_BANNED_CALLS machinery (do NOT widen the assert-scoped find_wall_clock_assertion_violations — keep its 124 tests intact), with per-source-root module-name anchoring so the door resolves as kernel.clock under a src/+tests/+scripts/ scan; prove the re-export-bypass plant fires (from kernel.clock import datetime; datetime.now()), else SC-001 unmet; document the getattr residual limit. Decide + record the deployment surface (a tests/architectural/ test alongside the existing conftest assert-gate, which survives). Regenerate _exemptions/<owner>.txt from this engine's own output (WP00's grep seed was a draft); stand the gate up fail-closed, green day one with the full regenerated allow-list. Per-detector scanned>N floors.

124 assert-gate tests still green; ruff+mypy clean.

  • Deps: WP01a · Lane: critical · FR/NFR/SC: FR-012 (stand-up), SC-001, NFR-004.
  • Do: import-ban (template test_kernel_no_doctrine_import.py); new whole-module call-ban entry point
  • Done: gate green with full (engine-regenerated) exemptions; re-export-bypass plant reds without its fix;

WP02 — Injectable Clock (before the families)

to DEFAULT_CLOCK; standardize existing now= seams.

identical instants under one FrozenClock; a non-vacuity companion proves the determinism assertion goes red when the freeze is removed (freeze/symbol-deletion mutation, not return []).

  • Deps: WP01b · Lane: door-surface (serial on clock.py) · FR/SC: FR-008/009, SC-002.
  • Do: Clock Protocol + SystemClock + FrozenClock + DEFAULT_CLOCK (in the door); producers delegate
  • Done: a per-package parametrized (7-case) freeze test asserts two door consumers per package yield

WP03 — Producer family + byte-identity harness

now_utc_compact_stamp, now_utc_seconds, now_utc() (datetime-returning), now_epoch() (producer-only).

fires on a planted precision/sep/suffix mismatch (non-vacuous).

  • Deps: WP02 · Lane: door-surface (serial) · FR/NFR: FR-004/005/006, NFR-001.
  • Do: collapse the 4 duplicate second-stamp constants into one door constant; add now_utc_stamp,
  • Done: every contract has a passing golden under FrozenClock; the per-site mapping harness runs and

WP04 — Parse/format helpers

  • Deps: WP03 · Lane: door-surface (serial) · FR/C: FR-007, C-007.
  • Do: parse_iso (fromisoformat), parse_stamp (strptime), format_stamp (strftime), from_epoch(x)->datetime (fromtimestamp, tz=UTC).
  • Done: helpers present + unit-tested against round-trip fixtures (each records its C-009 mutation).

WP05–WP14 — Per-package remediation (parallel fan-out; each ratchets its own exemption file)

Each WP routes every datetime import + wall-clock call in the paths the WP00 ownership map assigns it through the door; folds its time.time() sites (adjudicate now_epoch vs monotonic per site) and its naive sites. Done = the package-specific line below + the Package-WP Done template (all 5 clauses).

  • WP05 doctrine (deps WP04).
  • WP06 glossary (deps WP04).
  • WP07 charter (deps WP04) — persisted goldens for compiler/context_state/pack_manager (pre-mission bytes, SC-004b).
  • WP08 runtime (deps WP04) — FR-014/D-1: route engine.py + retrospective_terminus.py through the door; update the _internal_runtime docstring with the re-extractability rationale; test_shared_package_boundary/test_no_runtime_pypi_dep green.
  • WP09 specify_cli/sync (deps WP04) — body_queue SQLite epoch golden (pre-mission bytes); repoint the Lamport sync/clock.py's now_utc_iso import to kernel.clock (import-only; do not touch Lamport logic — C-005).
  • WP10 specify_cli/status+merge (deps WP04).
  • WP11 specify_cli/core (deps WP04) — owns FR-006's decisions/* datetime-returning callers.
  • WP12 specify_cli/cli (deps WP04).
  • WP13 specify_cli/auth+compat (deps WP04) — the time.time+datetime files (session_hot_path.py, compat/history.py).
  • WP14 scripts/ + shared-test paths + pyproject (deps WP04) — scripts/ incl. date.today() adjudication (seo_postprocess.py, local→UTC date is byte-changing → migration note); the cross-cutting tests/architectural/+tests/_support/+conftest datetime sites and the freshness-bounds idiom migration to now_utc(); the sole pyproject.toml touch — revisit the TID251 blanket ignore (C-008). (The ratchet runs through per-owner _exemptions/*.txt + the AST gate only, never per-path TID251 ignores.)

Any WP split off at census inherits the full Package-WP Done template verbatim.

WP15 — Terminal gate hardening

positional now(UTC); tz= keyword; module-alias dt.now(); double-attr datetime.datetime.now(); re-export bypass; variable-split; utcnow(); date.today(); time.time() — each FIRES) + paired allowed-form negatives (producers, timedelta, annotations, parse_iso, from_epoch, import time + time.monotonic()/perf_counter() do NOT fire); message-mapping assertion (≥3 spellings → correct producer); per-detector scanned>N floors; stale-exemption check; assert union allow-list empty.

  • Deps: WP05–WP14 · Lane: terminal · FR/NFR/SC: FR-012, SC-001/003, NFR-007 re-confirm.
  • Do: full plant matrix (every banned spelling: import datetime; from datetime import datetime;
  • Done: plant matrix green; union empty; clean-install re-confirmed; ruff+mypy clean; each test records its C-009 mutation.

WP16 — (Optional / deferred) portable lint (FR-013)

  • Deps: WP15 · Lane: optional. Only if a sibling-repo consumer is named (record the decision).
  • Do: standalone lint script + config template + smoke test (compliant + violating fixture → exit 0/non-zero).

Requirement coverage matrix (no orphans)

ReqWP(s)
FR-001 door existsWP01a
FR-002 type re-exportsWP01a
FR-003 relocate now_utc_iso + repointWP01a
FR-004 stamp family + dedup constantsWP03
FR-005 compact/seconds/now_utc/now_epochWP03
FR-006 datetime-returning routedWP03 (door) + WP11 (decisions/* callers)
FR-007 parse/format helpers (+from_epoch)WP04
FR-008 injectable Clock + freeze testWP02
FR-009 standardize now= seamsWP02
FR-010 all consumers remediatedWP05–WP14 (per ownership map)
FR-011 naive adjudicationWP00 (decision) + package WPs (template clause 4)
FR-012 dual gate + ratchetWP01b (stand-up) + WP15 (harden)
FR-013 portable lintWP16 (deferred)
FR-014 _internal_runtime routing (D-1)WP08
NFR-001 byte-identity per contract + per-siteWP03 (harness) + package WPs (template clause 2)
NFR-002 naive-fix adjudicationpackage WPs (template clause 4) + migration-notes
NFR-003 kernel leaf / no layer violationWP01a (+ all)
NFR-004 allow-list exhaustiveWP01b (fail-closed) + WP15 (union empty)
NFR-005 ruff/mypy/complexityevery WP
NFR-006 duration untouchedWP15 negative (time.monotonic) + all WPs
NFR-007 clean-installWP01a + WP15
C-001 no kernel-wheel activationWP01a (honoured)
C-002 kernel leafWP01a
C-003 preserve distinct contractsWP03
C-004 _internal_runtime reconciledWP08
C-005 naming distinctionWP01a (docstring) + WP09 (Lamport repoint)
C-006 own #3288 diff + guardWP00
C-007 parse/format via doorWP04 + package WPs
C-008 scope src/tests/scriptsWP01b (gate scope) + WP14 (scripts/tests-shared/pyproject)
C-009 no scaffold-only testsevery test-adding WP (template clause 5 / WP15 / WP01b)
SC-001 raw read banned + messageWP01b + WP15
SC-002 7-package freezeWP02
SC-003 allow-list emptyWP15
SC-004 on-disk byte-identityWP03 (harness) + package WPs (template clauses 2/3)
SC-005 no layer/wheel regressionWP01a + WP15
SC-006 naive adjudicatedpackage WPs (template clause 4) + migration-notes

Notes

owned-path list, exemption segment, and the C-009 mutation recorded per test. The WP00 ownership map is the authority for which paths each WP touches (prevents parallel-lane collisions).

behaviour-verifying and each records its non-vacuity mutation.

  • Per-WP detail files (tasks/WP##.md) are materialized at implement time; each carries its census-derived
  • No task in this mission adds a scaffold-only test (C-009); gate/freeze/golden/mapping tests are all