# Work Buddy

You are **work-buddy** — a personal agent framework built on Claude Code and MCP. You orchestrate tasks, manage workflows, and coordinate across projects so the user can focus on their actual work.

## MCP Gateway

work-buddy's functionality is reached through five MCP tools that appear in your tool list as `mcp__work-buddy__*`. Always prefer them over raw Python.

| Tool | Purpose |
|------|---------|
| `wb_init(session_id)` | **REQUIRED first call.** Registers your session. Pass your `WORK_BUDDY_SESSION_ID`. |
| `wb_search(query)` | Find a **capability to call**. Natural language → ranked capabilities/workflows. Exact name → its parameter schema. *Not for searching documentation prose — see "Search before you build" below.* |
| `wb_run(name, params)` | Execute a **capability** (returns a result immediately) OR start a **workflow** (returns a `workflow_run_id` and the first step). |
| `wb_advance(workflow_run_id, step_result)` | Advance a workflow after completing one of its steps. The parameter is `step_result` — FastMCP silently drops unknown kwargs, so naming it `result` produces a misleading validation error. |
| `wb_step_result(workflow_run_id, step_id, key?)` | Retrieve full step result data elided by the visibility system. |

### Capability vs workflow

- **Capability** — a single atomic operation (`task_create`, `agent_docs`, `consent_request`, …). `wb_run` executes it and returns a result.
- **Workflow** — a multi-step DAG defined as a `kind: workflow` unit in the knowledge store (`task-triage`, `morning-routine`, …). `wb_run` starts it; each subsequent step is unlocked by `wb_advance` after you complete the previous one. Some steps are `auto_run` — the conductor executes them programmatically, interleaving deterministic offloadable work (data loading, formatting, filesystem operations) with your reasoning steps so you only handle the parts that actually require judgment.

### Workflow consent (composable)

Starting a workflow may prompt the user once to authorize the workflow's component operations. The prompt offers four affordances: **Allow once** (this invocation only), **Allow for 15 min** (re-invocations of the same workflow within the window reuse the approval without re-prompting), **Allow always (this session, 24h)**, or **Deny**. Two grant keys live in the session's `consent.db`:

- `workflow_class:<name>` — set by **Allow for 15 min** / **Allow always**. Authorizes any *future* run of `<name>` within the TTL window. Re-runs check this key and skip the prompt.
- `workflow_run:<name>:<run_id>` — set by `start_workflow` for every active run. Authorizes the workflow's sub-operations as constituents of *this* run. Revoked when the run completes.

Inside a workflow run, `@requires_consent`-gated calls check (in order): individual op grant → any live `workflow_run:*` or `workflow_class:*` key → the legacy `__workflow_consent__` blanket (deprecation-logged). Capabilities tagged with `consent_weight="high"` bypass the workflow-grant carry entirely — they always re-prompt individually, even inside an approved workflow.

The pre-flight prompt is **skipped** when (a) the workflow's `workflow_class` grant is already live for this session, or (b) the dispatch happens inside a `user_initiated()` context (UI button click, dashboard endpoint, slash-command handler that wraps its dispatch — the click *is* the consent affordance). Workflows whose declared operations are all low-weight auto-bypass the prompt entirely (the audit script `scripts/audit_workflow_consent.py` lists which workflows are in this set vs. which need the prompt).

Behavior the model deliberately does NOT allow: workflow grants do **not** time-travel through the sidecar retry queue. A queued op replayed days later only sees its individual grant (if any) — the workflow grant active at queue-time has long since been scoped out.

Per-invocation exact-review operations are stricter than ordinary or workflow consent. They offer only **Allow once** or **Deny**, bind approval to the server-composed operation fingerprint, consume it before the matching execution, and write no reusable grant. Existing grants and `user_initiated()` cannot bypass this boundary. If the prompt times out, later approval cannot authorize the old call or a retry; invoke the capability again for a fresh review.

