ABSoW Stamp Clear — Centralized in Write Service
Date: 2026-07-30 (session 65)
Type: decision + service change
Commits: entirely-api a161f07 (code + tests), d7dc8c2 (workflow diagram)
Rule
The migration-026 invariant — any SOW line create/delete/scope edit clears
projects.absow_reconciled_at/by — is enforced inside
services/supabase_write_service.py, not in routes:
create_sow_line,create_synthetic_sow_line,delete_record(sow_lines), andupdate_sow_line(only when the write touches a scope key)._SOW_SCOPE_KEYS= permitted quantity + linked product + linked project. As-built / inspected / notes / production-days / post_production writes do NOT clear — applying as-built IS the reconciliation, sofield_inspection.update_absow(and therefore the ABSoW proposal apply path) never self-clears. No opt-out flag exists or is needed.- Billing pseudo-products exempt: product codes
ADM-*andPRD-*(trip charges, rush fees, PRD-DAY production-day propagation) never clear the stamp — billing, not scope. Decision made with Dustin 2026-07-30.
Do NOT add per-route clears. The three cert_gen route-level clears were removed; every current and future SOW-mutating path (document parser reparse, field paths, intake, portal_files, whatsapp, trip charge, service-layer creators) inherits the centralized clear.
Audit trail
audit_log.db now records category absow: unreconcile (inside
clear_absow_reconciliation with caller + prior stamp, and the manual portal
route), reconcile (manual portal route), proposal_apply
(absow_proposal_service.apply_proposal). Closes the attribution gap from
the VAB-9002 stamp investigation (2026-07-30), where an out-of-band
create_sow_line_from_product_code call added scope without touching the
stamp and nothing recorded who/what changed reconciliation state.
Known edge (accepted)
Parent-service enrichment (create_synthetic_sow_line for missing WTR/ENC
parents at cert-validation/invoice time) DOES clear the stamp — strict
reading of the invariant. A reconciled project that gets enriched will need
one re-reconcile round-trip; self-heals because the parent then exists.
Related
cert-generation-v3 · confidence-routing · migration 026 ·
tests/test_absow_stamp_clear.py