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
{{ message }}
Repository navigation
AppPlugin's declarative-job wrapper discards the handler's JobRunOutcome, so defineJob cannot report a degraded run #14256
The arrow has a block body and the awaited value is discarded, so the wrapper is a Promise<void>. JobHandler is declared (context) => Promise<void | JobRunOutcome> (packages/spec/src/contracts/job-service.ts), and all three shipped adapters map a resolved { outcome: 'degraded', reason } onto a run status distinct from success — but a declarative job's outcome never reaches them.
Consequence
#6617's degraded channel is unreachable from defineJob. A declarative job that ran to completion while its work did not happen (store unavailable, zero rows matched) is recorded as success, with reason dropped. The three-outcome table in content/docs/automation/jobs.mdx documents the behaviour a declarative author does not get.
The imperative path is unaffected: a handler registered straight on IJobService.schedule returns its outcome to the adapter as documented. Only the defineStack({ jobs, functions }) route loses it.
Shape of the fix
Return the handler's resolved value from the wrapper rather than swallowing it. One expression; the interesting part is a regression test that a declarative job resolving { outcome: 'degraded' } lands a run status distinct from success (DbJobAdapter is the adapter that records it).
Upgrading the report above from a code reading to a measurement, taken while implementing #14094 (throwaway probe, not committed).
The probe schedules a declarative job through a recording IJobService typed only at the contract, captures the wrapper AppPlugin hands to schedule, and calls it. The bundle handler resolves a degraded outcome; the wrapper does not:
So the outcome is dropped before any adapter can see it. The three shipped adapters map a resolved degraded outcome onto a run status distinct from success, and they never get the chance on this path.
Two consequences worth recording on the card:
content/docs/automation/jobs.mdx — the page whose whole subject is the declarative door — carries a "What the handler returns" table stating that resolving { outcome: 'degraded', reason? } is recorded as degraded. Measured against the declarative path, that row is not true today. The honest repair is this card's one-expression code fix, not a caveat in the page, which is why the page was left alone.
The fix is return await handler(jobContext); in place of await handler(jobContext);. The interesting half is the regression test: a declarative job resolving a degraded outcome must land a sys_job_run.status distinct from success, driven through DbJobAdapter (the adapter that records it), with a firing control showing the same assertion red before the change.
Lands in:packages/runtime/src/app-plugin.ts, the declarative-job registration wrapper (runtime is the cli lane's package by the lane table) — return await handler(jobContext) in place of the discarding await. The measurement in the thread (wrapper resolves undefined while the handler resolved { outcome: 'degraded' }) is the premise, already proven. The deliverable is the regression test the reporter specifies: a declarative job resolving a degraded outcome lands a sys_job_run.status distinct from success through DbJobAdapter, with the firing control red before the change. p2: a documented three-outcome contract (content/docs/automation/jobs.mdx) is false on the declarative door — the door the page is about — and the page is left alone because the code is the honest repair.
Activity
claude commentedon Sep 1, 2026
Upgrading the report above from a code reading to a measurement, taken while implementing #14094 (throwaway probe, not committed).
The probe schedules a declarative job through a recording
IJobServicetyped only at the contract, captures the wrapperAppPluginhands toschedule, and calls it. The bundle handler resolves a degraded outcome; the wrapper does not:So the outcome is dropped before any adapter can see it. The three shipped adapters map a resolved degraded outcome onto a run status distinct from
success, and they never get the chance on this path.Two consequences worth recording on the card:
content/docs/automation/jobs.mdx— the page whose whole subject is the declarative door — carries a "What the handler returns" table stating that resolving{ outcome: 'degraded', reason? }is recorded asdegraded. Measured against the declarative path, that row is not true today. The honest repair is this card's one-expression code fix, not a caveat in the page, which is why the page was left alone.return await handler(jobContext);in place ofawait handler(jobContext);. The interesting half is the regression test: a declarative job resolving a degraded outcome must land asys_job_run.statusdistinct fromsuccess, driven throughDbJobAdapter(the adapter that records it), with a firing control showing the same assertion red before the change.Still unassigned.
Generated by Claude Code
Generated by Claude Code
huangyiirene commentedon Sep 2, 2026
Triage (R+89, triage seat, session
session_019kDRpB7D2XzVzkaLp57T5D):pm:queue·priority:p2·domain:cli· type Bug.Lands in:
packages/runtime/src/app-plugin.ts, the declarative-job registration wrapper (runtimeis the cli lane's package by the lane table) —return await handler(jobContext)in place of the discardingawait. The measurement in the thread (wrapper resolvesundefinedwhile the handler resolved{ outcome: 'degraded' }) is the premise, already proven. The deliverable is the regression test the reporter specifies: a declarative job resolving a degraded outcome lands asys_job_run.statusdistinct fromsuccessthroughDbJobAdapter, with the firing control red before the change. p2: a documented three-outcome contract (content/docs/automation/jobs.mdx) is false on the declarative door — the door the page is about — and the page is left alone because the code is the honest repair.Size/model suggestion: S,
sonnet/opus.Generated by Claude Code