# Shipwright SDLC Framework

## WHAT
- **Purpose**: AI-powered SDLC pipeline built on Claude Code — from user description to deployed, tested, secured application
- **Architecture**: Monorepo of Claude Code plugins (skills + hooks + scripts)
- **Stack**: Python 3.11+ scripts, Claude Code plugin system, uv package manager

## Structure
```
plugins/                    # Claude Code plugins (one per SDLC phase)
  shipwright-run/           # Orchestrator (entry point)
  shipwright-project/       # Requirements decomposition (IREB)
  shipwright-design/        # UI mockups from IREB specs (HTML)
  shipwright-plan/          # Deep planning + external LLM review
  shipwright-build/         # TDD implementation
  shipwright-test/          # Testing (unit + smoke + Playwright E2E)
  shipwright-security/      # Scanner chain + remediation loop
  shipwright-deploy/        # Deployment (extensible flavors)
  shipwright-changelog/     # Git sync + changelog + PR
  shipwright-compliance/    # IREB traceability, RTM, SBOM, dashboard
  shipwright-iterate/       # Daily iteration (complexity-adaptive)
  shipwright-preview/       # Local browser preview
  shipwright-adopt/         # Brownfield onboarding (analyze an existing repo)
  shipwright-grade/         # Read-only Control Grade (A–F) for any repo (lead magnet)
# Command Center WebUI lives at github.com/svenroth-ai/shipwright-webui since v0.4.0
shared/                     # Shared across all plugins
  contracts/                # Cross-plugin public API (B8): compliance.py, iterate.py
  profiles/                 # Stack profile definitions (JSON) + deploy profiles
  templates/                # CLAUDE.md, .shipwright/agent_docs, CI templates
  prompts/                  # Shared subagent prompts (code_reviewer, iterate_reviewer)
  schemas/                  # JSON schemas (run_config v2)
  config/                   # Shared config (external_review.json)
  scripts/                  # Shared Python utilities
  tests/                    # Tests for shared scripts and hooks
  constitution.md           # ALWAYS / ASK FIRST / NEVER rules for all agents
scripts/                    # Top-level scripts (install.sh, verify-setup.sh)
docs/                       # User-facing docs (guide.md, hooks-and-pipeline.md)
integration-tests/          # Cross-plugin integration tests
CHANGELOG-unreleased.d/     # Pending changelog drop files (aggregated at release)
```

## HOW

### Development
```bash
uv sync                              # Install dependencies
uv run pytest tests/ -v               # Run tests for a plugin (from plugin dir)
uv run pytest integration-tests/ -v   # Run integration tests (from root)
uvx ruff@0.15.15 check .              # Bug-focused lint — GATING in CI (ci.yml)
```

**Lint is a hard CI gate.** `.github/workflows/ci.yml` runs `uvx ruff@0.15.15
check .` with no `|| true` / `continue-on-error`, so a lint failure blocks merge.
The ruleset is deliberately curated (Pyflakes + a few bug-class pycodestyle
rules, cosmetic rules omitted) and lives in the root `pyproject.toml`
`[tool.ruff.lint]` — run it locally before pushing. ruff is pinned (not a project
dependency) so a new release can't silently change the gate.

### Plugin Structure (each plugin follows this pattern)
```
plugins/shipwright-{name}/
  .claude-plugin/plugin.json          # Plugin metadata
  hooks/hooks.json                    # Claude Code hooks
  agents/                             # Subagent definitions (markdown)
  skills/{name}/SKILL.md              # Main skill definition (folder = slash command suffix)
  scripts/                            # Python scripts (checks, hooks, lib, tools)
  tests/                              # Plugin-specific tests
  pyproject.toml                      # Plugin dependencies
```

### Conventions
- All scripts invoked via `uv run`; hooks resolve paths via `${CLAUDE_PLUGIN_ROOT}`
- Env var prefix `SHIPWRIGHT_` (`SHIPWRIGHT_SESSION_ID` = unified session id;
  `SHIPWRIGHT_PLUGIN_ROOT` = active plugin dir); config-file prefix
  `shipwright_` (`shipwright_*_config.json`, written to the target project)

### Hooks & Pipeline Reference
`docs/hooks-and-pipeline.md` is the single source of truth for what fires when:
the context-loading matrix (who reads what), the artifact-write matrix (who
writes what), the hooks registry, config data flow, and between-phase actions.
**ALWAYS read it first** when working on any plugin. **Rule:** modifying a hook
(`hooks.json`), adding/removing a pipeline phase, changing phase validators or
between-phase actions, or changing what a plugin reads at startup means you MUST
update it in the same diff.

### When editing plugin-side files

Changes under `plugins/*`, `shared/scripts/`, or any `SKILL.md` file do
NOT auto-sync to the plugin cache at `~/.claude/plugins/cache/shipwright/`
that Claude Code uses at runtime. After `git push`, run:

