# Spec: ADR Plugin Packaging

## Purpose

Defines how the `os-adr` plugin is packaged, named, installed, and structured: cc-os naming conventions, git-tracked source with a symlinked install, full three-place marketplace registration, plugin-relative hook wiring, and an object-oriented Ruby core with model-free deterministic tests.

## Requirements

### Requirement: Plugin naming and skill naming conventions
The plugin SHALL be named `os-adr` (kebab-case, `os-` prefix) and its skills SHALL use
verb-first kebab-case names invoked as `/os-adr:<verb>` (`create`, `init`, `migrate`, `find`),
with no `commands/` dispatcher directory — matching the cc-os plugin/skill naming convention.

#### Scenario: Skills register under verb-first names
- **WHEN** the plugin is installed and `claude plugin details os-adr@local-plugins` is run
- **THEN** the listed skills are `create`, `init`, `migrate`, and `find`, and no `commands/`
  directory exists in the plugin

### Requirement: Source of truth in cc-os with symlinked install
The plugin source SHALL live git-tracked at `cc-os/plugins/os-adr/` and be installed via a
symlink `~/.claude/plugins/os-adr` pointing at it, matching the `os-vault`/`os-doc-hygiene`/
`os-orchestration` pattern.

#### Scenario: Symlinked install
- **WHEN** installation is complete
- **THEN** `~/.claude/plugins/os-adr` is a symlink resolving to the git-tracked plugin directory

### Requirement: Full three-place marketplace registration
Installation SHALL update all three registration surfaces — the plugin directory/symlink, the
`local-plugins` marketplace manifest (`~/.claude/plugins/.claude-plugin/marketplace.json`), and
`settings.json` `enabledPlugins` — then refresh plugin-manager state via
`claude plugin marketplace update` and `claude plugin install`, and verify skill registration
with `claude plugin list` and `claude plugin details`.

#### Scenario: Skills resolve after install
- **WHEN** the registration procedure completes
- **THEN** `claude plugin list` shows `os-adr@local-plugins` enabled and its skills are
  invocable as slash commands in a fresh session

### Requirement: Hooks wired through plugin hooks.json
Plugin hooks SHALL be declared in `hooks/hooks.json` using `${CLAUDE_PLUGIN_ROOT}`-relative
commands (the `os-doc-hygiene` pattern), not by absolute paths in `settings.json`, so hook
registration cannot silently outlive or diverge from plugin registration.

#### Scenario: Hook path is plugin-relative
- **WHEN** the plugin's hook configuration is inspected
- **THEN** the SessionStart command references `${CLAUDE_PLUGIN_ROOT}` and no os-adr hook entry
  exists in `settings.json`

### Requirement: OO core with tested deterministic behavior
The plugin's core logic SHALL be implemented as an object-oriented Ruby library (small
single-responsibility classes, dependency-injected paths/IO, no globals) exposed through thin
CLI entry points, with a thin Python script only for the SessionStart hook. All deterministic
behaviors (ID assignment, index regeneration, shape detection, existence check, retrieval layers
1–2) SHALL have automated tests runnable without any LLM, including golden-example fixtures.

#### Scenario: Deterministic tests run model-free
- **WHEN** the plugin's test suite runs on a machine with no model access
- **THEN** all tests for ID assignment, index regeneration, detection, existence check, and
  frontmatter retrieval pass

#### Scenario: Skills delegate to CLI entry points
- **WHEN** a skill performs a mechanical operation (numbering, indexing, detection, filtering)
- **THEN** it does so by invoking a `bin/` CLI rather than re-implementing the logic in prompt
  instructions
