Repository navigation
Description
Summary
ShellParse.prefix() looks the command head up in the ARITY table with a plain index, so a shell command whose head is an Object.prototype member name — constructor, __proto__ — is treated as a known multi-word command and produces an empty permission prefix. The saved "always allow" pattern for that command becomes " *" instead of "<command> *": the grant the user approves can never match a future command, and the permission prompt's saved pattern is meaningless.
Evidence (verified this run, main fd36da324a)
packages/core/src/shell/parse.ts:23-161—ARITY: Record<string, number>is a plain object literal.packages/core/src/shell/parse.ts:375-382—prefix():const arity = ARITY[tokens.slice(0, length).join(" ")];if (arity !== undefined) return tokens.slice(0, arity). Forconstructor,ARITY["constructor"]is the inheritedObjectfunction (notundefined), andtokens.slice(0, <function>)coerces the function toNaN, i.e.slice(0, 0)→[]. For__proto__the inherited value isObject.prototype, same result.- Both scan paths consume it identically: legacy
parse.ts:198(save:${prefix(tokens).join(" ")} *) and portable `parse.ts:267-271` (`selected = prefix(...)`, `conventional = `${selected.join(" ")} *, andwordEnds[selected.length - 1]iswordEnds[-1]). packages/core/src/tool/plugin/shell.ts:150-166— the scan'ssavepatterns go intopermission.assert({ save }); on an "always" replypackages/core/src/permission.ts:299-306persists them as saved allow rules, later matched withWildcard.match(packages/core/src/util/wildcard.ts) — the pattern" *"compiles to^( .*)?$, which no real command resource (no leading space) can match.
Laptop probe (verbatim ARITY table, lines 23-161, plus prefix() spliced unchanged from shell/parse.ts, md5 9c8057aa2… byte-identical on both checkouts; probe at laptop ~/git/fleet/probe-2026-10-28-creator3/arity-table.ts):
["constructor","foo.txt"]→ prefix[], save" *"["__proto__","x"]→ prefix[], save" *"- Controls:
["git","status"]→"git status *";["ls","-la"]→"ls *";["cargo","run","--release"]→"cargo run --release *"; unknown head["unknowncmd","arg"]→"unknowncmd *"(the correct fall-through the prototype names bypass)
Why it matters
This is the permission-analysis surface: the save pattern is what the user sees and approves as the persistent grant for a shell tool call. A command head colliding with a prototype name silently degrades that grant to a pattern that matches nothing, so "always" approvals for such a command never take effect (the user is re-prompted forever) — and the corrupted empty prefix also feeds the portable path's wordEnds[-1] boundary logic. Same prototype-chain class as #747/#774/#791, in a table none of those covered. Any executable, script, or package binary named constructor (e.g. invoked via a project-local bin) hits it.
Acceptance criteria
prefix(["constructor", …])andprefix(["__proto__", …])fall through to the unknown-head behaviour:[firstToken], save"<head> *".- Known commands keep their exact arities (
git status,cargo run, single-word heads, multi-word keys like"bun run"). - The
ARITYlookup is own-property only (Object.hasOwn,Map, or null-prototype table); no other change to scan output for ordinary commands. - Tests pin prototype-named heads in the shell parse/scan tests, for the saved pattern in at least one scan path.
Activity
developing-today commentedon Oct 29, 2026
OPENCODE-169
dezren39 commentedon Oct 29, 2026
Filed this run after verification against main
fd36da324a(laptop probe with the verbatim ARITY table + prefix(); see issue body).Found via: filed this run
dezren39 commentedon Oct 29, 2026
🤖 Dispatcher hand-back — executor-saturated · openchamber-dispatcher run 2026-10-28 20:15 CDT
small_fix, low risk, no flags, priority 50, timebox 20m) ↔ this issue (fix(core): ShellParse ARITY table reads through the prototype chain — a "constructor" command head yields an empty permission prefix and a " *" saved pattern #792). Packet verified this run: labels clean (kind/bug, task/small-fix, status/needs-triage, source/creator, priority/normal, queue/queued, app/core, effort/xs — no held / user/owned / do-not-merge / do-not-auto-edit, no work/*, no queue/claimed); taskqueuedatt 0 on the fleet read + targeted packet dump; no open PR for fix(core): ShellParse ARITY table reads through the prototype chain — a "constructor" command head yields an empty permission prefix and a " *" saved pattern #792 (open-PR list checked viagh); no live session for fix(core): ShellParse ARITY table reads through the prototype chain — a "constructor" command head yields an empty permission prefix and a " *" saved pattern #792 (all 12 live sessions belong to earlier dispatcher tasks)./api/session-activityreads over the laptop (~20:17 and ~20:19 CDT, plus the babysitter's fresh 19:57–20:02 CDT round). 0 free slots, so no session was launched for this task. Idle completed sessions still hold their slots — harvest by the owning lanes remains the unblock./api/quota/*read ~20:18 CDT — meta 5h 1% (reset 9:23 PM) / weekly 21%; opencode-go weekly 29.59% / monthly 77.28% (fresh 5h window not yet started after the 7:26 PM roll); cline-pass 5h 9% (reset 11:01 PM) / weekly 69% / monthly 91%. Saturation is slots only. (OpenRouter monthly remains walled at a known 100%; claude/command-code reads failed and antigravity/opendesign are not configured — those pools blocked only themselves. No new cline-pass placement would be made in any case — monthly 91% is finishing-only for in-flight work per the steward note.)queued, attempts 0, visible to the owning lane — there is no targeted claim/park verb for a dispatcher hand-back (the known binding gap, fix(script): stats.ts fetches have no deadline and buffer unbounded .json() bodies — PostHog, npm downloads, and the GitHub releases pager #1337/fix(changelog): read upstream changelog through a 1 MiB capped reader #1272), and the generic queue claim would claim a different task, so no park verb was misfired at the wrong task. This comment is the hand-back record with reason classexecutor-saturated; the next dispatcher run with a free slot should dispatch it (Workhorse/Fast by default — mechanical, well-specified: own-property ARITY lookup in ShellParse.prefix), or a lane may claim it if urgent.dezren39 commentedon Oct 29, 2026
Picked up by issue sweep (rotating) this run; will fix on the laptop in a fleet worktree, branch sweep/792-shell-arity-prototype, PR to follow. Queue task 2937 is being cancelled in the same run per the lifecycle protocol (self-assigned pickup).
Agent: issue sweep (issue-sweep-rotating)
Session/ID: issue-sweep-rotating run 2026-10-28 20:24 CDT
Found via: sweep pickup
Task: own-property ARITY lookup in ShellParse.prefix + prototype-name tests
Expectation: effort/xs — done = PR opened with prefix(["constructor",…]) / prefix(["proto",…]) falling through to unknown-head behaviour, known arities unchanged
dezren39 commentedon Oct 29, 2026
PR #799 opened: #799 (branch
sweep/792-shell-arity-prototype@ 8a96fe7, laptop worktree~/git/fleet/sweep-792-shell-arity).Fix:
prefix()inpackages/core/src/shell/parse.tsnow does an own-property ARITY lookup (Object.hasOwn), soconstructor/__proto__heads fall through to the unknown-head[firstToken]behaviour. Tests pin prototype-named heads in the legacy scan, the portable scan, andscanPortable, plus agit statusarity control.Laptop verification: shell-parse 22/22 pass; shell-parse-parity + shell-parse-compatibility 94/94 pass.
Parked on PR #799 CI — resume: merge when green except the known standings (unit #565 pair; e2e app-2 #631). Push note: the first push's SSH session hung after the ref landed; the branch was independently verified on the remote at 8a96fe7 before the PR was created.
Agent: issue sweep (issue-sweep-rotating)
Session/ID: issue-sweep-rotating run 2026-10-28 20:24 CDT
Found via: sweep pickup
Task: own-property ARITY lookup in ShellParse.prefix + prototype-name tests
Expectation: parked on CI; done = PR #799 merged and #792 closed
dezren39 commentedon Oct 29, 2026
agent-note:v1
State: resumed / merging
Agent: issue sweep (issue-sweep-rotating)
Session/ID: issue-sweep-rotating run 2026-10-28 21:24 CDT
Found via: sweep pickup (own parked PR from previous sweep run)
Task: verify PR #799 CI and merge if failures are only the known standings
Expectation: done — unit (linux+windows) log-verified as exactly the 2 known #565 permission-focus tests; typecheck/build/nix-eval/affected/standards/compliance all SUCCESS; e2e still QUEUED (fleet runner congestion, any app-2 failure is tracked in #631). Merging PR #799 (squash) per the park resume brief and Drew's standing merge directive.
fix(core): use own-property lookup for ShellParse ARITY table (#792) (#…