Skip to content

Test coverage for memory + scratchpad subsystem#104

Open
mlund01 wants to merge 1 commit into
mainfrom
claude/memory-test-coverage
Open

Test coverage for memory + scratchpad subsystem#104
mlund01 wants to merge 1 commit into
mainfrom
claude/memory-test-coverage

Conversation

@mlund01
Copy link
Copy Markdown
Owner

@mlund01 mlund01 commented May 28, 2026

Summary

Follow-up to #102. The post-ship audit flagged that the memory + scratchpad rework had solid coverage at the config-parse and store-build layers (~90%) but the tool surface, the agent auto-attach branch, the prompt formatter, and the wsbridge file browser were untested (~20-30%). This PR closes those gaps.

50 new tests, ~960 lines, no production-code changes.

What's covered

aitools/memory_tools_test.go (27 tests)

Direct unit tests for all six file_* tools against a fake MemoryStore that backs slots with real temp dirs (so syscalls exercise actual on-disk behavior):

  • Every tool's schema advertises slot (not legacy folder)
  • Legacy {\"folder\": ...} JSON payloads are rejected at unmarshal
  • file_list: flat, recursive, pagination, empty, unknown slot
  • file_read: max_lines, max_bytes, missing path, directory rejected, ../ escape
  • file_create: create-new, fail-if-exists, overwrite, append (+ missing target), parent-dir creation
  • file_delete: happy path, directory rejected
  • file_search: regex match, no match, bad regex
  • file_grep: flat, recursive, no-recursion-by-default

agent/internal/prompts/prompts_test.go (7 tests)

FormatMemoryContext behavior:

  • nil store and empty store both return empty string
  • memory slot gets the persistent-mission label
  • scratchpad slot gets the ephemeral label
  • Shared slots get no special label
  • Output names the slot param and all six file_* tools
  • Iteration order is preserved (sort lives in mission/memory_store.go, not prompts)

wsbridge/memory_browser_test.go (13 tests)

File-browser RPC layer that was previously fully untested:

  • collectMemoryInfos: shared + per-mission memory both emitted with correct paths under SquadronHome, Editable: true for all
  • Missions without memory { } are skipped; scratchpads aren't exposed (preserved behavior, now pinned by test)
  • resolveMemoryPath: shared memory, mission memory, unknown name, collision (shared wins — pinned so a future priority swap is loud), mission without memory{} not resolvable
  • resolveSafePath: rejects ../, mid-path ../, absolute paths; accepts valid relative and empty (root)
  • End-to-end read of a real file via the resolver

agent/memory_tools_attach_test.go (3 tests)

The auto-attach contract — that file_* tools appear iff a MemoryStore is wired:

  • All six tools appear when MemoryStore != nil
  • None appear when MemoryStore == nil
  • Attached tools are wired to the exact store passed in (not nil, not a different instance)

What's still uncovered

Low ROI, deliberately skipped:

Test plan

  • go build ./...
  • go test ./... — all 19 packages pass
  • New tests run in ~2s combined, no flakes across multiple runs

🤖 Generated with Claude Code

Follow-up to #102 — fills the test gaps the post-ship audit flagged at the
tool-implementation, agent auto-attach, prompt-formatter, and wsbridge
file-browser layers.

All new tests use Ginkgo + Gomega to match the project's BDD pattern.

aitools/memory_tools_test.go (28 specs, package aitools_test)
  - schema: every file_* tool advertises `slot`, not legacy `folder`
  - schema: legacy {"folder": ...} payloads are rejected at unmarshal
  - file_list: flat, recursive, pagination, empty, unknown slot
  - file_read: contents verbatim, max_lines, max_bytes, missing path,
    directory rejected, path traversal rejected
  - file_create: new, fail-if-exists, overwrite, append (success +
    fail-if-missing), creates parent dirs
  - file_delete: file removed, directories rejected
  - file_search: regex match, no-match summary, bad regex
  - file_grep: flat match, recursive descent, no-recursion-by-default

agent/internal/prompts/prompts_test.go (7 specs, new suite)
  - FormatMemoryContext nil/empty → empty string
  - "memory" slot gets the persistent-mission label
  - "scratchpad" slot gets the ephemeral label
  - Shared slots get no reserved-name label
  - Output advertises `slot` param + names all six file_* tools
  - Iteration order is preserved (sort lives in mission/memory_store.go)

wsbridge/memory_browser_test.go (17 specs, package wsbridge, joins the
existing Wsbridge HumanInput Suite)
  - collectMemoryInfos: shared + per-mission memory both emitted with
    correct paths under SquadronHome
  - collectMemoryInfos: Editable=true for every entry (no read-only mode)
  - collectMemoryInfos: missions without memory{} skipped, scratchpads
    not exposed (preserved behavior, pinned)
  - resolveMemoryPath: shared, mission, collision priority (shared wins
    — pinned so a future priority swap is loud), unknown name, mission
    without memory{}, end-to-end real file read
  - resolveSafePath: rejects ../, mid-path ../, absolute paths; accepts
    valid relative + empty (root)

agent/memory_tools_attach_test.go (3 specs, new suite, package agent_test)
  - All six file_* tools attached when MemoryStore is non-nil
  - All six omitted when MemoryStore is nil (negative case)
  - Attached tools wired to the exact store passed in

What's still uncovered (low ROI):
  - cmd/engage.go runScratchpadCleanupLoop — tiny shim around
    mission.SweepExpiredScratchpads
  - Full end-to-end mission with memory + scratchpad — covered by the
    manual smoke test in #102; a true integration test would need an
    LLM call
@mlund01 mlund01 force-pushed the claude/memory-test-coverage branch from 5a1a26a to 4f30aa7 Compare May 30, 2026 03:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant