# Agent Workspace Architecture — full text > A redacted reference for building agent workspaces: the roles, routines, > hooks, memory, and task coordination that turn a coding agent into a system > you can hand work to. One person's working setup, published with the > reasoning attached. This file is the `llms-full.txt` companion to `llms.txt`. It inlines the full text of the repository's substantive documentation so a language model can read the whole reference in one request instead of following the link map. Generated by `scripts/gen_llms_full.py` — edit the source documents, not this file. Canonical HTML tour: https://jimy-r.github.io/agent-workspace-architecture/ Repository: https://github.com/jimy-r/agent-workspace-architecture Contents, in reading order: 1. README.md 2. PATTERNS.md 3. META_ARCHITECTURE.md 4. EVALUATION.md 5. ADOPTION.md 6. WORKFLOW.md ============================================================================== SOURCE: README.md URL: https://github.com/jimy-r/agent-workspace-architecture/blob/main/README.md ============================================================================== # agent-workspace-architecture [![Redaction check](https://img.shields.io/github/actions/workflow/status/jimy-r/agent-workspace-architecture/redaction-check.yml?label=redaction)](https://github.com/jimy-r/agent-workspace-architecture/actions/workflows/redaction-check.yml) [![Link check](https://img.shields.io/github/actions/workflow/status/jimy-r/agent-workspace-architecture/link-check.yml?label=links)](https://github.com/jimy-r/agent-workspace-architecture/actions/workflows/link-check.yml) [![Validate samples](https://img.shields.io/github/actions/workflow/status/jimy-r/agent-workspace-architecture/validate-samples.yml?label=samples)](https://github.com/jimy-r/agent-workspace-architecture/actions/workflows/validate-samples.yml) A reference implementation of **agent-ready knowledge architecture**: the roles, routines, hooks, skills, memory, and task coordination that make a body of working knowledge legible to AI agents, and turn a coding agent into a system you can hand work to and trust to make progress while you're away. [![The entire architecture in one diagram: interactive, remote, and scheduled entry points feed the session, which runs with rules, typed memory, and lessons loaded; a governance floor sits under every action; state persists on one side, outputs ship past a human gate on the other, and a watcher rail checks the whole system](docs/assets/workspace-map.png)](https://jimy-r.github.io/agent-workspace-architecture/workspace-map.html) The diagram above is the whole system — click it for the [full-page map with legend](https://jimy-r.github.io/agent-workspace-architecture/workspace-map.html), a thirty-second read. **▶ [Take the interactive tour](https://jimy-r.github.io/agent-workspace-architecture/)** — the clickable five-minute version: the layered architecture, the eighteen load-bearing patterns, and one task moving through the system end to end. Feeding this to a model instead? [`llms.txt`](https://jimy-r.github.io/agent-workspace-architecture/llms.txt) is the link map, and [`llms-full.txt`](https://jimy-r.github.io/agent-workspace-architecture/llms-full.txt) inlines the whole reference in a single fetch. New work ships irregularly: patterns, teardowns, tools, and the occasional essay. Follow along at [Agent Workspaces](https://jimyr.substack.com), or watch the repo. ## Try it in 60 seconds One runnable artefact ships here: a single-file, read-only linter that scores a workspace against the checkable patterns and prints one evidence line per check. No install, no dependencies beyond Python 3.9+, and it writes nothing. ```bash curl -sSLO https://raw.githubusercontent.com/jimy-r/agent-workspace-architecture/main/tools/workspace_check.py python3 workspace_check.py /path/to/your/workspace ``` What you see (this repo's own score, abbreviated): ```text [PASS] context-budget — 4.7 KB across 1 always-loaded file(s) (pattern 9) [FAIL] secrets-in-files — 2 hit(s) (pattern 6) Score 6.4/10 — maturing (7 scored, 5 not applicable) ``` Each check names the pattern it comes from, so a `[FAIL]` is a link into [PATTERNS.md](PATTERNS.md) rather than a scolding. Post your score in [Show and tell](https://github.com/jimy-r/agent-workspace-architecture/discussions/categories/show-and-tell) if you want a second read on it. The example runs in [Claude Code](https://claude.com/claude-code), so the file conventions you'll see (`CLAUDE.md`, `.claude/skills/`, MCP config) are Claude-Code-specific. The architecture is not. The roles library, memory hygiene, audit cadence, explicit-delegation task board, dead-man's switch, and tier-by-impact gating port to Cursor, Cline, Continue, Windsurf, or a custom Agent-SDK build. Pick your runtime; the decisions translate. This is one person's actual setup, redacted and published as a reference. Not a framework, not a product. A documented working arrangement of the pieces Claude Code already gives you, with the reasoning attached. It is also the reference version of the agent-ready memory layer I build for organisations, running at one-person scale. The scale is real: 17 expert roles, 18 load-bearing patterns, an explicit-delegation task board that succeeded a retired 2-hourly heartbeat, a weekly self-audit with synthetic canaries, a dead-man's switch over scheduled jobs, and typed memory that points at sources instead of copying them — all of it running in one person's daily workspace. ## What's inside - **Roles library.** 17 pure expert personas (security-auditor, researcher, accountant, developmental-editor, and more) that compose with project `CONTEXT.md` files through thin bindings. - **Task board + audit subagent.** One canonical markdown card store rendered to a local served view, with an explicit delegation queue. The operator marks a card queued, a short intake interview captures what done looks like and which folders may be written, and a drain skill actions the queue inside a live session. The close-out ritual logs a per-task token record, so the metrics page charts capacity from finished work rather than from a schedule. A 2-hourly classify-then-act heartbeat held this job until August 2026. It was retired, and its design stays in [`samples/tasks/`](samples/tasks/) as the studied predecessor. Alongside sits the weekly upgrade audit, whose first job is finding improvements (public-source research plus a module-by-module critique against current best practice), with configs, security, and drift checked in the same sweep. - **Custom skills.** `orient`, `wrap`, `tasks`, `review-queue`, `audit-workthrough`, `terse-mode`, `verify-completion`, `systematic-debugging`, `goal-design`, `role-pressure-test`. - **Scheduled routines.** A daily morning brief (calendar, weather, AI news, task state) and a memory-consolidation pass, fired by the OS scheduler. - **Memory system.** Typed files (`user` / `feedback` / `project` / `reference`) indexed by `MEMORY.md`, pointing at sources rather than copying them. - **Hardening.** A `PreToolUse` file-and-command guard, a password-manager credential law, encrypted `restic` backups, and container sandboxing for web-facing agents. - **Evaluation.** A golden set of frozen cases, each one an already-burned failure turned into a regression test, replayed K times and reported as a pass-rate with variance. Deterministic checks only, never an LLM judge, so a drop in the rate is a real regression rather than judge noise. See [EVALUATION.md](EVALUATION.md). - **Workspace check.** A single-file, read-only linter ([`tools/workspace_check.py`](tools/workspace_check.py)) that scores your own workspace against the mechanically-checkable patterns (context budget, hook shape, permission floor, credential hygiene, skill and agent frontmatter, duplicated instruction blocks) and prints one evidence line per check. - **The ecosystem map.** A separately maintained curated index of the wider tooling space: [awesome-agent-workspaces](https://github.com/jimy-r/awesome-agent-workspaces) — memory systems, evaluation, guardrails, observability, with a stated inclusion bar and a public rejection log. - **Token budget.** A deterministic preflight gate that skips no-op scheduled cycles, a model-tier policy for unattended work, and daily spend telemetry feeding the weekly audit. Tables throughout mark each component `[stock]` / `[plugin]` / `[local]` / `[custom]`, so you can see what ships with Claude Code versus what someone had to write. ## Start with the why If you read one thing past this page, read **[PATTERNS.md](PATTERNS.md)** — the eighteen load-bearing architectural decisions, each as *problem → pattern → why it beats the obvious alternative → what it costs*. That's where the actual thinking lives. The rest of the docs follow [Diátaxis](https://diataxis.fr/): | Quadrant | Doc | Read it for | |---|---|---| | Explanation | [PATTERNS.md](PATTERNS.md) | why the shape is the way it is | | Evidence | [teardowns/](teardowns/) | published architectures read against the patterns | | Reference | [META_ARCHITECTURE.md](META_ARCHITECTURE.md) | the full structural map, with diagrams | | Tutorial | [ADOPTION.md](ADOPTION.md) | a 5-step build, minimum-viable at each step | | Tutorial | [learn/](learn/) | a guided track through the patterns, by capability, with exercises | | Explanation | [EVALUATION.md](EVALUATION.md) | how to tell whether a workspace change actually helped | | How-to | [samples/](samples/) | scaffold files to fork and adapt | | How-to | [tools/workspace_check.py](tools/workspace_check.py) | run a scored check of your own workspace | Two more views. **[WORKFLOW.md](WORKFLOW.md)** shows a day of actually using it: session discipline, phone dispatch, how a task moves thought-to-done, and the open structured-vs-autonomous tension the whole design sits inside. And you can hand the repo to your own agent: > *"Tour this repo. Read PATTERNS.md, then META_ARCHITECTURE.md, then WORKFLOW.md, then scan samples/. Summarise the patterns most applicable to my workspace."* The repo's [`CLAUDE.md`](CLAUDE.md) auto-loads on session start, so your agent inherits the conventions before it answers. ## Who built this James Ross. I work as an AI Knowledge Architect; the practice is **Agent-Ready Knowledge Architecture** — making an organisation's knowledge legible to AI agents. This workspace is the reference version of my own agent-ready memory layer: the source-of-truth conventions, context architecture, and memory governance the practice teaches, running daily in production. If you're standing up something similar inside an organisation, or want these patterns adapted to your stack, the practice site is **[jamesross.ai](https://jamesross.ai/?utm_source=github&utm_medium=readme&utm_campaign=flagship)**. ## Using it Fork freely ([MIT](LICENSE)); that's what it's for. Adapt the samples, lift the patterns, localise the domain-flavoured bits (the `accountant` role is Australian-CPA shaped, the morning brief fetches Brisbane weather). This is a **curated solo reference**, maintained best-effort. Questions, comparisons with your own setup, and "does this hold at team scale" go to [Discussions](https://github.com/jimy-r/agent-workspace-architecture/discussions), the canonical Q&A home, where answers stay findable. If you spot a privacy leak, a broken link, or a pattern that's plainly wrong, [open an issue](https://github.com/jimy-r/agent-workspace-architecture/issues/new/choose) and I'll get to it when time allows. Substantial PRs are welcome, but a good one can still be declined if it pulls the doc off its shape: it stays one coherent worked example, not a grab-bag. **One hard rule for anything you send:** no personal identifiers, no credentials, no business / health / financial specifics. Every commit is safe for a public audience. Full guidance in [CONTRIBUTING.md](CONTRIBUTING.md). ## Caveats - Paths are generic (``, ``); a real setup substitutes its own. - Nothing here executes on its own. The repo describes structure and ships sample code; it isn't a runnable product. - Domain-flavoured content (Australian tax terms, Brisbane weather) is a template to localise, not a default. ## Related [signal-sweep](https://github.com/signal-sweep/signal-sweep): the human-gated presence tooling that grew out of this workspace's thread-sweep module, generalized to config-driven form and co-maintained as a standalone project. Its worked-example config is this repo's own topic set. [agent-workspace-starter](https://github.com/jimy-r/agent-workspace-starter): the runnable template version of this workspace. A minimal scaffold with the session discipline and two safety hooks already running, to start a new workspace from rather than read about one. [dead-mans-switch](https://github.com/jimy-r/dead-mans-switch): a freshness checker for scheduled agent jobs. It watches for the absence of success rather than for errors, the failure mode [pattern 3](PATTERNS.md) describes. [redaction-check-action](https://github.com/jimy-r/redaction-check-action): a reusable GitHub Action version of a redaction gate. Scans a pull request's added lines for the shapes of private content before merge. ## Also here [Discussions](https://github.com/jimy-r/agent-workspace-architecture/discussions) (Q&A, and Show and tell for your own workspace score) · [AGENTS.md](AGENTS.md) (instructions for an agent working in this repo) · [SUPPORT.md](SUPPORT.md) (where to go for what) · [STYLE_GUIDE.md](STYLE_GUIDE.md) · [SECURITY.md](SECURITY.md) (privacy-leak and workflow-vuln reporting) · [CHANGELOG.md](CHANGELOG.md) · [ATTRIBUTION.md](ATTRIBUTION.md) (patterns this borrows from) · [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) The repo was renamed from `claude-workspace-architecture` on 2026-05-28; the old URL 301-redirects, so external links keep working. ## License [MIT](LICENSE). Reuse freely. --- *Last verified against the repo structure on 2026-09-06.* ============================================================================== SOURCE: PATTERNS.md URL: https://github.com/jimy-r/agent-workspace-architecture/blob/main/PATTERNS.md ============================================================================== # Patterns The architectural decisions behind this workspace, stated as patterns: the problem each solves, the shape of the solution, why it beats the obvious alternative, and what it costs. The file conventions are Claude Code's; the patterns travel to any agent runtime. Read this when you want the *why*. [META_ARCHITECTURE.md](META_ARCHITECTURE.md) is the *what* (the structural map), and [ADOPTION.md](ADOPTION.md) is the *how* (where to start). Each pattern ends with a pointer to the sample files that implement it, so every claim here is inspectable. ## 1. Pure roles, composed with project facts **Problem.** Run security review across five projects and you end up with five near-identical 500-line prompts that drift apart the moment one is edited. **Pattern.** Keep the expert persona *pure*. A `security-auditor` role file holds method, constraints, and red flags, with zero entity facts. Project specifics live in a `CONTEXT.md`. A thin binding (`@project-security`) composes the two at invocation. **Why this beats the obvious.** The obvious move is one big prompt per project. Extraction means a fix to the auditor's method reaches every project at once, and a new project gets an expert reviewer by writing one `CONTEXT.md` instead of cloning a prompt that immediately starts to rot. **Cost.** Indirection (two files, not one) plus a validator to catch bindings that reference a role or context that moved. Worth it past about three projects; overkill for one. **Where it lives:** [`samples/roles/`](samples/roles/) (17 roles + the template) and [`samples/example-project/`](samples/example-project/) (a binding composing role + context). ## 2. Classify-then-act, not ask-then-wait **Problem.** An autonomous background agent has two failure modes: it nags for input on everything, or it acts confidently on tasks it doesn't understand. **Pattern.** Classify every incoming task first: `has-default` (an obvious correct action exists), `needs-intent` (genuinely ambiguous), or `out-of-scope`. Build the `has-default` work speculatively in a sandbox, lodge it for review, and act only on approval. Log every rejection as a short decision record the agent greps before classifying anything similar again. **Why this beats the obvious.** A pure question-queue stalls on the user. A pure autonomous loop ships things you didn't want. Classification routes each task to the safe handling, and the rejection log stops the same bad idea coming back next cycle. **Cost.** A sandbox, a review queue, and a rejection history to maintain. The agent does speculative work that sometimes gets discarded. **Where it lives:** [`samples/scripts/heartbeat/classify_task.py`](samples/scripts/heartbeat/classify_task.py) and [`samples/tasks/HEARTBEAT.md`](samples/tasks/HEARTBEAT.md). ## 3. Make silent failure loud (the dead-man's switch) **Problem.** A scheduled task that stops firing fails silently. You find out weeks later, when the thing it was supposed to produce is missing. **Pattern.** Each task emits a success sentinel to its log. A watchdog scans for that sentinel inside a staleness window and raises a finding when it is missing or stale. Self-hosted, no external uptime service required. **Why this beats the obvious.** "I'll notice if it breaks" is the alternative, and it is false. The whole point of a background task is that nobody is watching it. A sentinel converts absence-of-success into a visible, dated finding. **Cost.** Per-task staleness configuration, plus a tolerance flag for tasks that run on demand rather than on a clock. **Where it lives:** [`samples/scripts/security/check_task_freshness.py`](samples/scripts/security/check_task_freshness.py). ## 4. Tier by mechanical impact, not by tone **Problem.** A system that auto-applies its own findings needs a line between "apply automatically" and "ask a human first." Drawing that line from how confident a finding *sounds* is a trap. **Pattern.** Classify each change by mechanical reversibility. A typo fix and a deleted file sit in different tiers regardless of how the finding is worded. Low-impact, trivially reversible changes auto-apply; anything that deletes, publishes, or spends money gets a human gate. **Why this beats the obvious.** Tone-based heuristics ("the finding says critical") are gameable and drift over time. Mechanical impact is a property of the action itself, not the language describing it. **Cost.** An explicit impact table, kept current as new action types appear. **Where it lives:** [`samples/.claude/agents/audit.md`](samples/.claude/agents/audit.md) (the tier-classification table). ## 5. Memory points, it doesn't mirror **Problem.** Agent memory that copies your source documents goes stale the moment a source changes, then quietly contradicts it. **Pattern.** Memory holds an index plus typed notes (`user` / `feedback` / `project` / `reference`) that *point at* the source of truth instead of duplicating it. Every write is one of four operations (add, update, delete, no-op), never a blind append. Check a memory claim against current state before asserting it as fact. **Why this beats the obvious.** Dumping everything into memory feels safe and rots fast. A pointer cannot contradict its source; a copy eventually always does. **Cost.** Discipline at write time, plus a periodic consolidation pass to merge duplicates and prune the index. **Where it lives:** [`samples/.claude/scheduled-tasks/consolidate-memory/SKILL.md`](samples/.claude/scheduled-tasks/consolidate-memory/SKILL.md) and [`samples/scripts/memory_lint.py`](samples/scripts/memory_lint.py). ## 6. Credentials live in one place, never in files **Problem.** A secret written to a file leaks: into git history, into a backup, into an agent's context window, into a screenshot. **Pattern.** A password manager is the single store. Files reference the item *name*, never the value. Running code resolves the secret at runtime and scrubs it afterward. Allow one narrow, audited exception per genuine need (a self-only email sender, say), not a general waiver. **Why this beats the obvious.** A `.env` file and "I'll just paste it for now" are how secrets end up in transcripts forever. One store with runtime resolution keeps the value out of every durable surface. **Cost.** A runtime lookup step, and the discipline to refuse the convenient shortcut. **Where it lives:** [`samples/scripts/backup-restic.ps1`](samples/scripts/backup-restic.ps1) (runtime resolution + scrub) and [`samples/scripts/send_self_email.py`](samples/scripts/send_self_email.py) (the one narrow, audited exception). ## 7. A cheap hook beats a careful agent **Problem.** An agent that has misread the task can overwrite your `.env`, delete a record, or force-push. "Be more careful" does not scale. **Pattern.** A `PreToolUse` hook intercepts file writes and shell commands against a blocklist (sensitive paths, destructive verbs, pushes to protected branches, exec-hijacking env-var prefixes like `GIT_SSH_COMMAND='…' git fetch`) and blocks them before they run. It fails *open*: a bug in the guard must never wedge the session. **Why this beats the obvious.** Trusting the model to never err is a hope, not a control. A ten-line deterministic check catches the large majority of accidental damage for almost nothing. **Cost.** Occasional false positives (a legitimately named file that matches a protected substring), best resolved by naming around them rather than widening the gap. **Where it lives:** [`samples/scripts/security/check_bash_command.py`](samples/scripts/security/check_bash_command.py) and the hook config in [`samples/.claude/settings.example.json`](samples/.claude/settings.example.json). ## 8. Audit the workspace like a fitness function **Problem.** A workspace degrades while the ecosystem around it improves. Context bloats, configs drift, a hook stops firing, memory contradicts reality, better patterns ship every week — and nobody's job is to notice either direction. **Pattern.** A scheduled auditor runs on a cadence with two jobs. The first is finding improvements: research public sources for what the ecosystem has learned, and critique the workspace module-by-module against current best practice. The second is housekeeping: sweep configs, the security envelope, and drift. Findings land in the task list. Synthetic canaries verify the audit still detects known-bad fixtures every run. A finding ledger tracks accept and dismiss rates. There is deliberately **no single numeric score**: a self-improving audit that emits its own grade optimises for the grade (Goodhart's law). **Why this beats the obvious.** "I'll clean it up when it bothers me" loses to a cadence with canaries, because drift is gradual and invisible right up until it isn't; "I'll look for upgrades when I have time" never fires at all. **Cost.** The audit is itself a system to maintain, and it can cry wolf, so findings are tiered and tracked rather than dumped raw into the queue. **Where it lives:** [`samples/.claude/agents/audit.md`](samples/.claude/agents/audit.md) and [`samples/tests/audit_canaries/`](samples/tests/audit_canaries/). ## 9. Context is a budget, not a constant **Problem.** Everything auto-loaded into a session — instruction files, the memory index, skill descriptions, hook strings — costs tokens on every turn, in every session. No single addition is large, so the total grows a few percent a week, and unattended agents spend with nobody watching. Quality erodes before any cost alarm fires. **Pattern.** Meter it like money. A baseline counter measures every always-loaded source individually and keeps history. A trend alarm fires when the baseline beats its rolling median by a set margin, because accretion is the common failure, not the blowout. Unattended runs carry hard spend ceilings sized as belts (10–50x a normal cycle), multi-agent fan-outs are bounded by construction, index files carry explicit size ceilings, and a standing note tells the runtime what must survive context compaction. **Why this beats the obvious.** The obvious control is a size warning on the main instruction file: one source, one absolute threshold. The real failure is distributed (a dozen sources each growing slightly) and relative (this month versus last), so per-source measurement with trend detection catches what a static ceiling misses. Attribution is the payoff: a total says something grew; the breakdown says what to trim. **Cost.** A counter and its history to maintain, estimates that drift from true tokenizer counts, and ceilings that need sizing judgment — a cap set as a governor instead of a belt aborts legitimately heavy runs. **Where it lives:** [`samples/scripts/ghost_token_counter.py`](samples/scripts/ghost_token_counter.py) (the per-source baseline) and [`samples/scripts/token_report.py`](samples/scripts/token_report.py) (spend telemetry feeding the audit's trend rule). ## 10. A skill is editable weights — never adopt a self-edit without a gate **Problem.** Instruction files are the part of the system that most invites quiet self-improvement. A `CLAUDE.md` or a skill doc is plain text the agent can rewrite, and an agent that watches its own transcripts can propose better wording every day. Let that loop close on itself and you have a system editing its own controlling instructions with nobody checking whether each edit actually helped. **Pattern.** Treat skill and instruction text as *optimizable weights*, and put a gate between a proposed edit and the live file. In this workspace the gate is a human: a proposed change is built speculatively in a sandbox, lodged for review, and applied only once approved; the memory-consolidation pass runs four-operation discipline (add, update, delete, no-op) instead of blind appends; the file-protection hook (Pattern 7) keeps even an approved edit from reaching a protected file by an unwatched path. Staging, then review, then adopt. The edit is a proposal until a check clears it. **Why this beats the obvious.** The obvious move is to let the agent fold its own lessons straight back into its instructions, and that is exactly the move with a measured failure mode. Microsoft Research's SkillOpt (arXiv:2605.23904, ~7.9k GitHub stars, MIT, v0.1.0 alpha) frames a skill doc as the trainable weights of a frozen model and optimizes it against a scored benchmark, accepting an edit only when a held-out split *strictly* improves. Their cautionary single-seed run shows why the gate is the load-bearing piece: an *ungated* self-edit loop on a weak model with a degraded signal collapsed from 0.554 to 0.026 (a 52.8-point drop) by learning to answer with the document-title string verbatim, while the gated twin rejected every bad edit and stayed flat. That figure is a single-seed research result, not a constant, and the published gains land only where tasks recur with a checkable correctness signal, going flat on saturated or noisy ones. The transferable lesson survives all those caveats: a self-edit loop without an accept/reject check can optimize itself straight off a cliff. This workspace runs the human-gated cousin of that loop. It does not run a trajectory-scored training gradient or a held-out-validation gate; the discipline it borrows is the refusal to adopt an edit on the strength of the edit alone. **Cost.** The gate is the slow part. A human in the staging loop means instruction improvements land in days, not seconds, and the speculative work behind a rejected edit is thrown away. That latency is the price of never waking up to a controlling file that an unsupervised loop quietly rewrote. **Where it lives:** [`samples/tasks/HEARTBEAT.md`](samples/tasks/HEARTBEAT.md) (classify → sandbox → stage → review → adopt) and [`samples/.claude/scheduled-tasks/consolidate-memory/SKILL.md`](samples/.claude/scheduled-tasks/consolidate-memory/SKILL.md) (four-operation write discipline). SkillOpt itself is credited in [ATTRIBUTION.md](ATTRIBUTION.md). ## 11. A scaffold is a hypothesis — gate it behind a measurable signal **Problem.** The tempting way to make an agent smarter is to keep adding to the layer around it: another skill, another always-loaded directive, another reasoning rule, another self-critique pass. Most of it feels like an upgrade and never gets checked. Two failure modes hide in that habit. The first is the scaffold that does nothing (a second same-model pass over the same prompt, an ungrounded "now critique your answer" step) and can even degrade the result while costing tokens. The second is accretion: every always-loaded line dilutes the signal of every other, so a workspace can get measurably dumber by growing. **Pattern.** Treat the workspace itself as fixed weights and everything around it as the trainable part, then borrow the discipline that makes training honest: a held-out check. A change earns its place only when it adds a *checkable external signal* (a golden expectation, a re-fetched source, a deterministic lint, a test) or *genuine divergence* (a critic working from a different rubric, parallel attempts seeded from genuinely different strategies). More same-model compute is neither, so it doesn't count. Operationally: register every scaffold with a falsifiable hypothesis and a review date; measure with a small golden-set reasoning-regression suite replayed under a variance floor (each case run several times, scored as a pass-rate ± stddev, never a single number); and at the review date, beat baseline on that suite or get cut. Removal is a first-class outcome, not an admission of failure. Trimming the always-loaded surface is itself a way to raise effective intelligence. **Why this beats the obvious.** The obvious move is to add the thing that sounds smart and trust that it helped. That trust is exactly what a self-improving layer can't afford, because the same plausibility that sells a good scaffold sells a useless one. A correctness signal scores the answer, not the story; a variance floor stops a single lucky run from masquerading as a gain. And the remove-bias names the asymmetry an upgrade pass always has: it is wired to add, so it under-weights the cut that would help more. **Cost.** The suite is a system to build and maintain, and a meaningful one needs real cases with deterministically-checkable answers, which take effort to write. The variance floor multiplies every measurement by the re-run count. And the register only works if it is actually consulted at the review date rather than becoming another stale list. The discipline is the load-bearing part, not the file. **Where it lives:** the same fitness-function machinery as [`samples/.claude/agents/audit.md`](samples/.claude/agents/audit.md) (Pattern 8). The reasoning-regression suite runs on the audit's cadence and reports like its other coded checks, and it is the measurement-gated cousin of the self-edit gate in Pattern 10: a correctness signal certifying a *scaffold* the way a human gate certifies a *self-edit*. ## 12. Loop selection: not everything should be a loop **Problem.** Give an agent real capability and the tempting response is to automate everything: put a loop on every recurring task, "remove yourself as the bottleneck." But most real work is judgment-heavy, irreversible, or unverifiable. Loop the judgment work and you remove the value (the judgment was the point). Loop the irreversible work and you ship damage unattended. Autonomy gets treated as a pure good, and the question of *which* tasks earn it never gets asked. **Pattern.** A four-box test. A task earns an **autonomous loop** only when it is *all* of: (1) **recurring**, a cadence or repeated event, not a one-off; (2) **mechanically verifiable**, where a script, exit code, schema, or diff confirms it worked, not human taste; (3) **low-judgment-per-instance**, the same decision every time, not a fork on context only you hold; (4) **headless-executable**, able to run unattended with no interactive credential, no GUI, no human mid-step. An **irreversibility override** caps any outward or destructive act (email sent, comment posted, money moved, history pruned) at *surface* even when all four boxes pass: verifiability gates correctness, it does not gate consequence. Three buckets fall out. **Loop:** all four boxes and reversible/inward, runs autonomously on a trigger, the verifier is the gate. **Surface:** recurring and verifiable but judgment-heavy or irreversible, so a read-only nudge or an approval-gated act, never silent autonomy. **Keep manual:** fails recurring or carries high judgment per instance — you drive, tooling assists. A worked surfacing case: a close-out routine that, the moment a task finishes, runs a read-only scan and shows what drifted across the workspace (stale context docs, fired strategic triggers, aging backups, open questions going cold) for the operator to action while still in context. It adds no new autonomy; it widens the operator's view at the moment state changed. **Why this beats the obvious.** The obvious move is to automate everything and treat the human as the bottleneck to engineer out. But an autonomous loop only pays where the work is verifiable and low-judgment; pointed at judgment work it either strips the value or acts wrongly with nobody watching. This is the same route-by-consequence instinct as classify-then-act (Pattern 2), tier-by-impact (Pattern 4), the skill-as-weights gate (Pattern 10), and the scaffold-as-hypothesis gate (Pattern 11): gate by what an action *costs if wrong*, not by how automatable it feels. For a solo operator whose own judgment is the product, the restraint is the point — the loops exist to protect attention for the judgment-heavy work, not to hand that work to the machine. **Cost.** A test you have to actually run, honestly, against the pull to automate. And a verification step before it: check the target against the *code*, not its description, on three axes. Does it exist on the relevant branch (`git ls-tree`, never a working-tree glob alone)? Does it do what its description claims (read the script: an "additive backup" turned out to be a destructive `--prune`)? Can it run headless? A task can pass every quality box and still fail box 4. Scoring from descriptions instead of code manufactured three wrong "loop this" calls in a single design pass. **Where it lives:** [`samples/scripts/wrap_drift_scan.py`](samples/scripts/wrap_drift_scan.py) (the read-only close-out surfacing scan — the worked *surface* case). ## 13. Challenge half-formed ideas with a different lens — and hold a sample back to prove it helps **Problem.** Every tool that critiques an agent's work assumes a finished artifact: a draft to red-team, a decision to rank, a result to verify. None help during the messy part, where you're exploring a design or framing a problem and nothing is built yet and no test exists to check against. The tempting fix is to bolt a second agent onto the thinking itself and let it challenge every idea as it forms, always on, in the background. Two traps spring at once. The challenge is usually the same model re-reading its own frame, which rationalizes more than it challenges and can degrade the answer while burning tokens. And once it runs on everything, you have lost the one thing that would prove it helps: a comparison against not running it. **Pattern.** Make it a single *divergent* lens rather than a debate, and hold a sample back so you can still measure it. When a real fork appears in open-ended thinking, fire one challenge from a deliberately different frame: the strongest objection to this direction, the assumption being baked in, the option not being considered. Ground it in a stated criterion, a retrieved fact, or a checkpoint with the human, who stays the arbiter. It widens the option set; it never argues toward a winner. Then the measurement move: roll on each eligible fork and deliberately skip the challenge on a fraction (say one in three), logging both the fired and held-out forks. The human tags each fired challenge as changed-the-call, real-but-didn't, or noise. At review the noise rate and the fired-versus-held-out comparison say whether it earns its place; a noise-dominant result cuts it. **Why this beats the obvious.** The obvious move is two agents bouncing an idea back and forth until something better falls out, and the evidence is hostile to exactly that shape: same-model same-prompt debate loses to plain majority voting at equal compute, ungrounded self-critique with no external signal typically fails to help and often degrades, and *assigned* devil's advocacy is reliably weaker than dissent that carries real information, a finding that predates LLMs by decades in the group-decision literature. What survives is the narrow form: a different lens not a louder echo, aimed at divergence not convergence, grounded not assertion-trading. The measurement is not optional either. An always-on aid is unmeasurable by construction: if it fires on everything, every decision got it, and you never see the one without it. Holding out a sample keeps the counterfactual alive, the same instinct as a control group applied to a behavioral aid in production. **Cost.** A behavioral trigger that depends on the agent actually firing it, since no hook can detect "this is a real fork," so adherence has to be watched (the log's own fire count doubles as that watch). The held-out sample means deliberately skipping the aid on work it might have improved, to buy the ability to measure at all. And a tagging step for the human on each firing, the only honest source of the verdict, because a model grading its own challenges inherits a measured self-preference bias. **Where it lives:** [`samples/scripts/ideation_spar_log.py`](samples/scripts/ideation_spar_log.py), the roll / hold-out / log / report engine that keeps an always-on-feeling aid measurable. It is the production-side cousin of the offline golden set in Pattern 11: there a held-out *case set* certifies a scaffold; here a held-out *slice of live firings* certifies a behavioral aid that has no offline test. ## 14. Delegation is a queue you fill, not work the agent finds **Problem.** A background project-manager agent that finds its own work has to guess what you meant by it. This workspace ran one on a two-hourly cycle: it read the task list and classified each item, then either built the safe-by-default work or posted a clarifying question. The questions were the flaw. They went to a file nobody opened, thirteen of them piled up unanswered, and every task sitting behind one stopped moving. The second failure was operational. The scheduled runtime rode an ambient credential that expired without raising anything, so the cycles failed dark for about five weeks, and the dead-man's-switch alarms of Pattern 3 fired into channels whose only readers were the dead systems themselves. **Pattern.** Invert the direction of authorization. Work reaches the agent only when the operator marks a card on the task board as delegated, and marking it runs a short intake interview while the intent is still in the operator's head. What does done look like, which folders may be written, what constrains the approach, and how should the one or two foreseeable decision points be ruled if the work hits them. A drain skill actions the queue on demand inside an ordinary interactive session, validating a template floor per card first: a literal next action, a link to a folder that exists, a checkable done-when, an effort size. When the work reaches a fork the intake didn't pre-rule, the question goes onto the card itself. No cron sits behind any of it. **Why this beats the obvious.** The obvious design is the one that failed. Let the agent discover work, classify it, and ask when it isn't sure. Discovery makes authorization ambiguous, so the agent's first job becomes inferring intent from a line written for a human reader, and that inference is where the thirteen questions came from. A queued card is the mandate, stated by the person who holds the intent at the moment they hold it. The same move fixes the question channel. A fork written on the card gets read because the board is where work gets picked up; a dedicated questions file is a channel with no reader. Running on demand in a live session then removes the whole class of unattended-runtime failure. The credential is the session's own, the operator is present, and a broken drain is visible in the moment instead of five weeks later. This is Pattern 12's four-box test applied to the agent's own coordinator, and the honest answer is *surface* rather than *loop* — the work recurs, but the judgment per instance is high. **Cost.** Nothing happens while you're away. The queue is inert until someone runs the drain, so a queue nobody drains goes stale as quietly as a dead scheduler; the mitigation is the session-start briefing, which reports the queue count and offers to drain it. Delegation also costs a minute of attention per card at the moment you would rather move on, and that minute is where the quality comes from, so a card queued in a hurry buys plausible-wrong work later. Early evidence is thin but positive. The first drain closed two of two cards with no rework. **Where it lives:** [`samples/board/agent-queue.SKILL.example.md`](samples/board/agent-queue.SKILL.example.md) (the intake interview and the drain protocol it feeds), with the card schema and the succession reasoning in [`samples/board/README.md`](samples/board/README.md). Pattern 2 is the predecessor it replaced; the classification logic there holds wherever the mandate is already unambiguous, and discovery is the half that failed. ## 15. Price the lane before you migrate it **Problem.** Token consumption climbs even after efficiency work, because every saving gets reinvested in more agent work — Jevons, applied to your own subscription. At the ceiling, the instinctive fix is structural: migrate the execution tier to cheaper open-weight models, buy a GPU, stand up a second inference stack. Plans of that shape are expensive to be wrong about, and the headline per-token price lists that motivate them hide everything that decides the outcome. **Pattern.** Instrument before migrating. Walk the transcripts and split consumption three ways: by lane (orchestrator versus dispatched subagents), by token class (fresh input, cache writes, cache reads, output), and by model. Then pull the configuration levers the measurement exposes, cheapest first, one variable at a time, each as a registered trial with a kill criterion and an instrument the weekly audit reads. In this workspace that meant three moves before any migration: always-on orchestration became opt-in (the evidence for selective triggering over always-on is strong), maximum reasoning effort was scoped to judgment lanes only (output was ~11% of cost, so effort is a quality dial, not a cost lever), and execution dropped one rung within the provider's own ladder as a measured trial. The migration project stays gated on the re-measure: it opens only if spend still exhausts after the cheap levers land. **Why this beats the obvious.** The obvious move nearly shipped here. Measurement killed it in an afternoon, three ways. The workload's cost was 88% input-side and ran at a 95% cache-hit rate, with cache reads priced at a tenth of input — a subsidy the migration would silently forfeit, since open-weight endpoints cache weakly or not at all. The local GPU could not serve a single day's execution volume. And the per-subagent provider routing the design assumed did not exist in the harness, so the split would have required an unaudited third-party proxy in front of a live session credential. None of those facts is visible on a price list; all of them came out of a two-second transcript walk. The measured configuration changes captured most of the saving with zero integration risk, and the trial that remained changes one variable behind the existing review gate, with rework marked in the session record so the kill metric cannot be quietly rationalised away. **Cost.** The instrument itself, and the honesty it demands: a rework marker convention that only works if every escalation is recorded, a baseline that must be re-verified rather than remembered, and one-variable-at-a-time patience, which means the full ladder takes weeks. The meter that proves the upgrade worked is the same meter that will prove it didn't. **Where it lives:** [`samples/scripts/tier_metrics.py`](samples/scripts/tier_metrics.py) (the lane-split instrument with its four advisory checks and selftest), consumed by the audit's checks-as-code the same way as the Pattern 11 machinery. Pattern 9 is the parent instinct (context as a budget) applied here to the model ladder; Pattern 11 supplies the trial discipline: falsifiable hypothesis, review date, kill criterion. ## 16. A claim carries its provenance, or it is a guess **Problem.** An agent writes a paragraph in which "the config sets `X`", "the docs recommend `Y`" and "this is probably `Z`" are typographically identical. A verified fact and a plausible invention render the same. The reader either checks everything, which defeats the point of delegating, or checks nothing, which is how a fabrication becomes a decision. The failure is not that the agent guessed. It is that the guess arrived wearing the same clothes as the fact. **Pattern.** Three mechanisms, applied to load-bearing claims only. *Cite the location, not the recollection.* For any claim about system state (a file's contents, a path, a config flag, a status, a line number), read the source and cite `path:line` before asserting. "Not found in ``" is a complete and useful answer. A guess dressed as a finding is not. *Grade on two axes, visibly.* Source reliability (A authoritative to E anecdotal) and claim credibility (1 confirmed by multiple sources to 5 unverified), tagged inline as `A1` or `B3`. Then label each load-bearing claim `[observed]` (quotable from a source), `[inferred]` (reasoned from evidence) or `[unverified]`. Two axes, because a reliable source can still make a weak claim. *Record what would falsify it.* A durable brief carries the date it was last verified and an explicit list of the checks that would confirm or break its conclusions. This is what lets a stale document announce its own staleness instead of quietly lying to the next reader. Scope matters. Tag load-bearing workspace-state claims, not general reasoning or well-known facts. An `[unverified]` on everything carries the same information as an `[unverified]` on nothing. **Why this beats the obvious.** The obvious instruction is "be accurate," which is unenforceable, unmeasurable, and already what the agent was trying to do. Grading is mechanical. It survives handover, it degrades gracefully (a wrong grade is still a visible grade), and it makes uncertainty legible to the *next* reader, including the next agent, which cannot ask what you meant and will otherwise inherit a guess as a premise. That inheritance is the real cost: an unmarked guess does not stay one claim wrong, it becomes the foundation of the next three. The pattern also fails usefully. When a claim turns out wrong, the grade shows whether the process failed or the source did, which is the difference between fixing a habit and distrusting a document. **Cost.** Friction at write time, on every claim, forever. A reader who learns the grades and then finds them applied carelessly trusts them less than no grades at all, so the discipline is all-or-nothing per document. And it does not catch the confident wrong answer drawn from a real source that says something else. For that, see Pattern 8, which measures rather than labels. **Where it lives:** [`samples/roles/researcher.md`](samples/roles/researcher.md) carries the full two-axis grading scheme and the claim-evidence-inference separation it sits inside. [`samples/CLAUDE.md.example`](samples/CLAUDE.md.example) carries the `path:line` rule that extends it from research tasks to ordinary answers. **Boundary with its neighbours.** Pattern 5 governs what memory *stores*; this governs what an assertion *carries at the moment it is made*. Pattern 8 measures whether the workspace is drifting; this makes a single claim auditable without measuring anything. The three are separable: a workspace can store pointers faithfully, audit itself weekly, and still hand you a confident sentence with nothing behind it. ## 17. One canonical copy, and pointers from everywhere else **Problem.** The same rule ends up written in three places: the user-global instructions, the workspace instructions, and a project file. All three were correct on the day they were written. Then one gets edited. Now an agent loading all three reads two versions of the rule and silently picks one, and a reader checking the project file gets an answer that the workspace file contradicts. Nobody notices, because each copy looks authoritative on its own. Duplication does not announce itself as duplication; it announces itself as a wrong answer, months later, with no obvious cause. **Pattern.** Each fact has exactly one canonical location. Everywhere else points at it and states that it is a pointer. Three mechanics make that hold. *Delete the copy, keep the pointer.* When the same rule appears twice, the second instance becomes a one-line reference naming the canonical file and section, not a summary of it, because a summary is a copy that drifts more slowly. *Banner what is superseded.* A document that has been replaced says so at the top, names its replacement, and stays on disk, so the reader learns which copy governs from the document itself rather than from folder archaeology. *Measure the duplication mechanically.* Always-loaded files accumulate shared boilerplate that no single edit introduced, so something has to scan across them and report the overlap. **Why this beats the obvious.** The obvious fix is to keep the copies synchronised, which is a promise to do unbounded manual work forever, made by whoever is least likely to remember. It also fails silently: nothing breaks when a copy drifts, so nothing prompts the fix. The pointer approach has a property the sync approach lacks. A pointer cannot disagree with its target. It can be *stale* (pointing at something moved or renamed), but stale-and-broken is loud, whereas stale-and-plausible is not. Trading a silent failure for a noisy one is most of the value. This workspace's own instructions carry a worked instance, left in place deliberately: a communication-standards section that had been duplicated into the workspace file was cut back to a pointer after the inline copy was found to have drifted from its source. Two bullets had diverged and one had gone missing entirely. Neither file looked wrong. **Cost.** Indirection. A reader following a pointer needs a second lookup, which is a real tax on comprehension, and pointer chains longer than one hop become their own problem. Canonical placement also has to be decided rather than discovered, and the wrong choice is expensive to reverse once other files reference it. Duplication is genuinely cheaper right up until the first edit. **Where it lives:** [`samples/scripts/claudemd_audit.py`](samples/scripts/claudemd_audit.py) inventories every always-loaded instruction file and flags size, staleness, broken imports, and boilerplate duplicated across files. Pattern 5 is the same instinct applied to memory; this is it applied to instructions, which are loaded on every session and so cost on every session. ## 18. Position is price — a token costs more the earlier you add it **Problem.** A team optimising context cost measures how *large* the context is and stops there. But the model API is stateless: the entire running transcript is re-sent on every inference step, and an agentic turn is not one message but a chain of them — every tool call, every file read, every edit is a step that carries the whole history back. A long session re-reads its context not once but hundreds of times, so the real cost of a token is its size times the number of steps left after it arrives. A large read at the start is paid on nearly every step that follows; the same read near the end is paid a handful of times. Prompt caching discounts the repeated prefix, often by an order of magnitude, but a tenth of a cost paid hundreds of times is still the largest line on the bill, and the discount does not stop the re-read from happening. Size is the factor everyone watches. Position and step-count are the factors that multiply it. **Pattern.** Treat context as rent paid per step, and manage both multipliers instead of only the size. *Read somewhere else:* bulk or exploratory reading goes to a subagent whose context is a separate transcript, returning a short summary so the detail never enters the caller — and give that subagent a return budget, writing detail to a file and returning a summary plus the path, because a long report pasted back is re-read for the rest of the session. *Read narrower, and later:* prefer a ranged read to a whole-file read, and defer a necessary large read to the step that needs it, since the same bytes cost a fraction when fewer steps remain to re-pay them. *Cut the steps:* batch independent tool calls into one step — five sequential reads are five full context re-reads, the same five issued together are one — and sequence only when a later call truly depends on an earlier result. *Mind the cache clock:* a cache re-write bills the whole context at a premium, and the events that force one are resuming after an idle gap longer than the cache lifetime, switching model mid-session, and compaction. Editing a standing instruction or memory file is not one of them, because the loaded copy is captured at session start and never re-sent (measured 2026-09-02 on a 199-step session: three such edits at 168k–397k context were followed by fully cached steps, while nine post-gap resumes and one model switch made up about 82% of the session's cache-write tokens). So wrap and clear before any break that will outlive the cache, and change models only in a fresh session. *Keep the cache warm across a break:* on a model whose cache reads are priced far below its writes, a no-op turn shortly before the entry would expire refreshes it for a small fraction of a re-write (about 1/80th where reads are 0.025x and one-hour writes 2x), so a session that will resume the same task within a couple of days is cheaper kept warm than re-written. Arm it at session start with a self-paced wakeup that stops itself at the crossover, so an abandoned session cannot ping forever, and let wrap-and-clear stay the answer for a pivot. *Compact on purpose:* once carry cost is measurable, compaction is a cost tool and not only hygiene, shortening the prefix that every remaining step re-reads — paired with a pre-compaction dump so the summary loss is safe. **Why this beats the obvious.** The obvious move is "use less context," which collapses to reading less, and reading less makes the agent dumber. These levers spend the same information for less by changing *where* and *when* it is paid, not *whether* it is available: the subagent still reads the files, the ranged read still finds the fact, the deferred read still happens. Nothing is surrendered except the position that made it expensive. The insight is only actionable once measured, though — "read earlier costs more" is a hunch until an instrument reads per-step usage out of the transcript and shows a single early bulk read dominating a whole session's re-read volume while the same read late would have cost a sliver. Build the meter before trusting the intuition; the meter is what tells you which read to move. **Cost.** Every lever trades something. A subagent summary loses detail the caller might later want, which is why the return budget pairs with write-to-file rather than replacing it. Batching independent calls removes the chance to reason between them. Aggressive compaction can drop load-bearing state, so it needs the state-dump guard. And the discipline presupposes an instrument that parses per-turn usage — one more read-only tool to maintain. The failure mode to refuse is letting the cost lens override the quality one: the cheapest session is the one that read nothing and answered badly, and no token saved is worth an adversarial review skipped. **Where it lives:** [`samples/CLAUDE.md.example`](samples/CLAUDE.md.example) carries the read-in-subagent, ranged-read, batch-calls, mind-the-cache-clock, and compact-on-purpose rules as a standing directive; [`samples/scripts/cache_write_scan.py`](samples/scripts/cache_write_scan.py) attributes a transcript's cache writes to idle gaps, model switches, compaction or standing-file edits so the claim can be re-tested on any session; [`samples/.claude/skills/orient/SKILL.md`](samples/.claude/skills/orient/SKILL.md) step 4 arms the keep-alive at session start. Pattern 9 frames context as a budget; this prices that budget by position and step-count. The close-out ritual is this pattern's worst case, a twenty-step procedure run at the session's peak; [`samples/.claude/skills/wrap/SKILL.md`](samples/.claude/skills/wrap/SKILL.md) splits it so the main thread composes a brief and reviews the result while an execution-tier subagent does the reads and edits at a small context (measured before the split: 9–22% of a session's cache reads). **Boundary with its neighbours.** Pattern 9 says context is finite and must be spent deliberately; this says the *same* token costs differently by when in the session it is spent, a claim about price rather than quantity. Reading in a subagent to keep the caller clean is one lever here, not the whole pattern. And Pattern 8's audit instinct supplies the meter: position cost you cannot see is position cost you cannot manage. ## How they compose These are not independent. The credential law and the file-protection hook are the same instinct (keep damage out of durable surfaces) applied at two layers. The roles library and memory hygiene are the same instinct (one source of truth, referenced rather than copied) applied in two domains. Classify-then-act, tier-by-impact, the skill-as-weights gate, and loop-selection are the same instinct (route by consequence, not by confidence) applied to incoming tasks, to audit findings, to the agent's edits of its own instructions, and to the choice of what gets automated at all — the edit-your-own-instructions case is the riskiest, because the thing being changed is the controlling text itself, and loop-selection is the instinct turned upstream: it asks which work should reach an autonomous loop before any of the other gates get a say. And the context budget is the audit's instinct (notice drift before it bites) pointed at the one resource every other pattern spends. The scaffold-as-hypothesis gate is that same audit instinct again, pointed inward at the workspace's own additions: the self-edit gate (Pattern 10) certifies a change to the instructions, and the reasoning-regression suite certifies a change to the capability layer — both refuse to adopt on the strength of how good the change sounds. Pattern 13 turns the same gate on a critic for unfinished thinking, and adds the twist the offline checks miss: an always-on aid erases its own control group, so it holds out a slice of live firings the way Pattern 11 holds out a case set. Pattern 15 is Pattern 9's budget instinct meeting Pattern 11's trial discipline at the model ladder: measure the lane, register the hypothesis, and let the same instrument that motivated the change adjudicate it. Pattern 14 is that same route-by-consequence family turned on the coordinator itself, and it is the one place where the answer came back negative. The classify-then-act loop of Pattern 2 ran here for months and was retired, because discovery left authorization ambiguous and the unattended runtime failed silently. What replaced it moves authorization to the human, and the question channel onto the board the human already reads. Pattern 16 is the audit instinct compressed to a single sentence. Where Pattern 8 measures the workspace periodically and Pattern 11 gates an addition on evidence, Pattern 16 asks the same question of every load-bearing claim at the moment it is written: what is this standing on, and would the reader be able to tell if the answer were nothing. It pairs with Pattern 5 the way a citation pairs with a library: 5 keeps the source of truth singular and pointed-at, while 16 makes each individual assertion say which source it came from and how far it is from one. The two failure modes it addresses are the same failure at different distances: memory that quietly contradicts its source, and a sentence that quietly contradicts the file it claims to describe. Pattern 17 completes a family that runs through 5, 9 and 16. Pattern 5 keeps memory pointing rather than mirroring; 17 does the same for the instruction surface, where the cost is paid on every session rather than every recall. Pattern 9 measures what that surface costs; 17 removes the part of the cost that buys nothing, because a duplicated rule spends context twice and can contradict itself. And 16 is the same discipline at sentence scale: cite where a claim comes from rather than restating it from memory. The through-line is one idea at four sizes: a fact should exist once, be referenced from everywhere it applies, and carry a visible route back to its source. Pattern 18 prices what Pattern 9 budgets. Where 9 says context is finite and spent deliberately, 18 says the same token costs differently by *when* it is spent, because a stateless transcript is re-read on every step, so its levers move a read's position rather than deleting it. It closes a loop with 1 and 8: reading in a subagent — the isolation the roles library uses to keep a caller's context clean — is one of its levers, and the audit instinct of Pattern 8 supplies the meter without which position cost stays invisible. Adopt them when you feel the friction each one removes. Not before. --- *Last verified against the repo structure on 2026-08-27.* ============================================================================== SOURCE: META_ARCHITECTURE.md URL: https://github.com/jimy-r/agent-workspace-architecture/blob/main/META_ARCHITECTURE.md ============================================================================== # Agent Workspace — Meta Architecture *(redacted)* > **Scope:** how a personal **agent workspace** is wired *for the agent*. Personas, routines, hooks, memory, and the coordination layer between them. The worked example runs on Claude Code (so file conventions like `CLAUDE.md` and `.claude/skills/` are Claude-Code-specific), but the architectural patterns port to any agent substrate. **Not** the architecture of any individual project inside it — project application architecture lives alongside each project. > > **Audience:** anyone curious about how a practical agent workspace is structured end-to-end, regardless of which agent runtime they use. > > **Last updated:** 2026-09-09 — **The content machinery became a learning loop.** Three months of outward work (60 answers posted in other people's threads, 54 replies drawn, five teardowns, six index submissions, a newsletter and a free-tools funnel) changed a standing file three times. The unmeasured lane, reading other people's systems closely, changed dozens. So the layer's only metric is now whether an interaction changed a standing file, kept in an append-only learning ledger that warns when a run of decisions produces no lift. Reviews of external systems become the rhythm, teardowns publish only when something was lifted, and the publishing cadences and the scheduled traffic capture are retired. See [CHANGELOG.md](CHANGELOG.md). *Earlier:* 2026-09-08 — **The cache clock gained a keep-alive.** Idle-gap re-writes measured 44–82% of cache writes across two scanned sessions, and on a model with 0.025x cache reads a refresh costs about 1/80th of a 2x re-write, so the orient skill now arms a self-paced 50-minute wakeup (single-dot reply, self-stop at the ~60-hour crossover). See [CHANGELOG.md](CHANGELOG.md). *Earlier:* 2026-09-06 — **Twentieth audit drained: every finding decided or deferred, the router ledger lists unlabeled dispatches, three audit checks carry known-positive fixtures, all eighteen roles carry Red Flags.** See [CHANGELOG.md](CHANGELOG.md). *Earlier:* 2026-09-02 — **The close-out ritual's own cost got measured and cut.** Closing out a task had grown to about twenty steps run at the session's peak context — measured from the moment close-out starts, that cost 22% of one session's cache reads and 9% of a longer session's, both at peak context. The fix: close-out split into a main-thread brief plus an execution-tier subagent that applies it, so the main thread now makes about three inferences instead of twenty. See [CHANGELOG.md](CHANGELOG.md). *Earlier:* 2026-08-30 — **Two modules were added (#15–#16), and the workspace became agent-callable, not just agent-readable.** *Site & agent surface*: the practice site grew four free client-side tools and an MCP server whose five tools include `request_capability` — an explicit channel for agents to say what they needed and didn’t find, with unknown-tool calls logged for the same reason; discovery via an agent card, `patterns.json`, and `llms.txt`. *Content machinery (operating layer)*: the workspace-side wiring around the public signal-sweep engine — per-item-approved outward skills, posted ledgers, promotion metrics, one canonical Q&A home — registered so the weekly audit critiques it like every other module. The map’s payoff counts were corrected in the same pass (sixteen modules, eighteen roles). See [CHANGELOG.md](CHANGELOG.md). *Earlier:* 2026-08-26 — **The evaluation method was published ([EVALUATION.md](EVALUATION.md)) and two patterns were added, closing the last gaps in the problem set the architecture claims to address.** Pattern 16 (a claim carries its provenance, or it is a guess) and Pattern 17 (one canonical copy, and pointers from everywhere else). The evaluation doc writes up the golden-set method that had been a single paragraph in §2: deterministic checks rather than an LLM judge because the instrument detects *regression* and a judge adds its own variance; a pass-rate with variance rather than a scalar; and a five-class fixture discipline whose lucky-correct-negative and outside-scope classes are what actually validate a check. Harness, authoring guide and three representative cases ship as samples; the corpus does not, because cases encode workspace-specific failures. *Earlier:* 2026-08-15 — **The Token Budget module became a measured control loop (Pattern 15).** A lane-split measurement of the workspace's own transcripts redirected a planned open-weights migration into three configuration changes (opt-in orchestration, effort scoped to judgment lanes, a registered lower-tier execution trial with a kill criterion), and the module gained the instrument the weekly audit now reads: `tier_metrics.py`, shipped as a sample. *Earlier:* **2026-08-12 — An unused voice web-UI channel was retired — subtraction as the security fix.** The browser-based voice/text web UI (a local MCP server serving a LAN page) had been given a shared-secret auth gate after an audit flagged its unauthenticated all-interfaces binding — but the token was opt-in and never configured, so every real launch still ran open. A task-board review then found the channel itself unused (an OS-level dictation tool had taken over voice input), so the fix became deletion: server, launcher, and registry rows removed. An opt-in control that ships unconfigured protects nothing; retiring an unused surface beats hardening it. See [CHANGELOG.md](CHANGELOG.md). _Earlier:_ **2026-08-09 — An external eval library was reviewed the lift-don't-install way, and a judge layer came out of it.** An adversarially-verified review of braintrustdata's [autoevals](https://github.com/braintrustdata/autoevals) (LLM-as-judge evaluator library, MIT) and [agentbehavior](https://github.com/braintrustdata/agentbehavior) (behavior-spec standard, Apache-2.0) reached the same verdict as every prior third-party candidate here: lift the patterns, install nothing. The decisive facts were read at source, not assumed — the unconfigured library's default base URL routes judge prompts *and the caller's API key* to the vendor's hosted gateway, and the companion Claude Code plugin writes raw keys into settings files and ships tool payloads off-host unredacted. What was lifted and rebuilt native: a reusable **choice-scored judge primitive** (judge specs as data files; verdicts code-enforced against an explicit choice map, one structured retry then abstain-never-zero; chain-of-thought forced *before* the verdict; per-criterion boolean decomposition instead of one averaged rating; every judged variable auto-wrapped as untrusted content, closing an injection gap the upstream library itself carries); a **judge-diversity upgrade to the best-of-N workflow** (a different, higher-tier judge model over the attempt tier, anchored labels mapped to numbers in code, dual forward/reversed-order passes with an order-sensitivity flag); **behavior-spec authoring adopted as a skill** (four markdown files, three adaptations, Apache-2.0 attribution kept); and a **five-class eval-fixture discipline** — above all the *lucky-correct negative* (right answer, wrong process) and the *outside-scope* case that proves a check doesn't fire spuriously. Repair rider with a portable moral: an all-zero record in the reasoning-trend store turned out to be a real run whose every call had failed on a dead credential, hand-flagged afterwards by overloading an unrelated field; the harness now counts call outcomes and self-marks invalid runs, and the trend check filters on the dedicated field. An outage must be distinguishable from a regression by the record itself. See [CHANGELOG.md](CHANGELOG.md). _Earlier:_ **2026-08-08 — The 2-hourly project-manager agent was retired, and delegation became an explicit queue on a task board.** Two failure classes killed the old cycle. It inferred intent from task lines written for a human reader and posted clarifying questions into a file nobody opened, where thirteen accumulated unanswered. And its unattended runtime rode an ambient credential that expired silently, so roughly five weeks of runs failed dark while the dead-man's-switch alarms landed in channels whose only readers were the dead systems themselves. The successor is the **Task board** module (#14), one canonical markdown card store rendered to a locally served view, where `delegate: queued` on a card is the entire authorization surface and only the operator sets it, through a short intake interview that confirms what done looks like and fixes write-scope, constraints, and pre-rulings for the forks the work will probably hit. A drain skill actions the queue on demand inside an interactive session, and a fork the intake did not pre-rule goes back onto the card rather than into a separate tracker. There is deliberately no cron. The session-start briefing surfaces the queue count, and metrics ride the close-out ritual rather than a schedule, so a stale tail means no wrap has run. See [CHANGELOG.md](CHANGELOG.md). _Earlier entries, 2026-07-26 back to the first public release, are in [docs/history.md](docs/history.md); the per-release change history is in [CHANGELOG.md](CHANGELOG.md)._ ## Contents 1. [Layers at a glance](#1-layers-at-a-glance) 2. [Modules — cohesive clusters by purpose](#2-modules--cohesive-clusters-by-purpose) 3. [Personas — the Roles Library](#3-personas--the-roles-library) 4. [Routines — recurring agents and one-shot launchers](#4-routines--recurring-agents-and-one-shot-launchers) 5. [Hooks — automatic behaviours on events](#5-hooks--automatic-behaviours-on-events) 6. [Skills — invokable capabilities](#6-skills--invokable-capabilities) 7. [Subagents — specialised workers](#7-subagents--specialised-workers) 8. [MCP servers — external capability bridges](#8-mcp-servers--external-capability-bridges) 9. [Memory system — persistent context across sessions](#9-memory-system--persistent-context-across-sessions) 10. [Task coordination layer](#10-task-coordination-layer) 11. [File protection / safety](#11-file-protection--safety) 12. [Project layout](#12-project-layout) 13. [Where things live (quick reference)](#13-where-things-live-quick-reference) 14. [Source attribution — patterns this workspace draws on](#14-source-attribution--patterns-this-workspace-draws-on) 15. [Maintenance](#15-maintenance) 16. [Planned future upgrades](#16-planned-future-upgrades) Companion docs: [ADOPTION.md](ADOPTION.md) — 5-step walkthrough for setting up a similar workspace · [samples/](samples/) — scaffold files illustrating each layer. ## Conventions - **``** / **``** / **``** are placeholders; substitute your own paths. - Type markers in tables: - **[stock]** — ships with Claude Code out of the box - **[plugin]** — installed via a plugin - **[local]** — local external install (npm global, uvx, standalone binary) - **[custom]** — written for this workspace --- ## 1. Layers at a glance ```mermaid flowchart TB I["Input methods
Keyboard · Whisper dictation — desktop (hotkey → text)
· Whisper dictation — mobile (tap text box → dictation)"] E["User entry points
Terminal · Remote Control terminal · Desktop app
· Mobile app · Remote chat · Voice UI"] S["Claude session
CLAUDE.md · Memory · Hooks"] P["Personas
roles/ + project bindings
(subagents composed with CONTEXT.md)"] R["Routines
scheduled tasks + launcher scripts
(morning brief, consolidate-memory, upgrade-audit)"] M["MCP servers
voice · remote chat · scheduled-tasks ·
directory · browser · preview · registry ·
Calendar · Workspace"] I --> E E --> S S --> P S --> R S --> M ``` Input methods layer above entry points: text typed into any surface (terminal, Remote Control terminal, desktop app, mobile app, remote chat, voice UI) can come from a keyboard or from an OS-level Whisper dictation layer. The same dictation tool runs on both desktop (hotkey → text into focused field) and phone (tap any text box → dictation icon), so voice input is available everywhere the user talks to Claude without any workspace-side integration. Six surfaces sit above the Claude session (entry points), three sit below (personas, routines, MCP). Each section from §3 onward details one slice (§2 is the *modular* view of the same surface — see Contents). --- ## 2. Modules — cohesive clusters by purpose A *module* is a cohesive cluster of files (subagents, skills, scripts, state) that shares one upgrade boundary. The §1 layered view is the inventory by type; this section is the same content sliced by purpose. Shared primitives are cross-referenced, not duplicated. The Audit module best-practice-checks **all modules in one full sweep every run** (the earlier 4-group weekly rotation was retired — an on-demand audit should surface the complete opportunity set in a single pass, bounded by subagent concurrency rather than a rotation; see the audit sample's Phase 2.9 under [samples/](samples/)), reading a per-module best-practice map (sources + concrete checks + gaps). Upgrade findings are captured exhaustively into a deduplicated **backlog**: each upgrade carries a stable source-derived key, so a fit-passing upgrade is recorded once and never re-surfaced on later runs, and none is dropped by a fixed finding cap. | Module | Charter | Subagents | Skills | Scripts | State | Owner doc | |---|---|---|---|---|---|---| | **Audit** | Weekly + on-demand upgrade audit. First job: find improvements — public-source research (Phase 2.5b) plus a module best-practice critique (Phase 2.9). The same sweep reviews configs, hooks, security envelope, plugin/MCP bloat, memory drift. Findings → ledger → full report at `/tasks/audit/SETUP_REVIEW.md` + a short digest in the task list (relocated 2026-06-10 — the full block was taxing every reader of the task list). Cheap assertions run as code (`audit_checks/run_all.py`); externally-sourced findings never auto-apply (provenance Gate 0); pending queue drained via `audit-workthrough`. Router ledger lists unlabeled dispatches; three audit checks carry known-positive fixtures; long-running agents carry a per-agent 1h cache TTL; every role carries a Red Flags section. | `audit`, `audit-second-opinion` | `audit-workthrough` | `audit.bat`, `audit-second-opinion.bat`, `audit_ledger.py`, `audit_cost.py`, `audit_checks/run_all.py`, `security/check_task_freshness.py`, `ghost_token_counter.py` (shared with *Token Budget*), `tests/audit_canaries/` | `/scripts/_state/audit_findings.jsonl`, `audit_cost.jsonl`, `ghost_tokens.db`, `tasks/audit/SETUP_REVIEW.md`, `tasks/scheduled-logs/upgrade-audit_*.log` | `/.claude/agents/audit.md` | | **Heartbeat** *(retired)* | **RETIRED 2026-08 in the source workspace**, superseded by the *Task board* module's explicit delegation queue. The row stays as predecessor documentation, because the classify-then-act half still holds wherever the mandate is already unambiguous; discovery is the half that failed. What it was: a cron-driven (2h) project manager, **gated from 2026-06-10**, where a Stage-0 wrapper preflight (`preflight_gate.py`) skipped the LLM entirely when the watched task files were unchanged, the deterministic scans passed, and an agent cycle had run <24h ago, and the act stage ran the work-tier model at max effort. Classify-then-act on the task + questions files; build `has-default` tasks in sandbox; lodge to review queue; log rejections as ADRs. Why it went: clarifying questions landed in a tracker file nobody opened (thirteen unanswered at the end) and the unattended runtime failed dark for about five weeks behind an expired ambient credential. The container-isolated variant was parked before retirement (the agent CLI's subscription auth needs the desktop app's IPC). | `heartbeat` | `review-queue` | `/scripts/heartbeat/*` (preflight_gate, classify_task, create_staging, check_rejections, idle_observations, host_reviewer, anthropic_proxy, observe_cycles) | `/tasks/HEARTBEAT_REVIEWS.md`, `HEARTBEAT_REJECTIONS.md`, `tasks/heartbeat-sandbox/`, `scripts/_state/heartbeat_gate.json`, `tasks/scheduled-logs/heartbeat-monitor_*.log` | `/tasks/HEARTBEAT.md`, project-level `OBSERVATION.md` runbook | | **Brief** | Daily situational-awareness digest: appointments (14d), local weather, AI news, task counts, open questions, overnight activity → markdown → HTML → SMTP self-email. Idempotent. | — | — | `appointments.py`, `ai_news.py`, `brief_render.py`, `send_self_email.py` | `/tasks/morning_brief_YYYY-MM-DD.md`, `scripts/_state/ai_news_seen.db`, `tasks/scheduled-logs/morning-brief_*.log` | `/.claude/scheduled-tasks/morning-brief/SKILL.md` | | **Inbox** | Hands-on processing of email + photo inbox. Classify against rules/registry, group by proposed action, gate per-batch approval before applying labels/archive/trash or writing to a personal finance ledger. Iron rule: no state changes without approval. | — | `email-triage`, `file-receipts` | `email_rules.py`, `receipts_pipeline.py`, `bill_tracker.py` | personal-finance ledger workbooks + photo inbox folder; consumes the email-rules + services-registry (owned by *Reference data*) | skill `SKILL.md` files + a feedback memory codifying the iron rule | | **Roles** | Pure persona library (17 canonical roles) + project bindings under `.claude/agents/`. Composition: thin binding `@`-imports canonical role + project `CONTEXT.md`. Validator runs manually or in the audit's checks phase (the 2-hourly heartbeat that ran it was retired 2026-08-08). | 17 canonical + project bindings | `role-pressure-test` | `/roles/_validate.py` | — | `/roles/README.md`, `/roles/_template.md` | | **Memory** | User-global file-based memory: `MEMORY.md` index + topic files (`user_*`, `feedback_*`, `project_*`, `reference_*`) + `episodes/`. Weekly consolidation, run through a structural no-regression gate before adoption: snapshot the memory tree → let the pass edit → re-check after, and reject the whole pass on a newly broken source reference, an index-ceiling breach, a dropped standing-rule line, or injected directive-shaped text. A consolidation pass rewrites the agent's own standing context, so a correctness signal alone can't certify it safe to keep. Four-op discipline per fact. | — | `consolidate-memory` (scheduled-task) | `/scripts/memory_lint.py` | `/.claude/projects//memory/*` | `/.claude/scheduled-tasks/consolidate-memory/SKILL.md`, user-global `CLAUDE.md § Memory hygiene` | | **Security envelope** | Multi-layer file/command protection: PreToolUse hooks (Edit/Write + Bash), command-safety plugin (interactive CLI only), PreCompact transcript backup, the always-applies `permissions.deny` floor (re-homed from `autoMode.hard_deny` on the 2026-06-16 *Sentinel* cutover; `autoMode` doesn't apply under bypass). Credential discipline (password-manager + `.env` exception). The *Sentinel* module owns the zero-prompt posture + post-hoc monitor built on these hooks. | — | — | `/scripts/security/check_bash_command.py`, `check_file_protection.py`, `precompact_backup.py` | hook execution log, `/tasks/transcript-backups/` | META_ARCH § Hooks + § File protection | | **Backup** | Encrypted incremental backup to S3-compatible object storage via `restic`. Credentials resolved from a password-manager CLI at runtime. Verify via check + file-level restore round-trip. | — | — | `backup-restic.bat`/`.ps1`, `restic-verify.bat`/`.ps1`, `backup-excludes.txt` | off-machine restic repo | META_ARCH § Routines launcher rows | | **Session workflow** | Skills that manage the session experience start-to-finish: orientation at start, terse-mode mid-session, checkpoints to bridge compactions, task readouts, close-out at end. `/tasks/checkpoints/` is the cross-session continuity store. The `wrap` close-out fires a read-only **drift scan** (`wrap_drift_scan.py`) at the final step: when a task finishes it surfaces workspace-wide drift the operator is otherwise blind to (stale CONTEXT.md/PLAN.md, fired strategy triggers, backup staleness, aging open questions) for them to action while still in-context. The scan is the worked example of the **loop-selection framework** (see below): a close-out is recurring and verifiable but judgment-heavy, so it lands in the SURFACE bucket — a nudge, not silent autonomy. | — | `orient`, `wrap`, `tasks`, `context-save`, `context-restore`, `terse-mode` | `wrap_drift_scan.py` | `/tasks/checkpoints/YYYY-MM-DD_HHMM_.md` | each skill's `SKILL.md` | | **Public mirror** | Redacted world-readable snapshot of the workspace published as a sister repo (this repo). Privacy bar maintained by redaction discipline; periodic mirror-sweep PRs. `ATTRIBUTION.md` + `ADOPTION.md` + `SUPPORT.md` + `samples/` are public-only assets. | — | — | (manual mirror-sweep workflow; no script yet) | mirror commit log + public-only docs | this repo's own `CLAUDE.md` (privacy iron law + grep checklist) | | **Reference data** | Durable structured reference inputs consumed by multiple modules. Each input carries its own schema (registry frontmatter, YAML rules, research-brief README format). | — | — | — | `/Reference/services-registry.md` (consumed by *Audit*, *Inbox* bill-tracker, *Brief* renewal scan), `/Reference/email-rules.md` (consumed by *Inbox* email-triage), `/Reference/Research/` (consumed by *Audit* + ad-hoc) | `Reference/Research/README.md` (briefs); inline frontmatter in `services-registry.md` + `email-rules.md` for schemas | | **Token Budget** | **Added 2026-06-10 (absorbs the former *Context Budget* not-yet-module).** Measure, cap, and reduce token spend across interactive, scheduled, and API workloads — driven by the mid-2026 split of headless agent usage into a capped monthly credit pool at API rates. Measurement: a daily `token_report.py log` (run by the brief) + the ghost-token baseline; the audit's Phase 1 reads the trend (a +25%-over-median rule). Policy — a **tiered-execution rule**: plan at the top tier, hand implementation to subagents on a lower tier, review at the top tier. **Refined mid-August 2026 into a measured control loop (Pattern 15):** a lane-split measurement (orchestrator vs subagent transcripts, by token class and model) showed cost was overwhelmingly input-side at a ~95% cache-hit rate, which reshaped the levers — always-on workflow orchestration became opt-in, max effort was scoped to judgment lanes only (output was ~11% of cost, so effort is a quality dial), and execution builders dropped one further rung within the provider ladder as a registered trial with a kill criterion (rework rate over a marker convention, denominated in dispatches). The trial's instrument (`tier_metrics.py`: spend trend vs baseline, lane split, cache-hit floor, adoption share, rework ratio) feeds the audit's checks-as-code, and the audit carries a standing hold/tweak/kill assessment step until the trial's review date. Scheduled/unattended work runs the work tier (wrapper model map); the top tier stays interactive/escalation-only. (A fixed per-task thinking-token cap was dropped once the models moved to adaptive thinking — effort is the depth control now.) Cache hygiene (corrected 2026-09-02 by a transcript scan): the re-writes that matter are resumes after an idle gap longer than the cache lifetime, mid-session model switches and compaction; editing an always-loaded file mid-session does not re-write the prefix, because the loaded copy is captured at session start — so wrap before a break that outlives the cache and change models only in a fresh session. `cache_write_scan.py` attributes a transcript's cache writes to those causes so the rule stays measured. **Keep-alive (2026-09-08):** the idle-gap cause dominated both scanned sessions (44% and 82% of cache-write tokens), and where cache reads are priced at 0.025x a refresh costs about 1/80th of a 2x re-write, so `orient` step 4 arms a self-paced wakeup every 50 minutes (single-dot reply, 60-hour self-stop at the crossover, interactive sessions only). The one-hour TTL itself is set with `promptCacheTtl: "1h"` in the user settings file, which reaches the desktop app; the launcher env var never did. | — | `session-report` (sibling instrument) | `token_report.py`, `tier_metrics.py`, `cache_write_scan.py`, `ghost_token_counter.py` (shared with *Audit*), `heartbeat/preflight_gate.py` (shared with *Heartbeat*) | `/scripts/_state/token_history.jsonl`, `ghost_tokens.db`, `heartbeat_gate.json` | this row + `token_report.py` docstring + a per-module best-practice brief in `Reference/Research/` | | **Sentinel** | **Added 2026-06-16.** Zero-prompt pass-through + silent deny-floor + quiet post-hoc monitor — removes approval fatigue without losing enforcement. `permissions.defaultMode = bypassPermissions` (set in user-global settings only — project settings ignore it) removes every interactive prompt; an always-applies `permissions.deny` floor (catastrophic recursive deletes re-homed from `autoMode` + security-envelope self-modification protection [settings files, MCP config, the hook-scripts dir, the scheduled-tasks dir] + secret-file globs) plus the *Security envelope* PreToolUse hooks block the irreversible / outward / self-modifying minority **silently** — asking is exactly what's being removed. **One deliberate exception (added later):** a single high-churn, low-risk local config file was moved deny→**ask**, so the agent can edit it under a one-click approval prompt — confirming an `ask` rule still fires under `bypassPermissions`, while the enforcement core (the settings file itself, MCP config, the hook-scripts dir, the deny-floor) stays hard-denied. It is the lone `ask` rule; everything else is silent-block or silent-allow. A deterministic PostToolUse action-log (`sentinel_actionlog.py` → `sentinel_log.py`) records every call (shapes/hashes, never secrets); a closed set of hard tripwires (an executed envelope-write, an exec-hijack or force-push that actually ran, raw shell egress, a credential-shaped string in an outbound payload, a read of a known secret-key file) fires a desktop toast. Near-zero false alerts **by construction**: silent-log default, closed toast set, time-windowed shape-dedup, reads-never-toasted (bar the narrow secret-read), fail-open. **MVP shipped; the Layer-2 LLM-judge digest (the semantic "truly unusual" layer) is deferred** until the action-log has real data to tune its high bar against — shipping it untuned would just trade approval fatigue for alert fatigue. Single-line revert: flip the mode back to the prompting default. Honest residual: the reversible majority becomes detect-not-prevent, and an in-process script write/egress bypasses the shell + edit layers (detection-only, backstopped by the encrypted backup). Bypass applies to the local CLI/desktop runtime only (cloud/SDK runtimes ignore it). Builds on *Security envelope* (which owns the `check_*.py` hooks). | — | — | `/scripts/security/sentinel_actionlog.py`, `sentinel_log.py`, extends `check_{bash_command,file_protection,webfetch}.py`, `/tests/security_canaries/floor_canary.py` | `/scripts/_state/sentinel_actionlog.jsonl`, `sentinel_notify_state.json`; the user-global settings file (`defaultMode` + the deny floor) | this row + `sentinel_log.py` docstring | | **Task board** | **Added 2026-08. Successor to the retired *Heartbeat* module.** One canonical markdown card store for everything outstanding, rendered to a locally served view. Markdown is the truth and the page is a view, overwritten on every render. Columns are **categories, never workflow stages** (`blocked:` is a field that renders as a badge, so a waiting card stays in the domain you go looking for it in), and a `repeat:` card leaves its column for a collapsed **recurring lane** excluded from every count, so standing rhythms never inflate the outstanding figure. `owner:` (`me` / `agent` / `external`) is the highest-value field, and a card without a literal next action is not a card. **Explicit delegation queue:** `delegate: queued` is the entire authorization surface for agent work, set only by the operator, and an intake interview at delegation time records `done-when:` (always confirmed, never inferred), `write-scope:`, `constraints:` and pre-rulings for foreseeable forks. A template floor gates actionability; the `agent-queue` drain actions the queue on demand in an interactive session, and a fork the intake did not pre-rule is written back into the card's `blocked:` field instead of a separate questions file. No cron sits behind any of it. Machine queues (audit findings, questions, lesson candidates, reviews) roll up to one live-count card each rather than exploding into cards. **Analytics ride the served view:** two read-only pages computed per load — `/flow` (board motion: vitals, closes per week, cycle time, aging by days since last touch) and `/metrics` (daily spend, a full-history weekly capacity chart with four-week and all-time average lines, per-task cost, a per-model usage profile, throughput beside queue health). A `wrap` step logs the per-task token record that feeds the cost panels, so freshness is event-driven and a stale tail means no wrap has run. | — | `board`, `agent-queue` | the board engine (`parse` / `validate` / `render` / `serve` / `stats` / `selftest` subcommands; a malformed card is a hard error that blocks the render) + its per-task token-log subcommand | `/board/board.md` (canonical cards + the capture scratchpad), `/board/board.html` (generated view, never hand-edited), `/scripts/_state/task_token_log.jsonl` | [`samples/board/README.md`](samples/board/README.md) | | **Site & agent surface** | **Added 2026-08-30.** The practice site as a discovery instrument, human- and agent-facing. Four free browser tools (a two-door maturity/readiness assessment over the pattern set's six dimensions, a redaction pre-flight, a context carry-cost calculator priced with measured numbers), all computing client-side with capture strictly opt-in (an unchecked telemetry box and an explicit email-a-report action; pasted text never leaves the browser). The same worker exposes the practice to agents: an **MCP server** (stateless streamable HTTP, hand-rolled, zero dependencies) with five tools — `lookup_pattern`, `assess_workspace`, `price_context_read`, `check_redaction`, and `request_capability`, the unmet-demand channel ("tell this server what you needed but didn't find"); calls to tool names that don't exist are logged too, so expected-but-absent capabilities surface. Discovery: `/.well-known/agent.json`, `patterns.json`, `llms.txt`, permissive robots. One upgrade boundary: the site worker deploy. Telemetry is treated as a positioning experiment, not a demand register, until call patterns correlate with humans arriving. | — | — | the site worker (routes: capture + MCP), a KV aggregate reader | a consented capture store (opt-in telemetry, report requests, MCP call rows) | the site's consent-design record; live surface at [jamesross.ai](https://jamesross.ai/llms.txt) | | **Content machinery (operating layer)** | **Added 2026-08-30.** The workspace-side operating layer around the public [signal-sweep](https://github.com/signal-sweep/signal-sweep) engine, which documents its own internals — this module points there and owns what the repo does not ship: the outward-posting skills and their **per-item human approval** Iron Laws (no reply, comment, or post ships without an explicit per-item yes; nothing outward is ever scheduled), the posted-answer ledgers and own-repo engagement intake, promotion placement metrics, the newsletter channel conventions, the canonical Q&A home (the flagship's Discussions, watched by the same intake), and an own-property mention watch so shares of the site or tools surface in the digest. One upgrade boundary: the skills + workspace scripts/state (the engine repo upgrades on its own cadence). **Re-pointed 2026-09-08: this layer is a learning loop.** Its one metric is whether an interaction changed a standing file, recorded in an append-only learning ledger: a row is added when a reply is drained, a human later rules it a lift, a reject or a defer, and the report warns when ten decided items in a row produced no lift. Teardowns publish only when their "what changed here" section names a lift. The thread sweep looks for threads that will push back on an answer rather than threads to answer. The publishing cadences and the scheduled traffic capture are retired, and the demand registers are parked. Reach, subscribers and reply volume are no longer outputs of this layer. | — | the sweep / response-check / teardown skills | promotion metrics, engagement intake, a capture-store reader | posted ledgers, intake queue, demand log | [signal-sweep](https://github.com/signal-sweep/signal-sweep) for the engine; this row for the operating layer | **Module tags** (cross-cutting themes for grouping + navigation; a module can carry several, owner in **bold** — these mirror the repo's GitHub topics so the internal map and the discovery surface share one vocabulary): - **`token-optimization`** / `context-engineering` — **Token Budget** (owner) · *Audit* (ghost-token baseline + the over-median spend-trend rule) · *Task board* (the per-task token log behind the metrics page) · *Session workflow* (terse-mode, checkpoints that cut re-load cost) · *Heartbeat* (retired; its Stage-0 preflight gate was the flagship spend-avoidance control while the cron ran). The most cross-cutting tag in the workspace. - `safety` — **Security envelope** (owner) · *Task board* (delegation is operator-set; the template floor and the write-scope field bound what a drain may touch) · *Audit* (security sweep) · *Heartbeat* (retired; sandboxed builds + review gate). - `agent-memory` — **Memory** (owner) · *Audit* (retrospective + semantic-drift check). - `autonomy` — **Task board** (owner since 2026-08 — an explicit queue drained on demand, no cron) · *Brief* · *Inbox* (scheduled / approval-gated). The previous owner, *Heartbeat*, was retired; the tag now describes bounded delegation rather than a background cycle. - `knowledge` — **Reference data** (owner) · *Roles* · *Memory*. **Shared primitives** (belong to multiple modules — cross-referenced, not duplicated): - `ghost_token_counter.py` — *Audit* (Phase 1 baseline) + *Token Budget*. - `/scripts/heartbeat/preflight_gate.py` — the retired *Heartbeat* module owned it, and *Token Budget* claimed it as its flagship spend-avoidance control for as long as the cron ran. Dormant since the retirement; kept as the worked example of gating a scheduled run before any model call. - `/scripts/_state/audit_findings.jsonl` — *Audit* writes; the *Task board* reads the pending count for its rolled-up audit card. - `/scripts/security/*` — *Security envelope* owns; *Audit* consumes (as did the retired *Heartbeat*). - `/roles/` library — *Roles* owns; every project module that composes a binding consumes it. - `/Reference/services-registry.md` — *Reference data* owns; *Inbox*, *Audit*, *Brief* consume. - `/Reference/email-rules.md` — *Reference data* owns; *Inbox* consumes. **Standalone skills (not modules)** — singletons in `/.claude/skills/` that don't cluster into a module: - `verify-completion` — pre-completion self-review gate. - `systematic-debugging` — structured bug-investigation methodology. - `health` — 30-second workspace health composite score. - `subagent-driven-development` — multi-task plan execution with two-stage review. - `dispatching-parallel-agents` — fan out independent subagents in parallel. - `grocery-run` — shopping-agent stub (placeholder). - `writing-agent-behavior` — author/revise BEHAVIOR.md conduct specs: review-time artifacts (never auto-loaded into runtime prompts) capturing recurring, trajectory-judgeable agent conduct, with a five-class calibration matrix (positive, negative, lucky-correct negative, outside-scope, allowed boundary). Adapted from [braintrustdata/agentbehavior](https://github.com/braintrustdata/agentbehavior) (Apache-2.0); the unpublished upstream CLI is replaced by manual validation against the bundled spec. Companion primitive: a stdlib choice-scored LLM-judge script (judge specs as data files, code-enforced verdicts, per-criterion boolean decomposition, automatic untrusted-content wrapping; adapted from [braintrustdata/autoevals](https://github.com/braintrustdata/autoevals), MIT). Both scaffold-registered with review dates. **Anthropic + plugin skills** are listed in §6 Skills (not workspace-owned, so not modules). They include harness-config skills (`update-config`, `hookify`, `keybindings-help`, `fewer-permission-prompts`, `loop`, `schedule`, `claude-api`), publishing primitives (`pdf`, `docx`, `pptx`, `xlsx`), and tool-connector skills (`setup-cowork`, plugin chat skills). **Not yet modules** (loose ends worth naming for future cohesion): - *Voice / Home Integration* — a home-voice-interface project + a voice-booking-agent project. (An earlier browser voice/text web-UI MCP server was retired 2026-08: unused once an OS-level dictation tool covered voice input — see CHANGELOG.) - *Containerised execution* — the retired Heartbeat was the first instance; the sidecar pattern + check-mounts + anthropic-proxy generalise. Parked before the retirement (subscription auth needs the desktop app's IPC) and no longer has a host module; it waits for a second concrete instance. *(The former* Context Budget *entry graduated to the **Token Budget** module on 2026-06-10.)* **Dynamic workflows** (runtime-orchestrated subagent scripts, invoked as `/`; distinct from skills, which the agent follows turn-by-turn): - `best-of-n` — **Added 2026-06-18.** Best-of-N + rubric-judge for one HIGH-cost-of-error one-shot decision where no single test verifies the answer. Generates N deliberately *divergent* attempts in parallel (a different seed lens/role/strategy each — risk-first, upside-first, first-principles/contrarian — enforced by a divergence contract), then a judge ranks them against an explicit rubric (correctness, risk, fit, reversibility) and returns the recommended decision plus a synthesis grafting the best of the runners-up. **Judge shape upgraded 2026-08-09:** the judge runs a *different, higher-tier model* than the attempts (same-model self-preference bias is measured at 10–25%), writes its reasoning before any verdict, picks anchored labels that code maps to numbers (an off-list label fails loudly, never a silent zero), and runs twice — forward and reversed presentation order — with disagreement surfaced to the human rather than resolved silently. The judge is the checkable selection mechanism that grounds the second pass. The most expensive coordination route — used sparingly, not for routine work. **Reasoning-measurement + scaffold discipline (cross-cutting policy, added 2026-06-18 — not a module).** *The method is now documented in full at [EVALUATION.md](EVALUATION.md), with the harness, the authoring guide and three representative cases shipped as samples.* A workspace that keeps adding skills, rules, and always-loaded directives needs a way to tell which ones actually help. Two pieces hold that line. First, a **golden-set reasoning-regression suite**: a few dozen frozen cases with deterministically-checkable expectations, replayed headless at audit time under a variance floor (each case run several times, reported as a pass-rate ± stddev, never a single scalar) and trended — the first instrument that watches answer *accuracy* drift rather than config drift. Second, a **scaffold-discipline rule**: every capability scaffold (a skill, an always-loaded directive, a rule, a gate, a workflow) registers a falsifiable hypothesis and a review date; at review it must beat baseline on the suite or it is cut. Removal is a first-class outcome, because bloat suppresses effective intelligence as much as a good scaffold raises it — a build-biased upgrade pass undervalues trimming. Companion to the self-edit-loop gate (Pattern 10): a correctness signal certifies a *scaffold* the same way a human gate certifies a *self-edit*. These ride the Audit module's upgrade boundary, not a module of their own. **Loop-selection framework (cross-cutting decision tool, added 2026-06-18 — not a module).** A reusable test for the question a capable agent invites: given a recurring task, does it earn an autonomous loop, a surfacing nudge, or stay hand-driven? Two layers. First a **loop stack** for tagging: agent loop (a model calls tools until done) → verification loop (grade the output, feed failures back) → event-driven loop (fires on a hook, not a clock) → learning loop (proposes changes to standing files); you move up the stack for leverage and down for reliability. Then a **four-box selection test**: a task earns an autonomous loop only when it is all of recurring, mechanically verifiable (a script/exit-code/diff confirms it, not taste), low-judgment-per-instance, and headless-executable. An **irreversibility override** caps any outward or destructive act (email sent, comment posted, money moved, history pruned) at SURFACE even when all four boxes pass. Three buckets fall out: **LOOP** (autonomous on a trigger, the verifier is the gate), **SURFACE** (recurring + verifiable but judgment-heavy or irreversible → a read-only nudge or an approval-gated act, never silent autonomy), **KEEP MANUAL**. A standing corollary: verify the target against the *code* before deciding it loops (does it exist on the right branch, do what its description claims, run headless), because a task can pass every quality box and still fail the headless one. The framework's flagship output is the Session-workflow drift scan (`wrap_drift_scan.py`, SURFACE-bucket). It is the same route-by-consequence instinct as classify-then-act (#2), tier-by-impact (#4), the skill-as-weights gate (#10), and the scaffold-as-hypothesis check (#11); credit for the loop-stack + up/down framing goes to LangChain and latent.space/Swyx (see [ATTRIBUTION.md](ATTRIBUTION.md)), with the *selection* discipline (which work earns a loop) being the workspace's own addition. --- ## 3. Personas — the Roles Library > See also: [Claude Code subagents documentation](https://docs.claude.com/en/docs/claude-code/sub-agents). **Library:** `/roles/` — 17 pure, reusable canonical role definitions, each with a fixed schema (frontmatter + Identity / Directives / Constraints / Method / Output format / Red Flags / Rationalization Table). **Canonical roles (17):** `accountant`, `backend-developer`, `bookkeeper`, `data-engineer`, `developmental-editor`, `developmental-reviser`, `frontend-developer`, `health-data-analyst`, `learning-strategist`, `llm-engineer`, `nutritionist`, `platform-engineer`, `product-thinker`, `researcher`, `security-auditor`, `tester`, `wealth-manager`. **Composition:** each project has thin subagent bindings under `.claude/agents/` that compose a canonical role with the project's `CONTEXT.md` (entity facts) via `@` includes. **Rule:** roles are pure (no entity facts). Entity facts live in each project's `CONTEXT.md`. ```mermaid flowchart LR role["Canonical role
roles/security-auditor.md
(pure, no entity facts)"] ctx["Project CONTEXT.md
(entity facts: stack, paths, decisions)"] binding["Thin binding
project/.claude/agents/
project-security.md
(role + CONTEXT via @ includes)"] invoke["@project-security
invocable subagent"] role --> binding ctx --> binding binding --> invoke ``` **Validation:** a roles validator script checks frontmatter schema + binding composition. It ran every heartbeat cycle until that agent was retired in 2026-08; it now runs inside the `health` composite and on demand, and a non-zero exit surfaces to the operator in session rather than into a questions tracker. | Project type | Bindings (illustrative) | Context source | |---|---|---| | Personal finance | accountant / wealth-manager / bookkeeper | project `CONTEXT.md` | | Software product | backend / frontend / tester / security / llm / product | project `CONTEXT.md` | | Personal health | health-analyst / nutritionist | project context file | | Creative writing | developmental-editor / developmental-reviser | project `CONTEXT.md` | | Education | learning-strategist | project `CONTEXT.md` | **Not yet bound to any project:** `data-engineer`, `platform-engineer`, `researcher`. The `researcher` role is intentionally unbound — it's domain-agnostic (`requires_context: false`) and invoked directly for evidence-based investigation on any topic. **See also:** a `roles/README.md` with the schema and binding quick-reference; a `roles/_template.md` for new roles. A filled-in example lives at [`samples/roles/security-auditor.md`](samples/roles/security-auditor.md) (one of 17 canonical roles shipped in `samples/roles/`). --- ## 4. Routines — recurring agents and one-shot launchers ### Entry points — Claude Code app Previously the workspace was driven from a handful of terminal windows, each one launched by a `.bat` script and holding its own Claude session. The Claude Code desktop app now unifies that surface: - **Routines** — the app's built-in scheduler — replace most launcher `.bat` files for recurring work. Terminal launchers are retained only for flows that need specific env-var hygiene or direct shell control. - **Persistent parallel sessions** are the main win. The app holds many independent sessions open side-by-side, each anchored to a different workstream; the user actions whichever is ready. At the time of writing: 9 sessions cycled through earlier the same day, with ~50 agents and subagents running concurrently across them — each a different thread (bug fix, document edit, research query, project scaffold). Net effect: less context-swap tax. Each thread stays warm; the user returns to it when it's useful rather than reconstructing state every time. ### Launcher scripts (`/scripts/`) All entries below are **[custom]**. | Script | Purpose | |---|---| | `launch-claude.bat` | Bootstrap launcher with CLAUDE.md sanity check across all project folders. Primary entry point for terminal sessions. Delegates to `_bootstrap-check.bat`. | | `_bootstrap-check.bat` | Shared subroutine. Scans project folders for missing CLAUDE.md files and offers to create stubs. Called by the other launchers. | | `remote-control.bat` | Starts a Claude session with Remote Control enabled. Bootstrap check + interactive session. Must be double-clicked — cannot be invoked from within Claude Code (env inheritance issue). | | `shopping-chrome.bat` | Launches Chrome with remote debugging port and a dedicated profile. Persists store logins across automation sessions. Used by a personal shopping-agent project. | | `check-usage.bat` | Opens the Claude usage dashboard and runs a usage-stats CLI to show current 5-hour window burn rate. | | `audit.bat` | Runs the audit agent — hunts workspace improvements (public-source research plus an internal best-practice critique) and reviews configs, hooks, CLAUDE.md quality, test coverage, security; writes recommendations to the task list. Updated 2026-05-28 to tee stdout/stderr through `Tee-Object` to `tasks/scheduled-logs/upgrade-audit_.log` so `audit_cost.py` + `check_task_freshness.py` have a log to parse. | | `audit-second-opinion.bat` | **Added 2026-05-28 — R7.** Manual quarterly invocation of the `audit-second-opinion` subagent. Different prompt structure from the primary `audit` (skeptic/simplicity angle, narrative findings, max 5). Catches blind spots in the primary's own coverage. Brief written to `Reference/Research/_second-opinion-audit.md`. | | `audit_ledger.py` | **Added 2026-05-28 — R3.** Append-only JSONL of every audit finding with UUID/category/tier/status. CLI subcommands: `emit`, `mark accepted\|dismissed\|false_positive`, `stats`, `recent`, `category-weight` (the last drives R6 adaptive sampling in Phase 2.5b). Ledger at `scripts/_state/audit_findings.jsonl`. Stdlib only. | | `audit_cost.py` | **Added 2026-05-28 — R9.** Parses `upgrade-audit_*.log` files for `tokens=` and duration markers; appends per-run summary to `scripts/_state/audit_cost.jsonl`. CLI: `log [--all]`, `trend [--weeks N]`. Pairs with the ghost-token counter to provide an end-to-end audit budget view. Stdlib only. | | `security/check_task_freshness.py` | **Added 2026-05-28 — R1.** Dead-man's-switch (self-hosted alternative to Healthchecks.io). Scans `tasks/scheduled-logs/` per tracked task, confirms last log contains the task's success sentinel + is within the configured staleness window. Per-task `manual` flag tolerates first-run absence for manually-invoked tasks. CLI: `--json` / `--notes` (idempotent append to the task list). Exit 0 if all FRESH/MANUAL_OK; 1 otherwise. Stdlib only. | | `backup-restic.ps1` (+ `.bat` launcher) | Manual encrypted backup of the workspace to an S3-compatible object-storage target via `restic`. Client-side encryption — provider only ever sees ciphertext. Repo password + storage credentials pulled from the password-manager CLI at runtime (no secrets in the script or any synced file). Dedup + incremental + granular file-level restore. Retention: 7 daily + 4 weekly + 6 monthly. | | `restic-verify.ps1` (+ `.bat` launcher) | One-shot verification: lists snapshots, runs a read-data integrity check, performs a file-level restore round-trip and SHA256-diffs against source. Use before relying on the backup for recovery. | | `backup-excludes.txt` | Exclude patterns for the backup (`.venv`, `node_modules`, `__pycache__`, etc.). | | `email_rules.py` | **Gmail Automation Stack — Phase 1.** YAML parser + validator + matcher for the email-rules registry (~500 rules across 5 consumer tags: `bill-monitor`, `receipt-capture`, `email-triage`, `morning-brief`, `tax-receipts`). Handles `extends` inheritance, `senders: [...]` list expansion, split `action: {future:…, historical:…}`. Most-specific-wins matching. CLI: `validate`, `stats`, `index`, `match`, `match-batch`, `draft-rule`. | | `receipts_pipeline.py` | **Gmail Automation Stack — Phase 2.** Receipt ingestion: schema validation, categorisation, dedup against existing ledger rows, append + save, optional source-file filing. Supports both email-extracted and photo-OCR extracted receipts. | | `bill_tracker.py` | **Gmail Automation Stack — Phase 3.** Parses the services registry into typed `Service` rows with cost normalised to monthly. Matches incoming bills to services by hint/sender/domain. Appends to an actuals log. Four alert triggers: >20% over-threshold / unknown sender / cancelled-service renewal / duplicate. | | `appointments.py` | **Gmail Automation Stack — Phase 5.** Validates extracted appointment payloads, formats for the Calendar MCP `create-event`, generates dedup token embedded in event description. | | `send_self_email.py` | **Narrow Iron Law exception (2026-04-19).** The *only* path by which Claude sends email autonomously. Hardcodes recipient as the user's own address and raises a `SelfOnlyViolation` on any other address. Uses SMTP (not MCP) with an app password resolved from env var or OS keychain. Intended solely for morning-brief delivery; all other email operations still go through MCP drafts. | | `ai_news.py` | **Morning-brief AI-news helper (2026-04-21; expanded same day).** Stdlib-only (`urllib` + `xml.etree` + `sqlite3`, zero pip deps) RSS/Atom fetcher with SHA-256 content-hash dedup in a local SQLite store, 48h recency window, 30-day prune. Feeds grouped into three tiers — **aggregators/research** (Simon Willison, HN AI-tag, arXiv cs.AI), **provider-official** (OpenAI, Google DeepMind, Google AI Blog, Google Research), **tech media** (MIT Tech Review AI, Wired AI, TechCrunch AI). Per-source cap (default 6 items per run) prevents high-volume feeds from crowding out lower-volume provider-official posts. Anthropic has no public RSS (verified 2026-04-21 — all candidate paths 404); HN + Simon Willison cover Anthropic announcements within hours. Auto-marks returned items as seen; unreachable feeds skipped silently and surfaced in `feed_errors`. CLI: `fetch [--limit N]`, `stats`. Consumed by the `morning-brief` scheduled task's `## AI news` section. A working copy lives in [`samples/scripts/ai_news.py`](samples/scripts/ai_news.py). | | `brief_render.py` | **Morning-brief newsletter renderer (2026-04-21).** Parses the daily brief's markdown source and emits inline-CSS HTML suitable for email delivery. Section-aware: masthead + local-weather strip + two-column appointments + AI-news cards with source-hostname badges + tasks grouped by `##` header with count badges + numbered open-questions with posted-date + overnight + attention bullets + centred footer. Palette: warm off-white body, white card, deep navy primary, burnt-orange accent. System fonts for body, Georgia serif for the masthead. Max-width 640px, table-based layout for broadest email-client compatibility (Gmail, Apple Mail, Outlook, iOS Mail). Zero dependencies, stdlib only. Invoked by the `morning-brief` SKILL.md delivery step after the markdown is written; output feeds `send_self_email.py --html-file` for multipart text+HTML delivery. | | `ghost_token_counter.py` | **Baseline counter for always-loaded context (2026-04-21).** Stdlib-only, chars/4 approximation. Measures tokens loaded at session start across user + workspace CLAUDE.md, always-loaded memory files (excluding `episodes/`), skill + subagent + scheduled-task frontmatter descriptions, and hook command strings. Records per-source breakdown to `scripts/_state/ghost_tokens.db`. CLI: `baseline [--verbose]`, `trend [--weeks N]`. Invoked by the weekly `upgrade-audit` Phase 1 — a finding is surfaced if the baseline grows >10% above the prior 4-8 week median. Pattern reference (not dependency): `alexgreensh/token-optimizer`. A working copy lives in [`samples/scripts/ghost_token_counter.py`](samples/scripts/ghost_token_counter.py). | | `token_report.py` | **Daily spend telemetry (2026-06-10 — Token Budget module).** Wraps a local usage analyser for API-equivalent cost: `report` (last-N-days + quiet-day floor), `log` (idempotent per-date append to `scripts/_state/token_history.jsonl` — run by the morning brief), `brief-line` (one fail-safe line for the brief), `trend` (weekly averages; the audit applies a +25%-over-median rule). A working copy lives in [`samples/scripts/token_report.py`](samples/scripts/token_report.py). | | `audit_checks/run_all.py` | **Coded audit assertions (2026-06-10 — checks-as-code).** Canary fixture integrity, public-mirror drift, backup recency, rotation state, heartbeat budget/model-map/gate, health + token staleness — PASS/WARN/FAIL + evidence as JSON. The audit reasons over results instead of re-deriving checks in prose (LLM re-derivation of a coded assertion produced a false positive). A working copy lives in [`samples/scripts/audit_checks/run_all.py`](samples/scripts/audit_checks/run_all.py). | | `heartbeat/preflight_gate.py` | **Stage-0 heartbeat gate (2026-06-10 — gate-don't-loop).** Run by the scheduler wrapper before any model call: content-hash dirty-check over the watched task files, wrapper-run deterministic scans, a 24h agent floor. Exit 100 = skip the LLM entirely (the log still carries the success sentinel for the dead-man's switch); fail-open on gate bugs. `--mark-cycle` stamps state post-cycle so the agent's own edits don't re-trigger. A working copy lives in [`samples/scripts/heartbeat/preflight_gate.py`](samples/scripts/heartbeat/preflight_gate.py). | ### Scheduled tasks (`/.claude/scheduled-tasks/`) The scheduler itself is **[stock]** (either the Claude Code app's Routines UI or the `scheduled-tasks` MCP). The specific tasks below are **[custom]**. | Task | Cadence | Purpose | |---|---|---| | `heartbeat-monitor` *(retired 2026-08)* | Formerly every 2 hours | **Deregistered.** It read the task queue, posted clarifying questions, actioned cleared tasks, and flagged stale items, running the stale-CONTEXT.md scan, stale-PLAN.md scan, roles validator, and upcoming-renewals scan each cycle, with an anti-duplication guard that checked project folder state (`PLAN.md` checklist, `git log`, recent file activity, staging folders) and posted a progress-check question rather than re-scaffolding over prior work. Its job now belongs to the *Task board*, drained on demand in an interactive session. The recurring scans it carried moved to the `health` composite and the close-out drift scan. | | `morning-brief` | Daily (early morning) | Gmail automation orchestrator added 2026-04-19. Runs four pipelines: (1) email triage — applies Gmail actions (label/archive/trash), drafts new-sender proposals; (2) receipt capture — email path + photo path via a drop folder; appends to a ledger workbook; (3) bill & subscription tracker — matches bills against the services registry, logs to an actuals workbook, emits four alert triggers; (4) compose + deliver brief — appointments next 14 days via Calendar MCP + local weather + active task counts + open questions + overnight activity, written to a dated markdown file, then **rendered to newsletter-style inline-CSS HTML via `scripts/brief_render.py`** (added 2026-04-21 — masthead + weather strip + two-column appointments + AI-news cards + task sections) and **sent multipart (text + HTML)** self-to-self via the narrow-exception SMTP helper with a draft fallback. Appointment extraction runs between (3) and (4). Idempotent. | | `upgrade-audit` | Weekly | Runs the full audit agent — Phase 1 global setup, Phase 2 per-project, Phase 2.5a plugin/MCP bloat check, Phase 2.5b external opportunities (web research), Phase 2.6 security review, Phase 3 write recommendations. Writes to the task list under `## Setup Review` and `## Security` sections. | | `consolidate-memory` | Weekly | Memory hygiene pass — runs the memory-lint script with `--fix`, resolves contradictions between memory files and source-of-truth docs, converts relative→absolute dates, merges duplicates, moves stale episodes to the `episodes/` subfolder, keeps `MEMORY.md` under its 200-line ceiling. Four-op per fact (ADD / UPDATE / DELETE / NOOP). Iron Laws in memory are never consolidated away. | | `promo-capture` *(retired 2026-09)* | Formerly twice weekly | **Disabled.** The content-machinery layer was re-pointed to a learning loop, and traffic, stars and referrers stopped being signals it reads. The capture ran twice a week because the traffic API holds only a rolling 14-day window; that window is now allowed to lapse. The promotion-metrics tool keeps its manual `adoption` subcommand, which records another project folding one of these patterns into its own design. | | `check-usage` | Manual | Opens usage dashboard and runs usage stats. | | `remote-control` | Manual (disabled) | Disabled — cannot launch from Claude Code due to env inheritance. Use `remote-control.bat` directly. | > **Note on remote triggers:** Remote triggers run in Anthropic's cloud sandbox and cannot access local workspace files, so they could not do heartbeat/audit jobs that need to read or write locally. Local scheduled-tasks are the canonical path for any routine that needs to touch local files. ### Automated infrastructure (OS-level scheduler) On systems where the Claude Code app's built-in `scheduled-tasks` MCP is **not connected** (listed in `settings.json` permissions allowlist but absent from `claude mcp list`), SKILL.md files under `/.claude/scheduled-tasks//` will never fire on their own. The durable workaround is OS-level scheduling — Windows Task Scheduler (shown below) or `cron`/launchd on Linux/macOS — pointing at a thin wrapper that reads the SKILL.md and pipes it to `claude --print`. | Task | Cadence | What it does | |---|---|---| | Morning Brief | Daily, early morning | Invokes the wrapper with `-Skill morning-brief`. Must use `LogonType: Interactive/Background` — see critical note below. | | Consolidate Memory | Weekly | Invokes the wrapper with `-Skill consolidate-memory`. Same principal requirement. | | ~~Heartbeat Monitor~~ | Formerly every 2h | **Removed 2026-08.** Invoked the wrapper with `-Skill heartbeat-monitor`; registered after `schtasks /Query` confirmed the SKILL.md had never fired on its own. Retired with the module, so nothing on the OS scheduler drives task coordination now. | **Wrapper — `/scripts/run-scheduled-skill.ps1`:** reads `/.claude/scheduled-tasks//SKILL.md`, pipes the content as the prompt to `claude --print --add-dir `, tees output to `/tasks/scheduled-logs/_.log`. `-DryRun` resolves paths without invoking. Uses the Continue error-action preference per the PowerShell 5.1 native-CLI lesson (PS 5.1 otherwise promotes native-command stderr writes to terminating exceptions). **Critical: Task Scheduler principal setting (discovered 2026-04-21).** The `claude --print` CLI (Node.js) needs a real console handle to manage stdio. When a Task Scheduler task fires with `LogonType: Interactive only` (the default when you create a task without selecting the "Run whether user is logged on or not" option), PowerShell launches in a detached / hidden session with no console; `claude` dies immediately with exit code `0xC000013A` (`STATUS_CONTROL_C_EXIT`) before writing a single byte — the `tee` in the wrapper never gets any data. `scheduled-logs/` stays empty despite the task showing `Last Run Time` each firing. **Fix:** for each Claude task, Task Scheduler → Properties → General → **"Run whether user is logged on or not"** + Windows password. This flips `LogonType` to `Password` (shown as `Interactive/Background` in `schtasks /Query`), which gives the task a proper batch-logon session with a valid console. Verify with `schtasks /Query /TN "" /V /FO LIST | Select-String "Logon Mode"`. This was the root cause behind an initial period where every scheduled fire of the morning brief appeared to succeed (task state: `Ready`, `Last Result: 0xC000013A`) but actually crashed before producing output — manual recoveries masked the problem. The previous dedicated nightly backup job was removed in favour of manual-only invocation via the restic script. > **Note:** Remote Control cannot be launched from within Claude Code. Child processes inherit OAuth env vars that force API mode and break MCP server connections. Use the `remote-control.bat` launcher via double-click or desktop shortcut only. --- ## 5. Hooks — automatic behaviours on events > See also: [Claude Code hooks documentation](https://docs.claude.com/en/docs/claude-code/hooks). A sample hook config lives at [`samples/.claude/settings.example.json`](samples/.claude/settings.example.json). The **mechanism** is **[stock]**; each hook's **command** is **[custom]**. Configured globally in `/.claude/settings.json`. | Hook | Trigger | Effect | |---|---|---| | **PreToolUse (Edit/Write)** | Before `Edit` or `Write` | Blocks modification of protected files: `.env*`, `credentials*`, `secrets*`, lock files, a few specific sensitive project files, financial result workbooks, bank transaction CSVs, Google OAuth tokens. Path match is case-insensitive. Allows writes under `agent-workspace-architecture/samples/` so legitimate mirroring of sample files doesn't trip the hook. | | **PreToolUse (Bash)** — added 2026-04-22 | Before `Bash` | Closes the Bash-gap in Edit/Write protection. `/scripts/security/check_bash_command.py` inspects the command text for write-intent verbs (`>` / `>>` / `rm` / `mv ` / `cp ` / `sed -i` / `tee` / `touch` / `chmod` / `chown` / `truncate`) targeting any protected-path substring (normalised to forward slashes + lower-case). Also blocks dangerous git operations: `git push` to `main`/`master`, any `--force` / `--force-with-lease` push, `git reset --hard origin/main`. Also blocks inline assignments of exec-hijacking env vars (`GIT_SSH_COMMAND`, `NODE_OPTIONS`, `LD_PRELOAD`, `PYTHONSTARTUP`, `BASH_ENV`, …) prefixed to a command *(added 2026-06-11, audit finding `bbb1f3e4`)*. Allowlists `samples/` so legitimate mirror/sample copies proceed. Fails open on parse errors or script bugs. Python/Node file-writes via `open()` are out of scope — the hook inspects shell command text only. | | **PreToolUse (WebFetch)** *(added 2026-06-16)* | Before `WebFetch` | Stops WebFetch prompting for every page during research. `/scripts/security/check_webfetch.py` auto-approves (`permissionDecision: "allow"`) a curated **quality-source allowlist** — restricted-registry TLDs (`.gov`/`.gov.au`/`.edu`/`.ac.uk`/`.int`/`.mil`) plus academic, standards-body, reference and developer-docs domains — sends **risky shapes** to a confirmation prompt (`"ask"`: IP-literal hosts incl. the cloud-metadata address `169.254.169.254`, localhost / internal / single-label hosts, embedded `user:pass@` credentials, non-http(s) schemes, non-ASCII / IDN homoglyph hosts), and **falls through** (emits nothing) for well-formed unknown public domains so the settings allow-list still governs and unlisted domains still prompt — additive, never regresses the existing allow-list. **Fails closed:** any parse error / missing URL / exception → an explicit `"ask"`, never a silent allow (the error path emits `"ask"` rather than exit-2-hard-block, which would be too aggressive for research). Unit-tested (43 cases incl. suffix/subdomain spoofs + SSRF shapes) and adversarially reviewed (no false-allow survived). The trusted-TLD list carries an invariant: every entry must be a registry-restricted TLD, since a generic gTLD there would become a free auto-approve bypass. Activates per session (hooks load at start). | | **PostToolUse** | After `Edit` or `Write` | Auto-formats `.py` with `ruff format` + `ruff check --fix`; auto-formats `.ts/.tsx/.js/.jsx/.mjs/.cjs` with `prettier --write` (if prettier on PATH). | | **PostToolUse (Sentinel monitor)** *(added 2026-06-16)* | After tool calls (Bash / Edit / Write / WebFetch / Read + MCP) | **Sentinel module** (the quiet post-hoc monitor). Records every call to a local action-log (`scripts/_state/sentinel_actionlog.jsonl`, shapes/hashes only — never secrets) and fires a desktop toast ONLY on a hard tripwire: an executed security-envelope write, an exec-hijack/force-push that ran, raw shell egress (`curl`/`wget`/`nc`), a credential-shaped string in an outbound payload, or a read of a known secret-key file. Deterministic (no LLM), fail-open. `/scripts/security/sentinel_actionlog.py` → `sentinel_log.py`. See §2 *Sentinel*. | | **SessionStart** | After context compaction | A short prompt re-injects context: read the lessons file, check active task list, load path-scoped rules, remember the meta-architecture for structural questions. | | **Notification** | On tool result | OS notification (async, brief timeout). | | **PreCompact** *(added 2026-05-22)* | Before context compaction | Backs up the current transcript to a local folder (pruned to last 5) to guard against compaction context loss. `/scripts/security/precompact_backup.py`. | | **PreToolUse (Bash) — plugin** *(added 2026-05-26)* | Before `Bash` (interactive CLI only) | A third-party command-safety plugin (`claude-code-safety-net`, MIT) adds semantic destructive-command interception complementing the in-house bash-safety hook — catches `git checkout --` / `restore` / `branch -D` / `clean -f` / `find -delete` / `xargs rm -rf` + interpreter wrappers (`bash -c`, `python -c`). Loaded from the plugin's `hooks/hooks.json` when enabled; **does NOT load in a headless/SDK environment** with no plugin subsystem (the in-house hooks above still apply). Source-verified before install (one runtime dep, no telemetry; network limited to an opt-in version-check outside the hook path). Audit logs to a local dir. | --- ## 6. Skills — invokable capabilities > See also: [Claude Code skills documentation](https://docs.claude.com/en/docs/claude-code/skills). A sample custom skill lives at [`samples/.claude/skills/orient/SKILL.md`](samples/.claude/skills/orient/SKILL.md). ### Custom workspace skills (`/.claude/skills/`) All entries below are **[custom]**. | Skill | Purpose | |---|---| | `orient` | Session-start briefing. Reads the meta-architecture, CLAUDE.md, the task set, and freshness-checks project CONTEXT.md / PLAN.md files. Returns active state, in-flight work, open questions, staleness flags, and a recommended next action. | | `wrap` | Task close-out ritual. Updates the implementation plan review section, strikes through the matching task-list bullet, resolves linked questions, sweeps registries (command shortcuts, skill/subagent/scheduled-task/launcher/MCP/hook tables, project layout, file protection, memory index, project context, services registry). **Step 5b added 2026-05-27 — post-settings-change verification:** if `settings.json` permissions/hooks changed this session, the wrap requires checking the next scheduled-task log (empirical artefact, not config inspection) before declaring complete. Close-out split into a main-thread brief plus an execution-tier subagent that applies it; the main thread makes about three inferences at peak context instead of twenty. | | `tasks` | Task-queue readout. Parses the task list (active bullets, grouped by section) and the questions file (open questions only). Read-only. Lighter than `orient`. | | `context-save` | **Added 2026-04-24 (adapted from gstack).** Write a timestamped session checkpoint to `/tasks/checkpoints/YYYY-MM-DD_HHMM_.md` — captures task, files in flight, decisions, open questions, blockers, next action, git state. Use before likely compaction, before pivoting to unrelated work, before a long break. Pairs with `context-restore`. | | `context-restore` | **Added 2026-04-24 (adapted from gstack).** Load the most recent checkpoint from `/tasks/checkpoints/` (ordered by filename prefix, not mtime), run a drift check on cited files + branch + open questions, then resume from the checkpoint's next action. | | `verify-completion` | Mandatory self-review gate. Invoke before claiming any implementation task, bug fix, or test/build/lint pass is complete. | | `systematic-debugging` | Structured approach to investigating bugs, errors, test failures, or unexpected behaviour when not immediately obvious. | | `health` | **Added 2026-04-24 (adapted from gstack).** 30-second composite health dashboard — runs type-check, lint, test-collection, dead-code, secrets scan, memory lint, roles validator, ghost-token drift. Weighted 0-10 composite with A-D grade; JSONL trend history at `/scripts/_state/health_history.jsonl`; compares each category against its prior 10-run median. **Read-only** — diagnoses only, never fixes. | | `role-pressure-test` | Adversarial test one role against realistic pressure. Invoke when deploying a new role or significantly modifying an existing role's Constraints / Red Flags / Rationalization Table. | | `subagent-driven-development` | **Added 2026-04-24 (adapted from obra/superpowers).** Execute a multi-task plan by dispatching a fresh subagent per task, followed by a single review returning two separately-labelled verdicts (spec compliance + code quality, both blocking) before marking complete. **Hardened 2026-08-12 (upstream v6.0 + v6.2 patterns, read not installed):** a pre-flight plan-conflict scan batched to the user before task 1; the reviewer is read-only on the checkout but keeps a shell for focused verification; a suppression ban on the *controller* writing the review prompt, with a trigger-phrase test; a per-item cannot-verify marker inside a normal verdict rather than a fifth status; and a five-round fix loop with fresh implementers, escalation to the top model tier at round 4, scoped re-review, and controller adjudication written to a durable ledger — silent discards forbidden. Uses workspace `general-purpose` / project role bindings as the implementer; composes `/roles/review-templates/spec-reviewer.md` + `code-quality-reviewer.md` for the review stages. | | `dispatching-parallel-agents` | **Added 2026-04-24 (adapted from obra/superpowers).** Fan out 2+ subagents in parallel when facing independent problem domains (different test-file failures, unrelated bug investigations, concurrent research questions). Single Agent message with multiple tool-use blocks; prefer the `researcher` subagent over `general-purpose` for research-shaped work. | | `terse-mode` | Session-long output compression discipline (added 2026-04-21). Iron Law: compress prose, preserve precision — never compresses tool arguments, code, errors, security warnings, research-brief claim grades, or final deliverable content. Invoke via "terse" / "/terse" / "terse mode"; release via "verbose" / "/verbose" / "normal mode". Does not persist state — lives in the current conversation only. A working copy lives in [`samples/.claude/skills/terse-mode/SKILL.md`](samples/.claude/skills/terse-mode/SKILL.md). | | `review-queue` | **Retired with the heartbeat lane (2026-08-08).** Was: drain the heartbeat-PR-agent review queue (`tasks/HEARTBEAT_REVIEWS.md`) (added 2026-04-22). Walks each pending/reminded entry, presents the artifact (REVIEW.md / PR diff / draft), and actions the user's per-item decision: integrate / reject (appends ADR block to `HEARTBEAT_REJECTIONS.md`) / redirect / skip. Distinct from the built-in `/review` plugin which reviews a single PR. Invoke via "review queue" / "/review-queue" / "drain the queue" / "triage reviews". A working copy lives in [`samples/.claude/skills/review-queue/SKILL.md`](samples/.claude/skills/review-queue/SKILL.md). | | `audit-workthrough` | **Added 2026-06-10.** Walk the audit's pending-findings queue — folds the finding ledger's events per UUID, presents each `pending` finding with evidence from the audit's full-report file, actions the per-item decision (apply / dismiss / false-positive / defer; verify the flagged gap against actual state before any apply), and marks the ledger. The marks feed the audit's adaptive source weighting. Sibling of `review-queue`. Invoke via "work through the audit" / "/audit-workthrough" / "drain audit findings". A working copy lives in [`samples/.claude/skills/audit-workthrough/SKILL.md`](samples/.claude/skills/audit-workthrough/SKILL.md). | | `board` | **Added 2026-08 (Task board module).** Read or update the canonical card store — the outstanding-work readout, quick-add, inbox triage (scratchpad notes → cards, striking the lifted note), and completion sync. Always re-renders the view after an edit, using per-record block edits matched on `id` rather than a file-spanning regex. Invoke via "board" / "/board" / "what's on my plate" / "triage the inbox". | | `agent-queue` | **Added 2026-08 (Task board module).** The delegation half. Runs the intake interview when the operator delegates a card (done-when always confirmed, write-scope, constraints, pre-ruled forks), then drains `delegate: queued` cards on demand in the current session, enforcing the template floor per card and writing an unruled fork into the card's `blocked:` field instead of guessing. Successor to the retired heartbeat cron. A working copy lives in [`samples/board/agent-queue.SKILL.example.md`](samples/board/agent-queue.SKILL.example.md). | | `grocery-run` | **(Stub)** Placeholder for upcoming shopping-agent workflow. | | `structured-reasoning` | **Added 2026-06-18.** Decompose-then-solve for a hard one-pass design / architecture / trade-off / estimation question where no test framework anchors the answer — the single-strong-agent lane. Zero extra tokens; the value is the reviewable-decomposition pause that catches a bad split before the work is spent. | | `divergent-lens` | **Added 2026-06-18.** Adversarial second-pass critic for write-heavy deliverables (prose, analysis, strategy) where no machine-check exists. Fixed evidence-first rubric — the most-likely-fabricated claim, the strongest ignored counter-argument, an audit of every number/date/entity/citation, the load-bearing assumption — each grounded in the sentence it challenges. A *true-divergence* second pass earns its tokens (the rule: a 2nd pass helps only via a checkable signal or a divergent lens). Distinct from a writing-style linter (which catches surface tells — this attacks substance). | | `lesson-review-queue` | **Added 2026-06-18.** Drains the candidates flagged by the session-end correction miner with a human-approves-the-diff gate — presents each mined signal (wrapped as untrusted external content), actions the per-item decision (draft-lesson / false-positive / defer / already-covered), and marks a sidecar ledger so it is not re-surfaced. Closes the self-improvement loop's last mile. Sibling of `review-queue` + `audit-workthrough`. | | `goal-design` | **Added 2026-06-21.** Pre-flight interview that turns a fuzzy intent into a best-practice `/goal` loop artifact. Loads the target project's context, interviews for outcome → verifiable stop-condition → exact check → turn bound → guardrails, and applies a hard checkable-condition gate (routes away to a one-pass reasoning skill / research flow / normal session if no transcript-surfaceable check exists — the fast model that judges a `/goal` reads only the transcript). Carries a **context-durability** block (re-prove each turn, checkpoint to a file, never `/clear` mid-goal) because the `/goal`×compaction interaction is undocumented, so every generated goal is built to survive it. Writes the artifact then STOPS — the operator launches `/goal` deliberately. A working copy lives in [`samples/.claude/skills/goal-design/SKILL.md`](samples/.claude/skills/goal-design/SKILL.md). | ### Anthropic + plugin skills All entries below are **[stock]** or **[plugin]** (shipped by Anthropic or available as plugins). User-invocable via `/`. Typical set: `update-config`, `keybindings-help`, `simplify`, `less-permission-prompts`, `loop`, `schedule`, `claude-api`, `pdf`, `docx`, `pptx`, `xlsx`, `consolidate-memory`, `skill-creator`, `setup-cowork`, `init`, `review`, `security-review`. --- ## 7. Subagents — specialised workers > See also: [Claude Code subagents documentation](https://docs.claude.com/en/docs/claude-code/sub-agents). ### Workspace custom subagents (`/.claude/agents/`) All entries below are **[custom]**. | Agent | Role | |---|---| | `audit` | Setup / project / security audit. Read-only except for the task list. Canonical instructions drive both `audit.bat` and the weekly audit scheduled task. 2026-05-28 R1–R9 upgrade landed Phase 0 (canary verification), Phase 2.6b (runtime health), R3 finding ledger emission, R5 mechanical-impact tier table, R6 adaptive source weighting, R8 semantic-drift memory check, R9 cost line; source material section at top cites the public patterns behind each design (see §14). | | `audit-second-opinion` | **Added 2026-05-28 — R7.** Independent second-opinion auditor — deliberately different prompt structure from `audit`. Open-question driven, narrative findings, max 5. Quarterly manual cadence via `scripts/audit-second-opinion.bat`. Implements the two-auditor pattern from financial auditing (and from Vanta/Drata third-party-assessment requirements). Read-only; never auto-applies. | | `heartbeat` *(retired 2026-08)* | Project manager. Ran every 2 hours, read and wrote the task files, and managed the question-then-action loop + anti-duplication guard. The definition is kept for reference; the work moved to the *Task board*'s `agent-queue` drain, which runs in an interactive session on the operator's explicit queue. | | `researcher` | Evidence-based research with fabrication guards and source discipline. **Auto-routed** — when any agent spawns a subagent for a research-shaped task, Claude Code's subagent picker prefers this over `general-purpose` based on the description field. Composes the canonical `researcher` role via `@`-include (one source of truth). `requires_context: false` — no project binding needed; calling agent passes entity facts inline if required. Read-only + web tools + fan-out. | ### Project role bindings (per project, see §3) Each project directory keeps its own `.claude/agents/` folder with project-scoped bindings — all **[custom]**. ### Built-in subagent types All **[stock]**: `general-purpose`, `Explore` (codebase search), `Plan` (architecture/planning), `claude-code-guide`, `statusline-setup`, plus the two workspace-custom ones above. --- ## 8. MCP servers — external capability bridges > See also: [Claude Code MCP documentation](https://docs.claude.com/en/docs/claude-code/mcp). | Server | Type | Purpose | |---|---|---| | Remote chat channel | Plugin | **[plugin]** Task dispatch from a chat client. | | `scheduled-tasks` | Built-in | **[stock]** Create/list/update scheduled tasks. | | Directory access | Built-in | **[stock]** Request access to host directories outside CWD. | | Browser automation | Built-in | **[stock]** Tabs, screenshots, DOM, network. | | Preview server | Built-in | **[stock]** For dev work (start/stop, console, network, screenshots). | | Registry search | Built-in | **[stock]** Search and suggest connectors from the MCP registry. | | GitHub | Plugin | **[plugin]** Native GitHub issue/PR/CI tools. | | TypeScript LSP | Plugin | **[plugin]** Diagnostics, go-to-definition, find-references after edits. | | Context7 | Plugin | **[plugin]** Real-time, version-specific documentation from source repos. | | Command-safety | Plugin | **[plugin]** Third-party (`claude-code-safety-net`, MIT) providing a PreToolUse Bash hook (see §5) for destructive git/filesystem interception. Loads in interactive CLI only. | | Google Calendar | Local stdio (npm global) | **[local]** Google Calendar read+write. OAuth creds + tokens in a protected local folder. Workspace-scoped. | | Google Workspace | Local stdio (uvx) | **[local]** Gmail + Drive read-only. Shares the same OAuth client as the Calendar server. Workspace-scoped. | --- ## 9. Memory system — persistent context across sessions > See also: [Claude Code memory documentation](https://docs.claude.com/en/docs/claude-code/memory). **Location:** `/.claude/projects//memory/` **Index:** `MEMORY.md` — always loaded, ~150 chars per entry, **capped at 200 lines / 25 KB** (matches the Claude Code auto-memory ceiling). **Subfolder:** `episodes/` — one-off events (cleanups, migrations, launches). NOT referenced from `MEMORY.md`, NOT always loaded; browsed on demand when historical context is needed. Separating episodic from semantic content keeps the always-loaded prefix small and stops date-stamped "we did X" narratives silently masquerading as durable facts. **Types:** - **user** — profile, role, goals, preferences. Tailors how Claude communicates. - **feedback** — corrections and validated approaches. Prevents repeated mistakes. - **project** — durable state, Iron Laws, pointers at source-of-truth docs (`CONTEXT.md`, `PLAN.md`, registries). Prefer pointing over mirroring — the canonical source changes faster than memory, and a copy rots. - **reference** — pointers to external systems and to internal architecture (this file, the roles library). Carry `learned_on` / `last_verified` / `verify_by_checking` YAML frontmatter so drift is surfaceable. ### Discipline (workspace-specific rules supplementing the system-prompt auto-memory policy) - **Dedup on write.** Before creating a new file or appending a fact, grep existing memories — if information overlaps >60%, UPDATE the existing file, don't duplicate. - **Point, don't mirror.** If the fact has a canonical home, memory keeps a short pointer, not a copy. - **Four-op per fact:** ADD / UPDATE / DELETE / NOOP. Contradictions resolve to one verb, never both. - **Verify before asserting from memory.** Memory is a point-in-time snapshot, not live state. A claim that names a file, flag, or service must be verified against the current repo before acting on it. - **Anthropic's memory-tool system prompt, verbatim:** *"keep its content up-to-date, coherent and organized. You can rename or delete files that are no longer relevant. Do not create new files unless necessary."* ### Tooling - **Memory-lint script** (`/scripts/memory_lint.py`) — walks the memory directory and `episodes/`, checks every referenced file path exists. `--fix` refreshes `last_verified` on clean pass. `--notes` appends drift to the task list under a dated `## Memory — drift detected ` section, idempotent per-line. Runtime-created paths (e.g. browser-profile directories, MCP log folders, OAuth state dirs) are allowlisted so they don't flag. The retired heartbeat agent invoked the lint every cycle; it now runs inside the `health` composite and in the weekly consolidation pass. - **Weekly `consolidate-memory` scheduled task** — the deeper pass. Resolves contradictions between memory and source-of-truth docs, converts relative→absolute dates, merges duplicates, moves decayed episodes into the subfolder, keeps `MEMORY.md` under its ceiling. Iron Laws in memory are never consolidated away. Canonical instructions: `/.claude/scheduled-tasks/consolidate-memory/SKILL.md`. --- ## 10. Task coordination layer The canonical card store lives in `/board/`; everything else is in `/tasks/`: | File | Owner | Purpose | |---|---|---| | `board/board.md` | user + agent | **Canonical store of outstanding work (2026-08).** A flat list of `###` cards, one per outstanding thing, each carrying a literal `next:` action, an honest `owner:`, and `status` / `area` as *fields* so a move is a one-line edit. A `repeat:` card renders in the collapsed recurring lane; `delegate: queued` puts a card in the agent queue. The capture scratchpad at the head of the file takes raw notes, which an agent later triages into cards and strikes through. Rendered to `board/board.html`, which is a generated view and never hand-edited. | | `To Do Notes.md` | user-written | Raw capture inbox, and the historical master task list. Kept as the prose history that cards link back to via their `source:` field; it is no longer the tracker. | | `board/board.md` § queued cards | user (sets `delegate:`) | The delegation queue. The operator's mark is the whole authorization surface, and the intake interview writes `done-when:` / `write-scope:` / `constraints:` / `ruling:` lines into the card body. A drain that hits an unruled fork writes the question into the card's `blocked:` field, and the operator answers by clearing it. | | `HEARTBEAT.md` | static *(predecessor)* | The retired heartbeat agent's operational instructions — **classify-then-act flow** (from 2026-04-22): classifier procedure with rejection-history pre-check + circuit breaker, per-task-type staging recipes, review-surface writing, rejection logging. Protected by PreToolUse hook. Kept as the studied predecessor design; a redacted copy is at [`samples/tasks/HEARTBEAT.md`](samples/tasks/HEARTBEAT.md). | | `To Do Questions.md` | heartbeat *(predecessor)* | Q&A tracker — **open blocks only**, used for `needs-intent` + `out-of-scope` classifications. The heartbeat posted questions with a best-guess default embedded, the user answered inline, and the next cycle picked the answers up. This is the channel that failed: thirteen blocks accumulated unanswered because the file was never in anyone's path. Questions now attach to the card they are about. | | `answered/To Do Questions.md` | heartbeat *(predecessor)* | **Archive.** Closed blocks (REMOVED / COMPLETED / RESOLVED / SCOPED / SCAFFOLDED / SUPERSEDED / CONTEXT PROVIDED) migrated here at resolution time. Not loaded by `orient` or `tasks` skills; browse on demand only. | | `HEARTBEAT_REVIEWS.md` | heartbeat *(predecessor)* | **Added 2026-04-22.** Active review queue for completed `has-default` sandbox builds — one line per entry (date / status / task-slug / staging-location / summary). Morning brief surfaced it as `## Awaiting your review`. Drained interactively via the `review-queue` skill; the remaining entries are still drained that way, but nothing writes new ones. | | `HEARTBEAT_REJECTIONS.md` | heartbeat *(predecessor)* | **Added 2026-04-22.** Durable ADR-style rejection log — append-only `## YYYY-MM-DD — ` blocks with Attempted / Rejected because / Lesson for future attempts. The heartbeat grepped this before classifying any task; 3+ matches forced `needs-intent` (circuit breaker). Archived to `HEARTBEAT_REJECTIONS_archive.md` past 200 lines. The rejection-memory idea survives the retirement even though the classifier does not. | | `todo.md` | claude (per task) | Current implementation plan + review blocks for in-flight and recent work. Older reviews archive to `todo-archive.md`. | | `audit/SETUP_REVIEW.md` | audit (per run) | **Added 2026-06-10.** The audit's FULL current-run report (overwritten each run); the task list keeps only a short digest + pointer. Relocated out of the task list because the full block taxed every reader of it; the durable findings queue is the ledger, drained via `audit-workthrough`. | | `todo-archive.md` | claude | **Archive.** Older completed review blocks. Split from `todo.md` on 2026-04-21. Not loaded by `orient`. | | `lessons.md` | claude (after corrections) | Self-improvement loop. Rules to prevent repeated mistakes. Loaded at session start. | **Workflow (revised 2026-08):** the operator dumps a raw note into the board's scratchpad (or quick-adds a card) → a session triages each note into a card with a literal next action and an honest owner, then strikes the note → work is picked up from the board directly. For anything the operator wants an agent to do, one extra step: the operator says so, an **intake interview** runs while the intent is still in their head (what does done look like, which folders may be written, what constrains the approach, how should the foreseeable forks be ruled), and the card gets `delegate: queued`. The `agent-queue` drain then actions queued cards on demand inside an interactive session, checking a template floor per card first (a literal `next:`, a `links:` target that exists, a checkable `done-when:`, an `effort:`) and writing a blocking note rather than a guess when one is missing. A fork the intake did not pre-rule goes into the card's `blocked:` field, which the operator clears with a one-line edit. **Predecessor workflow (2026-04-22 to 2026-08, retired):** user adds raw note to the task list → heartbeat classifies (has-default / needs-intent / out-of-scope), checking `HEARTBEAT_REJECTIONS.md` first for prior attempts → **has-default** → build in sandbox (worktree or staging folder) → append review entry to `HEARTBEAT_REVIEWS.md`; **needs-intent** → post question with best-guess default embedded; **out-of-scope** → post scope-only question. User integrates a review entry (merge PR / delete staging) or rejects (heartbeat appends ADR block to `HEARTBEAT_REJECTIONS.md` on next cycle). Shared primitives live in `/scripts/heartbeat/`. The successor keeps the good half of that design (a queue, a review gate, sandboxed work, a rejection memory) and drops the two parts that failed, discovery and the unattended schedule. ```mermaid sequenceDiagram actor User participant Pad as board.md scratchpad participant Card as board.md card participant AQ as agent-queue drain
(on demand, in session) User->>Pad: Dump raw note Note over Card: Triage: note → card
(next action + owner) User->>Card: Delegate it (intake interview) Note over Card: delegate: queued
done-when / write-scope / rulings Note over AQ: Operator runs the drain AQ->>Card: Read queued cards, check template floor AQ->>Card: Unruled fork → write into blocked: User->>Card: Clear blocked: with the answer AQ->>Card: Finish the work, mark status: done ``` A worked example of the current card store lives in [`samples/board/`](samples/board/); the retired predecessor's coordination files are preserved in [`samples/tasks/`](samples/tasks/). ### Command Shortcuts A verbal-shortcut table in the workspace CLAUDE.md maps common user phrases to exact destination files so the agent doesn't have to guess as the folder structure grows. Covers things like "add to tasks", "add to food", "add to pantry", "book me…", "expense this", etc. Rules: - When a user phrase matches the table, go directly to the target — no clarifying question. - A new shortcut emerges? Agent asks once, then adds it to the table so the shortcut works next time. - Genuine ambiguity between two targets → ask rather than guess. --- ## 11. File protection / safety **Hook-blocked patterns** (PreToolUse, blocks `Edit`/`Write`, case-insensitive path match): - `.env*` — environment variables and secrets - `credentials*`, `secrets*` — API keys - Lock files (language dependency locks) - A small number of agent-core files (heartbeat operational doc, heartbeat rejection ADR log, a personal-health profile) - Google OAuth client + token files - Financial result workbooks - Bank transaction CSV records - Settings files, MCP config, the security-hook scripts dir, and the scheduled-tasks dir — **security-envelope self-modification protection** (Sentinel, 2026-06-16): the agent can't edit its own settings, MCP wiring, hooks, or schedules. **`permissions.deny` floor (Sentinel, 2026-06-16 — always applies, including under `bypassPermissions`):** the protected / envelope / secret Edit/Write patterns above, plus catastrophic shell deletes re-homed from `autoMode.hard_deny` (recursive deletes of a filesystem root / home / workspace root; force-push; push to main/master; history rewrite). This silent floor is what makes a zero-prompt posture safe — irreversible / outward / self-modifying actions are blocked in code with no prompt, while everything reversible flows through. The PostToolUse Sentinel monitor records what the floor lets through. See §2 *Sentinel*. Residual: an in-process file write (a script's own `open()`) bypasses the shell + Edit/Write layers — detection-only, backstopped by the encrypted backup. **Encrypted backup:** S3-compatible object storage via `restic` — client-side encryption, provider only ever sees ciphertext. Repo password + storage credentials retrieved from the password-manager CLI at runtime (no plaintext secrets in any script or synced file). Dedup + incremental + granular file-level restore. Retention: 7 daily + 4 weekly + 6 monthly. ### Credential store — password manager (canonical) A commercial password manager is the authoritative store for every credential touched by this workspace. No plaintext credentials live in files, ever — not in `.env` (those are machine-local runtime configs and hook-protected), not in scripts, not in CLAUDE.md, not in CONTEXT.md, not in this file. **Vault structure:** folders mirror the services-registry categories (personal and project-scoped). **Master password + account recovery code:** stored offline in a physically secured location, separate from any digital copy. Recovery code regenerated annually or on suspected exposure. **Index:** the services registry is the plaintext index — every service entry points at a password-manager item by name. When an agent needs a credential, it surfaces the item name; the user retrieves it manually. **Audit integration:** the weekly audit scans the services registry and flags missing 2FA, stale rotations (>12 months), unresolved placeholders, and new `.env` keys not recorded in the registry. --- ## 12. Project layout The workspace hosts a handful of parallel projects (a software product, a set of personal-admin folders, health records, creative-writing and non-fiction book projects, a professional-services workstream, a few ongoing personal-assistant experiments). Each is kept in its own folder with its own `CLAUDE.md`, `CONTEXT.md`, and — where relevant — a `PLAN.md`. Only a subset have role bindings today; the rest run on the main thread or generic subagents until their workflows stabilise. One of those experiments — **OS-level isolation of the heartbeat agent** — reached a runnable state (Phases 0–5 + Phase 6 setup complete) before the agent it isolated was retired in 2026-08, so the observation window never ran to completion. The design is recorded here because the sidecar shape is what generalises, not the agent it was built for. Sidecar architecture: heartbeat container on a Docker `internal: true` bridge network has zero internet egress; an `anthropic-proxy` sidecar straddles that internal network plus a separate egress network and forwards `/v1/*` requests to the model API after injecting the real auth header (so the heartbeat container's env never holds a credential). The heartbeat sees only `ANTHROPIC_BASE_URL=http://anthropic-proxy:8788` and a placeholder token. RO mounts on the workspace + narrow per-file RW on a handful of named coordination files + RW on a `/sandbox` directory for staging. tini PID 1 + pinned model-CLI version + BuildKit cache mounts in the Dockerfile. Phase 6 is a 1–2 week observation window with explicit entry/exit criteria; Phase 7 (extracting the primitives into a reusable container-agent library) is intentionally deferred until a second concrete instance appears (a future scoring/brief-pipeline agent in the software product, or containerisation of a second scheduled task). Supporting folders: - `roles/` — canonical persona library - `scripts/` — launcher `.bat` files + backup PowerShell + the Gmail Automation Stack helpers + the board engine + the retired heartbeat primitives - `containers/` — Dockerfiles + compose files + mount validators + boundary probes for any containerised agent - `board/` — the canonical card store + its generated view - `tasks/` — task coordination layer + one-off implementation plans - `Reference/` — reference material (services registry, email-rules registry, shared docs) --- ## 13. Where things live (quick reference) | Thing | Path | |---|---| | Workspace meta-architecture (this file, in the source workspace) | `/META_ARCHITECTURE.md` | | Workspace rolling changelog (split out 2026-04-21) | `/CHANGELOG.md` | | Workspace working context | `/CLAUDE.md` | | Global working context | `/.claude/CLAUDE.md` | | Roles library | `/roles/` | | Project role bindings | `/.claude/agents/` | | Project entity context | `/CONTEXT.md` | | Project plans | `/PLAN.md` (for multi-phase projects) | | Launchers + bootstrap + backup | `/scripts/` | | Scheduled tasks | `/.claude/scheduled-tasks/` | | Global hooks + settings | `/.claude/settings.json` | | Workspace permissions (local) | `/.claude/settings.local.json` | | Workspace custom agents | `/.claude/agents/` (audit, researcher, and the retired heartbeat definition) | | Workspace custom skills | `/.claude/skills/` | | Workspace path-scoped rules | `/.claude/rules/` | | Memory | `/.claude/projects//memory/` | | Memory episodes (one-off events) | `/.claude/projects//memory/episodes/` | | Memory lint script | `/scripts/memory_lint.py` | | Memory consolidation task | `/.claude/scheduled-tasks/consolidate-memory/SKILL.md` | | Scheduled-task wrapper (OS scheduler → SKILL.md) | `/scripts/run-scheduled-skill.ps1` | | Scheduled-task run logs | `/tasks/scheduled-logs/_.log` | | Task coordination | `/tasks/` | | Canonical card store (+ its generated view) | `/board/board.md` → `board.html` | | Per-task token log (feeds the `/metrics` page) | `/scripts/_state/task_token_log.jsonl` | | Older completed reviews (archive) | `/tasks/todo-archive.md` | | Closed question blocks (archive) | `/tasks/answered/To Do Questions.md` | | Heartbeat-PR-agent primitives, retired (classifier, rejection grep, sandbox creator, review-queue depth, idle observations, model-API proxy, host-side reviewer, observation analytics) | `/scripts/heartbeat/` | | Heartbeat review queue, retired (built work awaiting user integration) | `/tasks/HEARTBEAT_REVIEWS.md` | | Heartbeat rejection log, retired (ADR-style durable memory) | `/tasks/HEARTBEAT_REJECTIONS.md` | | Containerised heartbeat, retired — image + compose + mount validator + boundary probes | `/containers/heartbeat/` | | Observation runbook convention (status / next action / logbook / rollback) | `/OBSERVATION.md` (one runbook per long-running observation window) | | Google OAuth creds + tokens (hook-protected) | `/.claude/google-auth/` | | Encrypted backup | S3-compatible object storage, via `restic`; credentials pulled from the password-manager CLI | | Services registry | `/Reference/services-registry.md` | | Email rules registry | `/Reference/email-rules.md` | | Research briefs (preserved `researcher`-role outputs with source grades + appendices) | `/Reference/Research/` (README + `YYYY-MM-DD_.md` files) | | Credentials | password-manager vault (NOT in any file) | --- ## 14. Source attribution — patterns this workspace draws on This workspace's tooling is not invented from scratch; most load-bearing patterns trace back to public industry practice. Cited here so contributors and future-maintainers can return to the underlying source when tuning a tool — don't reinvent. **Audit system** (full bibliography: [`ATTRIBUTION.md` § Audit-system patterns](ATTRIBUTION.md)) - *Continual holistic fitness function* pattern — Ford, Parsons, Kua, Sadalage, *Building Evolutionary Architectures* (O'Reilly, 2nd ed. 2023). The weekly multi-phase audit fits this taxonomy. ArchUnit / NetArchTest / jQAssistant are concrete code-level implementations of the same idea. - *Scorecard per catalog entry* — [Backstage Soundcheck](https://backstage.spotify.com/plugins/soundcheck/) (Spotify). Phase 2 per-project checks. - *Drift detection* — Terraform plan, [driftctl](https://github.com/snyk/driftctl), AWS Config Rules. Phase 2.5a bloat checks. - *Tiered automated-vs-human evidence collection* — [Vanta](https://www.vanta.com/products/soc-2), [Drata](https://drata.com/compliance) SOC2-automation platforms. Tier 1/2/3 auto-apply mirrors their automated-vs-human-review distinction. - *Atomic security checks* — [OpenSSF Scorecard](https://scorecard.dev/). Phase 2.6 security. Deliberately NO numeric score emitted (Goodhart entry below). - *Dead-man's-switch* — [Healthchecks.io](https://healthchecks.io/) + Pont, *Patterns for Time-Triggered Embedded Systems* (2002). Self-hosted implementation in `samples/scripts/security/check_task_freshness.py`. - *Alert fatigue mitigation* — "Alert Fatigue in Security Operations Centres," ACM Computing Surveys 2025 ([DOI:10.1145/3723158](https://dl.acm.org/doi/10.1145/3723158)). Drives the finding-ledger + adaptive-weighting design. - *No numeric audit score* — Goodhart's Law (Charles Goodhart, 1975); David Manheim on metric gaming. A self-improving audit emitting its own score would optimise for the score, losing the ability to surface unanticipated findings. - *Two-auditor pattern* — financial-auditing convention. Implemented as `audit-second-opinion`. - *Memory drift vs staleness* — [arxiv:2603.10062](https://arxiv.org/pdf/2602.22406) (March 2026); [A-MEM](https://arxiv.org/abs/2502.12110) (Zettelkasten-style re-indexing); [Letta](https://docs.letta.com/) (production MemGPT). - *Compliance test injections* — SOC2/security-testing practice. Implemented as `samples/tests/audit_canaries/` with Phase 0 verification. **Skills + agent design** - *CSO-style auto-routing descriptions* — derived from [obra/superpowers](https://github.com/obra/superpowers) and refined through the routing-audit Phase 2.8. - *verify-completion / systematic-debugging skill patterns* — also from obra/superpowers. - *Rationalization Tables in roles* — pattern adapted from obra/superpowers + adversarial role-pressure-testing practice. - *Context-save / context-restore checkpoints* — adapted from [garrytan/gstack](https://github.com/garrytan/gstack). - *Health-dashboard composite scoring rubric* — also adapted from gstack. **Heartbeat architecture** - *Classify-then-act flow with rejection-log ADRs* — adapted from Architecture Decision Records (Michael Nygard) and analogous to Dependabot's ignored-versions list. **Writing-style audit** - Em-dash density rule + burned-word lexicon — Kobak, Gonzalez-Marquez et al. (2024), "Delving into ChatGPT usage in academic writing through excess vocabulary," [arXiv:2406.07016](https://arxiv.org/html/2406.07016v1); Plagiarism Today (2025); Wikipedia's "Signs of AI writing." Rules in `samples/.claude/rules/writing-style.md`. ## 15. Maintenance This file is the source of truth for the *meta* shape of the workspace. Update it when: - A new persona/role is added or removed - A new project gets role bindings (or an existing one loses them) - A new launcher script, scheduled task, hook, or MCP server is added - The memory taxonomy changes - A protected-file pattern is added to the safety hook - A new top-level project folder is created **Do not** put project-specific application architecture here — that belongs in the project's own architecture doc. --- ## 16. Planned future upgrades Drawn from the live task list and implementation-plan file as of 2026-04-19. Items already shipped are not listed. ### AI / workspace upgrades - **Bittorrent integration** — scope TBD (media server stack / public-domain ebook fetcher / general download manager) - **Home integration** — scope TBD (Home Assistant or direct smart-home device integration; potential tie-in with health data — sleep-room temp, morning light) - **Job scanner** — scope TBD (career scanner across major boards, grants/RFP scanner, or similar) - **`PreCompact` hook** — add to selected project settings to prevent loss of in-flight state during long tasks - **1-hour prompt-cache TTL** — `promptCacheTtl: "1h"` in the user settings file (the launcher-script env var only ever reached terminal sessions, never the desktop app); pair it with the orient keep-alive loop for gaps longer than the hour ### Containerisation — sandbox for external-facing agents Any agent that interacts with the open web — browser automation, web scraping, telephony integrations, retailer checkouts — is being moved behind a container boundary. The goal is security isolation of risky operations, **not** reproducibility; interactive Claude Code dev sessions continue to run on the host unchanged. **Pattern:** - Shared `agent-sandbox` base image (browser + agent runtime + minimal tooling), with per-project `docker-compose.yml` layered on top. - Agent and the browser it drives are co-located in the same container so automated traffic stays internal. - Credentials passed in at runtime via the password-manager CLI; never baked into the image. - Per-project persisted browser profile — store logins survive between runs, and the agent looks like a real user rather than a headless bot (sidesteps most storefront bot-detection). **Blast radius:** the container cannot see `.env` files, the personal finance folder, the credential-manager state, or any unrelated project directories. Only what the compose file explicitly mounts is reachable from inside. **Rollout:** - The shopping agent is the pilot — its `PLAN.md` already includes a "Phase 1b — container isolation" block. - The appointments agent and reselling pipeline inherit the same pattern when their next phases activate. - Status: plan drafted, Docker installable on host, not yet implemented. ### Personal projects (scaffolded, awaiting build-out) - **Shopping agent** — Phase 1b: container isolation (sandbox for browser automation); Phase 2-4: add additional retailers + pantry awareness (agent proactively suggests based on household consumption). Currently blocked on a prepaid-card setup by the user. - **Appointments agent** — Phase 1: online booking via browser automation (now unblocked by Google Calendar); Phase 2: phone calling via a hosted voice clone + telephony provider; Phase 3: proactive scheduling (agent books recurring checkups) - **Reselling pipeline** — Phase 2: agent-executed listing and payment acceptance; Phase 3: sales analytics + listing optimisation ### Health - **Fitness log** — structure decision pending (spreadsheet mirroring existing nutrition tracker / smartwatch sync / voice-channel freeform log) - **Health-device rollout** — BP monitor → smartwatch → smart scale → CGM, staggered ### Structural / quality - **Python unit tests for admin scripts** — cover categorisation + workbook update + extract scripts for regression protection on financial data - **Path-scoped rules for the personal-finance folder** — bank-code conventions, FY conventions, xlsx write guards - **Extend PreToolUse hook to cover more health data** — pathology, medication, immunisations folders; tracking workbooks --- *Last verified against the repo structure on **2026-08-27**. Flag drift via an Issue or correct in a PR.* ============================================================================== SOURCE: EVALUATION.md URL: https://github.com/jimy-r/agent-workspace-architecture/blob/main/EVALUATION.md ============================================================================== # Evaluation: knowing whether a workspace change helped A workspace that keeps adding skills, rules and always-loaded directives needs a way to tell which ones actually help. Without one, the surface only grows: every addition sounds reasonable at the moment it is proposed, nothing is ever removed, and the accumulated bloat suppresses the capability the additions were meant to raise. This is the method used here. It is deliberately unfashionable in three places, and those three are the whole contribution. ## The instrument A golden set of frozen cases, each replayed headless several times, reported as a pass-rate with variance, and trended over time. Every case is one already-burned failure mode. Not a hypothetical, not a capability probe. A thing that went wrong, was recorded, and is now a regression test. Each case carries a `source` field naming the failure record it was frozen from, so the suite is the failure library turned executable. That constraint does most of the work. A case you can point at a real incident is a case you can defend keeping. A case invented to look thorough is a case nobody can decide to delete. ## Three deliberate choices ### 1. Deterministic checks only, never a judge Checks are regex, exact-value and path-existence. There is no LLM grading anywhere in the harness. The reason is narrow and it matters: **this instrument exists to detect regression over time.** A judge introduces its own variance between runs, so a drop in the score becomes ambiguous. Did the workspace get worse, or did the judge? For a trend line, that ambiguity is fatal. A deterministic check drops only when behaviour actually changed. This is not a claim that judges are useless. A judge is the right tool for open-ended quality where no deterministic check exists, and there is a place for one in a decision harness. It is the wrong tool for a regression trend, and conflating the two is common. The check types that carry the most weight here, in rough order: | Check | Catches | |---|---| | `must_cite_path` | An answer about system state given without a concrete file path | | `no_fabricated_path` | A confidently-asserted path that does not exist on disk | | `uncertainty_tag` | A knowable-unknown answered with a number instead of "cannot be known" | | `must_not_contain` | A specific fabrication shape, expressed as a pattern | | `must_contain` / `exact_value` | The correct answer, where one exists | The first three are the ones worth stealing. They test *process* rather than *answer*, which is what makes them survive a model upgrade. ### 2. A pass-rate with variance, never a single number Every case runs K times (default 5) and the suite reports a rate plus standard deviation. LLM output is stochastic. A single run comparing before and after is noise presented as signal, and it will confidently tell you a change helped when it did nothing. Any measurement certifying a change here runs K≥3 and reports the spread. The practical consequence is that a small improvement is often not measurable, and the honest response is to say so rather than shipping the change on a one-run delta. ### 3. Five fixture classes, and two of them are the point Three passing positive cases prove very little. The value is in the classes that catch a check which looks right and is not. | Class | What it proves | |---|---| | **Positive** | The behaviour happens when it should | | **Negative** | The check fires when the behaviour is absent | | **Lucky-correct negative** | Right answer, wrong process. Catches a check that passes on the answer while the reasoning was unsound | | **Outside-scope** | The check does *not* fire on an unrelated case. Catches a check that passes everything | | **Allowed boundary** | The edge case that should pass, so the check is not over-tight | The lucky-correct negative and the outside-scope case are the two most often skipped and the two that actually validate the instrument. A check that has never been shown to fail on anything is not a check. Every case also carries a `dry_fixture`: a model answer that should pass every check. This lets the checks themselves be validated for free, with no model calls, before the case joins the suite. ## What it costs Real friction, and worth stating plainly. Authoring is slow. A case needs a self-contained prompt, deterministic checks, a dry fixture, and a walk through the five classes. Fifteen minutes each is optimistic. K=5 replay is five times the tokens of a single pass, which puts the full suite in the range where you run it at audit time rather than per change. Deterministic checks cannot see quality. They catch fabrication, missing citation and false certainty. They say nothing about whether an answer was *good*, and a suite that stays green while the work degrades is a real possibility. This measures a floor, not a ceiling. And the suite decays. Cases frozen from failures a model no longer makes become tests that always pass, which cost tokens and prove nothing. They need pruning on the same discipline as anything else here. ## How it connects to the rest This is one half of a pair. The other is [`PATTERNS.md` #11](PATTERNS.md), which requires every capability scaffold to register a falsifiable hypothesis and a review date. The suite is what the review reads. Without the instrument, the review date arrives and the decision falls back to whether the scaffold still sounds like a good idea, which is how surfaces grow forever. It also sits behind [`PATTERNS.md` #10](PATTERNS.md): a self-edit gate certifies a change to the instructions the way a correctness signal certifies a change to the capability layer. Both refuse to adopt on the strength of how good the change sounds. **One failure worth carrying.** An all-zero record once appeared in the trend store and read as a total regression. It was a real run whose every call had failed on a dead credential. The fix was structural rather than a re-run: the harness now counts call outcomes and self-marks invalid runs, and the trend check filters on that dedicated field. **An outage has to be distinguishable from a regression by the record itself**, or the instrument will eventually lie to you in the most alarming direction available. ## Where it lives - [`samples/scripts/reasoning_golden_run.py`](samples/scripts/reasoning_golden_run.py) carries the harness: case loading, K-replay, the check evaluators, the history record, and a free self-test - [`samples/tests/reasoning_golden/README.md`](samples/tests/reasoning_golden/README.md) is the authoring guide, including the full case schema - [`samples/tests/reasoning_golden/cases/`](samples/tests/reasoning_golden/cases/) holds three representative cases, one per transferable check family The corpus itself is not published. Cases encode workspace-specific paths and internal failures, so the method transfers and the corpus does not. Build your own from your own recorded failures. That is the point of the `source` field, and a suite inherited from someone else's incidents tests the wrong things. ============================================================================== SOURCE: ADOPTION.md URL: https://github.com/jimy-r/agent-workspace-architecture/blob/main/ADOPTION.md ============================================================================== # Adopting this pattern A 5-step walkthrough for setting up a similar **agent workspace**, instantiated in Claude Code. Each step is independent: you don't have to do all of them, and the order below is just the path of least resistance. The Claude-Code-specific file conventions (`CLAUDE.md`, `.claude/skills/`, MCP config) are the substrate; if you're on a different agent runtime, the architectural decisions still translate even though the file shapes won't. The full architecture is described in [META_ARCHITECTURE.md](META_ARCHITECTURE.md); this file is the "where to actually start" complement. ## Prerequisites - [Claude Code](https://claude.com/claude-code) installed and authenticated - A working directory for your workspace (e.g. `~/workspace/`) --- ## Step 1: Write a workspace `CLAUDE.md` `CLAUDE.md` is the always-loaded context Claude reads when a session opens in your working directory. Keep it short; every line costs tokens on every turn. **Minimum viable:** start with [`samples/CLAUDE.md.example`](samples/CLAUDE.md.example). Trim it to the principles you actually hold, then add a "Command shortcuts" table as you notice yourself saying the same phrase twice. **Why it matters:** this is the foundation. Every other layer below assumes `CLAUDE.md` is loaded: roles bind against what it declares, hooks rely on paths it specifies, a delegated drain reads its conventions. See: [Claude Code memory docs](https://docs.claude.com/en/docs/claude-code/memory). --- ## Step 2: Add one canonical role Create a `roles/` directory with one role file per expert persona you'd invoke. Start with one that matches your first real project: `security-auditor` for security reviews, `bookkeeper` for transaction categorisation, `developmental-editor` for long-form writing. **Minimum viable:** copy [`samples/roles/_template.md`](samples/roles/_template.md). Fill in Identity, Directives, Constraints. Add a Rationalization Table after you notice the role caving to pressure ("fine, skip the test this time"). **Why it matters:** roles are the reuse unit. A `security-auditor` that lives in `roles/` can be composed with five different projects' `CONTEXT.md` files and behave consistently. Without role extraction, security review becomes five near-identical 500-line prompts that drift from each other. See: [Claude Code subagents docs](https://docs.claude.com/en/docs/claude-code/sub-agents). --- ## Step 3: Wire a hook Hooks are Claude Code's automation layer; they run shell commands on tool events. Start with a `PreToolUse` hook that protects sensitive files from accidental modification. **Minimum viable:** drop [`samples/.claude/settings.example.json`](samples/.claude/settings.example.json) into `~/.claude/settings.json` and write a one-file `protect-files.py` that exits `2` (which blocks the tool call) when a blocked path is targeted. Typical blocklist: `.env*`, `credentials*`, `secrets*`, your financial result files, any medical data. **Why it matters:** this is the cheapest insurance in the whole system. A ten-line hook prevents an agent that's gone sideways from rewriting your `.env` or deleting financial records. It won't catch a determined misbehaviour, but it catches the overwhelming majority of accidental damage. See: [Claude Code hooks docs](https://docs.claude.com/en/docs/claude-code/hooks). --- ## Step 4: Build a task board with an explicit delegation queue One markdown file holds every outstanding thing as a `###` card. `status` and `area` are fields rather than sections, so moving a card is a one-line edit. Give each card a literal next action and an honest owner, and the file answers "what is actually on me" in one read. Then add delegation. A card is agent work only when you mark it, which is the whole authorization surface. **Minimum viable:** three pieces, no code required. 1. A cards file. Copy [`samples/board/board.example.md`](samples/board/board.example.md) and keep the field format. 2. A marker. Put `delegate: queued` on a card once you have answered four questions about it in writing: what does done look like, which folders may be written, what constrains the approach, and how should the one or two foreseeable decision points be ruled. 3. A drain you run yourself. Open a session and work the queued cards. When one hits a fork you did not pre-rule, write the question into that card's `blocked:` field and move on. See [`samples/board/agent-queue.SKILL.example.md`](samples/board/agent-queue.SKILL.example.md) for the full intake and drain procedure, and [`samples/board/README.md`](samples/board/README.md) for the card schema. **Why it matters:** it removes the guesswork about what an agent is allowed to pick up, and it puts the context in the card at the moment you have it in your head. Questions land where you already look, so nothing waits behind a channel you never open. **The predecessor, studied not recommended.** A scheduled "heartbeat" that reads the task list every two hours and asks clarifying questions is the classic starter, and this workspace ran one for months before retiring it. It fails in two places. The agent has to infer intent from lines written for a human reader, so it asks a lot, and its questions go to a file nobody opens. And an unattended runtime dies quietly, which here meant roughly five weeks of dark runs behind an expired credential. The design is preserved in [samples/tasks/](samples/tasks/) if you want the classifier and rejection-log ideas, both of which hold up wherever the mandate is already unambiguous. --- ## Step 5: Write your first custom skill Skills are invokable capabilities accessed via `/`. Each skill is a directory under `.claude/skills/` with a `SKILL.md` that has a CSO-style description; the description tells the loader when to invoke. **Minimum viable:** copy [`samples/.claude/skills/orient/SKILL.md`](samples/.claude/skills/orient/SKILL.md); it briefs a new session on the workspace state in under 300 words. Adapt the file set it reads to match your own `tasks/` layout. **Why it matters:** skills are where you codify your own recurring workflows. Once `/orient` exists, the first 5 minutes of every session gets reliable and terse instead of exploratory. See: [Claude Code skills docs](https://docs.claude.com/en/docs/claude-code/skills). --- ## What to layer on next Once the five basics above work, the harder-to-adopt parts start paying off: - **Role binding composition.** A role (`roles/security-auditor.md`) plus a project's `CONTEXT.md` makes a project-scoped subagent invoked via `@project-security`. See [`samples/example-project/.claude/agents/example-security.md`](samples/example-project/.claude/agents/example-security.md). - **A rendered board view.** A local server that re-renders the cards file on every request and writes edits straight back to it, so no browser buffer ever holds canonical state. Add it once reading the raw markdown gets tiring. See [`samples/board/README.md`](samples/board/README.md). - **Typed memory files** (`user` / `feedback` / `project` / `reference`) indexed by a `MEMORY.md`, for persistence across sessions. - **MCP servers** for external capabilities: browser automation, calendar, mail. - **Container sandboxing** for agents that touch the open web (security isolation, not reproducibility). Adopt these when you feel the friction they solve, not before. The pattern only stays useful if each piece earns its keep. --- *Last verified against the repo structure on **2026-08-27**. Flag drift via an Issue or correct in a PR.* ============================================================================== SOURCE: WORKFLOW.md URL: https://github.com/jimy-r/agent-workspace-architecture/blob/main/WORKFLOW.md ============================================================================== # WORKFLOW.md: how this workspace actually runs > **Scope:** what a day of using the workspace looks like. Not what's in it; the structural map lives in [META_ARCHITECTURE.md](META_ARCHITECTURE.md). The other docs describe the pieces. This file describes how they're held together in practice: session discipline, entry-point choices, how a task moves from thought to done. It's one person's habits, not a prescription. --- ## Core mental model **Sessions are task-scoped.** One session ≈ one task. The session title matches a bullet on the task list. When the task is done, the session is closed out and archived. **Many sessions run in parallel.** Two or three live at once, nine or ten total in various states: some actively worked on, some paused mid-task, some waiting on a subagent. Max observed: five live, ten total. **The workspace root is the launchpad.** Every session starts at `/` (the top of the personal Claude directory). Projects branch off from there. Sessions don't `cd` into project folders; CLAUDE.md auto-load and MCP scopes handle the rest. **Delegation is trusted, not micromanaged.** Role bindings (`@-`) and subagents aren't invoked by hand. The main thread picks the right subagent and role based on CSO-style descriptions, pulls project `CONTEXT.md` as needed, and routes itself. --- ## The command-dashboard model Work happens primarily in the **Claude desktop app**, treated as a command dashboard. Each live session is a pane. The CLI terminal still exists but has faded; the desktop app's session-juggling UI is what makes the parallelism workable. **Remote terminals.** One to three Remote Control terminal sessions are started in the morning and left running. They're not manually attended; they're there so the phone can dispatch commands into them from elsewhere. **Phone.** The Claude Android app speaks to those remote terminals. Combined with voice dictation, this lets work continue away from the desk: meeting breaks, evenings, walking, bed. **Voice.** OS-level Whisper dictation (a commercial hotkey-to-text tool) replaced an earlier home-built voice channel MCP. Works identically across desktop and phone. **Surfaces retained but unused day-to-day.** A web voice UI and a Discord dispatch channel exist in the architecture but aren't in the current flow. Kept as capability, not as daily tooling. --- ## A day in the workspace ### Morning - Read the morning brief email: appointments, weather, AI news, current task-list state, open questions, overnight activity. - Start one to three Remote Control terminals (for later phone dispatch). - Open the Claude desktop app. Resume an in-progress session or open a fresh one. - On fresh sessions: run the [`orient`](samples/.claude/skills/orient/SKILL.md) skill first, then the [`tasks`](samples/.claude/skills/tasks/SKILL.md) skill. That's the boot sequence. ### During the day - Work flows across two or three live sessions. When one hits a blocker or waits on a subagent, focus shifts to another. Sessions aren't "background"; they're parked and resumed. - New task surfaces → new session, named for the task. Context doesn't get switched inside an existing session; a new session gets spun up. - Items that don't warrant action this minute → dumped into the board's scratchpad, unstructured. A later session turns each note into a card carrying a literal next action and an honest owner, and strikes the note it came from. ### Away from desk - Phone takes over. Claude Android app + voice dictation + Remote Control terminals. Same workflow, different surface. - Can run several hours a day this way; the remote terminals keep state warm between interactions. ### Close-out - Task complete → invoke the [`wrap`](samples/.claude/skills/wrap/SKILL.md) skill. It handles the close-out ritual: close the card, resolve anything linked to it, sweep registries, and log a per-task token record for the metrics page. - Task ongoing → just walk away. Session stays open. Pick up next time. --- ## Lifecycle of a single session 1. **Open.** Launch from the desktop app (or the CLI launcher script). 2. **Name.** The session title matches a task from the task list. 3. **Orient.** `orient` skill for fresh sessions; `tasks` skill to see what's open. 4. **Plan (sometimes).** If the change is non-trivial, the session writes a plan to a todo file before implementing. Threshold is fluid; simple changes skip planning. 5. **Work.** The main thread does the bulk; subagents spawn for research, exploration, parallel analysis. 6. **Wrap or park.** `wrap` skill if done; walk away if continuing tomorrow. 7. **Archive.** Finished sessions are archived rather than deleted. --- ## How tasks flow ### Capture first, action later - Raw notes land in the board's scratchpad. No pressure to structure them at capture time, and a note is never counted as a task until someone decides it is one. - Triage happens in a session. Each note becomes a card with a real next action and an honest owner, or it gets struck as nothing. Judgment sits here, not at capture. - The user works cards as interest dictates, not strict priority or FIFO. The board is a standing offer, not a schedule. ### When the user plans, when the agent plans - Session starts → user states the goal → agent proposes a plan → user OKs or redirects. - No fixed threshold for "needs a plan first". Simple changes just happen. Anything structural gets a plan written before code. ### Delegation, and the colleague that used to do it - A heartbeat agent held this job until August 2026. Every two hours it read the task list, held items until they looked actionable, batched similar questions, and asked for whatever context it judged missing. It did not grind the queue mechanically, and that part worked. - The asking is what broke. Questions went into a separate tracker file and thirteen accumulated unanswered, each one freezing the task behind it. Separately, the schedule itself failed dark for about five weeks behind an expired credential nobody was watching. - Delegation is now explicit. The user marks a card delegated, answers a short intake round on the spot (what done looks like, which folders are writable, what constrains the approach, how the likely forks should be ruled), and a drain skill works the queued cards on demand inside a live session. - A question raised mid-work goes onto the card being worked, in the same view the user already reads. Answering it is a one-line edit. The interplay between queued delegation and in-session planning is still under active tuning. Two cards have been drained this way, both clean, which is early evidence rather than a track record. --- ## Delegation: trust the routing - **Don't invoke `@-` manually.** The CSO-style descriptions on role bindings let the main thread pick the right one automatically. Project `CONTEXT.md` files ride along when relevant. - **Don't manually fan out to `researcher` / `Explore` / `general-purpose`.** The main thread judges when parallel research pays. Over-directing wastes context. - **Trust compaction + memory.** Long sessions compact as they go; the memory system carries durable facts across sessions. The pattern: hand the main thread the task and the context pointers; let it route the rest. --- ## What the workspace gives you that a single long loop does not - **Resumable state across many surfaces.** Desktop, phone, voice, and remote terminals all see the same card store and memory. - **A morning brief that is actually actionable.** Calendar, weather, AI news, open questions, and overnight activity in one place. - **A delegation queue where a question tracker used to sit.** The old claim here was that a background agent narrowing its questions kept work from stalling on vague input. Work stalled anyway, thirteen questions deep, in a file nobody opened. Intent is now taken at the moment of delegation from the person who holds it, and anything the work raises later is written on the card being worked. - **Typed memory.** User profile, feedback, project state, and reference pointers stay separable rather than collapsing into one long note. - **A credentials discipline.** Every API key and token lives in a password manager; files reference item names only, never values. - **Routine skills.** `orient`, `tasks`, `wrap`, `verify-completion`, and `systematic-debugging`, invokable the same way whether you're at desktop, phone, or voice. The cost is real: when the platform ships a new feature, integrating it is work, and the structure can lag what a single autonomous loop gives you out of the box. --- ## The open tension Worth naming: there's a real question about how much structure versus autonomy is optimal. **Structured workspaces** (this approach) buy: predictable entry points, reusable skills and roles, a shared card store, credential safety, a morning brief, an explicit delegation queue. Cost: platform features land in the framework on a lag; integrations are ongoing work. **Autonomous agents** (single loop, long-horizon) buy: pace-of-platform, less integration work, more emergent behaviour. Cost: weaker auditability, fewer guardrails, harder to split work across days and surfaces. One direction being explored is a **hybrid**: a human at the top, a workspace of *executive* agents (structured, reviewable), and more independent autonomous agents below. The executive agents review and prune candidate solutions from the autonomous ones, evolutionary rather than prescriptive. Still a direction of travel, not a delivered design. If that shape resonates, you're the audience for this repo. If it feels over-structured, you'll probably settle closer to a single Claude loop plus a memory file. --- *Last verified against the repo structure on **2026-08-27**.*