Commit 8261ff7
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.ts` card
— 4 rows of the 21-row
delta enumerated in PR #17635, which lands LAST.
## What changed
| def | before | after | nesting |
|:--|:--|:--|:--|
| `HttpDestinationConfig` | `batch.flushInterval: 5000` |
`batch.flushIntervalMs` | nested under `batch` |
| `HttpDestinationConfig` | `retry.initialDelay: 1000` |
`retry.initialDelayMs` | nested under `retry` |
| `HttpDestinationConfig` | `timeout: 30000` | `timeoutMs` |
**top-level** |
| `LoggingConfig` | `buffer.flushInterval: 1000` |
`buffer.flushIntervalMs` | nested under `buffer` |
Every 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 value
in 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-keys` census on this tree read all four as
`[name: -] [prose: -]` before
the change, so `content/docs/references/system/logging.mdx` published 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
**`flushInterval` is declared twice, in two different defs, with two
different defaults** — 5000 on
the HTTP destination's `batch`, 1000 on the logging `buffer`. They are
two keys. Each gets its own
tombstone, its own `RETIRED_KEYS_BY_MAJOR[18]` row, and a prescription
that names its def so an
author 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-surface` records top-level
keys per def (`build-schemas.ts` reads `schema.properties` one level
deep). `HttpDestinationConfig.timeout`
is the only top-level key of the four, so it is the only one whose rows
move:
`system/HttpDestinationConfig:timeout` becomes `[RETIRED]` beside a new
`system/HttpDestinationConfig:timeoutMs`, and the `authorable-defaults/`
row is renamed with it. The
three nested keys move neither file — correct, not an omission.
`authorable-surface.base.json` is
untouched (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/src` against 75 `*Seconds:`. The only
competing unit spellings are
3 `*MS:` and 9 `*Millis:`, and every one mirrors a name fixed outside
this repo — MongoDB's
`maxCommitTimeMS` / `connectTimeoutMS`, node-postgres's
`idleTimeoutMillis` /
`connectionTimeoutMillis` on `PoolConfigSchema` — so unlike the
`Ttl`-versus-`TTL` question a
sibling round settled, there is no in-repo alternative to choose
between. All three target spellings
were already attested as key-position `*.zod.ts` declarations:
`flushIntervalMs` 1 (on
`kernel/events/integrations.zod.ts`, at the same 1000 default),
`initialDelayMs` 5, `timeoutMs` 30.
## Changeset level
`.changeset/17782-logging-duration-units.md` declares
`"@objectstack/spec": minor`, a `feat(spec)!`
summary, a `**BREAKING**` banner and the `adr-0087: registered
logging-durations-unit-in-key`
disposition. Ruling A wrote `patch`; that half of the clause is
falsified by the precedent the same
sentence names — every `#15678` / `#15679` entry in
`packages/spec/CHANGELOG.md` under `## 17.4.0`
is `feat(spec)!` at minor — and the correction is recorded publicly on
#15939 (`5652120294`). The
declaration is `Clause-②: yes` for the same mechanical reason: a rename
puts a new authorable
spelling on a published payload, and the floor in
`references/contract-review.md` makes that a
mandatory affirmative whatever the round's size.
## Registration
One ADR-0087 D3 semantic entry `logging-durations-unit-in-key` plus four
`RETIRED_KEYS_BY_MAJOR[18]`
rows, one per key — the shape settled for the four remaining rename
cards on #15939. No D2
conversion: `stack.zod.ts` declares no logging collection and neither
`LoggingConfigSchema` nor
`HttpDestinationConfigSchema` is referenced anywhere in
`packages/spec/src` outside
`system/logging.zod.ts`, so the chain has no rehydration seam that runs
on an authored logging
document.
Registry regenerated with `gen:migration-registry`, never hand-merged.
Set-difference over every
registry row (every `id:` line plus every lone quoted array element),
measured three ways:
| comparison | rows | lost | gained |
|:--|:--|--:|:--|
| my regeneration, before → after | 1624 → 1629 | **0** | my 1 semantic
id + 4 retired-key rows |
| the two `os-regen-merge.sh` syncs of `main`, pre → post regeneration |
1631 → 1631 and 1631 → 1633 | **0** | only what `main` brought (#17784,
#17781) |
| merge base `ae1b258889` → head | 1628 → 1633 | **0** | exactly my 5
rows |
Exact-name occurrence counts on the regenerated registry after the
second sync, guarding the silent
drop the `os-regen` driver can produce: sibling #17781's
`kernel-runtime-config-timeout-unit-in-key`
1 and its row `kernel/RuntimeConfig:resourceLimits.timeout` 1; sibling
#17784's
`tenant-schema-cache-ttl-unit-in-key` 1 and
`system/SchemaLevelIsolationStrategy:performance.schemaCacheTTL`
1; my own id and four rows 1 each; dark control 0. Both siblings'
implementation bodies survive too
(`timeoutMs` 7 in `kernel/plugin-security-advanced.zod.ts`,
`schemaCacheTtlSeconds` 3 in
`system/tenant.zod.ts`).
## Cross-repo
The pinned objectui checkout at `.objectui-sha` pin
`53ded82bf7a494f54e344e19099dbf00854b8694`
spells `flushInterval` 0 times, `initialDelay` 0,
`HttpDestinationConfig` 0 and `LoggingConfig` 0
across its 6409 tracked files, against lit controls `useState` 2304 and
`timeout` 702 on the same
corpus. Nothing in the sibling reads these keys.
## Tests
All readings below are from head `a5976809c2`, the head this PR now
carries, re-run after both
`os-regen-merge.sh` syncs of `main`.
- `pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2
src/system/logging.test.ts` —
46 passed. Three existing pins asserted the retired spellings
(`config.timeout` 30000,
`batch.flushInterval` 10000, `buffer.flushInterval` 2000); each was
**moved to the new key**, its
comment rewritten to say why, and none was deleted or weakened.
- `pnpm --filter @objectstack/spec typecheck && pnpm --filter
@objectstack/spec test` — VERDICT
command-exit 0, 476 files / 13533 tests passed.
- `pnpm --filter @objectstack/spec check:generated` — **15 of 15
artifacts up to date** on this
head, with the working tree clean, after `gen:migration-registry`, the
package build and
`gen:docs`.
- `node scripts/pm/dispatch-gates.mjs --commands` derives **109**
families for these 12 paths;
`--ran` with a recorded exit code per command reconciles **109
accounted, 107 run green, 2
NOT MEASURED, 0 unrun**. The two are `check:dual-build-cjs-loads` and
`check:type-check-debt`,
both exit **3** `PREREQUISITE NOT MET` — they need every workspace
package built, which CI's
`Build Core` supplies 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 the
census's unit-nowhere population.
- `pnpm --filter @objectstack/spec check:objectui-pin-citations` —
**exit 0**: 16 asserting
citations match `.objectui-sha` (53ded82bf), 26 historical recorded and
not checked. The asserting citation sits on ONE physical source line in
both its sites (the
semantic 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 no` it exits 4 with **4 T1 tells, one per `retiredKey()`
line** (:326 :351 :371
:759) — the inversion already filed as #17955, and the four newly
writable `*Ms` keys are not what
it flags. ⛔ The diff is not reshaped and the checker is not weakened.
- **Ablation**, from the committed state, with an `EXIT INT TERM`
restore trap. All four
`retiredKey(...)` calls replaced by a marker comment: occurrence counts
1/1/1/1 → 0/0/0/0 and the
injected marker 0 → 4, `git hash-object` `9768a4bb…` → `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 spelling` pins, no others. Restored with `git
checkout HEAD -- ` on an
absolute path: `git hash-object` back to `9768a4bb…` and `git diff HEAD`
empty. That blob is still
the 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)
and
`packages/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-regen` in `.gitattributes`, so the local driver defers them
and `pre-commit` collects the
debt — 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:schema` recomputes the whole shard from the merged Zod graph
anyway. One thing measured here
that the later lander can rely on: the ratchet moved for exactly one of
my four keys, so the
regenerated shard differs from `main` by three lines and nothing else on
this file is mine.
## Acceptance notes
- `packages/spec/src/system/logging.zod.ts:298` `backoffMultiplier` also
reads `[name: -]
[prose: -]` in the census, but it is a ratio, not a duration, and is not
one of this card's four
rows. Noted, not filed.
- Six gate families in this diff's derived set refuse with `PREREQUISITE
NOT MET` on a
worktree that has not built the whole repo; they are named in the report
comment as NOT MEASURED
rather than passed. CI's `Build Core` supplies 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.com/claude-code)
https://claude.ai/code/session_015c5G6TmpMKgnusmTpD7Ntt
---
_Generated by [Claude Code](https://claude.ai/code)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 2f1a6f6 commit 8261ff7
12 files changed
Lines changed: 597 additions & 21 deletions
File tree
- .changeset
- content/docs/references/system
- packages/spec
- authorable-defaults
- authorable-surface
- src
- migrations
- entries
- retired-keys
- semantic
- system
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
109 | 109 | | |
110 | 110 | | |
111 | 111 | | |
112 | | - | |
113 | | - | |
114 | | - | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
115 | 116 | | |
116 | 117 | | |
117 | 118 | | |
| |||
124 | 125 | | |
125 | 126 | | |
126 | 127 | | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
127 | 145 | | |
128 | 146 | | |
129 | 147 | | |
| |||
163 | 181 | | |
164 | 182 | | |
165 | 183 | | |
166 | | - | |
167 | | - | |
168 | | - | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
169 | 188 | | |
170 | 189 | | |
171 | 190 | | |
| |||
297 | 316 | | |
298 | 317 | | |
299 | 318 | | |
300 | | - | |
| 319 | + | |
301 | 320 | | |
302 | 321 | | |
303 | 322 | | |
| |||
354 | 373 | | |
355 | 374 | | |
356 | 375 | | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
357 | 386 | | |
358 | 387 | | |
359 | 388 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
107 | 107 | | |
108 | 108 | | |
109 | 109 | | |
110 | | - | |
| 110 | + | |
111 | 111 | | |
112 | 112 | | |
113 | 113 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
460 | 460 | | |
461 | 461 | | |
462 | 462 | | |
463 | | - | |
| 463 | + | |
| 464 | + | |
464 | 465 | | |
465 | 466 | | |
466 | 467 | | |
| |||
Lines changed: 14 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
Lines changed: 11 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
Lines changed: 14 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
Lines changed: 13 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
0 commit comments