You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feature: verified Linux install and systemd service lifecycle #945
Status update from the #1076/#1079/#1080 (+follow-up #1107) review cycle, full analysis in authbridge/docs/superpowers/specs/2026-09-16-linux-systemd-lifecycle-design.md:
abctl service status \| start \| stop \| restart accurate in every state — the goos refactor and a fakeSystemctl/fakeLoginctl unit-test harness now exist (Test: Add a fakeSystemctl/fakeLoginctl harness for the Linux service path #1080), matching what macOS already had. Still missing macOS's other half: a real-systemd integration test that drives loadService/controlService/supervisorRunning themselves (not just raw systemctl) against a live session, analogous to TestWaitBootedOut_RealLaunchd.
Untouched:
Fresh install via curl \| sh on a real Linux box, and upgrade-over-existing against a real running unit — both need actual CI infrastructure, likely belongs with test: release smoke test on Linux CI runners #957 (Linux release smoke test) rather than this issue.
No Linux equivalent of launchdUsable()'s clean preflight — explicitly left as an open decision in the design doc, not yet resolved either way.
#1108 addresses the last untouched bullet — "Works under user systemd, and states what happens where systemd is absent" — by adding systemdUsable() (the Linux equivalent of launchdUsable()) and wiring it into serviceInstall's existing preflight. Not checking the box until it merges.
#1108 merged — checking off "Works under user systemd, and states what happens where systemd is absent."
Unrelated but worth flagging here: main has since undergone a large reorg (afb49e9f, "Flatten authbridge/ into the repo root") — authbridge/cmd/abctl/... is now just cmd/abctl/..., authbridge/authlib/ is now core/, etc. Our code landed before that and moved automatically with it, but the design doc's own path references (authbridge/cmd/abctl/cmd_service_platform.go etc.) still say authbridge/... and are now stale in prefix. Not fixing preemptively — flagging so whoever picks up the remaining bullets knows to use the new paths, and can fix the doc's prefixes as part of that work rather than as a separate no-op PR.
Bullet 3's remainder (full serviceUninstall end-to-end: unit file, stamp file, ~/.cortex actually removed) and bullet 6's remainder (a real-systemd integration test that drives loadService/controlService/supervisorRunning themselves, not just raw systemctl — the actual analog of TestWaitBootedOut_RealLaunchd) are the two pieces of actual #945-scoped work still open.
Checked off the upgrade box — now covered end-to-end by the Linux release smoke test (#957, landed in #1288): assert_running_binary_is_current + assert_running_version_is confirm the systemd unit is actually running the newly-installed binary post-upgrade (not a stale process, the exact #1203-class regression this is guarding against), and a config marker confirms the config survives untouched.
Split and corrected the uninstall bullet: the original wording bundled 'no unit file, no ~/.cortex, no modified agent settings' as one guarantee. In practice:
unit file removal + leaving ~/.cortex (config, CA) untouched is done and tested (checksum comparison in the Linux release smoke test) — checked that half.
reverting agent settings is NOT automatic on uninstall. serviceUninstall explicitly tells the user to separately run agentop configure claude-code disable / agentop configure opencode disable — left that half unchecked and split out as its own line, since it's a real gap (or a deliberate scoping decision someone should confirm), not a wording issue. Possibly belongs with feature: agent integration contract — reusable detection, settings patching, and revert #939's settings-revert work.
#1405 adds the arm64 half of "Fresh install via curl | sh, amd64 and arm64" — matrix-izes the Linux release smoke test (#957) to also run on GitHub's ubuntu-24.04-arm runner, exercising the exact same fresh-install/upgrade/uninstall path install.sh's own uname -m detection and release-binaries.yaml's linux/arm64 tarballs already support. Not checking the box until it merges.
Fresh install via curl | sh, amd64 and arm64.#1405 (merged 2026-10-15) runs the Linux release smoke test on both ubuntu-24.04 and ubuntu-24.04-arm. It installs the way curl | sh does: it downloads install.sh and feeds it to sh -s on stdin, so the script's own re-exec bootstrap behaves as it does for a real user. The latest run passed on both arches: https://github.com/rossoctl/cortex/actions/runs/40712931917
Uninstall reverts agent settings automatically. When I split this line out on 2026-10-08, uninstall did not revert anything. That has since changed: agentop uninstall (#1252, merged 2026-10-03) unroutes Claude Code, OpenCode, IBM Bob and the bob shell function before it stops the service, and leaves a key alone if the user has edited it since enable. Its tests cover each agent (TestUninstallUnroutesOpenCode, TestUninstallUnroutesBobAndTheBobShell, TestUninstallLeavesAClaudeCodeKeyChangedSinceEnable, …). I reworded the line to say which command it means, because there are two:
agentop uninstall removes Cortex: agents unrouted, service removed, binaries and PATH lines removed, ~/.cortex kept unless --purge.
agentop service uninstall removes only the service. It warns that Claude Code and OpenCode fail while the proxy is down and names the configure … disable commands, but changes no agent settings. That is deliberate, since it is the command for managing the service, not for removing Cortex.
Still open: agentop service status | start | stop | restart accurate in every state. Checking it on a real host showed it is not:
State
systemd
agentop service status
exit
running
active
Cortex is healthy
0
stopped with service stop
inactive
Cortex is NOT answering … Claude Code and OpenCode will fail
1
crash-looping
activating
Cortex is NOT answering … Claude Code and OpenCode will fail
1
A deliberate stop reads as a failure. I'm opening a PR that makes status ask systemd for the unit's state, and adds the real-systemd test this item has been missing: one that drives agentop service itself through install, status, stop, start, restart, a crash and uninstall, in CI's real-systemd job.
#1529 is the PR for the last unchecked item, "agentop service status | start | stop | restart accurate in every state". It makes status ask systemd why Cortex is silent, so a service stop reports as stopped (exit 0) rather than as a failure. It makes start/restart/install clear systemd's start limit, so they can recover a unit that crash-looped. And it adds TestServiceLifecycle_RealSystemd, which drives agentop service itself through every state against real systemd in CI. It passes on systemd 257 (Debian 13) and 261 (Ubuntu 26.10). I'll check the box once it merges.
The last box is done: #1529 (merged) makes agentop service status report each systemd state accurately: running, stopped by service stop, stopped some other way, restarting after a crash, and given up after a crash loop. start and restart now recover a unit that hit systemd's start limit, and TestServiceLifecycle_RealSystemd drives the whole lifecycle against a real systemd. I verified it on Debian 13 (systemd 257) and Ubuntu (261). One edge case from its review, stopping a unit after systemd gave up on it, is fixed in #1660. The reboot-survival work continues in #964. Closing.
Activity
fix: Address round-2 review findings on the fake-harness PR
Alan-Cha commentedon Sep 23, 2026
Status update from the #1076/#1079/#1080 (+follow-up #1107) review cycle, full analysis in
authbridge/docs/superpowers/specs/2026-09-16-linux-systemd-lifecycle-design.md:Closed:
Re-running install is idempotent—lingerEnabled()'s parsing had zero test coverage; now fully tested including the exact invocation shape (Test: Add a fakeSystemctl/fakeLoginctl harness for the Linux service path #1080, Fix: Follow-up review findings from #1076/#1080 #1107).stoptolerates the proxy's ~15s drain~~ —renderUnitFor("linux", ...)now setsTimeoutStopSec=20explicitly (Fix: Set an explicit TimeoutStopSec on the Linux service unit #1079).Partial, not checking off yet:
unloadService's systemctl/loginctl invocation shape is now fully tested (Test: Add a fakeSystemctl/fakeLoginctl harness for the Linux service path #1080), but nothing drivesserviceUninstallitself end-to-end (unit file removal, stamp file,~/.cortex). Still open.Restart=on-failuregenuinely restarts onkill -9, stays stopped on deliberate stop). The reboot half is out of automated-CI scope (runners don't reboot) and tracked separately in test: verify the installed Cortex service survives reboot on macOS and Linux #964.abctl service status \| start \| stop \| restartaccurate in every state — thegoosrefactor and afakeSystemctl/fakeLoginctlunit-test harness now exist (Test: Add a fakeSystemctl/fakeLoginctl harness for the Linux service path #1080), matching what macOS already had. Still missing macOS's other half: a real-systemd integration test that drivesloadService/controlService/supervisorRunningthemselves (not just rawsystemctl) against a live session, analogous toTestWaitBootedOut_RealLaunchd.Untouched:
curl \| shon a real Linux box, and upgrade-over-existing against a real running unit — both need actual CI infrastructure, likely belongs with test: release smoke test on Linux CI runners #957 (Linux release smoke test) rather than this issue.launchdUsable()'s clean preflight — explicitly left as an open decision in the design doc, not yet resolved either way.Alan-Cha commentedon Sep 23, 2026
#1108 addresses the last untouched bullet — "Works under user systemd, and states what happens where systemd is absent" — by adding
systemdUsable()(the Linux equivalent oflaunchdUsable()) and wiring it intoserviceInstall's existing preflight. Not checking the box until it merges.feat: Add a Linux preflight for abctl service install
Alan-Cha commentedon Sep 29, 2026
#1108 merged — checking off "Works under user systemd, and states what happens where systemd is absent."
Unrelated but worth flagging here:
mainhas since undergone a large reorg (afb49e9f, "Flatten authbridge/ into the repo root") —authbridge/cmd/abctl/...is now justcmd/abctl/...,authbridge/authlib/is nowcore/, etc. Our code landed before that and moved automatically with it, but the design doc's own path references (authbridge/cmd/abctl/cmd_service_platform.goetc.) still sayauthbridge/...and are now stale in prefix. Not fixing preemptively — flagging so whoever picks up the remaining bullets knows to use the new paths, and can fix the doc's prefixes as part of that work rather than as a separate no-op PR.Remaining, for reference:
serviceUninstallend-to-end: unit file, stamp file,~/.cortexactually removed) and bullet 6's remainder (a real-systemd integration test that drivesloadService/controlService/supervisorRunningthemselves, not just rawsystemctl— the actual analog ofTestWaitBootedOut_RealLaunchd) are the two pieces of actual#945-scoped work still open.Alan-Cha commentedon Oct 7, 2026
Checked off the upgrade box — now covered end-to-end by the Linux release smoke test (#957, landed in #1288):
assert_running_binary_is_current+assert_running_version_isconfirm the systemd unit is actually running the newly-installed binary post-upgrade (not a stale process, the exact #1203-class regression this is guarding against), and a config marker confirms the config survives untouched.Alan-Cha commentedon Oct 8, 2026
Split and corrected the uninstall bullet: the original wording bundled 'no unit file, no ~/.cortex, no modified agent settings' as one guarantee. In practice:
serviceUninstallexplicitly tells the user to separately runagentop configure claude-code disable/agentop configure opencode disable— left that half unchecked and split out as its own line, since it's a real gap (or a deliberate scoping decision someone should confirm), not a wording issue. Possibly belongs with feature: agent integration contract — reusable detection, settings patching, and revert #939's settings-revert work.Alan-Cha commentedon Oct 15, 2026
#1405 adds the arm64 half of "Fresh install via
curl | sh, amd64 and arm64" — matrix-izes the Linux release smoke test (#957) to also run on GitHub'subuntu-24.04-armrunner, exercising the exact same fresh-install/upgrade/uninstall pathinstall.sh's ownuname -mdetection andrelease-binaries.yaml'slinux/arm64tarballs already support. Not checking the box until it merges.Alan-Cha commentedon Oct 28, 2026
Checked off three more boxes:
Fresh install via
curl | sh, amd64 and arm64. #1405 (merged 2026-10-15) runs the Linux release smoke test on bothubuntu-24.04andubuntu-24.04-arm. It installs the waycurl | shdoes: it downloadsinstall.shand feeds it tosh -son stdin, so the script's own re-exec bootstrap behaves as it does for a real user. The latest run passed on both arches: https://github.com/rossoctl/cortex/actions/runs/40712931917Uninstall reverts agent settings automatically. When I split this line out on 2026-10-08, uninstall did not revert anything. That has since changed:
agentop uninstall(#1252, merged 2026-10-03) unroutes Claude Code, OpenCode, IBM Bob and the bob shell function before it stops the service, and leaves a key alone if the user has edited it sinceenable. Its tests cover each agent (TestUninstallUnroutesOpenCode,TestUninstallUnroutesBobAndTheBobShell,TestUninstallLeavesAClaudeCodeKeyChangedSinceEnable, …). I reworded the line to say which command it means, because there are two:agentop uninstallremoves Cortex: agents unrouted, service removed, binaries and PATH lines removed,~/.cortexkept unless--purge.agentop service uninstallremoves only the service. It warns that Claude Code and OpenCode fail while the proxy is down and names theconfigure … disablecommands, but changes no agent settings. That is deliberate, since it is the command for managing the service, not for removing Cortex.Service survives reboot and a crash.
Crash: verified against real systemd by Test: Real-systemd integration test for crash recovery, wired into CI #1076 (merged 2026-09-22).
Restart=on-failurerestarts the unit afterkill -9, and a deliberate stop stays stopped.Reboot: verified with
scripts/dev/verify-reboot-lingering.sh(Test: Add a real-reboot verification script for Linux lingering #1409, merged 2026-10-27), 5 reboots per host, on two VMs:cortex.serviceactive (median)On every boot, Cortex came up 22–29 s before the first login. The script compares the two timestamps the host recorded, so the result does not depend on the script's own SSH login. Details are in Test: Add a real-reboot verification script for Linux lingering #1409 and on test: verify the installed Cortex service survives reboot on macOS and Linux #964.
The Debian run also found bug: agentop setup leaves lingering off on Linux without polkit, and the warning does not say why #1521: on a host without polkit,
agentop setupcannot turn lingering on and said so unhelpfully. Fixed by Fix: Say why lingering failed when polkit is missing, and how to fix it #1522 (merged 2026-10-27). The Debian run above had polkit installed.Still open:
agentop service status | start | stop | restartaccurate in every state. Checking it on a real host showed it is not:agentop service statusservice stopA deliberate stop reads as a failure. I'm opening a PR that makes
statusask systemd for the unit's state, and adds the real-systemd test this item has been missing: one that drivesagentop serviceitself through install, status, stop, start, restart, a crash and uninstall, in CI's real-systemd job.Alan-Cha commentedon Oct 28, 2026
#1529 is the PR for the last unchecked item, "
agentop service status | start | stop | restartaccurate in every state". It makesstatusask systemd why Cortex is silent, so aservice stopreports as stopped (exit 0) rather than as a failure. It makesstart/restart/installclear systemd's start limit, so they can recover a unit that crash-looped. And it addsTestServiceLifecycle_RealSystemd, which drivesagentop serviceitself through every state against real systemd in CI. It passes on systemd 257 (Debian 13) and 261 (Ubuntu 26.10). I'll check the box once it merges.Alan-Cha commentedon Nov 4, 2026
The last box is done: #1529 (merged) makes
agentop service statusreport each systemd state accurately: running, stopped byservice stop, stopped some other way, restarting after a crash, and given up after a crash loop.startandrestartnow recover a unit that hit systemd's start limit, andTestServiceLifecycle_RealSystemddrives the whole lifecycle against a real systemd. I verified it on Debian 13 (systemd 257) and Ubuntu (261). One edge case from its review, stopping a unit after systemd gave up on it, is fixed in #1660. The reboot-survival work continues in #964. Closing.