---
description: Bring the current project up to the current cc-os approach by remediating whatever /os-status checks flag — idempotent, doubles as the update path. Invoked by /os-status:fix.
---

# fix

Unified project setup/update. Runs the same check registry the SessionStart hook
runs, then drives each failing check's remediation. Per ADR-026: `fix` orchestrates
existing per-plugin skills — it never reimplements them. Idempotent by construction:
re-running `fix` on an already-configured project is the update path, not a separate
command.

**Scope: `fix` never stages or commits.** It edits files and reports what changed —
staging and committing is always the user's call, even for mechanical remediations
like a `.gitignore` addition or a config stamp.

## Flow

1. **Get machine-readable results.** From the project root, run:

   ```
   python3 ${CLAUDE_PLUGIN_ROOT}/hooks/checks.py --json
   ```

   This prints a JSON array of `{name, status, message, remediation}` for every
   check applicable to the current project (project-scoped checks are skipped
   outside a git project, same rule the SessionStart hook uses).

2. **All `ok` (and only `note`/`ok`)?** Report "this project is up to date" and go
   straight to steps 3b–4 (gitignore + version stamp) — nothing else to remediate.

3a. **Ensure `.cc-os/` is gitignored.** Check the project's `.gitignore` for a
   `.cc-os/` entry; add one if missing. Per ADR-027, this single entry covers every
   cc-os plugin's per-project state (os-status's `.cc-os/config` + `.cc-os/status/`,
   os-backlog's tracker key, etc.) — there is no separate `.os-adr/` entry to add
   anymore. This step runs every `fix` invocation, not just when a check flags it.

3b. **Otherwise, walk the non-`ok` entries in this order** (mechanical → decision-
   bearing, so autonomous fixes land before anything needing a human gate):

   a. **`adr-system-present`** → invoke `/os-adr:init` (or `/os-adr:migrate` if the
      project already has decision-log-like content the message/context suggests —
      use judgment, this is mechanical either way).

   b. **`vault-hub-note-present`** → either invoke `/os-vault:write` to create a hub
      note (tags `type/hub` + `project/<name>`), or if the user says a hub note
      already exists under a different name, set `hub = <slug>` in `.cc-os/config`
      via the config-write helper (see step 4).

   c. **`project-graph-present`** → invoke `/os-vault:onboard-project`.

   d. **`tracker-configured`** → invoke `/os-backlog:route` directly and let it run
      its own inspect → propose → confirm flow. Do NOT pre-ask the user "forgejo or
      planka?" — that framing is wrong: the boundary rule uses BOTH (Planka = state,
      git issues = specs; a project routinely keeps its git-issue spec chains AND a
      `planka:` tracker key for new ad hoc state). The route skill owns that
      decision conversation and its gates. If `/os-backlog:route` is not installed,
      tell the user and skip — do not fabricate a `.cc-os/config` tracker value
      yourself.

   e. **`subagent-model-env-override`** → **human gate, and typically out of scope
      for a project-level fix.** This is an environment/settings.json condition, not
      a per-project one. Report it and ask the user to remove the env var
      themselves; do not edit `~/.claude/settings.json` from this skill.

   f. **`config-version-current`** → resolved automatically by step 4 below; no
      separate action.

   Re-run the JSON check after each remediation that plausibly changed check-registry
   state (creating a hub note, running `/os-adr:init`, setting a config key), so later
   steps see fresh results (e.g. don't act on a stale `vault-hub-note-present` warning
   after already creating the note). Remediations that don't affect check-registry
   state — step 3a's gitignore edit is the example — don't require a re-run.

   Snoozed warns self-clear: whenever a check now evaluates to `ok`/`note` (whether
   because this skill just fixed it or it was already fine), the next SessionStart
   or `fix` run clears its stale `snooze-<check>` file automatically (state.py's
   `clear_snooze`) — nothing to do here beyond re-running the JSON check per the
   rule above.

4. **Stamp the config version.** Once the mechanical/human-gated fixes above are
   done (or were already `ok`), write the current version into `.cc-os/config`,
   preserving every other key. Use the helper:

   ```python
   import sys
   sys.path.insert(0, "${CLAUDE_PLUGIN_ROOT}/hooks")
   from state import write_config_value, find_project_root
   from checks import CURRENT_CONFIG_VERSION
   from pathlib import Path

   root = find_project_root(Path.cwd())
   write_config_value(root, "version", str(CURRENT_CONFIG_VERSION))
   ```

   (Equivalently, run it as a one-off `python3 -c "..."` from the project root.)
   This is what makes `config-version-current` pass on the next run and what makes
   re-running `fix` on a fully-configured project a fast, silent no-op.

5. **Report a short summary**: which checks were already `ok`, which were fixed and
   how, which were skipped pending a human decision, and confirm the config version
   was stamped.

## Notes

- Never edit `.cc-os/config` by hand-writing the whole file — always go through
  `write_config_value` (or the equivalent read-modify-write) so unrelated keys
  (`hub`, `tracker`, `vault_path`, ...) are preserved.
- Decision-bearing steps (tracker destination, anything destructive) keep their
  human gate even when this skill is otherwise running autonomously. Mechanical
  steps (running `/os-adr:init`, `/os-vault:onboard-project`, stamping the version)
  proceed without asking.
- This skill does not touch `subagent-model-env-override` state — that's a machine
  environment condition, not a per-project one, and editing global settings.json is
  out of scope.