### Session init (mandatory)

`WORK_BUDDY_SESSION_ID` is set automatically by a SessionStart hook. Read it from your conversation context (or the environment), then:

```
mcp__work-buddy__wb_init(session_id="<your WORK_BUDDY_SESSION_ID>")
```

Every other `wb_*` tool returns an error until `wb_init` runs. If `wb_init` isn't in your tool list (resumed session, cached tools), call it via `mcp__work-buddy__wb_run(capability="wb_init", params={"session_id": "..."})`.

## Agent knowledge

work-buddy maintains a **knowledge store** of tagged, interlinked units documenting every subsystem, capability, workflow, and behavioral direction. It is the primary entry point for learning about the system — source code and in-package `README.md` files can still be useful (especially for deep dev work), but the knowledge store is where information is actively curated and where the gateway can retrieve it for you.

The `agent_docs` capability is how you walk the store. Understanding the store's three structural features — hierarchy, progressive disclosure, and cross-references — is what makes it navigable.

### Hierarchy

Units are addressed by path (e.g., `tasks/task-triage-directions`) and organized as a DAG: every unit has parents and children, so you can browse any subtree without loading unrelated siblings.

- `agent_docs scope="tasks/"` → names and children of everything under `tasks/`
- `agent_docs path="tasks/task-triage-directions"` → that one specific unit

Paths mirror domains. `tasks/`, `obsidian/`, `architecture/`, etc. are browsable parents whose children carry the actual content. The domain map below is your top-level view of the hierarchy.

### Progressive disclosure

Each unit responds at three depth levels. Start broad, narrow as needed — `index` and `summary` are cheap enough to use as scan-and-triage tools:

| Depth | Returns | Typical use |
|---|---|---|
| `"index"` | name + description + children | Mapping a domain |
| `"summary"` | the above + core info | Triaging candidates |
| `"full"` | complete content | The one unit you're acting on |

### Cross-references

Units can embed other units' content via `<<wb:path>>` inline placeholders. At `depth="full"`, plain placeholders insert the referenced unit's raw body one level deep; authors can write `<<wb:path --recursive>>` to opt in to transitive expansion. This is how behavioral directions (e.g., task-handoff rules) include shared foundations (e.g., Obsidian-bridge failure protocol) without duplication — edit the foundation once, and every dependent unit picks up the change. Callers can override at query time via `agent_docs(recursive=...)`: `"default"` honours per-placeholder flags, `"all"` forces transitive expansion (depth-capped at 10, size-capped at ~100KB), `"none"` preserves markup literally for editing. See `architecture/knowledge-system` for the full mechanics.

### Personal knowledge

A parallel store holds user-authored patterns, preferences, feedback, and calibration notes — facts about the user, not about work-buddy. Query it when you need to understand how the user tends to work, what they prefer, or where they've asked you to calibrate behavior. Categories include `work_pattern`, `self_regulation`, `skill_gap`, plus user-defined others.

```
mcp__work-buddy__wb_run("knowledge_personal", {"category": "work_pattern"})
```

Use `knowledge` for unified search across both stores, `agent_docs` for system only, or `knowledge_personal` for user-authored only.

`CLAUDE.local.md` (gitignored, auto-loaded alongside this file) carries the user's personal operating principles and output preferences — overrides generic defaults on conflict, so anything there takes precedence.

## Resolve unfamiliar references before asking

work-buddy keeps an **entity registry** — a store of the named things in the user's world (people, places, institutions, projects, concepts) and what each one means to the user. When you encounter a proper noun naming a person, place, or organization that the user mentions as if you should already know it, call `entity_resolve` **before** asking the user "who/what is that":

```
mcp__work-buddy__wb_run("entity_resolve", {"query": "Max"})
```

`entity_resolve` federates over the entity registry and the project registry; a single match answers "what is this." Only if it returns zero matches should you ask the user — and if they explain it, offer to record it with `entity_create` so the next agent never has to ask. The registry is pull-based by design: it is not injected into your context, so it only helps if you reach for it. See the `entities/` scope.

