Repository navigation
The failure notifier is unreachable on a first install #12
Description
What happens
On a first install, any daemon that fails before install.sh reaches its last section cannot reach the failure notifier:
macarchy-auto-appearance.service: Triggering OnFailure= dependencies.
macarchy-auto-appearance.service: Failed to enqueue OnFailure= job, ignoring:
Unit macarchy-failed@macarchy-auto-appearance.service.service not found.
The template that would have answered is installed at install.sh:425, in "Wiring the machine's own health report" — the second-to-last section. The three units that name it in OnFailure=%n are installed and started far earlier:
| Unit | Named in | Installed by |
|---|---|---|
macarchy-auto-appearance.service |
macarchy-core/systemd/…:8 |
install.sh ~line 250 |
macarchy-bar-contrast.service |
macarchy-core/systemd/…:16 |
install.sh ~line 250 |
macarchy-touchbar.service |
macarchy-touchbar/systemd/…:14 |
install.sh ~line 260 |
doctor.sh:82 reports ok failure notifier throughout, because it only tests that ~/.config/systemd/user/macarchy-failed@.service exists — never that a unit naming it can reach it. So the check is green precisely when the notifier is useless.
The consequence is the one the notifier exists to prevent. From macarchy-touchbar's own README rationale: a dead bar is invisible — it looks like a bar with nothing on it. The notifier is what turns that into a toast. On a first install it is not there, and a Touch Bar daemon that exhausts StartLimitBurst=10 dies in silence.
Second and later runs are fine — the template is on disk by then. That is why it survived: the maintainer's laptop and CI's second install.sh pass both have it.
What should happen
The notifier template is on disk before any unit that names it is installed, so the very first failure is announced. And doctor.sh reports the notifier as ok only when it is genuinely reachable.
Reproduction
- On a clean aarch64 machine (or
tests/clean-machine.sh, one pass only), runinstall.sh. - Make
macarchy-auto-appearancefail — a machine without coordinates does it by itself. journalctl --user -u macarchy-auto-appearance.service→Failed to enqueue OnFailure= job … not found../doctor.sh→ok failure notifier.
Seen in CI run 33925266524, journals quoted in #9's evidence comment.
Area
install
🧠 Brainstorm
Problem / context
install.sh is written as a sequence of converging steps, and its ordering rationale is stated inline wherever it matters (macos-dynamic-wallpaper is "ordered before the themes on purpose"; the legacy migration documents at length why working copies are renamed before anything is removed). The notifier's placement is the one that was never reasoned about: it sits with macarchy-doctor.service under "Wiring the machine's own health report" because both are health-reporting units, which is a tidy grouping and the wrong constraint.
This is the same defect shape #10 just fixed twice: an artifact installed after the thing that consumes it. There it was the theme's wallpapers landing after the unit that reads them; here it is the notifier template landing after the units that name it. Worth noting because it suggests the ordering constraint is worth stating explicitly rather than rediscovering per artifact.
No ADRs in this repo — nothing to check against (docs/adr/ absent; the profile records none).
Approaches
A. Move the template install to the top, before the repo loop. Three lines move. The template is a static file in this repo, depends on nothing, and needs no omarchy — there is no reason for it to be late. Smallest possible diff; fixes the cause.
B. Have each component repo ship its own notifier template. Removes the cross-repo ordering dependency entirely, but duplicates one file across three repos and makes macarchy-install no longer the owner of the health-report story. Trades a one-line ordering fix for a synchronisation problem.
C. Only strengthen the doctor check, leave the ordering alone. The doctor would report the truth, which is an improvement — but it would report a MISS on every first install for a condition the installer could simply have avoided. Fixing the diagnosis without fixing the disease.
Recommendation
A, plus the doctor half of C — they answer different questions. A makes the notifier reachable; the strengthened check makes the doctor able to notice if it ever stops being reachable, which is what would have caught this in the first place. B is rejected: one file, one owner.
📋 Spec
Goal
The notifier template is installed before any unit that names it in OnFailure=, and doctor.sh can tell a reachable notifier from a merely present file.
Scope
install.sh— move themacarchy-failed@.serviceinstall ahead of the component installs.doctor.sh— thefailure notifiercheck verifies reachability, not just existence.
Non-goals
- Changing which units declare
OnFailure=, or the notifier's own behaviour. - Moving
macarchy-doctor.service; only the template has an ordering constraint.
Design
flowchart TD
A[sanity + packages] --> B[install macarchy-failed@.service]
B --> C[repo clone loop]
C --> D[macarchy-core: auto-appearance, bar-contrast]
D --> E[macarchy-touchbar]
E --> F[…]
F --> G[macarchy-doctor.service stays here]
The template is a static file in this repo's systemd/. It needs no clone, no omarchy, no session — so it can be installed immediately after the sanity checks, before anything that could fail while naming it.
The doctor's check becomes: for each unit on disk that declares OnFailure=macarchy-failed@…, the template it resolves to must exist. systemctl --user show -p CanStart on an instance is not usable here (an instance of a template is always startable in principle); reading the OnFailure= declarations and checking the template file is the honest test, and it is what a human would do.
Edge cases
- No unit declares
OnFailure=at all → the check has nothing to verify, and says so rather than passing silently. - A unit names a different notifier template → reported by name, not assumed to be ours.
- The template exists but
systemd --userhas not reloaded → out of scope;install.shalready runsdaemon-reload.
Assumptions
macarchy-doctor.servicehas no ordering constraint of its own — it is enabled, not started, at install time. Unverified; if it turns out to need late placement, only the template moves, which is what this spec says anyway.
Acceptance criteria
- On a one-pass clean install,
journalctl --user -u macarchy-auto-appearance.servicecontains noFailed to enqueue OnFailure= jobline. install.shinstallsmacarchy-failed@.servicebefore the first component install;grep -nshows its line number below the sanity section and above the repo loop.doctor.shreports MISS when a unit on disk declaresOnFailure=macarchy-failed@…and the template is absent.doctor.shstill reports ok on a correctly installed machine, and names what it verified.- Two
install.shpasses remain idempotent — the template install converges, it does not re-copy noisily.
Testing decisions
Seams under test: doctor.sh's MISS/ok output lines, and install.sh's ordering as read by grep -n — the same text-level seams tests/test_doctor_noop_signals.sh and tests/test_doctor_touchbar_modules.sh already assert through.
Prior art: tests/test_doctor_noop_signals.sh — temp HOME, PATH stubs, a fixture the stub replays.
A good test here: a temp HOME holding one unit file that declares OnFailure=macarchy-failed@%n.service, with and without the template beside it, asserting the check flips.
Out of scope
- Any change to
macarchy-core's ormacarchy-touchbar's unit files. - Making the notifier itself more informative.
🛠️ Implementation plan
For agentic workers: execute this plan task-by-task with
implement-issue(subagent-per-task for broad plans, inline for small ones). Steps use checkbox (- [ ]) syntax for tracking.
Goal: the notifier is reachable from the first failure, and the doctor can tell reachable from merely present.
Architecture: plain bash — install.sh, doctor.sh, tests/*.sh; suites are hermetic (temp HOME + PATH stubs) and CI runs tests/test_*.sh plus bash -n.
Tech stack: bash 5, systemd user units, GitHub Actions on ubuntu-24.04-arm.
Seams under test: doctor.sh's MISS/ok output lines, and install.sh's ordering as read by grep -n.
Global constraints
- Hermetic suites only: temp
HOME, PATH stubs, no compositor, no Apple hardware. CI globstests/test_*.sh— a new suite must match it. bash -nmust pass oninstall.sh,boot.sh,doctor.shandtests/*.sh.- Never silence a check a real laptop would legitimately report; a
skipstates its reason and is gated on a condition false on the laptop. - Conventional Commit PR title decides the release bump; commits below use
fix. - Commit trailer:
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>.
Task 1: install the notifier template before anything can name it
Files: modify install.sh; test tests/test_notifier_order.sh (new).
Interfaces: macarchy-failed@.service lands in ~/.config/systemd/user/ before the repo loop and before any component install. macarchy-doctor.service stays where it is.
- Step 1: Write the failing test in
tests/test_notifier_order.sh(seam:grep -noverinstall.sh) — assert themacarchy-failed@.serviceinstall line number is below the sanity section and above the repo clone loop. - Step 2: Run
bash tests/test_notifier_order.sh→ FAIL (it currently sits at ~425). - Step 3: Move only the
macarchy-failed@.servicehalf ofinstall.sh:425up to just after the packages step, with a comment naming the three units that declareOnFailure=and why late is wrong. Leavemacarchy-doctor.servicein place. - Step 4: Re-run → PASS, and
bash -n install.shclean. - Step 5: Commit:
fix(install): install the failure notifier before the units that name it.
Task 2: the doctor checks reachability, not just presence
Files: modify doctor.sh; test tests/test_notifier_order.sh (extend).
Interfaces: for every unit file in ~/.config/systemd/user/ declaring OnFailure=macarchy-failed@…, the named template must exist; the check names what it verified.
- Step 1: Write the failing case (seam:
doctor.sh's output lines) — a tempHOMEwith one unit declaringOnFailure=macarchy-failed@%n.serviceand no template beside it; assertMISS. - Step 2: Add the mirror case — same unit with the template present; assert
ok, and that the line says how many declarations it verified. - Step 3: Add the empty case — no unit declares
OnFailure=at all; assert it says so rather than passing silently. - Step 4: Run → FAIL (the check is still a bare
[[ -e ]]). - Step 5: Replace
doctor.sh:82-83with the reachability check. - Step 6: Re-run the whole suite and
bash -n→ PASS. - Step 7: Confirm
clean machineis still green (0 missing) on the PR. - Step 8: Commit:
fix(doctor): verify the failure notifier is reachable, not just present.
Activity
fix(install): make the failure notifier reachable from the first fail…