```bash
bash scripts/update-marketplace.sh
```

Then verify with `uv run scripts/check_plugin_cache_sync.py --strict`.
Without the sync, plugin-side fixes land in the dev repo but never reach
runtime — that silently cost iterates 7-11 their fixes.

**Scope:** monorepo-only. End-users consuming the plugins on their own
projects run the installed versions and never need this.

**Full procedure + rationale:** `shared/prompts/writing-plugin.md`.

### Documentation Guide
`docs/guide.md` is the primary user-facing documentation (README.md is a
summary that links to it). **Rule:** a new skill, a changed skill
command/argument/flag, a pipeline-flow change, or a constitution change means
checking whether the guide needs an update — its Chapter 4 (phases),
Chapter 7.5 (constitution), Chapter 8 (quality gates) and Appendix B (command
reference) are the sections that go stale.

### Where documents live

**`docs/` holds hand-written instructions** — for users and for developers
alike. The test is not who reads it, but that someone *wrote* it and someone
*reads* it. `guide.md` and `hooks-and-pipeline.md` sit there as equals.

**`.shipwright/` holds the artifacts Shipwright itself keeps** — specs under
`.shipwright/planning/`, evidence under `.shipwright/compliance/`, architecture
and decision memory under `.shipwright/agent_docs/`. They come out of runs;
they are not composed by hand.

**A file that is neither belongs nowhere:**

- A **record of finished work** is deleted. Git history keeps it.
- A **generated file** is not filed among the hand-written ones. If it is
  committed at all, it lives **next to the source it is generated from**, so
  source and render are read together and the drift test has an obvious
  subject. `shared/config/gate_catalog.json` + `gate_catalog.md` is the shape.

For neither case is the answer "then put it under `.shipwright/`". The
`agent_docs`, `planning` and `compliance` trees are read as well — they are not
a parking lot for files nobody reads. Relocating an unread file into a read
directory is filing, not deciding.

### Testing
```bash
cd plugins/shipwright-build && uv run pytest tests/ -v   # single plugin
uv run pytest integration-tests/ -v                      # integration
```

**One test root per pytest process — a hard rule, enforced by the repo-root
`conftest.py` (exit 4).** Roots: `integration-tests`, `shared/tests`,
`shared/scripts/tests`, `shared/scripts/tools/tests`, each `plugins/*/tests`.
Each needs `scripts`/`lib`/`tools` to mean a *different* directory, and Python
caches whichever package loads first — `sys.path` order cannot fix it
(ADR-044). One root per invocation, one `--junitxml` per root; merge
afterwards. Why, plus the two gaps the guard does not cover:
`.shipwright/planning/iterate/iterate-2026-07-27-pytest-root-composition.md`.

## Context
- **Guide**: docs/guide.md (primary user-facing documentation)
- **Hooks & Pipeline**: docs/hooks-and-pipeline.md (context loading, hooks registry, between-phase actions)
- **Glossary**: shared/glossary.md (mandatory-read — shared vocabulary
  used by hooks, agents, subagents, and compliance audits — Allowlist,
  Ratchet, Anti-Ratchet, Producer, Action-Unit, Canon-Gate, …)

## Pre-commit hooks

Contributors must install the bloat anti-ratchet pre-commit hook
**once per clone**:

```bash
bash scripts/install-hooks.sh       # POSIX / Git-Bash on Windows
.\scripts\install-hooks.ps1         # PowerShell on Windows
```

This sets `git config core.hooksPath scripts/hooks` (idempotent;
refuses to overwrite an existing different value without `--force`).
The hook only blocks commits that ratchet an existing entry in
`shipwright_bloat_baseline.json` — new crossings are surfaced by the
Group H detective audit post-merge. See `shared/glossary.md` for the
terminology and `shared/scripts/lib/anti_ratchet.py` for the rule.

## Asking the user questions (plain language)

Every interactive question — clarification, choice, confirmation — must be
answerable by a **non-senior developer or a normal user** without decoding
jargon. The person answering may not know the internals.

- **Lead with the functional meaning:** what the choice changes about how the
  app behaves, not the implementation. "Should a deleted item be recoverable,
  or gone for good?" — not "soft delete with a tombstone flag, or hard delete?".
- **Gloss any unavoidable term** in parentheses (e.g. "idempotent — safe to run
  twice without doubling the effect").
- **Make options concrete and comparable:** each in plain words with its
  real-world trade-off ("A is simpler but slower to load; B is faster but adds
  a setup step"), never a raw technical menu.
- **Rule of thumb:** a product owner should be able to answer without asking
  "what does that mean?". If they couldn't, rewrite it.

This applies to every interactive question — clarifications, plan approvals,
design feedback, and remediation choices alike. It governs *phrasing only*;
the underlying rigor of the work is unchanged.
