Skip to content

Scribe archival can silently delete history: Safety Rules are prompt directives, not an enforced gate (2nd occurrence) #1836

Description

@bradygaster

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:

  1. squad consult writes .squad/ to the shared info/exclude, silently hiding state in main and all sibling worktrees #1826.git/info/exclude hides untracked files only. Tracked .squad/ files commit normally; new .squad/ files are silently unstageable while git status reads clean. An archive write appears to succeed and then evaporates. Requires git add -f.
  2. 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:

  1. Dangling-pointer scan. Walk .squad/**/*.md for archived at <path> / Prior archive at <path> and assert each target both exists on disk and returns exit 0 from git ls-files --error-unmatch. Any dangling pointer = fail loud, naming the file.
  2. Wire it into squad doctor, alongside the checkWorktreeEol check added in #1790's *.mjs text eol=lf does not repair existing working trees — 3 suites stay dead after merge #1793 — same shape, same reporting surface.
  3. 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=lf does 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 doctor fails 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.md for existing dangling pointers, each enumerated with recoverability
  • Rules A–E remain in the prompt but are no longer the only line of defense

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions