Skip to content

Latest commit

 

History

History
247 lines (200 loc) · 11.6 KB

CONTRIBUTING.md

File metadata and controls

247 lines (200 loc) · 11.6 KB

Contributing

Layout

AGENTS.md                     universal entry point — any agent starts here
.cursor/rules/                Cursor auto-discovery, points at AGENTS.md
.github/copilot-instructions.md  Copilot auto-discovery, same
.github/workflows/cross-platform.yml  repository gates and Windows/Linux/macOS regression tests
.github/workflows/release.yml    tag-gated build, attestation, and publication
.github/dependabot.yml        update proposals for actions, hooks, and Python tools
.pre-commit-config.yaml       this repository's own gates, frozen to commit SHAs
requirements-dev.in/.txt      hash-locked Python tools for local checks and CI
.coveragerc                   coverage settings and the enforced floor
.claude-plugin/               Claude Code manifests — packaging only
skills/
  project_setup/    SKILL.md — new-project scaffolding
  quality_retrofit/ SKILL.md — existing-codebase compliance
  feature_delivery/ SKILL.md — scoped feature delivery and safe resume
scripts/
  skill-root.ps1/.sh   resolves SKILL_ROOT from anywhere, no env needed
  detect-stack.ps1/.sh workspace inventory, emits JSON
  build-release.ps1     exact-commit archives and release metadata
  verify-format-safe.py  compares Python ASTs before and after formatting
  verify-delivery.py     read-only structural/evidence verification
  update-delivery.py     atomic candidate updates with conflict detection
  delivery/             shared first-party standard-library implementation
templates/          strict configs, copied into target projects
docs/               philosophy and extended reference

The workflows are vendor-neutral; only the packaging is not. The three SKILL.md files are plain Markdown that any agent can follow. Keep them that way — no vendor-set environment variables, no assumptions about slash commands or a particular runner.

Claude Code uses the skills/ layout, and each directory name becomes the skill segment of its namespaced command. This package preserves its established underscore naming convention. Keep each name: consistent with its directory and verify discovery after changes; do not rename existing entry points casually.

Paths inside the workflows use ${SKILL_ROOT} as a placeholder, resolved by the native scripts/skill-root.ps1 or scripts/skill-root.sh. Both locate themselves. Never reintroduce ${CLAUDE_PLUGIN_ROOT} as the only path source in a workflow file — it is unset for every other agent, so the path silently points at the wrong place.

The three auto-discovery files (AGENTS.md, .cursor/rules/, .github/copilot-instructions.md) are thin pointers on purpose. Put content in the workflow files, not in the pointers, to minimize duplicated text and drift.

Editing a skill

SKILL.md is a prompt, not documentation. It is read by a model that will act on it, so:

  • Be imperative. "Run X, then Y" beats "X should be run."
  • Give the reason with the rule. A model that knows why ASan and TSan cannot be combined will handle a case you did not anticipate. One that only knows the rule will not.
  • Show the failure. "Without -fno-sanitize-recover=all, a recoverable UBSan finding can report and continue" is worth more than "use this flag."
  • Keep the refusals explicit. The "Refuse to" section in quality_retrofit is load-bearing — it is what stops the skill deleting code it has not verified.

Skills cost context every time they load. New content should earn its tokens: prefer a sentence that changes behavior over a paragraph that restates a default.

Scan by responsibility before editing, read canonical implementations and their consumers, and enhance them. Record why any new path cannot reuse existing work; check for overlap again before completion. Keep the shared red-drill procedure in docs/RED-DRILLS.md and route all workflows to it rather than copying it. Apply docs/CODE-QUALITY.md for the affected languages. Record template canary commands, versions, intended diagnostics, restoration, and limitations in the dated docs/QUALITY-REVIEW.md when refreshing the guidance.

Editing a template

Every rule that is off needs a comment saying why, right next to it. A future reader must be able to tell "deliberately disabled, here's the tradeoff" from "nobody thought about this."

Before committing a template change, run its gate against deliberately broken code and confirm it still fires. A config that silently stops catching things is worse than no config — see the false-green argument in docs/PHILOSOPHY.md. tests/test_templates.py also parses every template the standard library can read and fails when a new template is not classified; it caught an XML comment that made Directory.Build.props unloadable, which no drill of an individual rule would have reached.

Editing the detect-stack scripts

Both implementations must return the same JSON contract. Each must:

  • Exit 0 always. A scan that finds nothing is not an error.
  • Emit valid JSON on stdout, unconditionally. All workflows inspect it.
  • Return {"error":"unreadable path"} for an invalid root and {"error":"scan failed"} for an internal inventory failure. Exit 0 is the transport contract, not proof that the scan succeeded.
  • Never write anything. It is a read-only inventory.
  • Stay fast on large trees — prune node_modules, target, .git, venv.

Test with:

./scripts/detect-stack.sh . | jq -e . >/dev/null && echo ok
& .\scripts\detect-stack.ps1 . | ConvertFrom-Json | Out-Null
& .\tests\cross-platform-smoke.ps1
& .\tests\cross-platform-smoke.ps1 -RedDrills
& .\tests\release-package-smoke.ps1

