Move the spike workspace from the gitignored tmp/ scratch dir into
scripts/browser-jail-spike/ so Codex (or anyone) can re-run it on
FreeBSD with the keys already configured on the host. Self-contained:
fixtures, CDP renderer, OpenAI-compat harness, scorer, plus the
committed screenshots and ground-truth JSON so the experiment is
reproducible without re-rendering.
Claude Opus 4.7 baseline included in results/ (17/17 PASS at 30 px,
mean 1 px). Pending columns:
- GPT-4o via OPENAI_API_KEY
- GLM-4V via ZAI_API_KEY (pi's existing provider)
- UI-TARS-7B via vLLM if/when an endpoint exists
Path references in VISION-GROUNDING-FINDINGS.md and
BROWSER-JAIL-HANDOFF.md updated to match the new location.
Route remaining sudo call sites through hostd-call.sh / hostd:
- scripts/destroy-jails.sh: bastille stop/destroy via hostd-call.sh
- scripts/docs-sync.cron.sh: nginx reload via service-restart op
- scripts/heartbeat.sh: bastille list via hostd-call.sh
- src/startup-report.ts: drop sudo bastille/pkg fallbacks; tighten
buildStartupReport signature now that hostdData is always supplied
Relies on 537c613 (non-interactive bastille-destroy) so the
yes-pipe in destroy-jails.sh is no longer needed.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Step 5 of system-namespace cutover: complete the env-var removal that
step 4 set up. All consumers now import SERVICE_NAME from
src/platform-identity.ts directly; the deprecated PLATFORM_*
re-exports in src/config.ts are gone.
src/config.ts:
- PLATFORM_ID, PLATFORM_SERVICE_NAME, PLATFORM_RUNTIME_USER exports
removed.
- PLATFORM_RUNTIME_HOME stays (derived from SERVICE_NAME, used by
~10 consumers for path construction).
- Env-var allowlist drops PLATFORM_ID / PLATFORM_SERVICE_NAME /
PLATFORM_RUNTIME_USER / PLATFORM_RUNTIME_HOME entries.
- CONTROLPLANE_AIDER_TMUX_SESSION uses SERVICE_NAME directly.
setup/onboarding.ts:
- writeIdentity() simplified to write only ASSISTANT_NAME (display).
PLATFORM_ID / PLATFORM_SERVICE_NAME / PLATFORM_RUNTIME_USER are no
longer written to .env. Fresh installs have no PLATFORM_* keys.
- Status emission switched from PLATFORM_ID to SERVICE_NAME.
setup/env-audit.ts:
- Audit lists SERVICE_NAME instead of PLATFORM_ID; the env-file
PLATFORM_ID read is gone.
24 source files (src/*.ts, setup/*.ts, scripts/dashboard.ts):
- Bare PLATFORM_ID / PLATFORM_SERVICE_NAME / PLATFORM_RUNTIME_USER
references replaced with SERVICE_NAME.
- Imports rewired: SERVICE_NAME comes from
../{src/}platform-identity.js, not from config.js.
- Imports deduped where the sed sweep produced collisions.
Shell scripts (scripts/bhyve-evidence.sh, glass.sh, inspect-system.sh):
- Hardcoded SERVICE_NAME='clawdie' and SERVICE_USER='clawdie'.
No more grep-the-.env fallbacks; the constants are the source.
Tests (middle path):
- Mechanical fixes (import path, renamed assertion text):
src/hostd/privileged-commands.test.ts, src/startup-report.test.ts,
setup/env-audit.test.ts, setup/install-mode.test.ts.
- Skipped with `// system-namespace:` markers (pinned removed
env-driven override behavior; Codex rewrites once the bootstrap-
config service-user override path lands):
setup/verify.test.ts > 'uses the platform service name for PID candidates'
setup/service.test.ts > 'resolves a platform runtime separately from the tenant'
Test files still containing PLATFORM_* strings in vi.mock contents,
ENV_KEYS arrays, or comments are left untouched — they are test
artifacts that don't affect runtime; mock contents resolve to
'clawdie' which still equals SERVICE_NAME.
tsc clean. 2095 tests pass, 4 skipped, 0 fail.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---
Build: pass | Tests: pass — Tests 2095 passed | 4 skipped (2099)
Codex caught zroot hardcodes in setup/sanoid.ts and setup/db.ts; same
pattern remained in three more shipping locations:
- scripts/backup.ts: jail and shared dataset paths
- src/tenant-registry.ts: default tenant dataset list
- setup/sanoid.ts: npm-global retention candidate
Add zfsPool() helper to maintenance-snapshots.ts (where the analogous
buildHostDbDatasets reads ZFS_POOL) and use it in all three. Operators
running on non-default pools no longer get silently-wrong dataset paths
in backup, tenant provisioning, or sanoid retention.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---
Build: pass | Tests: pass — Tests 2099 passed (2099)
Replace remaining executable-code mevy assumptions with config-derived values. This updates operator messaging, runtime prompts, agent-task role defaults, inspect-system fallbacks, and OpenRouter metadata headers to follow PLATFORM_SERVICE_NAME, PLATFORM_ID, TENANT_ID, and PROJECT_ROOT instead of the live example tenant.
---
Build: pass | Tests: FAIL — Tests 3 failed | 2089 passed (2092)
Round 5 in the handoff doc captures the five agreed adopt-mode
decisions (INSTALL_MODE field, fill-blanks default, identity
mismatch blocks, Telegram identity changes require explicit flag,
fingerprint gate) so they survive into Codex's design doc.
Implementation doc gets an "Adopt Mode (V1.1)" section with the
proposed 4-task split + per-field freeze contract table, plus a
task-4 followup subsection naming the legacy `operators` table
sync gap and the unification plan with Codex's
setup/operator-auth.ts. scripts/set-operator.ts gets a TODO(unify)
header pointing at the same gap.
first-boot.md notes adopt mode is V1.1 and to back up before
reflashing until then.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---
Build: pass | Tests: FAIL — Tests 3 failed | 1972 passed (1975)
Lands task 4 from the ISO first-boot implementation split as a
standalone scripts/set-operator.ts (matches existing scripts/
convention — no clawdie-admin umbrella). Reuses
ensureControlplaneBootstrapOperator() for the Better Auth signUp
path. Prompts password via stdin with echo suppressed; refuses
non-TTY runs; updates OPERATOR_PASSWORD in .env (mode 0600).
First-set only — rotation goes through the dashboard.
Both planning docs updated to drop "notional" references and point
at the real npm run set-operator -- <email> command.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---
Build: pass | Tests: FAIL — Tests 3 failed | 1972 passed (1975)
Reads JSON status files written by scripts/write-test-build-status.sh
so /testreport reflects the last real build/test run instead of model
memory. Missing or stale status degrades to "unknown" with an action
note rather than fabricating success.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---
Build: pass | Tests: pass — Tests 1914 passed (1914)
---
Build: pass | Tests: pass — Tests 1917 passed (1917)
---
Build: pass | Tests: pass — Tests 1921 passed (1921)
Drop the allowedResources field from TenantApplyPlan — it was derived
field-for-field from resourceChecklist already, which was exactly the
"triplicate representation" flagged in the handoff's consolidation list.
Update scripts/tenant-lifecycle.ts to compute the same lists from the
checklist when it prints, and drop the tautological equality assertions
from the test (resourceChecklist is now the single source).
---
Build: pass | Tests: pass — 33 passed (1 file)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Fix the remaining operator-surface drift after the naming cutover. This aligns controlplane defaults around ai.<base>, makes the dashboard use the shared display-date helper and approved controlplane host, reuses the derived code-service hostname in Forgejo config, and fixes local-host syncing so underscore-form tenant jails are no longer skipped.
---
Build: pass | Tests: pass — 67 passed (5 files)
Explain in tenant-apply output when an existing tenant still carries declared non-default state, so operators can distinguish current tenant-specific carryover from the smaller V2 default for new tenants.
---
Build: pass | Tests: pass — 31 passed (1 file)
Reduce duplication in planTenantApply by treating the resource checklist as the canonical resource list, deriving blockers from preflight state, and trimming redundant action-policy payloads.
---
Build: pass | Tests: pass — 31 passed (1 file)
Refine tenant-apply dry runs with per-resource status entries so databases, worker jails, and datasets are reported as explicit future-create candidates instead of only appearing inside summary sections.
---
Build: pass | Tests: pass — 31 passed (1 file)
Turn tenant-apply into a structured preflight contract that marks what already passes in the declarative model, what remains manual, and what still blocks any future automatic host mutation.
---
Build: pass | Tests: pass — 31 passed (1 file)
Introduce a separate tenant-apply contract that describes what a future live apply would be allowed to touch, what prerequisites it would require, and what stays explicitly manual or out of scope.
---
Build: pass | Tests: pass — 28 passed (1 file)
- platform record now accepts internal_domain and internal_base; tenant
internal-domain derivation honors platform.internal_base instead of
hard-coding home.arpa
- validateTenantRecord now rejects a tenant whose internal_domain
collides with the platform internal_domain
- tenant-lifecycle CLI now accepts --internal-domain, --service, and
repeatable --dataset flags; tenant-list now prints
id\\tservice\\tinternal-domain\\tdisplay-name
- writeTenantRegistry preserves YAML comments and key order via the
yaml Document API instead of parse/stringify round-tripping
- platformHostd{SocketPath,PidFile} now use normalizeResourceId
directly so platform-side helpers stop calling normalizeTenantId
Build: pass | Tests: pass — 1783 passed (114 files); two failing
suites (vision.test.ts, controlplane-api.test.ts) are pre-existing
on origin/multitenant and unrelated to this change.
Turn tenant planning into an explicit declarative contract that states which logical resources belong to a tenant and which host-level concerns remain intentionally out of scope.
---
Build: pass | Tests: pass — 20 passed (1 file)
Keep tenants as logical platform identities, preserve human display names while normalizing system ids, and add a dry-run removal path plus stronger registry validation.
---
Build: pass | Tests: pass — 28 passed (3 files)