Skip to content

opencode-claimant(install): unrecognized arguments are silently ignored — a typo'd --dry-rnu performs a real install, and a typo'd --stor writes the real OpenChamber store #3278

Closed

Description

@dezren39

Problem

pm-stack/opencode-claimant/install.mjs — the installer that writes the #3026 claimant scheduled task into the laptop's real OpenChamber project store — performs no argument validation. main() (install.mjs:65-71) collects only the spellings it knows:

const args = new Set(argv.filter((a) => a.startsWith("--")));
const storeIdx = argv.indexOf("--store");
const storePath = storeIdx >= 0 ? argv[storeIdx + 1] : DEFAULT_STORE;
...
enabled: args.has("--enable") ? true : (existing?.enabled ?? false),

Everything else on the command line is silently dropped, and the script proceeds to do a real install: it backs up and then atomically renames over the target store (install.mjs:91-94). Three concrete failure shapes:

  1. A typo'd --dry-rnu performs a real install. The operator believes they are previewing; the store is actually rewritten and the claimant task inserted/refreshed. If the task already exists enabled in that store, this also silently refreshes the live task's prompt to the current prompt.md — a production behavior change made by a preview command.
  2. Any unknown flag (--bogus-flag, a stray positional) is ignored the same way, with exit 0 and a normal-looking summary. The only difference between the dry-run and real-run summaries is a dry_run: true field the operator has to notice the absence of.
  3. A typo'd --stor <path> falls back to DEFAULT_STORE (install.mjs:69-71: indexOf("--store") misses, so the real ~/.config/openchamber/projects/… store is used) — a preview aimed at a scratch store writes the operator's real OpenChamber store instead. (Code-read consequence; not probed, for obvious reasons.)

Evidence (verified this run, main 3186e25)

install.mjs sha256 2ff84f5a48b2… and prompt.md sha256 dec788608534… are identical in the sandbox clone at 3186e25 and the laptop clone. Laptop probe ~/git/fleet/probe-2026-10-31-laneA-claimant-install/ (node v24.21.0, real install.mjs, temp stores seeded with one unrelated task):

  • node install.mjs --store store-typo.json --dry-rnu → exit 0, summary {action: "inserted", tasks_before: 1, tasks_after: 2} with no dry_run marker; the store afterwards contains the claimant task f1b4a33b-14d5-4a69-9ad6-d221e42e3ac9 alongside the seed task, and a store-typo.json.bak-<stamp> backup was created — i.e. a real write happened.
  • Control: the same command with correctly spelled --dry-run → store byte-identical afterwards (still only the seed task, 84 bytes), no backup file created.
  • node install.mjs --store store-bogus.json --bogus-flag → same real write as the typo case (task inserted, backup created, exit 0).

Existing tests (pm-stack/opencode-claimant/test/install.test.mjs, 7 tests) cover buildTask/mergeTasks only; nothing exercises main()'s argument handling, so no test pins the unknown-argument behavior either way.

Why it matters

This installer's whole safety story is in its header comment: the store "is runtime state", installing is an idempotent merge, and the task "installs DISABLED … enabling is the parallel-run gate's decision". A silent-argument-drop defeats the two operator-facing safeguards on that story — --dry-run (preview before touching runtime state) and --store (aim at a scratch store, not the real one). The failure is silent and exit-0, so scripts and operators cannot tell a preview from a write except by diffing the store afterwards.

Distinct from #3264 (same silent-args pattern in pkgs/pkarr-worker/deploy.mjs, a different tool whose consequence is a production deploy; that issue's fix does not touch this file) and from #3252/#3261 (pkarr-worker deploy/retention arguments).

Acceptance criteria

  • Any unrecognized argument — unknown --flag, unexpected positional, or a --store with no following value — makes install.mjs exit non-zero with a usage error naming the offending argument, and writes nothing (no store write, no backup file).
  • --dry-rnu, --bogus-flag, and --stor <path> are each refused by a regression test that drives main() (or an extracted argument parser) against a temp store and asserts the store is byte-identical afterwards.
  • The documented invocations keep working exactly as before: bare install, --dry-run (no write, no backup), --store <path>, --enable, and their combinations; the existing 7 install.test.mjs tests still pass.
  • --enable handling is unchanged in meaning: a typo must never silently enable, and a refused invocation must never change an existing task's enabled state.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/triageQueue packet kind: verify, reproduce, and route this item
    source/creatorFiled by the new-issue-creator job
    priority/normalOrdinary priority (the default)
    queue/queuedHas a queued task in the Supabase agent queue
    effort/sSmall: expected under 2 hours of work
    app/pm-stackpm-stack (Pad/Grist) in developing-today/code
    on Nov 1, 2026
  2. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator-code-a
    • Session / ID: new-issue-creator-code-a cron run 2026-10-31 20:05 CDT
    • Found via: filed this run — read pm-stack/opencode-claimant/install.mjs on main 3186e25 and reproduced on the laptop with the real install.mjs (install.mjs/prompt.md sha256-matched across clones; node v24.21.0): --dry-rnu and --bogus-flag against temp stores each performed a real write (claimant task inserted, .bak backup created, exit 0), while the correctly spelled --dry-run control left its store byte-identical with no backup.
    • Task: Filed this issue: install.mjs main() (install.mjs:65-71) validates no arguments — unknown flags are silently dropped so a typo'd preview installs for real, and a typo'd --stor falls back to DEFAULT_STORE, the real OpenChamber project store.
    • Expectation: effort s; done when any unrecognized argument (or a valueless --store) exits non-zero with a usage error and writes nothing, regression tests drive main()/the parser for --dry-rnu/--bogus-flag/--stor, and the documented invocations plus the existing 7 install.test.mjs tests still pass.
    • Links: pkarr-worker(deploy): unrecognized arguments are silently ignored — a typo'd --dry-rnu or --deploy-version=<id> performs a real deploy of HEAD #3264 (same silent-args pattern in pkgs/pkarr-worker/deploy.mjs — different tool, distinct) · Move opencode executions to laptop-side scheduled tasks — retire the Muse boot handoff #3026 (the claimant this installer installs)
  3. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    Adopted as Phase 1 of design #3352 (incl. the --store fallback: an unrecognized value-option spelling must not silently re-aim the tool). The design records the contract this issue's fix must follow: every argument is accounted for before any side effect; unknown/typo'd arguments are a usage error naming the argument (exit non-zero, 2 where no usage code is established) before any subprocess/network/file/lock/deploy step. Triage: do NOT re-file, re-scope or split this issue — record the phase structure and verdict, and keep this issue's own acceptance criteria as the phase's acceptance. Design doc: files/designs/code-operator-argument-contract.md (keep-the-four-repos-healthy goal workspace).

  4. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  5. 5 remaining items

  6. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · follow-up queued

  7. added
    queue/queuedHas a queued task in the Supabase agent queue
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 1, 2026
  8. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  9. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done (PR opened)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/pm-stackpm-stack (Pad/Grist) in developing-today/codeeffort/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/normalOrdinary priority (the default)source/creatorFiled by the new-issue-creator jobsource/design-architectFiled by the repo design architect jobtask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions