You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Job status vocabulary has no degraded value — #5548's services half cannot compile or persist without widening JobExecutionStatus + the two sys_job* selects #7072
Blocks #5548 (the services half of the 2026-08-08 B-minimal maintainer ruling). Filed by the services-lane dev agent dispatched on #5548 (session session_01USNUyHEr7uaU6MoEWXitei) after measuring that #5548's deliverable is unreachable inside its declared file surface — and unreachable inside domain:services at all.
Not a design question: the maintainer already ruled the vocabulary gets exactly one more value. This card is the mechanical execution of that ruling in the two lanes that own the declarations.
What the ruling requires
From the 2026-08-08 maintainer ruling on #5548 (comment 5224023201), verbatim:
Vocabulary stays minimal — one additional outcome meaning "completed without accomplishing the work". ⛔ Do not open an enum family; a second key would need its own pull.
and
DbJobAdapter maps a handler-resolved degraded outcome to a sys_job_run.statusdistinct from success; bumpJob (sys_job.last_status) in sync.
"One additional status value" is the vocabulary widening. Nothing has performed it.
What the spec half actually shipped
#6617 (closed completed 2026-08-09T12:27:24Z, commit e18a1626) delivered the producer side only:
packages/spec/src/contracts/job-service.ts — new JobRunOutcome { outcome: 'completed' | 'degraded'; reason?: string }, JobHandler widened to resolve void | JobRunOutcome;
packages/services/service-job/src/run-with-policy.ts — made generic so the resolved value is no longer erased.
Its declared file surface (claim comment on #6617) was contracts/job-service.ts + spec contract tests + the api-surface shard. packages/spec/src/system/job.zod.ts was not in it, and the shipped TSDoc says so plainly: "the shipped adapters currently discard the resolved value". Correct scoping by #6617 — the gap is simply that the consumer-side vocabulary was never filed as anyone's card.
The gap — three declaration sites, none carrying degraded
Exhaustive — a repo-wide grep for the four-value tuple returns exactly these three plus the spec pin test.
Measured consequences (both directions, both fatal)
1. It does not compile.DbJobAdapter.finishRun's status parameter is typed JobExecution['status'], i.e. site 1. Applying the minimal #5548 mapping in wrap() and nothing else:
src/db-job-adapter.ts(169,48): error TS2345: Argument of type '"success" | "degraded"' is not assignable to parameter of type '"timeout" | "success" | "failed" | "running"'.
Type '"degraded"' is not assignable to type '"timeout" | "success" | "failed" | "running"'.
src/db-job-adapter.ts(170,34): error TS2345: Argument of type '"success" | "degraded"' is not assignable to parameter of type '"success" | "failed"'.
(The second error is in-lane and trivially fixable; the first is site 1 and is not.)
2. Even if it compiled, the write would be refused — and refused silently. Site 2 is enforced, not merely declared: packages/objectql/src/validation/record-validator.ts:578 rejects an out-of-vocabulary select value with invalid_option, and JobServicePlugin hands DbJobAdapter the real ObjectQL engine (ctx.getService('objectql')). DbJobAdapter.finishRun wraps its update in a best-effort try/catch that logs logger.warn — so the rejection would be swallowed and the run row would stay status: 'running'forever. That is a strictly worse audit face than the defect #5548 is fixing.
The underlying defect, reproduced on unmodified origin/main
A handler resolving the shipped JobRunOutcome degraded shape, driven through DbJobAdapter.trigger:
packages/spec/src/system/job.test.ts:399-408 and :467 — the accept/reject pins and the Array< JobExecution['status'] > round-trip list both enumerate the four values.
packages/platform-objects/src/audit/sys-job-run.object.ts:49 — status select.
Regenerated i18n bundles — the select options are carried per-locale in packages/platform-objects/src/apps/translations/{en,zh-CN,ja-JP,es-ES}.objects.generated.ts (sys_job_run.fields.status.options, sys_job.fields.last_status.options); node scripts/check-i18n-bundles.mjs --write, gated by check:i18n / check:i18n-coverage.
Regenerated spec artifacts — JobExecutionStatus appears in packages/spec/json-schema.manifest/system.json and packages/spec/api-surface/system.json; per AGENTS.md run pnpm --filter @objectstack/spec build && check:generated.
Why one card and not two
Splitting sites 1 and 2/3 into a domain:spec card and a domain:metadata card opens a window in which the spec enum and the platform object disagree about the legal value set. That is precisely the defect shape #6036 was filed and fixed for (sys_setting.scope declared a runtime option the spec enum did not know) — its resolution note put it best: 「两边本就该是同一个真相」. Recommend one card spanning both lanes, seated wherever the PM seats cross-lane vocabulary work; if it must be split, the metadata half should land first so no window exists in which the type system permits a value the engine refuses.
Downstream note — not for this card
objectui's Studio jobs view reads sys_job_run.status / sys_job.last_status and will meet a fifth value it has no label, colour or filter option for. Out of scope here (separate repo) and premature to file there while the value does not exist; worth a follow-up in objectui once this lands.
Filed unlabeled and unassigned for PM triage per #4949 discipline. Duplicate search before filing: sys_job_run, JobExecutionStatus, "job status enum" over open issues — only #5548 itself.
Blocks #5548 (the services half of the 2026-08-08 B-minimal maintainer ruling). Filed by the services-lane dev agent dispatched on #5548 (session
session_01USNUyHEr7uaU6MoEWXitei) after measuring that #5548's deliverable is unreachable inside its declared file surface — and unreachable insidedomain:servicesat all.Not a design question: the maintainer already ruled the vocabulary gets exactly one more value. This card is the mechanical execution of that ruling in the two lanes that own the declarations.
What the ruling requires
From the 2026-08-08 maintainer ruling on #5548 (comment 5224023201), verbatim:
and
"One additional status value" is the vocabulary widening. Nothing has performed it.
What the spec half actually shipped
#6617 (closed completed 2026-08-09T12:27:24Z, commit
e18a1626) delivered the producer side only:packages/spec/src/contracts/job-service.ts— newJobRunOutcome { outcome: 'completed' | 'degraded'; reason?: string },JobHandlerwidened to resolvevoid | JobRunOutcome;packages/services/service-job/src/run-with-policy.ts— made generic so the resolved value is no longer erased.Its declared file surface (claim comment on #6617) was
contracts/job-service.ts+ spec contract tests + the api-surface shard.packages/spec/src/system/job.zod.tswas not in it, and the shipped TSDoc says so plainly: "the shipped adapters currently discard the resolved value". Correct scoping by #6617 — the gap is simply that the consumer-side vocabulary was never filed as anyone's card.The gap — three declaration sites, none carrying
degradedRead from
origin/main@2c7e62d5:packages/spec/src/system/job.zod.ts:178JobExecutionStatus = z.enum(['running','success','failed','timeout'])domain:specpackages/platform-objects/src/audit/sys-job-run.object.ts:49status: Field.select(['running','success','failed','timeout'], …)domain:metadatapackages/platform-objects/src/audit/sys-job.object.ts:73last_status: Field.select(['success','failed','timeout','running'], …)domain:metadataExhaustive — a repo-wide grep for the four-value tuple returns exactly these three plus the spec pin test.
Measured consequences (both directions, both fatal)
1. It does not compile.
DbJobAdapter.finishRun's status parameter is typedJobExecution['status'], i.e. site 1. Applying the minimal #5548 mapping inwrap()and nothing else:(The second error is in-lane and trivially fixable; the first is site 1 and is not.)
2. Even if it compiled, the write would be refused — and refused silently. Site 2 is enforced, not merely declared:
packages/objectql/src/validation/record-validator.ts:578rejects an out-of-vocabularyselectvalue withinvalid_option, andJobServicePluginhandsDbJobAdapterthe real ObjectQL engine (ctx.getService('objectql')).DbJobAdapter.finishRunwraps its update in a best-efforttry/catchthat logslogger.warn— so the rejection would be swallowed and the run row would staystatus: 'running'forever. That is a strictly worse audit face than the defect #5548 is fixing.The underlying defect, reproduced on unmodified
origin/mainA handler resolving the shipped
JobRunOutcomedegraded shape, driven throughDbJobAdapter.trigger:{ outcome: 'degraded', reason: 'STORE_UNAVAILABLE' }in,status: "success"/error: nullout. #5548's premise holds verbatim.Complete change set this card owes
packages/spec/src/system/job.zod.ts:178— add'degraded'toJobExecutionStatus, with a TSDoc pointing at DbJobAdapter 把「handler 没抛错」记成 sys_job_run.status='success' —— 内部自行降级的 job(如 wait 唤醒打空)在作业审计面上仍显示成功 #5548's ruling and stating that degraded is not a failure and never retries (matching theJobHandlerTSDoc spec: JobHandler 增加可选的「跑完但没干成」回报通道 —— #5548 裁决(B-minimal)的契约半边,可加性,Promise<void> handler 零改动 #6617 shipped).packages/spec/src/system/job.test.ts:399-408and:467— the accept/reject pins and theArray< JobExecution['status'] >round-trip list both enumerate the four values.packages/platform-objects/src/audit/sys-job-run.object.ts:49—statusselect.packages/platform-objects/src/audit/sys-job.object.ts:73—last_statusselect.packages/platform-objects/src/apps/translations/{en,zh-CN,ja-JP,es-ES}.objects.generated.ts(sys_job_run.fields.status.options,sys_job.fields.last_status.options);node scripts/check-i18n-bundles.mjs --write, gated bycheck:i18n/check:i18n-coverage.JobExecutionStatusappears inpackages/spec/json-schema.manifest/system.jsonandpackages/spec/api-surface/system.json; per AGENTS.md runpnpm --filter @objectstack/spec build && check:generated.Why one card and not two
Splitting sites 1 and 2/3 into a
domain:speccard and adomain:metadatacard opens a window in which the spec enum and the platform object disagree about the legal value set. That is precisely the defect shape #6036 was filed and fixed for (sys_setting.scopedeclared aruntimeoption the spec enum did not know) — its resolution note put it best: 「两边本就该是同一个真相」. Recommend one card spanning both lanes, seated wherever the PM seats cross-lane vocabulary work; if it must be split, the metadata half should land first so no window exists in which the type system permits a value the engine refuses.Downstream note — not for this card
objectui's Studio jobs view reads
sys_job_run.status/sys_job.last_statusand will meet a fifth value it has no label, colour or filter option for. Out of scope here (separate repo) and premature to file there while the value does not exist; worth a follow-up inobjectuionce this lands.Filed unlabeled and unassigned for PM triage per #4949 discipline. Duplicate search before filing:
sys_job_run,JobExecutionStatus, "job status enum" over open issues — only #5548 itself.