Skip to content

spec(changelog): the published 17.4.0 notes still say the burn-rate window and the OTel exporter timeout are outside the duration-unit gate — #17783 and #17785 renamed both #18076

Description

@claude

The published packages/spec/CHANGELOG.md tells a reader that two duration keys are outside check:duration-unit-keys's population and will keep their bare names. Both were renamed under #15939 ruling A, in @objectstack/spec 17. The sentence now actively misdirects anyone reading the 17.4.0 notes to find out whether a key moved.

The falsified text

packages/spec/CHANGELOG.md:2865-2869 on origin/main @ 484484041a, inside the already-released ## 17.4.0 section, under the heading "Keys deliberately left alone":

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".

Three claims, all now false: "name no unit anywhere in their prose", "outside the gate's population entirely", and the pin-test assertion that was supposed to hold them bare.

The measurements that falsify it

Read from origin/main @ 484484041a, 2026-09-13T19:00Z. The tree states the contradiction in its own words:

Half 1 — the burn-rate window, renamed by #17783. packages/spec/src/system/metrics.zod.ts:368-375 carries the retirement string:

`ServiceLevelObjective.errorBudget.burnRateWindows[].window` was renamed to `durationSeconds` in @objectstack/spec 17 — the unit of a duration-shaped number lives in …

It also records why not windowSeconds ("the array this object sits in is already called burnRateWindows, so the key would stutter"). The pin that asserted the bare spelling was itself rewritten by #17783 into it('REFUSES the burn-rate \window` with a rename naming `durationSeconds`', …)`.

Half 2 — the OpenTelemetry exporter timeout, renamed by #17785. packages/spec/src/system/tracing.zod.ts:546 is now timeoutMs: z.number().int().positive().optional().default(10000), with timeout: retiredKey( tombstoned at :550. The sibling moved the same way: exportTimeoutMs at :586, exportTimeout: retiredKey( at :601.

⇒ Not merely "no longer accurate": the CHANGELOG says these keys are outside the very gate whose ruling renamed them.

Why this is its own PR and was deliberately kept out of #17635

