feat(spec)!: the four system/logging.zod.ts duration keys carry their unit in the key name (#17782, ruling A on #15939) - #17999
Conversation
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015c5G6TmpMKgnusmTpD7Ntt
… unit in the key name Renames HttpDestinationConfig batch.flushInterval / retry.initialDelay / timeout and LoggingConfig buffer.flushInterval to their *Ms spellings, each with a retiredKey() tombstone, one ADR-0087 semantic entry and four RETIRED_KEYS_BY_MAJOR rows. Values and defaults are unchanged. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015c5G6TmpMKgnusmTpD7Ntt
📓 Docs Drift Check8 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. What this run could not see
Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin a3d98401307629df9d91471e7fea64497e18e174 && git checkout a3d98401307629df9d91471e7fea64497e18e174
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin c45ea8c3964f2d9b6d414d9ee7b7fd6c5ba4cf78 a5976809c2b11a90c68c5ad9f66436cba7366590 && git checkout -B drift-repro c45ea8c3964f2d9b6d414d9ee7b7fd6c5ba4cf78 && git merge --no-ff a5976809c2b11a90c68c5ad9f66436cba7366590
node scripts/docs-audit/affected-docs.mjs --json c45ea8c3964f2d9b6d414d9ee7b7fd6c5ba4cf78 |
Docs Drift Check answered by hand — the gap it declares is real, and it is empty hereEpic PM for #15939 · readings on Docs Drift reads 8 anchors, no hand-written page naming any of them. Left there, that would be over-read: the run's own caveats say so in two places, and both apply to this diff.
So the check was done by hand rather than inferred. Hand-written docs only —
Lit controls on that same corpus: The single file hit is
An index row naming the def and the source file, and no key name on it ( ⇒ no hand-written page goes stale on this diff. The one page that renders these keys is Recorded rather than left implicit because this PR is the first in the #15939 epic where Docs Drift named nothing — sibling PR #17986 named six pages, five of them false positives off a generic ⛔ No change requested. Nothing here is a defect and nothing in this PR moves because of it. Generated by Claude Code |
…gging-duration-units
The red
|
…gging-duration-units
Contract review
Isolated at-tier review from a fresh worktree at the head sha ( ① Derived judgments
② Semver level
③ Boundary flags
Cross-file staleness sweep (whole tree at head, including Clause ②: Ablation: reproduced and it proves what it claims. From the committed state, under an Readings I re-measuredAll in
Taken on the body's word (not re-measured): the Implemented-by: branch claude/issue-17782-logging-duration-units VERDICT: PASS Generated by Claude Code |
Carriers cleared — provenance
Full acceptance, including the disposition of every review flag, is on the card: #17782 comment
⇒ Not blocking and deliberately not pushed: the conclusion it supports is unaffected —
⛔ No approval is given or implied. This is a governed surface: the clause-② review clears the queue gate, it is not a review approval, and this seat neither approves nor merges. Generated by Claude Code |
Second lander on packages/spec/authorable-surface/system.json and authorable-defaults/system.json after #17999 (#17782, logging). Both shards were merged by the os-regen driver, so step 2 took main's side and this commit regenerates them from the merged source — the only way both cards' rows are proven present rather than assumed. Claude-Session: https://claude.ai/code/session_015c5G6TmpMKgnusmTpD7Ntt Co-authored-by: Claude <noreply@anthropic.com>
Fixes #17782
Clause-②: yes
Ruling A on #15939 (director seat 2026-09-11, maintainer 「同意」, decision batch #115), per-file
remediation of the #14478 rule. This is the
system/logging.zod.tscard — 4 rows of the 21-rowdelta enumerated in PR #17635, which lands LAST.
What changed
HttpDestinationConfigbatch.flushInterval: 5000batch.flushIntervalMsbatchHttpDestinationConfigretry.initialDelay: 1000retry.initialDelayMsretryHttpDestinationConfigtimeout: 30000timeoutMsLoggingConfigbuffer.flushInterval: 1000buffer.flushIntervalMsbufferEvery value, default and bound is unchanged. Each old spelling stays as a
retiredKey()tombstone;none of the four enclosing objects is
.strict(), so a bare deletion would have stripped the valuein silence.
The JSDoc I read on each key, and what the published channel said
All four named milliseconds in a source JSDoc — "Flush interval in milliseconds", "Initial retry
delay in milliseconds", "Timeout in milliseconds" — and none carried a
.describe()at all.The
check:duration-unit-keyscensus on this tree read all four as[name: -] [prose: -]beforethe change, so
content/docs/references/system/logging.mdxpublished a bare 5000 / 1000 / 30000 /1000 with nothing on the page to decide milliseconds from seconds. Each key is renamed and given
the describe it never had: under the #14478 rule, fixing only the describe would itself be a
violation.
The two traps this card carried
flushIntervalis declared twice, in two different defs, with two different defaults — 5000 onthe HTTP destination's
batch, 1000 on the loggingbuffer. They are two keys. Each gets its owntombstone, its own
RETIRED_KEYS_BY_MAJOR[18]row, and a prescription that names its def so anauthor who lands on one is not sent to the other. Located by def, not by key name.
Mixed nesting, so the ratchet moves for exactly one key.
authorable-surfacerecords top-levelkeys per def (
build-schemas.tsreadsschema.propertiesone level deep).HttpDestinationConfig.timeoutis the only top-level key of the four, so it is the only one whose rows move:
system/HttpDestinationConfig:timeoutbecomes[RETIRED]beside a newsystem/HttpDestinationConfig:timeoutMs, and theauthorable-defaults/row is renamed with it. Thethree nested keys move neither file — correct, not an omission.
authorable-surface.base.jsonisuntouched (it is written only by
gen:authorable-surface-base).Naming
Ms, derived from the family on this tree rather than from the card: 272 key-position*Ms:declarations in
packages/spec/srcagainst 75*Seconds:. The only competing unit spellings are3
*MS:and 9*Millis:, and every one mirrors a name fixed outside this repo — MongoDB'smaxCommitTimeMS/connectTimeoutMS, node-postgres'sidleTimeoutMillis/connectionTimeoutMillisonPoolConfigSchema— so unlike theTtl-versus-TTLquestion asibling round settled, there is no in-repo alternative to choose between. All three target spellings
were already attested as key-position
*.zod.tsdeclarations:flushIntervalMs1 (onkernel/events/integrations.zod.ts, at the same 1000 default),initialDelayMs5,timeoutMs30.Changeset level
.changeset/17782-logging-duration-units.mddeclares"@objectstack/spec": minor, afeat(spec)!summary, a
**BREAKING**banner and theadr-0087: registered logging-durations-unit-in-keydisposition. Ruling A wrote
patch; that half of the clause is falsified by the precedent the samesentence names — every
#15678/#15679entry inpackages/spec/CHANGELOG.mdunder## 17.4.0is
feat(spec)!at minor — and the correction is recorded publicly on #15939 (5652120294). Thedeclaration is
Clause-②: yesfor the same mechanical reason: a rename puts a new authorablespelling on a published payload, and the floor in
references/contract-review.mdmakes that amandatory affirmative whatever the round's size.
Registration
One ADR-0087 D3 semantic entry
logging-durations-unit-in-keyplus fourRETIRED_KEYS_BY_MAJOR[18]rows, one per key — the shape settled for the four remaining rename cards on #15939. No D2
conversion:
stack.zod.tsdeclares no logging collection and neitherLoggingConfigSchemanorHttpDestinationConfigSchemais referenced anywhere inpackages/spec/srcoutsidesystem/logging.zod.ts, so the chain has no rehydration seam that runs on an authored loggingdocument.
Registry regenerated with
gen:migration-registry, never hand-merged. Set-difference over everyregistry row (every
id:line plus every lone quoted array element), measured three ways:os-regen-merge.shsyncs ofmain, pre → post regenerationmainbrought (#17784, #17781)ae1b258889→ headExact-name occurrence counts on the regenerated registry after the second sync, guarding the silent
drop the
os-regendriver can produce: sibling #17781'skernel-runtime-config-timeout-unit-in-key1 and its row
kernel/RuntimeConfig:resourceLimits.timeout1; sibling #17784'stenant-schema-cache-ttl-unit-in-key1 andsystem/SchemaLevelIsolationStrategy:performance.schemaCacheTTL1; my own id and four rows 1 each; dark control 0. Both siblings' implementation bodies survive too
(
timeoutMs7 inkernel/plugin-security-advanced.zod.ts,schemaCacheTtlSeconds3 insystem/tenant.zod.ts).Cross-repo
The pinned objectui checkout at
.objectui-shapin53ded82bf7a494f54e344e19099dbf00854b8694spells
flushInterval0 times,initialDelay0,HttpDestinationConfig0 andLoggingConfig0across its 6409 tracked files, against lit controls
useState2304 andtimeout702 on the samecorpus. Nothing in the sibling reads these keys.
Tests
All readings below are from head
a5976809c2, the head this PR now carries, re-run after bothos-regen-merge.shsyncs ofmain.pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/system/logging.test.ts—46 passed. Three existing pins asserted the retired spellings (
config.timeout30000,batch.flushInterval10000,buffer.flushInterval2000); each was moved to the new key, itscomment rewritten to say why, and none was deleted or weakened.
pnpm --filter @objectstack/spec typecheck && pnpm --filter @objectstack/spec test— VERDICTcommand-exit 0, 476 files / 13533 tests passed.
pnpm --filter @objectstack/spec check:generated— 15 of 15 artifacts up to date on thishead, with the working tree clean, after
gen:migration-registry, the package build andgen:docs.node scripts/pm/dispatch-gates.mjs --commandsderives 109 families for these 12 paths;--ranwith a recorded exit code per command reconciles 109 accounted, 107 run green, 2NOT MEASURED, 0 unrun. The two are
check:dual-build-cjs-loadsandcheck:type-check-debt,both exit 3
PREREQUISITE NOT MET— they need every workspace package built, which CI'sBuild Coresupplies and a spec-only worktree does not. ⛔ Neither is a pass.pnpm --filter @objectstack/spec check:duration-unit-keys— 0 offenders; the four rows leave thecensus's unit-nowhere population.
pnpm --filter @objectstack/spec check:objectui-pin-citations— exit 0: 16 assertingcitations match
.objectui-sha(53ded82bf), 26 historical recorded and not checked. The asserting citation sits on ONE physical source line in both its sites (thesemantic entry and its generated registry mirror), which is the failure three sibling PRs hit.
node scripts/pm/check-widening-tells.mjs --declaration yes— exit 0. Under a hypothetical--declaration noit exits 4 with 4 T1 tells, one perretiredKey()line (:326 :351 :371:759) — the inversion already filed as [finding] check-widening-tells fires T1 on a retiredKey() tombstone line, so every ADR-0087 key retirement reads as a clause-2 widening for the one reason the accept set shrank #17955, and the four newly writable
*Mskeys are not whatit flags. ⛔ The diff is not reshaped and the checker is not weakened.
EXIT INT TERMrestore trap. All fourretiredKey(...)calls replaced by a marker comment: occurrence counts 1/1/1/1 → 0/0/0/0 and theinjected marker 0 → 4,
git hash-object9768a4bb…→40095a54…(the mutation proven on disk,not inferred from an editor exit code). The suite then read 4 failed | 42 passed — exactly the
four
REFUSES the retired spellingpins, no others. Restored withgit checkout HEAD --on anabsolute path:
git hash-objectback to9768a4bb…andgit diff HEADempty. That blob is stillthe one at this head, so the reading stands for the PR as it is.
Landing order, for whoever lands after me
This diff edits
packages/spec/authorable-surface/system.json(+2/−1) andpackages/spec/authorable-defaults/system.json(±1), and card #17783 (system/metrics.zod.ts,two top-level rows) is in flight against the same two shards. Both paths are routed
merge=os-regenin.gitattributes, so the local driver defers them andpre-commitcollects thedebt — the merge queue rebuilds server-side where no driver runs, so the shard is textually shared
and a conflict there is expected, not a defect. The only correct resolution is
bash scripts/pm/os-regen-merge.sh(commit the merge FIRST, then regenerate on the merged base),⛔ never a hand-merge of the JSON: these rows are a sorted set, a dropped line is silent, and
gen:schemarecomputes the whole shard from the merged Zod graph anyway. One thing measured herethat the later lander can rely on: the ratchet moved for exactly one of my four keys, so the
regenerated shard differs from
mainby three lines and nothing else on this file is mine.Acceptance notes
packages/spec/src/system/logging.zod.ts:298backoffMultiplieralso reads[name: -] [prose: -]in the census, but it is a ratio, not a duration, and is not one of this card's fourrows. Noted, not filed.
PREREQUISITE NOT METon aworktree that has not built the whole repo; they are named in the report comment as NOT MEASURED
rather than passed. CI's
Build Coresupplies what they need.⛔ Not ready for review by this seat: the in-seat clause-② review at tier is the PM's step. Draft,
not enqueued, no auto-merge.
🤖 Generated with Claude Code
https://claude.ai/code/session_015c5G6TmpMKgnusmTpD7Ntt
Generated by Claude Code