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
runtime: two jobs of one name in one artifact share one job-service key, so the later one silently replaces the earlier and the replaced job never runs (no log line) #32411
Filing gate: ① This defect was measured by the #32355 dev (PR #32410, out-of-scope finding, class a; report 6792591092).
Where: in packages/runtime/src/app-artifact-handlers.ts, jobKeyFor returns the app's existing key for a second job of the same name. CronJobAdapter.schedule and IntervalJobAdapter.store (packages/services/service-job) cancel the existing job and replace it.
What happens:
os build accepts a preserve composition whose manifest-less input carries a job with the same name as the job of the body that carries the artifact's id (measured: exit 0).
At boot both jobs are scheduled under one (app, name) key, and the later one silently wins.
refuse at bind with a coded refusal naming both jobs; or
build-side, refuse the composition (os build / composeStacks). This is spec lane; report it if that is where it belongs.
Never keep one job silently.
Pins:
two same-name jobs are refused, naming both;
control: one job binds as today.
Ablate once.
Clause-②:no (narrowing) if a composition that booted is now refused. First-party census expected to be 0. Business reason: a scheduled pull or job silently never runs, and the operator gets no signal.
Dedupe (job service key collision, residual job same name dropped, jobKeyFor duplicate job name, scheduleAppArtifactJobs one key two jobs): none found.
Lane:domain:server (packages/runtimejobKeyFor and the job adapters). If the build-side refusal is the chosen fix, the composeStacks half is reported to domain:spec, as the card says.
Grade: p2. A job that never runs, with no log line, is serious when it happens, but it needs a same-name job inside one artifact's preserve composition. No first-party count is given.
Claim: PM loop round 5 · 2026-11-12T04:07Z
Session: session_01Utp6CbjqtQgKYD9snKe5g9 (card thread; seat session_01Utp6CaP1iy4RSEQbW6177G)
Account: os-bill
Branch: claude/project-thread-0unrso; measured platform exception to claude/issue-<n>-<slug>
Worktree: objectstack-issue-32411
Domain: domain:server
Seat: domain:server#2
File surface: packages/runtime/src/app-artifact-handlers.ts, its job tests (app-artifact-handlers*.test.ts), packages/services/service-job/** only if measured necessary, content/docs/** / skills/** sentences this PR makes false, one root .changeset/. ⛔ packages/spec/**, ui/**, docs/adr/**, .claude/**, AGENTS.md. (stop on breach)
Container & model: S/M, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: introduced with per-artifact job keys (jobKeyFor); residual-owner half closed by #32410; no bisect owed (keying gap, not a regression)
Thread-read: 6796478766
Premise: holds per the card's measurement (os build exit 0, one pull silently wins); step 0 re-measures on maind8a2f0d2 on both adapters and both owner arrangements before code. Seat preference: refuse at bind (runtime); a build-side refusal is spec lane and comes back as needs_decision.
Serial constraints cleared: #32410 merged, #32349 (PR #32468) merged.
Provenance: epic #31527 card routed to domain:server#2 by the epic seat (coordinator, 2026-11-12T02:23Z); the maintainer, verbatim 「server 车道暂时也只处理 p1 或是专题相关的 卡」, 2026-11-12T02:21Z, this project's chat.
{
"issue": 32411,
"status": "done",
"branch": "claude/project-thread-0unrso",
"pr": "https://github.com/objectstack-ai/objectstack/pull/32563",
"session": "session_01Utp6CbjqtQgKYD9snKe5g9",
"premise_still_valid": true,
"summary": "Measured on main d8a2f0d2 on real CronJobAdapter and IntervalJobAdapter (identical): every artifact with two enabled jobs of one name binds both under one job-service key and keeps the later; residual-not-body: survivor's run refused 409; residual-is-body: alpha_map runs, host_map never, no log; two bodies' body-form jobs and a single-package JSON listing a name twice: one silently dropped. composeStacks accepts A and C, and D reaches the binder with no build, so the refusal is at bind (seat ruling on step 0). scheduleAppArtifactJobs now counts enabled jobs by name (jobNameCollisions); a shared name schedules none of its jobs, lists each in the new AppArtifactJobScheduling.refused, logs one error per name with PackageScopedNameRefusedError (RESOURCE_CONFLICT 409, ambiguous-in-environment) naming every holder and each job's run form, and an earlier-scheduled job of the name is cancelled. Disabled twins are not counted; jobDeclaringPackages answers unchanged (its body walk extracted to bodyJobDeclarations). Clause-②: no (narrowing) per the seat's step-0 ruling; first-party census 0.",
"tests": "Red first at 0252ae53 on main: `vitest run src/app-artifact-handlers.job-pull.test.ts` Tests 11 failed | 43 passed (54), exactly the 11 new [#32411] cases. Green at 9d82ba29: job-pull + jobs + job-pull-run Test Files 3 passed, Tests 101 passed (101). Ablation once via scripts/ablation-replace.mjs under the verify lock: anchor `if (shared.length === 0) return refusals;` -> `>= 0` (x1->x0, blob f75b12fb3e20 -> b527ef0f59ba), Tests 11 failed | 43 passed (54), CONTROL pins green; restored blob == HEAD f75b12fb3e20, git diff HEAD empty; src imported relatively, no dist leg. Final head 431705c8: `pnpm --filter @objectstack/runtime test` Test Files 581 passed (581), Tests 9896 passed | 19 skipped; `pnpm --filter @objectstack/runtime typecheck` VERDICT command-exit 0.",
"gates": "Final head 431705c8 (merged origin/main 2359ceadc). dispatch-gates --commands: 97 derived, each run, exit codes captured before any pipe (/mnt/project-files/server-seat2-p2/card-32411/gates-ran-431705c8.txt); --ran: 97 accounted, 95 run, 2 NOT-MEASURED (exit 3 PREREQUISITE NOT MET: `pnpm --filter @objectstack/spec run check:skill-examples` needs built client SDK, `pnpm check:dual-build-cjs-loads` needs a full pnpm build; CI runs both). check-plugin-teardown-shape --self-test first exit 3 on a shallow clone, exit 0 after git fetch --unshallow. pnpm lint exit 0. PR check-runs: in_progress at report time.",
"dogfood_battery": "431705c8 · VERDICT command-exit 1 · Test Files 1 failed | 450 passed | 3 skipped (454) · Tests 1 failed | 3779 passed | 42 skipped (3822) · the one failure: test/package-commit-revert-path-package.dogfood.test.ts '[#29414] control: a writable package's edit commit restores its own row in place' — Test timed out in 5000ms (package commit revert, no job surface); re-run alone at the same head: Test Files 1 passed, Tests 12 passed (12), VERDICT command-exit 0. Read as a load timeout, not a consequence of this diff.",
"mcp_calls": "0",
"api_writes": "2 — fleet-write relay pr_create (run 43158205252, PR #32563, read-back identical); this os-dev-report comment via post-stamped.mjs",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: none · noted in PR acceptance notes, not filed: the install-local door's up-front refusal (collectJobsWithoutBody, 422) does not judge duplicate job names; such a package installs and the bind refusal logs at error. DbJobAdapter replace-by-name inferred by delegation, not measured separately."
],
"deviations": "Claim Clause-② `no`; PR and changeset carry `no (narrowing)` on the seat's step-0 ruling. Three existing #32071/#32355 pins moved from a run refusal or silent winner to a bind refusal (their jobDeclaringPackages assertions unchanged), and the #32355 fixtures moved to module scope. Serial dependency on #32567 (#32412) announced by the seat after the PR opened: merge main and adapt candidates to the residual holder kind once it lands.",
"files_changed": [
".changeset/32411-same-name-jobs-refused-at-bind.md",
"content/docs/automation/jobs.mdx",
"packages/runtime/src/app-artifact-handlers.job-pull.test.ts",
"packages/runtime/src/app-artifact-handlers.jobs.test.ts",
"packages/runtime/src/app-artifact-handlers.ts"
],
"line_budget": "495 changed lines (+386 / -109, 5 files) vs the 3000 human-merge threshold"
}
Activity
objectstack-fleet commentedon Nov 11, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage:
findingpromoted →domain:server· p2 ·pm:blocked· 2026-11-11T09:59ZBlocked-by: #32410
⛔ Not a claim.
domain:server(packages/runtimejobKeyForand the job adapters). If the build-side refusal is the chosen fix, thecomposeStackshalf is reported todomain:spec, as the card says.preservecomposition. No first-party count is given.Generated by Claude Code
objectstack-fleet commentedon Nov 11, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: unblocked →
pm:queue·domain:server· p2 · 2026-11-11T12:58Z⛔ Not a claim.
Generated by Claude Code
objectstack-fleet commentedon Nov 12, 2026
domain:server#2 · os-bill · session_01Utp6CaP1iy4RSEQbW6177G
Claim: PM loop round 5 · 2026-11-12T04:07Z
Session:
session_01Utp6CbjqtQgKYD9snKe5g9(card thread; seatsession_01Utp6CaP1iy4RSEQbW6177G)Account:
os-billBranch:
claude/project-thread-0unrso; measured platform exception toclaude/issue-<n>-<slug>Worktree:
objectstack-issue-32411Domain:
domain:serverSeat:
domain:server#2File surface:
packages/runtime/src/app-artifact-handlers.ts, its job tests (app-artifact-handlers*.test.ts),packages/services/service-job/**only if measured necessary,content/docs/**/skills/**sentences this PR makes false, one root.changeset/. ⛔packages/spec/**,ui/**,docs/adr/**,.claude/**,AGENTS.md. (stop on breach)Container & model:
S/M,mode:thread,model: default tier (dispatch-gates --tier: no path-derived mandate),effort: highClause-②: no
Metadata-protocol: none
Responsibility: introduced with per-artifact job keys (
jobKeyFor); residual-owner half closed by #32410; no bisect owed (keying gap, not a regression)Thread-read: 6796478766
Premise: holds per the card's measurement (
os buildexit 0, one pull silently wins); step 0 re-measures onmaind8a2f0d2on both adapters and both owner arrangements before code. Seat preference: refuse at bind (runtime); a build-side refusal is spec lane and comes back asneeds_decision.Serial constraints cleared:
#32410 merged,#32349 (PR #32468) merged.Provenance: epic #31527 card routed to
domain:server#2by the epic seat (coordinator, 2026-11-12T02:23Z); the maintainer, verbatim 「server 车道暂时也只处理 p1 或是专题相关的 卡」, 2026-11-12T02:21Z, this project's chat.objectstack-fleet commentedon Nov 12, 2026
domain:server#2 · os-bill · session_01Utp6CaP1iy4RSEQbW6177G
os-dev-report
{ "issue": 32411, "status": "done", "branch": "claude/project-thread-0unrso", "pr": "https://github.com/objectstack-ai/objectstack/pull/32563", "session": "session_01Utp6CbjqtQgKYD9snKe5g9", "premise_still_valid": true, "summary": "Measured on main d8a2f0d2 on real CronJobAdapter and IntervalJobAdapter (identical): every artifact with two enabled jobs of one name binds both under one job-service key and keeps the later; residual-not-body: survivor's run refused 409; residual-is-body: alpha_map runs, host_map never, no log; two bodies' body-form jobs and a single-package JSON listing a name twice: one silently dropped. composeStacks accepts A and C, and D reaches the binder with no build, so the refusal is at bind (seat ruling on step 0). scheduleAppArtifactJobs now counts enabled jobs by name (jobNameCollisions); a shared name schedules none of its jobs, lists each in the new AppArtifactJobScheduling.refused, logs one error per name with PackageScopedNameRefusedError (RESOURCE_CONFLICT 409, ambiguous-in-environment) naming every holder and each job's run form, and an earlier-scheduled job of the name is cancelled. Disabled twins are not counted; jobDeclaringPackages answers unchanged (its body walk extracted to bodyJobDeclarations). Clause-②: no (narrowing) per the seat's step-0 ruling; first-party census 0.", "tests": "Red first at 0252ae53 on main: `vitest run src/app-artifact-handlers.job-pull.test.ts` Tests 11 failed | 43 passed (54), exactly the 11 new [#32411] cases. Green at 9d82ba29: job-pull + jobs + job-pull-run Test Files 3 passed, Tests 101 passed (101). Ablation once via scripts/ablation-replace.mjs under the verify lock: anchor `if (shared.length === 0) return refusals;` -> `>= 0` (x1->x0, blob f75b12fb3e20 -> b527ef0f59ba), Tests 11 failed | 43 passed (54), CONTROL pins green; restored blob == HEAD f75b12fb3e20, git diff HEAD empty; src imported relatively, no dist leg. Final head 431705c8: `pnpm --filter @objectstack/runtime test` Test Files 581 passed (581), Tests 9896 passed | 19 skipped; `pnpm --filter @objectstack/runtime typecheck` VERDICT command-exit 0.", "gates": "Final head 431705c8 (merged origin/main 2359ceadc). dispatch-gates --commands: 97 derived, each run, exit codes captured before any pipe (/mnt/project-files/server-seat2-p2/card-32411/gates-ran-431705c8.txt); --ran: 97 accounted, 95 run, 2 NOT-MEASURED (exit 3 PREREQUISITE NOT MET: `pnpm --filter @objectstack/spec run check:skill-examples` needs built client SDK, `pnpm check:dual-build-cjs-loads` needs a full pnpm build; CI runs both). check-plugin-teardown-shape --self-test first exit 3 on a shallow clone, exit 0 after git fetch --unshallow. pnpm lint exit 0. PR check-runs: in_progress at report time.", "dogfood_battery": "431705c8 · VERDICT command-exit 1 · Test Files 1 failed | 450 passed | 3 skipped (454) · Tests 1 failed | 3779 passed | 42 skipped (3822) · the one failure: test/package-commit-revert-path-package.dogfood.test.ts '[#29414] control: a writable package's edit commit restores its own row in place' — Test timed out in 5000ms (package commit revert, no job surface); re-run alone at the same head: Test Files 1 passed, Tests 12 passed (12), VERDICT command-exit 0. Read as a load timeout, not a consequence of this diff.", "mcp_calls": "0", "api_writes": "2 — fleet-write relay pr_create (run 43158205252, PR #32563, read-back identical); this os-dev-report comment via post-stamped.mjs", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "carrier: none · noted in PR acceptance notes, not filed: the install-local door's up-front refusal (collectJobsWithoutBody, 422) does not judge duplicate job names; such a package installs and the bind refusal logs at error. DbJobAdapter replace-by-name inferred by delegation, not measured separately." ], "deviations": "Claim Clause-② `no`; PR and changeset carry `no (narrowing)` on the seat's step-0 ruling. Three existing #32071/#32355 pins moved from a run refusal or silent winner to a bind refusal (their jobDeclaringPackages assertions unchanged), and the #32355 fixtures moved to module scope. Serial dependency on #32567 (#32412) announced by the seat after the PR opened: merge main and adapt candidates to the residual holder kind once it lands.", "files_changed": [ ".changeset/32411-same-name-jobs-refused-at-bind.md", "content/docs/automation/jobs.mdx", "packages/runtime/src/app-artifact-handlers.job-pull.test.ts", "packages/runtime/src/app-artifact-handlers.jobs.test.ts", "packages/runtime/src/app-artifact-handlers.ts" ], "line_budget": "495 changed lines (+386 / -109, 5 files) vs the 3000 human-merge threshold" }Generated by Claude Code
objectstack-fleet commentedon Nov 12, 2026
domain:server#2 · os-bill · session_01Utp6CaP1iy4RSEQbW6177G
ACCEPT → PR #32563 comment 6818147506
objectstack-fleet commentedon Nov 12, 2026
domain:server#2 · os-bill · session_01Utp6CaP1iy4RSEQbW6177G
Landed — PR #32563
1a17e96a2115fe5cf946639a1bdcd4004487acb0Fixes #32411State:
pm:dispatchedremoved; card closed. Follow-up: #32572 (install-local answers 200 for twins). Nothing else is left on this card.