Add OsGit::Sync and replace the os-vault and memsearch git hooks #1590

Open
opened 2026-11-06 16:52:22 +00:00 by jared · 2 comments
Owner

Problem

plugins/os-vault/hooks/vault_sync.py and memsearch_sync.py are near copies. Both run add -A, commit, and push at SessionEnd. session_start.py runs a network pull --ff-only at SessionStart. session_context.py and session_end.py run rev-parse --show-toplevel on every prompt and at session end. The house rule bans add -A, and no object owns the exception.

Design: docs/os-git/use-cases-and-domain.md (design) and docs/os-git/call-site-checklist.md (call sites). Settled decisions are in design section 1 (docs/os-git/use-cases-and-domain.md): the gem lives in plugins/os-git with a path: source; git-context and os:worktree merge into os-git; old wrappers go only after all references are updated; design order is use-cases, objects, interfaces; os-context and os-vault become Ruby; ticket tracking stays out of os-git.

Reproduce

n/a: structural change. The call sites are rows G-051 to G-057 in the checklist.

Approach

Build OsGit::Sync (design section 3.1): Sync.new(repository, branch:, message:), #pull(timeout: 10), #publish(timeout: 30). Both return Outcome(:done | :skipped, reason) and raise OsGit::Error on failure. The hook edge wraps each call in OsGit::FailOpen.call(default:) so a hook never disturbs a session. Only Sync may stage everything. Commit::Plan stays path-only.

Q1 ruling (made by /os-me:predict and recorded in design section 8): Sync may stage everything inside the vault and memsearch repositories, only for a repository whose .gitignore the caller names (ADR-0015 whitelist .gitignore). The os-doc-hygiene WIP checkpoint does not use this exception.

The Ruby port of the os-vault hooks is ticket #1527. This ticket starts after #1527 ships, because Sync replaces the git code in the new Ruby hooks, and the Python files go with the port. Capture applies no state labels, so the wait is stated here and no waiting label is set.

Rows to mark in docs/os-git/call-site-checklist.md: G-051 to G-055 (replaced by Sync and RepoRoot), G-056 and G-057 (eval harness: replace with Repository or mark out of scope with a reason), and G-187 (plugins/os-vault/tests/generate-fixtures.sh).

Required work:

  1. OsGit::Sync with pull and publish — check: present publish in plugins/os-git/lib/os_git/sync.rb
  2. The Ruby SessionStart, SessionEnd, and UserPromptSubmit hooks of os-vault call Sync and RepoRoot and never run git themselves — check: absent git in plugins/os-vault/hooks/vault_sync.rb

Acceptance criteria

  1. Given a vault repository with changes and a remote, when Sync#publish runs, then it stages, commits with the message, and pushes, and returns :done.
  2. Given a vault repository with no changes, when Sync#publish runs, then it makes no commit and returns :skipped.
  3. Given a remote that does not answer within the timeout, when Sync#pull or #publish runs, then it raises OsGit::TimedOut, and the hook prints one stderr line and exits 0.
  4. Given a diverged remote, when Sync#pull runs, then it raises OsGit::Diverged, and the hook exits 0.
  5. Given the memsearch repository, when its hook runs, then it uses the same Sync object as the vault hook, and no second copy of the sequence exists.
  6. Given a repository with no .git, when the hooks run, then they skip silently, as the Python hooks did.
  7. Given the per-prompt hook, when it runs, then no git process starts (RepoRoot).
  8. Given rows G-051 to G-057 and G-187, when the ticket closes, then each row is marked - [x] or marked out of scope with a reason.

Owner sketch

class OsGit::Sync
  def initialize(repository, branch: "main", message:); end
  def pull(timeout: 10); end
  def publish(timeout: 30); end
end

Non-goals

