Skip to content

feat: panopticon doctor preflight command and fail-fast spawn errors#247

Draft
tildesrc wants to merge 5 commits into
mainfrom
panopticon/doctor-preflight-fail-fast
Draft

feat: panopticon doctor preflight command and fail-fast spawn errors#247
tildesrc wants to merge 5 commits into
mainfrom
panopticon/doctor-preflight-fail-fast

Conversation

@tildesrc

@tildesrc tildesrc commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

panopticon doctor — a preflight self-check for a pip-installed operator host, oriented around the question "can panopticon quickstart and the setup-repo workflow succeed here?"

  • Host prerequisites (hard FAIL; the only thing that sets a non-zero exit) — Docker, git, tmux, and the claude CLI. setup-repo is a shell workflow the session service runs on the host to exec claude setup-token, so quickstart's final step needs claude present; remediation points at https://claude.ai/install.sh.
  • Setup status (non-fatal WARN) — the panopticon-base image, the task service, and per-repo tokens. Before first start none exist yet (the base image is auto-built on first spawn; the service is started by panopticon start; the token is what setup-repo mints), so doctor reports them as a "here's what quickstart will do" checklist rather than red-flagging a machine that simply hasn't run quickstart. A fresh host with the four tools installed exits 0.
  • doctor fetches repos lazily — only after the task service check passes — so the offline-before-first-start case reports cleanly instead of crashing.

Fail-fast spawn errors (defense-in-depth for container tasks):

  • The spawner checks the env-file for a recognisable CLAUDE_CODE_OAUTH_TOKEN/ANTHROPIC_API_KEY before spawning a container task (the shell setup-repo path is exempt), and LocalRunner.spawn() checks the image exists and tmux is available before docker run/tmux. Missing preconditions surface as a legible LifecyclePhase.FAILED detail in the dashboard (referencing panopticon doctor) instead of a silently idle container.

Also wires make doctor (dev convenience) into the Makefile and updates docs/macos-setup.md.

Plan: plan.md

🤖 Generated with Claude Code

Panopticon Agent and others added 2 commits July 8, 2026 20:54
Adds `panopticon doctor` (and `make doctor`) — a preflight self-check
that verifies Docker, tmux, uv, the panopticon-base image, the task
service, and per-repo env-file token presence/shape. Prints OK/WARN/FAIL
with a one-line remediation per check; exits non-zero on any FAIL.

Adds fail-fast spawn errors: the spawner now checks the env-file for a
recognisable auth token (CLAUDE_CODE_OAUTH_TOKEN / ANTHROPIC_API_KEY)
before attempting to spawn, and LocalRunner.spawn() checks that the
target image exists and tmux is available before issuing docker run /
tmux commands. Missing preconditions surface as a legible
LifecyclePhase.FAILED detail in the dashboard (with "run panopticon
doctor") instead of a silently idle container.

Wire-up: macOS setup docs updated to include `make doctor` in the quick
start, and the tmux limitation note now points to the doctor command.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@tildesrc

tildesrc commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Code review — panopticon doctor + fail-fast spawn errors

Verdict: Request changes (one must-fix; otherwise solid). mypy strict is clean and the relevant 98 tests pass on the branch. LLM-free placement, clean command-runner injection, long-form flags, and no token leakage — all good; plan criteria essentially met.

Must-fix

  • panopticon doctor crashes when the task service is down. terminal/__main__.py:47 evaluates client.list_repos() eagerly before any check runs, so an unreachable task service — doctor's headline use case — raises httpx.ConnectError and dies with a traceback instead of printing [FAIL] Task service not reachable … Run: make serve. Fetch repos defensively (e.g. run check_task_service first, then list repos, skipping the per-repo checks with a note when the service is down) and add a test. This path is currently untested — every test_doctor test passes a pre-fetched repo list, so the crash isn't caught.

Nits (non-blocking)

  • Spawner fail-fast checks token presence but not shape (spawner.py _check_env_file_token). A malformed CLAUDE_CODE_OAUTH_TOKEN=garbage passes the pre-spawn check, the container spawns, and the agent 401s — exactly the silent-idle failure mode criterion 2 targets. doctor already has _TOKEN_PATTERNS; consider reusing it in the spawner, or noting the divergence deliberately.
  • Env-file mode check is exact-equality (doctor.py:172, if mode != 0o600) — a stricter 0o400 file is reported FAIL. mode & 0o077 (reject any group/other access) matches the intent better.
  • Minor: env-file line parsing is duplicated across spawner._check_env_file_token and doctor.check_repo_env_file (different packages, so sharing is awkward — acceptable).

Nice work on the test isolation and the actionable remediation messages.

🤖 Automated review via Claude Code

Panopticon Agent and others added 3 commits July 11, 2026 21:12
Reframe `panopticon doctor` for a pip-installed operator (no Makefile/uv/
source checkout):
- Remediations now point at `panopticon build` / `panopticon start`, not
  `make build` / `make serve`.
- Drop the dev-only `uv` check; add a `git` check (the session service clones
  each task's checkout on the host).
- Fix crash when the task service is down: fetch repos lazily via a callback,
  only after the service check passes, so doctor's headline case (service
  offline) reports cleanly instead of raising ConnectError.
- env_file mode check rejects any group/other access (`mode & 0o077`) instead
  of exact `!= 0o600`, so a stricter 0o400 no longer FAILs.
- local_runner spawn-precheck remediation: `make build` -> `panopticon build`.
- Note the spawner's presence-only token check divergence from doctor's shape
  check deliberately (a spawn false-positive fails the whole task).
- Move tests to tests/terminal/test_doctor.py per the new package layout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`panopticon doctor` is meant to run *before* the first `panopticon start`.
At that point the base image isn't built and the task service isn't running
— both by design (the runner auto-builds the image on first spawn; the
service is started by `panopticon start`). Red-FAILing them told a fresh
machine its environment was broken when it was simply not set up yet.

Split the checks:
- Host prerequisites (Docker, git, tmux) — tools you install yourself — stay
  hard FAILs; a missing one is the only thing that sets a non-zero exit.
- Base image and task service become non-fatal WARN heads-up items. A
  fresh-but-ready machine (Docker/git/tmux present) now exits 0 with a "here's
  what happens at first start" list.

Per-repo token checks are unchanged (still run only when the service is up).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Main added `panopticon quickstart` and the `setup-repo` shell workflow (a
`runner_type="shell"` task the session service runs on the host, no container,
to exec `claude setup-token`). Reframe the preflight so its yardstick is "can
quickstart + setup-repo succeed here?":

- Add a host `claude` CLI check (FAIL) — setup-repo execs `claude setup-token`
  on the host, so quickstart's final step needs it. Remediation points at
  https://claude.ai/install.sh.
- Soften the per-repo env-file checks from FAIL to WARN: the token is what
  setup-repo mints and quickstart writes the secrets file at default mode, so a
  missing/loose/absent token is the normal pre-setup state, not a machine fault.
  Enforcement stays at spawn time (spawner FAILs a container task with no token).
- Host prerequisites (Docker, git, tmux, claude) remain the only FAILs / the
  only thing that sets a non-zero exit.

Merge mechanics: main refactored `_spawn` into `_spawn_container`/`_spawn_shell`
(execution router) — moved the env-file fail-fast check into `_spawn_container`
so it applies to container tasks only (shell setup-repo is exempt). Re-applied
the local_runner spawn pre-check call-index shifts and env-file reader injection
onto main's reformatted tests. B904 (`raise ... from`) + ruff format applied.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant