FS-03 · Auto-suggest and confirm SFRS taxonomy mappings
SOP:
SOP_Financial_Statements.md§6 / Step 3.0 (TB_UPLOADED → TB_MAPPED)Actors: FS Preparer (fs-preparer@spade.local) — holdsCONFIRM_FS_MAPPINGandOVERRIDE_FS_MAPPING. Pre-state: Engagement atTB_UPLOADED(orTB_MAPPEDif you are revising mappings). Post-state: EveryFsTrialBalanceItemhas a non-nulltaxonomyKey. Engagement atTB_MAPPED.
0. Prerequisites
- Engagement at
TB_UPLOADED(see FS-02). - The
auto-suggest-mappingworker is registered and has access to an Anthropic API key (or the mock adapter is enabled — confirm viapnpm devworker logs). - The SFRS taxonomy constitution loaded fine (run
pnpm --filter @breezycorp/financial-statements testto confirm).
1. Steps
1.1 Run auto-suggest
POST /ops/financial-statements/engagements/<engagementId>/mapping/auto-suggest
Authorization: Bearer <fs-preparer-jwt>(Or click Auto Suggest on the Mapping tab.)
The handler:
- Asserts
CONFIRM_FS_MAPPING. - Enqueues
auto-suggest-mappingwith{engagementId}. - Returns
202 Accepted.
The worker:
- Reads every
FsTrialBalanceItemfor the engagement. - Calls Claude Sonnet with the TB rows + the full flattened SFRS taxonomy. The prompt is templated in
packages/financial-statements/src/ai/mapping-prompt.ts(system + user; system is prompt-cached). - Parses the response —
[{accountCode, taxonomyKey, taxonomyLabel, confidence, rationale}]. - Asserts every
taxonomyKeyexists in the constitution (findTaxonomyItem(key) != undefined); rows with unknown keys are kept null and re-flagged for manual mapping. - Upserts
FsTrialBalanceItemrows by(engagementId, accountCode). - If every row is mapped, transitions engagement
TB_UPLOADED → TB_MAPPED. - Emits
AuditEventfs.mapping.auto_suggestedwith{inputTokens, outputTokens, mappedRows, unmappedRows}.
1.2 Walk LOW and MEDIUM rows
Open the Mapping tab. Filter for confidence != HIGH. For each row:
- Read the rationale tooltip (it carries the Claude justification).
- Open the taxonomy dropdown — it's constrained to the constitution. Type-ahead filters by key + label.
- Accept the suggestion or pick an alternative.
1.3 Manual override
For a row that auto-suggest returned UNCLASSIFIED for, or where you disagree with the AI:
PATCH /ops/financial-statements/engagements/<engagementId>/mapping/<tbItemId>
Content-Type: application/json
Authorization: Bearer <fs-preparer-jwt>
{
"taxonomyKey": "ppe",
"mappingRationale": "Reclassified from intangibles — this is plant kept at the leased site"
}The handler:
- Asserts
OVERRIDE_FS_MAPPING. - Asserts
taxonomyKeyexists in the constitution. - Sets
isOverride = true,mappingConfidence = MANUAL, and the rationale. - Emits
AuditEventfs.mapping.overridden.
1.4 Engagement transitions
If auto-suggest left every row mapped, the engagement is already at TB_MAPPED. Otherwise, the engagement transitions to TB_MAPPED automatically on the manual override that leaves no row unmapped.
2. Verification
Database
SELECT count(*) AS total,
count(taxonomy_key) AS mapped,
count(*) FILTER (WHERE mapping_confidence = 'HIGH') AS high,
count(*) FILTER (WHERE mapping_confidence = 'MEDIUM') AS medium,
count(*) FILTER (WHERE mapping_confidence = 'LOW') AS low,
count(*) FILTER (WHERE mapping_confidence = 'MANUAL') AS manual,
count(*) FILTER (WHERE is_override = true) AS overrides
FROM fs_trial_balance_items WHERE engagement_id = '<engagementId>';
-- expect mapped == total, overrides ≥ 0SELECT status FROM fs_engagements WHERE id = '<engagementId>';
-- expect 'TB_MAPPED'Audit log
| Event type | Notes |
|---|---|
fs.mapping.auto_suggested | One per auto-suggest run. Payload includes token counts and mapped / unmapped totals. |
fs.mapping.overridden | One per manual override. Actor is the preparer. |
3. Negative & edge cases
- Claude returns an unknown taxonomy key — the handler refuses the bad key, logs a warning, and leaves the row unmapped. The reviewer maps manually.
- Claude returns valid JSON but with a sign that contradicts the constitution — the mapping is accepted (sign flips are tolerated at this step); the Statements step surfaces a
sign_flipanomaly. - Auto-suggest re-run after partial manual overrides — the worker upserts by
accountCodebut does not clobberisOverride = truerows. Manual judgements are preserved. - Claude error / timeout —
fs.mapping.auto_suggestedis emitted witherror: true. Engagement stays atTB_UPLOADED. Reviewer retries or maps manually. - Preparer changes a
HIGH-confidence mapping by override — allowed (humans win); the row flips toMANUALwithisOverride = true.
Next
Proceed to FS-04 · Generate statements.