The Ruby port of os-vault itself (#1527). The graphify rebuild hook (#1576).

Blocking edges

Blocked by #1527 (the Ruby port of the os-vault hooks has not shipped) and by the core ticket (#1588).

Origin

  • Trigger: User request in session 1a5b2630-09c9-46cb-aee4-a5a98e2e7d24 on 2026-11-06: one os-git gem for all local git work in cc-os and the user's projects.
  • Improvised this session: none
  • Chain: two near-identical sync scripts with add -A ← no owner for dedicated-repo sync ← DESIGN (docs/os-git/use-cases-and-domain.md section 2.3 UC-26 and section 8 Q1)
  • Root candidate: none found; searched open issues for git, worktree, octokit, ruby port, python, statusline, reset --hard. Related but not a match: #845, #853, #857, #1527, #1554.
  • Where: plugins/os-vault/hooks/
  • Session: 1a5b2630-09c9-46cb-aee4-a5a98e2e7d24
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/1a5b2630-09c9-46cb-aee4-a5a98e2e7d24.jsonl
## Problem `plugins/os-vault/hooks/vault_sync.py` and `memsearch_sync.py` are near copies. Both run `add -A`, commit, and push at SessionEnd. `session_start.py` runs a network `pull --ff-only` at SessionStart. `session_context.py` and `session_end.py` run `rev-parse --show-toplevel` on every prompt and at session end. The house rule bans `add -A`, and no object owns the exception. Design: `docs/os-git/use-cases-and-domain.md` (design) and `docs/os-git/call-site-checklist.md` (call sites). Settled decisions are in design section 1 (`docs/os-git/use-cases-and-domain.md`): the gem lives in `plugins/os-git` with a `path:` source; `git-context` and `os:worktree` merge into `os-git`; old wrappers go only after all references are updated; design order is use-cases, objects, interfaces; `os-context` and `os-vault` become Ruby; ticket tracking stays out of `os-git`. ## Reproduce n/a: structural change. The call sites are rows G-051 to G-057 in the checklist. ## Approach Build `OsGit::Sync` (design section 3.1): `Sync.new(repository, branch:, message:)`, `#pull(timeout: 10)`, `#publish(timeout: 30)`. Both return `Outcome(:done | :skipped, reason)` and raise `OsGit::Error` on failure. The hook edge wraps each call in `OsGit::FailOpen.call(default:)` so a hook never disturbs a session. Only `Sync` may stage everything. `Commit::Plan` stays path-only. Q1 ruling (made by `/os-me:predict` and recorded in design section 8): `Sync` may stage everything inside the vault and memsearch repositories, only for a repository whose `.gitignore` the caller names (ADR-0015 whitelist `.gitignore`). The `os-doc-hygiene` WIP checkpoint does not use this exception. The Ruby port of the os-vault hooks is ticket #1527. This ticket starts after #1527 ships, because `Sync` replaces the git code in the new Ruby hooks, and the Python files go with the port. Capture applies no state labels, so the wait is stated here and no `waiting` label is set. Rows to mark in `docs/os-git/call-site-checklist.md`: G-051 to G-055 (replaced by `Sync` and `RepoRoot`), G-056 and G-057 (eval harness: replace with `Repository` or mark out of scope with a reason), and G-187 (`plugins/os-vault/tests/generate-fixtures.sh`). Required work: 1. `OsGit::Sync` with pull and publish — check: present `publish` in `plugins/os-git/lib/os_git/sync.rb` 2. The Ruby SessionStart, SessionEnd, and UserPromptSubmit hooks of os-vault call `Sync` and `RepoRoot` and never run `git` themselves — check: absent `git` in `plugins/os-vault/hooks/vault_sync.rb` ## Acceptance criteria 1. Given a vault repository with changes and a remote, when `Sync#publish` runs, then it stages, commits with the message, and pushes, and returns `:done`. 2. Given a vault repository with no changes, when `Sync#publish` runs, then it makes no commit and returns `:skipped`. 3. Given a remote that does not answer within the timeout, when `Sync#pull` or `#publish` runs, then it raises `OsGit::TimedOut`, and the hook prints one stderr line and exits 0. 4. Given a diverged remote, when `Sync#pull` runs, then it raises `OsGit::Diverged`, and the hook exits 0. 5. Given the memsearch repository, when its hook runs, then it uses the same `Sync` object as the vault hook, and no second copy of the sequence exists. 6. Given a repository with no `.git`, when the hooks run, then they skip silently, as the Python hooks did. 7. Given the per-prompt hook, when it runs, then no `git` process starts (`RepoRoot`). 8. Given rows G-051 to G-057 and G-187, when the ticket closes, then each row is marked `- [x]` or marked out of scope with a reason. ## Owner sketch ```ruby class OsGit::Sync def initialize(repository, branch: "main", message:); end def pull(timeout: 10); end def publish(timeout: 30); end end ``` ## Non-goals The Ruby port of os-vault itself (#1527). The graphify rebuild hook (#1576). ## Blocking edges Blocked by #1527 (the Ruby port of the os-vault hooks has not shipped) and by the core ticket (#1588). ## Origin - Trigger: User request in session 1a5b2630-09c9-46cb-aee4-a5a98e2e7d24 on 2026-11-06: one os-git gem for all local git work in cc-os and the user's projects. - Improvised this session: none - Chain: two near-identical sync scripts with add -A ← no owner for dedicated-repo sync ← DESIGN (docs/os-git/use-cases-and-domain.md section 2.3 UC-26 and section 8 Q1) - Root candidate: none found; searched open issues for git, worktree, octokit, ruby port, python, statusline, reset --hard. Related but not a match: #845, #853, #857, #1527, #1554. - Where: plugins/os-vault/hooks/ - Session: 1a5b2630-09c9-46cb-aee4-a5a98e2e7d24 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/1a5b2630-09c9-46cb-aee4-a5a98e2e7d24.jsonl
Author
Owner

Skipped in the #1610 build: blocker #1527 is open. Rows G-051 to G-057 and G-187 are marked deferred in docs/os-git/call-site-checklist.md. OsGit::Sync is not built; the hooks stay as they are until the Ruby port.

Skipped in the #1610 build: blocker #1527 is open. Rows G-051 to G-057 and G-187 are marked deferred in docs/os-git/call-site-checklist.md. OsGit::Sync is not built; the hooks stay as they are until the Ruby port.
Author
Owner

Blocked by #1527 (open, P3): the os-vault hooks are still Python (plugins/os-vault/hooks/*.py), and OsGit::Sync replaces the git code in the Ruby port. Checklist rows G-051 to G-057 and G-187 already carry the deferral with that reason (docs/os-git/call-site-checklist.md). Remove waiting when #1527 ships.

Blocked by #1527 (open, P3): the os-vault hooks are still Python (`plugins/os-vault/hooks/*.py`), and `OsGit::Sync` replaces the git code in the Ruby port. Checklist rows G-051 to G-057 and G-187 already carry the deferral with that reason (`docs/os-git/call-site-checklist.md`). Remove `waiting` when #1527 ships.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jared/cc-os#1590
No description provided.