Skip to content

aggregator(charges-producer): staged mode falls back to the live Gmail CLI when gmail-triage.json is short — the whole Gmail source is silently dropped and the run exits 0 #4660

Closed

Description

@dezren39

Problem

The charge-candidates producer's new staged mode (#4641, commit 7757c2e) is supposed to run on the laptop, where the Plaid/Gmail connector CLIs do not live, reading only staged inputs. But the Gmail triage loop falls back to the live CLI for any query whose staged response is missing:

  • pm-stack/aggregator/scripts/produce-charge-candidates.ts:286-298 — stagedResponses[i] ?? (await run(GMAIL_CLI, ["gmail", "+triage", ...])). gmail-triage.json is documented as "the two +triage responses, in query order", but nothing checks that both are present.
  • On the laptop the default GMAIL_CLI (/opt/hatch/bin/hatch_gws_cli) does not exist, so the fallback throws ENOENT, gmailCandidates rejects as a whole, and main's catch (:402, "gmail read failed …; plaid candidates only") swallows it: the run exits 0 and writes a candidates file with the entire Gmail source missing — including messages the staging did deliver.
  • This contradicts the staged contract the same change documents at :66-69 (stagedText): "a stale or absent staging must fail visibly, never silently produce an empty batch." A short staging is exactly stale/absent staging, and the batch it produces is silently partial instead: the daily collector consumes it as the period's complete candidate set, so that period's Gmail charges simply never match.

The Plaid path does not have this flaw: a missing plaid-transactions.json throws out of plaidCandidates, which main does not catch, so the run fails visibly. The Gmail triage path is the inconsistent one. (Missing gmail-bodies.json is different and correct by design: bodies are requested via gmail-body-requests.json for a later run.)

Evidence (verified this run, laptop probe at origin/main 17610f5)

Probe: ~/git/fleet/probe-2026-11-02-laneB-chargestaged/ on the laptop — the producer extracted verbatim from origin/main (script md5 70bff805), staged inputs for period 2026-11-01..2026-11-02 with one Plaid transaction (Coffee Shop $12.34) and one Gmail receipt ("Your Bakery order receipt $9.99").

  • CONTROL — gmail-triage.json = both responses staged: exit 0, 2 candidates (gmail Bakery $9.99 + plaid Coffee Shop $12.34), gmail skipped 0.
  • PROBE — gmail-triage.json = only the first of the two responses staged: stderr producer: gmail read failed (ENOENT: no such file or directory, posix_spawn '/opt/hatch/bin/hatch_gws_cli'); plaid candidates only, exit 0, output file contains only the Plaid candidate. The staged Gmail message was discarded along with the unstaged query.

Related: #4641 (the staged-mode split this defect ships in), #2448 (a different whole-batch Gmail discard, fixed — same failure shape, different cause).

Acceptance criteria

  • In staged mode (CHARGE_INPUTS_DIR set), the producer never invokes the Plaid/Gmail CLIs, for any input.
  • A gmail-triage.json that is missing, unparseable, or holds fewer responses than the producer issues queries fails the run visibly (non-zero exit, no candidates file written or the file clearly marked failed) instead of producing a plaid-only batch with exit 0.
  • With complete staging, output is unchanged from today (control case above: both sources' candidates present).
  • Live mode (env unset) is unchanged.
  • A regression test or scripted fixture covers the short-staging case.

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/aggregatorPackage/app: aggregator (pm-stack in this repo)
    app/pm-stackpm-stack (Pad/Grist) in developing-today/code
    on Nov 2, 2026
  2. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator job (lane B, new-issue-creator-code-b)
    • Session / ID: cron new-issue-creator-code-b, run 2026-11-02 17:20 CST
    • Found via: filed this run
    • Task: Filed after verifying on the laptop (probe probe-2026-11-02-laneB-chargestaged @ origin/main 17610f5): with only 1 of the 2 staged Gmail triage responses present, staged mode shells out to the absent live Gmail CLI, the whole Gmail source is dropped, and the producer still exits 0 writing a plaid-only candidates file.
    • Expectation: effort/s — triage confirms the staged-mode CLI fallback and routes the fix (staged mode never invokes the CLIs; short staging fails visibly).
    • Links: queue packet creator-b-2026-11-02-1720/packets/packet-4660-triage.json (enqueued to repo-health-code-pilot)
  3. 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 2, 2026
  4. 10 remaining items

  5. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    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
  6. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up (resume)

  7. added a commit that references this issue on Nov 3, 2026
  8. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    Agent note — done (queue worker, lane code-e)

    • Agent: queue worker (repo-worker-code), lane code-e
    • Session / ID: queue task 6625, completed 2026-11-03
    • Outcome: merged PR fix(aggregator): staged mode never falls back to the live Gmail CLI; short staging fails visibly (#4660) #4671 (merge 10e4ec1); issue auto-closed by the PR. Resumed from the prior lane's implementation_complete checkpoint — branch and tests were already pushed; this lane opened the PR once the GitHub write throttle cleared, dispositioned Codex, and merged.
    • Validation: bun test test/produce-charge-candidates.test.ts — 26 pass, 0 fail (prior lane, laptop; includes the 2 new staged-mode regression tests: short staging fails non-zero with no candidates file, complete staging yields both candidates). Codex review of 9d93373: no findings. PR guard check never left QUEUED (fleet runner backlog); merged under standing merge authority.
  9. dezren39 commented on Nov 3, 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)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 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