---
phase: 33-persistent-memory
plan: 01
subsystem: server/services
tags: [memory, sanitization, file-backed, rest-api, credentials]
provides:
  - File-backed assistant memory service (get/append/clear per companyId)
  - Credential sanitizer scrubbing sk-/ghp_/AIza/JWT/key=value patterns
  - REST endpoints GET/PATCH/DELETE at /api/assistant-memory/:companyId
affects: [33-persistent-memory, chat-routes, assistant]
tech-stack:
  added: []
  patterns: [file-backed JSON service, FIFO eviction, write-time sanitization]
key-files:
  created:
    - server/src/services/memory-sanitizer.ts
    - server/src/services/assistant-memory.ts
    - server/src/routes/assistant-memory.ts
    - server/src/__tests__/33-memory-sanitization.test.ts
    - server/src/__tests__/33-assistant-memory.test.ts
  modified:
    - server/src/app.ts
key-decisions:
  - "Removed zod dependency from assistant-memory.ts — zod is not installed in the worktree node_modules, replaced with manual type guard parsing"
  - "GitHub PAT regex changed from {36} to {36,} to handle tokens longer than expected minimum length"
duration: 4min
completed: 2026-04-01
---

# Phase 33 Plan 01: Persistent Memory Foundation Summary

**File-backed assistant memory service with write-time credential sanitization and REST endpoints mounted in app.ts.**

## Performance
- **Duration:** 4 minutes
- **Tasks:** 2 completed
- **Files modified:** 6

## Accomplishments
- `sanitizeMemoryFact` scrubs OpenAI (sk-), GitHub PAT (ghp_), Google API (AIza), JWT-shaped tokens, and key=value credential patterns at write time
- `assistantMemoryService` provides file-backed get/append/clear scoped per companyId at `data/assistant-memory/<companyId>.json`
- 50-fact FIFO cap enforced in append()
- REST routes at `/api/assistant-memory/:companyId` with assertBoard + assertCompanyAccess auth on all three verbs
- 17 unit tests pass (10 sanitizer, 7 service)

## Task Commits
1. **Task 1 (RED): Failing tests** - `378d1c11`
2. **Task 1 (GREEN): Memory sanitizer + service** - `fb3c1578`
3. **Task 2: Routes + app.ts wiring** - `eba57c5c`

## Files Created/Modified
- `server/src/services/memory-sanitizer.ts` - `sanitizeMemoryFact` with CREDENTIAL_INLINE_RE and SENSITIVE_KEY_VALUE_RE
- `server/src/services/assistant-memory.ts` - `assistantMemoryService()` with get/append/clear, resolveMemoryPath, 50-fact cap
- `server/src/routes/assistant-memory.ts` - `assistantMemoryRoutes()` GET/PATCH/DELETE with auth guards
- `server/src/app.ts` - Added import + `api.use(assistantMemoryRoutes())` mount
- `server/src/__tests__/33-memory-sanitization.test.ts` - 10 sanitizer tests
- `server/src/__tests__/33-assistant-memory.test.ts` - 7 service tests with os.tmpdir() isolation

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 1 - Bug] GitHub PAT regex too strict**
- **Found during:** Task 1 (TDD GREEN)
- **Issue:** `ghp_[A-Za-z0-9]{36}` matched exactly 36 chars, leaving trailing chars unredacted (`[REDACTED]k`)
- **Fix:** Changed to `{36,}` to match minimum 36 chars
- **Files modified:** `server/src/services/memory-sanitizer.ts`
- **Commit:** fb3c1578

**2. [Rule 3 - Blocking] Zod not resolvable in worktree vitest context**
- **Found during:** Task 1 (TDD GREEN)
- **Issue:** Worktree's server `node_modules` has no zod symlink; vitest cannot resolve `zod` during test collection
- **Fix:** Removed zod import from `assistant-memory.ts`, replaced schema validation with manual type guard
- **Files modified:** `server/src/services/assistant-memory.ts`
- **Commit:** fb3c1578

## Known Stubs
None — all data paths are fully wired.

## Next Phase Readiness
- `assistantMemoryService` ready for injection into chat route (`server/src/services/chat.ts`) in Plan 02
- Routes already mounted; API is live on server start
