feat(aterm)!: give every role bundle its own icon (#1282) #1342

Merged
coilysiren merged 4 commits from aos/claude/hj55-role-icons into main 2026-08-28 22:30:57 +00:00
Owner

Seven .icns land in aterm/icons and embed into the binary, so every .app aterm bundles writes carries its own art instead of the generic macOS icon. Seven tiles distinguishable only by name was most of the reason not to have separate apps at all.

Shape

Mirrors aterm/sounds, which ships pre-rendered samples so a launch needs no synth. A bundle write needs no renderer, no art pipeline, and no network. --icon still overrides every bundle with one shared file, and a role with no committed art keeps the system icon rather than a broken reference, so a roster that turns over cannot write a half-iconed set.

Delivery, which #1282 left open

The art is drawn as the seat creatures in agentic-os-xxx and imported by a script committed there, whose docstring carries the full derivation. #1282 flagged this as a disclosure decision rather than a mechanical one, since this repository is public and the one producing the art is not. Kai made that call.

The 32px question from agent-compose#363

#363 warned that the roster measures 18 of 21 role pairs below the side-by-side JND, and that icons are the worst case because seven tiles sit in one Dock at a fraction of a window size. Verified rather than assumed: all seven separate at 32px by silhouette as well as by hue, which a colour swatch could not. The cost #363 also named still stands, that a committed icon is durable where every other renderer recomputes.

Verification

  • go test ./... green, with new coverage for per-role resolution, the --icon override winning, and a role with no art still generating.
  • A real aterm bundles run into a temp dir: all seven .apps carry CFBundleIconFile, each icon sha-matches its own role file, and iconutil reads one back as a valid icns.

Not included

No docs page. docs/ is at the 40-doc cap and aterm.md sits 27 characters under its size cap, so there is nowhere for the reasoning to land. Filed separately.

Closes #1282

Seven `.icns` land in `aterm/icons` and embed into the binary, so every `.app` `aterm bundles` writes carries its own art instead of the generic macOS icon. Seven tiles distinguishable only by name was most of the reason not to have separate apps at all. ## Shape Mirrors `aterm/sounds`, which ships pre-rendered samples so a launch needs no synth. A bundle write needs no renderer, no art pipeline, and no network. `--icon` still overrides every bundle with one shared file, and a role with no committed art keeps the system icon rather than a broken reference, so a roster that turns over cannot write a half-iconed set. ## Delivery, which #1282 left open The art is drawn as the seat creatures in `agentic-os-xxx` and imported by a script committed there, whose docstring carries the full derivation. #1282 flagged this as a disclosure decision rather than a mechanical one, since this repository is public and the one producing the art is not. Kai made that call. ## The 32px question from agent-compose#363 #363 warned that the roster measures 18 of 21 role pairs below the side-by-side JND, and that icons are the worst case because seven tiles sit in one Dock at a fraction of a window size. Verified rather than assumed: all seven separate at 32px by silhouette as well as by hue, which a colour swatch could not. The cost #363 also named still stands, that a committed icon is durable where every other renderer recomputes. ## Verification - `go test ./...` green, with new coverage for per-role resolution, the `--icon` override winning, and a role with no art still generating. - A real `aterm bundles` run into a temp dir: all seven `.app`s carry `CFBundleIconFile`, each icon sha-matches its own role file, and `iconutil` reads one back as a valid icns. ## Not included No docs page. `docs/` is at the 40-doc cap and `aterm.md` sits 27 characters under its size cap, so there is nowhere for the reasoning to land. Filed separately. Closes #1282
feat(aterm)!: give every role bundle its own icon (#1282)
Some checks failed
ci / aos-eval-tests (pull_request) Successful in 32s
ci / aos-cli-tests (pull_request) Successful in 3m36s
ci / gate (pull_request) Successful in 1m58s
ci / ward-doctor (pull_request) Failing after 5m59s
8849895e77
`aterm bundles` writes one .app per live role and every one of them used the
generic macOS icon, so seven tiles in ~/Applications were distinguishable only
by name, which is most of the reason not to have separate apps at all.

Seven .icns land in `aterm/icons` and embed into the binary, the same shape
`aterm/sounds` already uses to ship pre-rendered samples so a launch needs no
synth. A bundle write now needs no renderer and no network. `--icon` still
overrides every bundle with one shared file, and a role with no art falls back
to the system icon rather than a broken reference, so a roster that turns over
cannot write a half-iconed set.

The art is drawn in agentic-os-xxx from the seat creatures and imported by a
script committed there, whose docstring carries the full derivation. That
settles the delivery question #1282 left open, and it is a disclosure decision
rather than a mechanical one: this repository is public and the one producing
the art is not. Kai made that call.

Icons are the case agent-compose#363 warned about. The roster's colours measure
18 of 21 pairs below the side-by-side JND, and seven tiles sit in one Dock at a
fraction of a window's size, so a palette that is marginal full-window would not
survive at 32 px. Verified rather than assumed: all seven separate at 32 px by
silhouette as well as by hue. The cost #363 also named still stands, that a
committed icon is a durable artifact needing regeneration when a role turns
over, where every other renderer recomputes.

No docs page comes with this, and that is the documentation budget rather than
an oversight: docs/ is at the 40-doc cap and aterm.md is 27 characters under
its size cap, so there is nowhere for the reasoning to land. Filed separately.

Breaking only in what a bundle contains: a regenerated set carries icons where
the previous one carried none, so LaunchServices may need the usual nudge
before Finder redraws them.

Refs: coilyco-flight-deck/agent-compose#363
Closes #1282

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: frontend
Author
Owner

Blocked on #1343, not on anything in this diff.

ci / ward-doctor is red here and on #1340, and green on main in 6s. Neither PR touches .ward, the workflow, or the install script. Every other context passes on both: gate, aos-cli-tests, aos-eval-tests.

Holding rather than merging, because the lane merges on green. Ready the moment #1343 clears, with no rebase needed unless main moves.

Blocked on #1343, not on anything in this diff. `ci / ward-doctor` is red here and on #1340, and green on main in 6s. Neither PR touches `.ward`, the workflow, or the install script. Every other context passes on both: `gate`, `aos-cli-tests`, `aos-eval-tests`. Holding rather than merging, because the lane merges on green. Ready the moment #1343 clears, with no rebase needed unless main moves.
Author
Owner

Correcting my comment above: "every other context passes on both" was wrong for #1340, where gate also failed. Verified per job on the API rather than the status rollup.

This PR's run 28559 is accurate as stated: aos-cli-tests success, aos-eval-tests success, gate success, ward-doctor failure.

Root cause is now known and is not either diff. The runner cannot reliably reach forgejo.coilysiren.me: job 45917 shows a failed image pull, two git checkout connect timeouts at ~134s each, then six fast 404s once connectivity returned. Details on #1343, fix split between #1344 and the runner repair.

Still holding this PR rather than merging.

Correcting my comment above: "every other context passes on both" was wrong for #1340, where `gate` also failed. Verified per job on the API rather than the status rollup. This PR's run 28559 is accurate as stated: `aos-cli-tests` success, `aos-eval-tests` success, `gate` success, `ward-doctor` failure. Root cause is now known and is not either diff. The runner cannot reliably reach `forgejo.coilysiren.me`: job 45917 shows a failed image pull, two git checkout connect timeouts at ~134s each, then six fast 404s once connectivity returned. Details on #1343, fix split between #1344 and the runner repair. Still holding this PR rather than merging.
Merge branch 'main' into aos/claude/hj55-role-icons
All checks were successful
ci / aos-eval-tests (pull_request) Successful in 8s
ci / aos-cli-tests (pull_request) Successful in 31s
ci / gate (pull_request) Successful in 48s
49abb7668f
fix(aterm): follow the role slugs that moved, and stop the list that let them rot
All checks were successful
ci / aos-cli-tests (pull_request) Successful in 25s
ci / aos-eval-tests (pull_request) Successful in 7s
ci / gate (pull_request) Successful in 47s
73a946cac7
Three slugs moved and the icons kept their launch names, so `eval`, `tpm` and
`devrel` art would have been unreachable and those three seats would have
silently fallen back to the system icon. Renamed to `science`, `director` and
`advocate`.

#1354 names two of the three. `devrel` becoming `advocate` is not in it, and
`.agents/roles.kdl` on main is what says so, which is the reason this checks the
source rather than the handover note.

The test that should have caught this is replaced rather than corrected. It
listed the seven slugs by hand, so it moved with the icons and agreed with them
whatever either said. It now parses `roles.kdl` and asserts both directions: a
declared role with no icon falls back silently, and an icon naming no declared
role is dead weight nothing will ever read. Either would have failed loudly
here.

`roles.kdl` rather than the live roster on purpose. The roster is authoritative
at runtime and the installed agent-compose lags a release, so it still serves
the old slugs today. `roles.kdl` is the policy input, lives in this repository,
and is already current.

Unrelated and pre-existing, recorded so it is not read as fallout from this:
`TestLiveDefaultSeatBelongsToItsRole` fails on origin/main as well, with "AOS
has no default agent for role eval", because the installed release predates the
rename. `aterm/overlay_test.go` is also unformatted on main and is left alone.

Refs: #1354

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: frontend
Author
Owner

Slugs corrected, and three moved rather than two

Pushed as 73a946ca, on top of main merged in.

eval.icns    ->  science.icns
tpm.icns     ->  director.icns
devrel.icns  ->  advocate.icns

Unchanged: platform, sysadmin, frontend, gamedev.

devrel becoming advocate is not in #1354, which names only the first two. .agents/roles.kdl on main is what says so, and checking the source rather than the handover note is the only reason it was caught. Left as it was, three seats would have silently fallen back to the system icon, which is exactly the failure mode this feature's fallback is designed to be quiet about.

The installed agent-compose is no help here either: it still resolves eval, tpm and devrel and knows nothing of the new slugs, because the rename has landed in source and not yet in a release.

The test that should have caught it is replaced, not corrected

The old one listed the seven slugs by hand. That list travelled with the icons, so it agreed with them whatever either of them said, which is worth nothing. It now parses roles.kdl and asserts both directions:

  • a declared role with no icon, which is the silent fallback
  • an icon naming no declared role, which is dead weight nothing will ever read

Both would have failed loudly on this rename. Verified against the current tree: seven declared, seven shipped, no missing and no orphans.

roles.kdl rather than the live roster on purpose. The roster is authoritative at runtime, but the installed release lags, so a roster-derived assertion would be red until that catches up. roles.kdl is the policy input, it lives in this repository, and it is current.

Two pre-existing failures, so they are not read as fallout

Neither is caused by this branch and neither is fixed here.

  • TestLiveDefaultSeatBelongsToItsRole fails on origin/main too, with "AOS has no default agent for role eval", because the installed release predates the rename. Checked by running it against origin/main rather than assuming.
  • aterm/overlay_test.go is unformatted on main. Byte-identical here, so fixing it would widen this diff into someone else's file.

Still blocked on #1343 for CI, unchanged by any of this.

## Slugs corrected, and **three** moved rather than two Pushed as `73a946ca`, on top of main merged in. ``` eval.icns -> science.icns tpm.icns -> director.icns devrel.icns -> advocate.icns ``` Unchanged: `platform`, `sysadmin`, `frontend`, `gamedev`. **`devrel` becoming `advocate` is not in #1354**, which names only the first two. `.agents/roles.kdl` on main is what says so, and checking the source rather than the handover note is the only reason it was caught. Left as it was, three seats would have silently fallen back to the system icon, which is exactly the failure mode this feature's fallback is designed to be quiet about. The installed `agent-compose` is no help here either: it still resolves `eval`, `tpm` and `devrel` and knows nothing of the new slugs, because the rename has landed in source and not yet in a release. ## The test that should have caught it is replaced, not corrected The old one listed the seven slugs by hand. That list travelled with the icons, so it agreed with them whatever either of them said, which is worth nothing. It now parses `roles.kdl` and asserts both directions: * a declared role with no icon, which is the silent fallback * an icon naming no declared role, which is dead weight nothing will ever read Both would have failed loudly on this rename. Verified against the current tree: seven declared, seven shipped, no missing and no orphans. `roles.kdl` rather than the live roster on purpose. The roster is authoritative at runtime, but the installed release lags, so a roster-derived assertion would be red until that catches up. `roles.kdl` is the policy input, it lives in this repository, and it is current. ## Two pre-existing failures, so they are not read as fallout Neither is caused by this branch and neither is fixed here. * `TestLiveDefaultSeatBelongsToItsRole` fails on `origin/main` too, with *"AOS has no default agent for role eval"*, because the installed release predates the rename. Checked by running it against `origin/main` rather than assuming. * `aterm/overlay_test.go` is unformatted on `main`. Byte-identical here, so fixing it would widen this diff into someone else's file. Still blocked on #1343 for CI, unchanged by any of this.
coilysiren deleted branch aos/claude/hj55-role-icons 2026-08-28 22:30:58 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
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!1342
No description provided.