feat(aterm): generate one macOS .app launcher per live role #1275

Merged
coilyco-ops merged 4 commits from aos/claude/rc44 into main 2026-08-26 08:00:06 +00:00
Owner

aterm bundles writes one .app per live role into ~/Applications, so a role opens from Spotlight, the Dock, or Finder with no terminal to type in. Each bundle wraps aterm <role>, so the launcher it opens is unchanged.

Why the wrapper is generated

A Finder launch starts on a login PATH carrying neither aos, agent-compose, nor kitty, so generation resolves all four binaries once and bakes them into the wrapper through the env vars aterm already reads. The wrapper exits as soon as aterm detaches the window, so LSUIElement keeps it from flickering a Dock tile of its own, and a launch that fails before a window opens reports in an osascript alert rather than vanishing.

Safety

Roles come from the live roster, the same read the launcher and its completion use, so no second list of roles exists to go stale. A bundle whose role has left the roster is reported rather than deleted, an app this command did not write is never overwritten, and every target is checked before any is written so a refusal cannot half-regenerate the set.

Docs

docs/aterm.md sat at exactly its 120-line cap and docs/*.md at exactly the 40-doc cap, so neither a new section nor a new page could land. A prose pass folds the composer's three sub-headings into the bold lead-ins the aterm half already uses and merges related paragraphs, which fits the new section with margin and drops no rule.

Verification

  • just aterm-test green, ten new tests covering the launcher, plist, stale detection, and the overwrite guard.
  • pre-commit run --all-files green.
  • A generated launcher run under env -i PATH=/usr/bin:/bin:/usr/sbin:/sbin reaches aterm with the right arguments, and a failing one hands its stderr to the alert.
  • plutil -lint clean on all seven plists.

Icons are deliberately out of scope: --icon <file.icns> sets one shared icon, and per-role icons derived from each role's favorite color are follow-up work.

`aterm bundles` writes one `.app` per live role into `~/Applications`, so a role opens from Spotlight, the Dock, or Finder with no terminal to type in. Each bundle wraps `aterm <role>`, so the launcher it opens is unchanged. ## Why the wrapper is generated A Finder launch starts on a login PATH carrying neither `aos`, `agent-compose`, nor kitty, so generation resolves all four binaries once and bakes them into the wrapper through the env vars `aterm` already reads. The wrapper exits as soon as `aterm` detaches the window, so `LSUIElement` keeps it from flickering a Dock tile of its own, and a launch that fails before a window opens reports in an `osascript` alert rather than vanishing. ## Safety Roles come from the live roster, the same read the launcher and its completion use, so no second list of roles exists to go stale. A bundle whose role has left the roster is reported rather than deleted, an app this command did not write is never overwritten, and every target is checked before any is written so a refusal cannot half-regenerate the set. ## Docs `docs/aterm.md` sat at exactly its 120-line cap and `docs/*.md` at exactly the 40-doc cap, so neither a new section nor a new page could land. A prose pass folds the composer's three sub-headings into the bold lead-ins the aterm half already uses and merges related paragraphs, which fits the new section with margin and drops no rule. ## Verification - `just aterm-test` green, ten new tests covering the launcher, plist, stale detection, and the overwrite guard. - `pre-commit run --all-files` green. - A generated launcher run under `env -i PATH=/usr/bin:/bin:/usr/sbin:/sbin` reaches `aterm` with the right arguments, and a failing one hands its stderr to the alert. - `plutil -lint` clean on all seven plists. Icons are deliberately out of scope: `--icon <file.icns>` sets one shared icon, and per-role icons derived from each role's favorite color are follow-up work.
feat(aterm): generate one macOS .app launcher per live role
All checks were successful
ci / ward-doctor (pull_request) Successful in 7s
ci / aos-eval-tests (pull_request) Successful in 7s
ci / aos-cli-tests (pull_request) Successful in 26s
ci / gate (pull_request) Successful in 55s
fa91f2cb8c
`aterm bundles` writes a `.app` per live role into ~/Applications, so a
role opens from Spotlight or the Dock with no terminal to type in. Each
bundle wraps `aterm <role>`, so the launcher it opens is unchanged.

A Finder launch starts on a login PATH carrying neither aos,
agent-compose, nor kitty, so generation resolves all four binaries once
and bakes them into the wrapper through the env vars aterm already
reads. The wrapper exits as soon as aterm detaches the window, so
LSUIElement keeps it from flickering a Dock tile of its own, and a
launch that fails before a window opens reports in an osascript alert
rather than vanishing.

Roles come from the live roster, the same read the launcher and its
completion use, so no second list of roles exists to go stale. A bundle
whose role has left the roster is reported rather than deleted, an app
this command did not write is never overwritten, and every target is
checked before any is written so a refusal cannot half-regenerate the
set.

docs/aterm.md sat at exactly its 120-line cap, and docs/*.md at exactly
the 40-doc cap, so neither a new section nor a new page could land. A
prose pass folds the composer's three sub-headings into the bold
lead-ins the aterm half already uses and merges related paragraphs,
which fits the new section with margin and drops no rule.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Agent-Role: platform
Merge origin/main into the .app bundle branch
All checks were successful
ci / ward-doctor (pull_request) Successful in 6s
ci / aos-eval-tests (pull_request) Successful in 18s
ci / aos-cli-tests (pull_request) Successful in 28s
ci / gate (pull_request) Successful in 49s
e06907794b
main landed `aterm doctor`, split exit codes, and a `--dry-run` that
renders for a person with `--dry-run --json` behind it. `aterm bundles`
now follows all three: it joins the doctor command rather than replacing
it, classifies its own refusals through withExit, and splits its dry run
the same way instead of always printing JSON.

main also rewrote docs/aterm.md to one paragraph per line, which left it
at 7995 of its 8000-char cap, so the bundles section needed room again.
Two paragraphs recounting retired scripts went, since the live rationale
each carried is already stated below them: "no fork" under Discovery,
and the SessionStart banner in dev-base-agent-identity.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Agent-Role: platform
docs: split the status-line composer onto the page that bakes it
All checks were successful
ci / ward-doctor (pull_request) Successful in 9s
ci / aos-eval-tests (pull_request) Successful in 20s
ci / aos-cli-tests (pull_request) Successful in 25s
ci / gate (pull_request) Successful in 50s
dev-base-pr / build (pull_request) Successful in 1m55s
8dacafdbcd
docs/aterm.md carried two unrelated features and had hit both caps twice
from two directions, once in this branch and once when main rewrote it.
The composer half was always misfiled: dev-base bakes the
managed-settings.json that points statusLine at it, and
dev-base-agent-identity.md already documented that wiring, the
20-container.sh provider, and the SessionStart banner beside it.

So the composer moves there rather than onto a new page, which the
40-doc cap blocks and which the validator refuses as trading one
violation for another. The move drops a duplicate: both pages told the
retired agent-name.sh story, and the identity page tells it better.
Its two self-links resolve to prose now that the target is the page
itself, and the composer's inbound links follow it.

aterm.md is now 52 of its 120 lines, so the bundles section returns at
full length with its example block.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Agent-Role: platform
Merge origin/main, which drew the identity card
All checks were successful
ci / ward-doctor (pull_request) Successful in 9s
ci / aos-cli-tests (pull_request) Successful in 31s
ci / aos-eval-tests (pull_request) Successful in 50s
dev-base-pr / build (pull_request) Successful in 52s
ci / gate (pull_request) Successful in 57s
e3c9a3ab23
main's card work reshaped plan.go, render.go, and session.go. The
bundles command sits beside doctor as before and needed no change, so
the only conflict was the FEATURES line both sides had edited: it keeps
main's card sentence and this branch's .app launcher clause.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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!1275
No description provided.