The canonical read-first handoff for the v3 framework. Three redundant repos (atlas, monitor, v3) feed this. Not a commitment log — a sparring snapshot.
Working title is AVPv1 (Anti-Vibing Patterns v1). Three alternatives below. Pierre picks.
Anti-Vibing Patterns v1
For: Catchy, culturally positioned against vibe-coding.
Against: Defines by opposition. "Vibe" is a meme — dates weirdly. No-hype rule cuts against clever framings.
Pierre's own tsoftwa metaphor.
For: Distributed sessions as workers, brain as colony memory. Positive framing. Short.
Against: "Hive mind" is a tech cliché.
Points at the load-bearing concept.
For: Session invariants hard, pipeline invariants soft — the thing that makes this not vibe-coding. Quietly technical.
Against: Dry. Less memorable.
The book metaphor, all in.
For: Unique. No collision. Carries the metaphor as the name.
Against: Unpronounceable cold. Inside-joke risk.
Tsoftwa — the soul of the white ant. One mind distributed across workers (sessions). Each session sees the whole colony's knowledge and contributes back.
Not Zapier. Not IFTTT. This is situated cognitive guidance — clarifies what's invariant while the human decides. The system doesn't execute predetermined workflows; it enhances the human's capacity to work through their own process.
Academic grounding: Activity Theory. The invariant chains map to actions (stable across tools). Operations are fluid and technology-adoptable.
Source: mcp-and-skills-framework-and-brain-v3/research/invariant_driven.md
Brain · Nervous system · Distribution. Three structural roles, clean separation.
Single substrate on Hetzner (100.97.123.98), docker container postgres, accessible via Tailscale. pgvector + JSONB + stored procedures + full-text search + HNSW indexes.
Triple-index: pgvector + LanceDB + Tantivy. LanceDB non-negotiable for voice features.
brain:// resources.Skills + code + markdown in git (this repo + others). Dynamic state in Postgres on Hetzner, via Tailscale. Git is the distribution mechanism. Postgres is the runtime truth.
Never install Claude Code on Hetzner. Production surface, not dev surface. Admin only via SSH from dolphin or spud2.
Named characters, not role tags. ADHD-friendly handles for parallel threads. Check-in-ability is as important as silent work.
| Tier | Binding | Examples | Creation | Deletable? |
|---|---|---|---|---|
| System | Hooks | Session Boss, Librarian, Auditor, Cop, Archivist | Hard-coded, seeded on install | No — type='system' |
| User | Skills | Steve (APIs), future Dave (DBs), etc. | /persona create Name 🎯 "job" | Yes |
sessions rowturns row/done skill · async batchdrift_events/undopersona_comments WHERE persona_name='Steve'.learnings row as byproduct. Steve only exists when invoked. NOT a background bot./broadcast fires all actives; each gets ~1000 tokens to report; collated into one chat block. Explicit tool, NEVER auto-fires.Example footer:
---
🔧 Steve (APIs): no external calls this turn
📚 Librarian: turn 17 queued
👮 Cop: no drift
Session invariants HARD. Session Boss enforces. No drift. Examples: declared purpose, mode (sparring / execution / debug), budget cap.
Pipeline invariants SOFT. Outcome chains, not schedules. Each link checked at runtime; break → ntfy the pipeline's persona.
Groceries = need → captured → bought → dispatched → eaten
Research = question → queried → answered → extracted → stored
Email = arrived → triaged → digest → delivered → decided
Workbench = data → cleaned → analysed → reported → acted on
At session close, each persona_comments row gets a rating:
useful | noise | wrong
Three categories only. No KPI proliferation. No "persona quarterly reviews." Pierre's stated boundary — locked.
Strangler fig at skill granularity. MCP for discovery, skills for procedure, CLAUDE.md for declarative context.
semantic-search-skills(query, k) hits capability_view.Two candidates. Pick one (or adopt both in layers).
---
name: skill-name
description: one-liner with triggering keywords
---
Full body as markdown — prose, examples, commands.
skill:
id: "skill-name"
version: "1.0.0"
description: "what it does"
keywords: ["tag1", "tag2"]
category: "data-access|meta|domain|..."
inputs: [{name, type, required}]
outputs: [{name, type}]
trust_level: "untested|trusted|verified|critical"
cost_estimate: {tokens: 500, usd: 0.002}
dependencies: ["postgres:brain", "mcp:vector-search"]
last_tested: "2026-04-15"
health: "healthy|degraded|broken"
skills table when governance is needed (trust levels, health, cost budgets).| Location | Count | Format | Status |
|---|---|---|---|
~/.claude/skills/ | 72 | native SKILL.md | live GSD family + utility |
projects/pierre-ops/skills/ | 2 | SKILL.md | local only |
projects/mentor-workbench/skills/ | 2 | Python modules | NOT SKILL.md can't auto-discover |
projects/acebuddy/scripts/hooks/ | 9 bash | NOT skills | dormant 0 registered |
brain DB skills table | 40+ | DB metadata | referenced in atlas PROPOSAL |
skills with trust_level='verified'; update CLAUDE.md.trust_level on failure.A user persona like Steve = one row in personas + one SKILL.md with his system prompt, scope, and tools.
/ask Steve, "Steve, status?", turn-footer 🔧 Steve (APIs): ...Identified in monitor research (Apr 14, skill-framework-migration/output.md). The draft → extract → test → version → share loop has no tool yet. Not urgent. Future work.
Canonical base doc: atlas/MIGRATION.md (22 KB, 17 decisions, 0% deployed). Amendment below adds the tables personas + sessions need.
-- Template for every v2 table
CREATE TABLE v2.example (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
status TEXT NOT NULL DEFAULT 'active'
CHECK (status IN ('active','inactive','archived','superseded')),
source TEXT,
metadata JSONB NOT NULL DEFAULT '{}'
);
v2.set_updated_at() trigger.'manual', 'bot:weather', 'migration:v1', 'workbench:wb-...'Every table gets system-period temporal columns for full history.
ALTER TABLE v2.example ADD COLUMN
valid_from TIMESTAMPTZ NOT NULL DEFAULT now(),
valid_to TIMESTAMPTZ NOT NULL DEFAULT 'infinity';
CREATE TABLE v2.example_history (LIKE v2.example INCLUDING ALL);
-- Trigger on UPDATE copies OLD to history with valid_to = now()
Time-travel query:
SELECT * FROM v2.entities
WHERE valid_from <= '2026-03-15' AND valid_to > '2026-03-15';
Every content-bearing table:
confidence FLOAT, -- Machine-generated, 0.0–1.0
importance INTEGER, -- Human-assigned, 1–5 (Pierre)
confidence set by ingest pipelines. Transparent. Never a hidden filter.importance set by Pierre manually. Overrides machine scoring.These block the v3 framework. Follow universal standards (6 cols + temporal triggers where applicable).
CREATE TABLE v2.sessions (
-- 6 universal columns
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
status TEXT NOT NULL DEFAULT 'active'
CHECK (status IN ('active','ended','abandoned','archived')),
source TEXT,
metadata JSONB NOT NULL DEFAULT '{}',
-- Session-specific
project_path TEXT,
purpose TEXT, -- declared by Session Boss
mode TEXT, -- 'sparring'|'execution'|'debug'
started_at TIMESTAMPTZ NOT NULL,
ended_at TIMESTAMPTZ,
turn_count INTEGER DEFAULT 0,
tokens_total INTEGER DEFAULT 0,
budget_cap INTEGER,
-- Temporal
valid_from TIMESTAMPTZ NOT NULL DEFAULT now(),
valid_to TIMESTAMPTZ NOT NULL DEFAULT 'infinity'
);
CREATE TABLE v2.turns (
-- 6 universal columns
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
status TEXT NOT NULL DEFAULT 'ok'
CHECK (status IN ('ok','errored','archived')),
source TEXT,
metadata JSONB NOT NULL DEFAULT '{}',
-- Turn-specific
session_id UUID NOT NULL REFERENCES v2.sessions(id),
turn_num INTEGER NOT NULL,
started_at TIMESTAMPTZ NOT NULL,
ended_at TIMESTAMPTZ,
user_message TEXT,
assistant_response TEXT,
tokens_in INTEGER,
tokens_out INTEGER,
tool_calls_count INTEGER DEFAULT 0,
error_note TEXT,
-- Auditor fills async
classified_at TIMESTAMPTZ,
classification JSONB,
UNIQUE(session_id, turn_num)
);
CREATE TABLE v2.personas (
-- 6 universal columns
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
status TEXT NOT NULL DEFAULT 'active'
CHECK (status IN ('active','inactive','archived')),
source TEXT,
metadata JSONB NOT NULL DEFAULT '{}',
-- Persona-specific
name TEXT NOT NULL UNIQUE,
emoji TEXT NOT NULL,
job TEXT NOT NULL,
scope TEXT,
tier TEXT NOT NULL CHECK (tier IN ('system','user')),
skill_binding TEXT,
created_by_session UUID REFERENCES v2.sessions(id)
);
CREATE TABLE v2.persona_comments (
-- 6 universal columns
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
status TEXT NOT NULL DEFAULT 'active',
source TEXT,
metadata JSONB NOT NULL DEFAULT '{}',
-- Comment-specific
persona_id UUID NOT NULL REFERENCES v2.personas(id),
session_id UUID NOT NULL REFERENCES v2.sessions(id),
turn_id UUID REFERENCES v2.turns(id),
turn_num INTEGER,
comment TEXT NOT NULL,
-- Auditor fills at session close
rating TEXT CHECK (rating IN ('useful','noise','wrong',NULL)),
rated_at TIMESTAMPTZ,
UNIQUE(persona_id, session_id, turn_num)
);
CREATE TABLE v2.drift_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
status TEXT NOT NULL DEFAULT 'active',
source TEXT,
metadata JSONB NOT NULL DEFAULT '{}',
session_id UUID REFERENCES v2.sessions(id),
turn_id UUID REFERENCES v2.turns(id),
drift_type TEXT NOT NULL CHECK (drift_type IN ('ai','user')),
detail TEXT,
triggered_checkpoint BOOLEAN DEFAULT false
);
status column on v1 learnings — required to triage 973 unreviewed rows. Blocked on migration.github_repos sync — currently 8 rows, actual ≈60. Fix before atlas Phase 1.claude_sessions sync — broke 2026-04-05. Fix.| Domain | Tables | Notes |
|---|---|---|
| Identity & Relationships | 6 | entities, entity_links, contacts (+ channel discriminator), user_profiles, concerns (the spine, 9 entries), concern_manifestations |
| Knowledge & Memory | 5 | learnings, memory, scratchpad, documents (consolidates 5 v1 tables), chunks (pgvector) |
| Communication | 3 | messages (consolidates email/whatsapp/matrix via channel), message_actions, sync_state |
| Bots & Skills | 6 | bot_registry, bot_actions, skills, teams, team_members, team_workflows |
| Finance | 5 | accounts, envelopes, transactions, budgets (consolidates 3), payees |
| Health & Wearables | 2 | health_events, wearable_data (consolidates 9 fitbit + oura tables) |
| House | 3 | properties, rooms, house_items (+ house_systems folded) |
| Grocery | 3 | recipes, recipe_ingredients, grocery_regulars (already well-structured) |
| Reflector & Embeddings | 2 | reflector_entities (1.3M rows), reflector_analytics (consolidates 5) |
| Infrastructure | 7 | audit_log, workflow_runs, cost_ledger, resource_snapshots, task_queue, invariant_events (NEW), alembic_version |
| Projects & Planning | 3 | projects (consolidates 6), experiments, datasets |
| External Integrations | 3 | github_repos, claude_sessions, sso_apps |
| Assets | 2 | assets, asset_links |
| Workbench | 3 | workbench_sessions (+ parent_session_id for chaining), workbench_learnings, workbench_discoveries |
| Ingest | 2 | ingest_sources, ingest_log |
| V3 amendment (this doc) | +5 | sessions, turns, personas, persona_comments, drift_events |
| Total | 41 → 46 with amendment | |
Pierre's ruling (2026-04-14, session 2861cad1 line 79): "spine first. let's make that solid. start with a small spine and then enlarge it."
Three files. Nothing else. Schema contract is the hard-to-change surface; bash hooks are trivial.
mcp-and-skills-framework-and-brain-v3/
migrations/
001_spine.sql -- v2.sessions + v2.turns (6 universal cols + triggers)
~/.claude/hooks/
session-start.sh -- INSERT session row via SSH + docker exec, exit
stop.sh -- INSERT turn row via SSH + docker exec, exit
Then register both in ~/.claude/settings.json.
session-start.sh as bail condition.SELECT SUM(tokens_in+tokens_out) FROM v2.turns WHERE session_id=$1. No state needed.session-start.sh opens HANDOFF.md for append; stop.sh writes turn summary./persona create skill, footer enforcement/done skill (batch classification, ratings, mistake detection)/undo9 bash hooks already written in acebuddy/scripts/hooks/. None registered in settings.json. Targets acebuddy-specific concerns (fleet health, ARM64 migration, SLO metrics) — NOT the v3 spine.
session-cost-log.sh + error-logger.sh). The v3 Tier A hooks should be NEW, targeted at v2.sessions / v2.turns.| File | Exists | Registered | What it does |
|---|---|---|---|
session-start.sh | ✅ | ❌ | Injects orientation (git branch, fleet health, ARM64 phase, audit entries, SLO). Caches state. |
session-end.sh | ✅ | ❌ | Logs session end to audit_log if >5 tool uses. Cleans temp. |
skill-tracker.sh | ✅ | ❌ | PostToolUse. Parses Skill invocations, logs to audit_log with skill name + args. |
error-logger.sh | ✅ | ❌ | PostToolUse. Detects Bash failures. Injects guidance nudge after 1st error, then every 3rd. |
post-compact.sh | ✅ | ❌ | Re-injects S4-NO_DESTROY, A1-AUDIT_TRAIL, O1-REGISTRY_TRUTH after compaction. |
session-cost-log.sh | ✅ | ❌ | SessionEnd. Parses session jsonl, calculates Opus API-equivalent cost token-by-token. |
ntfy-notify.sh | ✅ | ❌ | ntfy integration. |
progress-check.sh | ✅ | ❌ | Purpose unclear from filename. |
verification-gate.sh | ✅ | ❌ | Purpose unclear from filename. |
#!/bin/bash
# v3 Tier A: SessionStart hook — writes v2.sessions row
SESSION_ID=$(cat /proc/sys/kernel/random/uuid)
PROJECT_PATH="$CLAUDE_PROJECT_DIR"
STARTED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
ssh root@100.97.123.98 "docker exec postgres psql -U acebuddy -d brain -c \"
INSERT INTO v2.sessions (id, project_path, started_at, source, mode)
VALUES ('$SESSION_ID', '$PROJECT_PATH', '$STARTED_AT', 'claude-code', 'sparring');
\"" || {
echo '{\"hookSpecificOutput\":{\"hookEventName\":\"SessionStart\",\"additionalContext\":\"⚠️ BRAIN WRITE FAILED — session not logged\"}}'
exit 2
}
echo "$SESSION_ID" > /tmp/claude-session-id
exit 0
#!/bin/bash
# v3 Tier A: Stop hook — writes v2.turns row after every turn
SESSION_ID=$(cat /tmp/claude-session-id 2>/dev/null)
[ -z "$SESSION_ID" ] && exit 0 # no session? no turn.
TURN_NUM=$(ssh root@100.97.123.98 "docker exec postgres psql -U acebuddy -d brain -tAc \"
SELECT COALESCE(MAX(turn_num),0)+1 FROM v2.turns WHERE session_id='$SESSION_ID';
\"")
# (read turn data from $CLAUDE_TRANSCRIPT_PATH, extract tokens/tool calls)
# (INSERT v2.turns row)
exit 0
Actual implementation: escape properly, fail loud on DB error, extract tokens from transcript.
| Layer | Spec | Built | Registered | Confidence |
|---|---|---|---|---|
| v2 schema base (atlas) | ✅ | ❌ 0 tables | — | 95% |
| v2 amendment (this doc) | ✅ NEW | ❌ | — | 95% |
| Skills framework arch | ✅ | ⚠️ partial | ⚠️ 72 in ~/.claude/skills/ | 85% |
| SKILL.md canonical format | ❌ 2 candidates | — | — | OPEN |
| Personas — two tiers, four behaviours | ✅ designed | ❌ | — | 95% |
/persona create skill | ✅ shape agreed | ❌ | — | 90% |
| Session spine (Tier A) | ✅ scoped | ❌ | ❌ | 99% |
| Dormant acebuddy hooks | — | ✅ 9 files | ❌ 0 registered | 99% |
| Brain MCP server | ✅ | ⚠️ stub failing | — | per atlas |
Decreasing blocking-ness. Next Claude picks any of these up — or Pierre says the words.
/ask Steve) vs both. Leaning: natural only; learn the pattern.tier discriminator (proposed) vs separate tables. Leaning: same, with active BOOLEAN to protect system personas.skills table for governance./broadcast. Cap or per-persona configurable?brain/wiki/stack.md (~18 KB estimated — Hetzner + spud2 + dolphin + brain DB + pipelines + connexions + env + vocabulary + norms). Not yet written. Blocks full prime load.status column migration first.ses_20260415_v3_spine). Leaning: UUID for cross-project portability + store a human tag in metadata./persona create skill lives~/.claude/skills/) so all Claude sessions can create personas, vs repo scope.Tier A only. One focused day of work, IF Pierre says "make it so."
| # | Artifact | Effort |
|---|---|---|
| 1 | migrations/001_spine.sql — v2.sessions + v2.turns with 6 cols, triggers, temporal. Run against brain DB. | 2 hr |
| 2 | ~/.claude/hooks/session-start.sh — INSERT session row, fail loud | 2 hr |
| 3 | ~/.claude/hooks/stop.sh — INSERT turn row, fail loud | 2 hr |
| 4 | Register both in ~/.claude/settings.json | 15 min |
| 5 | Smoke test: new session → 1 session row → 3 turns → 3 turn rows | 30 min |
| 6 | Commit + push this repo | 15 min |
mcp-and-skills-framework-and-brain-v3/migrations/~/.claude/hooks/ (user scope, cross-project)docker exec postgres psql -U acebuddy -d brainpersonas + persona_comments in 001_spine.sql (include-as-empty) or separate 002_personas.sql laterv2 schema or separate spine schema (leaning: v2 — one cutover path)atlas/MIGRATION.md — schema v2 spec (canonical base)atlas/PROPOSAL.md — skill layer + spine architecture (outdated on delivery mechanism; extension approach still correct)atlas/HANDOFF.md — atlas session 3 (MCP-first correction)atlas/research/validation/VALIDATION.md — proposal critiqueatlas/research/orchestration/answer.md — multi-session orchestration researchmcp-and-skills-framework-and-brain-v3/.planning/HANDOFF.md — Apr 13 architecture sparringmonitor/HANDOFF-session-2.md — Apr 14 personas + four-block sparringmonitor/sparring-deploy/spoken-script.txt — 7-min MP3 narration source2861cad1-5ab5-4a0d-86c0-8ac9172756c0.jsonl lines 33–83 — Steve/personas deep sparring (Apr 14 evening)monitor/results/skill-framework-migration/output.md — strangler fig validation, skill-builder gapmonitor/results/brain-db-skill-integration/output.md — MCP + read-only views, three-tier write patternmcp-and-skills-framework-and-brain-v3/research/invariant_driven.md — Activity Theory grounding, five-layer archmcp-and-skills-framework-and-brain-v3/research/hive_mind.md — distributed cognition, LISTEN/NOTIFY, offline-firstmcp-and-skills-framework-and-brain-v3/research/skills_first.md — skill metadata, lock-it-in gateAt ~/.claude/projects/C--Users-acebu-projects/memory/:
user_persona_cognitive_style.md — the non-negotiable cognitive rulefeedback_satisficing_redundancy.md — why three repos are not to be consolidatedproject_v3_framework_state.md — sparring state snapshot (points here)reference_v3_canonical_handoff.md — where this doc livesreference_atlas_brain_v2.md — atlas hub pointerreference_ingest_api.md — brain DB ingest endpoint