# Project export: Vault Mind

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: UC Berkeley AI Hackathon 2026
- Tagline: Your AI's memory, preserved and portable across every model.
- Devpost: https://devpost.com/software/vault-mind
- GitHub: https://github.com/timothyouu/vault_mind
- Video: https://www.youtube.com/embed/ohAVd_HtAJQ?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 5 GitHub contributor(s) — Samson Du (46 commits), Claude Sonnet 4.6 (34 commits), Timothy (29 commits), Emily Tsai (11 commits), Devin (9 commits)

## Devpost submission (written by the team)

### Overview

##

### Inspiration

Every developer who juggles multiple LLM subscriptions has hit the same wall: you're deep in a Claude Code session, you've made dozens of decisions, debugged a subtle constraint, and established a clear direction — and then the session limit hits. The standard fix is asking the model to "summarize what we discussed," but that summary is generated at the worst possible moment: late, under context pressure, from a degraded view of its own history. LLMs attend well to the start and end of long contexts; the middle — often where the most important reasoning happened — gets fuzzy. The summary becomes a prediction about what probably happened, not a reliable record of what did. We built VaultMind because there is no clean, reliable way to hand off a working session from one LLM to another, and we wanted to fix that at the infrastructure level, not just paper over it. ##

### What it does

VaultMind gives any LLM a persistent, structured memory that is fully transferable to any other LLM, without late-stage summarization. As a developer works in Claude Code or Codex CLI, a lightweight background watcher picks up each completed turn via Stop/SessionEnd hooks. After every turn: A Scribe Agent extracts what just happened (decisions, constraints, goals, open questions) while the content is still fresh, before any context decay sets in. A Note Creator Agent structures that extraction into a Markdown node and writes it to an Obsidian-compatible vault living directly in the project git repo. A Connector Agent links related nodes together — using both keyword heuristics and Redis vector search — building a real knowledge graph. A Fetch.AI uAgent Orchestrator coordinates the pipeline and exposes project state and handoffs over ASI:One. When the developer hits a session limit or wants to switch to Gemini (or a fresh Claude Code session), they open the VaultMind web app, review the live graph in a trust interface, and confirm. The receiving agent reads the vault's .md files directly from disk. No serialization, no synthesis, no re-summarization. It picks up with the same structured context the first agent built. Two small deterministic files anchor every handoff: VaultIndex.md (a map of the vault structure) and IntentLog.md (a running, append-only log of what the developer wants each session to accomplish, in their own words). ##

### How we built it

We approached this as an infrastructure problem first, and engineered the seams before writing a single line of production code. Stack: Python — Stop/SessionEnd hooks, the agent pipeline (Scribe, Note Creator, Connector), and the single shared scanForSecrets() regex utility Claude (claude-sonnet-4-6) — structured JSON extraction via the Anthropic Messages API across all three generative agents, plus per-hunk merge-conflict recommendations Fetch.AI uAgents — the Orchestrator, published on Agentverse with the Agent Chat Protocol, usable via ASI:One; bridged to the web app through a small Flask service Redis Stack (Streams + Pub/Sub + RediSearch) — the hook-to-watcher work queue and the pipeline-to-web-app live event bus; the same instance serves vector search (RedisVL + all-MiniLM-L6-v2, 384-dim, fully offline) for semantic memory Next.js 15 (App Router) + React 19 — full-stack web app with Server-Sent Events for live graph updates, no separate Express server Custom SVG knowledge graph — a hand-built, dependency-free live visualization of nodes and their related links, with dark/light theming Arize + OpenTelemetry — tracing across all three LLM call sites (Scribe, Connector, Orchestrator) feeding one end-to-end evaluator that checks the full chain: did the extracted node accurately reflect the source turn, are the links relevant, does anything downstream drift from what's in the vault? Devin Cloud — the three backend streams (P1 ingestion, P2 extraction/writing, P3 linking + Orchestrator) ran as parallel Devin Cloud sessions against the frozen spec seams, with human checkpoint review at every bucket boundary via Devin Review. P4 (the web app) stayed human-driven throughout. ##

### Challenges we ran into

