Skip to content

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) — holds CONFIRM_FS_MAPPING and OVERRIDE_FS_MAPPING. Pre-state: Engagement at TB_UPLOADED (or TB_MAPPED if you are revising mappings). Post-state: Every FsTrialBalanceItem has a non-null taxonomyKey. Engagement at TB_MAPPED.

0. Prerequisites

  • Engagement at TB_UPLOADED (see FS-02).
  • The auto-suggest-mapping worker is registered and has access to an Anthropic API key (or the mock adapter is enabled — confirm via pnpm dev worker logs).
  • The SFRS taxonomy constitution loaded fine (run pnpm --filter @breezycorp/financial-statements test to confirm).

1. Steps

1.1 Run auto-suggest

http
POST /ops/financial-statements/engagements/<engagementId>/mapping/auto-suggest
Authorization: Bearer <fs-preparer-jwt>

(Or click Auto Suggest on the Mapping tab.)

The handler:

  1. Asserts CONFIRM_FS_MAPPING.
  2. Enqueues auto-suggest-mapping with {engagementId}.
  3. Returns 202 Accepted.

The worker:

  1. Reads every FsTrialBalanceItem for the engagement.
  2. 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).
  3. Parses the response — [{accountCode, taxonomyKey, taxonomyLabel, confidence, rationale}].
  4. Asserts every taxonomyKey exists in the constitution (findTaxonomyItem(key) != undefined); rows with unknown keys are kept null and re-flagged for manual mapping.
  5. Upserts FsTrialBalanceItem rows by (engagementId, accountCode).
  6. If every row is mapped, transitions engagement TB_UPLOADED → TB_MAPPED.
  7. Emits AuditEvent fs.mapping.auto_suggested with {inputTokens, outputTokens, mappedRows, unmappedRows}.

1.2 Walk LOW and MEDIUM rows

Open the Mapping tab. Filter for confidence != HIGH. For each row:

  1. Read the rationale tooltip (it carries the Claude justification).
  2. Open the taxonomy dropdown — it's constrained to the constitution. Type-ahead filters by key + label.
  3. 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:

http
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:

  1. Asserts OVERRIDE_FS_MAPPING.
  2. Asserts taxonomyKey exists in the constitution.
  3. Sets isOverride = true, mappingConfidence = MANUAL, and the rationale.
  4. Emits AuditEvent fs.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

sql
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 ≥ 0
sql
SELECT status FROM fs_engagements WHERE id = '<engagementId>';
-- expect 'TB_MAPPED'

Audit log

Event typeNotes
fs.mapping.auto_suggestedOne per auto-suggest run. Payload includes token counts and mapped / unmapped totals.
fs.mapping.overriddenOne 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_flip anomaly.
  • Auto-suggest re-run after partial manual overrides — the worker upserts by accountCode but does not clobber isOverride = true rows. Manual judgements are preserved.
  • Claude error / timeoutfs.mapping.auto_suggested is emitted with error: true. Engagement stays at TB_UPLOADED. Reviewer retries or maps manually.
  • Preparer changes a HIGH-confidence mapping by override — allowed (humans win); the row flips to MANUAL with isOverride = true.

Next

Proceed to FS-04 · Generate statements.

Internal use only — BreezyCorp