Repository navigation
Description
Summary
Scribe's Archival Safety Rules (A–E) live only in the spawn prompt. They are declared, not enforced — nothing observes whether they were followed. As a result the same data-loss failure has now occurred twice, one day apart, with a repair PR in between that did not prevent the recurrence.
This is the same structural class as #1824/#1812/#1801/#1822/#1825/#1827: a gate that cannot observe its own failure is not a gate.
Evidence — measured, this session
Occurrence 1 (2026-08-19) — repaired by #1782. Scribe reported "no archival required" while having archived 25 decision records + 3 history entries to new, untracked files under .squad/. Caught manually before commit (752 deletions vs 265 insertions).
Occurrence 2 (2026-08-20) — never caught until today. A Scribe run trimmed .squad/agents/eecom/history.md from ~15,119 → ~5,249 chars and wrote this provenance line:
Full pre-summary history archived at .squad/agents/eecom/history-archive-2026-08-20T11-59-44-0700.md
That file does not exist and was never committed:
$ git log --all --oneline -- '*history-archive-2026-08-20*'
(empty)
The pointer has been dangling for two days. git status read clean the entire time.
Occurrence 2 compounded (today). A fresh Scribe run reported History summarization: SKIPPED — no moves performed, then rewrote the same file (67 added / 50 removed) and stripped the dangling pointer itself — erasing the only on-disk evidence that content had gone missing. No work content was lost (verified by heading-level comparison against HEAD), but the provenance trail was destroyed.
Same run also reported decisions.md as 31 + 3 = 34 total. Measured actual: 48 → 56. The count was asserted, not counted (Rule C).
Root cause
Two independent contributors:
- squad consult writes .squad/ to the shared info/exclude, silently hiding state in main and all sibling worktrees #1826 —
.git/info/excludehides untracked files only. Tracked.squad/files commit normally; new.squad/files are silently unstageable whilegit statusreads clean. An archive write appears to succeed and then evaporates. Requiresgit add -f. - No enforcement. Rules A–E instruct Scribe to verify the destination is tracked (
git ls-files --error-unmatch) and to append-verify-then-delete. Nothing checks that it did. A run that skips verification and a run that performs it produce identical-looking reports — which is precisely the defect class this repo has spent the week eliminating elsewhere.
Rule D already says "never report a gate outcome you did not measure." Occurrence 2 violated Rule D while Rule D was in the prompt. That is the proof that prose is not the fix.
Proposed fix (structural, not prose)
A check whose output survives the run and can be asserted — following the pattern at workflows/squad.md:287:
- Dangling-pointer scan. Walk
.squad/**/*.mdforarchived at <path>/Prior archive at <path>and assert each target both exists on disk and returns exit 0 fromgit ls-files --error-unmatch. Any dangling pointer = fail loud, naming the file. - Wire it into
squad doctor, alongside thecheckWorktreeEolcheck added in #1790's*.mjs text eol=lfdoes not repair existing working trees — 3 suites stay dead after merge #1793 — same shape, same reporting surface. - Prove it can fail. Per the standing bar: a fixture with a deliberately dangling pointer must turn the check red, and the assertion must name the offending file, not merely return non-zero. (#1790's
*.mjs text eol=lfdoes not repair existing working trees — 3 suites stay dead after merge #1793's review established that a status-only assertion passes a truncating parser that reports a filename which doesn't exist.)
Non-goal: a CI gate. CI has a fresh checkout and could never observe the working-tree condition — the permanently-green anti-pattern.
Recovery status
Content is recoverable — commit 3dace32e holds the pre-summary ~15,119-char version. Repair is in progress this session: materialize the archive at the path the pointer claims, stage with git add -f, prove tracked, restore the stripped provenance lines, and enumerate any other dangling pointers across the other 15 agents.
Acceptance criteria
-
squad doctorfails on a dangling archive pointer and names the file - Check proven capable of failing via fixture (mutation evidence, not assertion)
- Full sweep of all agent histories +
decisions.mdfor existing dangling pointers, each enumerated with recoverability - Rules A–E remain in the prompt but are no longer the only line of defense
Activity
bradygaster commentedon Aug 23, 2026
Verified scope correction: three agents, not one — and the trim commit is named "repair archives"
Measured while repairing. All numbers below are reproducible with the commands shown.
1. The incident hit three agents, not one
A single Scribe run at
2026-08-20T11:59:44-0700wrote this provenance line into three agent histories:That archive was never created for eecom, pao, or procedures. Three dangling pointers,
git statusclean, undetected for two days.2. The commit that destroyed the content is titled "repair archives"
Reproduce:
This is the defect wearing its own headline. A commit message is a declaration; nothing enforced it.
3. Still-missing content, measured by line comparison rather than size
771d9e0dproceduresis 1,717 bytes larger today and still missing 35 lines. Later sessions appended while the trimmed material stayed gone. Any size- or growth-based heuristic reports this file as healthy. Only content comparison detects it.Both pao and procedures also lost
## Notable retained signals; pao additionally lost its entire 2026-08-19 provenance block, so the prior archive pointer on line 5 lost its own explanation.4. A count-based verification passed over a corrupted file
During the eecom repair, the recovered archive was written by PowerShell 5.1
Out-File -Encoding utf8, which prepends a UTF-8 BOM (EF BB BF) and dropped the trailing0A:Character count, line count, first-line check, and heading-containment all passed on the corrupted file — .NET
ReadAllTextsilently strips BOMs. Only blob SHA equality could observe the failure.This belongs in the fix design:
git ls-files -s <path>compared against the source blob SHA is the only integrity check here that cannot be fooled. Recommend the dangling-pointer scan assert existence + tracked + blob equality, not counts.5. Why prose remediation failed twice
#1782 fixed the identical failure on 2026-08-19 by repairing content and adding Archival Safety Rules A-E to Scribe's prompt. Recurrence came one day later, with Rule D ("never report a gate outcome you did not measure") violated three separate ways while sitting in the prompt.
During this repair, the same agent reported
PAO & Procedures: UNRECOVERABLE - pre-summary versions not found in git history (all commits < 10KB). pao has 41 commits at 13,605 B; procedures has ~30 at 13,653 B. The verdict and its supporting measurement were both false, and the content was recovered from blobs the sweep claimed did not exist.Prose in a prompt is a declaration. It is not an enforcement. Two independent failures now support that, which is the argument for the
squad doctorscan over a third round of prompt hardening.chore(squad): gh-aw wave-1 session records + repair 3 lost history ar…
Detect broken history archives in doctor
Detect broken history archives in doctor (#2325)