Skip to content

aggregator(bitwarden): the new bws count extractor truncates comma-grouped counts — '1,121 secrets' records 121 and '1,000 projects' records 0, and the wrong figures flow into snapshot records #2997

Closed

Description

@dezren39

Summary

The Bitwarden extractor added today in PR #2987 (#1111) parses the dated bws counts with bare \d+, so a comma-grouped count is silently truncated to its last group — and the wrong figure then flows into the snapshot store as a permanent record. 1,121 secrets is recorded as 121 (indistinguishable from a real 121 read) and 1,000 projects is recorded as 0.

Evidence (main 74afc26e)

pm-stack/aggregator/src/saas-extract.ts:475-478 (bitwardenMetrics, added by commit 795bbdb4, merged in #2987):

const secrets = /(\d+)\s*secrets?/i.exec(usage);
const projects = /(\d+)\s*projects?/i.exec(usage);

\d+ cannot cross the comma, so the regex engine slides forward and matches only the final digit group before the noun: for 1,121 secrets it matches 121 secrets; for 1,000 projects it matches 000 projects → Number("000") = 0.

Verified this run on the laptop with the real extractSaasServices (laptop clone at the same main, saas-extract.ts md5 2b540acdb7924e834941a63b0416cf44 on both clones; probe ~/git/fleet/probe-2026-10-31-laneB-bwcomma/):

Usage bullet: "1,121 secrets, 1,000 projects (bws count-only read 2026-10-26)"
measuredAt 2026-10-26
secrets-count used = 121     ← want 1121
projects-count used = 0      ← want 1000
control ("121 secrets, 1 project"): secrets-count = 121, projects-count = 1

Note the corrupted secrets figure is identical to the control — there is no signal in the output that anything was lost.

Why it matters

  • These metrics are not display-only: the extractor's commit message and recordsFromSaasServices (pm-stack/aggregator/src/snapshot-records.ts:85, called from scripts/collect-fleet-feed.ts:598 and scripts/append-usage-snapshots.ts:67) turn used into durable snapshot records keyed by (service, metric, ts, value). A misparsed count becomes wrong history, not a transient render glitch.
  • Every sibling extractor in this codebase already handles grouped numbers: parsePriceUsd in dns.ts and the DeepSeek ¥ parse in snapshot-records.ts:177 use [\d,]+ and strip commas; the Cloudflare limits in the same file parse 10M-style figures. The new extractor (merged hours ago) is the outlier, and its own test fixture only exercises ungrouped counts (121 secrets, 1 project), so the gap is invisible to the suite.
  • The Modal extractor added in the same commit has the same \d+-only shape for its dollar figures (saas-extract.ts:448-455); there the failure is fail-soft (a $1,250 credits used read matches nothing and stays null). Triage should decide whether to harden it in the same pass or leave it as a noted sibling.

Acceptance criteria

  • bitwardenMetrics parses comma-grouped counts at face value: 1,121 secrets → 1121, 1,000 projects → 1000 (accept the house [\d,]+ + strip-commas form, or an equivalent grouped-number parse).
  • Regression tests in pm-stack/aggregator/test/usage.test.ts cover grouped secrets and projects counts alongside the existing ungrouped case.
  • Ungrouped reads are unchanged (121 secrets, 1 project → 121 / 1), and a bullet with no counts still yields no metrics.
  • Triage rules on the Modal comma-dollar sibling (same-commit \d+-only patterns): fix in the same change, or record it as an explicit follow-up — not silently left.

Dedup

Searched open and closed issues for bitwarden, bws count, and comma/thousands parsing in saas-extract on 2026-10-31: no existing issue covers this extractor (it landed today in #2987). The extractor's parent work is #1111 / #2990 (per-credential rows and remainders — different items). Sibling number-reinterpretation issues (#2985 aa.ts num(), #2791 cloudflare-export num()) concern different functions that reinterpret non-decimal strings; this one truncates a grouped decimal.

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/xsTrivial: expected under 30 minutes of work
    on Oct 31, 2026
  2. dezren39 commented on Oct 31, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator job (lane B, agent slug new-issue-creator-code-b)
    • Session / ID: cron job new-issue-creator-code-b, run 2026-10-31T08:50Z
    • Found via: filed this run
    • Task: Filed after verifying on the laptop with the real extractSaasServices (main 74afc26e, saas-extract.ts md5 2b540acdb7924e834941a63b0416cf44 on both clones) that the Bitwarden extractor added today in feat(usage-hub): modal used-credits + bitwarden bws count extractors (#1111) #2987 parses 1,121 secrets as 121 and 1,000 projects as 0 — its bare \d+ patterns (saas-extract.ts:477-478) match only the last comma group, while sibling extractors use [\d,]+ + strip. The wrong counts flow into snapshot records via recordsFromSaasServices.
    • Expectation: effort/xs — triage confirms the grouped-number parse form (and rules on the Modal comma-dollar sibling) and routes the fix + regression tests.
    • Links: probe ~/git/fleet/probe-2026-10-31-laneB-bwcomma/ (laptop); evidence body in the goal hidden_files (issue-code-bitwarden-comma-count-body.md)
  3. dezren39 commented on Oct 31, 2026

    @dezren39
    MemberAuthor

    🤖 Design-architect pass 25 (2026-10-31) — agent note: this issue is ADOPTED as Phase 2 (with #3000) of design #3005 (a measurement is a plain decimal), linked as its sub-issue and labelled source/design-architect. Triage: do NOT re-file, re-scope, or split it; record the phase structure instead. The design records the prose form of the grammar: count extractions capture the grouped form ([\d,]+, strip commas — the house idiom in dns.ts parsePriceUsd and snapshot-records.ts:177); grouping well-formedness is stripped, not validated. The Modal comma-dollar sibling this issue names for a triage ruling is ruled IN by the design: fix it in this phase — same file, same defect, same probe matrix — not as a follow-up. Flag genuine blockers on #3005.

  4. removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 1, 2026
  5. 51 remaining items

  6. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — holder: repo-worker-code (OpenChamber scheduled lane), at 2026-11-02T14:57:00Z. event: picked-up; task: 5505; queue: repo-health-code-pilot. Fresh metadata: #2997 OPEN; canonical PR #4289 OPEN and authored/assigned to Drew. No PR contents, checks, reviews, branch, worktree, or source inspected.

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

    @dezren39
    MemberAuthor

    agent-note:v1 — holder: repo-worker-code (OpenChamber scheduled lane), at 2026-11-02T14:58:00Z. event: parked; task: 5505; queue: repo-health-code-pilot. Canonical PR #4289 remains OPEN, non-draft, Drew-authored/assigned; hard stop per this runbook. This lane did not inspect/touch PR contents, checks, reviews, branch, worktree, or source and created no competing PR. Resume after PR disposition or explicit ownership handoff, then revalidate fresh.

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

    @dezren39
    MemberAuthor

    🤖 Agent note · done

  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/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 jobsource/design-architectFiled by the repo design architect jobtask/small-fixQueue packet kind: bounded fix with reproduction and targeted tests

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions