docs+fix: refresh /docs to match current container layout, fix BuildRunner filters

Audit pass over docs/ + adjacent code following the cial-* → core/platform/app
layout consolidation.

Bug fix:
- core/back BuildRunner ALL_FILTERS referenced @cial/core-back and
  @cial/core-front, which no longer exist (the packages are @cial/back +
  @cial/front). Self-edit deploys with scope=all would have silently
  skipped those packages. Filters corrected.

Docs aligned with reality:
- docs/README.md       — promotes file-structure.md to the start-here entry.
- architecture/dev-tenant.md  — full rewrite: paths now /cial/* throughout,
  documents the read-only :ro overlay of /cial/core, the new
  --config.confirm-modules-purge=false install flag, the symlink dance for
  project skills, and the agent's cwd=/cial + HOME=/cial/data/home setup.
- architecture/deploy-pipeline.md  — package-name fix for ALL_FILTERS.
- architecture/core-vs-platform.md — package-name fix for the build list.
- ops/supervisor.md     — drops stale "added in Phase 7" annotation.
- ops/deploy-logs.md    — example log line uses @cial/back.
- self-edit/recipes.md  — protocol path and dependency chain naming.
- design/self-edit-unrestricted.md — banner clarifying it's the original
  design record (pre-rename) so an agent doesn't follow stale paths from it.

Tiny code touch:
- core/edge/src/supervisor.dev.ts — comment on CIAL_MONOREPO_ROOT no longer
  contradicts itself ("not /cial" → "the bind-mounted repo at /cial").

Build verified: turbo run build for @cial/back still passes (cache miss
re-executed cleanly with the updated runner.ts).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Eliot M 2026-04-29 13:17:19 +00:00
parent 341c6dd728
commit a0c9973412
10 changed files with 60 additions and 32 deletions

View file

@ -51,8 +51,8 @@ const ALL_FILTERS = [
'--filter', '@cial/protocol',
'--filter', '@cial/sdk',
'--filter', '@cial/core-ui',
'--filter', '@cial/core-back',
'--filter', '@cial/core-front',
'--filter', '@cial/back',
'--filter', '@cial/front',
'--filter', '@cial/edge',
'--filter', '@cial/platform-back',
'--filter', '@cial/platform-front',

View file

@ -80,7 +80,7 @@ const SERVICES: ServiceSpec[] = [
CLAUDE_HOME: `${STATE_DIR}/home`,
// Tell core-back where to find the supervisor socket.
CIAL_SUPERVISOR_SOCK: SOCKET_PATH,
// Workspace is the dev monorepo root (bind-mounted), not /cial.
// Workspace root inside the container — the bind-mounted repo at /cial.
CIAL_MONOREPO_ROOT: ROOT,
CIAL_PLATFORM_ROOT: `${ROOT}/platform`,
},

View file

@ -3,6 +3,7 @@
Index for everything an agent (or human) might need to know to safely edit Cial from inside its own dev container.
## Start here
- **`file-structure.md`** — canonical map of `/cial/{core,platform,app,docs,.claude,data}` inside the running container. **Read this first.**
- **`architecture/overview.md`** — the 3-layer model (core / platform / app), what runs in the tenant container, and the supervisor + edge proxy.
- **`architecture/core-vs-platform.md`** — what the agent can edit by default, and what `--unrestricted` unlocks.
- **`self-edit/unrestricted-mode.md`** — what `pnpm dev:tenant --unrestricted` does and the risks.

View file

@ -24,7 +24,7 @@ What `pnpm dev:tenant --unrestricted` adds:
- Edit any file under `core/`, `core/protocol/`, `core/sdk/`.
- `POST /api/v1/self/deploy` with `scope=auto` builds:
- `@cial/protocol`, `@cial/sdk`, `@cial/core-ui`,
- `@cial/core-back`, `@cial/core-front`,
- `@cial/back`, `@cial/front`,
- `@cial/edge`,
- `@cial/platform-back`, `@cial/platform-front`.
- `POST /api/v1/self/restart` with `scope=auto` bounces every service, including `edge` (which exits the supervisor → Docker restart-policy bounces the container).

View file

@ -57,8 +57,8 @@ const ALL_FILTERS = [
'--filter', '@cial/protocol',
'--filter', '@cial/sdk',
'--filter', '@cial/core-ui',
'--filter', '@cial/core-back',
'--filter', '@cial/core-front',
'--filter', '@cial/back',
'--filter', '@cial/front',
'--filter', '@cial/edge',
'--filter', '@cial/platform-back',
'--filter', '@cial/platform-front',

View file

@ -4,7 +4,7 @@ End-to-end flow when you run `pnpm dev:tenant` (or `pnpm dev:tenant --unrestrict
## Driver
`scripts/dev-tenant.mjs` is a pure Node.js script. On each invocation:
`core/scripts/dev-tenant.mjs` is a pure Node.js script. On each invocation:
1. Parses `--reset` and `--unrestricted` flags.
2. Extracts host Claude Code OAuth credentials:
@ -12,10 +12,11 @@ End-to-end flow when you run `pnpm dev:tenant` (or `pnpm dev:tenant --unrestrict
- Linux: from `~/.claude/.credentials.json`.
3. Builds `cial-tenant-dev:latest` from `core/docker/Dockerfile.dev` (cached on subsequent runs).
4. `docker run` with:
- bind mount `/cial` ← repo root (so source edits hot-reload).
- named volume `cial-dev-tenant-state``/cial/data` (sqlite, claude home).
- named volume `cial-dev-tenant-modules``/cial/node_modules`.
- per-package anonymous volumes shadowing each `node_modules`.
- bind mount `${ROOT}:/cial:delegated` ← repo root (so source edits hot-reload).
- **`${ROOT}/core:/cial/core:ro,delegated`** when `--unrestricted` is NOT passed — read-only overlay enforcing the trust boundary at the FS level.
- named volume `cial-dev-tenant-state``/cial/data` (sqlite, agent home, deploy logs).
- named volume `cial-dev-tenant-modules``/cial/node_modules` (top-level workspace deps).
- per-package anonymous volumes shadowing each `node_modules` (Linux-built per-pkg deps).
- named volume `cial-dev-tenant-pnpm-store``/pnpm-store`.
- port 8080 forwarded to the host.
- host creds bind-mounted read-only at `/run/secrets/claude-credentials.json`.
@ -25,34 +26,53 @@ End-to-end flow when you run `pnpm dev:tenant` (or `pnpm dev:tenant --unrestrict
`core/docker/dev-entrypoint.sh` runs once at container start:
1. Copies host creds → `/cial/data/home/.claude/.credentials.json` (mode 0600).
2. `pnpm install --frozen-lockfile --prefer-offline`.
3. Pre-builds `@cial/protocol` + `@cial/sdk` + `@cial/edge` (these don't have watchers).
4. Execs `node /cial/core/edge/dist/supervisor.dev.js`.
2. Symlinks `${CLAUDE_HOME}/.claude/{skills,agents,commands}``/cial/.claude/{skills,agents,commands}` so the harness picks up project skills.
3. `pnpm install --frozen-lockfile --prefer-offline --config.confirm-modules-purge=false` (the last flag prevents pnpm from stalling on a stale modules volume after a layout change).
4. Pre-builds `@cial/protocol` + `@cial/sdk` + `@cial/edge` (these don't have watchers).
5. Execs `node /cial/core/edge/dist/supervisor.dev.js`.
## Dev supervisor
`core/edge/src/supervisor.dev.ts` spawns five children with watchers:
- `core-back`: `tsx watch src/index.ts` on :4000
- `core-front`: `next dev --turbopack -p 4001`
- `platform-back`: `tsx watch src/index.ts` on :3001
- `platform-front`: `next dev --turbopack -p 3000`
- `edge`: `node dist/edge.js` on :8080 (no watcher — restart container if you edit it)
`core/edge/src/supervisor.dev.ts` spawns five children with watchers, all rooted under `/cial`:
| Child | Command | cwd | Port |
| ---------------- | ------------------------------------ | --------------------- | ---- |
| `core-back` | `tsx watch src/index.ts` | `/cial/core/back` | 4000 |
| `core-front` | `next dev --turbopack -p 4001` | `/cial/core/front` | 4001 |
| `platform-back` | `tsx watch src/index.ts` | `/cial/platform/back` | 3001 |
| `platform-front` | `next dev --turbopack -p 3000` | `/cial/platform/front`| 3000 |
| `edge` | `node dist/edge.js` | `/cial/core/edge` | 8080 |
Each child's stdout/stderr is prefixed with a colored `[name]` tag and merged into a single stream the host's `pnpm dev:tenant` tails.
The dev supervisor also opens the same Unix-socket IPC server as prod (`/run/cial-supervisor.sock`), so the self-edit endpoints work in dev too.
The dev supervisor also opens the same Unix-socket IPC server as prod (`/run/cial-supervisor.sock`), so the self-edit endpoints work in dev too. The agent harness is spawned from `core-back` with:
- `cwd = /cial`
- `HOME = /cial/data/home`
so `/cial:self-edit`, `/cial:build`, `/cial:restart` are discoverable via `${CLAUDE_HOME}/.claude/skills` (symlinked to `/cial/.claude/skills`).
## Volumes (state survives restart)
| Volume | Mount in container | Persists |
| ----------------------------------- | ------------------------------ | --------------------------- |
| `cial-dev-tenant-state` | `/cial/data` | sqlite, claude home, logs |
| `cial-dev-tenant-modules` | `/cial/node_modules` | top-level node_modules |
| `cial-dev-tenant-state` | `/cial/data` | sqlite, agent home, logs |
| `cial-dev-tenant-modules` | `/cial/node_modules` | top-level node_modules |
| `cial-dev-tenant-pnpm-store` | `/pnpm-store` | pnpm cache |
| anonymous (per workspace package) | `/cial/<pkg>/node_modules`| Linux-built per-pkg deps |
| anonymous (per workspace package) | `/cial/<pkg>/node_modules` | Linux-built per-pkg deps |
`pnpm dev:tenant --reset` removes all four named volumes and the `.dev-tenant/` host state dir.
`pnpm dev:tenant --reset` removes all four named volumes and the `.dev-tenant/` host state dir. Use this after a workspace layout change (renames, new packages) if you see "module not found" errors at startup.
## Why the named volumes for node_modules?
Without them, the host's macOS-built `node_modules` would leak into the Linux container through the bind mount, and `pnpm` would try to symlink to a `.pnpm/` store that points at darwin-arm64 binaries. The shadow volumes give the container its own clean Linux deps.
## Trust boundary in dev (`--unrestricted` vs default)
| Surface | Default (`pnpm dev:tenant`) | `--unrestricted` |
| -------------------------------------- | ------------------------------- | ------------------------------ |
| Bind mount of `/cial/core` | `:ro` (read-only) | `:rw` (writable) |
| `BuildRunner` `scope=all` | rejected (`unrestricted_required`) | accepted |
| Supervisor restart for `core-*`/`edge` | rejected | accepted |
The FS-level read-only mount is defense-in-depth — even if a bug let the agent reach the build endpoint with `scope=all`, the underlying `pnpm build` would fail to write into `/cial/core/*/dist`.

View file

@ -1,6 +1,13 @@
# Self-Edit & `--unrestricted` Mode — Design
> Status: design (locked); next step `/sc:implement`.
> **Status: implemented + superseded.** This is the original design record from before the layout consolidation. References to `cial-core/`, `cial-platform/`, `cial-app/`, `scripts/dev-tenant.mjs`, `@cial/core-back`, `@cial/core-front` describe the pre-rename layout. For current paths and package names, see:
>
> - `docs/file-structure.md` — current container layout
> - `docs/architecture/core-vs-platform.md` — current edit boundaries
> - `docs/architecture/dev-tenant.md` — current dev launcher behavior
> - `docs/self-edit/{api,recipes,unrestricted-mode}.md` — current API contract
>
> Kept as a design record. Do not use as a path/name reference.
## Goal

View file

@ -15,7 +15,7 @@ Plain text. Each line is prefixed with `OUT ` (stdout) or `ERR ` (stderr) follow
```
OUT [@cial/protocol] tsc -b
OUT [@cial/protocol] ✓ done in 1.2s
ERR [@cial/core-back] error TS2345: ...
ERR [@cial/back] error TS2345: ...
```
## Tail in real time

View file

@ -72,7 +72,7 @@ export function getRestartableServices(unrestricted: boolean): ReadonlySet<Resta
| ------------------------- | --------------------------------------- | ------------------------------------------ |
| User per child | gosu cial / agent | single root user |
| Children | compiled `node dist/...` | watchers (`tsx watch`, `next dev`) |
| IPC server | yes | yes (added in Phase 7) |
| IPC server | yes | yes |
| Socket mode | 0660 cial:cial | 0666 (single-user dev) |
| Edge restart | `process.exit(0)` → docker restart | same |

View file

@ -58,13 +58,13 @@ curl -sf -X POST http://127.0.0.1:4000/api/v1/self/restart \
## Add a field to a protocol type
```sh
$EDITOR /cial/protocol/src/sessions.ts # add field
$EDITOR /cial/core/back/src/... # use it
$EDITOR /cial/platform/front/src/... # use it
$EDITOR /cial/core/protocol/src/sessions.ts # add field
$EDITOR /cial/core/back/src/... # use it
$EDITOR /cial/platform/front/src/... # use it
curl -sf -X POST http://127.0.0.1:4000/api/v1/self/deploy -d '{}'
```
In `--unrestricted`, the build filter graph picks up `@cial/protocol` first, then anything that depends on it (core-back, core-front, sdk, platform-*). One call rebuilds the chain.
In `--unrestricted`, the build filter graph picks up `@cial/protocol` first, then anything that depends on it (`@cial/back`, `@cial/front`, `@cial/sdk`, `@cial/platform-*`). One call rebuilds the chain.
## Cancel an in-flight build