From 98c651703e223c0286b74b557b640396fdc46d0c Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 9 Sep 2026 00:59:26 +0000 Subject: [PATCH] docs(qa): checklist refs name the live duration keys, not the #14478 tombstones The hook and job liveness ledgers split their `timeout` row in two when `#14478` ruling B renamed the key to `timeoutMs`: a `live` row for the new spelling and a `dead` tombstone row for the old one. Five checklist rows were not moved with the rename, so each cited a key its own referent records as retired. `records-forms.object-hook-lifecycle` (rev 1 -> 2), source citation only: packages/spec/liveness/hook.json#retryPolicy asserted `timeout` among the props that are 'live'. Measured on the ledger it names, `timeout` is `dead` and `timeoutMs` is `live`. Because the row is a REF -- the coordinate a runner opens to learn what the item must prove -- following it landed the reader on a tombstone row with the replacement key unmentioned, leaving them to reconstruct which of the two spellings was meant. The citation now names `timeoutMs` as live and anchors the `timeout` tombstone explicitly. `integration-system.job-scheduled-run` (rev 2 -> 3), key spelling only, found by the bounded sweep for residue of the same rename: - the fixture citation said `timeout 300000`; the file it names, examples/app-showcase/src/automation/jobs/index.ts, authors `timeoutMs: 300000`; - the timing step and the over-limit acceptance clause instructed a runner to author `timeout`, which the schema now refuses at parse and at tsc; - the job.zod.ts citation named the `timeout` docs, which are the tombstone's. No clause moved in either item and no oracle changed; the job execution STATUS is still spelled 'timeout' and is left alone, as are the ladder-step name in hook-wrappers.ts, the ApprovalEscalationSchema `timeoutHours` rows and their `timeout`/`sla` alias, and the webhook and CLI prose uses. Verified: pnpm check:platform-checklist OK (631/631 symbol anchors resolved, 17 file floors held; areas/records-forms.json 78 -> 79 anchors, well over its floor). No changeset: docs/qa/** is in no package's files[], and an npm pack of @objectstack/spec -- the only publishable package whose files[] reaches past dist/README/CHANGELOG, and the one that publishes the referent liveness/ tree -- carries none of the changed text, with positive controls firing in the same sweep. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x --- .../areas/integration-system.json | 16 +++++++++++----- .../platform-checklist/areas/records-forms.json | 10 ++++++++-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/docs/qa/platform-checklist/areas/integration-system.json b/docs/qa/platform-checklist/areas/integration-system.json index 98b655a8ed..e7a03c9013 100644 --- a/docs/qa/platform-checklist/areas/integration-system.json +++ b/docs/qa/platform-checklist/areas/integration-system.json @@ -652,13 +652,13 @@ "title": "Job metadata schedules through every ScheduleSchema variant, executes with retry/timeout enforced, records sys_job/sys_job_run truth, and refuses the closed doors (id key, runtime create, missing handler)", "since": "v15", "status": "active", - "revision": 2, + "revision": 3, "priority": "P1", "surface": "mixed", "fixtures": { "app": "showcase", "requires": [ - "the shipped cron fixture: showcase_health_sweep (schedule cron '0 1 * * *' UTC, handler 'sweepProjectHealth' registered in defineStack({ functions }), retryPolicy { maxRetries: 2, backoffMs: 5000, backoffMultiplier: 2 }, timeout 300000 — examples/app-showcase/src/automation/jobs/index.ts; the handler-missing regression here was #4774/#4888)", + "the shipped cron fixture: showcase_health_sweep (schedule cron '0 1 * * *' UTC, handler 'sweepProjectHealth' registered in defineStack({ functions }), retryPolicy { maxRetries: 2, backoffMs: 5000, backoffMultiplier: 2 }, timeoutMs 300000 — examples/app-showcase/src/automation/jobs/index.ts; the handler-missing regression here was #4774/#4888)", "scratch jobs in a writable package for the interval / once / failing / timing-out probes (near-term schedules so the run observes real executions)" ] }, @@ -675,7 +675,7 @@ "read sys_job over /api/v1/data: the row is keyed by NAME (the adapter mints its own row id) with the schedule persisted", "author three scratch jobs (interval ~5s, once at now+1min, and a cron) with observable side effects; boot; let each fire; read sys_job_run rows and sys_job.last_run_at/last_status/run_count", "author a deliberately-throwing job with retryPolicy { maxRetries: 2, backoffMs: 1000, backoffMultiplier: 2 }; let it exhaust; capture run rows + failure_count", - "author a job whose handler sleeps past a small `timeout`; capture the run's status", + "author a job whose handler sleeps past a small `timeoutMs`; capture the run's status", "author a job with enabled:false and one whose handler string names NO registered function; boot; capture how each is skipped", "author a job carrying `id: 'x'` and one with a stray key; build both; capture the errors", "attempt to create a job at runtime through the meta door and capture the refusal (allowRuntimeCreate:false, #4509)" @@ -700,7 +700,7 @@ "evidence": "run rows + timestamped log" }, { - "clause": "an over-`timeout` run is recorded with execution status 'timeout' (the in-flight handler is abandoned, not force-cancelled — as documented), and timeouts COUNT as failures for the retry loop (#3494)", + "clause": "an over-`timeoutMs` run is recorded with execution status 'timeout' (the in-flight handler is abandoned, not force-cancelled — as documented), and timeouts COUNT as failures for the retry loop (#3494)", "oracle": "api", "verify": "the timeout probe's run row status", "evidence": "the run row" @@ -727,7 +727,7 @@ "seed-data-thin" ], "source": [ - "packages/spec/src/system/job.zod.ts#ScheduleSchema (ScheduleSchema discriminated union; JOB_ID_RETIRED; retryPolicy/timeout docs incl. the 17.0.0 maxRetries default flip #4661; JobExecutionStatus)", + "packages/spec/src/system/job.zod.ts#ScheduleSchema (ScheduleSchema discriminated union; JOB_ID_RETIRED; retryPolicy/timeoutMs docs incl. the 17.0.0 maxRetries default flip #4661; JobExecutionStatus)", "packages/spec/liveness/job.json (per-prop verdicts + the #4509 closed-door rationale)", "packages/runtime/src/app-plugin.ts#handler (registration, enabled/handler skip lines)", "packages/services/service-job/src/cron-job-adapter.ts + db-job-adapter.ts (all three schedule shapes; sys_job/sys_job_run persistence) + run-with-policy.ts (retry/timeout enforcement, #3494)", @@ -745,6 +745,12 @@ "date": "2026-08-07", "change": "expanded to deep-test contract: concrete steps, multi-clause acceptance, negatives, variants", "ref": "claude/platform-test-checklist-ocwugl" + }, + { + "revision": 3, + "date": "2026-09-09", + "change": "key spelling only, no clause moved — the same #14478 rename residue as records-forms.object-hook-lifecycle: `job.timeout` became `timeoutMs` (old spelling kept as a retiredKey tombstone), so the fixture citation, the timing step, the over-limit clause and the job.zod.ts citation all named a key the schema now refuses. What is exercised is unchanged; the execution STATUS is still 'timeout'.", + "ref": "#15839" } ] }, diff --git a/docs/qa/platform-checklist/areas/records-forms.json b/docs/qa/platform-checklist/areas/records-forms.json index 1924808b3d..b786f85202 100644 --- a/docs/qa/platform-checklist/areas/records-forms.json +++ b/docs/qa/platform-checklist/areas/records-forms.json @@ -3030,7 +3030,7 @@ "title": "Object lifecycle hooks fire on the write path with the right timing, gate, async and error semantics — driven over /api/v1/data/*, oracled by the record effect and the log line", "since": "v15", "status": "active", - "revision": 1, + "revision": 2, "priority": "P1", "surface": "mixed", "personas": [ @@ -3168,7 +3168,7 @@ "packages/objectql/src/hook-wrappers.ts#wrapDeclarativeHook (wrapDeclarativeHook wrapping order condition→async→retry→timeout→onError; pickRecordPayload #4770 record = stored ⊕ payload total over declared fields; pickPreviousPayload #4784 previous binding; HookConditionError #4775 fail-closed, raised OUTSIDE onError; fireAndForget = async && isAfterEvent)", "packages/objectql/src/hook-binder.ts#bindHooksToEngine (bindHooksToEngine: per-event × per-object engine.registerHook with priority; unresolved-body / empty-target skips)", "packages/runtime/src/sandbox/body-runner.ts#engineCtx (log: engineCtx.logger — the ['log'] capability routing that makes the audit/warn lines an observable log oracle)", - "packages/spec/liveness/hook.json#retryPolicy (object/events/body/priority/async/condition/retryPolicy/timeout/onError all 'live'; label/description 'dead' but kept as docs)", + "packages/spec/liveness/hook.json#retryPolicy (object/events/body/priority/async/condition/retryPolicy/timeoutMs/onError all 'live'; packages/spec/liveness/hook.json#timeout is 'dead' — the #14478 tombstone left when the key was renamed to timeoutMs, so a runner reads the wall-clock abort off the timeoutMs row; label/description 'dead' but kept as docs)", "examples/app-showcase/src/data/objects/{task,inquiry,project}.object.ts (task.done boolean + task.title required; inquiry.status select['new' default:true]/source text no-default; project.budget/spent currency + spent_within_budget rule rejecting spent > budget*1.2)", "examples/app-showcase/src/data/seed/index.ts#tasks (done tasks 'Audit current IA'/'App wireframes'; not-done 'Build homepage'; projects 'Data Platform' 600000/420000 and 'Website Relaunch' 150000/60000)", "packages/qa/dogfood/test/showcase-public-form.dogfood.test.ts + hook-error-format.dogfood.test.ts (the two existing dogfood pins this item cites)" @@ -3179,6 +3179,12 @@ "date": "2026-08-08", "change": "initial — the hook coverage.json waiver was STALE (showcase ships 4 observable hooks); authored a lifecycle+condition+async+onError+priority item", "ref": "claude/platform-test-checklist-ocwugl" + }, + { + "revision": 2, + "date": "2026-09-09", + "change": "citation only, no clause moved — the hook liveness citation asserted `timeout` 'live'; #14478 renamed it to `timeoutMs` and left `timeout` as a 'dead' tombstone row, so the ref sent a runner to a contradiction. Now names the live key and the tombstone.", + "ref": "#15839" } ] },