Skip to content

aggregator(backfill): trailing --dir crashes with a raw TypeError, and a flag-like value (--dir --print) silently writes the rebuilt history into a directory named '--print' #3330

Closed

Description

@dezren39

Bug

backfill-usage-snapshots (the one-shot usage-snapshot backfill, pm-stack/aggregator/scripts/backfill-usage-snapshots.ts) reads its only flag with a bare index lookup and no validation:

// backfill-usage-snapshots.ts:32-33
const dirArg = process.argv.indexOf("--dir");
const storeDir = dirArg >= 0 ? process.argv[dirArg + 1] : (process.env.PM_AGG_SNAPSHOTS_DIR ?? join(PKG, "snapshots"));

Two failure modes, both verified this run:

  1. Trailing --dir crashes with a raw TypeError, not a usage error. process.argv[dirArg + 1] is undefined, so storeDir is undefined, and the run dies inside SnapshotStore.append → withSnapshotStoreLock at mkdir(dir) (src/snapshots.ts:347): TypeError: path must be a string or TypedArray with a Bun stack trace, exit 1. Nothing names --dir or its missing value.
  2. A flag-like next token is silently taken as the directory. --dir --print appends the derived records into a newly created directory literally named --print in the current working directory and exits 0 reporting appended to --print — the real snapshot store is untouched while the report claims success. Any mistyped following token does the same.

Evidence (verified this run)

Probe on the laptop (~/git/fleet/probe-2026-10-31-laneA-backfilldir/): script run verbatim from main 6e7f8fcf (backfill-usage-snapshots.ts sha256 f55386364648275ac53f35752dee2302006f23545c68a5c409ca6d47c22d8da7 on both laptop and sandbox clones), Bun 1.4.2, fixture QUOTA_VIEW_PATH carrying one DeepSeek ¥44.63 / 2026-10-25 line (exactly 1 derived record; the 31 unmatched-pattern warnings are the script's normal fail-soft behaviour):

=== CONTROL: --dir <valid temp dir> ===
backfill: 1 record(s) appended to .../store-control (0 already present, 31 skipped)   exit=0
store-control/usage-2026-10.jsonl written

=== BUG 1: --dir            (trailing, no value) ===
TypeError: path must be a string or TypedArray
  at mkdir (node:fs/promises)
  at withSnapshotStoreLock (src/snapshots.ts:347)
  at append (src/snapshots.ts:606)
  at main (scripts/backfill-usage-snapshots.ts:431)                                     exit=1

=== BUG 2: --dir --print    (flag-like value) ===
backfill: 1 record(s) appended to --print (0 already present, 31 skipped)             exit=0
# a directory named `--print` was created in the CWD (the aggregator package dir)
# containing usage-2026-10.jsonl; the real store never saw the record.
# (Probe cleanup: the stray `--print` directory was removed from the laptop clone.)

Contrast with the sibling scripts in the same package: compact-usage-snapshots.ts:28-31 validates its --months value and exits 2 with invalid --months '<value>', and append-usage-snapshots.ts was already filed for its --by variant of this shape (#3295). This script has no guard at all.

Why it matters

The backfill is the recovery/seed path for the usage snapshot store — it is run by hand, rarely, exactly when history needs rebuilding. A forgotten --dir value currently either crashes with a stack trace that points into the store lock code (suggesting store corruption, not a typo) or, worse, silently writes the rebuilt history into a stray flag-named directory in the working tree and reports success, so the operator believes the store was seeded when nothing was. Because re-running is documented as safe (dedupe), the silent case can go unnoticed until the missing history is queried.

Acceptance criteria

  • backfill-usage-snapshots.ts --dir with no following value, or with a following token that starts with --, prints a usage/error message naming --dir and its missing value to stderr and exits 2 — no stack trace, no directory created, no records written.
  • --dir <valid path> behaves exactly as today (records appended to that store, exit 0, same summary line).
  • The no-flag default (env PM_AGG_SNAPSHOTS_DIR, else the package snapshots/ directory) is unchanged.
  • A regression test (or script-level test harness case) covers the trailing---dir and flag-like-value cases.

Distinct from: #3295 (append-usage-snapshots --by voiding observedBy), #3285 (compact --months 0 guard gap — compact does validate non-numeric values), #3312 (docs-history trailing flag panic, Go tool). Same family, different script and different failure: this one both crashes and silently misdirects the write.

Activity

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

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

  3. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    Adopted as Phase 2 of design #3352 (corollary 3: present-but-valueless is not absent — neither default nor flag-as-value nor downstream crash). 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. removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 1, 2026
  5. 22 remaining items

  6. added
    queue/claimedQueue worker lifecycle state
    and removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Nov 2, 2026
  7. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · state: parked

    • Agent: OpenChamber scheduled lane (repo-worker-code)
    • Session / ID: queue task 5929
    • Found via: repo-health-code-pilot claim; resumed prior parked task
    • Task: PR fix(aggregator): validate backfill snapshots --dir (#3330) #4393 remains open at 0accb1a860c993948c16f1062c537995a5d0b2a4; latest fresh checks aggregator + projector and test are both QUEUED. Targeted Bun regression tests were recorded passing 4/4.
    • Reason: Parked for 20 minutes under the CI-pending rule; work/assigned remains set.
    • Resume: Recheck PR/checks fresh after 20 minutes; merge if both green and diff remains verified, otherwise address/park.
    • Links: task 5929 · PR fix(aggregator): validate backfill snapshots --dir (#3330) #4393
  8. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    and removed
    queue/claimedQueue worker lifecycle state
    on Nov 2, 2026
  9. added a commit that references this issue on Nov 2, 2026
  10. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — done (verified on main, closing)

    • Agent: repo-worker-code (OpenChamber scheduled lane)
    • Queue task: 5929; session ses_e5e63152218ffeSSUYn3t1sgWt
    • Evidence: PR fix(aggregator): validate backfill snapshots --dir (#3330) #4393 merged 2026-11-02T15:26:00Z (merge 156b157); git merge-base --is-ancestor confirms the PR head is in origin/main; the --dir guard (--dir requires a directory value) is present at origin/main:pm-stack/aggregator/scripts/backfill-usage-snapshots.ts:32-36, and the regression test test/backfill-usage-snapshots-dir-3330.test.ts is on main.
    • Tests: new regression file 2/2 pass (bun); full aggregator suite 1071 pass, 1 fail + 1 error both from missing web-push package in node_modules (declared dep, not installed in the worktree) via src/push.ts imported by test/alerts.test.ts — pre-existing environment gap, unrelated to this change (diff touches only the backfill script + the new test).
    • CI note: PR checks for fix(aggregator): validate backfill snapshots --dir (#3330) #4393 show aggregator+projector and test failing post-merge — recorded on the task.
  11. removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    work/assignedQueue worker lifecycle state
    on Nov 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/aggregatorPackage/app: aggregator (pm-stack in this repo)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