Run the smoke test in both PowerShell 7 and Windows PowerShell 5.1 before changing the PowerShell scripts. It covers self-location, environment override precedence, source counts, pruned directories, config detection, and the unreadable-path JSON contract. GitHub Actions repeats those checks on Windows, Linux, and macOS, then compares the Bash and PowerShell scanner inventories on both POSIX runners.

-RedDrills reuses the same smoke suite in a temporary copy of current source. It runs a passing baseline, separately breaks directory pruning (including its case-insensitive match), the explicit root override, the unreadable-path error, knip.jsonc detection, and the release workflow's SHA-pinned action, and requires each existing assertion to fail with a non-zero child exit. Every mutation is restored byte-for-byte before a fresh green run. The parent command fails on surviving mutations, wrong failures, timeouts, or failed restoration. CI uses this mode, which includes the normal suite; run it under both PowerShell 7 and 5.1 locally.

Keep mutation recipes aligned with observable contracts when implementation changes. A recipe that no longer applies must fail rather than silently skip. Behavioral drills do not replace exercising the skill on real tasks, and string checks on prompt wording do not prove an agent will follow the instructions.

The release-package smoke test requires a clean committed tree because it archives HEAD, not uncommitted files. Run it after the release commit. It builds into ignored dist/ under conflicting ambient time zones, requires byte-identical output, checks versioned archive roots, verifies every SHA-256 entry, and removes its test output.

Testing changes locally

claude plugin marketplace add /absolute/path/to/high-quality-projects-skill
claude plugin install high-quality-projects-skill@high-quality-projects-skill
# in an active Claude Code session, run /reload-plugins (or restart it)
claude plugin list

Exercise all three workflows against isolated realistic projects. Follow the behavioral protocol: fresh agent trials, independent product oracles, negative controls, and semantic review of actual artifacts. Reading a skill or matching its wording is not behavioral testing.

Delivery helpers use Python 3.12+ and only the standard library. The quality tools are hash-locked in requirements-dev.txt; install them into a virtual environment, then install the hooks (PowerShell hooks also need pwsh with PSScriptAnalyzer):

python -m venv .venv
.venv/bin/python -m pip install --require-hashes -r requirements-dev.txt   # Windows: .venv\Scripts\python.exe
.venv/bin/python -m pre_commit install

With that environment active, run before a change:

python -m pre_commit run --all-files
python -m coverage run -m unittest discover -s tests -p "test_*.py"
python -m coverage combine
python -m coverage report

The hooks run Ruff, both mypy scopes with the shipped mypy template, vulture, Bandit, ShellCheck, shfmt, PSScriptAnalyzer with the shipped settings, actionlint, zizmor, and staged-change secret scanning. The entry scripts and the tests import the delivery package under different module names, so mypy runs once for each. Coverage includes the command-line entry points run as child processes, and coverage report fails below the floor in .coveragerc.

CI runs the same hooks plus a full-history gitleaks scan, then the tests with coverage and both mypy runs on Windows, Linux, and macOS with Python 3.12 and 3.14. Its validator mutations reuse existing regression assertions. Outcome-oracle controls also reject broken behavior, vacuous tests, duplicated domain logic, premature implementation, and replayed side effects. Live agent trials are recorded separately; CI does not pretend to rerun a model.

After changing a pin in requirements-dev.in, regenerate the lock with the command in that file's header. Hook revisions are frozen to commit SHAs; refresh them with pre-commit autoupdate --freeze. Dependabot proposes both.

Releasing

Releases are tag-driven. Never upload a hand-built archive or create a GitHub Release before its tag workflow.

  1. Choose a SemVer version and update .claude-plugin/plugin.json.

  2. Add the matching top-level CHANGELOG.md section, update documented stable versions, and update the manifest-version assertion in tests/cross-platform-smoke.ps1.

  3. Commit with a clean tree, then run:

    $ReleaseVersion = (Get-Content -Raw '.\.claude-plugin\plugin.json' |
        ConvertFrom-Json).version
    & .\tests\cross-platform-smoke.ps1
    & .\tests\release-package-smoke.ps1
    & .\scripts\build-release.ps1 -Version $ReleaseVersion

    The builder writes to dist/ and replaces only its own earlier artifacts. It refuses an output directory that holds anything else, including subdirectories: keep other local evidence outside dist/, or pass -OutputDirectory dist/release-<version>.

  4. Push the commit and wait for Cross-platform package checks to pass.

  5. Create and push annotated tag v<manifest version>.

The tag starts .github/workflows/release.yml. It reuses all cross-platform gates, requires exact tag/manifest/changelog agreement, builds ZIP and tar.gz archives from the tagged Git tree, generates checksums and metadata, creates GitHub provenance attestations, then publishes the GitHub Release. Verify the published assets using docs/INSTALLATION.md before calling the release done.

Commits

Conventional Commits. Subject ≤ 50 chars, imperative mood.

feat: add Go to detect-stack language table
fix: eslint template must be .mjs, not .js
docs: explain why MSan needs instrumented libc++

Update CHANGELOG.md in the same commit as the change it describes. Track what actually happened — never list planned work as done.

Reporting a bug

Include: the skill invoked, what it did, what you expected, and the output of scripts/detect-stack.ps1 on PowerShell or scripts/detect-stack.sh on POSIX in the affected workspace.