Skip to content

arch: Scribe bypasses the governed memory pipeline — entire mutable-state lifecycle uses squad_state_* exclusively #1268

Closed as not planned

Description

@tamirdresher

Summary

Scribe (packages/squad-cli/templates/scribe-charter.md + spawn template at squad.agent.md:600-618) runs the entire mutable-state lifecycle — inbox merging, decisions archival, history summarization, cross-agent propagation — using squad_state_* tools exclusively. It never calls any memory.* tool. This means the governance pipeline (classifier → FORBIDDEN scan → audit) only ever runs at first-write, not at any downstream lifecycle event.

The classifier is single-touch: it engages when an agent calls memory.write directly, and never again. Every promotion, archival, propagation, or merge bypasses it.

Evidence

Scribe's task list, verbatim from squad.agent.md:600-618:

Step What Tool used
0 Health check squad_state_health ✓
0b Pre-check decisions/inbox state squad_state_list + squad_state_read
1 Decisions archive (>=20KB → 30d, >=50KB → 7d) implicit rewrite (no audit row)
2 Merge inbox into decisions.md squad_state_list + squad_state_read + squad_state_write + squad_state_delete
3 Write orchestration log squad_state_write
4 Write session log squad_state_write
5 Cross-agent updates to others' history.md squad_state_append
6 Summarize history.md (≥15KB) implicit rewrite
8 Health report squad_state_write / squad_state_append

Verified against packages/squad-sdk/src/memory/index.ts:69-79 — the MemoryAuditRecord type supports 'classify' | 'write' | 'reject' | 'promote' | 'delete' | 'search' | 'configure' | 'provider-error'. None of these get written during Scribe's lifecycle steps.

Compare to the general "Memory Governance Tools" section at squad.agent.md:275-278 which IS pitched as general advice for all agents — Scribe's own template bypasses it.

Concrete failure modes

1. Forensics gap — archived items have no provenance

When Scribe archives a 35-day-old decision during the 20KB trigger, the audit log has no entry. If three months later you ask "when did we lose the X decision?" — there's nothing in audit.jsonl to answer. The decision is preserved (in decisions-archive.md) but the disposition event is invisible.

A memory.delete call writes a tombstone with id, actor, timestamp, reason. Scribe's rewrite produces nothing equivalent.

2. Late-discovered FORBIDDEN content can propagate

If an agent wrote a decision via squad_state_write directly (bypassing the classifier — which is allowed today), the content lands in decisions/inbox/. Scribe step 2 reads it via squad_state_read and writes it to decisions.md via squad_state_write. The FORBIDDEN scan never runs on the inbox-to-ledger transition. A credential or PII would propagate from inbox to canonical ledger silently.

If Scribe step 2 called memory.classify on each inbox entry before merging, late-discovered FORBIDDEN content would be caught.

3. Cross-agent propagation bypasses classification

Step 5 appends 📌 Team update (...): {summary} — decided by {Name} to N affected agents' history.md files. The summary text is whatever Scribe extracts from the decision. If the original decision body had something that needed redaction, the summary inherits it. No classifier touch.

4. memory.promote is dead code

The SDK has the full promote(id, targetClass) API (packages/squad-sdk/src/memory/index.ts:1328-1338-area, exposed via PR #1251's bridge as memory.promote). Nothing in the spec or any agent's charter ever calls it. There's no automated path that says "this LOCAL entry has been cited 5 times — promote it to DECISION."

5. lastReadAt / usage stats don't exist

memory/index.json tracks createdAt / updatedAt but not lastReadAt. Since reads happen via filesystem grep (the read-side gap I previously surfaced), there's nothing to update even if the field existed. Without read tracking, Scribe can't make importance-aware archival decisions (see related issue: importance-aware archival).

Proposed direction

Scribe should be the showcase consumer of the governed memory pipeline, not its biggest blind spot.

Phase 1 — additive, non-breaking:

  1. Step 2 (inbox merge): call memory.classify on each entry. If FORBIDDEN, log and skip; otherwise proceed.
  2. Step 1 (archive): replace bare file rewrite with memory.delete(id, actor='scribe', reason='archive-30d-trigger') per archived block. This writes the tombstone.
  3. Step 0b: optionally call memory.audit to get the existing audit trail for the current decisions.md state — useful for the health report.

Phase 2 — once usage data exists:
4. Step 1: archival decisions consult memory/index.json for class and loadGuidance — never archive POLICY or loadGuidance: ALWAYS (see related issue).
5. Step 6 (history summarization): use memory.search(author) to find related governed entries before discarding history detail.

Phase 3 — long term:
6. memory.promote automation: items cited N times via search get auto-promoted to DECISION; items unread for N days get auto-demoted to ARCHIVE.

Related

  • #1243 — history.md vs governed memory direction (touches the cross-agent propagation point)
  • #600 and #595 — tiered history retrieval (overlaps with summarization step)
  • PR #1251 — the bridge that makes memory.* MCP-callable in the first place (prerequisite for any fix here)
  • #1037 (closed) — context budget proposal that anticipated this kind of integration

Severity

🔴 Critical for governance integrity, not for day-1 functionality. Squads work today; the audit log just doesn't capture what's actually happening during lifecycle events. As governed memory adoption grows (post-PR #1251), this gap becomes the chokepoint for any compliance, security, or forensics story.

Activity

  1. added
    stalled-initiativeWork area that was started but never completed — cut, close, finish, or reprioritize
    on Aug 30, 2026
  2. bradygaster commented on Sep 24, 2026

    @bradygaster
    Collaborator

    Consolidation direction: #1270 is the canonical architecture issue for the governed-memory versus raw-state API boundary. This issue remains open as the concrete Scribe lifecycle gap (reclassification, audit/provenance, promotion, and cross-agent propagation) to implement after that direction is chosen. #1243 separately owns bounded history.md context and authority.

  3. bradygaster commented on Nov 5, 2026

    @bradygaster
    Collaborator

    Consolidated into #2392 during 2.0 board triage (owner decision). The decision and its validation tests are tracked there; original text remains here for reference.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    stalled-initiativeWork area that was started but never completed — cut, close, finish, or reprioritizetriagedIssue has been triagedwave:2-soonWave 2 — next 2-4 weeks

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions