⌂ Home ☷ Board

PKA Outside-Eyes Audit — 2026-04-20

Executive Summary

Top-line verdict: This is a sophisticated, well-designed system with excellent architectural discipline — high marks for memory model, agent separation, and strategic dispatch. The main risk is over-engineering at the margins — GBrain is powerful but adds moving parts, and some memory/rule guidance is contradictory or stale. Fix the contradictions first; the moving parts are fine if GBrain holds.


1. Architecture at a Glance

Lucienne is a multi-machine Chief of Staff system running across four hosts:

Knowledge layers (most to least important):

  1. vault.db (local, Lucienne-owned): Knowledge graph over markdown files. Markdown is source of truth; DB is rebuildable index (v2 architecture). Contains files from PKA + ~/.claude/vault/ (shared personal data) + SecondBrain (from index.py scans).
  2. mc.db (remote, Luci-owned): Operational database (tickets, task_runs, sessions, heartbeats). Vault MCP server merges both at query time via HTTP.
  3. GBrain (hybrid retrieval): New layer (2026-04-17 decision, phase 5/6 in progress). Indexes SecondBrain + PKA wiki + PKA docs. Retrieval is 31% better than vault.db waterfall; 1000× faster (20ms vs 22s p50). Replaces old wiki-query/search-brain/LightRAG.
  4. Auto-memory layer: Machine-specific (Mac: ./Vault/memory/auto/mac/). Claude's memory system appends here per session; gets indexed into vault.db automatically.

Team model: Permanent Chief of Staff (Lucienne) + permanent PM (Luci) + permanent specialists (Atlas, Tessa, Scott) + lightweight auto-dispatch agents (architect, developer, reviewer, qa, security) + retained consultant (Larry). No persona briefs — roles defined in agent YAML + short team patterns.


2. Memory System Review

What's Good

  1. Markdown-first discipline. CLAUDE.md lines 279-285 declare it crisply: "Markdown files are the source of truth. vault.db is a query index over them. ... Only activity_log is authoritative in the DB" (line 85 SETUP.md clarified this on 2026-04-18 — activity_log was demoted from authoritative to derived). This is a correct and defensible choice.

  2. Auto-memory routing per device. Boot sequence (CLAUDE.md line 5) verifies .claude/settings.local.json contains "autoMemoryDirectory": "./Vault/memory/auto/mac/". Each machine writes to its own subdirectory. This solves the "which device did Claude write this from?" ambiguity that killed Echo's memory system.

  3. Frontmatter discipline. Memory files use consistent YAML: name, description, type (user|feedback|project|reference), lifecycle (active|archived), created, last_reviewed, stale_after. Schema v2 (Vault/schema.sql) includes a v_memory_health view that flags stale memories by checking stale_after dates. This is solid.

  4. Activity log as derived index. The decision to rebuild activity_log from text feeds (skills_log.jsonl, git log, task frontmatter, session transcripts) rather than writing to it live is clever — avoids sync conflicts across machines and keeps markdown as canonical.

  5. 139 memory files indexed. Not bloated; each has a clear purpose.

What's Broken or At-Risk

  1. Memory file guidance is contradictory and stale. CLAUDE.md lines 77-79 say:

    "invoke memory-manager skill before answering... Applies to DECLARATIONS too — 'doesn't happen in our setup', 'we haven't done X'... check memory BEFORE denying, not after Elmar corrects you."

But MEMORY.md line 46 (the actual memory index entry for this) is:

[Load Skills First](feedback_load_skills_first.md) — Always load the relevant skill before Luci tasks; don't wing it from memory.

And CLAUDE.md line 351 says:

Fall back to vault.db graph queries only if GBrain is unreachable or the topic is clearly structured-ops (activity_log, tasks, sessions).

Contradiction: Should Lucienne use memory-manager skill, or query vault.db, or GBrain? All three reference memories exist. The CLAUDE.md dispatch order is: GBrain first (line 351) → vault memory search for agent outcomes (line 352) → Atlas/wiki for system questions (line 350). But the memory file layout doesn't distinguish "agent outcome memories" from "reference memories" — they're all in Vault/memory/, mixed together, with some living in auto/ and others not.

Fix required: Rewrite MEMORY.md frontmatter to tag each file with query tier (gbrain_first | vault_first | check_before_denying). Update CLAUDE.md dispatch logic to match. As-is, a session will try all three methods in the wrong order and waste context.

  1. No curation/garbage-collection rule. vault.db has 139 memory files. The schema includes stale_after dates and a v_memory_health view, but I found no automated job that sweeps old memories into an archive. CLAUDE.md mentions "Do not pre-read specialist files... memory files at boot" (line 26) but doesn't say what happens to a memory that reaches stale_after date. Does it get archived? Deleted? Flagged in the dashboard?

Symptom: project_mc601_cancelled.md (MEMORY.md line 24) is marked cancelled 2026-04-20 but still indexed. If Elmar asks "should we do PostgreSQL migration?", vault.db will surface the "cancelled" note — which is correct behavior, but only if the note is reviewed regularly. No evidence that happens.

Fix required: Add a monthly cycle: "Atlas reports on stale memories" or a dashboard card flagging v_memory_health rows where is_stale = 1.

  1. "Lifecycle" frontmatter never used in queries. vault.db schema defines lifecycle: active|archived (SETUP.md line 125) but vault.mcp.py never checks it. A memory tagged lifecycle: archived still appears in searches and gets indexed into vault.db. Archival is only a semantic signal, not a query filter.

Fix required: Either make lifecycle a real query filter in vault_mcp.py (skip archived in search results), or remove the field and use subdirectories (./Vault/memory/_archived/) instead.

  1. MEMORY.md is a pointer, not a query. It's a human-readable list of files but has no machine-readable aliases for matching triggers. Feedback file feedback_auto_dispatch.md says "auto-dispatch agents autonomously without being asked" but CLAUDE.md lines 77-79 reference "memory-manager skill" to check before answering. Where is memory-manager defined? I found it in the skills list at the top of this review but no MEMORY.md entry pointing to its behavior.

Risk: Session may invoke memory-manager skill when a simpler vault search would suffice, or vice versa.


3. SecondBrain / Wiki / vault.db Review

What's Good

  1. GBrain adoption is well-executed. Decision doc (2026-04-17-gbrain-phase5-decision.md) is rigorous: 30-query A/B across 5 classes, scored on Recall@5 and nDCG@5. GBrain wins on 4 of 5 classes (person +51%, financial +46%, temporal -18% but still GBrain, entity +40%) and loses only on "decision" class where vault.db's LLM synthesis edge is noted but deemed trivially replaceable (Lucienne naturally synthesizes from chunks). Latency is 1000× better (20ms vs 22s).

  2. Phased rollout is sensible. Phase 5 (decision) → Phase 6 (wiring) with clear Week 1/Week 2 breakdown. GBrain MCP is already in .mcp.json. Luci parallel install planned. CONFIG file (atlas.md:47) correctly documents the three scopes: SecondBrain + PKA wiki + PKA docs.

  3. wiki-compiler is NOT retiring. (project_gbrain_operational.md lines 36-40). The confusion is real but addressed: "compilation script (compile.py) is NOT retiring — it still produces the wiki pages GBrain indexes. What IS retiring is using wiki-compiler as a retrieval mechanism (the wiki_lookup and semantic_search MCP tools)". This distinction is explicit and correct.

  4. Skill thin-adapter pattern. Retiring wiki-query / search-brain by rewriting their bodies to call gbrain.query() (not retiring the skill names) means existing agent calls don't break. Clever and low-risk.

  5. Platform-aware paths. vault_mcp.py lines 44-48 detect platform.system() == "Darwin" to set WIKI_ROOT correctly. Cross-machine knowledge flows through feedback_cross_machine_knowledge.md (2026-04-19 padel-racket gap) which explicitly tells Lucienne to query GBrain before claiming "no prior work on X".

What's At-Risk

  1. GBrain has a stale-PID bug that's been fixed but not widely documented. project_gbrain_operational.md lines 42-52 document it; feedback_gbrain_stale_pid.md has quick-fix (rm the file). But atlas.md (which every PKA system dispatch reads) has zero mention of GBrain as a tool chain entry point. If Atlas needs to query GBrain during a brief and GBrain fails to connect (e.g., stale PID), Atlas will silently skip it and not consult the wiki, violating the 2026-04-19 hard rule in atlas.md lines 47-49 ("FIRST tool call must be mcp__gbrain__query").

Fix: Add a "GBrain health check" to the PreToolUse hook (currently only fires on Glob|Grep). Have it remove stale postmaster.pid on session start.

  1. No documented fallback if GBrain is unreachable or queries fail. CLAUDE.md line 351 says "Fall back to vault.db graph queries only if GBrain is unreachable". But what IS "unreachable"? A network error? A timeout? An empty result set? There's no defined timeout, no retry logic, no fallback behavior in vault_mcp.py if the GBrain MCP fails.

Risk: Session may hang waiting for GBrain, or silently degrade to wrong results if the hybrid retriever returns partial data.

  1. SecondBrain indexing is one-way and stale-prone. index.py lines 78-90 scan ~/cowork/SecondBrain/wiki/ daily via cron (implied by "daily" in CLAUDE.md line 196). But there's no evidence of a validation that vault.db's SecondBrain index is up to date. If SecondBrain changes and git pulls don't sync immediately, vault.db can be out of date by up to 15 min. GBrain has ~/.gbrain/pka-sync.sh running every 15 min (project_gbrain_operational.md line 57), but vault.db has no parallel sync guarantee.

Risk: Two different indexing schedules (vault.db daily, GBrain every 15 min) can diverge. If Lucienne queries vault.db and gets stale results while GBrain is fresh, she won't know.

  1. "Check SB markdown before building extractors" (memory line 81) is a pattern, not a process. It says "CoWork/SecondBrain/sources/ already has wiki-ingest-converted markdown for most docs" but provides no catalog or search interface. A session would have to browse the folder manually to know if a source already exists.

Fix: Create a dashboard card listing source files in SecondBrain/sources/ so Lucienne can check at a glance before building a new extractor.


4. Tool Call / Agent / MCP Review

What's Good

  1. Agent tool allowlists are correctly scoped. I checked developer.md, atlas.md, scott.md, reviewer.md. Each has a tools: line restricting what they can call. Examples:
  2. atlas.md: Read, Grep, Glob, Edit, Write, Bash, mcp__gbrain_* (all variants), Skill
  3. developer.md: Read, Edit, Write, Bash, Grep, Glob, mcp__gbrain_*, Skill, WebFetch
  4. scott.md: Read, Grep, Glob, WebFetch, WebSearch, Bash, Skill, mcp__gbrain_, mcp__vault_
  5. reviewer.md (line 15): "Reads code, checks against company style and spec; suggests fixes; submits to team. No Edit or Write. Flags findings; developer fixes."

This is correct. Reviewer can't edit because that violates the review gate.

  1. PreToolUse hook for Graphify. .claude/settings.json lines 37-47 inject a reminder to consult graphify-out/GRAPH_REPORT.md before using Glob|Grep on codebase questions. This is smart — prevents re-deriving god nodes from source when the AST analysis already has it.

  2. UserPromptSubmit hook injects datetime. Lines 27-35 inject "Current local datetime: <formatted>". This prevents the "what time is it" ambiguity. Documented in feedback line 70: "Clock now injected via UserPromptSubmit hook; read the injected datetime, don't guess".

  3. Vault MCP server has graceful fallback. vault_mcp.py lines 20-28 try to import salience re-ranker but continue if unavailable. FTS5 ordering is the fallback. This is good defensive coding.

  4. Two-DB split is architecturally sound. vault.db (Lucienne, markdown-first) vs mc.db (Luci, operational). Vault MCP server exposes both transparently via HTTP (project_vault_db_split.md). No write conflicts because they own different domains.

What's Broken or At-Risk

  1. Atlas has a documented blind spot (2026-04-19 feedback). atlas.md lines 81-94 detail a "pre-ruling checklist" added after Atlas missed the exco-ingest function migration from Luci to Mac LaunchAgent. The rule says: before declaring something missing, check wiki grep (name AND function), Mac LaunchAgents, sources-watch.yaml, and Luci scheduler tasks.

The rule is clear but reactive. It was added AFTER a failure. There's no automated check preventing this pattern. If Atlas reasons from incomplete search, they'll make the same mistake again (now with a written rule to consult, but still prone to human skip). This is a soft control, not a hard gate.

Fix: Create a check_multi_host Bash wrapper that checks all four locations in parallel when Atlas invokes it. Make it part of the pre-brief checklist.

  1. Skills have overlapping / vague trigger descriptions. I checked 10 skill descriptions:
  2. wiki-compiler: "Triggers on 'wiki compile', 'compile wiki', 'update wiki'..." ✓ Clear
  3. search-brain: [Not readable in output, but MEMORY.md line 46 references it] — likely vague overlap with GBrain now that it's thin-adapter
  4. brain: [Available but not read] — possible name collision with "brain explorer"
  5. autoplan: Triggers on "auto review", "autoplan", "run all reviews" ✓ Clear but only works if the user says those words
  6. memory-manager: [Expected to exist from CLAUDE.md line 14 but not found in skills list] ⚠️ Missing or misnamed

Risk: If memory-manager is missing, CLAUDE.md line 14's "call mcp__vault__memory_search or invoke the memory-manager skill" is a false instruction.

Fix: Verify memory-manager exists at ~/.claude/skills/memory-manager/. If not, either create it or remove the instruction from CLAUDE.md.

  1. Skill tool allowlists missing from some skill definitions. E.g., wiki-compiler/SKILL.md has no tools: line in frontmatter. If this skill is dispatched as a subagent, it will inherit the default tool set, which may be broader than needed.

Fix: Add tools: frontmatter to all skills. Recommend: Bash, Read, Write, Edit, Glob, Grep, WebFetch, Skill (no MCP tools except if skill-specific).

  1. mcp__vault MCP server has no documented rate limits or connection pooling. vault_mcp.py lines 30-42 implement a requests.Session for MC API calls, but the vault.db queries themselves are raw sqlite3 calls. If Lucienne runs 20 parallel queries in the same session, each will open its own sqlite3 connection. WAL mode prevents corruption but doesn't bound memory.

Risk: Long sessions with high query frequency could exhaust connection pools.

Fix: Add a connection pool (sqlite3.StaticPool or equivalent) to vault_mcp.py::_db_connection(). Document the limit in vault_mcp.py docstring.

  1. PostToolUse hook with no matcher freezes sessions. Documented in MEMORY.md line 73: "PostToolUse hook with no matcher freezes sessions; NEVER create hooks without a matcher". But .claude/settings.json doesn't show ANY PostToolUse hooks — only UserPromptSubmit and PreToolUse. This is correct (no broken hooks) but also means there's zero feedback logging after skill invocation or tool use. Sessions have no audit trail of "which skill was invoked, what did it return".

Risk: If a skill fails silently, Lucienne won't know. No dashboard visibility into skill outcomes.

