Skip to content

Fixers layer

Per-requirement repair functions: clicking 'Fix' on a failed requirement runs the registered fixer to bring it back to passing.

Details

Per-requirement repair functions. Each RequirementDef may opt into a fixer that runs when the user clicks Fix in the Settings tab. Fixers attach to requirements, not to components — runtime probe failures need a different kind of remediation (restart the service, fix upstream).

The four fix_kind values

  • none (default) — no automated fix. The requirement's fix_hint text is the only guidance; the user must follow it manually.
  • programmatic — one click runs fix_fn() with no input. Use when everything the fix needs is already known (e.g., create a directory whose path comes from config).
  • input_required — click pops a form rendered from fix_params; user fills it; fix_fn(**form_values) runs. Use when the fix needs information only the user can supply (e.g., a timezone string, a path, a secret).
  • agent_handoff — click spawns a Claude Code session with fix_agent_brief as prompt. Use for setups too clicky or context-dependent for programmatic capture (e.g., installing an Obsidian community plugin).

Fixer return shape

{
  "ok": bool,
  "detail": str,
  "side_effects": list[str]   # optional; surfaced in the UI
}

Conventions

  • Idempotent. Running the fixer twice produces the same end state. The dispatcher re-runs the requirement check after the fix — a non-idempotent fixer that creates duplicates breaks this.
  • Specific in detail. Say what was created or changed: "Created <vault>/journal/" beats "Done".
  • Honest about partial failure. Return ok=False if anything blocks completion. Never raise — the dispatcher converts exceptions to {ok: False, detail: "..."} for endpoint consistency, but a fixer that handles its own failures gives better detail strings.

fix_params schema (for input_required)

fix_params = {
  "<field_name>": {
    "type": "str" | "path" | "secret",
    "label": "<UI label>",
    "hint": "<placeholder / format hint>",
    "default": <Any>,        # optional
    "required": True | False,
    "secret": True | False,  # optional; redacts in UI
  },
}

The Settings tab renders one form input per field; on submit, values are passed as keyword args to fix_fn.

fix_agent_brief (for agent_handoff)

A string prompt that's handed to a freshly spawned Claude Code session. Should explain: what the user is trying to fix, what the agent is empowered to do, the steps to walk the user through, how to verify completion (often: ask the user to refresh the dashboard Settings tab). The spawn flow is desktop-only (non-remote) — the user must be at the machine.

Dispatch

Fixers are invoked by the dashboard endpoint POST /api/control/fix/<req_id>. The endpoint validates that the requirement opts in to a fix, gates on consent, runs the fixer, re-runs the requirement check, busts the control-graph cache, and returns {ok, detail, side_effects, recheck, spawned}. Full endpoint surface and Settings-tab UI: architecture/control-graph.

See also