From data-science
Enforces PolicyEngine coding standards, formatters, and CI requirements. Activates for linting/formatting issues, PR standards, and pre-commit checks. Covers Python 3.13, uv run, Jupyter Book 2.0, and create-pr workflow.
How this skill is triggered — by the user, by Claude, or both
Slash command
/data-science:policyengine-standards-skillThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use this skill to ensure code meets PolicyEngine's development standards and passes CI checks.
Use this skill to ensure code meets PolicyEngine's development standards and passes CI checks.
⚠️ MUST USE Python 3.13 - Do NOT downgrade to older versions
python --versionpyproject.toml to specify version requirements⚠️ ALWAYS use uv run for Python commands - Never use bare python or pytest
uv run python script.py, uv run pytest tests/python script.py, pytest tests/⚠️ MUST USE Jupyter Book 2.0 (MyST-NB) - NOT Jupyter Book 1.x
myst build docs (NOT jb build)make format or language-specific formattermake test to ensure all tests passCommon failure pattern:
User: "Create a PR and mark it ready when CI passes"
Claude: "I've created the PR as draft. CI will take a while, I'll check back later..."
[Chat ends - Claude never checks back]
Result: PR stays in draft, user has to manually check CI and mark ready
When creating PRs, use the /create-pr command:
/create-pr
This command:
Why this works: The command contains explicit polling logic that Claude executes, so it actually waits instead of giving up.
If the command isn't installed, implement the pattern directly:
# 1. Create PR as draft
# CRITICAL: Use --repo flag to create PR in upstream repo from fork
gh pr create --repo PolicyEngine/policyengine-us --draft --title "Title" --body "Body"
PR_NUMBER=$(gh pr view --json number --jq '.number')
# 2. Wait for CI (ACTUALLY WAIT - don't give up!)
POLL_INTERVAL=15
ELAPSED=0
while true; do # No timeout - wait as long as needed
CHECKS=$(gh pr checks $PR_NUMBER --json status,conclusion)
TOTAL=$(echo "$CHECKS" | jq '. | length')
COMPLETED=$(echo "$CHECKS" | jq '[.[] | select(.status == "COMPLETED")] | length')
echo "[$ELAPSED s] CI: $COMPLETED/$TOTAL completed"
if [ "$COMPLETED" -eq "$TOTAL" ] && [ "$TOTAL" -gt 0 ]; then
FAILED=$(echo "$CHECKS" | jq '[.[] | select(.conclusion == "FAILURE")] | length')
if [ "$FAILED" -eq 0 ]; then
echo "✅ All CI passed! Marking ready..."
gh pr ready $PR_NUMBER
break
else
echo "❌ CI failed. PR remains draft."
gh pr checks $PR_NUMBER
break
fi
fi
sleep $POLL_INTERVAL
ELAPSED=$((ELAPSED + POLL_INTERVAL))
done
# Important: No timeout! Population simulations can take 30+ minutes.
CRITICAL: Never say "I'll check back later" — the chat session ends. Always use the polling loop above to actually wait. Default to creating PRs as draft; only create as ready when user explicitly requests it or CI is already verified.
PolicyEngine follows TDD: write test first (RED), implement (GREEN), refactor. In multi-agent workflows, @test-creator and @rules-engineer work independently from the same regulations. See policyengine-core-skill for details.
Example test:
def test_ctc_for_two_children():
"""Test CTC calculation for married couple with 2 children."""
situation = create_married_couple(income_1=75000, income_2=50000, num_children=2, child_ages=[5, 8])
sim = Simulation(situation=situation)
ctc = sim.calculate("ctc", 2026)[0]
assert ctc == 4400, "CTC should be $2,200 per child"
# Python
make test # All tests
uv run pytest tests/ -v # With uv
uv run pytest tests/test_credits.py::test_ctc -v # Specific test
# React
make test # All tests
bun test -- --watch # Watch mode
make format or ruff format .ruff format --check .# Format all Python files
make format
# Check if formatting is needed (CI-style)
ruff format --check .
exceptbun run lint -- --fix && bunx prettier --write .bun run lint -- --max-warnings=0# Format all files
make format
# Or manually
bun run lint -- --fix
bunx prettier --write .
# Check if formatting is needed (CI-style)
bun run lint -- --max-warnings=0
src/config/environment.js pattern for env config (not REACT_APP_ env vars)CRITICAL: NEVER manually update CHANGELOG.md. Check which system the repo uses, then follow that system.
How to tell which system a repo uses:
changelog.d/ directory -- if yes, use towncrier (new system)changelog.d/ but the repo uses changelog_entry.yaml, use the legacy systemchangelog.d/ fragments)Used by: policyengine-skills, the generated policyengine-claude wrapper, and newer repos with a changelog.d/ directory.
echo "Description of change." > changelog.d/branch-name.added.md
Fragment filename format: {name}.{type}.md
Types: added (minor), changed (patch), fixed (patch), removed (minor), breaking (major)
GitHub Actions runs towncrier build on merge to compile fragments into CHANGELOG.md.
changelog_entry.yamlUsed by: policyengine-us, policyengine-uk, and other country model repos.
Create changelog_entry.yaml at repository root:
- bump: patch # or minor, major
changes:
added:
- Description of new feature
fixed:
- Description of bug fix
GitHub Actions automatically updates CHANGELOG.md and changelog.yaml on merge.
DO NOT (either system):
make changelog manually during PR creationCHANGELOG.md in your PRSee the parent PolicyEngine/CLAUDE.md for full git workflow, branch naming, commit message format, and common AI pitfalls (file versioning, formatter not run, env vars, wrong Python version). Key points:
make format before committingpolicyengine-package/
├── policyengine_package/
│ ├── __init__.py
│ ├── core/
│ ├── calculations/
│ └── utils/
├── tests/
│ ├── test_calculations.py
│ └── test_core.py
├── pyproject.toml
├── Makefile
├── CLAUDE.md
├── CHANGELOG.md
└── README.md
policyengine-app/
├── src/
│ ├── components/
│ ├── pages/
│ ├── config/
│ │ └── environment.js
│ └── App.jsx
├── public/
├── package.json
├── .eslintrc.json
├── .prettierrc
└── README.md
Standard commands across PolicyEngine repos:
make install # Install dependencies
make test # Run tests
make format # Format code
make changelog # Update changelog (automation only, not manual)
make debug # Start dev server (apps)
make build # Production build (apps)
See PolicyEngine/CLAUDE.md for full CI stability details (fork failures, rate limits, linting). Quick fixes: use make format before committing, use uv run pytest not bare pytest, create branches on PolicyEngine repos not forks.
When renaming a PolicyEngine repository, references to the old name are often hardcoded across the org. Follow this checklist to avoid broken links, builds, and embeds.
# Find every file in the org that mentions the old repo name
gh api "/search/code?q=org:PolicyEngine+OLD_REPO_NAME" --paginate | jq '.items[] | {repo: .repository.full_name, path: .path}'
Review every result -- some will be docs/changelogs (safe to update later), others will break builds if not updated before the rename.
| Location | What to look for | Example |
|---|---|---|
| GitHub Actions workflows | PUBLIC_URL, checkout paths, artifact names | PUBLIC_URL: https://policyengine.github.io/OLD_NAME |
| Iframe embeds in policyengine-app-v2 | src URLs in page components | app/src/pages/*.jsx referencing OLD_NAME.github.io |
| README badges and links | Shield.io badges, repo links |  |
| package.json / pyproject.toml | name, repository, homepage fields | "name": "old-name" |
| GitHub Pages URLs | Any URL containing policyengine.github.io/OLD_NAME | Links in docs, blog posts, other READMEs |
| CLAUDE.md | Repo-specific instructions that reference the old name | Paths, URLs, skill references |
| Import paths (Python) | Package name derived from repo name | from old_name import ... |
| Vercel / deployment configs | Project names, domain aliases | vercel.json, Vercel dashboard settings |
| policyengine-skills source | Skill files that reference the repo | Links in SKILL.md files across the canonical source repo |
If the renamed repo is embedded in another site (e.g., via iframe or GitHub Pages), both repos need updates:
PUBLIC_URL and any self-referencing URLs in workflows, configs, and docs.src URLs, links, and any CI that depends on the old name.policyengine.github.io/old-name will 404.gh api "/search/code?q=org:PolicyEngine+OLD_REPO_NAME" --paginate | jq '.total_count'
/PolicyEngine/CLAUDE.mdSee PolicyEngine repositories for examples of standard-compliant code:
npx claudepluginhub policyengine/policyengine-claude --plugin analysis-toolsEncodes human-readable governance policies into machine-executable JSON constraints for AI agents and CI pipelines to validate automatically. Outputs rule files in .ai/governance/.
Reviews PolicyEngine implementations for common issues: unnecessary wrapper variables, vectorization violations, hard-coded values, and test quality. Includes a priority checklist.
Guides monorepo design including structure patterns, package organization, polyrepo tradeoffs, dependency management, and scalable workspace configs.