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
core + runtime: a residual-twin job refusal (ambiguous-in-environment) can carry one candidate; no PackageScopedHolder kind names a top-level job outside every package (ADR-0150 D3) #32412
Child of the ADR-0150 epic #31527. This card carries S1 and N1 of the epic red-line read 6793085339 on PR #32410 (#32355).
Where. After #32410, a residual top-level job that shares its name with one body's job is refused 409 RESOURCE_CONFLICTambiguous-in-environment. The refusal reuses #32294's PackageScopedRefusal shape: candidates of kind: 'package'.
What is wrong:
S1. When no body carries the artifact's id, candidates holds only the declaring body. The residual is named in the message alone, so a consumer reading candidates sees an "ambiguous" refusal with one package. No PackageScopedHolder kind spells "top level, no package".
N1. When the residual's owner is another body, that owner appears in candidates as a plain package holder, although its body declares no such job.
Reach today is limited to the job run's failure record. No door answers this refusal to a client.
Do:
Decide how a top-level job outside every package is named as a holder. This is either a new holder kind in @objectstack/core's PackageScopedHolder (ADR vocabulary, so the ADR-0150 amendment card records it) or an existing kind if one fits. Read the doors' details.candidates consumers before choosing.
Make the residual-twin refusal list both holders in candidates, each with the right kind.
Pin it with the two arrangements, with and without a body carrying the artifact's id, and ablate once.
Serial: after #32410. Clause-②: the claimer declares it. A new holder kind is a public-surface widening of @objectstack/core. Business reason: a machine reader of the refusal cannot tell what the two holders are, so a UI or agent offering "choose a package" offers one option.
Grade: p2. Today the reach is only the job run's failure record: no door answers this refusal to a client. If the chosen fix adds a PackageScopedHolder kind to @objectstack/core, the claimer declares the public-surface widening, and the ADR-0150 amendment records the vocabulary.
Claim · #32412 · 2026-11-12T02:26Z (second epic seat epic:#31527#2, seated by the coordinator after the maintainer's 「再开一个座位能进一步提速吗」「太慢了」, 2026-11-12T02:17Z/02:19Z)
Assignee os-bill, labels pm:queue → pm:dispatched, in this same write.
Claim: epic seat 2 round 1 · #32412 (residual-twin job refusal: both holders in candidates, top-level job holder kind)
Session: session_01Utp6CZXK1owt8RkS773Prv
Account: os-bill
Branch: claude/project-thread-2dl9jd
Worktree: objectstack-issue-32412
Domain: domain:server
Seat: epic:#31527#2
File surface: packages/runtime/src/app-artifact-handlers.ts (the residual-twin refusal from #32410), its job-pull tests; @objectstack/corePackageScopedHolder only if no existing kind fits (a new kind is reported to the seat before it is built); changeset. Stop on breach; explain in the report.
Container & model: M, mode:thread, model: default tier, effort: high
Clause-②: no (the dev declares the final arm; a new holder kind is a public-surface widening)
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: 6796494012
Premise: holds (blocker #32410 merged; triage 6796494012)
Serial constraints cleared: sibling #32411 (same file, server seat A) — whichever lands second merges main; seat 1's chain shares packages/core/src/package-scoped-resolution.ts only if the dev edits it.
Seat ruling · #32412 · holder kind for a top-level job outside every package · 2026-11-12T02:28Z
⛔ Not a maintainer decision: a new export value with a same-word, same-concept precedent is seat discretion with a veto window (pm-dispatch 〈升级与决策〉).
The dev's step 0 (card thread session_01Utp6CZXK1owt8RkS773Prv) measured that none of the three existing kinds fits:
environment is an environment-written, package-less item (D1 bucket 4, the _env qualifier); a residual is artifact code. No.
platform is a plugin or host registration reachable by all; marketplace-installed artifacts have residuals too. No.
package { packageId: appId } has no package to address when no body carries the artifact's id, and is N1 itself when one does. No.
Ruled: A.@objectstack/corePackageScopedHolder gains { kind: 'residual'; ownerId: string }, described as the stack's top-level items outside every package body. Precedent, same word and concept: ADR-0130 D4 (the residual rule), metadata UnclaimedTopLevel.ownerId, ObjectQL recordResidualOwner(ownerId), ADR-0150 D2 / #31720 ("the residual as one holder of the stack's namespace").
Conditions:
S1 and N1 both become [package A, residual(app)]; the one-holder case (the owner is the only declaring body) is unchanged and pinned unchanged.
Clause-②: yes: the core union widens; N1's candidate moves FROM package B TO residual(app) and is declared FROM → TO in the PR body.
Every consumer of details.candidates[].kind is listed in the PR body with its handling of the new kind (no exhaustive switch left unhandled; typecheck proves it).
The package-scoped-resolution.ts hunk stays small and is reported (seat 1's chain shares the file).
ADR-0150's vocabulary is not edited in this PR (Tier H). The PR's Acceptance notes carry carrier: for the ADR-0150 amendment line naming the new kind; the seat routes it.
{
"issue": 32412,
"status": "done",
"branch": "claude/project-thread-2dl9jd",
"pr": "https://github.com/objectstack-ai/objectstack/pull/32567",
"head": "768c808c8",
"session": "session_01Utp6CZXK1owt8RkS773Prv",
"premise_still_valid": true,
"summary": "Per the seat's kind ruling A (Claim 6812549503): @objectstack/core PackageScopedHolder gains { kind: 'residual'; ownerId: string } (a stack's residual, ADR-0130 D4), described as `the top level of stack \"<ownerId>\" outside every package body (its residual)`; a resolution's holder is typed Exclude<..., residual> since the rule never answers it. runtime jobNameRefusal's residual arm now lists [...declaring bodies, residual(appId)]: S1 [package A] -> [package A, residual(app)]; N1 [package A, package B] -> [package A, residual(B)]; owner among several declarers adds residual(B) after the bodies. One-holder answer and #32071 two-body refusal unchanged. cli stored-package-references candidateOf gains a residual branch (compile error before it).",
"tests": "Red on main 89d815c5 + pins: runtime job-pull 4 failed | 44 passed, core 1 failed | 28 passed (control green). Green: 48/48, 29/29. Ablation via scripts/ablation-replace.mjs (old candidates line back): 5 failed | 43 passed, restored, git diff HEAD empty. #32355 JSON-loaded pin's two candidates assertions flipped (FROM -> TO in PR body).",
"gates": "On b9722a774 after a full non-docs build (119/119): dispatch-gates 73 derived, 73/73 exit 0; --ran 73 run, 0 NOT-MEASURED, 0 UNRUN. pnpm lint 0; core/runtime/cli test 0; core/runtime typecheck 0; cli typecheck 0 with NODE_OPTIONS heap 7168 (default heap OOM in check:test-typecheck). tsc --noEmit 0 on all 17 packages importing package-scoped symbols. Final merge 768c808c8 (9 main commits, none touching these files): job-pull 48/48, core file 29/29. Records in /mnt/project-files/epic-31527-seat2/card-32412/.",
"dogfood_battery": "not owed: Clause-② yes (widening), no BREAKING",
"line_budget": "175+/41- over 6 files",
"files_changed": [
".changeset/32412-residual-holder-kind.md",
"packages/cli/src/utils/stored-package-references.ts",
"packages/core/src/package-scoped-resolution.ts",
"packages/core/src/package-scoped-resolution.test.ts",
"packages/runtime/src/app-artifact-handlers.ts",
"packages/runtime/src/app-artifact-handlers.job-pull.test.ts"
],
"deviations": [
"Beyond the runtime + core surface: core's resolved holder type narrowed (Exclude residual) and cli candidateOf branch, both forced by typecheck (service-automation and cli did not compile against the widened union). Changeset patch for core, runtime and cli.",
"#32355 fixtures hoisted to module scope in job-pull.test.ts so both describes share them (unchanged content)."
],
"mcp_calls": "0 GitHub MCP calls; subscribe_pr_activity x1 (session tooling, not a GitHub write)",
"api_writes": "relay pr_create x1 (run 43162791428, body read back identical 11230 bytes, assignees none); this os-dev-report through post-stamped; git pushes to the designated branch",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"Sibling #32411's draft PR #32563 (head 431705c8): its new bind refusal jobNameCollisions builds residual candidates the old way (owner as package, or message only), so it needs the residual kind too; and merge-tree shows one textual conflict in app-artifact-handlers.job-pull.test.ts (both hoist the same fixtures). With both twins enabled #32563 schedules neither, so this PR's real-CronJobAdapter pin moves for whichever lands second. carrier: the second lander's merge of main (seat to route)."
]
}
Activity
objectstack-fleet commentedon Nov 11, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage:
findingpromoted →domain:server· p2 ·pm:blocked· 2026-11-11T10:53ZBlocked-by: #32410
⛔ Not a claim.
PackageScopedHolderkind to@objectstack/core, the claimer declares the public-surface widening, and the ADR-0150 amendment records the vocabulary.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:59Z⛔ Not a claim.
Generated by Claude Code
objectstack-fleet commentedon Nov 12, 2026
epic:#31527#2 · os-bill · session_01Utp6Ca5UHVBDE3DqQXgJsW
Claim · #32412 · 2026-11-12T02:26Z (second epic seat
epic:#31527#2, seated by the coordinator after the maintainer's 「再开一个座位能进一步提速吗」「太慢了」, 2026-11-12T02:17Z/02:19Z)Assignee
os-bill, labelspm:queue→pm:dispatched, in this same write.Claim: epic seat 2 round 1 · #32412 (residual-twin job refusal: both holders in
candidates, top-level job holder kind)Session:
session_01Utp6CZXK1owt8RkS773PrvAccount:
os-billBranch:
claude/project-thread-2dl9jdWorktree:
objectstack-issue-32412Domain:
domain:serverSeat:
epic:#31527#2File surface:
packages/runtime/src/app-artifact-handlers.ts(the residual-twin refusal from #32410), its job-pull tests;@objectstack/corePackageScopedHolderonly if no existing kind fits (a new kind is reported to the seat before it is built); changeset. Stop on breach; explain in the report.Container & model:
M,mode:thread,model: default tier,effort: highClause-②: no (the dev declares the final arm; a new holder kind is a public-surface widening)
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: 6796494012
Premise: holds (blocker #32410 merged; triage 6796494012)
Serial constraints cleared: sibling #32411 (same file, server seat A) — whichever lands second merges
main; seat 1's chain sharespackages/core/src/package-scoped-resolution.tsonly if the dev edits it.Generated by Claude Code
objectstack-fleet commentedon Nov 12, 2026
epic:#31527#2 · os-bill · session_01Utp6Ca5UHVBDE3DqQXgJsW
Seat ruling · #32412 · holder kind for a top-level job outside every package · 2026-11-12T02:28Z
⛔ Not a maintainer decision: a new export value with a same-word, same-concept precedent is seat discretion with a veto window (pm-dispatch 〈升级与决策〉).
The dev's step 0 (card thread
session_01Utp6CZXK1owt8RkS773Prv) measured that none of the three existing kinds fits:environmentis an environment-written, package-less item (D1 bucket 4, the_envqualifier); a residual is artifact code. No.platformis a plugin or host registration reachable by all; marketplace-installed artifacts have residuals too. No.package { packageId: appId }has no package to address when no body carries the artifact's id, and is N1 itself when one does. No.Ruled: A.
@objectstack/corePackageScopedHoldergains{ kind: 'residual'; ownerId: string }, described as the stack's top-level items outside every package body. Precedent, same word and concept: ADR-0130 D4 (the residual rule), metadataUnclaimedTopLevel.ownerId, ObjectQLrecordResidualOwner(ownerId), ADR-0150 D2 / #31720 ("the residual as one holder of the stack's namespace").Conditions:
[package A, residual(app)]; the one-holder case (the owner is the only declaring body) is unchanged and pinned unchanged.Clause-②: yes: the core union widens; N1's candidate moves FROMpackage BTOresidual(app)and is declared FROM → TO in the PR body.details.candidates[].kindis listed in the PR body with its handling of the new kind (no exhaustive switch left unhandled; typecheck proves it).package-scoped-resolution.tshunk stays small and is reported (seat 1's chain shares the file).carrier:for the ADR-0150 amendment line naming the new kind; the seat routes it.Generated by Claude Code
test(runtime): the #32071 control meets the two-body name by a fresh …
fix(core,runtime): the residual-twin job refusal names the residual a…
fix(core): a resolution's holder is never a residual — the rule answe…
fix(cli): the stored-reference report spells a residual candidate (#3…
chore(changeset): #32412 residual holder kind
objectstack-fleet commentedon Nov 12, 2026
epic:#31527#2 · os-bill · session_01Utp6CZXK1owt8RkS773Prv
os-dev-report
{ "issue": 32412, "status": "done", "branch": "claude/project-thread-2dl9jd", "pr": "https://github.com/objectstack-ai/objectstack/pull/32567", "head": "768c808c8", "session": "session_01Utp6CZXK1owt8RkS773Prv", "premise_still_valid": true, "summary": "Per the seat's kind ruling A (Claim 6812549503): @objectstack/core PackageScopedHolder gains { kind: 'residual'; ownerId: string } (a stack's residual, ADR-0130 D4), described as `the top level of stack \"<ownerId>\" outside every package body (its residual)`; a resolution's holder is typed Exclude<..., residual> since the rule never answers it. runtime jobNameRefusal's residual arm now lists [...declaring bodies, residual(appId)]: S1 [package A] -> [package A, residual(app)]; N1 [package A, package B] -> [package A, residual(B)]; owner among several declarers adds residual(B) after the bodies. One-holder answer and #32071 two-body refusal unchanged. cli stored-package-references candidateOf gains a residual branch (compile error before it).", "tests": "Red on main 89d815c5 + pins: runtime job-pull 4 failed | 44 passed, core 1 failed | 28 passed (control green). Green: 48/48, 29/29. Ablation via scripts/ablation-replace.mjs (old candidates line back): 5 failed | 43 passed, restored, git diff HEAD empty. #32355 JSON-loaded pin's two candidates assertions flipped (FROM -> TO in PR body).", "gates": "On b9722a774 after a full non-docs build (119/119): dispatch-gates 73 derived, 73/73 exit 0; --ran 73 run, 0 NOT-MEASURED, 0 UNRUN. pnpm lint 0; core/runtime/cli test 0; core/runtime typecheck 0; cli typecheck 0 with NODE_OPTIONS heap 7168 (default heap OOM in check:test-typecheck). tsc --noEmit 0 on all 17 packages importing package-scoped symbols. Final merge 768c808c8 (9 main commits, none touching these files): job-pull 48/48, core file 29/29. Records in /mnt/project-files/epic-31527-seat2/card-32412/.", "dogfood_battery": "not owed: Clause-② yes (widening), no BREAKING", "line_budget": "175+/41- over 6 files", "files_changed": [ ".changeset/32412-residual-holder-kind.md", "packages/cli/src/utils/stored-package-references.ts", "packages/core/src/package-scoped-resolution.ts", "packages/core/src/package-scoped-resolution.test.ts", "packages/runtime/src/app-artifact-handlers.ts", "packages/runtime/src/app-artifact-handlers.job-pull.test.ts" ], "deviations": [ "Beyond the runtime + core surface: core's resolved holder type narrowed (Exclude residual) and cli candidateOf branch, both forced by typecheck (service-automation and cli did not compile against the widened union). Changeset patch for core, runtime and cli.", "#32355 fixtures hoisted to module scope in job-pull.test.ts so both describes share them (unchanged content)." ], "mcp_calls": "0 GitHub MCP calls; subscribe_pr_activity x1 (session tooling, not a GitHub write)", "api_writes": "relay pr_create x1 (run 43162791428, body read back identical 11230 bytes, assignees none); this os-dev-report through post-stamped; git pushes to the designated branch", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "Sibling #32411's draft PR #32563 (head 431705c8): its new bind refusal jobNameCollisions builds residual candidates the old way (owner as package, or message only), so it needs the residual kind too; and merge-tree shows one textual conflict in app-artifact-handlers.job-pull.test.ts (both hoist the same fixtures). With both twins enabled #32563 schedules neither, so this PR's real-CronJobAdapter pin moves for whichever lands second. carrier: the second lander's merge of main (seat to route)." ] }Report taken 2026-11-12T05:40Z.
Generated by Claude Code
objectstack-fleet commentedon Nov 12, 2026
epic:#31527#2 · os-bill · session_01Utp6Ca5UHVBDE3DqQXgJsW
Landed · #32412 · 2026-11-12T06:39Z
3452dec219State change:
pm:dispatchedremoved now that the card is closed.Generated by Claude Code