The late-summarization trap is subtler than it looks. Our earliest instinct was to have agents summarize more aggressively, but every time we pulled on that thread, we ended up recreating the exact failure mode we were trying to eliminate. The insight that unlocked the design: compressing a small fresh thing well is a fundamentally easier problem than compressing a large stale thing well. Per-turn extraction from a fresh, verbatim turn is tractable; one big compression of a degraded context window is not. Claude Code's own auto-compaction. Claude Code marks a compact_boundary event in its transcript when it runs low on context. Our Scribe Agent reads from the literal transcript, not Claude Code's internal context, so compaction doesn't corrupt the vault directly. But nodes extracted from turns shortly after a compaction boundary may accurately reflect what Claude said while Claude was already working from a thinner-than-usual memory. Our solution: flag, never infer or repair. A warning: Context compacted at [timestamp] entry goes into SessionState.md, and affected nodes get a subtle marker in the trust interface. We deliberately do not try to reconstruct what Claude may have lost — that would mean asking an AI to fill another AI's memory gap, which is exactly the generative risk this architecture is designed to avoid. Secret protection had a real gap. An early version claimed secrets "always block both the commit and any handoff," but the only mechanism described was a git pre-commit hook — which by definition cannot fire on a handoff (no commit is involved). In Auto Mode specifically, a developer could hit Handoff without ever running git commit. The fix: scanForSecrets(content) is one shared regex utility called from three independent call sites — write-time, commit-time, and handoff-time. All three use the same pattern list. All three are deterministic and instant. The handoff trigger runs the check before proceeding, independent of git entirely. Orchestrator round-trip latency. An early draft had every step bouncing back through the Orchestrator after each stage, creating multiple sequential network round-trips before a single node was even visible. The fix was not collapsing Scribe, Note Creator, and Connector into one call (which would re-concentrate exactly the unverifiable judgment we split them apart to avoid). Instead, Note Creator and Connector chain directly to each other for the fast, deterministic steps; the Orchestrator is invoked to kick off the pipeline and handle cross-turn coordination, not to broker every single hop. IntentLog.md authorship. The intent log is meant to carry the developer's own words with zero hallucination risk. Allowing the AI to detect mid-session focus shifts and write to this file in Auto Mode is a real tradeoff: the developer trades the no-AI-writes guarantee for less friction. We resolved this by making it explicitly mode-dependent (consistent with the Auto/Review split that already governs the rest of the system) — and any AI-written entry is labeled ai-detected — rather than glossing over the tradeoff. ##

### Accomplishments we're proud of

A handoff that actually works. A developer can be mid-session in Claude Code, hit a session limit, confirm the handoff, and have Gemini (or a fresh Claude session) pick up with full structured context — no re-explanation, no copy-pasting summaries. Honest architecture. Every design decision in VaultMind has a documented rationale and a documented tradeoff. We caught our own gaps — the secret-protection hole, the Orchestrator latency problem, the IntentLog authorship question — before they became bugs. Sponsor integrations that reinforce the product. Redis vector search is the actual semantic search feature we needed. The Orchestrator's Agentverse registration is a real secondary interface, not a token integration. Arize tracing feeds one evaluator watching the whole pipeline end-to-end, verifying the system meets its own stated bar rather than just asserting it. Devin Cloud literally built the three backend streams in parallel against the same frozen seams that make VaultMind's own architecture safe. The Devin meta-story. VaultMind's premise — careful, structured, verifiable handoff beats one big unverifiable one — is exactly the discipline that makes Devin's parallel cloud sessions safe to run unattended. The project doesn't just use Devin; it demonstrates the same principle Devin's own architecture is built on. ##

### What we learned

The most durable lesson: the failure mode that makes context handoff unreliable isn't compression per se — it's one large, late, unverifiable compression of an entire degraded conversation. Replace that with many small, immediate, high-fidelity extractions from fresh single turns, and the whole character of the problem changes. A single imperfect node doesn't sink the handoff; the surrounding nodes and links still carry real signal, and the user can see and fix any node before it reaches a receiving agent. We also learned that externalizing plan and progress state to deterministic files that survive compaction (SessionState.md, IntentLog.md) is already established practice among heavy Claude Code users. VaultMind formalizes that pattern rather than inventing it from nothing — which is a feature, not a limitation. And practically: frozen byte-level contracts at every seam boundary aren't just good practice for a four-person team under time pressure — they're the literal precondition for safely handing work to autonomous parallel agents. An ambiguous spec is fine when a human can ask a clarifying question mid-build. It's a real risk when cloud sessions run unattended against a shared repo. ##

### What's next

for VaultMind Dynamic VaultIndex.md — a deterministic graph traversal (breadth-first from the most recently active node) surfacing the 3–5 most connected/recent nodes, giving receiving agents a sharper entry point than a static vault map. Team support — the vault already lives in the shared repo, so multiple developers naturally contribute to the same knowledge graph. The merge-conflict UI exists; we want to polish the multi-author experience into a first-class feature. Broader CLI support — extending hooks beyond Claude Code and Codex to any tool that exposes a session transcript. Arize feedback loops — the one end-to-end evaluator we shipped covers the full pipeline; we want to use what it surfaces to drive automated prompt refinement across sessions, closing the loop between tracing and improvement systematically rather than manually.

## README (from the GitHub repository)

# VaultMind

Persistent, structured project memory in Obsidian-compatible Markdown. All transferable between LLM tools (Claude Code, Codex, Gemini) without needing late-stage summarization.

A multi-agent pipeline watches your Claude Code / Codex sessions and writes a git-native vault of decisions, constraints, goals, and questions as you work. A Next.js web app lets you review, approve, and hand off that vault to a receiving agent. A Fetch.AI Orchestrator uAgent sits on ASI:One so you can query project state or trigger handoff via natural language.

---

## Architecture

```
Claude Code / Codex hooks
        │  Stop / SessionEnd
        ▼
  Python watcher  ──(Redis Streams)──►  Scribe → Note Creator → Connector
        │                                                              │
        │                                              vault/nodes/*.md (disk)
        │                                                              │
        └──────────────────(Redis pub/sub)──────────────────────────► SSE
                                                                       │
                                                                  Next.js app
                                                                  (port 3000)
                                  Orchestrator uAgent (Fetch.AI / ASI:One)
```

**Stack:** Python 3.11+ · Next.js 15 / React 19 · Redis (Streams + pub/sub + vector) · Fetch.AI uAgents · Arize (LLM observability)

---

## Prerequisites

| Tool | Minimum version | Notes |
|---|---|---|
| Python | 3.11 | pipeline + hooks |
| Node.js | 18 | webapp |
| Docker | any recent | Redis via `docker compose` |
| Git | any | pre-commit hook for secret scanning |

---

## Quickstart (running the product)

1. **Clone and install**

   ```bash
   git clone <repo-url>
   cd vault_mind
   pip install -e .
   cd webapp && npm install && cd ..
   ```
 
The `-e` flag installs the Python package in editable mode, so changes you make to the source are picked up immediately without reinstalling.

2. **Set environment variables**

   Copy the example and fill in your keys:

   ```bash
   cp .env.example .env
   ```

   Required:

   ```
   ANTHROPIC_API_KEY=sk-ant-...       # Scribe extraction + evaluator judge
   ARIZE_SPACE_KEY=...                # Arize LLM observability
   ARIZE_API_KEY=...                  # Arize LLM observability
   REDIS_URL=redis://localhost:6379   # default; change if using external Redis
   ```

   Optional:

   ```
   VAULTMIND_VAULT_ROOT=/path/to/vault   # defaults to <repo>/vault
   REPO_ROOT=/path/to/repo               # used by webapp conflict resolver
   ```
   
> `VAULTMIND_VAULT_ROOT` is useful if you want the vault to live outside the repo — for example, inside an Obsidian vault you already have open. The app will still track it the same way.

3. **Start everything**

   ```bash
   npm run vaultmind:start
   ```

   This starts three processes concurrently:
   - **Redis** on port 6379 via `docker compose up -d` (RedisInsight UI on port 8001)
   - **Python watcher** (`python -m vaultmind.watcher`) — pipeline consumer loop
   - **Next.js dev server** at http://localhost:3000

4. **Wire the hooks**

   Add the hook configs so VaultMind captures your sessions:

   **Claude Code** — `.claude/settings.json`:
   ```json
   {
     "hooks": {
       "Stop": [{ "hooks": [{ "type": "command", "command": "python3 .vaultmind/hooks/on_stop.py", "async": true }] }],
       "SessionEnd": [{ "hooks": [{ "type": "command", "command": "python3 .vaultmind/hooks/on_session_end.py", "async": true }] }]
     }
   }
   ```

   **Codex** — `.codex/hooks.json`:
   ```json
   {
     "hooks": {
       "Stop": [{ "hooks": [{ "type": "command", "command": "python3 .vaultmind/hooks/on_stop.py" }] }]
     }
   }
   ```

5. **Open the app** at http://localhost:3000 and start a Claude Code or Codex session — nodes will appear in real time.

---

## Developer setup

Everything above, plus:

1. **Install dev dependencies**

   ```bash
   pip install -e ".[dev]"
   ```

2. **Install the pre-commit hook** (blocks commits that contain secrets in `vault/`)

   ```bash
   git config core.hooksPath .git/hooks
   cp .vaultmind/hooks/pre-commit .git/hooks/pre-commit
   chmod +x .git/hooks/pre-commit
   ```

3. **Run tests**

   ```bash
   pytest
   ```

4. **Run the webapp in isolation** (without the Python pipeline)

   ```bash
   cd webapp
   npm run dev
   ```

5. **Scan a vault node for secrets manually**

   ```bash
   python -m vaultmind.secrets vault/nodes/<node>.md
   ```

   Exits 0 always; prints a JSON array of matches (`[]` = clean). The pre-commit hook reads this and exits 1 itself if matches are present.

---

## Project structure

```
vault_mind/
├── vaultmind/            # Python package — pipeline, hooks, agents
│   ├── contracts.py      # Pydantic v2 message contracts (frozen — do not edit)
│   ├── secrets.py        # scanForSecrets — one implementation
│   ├── watcher.py        # Redis Streams consumer loop
│   ├── scribe/           # LLM extraction agent
│   ├── notecreator/      # writes vault/nodes/*.md
│   ├── connector/        # links related nodes
│   ├── orchestrator/     # Fetch.AI uAgent (ASI:One face + handoff)
│   ├── ingest/           # hook → queue producer (P1)
│   ├── evals/            # end-to-end pipeline evaluator
│   └── hooks/            # on_stop.py, on_session_end.py, pre-commit
├── webapp/               # Next.js 15 app (TypeScript, Tailwind, App Router)
│   ├── types.ts          # TS contracts mirroring contracts.py (frozen — do not edit)
│   └── src/app/
│       ├── page.tsx            # vault live view (SSE)
│       ├── merge/page.tsx      # conflict resolution UI
│       └── api/
│           ├── events/         # SSE endpoint → Redis pub/sub
│           └── conflicts/      # conflict list + per-node resolve
├── vault/                # the live vault (git-tracked Markdown)
│   ├── nodes/            # turn-nodes: YYYY-MM-DD-HHMM-<slug>.md
│   ├── IntentLog.md      # append-only developer intent
│   ├── SessionState.md   # compaction + session-end events
│   └── VaultIndex.md     # static read-order map for receiving agents
├── fixtures/             # fixture transcript + fixture vault for tests
├── scripts/start.sh      # starts Redis + watcher + Next.js
├── docker-compose.yml    # Redis (redis-stack with RedisSearch)
├── pyproject.toml
└── package.json          # root — vaultmind:start script
```

