SCRAPE¶
Per-account transaction walk. Emits the typed inputs that BALANCE-RESOLVE consumes.
| Always-on? | Yes (ifAnyScraper) |
| Owner slot | scrape: Option<{ accounts, accountIdentities, balanceFetchTemplate }> |
| Source | ScrapePhase.ts + ScrapePhaseActions.ts |
Sub-step contract¶
| Hook | What it does |
|---|---|
.pre | Forensic priming (run PopupInterceptor); DIRECT discovery (read accountDiscovery.ids, freeze the network pool, seal). |
.action | Sealed action: frozen matrix loop — for each accountId, run the bank's IFetchStrategy against the frozen txn endpoint + harvest, parse transactions. |
.post | v6 emission — build accountIdentities (per-card triples) + balanceFetchTemplate from the captured pool; audit forensic per-account txn counts; consult the empty-gate heuristic. |
.final | Stamp account count into diagnostics. |
What v6 changed¶
Before v6, .post also computed perAccountResponses — a partial pool with attribution heuristics — that BALANCE-RESOLVE consumed. v6 removed that path (~370 LOC) and replaced it with two typed fields:
| Field | Built from | Consumed by |
|---|---|---|
accountIdentities: ReadonlyMap<cardDisplayId, IAccountIdentity> | accountDiscovery.records via buildAccountIdentities | BALANCE-RESOLVE.pre |
balanceFetchTemplate: IBalanceFetchTemplate | Captured pool via discoverBalanceFetchTemplate (tries POST-with-bodyKey → GET-with-queryKey → GET-with-path-tail → bulk fallback) | BALANCE-RESOLVE.pre |
See Architecture → BALANCE-RESOLVE for the rationale.
Empty-gate heuristic (v4 Issue 2)¶
executeValidateResults distinguishes a real scrape miss from a legitimate empty result:
| Condition | Action |
|---|---|
0 accounts in scrape.accounts | Procedure fail "no accounts produced" |
Accounts produced, all with txns.length === 0, AND network.countSuccessfulResponses() > 0 | succeed (legitimate empty month) |
Accounts produced, all empty, AND countSuccessfulResponses() === 0 | Procedure fail "scrape miss" |
Test coverage: EmptyGateHeuristic.test.ts.
Forensic audit observability¶
.post invokes logForensicAudit which emits the per-account --- Account <masked> | <N> txns --- line. Same hook runs in API-DIRECT-SCRAPE.post so every scrape path produces the same diagnostic.