|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## What this is |
| 6 | + |
| 7 | +The Strata Documentation Engine generates and self-verifies documentation for the Strata project |
| 8 | +family (the SDK, Rails app template, infra template, `platform-cli`, and SDK-consuming apps like |
| 9 | +OSCER). It pulls source repos listed in `sources.md`, has Claude write per-source docs, then builds |
| 10 | +a frontmatter-derived index + graph and runs an adversarial verify→fix loop over each doc. |
| 11 | + |
| 12 | +There are two distinct layers, and they are edited very differently: |
| 13 | + |
| 14 | +1. **Generation layer** — non-deterministic, Claude-driven. The `generate-strata-docs` skill |
| 15 | + (`skills/generate-strata-docs/SKILL.md`) orchestrates two multi-agent `Workflow` runs. You |
| 16 | + *invoke* this; you rarely hand-edit its output. |
| 17 | +2. **Validation / graph layer** — deterministic Python in `scripts/`. This is the spine that |
| 18 | + enforces correctness and is what you'll most often edit and test. Output is reproducible from |
| 19 | + doc frontmatter alone. |
| 20 | + |
| 21 | +## Commands |
| 22 | + |
| 23 | +Requires Python 3.13. |
| 24 | + |
| 25 | +```bash |
| 26 | +# Python setup |
| 27 | +pip install -r scripts/requirements.txt pytest |
| 28 | + |
| 29 | +# Tests |
| 30 | +python -m pytest -v # all |
| 31 | +python -m pytest tests/test_lint_docs.py -v # one file |
| 32 | +python -m pytest tests/test_lint_docs.py::test_validate_doc_accepts_valid -v # one test |
| 33 | +python -m pytest -k frontmatter -v # by pattern |
| 34 | + |
| 35 | +# Lint / build pipeline (run in this order; each prints a *_OK sentinel on success) |
| 36 | +python -m scripts.lint_manifest # validates sources.md -> "MANIFEST_OK" |
| 37 | +python -m scripts.lint_manifest --json # emits parsed sources (used by the skill at runtime) |
| 38 | +python -m scripts.lint_docs # validates doc frontmatter + registry usage -> "DOCS_OK" |
| 39 | +python -m scripts.build_graph # writes docs/INDEX.md + docs/graph.json -> "GRAPH_OK" |
| 40 | +python -m scripts.source_delta --json # update mode: classify sources new/changed/unchanged/throttled/orphaned (skill passes --shas-file; --now/--min-age-days tune the staleness throttle) |
| 41 | +``` |
| 42 | + |
| 43 | +Scripts are run as modules (`python -m scripts.x`), not as files — `pyproject.toml` sets |
| 44 | +`pythonpath = ["."]` so `scripts/` and `tests/` resolve. |
| 45 | + |
| 46 | +## Architecture |
| 47 | + |
| 48 | +### The manifest drives everything |
| 49 | + |
| 50 | +`sources.md` is a markdown table (one row per source: `id`, `type`, `repo`, `ref`, `subpaths`, |
| 51 | +`notes`). A source's `type` selects a **profile** in |
| 52 | +`skills/generate-strata-docs/references/profiles/<type>.md` that tells the documenter agent how to |
| 53 | +treat that kind of repo. `lint_manifest` rejects a source whose `type` has no matching profile, so |
| 54 | +adding a new source type means adding both a manifest row and a profile file. |
| 55 | + |
| 56 | +### The skill orchestration (SKILL.md) |
| 57 | + |
| 58 | +``` |
| 59 | +Setup (clone sources to .sources/) → Run 1 DOCUMENT → build graph |
| 60 | + → Run 2 VERIFY→ADJUDICATE→FIX (per doc) → rebuild graph → CURATE → report |
| 61 | +``` |
| 62 | + |
| 63 | +- **Run 1** (`workflows/run-1-document.mjs`): one `general-purpose` agent per source, in parallel. |
| 64 | + Each reads `agents/source-doc.md` + its profile + the registries, then writes docs to |
| 65 | + `docs/sources/<id>/` and a distillation log to `.logs/<id>.distillation.md`. |
| 66 | +- **Run 2** (`workflows/run-2-verify-fix.mjs`): per doc, a bounded loop (`max_rounds`, default 2) of |
| 67 | + verify → adjudicate → fix using four agent roles (`agents/{verifier,adjudicator,fixer}.md`). A |
| 68 | + verifier finds claims unsupported by the source; an adjudicator confirms/rejects each by |
| 69 | + re-checking the source; a fixer edits only confirmed findings. Residual findings after the last |
| 70 | + round mark the doc `verified: needs-review`, with the audit trail in `docs/.verification/`. |
| 71 | +- The skill commits to the `Workflow` tool with **no fallback** — if `Workflow` is unavailable it |
| 72 | + stops and escalates. Subagents never talk to the user. |
| 73 | +- The skill runs in **full** mode (document every source) or **update** mode (only new + changed |
| 74 | + sources, computed by `scripts/source_delta.py`, with unchanged sources skipped); the invoking |
| 75 | + prompt picks the mode. See SKILL.md "Modes". Drift is detected by comparing each clone's resolved |
| 76 | + commit SHA against the `source_ref.ref` recorded in that source's existing docs. Update mode also |
| 77 | + **throttles**: a drifted source is re-documented only once its docs (`last_documented` date) are |
| 78 | + at least a week old, so a frequently-changing repo isn't re-documented every run. |
| 79 | + |
| 80 | +### Frontmatter is the single source of truth for the graph |
| 81 | + |
| 82 | +Every doc under `docs/sources/<id>/` starts with YAML frontmatter |
| 83 | +(contract: `references/doc-frontmatter-schema.md`). `build_graph.py` derives `docs/INDEX.md` and |
| 84 | +`docs/graph.json` *purely* from that frontmatter — never edit those two files by hand. |
| 85 | + |
| 86 | +Two cross-link axes connect docs, both resolved through **registries** (fenced kebab-case key lists |
| 87 | +that the linter parses): |
| 88 | + |
| 89 | +- **Feature axis** (`references/feature-keys.md`): an `sdk` doc *owns* a key via `feature_keys`; an |
| 90 | + `example` doc *uses* it via `demonstrates`. The graph builder resolves each `demonstrates` key to |
| 91 | + the owning SDK doc and emits an `example-of` edge. |
| 92 | +- **Platform axis** (`references/platform-components.md`): a doc *owns* a component id via |
| 93 | + `component_keys`; `platform-cli` docs declare `manages`, and app/infra docs declare |
| 94 | + `integrates_with`, both resolved to the owning doc (`manages` / `integrates-with` edges). |
| 95 | + |
| 96 | +`lint_docs` **hard-fails** on any `feature_keys`/`demonstrates`/`component_keys`/`manages`/ |
| 97 | +`integrates_with` value not present in its registry. So a doc may only reference a feature/component |
| 98 | +by a registry key — never by a not-yet-generated doc id. Adding a new feature/component means adding |
| 99 | +its key to the registry's fenced block first. |
| 100 | + |
| 101 | +### "Never silently drop" invariant |
| 102 | + |
| 103 | +The pipeline surfaces every gap instead of hiding it: a clone failure → the source is recorded |
| 104 | +**skipped** (not dropped); a registry-valid `demonstrates`/`manages` key that no doc owns → |
| 105 | +`build_graph` prints a `GAP:` line; unresolved verification findings → `verified: needs-review`; |
| 106 | +a `docs/sources/<id>/` whose source left `sources.md` → `source_delta` reports it **orphaned** |
| 107 | +(never auto-deleted); a drifted source documented less than a week ago → `source_delta` reports it |
| 108 | +**throttled** with a warning (skipped this run, not silently dropped). When editing the graph builder, linter, or delta classifier, preserve this — |
| 109 | +emit a visible record rather than discarding. |
| 110 | + |
| 111 | +## CI (`.github/workflows/`) |
| 112 | + |
| 113 | +- **lint.yml** (`scripts/`, `tests/`, `docs/`, `sources.md`, `skills/` changes): pytest → |
| 114 | + lint_manifest → lint_docs → **graph freshness** (`build_graph` then `git diff --exit-code` on |
| 115 | + `docs/INDEX.md` + `docs/graph.json`). The freshness check means **you must commit regenerated |
| 116 | + `INDEX.md`/`graph.json` whenever doc frontmatter changes**, or CI fails. |
| 117 | +- **generate-docs.yml**: **manual only** (`workflow_dispatch`). Runs the skill in **full** mode and |
| 118 | + opens a PR on `docs/full-regen`. For deliberate, maintainer-run full rebuilds — not a cron job, |
| 119 | + and not triggered by pushes/merges. |
| 120 | +- **update-docs.yml**: **manual only** (`workflow_dispatch`). Runs the skill in **update** mode |
| 121 | + (re-document only changed sources, full-generate brand-new ones, skip unchanged) and opens a PR |
| 122 | + on `docs/auto-update`. Both Claude workflows need `ANTHROPIC_API_KEY` and `SOURCES_READ_TOKEN` |
| 123 | + secrets. |
| 124 | + |
| 125 | +## Conventions & gotchas |
| 126 | + |
| 127 | +- `.sources/` (source checkouts) and `.logs/` (distillation logs) are runtime, gitignored |
| 128 | + directories — present during a skill run, absent in a clean tree. |
| 129 | +- `docs/.verification/` (per-doc findings) and `docs/.curation/improvements.md` (advisory process |
| 130 | + notes from the curator) are kept as audit trail under `docs/`. |
| 131 | +- `scripts/frontmatter.py` is the shared YAML-frontmatter parser used by both the linter and the |
| 132 | + graph builder — change it in one place. |
| 133 | +- The design spec and plan live in `docs/superpowers/{specs,plans}/`; section references like |
| 134 | + "§3.3" in the code and skill point back to that spec. |
0 commit comments