A GitHub template repository carrying the rossoctl documentation convention: a docs/
tree where every kind of document has one home and one lifecycle, with CI gates that keep the
convention from rotting.
Derived from the shape proven in rossoctl/moca, plus an
append-only docs/reports/ for measured implementation results.
Using this template? Click Use this template above, then follow Bootstrapping to replace the placeholders. Delete this section and everything above
---once done, replacing it with your project's own README.
docs/
├── README.md # the router + the lifecycle rules (read this first)
├── <project>-overview.md # executive overview — all essential info, kept current
├── glossary.md
├── specs/ # deep designs — revised in place, versioned
│ ├── README.md # the registry: canonical ids + status
│ └── 0000-spec-template.md
├── adrs/ # one decision per file — permanent, immutable
│ ├── README.md
│ └── 0000-adr-template.md
├── reports/ # what a phase built and measured — append-only
│ ├── README.md
│ └── 0000-report-template.md
├── demos/ # performed walkthroughs, in acts
│ └── README.md
├── notes/ # investigations, runbooks, dead ends
│ └── README.md
└── plans/ # LOCAL ONLY — gitignored, CI-enforced
└── README.md
Plus: CLAUDE.md (agent instructions for the convention), GOVERNANCE.md (pointers to the
org-level documents), LICENSE, CODEOWNERS, CONTRIBUTING.md, the org lint configs, and a
docs CI workflow.
| Artifact | Answers | Lifecycle |
|---|---|---|
| Overview | the whole picture, briefly | kept current |
| Spec | what & why, in depth | revised in place, versioned |
| ADR | one decision + consequences | permanent, immutable |
| Report | what was built and measured | append-only, never rewritten |
| Demo | how to show it working | updated when commands change |
| Note | an investigation, runbook, dead end | dated, left as-is |
| Plan | how, in what order | local-only — written, run, deleted |
The two distinctions that carry the most weight:
- A plan is never committed. Code is the source of truth for how; a committed plan is a stale copy of the code that misleads. Write it → execute it → delete it.
- Specs revise, reports do not. A spec says what will be true and is corrected as you learn. A report says what was true when measured, and is never retro-edited — because its most valuable content is the record of a prediction that turned out wrong.
After creating your repo from this template:
# 1. Replace the placeholders. <PROJECT> is the repo name; <TEAM> the owning GitHub team.
grep -rl --exclude-dir=.git -e '<PROJECT>' -e '<TEAM>' . \
| xargs sed -i '' -e 's|<PROJECT>|myrepo|g' -e 's|<TEAM>|myrepo-maintainers|g'
# (GNU sed: use `sed -i` without the '' argument)
# 2. Rename the overview to match.
git mv docs/PROJECT-overview.md docs/myrepo-overview.md
# 3. Install the hooks — the commit-msg one enforces the Assisted-By trailer.
pre-commit install && pre-commit install --hook-type commit-msg
# 4. Check it all still hangs together.
./scripts/check-no-committed-plans.sh && ./scripts/check-doc-headers.sh
# 5. Protect `main`. DO THIS BEFORE THE FIRST PULL REQUEST.
# A template copies files, not settings, so `main` starts wide open — which is how
# several rossoctl repos ended up merging PRs with nothing required at all.
./scripts/protect-main.sh # one review + DCO
./scripts/protect-main.sh --checks DCO,lint,test # once CI exists, require it tooStep 5 is the one people skip, and it is the only step whose absence is invisible:
nothing fails, PRs just merge unreviewed. scripts/protect-main.sh --dry-run prints the
request without sending it, and --list-checks shows the contexts seen on both main and
the latest PR as a starting point — but only require a check that runs on
pull_request. A push- or schedule-triggered context never reports on a PR, so the PR
waits for it forever. A context that appears only in the PR column is pull_request-
triggered; one that appears only on main is not, and requiring it will wedge every PR.
Then:
- Fill in
docs/<project>-overview.md— it is the front door. - Confirm
@rossoctl/<TEAM>inCODEOWNERSexists and has write access, or GitHub ignores the file silently. - Add your language's ignores to
.gitignoreand hooks to.pre-commit-config.yaml(both have a marked section at the end). - Replace this README.
Paths inside the templates that are still placeholders (`<file>.md`, `<topic>-report.md`)
are written as inline code, not links, so the link checker has nothing unresolvable to
chase while the template is still a template. Turn them into real markdown links as you fill
them in — CI then keeps them honest.
Issue templates, the PR template and the org-wide security policy are inherited
automatically from rossoctl/.github, so they are not
duplicated here. LICENSE and CODEOWNERS are included, because GitHub does not inherit
those — a repo without a LICENSE is all-rights-reserved whatever the org intends.
.github/workflows/docs.yml runs on every PR:
| Gate | What it catches |
|---|---|
| No committed plans | a plan slipped into a PR; .gitignore alone is advisory |
| Doc headers | a spec without Version:/Status:, an ADR without Deciders:, a report without its "not verified" section |
| Markdown lint | structural markdown problems (org .markdownlint-cli2.yaml) |
| Link check | relative cross-links broken by a rename (org .lychee.toml) |
Assisted-By: Claude Code