Scaffold the os-git plugin with the OsGit namespace and backends #1587

Closed
opened 2026-11-06 16:51:24 +00:00 by jared · 2 comments
Owner

Problem

No plugins/os-git exists. Every later os-git ticket needs a plugin layout, a namespace, a backend seam, and a test fixture builder first.

Design: docs/os-git/use-cases-and-domain.md (design) and docs/os-git/call-site-checklist.md (call sites).

Settled by the user on 2026-11-06. Do not reopen these:

  1. The gem lives inside plugins/os-git now. Plugins load it by path:. It moves to ruby-gems later.
  2. git-context and os:worktree merge into os-git.
  3. An old wrapper is removed only after every reference is updated.
  4. Design order: use-cases, objects, interfaces. Write to an interface.
  5. os-context and os-vault will be Ruby. Their Python git sites are replaced by the port.
  6. Ticket tracking stays out of os-git.

Reproduce

n/a: new component.

Approach

Follow plugins/cc-architect/references/conventions/cc-os-naming.md: no commands/ dispatcher directory, no name: field in any SKILL.md frontmatter. Follow ADR-0166 (a Gemfile per plugin), ADR-0241 (one boot.rb, Zeitwerk is a hard dependency), and ADR-0302. Use the layout in design section 3.4. Use the default adapter that the spike (#1586) records. Use the namespace that the ADR (#1585) records.

Required work:

  1. Plugin manifest and Gemfile for plugins/os-git — check: present os-git in plugins/os-git/.claude-plugin/plugin.json
  2. Concept root and Zeitwerk boot — check: present OsGit in plugins/os-git/lib/os_git.rb
  3. OsGit::Backend interface documented as a shared contract test module, with a Fake adapter that records messages and returns canned values — check: present Fake in plugins/os-git/lib/os_git/backend/fake.rb
  4. Backend::Cli adapter that runs git -C <root> <argv> through Open3, raises OsGit::Error with argv, exit_status, and stderr, and kills the child on timeout — check: present Open3 in plugins/os-git/lib/os_git/backend/cli.rb
  5. No Backend::RubyGit: the spike (#1586) dropped it. The implementation-status leaf states why, in one line — check: present RubyGit in docs/implementation-status/os-git.md
  6. OsGit::Error and its subclasses NotARepository, CommandFailed, TimedOut, MergeConflict, MergeOpen, NotMerged, NotAncestor, NotClean, Diverged — check: present TimedOut in plugins/os-git/lib/os_git/error.rb
  7. OsGit::FailOpen.call(default:) { ... } for hook edges — check: present FailOpen in plugins/os-git/lib/os_git/fail_open.rb
  8. OsGit::Testing::ScratchRepo with create, write, commit_all, branch, add_worktree, repository — check: present ScratchRepo in plugins/os-git/lib/os_git/testing/scratch_repo.rb
  9. Implementation-status leaf with a History row, and a registry row — check: present os-git in docs/implementation-status.md
  10. Run bin/refresh-plugins after the change.

Acceptance criteria

  1. Given a fresh checkout, when bundle install and the plugin test runner run in plugins/os-git, then they pass.
  2. Given OsGit::Backend::Fake, when a domain object sends a message, then the fake records it and returns the canned value.
  3. Given OsGit::Backend::Cli on a ScratchRepo, when a command exits nonzero, then it raises OsGit::CommandFailed with argv, exit_status, and stderr.
  4. Given a command that exceeds its timeout, when Backend::Cli runs it, then it raises OsGit::TimedOut and no child process remains.
  5. Given a block that raises OsGit::Error, when FailOpen.call(default: []) runs it, then it prints one stderr line and returns [].
  6. Given ScratchRepo.create, when the block runs, then the repository has a fixed author identity and the files and commits that the block wrote.
  7. Given the plugin source, when it is searched for SKILL.md frontmatter, then no file has a name: field and no commands/ directory exists.
  8. Given the plugin cache, when bin/refresh-plugins has run, then plugins/os-git is loaded in a new session.

Owner sketch

module OsGit
  class Backend; end          # interface and shared contract test
  class Backend::Cli; end
  class Backend::Fake; end
  class Error < StandardError; end
  module Testing; class ScratchRepo; end; end
end

Blocking edges

Blocked by the ADR ticket (#1585) and the spike (#1586).

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: no shared git seam ← wrappers grew per plugin ← DESIGN (docs/os-git/use-cases-and-domain.md sections 3.3, 3.4, 4.2)
  • 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-git
  • Session: 1a5b2630-09c9-46cb-aee4-a5a98e2e7d24
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/1a5b2630-09c9-46cb-aee4-a5a98e2e7d24.jsonl
## Problem No `plugins/os-git` exists. Every later os-git ticket needs a plugin layout, a namespace, a backend seam, and a test fixture builder first. Design: `docs/os-git/use-cases-and-domain.md` (design) and `docs/os-git/call-site-checklist.md` (call sites). Settled by the user on 2026-11-06. Do not reopen these: 1. The gem lives inside `plugins/os-git` now. Plugins load it by `path:`. It moves to `ruby-gems` later. 2. `git-context` and `os:worktree` merge into `os-git`. 3. An old wrapper is removed only after every reference is updated. 4. Design order: use-cases, objects, interfaces. Write to an interface. 5. `os-context` and `os-vault` will be Ruby. Their Python git sites are replaced by the port. 6. Ticket tracking stays out of `os-git`. ## Reproduce n/a: new component. ## Approach Follow `plugins/cc-architect/references/conventions/cc-os-naming.md`: no `commands/` dispatcher directory, no `name:` field in any `SKILL.md` frontmatter. Follow ADR-0166 (a Gemfile per plugin), ADR-0241 (one `boot.rb`, Zeitwerk is a hard dependency), and ADR-0302. Use the layout in design section 3.4. Use the default adapter that the spike (#1586) records. Use the namespace that the ADR (#1585) records. Required work: 1. Plugin manifest and `Gemfile` for `plugins/os-git` — check: present `os-git` in `plugins/os-git/.claude-plugin/plugin.json` 2. Concept root and Zeitwerk boot — check: present `OsGit` in `plugins/os-git/lib/os_git.rb` 3. `OsGit::Backend` interface documented as a shared contract test module, with a `Fake` adapter that records messages and returns canned values — check: present `Fake` in `plugins/os-git/lib/os_git/backend/fake.rb` 4. `Backend::Cli` adapter that runs `git -C <root> <argv>` through `Open3`, raises `OsGit::Error` with `argv`, `exit_status`, and `stderr`, and kills the child on timeout — check: present `Open3` in `plugins/os-git/lib/os_git/backend/cli.rb` 5. No `Backend::RubyGit`: the spike (#1586) dropped it. The implementation-status leaf states why, in one line — check: present `RubyGit` in `docs/implementation-status/os-git.md` 6. `OsGit::Error` and its subclasses `NotARepository`, `CommandFailed`, `TimedOut`, `MergeConflict`, `MergeOpen`, `NotMerged`, `NotAncestor`, `NotClean`, `Diverged` — check: present `TimedOut` in `plugins/os-git/lib/os_git/error.rb` 7. `OsGit::FailOpen.call(default:) { ... }` for hook edges — check: present `FailOpen` in `plugins/os-git/lib/os_git/fail_open.rb` 8. `OsGit::Testing::ScratchRepo` with `create`, `write`, `commit_all`, `branch`, `add_worktree`, `repository` — check: present `ScratchRepo` in `plugins/os-git/lib/os_git/testing/scratch_repo.rb` 9. Implementation-status leaf with a History row, and a registry row — check: present `os-git` in `docs/implementation-status.md` 10. Run `bin/refresh-plugins` after the change. ## Acceptance criteria 1. Given a fresh checkout, when `bundle install` and the plugin test runner run in `plugins/os-git`, then they pass. 2. Given `OsGit::Backend::Fake`, when a domain object sends a message, then the fake records it and returns the canned value. 3. Given `OsGit::Backend::Cli` on a `ScratchRepo`, when a command exits nonzero, then it raises `OsGit::CommandFailed` with `argv`, `exit_status`, and `stderr`. 4. Given a command that exceeds its timeout, when `Backend::Cli` runs it, then it raises `OsGit::TimedOut` and no child process remains. 5. Given a block that raises `OsGit::Error`, when `FailOpen.call(default: [])` runs it, then it prints one stderr line and returns `[]`. 6. Given `ScratchRepo.create`, when the block runs, then the repository has a fixed author identity and the files and commits that the block wrote. 7. Given the plugin source, when it is searched for `SKILL.md` frontmatter, then no file has a `name:` field and no `commands/` directory exists. 8. Given the plugin cache, when `bin/refresh-plugins` has run, then `plugins/os-git` is loaded in a new session. ## Owner sketch ```ruby module OsGit class Backend; end # interface and shared contract test class Backend::Cli; end class Backend::Fake; end class Error < StandardError; end module Testing; class ScratchRepo; end; end end ``` ## Blocking edges Blocked by the ADR ticket (#1585) and the spike (#1586). ## 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: no shared git seam ← wrappers grew per plugin ← DESIGN (docs/os-git/use-cases-and-domain.md sections 3.3, 3.4, 4.2) - 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-git - Session: 1a5b2630-09c9-46cb-aee4-a5a98e2e7d24 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/1a5b2630-09c9-46cb-aee4-a5a98e2e7d24.jsonl
Author
Owner

Work started by the #1610 orchestrator. Branch: ticket-1587. Adapter per #1586: Backend::Cli and Fake; RubyGit dropped.

Work started by the #1610 orchestrator. Branch: ticket-1587. Adapter per #1586: Backend::Cli and Fake; RubyGit dropped.
Author
Owner

Resolution

Done: plugins/os-git exists: Backend seam with Cli and Fake, error family, FailOpen, ScratchRepo, status leaf, registry row, marketplace entries, plugin installed. Hand-finished after escalation unexpected_pass.

Evidence: fef44087. 27 runs, 77 assertions, 0 failures; rubocop clean. Design audit found 7 items, all fixed. claude plugin details os-git@local-plugins resolves. Implementation 242 abandoned as hand-finished.

Follow-ups: Clearing GIT_CONFIG_PARAMETERS in ScratchRepo goes into #1588. ScratchRepo fixed dates dropped: no test needs them.

## Resolution **Done:** plugins/os-git exists: Backend seam with Cli and Fake, error family, FailOpen, ScratchRepo, status leaf, registry row, marketplace entries, plugin installed. Hand-finished after escalation unexpected_pass. **Evidence:** fef44087. 27 runs, 77 assertions, 0 failures; rubocop clean. Design audit found 7 items, all fixed. claude plugin details os-git@local-plugins resolves. Implementation 242 abandoned as hand-finished. **Follow-ups:** Clearing GIT_CONFIG_PARAMETERS in ScratchRepo goes into #1588. ScratchRepo fixed dates dropped: no test needs them.
jared closed this issue 2026-11-06 17:45:57 +00:00
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#1587
No description provided.