Home / Projects / Orchestrata Ultimate / Decisions / ADR index / ADR-0071
ADR-0071: REQ-110-135 amended: codemod daemon lifecycle is delegated to systemd user units
| field | value |
|---|---|
| Status | Proposed |
| Date | 2026-10-06 |
| Register | factory (docs/adr, 4-digit ids) |
| Source | docs/adr/0071-req-110-135-amended-codemod-daemon-lifecycle-is-delegated-to-systemd-user-units.md — permalink at ffadad98c (unchanged on master since that commit) |
| Wiki register entry | ADR-0071 (wiki status: Proposed) |
The record
Status: Proposed · Date: 2026-10-06
Context
SPEC-110 REQ-110-135 (specs/110-codemod-engine/spec.md:2003) reads verbatim:
REQ-110-135: The daemon SHALL support lifecycle management via
codemod daemon start|stop|status|restartsubcommands. Thestartcommand SHALL fork to background, parse the workspace, create the Unix socket, and start an inotify watcher thread. Thestopcommand SHALL send SIGTERM, wait 5s for graceful shutdown, and force kill if necessary.
ADR-0067 (Accepted) rules that every development daemon, the codemod daemon
included (Decision item 5, wave R2), is deployed code under
/opt/orchestrata-tools run as a systemd user unit, and that no process is
spawned from a checkout. Packet P4 of plans/ast-daemon-deployment-plan.md
executed that ruling in src/orchestrata_ultimate/refactor/codemod/daemon.py:
start()no longer forks. It refuses when the unit instance for the repo is already active, refuses when the deployed launcher is missing (nothing is spawned), runssystemctl --user start <instance>, waits for the unit'sMainPID, and writes that pid to.codemod_daemon/pid. The deployed launcher does the parse, socket and inotify work.stop()on a loaded unit runssystemctl --user stop <instance>and signals no pid at all (a pid file can be stale after a unit restart and a recycled pid is not ours to kill). Only when no unit is loaded does a live pid-file pid get SIGTERM, ashutdown_timeoutwait, then SIGKILL, and only after_pid_is_codemod_daemon(pid)verifies the argv is the codemod daemon entry module.
The P4 reviewer (round 3, 2026-10-06) found the result correct against
ADR-0067 and contradictory to REQ-110-135 as written: "fork to background" is
false (the unit forks nothing; systemd owns the process), and "send SIGTERM,
wait 5s, force kill" is now the exception path, not the rule. Both decorated
methods cite REQ-110-135, so RUL-013 (scripts/check_spec_authority.py) keeps
pointing tests at a clause the code deliberately does not satisfy.
ADR-0067's Decision text does not name REQ-110-135. Changing a requirement's
words is a spec amendment, and ADR-0068 set the mechanism for that: a
Proposed ADR carrying the new text, edited into spec.md only once Accepted.
Editing the clause under the ADR-0067 waiver would be a self-issued ruling.
Decision (proposed)
Amend REQ-110-135 to read:
REQ-110-135: The daemon SHALL support lifecycle management via
codemod daemon start|stop|status|restartsubcommands that delegate to the systemd user unit instance named by the repository root (ADR-0067).startSHALL refuse when that instance is already active, SHALL refuse when the deployed launcher under/opt/orchestrata-toolsis missing (spawning nothing), SHALL start the instance withsystemctl --user start, and SHALL record the instance'sMainPIDin.codemod_daemon/pid; the deployed launcher parses the workspace, creates the Unix socket, and starts the inotify watcher thread.stopon a loaded instance SHALL runsystemctl --user stopand SHALL signal no pid. Only when no instance is loaded for the repository SHALLstopsend SIGTERM to the pid-file pid, wait 5s for graceful shutdown, and force kill if necessary, and only after verifying that pid's argv is the codemod daemon entry module; any other pid is left untouched and the stale file removed.
REQ-110-134 is unchanged: the daemon still writes its pid to
.codemod_daemon/pid (via the manager recording MainPID), creates the
socket at .codemod_daemon/socket, and holds .codemod_daemon/lock.
REQ-110-133, REQ-110-136 and REQ-110-137 are unchanged.
Alternatives rejected
- Leave REQ-110-135 as written and let the code diverge. Two
@traces(reqs=["REQ-110-135"])methods then certify a clause they do not implement; the RTM grades the clause TESTED on tests that prove the opposite behaviour. That is the false-confidence pattern ADR-0069 exists to stop. - Restore the forking start and the always-SIGTERM stop to satisfy the
clause. Reverses ADR-0067 for one daemon: a process spawned by the CLI
from whatever Python it runs under is a checkout-bound process again, and
stopkilling an unverified pid-file pid is the recycled-pid hazard the P4 review named. - Treat the amendment as covered by ADR-0067's waiver and edit
spec.mddirectly. ADR-0067 rules on deployment and process identity, not on the words of SPEC-110; spec text changes went through a Proposed ADR for REQ-110-034 (ADR-0068) and get the same path here. - Mark REQ-110-135 superseded with no replacement. Leaves the lifecycle subcommands, which exist and are tested, without a requirement to cite.
Consequences
daemon.pystart()/stop()andtests/refactor/codemod/ test_daemon_lifecycle.pycite a clause that describes what they do; RUL-013 stays satisfied on the real behaviour.- The pid-file path becomes a documented legacy branch (a daemon started before the unit-only launch), gated on argv verification, so the only SIGTERM/SIGKILL the manager ever sends is to a process it has identified.
scripts/check_adr_graph_sync.pymust report this idOK in sync.- On Accepted: edit REQ-110-135 in
specs/110-codemod-engine/spec.mdto the Decision text, keepchecklists/requirements.md:119at[x]with the ADR cited, record the ruling date and words in this file's Provenance, and close the P4 round-3 HOLD item that names it.
Provenance
- Directive: Mohamed, ADR-0067 (Accepted): dev daemons are deployed code serving checkouts as data; codemod daemon in the same wave (R2).
- Finding: P4 reviewer round 3, 2026-10-06,
plans/ast-daemon-deployment-plan.md(HOLD reasons list): "an amended REQ-110-135 ... done through an ADR the way ADR-0068 was done; the spec amendment needs Mohamed's ruling". - Code:
src/orchestrata_ultimate/refactor/codemod/daemon.pystart()(lines 188-245) andstop()(lines 247-328) onfeat/ast-daemon-p4-hook-clientat cc46bfbc6, read viascripts/serialize_codebase_ast.py source. - Precedent: ADR-0068 (spec-text amendment through a Proposed ADR, edited on Accepted).
- Proposed 2026-10-06 with the recommendation Accept (orchestrator's).
Related ADRs
Rulings cited
- RUL-013: RUL-013 — Specs are authoritative — resolve decisions from them, not guesses