From a322d2f195787001dfa45d00ca6db9d2aec2d462 Mon Sep 17 00:00:00 2001 From: Marsh Macy Date: Sun, 26 Jul 2026 13:23:46 -0700 Subject: [PATCH] Load the agent guide via CLAUDE.md and lazy-load its runbooks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude Code reads CLAUDE.md, not AGENTS.md, so this repo's agent guide was never actually reaching a session. Add a root CLAUDE.md that imports it with @AGENTS.md. That alone would put the whole guide in context on every session, so the two sections that only matter during a specific task move to skills, which load on invocation instead: - .claude/skills/phase-loop/ — planning, implementing, the rubber-duck loop - .claude/skills/release/ — the release runbook Each section keeps a one-line stub in AGENTS.md naming the skill file, so a tool that only reads AGENTS.md can still follow the pointer. The golden-file trap stays in the stub verbatim — regenerating a golden for a version bump is a defect by definition, and phase 8's plan put it in the runbook precisely because it is the "fix" a contributor reaches for unprompted. Everything else stays always-loaded: determinism, layering, events, commands, schema discipline, frozen data, greenfield discipline, and the OGL/MIT split. No content was lost; the sections were relocated verbatim. Claude-Session: https://claude.ai/code/session_012jzfBee7CQ43JBTwVihSjS --- .claude/skills/phase-loop/SKILL.md | 27 +++++++++++++++++++++++++++ .claude/skills/release/SKILL.md | 13 +++++++++++++ AGENTS.md | 28 ++-------------------------- CLAUDE.md | 7 +++++++ 4 files changed, 49 insertions(+), 26 deletions(-) create mode 100644 .claude/skills/phase-loop/SKILL.md create mode 100644 .claude/skills/release/SKILL.md create mode 100644 CLAUDE.md diff --git a/.claude/skills/phase-loop/SKILL.md b/.claude/skills/phase-loop/SKILL.md new file mode 100644 index 0000000..e00a433 --- /dev/null +++ b/.claude/skills/phase-loop/SKILL.md @@ -0,0 +1,27 @@ +--- +name: phase-loop +description: Run the osrlib phase loop — plan a roadmap phase, implement one, and drive the rubber-duck review to SOLID. Use when asked to work up a plan for phase N, implement the plan for phase N, or rubber-duck an artifact. +--- + +# The phase loop + +Each roadmap phase in `docs/spec.md` ships as two PRs — a plan, then an implementation — and both follow the same create → rubber-duck → revise-until-solid → PR loop. "Work up a plan for phase N" or "implement the plan for phase N" means run this loop end to end, unprompted. Precedent: PRs #2/#3 (phase 0) and #4 (phase 1 plan). + +## Planning a phase + +1. Research first: the phase's roadmap entry and every rules-scope item it touches in `docs/spec.md`, the prior phase plans in `docs/`, the existing code, and the SRD pages the phase consumes. Survey the actual SRD tables — filenames mislead (`srd/Weapons.md` is the *magic* weapons page; mundane weapons live in `srd/Weapons_and_Armour.md`), and parse hazards found during survey belong in the plan so the implementer doesn't rediscover them. +2. Write `docs/phase-N-plan.md` following the structure of the prior plans: intro with the spec milestone, scope (in and out, naming the phase that picks up each deferral), work items, sequencing, definition of done. Plans are decision-complete: every choice an implementer would otherwise guess at is pinned with a rationale, and RAW-ambiguous rules readings are called out as pinned interpretations destined for the `docs/adaptations.md` register. +3. Branch `phase-N-plan`; commit the draft as `add phase N implementation plan (pre-review draft)`. +4. Rubber-duck it (below), revise until SOLID, open the PR. + +## Implementing a phase + +The same loop on branch `phase-N-impl`: implement to the plan with tests green, commit, rubber-duck the result, and address findings as `address rubber-duck review findings`. The plan is the contract — when implementation reveals the plan was wrong or silent, amend the plan document on the same branch (`amend phase N plan: ...`) so plan and code never diverge. + +## The rubber-duck loop + +- Spawn a fresh subagent as a skeptical senior reviewer. Give it an ordered reading list — spec, prior plans, `AGENTS.md`, the artifact under review, the relevant code, and the exact SRD pages touched — and require evidence: every finding must quote the spec, the SRD, or the artifact, be ranked blocking vs non-blocking, and the review must end in a verdict (SOLID or NEEDS REVISION) plus a verified-good list of claims it actively checked. +- The reviewer's mandate covers design hygiene, not just rules and spec fidelity: it must hunt for the greenfield anti-patterns in `AGENTS.md` (back-compat shims, dual import paths, deprecation scaffolding, dead accommodation code) and flag any it finds — precedent: the `Alignment` re-export that survived one review round was cut when the human reviewer caught it on PR #8. +- Judge findings on the merits. Verify disputed rules readings against `srd/` yourself; push back on findings that are wrong instead of deferring to the duck. Address what survives and commit as `revise phase N plan per rubber-duck review` (or the address-findings message above). +- Send the revision back to the same reviewer, context intact, for re-verification of each fix. Loop until SOLID. Fold in any sign-off notes. +- Commits tell the honest story — draft, revision(s), sign-off tweaks — and the PR description summarizes the notable decisions plus the review provenance (what the duck found, what changed). diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md new file mode 100644 index 0000000..ff15b12 --- /dev/null +++ b/.claude/skills/release/SKILL.md @@ -0,0 +1,13 @@ +--- +name: release +description: Cut an osrlib release — version bump, changelog, the annotated tag that drives release.yml, the local dry run before tagging, and recovery when a release fails. Use when cutting a release, bumping the version, or working on release.yml. +--- + +# Releasing osrlib + +- The version lives in `pyproject.toml` alone; `osrlib.versioning.engine_version()` reads installed metadata at runtime. The bump procedure is: edit the version, run `uv lock`, and nothing else. The golden comparisons normalize the `engine_version` stamp, so **regenerating a golden file for a version bump is a defect by definition** — the goldens keep the stamp of the engine that produced them. +- A PR that changes user-visible behavior adds its bullet to the `[Unreleased]` section of `CHANGELOG.md` in the same PR. A release renames that section to the version and date. +- A release is an annotated `vX.Y.Z` tag on the merge commit (`git tag -a vX.Y.Z -m "osrlib X.Y.Z"`, then push the tag). `release.yml` does the rest: fails fast if the tag doesn't match the pyproject version, re-runs the full standing gate, builds once, audits the artifact with `tools/release/check_dist.py`, smoke-tests the wheel in a fresh venv on both OSes with `tools/release/install_smoke.py`, publishes to PyPI via trusted publishing (no tokens anywhere in the repository), and creates the GitHub Release from the tagged version's changelog section. +- The local dry run before tagging: `uv build`, then `uv run python tools/release/check_dist.py dist X.Y.Z`, then install the wheel into a fresh venv and run `tools/release/install_smoke.py X.Y.Z` with that venv's interpreter. +- Recovery: any failure before the publish job leaves PyPI untouched — delete the tag, fix on a branch, re-tag. Once publish succeeds, that version's filenames are burned on PyPI and the next attempt is a new version. +- Versioned documentation is not adopted; Pages-from-`main` is the whole deployment. The adoption trigger: the first post-1.0 release whose published docs must describe behavior different from `main` — practically, the first minor or major with behavior or API changes — adopts mike or equivalent in that release's own plan. Patch and docs-only releases do not trigger it. diff --git a/AGENTS.md b/AGENTS.md index 25aafca..2584d3f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,26 +11,7 @@ osrlib is a Python library implementing the Old-School Essentials (OSE) SRD rule ## The phase loop -Each roadmap phase in `docs/spec.md` ships as two PRs — a plan, then an implementation — and both follow the same create → rubber-duck → revise-until-solid → PR loop. "Work up a plan for phase N" or "implement the plan for phase N" means run this loop end to end, unprompted. Precedent: PRs #2/#3 (phase 0) and #4 (phase 1 plan). - -### Planning a phase - -1. Research first: the phase's roadmap entry and every rules-scope item it touches in `docs/spec.md`, the prior phase plans in `docs/`, the existing code, and the SRD pages the phase consumes. Survey the actual SRD tables — filenames mislead (`srd/Weapons.md` is the *magic* weapons page; mundane weapons live in `srd/Weapons_and_Armour.md`), and parse hazards found during survey belong in the plan so the implementer doesn't rediscover them. -2. Write `docs/phase-N-plan.md` following the structure of the prior plans: intro with the spec milestone, scope (in and out, naming the phase that picks up each deferral), work items, sequencing, definition of done. Plans are decision-complete: every choice an implementer would otherwise guess at is pinned with a rationale, and RAW-ambiguous rules readings are called out as pinned interpretations destined for the `docs/adaptations.md` register. -3. Branch `phase-N-plan`; commit the draft as `add phase N implementation plan (pre-review draft)`. -4. Rubber-duck it (below), revise until SOLID, open the PR. - -### Implementing a phase - -The same loop on branch `phase-N-impl`: implement to the plan with tests green, commit, rubber-duck the result, and address findings as `address rubber-duck review findings`. The plan is the contract — when implementation reveals the plan was wrong or silent, amend the plan document on the same branch (`amend phase N plan: ...`) so plan and code never diverge. - -### The rubber-duck loop - -- Spawn a fresh subagent as a skeptical senior reviewer. Give it an ordered reading list — spec, prior plans, this file, the artifact under review, the relevant code, and the exact SRD pages touched — and require evidence: every finding must quote the spec, the SRD, or the artifact, be ranked blocking vs non-blocking, and the review must end in a verdict (SOLID or NEEDS REVISION) plus a verified-good list of claims it actively checked. -- The reviewer's mandate covers design hygiene, not just rules and spec fidelity: it must hunt for the greenfield anti-patterns below (back-compat shims, dual import paths, deprecation scaffolding, dead accommodation code) and flag any it finds — precedent: the `Alignment` re-export that survived one review round was cut when the human reviewer caught it on PR #8. -- Judge findings on the merits. Verify disputed rules readings against `srd/` yourself; push back on findings that are wrong instead of deferring to the duck. Address what survives and commit as `revise phase N plan per rubber-duck review` (or the address-findings message above). -- Send the revision back to the same reviewer, context intact, for re-verification of each fix. Loop until SOLID. Fold in any sign-off notes. -- Commits tell the honest story — draft, revision(s), sign-off tweaks — and the PR description summarizes the notable decisions plus the review provenance (what the duck found, what changed). +Each roadmap phase in `docs/spec.md` ships as two PRs — a plan, then an implementation — and both follow the same create → rubber-duck → revise-until-solid → PR loop. "Work up a plan for phase N" or "implement the plan for phase N" means run this loop end to end, unprompted. The full runbook — planning, implementing, and the rubber-duck review loop — is `.claude/skills/phase-loop/SKILL.md`. ## Toolchain @@ -68,12 +49,7 @@ These are contracts, not suggestions — see the corresponding spec sections bef ## Releasing -- The version lives in `pyproject.toml` alone; `osrlib.versioning.engine_version()` reads installed metadata at runtime. The bump procedure is: edit the version, run `uv lock`, and nothing else. The golden comparisons normalize the `engine_version` stamp, so **regenerating a golden file for a version bump is a defect by definition** — the goldens keep the stamp of the engine that produced them. -- A PR that changes user-visible behavior adds its bullet to the `[Unreleased]` section of `CHANGELOG.md` in the same PR. A release renames that section to the version and date. -- A release is an annotated `vX.Y.Z` tag on the merge commit (`git tag -a vX.Y.Z -m "osrlib X.Y.Z"`, then push the tag). `release.yml` does the rest: fails fast if the tag doesn't match the pyproject version, re-runs the full standing gate, builds once, audits the artifact with `tools/release/check_dist.py`, smoke-tests the wheel in a fresh venv on both OSes with `tools/release/install_smoke.py`, publishes to PyPI via trusted publishing (no tokens anywhere in the repository), and creates the GitHub Release from the tagged version's changelog section. -- The local dry run before tagging: `uv build`, then `uv run python tools/release/check_dist.py dist X.Y.Z`, then install the wheel into a fresh venv and run `tools/release/install_smoke.py X.Y.Z` with that venv's interpreter. -- Recovery: any failure before the publish job leaves PyPI untouched — delete the tag, fix on a branch, re-tag. Once publish succeeds, that version's filenames are burned on PyPI and the next attempt is a new version. -- Versioned documentation is not adopted; Pages-from-`main` is the whole deployment. The adoption trigger: the first post-1.0 release whose published docs must describe behavior different from `main` — practically, the first minor or major with behavior or API changes — adopts mike or equivalent in that release's own plan. Patch and docs-only releases do not trigger it. +The version lives in `pyproject.toml` alone and the bump procedure is: edit the version, run `uv lock`, and nothing else. The golden comparisons normalize the `engine_version` stamp, so **regenerating a golden file for a version bump is a defect by definition** — the goldens keep the stamp of the engine that produced them. A PR that changes user-visible behavior adds its `CHANGELOG.md` bullet in the same PR. The full runbook — the annotated tag that drives `release.yml`, the local dry run, recovery, and the versioned-docs adoption trigger — is `.claude/skills/release/SKILL.md`. ## Licensing diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..40cdb80 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,7 @@ +# Claude Code entry point + +Claude Code loads this file automatically at the start of every session. The project's +agent guide lives in `AGENTS.md` so that other tools can read it too — this file just +imports it. + +@AGENTS.md