CLAUDE.md · git:20260920.2a23c63 · 2026-09-20 · sha256 2c1425c26b1932b2
CLAUDE.md git:20260920.2a23c63A
Immutable. This exact content is served forever at /api/v1/blob/2c1425c26b1932b2.
# sdlc-graph-engineering — repository guide
**A bundle of three Claude Code plugins, not a catalog.** Two of them are one product in two halves
— `sdlc-graph` (the SDLC as a guarded graph) and `sdlc-graph-viewer` (the page that renders a run).
The third, `sdlc-graph-engineering-install`, is the method that produces a graph for any *other*
process. The `plugins/<name>/` layout and the root `marketplace.json` exist because installing from
a repository requires a manifest — the manifest is a bundle, not a catalog.
## Sources of truth
- Bundle manifest: `.claude-plugin/marketplace.json` · plugin manifests: `plugins/*/.claude-plugin/plugin.json`
- The graph model: `plugins/sdlc-graph/skills/sdlc-graph/graph/{nodes,edges,state}.md`
- **A plugin's own rules live with the plugin.** Before changing the graph, read
[`plugins/sdlc-graph/docs/EDITING.md`](plugins/sdlc-graph/docs/EDITING.md) — it comes before this
file, and testing is [`plugins/sdlc-graph/docs/TESTING.md`](plugins/sdlc-graph/docs/TESTING.md).
- **The tools the graph dispatches to are listed once**, in
[`plugins/sdlc-graph/docs/DEPENDENCIES.md`](plugins/sdlc-graph/docs/DEPENDENCIES.md) — all of them
third-party and optional, `git` being the only hard requirement. `/sdlc-graph:onboarding` walks
that file; a second roster anywhere else is a check failure.
- A skill directory groups files by kind (`graph/ nodes/ evals/ …`) — never a pile.
## Rules that bind every change here
- **A plugin references nothing above its own root.** No `../`; `${CLAUDE_PLUGIN_ROOT}` for absolute
paths. A plugin that reads above its root breaks on install.
**One legal exception:** the viewer's `evals/lib/paths.py` reads the sibling graph spec, because
the viewer holds a copy of the transition table and is the only side allowed to look. That is why
both plugin directories must keep their names.
- **Twinned skills.** The eight `nodes/*-node.md` carry a `copied-from:` header naming a source that
does not live in this repository. They are *meant* to diverge — `plan-guidelines-node.md` has its
milestone loop removed because the graph drives that loop; `pr-mr-prepare-node.md` has its quality
step removed because Gate A and Gate B are their own nodes. "Make them identical" is the wrong
default.
- **Bump `version` in BOTH manifests** — `plugin.json` and `marketplace.json`. They must agree, and
installed users only receive a change if it moves.
- **Re-check published counts.** Node, edge, cycle and stop counts appear in `SKILL.md`, both
READMEs, the manifests and the rendered HTML. They drift silently, and they are the first thing a
reader uses to decide whether the docs are current. `published-counts-match-reality` checks them.
- **Validate before opening a PR:** `claude plugin validate ./plugins/<plugin> --strict` and
`claude plugin validate .`
- **`main` is PR-only.** Push a side branch and open a PR.
- The repo is public: no private paths, hostnames or internal repo names — **and no name of an
unpublished plugin.** `sdlc-graph` live-dispatches *the project's installed* coding-standards and
local-stack skills; it names none of them anywhere, in any file, and requires none.
`no-unpublished-plugin-name-ships-in-this-plugin` in `evals/spec/spec_consistency.py` fails on one
that comes back.
- Keep this file under 100 lines; describe current state, not history.
## The evals — what runs when
```bash
git config core.hooksPath .githooks # once per clone
python3 plugins/sdlc-graph/skills/sdlc-graph/evals/run_all.py # 9 suites
python3 plugins/sdlc-graph-viewer/skills/view-run/evals/run_all.py # 8 suites
python3 .claude/hooks/run-graph-evals.selftest.py # the hook still routes
python3 .claude/hooks/pre-push-eval-gate.selftest.py # the gate can still fail
python3 plugins/sdlc-graph-viewer/skills/view-run/evals/sync/sync_graph.py --write # regenerate the
# viewer's GRAPH from the spec
```
- **`.claude/hooks/run-graph-evals.py` (PostToolUse)** runs the affected suites after every
Edit/Write under a graph plugin. **Editing the graph also fires the viewer's suite** — a guard
edited in `edges.md` is exactly what makes the viewer's copy stale. The whole set costs ~3.6s.
- **`.claude/hooks/pre-push-eval-gate.py`** blocks a push carrying a red suite. It fires on any
pushed path under a directory whose name contains `sdlc`, so `sdlc-graph-engineering-install` is
in scope; it has no suite of its own, so such a push runs the routing self-test and nothing else.
`SDLC_SKIP_EVAL_GATE=1` overrides, loudly. A `PreToolUse` twin catches agents, who push through
Bash and never reach a git hook.
- **A red suite mid-change is expected** — it is the second half of the edit, not an error.
- **The executing tier is reported, never gated, and driven on demand:**
`python3 .claude/hooks/eval-receipts.py --list` (`--drive --only <id>` runs one). **This repo
carries no receipts** — the registry stayed with the fork this graph was promoted out of — so
every executing test reads `NO RECEIPT` and the gate passes anyway. That is honest: authoring the
case is mandatory, driving it is a decision someone makes with the minutes in front of them.
> `sdlc-graph` also ships **its own** hook (`plugins/sdlc-graph/hooks/hooks.json`), separate from
> the repo's: it snapshots every state a `--trace` run passes through. It fires in every project the
> plugin is installed in, so it self-gates to a no-op and never exits non-zero.
## Licensing
MIT, and every plugin manifest says so. Two node procedures derive from obra/superpowers (MIT) and
are covered by [`THIRD-PARTY-NOTICES.md`](THIRD-PARTY-NOTICES.md); each points at it from its own
header. **Anything new that is not first-party gets a row there in the same change.**