Help us improve
Share bugs, ideas, or general feedback.
From purlin
Detects spec drift by comparing code changes against verified specs, summarizing commits, file categories, spec changes, proof status, and broken scopes into a PM/QA/eng-readable report.
npx claudepluginhub rlabarca/purlin --plugin purlinHow this skill is triggered — by the user, by Claude, or both
Slash command
/purlin:driftThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Read-only skill. Calls the `drift` MCP tool for structured data, then interprets and formats the results into a PM/QA-readable report with actionable directives.
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-only skill. Calls the drift MCP tool for structured data, then interprets and formats the results into a PM/QA-readable report with actionable directives.
Classification criteria: see references/drift_criteria.md (Criteria-Version: 1).
purlin:drift Summarize changes since last verification
purlin:drift pm Summarize with PM priorities only
purlin:drift eng Summarize with engineer priorities only
purlin:drift qa Summarize with QA priorities only
purlin:drift --since <N> Last N commits
purlin:drift --since <date> Since a date (YYYY-MM-DD)
The role can be passed as a bare positional argument (purlin:drift pm) or as a flag (purlin:drift --role pm). Both are equivalent. If no role is given, show all three priority sections.
Drift detection compares committed git history. Uncommitted changes to specs, code, or proof files are invisible to drift. If sync_status warns about uncommitted changes, commit them first for accurate drift reporting.
drift(since: <from --since arg if given>, role: <from role arg if given, default "all">)
If the tool returns a JSON object with a recommendation field instead of the normal drift data, the project has no verification history and too many commits for drift tracking to be useful. Display:
No verification anchor found. This project has <commits_since_init> commits without verification history.
→ Use purlin:spec-from-code to generate initial specs from the existing codebase.
→ After specs exist and purlin:verify has run, purlin:drift will track ongoing changes.
Do not attempt to process the response as normal drift data. Return immediately after displaying this message.
Otherwise, the tool returns structured JSON containing:
since — human-readable description of the anchor pointcommits — list of one-line commit summariesfiles — each changed file with path, category (CHANGED_SPECS, CHANGED_BEHAVIOR, TESTS_ADDED, NEW_BEHAVIOR, NO_IMPACT), spec (matched spec name or null), and diff_statspec_changes — for each changed spec: new_rules and removed_rulesproof_status — per-feature: proved, total, status (VERIFIED/PASSING/PARTIAL/FAILING/UNTESTED), failing_rulesdrift_flags — precomputed drift indicators: features with structural-only coverage that have changed files. Each entry has spec, reason, and files.broken_scopes — specs whose > Scope: references files or directories that no longer exist on disk. Each entry has spec, missing_paths, and existing_paths.Deleted files are already filtered out by the tool — only files that exist on disk are included.
For each file in the drift MCP output, the agent must perform the following analysis. Do not summarize multiple files into one paragraph — analyze each change individually, then group by logical change.
The MCP tool tells you WHICH files changed. You need to understand WHAT the changes mean. For every file categorized as CHANGED_BEHAVIOR or NEW_BEHAVIOR, read the git diff for that file:
git diff <since_ref> -- <file_path>
Use the since field from the MCP output to determine the ref. Do not skip this step — the MCP tool's category and diff_stat are mechanical classifications, not semantic understanding.
For each changed file, assign a significance level based on the diff content (not just the MCP category):
| Significance | Meaning | Who cares |
|---|---|---|
| BEHAVIORAL | Changes what the software does — new features, changed rules, removed capabilities | PM, Engineer, QA |
| STRUCTURAL | Changes how the software is organized — refactors, renames, file moves, dependency updates | Engineer |
| OPERATIONAL | Changes how the software runs — CI config, deployment, env vars, Dockerfiles, Makefiles | Engineer, QA |
| DOCUMENTATION | Changes to docs, comments, READMEs, guides | PM (if user-facing), otherwise no one |
| TRIVIAL | Formatting, whitespace, typo fixes, auto-generated files | No one |
The MCP tool's NO_IMPACT category maps roughly to DOCUMENTATION and TRIVIAL, but many files classified as CHANGED_BEHAVIOR might actually be STRUCTURAL (a refactor that doesn't change behavior) or OPERATIONAL (a Makefile change). The diff tells you which.
For BEHAVIORAL and OPERATIONAL changes, write one entry per logical change — not one entry per file, and not one paragraph for all files.
A "logical change" is a set of related files that implement one thing:
Each entry must include:
For each BEHAVIORAL change, perform rule-level analysis using the rule_details field from the MCP tool. This field provides, for each spec with changed behavior files: the full list of rules with descriptions, their proof status, and which scope files changed.
Step 1 — Read the diff and identify changed behavior:
For each spec in rule_details, read the diffs for its changed_files. Identify what behavioral aspects changed: new functions, modified conditionals, changed return values, added/removed error handling, new UI sections, changed responsive behavior, etc.
Step 2 — Cross-reference changes against individual rules:
For each rule in the spec's rules array, ask: does this rule's description relate to any of the changed behavior? Classify each rule:
✓ RULE-N: <description>⚠ RULE-N: <description> — <what changed>proof_status: "unproved" regardless of changes → ✗ RULE-N: <description> — no proof+ Suggested RULE: <description of uncovered behavior>Step 3 — Format rule-level findings per spec:
Spec: <name> (<total_rules> rules, <proved_rules> proved)
Changed files: <file1>, <file2>
✓ RULE-1: Parse config and return defaults
⚠ RULE-2: Return 400 on invalid input — new validation path added for empty arrays
✓ RULE-3: Log warning on retry
✗ RULE-4: Rate limit to 100 req/s — no proof exists
+ Suggested: Batch endpoint accepts up to 50 items (new behavior, no rule)
→ Update spec: purlin:spec <name> (address ⚠ and + items)
→ Write tests: test <name> (address ✗ items)
If no rule is potentially stale and no new behavior is uncovered → Spec up to date ✓
If no spec exists → No spec exists → Run: purlin:spec <name>
Behavioral gap drift: Check the drift_flags array in the MCP tool output. Each entry identifies a feature with no behavioral proofs whose code changed. For each:
Spec: purlin_agent (8 rules, 0 behavioral proofs)
⚠ All proofs are structural checks. Code changed but no behavioral test verifies the new behavior.
→ Run: purlin:spec purlin_agent (add behavioral rules)
Also check individual file entries for behavioral_gap: true — these are CHANGED_BEHAVIOR files whose spec has only structural checks (no behavioral proofs).
Broken scopes: Check the broken_scopes array in the MCP tool output. Each entry identifies a spec whose > Scope: references a path that no longer exists on disk. For each:
Spec: login
⚠ Broken scope — src/auth/old_login.py no longer exists
→ Was it renamed? Run: purlin:rename login (to update scope and references)
→ Was it deleted? Run: purlin:spec login (to update or remove the spec)
Check the external_anchor_drift array in the MCP tool output. Each entry has:
anchor — the anchor namesource_url — the external reference URLpinned — the stored SHA or timestampstatus — one of: stale (pinned behind remote HEAD), unpinned (no Pinned field), error (source unreachable)remote_sha — the current remote HEAD (when available)error — error message (when status is error)For each entry, format as:
⚠ Anchor <name> is stale — pinned at <pinned[:7]> but remote is at <remote_sha>. Run: purlin:anchor sync <name>⚠ Anchor <name> has > Source: but no > Pinned: value. Run: purlin:anchor sync <name> to pin✗ Anchor <name> — source unreachable: <error>. Verify URL in specs/_anchors/<name>.mdWhen an anchor with > Source: has been synced (Pinned changed) and also has local rules, flag as a PM action item:
Group the entries by significance, not by MCP category:
NEEDS ATTENTION:
<logical change title>
What: <plain English description of the change>
Files: <file1>, <file2>, <file3>
Spec: <spec_name> (<N> rules) — <spec drift status from 2d>
→ <directive>
<logical change title>
What: <plain English description>
Files: <file1>
Spec: none — <reason no spec needed, or directive to create one>
→ <directive>
FOR AWARENESS:
<description>
Files: <file1>, <file2>
No spec impact.
<description>
Files: <file1>
No spec impact.
TRIVIAL:
(nothing this cycle)
NEEDS ATTENTION includes all BEHAVIORAL and OPERATIONAL changes — anything that affects what the software does or how it runs. FOR AWARENESS includes STRUCTURAL and DOCUMENTATION changes. TRIVIAL includes formatting and whitespace. Omit empty sections.
Print the report header, then the grouped entries from Step 2e.
Since <since field from JSON>:
<NEEDS ATTENTION section from 2e>
<FOR AWARENESS section from 2e>
<TRIVIAL section from 2e>
After all sections, print a --- separator and role-aware action items. These MUST reference the actual analysis from Step 2, not just the raw proof counts from the MCP tool. List ALL actionable items for each role — do not truncate. When the agent sees "handle PM items" it needs the complete list.
List every item that applies, in this order:
(assumed) that need PM confirmation → → Run: purlin:spec <name>→ Run: purlin:spec <name>→ Run: purlin:spec <name>new_rules that lack proofs → → Run: test <name>List every item that applies, in this order:
status: "FAILING" in proof_status → → Run: test <name>status: "PARTIAL" or "UNTESTED" → → Run: test <name> (PARTIAL means more tests needed to reach PASSING)→ Run: purlin:spec <name>List every item that applies, in this order:
→ Run: purlin:verify --manual <feature> <PROOF-N>status: "PASSING" (all rules proved, no receipt yet) → → Run: purlin:verifystatus: "PARTIAL" or "UNTESTED" → → Run: test <name>Before emitting ACTION ITEMS, cross-reference against the NEEDS ATTENTION entries:
proof_status shows all rules proved (the proofs were from before the change)⚠ (potentially stale) or + (missing) rules from rule_details analysis must appear in PM action items with the specific rules listed: Spec drift: <name> — RULE-2 may be stale (new validation path), 1 new behavior uncovered → Run: purlin:spec <name>drift_flags must appear in PM action items: Spec drift: <name> — code changed, no behavioral proofs (only structural checks) → Run: purlin:spec <name>broken_scopes must appear in PM action items: Broken scope: <name> — <path> no longer exists → Run: purlin:rename <name> or purlin:spec <name>---
ACTION ITEMS (PM):
1. <description> — <action>
2. <description> — <action>
...
ACTION ITEMS (Engineer):
1. <description> — <action>
...
ACTION ITEMS (QA):
1. <description> — <action>
...
Omit any role section with no action items.
drift MCP call.