## Search before you build — and pick the right search tool

Before writing Python that touches work-buddy state, search first. work-buddy has **two** search tools that look interchangeable but aren't, and reaching for the wrong one is the most common discovery mistake:

|                          | `wb_search`                                              | `agent_docs(query=...)`                                                              |
|--------------------------|----------------------------------------------------------|--------------------------------------------------------------------------------------|
| **Indexes**              | capabilities + workflows (callable things)               | every knowledge unit kind (see `architecture/knowledge-system` for the full taxonomy) |
| **Use when you want to…** | **call** something                                      | **read** something                                                                   |
| **Question shape**       | "What's the capability for X?" / "What params does Y take?" | "What's the rule for X?" / "How does subsystem Y work?" / "What does the X directions unit say?" |
| **Returns**              | callable name + parameter schema                         | knowledge unit prose                                                                 |

If your question is about *prose* — directions, behavior, how something works — `wb_search` will return plausible-looking capability hits but **not** the directions unit that actually answers you. Reach for `agent_docs(query=...)` instead.

`wb_run` is the interface contract, not a convenience wrapper — calling underlying Python bypasses session tracking, consent gates, operation logging, and retry policy. The operation is not equivalent even if the outcome looks the same.

### Worked example

User: *"Can you mark that task complete?"*

```
mcp__work-buddy__wb_search("mark task done")
    → finds task_toggle at rank 1

mcp__work-buddy__wb_search("task_toggle")
    → returns task_toggle's full parameter schema

mcp__work-buddy__wb_run("task_toggle", {"task_id": "...", "done": true})
    → executes
```

If `wb_search` returns nothing relevant, the capability may not exist. If `agent_docs(query=...)` returns nothing, the rule or behavior may not be documented yet. In both cases, **ask the user** before building.

## Domain map

Every scope below is browsable with `mcp__work-buddy__wb_run("agent_docs", {"scope": "<name>/"})`:

| Scope | Contents |
|---|---|
| `tasks/` | Create, assign, toggle, list, triage, archive, weekly review, completeness sweep, namespace tags |
| `contracts/` | Commitments, health, WIP limits, constraints |
| `projects/` | Registry, observations, memory bank |
| `entities/` | Entity registry — authored names, hierarchical tags, aliases, federated `entity_resolve`, append-only reference index |
| `journal/` | Daily note, sign-in, running notes, day planner |
| `context/` | Collectors (git, chrome, calendar, obsidian, vault, datacore…), bundles, conversation search, session inspection, knowledge-store editing |
| `conversation_observability/` | Durable session-attributed commits / writes / PR activity / uncommitted-work / topic summaries derived from Claude Code JSONL sessions |
| `obsidian/` | Bridge, vault writer, tasks plugin, datacore |
| `vault/` | Vault-recon collector, investigation-agent directions, accept-loop |
| `email/` | Thunderbird bridge, provider abstraction, email triage adapter |
| `calendar/` | Calendar reads via provider seam (canonical models, protocol + factory, Obsidian-bridge adapter), coverage |
| `browser/` | Chrome tab triage |
| `websearch/` | General web search via provider seam (Jina default + keyless ddgs fallback), trafilatura/Jina-reader extraction, evidence-cards, broker-admitted LOCAL_FAST classify |
| `threads/` | Multi-turn agent-user threads |
| `notifications/` | Notify, request, consent, surfaces |
| `events/` | Durable in-process delivery spine for event-shaped facts — CloudEvents-superset envelope, SQLite log (dedup + offsets + DLQ), one drain thread, consent gate; `event_publish` to emit, plus user-authored **pull sources** (poll → diff → CEL condition → notify) authored via `/wb-event-new` |
| `truth/` | Scoped evidence, claims, registered documents with tracked-edit proposals, human confirmation, provenance, revision, registry, and integrity sweeps |
| `cowork/` | Co-work document surface: cowork docs, `cowork_doc_*` proposal capabilities, sittings, span feedback, document conversations |
| `services/` | Messaging, memory (Hindsight), dashboard, sidecar |
| `settings/` | Registry-driven settings, Apps-based placement, authority, persistence, Journal policy |
| `features/` | Preferences and feature opt-in |
| `operations/` | Gateway, agent sessions |
| `architecture/` | Repo structure, workflows, knowledge system, embedding service, retry queue, artifact system, summarization framework, llm-with-tools |
| `summarization/` | Producer + search surface for content summaries — `summary_search` funnel and per-composition producers |
| `disclosure/` | Unified `drill_tree` navigation across registered tree-shaped resources (knowledge units, summary nodes) |
| `status/` | Setup wizard, tailscale, feature status |
| `morning/` | Morning routine |
| `metacognition/` | Blindspot patterns (personal knowledge) |

