Repository navigation
install-local accepts a package whose job pull cannot bind: it answers 200 and the job is never scheduled. Refuse it at the door, as a body job that does not bind is refused #21672
Description
Activity
objectstack-fleet commented
on Oct 4, 2026 ContributorAuthorMore actionsTriage: first grade —
bug·priority:p3·domain:cli·area:devpath·pm:blocked. install-local refuses apulljob that cannot bind, as one clause in the existing unrunnable judgementTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-04T03:01Z. ⛔ Not a claim, ⛔ not a dispatch.Blocked-by: #20281
Why blocked. The refusal reads
judgeJobPullfrom PR #21668 (#20281 stage 3), which is not onmainyet.Why p3. There are zero pull authors today, and the authoring doors already refuse an undeclared mapping. Only a hand-edited package reaches this door.
Routing:
packages/cloud-connectionwith the runtime half, sodomain:cli, as #21585 was.Direction: the card's own, confirmed.
UnrunnableCode.jobsanddescribeUnrunnablegain a pull clause, read from the samejudgeJobPullthe binder uses. The install answers 422 with that clause, and rehydrate withholds the job, as for a body that does not bind.- ⛔ No second judge.
Generated by Claude Code
- addedarea:devpathThe road — create, dev, verify, publish/install, connect an agent, iterateThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingSomething isn't working
on Oct 4, 2026 objectstack-fleet commented
on Oct 4, 2026 ContributorAuthorMore actionsTriage: unlocked. #20281 stage 3 landed through PR #21668, so
pm:blocked→pm:queue.judgeJobPullis onmainTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-04T03:56Z. ⛔ Not a claim, ⛔ not a dispatch.- Measured now: PR feat(spec,runtime,service-automation): a job pulls a mapping by declaration (pull: { mapping }) and runs as its declared organization (#20281 stage 3) #21668 merged at 2026-10-04T03:16Z as
909229e976.packages/runtime/src/app-artifact-handlers.tscarriesjudgeJobPull(docblocks about:81and:369). - The direction in
5975994778stands: one pull clause inUnrunnableCode.jobs/describeUnrunnable, read from that same judge, which answers 422 at install and withholds the job on rehydrate. - The grade is unchanged:
bug· p3 ·domain:cli·area:devpath.
Generated by Claude Code
- Measured now: PR feat(spec,runtime,service-automation): a job pulls a mapping by declaration (pull: { mapping }) and runs as its declared organization (#20281 stage 3) #21668 merged at 2026-10-04T03:16Z as
objectstack-fleet commented
on Oct 4, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 (unblocked by PR #21668,
909229e976; this is the seat's only active dispatch, the serial posture)
Session:session_016GiHYRmLSNWTfbX9gVQkpz
Account:os-bill(the seat's linked user asget_meanswers it; the card's assignee)
Branch:claude/issue-21672-install-local-pull-refusal
Worktree:objectstack-issue-21672
Domain:domain:cli
Seat:domain:cli#1
File surface, per triage5975994778and5976336256(one pull clause in the existing unrunnable judgement; ⛔ no second judge):packages/runtime/src/app-artifact-handlers.ts: the job collector (collectJobsWithoutBody, the landed name, ⛔ not renamed: a liveness ledger anchor cites it) names an enabledpulljob that does not bind, with the refusaljudgeJobPullgives, the judge the binder already uses.packages/cloud-connection/src/marketplace-install-local-plugin.ts:UnrunnableCode.jobsgains the pull refusal, anddescribeUnrunnablegains its clause. Install answers422with it, and rehydrate withholds the job and warns, as for a body that does not bind.- Pins, at the public door (
os package install):- a pull job naming an undeclared mapping is refused with
422and the clause; - a pull job without a
connectorSourceis refused the same way; - a pull job naming a declared mapping installs and is scheduled;
- rehydrate of an entry that an earlier build persisted withholds it.
- a pull job naming an undeclared mapping is refused with
.changeset/.
⛔ No
packages/specedit (JobSchema.pullis #20281's), ⛔ noservice-automationedit, and ⛔ no second pull judgement: the door, the collector and the binder read onejudgeJobPull. (stop on breach; explain in the report)
Container & model:M,mode:subagent,model: default (opus)build. The path globs mandate nothing;dispatch-gates --tiersays so, derived. The contract review is owed at the ceiling,CONTRACT_REVIEW_TIER. reason:Clause-②: yes (narrowing)(install-local refuses a package it accepted).
Clause-②: yes (narrowing). The install-local door refuses a package it answered200before: the!, the ADR-0087 marker andminor, in PR #21615's shape.
Thread-read: 5976336256
Serial constraints, other open PRs read 2026-10-04T04:07Z onorigin/maineed2dee481:- No open PR touches
app-artifact-handlers.tsorpackages/cloud-connection/src/. - This seat's other card, [Decision] security(runtime): may an app-authored body still READ the stored-metadata family's tables, served projected, or is that refused too (#21454 option C) #21594, is
needs-user-decisionwith no dev running; its PR fix(runtime)!: an app-authored body may not read the stored-metadata tables; it reaches them through the metadata API only (#21594) #21660 touchespackages/runtime/src/stored-metadata-*,sandbox/body-runner.tsandaction-execution.tsonly.
domain:cliseat ·session_016GiHYRmLSNWTfbX9gVQkpz· 2026-10-04T04:07Z
Generated by Claude Code
objectstack-fleet commented
on Oct 4, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 21672,
"status": "done",
"branch": "claude/issue-21672-install-local-pull-refusal",
"pr": "#21683",
"session": "session_016GiHYRmLSNWTfbX9gVQkpz — a subagent run, so this is the parent PM session id the harness stamps (Claude-Session trailer)",
"premise_still_valid": true,
"summary": "Implemented as triaged, in PR #21615's shape.collectJobsWithoutBodykeeps its name, since the liveness ledger anchors it. It now judges a job that declarespullby callingjudgeJobPull(job, bundle), the function the binder schedules by, and names an unbindable one with a new optional field,JobWithoutBody.pullRefusal.UnrunnableCode.jobsanddescribeUnrunnablegain the pull clause. install-local now answers 422VALIDATION_ERROR(the existing code) and registers, persists and schedules nothing. A disabled pull job and a pull job naming a declared mapping install as before. A1 reproduced at the public door on eed2dee. A2: one judge, pinned by an equivalence test. A4 already held on main, so it is pinned and no rehydrate code was added. A5: one leg, red in the expected direction. One sentence in the unreleased #20281 changeset became false and was corrected (open_questions), soCheck Changesetstays red by design. That context is not required.",
"tests": [
"A1, before the fix, on unmodified runtime and cloud-connection dist built at eed2dee:pnpm --filter @objectstack/cli exec vitest run --project integration --maxWorkers=2 test/package-install-local-jobs-pull.integration.test.tsgave 3 failed and 5 passed (8). For both the undeclared mapping and the mapping with no connectorSource, the CLI printedPackage installed into the running kernel(exit 0). The server warnedjob pull does not bind — the job is NOT scheduled: pull.mapping: this artifact declares no mapping 'orders_pul' …with{\"appId\":\"com.example.pullmissing\",\"job\":\"pull_missing_orders\"}, and the same warn withmapping 'orders_pull' declares no connectorSourcefor pull_nosource_orders. Both refused packages were in the ledger. The rehydrate pins (withheld, warn names the job) were already green.",
"After the fix, everything at 413869d. runtime:typecheckgreen, including check:test-typecheck.vitest run --project local --maxWorkers=2: 320 files, 4555 passed, 19 skipped.",
"cloud-connection:typecheckgreen, including tsconfig.test.json. Full suite: 36 files, 443 passed. The jobs unit file alone: 17 passed.",
"cli:typecheckgreen, andtsc -p tsconfig.test.json --listFilesOnlycounts the new integration file (1 hit).vitest run --project unit --maxWorkers=2: 257 files, 3771 passed.",
"install-local integration pins on built packages (--project integration), on package-install-local-{jobs-pull,jobs,jobs-shared-name,hooks,handlers,boot-steps,uninstall-cleanups}: 7 files, 76 passed. The new file is 8 of 8. Its control run records sys_job_run statusfailedwithMapping \"orders_pull\" pulls from connector \"orders_api\", which is not registered …: the run reached the pull door. That was read once through a temporary print, restored by trap (blob b4c3387a69b6 == HEAD, diff empty).",
"pnpm --filter @objectstack/spec exec vitest run --project repo scripts/liveness/evidence.test.ts: 42 passed. Agit grepof collectJobsWithoutBody, JobWithoutBody, judgeJobPull, describeUnrunnable, UnrunnableCode and pullRefusal across packages/spec/liveness/** and .ledger. finds only job.json:85 (judgeJobPull) and job.json:122 (collectJobsWithoutBody), both unchanged.",
"Fullpnpm lintat 413869d: VERDICT command-exit 0, no findings.",
"A5 ablation, one leg, through scripts/ablation-replace.mjs WRAP mode on committed 413869d. Anchor:if (judged.binds) continue;plus a newline pluspullRefusal = judged.refusal;(the collector's pull leg). The mutation adds|| String('ABLATED_21672_PULL') !== ''to the condition: anchor 1 -> 0, blob d207bdb16564 -> f4e6ffa8924b. The runtime was rebuilt, and ablation-dist-preflight found the marker in dist/index.js and index.cjs. Red: runtime collector unit 2/21. cloud-connection jobs unit 3/17 (both refusals and the one-answer pin). CLI integration 3/8 (both refusals, the CLI printed Package installed, and the ledger pin). Green: the declared-mapping control, the disabled job and the rehydrate pins, in all three layers. Restore: blob d207bdb16564 == HEAD,git diff HEADempty. Rebuilt, then--absent: marker absent from all 6 built files, tree clean. Direction: red, as expected."
],
"gates": {
"derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, no paths: 9 paths vs merge base eed2dee, HEAD 413869d, 93 commands",
"ran": "93 run. 92 exit 0. 1 exits 1 by design:node scripts/check-empty-changeset.mjs --base origin/main, the DELIBERATE CORRECTION class for .changeset/20281-job-pull-organization.md.",
"reconciliation": "--ranwith the record:Run reconciliation — 93 derived, 93 run, 0 NOT-MEASURED, 0 UNRUN.",
"reruns": "check:skill-examples (line 47) and check:dual-build-cjs-loads (line 65) first exited 3, PREREQUISITE NOT MET, because packages outside this closure had no dist. Both exited 0 on rerun once client-react, studio, connector-slack, embedder-openai, knowledge-memory, knowledge-ragflow, organizations and service-cluster-redis were built.",
"ci": "Read once at report time: the required contexts (Lint & Repo Gates, TypeScript type-check lanes, Test Core 1-6, Dogfood Regression Gate 1-3, Build Core, Temporal Conformance) were in_progress. Governed Surface Queue Guard was success. Check Changeset was in_progress, and will end red by design (above)."
},
"line_budget": "n/a",
"deviations": [
"Edited .changeset/20281-job-pull-organization.md, PR #21668's pending release note, outside the expected landing. One sentence changed, "collectJobsWithoutBodyno longer names apulljob, soos package installdoes not refuse one", to "collectJobsWithoutBodydoes not name apulljob that binds, soos package installinstalls one", because this change makes the original false in the same release. check-empty-changeset reds on it. Its own text prescribes keeping the correction and confirming it in writing on the PR, and never skip-changeset. Check Changeset is not a required context. Confirmation is asked in open_questions and on PR #21683.",
"A5 ablated the collector's pull leg (runtime) rather than describeUnrunnable's sentence. The door's acceptance condition has no pull-specific term (unrunnable.jobs.length > 0), so ablating the sentence alone would keep the 422 and change only prose. The collector's pull leg is the door's pull judgement.",
"The first ablation invocation was refused by ablation-replace before anything ran. Its replacement contained the anchor, so the anchor count stayed 1 -> 1. The tool restored the file (blob d207bdb16564 == HEAD, diff empty), and no reading was taken from it. The second invocation used a two-line anchor.",
"Attribution: the harness reminder asked for a model-named Co-Authored-By trailer and a "Generated with" PR footer pair. Commits carry the model-free pair (Co-Authored-By: Claude,Claude-Session:), and the PR body carries the session-URL footer, per AGENTS.md and the dispatch."
],
"files_changed": [
".changeset/21672-install-local-pull-refusal.md (new)",
".changeset/20281-job-pull-organization.md (one sentence; see deviations)",
"content/docs/automation/jobs.mdx",
"packages/runtime/src/app-artifact-handlers.ts",
"packages/runtime/src/index.ts (comment only)",
"packages/runtime/src/app-artifact-handlers.job-pull.test.ts",
"packages/cloud-connection/src/marketplace-install-local-plugin.ts",
"packages/cloud-connection/src/marketplace-install-local-jobs.test.ts",
"packages/cli/test/package-install-local-jobs-pull.integration.test.ts (new)"
],
"mcp_calls": "0 — no MCP GitHub tool was called. Reads went throughgh apisingle-card and single-PR REST reads.",
"api_writes": "3 — each one repository_dispatch to the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches), executed as objectstack-fleet[bot]: (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls, giving PR #21683 as a draft (relay run 37179309250, body read back 12452/12452 bytes identical); (2) assign, POST /repos//issues/21683/assignees with os-bill, through label-write.mjs (run 37179344846, read back matches); (3) comment, POST /repos//issues/21672/comments, this os-dev-report.git pushto the branch is not REST and is not counted.",
"open_questions": [
{
"question": "Confirm the correction to the pending #20281 release note (.changeset/20281-job-pull-organization.md). This PR makes its sentence "collectJobsWithoutBodyno longer names apulljob, soos package installdoes not refuse one" false. It now reads "collectJobsWithoutBodydoes not name apulljob that binds, soos package installinstalls one". check-empty-changeset asks for this confirmation in writing on the PR, and stays red until a person merges over it.",
"options": [
"A: keep the correction. Confirm it in writing on PR #21683. Check Changeset (not required) stays red, and the approver merges over it.",
"B: restore the note from the base. The same release would then carry "os package install does not refuse one" (a pull job) beside this PR's "refuses a package whose enabled job declares a pull that does not bind"."
],
"recommendation": "A. Real need: an upgrading agent greps CHANGELOG.md, and under B it reads a contradiction about the same door in one version. Long term: one true release note, and the gate names this exact class DELIBERATE CORRECTION. Preventing AI mistakes: B leaves the false sentence ("does not refuse") as the text an AI author reads first. Startup scope: A adds no surface and no gate, only a one-sentence edit."
}
],
"out_of_scope_findings": [
"carrier: 承接者:无 (none named) · noted, not filed. content/docs/references/system/job.mdx is generated from JobSchema.body's describe in packages/spec. It says os package install "refuses an enabled job with nobody(apulljob excepted: it is data too)". That stays literally true, because the exception is from the no-body refusal, but it no longer says an unbindable pull is refused. packages/spec is this card's stay-out. Recorded in the PR's Acceptance notes.",
"carrier: 承接者:无 (none named) · noted, not filed. Version skew: a newer @objectstack/runtime behind an older @objectstack/cloud-connection would describe an unbindable pull job with the no-body clause. That is the wrong remedy text, though still a 422. The two packages are in onefixedrelease group (.changeset/config.json), and the door already says to upgrade them together. Recorded in the PR's Acceptance notes."
]
}
Generated by Claude Code
objectstack-fleet commented
on Oct 4, 2026 ContributorAuthorMore actionsACCEPT — PR #21683 at
413869dfe1, pending its contract reviewdomain:cliseat ·session_016GiHYRmLSNWTfbX9gVQkpz· review of record, read on GitHub 2026-10-04T05:20ZWhat holds, read from the diff and the report (the comment before this one):
- A1 reach: reproduced at the public door on
eed2dee481. An undeclared mapping and a mapping with noconnectorSourceeach installed with exit 0 and were never scheduled. - A2, one judge.
collectJobsWithoutBodykeeps its landed name, so the ledger anchorjob.json:122is untouched. It callsjudgeJobPull(job, bundle), the function the binder schedules by, and names an unbindable job with a new optionalJobWithoutBody.pullRefusal. An equivalence pin holds the door and the binder to one answer. - A3, the door.
UnrunnableCode.jobsanddescribeUnrunnablegain the pull clause, which answers422 VALIDATION_ERRORwith the existing code. Nothing is registered, persisted or scheduled. A disabled pull job, and a pull job naming a declared mapping with aconnectorSource, install as before. - A4, rehydrate: it already held on
main, so it is pinned and no code was added. - A5, ablation: the collector's pull leg. It is red in three layers and green on the controls, with a blob-proven restore and a dist preflight.
- Ablating the sentence alone would have changed only prose, because the door's acceptance condition is
jobs.length > 0. So the right leg was taken.
- Ablating the sentence alone would have changed only prose, because the door's acceptance condition is
- Gates and tests: 93 of 93 derived gates ran; 92 exit 0, and
check-empty-changesetexits 1 by design (below). Fullpnpm lintexits 0.- Suites: runtime 4555 and cloud-connection 443 in full, plus the CLI unit project.
- Seven install-local integration files on built packages, 76 tests.
- Liveness evidence 42 of 42.
Deviation: the pending #20281 release note was corrected. Confirmed by this seat (the open question, answered A). PR #21668 put this sentence in
.changeset/20281-job-pull-organization.md: "collectJobsWithoutBodyno longer names apulljob, soos package installdoes not refuse one." This PR makes it false in the same release.- The rewrite: "does not name a
pulljob that binds, soos package installinstalls one". It changes nothing else. - Why not restore it: restoring the base sentence would republish a false sentence about the same door in the same version.
- The gate:
check-empty-changeset's DELIBERATE CORRECTION class says to say so on the PR and get it confirmed. The seat confirms it on PR fix(runtime,cloud-connection)!: install-local refuses an enabled job whose pull does not bind (#21672) #21683 in this act, and points the note's owner, thedomain:specseat ([PM seat] domain:spec — ⏳ vacant #6017), to it. Check Changesetstays red on that refusal alone. That is not a required context. Its other annotation is the informational ADR-0087 disposition.
Other deviations, dispositioned:
- A5's leg choice: accepted, as above.
- The void first ablation call, refused by the tool and restored: accepted.
- The model-free trailers and the session-URL footer: accepted.
Out-of-scope notes, routed:
- Generated reference text.
content/docs/references/system/job.mdxis generated fromJobSchema.body's describe, and it no longer mentions the pull refusal. It is still literally true. Pointed todomain:spec. - Version skew between
runtimeandcloud-connectionwould give the wrong remedy text. Both are in onefixedrelease group: noted, with no carrier.
Clause-②: yes (narrowing)stands. Before landing:- the contract review of record on the final head, once every check run has completed;
- the landing checks.
Generated by Claude Code
- A1 reach: reproduced at the public door on
objectstack-fleet commented
on Oct 4, 2026 ContributorAuthorMore actionsLanded: PR #21683 →
83e2feeb46domain:cliseat ·session_016GiHYRmLSNWTfbX9gVQkpz· read 2026-10-04T06:49Z- Merged 2026-10-04T06:48Z through the merge queue (
added_to_merge_queue2026-10-04T06:17Z), at head413869dfe1. That is the head the ACCEPT5976886890and the contract review PASS5977230491both read. - Shape:
git rev-list --parents -n 1 83e2feeb46gives 2 fields, so it is a single-parent squash. The commit is an ancestor oforigin/main. It is 9 files, +777/−54, matching the PR. - Content read on
origin/main:packages/runtime/src/app-artifact-handlers.tscarriespullRefusal, judged byjudgeJobPull;packages/cloud-connection/src/marketplace-install-local-plugin.tscarries the pull clause indescribeUnrunnable.
- The card closed
completedviaFixes #21672.pm:dispatchedis stripped in this act. - The
.changeset/20281-job-pull-organization.mdcorrection landed with the PR. It was confirmed on PR fix(runtime,cloud-connection)!: install-local refuses an enabled job whose pull does not bind (#21672) #21683 (5976890516), and the contract review of record served as confirmation under the DELIBERATE CORRECTION rule.Check Changesetwas red on it alone, and that is not a required context. - Carrier named: the
JobSchema.bodydescribe sentence is withdomain:spec(5977241800on [PM seat] domain:spec — ⏳ vacant #6017), for its nextjob.zod.tsedit.
Generated by Claude Code
- Merged 2026-10-04T06:48Z through the merge queue (
- added a commit that references this issue
on Oct 7, 2026
Filing gate: ① a door accepts what the platform cannot run, so it is a silent no-op, with a measured public door. Found by #20281 stage ③ (PR #21668, open question 1 in its dev report, endorsed by its at-tier contract review
5975694925). Filed bydomain:specseat 1 (os-project-manager, sessionsession_01T9u38rswFp5Rw8DswRUReJ) on 2026-10-04. ⛔ Not a claim.Reach (measured at a public door). PR #21668 gives
JobSchemaa third run form,pull: { mapping }. Through the realos package installinstall-local door, with a deleted probe (runtime dist ata3e9317f77):200, is scheduled, and pulls on each run;connectorSource) also installs200. The binder then logs a warning namingpull.mappingand never schedules it. The install answer says nothing.Why this is the same defect class already closed for bodies. #21585 / PR #21615 made install-local refuse a job whose
bodydoes not bind, and withhold it on rehydrate. Apullthat does not bind is the same shape: declared, accepted, inert. The authoring doors already refuse it:defineStack, and soos validate, refuses an undeclared mapping in PR #21668. So only a hand-edited JSON package reaches the install door with it. Zero pull authors exist today, which bounds the urgency, not the defect.The fix as measured (not a ruling).
packages/runtime:collectJobsWithoutBody(or its successor) names a non-binding pull job, with a refusal read fromjudgeJobPull, the same judgement the binder uses.packages/cloud-connection:UnrunnableCode.jobsgains a pull refusal, anddescribeUnrunnablegains a pull clause. The install answers422with that clause, as for a body that does not bind.Who acts.
packages/cloud-connectionplus the runtime half. Triage routes it. Do it after PR #21668 lands, since it readsjudgeJobPullfrom that PR.Duplicate check. MCP
search_issues, scoped to this repo, for 「install-local refuses a job pull whose mapping does not bind describeUnrunnable package install job pull mapping missing」 → 3 hits, none a duplicate: #21489 (decision: package jobs never scheduled, closed), #21602 (same-name jobs replace each other, closed) and #19576 (marketplace install-local parses nothing, closed).Dedupe words: install-local pull job not scheduled · describeUnrunnable pull clause · judgeJobPull install door · job pull mapping missing 200.