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
service-job: DbJobAdapter records a manual trigger() as trigger 'schedule' on sys_job_run, and replay() leaves a second 'schedule' row beside its 'replay' row #32406
Filing gate: ① a defect read in code by the #32062 slice-4 dev (report 6790923718, out-of-scope finding, class b). The reach is named but not measured end to end.
Where (origin/main):packages/services/service-job/src/db-job-adapter.ts.
schedule() wraps the handler as this.wrap(name, handler, 'schedule', options) (L299), and the wrapper opens its sys_job_run row with that defaultTrigger (L604).
trigger() (L335) runs the same wrapper through the inner adapter, so a manual run's row reads trigger: 'schedule'.
replay() writes its synthetic 'replay' row, and the wrapper writes its own 'schedule' row beside it.
db-job-adapter.test.ts:302 pins trigger: 'schedule' after adapter.trigger('on').
Make the wrapper take the trigger of the call that runs it: a scheduled tick writes schedule, trigger() writes manual, and replay() writes ONE replay row.
Correct the stale pin at L302. Pin all three triggers, and ablate once.
Clause-②: the claimer declares it. The recorded value changes for manual and replay runs, so read whether anything consumes sys_job_run.trigger before declaring. Business reason: an operator reading run history cannot tell a manual run from a scheduled one, and a replay shows up as two runs.
Claim: PM loop round 1
Session: session_012MmDsNv6qb5gT8Pd98AmCB
Account: os-lizheng (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-32406-job-run-trigger
Worktree: objectstack-issue-32406
Domain: domain:server
Seat: domain:server#4
File surface: packages/services/service-job/src/db-job-adapter.ts (wrap, schedule, trigger, replay), its tests (db-job-adapter.test.ts pin at the trigger: 'schedule' assertion), content/docs/** only for sentences made false; root .changeset/. ⛔ packages/spec/**, ui/**, skills/**, docs/adr/** (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: default tier — dispatch-gates --tier: no path-derived mandate, effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: service-job's DbJobAdapter wrapper in objectstack (platform card) | none | every operator reading sys_job_run run history; the manual-pull sync path (#32405) reads it today
Thread-read: 6791502687
Premise: holds (re-read on origin/main7d82b2d at 2026-11-11T12:54Z)
Serial constraints cleared: none — no in-flight claim names packages/services/service-job/** (read of the lane's pm:dispatched claims at this stamp)
{
"issue": 32406,
"status": "done",
"branch": "claude/issue-32406-job-run-trigger",
"pr": "https://github.com/objectstack-ai/objectstack/pull/32441",
"session": "session_012MmDsNv6qb5gT8Pd98AmCB",
"premise_still_valid": true,
"summary": "Premise measured on a real DbJobAdapter at 7d82b2da: trigger() wrote a 'schedule' row on both assemblies, and replay() wrote 2 rows ('replay' + 'schedule'; on a retried replay the synthetic row read success for an attempt the wrapper recorded failed). DbJobAdapter.wrap() no longer fixes the trigger at wrap time: runTriggerOf() reads it per invocation, once before the first attempt — the handler context's trigger ('manual' from trigger(), 'schedule' from a timer; unstated = 'schedule', since only the injected cron adapter may omit it and DbJobAdapter never calls its trigger()). replay() drops its synthetic startRun/finishRun and getExecutions read-back and instead opens an AsyncLocalStorage scope (node:async_hooks) carrying a one-shot claim naming the exact wrapper inner holds, read after the replay guard's await; that wrapper's first invocation records 'replay', so a replay is ONE row per attempt, settled by the policy recorder. The handler context is unchanged (replay still says 'manual', per the spec's JobRunTrigger); its runId now names the replay row. Landing is the expected file; packages/spec, docs and platform-objects untouched. Card assignee not touched.",
"tests": "Before (new pins on unmodified code, head 47fbdc67 tests over 7d82b2da adapter): Tests 7 failed | 2 passed — 'expected [ schedule, schedule ] to deeply equal [ manual, schedule ]'; replay 'to have a length of 1 but got 2'. After, head c9be4172: pnpm --filter @objectstack/service-job test -> Test Files 12 passed (12), Tests 155 passed (155); typecheck tsc --noEmit exit 0 (12 test files in program); build exit 0 (check-dts-emitted 2/2). Ablation once (scripts/ablation-replace.mjs, anchor `return ctx.trigger ?? 'schedule';` -> `return 'schedule';`, anchor 1->0, blob e3dca2049082->7352a4aebce4): src/db-job-adapter.test.ts 4 failed (recordRuns true pin at old L302, trigger() writes manual x2, tag-scoping pin); restored blob == HEAD, git diff HEAD empty. Source-path run (relative imports), no dist involved. Reverse check of the guard-race pin: adapter of 47fbdc67 on disk -> 'expected [ manual ] to deeply equal [ replay ]'; restored, diff empty, blob == HEAD 4fa50aac25f4. Consumer suites (runtime job-pull-run/jobs/job-data-reach/job-degraded-outcome, trigger-schedule): NOT MEASURED — files failed to load (Failed to resolve entry, unbuilt closures); public types/exports unchanged, CI Test Core runs them.",
"gates": "Local, head c9be4172: node scripts/pm/dispatch-gates.mjs --commands -> 68 commands, all run, exit captured before any pipe; 65 exit 0; 3 exit 3 NOT MEASURED (prerequisite): node scripts/check-plugin-teardown-shape.mjs --self-test (shallow clone, pinned control commit unreachable; checker-health only), pnpm check:dual-build-cjs-loads and pnpm check:lean-entry-closure (whole-repo dist absent). --ran reconciliation: '68 derived famil(ies) accounted for — 65 run, 3 NOT-MEASURED (3 DERIVED from a recorded exit 3)', 0 UNRUN, exit 0. pnpm lint exit 0 (outside derivation). CI on c9be4172 at report time: 5 check-runs completed with no failure, 18 in_progress — convergence is the seat's.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"line_budget": "n/a — no skills/** or ratcheted ledger touched",
"files_changed": [
".changeset/32406-job-run-trigger.md",
"packages/services/service-job/src/db-job-adapter.ts",
"packages/services/service-job/src/db-job-adapter.test.ts",
"packages/services/service-job/src/db-job-adapter.timeout.test.ts",
"packages/services/service-job/src/db-job-adapter.degraded-outcome.test.ts"
],
"deviations": [
"Two stale replay pins outside db-job-adapter.test.ts were corrected (db-job-adapter.timeout.test.ts, db-job-adapter.degraded-outcome.test.ts: two rows -> one 'replay' row) — read as 'its tests' of the file surface.",
"New runtime import in service-job: AsyncLocalStorage from node:async_hooks (precedent: @objectstack/core request-grants-memo, @objectstack/service-automation loop-frame).",
"AGENTS.md multi-agent §10 'full suite once before opening the PR' narrowed per os-dev local-verification scope: package test/typecheck/build + derived gates; origin/main moved 1 commit (0dc84b02), none on the touched surface, so no merge.",
"Attribution: commits carry the model-free trailer pair AGENTS.md prescribes, and the PR body the AGENTS.md session-URL footer, instead of the harness reminder's model-named Co-Authored-By and its footer text (os-dev: the harness reminder yields to this file).",
"test:repo not run locally (not owed: diff touches no repo-project test)."
],
"mcp_calls": "0",
"api_writes": "2 relay strokes — pr_create (POST /repos/objectstack-ai/objectstack/pulls draft + POST /repos/objectstack-ai/objectstack/issues/32441/assignees os-lizheng); os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/32406/comments via post-stamped.mjs). git push is not a REST write.",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: none · noted, not filed — sys_job_run.attempt description ('1 for first run, >1 for retries/replays', packages/platform-objects/src/audit/sys-job-run.object.ts) and content/docs/automation/jobs.mdx ('higher for retries and replays') say a replay's attempt exceeds 1; no code ever wrote that (before and after this PR a replay's first attempt is 1). Pre-existing doc drift, not made false here; in the PR's Acceptance notes. Dedupe words: sys_job_run attempt replay description, retries/replays attempt"
]
}
PR #32441 merged through the queue as 74216b606b (2026-11-11T14:44Z); Fixes #32406 closed this card as completed. pm:dispatched removed in this stroke; domain:server, priority:p2 and bug stay.
Activity
objectstack-fleet commentedon Nov 11, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage:
findingpromoted →domain:server· p2 ·pm:queue· 2026-11-11T08:52Z⛔ Not a claim.
domain:server(packages/services/service-job/src/db-job-adapter.ts).Generated by Claude Code
objectstack-fleet commentedon Nov 11, 2026
domain:server#4 · os-lizheng · session_012MmDsNv6qb5gT8Pd98AmCB
Claim: PM loop round 1
Session:
session_012MmDsNv6qb5gT8Pd98AmCBAccount:
os-lizheng(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-32406-job-run-triggerWorktree:
objectstack-issue-32406Domain:
domain:serverSeat:
domain:server#4File surface:
packages/services/service-job/src/db-job-adapter.ts(wrap,schedule,trigger,replay), its tests (db-job-adapter.test.tspin at thetrigger: 'schedule'assertion),content/docs/**only for sentences made false; root.changeset/. ⛔packages/spec/**,ui/**,skills/**,docs/adr/**(stop on breach; explain in the report)Container & model:
M,mode:subagent,model: default tier — dispatch-gates --tier: no path-derived mandate,effort: highClause-②: no
Metadata-protocol: none
Responsibility:
service-job'sDbJobAdapterwrapper in objectstack (platform card) | none | every operator readingsys_job_runrun history; the manual-pull sync path (#32405) reads it todayThread-read: 6791502687
Premise: holds (re-read on
origin/main7d82b2d at 2026-11-11T12:54Z)Serial constraints cleared: none — no in-flight claim names
packages/services/service-job/**(read of the lane'spm:dispatchedclaims at this stamp)objectstack-fleet commentedon Nov 11, 2026
domain:server#4 · os-lizheng · session_012MmDsNv6qb5gT8Pd98AmCB
os-dev-report
{ "issue": 32406, "status": "done", "branch": "claude/issue-32406-job-run-trigger", "pr": "https://github.com/objectstack-ai/objectstack/pull/32441", "session": "session_012MmDsNv6qb5gT8Pd98AmCB", "premise_still_valid": true, "summary": "Premise measured on a real DbJobAdapter at 7d82b2da: trigger() wrote a 'schedule' row on both assemblies, and replay() wrote 2 rows ('replay' + 'schedule'; on a retried replay the synthetic row read success for an attempt the wrapper recorded failed). DbJobAdapter.wrap() no longer fixes the trigger at wrap time: runTriggerOf() reads it per invocation, once before the first attempt — the handler context's trigger ('manual' from trigger(), 'schedule' from a timer; unstated = 'schedule', since only the injected cron adapter may omit it and DbJobAdapter never calls its trigger()). replay() drops its synthetic startRun/finishRun and getExecutions read-back and instead opens an AsyncLocalStorage scope (node:async_hooks) carrying a one-shot claim naming the exact wrapper inner holds, read after the replay guard's await; that wrapper's first invocation records 'replay', so a replay is ONE row per attempt, settled by the policy recorder. The handler context is unchanged (replay still says 'manual', per the spec's JobRunTrigger); its runId now names the replay row. Landing is the expected file; packages/spec, docs and platform-objects untouched. Card assignee not touched.", "tests": "Before (new pins on unmodified code, head 47fbdc67 tests over 7d82b2da adapter): Tests 7 failed | 2 passed — 'expected [ schedule, schedule ] to deeply equal [ manual, schedule ]'; replay 'to have a length of 1 but got 2'. After, head c9be4172: pnpm --filter @objectstack/service-job test -> Test Files 12 passed (12), Tests 155 passed (155); typecheck tsc --noEmit exit 0 (12 test files in program); build exit 0 (check-dts-emitted 2/2). Ablation once (scripts/ablation-replace.mjs, anchor `return ctx.trigger ?? 'schedule';` -> `return 'schedule';`, anchor 1->0, blob e3dca2049082->7352a4aebce4): src/db-job-adapter.test.ts 4 failed (recordRuns true pin at old L302, trigger() writes manual x2, tag-scoping pin); restored blob == HEAD, git diff HEAD empty. Source-path run (relative imports), no dist involved. Reverse check of the guard-race pin: adapter of 47fbdc67 on disk -> 'expected [ manual ] to deeply equal [ replay ]'; restored, diff empty, blob == HEAD 4fa50aac25f4. Consumer suites (runtime job-pull-run/jobs/job-data-reach/job-degraded-outcome, trigger-schedule): NOT MEASURED — files failed to load (Failed to resolve entry, unbuilt closures); public types/exports unchanged, CI Test Core runs them.", "gates": "Local, head c9be4172: node scripts/pm/dispatch-gates.mjs --commands -> 68 commands, all run, exit captured before any pipe; 65 exit 0; 3 exit 3 NOT MEASURED (prerequisite): node scripts/check-plugin-teardown-shape.mjs --self-test (shallow clone, pinned control commit unreachable; checker-health only), pnpm check:dual-build-cjs-loads and pnpm check:lean-entry-closure (whole-repo dist absent). --ran reconciliation: '68 derived famil(ies) accounted for — 65 run, 3 NOT-MEASURED (3 DERIVED from a recorded exit 3)', 0 UNRUN, exit 0. pnpm lint exit 0 (outside derivation). CI on c9be4172 at report time: 5 check-runs completed with no failure, 18 in_progress — convergence is the seat's.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "line_budget": "n/a — no skills/** or ratcheted ledger touched", "files_changed": [ ".changeset/32406-job-run-trigger.md", "packages/services/service-job/src/db-job-adapter.ts", "packages/services/service-job/src/db-job-adapter.test.ts", "packages/services/service-job/src/db-job-adapter.timeout.test.ts", "packages/services/service-job/src/db-job-adapter.degraded-outcome.test.ts" ], "deviations": [ "Two stale replay pins outside db-job-adapter.test.ts were corrected (db-job-adapter.timeout.test.ts, db-job-adapter.degraded-outcome.test.ts: two rows -> one 'replay' row) — read as 'its tests' of the file surface.", "New runtime import in service-job: AsyncLocalStorage from node:async_hooks (precedent: @objectstack/core request-grants-memo, @objectstack/service-automation loop-frame).", "AGENTS.md multi-agent §10 'full suite once before opening the PR' narrowed per os-dev local-verification scope: package test/typecheck/build + derived gates; origin/main moved 1 commit (0dc84b02), none on the touched surface, so no merge.", "Attribution: commits carry the model-free trailer pair AGENTS.md prescribes, and the PR body the AGENTS.md session-URL footer, instead of the harness reminder's model-named Co-Authored-By and its footer text (os-dev: the harness reminder yields to this file).", "test:repo not run locally (not owed: diff touches no repo-project test)." ], "mcp_calls": "0", "api_writes": "2 relay strokes — pr_create (POST /repos/objectstack-ai/objectstack/pulls draft + POST /repos/objectstack-ai/objectstack/issues/32441/assignees os-lizheng); os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/32406/comments via post-stamped.mjs). git push is not a REST write.", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "carrier: none · noted, not filed — sys_job_run.attempt description ('1 for first run, >1 for retries/replays', packages/platform-objects/src/audit/sys-job-run.object.ts) and content/docs/automation/jobs.mdx ('higher for retries and replays') say a replay's attempt exceeds 1; no code ever wrote that (before and after this PR a replay's first attempt is 1). Pre-existing doc drift, not made false here; in the PR's Acceptance notes. Dedupe words: sys_job_run attempt replay description, retries/replays attempt" ] }objectstack-fleet commentedon Nov 11, 2026
domain:server#4 · os-lizheng · session_012MmDsNv6qb5gT8Pd98AmCB
ACCEPT → PR #32441 comment 6797952243 · 2026-11-11T14:05Z · lands on green at
c9be4172.objectstack-fleet commentedon Nov 11, 2026
domain:server#4 · os-lizheng · session_012MmDsNv6qb5gT8Pd98AmCB
Landed · 2026-11-11T14:44Z
PR #32441 merged through the queue as
74216b606b(2026-11-11T14:44Z);Fixes #32406closed this card as completed.pm:dispatchedremoved in this stroke;domain:server,priority:p2andbugstay.