Fix: Add optional PostToolUse logging (with a matcher that's always true: "matcher": "Bash|Skill|Agent") to log tool outcomes to a skill_invoked.jsonl.


5. Red-Flag Issues (Fix NOW)

  1. memory-manager skill is referenced in CLAUDE.md line 14 but not found in ~/.claude/skills/. This is a false instruction. Either the skill is missing or misnamed. If missing, CLAUDE.md's core rule ("check memory before denying") cannot be executed. Severity: CRITICAL. Blocks CLAUDE.md boot sequence.

Fix: Search for the skill. If it doesn't exist, either remove the line from CLAUDE.md or create the skill. If it exists with a different name (e.g., vault-search or memory-search), update the reference.

  1. CLAUDE.md memory/rule guidance contradicts itself on query order. Lines 77-79 (memory-manager before answering) vs line 351 (GBrain first) vs line 352 (vault memory search for agent outcomes) create three different dispatch rules for the same question type. A session will try them in the wrong order. Severity: HIGH. Wastes context and delays answers.

Fix: Write a single, definitive query order: (1) GBrain for knowledge questions, (2) vault.mcp for agent-outcome questions, (3) Atlas/wiki for system architecture questions. Update all three sections to refer to this unified order.

  1. GBrain MCP fails on stale postmaster.pid but fix is not automated. Documented in feedback_gbrain_stale_pid.md (2026-04-20). Session-end.sh and mcp-serve.sh have been fixed, but existing sessions that fail to connect won't auto-recover. The fix requires manual rm -f ~/.gbrain/brain.pglite/postmaster.pid. Severity: MEDIUM (session-start issue, not production-blocking, but frustrating).

Fix: Add a PreToolUse hook that checks for stale postmaster.pid and removes it silently if no gbrain process is running. Or add a gbrain health-check command that runs at session start.

  1. Activity_log is "authoritative" in vault.db schema.sql but not protected. Line 85 SETUP.md says activity_log is "the only authoritative DB table" but vault_mcp.py has no UPDATE permission guards. If a session accidentally calls an UPDATE query, it can corrupt the authoritative log. Severity: MEDIUM. Low probability but high impact.

Fix: Add a sqlite3 trigger (BEFORE INSERT/UPDATE on activity_log) that prevents manual writes. Only index.py can write via the rebuild_activity_log() function.


6. Yellow-Flag Issues (Fix Soon)

  1. No automated memory archival / garbage collection. 139 memory files, some marked lifecycle: archived or stale_after dates in the past, but no job sweeps them into an archive. Dashboard has no card showing stale memories. Severity: LOW-MEDIUM (functional but accumulates cruft).

Fix: Create a monthly task: run python index.py --archive-stale to move files with stale_after date in the past to Vault/memory/_archived/. Add a dashboard card showing v_memory_health rows where is_stale = 1.

  1. Vault.db and GBrain have different sync schedules (daily vs 15-min). Lucienne could query vault.db (stale) and miss recent GBrain results (fresh), without knowing which is authoritative. Severity: LOW-MEDIUM (rare but confusing).

Fix: Align sync schedules. Make vault.db hourly or make GBrain 15-min. Document in CLAUDE.md which index is fresher.

  1. "Check SB markdown before building extractors" is a pattern, not a process. No searchable catalog of SecondBrain/sources/. Severity: LOW. Requires manual folder browsing to know if a source exists.

Fix: Add a ls SecondBrain/sources/ dashboard card or a search-sources skill to query that folder.

  1. Overlapping/vague skill trigger descriptions. Multiple skills may respond to "compile wiki" or similar. No de-duplication logic. Severity: LOW-MEDIUM. Depends on user being precise; risk is sending request to wrong skill.

Fix: Audit all skill descriptions for collisions. Rewrite ambiguous ones (e.g., "wiki-compiler" vs "wiki-query" both involve wiki). Add a "Skills Index" dashboard card that lists all skills and their exact trigger phrases.

  1. Lifecycle field in memory files is unused in queries. Frontmatter says lifecycle: archived but vault_mcp.py ignores it. Severity: LOW. Semantic signal only; easy to add filter, but not blocking.

Fix: Make vault_mcp.py skip lifecycle: archived files in search results, OR use subdirectories instead and remove the field.


7. Green-Flag / Keep Doing


8. Three Highest-Leverage Improvements

Priority 1: Fix CLAUDE.md memory/rule contradictions (2-3 hours)

What: Write a single, unified query order for all knowledge questions. Remove the three conflicting rules (lines 77-79, 351-353, memory-manager skill). Replace with one definitive flowchart:

Query Order for Knowledge Questions:
1. Is this a system architecture question?
   → Dispatch Atlas (Explain mode). He has wiki loaded.
2. Is this a project/person/decision question from Elmar's work?
   → Query GBrain (mcp__gbrain__query). Covers SecondBrain + PKA wiki + docs.
3. Is this about agent outcomes or recent session work?
   → Query vault.mcp (mcp__vault__memory_search). Covers agent-generated memories + activity_log.
4. Is this about tasks, deadlines, or Lucienne's local ops?
   → Query vault.db v_upcoming_tasks / v_active_projects views.
5. Fallback: raw file reads as last resort.

Update all three CLAUDE.md sections to reference this flowchart. Remove memory-manager skill instruction if it doesn't exist, or document how it fits.

Why: Unambiguous dispatch order prevents wrong-tool selection, saves context, speeds answers. Currently Lucienne has three conflicting instructions.


Priority 2: Verify and document memory-manager skill (1-2 hours)

What: Confirm whether ~/.claude/skills/memory-manager/ exists. If yes, read SKILL.md and add it to MEMORY.md as an entry under "Reference" or "Procedures". If no, either: - Create a minimal skill (Read, Glob, Grep over Vault/memory/) that finds memories by name/description, OR - Remove the reference from CLAUDE.md and use vault.mcp tools directly.

Why: Currently CLAUDE.md references a tool that may not exist, making the boot sequence unreliable.


Priority 3: Automate GBrain health-check and postmaster.pid cleanup (1-2 hours)

What: Add a session-start hook that: 1. Checks if ~/.gbrain/brain.pglite/postmaster.pid exists 2. Runs pgrep -f "gbrain serve" to see if gbrain is actually running 3. If pid file exists but process is gone, remove the file silently 4. On next claude mcp list, GBrain should reconnect

Also: Add PreToolUse hook to verify GBrain is connected before running Atlas brief. If GBrain fails, log it and fall back to wiki files (not silently).

Why: GBrain is the speed layer for knowledge; if it fails silently, Lucienne reverts to 22-second waterfall without realizing it.


Summary

PKA is a well-engineered system with excellent architecture discipline, clean agent separation, and rigorous decision-making (GBrain A/B testing, council review gates). The memory model is sound; the two-DB split is correct; the dispatch model is lean.

The main work is housekeeping: fix contradictory rules, verify missing tools, automate health checks. None of these are architectural; they're maintenance and clarity.

Do not over-refactor. The system works. The improvements above are force multipliers (faster answers, fewer wrong turns, better reliability), not foundational fixes.


Audit conducted 2026-04-20. Report by outside-eyes reviewer.