From 4b4e08c89b0a773709469f48edfaa905d65535c6 Mon Sep 17 00:00:00 2001 From: jared Date: Fri, 10 Jul 2026 17:13:57 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20ADR-025=20=E2=80=94=20standard=20Ruby?= =?UTF-8?q?=20structure=20for=20cc-os=20plugins?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lib/ + single-dispatcher bin/, fail-soft errors, installed-gem-only dependencies, minitest with in-memory fakes; os-backlog is the template, os-adr retrofit opportunistic. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01BS59Nkb7fvJMFB9dZiwZ7H --- docs/memory-system/03-architecture-decisions.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/memory-system/03-architecture-decisions.md b/docs/memory-system/03-architecture-decisions.md index 9ded933..7e4f0c4 100644 --- a/docs/memory-system/03-architecture-decisions.md +++ b/docs/memory-system/03-architecture-decisions.md @@ -653,6 +653,22 @@ _Date: 2026-07-10_ - **Cross-references**: ADR-023 (os-backlog / ecosystem role); vault note `vault-backlog-pilot-plan` (notification policy v2); gem repo `~/dev/ruby-gems/planka` (tick delivery); Apprise docs https://appriseit.com/. - **Status**: Accepted 2026-07-10. Server deploy + P0 swap queued on the boards; expansion cards seeded at P2/P3. +## ADR-025 — Standard Ruby structure for cc-os plugins (lib/ + single-dispatcher bin/, fail-soft, installed-gem-only dependencies) + +_Date: 2026-07-10_ + +- **Context**: Two plugins ship Ruby (os-adr, os-backlog) and more will follow (os-notify is queued). implementation-status.md informally calls os-backlog's layout "the os-adr `lib/`+`bin/` pattern", but no ADR pins it, and the two plugins already diverge: os-adr uses one `bin/` script per verb (`adr-new`, `adr-find`, …) invoked as `ruby ${CLAUDE_PLUGIN_ROOT}/bin/adr-new`, inline `abort` error handling, stdlib only; os-backlog uses a single dispatcher `bin/os-backlog ` invoked without a `ruby` prefix, a centralized `fail_soft` helper, and a lazily `require`d installed gem (`planka-api`) guarded by `rescue LoadError`. Without a recorded standard, each new plugin re-decides and the surfaces drift. +- **Decision**: New cc-os plugin Ruby follows this standard (deviate only with a recorded reason): + 1. **Library**: `lib/.rb` + `lib//*.rb`, one `module ` namespace, loaded via `require_relative`, stdlib-first. No gemspec/Gemfile inside a plugin — plugins are source trees, not gems. + 2. **CLI**: a single dispatcher `bin/os-` with subcommands (os-backlog style), executable with a `#!/usr/bin/env ruby` shebang; skills invoke `${CLAUDE_PLUGIN_ROOT}/bin/os- ` with no `ruby` prefix. One entry point per plugin keeps skill docs uniform and namespaces the verbs. + 3. **Errors**: every user-reachable failure goes through one fail-soft helper printing exactly one line, `os-: `, exit 1. Never a raw stack trace from a skill-invoked path. + 4. **External gems**: consumed as *installed* gems, `require`d lazily inside the method that needs them with `rescue LoadError` → fail-soft. Never vendored, never source-coupled to a sibling repo, never shelled out to another gem's binary when the client classes suffice. Each required gem is named in the plugin README and implementation-status.md (installation is an environment prerequisite, so it must be discoverable). + 5. **Tests**: minitest, `tests/all.rb` + `tests/test_helper.rb`, with in-memory fakes for any network service so the suite runs on machines without the gem or credentials (os-backlog's `FakePlankaClient` is the template). +- **Rationale**: os-backlog is the newer, deliberate iteration of the pattern and its choices earned their place: the dispatcher scales without `bin/` sprawl, fail-soft is mandatory for network-touching plugins (ADR-023's hook-hygiene argument), and lazy installed-gem requires keep pure subcommands (e.g. `resolve`) working on machines where the gem is absent — verified in practice 2026-07-10. +- **Alternatives rejected**: **Per-verb bin scripts (os-adr style)** — fine at 4 verbs, sprawl at 9; grandfathered in os-adr, not worth a retrofit until os-adr is next touched for other reasons. **Packaging plugins as real gems** — adds release/version ceremony for code that ships by symlink + cache refresh (ADR-018). **Vendoring external gems** — duplicates code and hides the dependency instead of documenting it. +- **Cross-references**: ADR-023 (plugin boundaries, hook hygiene); implementation-status.md os-adr/os-backlog component sections; `cc-os-plugin-skill-naming-convention.md` (naming layer above this structural layer). +- **Status**: Accepted 2026-07-10. Applies to all new plugin Ruby; os-adr retrofit is opportunistic, not scheduled. + ## Rejected tools (summary) | Tool | Why rejected for our use |