rawResponse mutation broke SSI chain
Outcome attachment helper mutated a signed column post-commit, invalidating one row's chainHash. Detected by boot-time integrity check; service fail-closed. Architectural prevention deployed and broken row restored — full workspace chain reverified end-to-end (313/313 rows).
Detected: · Resolved:
Published response timeline
- done ·
Detected
Boot-time integrity check fail-closed the workspace. /api/audit/verify walked 312 prior rows successfully, then refused to certify row cmotjhpsyl3450nomj0q40ew7.
- done ·
Diagnosed
Removing the appended `outcome` key from rawResponse exactly reproduced the committed chainHash — byte-exact identification of the mutation site (attachOutcomeToDecision in /v1/outcomes).
- done ·
Honest verification UI shipped
Header chip recomputes the chainHash on every render. Mismatched rows now render '⚠ chain broken — why?' linked to a forensic page. Replaces the old optimistic non-null check.
- done ·
Architectural prevention shipped
New DecisionLog.outcomeAttached column (NOT in SIGNED_FIELDS) replaces the rawResponse mutation. Future outcome attribution cannot recreate this incident.
- done ·
Broken row restored
scripts/unbreak_chain.mts (Node-based; Python could not reproduce JSON.stringify byte-for-byte for Float columns) removed the appended outcome key, verified the recomputed chainHash matched the committed one, and wrote the restored bytes. Pre-write mutated state archived to incident-backups/ for forever-traceability.
- done ·
Workspace chain reverified
Full chain walk over the affected workspace returned CHAIN OK — 313/313 rows verified end-to-end. Header chip on the affected row flips from rose back to emerald. Boot-time integrity check no longer fail-closes.