SOW-0001 Chunk 19: systemd user units + install script#23
Merged
Conversation
Run ai-viewer persistently on a workstation via systemd USER units
(localhost, run-on-login, auto-restart) — no privilege, no remote scope.
- deploy/systemd/ai-viewer-{ingest,serve}.service: USER unit templates
(%h paths, Restart=on-failure/RestartSec=3s, serve After=ingest). The
serve unit documents the intentional start-order behaviour: it may
start before the ingester migrates the schema, CheckSchema exits, and
Restart retries until the DB exists.
- scripts/install-systemd-user.sh (install/uninstall/status): install
always rebuilds from source, copies binaries to ~/.local/bin and units
to the user systemd dir, daemon-reloads, then PRINTS the enable command
for the operator to run — it never enables/starts services itself.
Idempotent uninstall keeps binaries + data. No sudo, no home-path
literals (XDG/$HOME/%h).
- scripts/test/systemd-units-test.sh: directive lint + systemd-analyze
verify (offline), asserting exact ExecStart, Restart, RestartSec, and
the serve After= ordering.
deployment.md updated (install/uninstall/status, update path via the
install script, start-order note). shellcheck + systemd-analyze verify
clean; nothing is installed or started by the build/test.
Wire scripts/test/systemd-units-test.sh into the gates job (guarded on the test existing) so unit-template regressions — a wrong ExecStart, a dropped Restart/RestartSec, a broken After= ordering — are caught in CI, not only on a developer's machine. GitHub ubuntu runners provide systemd-analyze; the test skips the verify step gracefully if absent.
ktsaou
added a commit
that referenced
this pull request
May 30, 2026
…, EndTs
Round-3 fixes completing the partially-fixed exec/patch enrichment and
web_search pairing plus two correctness gaps, all verified against the
real ~/.codex wire shapes.
- exec_command_end exit_code is now authoritative for op status in BOTH
orders: exec-first applies at finalize; output-first emits a correcting
OpFinalized(failed,command_failed) via the finalizedOps lookup (a
non-zero exit no longer leaves a failed command marked completed).
- patch_apply_end is now order-independent (finalizedOps path) and merges
patch_success/patch_status into op Extras; success=false -> failed.
- exec_duration_ms is now emitted (real duration is {secs,nanos} ->
secs*1000 + nanos/1e6).
- web_search pairing uses a per-turn FIFO queue of open searches (oldest
pairs with each web_search_end) so interleaved searches don't mis-pair;
the end event's action object is now decoded and emitted alongside query.
- NativeID is taken from the authoritative session_meta.payload.id (the
UUID parent_thread_id/forked_from_id reference); the filename UUID is
only a fallback.
- old-format turn_context-only sessions now finalize their EOF turn at the
turn's last-activity timestamp (deterministic), not the file mtime, so
the golden is stable across runs; the new-format stale crash finalize
still uses the stale mtime.
New fixtures l_exec_failed / n_patch_apply / m_multi_web_search /
o_payload_id_filename + regenerated f_exec_truncated / b_old_turncontext /
k_web_search, each line-checked against the spec and byte-identical across
repeated -update-golden runs. Spec pinned the order-independence + the
{secs,nanos} and {patch_success,patch_status} shapes (rules #14/#16/#23).
Gates green: golangci(0)/gosec(0)/vet; race 13/13; codex coverage 92.6%;
FuzzParseLine 0 crashes; secret + AI-attribution scans clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Run ai-viewer persistently on a workstation via systemd USER units (localhost, run-on-login, auto-restart) — no privilege, no remote scope.
deploy/systemd/ai-viewer-{ingest,serve}.service— USER unit templates (%hpaths,Restart=on-failure/RestartSec=3s, serveAfter=ingest). The serve unit documents the intentional start-order race: it may start before the ingester migrates the schema →CheckSchemaexits →Restartretries until the DB exists.scripts/install-systemd-user.sh(install/uninstall/status) —installalways rebuilds from source, copies binaries to~/.local/bin+ units to the user systemd dir,daemon-reloads, then prints theenable --nowcommand for the operator to run (never enables/starts services itself). Idempotentuninstallkeeps binaries + data. Nosudo, no home-path literals.scripts/test/systemd-units-test.sh— directive lint +systemd-analyze verify(offline); asserts exactExecStart,Restart,RestartSec, and the serveAfter=ordering.Quality gates (local, master-run)
shellcheckboth scripts clean;bash -ncleansystemd-analyze verify+ directive lint PASS (a typo'd binary name and a removedRestartSecboth fail it)install --helpmutates nothing;systemctl --user is-enabledconfirms nothing was installed/started on the build machinescripts/build.shstill greenExternal review: codex + glm + minimax, 3 iterations to convergence (codex found the stale-binary install path + the lint-masking hole + the uninstall failure-swallow; all fixed).
Test plan
gatesjob runs the unit lint (systemd-units-test.sh) greenlint/test/frontend/embed-smokestay green (no Go/app/CI-runtime change)