Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .claude/skills/phase-loop/SKILL.md
Original file line number Diff line number Diff line change
@@ -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).
13 changes: 13 additions & 0 deletions .claude/skills/release/SKILL.md
Original file line number Diff line number Diff line change
@@ -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.
28 changes: 2 additions & 26 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
7 changes: 7 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -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
Loading