Files
claudetools/.claude/commands/save.md
Mike Swanson 6b0ce9aa04 feat(sync): serialize sync.sh with a per-machine lock; per-session log filenames
Multiple concurrent Claude sessions (and the scheduled-task sync) were
stepping on each other's git state. sync.sh now takes an atomic mkdir
lock in .git/ around the whole run (stage/commit/fetch/rebase/push +
vault), exits 75 (EX_TEMPFAIL = deferred) on contention instead of
racing, and reclaims stale/dead-owner locks with a re-verify-before-clear
guard (closes two TOCTOU races caught in review). /save now mandates
per-session-unique log filenames (never the bare YYYY-MM-DD-session.md).
Docs updated for the lock + deferred-exit semantics.

Note: git add -A is still the catch-all sweep; full per-session commit
isolation and routing /scc + /checkpoint through the lock are follow-ups.
2026-06-05 18:50:52 -07:00

7.4 KiB
Raw Blame History

Save a comprehensive session log to the appropriate session-logs/ directory, then sync the repo.

/save and /sync share bash .claude/scripts/sync.sh as the canonical driver for git operations. The only difference: /save writes a session log first, then calls sync. /sync calls sync directly (no log).


Phase 1 — Generate the narrative

Claude writes all sections directly. Be concise, factual, technical. No filler phrases. Past tense. No emojis.

Author Sections
Claude All sections

Narrative sections (Claude writes directly)

Session Summary — 3-5 paragraphs: what was accomplished, in what order, why.

Key Decisions — bullet list of non-obvious decisions and their rationale.

Problems Encountered — bullet list of problems hit and how each was resolved. Omit section if none.


Phase 2 — Write to disk

Location

Work scope Path
Single project projects/<project>/session-logs/YYYY-MM-DD-session.md
Client clients/<slug>/session-logs/YYYY-MM-DD-session.md
Multi-project / general session-logs/YYYY-MM-DD-session.md

Filename + append behavior

Per-session-unique filenames are mandatory — 34 Claude sessions can run against this one working tree at once, and a shared YYYY-MM-DD-session.md lets them overwrite each other's logs. Never use the bare YYYY-MM-DD-session.md.

  • Default: YYYY-MM-DD-<user>-<topic>.md<user> from the User block (identity.json), <topic> a short kebab slug of this session's main work (e.g. 2026-06-05-mike-gururmm-platform-day.md). The topic naturally separates concurrent sessions.
  • Collision guard: if that exact filename already exists and belongs to a different session (different work), append a discriminator — YYYY-MM-DD-<user>-<topic>-2.md (increment until free). Never overwrite another session's file.
  • Same-session continuation (re-saving your own ongoing work): append a ## Update: HH:MM PT — <topic> section to this session's own file. Do not overwrite.

Required sections (in order)

  1. User block — generate it deterministically; do NOT hand-write or infer it. Run:
    bash .claude/scripts/whoami-block.sh
    
    Paste its output verbatim as this section. The script reads .claude/identity.json (+ users.json for role) — the only authoritative attribution sources. Never derive the user from the hostname, the # userEmail context hint, or memory. If the script emits a [WARNING] about a stale machine/hostname mismatch, stop and fix identity.json before saving.
  2. Session Summary (Ollama)
  3. Key Decisions (Ollama)
  4. Problems Encountered (Ollama)
  5. Configuration Changes — files modified / created / deleted (with paths)
  6. Credentials & Secrets — UNREDACTED if newly discovered or created. Vault paths if vaulted. Never half-redact a value future-Claude might need.
  7. Infrastructure & Servers — IPs, hostnames, ports, tenant IDs, container names, DNS, certs
  8. Commands & Outputs — important one-liners, key outputs, error messages with resolution
  9. Pending / Incomplete Tasks — what's left, blockers, next steps
  10. Reference Information — URLs, endpoints, commit SHAs, ticket IDs, routine IDs, file paths

When in doubt, include MORE detail — future sessions search these logs to recover context.


Phase 3 — Wiki Compile (before sync)

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 subagentmodel: "sonnet", not Ollama), so new findings/patterns actually land — not just dynamic fields.

  1. Derive the slug from the session-log path written in Phase 2:

    • clients/<slug>/session-logs/... → client <slug>
    • projects/<project>/session-logs/... → project article slug (e.g. guru-rmm, guru-connect)
    • Root session-logs/...skip this phase entirely (no single article is implied)
  2. Run the /wiki-compile generation for that target, writing the article + updating wiki/index.md, but stop before its commit/push stepsync.sh (Phase 4) commits everything together in one commit:

    • Article existsfull recompile (/wiki-compile <type>:<slug> --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 yetseed (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.
  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).


Phase 4 — Sync

bash .claude/scripts/sync.sh

sync.sh is serialized by a per-machine lock (.git/claudetools-sync.lock) so concurrent sessions or the scheduled-task sync cannot interleave commits/rebases; if another sync is mid-flight it waits up to ~120s, then exits 75 (deferred) rather than racing — the next sync catches up. On a 75, do NOT print a success summary; report "sync deferred — another sync is running; your session log is written locally and will sync on the next run". Otherwise it: reconciles this machine's git config user.name/email to .claude/identity.json (so commit authorship can't drift), stages all changes with git add -A (after purging garbled Windows path-as-filename cruft), auto-commits, fetch + rebase, push, then the same flow for the vault repo, then surfaces cross-user ## Note for <user> blocks.

Note: git add -A is still the catch-all sweep, so a save run will also pick up any other dirty files in the shared tree. The lock prevents two syncs from racing, and per-session-unique log filenames prevent log overwrites — but the bare-add -A capture means full per-session commit isolation is a later step (see the isolation plan: drop blind add -A in favour of explicit per-session staging). For now, avoid running /save from two sessions at the exact same moment.

After sync, emit a Post-commit Summary:

## Post-commit Summary
Commit:  <sha>  <subject>
Author:  <name> <<email>>
Push:    <old>..<new>  main -> main  (origin)
File:    <session log path>  (+N lines, appended/created)

Wiki updates (if any): <count> articles updated (clients/projects/systems/patterns)

Cross-user note handling (CRITICAL)

If sync.sh surfaces a ## Note for <user> or ## Message for <user> block from an incoming session log, display it prominently at the top of the response, before the sync summary:

============================================================
MESSAGE FROM <author> (<date>)
============================================================
<full note content>
============================================================

Explicitly address each action item or question before moving on.