Skip to content

aggregator(charges-producer): staged mode never validates the manifest period — a field-less manifest writes a candidates file with period {} and exits 0 #4663

Closed

Description

@dezren39

Problem

In staged mode (#4641), the charge-candidates producer takes its period from the fetcher's charge-inputs-manifest.json — and never validates it:

  • pm-stack/aggregator/scripts/produce-charge-candidates.ts main() (:371-378): start = manifest.start; end = manifest.end; straight from JSON.parse, with no presence, format, calendar, or ordering check. The same file already imports and applies isRealCalendarDate to every Plaid transaction date (:133) and every Gmail message day (:326) — the manifest period is the one date input exempt from it.
  • A manifest whose start/end are missing (e.g. a fetcher schema drift or a hand-staged directory) makes start/end undefined: the Gmail path dies on d.replaceAll inside gmailCandidates and is swallowed by main's catch ("plaid candidates only"), the Plaid path ignores the period entirely in staged mode, and the producer writes a candidates file with period: {} (undefined fields vanish in JSON.stringify), logs undefined..undefined, and exits 0.
  • The daily collector consumes the file keyed by its period; a period-less batch is indistinguishable from a successful run at the exit-code/log level the fleet monitors, so the corruption surfaces only as missing/misattributed charges downstream. A malformed date string (e.g. 2026-13-40) or an inverted window (end before start) passes the same way: nothing ever compares the staged payloads against a real window.

This is the manifest counterpart of the staged-mode contract at :66-69: staging that does not describe a real period is absent staging, and must fail visibly.

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

Probe: ~/git/fleet/probe-2026-11-02-laneB-chargestaged/ — producer extracted verbatim from origin/main (script md5 70bff805), complete staging (both Gmail triage responses, one Plaid transaction, one Gmail receipt), manifest replaced with {"note":"no period fields"}:

  • Output: producer: wrote out-probe2.json — 1 candidates for undefined..undefined (plaid 1, gmail 0, gmail skipped 0), exit 0; stderr producer: gmail read failed (undefined is not an object (evaluating 'd.replaceAll')); plaid candidates only.
  • The written file's period is literally "period": {}.
  • Control with the good manifest ({"start":"2026-11-01","end":"2026-11-02"}) in the same probe directory: 2 candidates, correct period, exit 0.

Related: #4660 (staged triage fallback in the same feature — different mechanism: the triage loop, not the manifest read), #4641 (the staged split), #3333 (live-mode --start/--end argument handling — different input path).

Acceptance criteria

  • In staged mode, a manifest with a missing, malformed, non-calendar, or inverted (end < start) period fails the run visibly: non-zero exit and no candidates file presented as a successful batch.
  • Validation reuses the file's existing isRealCalendarDate standard for both fields.
  • With a valid manifest, staged output is unchanged (control case above).
  • Live mode (env unset) is unchanged.
  • A regression test or scripted fixture covers the missing-fields manifest 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/xsTrivial: expected under 30 minutes 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): a staged manifest without start/end makes the producer write a candidates file with period: {}, log undefined..undefined, drop Gmail via a swallowed TypeError, and exit 0 — the manifest period is the one date input never passed through the file's own isRealCalendarDate checks.
    • Expectation: effort/xs — triage confirms the unvalidated manifest read and routes the fix (validate both fields as real calendar dates, end >= start, fail visibly otherwise).
    • Links: queue packet creator-b-2026-11-02-1720/packets/packet-4663-triage.json (enqueued to repo-health-code-pilot); companion staged-mode defect 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
  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. 15 remaining items

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

    @dezren39
    MemberAuthor

    Agent note — parked (CI pending)

  7. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

    Triage verdict: CONFIRMED on current main (17610f5, verified fresh via gh raw read of pm-stack/aggregator/scripts/produce-charge-candidates.ts this shift):

    • :371-376 — staged branch: const manifest = JSON.parse(...), then start = manifest.start; end = manifest.end; with no presence, format, calendar, or ordering check anywhere in the file for the manifest period (grep: manifest occurs only at :369-376).
    • isRealCalendarDate is imported at :58 and applied to Plaid transaction dates (:133) and Gmail message days (:326) — the manifest period is the one date input exempt.
    • The Gmail failure from undefined bounds is swallowed by main's catch ("plaid candidates only"), after which the producer writes period: {start, end} — undefined fields drop in JSON.stringify, yielding the literal period: {} — and exits 0. This matches the creator's laptop probe on the same commit (field-less manifest → 1 candidates for undefined..undefined, exit 0, file period {}; good-manifest control → 2 candidates, correct period).

    Routing: fix enqueued as task 6627 (small_fix, dedupe_key repo-health:v1:fix:developing-today/code:issue:4663): validate both manifest fields with isRealCalendarDate plus end >= start in the staged branch before any candidate work; any failure exits non-zero with no successful candidates file; regression fixture for the missing-fields case; live mode unchanged. Issue #4663 stays open for task 6627.

  8. 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 3, 2026
  9. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up — PR #4670 conflicted with main, fixing forward

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

    @dezren39
    MemberAuthor

    🤖 Agent note · done — PR #4670 merged, issue #4663 closed

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/xsTrivial: expected under 30 minutes 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