Skip to content

aggregator(snapshots): append-usage-snapshots --by with no value silently discards every derived record — exit 0, 'no records derived' #3295

Closed

Description

@dezren39

aggregator(snapshots): append-usage-snapshots --by with no value silently discards every derived record — exit 0, "no records derived"

Evidence (verified this run, main fdfb377c)

pm-stack/aggregator/scripts/append-usage-snapshots.ts:54-62 reads the observer name with the bare index-of pattern, in both modes:

const byArg = process.argv.indexOf("--by");
const observedBy = byArg >= 0 ? process.argv[byArg + 1] : "check-usage";        // --from-provider-usage mode (:57)
const observedBy = byArg >= 0 ? process.argv[byArg + 1] : "stewardship-cycle";  // default mode (:61)

When --by is the last argument, process.argv[byArg + 1] is undefined; with --by "" it is the empty string. There is no validation of the value. Every derived record then carries that observedBy, and the script's own filter drops all of them: isSnapshotRecord requires a non-empty string observer (pm-stack/aggregator/src/snapshots.ts:310, !isNonEmptyString(r.observedBy) → false). The script then reaches the empty-result branch (append-usage-snapshots.ts:71-80) and prints the misleading no records derived; outbox untouched — and exits 0.

Probe (laptop, real script run from the laptop clone; script sha256 e2612f0f… and src/snapshots.ts c6685035… identical on both clones; fixture provider-usage.json = {"generated_at":"2026-10-31T12:00:00.000Z","sessions":[{},{}]}, which derives exactly one record; probe dir ~/git/fleet/probe-2026-10-31-laneA-appendby/):

run result
control: --from-provider-usage --by tester appended 1 record(s); outbox holds the sessions-listed record with "observedBy":"tester"; exit 0
--from-provider-usage --by (trailing, no value) no records derived; outbox untouched; no outbox file created; exit 0
--from-provider-usage --by "" same: no records derived; outbox untouched; no outbox file; exit 0

Why it matters

This script is a snapshot writer (hub design §4): stewardship / check-usage runs append the dated quota observations that become the fleet's measured usage history. A typo'd or wrapper-generated trailing --by — one missing value — silently throws away the whole run's records while reporting a routine "nothing to do" outcome and success. Nothing downstream alarms, because the outbox legitimately stays empty on genuinely-empty runs too; the two cases are indistinguishable in the output. The sibling script in the same folder already shows the house pattern for this: compact-usage-snapshots.ts:28-31 validates its --months value and exits 2 naming the bad value.

Acceptance criteria

  • append-usage-snapshots.ts --by with no following value, an empty value, or a value that is itself a flag (starts with --) exits 2 with a clear invalid --by usage error, before reading inputs or touching the outbox — in both the default and --from-provider-usage modes.
  • --by <name> and a missing --by (defaults check-usage / stewardship-cycle) behave exactly as today: the probe control above still appends 1 record with the given observer.
  • The "no records derived" message is only reachable when records were actually derived-and-filtered for content reasons; a test (or script-level check) pins the trailing---by refusal so the silent-discard path cannot return.
  • Existing aggregator snapshot tests still pass.

Dedup: searched open + closed issues for append-usage-snapshots and observedBy — neighbours are #3285 (compact --months 0, same folder, different script/guard), #2568 / #2571 (percent range checks), #2941 (__proto__ grouping) and #1205 (writer-health epic); none covers the --by value going unvalidated and voiding the run.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    priority/normalOrdinary priority (the default)
    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
    app/aggregatorPackage/app: aggregator (pm-stack in this repo)
    effort/sSmall: expected under 2 hours of work
    on Nov 1, 2026
  2. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator (new-issue-creator-code-a)
    • Session / ID: cron job new-issue-creator-code-a, run 2026-10-31 21:05 CDT (2026-11-01T02:19Z)
    • Found via: filed this run
    • Task: Filed a verified silent-discard edge: append-usage-snapshots.ts reads --by by bare index-of with no value validation (scripts/append-usage-snapshots.ts:54-62, both modes), so a trailing or empty --by gives every derived record an undefined/empty observedBy, isSnapshotRecord (src/snapshots.ts:310) filters them all out, and the script exits 0 saying "no records derived" with the outbox untouched. Verified with a laptop Bun probe running the real sha-matched script against a fixture provider-usage snapshot (control --by tester appends 1 record; trailing --by and --by "" append nothing, exit 0).
    • Expectation: effort/s — triage confirms the trailing/empty --by case against main, then the fix validates the --by value (missing/empty/flag-like → exit 2, mirroring compact-usage-snapshots' --months guard) and pins it with a test.
    • Links: probe (laptop) ~/git/fleet/probe-2026-10-31-laneA-appendby/ · related aggregator(snapshots): compact-usage-snapshots accepts --months 0 — --apply compacts the in-progress month, deleting its raw file and leaving only min/max/last #3285 (same folder, compact --months guard pattern), Usage snapshots: move ingestion out of the aggregator read path and report writer health #1205 (writer-health epic)
  3. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    Adopted as Phase 2 of design #3352 (corollary 3: present-but-valueless is not absent — an empty observer value is a usage error, not a silently discarded run). 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. added
    work/assignedQueue worker lifecycle state
    and removed on Nov 1, 2026
  5. 6 remaining items

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

    @dezren39
    MemberAuthor

    🤖 Agent note · done (triaged)

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

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  10. added a commit that references this issue on Nov 1, 2026
  11. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

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)effort/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