Skip to content

fix(service-job,service-automation): map a degraded job outcome to sys_job_run.status instead of success (#5548) - #7446

Merged
os-help merged 2 commits into
mainfrom
claude/issue-5548-job-degraded-status
Aug 10, 2026
Merged

fix(service-job,service-automation): map a degraded job outcome to sys_job_run.status instead of success (#5548)#7446
os-help merged 2 commits into
mainfrom
claude/issue-5548-job-degraded-status

Conversation

@os-help

@os-help os-help commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Fixes #5548

The services half of the 2026-08-08 maintainer ruling (B-minimal). The producer half shipped as #6617 (JobRunOutcome on JobHandler) and the consumer vocabulary as #7072/#7340 (degraded in JobExecutionStatus and both Field.select sites); the adapters still discarded the resolved value, so DbJobAdapter decided a run's outcome purely by whether the handler threw.

The ruling, quoted verbatim and untranslated:

采纳 B-minimal —— 给 handler 一个「跑完了但没干成」的可加性回报通道,由适配器映射到一个区别于 successsys_job_run.status

What changed

packages/services/service-job

  • DbJobAdapter.wrap() reads the handler's resolved value. { outcome: 'degraded', reason? } lands sys_job_run.status: 'degraded' with the reason in error; undefined and { outcome: 'completed' } keep landing success; a throw keeps landing failed and retrying.
  • bumpJob() mirrors degraded onto sys_job.last_status / last_error and leaves failure_count flat — degraded is not a failure, so the failure/alerting signal must not move (contract, job-service.ts).
  • IntervalJobAdapter / CronJobAdapter map the same third state onto the in-memory JobExecution. Without this, DbJobAdapter.getExecutions() (which delegates to the inner adapter) would report success for the very run whose persisted row says degraded.
  • replay()'s synthetic row is settled from the execution the inner adapter just recorded, so a replayed degraded run no longer writes one degraded row next to one success row. IJobService.trigger resolves void by contract, so the outcome cannot travel back through it.

packages/services/service-automation

Two judgement calls the card left open, and why

  1. Where reason lands — the existing error / last_error columns, no new column. This is not a fresh decision: it was taken at the domain:services seat on Job status vocabulary has no degraded value — #5548's services half cannot compile or persist without widening JobExecutionStatus + the two sys_job* selects #7072 and is recorded in the JobExecutionStatus TSDoc, together with its honest cost (a column labelled "Error" carries a non-error note when status === 'degraded', so a reader must gate on the status). Implementing anything else here would contradict a shipped contract comment.
  2. Whether bumpJob mirrors degraded onto sys_job.last_status — yes. The summary row is the operator's first stop, and leaving it on success would move the defect one table over rather than fixing it; sys_job.last_status's own Field.select was widened by feat(spec,platform-objects): add degraded to the job status vocabulary (#7072) #7340 for exactly this. What deliberately does not move is failure_count (and nothing about retry), which is the direct consequence of "degraded is not a failure".

Verification

  • packages/services/service-job: 56/56 pass (13 new). packages/services/service-automation: 890/890 pass (3 new).
  • pnpm --filter '@objectstack/service-job' typecheck clean; tsc --noEmit on service-automation reports only three pre-existing TS2341 errors in nested-region-parity.test.ts (untouched file, present on origin/main; that package has no typecheck script).
  • Reverse verification, both halves separately, taking each file out with git checkout origin/main -- PATH (never git stash):
    • adapter removed ⇒ 7 of 13 new cases red, every one naming the cell: expected 'success' to be 'degraded'. The 6 that stay green are exactly the additivity/legacy cases plus the two timer-adapter cases (different file, still fixed) — the predicted direction.
    • wait-node.ts removed ⇒ 2 of the 3 end-to-end cases red (expected 'success' to be 'degraded'), success control still green. That is what proves the end-to-end pin exercises the producer adoption and not just the adapter.
  • Additivity has its own reverse evidence: a handler resolving undefined is asserted to still land success with a null error and a flat failure_count.
  • Gates derived from the changed paths with scripts/pm/dispatch-gates.mjs: check:nul-bytes, check:docs-audit-scope, check:changeset-gate-self-tests, check-changeset-fixed, check-changeset-no-major — all green locally. ESLint clean on the changed files.

Every rejection-class / status-class assertion reads the value written into the sys_job_run / sys_job cell — never "the handler was called" or "it did not throw", since that criterion is the defect itself.

⛔ Out of scope by the dispatch's own boundary: packages/spec and packages/platform-objects are untouched (both halves already landed). Nothing further was found missing in either.


Generated by Claude Code

@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 10, 2026 12:34pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/service-automation, @objectstack/service-job.

3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/automation/flows.mdx (via @objectstack/service-automation)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/service-automation, @objectstack/service-job)
  • content/docs/plugins/packages.mdx (via @objectstack/service-automation, @objectstack/service-job)

2 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx (via @objectstack/service-automation, @objectstack/service-job)
  • content/docs/releases/v9.mdx (via @objectstack/service-automation)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-help
os-help marked this pull request as ready for review August 10, 2026 12:47
@os-help
os-help added this pull request to the merge queue Aug 10, 2026
Merged via the queue into main with commit 05ac83d Aug 10, 2026
27 checks passed
@os-help
os-help deleted the claude/issue-5548-job-degraded-status branch August 10, 2026 13:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

1 participant