Repository navigation
Description
Bug Report: archiveDecisions() silently fails on real-world content
Summary
Two logic bugs in packages/squad-cli/src/cli/core/nap.ts archiveDecisions() cause it to silently return null on real-world decisions.md files, allowing unbounded growth. A user reported their decisions.md reached 145KB / 2,219 lines / ~35K tokens — every agent spawn was burning 35K tokens on stale decisions before doing any work.
Bug 1: Undated entries immune to archival
Location: archiveDecisions(), lines ~335-343
daysAgoFromLine() returns null for entries without YYYY-MM-DD in their ### heading. The split logic puts null daysAgo into recent (the keep bucket) unconditionally:
if (e.daysAgo !== null && e.daysAgo > DECISION_MAX_AGE_DAYS) {
old.push(e);
} else {
recent.push(e); // ← undated entries end up here, never archived
}Impact: Any ### entry without a date is immortal. Foundational directives written before the date convention was established (like ### Type safety — strict mode non-negotiable) can never be archived.
Note: These undated entries are often foundational directives that should be preserved. The fix needs to distinguish "undated but permanent" from "undated and stale." A lifecycle state field (Status: permanent) would solve this cleanly — but even without it, the count-based fallback (Bug 2 fix) handles the growth problem.
Bug 2: Age-only pruning fails for active projects
Location: archiveDecisions(), line ~345
if (old.length === 0) return null; // ← 345KB file, returns nullIf ALL entries are <30 days old (realistic for any active project generating 15 decisions/session), archival does nothing regardless of file size. There is no secondary threshold, no count-based fallback.
Impact: An active project will blow past 20KB within days, and squad nap will report "nothing to clean up" while the file is hundreds of KB.
Origin
Both bugs trace to commit aaefeb1 (Feb 28 2026), where the implementation and all 38 tests were written in a single pass. Tests used synthetic 2024-01-XX dated fixtures — the parser's assumption ("entries always have dates") was never challenged, and the all-recent early-exit was never reached.
Existing Work (fork PR)
A TDD fix has been implemented and reviewed on diberry/squad:
- PR: fix(nap): archive undated decisions and add count-based fallback diberry/squad#38
- Branch:
squad/20-fix-nap-archival-bugs - Tests: 5 new test cases, 43/43 pass
- Implementation: 3-bucket split (
old/undated/recent) + count-based fallback
Review status (3 reviewers):
- ✅ Flight (Lead): Approved — correctness, data safety, architecture all clean
- ✅ FIDO (Quality): Approved — test coverage adequate, data-loss invariant verified
- ❌ Procedures (Prompt Engineer): Rejected on one point — undated entries in this codebase are foundational directives that should NOT be archived blindly. Recommended: keep undated in
recentuntil lifecycle states exist, rely on count-based fallback for size control.
Recommended Fix
Minimal (Bug 2 only — safest):
- Add count-based fallback to
archiveDecisions()— when all entries are recent AND file is over threshold, keep the most-recent N entries that fit under 20KB, archive the rest - Leave undated entries as-is (kept forever) until a
Status: permanentconvention is established
Full (both bugs):
- Count-based fallback (as above)
- Treat undated entries as archivable BUT add a
permanentmarker convention first - Update
squad.agent.md:869Scribe template to match nap.ts behavior
Related Issues (on diberry/squad)
| # | Title |
|---|---|
| #20 | decisions.md grows unbounded (parent issue + 10-agent team review) |
| #21 | Diagnostics framework — squad doctor, blame attribution |
| #22-37 | Follow-up action items from team review and postmortem |
Postmortem
Full postmortem available at: diberry#20 (comment)
Key lesson: "AI writes the tests it believes in, not the tests that break it." Adversarial test generation must be a deliberate separate step by a different agent.
Activity
diberry commentedon Mar 26, 2026
cc @bradygaster — this was found while investigating decisions.md scaling for a Squad user. The fork PR at diberry#38 has a TDD fix with 5 new tests (43/43 pass), full postmortem, and 3-reviewer code review. The count-based fallback (Bug 2) is approved by all reviewers; the undated-entry handling (Bug 1) needs a design decision on whether undated = foundational (keep forever) or undated = stale (archive). Happy to discuss.
bradygaster commentedon Mar 26, 2026
good one!
fix: add count-based fallback to archiveDecisions() (#626)
docs: update EECOM history with #626 learnings
fix: add count-based fallback to archiveDecisions() (#626)
docs: update EECOM history with #626 learnings
fix: add count-based fallback to archiveDecisions() (#626) (#627)
fix: banner polish — simplify layout, standardize formatting (github#626