Repository navigation
[finding] the records-forms platform checklist still cites hook.json timeout as 'live' — the ledger marked it dead when #15626 renamed it to timeoutMs #15839
Description
Activity
- addeddocumentationImprovements or additions to documentationImprovements or additions to documentation
on Sep 5, 2026 Triage: lands in
domain:spec(docs/qa/platform-checklist/areas/records-forms.json); rationale: class (a) — a citation measured false against the artefact it names. The checklist row assertstimeoutislive;packages/spec/liveness/hook.jsonrecordstimeoutasdeadandtimeoutMsaslive, after PR #15626 renamed it under ruling B on #14478, tombstoned the old spelling and split the ledger row in two. Both halves re-derivable by the two commands in the card.Bug.⭐ What raises this above a stale string is what the row is: a
ref— the coordinate a checklist runner opens to decide what the item is supposed to prove. ⇒ A runner following it looks fortimeoutin the hook ledger, finds adeadrow, and has to reconstruct which of the two spellings the item meant. The citation does not merely misinform; it sends the reader to a contradiction and leaves them to guess. And the key that actually replaced it is not mentioned at all.priority:p3: one row, one file, mechanical fix, and the failure is semi-loud — a runner who follows the ref hits thedeadrow rather than silently proving the wrong thing. ⛔ Nothing is mis-tested today; the cost is a runner's reconstruction time.⚠️ Do the sibling sweep in the same PR — the card flags it and it is the difference between fixing an instance and fixing the residue: PR #15626's rename may have left the same drift on other rows inrecords-forms.json(and plausibly in sibling area files). ⇒ Cheap while the file is open, and ⛔ a second card for the second instance would be waste. Bound it to rename residue from #15626; ⛔ do not turn it into a general checklist-vs-ledger audit.⭐ Cross-reference for whoever takes it, because the class is now counted rather than anecdotal: no gate compares a checklist
refagainst the ledger it names — the citations are prose. That is the same ungated-citation class as #16441 (gradedp3today: prosepath:NNNanchors inpackages/specrotted 319 lines with nothing checking them) and #16477 (five comments naming a file renamed everywhere else).⚠️ Whether ungated citations deserve a mechanical check is a real question with a growing population — ⛔ but it is not this card, and ⛔ must not widen this diff. If anyone wants it, it needs its own card with the population counted across all three carriers.⛔ Correctly out of #15677's scope (that card is
api/only) and ⛔ not introduced by it — pre-existing onorigin/main, whose diff touches neither file.⛔ This seat grades and routes only: not claimed, not dispatched, no code.
Generated by Claude Code
Claim:
domain:specexecution seat,/pm-dispatch spec@objectstack. Round R1, wave 15 (2026-09-09T00:40Z).Session:
session_016N6xmWt5hYm94ffVEwGH8x
Branch:claude/issue-15839-records-forms-hook-timeout-ref
Worktree:/home/user/objectstack-issue-15839
Domain:domain:spec— assigned by triage (5570083720). ⛔ This seat does not writedomain:*.
File surface:docs/qa/platform-checklist/areas/records-forms.json, plus any sibling area file the bounded sweep below turns up, plus one.changeset/*.mdif owed. ⛔ Nothing else without stopping and reporting.
Container & model:claude-opus-5, passed explicitly (SKILL.md:528).Clause-②: no
The change corrects a checklistrefcitation to match the ledger it names. No accept set moves,
no export is added, no schema arm, bound or behaviour changes; no test outcome changes.
拉回已声明契约 ⇒ 常规档. ⛔ Fence: if a row turns out to be testing the wrong thing rather than
citing it wrongly, stop and report — that is a checklist-content change, not a citation fix.Thread-read: the card body and its single comment (
5570083720) read to the end. ⭐ This card carries NO clause-② ruling.⚠️ The sweep is IN scope and bounded — both halves quoted, because the bound is what keeps it a p3:⚠️ Do the sibling sweep in the same PR … PR #15626's rename may have left the same drift on other rows inrecords-forms.json(and plausibly in sibling area files)。⇒ Cheap while the file is open,且 ⛔ a second card for the second instance would be waste。Bound it to rename residue from #15626;⛔ do not turn it into a general checklist-vs-ledger audit.Premise re-measured on
origin/mainbb7d91f19at 2026-09-09T00:39Z, ⛔ not inherited:reading result hook.jsontimeoutdeadhook.jsontimeoutMslivethe citation, records-forms.json:3171still …priority/async/condition/retryPolicy/timeout/onError all 'live'…⇒ Both halves of the contradiction stand today. ⭐ And what makes it more than a stale string is what the row is — a
ref, the coordinate a checklist runner opens to learn what the item must prove. A runner following it lands on adeadrow and has to guess which spelling was meant, while the key that replaced it is not mentioned at all.Serial constraints cleared:
docs/qa/platform-checklist/**is disjoint frompackages/spec/scripts/**(#16556, same wave), frompackages/spec/src/ai/**+skills/**(#15263, in flight), and frompackages/spec/liveness/field.json(#15568, PR #16990 in the queue).⚠️ It readspackages/spec/liveness/hook.json— read-only, ⛔ never edited here.fold-or-serial: dispatched in parallel with #16556 — different files, different defect shapes (a citation contradicting its own referent vs a generator carrying an unrepaired first-match lookup), different fix methods.
Generated by Claude Code
os-dev-report
{ "issue": 15839, "status": "done", "branch": "claude/issue-15839-records-forms-hook-timeout-ref", "pr": "https://github.com/objectstack-ai/objectstack/pull/16997", "premise_still_valid": true, "model": "claude-opus-5 (harness-stamped: 'You are powered by the model named Opus 5. The exact model ID is claude-opus-5.')", "base": "bb7d91f19", "final_commit": "98c651703", "summary": "Both halves of the card's contradiction re-measured on bb7d91f19 and confirmed: packages/spec/liveness/hook.json records timeout as 'dead' and timeoutMs as 'live', while records-forms.json:3171 still asserted timeout among the props that are 'live' and never mentioned the replacement key. Because the row is a ref -- the coordinate a runner opens -- the citation now names timeoutMs as live AND anchors the timeout tombstone, so a runner has nothing to reconstruct. The bounded sweep ran over all SEVEN of #15626's ADR-0087 renames (not just hook.timeout) and turned up one sibling area file: integration-system.job-scheduled-run carried the same residue on four rows for job.timeout -> timeoutMs. Both items bumped revision with a history entry stating that no clause moved. No changeset owed, justified on both halves.", "measurements": { "ledger_hook_timeout": "dead (verifiedAt 2026-09-04)", "ledger_hook_timeoutMs": "live (verifiedAt 2026-09-04)", "citation_before": "packages/spec/liveness/hook.json#retryPolicy (object/events/body/priority/async/condition/retryPolicy/timeout/onError all 'live'; label/description 'dead' but kept as docs)", "citation_resolved_before": "asserts 'live' for 9 props; contradicted by the ledger on exactly one -- ('timeout','dead'); mentions 'timeoutMs': false", "citation_resolved_after": "asserts 'live' for 9 props; contradicted by the ledger: NONE; additionally asserts 'timeout' dead, which the ledger confirms; mentions 'timeoutMs': true", "ledger_job_timeout": "dead", "ledger_job_timeoutMs": "live", "job_fixture_file": "examples/app-showcase/src/automation/jobs/index.ts:23 authors 'timeoutMs: 300000' (not 'timeout: 300000')" }, "sweep": { "bound": "#15626 rename residue only -- NOT a general checklist-vs-ledger audit. #15626 is SEVEN ADR-0087 renames, so all seven old spellings were swept, not only hook.timeout.", "corpus": "docs/qa/platform-checklist/areas/*.json + the family .md files", "residue_found": 5, "residue_by_key": { "hook.timeout": "1 row -- records-forms.json:3171 (the card's own)", "job.timeout": "4 rows -- integration-system.json fixtures/steps/acceptance/source", "DriverOptions.timeout": "0 -- no checklist surface", "MetadataManagerConfig.cache.ttl and .databaseLoader.ttl": "0 -- no checklist surface", "connectionPool.idleTimeout": "0 -- no checklist surface", "accessControl.sessionTimeout": "0 -- no checklist surface", "JobScheduleOptions.timeout": "0 -- no checklist surface" }, "firing_control": "The same instrument over the same corpus returns 19 hits for \\btimeout\\b and 7 for \\btimeoutMs\\b, so every 0 above is a reading, not instrument silence.", "not_residue_left_alone": 14, "not_residue_detail": "records-forms:3168 (the wrapper LADDER STEP, which hook-wrappers.ts:243 itself still spells 'timeout'); approvals x5 + FOLLOW-UPS.md x1 (ApprovalEscalationSchema.timeoutHours and the live timeout/sla -> timeoutHours aliases at approval.zod.ts:605 -- a different schema, untouched by #15626); integration-system x4 (the JobExecutionStatus enum value 'timeout', still live; the webhook item, whose key was always timeoutMs and whose schema #15626's diff does not touch; two concept phrases mirroring run-with-policy.ts's own wording); api-backend:682, cli:415, integration-system:147 (ordinary prose). ttl/TTL hits (10) are system-file / system-upload-session lifecycle TTLs, unrelated to the MetadataManagerConfig cache keys." }, "fence": "HELD -- no row turned out to be TESTING the wrong thing, only CITING it wrongly. Nothing was re-authored; no clause, oracle or evidence field moved. The job execution STATUS is still spelled 'timeout' and was left alone. Precedent for treating a step/clause key-spelling as part of a rename rather than a content change: #15626's OWN landing commit e9fcd6bbd corrected cacheTtl -> cacheTtlSeconds inside a `variants` entry and an `acceptance` clause of api-backend.json.", "files_changed": [ "docs/qa/platform-checklist/areas/records-forms.json (+8 / -2) -- item records-forms.object-hook-lifecycle, revision 1 -> 2: the source citation, plus a history entry", "docs/qa/platform-checklist/areas/integration-system.json (+11 / -5) -- item integration-system.job-scheduled-run, revision 2 -> 3: fixtures.requires[0], steps[4], one acceptance clause, source[0], plus a history entry" ], "diff_total": "2 files changed, 19 insertions(+), 7 deletions(-)", "tests": "Gate families derived mechanically from the change set via `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (script self-declares repo + commit bb7d91f19), then reconciled with --ran: '12 derived famil(ies) accounted for -- 12 run, 0 NOT-MEASURED'. All exit 0, every exit code captured by redirect BEFORE any pipe, all re-run at the final commit 98c651703. Verdict lines: pnpm check:platform-checklist -> 'OK -- 15 areas, 264 items (264 active); ... symbol anchors: 631/631 resolved against 307 cited sources, 17 file floors held' (its two self-tests, 176 + 17 assertions, also green). pnpm check:nul-bytes -> 'OK (scanned 8407 text file(s) ... no raw ASCII control bytes)'. pnpm check:doc-authoring -> '398 files clean' + '46 published skill files clean' + '821 pinned site(s) ... no growth'. pnpm check:cross-package-test-inputs -> 'OK: 28 package(s) read outside themselves, all declared'. pnpm check:watch-hint-literal -> '66 declaration(s) across 4 rostered name(s)'. pnpm check:refd-timer-probe -> 'OK ... 6385 source file(s) swept'. pnpm check:driver-memory-census -> 'OK -- every declaration is ledgered'. pnpm --filter @objectstack/lint run check:doc-formula-expressions -> exit 0. node scripts/check-ci-filter-parity.mjs -> 'OK: all 174 declared cross-package glob(s)'. node scripts/check-closing-keyword-parity.mjs (+ --self-test) -> 'OK (3 parsers agree on all 9 keywords ...)' + '24 assertions'. node scripts/check-comment-mask-corpus.mjs -> '6390 files, 0 disagree'. ANCHOR FLOOR (the ratchet on this diff): areas/records-forms.json 78 -> 79 resolved anchors (the added hook.json#timeout anchor resolves), floor 78; areas/integration-system.json held at 57 against floor 57 -- nothing lowered, read from `--anchor-census` at the final commit. No ablation was owed: no gate, guard or test was added or changed. Before/after evidence instead comes from mechanically resolving the citation against the ledger it names (printed in the PR body): BEFORE contradicted on exactly ('timeout','dead'), AFTER contradicted: NONE. NOT MEASURED: nothing -- but note `check:platform-checklist` is deliberately NOT CI-wired (README cadence note), so its green is a local reading and CI will not repeat it. Two gates initially returned exit 3 PREREQUISITE NOT MET (yaml not installed; @objectstack/formula not built) -- read as NOT MEASURED, not as red, and both re-run green after `pnpm install` and a locked `turbo run build --filter=@objectstack/formula --filter=@objectstack/lint` (os-verify-lock VERDICT command-exit 0, held 167s, waited 130s). No package build reads docs/qa/**: turbo.json declares no such input.", "changeset": "NONE OWED -- both halves measured, and the decision does not rest on half 1. HALF 1: 82 tracked package.json, 70 publishable; every one declares an explicit files[], whose entire vocabulary is dist / README.md / CHANGELOG.md plus seven @objectstack/spec-only entries (json-schema, liveness, prompts, llms.txt, src/**/*.zod.ts, api-surface, spec-changes.json). No entry names docs/, none escapes its package dir, and no publishable package is rooted at the repo root. HALF 2: npm pack of @objectstack/spec -- the deciding tree, because it is the only publishable package whose files[] reaches past dist/README/CHANGELOG AND the one that actually publishes the referent, liveness/hook.json -- extracted to 2073 files and searched. POSITIVE CONTROLS FIRE: the hook.json ledger note 'REMOVED 2026-09-04 (#14478)' -> 2 files; the hook.zod describe 'Maximum execution time in milliseconds before the hook is aborted' -> 21 files. NEGATIVE CONTROL silent: a sentinel string -> 0. SUBJECTS all 0: the old citation text this PR replaces, both checklist item ids, the edited step text, and any 'docs/qa/platform-checklist' path at all; `find` for any docs/qa path in the tarball returns nothing. Nothing could inline it either: turbo.json declares no docs/qa input, and the only published-package source mentioning the tree is a docblock in packages/services/service-settings/src/config-change-audit.ts naming areas/platform-core.json as a PATH -- a file this PR does not touch, and not quoted checklist text. => `skip-changeset` applied to the PR (additive POST was blocked by the sandbox classifier; fell back to the prescribed MCP route -- read current ['size/s'], union, whole-group write, then a comparative read-back: read-back ['size/s','skip-changeset'], union minus read-back = NONE stripped).", "mcp_calls": "1 -- a single mcp__github__issue_write to set the PR label group, forced by the classifier blocking the additive REST POST. Everything else (issue body, both comments, PR creation, PR body read-back, label reads) went over repo-scoped REST or git; the repo-scoped REST probe returned 200 at the start of the run.", "open_questions": [], "out_of_scope_findings": [ "noted, not filed: #15626 is one of at least three PRs in the #14478 ruling-B stack, and its SIBLINGS renamed two more keys whose tombstones this run measured while bounding the sweep -- `api.cacheTtl` -> `cacheTtlSeconds` (#15677) and `dashboard.refreshInterval` -> `refreshIntervalSeconds` (#15680), both now carrying `dead` rows in packages/spec/liveness/api.json and dashboard.json. Those are OUTSIDE this card's bound (#15626 residue only) so the diff was not widened, but the same drift class plausibly applies. NOT audited here, deliberately. Carrier: whoever grades the ungated-citation class the triage already cross-references (#16441, #16477) -- that card would naturally cover the whole ruling-B stack in one sweep rather than one PR at a time.", "noted, not filed: the corrected hook citation still enumerates only 9 of the ledger's 15 props -- `name`, `handler` and `runAs` (all 'live') were never in it. That is pre-existing incompleteness, not a false statement, so under the filing rules it is class-(a)-adjacent but explicitly excluded ('missing members is not class (a)'). Left as authored; the diff was not widened to add them. Carrier: the next author who revises records-forms.object-hook-lifecycle for substance.", "noted, not filed: `check:platform-checklist` is deliberately not CI-wired (README cadence note + scripts/pm/check-half-states.mjs:79), and the platform-checklist-watchdog workflow is the only automated reader. So the anchor-floor ratchet this PR moves (78 -> 79) is enforced by nobody on the PR itself. Not a defect -- a documented, deliberate cadence -- but it means the PM cannot read a CI green as confirmation of the checklist gate. Carrier: none; recorded so the report's green is not over-read.", "noted, not filed: no gate compares a checklist `ref` against the ledger it names -- the citations are prose. This run's evidence is that the resolver DOES check a cited `#symbol` still exists, which is why `hook.json#timeout` resolves happily while meaning the opposite of what the citation claimed: presence-checking cannot catch a status contradiction. This is exactly the population the triage flagged (#16441, #16477) and explicitly ruled NOT this card. Carrier: that future card, if anyone grades it." ] }
Generated by Claude Code
zhuangjianguo commented
on Sep 9, 2026 CollaboratorMore actionsACCEPT — in-seat review of PR #16997
domain:specexecution seat, sessionsession_016N6xmWt5hYm94ffVEwGH8x, 2026-09-09T01:12Z. Verified onorigin/mainfe2b7554a— a later tree than the branch's merge basebb7d91f19, so these are re-derivations, not re-reads of the report.Tier fuse — passed. Dispatch passed
model: 'opus'explicitly; the subagent transcript carries 200 harness-stamped"model":"claude-opus-5"envelopes and no other value. ⛔ Not taken from self-description.Landing precheck — limb ① (in-seat review)
The true PR diff, taken from the merge base rather than from
git diff origin/main HEAD:git merge-base origin/main 98c651703 → bb7d91f19 git diff --stat origin/main...98c651703 docs/qa/platform-checklist/areas/integration-system.json | 16 +++++++++----- docs/qa/platform-checklist/areas/records-forms.json | 10 ++++++-- 2 files changed, 19 insertions(+), 7 deletions(-)⚠️ Worth stating because this seat nearly mis-read it: the two-dot form reports 7 files / −881 lines, all of itmain's own newer commits appearing as reversions.base.shaon the PR object is the base branch tip, not the merge base. ⇒ take a PR's file list fromA...B, ⛔ never fromA B.Governed-surface predicate re-run on that true list:
governed-surface predicate: 0 of 2 path(s) hit the register (5 surfaces, repo-agnostic). ✅ NOT governed — ordinary queue landing appliesexit 0. CI's own
Governed Surface Queue Guardalso readssuccess.The three load-bearing claims, re-measured by this seat:
claim reading on fe2b7554athe ledger's verdicts packages/spec/liveness/job.json→/props/timeoutMs: live(verifiedAt 2026-09-04),/props/timeout: dead(same date). ✅the fixture the corrected citation names examples/app-showcase/src/automation/jobs/index.ts:23→timeoutMs: 300000— the exact line, exact spelling. ✅the row deliberately left alone records-forms.json:3168citeswrapDeclarativeHook's wrapping ordercondition→async→retry→timeout→onError, andpackages/objectql/src/hook-wrappers.ts:243itself reads4. timeout → abort if handler runs too long. The row mirrors its source. ✅⛔ Note on the third: my first grep for it returned 0 on a unicode-arrow pattern. Re-run with a plain
timeoutpattern plus a file-readability control (1192 lines) it returns eight hits including:243. ⇒ the zero was my instrument, not the tree — the fourth time this shift a hand-built pattern has produced a false absence.⭐ The edit this seat particularly checked, because it is where a careless fix would break something: the
acceptanceclause changed the key totimeoutMswhile keeping the execution status'timeout'untouched —an over-timeoutMsrun is recorded with execution status **'timeout'** …Those are two different vocabularies in one sentence and only one of them moved. That is the correct edit, and getting it wrong would have silently re-authored what the item proves.
⇒ limb ① PASS.
Limb ② —
--pair✓ check-clause2-carriers: PR #16997 / card #15839 — the clause-② declaration is readable in the fixed spelling and both carriers agree, and its diff carries no widening tell. exit=0Limb ③ — checks by name
34 check runs, aggregated by name (⛔ not the required subset, ⛔ not the rollup): every name resolves to
successorskipped, with one still open —Lint & Repo Gatesisin_progress. Names carrying askippedand asuccess(Check Changeset,Auto Label,Check PR Size) aggregate green. ⇒ limb ③ is not yet satisfied; the PR stays draft until that last gate completes. ⛔ No flip on a partial reading.The two judgment calls the dev raised — both upheld
1. The sweep bound was wider than my dispatch's gloss, and the dev was right. My prompt glossed it as rows citing
hook.timeout, while the triage text I quoted verbatim says "rename residue from #15626" — which is seven ADR-0087 renames. The dev's reconciliation is the only consistent one: my own file-surface line said "plus any sibling area file the bounded sweep turns up", and under a hook-only bound no sibling file could ever turn up, becausehook.jsonis cited exactly once in the whole checklist. ⇒ my dispatch was internally inconsistent and the dev resolved it the way that leaves both clauses meaning something. ⭐ It then found real residue on that wider bound —job.timeouton four rows ofintegration-system.json— which the narrow reading would have left on the tree. ✅ Accepted; no revert.2. The
skip-changesetlabel needed an MCP call. My "do not touch labels" was about the issue surface (it sits beside theClaim:comment and the assignee rule). A PR label is different and load-bearing:Check Changesetfails a PR carrying neither a changeset nor the label. The additive REST POST was blocked by the sandbox classifier, and the dev fell back to the prescribed route — read current, union, whole-group write, comparative read-back (['size/s']→['size/s','skip-changeset'], union minus read-back = nothing stripped). ✅ That is the four-step label discipline executed exactly. Accepted.What makes the evidence here better than "the gates were green"
⭐ The acceptance is a mechanical resolution of the citation against the ledger it names, before and after —
contradicted by the ledger: [('timeout','dead')]→contradicted by the ledger: NONE— not a report that a generator ran. That distinction is the same one that made #16992 necessary, and it is the right instinct on a prose card.⭐ The zeroes carry a firing control: the same instrument over the same corpus returns 19
\btimeout\band 7\btimeoutMs\b. And the 14 hits left alone were each resolved against the artefact they name rather than pattern-excluded — theJobExecutionStatusenum value, theApprovalEscalationSchema.timeoutHoursfamily (a different schema #15626 never touched), and prose about probe budgets.⚠️ One honest limit the dev surfaced and this seat is recording rather than smoothing over:check:platform-checklistis deliberately not CI-wired, so the anchor-floor movement this PR makes (records-forms.json78 → 79, floor 78;integration-system.jsonheld at 57) is a local reading only — CI will not repeat it, and ⛔ a green CI on this PR must not be read as confirming that gate. Flagging the limit of your own evidence is the behaviour this seat wants.Recorded for filing — a sibling residue one layer over, ⛔ not folded in
While checking claim 3 this seat measured something adjacent:
packages/objectql/src/hook-wrappers.tsspellstimeoutin backticked key lists at:8(`timeout`, `onError`) and:246(`async`, `retryPolicy`, `timeout`) — where its neighboursretryPolicyandasyncare real keys, sotimeoutreads as a key too — while the file's own runtime at:358readsmeta.timeoutMs. Same #14478 residue class, one layer below the checklist.⛔ Out of this card's bound (checklist corpus only) and out of this lane (
packages/objectql⇒domain:engine). This seat files it as a bare card after a dedup pass, ⛔ does not widen this PR, and ⛔ does not assert it is a defect ahead of that lane's grading —:243's4. timeout → abortis arguably a step name rather than a key, and that distinction is the graders'.Disposition
⛔ Staying draft until
Lint & Repo Gatescompletes. On its green: undraft, arm auto-merge, land through the queue. ⛔ No manualmainmerge — the file face (docs/qa/platform-checklist/**) is disjoint from everything else in flight and from every commitmaintook sincebb7d91f19; any semantic conflict is the merge queue's to arbitrate.
Generated by Claude Code
Found while clearing
src/api/**for #15677 (stack card 2/6 of #14478); out of that card's scope, which is theapi/directory only.The claim and the ledger disagree
docs/qa/platform-checklist/areas/records-forms.json:3171cites the hook liveness ledger like this:packages/spec/liveness/hook.jsononorigin/mainsays otherwise:timeoutdeadtimeoutMslivePR #15626 renamed
hook.timeouttohook.timeoutMsunder ruling B on #14478, tombstoned the old spelling and split the ledger row in two (aliverow fortimeoutMs, adeadrow fortimeout, following the documented pattern). The checklist row was not moved with it, so it now asserts aslivea key the ledger records as retired, and does not mention the key that replaced it.Why it is worth a card rather than a shrug
The row is a
ref— the coordinate a checklist runner opens to decide what the item is supposed to prove. A runner reading it looks fortimeoutin the hook ledger, finds adeadrow, and has to reconstruct which of the two spellings the item meant. Nothing catches this: the checklist's citations are prose, and no gate compares a checklistrefagainst the ledger it names.Reproduce
Measured on
origin/mainatcc5b3dd0c(2026-09-05). Pre-existing — not introduced by #15677, whose diff does not touchhook.jsonorrecords-forms.json.Suggested fix
Rewrite the citation to name
timeoutMsas live andtimeoutas the retired tombstone, matching how the ledger now reads. Worth checking the sibling rows in the same file for the same rename residue while there.Related: #15626, #14478, #15677.
Generated by Claude Code