feat(aterm): add doctor, one command that answers why no window opened (#1257) #1273

Merged
coilyco-ops merged 1 commit from aos/claude/mu55-doctor into main 2026-08-26 07:43:18 +00:00
Member

Closes #1257. Tier 2 of the #1245 dispatch, unblocked by #1264 landing, so it is unix-only with one terminal and no per-platform branch.

What it looks like on a healthy host

ok    working directory    /Users/kai/projects/coilyco-flight-deck/agentic-os
ok    agent-compose        /opt/homebrew/bin/agent-compose
ok    roster               agent-compose.catalog.v1, 7 role(s), 7 with a launchable native seat
ok    overlay              agent-compose.overlay.v1 schema 1, read for platform claude
ok    identity vocabulary  2 personality(s), every sensory field present
ok    terminal             /Applications/kitty.app/Contents/MacOS/kitty // kitty 0.48.2 created by Kovid Goyal
ok    terminal config      kitty parses its resolved config
ok    font                 the terminal's own default, no font_family configured
ok    aos                  /Users/kai/.local/bin/aos
ok    session shadow       leased, the window gets its own worktree
ok    launch profiles      7 role(s) resolve to a launchable seat

aterm can open a window on this host.

--json emits aterm.doctor.v1. Exit 1 when any check fails, 0 otherwise, so a warning never fails the run.

Both corrections from the issue thread are taken

  • the terminal check runs against the resolved --terminal-bin, never a hardcoded name, so this stops needing an edit each time the terminal turns over
  • launch profiles are checked through aos, which owns them. aterm/launch_profiles.go does not exist and this does not create one, which keeps the no-second-parser property docs/aterm.md sells

The line worth the command

nativeShadowAvailable degrades to an unleased launch silently and correctly, and nothing surfaced that to a person outside --dry-run --json. It is a warning rather than a failure, because an unleased launch still works:

warn  session shadow       unleased, `aos _native-shadow --probe` failed and the window shares this checkout

Same shape for a missing aos: warn, not fail, since aterm launches without it.

Two checks the issue did not spell out

  • identity vocabulary - the sensory fields #1251 declared are what #1255 and #1256 will build from, and a roster that stops shipping one would degrade quietly
  • font - the Sombra baseline names no font_family, so the usual answer is the terminal's default and there is nothing to verify. When a host config does name one, doctor verifies it with fc-list and warns rather than passing when it cannot, since a silent pass is not a check

Test stub change worth reading

stubDeps had one run stub serving both aos _native-shadow --probe and, now, a terminal parsing its own config, so an unleased fixture also failed the config check. Split, and _launch-agent is answered from the fixture roster.

Verification

  • just aterm-fmt, just aterm-lint, just aterm-test clean, pre-commit run --all-files clean
  • new doctor_test.go: healthy host, unleased shadow named as a warning, missing terminal and missing agent-compose each failing with the right exit code, a missing aos degrading rather than failing, a launch profile naming an unlaunchable seat failing with the seat quoted, the human default, and the role positional surviving the new subcommand
  • run for real against agent-compose v2.54.0 and kitty 0.48.2, output above, plus the failure and --json paths

One thing this PR ran into and did not fix

docs/ is at 40 of 40 files and docs/aterm.md is at 7,995 of 8,000 chars. This doctor gets one sentence there rather than the walkthrough a subsystem command earns, and fitting even that meant re-trimming prose added earlier in this batch. #1255, #1256, and #1258 each want a page there is no slot for. I measured the merge candidates and no two related docs fit inside one 8,000-char file, so this is a decision rather than a defect and I did not restructure the folder to route around it. Filed separately.

Closes #1257. Tier 2 of the #1245 dispatch, unblocked by #1264 landing, so it is unix-only with one terminal and no per-platform branch. ## What it looks like on a healthy host ``` ok working directory /Users/kai/projects/coilyco-flight-deck/agentic-os ok agent-compose /opt/homebrew/bin/agent-compose ok roster agent-compose.catalog.v1, 7 role(s), 7 with a launchable native seat ok overlay agent-compose.overlay.v1 schema 1, read for platform claude ok identity vocabulary 2 personality(s), every sensory field present ok terminal /Applications/kitty.app/Contents/MacOS/kitty // kitty 0.48.2 created by Kovid Goyal ok terminal config kitty parses its resolved config ok font the terminal's own default, no font_family configured ok aos /Users/kai/.local/bin/aos ok session shadow leased, the window gets its own worktree ok launch profiles 7 role(s) resolve to a launchable seat aterm can open a window on this host. ``` `--json` emits `aterm.doctor.v1`. Exit 1 when any check fails, 0 otherwise, so a warning never fails the run. ## Both corrections from the issue thread are taken * the terminal check runs against the **resolved `--terminal-bin`**, never a hardcoded name, so this stops needing an edit each time the terminal turns over * launch profiles are checked **through `aos`**, which owns them. `aterm/launch_profiles.go` does not exist and this does not create one, which keeps the no-second-parser property `docs/aterm.md` sells ## The line worth the command `nativeShadowAvailable` degrades to an unleased launch silently and correctly, and nothing surfaced that to a person outside `--dry-run --json`. It is a warning rather than a failure, because an unleased launch still works: ``` warn session shadow unleased, `aos _native-shadow --probe` failed and the window shares this checkout ``` Same shape for a missing `aos`: warn, not fail, since aterm launches without it. ## Two checks the issue did not spell out * **identity vocabulary** - the sensory fields #1251 declared are what #1255 and #1256 will build from, and a roster that stops shipping one would degrade quietly * **font** - the Sombra baseline names no `font_family`, so the usual answer is the terminal's default and there is nothing to verify. When a host config does name one, doctor verifies it with `fc-list` and **warns rather than passing** when it cannot, since a silent pass is not a check ## Test stub change worth reading `stubDeps` had one `run` stub serving both `aos _native-shadow --probe` and, now, a terminal parsing its own config, so an unleased fixture also failed the config check. Split, and `_launch-agent` is answered from the fixture roster. ## Verification * `just aterm-fmt`, `just aterm-lint`, `just aterm-test` clean, `pre-commit run --all-files` clean * new `doctor_test.go`: healthy host, unleased shadow named as a warning, missing terminal and missing agent-compose each failing with the right exit code, a missing `aos` degrading rather than failing, a launch profile naming an unlaunchable seat failing with the seat quoted, the human default, and the role positional surviving the new subcommand * run for real against `agent-compose v2.54.0` and kitty 0.48.2, output above, plus the failure and `--json` paths ## One thing this PR ran into and did not fix `docs/` is at 40 of 40 files and `docs/aterm.md` is at 7,995 of 8,000 chars. This doctor gets one sentence there rather than the walkthrough a subsystem command earns, and fitting even that meant re-trimming prose added earlier in this batch. #1255, #1256, and #1258 each want a page there is no slot for. I measured the merge candidates and no two related docs fit inside one 8,000-char file, so this is a decision rather than a defect and I did not restructure the folder to route around it. Filed separately.
feat(aterm): add doctor, one command that answers why no window opened (#1257)
All checks were successful
ci / ward-doctor (pull_request) Successful in 7s
ci / aos-eval-tests (pull_request) Successful in 19s
ci / aos-cli-tests (pull_request) Successful in 30s
ci / gate (pull_request) Successful in 52s
b120558d78
Every other AOS surface has a doctor and aterm did not, so a launch that
produced nothing sent the operator probing the chain by hand.

`aterm doctor` checks the working directory, agent-compose and both contracts
it serves, the sensory vocabulary the identity card needs, the resolved
--terminal-bin and whether it parses its own config, the configured font, aos,
and every role's launch profile resolving to a seat that role can launch. It
exits 1 on a broken link and 0 otherwise, so a warning never fails it, and
--json emits aterm.doctor.v1.

The highest-value line is the shadow. nativeShadowAvailable degrades to an
unleased launch silently and correctly, and nothing surfaced that to a person
outside the dry-run plan JSON.

Both corrections from the issue thread are taken: the terminal check is against
the resolved --terminal-bin rather than a hardcoded name, so it survives the
next terminal turnover, and launch profiles are checked through `aos`, which
owns them, rather than through an aterm-side parser that does not exist.
Unix-only, since #1264 dropped the Windows build.

The shared test stub now answers `_launch-agent` and separates the shadow probe
from a terminal parsing its own config, which the one `run` stub conflated.

Closes #1257

Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Agent-Role: platform
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-flight-deck/agentic-os!1273
No description provided.