os-vault: add SessionStart git pull for multi-machine vault/memsearch freshness

Sync was push-only via SessionEnd (vault_sync.py/memsearch_sync.py); a second
machine's copies could go stale. session_start.py now runs a fast-forward-only,
10s-timeout git pull for both repos before the staleness check — silent on
success, quiet one-line stderr note on failure, never blocking session start.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Mb3V8NNzAuns7vgX9A2jU
This commit is contained in:
jared 2026-07-10 10:06:55 -04:00
parent c629959f37
commit cc450c385b
3 changed files with 39 additions and 6 deletions

View File

@ -1,6 +1,6 @@
# cc-os implementation status & changelog # cc-os implementation status & changelog
_Last updated: 2026-07-08. This is the append-only implementation-status record moved out of _Last updated: 2026-07-10. This is the append-only implementation-status record moved out of
`CLAUDE.md` (2026-07-08, task B1 of `CLAUDE.md` (2026-07-08, task B1 of
[system-prompt-profiles-tasks.md](plans/system-prompt-profiles-tasks.md)). CLAUDE.md keeps [system-prompt-profiles-tasks.md](plans/system-prompt-profiles-tasks.md)). CLAUDE.md keeps
orientation only; when a build step completes, record it HERE (plus orientation only; when a build step completes, record it HERE (plus
@ -113,15 +113,23 @@ drift — see "Editing a local plugin (cache refresh)" below. Investigation resu
caches (not manifest-naming issues) were the cause; slash command registration works caches (not manifest-naming issues) were the cause; slash command registration works
correctly once caches are fresh. correctly once caches are fresh.
**Remaining optional items:** SessionStart vault pull (multi-machine sync; push-only is the **Multi-machine freshness closed (2026-07-10):** `session_start.py` gained a fast-forward-only
current design); additional project onboarding (one at a time, per ADR-013); bulk vault `git pull` (`timeout 10 git pull --ff-only --quiet`) for both `cfg.vault_path` and
migration. All required build steps complete as of 2026-06-15. `cfg.memsearch_dir`, run before the staleness check — silent on success, quiet one-line stderr
note on failure (offline machine), never blocking session start. Closes the push-only gap noted
below; mirrors the push style of `vault_sync.py`/`memsearch_sync.py` in the opposite direction.
**Remaining optional items:** additional project onboarding (one at a time, per ADR-013); bulk
vault migration. All required build steps complete as of 2026-06-15.
## Implemented components ## Implemented components
**Global os-vault plugin** — `cc-os/plugins/os-vault/` (git-tracked, 2026-06-12); symlinked **Global os-vault plugin** — `cc-os/plugins/os-vault/` (git-tracked, 2026-06-12); symlinked
into `~/.claude/plugins/os-vault` into `~/.claude/plugins/os-vault`
- Hooks: `hooks/``session_start.py`, `session_context.py` (project graph path only), - Hooks: `hooks/``session_start.py` (SessionStart; staleness check + detached graph
rebuild, and, as of 2026-07-10, a fast-forward-only `git pull` for `cfg.vault_path` and
`cfg.memsearch_dir` before the staleness check — silent on success, quiet one-line stderr
note on failure, never blocking), `session_context.py` (project graph path only),
`post_tool_use_write.py`, `session_end.py` (vault journal), `memsearch_sync.py` (second `post_tool_use_write.py`, `session_end.py` (vault journal), `memsearch_sync.py` (second
SessionEnd hook; memsearch auto-commit+push, 30s timeout), `vault_sync.py` (third SessionEnd hook; memsearch auto-commit+push, 30s timeout), `vault_sync.py` (third
SessionEnd hook; vault auto-commit+push to forgejo.swansoncloud.com/jared/SecondBrain, 30s SessionEnd hook; vault auto-commit+push to forgejo.swansoncloud.com/jared/SecondBrain, 30s

View File

@ -238,7 +238,10 @@ pushes the vault to `forgejo.swansoncloud.com/jared/SecondBrain` on every sessio
daily journal note from `session_end.py` is included in the commit. Mirrors the daily journal note from `session_end.py` is included in the commit. Mirrors the
`memsearch_sync.py` pattern; uses `cfg.vault_path`. Smoke-tested (exit 0) 2026-06-15. `memsearch_sync.py` pattern; uses `cfg.vault_path`. Smoke-tested (exit 0) 2026-06-15.
**Remaining (optional):** SessionStart `git pull` for multi-machine freshness — push-only today. **Multi-machine freshness — DONE (2026-07-10):** `session_start.py` now runs a fast-forward-only
`git pull` (`timeout 10`, `--ff-only --quiet`) against both `cfg.vault_path` and
`cfg.memsearch_dir` before the staleness check. Silent on success; a quiet one-line stderr note
on failure (offline machine, diverged history) that never blocks or delays session start.
**Do not sync** the Graphify `graphify-out/` directories, Milvus caches, or the Ollama **Do not sync** the Graphify `graphify-out/` directories, Milvus caches, or the Ollama
models. Graphs are rebuilt per machine from the vault (the single source of truth). models. Graphs are rebuilt per machine from the vault (the single source of truth).

View File

@ -63,6 +63,25 @@ def _log(line):
pass pass
def _pull_repo(repo_dir):
"""Fast-forward-only pull for a sync'd repo. Silent on success; at most a
one-line stderr note on failure (offline machine must never block or
error out session start). Mirrors vault_sync.py / memsearch_sync.py push
style but in the opposite direction (Step 5b multi-machine freshness).
"""
if not os.path.isdir(os.path.join(repo_dir, ".git")):
return
try:
r = subprocess.run(
["timeout", "10", "git", "-C", repo_dir, "pull", "--ff-only", "--quiet", "origin", "main"],
stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, text=True,
)
if r.returncode != 0:
print(f"[session-start] pull skipped for {repo_dir} (offline or diverged)", file=sys.stderr)
except Exception:
print(f"[session-start] pull skipped for {repo_dir}", file=sys.stderr)
def _needs_rebuild(stamp, stale_days): def _needs_rebuild(stamp, stale_days):
if not os.path.exists(stamp): if not os.path.exists(stamp):
return True return True
@ -80,6 +99,9 @@ def main():
cfg = config_mod.load_config() cfg = config_mod.load_config()
_pull_repo(cfg.vault_path)
_pull_repo(cfg.memsearch_dir)
_emit_usage_note() _emit_usage_note()
if os.environ.get("OS_VAULT_SKIP_REBUILD"): if os.environ.get("OS_VAULT_SKIP_REBUILD"):