One-command make codex-login reading the configured source from the installed worker unit (#168) #169

Merged
adrian.tkacz merged 1 commit from task/168-codex-login-make-target into develop 2026-10-03 22:46:16 +00:00
Member

Closes #168 (kept open for human review; do not merge without the owner's approval).

What

One identical command on every host refreshes the local worker's Codex credentials:

make codex-login

No .dev/.prd variants, no host-specific arguments, no source id typed by the operator.

How the local Codex source is discovered

The persisted home of the operator's adapter selection is the generated systemd user unit itself (D-065: service install --codex-source <id> renders it into ExecStart). This PR adds an exact read-back in worker_service.py:

  • parse_exec_start(value) inverts render_unit's rendering layer by layer (the : no-substitution prefix, systemd word quoting, %% specifier unescaping) and validates the generated shape (<executable> run --state-dir <dir> plus the selection flags);
  • read_installed_service_configuration() reads the installed unit, gated on the generated marker line — a missing unit, a foreign unit, an unknown flag, a wrong shape or a dangling value refuses with the remediation recipe (service install --codex-source SOURCE_ID), never a guess;
  • scarcity-router-worker codex-login with --source omitted requires exactly one configured --codex-source (zero or several → actionable refusal listing the ids) and passes the discovered source, the unit's --state-dir and its pinned --codex-bin through. Explicit flags keep their exact prior semantics; discovery only fills what was omitted.

What is reused (nothing new invented)

  • The existing scarcity-router-worker codex-login machinery and run_official_codex_login — the official codex login --device-auth flow against the source-controlled CODEX_HOME (<state_dir>/codex-sources/<source_id>/, D-053).
  • The worker's existing state directory (the unit's own --state-dir), so the login addresses exactly the controlled home the running worker probes.
  • No second credential store, no Codex installation, and ~/.codex is never read, copied or adopted.

Worker restart

Not required: the Codex adapter re-probes account auth from the controlled home on every eligibility check, so the running worker picks up the fresh credentials on its next check.

Verification

  • make check green: 2649 tests OK (15 skipped), uv run basedpyright 0 errors / 0 warnings; git diff --check clean; origin/develop..HEAD contains only this change (single commit 7430d89).
  • New tests: ExecStartReadBackTests (9 — exact round-trip with hostile paths, agreement with the independent systemd_words oracle, marker gating, missing/foreign unit, unknown flag, wrong shape, dangling value, multi-source order, Ollama+ZCode selection preservation) and CodexLoginDiscoveryTests (6 — discovery passes source/state-dir/bin, explicit --source keeps exact prior semantics, no-source refusal with recipe, ambiguous-source refusal, missing unit, foreign unit).
  • ROG (this host): make help lists the target; make -n codex-login prints the single uv run scarcity-router-worker codex-login line; reading the actual installed unit resolves state_dir=/home/adrian/.local/share/scarcity-router/worker and zero configured codex sources — the command refuses with the install recipe (correct: the ROG service currently runs without a Codex source; after service install --codex-source <id> + service restart the same command resolves it).
  • Precision: not reachable from this session (no SSH key auth), so live execution is an operator step. Its unit carries --codex-source precision-codex-live (the documented install form); the exact flag shape — including the source id, state-dir pass-through and quoting — is covered by the round-trip and discovery tests against the same render_unit output.
  • Both hosts reach the same scarcity-router-worker codex-login path (verification points 3–6: controlled CODEX_HOME preserved, ~/.codex untouched, no second install/store — by construction of the reused machinery, negative-scanned by the existing suites).

UX impact

The user-facing consequence is the feature: refreshing credentials drops from "know and type the host-specific source id" to make codex-login. First-run on a host without a configured source prints the exact service install --codex-source SOURCE_ID recipe; a multi-source host is told to pass --source with its ids listed. No error path is silent; no secret is printed (the unit embeds none).

Review dimension checklist

  • Security: read-only unit parse, marker-gated, fail-closed; no new subprocess, network, credential or write surface (guardrail scan unaffected).
  • Frozen contracts: codex-login --source behavior unchanged; no serialized contract, protocol, catalog or selector change.
  • Decision record: D-066 appended to docs/decisions.md.
Closes #168 (kept open for human review; do not merge without the owner's approval). ## What One identical command on every host refreshes the local worker's Codex credentials: ```bash make codex-login ``` No `.dev`/`.prd` variants, no host-specific arguments, no source id typed by the operator. ## How the local Codex source is discovered The persisted home of the operator's adapter selection is the generated systemd user unit itself (D-065: `service install --codex-source <id>` renders it into `ExecStart`). This PR adds an exact read-back in `worker_service.py`: - `parse_exec_start(value)` inverts `render_unit`'s rendering layer by layer (the `:` no-substitution prefix, systemd word quoting, `%%` specifier unescaping) and validates the generated shape (`<executable> run --state-dir <dir>` plus the selection flags); - `read_installed_service_configuration()` reads the installed unit, gated on the generated marker line — a missing unit, a foreign unit, an unknown flag, a wrong shape or a dangling value refuses with the remediation recipe (`service install --codex-source SOURCE_ID`), never a guess; - `scarcity-router-worker codex-login` with `--source` omitted requires exactly one configured `--codex-source` (zero or several → actionable refusal listing the ids) and passes the discovered source, the unit's `--state-dir` and its pinned `--codex-bin` through. Explicit flags keep their exact prior semantics; discovery only fills what was omitted. ## What is reused (nothing new invented) - The existing `scarcity-router-worker codex-login` machinery and `run_official_codex_login` — the official `codex login --device-auth` flow against the source-controlled `CODEX_HOME` (`<state_dir>/codex-sources/<source_id>/`, D-053). - The worker's existing state directory (the unit's own `--state-dir`), so the login addresses exactly the controlled home the running worker probes. - No second credential store, no Codex installation, and `~/.codex` is never read, copied or adopted. ## Worker restart Not required: the Codex adapter re-probes account auth from the controlled home on every eligibility check, so the running worker picks up the fresh credentials on its next check. ## Verification - `make check` green: 2649 tests OK (15 skipped), `uv run basedpyright` 0 errors / 0 warnings; `git diff --check` clean; `origin/develop..HEAD` contains only this change (single commit `7430d89`). - New tests: `ExecStartReadBackTests` (9 — exact round-trip with hostile paths, agreement with the independent `systemd_words` oracle, marker gating, missing/foreign unit, unknown flag, wrong shape, dangling value, multi-source order, Ollama+ZCode selection preservation) and `CodexLoginDiscoveryTests` (6 — discovery passes source/state-dir/bin, explicit `--source` keeps exact prior semantics, no-source refusal with recipe, ambiguous-source refusal, missing unit, foreign unit). - **ROG (this host):** `make help` lists the target; `make -n codex-login` prints the single `uv run scarcity-router-worker codex-login` line; reading the actual installed unit resolves `state_dir=/home/adrian/.local/share/scarcity-router/worker` and **zero** configured codex sources — the command refuses with the install recipe (correct: the ROG service currently runs without a Codex source; after `service install --codex-source <id>` + `service restart` the same command resolves it). - **Precision:** not reachable from this session (no SSH key auth), so live execution is an operator step. Its unit carries `--codex-source precision-codex-live` (the documented install form); the exact flag shape — including the source id, state-dir pass-through and quoting — is covered by the round-trip and discovery tests against the same `render_unit` output. - Both hosts reach the same `scarcity-router-worker codex-login` path (verification points 3–6: controlled `CODEX_HOME` preserved, `~/.codex` untouched, no second install/store — by construction of the reused machinery, negative-scanned by the existing suites). ## UX impact The user-facing consequence is the feature: refreshing credentials drops from "know and type the host-specific source id" to `make codex-login`. First-run on a host without a configured source prints the exact `service install --codex-source SOURCE_ID` recipe; a multi-source host is told to pass `--source` with its ids listed. No error path is silent; no secret is printed (the unit embeds none). ## Review dimension checklist - Security: read-only unit parse, marker-gated, fail-closed; no new subprocess, network, credential or write surface (guardrail scan unaffected). - Frozen contracts: `codex-login --source` behavior unchanged; no serialized contract, protocol, catalog or selector change. - Decision record: D-066 appended to `docs/decisions.md`.
make codex-login runs the existing scarcity-router-worker codex-login
machinery with no operator-typed source id: the CLI now discovers the
host's configured --codex-source by reading the generated systemd user
unit back (parse_exec_start inverts render_unit exactly; only
marker-carrying units are interpreted). The unit's state directory and
pinned codex binary pass through, so the official device-auth login
addresses exactly the controlled home the running worker probes.
Explicit flags keep their prior semantics; absent or ambiguous
configuration refuses with the remediation recipe. D-066 records the
decision; no restart is required (auth is re-probed per eligibility
check).
adrian.tkacz deleted branch task/168-codex-login-make-target 2026-10-03 22:46:16 +00:00
Sign in to join this conversation.
No description provided.