A loop = context + AI + workdir, bound together in a per-loop bwrap sandbox. Every path the agent sees is composed from a few host-side sources. This is the map.
~/.dashscope/config.jsonhost-secrets/<user>/context/knowledge/<docs>/.loopat/claude/CLAUDE.md — L2 doctrine.loopat/claude/skills/ — team skills.loopat/claude/claude.json — team mcp.loopat/sandboxes/<name>/ — toolchain cataloginbox.md — workspace scratchfocus/<name>.md — task treesmemory/MEMORY.md — team memory (deliberate)memory/ — personal mem (SDK auto-recall).loopat/config.json — providers, mounts, shelldefault/ baselinedev/ prod/ test/ … exactly ONE selected per loop/context/chat/<id>/ (ro)/loopat/loop/<id>/workdir/ — git worktree, branch loop/<slug>-<id6>.claude/ — SDK session state.claude/CLAUDE.md ◀ ro-bind L2 from knowledge.claude/skills/ ◀ ro-bind from knowledge/loopat/context/knowledge//loopat/context/notes//loopat/context/personal/memory/ — personal memory.loopat/config.json — per-user config.loopat/vaults/ ◀ tmpfs catalog hidden.loopat/vault/ ◀ overlay selected vault's files/loopat/context/repos/<name>//loopat/context/chat/<id>/$HOME.loopat/vault/.ssh ► $HOME/.ssh/usr /etc /lib /bin /sbin/tmp shared.templates/CLAUDE.mdknowledge/.loopat/claude/CLAUDE.mdworkdir/CLAUDE.mdknowledge/.loopat/claude/skills/claude.json/personal/.loopat/vault/* (one vault only)/context/chat/<id>/ (if spawned from chat)knowledge/, other repos/<x>
loop/workdir/ ▶ repos/<name>/ (merge the loop's branch back)
The one thing to internalize. Two orthogonal axes, picked independently at spawn.
frontend — node, bun, jira-mcp, figma-mcpbackend — go, python, redis-clisre — kubectl, aws-cli, datadog-mcpdefault — shared baselinedev — dev env credentialsprod — prod credentials (you specifically have)| Layer | Source on host | Loaded by | Scope |
|---|---|---|---|
| L1 doctrine | server/templates/CLAUDE.md | system-prompt builder, always-on | sandbox basics, path conventions |
| L2 team | knowledge/.loopat/claude/CLAUDE.md | ro-bind to .claude/CLAUDE.md, SDK loads | workspace conventions |
| L3 project | workdir/CLAUDE.md | SDK loads from cwd | repo-specific conventions |
| L4 runtime | server-computed (id, vault, sandbox, …) | concat into system prompt | per-turn variables |
| skills | knowledge/.loopat/claude/skills/ | ro-bind to .claude/skills/, SDK discovers | callable procedures |
| mcp | knowledge/.loopat/claude/claude.json | passed to SDK at spawn | jira, github, datadog, … |
| personal memory | personal/memory/ | SDK auto-recall (per .claude/settings.json) | your habits, user facts |
| team memory | notes/memory/ | doctrine tells agent to read MEMORY.md | gotchas, conventions |
| chat thread | chat/<tid>/history.jsonl | ro-bind into sandbox if loop spawned from chat | seed conversation |
| credentials | personal/.loopat/vaults/<v>/ | walked & overlay-mounted at .loopat/vault/ | apiKey, ssh, tokens |
| Path | Persistence | Notes |
|---|---|---|
workdir/* | auto-commit on loop/<slug>-<id6> | the loop's actual work product |
notes/inbox.md | auto-commit to team notes git | append-only scratchpad |
notes/<focus>.md | auto-commit | small markdown task trees |
notes/memory/<name>.md + index | auto-commit | agent auto-promotes from personal when topic is workspace-wide |
personal/memory/<name>.md | SDK-managed, auto-commit | private observations |
/vault/* | git-crypt encrypted at commit | rare — credential rotation paths |
knowledge/ | — | NEVER — flows back via distillation |
repos/<x>/ | — | NEVER directly — only via the loop's workdir/ |
| Agent attempts to … | What stops it |
|---|---|
| read another user's secrets | personal/<other-user>/ isn't bound into this sandbox at all |
| read another vault's keys | host-side .loopat/vaults/ is tmpfs'd; only selected vault overlays as /.loopat/vault/ |
| escape via a symlink in the vault | walkVaultFiles checks realpath against personal/<user>/ and refuses targets outside |
| modify team knowledge | knowledge/ is ro-bind; writes return EROFS |
| commit into a sibling repo's mainline | repos are rw but workflow rules + worktree-branch isolation steer commits onto loop/… only |
see the host filesystem outside /loopat | sandbox root is fresh tmpfs; only explicitly-bound paths exist |
Every artifact is a file. State of a loop, vault contents, memory, branch — all ls-able. No "what does the DB say" debugging.
/loopat/loop/<id>/ dies with the loop. /loopat/context/ survives — branch + memory + notes remain.
Sandbox × vault. Same engine powers "alice testing the frontend" and "carol fighting a prod fire" — different cells of the matrix.
Knowledge flows downward (consumed). Writing back to knowledge/ takes a distill loop, not a one-line append. Friction is intentional.
Nothing crosses implicitly. Every path the agent sees is a --bind line in buildBwrapArgs. The host can sleep through misbehavior.
| Concept | File(s) |
|---|---|
sandbox composition (buildBwrapArgs) | server/src/bwrap.ts |
| vault catalog + symlink validation | server/src/vaults.ts |
| loop lifecycle + auto-init | server/src/loops.ts |
| L1 doctrine (bundled) | server/templates/CLAUDE.md |
| memory recall config | server/src/loops.ts (.claude/settings.json per loop) |
| auto-commit on writes | server/src/workspace.ts (vaultWrite) |
| chat → loop spawn | server/src/chat.ts |
| sandbox toolchain spec | server/src/sandboxes.ts, knowledge/.loopat/sandboxes/<name>/ |