⛔ A code PR never edits packages/*/CHANGELOG.md. #17635 (the gate, merged 484484041a) carried the two in-tree prose repairs and stopped there; this one is release-owned text and takes the documented erratum route instead.

The prescribed remedy shape — precedent, not improvisation

ef47459474 — "docs(changelog): correct the two published Seed.locale claims PR #17013 falsified, with erratum lines" (#17896, Fixes #17026) — is the on-point precedent. It implements director-seat decision batch #119 item 1, 2026-09-12T04:15Z, maintainer 「同意」:

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.

and the boundary that makes it legitimate, also verbatim from that ruling:

Immutability belongs to the shipped npm tarball, which this ruling does not and cannot touch; it does not belong to the repository's markdown.

That PR's own reading recorded the survival gate as passing (marker present before and after pnpm changeset version, with a lit control proving the tool really did rewrite the files). ⛔ Re-run it anyway rather than inheriting the result — it is cheap, and it is the one step the ruling says to stop on.

Shape to match: markdown only, Clause-②: no, skip-changeset, one erratum line naming the correcting PR and the date. ef47459474 was 2 files, +14/−2; this one touches packages/spec/CHANGELOG.md alone.

Scope note for whoever picks this up

This card applies batch #119's remedy shape to a new instance; it does not re-decide whether published CHANGELOG prose may be corrected — the ruling above settled that, and its boundary sentence is stated as a general principle rather than a fact about Seed.locale. ⚠️ If on inspection you judge this instance materially different from #17026 — for example if the correction cannot be made without changing what the released version is described as having done — ⛔ do not improvise: route it to the decision box.

Provenance and dedupe

Residue E of the five the maintainer named when directing #17635's dispatch (#15939 comment 5654385018); the epic closed at 18:45Z with this explicitly recorded as still owed. Deduped via issue search: the near matches are #18040 (tombstones naming the wrong major — a different axis), #18057 and #17487 (other prose-vs-tree drift, different subjects); no open card covers these two sentences.

Filed by the epic PM for #15939, session_015c5G6TmpMKgnusmTpD7Ntt, 2026-09-13T19:00Z. Every reading above taken from origin/main @ 484484041a, not relayed.


Generated by Claude Code

Activity

  1. claude commented on Sep 13, 2026

    @claude
    ContributorAuthor

    Claim: PM loop round 9 — epic PM for the #15939 subtree, carrying residue E after the epic closed
    Session: session_015c5G6TmpMKgnusmTpD7Ntt
    Branch: claude/issue-18076-changelog-duration-keys-erratum
    Worktree: objectstack-issue-18076
    Domain: domain:spec
    File surface: packages/spec/CHANGELOG.md (stop on breach; explain in the report)
    Container & model: S — one prose correction plus one gate run, mode:subagent, model: sonnet — quoting this dispatch's own node scripts/pm/dispatch-gates.mjs --tier packages/spec/CHANGELOG.md, 2026-09-13T19:00Z: "Model tier — no path-derived mandate … The tier stays the PM's per-card judgment call (floor sonnet · default opus · ceiling fable)". Floor is honest here: the correct text is already measured and quoted on the card, the remedy shape is a precedent to copy rather than a design, and the one judgment call — whether this instance differs materially from #17026 — is written into the order as a stop-and-report rather than left to the round.
    Clause-②: no
    Thread-read: none — this card carries no comments at the moment this claim is written
    Serial constraints cleared: measured, not recalled (2026-09-13T19:00Z). Every open PR's file list was read. Exactly one touches packages/spec/CHANGELOG.md: #17076, the chore: version packages release PR, which rewrites 78 CHANGELOG files including this one. Instrument check: that 78 is the lit control for the single hit, and a fabricated package path reads 0. ⛔ #17076 is not to be touched, merged or worked around — a PM never merges a Version Packages PR — but it is the reason the survival gate below is mandatory rather than inherited. No other open PR touches this file. The #15939 epic closed at 18:45Z and all seven of its cards are merged, so no sibling is in flight.

    Why the answer on clause ② is no

    The landing surface is markdown prose inside an already-compiled packages/spec/CHANGELOG.md section. 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. This matches the precedent PR ef47459474 (#17896), whose own declaration reads "Clause-②: no — the landing surface is prose inside two already-compiled packages/*/CHANGELOG.md entries plus erratum lines."

    ⚠️ If the round finds it cannot correct the text without changing what the released version is described as having done — as opposed to correcting a claim about what is true now — that is a different kind of change: stop and report. ⛔ Do not edit this line.

    epic PM for #15939 · session_015c5G6TmpMKgnusmTpD7Ntt · 2026-09-13T19:02Z


    Generated by Claude Code

  2. claude commented on Sep 13, 2026

    @claude
    ContributorAuthor

    Dispatch order — residue E, the 17.4.0 erratum

    Epic PM for the #15939 subtree, session_015c5G6TmpMKgnusmTpD7Ntt, 2026-09-13T19:03Z. Claim: 5655399040. Base: origin/main @ 484484041a.


    Zone 1 — Adjudicated. ⛔ Not re-litigable, quoted rather than paraphrased.

    Director seat, decision batch #119 item 1, 2026-09-12T04:15Z; maintainer's own word to the batch presented as 1A·2A·3A·4D·5A: 「同意」. Implemented by ef47459474 (#17896, Fixes #17026) — the precedent this card copies:

    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 — the triage seat's open confidence gap; if it does not survive, stop and report.

    skip-changeset: no package publishes anything from this change.

    And the boundary that makes it legitimate, also verbatim:

    Immutability belongs to the shipped npm tarball, which this ruling does not and cannot touch; it does not belong to the repository's markdown.

    ⛔ Two standing prohibitions this card sits next to: a code PR never edits packages/*/CHANGELOG.md (which is exactly why this is its own PR and was kept out of #17635), and a PM or dev never runs a release or merges the Version Packages PR.


    Zone 2 — PM mechanism assumptions. Every line is a premise to falsify first.

    Readings taken by this seat from origin/main @ 484484041a, 2026-09-13T19:00Z.

    ②‑1 · The text. packages/spec/CHANGELOG.md:2865-2869, inside the already-released ## 17.4.0 section, under "Keys deliberately left alone":

    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".

    Re-locate it by content, not by line number — #17076 or any other merge can move it.

    ②‑2 · Half 1 is falsified by #17783. packages/spec/src/system/metrics.zod.ts:368-375 carries the retirement string stating the burn-rate window "was renamed to durationSeconds in @objectstack/spec 17", and explaining why not windowSeconds. The pin that held the bare spelling is now it('REFUSES the burn-rate \window` with a rename naming `durationSeconds`', …)`.

    ②‑3 · Half 2 is falsified by #17785. packages/spec/src/system/tracing.zod.ts:546 is timeoutMs: z.number().int().positive().optional().default(10000) with timeout: retiredKey( at :550; the sibling went the same way (exportTimeoutMs :586, exportTimeout: retiredKey( :601).

    ⇒ All three of the sentence's claims are false: "name no unit anywhere in their prose", "outside the gate's population entirely", and the pin-test assertion. ⚠️ Verify each yourself — a correction built on my reading rather than yours is how a second erratum gets written later.

    ②‑4 · ⚠️ The survival gate is MANDATORY and ⛔ must NOT be inherited. The precedent PR measured that a manual edit inside an already-compiled section survives changeset version (marker present before and after, pushed down but intact, with a lit control proving the tool really rewrote the files). Re-run it here on a scratch branch off the same base, with your own marker and your own lit control, then discard the branch. If the edit does not survive: stop and report. ⛔ Do not proceed on #17896's result — that is exactly the "relayed rather than measured" failure this epic has already paid for once.

    ②‑5 · One open PR collides, and it is the release PR. #17076 (chore: version packages) rewrites 78 CHANGELOG files including this one — measured, with that 78 as the lit control for the single hit and a fabricated package path reading 0. No other open PR touches this file. ⛔ Do not touch, merge, rebase or work around #17076. If it merges first, your branch conflicts on this file and the resolution is to re-locate the sentence by content in the new layout and re-apply — ⛔ never to accept either side wholesale.

    ②‑6 · skip-changeset. No package publishes anything from this change. Follow the precedent; if the changeset gate disagrees on this diff, report what it said rather than adding a changeset to quiet it.


    Zone 3 — Suggested route. ⛔ Not adjudicated; measurement beats it.

    1. Worktree objectstack-issue-18076, branch claude/issue-18076-changelog-duration-keys-erratum off 484484041a.
    2. Survival gate first (②‑4), before any edit. Record the readings.
    3. Correct the sentence to the post-spec: 5 duration key(s) in system/metrics.zod.ts name their unit only in JSDoc — #15939 Ruling A remediation (5 of the 21-row delta) #17783 / post-spec: 4 duration key(s) in system/tracing.zod.ts name their unit only in JSDoc — #15939 Ruling A remediation (4 of the 21-row delta) #17785 truth, and append one erratum line naming this PR and the correction date, matching ef47459474's shape. Keep the surrounding paragraph's other claims (FailoverConfig.dns.ttl, CacheAvalanchePrevention.lockout.lockTimeoutMs, MetricExportConfig.batch.size, QueueConfig.rateLimit.max) — ⚠️ but check each one is still true while you are in there, and report any that is not rather than silently correcting beyond scope.
    4. Derive and run the gate family (node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack), reconcile with --ran. Exit 3 is PREREQUISITE NOT MET — ⛔ neither pass nor finding.
    5. Push, open a draft PR, report. ⛔ Do not turn it ready, ⛔ do not enqueue, ⛔ do not touch labels.

    ⚠️ The one stop-and-report that matters: if correcting the text would change what the released 17.4.0 is described as having done, rather than correcting a claim about what is true now, that is outside batch #119's shape — stop and report, and it goes to the decision box instead.

    epic PM for #15939 · session_015c5G6TmpMKgnusmTpD7Ntt · 2026-09-13T19:03Z


    Generated by Claude Code

  3. zhuangjianguo commented on Sep 13, 2026

    @zhuangjianguo
    Collaborator

    os-dev-report

    {
      "issue": 18076,
      "status": "done",
      "branch": "claude/issue-18076-changelog-duration-keys-erratum",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/18077",
      "premise_still_valid": true,
      "summary": "Corrected packages/spec/CHANGELOG.md's already-released 17.4.0 'Keys deliberately left alone' paragraph: verified independently that #17783 renamed ServiceLevelObjective.errorBudget.burnRateWindows[].window to durationSeconds and #17785 renamed OpenTelemetryCompatibility.exporter.timeout to timeoutMs (plus its exportTimeout->exportTimeoutMs sibling), both tombstoned with retiredKey() and both pins now rename-refusal tests, falsifying all three of the paragraph's original claims. Corrected the sentence to the post-#17783/#17785 truth and appended one erratum line naming this PR (#18077) and the correction date, matching precedent ef47459474 (#17896). Ran the mandatory survival gate on a discarded scratch branch before editing (marker survived changeset version with a lit control proving the tool actually rewrote files); pushed a draft PR, left it draft, touched no labels.",
      "tests": "SURVIVAL GATE (mandatory, run before any edit, on scratch branch scratch/issue-18076-survival-gate off 484484041a, discarded after): marker SURVIVALGATEMARKER18076XQ7 inserted into the target paragraph, count 1 before `pnpm changeset version`; dark control ZZQV18076NOPE count 0 before (grep exit 1, confirming a real zero). `pnpm changeset version` exit 0 ('All files have been updated'). AFTER: marker count still 1 (line 2869->7316, pushed down, content intact) = SURVIVES; dark control still 0. LIT CONTROL confirming the tool actually rewrote the tree: new `## 17.5.0` section prepended, packages/spec/package.json moved 17.4.0->17.5.0, 452 of 453 pending .changeset/*.md files consumed. Restored via `git checkout -f` back to the real branch; a diff between `git show origin/main:packages/spec/CHANGELOG.md` and the working file was empty (byte-identical); scratch branch deleted. PREMISE VERIFICATION (independent of the card): metrics.zod.ts:368-375 SLO_BURN_RATE_WINDOW_RETIRED confirms the window->durationSeconds rename and reason; metrics.test.ts:612 pin is exactly \"REFUSES the burn-rate `window` with a rename naming `durationSeconds`\". tracing.zod.ts:546-550 confirms timeoutMs with `timeout: retiredKey(` tombstone; :586/:601 confirms exportTimeoutMs/exportTimeout sibling; tracing.test.ts:594-611 it.each pin \"REFUSES the retired `%s` with the rename in the message\" covers both. All four 'still true' claims re-verified on tree: FailoverConfig.dns.ttl still bare (disaster-recovery.zod.ts:148, externalVocabulary exemption comment intact); CacheAvalanchePrevention.lockout.lockTimeoutMs still ms (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) -- none falsified, none touched. GATE DERIVATION: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 54 local gate families (all matched via broad packages/** or packages/spec globs, none specific to CHANGELOG.md). Ran full sweep TWICE under the shared verify-lock (OS_VERIFY_LOCK_SLOT=issue-18076-gates) -- once on commit bfc744c98c, again on final commit ac68eff364 after a placeholder fix -- identical result both times: 50/54 exit 0 (pass), 4/54 exit 3 declared PREREQUISITE NOT MET (check:dts-closure, check:dual-build-cjs-loads, check:lean-entry-closure, check:sourcemap-no-sources-content -- each explicitly refuses on a missing dist/ in this fresh worktree per its own printed message, e.g. 'Run `pnpm build` first ... this is NOT a pass: nothing was measured'; a full ~80-package build was judged disproportionate to a one-line CHANGELOG fix and was not run -- CI builds fresh and will cover these). Reconciled both runs with `--ran` (a file of \"command :: exit N\" lines): 'Run reconciliation - 54 derived, 50 run, 4 NOT-MEASURED, 0 UNRUN' both times, 0 UNRUN. check:slot-lookup needed >90s (my first artificial per-command timeout killed it at exit 124, a self-inflicted cap, not a platform cap-kill); re-run with a 280s budget it passed cleanly (exit 0) both sweeps, corrected in the record. Byte hygiene: `pnpm check:nul-bytes` exit 0 and a direct `grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]'` on the diff both clean. `node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD` clean on both commits (no card-relation trailer, model-free trailer pair) -- caught and fixed one violation pre-push (an initial `Fixes #18076` body line was reworded before pushing). PLACEHOLDER INCIDENT AND FIX (caught by PM review): first pushed commit bfc744c98c shipped the erratum line with a literal `#PRPLACEHOLDER18076` token (PR number unknowable before the PR existed); a local fixup commit (ac68eff364) substituting the real `#18077` was made but NOT pushed before the PM's status snapshot, so the PM correctly flagged a shipped placeholder. Pushed the fixup immediately; verified directly against the remote via `git show` of the pushed branch's CHANGELOG.md piped to `grep -c PRPLACEHOLDER` = 0 and `grep -n 'Corrected after publication, #18077'` present at line 7331. PLACEHOLDER SWEEP with controls (requested by PM) over the full diff (`git diff origin/main..HEAD -- packages/spec/CHANGELOG.md`, 35 lines): pattern class (case-insensitive): PLACEHOLDER, word-bounded TODO/TBD/XXX/FIXME, a bracket-wrapped all-caps token, or a double-curly interpolation = 0 matches; LIT CONTROL drawn a different way (appended a synthetic PRPLACEHOLDER99999 line to the same scanned text, re-ran identical pattern) = 1 match, confirming the regex class fires and the 0 is real; positive control `18077` appears exactly once in the diff. Re-ran full 54-gate sweep + trailer check on the final pushed commit ac68eff364, same clean result. No `pnpm --filter @objectstack/spec test`/`typecheck` run: diff touches no code, export or public-surface byte, so nothing in scope for them to newly exercise. CI on ac68eff364 as of 2026-09-13T19:37Z (in flight, not read by me since -- declared per operating constraints rather than awaited): 39 check runs, several completed success (Check Documentation Links, Spec property liveness, Governed Surface Queue Guard, PR-claim/single-writer-path guards, filter), 'Check Changeset' completed SKIPPED on both workflow runs (skip-changeset label already applied by automation, not by this session), 0 failures observed, remainder (Build Core, Test Core 1-6, Lint & Repo Gates, Type Check x4, Temporal Conformance, Dogfood Regression Gate x3, Dogfood Verify CLI) in_progress.",
      "mcp_calls": 10,
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  4. added a commit that references this issue on Sep 17, 2026
    fb29f62
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions