acpmux: ACPMUX_HARNESS_FIXTURE serves a capture fixture's harnesses and health

Merged
TE
10d ago
+508-19

acpmux reads ACPMUX_HARNESS_FIXTURE at start. It names a JSON file of harness profiles and health rows, and the daemon serves those in place of what it would find on the host, so a screenshot rig's before and after builds show the same Settings > Agents > Harnesses data on any machine.

The ask, from Leo's standing order for the infra lane: "get the proper reasonable features in. Period." The concrete problem it fixes: live captures of a Harnesses change (cmux#26083) could not be matched. Each host listed its own harnesses, and the running daemon ignored a seeded harness-health.json as soon as it ran its own check.

What the variable does

The file looks like {"harnesses": {ID: HarnessProfile}, "health": [HarnessHealth]}. With the variable set:

  • The catalog joins the fixture's harnesses in place of discovery (PATH, ~/.acpx and the cached ACP Registry). Profile files still join.
  • _acpmux/harness/health serves the fixture's rows as every check, including refresh, checkLatest and rescan. Nothing is probed, fetched or saved.
  • Every update path refuses: auto-update, _acpmux/harness/update, the cached update plan and update prepare.
  • None of these run: launcher verification, live model lists (cached or probed), model probes of a fixture harness, _acpmux/registry and _acpmux/harness/add.
  • The file is opened with O_NOFOLLOW. If the variable is set but empty, or the file is missing or unparsable, the daemon refuses to start, so a capture can never silently show the host's harnesses.

Risk

Low. With the variable unset, every path runs as before. The one refactor is moving auto-default selection into pick_auto_default, and its behaviour is unchanged. A signed app passes only an allowlist of variables to its daemon (AcpmuxDaemonLauncher.signedInheritedKeys), so only DEV builds and hand-run daemons can receive this one. A session the person starts on a fixture harness still runs that profile's argv; the rig never opens one.

Evidence

  • Rust: lib tests for the parser, the health service and catalog joining. tests/harness_fixture.rs drives the real daemon binary: harnesses, health with and without refresh, a refused update, a refused registry read, the fixture harness never started, no health copy saved, and an empty or missing fixture stopping the start.
  • Live rig, cmux#26083 (Settings > Agents > Harnesses). The same fixture file was used for both captures: Codex 0.3.1 → 0.4.0 (npm), Claude Code 2.0.5 → 2.0.9 (needs sudo) and GitHub Copilot 1.4.0 (current). Before is e519622e (the merge's parent) and after is d65a93cf (the merge). Each capture ran with this change applied on top (refs capture-fixture/26083-before and -after). The rows match; the after adds the update cards cmux#26083 introduced.
Before (e519622e + fixture)After (d65a93cf + fixture)
before lightafter light
before darkafter dark

Companion rig change (the capture state's harnessFixture field): manaflow-ai/cmuxterm-hq.

Agent session: agent session

šŸ¤– Generated with Claude Code