---

## Key rules (enforced by the codebase)

- **Disk is the source of truth.** Redis events are minimal triggers; the app re-reads files on every event.
- **One `scanForSecrets` implementation.** Always `vaultmind/secrets.py` — the webapp shells out to it, never reimplements it.
- **Node bodies are immutable after write.** The Connector edits only frontmatter `related`; it never touches the body.
- **No silent commits or handoffs.** Commits are manual; a detected secret blocks both commit and handoff.
- **`IntentLog.md` is the developer's own words.** Only Auto Mode may write an `ai-detected` entry, and it must be labeled as such.

See `SPEC.md` for the full technical contracts and `WORKSTREAMS.md` for the build execution plan.


## Detected evidence (automated analysis)

Indexed codebase: 102 recognized source files, 804 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- Next.js (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Redis (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- JavaScript (language) — claimed on Devpost, not found in the code
- AI coding agent: Claude Code — evidence: config files committed to the repository; commit authorship or trailers
- AI coding agent: Codex — evidence: config files committed to the repository

## Codebase structure (from repository index)

### Files (120 of 124)

```
.claude/settings.json
.env.example
.gitignore
.vaultmind/hooks/on_session_end.py
.vaultmind/hooks/on_stop.py
CLAUDE.md
DEVIN-PIVOT-SPEC.md
docker-compose.yml
docs/superpowers/plans/2026-06-21-eval-arize-wiring.md
docs/superpowers/plans/2026-06-21-p1-ingestion.md
docs/superpowers/plans/2026-06-21-p2-p3-backend.md
docs/superpowers/specs/2026-06-21-eval-arize-wiring-design.md
docs/superpowers/specs/2026-06-21-p1-ingestion-design.md
fixtures/queue_item.json
fixtures/scribe_result.json
fixtures/transcript.jsonl
fixtures/vault/Constraints.md
fixtures/vault/IntentLog.md
fixtures/vault/nodes/2026-06-21-0950-org-switch-invalidate-sessions.md
fixtures/vault/nodes/2026-06-21-1015-db-schema-users-table.md
fixtures/vault/nodes/2026-06-21-1408-supabase-keys.md
fixtures/vault/nodes/2026-06-21-1432-supabase-rls-policies.md
fixtures/vault/nodes/2026-06-21-1435-no-pii-in-logs.md
fixtures/vault/ProjectGoal.md
fixtures/vault/SessionState.md
fixtures/vault/TechStack.md
fixtures/vault/VaultIndex.md
package.json
pyproject.toml
README.md
register_agents.py
run_orchestrator.py
scripts/start.ps1
scripts/start.sh
SPEC.md
tests/__init__.py
tests/test_agent_bridge.py
tests/test_bucket2_parity.py
tests/test_bucket3_secrets.py
tests/test_bucket5_skeleton.py
tests/test_concurrent_write.py
tests/test_integration_handoff.py
tests/test_integration_pipeline.py
tests/test_integration_secrets.py
tests/test_integration_session.py
tests/test_p1_cursor.py
tests/test_p1_producer.py
tests/test_p1_reader.py
tests/test_p1_session_state.py
tests/test_p2_notecreator.py
tests/test_p2_scribe.py
tests/test_p3_connector.py
tests/test_p3_evaluator.py
tests/test_p3_handoff.py
tests/test_p3_orchestrator.py
tests/test_watcher_arize.py
vault-mind-orchestrate/.env.example
vault-mind-orchestrate/.gitignore
vault-mind-orchestrate/agent.py
vault-mind-orchestrate/Makefile
vault-mind-orchestrate/README.md
vault-mind-orchestrate/requirements.txt
vault-mind-orchestrate/tests/__init__.py
vault-mind-orchestrate/tests/test_agentverse_chat.py
vaultmind/__init__.py
vaultmind/arize_init.py
vaultmind/connector/__init__.py
vaultmind/contracts.py
vaultmind/evals/__init__.py
vaultmind/evals/pipeline_eval_prompt.md
vaultmind/handoff/__init__.py
vaultmind/hooks/__init__.py
vaultmind/hooks/claude_settings.json
vaultmind/hooks/codex_hooks.json
vaultmind/hooks/pre-commit.sh
vaultmind/ingest/__init__.py
vaultmind/ingest/cursor.py
vaultmind/ingest/producer.py
vaultmind/ingest/reader.py
vaultmind/ingest/session_state.py
vaultmind/memory/__init__.py
vaultmind/notecreator/__init__.py
vaultmind/orchestrator/__init__.py
vaultmind/scribe/__init__.py
vaultmind/scribe/prompt.md
vaultmind/secret-patterns.json
vaultmind/secrets.py
vaultmind/templates/__init__.py
vaultmind/templates/IntentLog.md
vaultmind/templates/scope_node.md
vaultmind/templates/SessionState.md
vaultmind/templates/turn_node.md
vaultmind/templates/VaultIndex.md
vaultmind/watcher.py
webapp/__init__.py
webapp/.gitignore
webapp/agent_bridge.py
webapp/AGENTS.md
webapp/CLAUDE.md
webapp/eslint.config.mjs
webapp/next.config.ts
webapp/package.json
webapp/postcss.config.mjs
webapp/README.md
webapp/requirements-bridge.txt
webapp/src/app/api/agent/route.ts
webapp/src/app/api/conflicts/[id]/resolve/route.ts
webapp/src/app/api/conflicts/[id]/route.ts
webapp/src/app/api/conflicts/route.ts
webapp/src/app/api/events/route.ts
webapp/src/app/api/nodes/route.ts
webapp/src/app/globals.css
webapp/src/app/graph/page.tsx
webapp/src/app/intent/page.tsx
webapp/src/app/layout.tsx
webapp/src/app/merge/page.tsx
webapp/src/app/page.tsx
webapp/src/app/setup/page.tsx
webapp/src/components/AgentChat.tsx
webapp/src/lib/conflicts.ts
[4 more files omitted for size]
```

### Dependencies

- pyproject.toml: anthropic@>=0.30, arize-otel@>=0.0.1, fakeredis@>=2.0, opentelemetry-api@>=1.20, opentelemetry-sdk@>=1.20, pydantic@>=2.0, pytest@>=8.0, pytest-asyncio@>=0.23, redis@>=6.0.0, redisvl@>=0.3.0, sentence-transformers@>=3.0
- vault-mind-orchestrate/requirements.txt: aiohappyeyeballs@==2.6.1, aiohttp@==3.12.15, aiosignal@==1.4.0, annotated-types@==0.7.0, attrs@==25.3.0, bech32@==1.2.0, certifi@==2025.8.3, charset-normalizer@==3.4.3, click@==8.2.1, cosmpy@==0.11.1, distlib@==0.4.0, ecdsa@==0.19.1, filelock@==3.19.1, frozenlist@==1.7.0, googleapis-common-protos@==1.70.0, grpcio@==1.74.0, h11@==0.16.0, idna@==3.10, jsonschema@==4.25.1, jsonschema-specifications@==2025.9.1, multidict@==6.6.4, platformdirs@==4.4.0, propcache@==0.3.2, protobuf@==5.29.5, pycryptodome@==3.23.0, pydantic@==2.11.9, pydantic_core@==2.33.2, python-dateutil@==2.9.0.post0, python-dotenv@==1.0.1, referencing@==0.36.2, requests@==2.32.5, rpds-py@==0.27.1, six@==1.17.0, sortedcontainers@==2.4.0, typing_extensions@==4.15.0, typing-inspection@==0.4.1, uagents@==0.22.8, uagents-core@==0.3.8, urllib3@==2.5.0, uvicorn@==0.35.0, virtualenv@==20.34.0, yarl@==1.20.1
- webapp/package.json: @tailwindcss/postcss@^4, @types/node@^20, @types/react@^19, @types/react-dom@^19, eslint@^9, eslint-config-next@16.2.9, next@16.2.9, react@19.2.4, react-dom@19.2.4, redis@^6.0.0, tailwindcss@^4, typescript@^5

### Recent commits (newest first)

- Merge pull request #6 from timothyouu/emi-ui
- Fixed light mode components
- Fixed display filters/switches functionality
- Reduced size of side panel on graph page
- fix(arize): update register() call to current arize-otel SDK signature
- Merge branch 'main' of https://github.com/timothyouu/vault_mind
- feat(watcher): wire run_eval after done stage with aggregated link results
- fix(watcher): restore comments, add span parentage assertions, guard link_results
- feat(watcher): add root turn span and per-stage child spans
- fix(watcher): restore inline comments and add get_tracer assertion to test
- feat(watcher): wire init_arize and tracer into run_watcher
- fix(test): make _vault helper idempotent with exist_ok=True
- feat(arize): add per-stage span name constants
- docs: add eval & arize wiring implementation plan
- fix(webapp): unify home page navbar with rest of app
- docs: add eval & arize wiring design spec
- feat(webapp): update demo graph to reflect VaultMind architecture
- merge: combine .env.example from main and feature/p2-p3-backend
- fix(startup): replace em dashes with ASCII hyphens in start.ps1
- feat(orchestrate): add vault-mind-orchestrate agent and runner scripts

## Key source files (fetched from GitHub, selected and truncated for size)

### CLAUDE.md

```markdown
# VaultMind

Persistent, structured project memory in Obsidian-compatible Markdown, transferable between LLM
tools (Claude Code, Codex, Gemini) without late-stage summarization. A multi-agent pipeline
writes a git-native vault as you work; a trust UI lets you review and hand off.

> **This file is the brief for both human sessions (Claude Code) and Devin Cloud sessions.**
> The standing rules and execution rules below apply to all readers; the only difference is
> executor — Devin for foundation Buckets 2–4 and streams P1–P3, human for P4.

## Read these first (don't re-derive them)
- **`SPEC.md`** — the technical contract: node schema, the six agent message contracts, the four
  file formats, `scanForSecrets`, hook configs, the session-end resolution, the buckets, and the
  **Execution Model** (roles, bucket-approval protocol, ACU allocation, the Bucket-5 trigger gate,
  and the AC-1…AC-8 unchanged note).
- **`WORKSTREAMS.md`** — who runs what session, what's mockable in isolation, when each seam goes
  live (checkpoints, not deadlines), per-session task order, per-bucket acceptance criteria, ACU
  table, and failure modes. Find your stream here.

## Standing rules (everyone, always — human or Devin session)
1. **Never let a downstream LLM touch the Scribe's content.** The Note Creator wraps the Scribe's
   extraction verbatim; the Connector edits **only** frontmatter `related` — never the body. The
   body is immutable after write.
2. **Always `scanForSecrets` before it matters:** write-time (before any disk write), commit-time
   (pre-commit hook), handoff-time (before the vault is exposed). One Python implementation —
   never add a second.
3. **Disk is the source of truth.** Redis events are minimal "re-read this id" triggers, not
   payloads; the web app re-reads files + `git status` on every event.
4. **`IntentLog.md` is the developer's own words.** Only Auto Mode may write an `ai-detected`
   entry, and it must be labeled. Review Mode never writes it without confirmation.
5. **VaultMind never commits or hands off silently.** Commits are manual; a detected secret
   blocks commit *and* handoff in both Auto and Review modes.
6. **Concurrent-write safety:** appends to `IntentLog.md` / `SessionState.md` use atomic
   write-temp-rename + a `.lock` sentinel (test required — see WORKSTREAMS.md).

## Execution-model rules (Devin sessions — foundation Buckets 2–4, and streams P1, P2, P3)
These rules also apply to human sessions working on any of these streams.

- **Hard-stop per bucket.** Complete exactly one bucket, post the diff, and halt until a human
  approves and merges via **Devin Review**. Do not begin the next bucket without that approval.
  ("Devin Review" is the interface a human reviewer uses — not Devin reviewing itself.)
- **Halt-on-ambiguity.** If a bucket is underspecified or a frozen contract appears to need
  changing, stop and surface the question. Never guess or invent an interface.
- **Stay-in-lane.** Touch only your session's
[truncated — 3971 more characters]
```

### DEVIN-PIVOT-SPEC.md

```markdown
# Spec: VaultMind Execution-Layer Pivot (Devin)

> Revision spec. Aligns `SPEC.md`, `WORKSTREAMS.md`, and `CLAUDE.md` with the proposal's one
> architecture pivot since they were written: **Devin is no longer an in-product feature — it is
> the execution layer for the build itself.** This is the contract for *that revision only*. It does
> not re-argue the product and reopens **no** byte-level contract. Sources: the current proposal PDF,
> sections "How the Build Actually Runs: Devin as the Execution Layer" and the Cognition (Devin)
> sponsor-track entry.

---

## Roles at a glance (read this first — it's the whole pivot)

| Who | Surface | Does what | Draws Devin ACUs? |
|---|---|---|---|
| **Devin** (4 sessions: foundation + P1 + P2 + P3) | Devin Cloud | **Executes** — writes the code for foundation Buckets 2–4 and streams P1, P2, P3, one bucket at a time, unattended between boundaries, subagents allowed *within* a bucket | **Yes** (the ~266 pool) |
| **Humans** (the team) | Devin Review + Claude Code | **Review** every Devin bucket via Devin Review (approve + merge before the next bucket starts); **witness Bucket 5** live fire; **build P4** (Claude Code session); own the **carve-outs** (Agentverse, ASI:One URL, demo video, integration) | No |

**"Devin Review" is the interface a *human* reviews through — it is not Devin reviewing itself.**
Devin is the executor; the human is the reviewer.

---

## Goal

Today `SPEC.md` and `WORKSTREAMS.md` assume a human types every bucket of every stream — "four
owners, each in their own Claude Code session," buckets reviewed by the person who wrote them. The
proposal changed that: **streams P1–P3 and foundation Buckets 2–4 are now executed by Devin Cloud
sessions; only P4 stays human-driven.**

After this revision, the three build docs describe *that* world and only that world:

- **P1, P2, P3** are written by Devin, **hard-stop per bucket** — a session does exactly one bucket
  (it may parallelize with subagents *inside* the bucket), then halts until a human approves +
  merges via Devin Review before the next bucket begins.
- **P4** is built by a human in Claude Code, with **no Devin session**, deliberately — UI/UX is a
  judged general-prize category (Best UI/UX) that benefits from direct human taste and iteration.
- **Foundation Buckets 2–4** are also executed by a dedicated Devin session — same hard-stop-per-
  bucket rules, human review at each boundary. Bucket 1 (this doc set) is already done. **Bucket 5
  is Devin-wired and human-witnessed** — the team observes the live fire together; passing it
  triggers the P1–P3 sessions.
- The work is governed by a **fixed ~266-ACU credit pool** ($600) shared across **P1–P3 and the
  foundation session**.

The byte-level contracts that made the seams parallel-safe for four humans are **unchanged** — and
are now the precondition that makes unattended cloud execution safe (an ambiguous spec is fine when a
human can ask a clarifying question mid-build; it is a real
[truncated — 11909 more characters]
```

### package.json

```
{
  "name": "vaultmind",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "vaultmind:start": "powershell -ExecutionPolicy Bypass -File scripts/start.ps1",
    "dev": "cd webapp && npm run dev"
  }
}

```

### docker-compose.yml

```yaml
version: "3.9"
services:
  redis:
    image: redis/redis-stack:latest   # includes RedisSearch for vector index (P3)
    ports:
      - "6379:6379"
      - "8001:8001"   # RedisInsight UI
    volumes:
      - redis-data:/data
    restart: unless-stopped

volumes:
  redis-data:

```

### pyproject.toml

```
[build-system]
requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "vaultmind"
version = "0.1.0"
description = "Persistent, structured project memory in Obsidian-compatible Markdown"
requires-python = ">=3.11"
dependencies = [
    "pydantic>=2.0",
    "redis>=6.0.0",
    "redisvl>=0.3.0",
    "sentence-transformers>=3.0",
    "anthropic>=0.30",
    "arize-otel>=0.0.1",
    "opentelemetry-api>=1.20",
    "opentelemetry-sdk>=1.20",
]

[project.optional-dependencies]
dev = [
    "pytest>=8.0",
    "pytest-asyncio>=0.23",
    "fakeredis>=2.0",
]

[tool.setuptools.packages.find]
where = ["."]
include = ["vaultmind*"]

[tool.setuptools.package-data]
vaultmind = ["*.json", "evals/*.md"]

```

### webapp/package.json

```
{
  "name": "webapp",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint"
  },
  "dependencies": {
    "next": "16.2.9",
    "react": "19.2.4",
    "react-dom": "19.2.4",
    "redis": "^6.0.0"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "16.2.9",
    "tailwindcss": "^4",
    "typescript": "^5"
  }
}

```

### vault-mind-orchestrate/requirements.txt

```
aiohappyeyeballs==2.6.1
aiohttp==3.12.15
aiosignal==1.4.0
annotated-types==0.7.0
attrs==25.3.0
bech32==1.2.0
certifi==2025.8.3
charset-normalizer==3.4.3
click==8.2.1
cosmpy==0.11.1
distlib==0.4.0
ecdsa==0.19.1
filelock==3.19.1
frozenlist==1.7.0
googleapis-common-protos==1.70.0
grpcio==1.74.0
h11==0.16.0
idna==3.10
jsonschema==4.25.1
jsonschema-specifications==2025.9.1
multidict==6.6.4
platformdirs==4.4.0
propcache==0.3.2
protobuf==5.29.5
pycryptodome==3.23.0
pydantic==2.11.9
pydantic_core==2.33.2
python-dateutil==2.9.0.post0
referencing==0.36.2
requests==2.32.5
rpds-py==0.27.1
six==1.17.0
sortedcontainers==2.4.0
typing-inspection==0.4.1
typing_extensions==4.15.0
uagents==0.22.8
uagents-core==0.3.8
urllib3==2.5.0
uvicorn==0.35.0
virtualenv==20.34.0
yarl==1.20.1
python-dotenv==1.0.1

```

### webapp/src/app/layout.tsx

```typescript
import type { Metadata } from "next";
import { Geist, Geist_Mono, JetBrains_Mono } from "next/font/google";
import "./globals.css";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

// JetBrains Mono is a variable font (wght 100–800); no weight array needed.
const jetbrainsMono = JetBrains_Mono({
  variable: "--font-jetbrains-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "VaultMind",
  description: "Persistent structured project memory",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html
      lang="en"
      className={`${geistSans.variable} ${geistMono.variable} ${jetbrainsMono.variable} h-full antialiased`}
    >
      <body className="min-h-full flex flex-col">{children}</body>
    </html>
  );
}

```

### webapp/src/app/page.tsx

```typescript
"use client";

import { useEffect, useState, useCallback } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import type { NodeChangedEvent } from "../../types";
import type { VaultNode } from "./api/nodes/route";

// ---------------------------------------------------------------------------
// Shared nav (matches graph, setup, intent pages)
// ---------------------------------------------------------------------------

function VaultNav({ theme, onToggle, connected }: {
  theme: "dark" | "light";
  onToggle: () => void;
  connected: boolean;
}) {
  const path = usePathname();
  const links = [
    { href: "/setup", label: "Setup" },
    { href: "/graph", label: "Graph" },
    { href: "/intent", label: "Intent log" },
    { href: "/merge", label: "Merge" },
  ];
  return (
    <header style={{
      flexShrink: 0,
      display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16,
      height: 56, padding: "0 20px",
      background: "var(--bg)", borderBottom: "1px solid var(--border)",
      position: "sticky", top: 0, zIndex: 30,
    }}>
      <div style={{ display: "flex", alignItems: "center", gap: 14 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <div style={{
            width: 26, height: 26, borderRadius: 7,
            background: "linear-gradient(135deg, var(--accent), #7d5bed)",
            display: "flex", alignItems: "center", justifyContent: "center",
            boxShadow: "inset 0 0 0 1px rgba(255,255,255,.12)",
          }}>
            <svg width="15" height="15" viewBox="0 0 24 24" fill="none">
              <path d="M12 2l8 4.5v9L12 22l-8-6.5v-9L12 2z" stroke="#fff" strokeWidth="1.6" strokeLinejoin="round" />
              <circle cx="12" cy="11" r="2.4" fill="#fff" />
            </svg>
          </div>
          <span style={{ fontWeight: 600, fontSize: 15, letterSpacing: "-0.2px" }}>VaultMind</span>
        </div>
        <nav style={{ display: "flex", alignItems: "center", gap: 2, marginLeft: 4 }}>
          {links.map(({ href, label }) => {
            const active = path === href || (path === "/" && href === "/intent");
            return (
              <Link key={href} href={href} style={{
                padding: "6px 11px", borderRadius: 7, fontSize: 13, textDecoration: "none",
                color: active ? "var(--text)" : "var(--muted)",
                fontWeight: active ? 500 : 400,
                background: active ? "var(--surface)" : "transparent",
                border: active ? "1px solid var(--border)" : "1px solid transparent",
              }}>{label}</Link>
            );
          })}
        </nav>
      </div>
      <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
        <div style={{
          display: "flex", alignItems: "center", gap: 7, padding: "5px 11px",
          background: "var(--surface)", border: "1px solid var(--border)", borderRadius: 9999,
          fontSize: 12, color: "var(--muted)",
        }}>
          <span style={{
            width: 7, height: 7, borderRadius: "50%",
            background: connected ? "var(--green)" : "var(--red)",
            animation: connected ? "vm-livedot 1.6s ease-in-out infinite" : "none",
          }} />
          {connected ? "Live · watching vault" : "Disconnected"}
        </div>
        <button onClick={onToggle} title="Toggle theme" style={{
          width: 34, height: 34, display: "flex", alignItems: "center", justifyContent: "center",
          background: "var(--surface)", border: "1px solid var(--border)", borderRadius: 8,
          color: "var(--muted)", cursor: "pointer",
        }}>
          {theme === "dark"
            ? <svg width="15" height="15" viewBox="0 0 24 24" fill="none"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z" stroke="currentColor" strokeWidth="2" strokeLinejoin="round" /></svg>
            : <svg width="15" height="15" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="4" stroke="currentColor" strokeWidth="2" /><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" /></svg>
          }
        </button>
      </div>
    </header>
  );
}

// ---------------------------------------------------------------------------
// Type badge colors
// ---------------------------------------------------------------------------

const TYPE_COLORS: Record<string, { bg: string; text: string; border: string }> = {
  decision:   { bg: "rgba(56,139,253,0.12)",  text: "#388bfd", border: "rgba(56,139,253,0.3)" },
  constraint: { bg: "rgba(248,81,73,0.1)",    text: "#f85149", border: "rgba(248,81,73,0.3)" },
  goal:       { bg: "rgba(63,185,80,0.12)",   text: "#3fb950", border: "rgba(63,185,80,0.3)" },
  question:   { bg: "rgba(163,113,247,0.12)", text: "#a371f7", border: "rgba(163,113,247,0.3)" },
  scope:      { bg: "rgba(210,153,34,0.15)",  text: "#d29922", border: "rgba(210,153,34,0.3)" },
};

const EVENT_COLORS: Record<string, string> = {
  created:        "#3fb950",
  linked:         "#388bfd",
  updated:        "#d29922",
  deleted:        "#f85149",
  "secret-detected": "#f85149",
  "intent-updated":  "#a371f7",
  "session-event":   "#7d8590",
};

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

function fmtTime(iso: string) {
  try {
    return new Date(iso).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" });
  } catch { return iso; }
}

function fmtDate(iso: string) {
  try {
    return new Date(iso).toLocaleDateString([], { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
  } catch { return iso; }
}

function shortId(id: string) {
  const parts = id.split("-");
  return parts.length >= 3 ? parts.slice(2, 4).join("-").slice(0, 8) : id.slice(0, 8);
}

// --
[truncated — 12539 more characters]
```

### webapp/src/app/api/conflicts/route.ts

```typescript
import { NextRequest, NextResponse } from "next/server";
import { listConflictedNodes } from "../../../lib/conflicts";

export const dynamic = "force-dynamic";
export const runtime = "nodejs";

export async function GET(_req: NextRequest) {
  const files = listConflictedNodes();
  return NextResponse.json({ files });
}

```

[84 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]