Help us improve
Share bugs, ideas, or general feedback.
From purlin
Loads spec rules and dependencies, then implements features from specs. Useful for structured feature development with visual references and external sources.
npx claudepluginhub rlabarca/purlin --plugin purlinHow this skill is triggered — by the user, by Claude, or both
Slash command
/purlin:buildThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Read a spec, load all its rules (including from `> Requires:` dependencies), and implement the feature.
Provides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Guides systematic root-cause debugging when tests fail, builds break, or unexpected errors occur. Provides a structured triage checklist to preserve evidence, localize, and fix issues instead of guessing.
Share bugs, ideas, or general feedback.
Read a spec, load all its rules (including from > Requires: dependencies), and implement the feature.
purlin:build <name> Build a feature from its spec
purlin:build Resume building the current feature
specs/**/<name>.md.RULE-N entries from ## Rules and all PROOF-N entries from ## Proof.> Requires: specs (including anchors in specs/_anchors/). Extract their RULE-N and PROOF-N entries too — both rules and proof descriptions are needed for implementation.> Source:, read the external reference:
get_design_context and get_screenshot via Figma MCP for full visual fidelity.> Visual-Reference: field, load the visual reference at full fidelity:
figma://fileKey/nodeId → call get_design_context and get_screenshot MCP tools./path/to/image.png → read the image file./path/to/file.html → read the HTML filehttps://url → take a screenshot via Playwright MCP if availableVisual reference loaded from: <source>Display the combined rule set with proof descriptions:
Building: <name>
Own rules: RULE-1, RULE-2, RULE-3
Required from api_rest_conventions:
RULE-1: All endpoints return JSON with {data, error, meta} envelope
PROOF-1: GET /endpoint returns {data: ..., error: null, meta: {}}
Required from design_modal:
RULE-1: Implementation must visually match the Figma design
PROOF-1: Screenshot comparison against Figma reference @e2e
Visual reference loaded from: figma://ABC123/1:234
Scope: src/auth.js, src/auth.test.js
Write code that satisfies all rules. Use > Scope: paths as guidance for where to write.
> Visual-Reference::
Write tests that prove each rule. Use proof markers so the test runner emits proof files. For marker syntax (pytest, Jest, Shell), see references/formats/proofs_format.md.
Each RULE must have at least one PROOF — both own rules AND required rules. For required rules, use the required spec's feature name in the proof marker, not your own feature name:
# Own rule — uses YOUR feature name
@pytest.mark.proof("login", "PROOF-1", "RULE-1")
# Required rule from api_rest_conventions — uses THE ANCHOR's name
@pytest.mark.proof("api_rest_conventions", "PROOF-1", "RULE-1")
Tier review (mandatory before running tests):
Review every proof marker just written. Apply tier heuristics from references/spec_quality_guide.md:
@integration@e2e@manualIf ANY proof marker is missing a tier tag and the test clearly isn't unit tier (it calls subprocess, hits a network endpoint, etc.), add the tag before running.
After writing tests, ALWAYS spawn a purlin-auditor teammate to review proofs. Do NOT audit your own tests in the same context — the auditor must be independent. This applies regardless of the number of proofs.
The iteration loop is: write code → write tests → run purlin:unit-test → read coverage output → fix → repeat. The loop does NOT end until coverage output shows PASSING for the target feature (all rules proved). PARTIAL means more tests are still needed.
purlin:unit-test <name> # runs tests, emits proofs, calls sync_status, reports coverage
purlin:unit-test handles test framework detection, proof file emission, freshness checks, and sync_status. Calling sync_status after tests is not optional — purlin:unit-test does this automatically. Do NOT call sync_status separately — it would be redundant. Read the coverage output from purlin:unit-test and follow any → directives for uncovered rules.
When a test fails, diagnose the root cause before fixing:
Never weaken an assertion to make it pass. If assert response.status == 401 fails because the code returns 200, the code is wrong. See references/spec_quality_guide.md "When Tests Fail" for the full diagnostic guide.
Assertion change detection (mandatory after fixing a failing test):
After fixing a failing test, before running tests again, re-read the original proof description from the spec's ## Proof section. Verify the assertion still matches. If the fix changed WHAT the test asserts (different status code, different field, looser check), flag it:
WARNING: Assertion for PROOF-2 (RULE-2) was changed during iteration.
Original proof description: "POST invalid password; verify 401"
New assertion: assert status == 400
Reason: API returns 400 for validation errors, not 401. Spec rule may need updating.
→ Run: purlin:spec <name> to review RULE-2
If you changed what a test asserts (not just how), the proof description in the spec may be wrong. The commit message MUST explain why the assertion changed.
After the build/test loop reaches a stable state (all rules pass), output the changeset summary as a visible block in your response to the user. This is the engineer's primary review artifact — it must be visible in the conversation, not buried silently in git history. The same text is then reused as the commit message body in Step 6.
The summary has three sections:
Changeset — maps each rule to the file(s) and line(s) where it was implemented, with a one-line description of the change. Every rule addressed in this session must appear. Format: RULE-N → file:line description. Rules satisfied by existing code (no changes needed): RULE-N → (already satisfied). Rules mapping to multiple files get multiple lines.
── Changeset ──────────────────────────────────────
RULE-1 → src/auth.py:34 Added sanitize_input() before query
RULE-2 → src/auth.py:71 Sliding window rate limiter (60/min)
tests/test_auth.py:12 2 proofs covering RULE-1 and RULE-2
Decisions — judgment calls where the agent chose between alternatives. Only genuine decisions, not mechanical translations. If there are no judgment calls: (No judgment calls — all rules had unambiguous implementations).
── Decisions ──────────────────────────────────────
• Middleware pattern over inline validation for RULE-1 — reusable across routes
• 60 req/min hardcoded — spec says "rate limit" with no threshold
Review — curated list of areas where the engineer should focus attention. Flag security-sensitive code, spec ambiguities, performance-critical paths, and anything non-obvious. Not a list of every change — just the parts that need human eyes. If nothing notable: (No notable risk areas — straightforward implementation).
── Review ─────────────────────────────────────────
→ src/auth.py:45 Regex for SQL injection — security-sensitive
→ Spec gap: RULE-3 says "rate limit" but doesn't specify the window size
Corner cases:
RULE-N → (already satisfied) for each ruleWhen spawned by the auditor to fix HOLLOW or WEAK proofs:
PROOF-N → file:line description of fix. Skip the Decisions section — proof fixes are mechanical, not judgment calls.Do NOT weaken assertions to satisfy audit — if the audit says a proof is HOLLOW because it mocks bcrypt, replace the mock with real bcrypt. Don't remove the assertion. If fixing a proof requires changing the spec rule (because the rule is wrong), report the issue: "RULE-N in needs updating — ."
After the changeset summary, commit all changed files. Use the changeset summary as the commit message body per references/commit_conventions.md ("Build Commit Body"):
git add <source files> <test files> specs/**/*.proofs-*.json
git commit # message body = changeset summary from Step 5
Do NOT commit after each failed iteration — only when stable. Do NOT defer the commit to a later step. Uncommitted proof files are invisible to drift detection and verification.
The build is NOT complete until all of the following are true. Verify each one before responding to the user.
purlin:unit-test run shows the target feature as PASSING or better.git status. If any source files, test files, or specs/**/*.proofs-*.json files are uncommitted, commit them now using the changeset summary as the commit message body per Step 6.git status must not show any modified or untracked .proofs-*.json files. These are invisible to sync_status until committed.