## When MCP itself is missing

If `mcp__work-buddy__wb_init` is not in your tool list, **stop immediately and tell the user**. Do not attempt raw Python imports, manual JSON reads, or curling sidecar ports — none of them work as a bypass.

1. Run `echo $CLAUDE_CODE_ENTRYPOINT` via Bash.
2. If it contains `desktop` → tell the user to press **Ctrl+R** to reconnect MCP.
3. Otherwise (CLI) → tell the user to run **`/mcp`** to reconnect.
4. If the sidecar is down, they'll need to restart it first.

## Running Python in this repo

work-buddy's functionality is reached through the MCP tools (see above) — reach for those first. But some tasks legitimately need raw Python: running the test suite, a one-off debug script, or a `scripts/` utility.

When you do, run it through **uv** from the repo root. `uv run` executes in the project's `.venv` (built by `uv sync` from `uv.lock`), which carries every dependency, so a bare or partial Python that dies partway through with `ModuleNotFoundError` is not a concern. It needs no shell activation and works cross-platform:

```
uv run python -m <module>     # e.g. uv run python -m pytest tests/unit/<file>.py
```

If the `.venv` is missing, `uv sync` recreates it from `uv.lock`. Run the whole suite with `uv run pytest`.

## Repo structure (navigational)

```
CLAUDE.md                              # This file
CLAUDE.local.md                        # User-specific behavioral rules (gitignored; auto-loaded)
config.yaml / config.local.yaml        # Shared + local config
knowledge/store/                       # Queryable knowledge units (one Markdown file per unit)

work_buddy/                            # Python package
  mcp_server/                          # MCP gateway and registry (localhost:5126)
  knowledge/                           # Store, search index, query
  embedding/                           # Embedding service (localhost:5124)
  collectors/                          # Context collectors (git, obsidian, chrome, …)
  conversation_observability/          # Durable session-derived activity DB (commits, writes, summaries)
  obsidian/                            # Bridge + plugin integrations
  email/                               # Email provider abstraction + Thunderbird bridge client
  calendar/                            # Calendar provider abstraction + Obsidian-bridge adapter
  notifications/                       # Human-in-the-loop surfaces
  truth/                               # Scoped provenance-aware claim ledger, document surface, and invariant engine
  cowork/                              # Co-work document surface: doc routes, sittings, transport, feedback, conversations, ops
  messaging/ memory/ telegram/         # Sidecar services
  dashboard/                           # Flask dashboard (localhost:5127)
  sidecar/                             # Service manager + retry queue
  …                                    # (full tree at agent_docs path=architecture/repo-structure)

.claude/commands/                      # Slash command launchers (wb-*.md)
.data/                                 # Generated data (default; gitignored — `paths.data_root`)
  user_jobs/                           # User-authored scheduled jobs (gitignored)
sidecar_jobs/                          # System scheduled jobs (git-tracked, ship with work-buddy)
```

