Skip to content

Commit fb29f62

Browse files
docs(spec): correct the 17.4.0 duration-keys erratum falsified by #17783/#17785 (#18077)
Fixes #18076 Clause-②: no — the landing surface is prose inside one already-compiled `packages/spec/CHANGELOG.md` entry plus an erratum line. No schema key, no accepted-set change, no new exported symbol, no new key on a published payload, and no path under `packages/**/src/**` moves. `skip-changeset`: no package publishes anything from this change. ## The ruling this PR implements Director seat, decision batch #119 item 1, 2026-09-12T04:15Z, maintainer's own word `1A·2A·3A·4D·5A`: 「同意」, implemented once already by `ef47459474` (#17896). This card applies the same remedy shape to a second falsified instance: > **A: correct the two published CHANGELOG entries in the repository, with a post-release erratum note.** … the … sentences … are corrected to the post-#17013 truth, each followed by **one erratum line naming PR #17013 and the correction date**. > > **Before editing**, run the release tooling's dry-run (`changeset version` on a scratch branch) and record that a manual edit inside an already-compiled section **survives** … **if it does not survive, stop and report**. > > `skip-changeset`: no package publishes anything from this change. ## Step 2 first — the survival gate, run before any edit On a local scratch branch off `484484041a` (`scratch/issue-18076-survival-gate`), discarded afterward: | reading | value | |:--|:--| | marker inserted into the already-compiled `## 17.4.0` "Keys deliberately left alone" paragraph | `SURVIVALGATEMARKER18076XQ7` | | marker count before `changeset version` | **1** | | dark control (`ZZQV18076NOPE`, invented at read time) before | **0** | | `pnpm changeset version` | exit 0, "All files have been updated" | | marker count **after** `changeset version` | **1** — the manual edit **survives** | | where the marker ended up | line 2869 → line 7316 — pushed down, content intact | | dark control after | **0** — unchanged, confirming the grep methodology (not a broken instrument reading a false zero) | | lit control — did the tool actually rewrite the files? | yes: a new `## 17.5.0` section is prepended, `packages/spec/package.json` moves `17.4.0` → `17.5.0`, and 452 of 453 pending `.changeset/*.md` files are consumed | | restore | `git checkout -f` back to the real branch; `diff <(git show origin/main:packages/spec/CHANGELOG.md) packages/spec/CHANGELOG.md` → **identical**; scratch branch deleted | ⇒ `changeset version` prepends a new section and never rewrites an already-compiled one. Re-measured on this tree rather than inherited from `ef47459474`'s reading, per the ruling's explicit instruction. ## What changed, and why each corrected claim is true of today's tree `packages/spec/CHANGELOG.md`, inside the already-released `## 17.4.0` entry (commit `e9fcd6b`, "Keys deliberately left alone"), said: > `ServiceLevelObjective.errorBudget`'s burn-rate `window` and the OpenTelemetry exporter `timeout` name no unit anywhere in their prose, so both are outside the gate's population entirely. Pin tests assert each of these, so a later sweep cannot read this card as "every duration-shaped number on these files". All three claims are false, verified on this tree independently of the card: - **Half 1** — `packages/spec/src/system/metrics.zod.ts:368-375` (`SLO_BURN_RATE_WINDOW_RETIRED`): `ServiceLevelObjective.errorBudget.burnRateWindows[].window` was renamed to `durationSeconds` in `@objectstack/spec` 17 (#17783), tombstoned with `retiredKey()` at `metrics.zod.ts:402`/`:498`. The pin that held the bare spelling is now `metrics.test.ts:612`, `it('REFUSES the burn-rate \`window\` with a rename naming \`durationSeconds\`', …)`. - **Half 2** — `packages/spec/src/system/tracing.zod.ts:546-550`: `OpenTelemetryCompatibility.exporter.timeout` was renamed to `timeoutMs` (#17785), tombstoned with `retiredKey()`; its `batch.exportTimeout` sibling moved to `exportTimeoutMs` the same way (`:586`/`:601`). The pin is now `tracing.test.ts:594-611`, `it.each([['exporter.timeout', 'timeoutMs'], …])('REFUSES the retired \`%s\` with the rename in the message', …)`. The corrected paragraph quotes the falsified sentence, states the truth with both PR numbers and both new key names, and closes with one erratum line naming this PR and the correction date, matching `ef47459474`'s shape. **The paragraph's other four claims were checked and are still true** (not touched, per the ruling's scope boundary): - `FailoverConfig.dns.ttl` — still bare, `disaster-recovery.zod.ts:148`, under the same `externalVocabulary` exemption comment. - `CacheAvalanchePrevention.lockout.lockTimeoutMs` — still milliseconds, `cache.zod.ts:162`. - `MetricExportConfig.batch.size` — still a bare record count, `metrics.zod.ts:702`. - `QueueConfig.rateLimit.max` — still a bare task count, `worker.zod.ts:~276`. ## The erratum line's PR number — how it went in, and the fix for a review finding `#18077` (the PR number for this correction) is not knowable before the PR exists, so the first pushed commit (`bfc744c98c`) carried a placeholder, `#PRPLACEHOLDER18076`, in the erratum line's parenthetical, meaning to fill it in once the PR was open. **That fill-in commit (`ac68eff364`) was made locally but not pushed before the round's first status check ran against `bfc744c98c`, so a reviewer correctly caught a shipped placeholder.** It is now pushed; **HEAD is `ac68eff364`**, verified directly against the remote rather than assumed: ``` $ git show origin/claude/issue-18076-changelog-duration-keys-erratum:packages/spec/CHANGELOG.md | grep -c PRPLACEHOLDER 0 $ git show origin/claude/issue-18076-changelog-duration-keys-erratum:packages/spec/CHANGELOG.md | grep -n 'Corrected after publication, #18077' 7331: population as of `@objectstack/spec` 17. (Corrected after publication, ``` **Placeholder sweep of the whole diff, with controls** (`git diff origin/main..HEAD -- packages/spec/CHANGELOG.md`, 35 lines): - Pattern `PLACEHOLDER|\bTODO\b|\bTBD\b|\bXXX\b|\bFIXME\b|<[A-Z_]+>|\{\{.*\}\}` (case-insensitive) over the diff: **0 matches**. - Lit control, drawn a different way: appended a synthetic `PRPLACEHOLDER99999` line to the same scanned text and re-ran the identical pattern — **1 match**, confirming the regex class actually fires and the 0 above is a real zero, not a typo'd pattern reading a false clean. - Positive control: `18077` appears **exactly once** in the diff — the one erratum reference, present and correctly substituted. ## Verification `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived **54** local gate families for this diff (all matched via broad `packages/**` / `packages/spec` globs — this path carries no gate of its own). Ran all 54 **twice** — once on `bfc744c98c`, again on the final `ac68eff364` after the placeholder fix landed — both times reconciled with `--ran` against the recorded exit codes, same result both runs: ``` Run reconciliation — 54 derived, 50 run, 4 NOT-MEASURED, 0 UNRUN. ✓ dispatch-gates --ran: 54 derived famil(ies) accounted for — 50 run, 4 NOT-MEASURED (4 DERIVED from a recorded exit 3). ``` - **50/50 that ran, passed** (`pnpm check:nul-bytes`, `check:release-notes`, `check:release-page-status`, `check:release-section-coverage[--strict]`, `check:changeset-gate-self-tests`, `check:doc-authoring`, `check:page-declaration-shape`, `check:published-files`, `check:slot-lookup`, `check:test-source-alias`, `check:type-source-resolution`, and 39 more — full list in this branch's derivation). - **4 NOT-MEASURED, exit 3 (`PREREQUISITE NOT MET`), declared rather than swallowed**: `check:dts-closure`, `check:dual-build-cjs-loads`, `check:lean-entry-closure`, `check:sourcemap-no-sources-content` — each refuses on a missing `dist/` in this fresh worktree ("Run `pnpm build` first … this is NOT a pass: nothing was measured"). These are whole-repo build-completeness gates unrelated to this diff's content (no code, no export, no `dist` path moves); running a ~80-package `pnpm build` to satisfy them is disproportionate to a one-line CHANGELOG correction, and CI builds fresh before running them. - No changeset added; `check:changeset-gate-self-tests` (the gate's own self-test) is green. `skip-changeset` now shows as an applied label on this PR (not applied by this session — labels were left untouched per this card's operating constraints). - `pnpm check:nul-bytes` and a direct `grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'` both clean on the diff. - `node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD`: clean on both commits — no card-relation trailer, model-free trailer pair. No `packages/spec` test/typecheck run beyond the above: this diff touches no code, no export and no public surface byte, so per this repo's verification-scope convention there is nothing for `pnpm --filter @objectstack/spec test`/`typecheck` to newly exercise. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_015c5G6TmpMKgnusmTpD7Ntt --- _Generated by [Claude Code](https://claude.ai/code/session_015c5G6TmpMKgnusmTpD7Ntt)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 4844840 commit fb29f62

