diff --git a/.claude/commands/save.md b/.claude/commands/save.md index 9aecb86..f934f94 100644 --- a/.claude/commands/save.md +++ b/.claude/commands/save.md @@ -68,25 +68,26 @@ When in doubt, include MORE detail — future sessions search these logs to reco --- -## Phase 3 — Wiki Compile (before sync) +## Phase 3 — Wiki: DECOUPLED (do NOT recompile inline) -Fold what you just worked on into the wiki article so it ships in the **same commit** as the session log. This runs before sync and **re-synthesizes** the article (via a **Sonnet subagent** — `model: "sonnet"`, not Ollama), so new findings/patterns actually land — not just dynamic fields. +Wiki synthesis is **decoupled from `/save`** (harness v1.2.0+, Task 2). Running a full +Sonnet recompile inline on every save, on every machine, caused concurrent-recompile +rebase conflicts — and once committed unresolved conflict markers into a wiki article. +So **`/save` no longer touches the wiki**: it writes the session log and syncs, nothing +more. Do NOT recompile the wiki here, and never block/delay the sync on wiki work. -1. Derive the slug from the session-log path written in Phase 2: - - `clients//session-logs/...` → client `` - - `projects//session-logs/...` → project article slug (e.g. `guru-rmm`, `guru-connect`) - - Root `session-logs/...` → **skip this phase entirely** (no single article is implied) +To refresh the wiki for this session's work, run `/wiki-compile` **separately** — it is +now **serialized** (per-article coord lock) and **staged** (writes a proposed update to +`.claude/wiki_staging/` for review before it touches the live article). -2. Run the `/wiki-compile` generation for that target, writing the article + updating `wiki/index.md`, but **stop before its commit/push step** — `sync.sh` (Phase 4) commits everything together in one commit: - - **Article exists** → **full recompile** (`/wiki-compile : --full`): the Sonnet subagent re-synthesizes, **preserving Patterns and History verbatim** (unless the new session log shows an item resolved) and refreshing everything else, absorbing this session's work. Clients also refresh live Syncro fields (hours, tickets). - - **No article yet** → **seed** (full synthesis) to create it. - - The main agent reviews the subagent's draft before writing — verify IPs/paths; never invent vault paths (use `(verify)`); keep billing fields Syncro-authoritative. +After the sync completes, derive the slug from the session-log path (Phase 2) and emit +the exact command for the operator to run when ready: +- `clients//session-logs/...` → `[INFO] Wiki decoupled — run: /wiki-compile client: --full (serialized + staged)` +- `projects//session-logs/...` → `[INFO] Wiki decoupled — run: /wiki-compile project: --full (serialized + staged)` +- Root `session-logs/...` → no single article implied; emit nothing. -3. **Softfail (critical) — a wiki failure must NEVER block the save:** - - If the synthesis subagent fails or is unavailable, fall back to a surgical **refresh** (bump `last_compiled` + `sources`; refresh client Syncro fields) so the article still records the session, and emit `[WARN] wiki refreshed, not recompiled; run /wiki-compile --full later`. - - Any other failure: log it and continue to sync. - -The article + `wiki/index.md` are committed alongside the session log by `sync.sh` (Phase 4). +The session log + `sync.sh` are the durable record; the wiki is refreshed deliberately, +not on every save. --- diff --git a/.claude/commands/wiki-compile.md b/.claude/commands/wiki-compile.md index 3839581..6ed7e8a 100644 --- a/.claude/commands/wiki-compile.md +++ b/.claude/commands/wiki-compile.md @@ -342,12 +342,33 @@ If the subagent is unavailable, the main agent writes the article directly using --- -## Phase 5 — Write Article + Update Index +## Phase 5 — Serialize, Stage, Review, Apply (Task 2) -**Write the article:** -- Seed: write `wiki/clients/.md` from generated content -- Full: overwrite `wiki/clients/.md` -- Refresh: edits already applied in Phase 4 +Wiki writes are SERIALIZED + STAGED so two machines never recompile the same article +into a conflict, and no synthesis lands in the live article without a review. + +**5.0 Claim a per-article coord lock** (via the `coord` skill): +`lock claim claudetools wiki// "wiki-compile " --ttl 1`. +- The TTL auto-evicts a dead session's lock (no permanent stranding). +- If the lock is **already held** → emit `[SKIP] wiki// is being compiled on + another machine; try again shortly` and exit cleanly. +- If **coord is unreachable** → emit `[WARN] coord down — proceeding without lock` and continue. +- RELEASE the lock in 5.3 — and on ANY error/abort before then. + +**5.1 Write the synthesized article to STAGING, not the live tree:** +- Staging path: `.claude/wiki_staging/-.md` (`mkdir -p .claude/wiki_staging`). + Write the generated/recompiled article THERE. Do NOT touch `wiki/...` yet. + +**5.2 Review the staged diff (NO blind merge):** +- `diff -u "" ".claude/wiki_staging/-.md" | head -120` (or + `(new article)` if none). The main agent reviews: Patterns/History preserved on full + recompile, IPs/paths/vault-paths accurate, billing Syncro-authoritative, NO structural + corruption or duplicated headers. If the diff looks wrong → STOP, fix the staged file or + abort (release the lock); do not apply. + +**5.3 Apply the staged article to the live tree** (then index + commit in Phase 6): +- `cp .claude/wiki_staging/-.md ` (seed/full); refresh edits + already applied in Phase 4 still go via this staging review. **Update `wiki/index.md`:** - Check if `wiki/clients/.md` is listed in the Clients table @@ -366,7 +387,11 @@ If the subagent is unavailable, the main agent writes the article directly using cd "$CLAUDETOOLS_ROOT" git add "wiki/clients/${SLUG}.md" wiki/index.md git commit -m "wiki: compile ${SLUG} (${MODE})" +git fetch origin && git rebase origin/main # serialized, but rebase defensively git push origin main +# Release the per-article lock and clear staging (ALWAYS — even on an earlier abort): +$PY "$CLAUDETOOLS_ROOT/.claude/skills/coord/scripts/coord.py" lock release claudetools "wiki/${TYPE}/${SLUG}" 2>/dev/null || true +rm -f "$CLAUDETOOLS_ROOT/.claude/wiki_staging/${TYPE}-${SLUG}.md" ``` Emit: diff --git a/.claude/harness/CHANGELOG.md b/.claude/harness/CHANGELOG.md index 182b6fa..8870f3b 100644 --- a/.claude/harness/CHANGELOG.md +++ b/.claude/harness/CHANGELOG.md @@ -17,3 +17,10 @@ or old harness during a heterogeneous rollout. See - Task 4: `harness-guard.sh` wired into `sync.sh` pre-commit, WARN-ONLY (logs conflict markers / unencrypted sops / private keys to .claude/harness/guard.log; does not block unless HARNESS_GUARD_FATAL=1; SKIP_HARNESS_GUARD=1 bypasses). + +## 1.2.0 — 2026-06-08 +- Task 2: wiki synthesis DECOUPLED from /save (the concurrent-recompile conflict source). + /save now only writes the log + syncs and emits the exact /wiki-compile command to run. + /wiki-compile is now SERIALIZED (per-article coord lock, TTL orphan-evict, coord-down = + warn+proceed) and STAGED (writes .claude/wiki_staging/-.md -> review diff -> + apply to live -> commit -> release lock). No blind background auto-merge. diff --git a/.claude/harness/VERSION b/.claude/harness/VERSION index 9084fa2..26aaba0 100644 --- a/.claude/harness/VERSION +++ b/.claude/harness/VERSION @@ -1 +1 @@ -1.1.0 +1.2.0 diff --git a/.claude/wiki_staging/README.md b/.claude/wiki_staging/README.md new file mode 100644 index 0000000..404d425 --- /dev/null +++ b/.claude/wiki_staging/README.md @@ -0,0 +1,6 @@ +# wiki_staging + +Transient staging for `/wiki-compile` (Task 2 of the harness-optimization spec). The +synthesized article is written here FIRST, the diff vs the live `wiki/` article is +reviewed, and only then applied to the live tree and committed. Staged `*.md` files are +gitignored and removed after apply — nothing here is canonical. diff --git a/.gitignore b/.gitignore index 7f35f67..d0db723 100644 --- a/.gitignore +++ b/.gitignore @@ -106,3 +106,7 @@ temp/ # Microsoft Office temp/lock files ~$* + +# Wiki synthesis staging (transient; review-before-apply). Keep only the README. +.claude/wiki_staging/* +!.claude/wiki_staging/README.md