docs: revise Phase 9 design after pre-implementation audit
Audit of the existing codebase surfaced gaps the original design
glossed over. Corrected the design before writing code.
Architecture findings folded in:
- The four-layer system-prompt assembly described in
01-architecture.md is NOT implemented today.
chatTools.ts:85 hardcodes SYSTEM_PROMPT as a constant.
users.custom_instructions is stored but never read into a
prompt. workspaces.instructions is stored but never read.
systemPromptExtra is only used by projectChat.ts for document
context. Phase 9 now builds the full assembly chain in a new
promptAssembly.ts helper, behind a use_layered_prompt feature
flag (default true) on org_settings for emergency rollback.
- Two chat code paths exist (routes/chat.ts and
routes/projectChat.ts). Both call the new builder.
- Auth uses res.locals.userId / res.locals.userRole, not req.user.
Updated the API surface notes accordingly.
- Audit events are emitted inline (INSERT INTO audit_events ...)
following the lib/users.ts pattern. The phantom lib/audit.ts
reference is removed from the file list.
- pipApi exports flat functions (listProjects, createProject,
...). Updated the planned API to listMemories, createMemory,
updateMemory, deleteMemory, clearMemories, exportMemories.
- The admin page is /admin/policy/, not /admin/ai-policy/.
Concrete safety corrections:
- LRU eviction under concurrent add_memory calls now runs inside
a single transaction with SELECT ... FOR UPDATE on the user's
rows, eliminating the race where two callers both pass the
count check.
- Pin cap (10) is enforced by a Postgres trigger
(enforce_user_memory_pin_cap) at INSERT and UPDATE OF pinned.
Raises 23514 when exceeded. App layer translates to 409.
- Memory content is rendered as a fenced code block in the
system prompt with explicit PINNED: / OTHER: subheadings.
Hostile content (e.g. lines starting with ## or ---) cannot
alter the document's markdown structure.
- DELETE /api/me/memories?confirm=1 replaced with
POST /api/me/memories/clear body { confirm: true }. CSRF-safe:
a JSON-body POST is not issuable cross-origin without explicit
fetch.
- POST /api/me/memories forces source='user' server-side and
ignores any source field in the body.
- Export endpoint returns chat_id as a bare uuid; does not
hydrate chat title or other chat metadata, to avoid leaking
metadata of chats the caller can no longer access.
- Audit actor for model-driven memory.create is the chat owner;
metadata carries actor_is_system: true and the chat_id.
Migration corrected:
- Adds touch_updated_at trigger for the new table, matching the
convention in 0002_users.sql, 0006_projects.sql, etc.
- Adds the pin-cap trigger function and trigger.
- Adds RLS enable + force + revoke from anon/authenticated, in
line with 0011_rls.sql.
- Adds use_layered_prompt to org_settings as the rollback switch.
- Confirmed next migration number is 0022 (last is 0021_branding).
LlmPolicy interface gains allow_memory, memory_max_per_user,
memory_max_chars, and use_layered_prompt loaded from org_settings.
loadLlmPolicy() selects the new columns.
| Repository | cpatpa/PIP |
|---|---|
| Author | Claude <noreply@anthropic.com> |
| Authored | |
| Parents | 5f2e7203 |
| Stats | 1 file changed , +177 , -65 |
| Part of | Phase 9 - persistent user memory and layered system-prompt assembly |
Capture this commit into my fork
Download a Markdown prompt that tells Claude how to port this
exact commit into your working tree. Run it via
claude -p < capture-commit-84aafcd6.md
from inside the repo you want the change in.