1 file changed

Lines changed: 19 additions & 5 deletions

File tree

packages/spec/CHANGELOG.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2862,11 +2862,25 @@
28622862
milliseconds where its `resetTimeoutSeconds` sibling is seconds, so the two must
28632863
not be migrated as if they were one unit. `MetricExportConfig.batch.size` is a
28642864
record count and `QueueConfig.rateLimit.max` is a task count: neither is a
2865-
duration, so neither has a unit to carry. `ServiceLevelObjective.errorBudget`'s
2866-
burn-rate `window` and the OpenTelemetry exporter `timeout` name no unit
2867-
anywhere in their prose, so both are outside the gate's population entirely.
2868-
Pin tests assert each of these, so a later sweep cannot read this card as
2869-
"every duration-shaped number on these files".
2865+
duration, so neither has a unit to carry. As published, this entry continued:
2866+
"`ServiceLevelObjective.errorBudget`'s burn-rate `window` and the
2867+
OpenTelemetry exporter `timeout` name no unit anywhere in their prose, so
2868+
both are outside the gate's population entirely. Pin tests assert each of
2869+
these, so a later sweep cannot read this card as 'every duration-shaped
2870+
number on these files'." Both keys were renamed in the same major: #17783
2871+
renamed the burn-rate `window`
2872+
(`ServiceLevelObjective.errorBudget.burnRateWindows[].window`) to
2873+
`durationSeconds`, and #17785 renamed the OpenTelemetry exporter `timeout`
2874+
(`OpenTelemetryCompatibility.exporter.timeout`) to `timeoutMs`, with its
2875+
`batch.exportTimeout` sibling moving to `exportTimeoutMs` the same way.
2876+
Both are inside the gate's population now, each tombstoned with
2877+
`retiredKey()` naming the new spelling, and the pin tests that held the
2878+
bare names were rewritten by the same two PRs into rename-refusal pins.
2879+
2880+
*Erratum, 2026-09-13 — #17783 and #17785 renamed the two keys this entry
2881+
named as gate-exempt; both are tombstoned and inside the gate's
2882+
population as of `@objectstack/spec` 17. (Corrected after publication,
2883+
#18077.)*
28702884
- fb77aa5: feat(spec)!: a `tree` field's `reference`, when present, must name the declaring object — any other target is refused at parse (#14892)
28712885

28722886
<!-- adr-0087: not-required (no-migration-prescription) No authorable key is renamed, retired or re-typed: `reference` keeps its name, its type and its optionality on a `tree` field, and every self-referencing or reference-less `tree` parses byte-identically to before. What is newly refused is a `tree` whose `reference` names a different object — a shape no prose surface ever documented, that no runtime reader consumed as a cross-object link, and whose one in-repo author (the showcase field zoo) hedged in its own label. The remedy is authoring intent, not a mechanical rewrite: the author decides whether the field is this object's own hierarchy (a self-reference, or no `reference` at all) or a link to another object (a `lookup`), so `objectstack migrate meta` has nothing to rewrite and this changeset carries no rewrite instructions. -->

0 commit comments

Comments
 (0)