Read entropy-report.md and queue PR-worthy violations as task-store tasks, one task per rule, with per-finding HITL classification. Requires entropy-scan to have run first.
Scanned 9/2/2026
Install to Claude Code
npx -y skills add app-vitals/shipwright --skill entropy-fix --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Entropy Fix?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/app-vitals-entropy-fix)More formats (shields.io, HTML) on the badges page.
---
name: entropy-fix
description: Read entropy-report.md and queue PR-worthy violations as task-store tasks, one task per rule, with per-finding HITL classification. Requires entropy-scan to have run first.
---
# Entropy Fix
Read the latest `entropy-report.md` and queue focused, human-reviewable tasks for
`PR-worthy` violations. Each task fixes one rule — no bundled concerns. Findings are
never turned into direct PRs; they always become task-store tasks that `dev-task`
(or a human, for HITL tasks) picks up later.
**Prerequisites:** Run `/entropy-scan` first to produce `entropy-report.md`.
> **Task store setup:** This skill pushes findings to the Shipwright task store. If
> `SHIPWRIGHT_TASK_STORE_URL` or `SHIPWRIGHT_TASK_STORE_TOKEN` is missing, invoke
> `/shipwright:task-store` for setup instructions.
---
## Setup: Parse Arguments
Before starting, check for flags:
- `--dry-run` — print what tasks would be queued without querying the task store for
dedup or writing any tasks
- `--rule {id}` — queue only violations of a specific rule ID (e.g., `--rule dead_exports`)
> **Note:** Queueing is the only mode. There is no PR mode and no `--queue` flag — every
> run queues tasks. `--dry-run` shows a preview and stops without touching the task store.
---
## Step 1: Verify entropy-report.md Exists
1. Look for `entropy-report.md` in the project root.
2. If it does not exist, print:
```
No entropy-report.md found. Run /entropy-scan first to generate a report.
```
Then stop.
3. Read the report.
---
## Step 2: Load Principles
Load the same principles file that the scan used, filtered the same way:
1. Check `.claude/shipwright/principles.md` in the project root. If it exists, load it.
2. Otherwise, load the plugin default: `references/principles.md` (relative to the plugin root).
3. Filter to only entries containing a `**Detection:**` field — the same entropy-scannable set `/entropy-scan` used.
4. Build a map of `rule_id → entry` for quick lookup. Retain each entry's `**Severity:**`,
`**PR-worthy:**`, and `**HITL:**` fields — the `**HITL:**` value (`always` / `never` /
`per-finding`) is the authoritative classification source used in Step 6q.4. Do not
hardcode a duplicate classification table here; principles.md is the single source of truth.
---
## Step 3: Filter and Group Findings
1. Parse the report's `## Findings` section. Collect all unchecked (`- [ ]`) findings.
2. Filter to only findings whose entry has `**PR-worthy:** true` in the principles file.
3. If `--rule` flag was passed, further filter to only that rule's findings. If no findings match that rule ID, print: "No unchecked findings for rule `{rule_id}`. Nothing to queue." and stop.
4. Group findings by `rule_id`. One task will be queued per group.
5. Sort groups: high-severity rules first, then medium, then low.
6. If no `PR-worthy` unchecked findings exist, print:
```
No PR-worthy findings to queue. All violations are either:
- Already checked off (fixed)
- In entries marked PR-worthy: false (fix manually)
Run /entropy-scan to refresh the report.
```
Then stop.
7. Run each surviving finding through the pre-filing verification checklist —
`references/pre-filing-verification.md` (relative to the plugin root) — before it proceeds
any further. This re-verifies the finding against the current repo state (the scan report
is a snapshot that may already be stale by the time this skill runs) and catches task ID /
branch collisions early. Treat `references/pre-filing-verification.md` as canonical for how
to apply the checklist. Per its four checks:
- Drop findings whose file/line no longer exists or whose described gap is already fixed
(Checklist Items 1–2) — do not queue a task for them. Log them the same way as other
skipped findings (Step 6q.6 summary).
- Route findings that can't be confirmed by a literal check to HITL rather than assuming
they're safe to drop (Checklist Item 3) — this feeds into the `hitl` computation in
Step 6q.4.
- Checklist Item 4 (task ID / branch collisions) is satisfied by this skill's own 6q.1 dedup
check; no separate action is needed here beyond noting the overlap.
This runs once, here in Step 3, so both the `--dry-run` preview (Step 4) and the real queue
path (Step 6) operate on the same already-verified finding set.
---
## Step 4: Dry-Run Output (if --dry-run)
If `--dry-run` was passed, print a preview and stop without querying or writing to the task store:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ENTROPY FIX — DRY RUN
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Would queue {N} tasks:
1. entropy-{rule-id}-{repo-slug}-{YYYY-Www}
Rule: {rule.description} ({severity})
Findings: {count} instances
Files: {list of unique file paths}
HITL: {true|false} (classification per Step 6q.4)
2. ...
No tasks written to task store.
Re-run without --dry-run to queue tasks.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
Stop after printing.
---
## Step 5: Cap Check
If there are more than 10 rule groups to queue, note:
```
Found {N} rules with PR-worthy findings. Capping at 10 tasks per run.
Queueing highest-severity rules first. Re-run after these land to continue.
```
Process only the first 10 groups (sorted by severity).
---
## Step 6: Queue Tasks
Queueing is the only mode. Run this workflow for every run.
### 6q.1 Dedup Check
First, detect the current repo from git: run `git remote get-url origin` and strip the
`https://github.com/` (or `git@github.com:`, stripping the `.git` suffix) prefix to get the
`org/repo` value — e.g. `app-vitals/shipwright`. This is the `repo` value used both to scope
the dedup queries below and, unchanged, as the task JSON's `repo` field in 6q.3 — compute it
once here and reuse it there.
Derive `repo-slug` from it too: the last path segment, lowercased — e.g. `app-vitals/shipwright`
→ `shipwright`. This slug is used in task IDs throughout this skill (6q.3, 6q.6) to keep IDs
unique per repo.
Run (URL-encode the detected repo, e.g. `app-vitals%2Fshipwright`). `GET /tasks` defaults to
`limit=50` and truncates silently unless the caller raises it — pass an explicit high
`limit=1000` on both queries:
```bash
curl -sf -H "Authorization: Bearer $SHIPWRIGHT_TASK_STORE_TOKEN" \
"$SHIPWRIGHT_TASK_STORE_URL/tasks?status=pending&repo={url-encoded-repo}&limit=1000" | jq .
curl -sf -H "Authorization: Bearer $SHIPWRIGHT_TASK_STORE_TOKEN" \
"$SHIPWRIGHT_TASK_STORE_URL/tasks?status=in_progress&repo={url-encoded-repo}&limit=1000" | jq .
```
The `&repo=` filter scopes dedup to tasks for the repo currently being scanned — without it, a
rule active for one repo would incorrectly block or interfere with dedup for a different repo.
**Pagination guard — do not silently proceed with a partial result set.** `limit=1000`
covers the common case, but a repo can still have more than 1000 active tasks. After each
call, check the response's `total` field against the number of tasks actually returned
(`.tasks | length`): if `total <= tasks.length`, the page is complete. If `total >
tasks.length`, the first `limit=1000` page did not cover every active task — re-run the same
query with `&offset=1000` (then `&offset=2000`, and so on) and merge the `.tasks` arrays
until `tasks.length` (summed across pages) `>= total`. Only treat the dedup query as complete
once every page has been fetched this way; never build the "already active" set (below) from
a page that fails this check.
Parse both `.tasks` arrays. From the combined results, collect tasks where:
- `source == "shipwright"`, OR
- `title` starts with `"Entropy fix:"`
Extract the rule IDs from existing tasks by parsing the `id` field (format:
`entropy-{rule-id}-{repo-slug}-{YYYY-Www}`) or from the `branch` field (format:
`fix/entropy-{rule-id}-...`). Build a set of "already active" rule IDs.
For each rule group: if its `rule_id` is in the "already active" set, skip it. Print: `Skipping {rule_id} — task already active`.
Keep both `.tasks` arrays in memory — the task-store cross-check in 6q.2 reuses them.
### 6q.2 Task-Store Cross-Check (dead-code deletion findings)
Before enqueueing any finding for a **deletion** rule — `dead_exports`, `unreferenced_files`,
or `commented_out_blocks` — cross-check it against the pending and in-progress tasks already
fetched in 6q.1. This prevents deleting code that another queued task is about to depend on.
(`commented_out_blocks` is currently `PR-worthy: false` in principles.md, so Step 3 filters
it out before it ever reaches this cross-check — this reference applies if that flag is
ever flipped to `true`.)
For each candidate finding under one of those three rules:
1. Extract the finding's flagged **file path** and, where the finding names one, its **symbol
name** (e.g. the export name for `dead_exports`).
2. Scan the `title` and `description` text of every pending/in-progress task for a reference
to that file path or symbol name (substring match on the path, and whole-word match on the
symbol name to avoid spurious hits).
3. If a match is found, **skip** that finding — do not add it to the task's findings list.
Record it for the final summary as: `deferred — {file}:{symbol} referenced by task {task-id}`.
After cross-checking, if a rule group has **zero** remaining findings (all were deferred),
skip the whole group and note it as deferred in the summary. Rules other than the three
deletion rules are not cross-checked — they pass through unchanged.
### 6q.3 Build Task JSON
For each remaining rule group, build a task object. Reuse the `repo` and `repo-slug` values
detected in 6q.1 — do not re-derive them:
```json
{
"id": "entropy-{rule-id}-{repo-slug}-{YYYY-Www}",
"title": "Entropy fix: {rule.description}",
"source": "entropy-fix",
"repo": "<repo, as detected in 6q.1>",
"branch": "fix/entropy-{rule-id}-{short-description}",
"layer": "Shared",
"status": "pending",
"hitl": <true | false — computed per Step 6q.4>,
"description": "<findings summary — see below>"
}
```
The `description` field must give dev-task (or the HITL executor) enough context to fix without re-reading entropy-report.md:
```
Entropy patrol finding: {rule.id} — {rule.description} ({rule.severity})
Findings ({count} total):
- {file_path}:{line} — {finding_description}
{Include ALL findings that survived the 6q.2 cross-check. If there are more than 20, include the first 20 and append: "(+N more — re-run /entropy-fix to see all)"}
Fix guidance: {entry's **Detection:** field, or remediation guidance from the principles file}
Rule: {rule.id} | Severity: {rule.severity} | Domain: {rule.domain} | HITL: {hitl}
```
The `{repo-slug}` segment is the last path segment of the detected `repo` value (from 6q.1),
lowercased — e.g. `app-vitals/shipwright` → `shipwright`. It namespaces the task ID per repo so
the same rule scanned in two different repos in the same week never collides.
The `{YYYY-Www}` suffix in the task ID uses ISO week format. Compute from the current date:
- Year: 4-digit year
- `W`: literal `W`
- Week number: 2-digit zero-padded ISO week number (01–53)
- Example: `entropy-dead_exports-shipwright-2026-W23`
`short-description`: lowercase, hyphens, max 5 words from rule.description.
### 6q.4 Compute the `hitl` Field
The `hitl` boolean is computed **per finding-group** by reading the rule's `**HITL:**` field
from the principles map loaded in Step 2 — this file is the single source of truth for the
classification, so do not maintain a duplicate table here. There is **no** numeric backstop:
no file-count or line-count threshold ever forces a task to HITL.
Look up `rule.hitl` (the `**HITL:**` field value) and route:
- **`never`** → `hitl: false` unconditionally. The fix is obvious by construction. This is
`dead_exports` (nothing imports it), `unreferenced_files`, and `commented_out_blocks` (once
reviewed, deletion/restoration is unambiguous). These three still pass through the 6q.2
cross-check before they are enqueued. (`commented_out_blocks` is currently
`PR-worthy: false`, so this `hitl` classification is dormant until that flag changes —
see the 6q.2 note.)
- **`always`** → `hitl: true` unconditionally. This is `hardcoded_secrets`: a committed secret
is compromised and needs rotation — an infra/access action outside the codebase that a code
edit alone cannot resolve. `entropy-fix` never autonomously "fixes" a secret.
- **`per-finding`** → evaluate the specific finding group and decide. This is
`duplicated_utility` and `architecture_layering`. Use judgment (you are the Claude agent
running this skill), grounded in the principle's prose:
- `hitl: false` (autonomous) when the fix is a clear, mechanical, single-approach move —
a single obvious call site, a clear drop-in replacement with matching behavior, or a
trivial one-line move up into an **existing** service method.
- `hitl: true` (needs a human) when it is ambiguous or multi-approach — behavior differs
between the local copy and the shared lib, there are multiple reasonable ways to
consolidate/restructure, or **no service boundary exists yet** to move the logic into.
- No default lean either way; judge each group on its own facts. This is a judgment call
made at runtime, never a count-based heuristic.
### 6q.5 Write and Append
1. Write all task objects to `/tmp/entropy-tasks-{unix-timestamp}.json` as a JSON array
2. Run:
```bash
curl -sf -X POST \
-H "Authorization: Bearer $SHIPWRIGHT_TASK_STORE_TOKEN" \
-H "Content-Type: application/json" \
"$SHIPWRIGHT_TASK_STORE_URL/tasks/bulk" \
--data-binary @/tmp/entropy-tasks-{unix-timestamp}.json | jq .
```
3. Delete the temp file after appending
### 6q.6 Print Summary
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ENTROPY FIX — QUEUED
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
QUEUED {N} tasks ({A} autonomous, {H} HITL)
SKIPPED {N} rule groups (already active)
DEFERRED {N} findings (referenced by pending/in-progress tasks)
Tasks queued:
entropy-{rule-id}-{repo-slug}-{YYYY-Www} — {rule.description} [hitl: {true|false}]
...
{If any skipped:}
Skipped (already active):
{rule_id} — task already in queue or in progress
{If any deferred by cross-check:}
Deferred (referenced by existing task):
{file}:{symbol} — referenced by task {task-id}
Run /shipwright:dev-task to execute autonomous tasks. HITL tasks are picked up via
/shipwright:hitl.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
Stop after printing — this is the sole final output.
---
## Error Handling
- **Task-store query fails** (dedup/cross-check in 6q.1): log the failure and stop. Do not
queue tasks without a dedup + cross-check pass, or you risk duplicate tasks and deleting
code a pending task depends on.
- **Bulk append fails** (`/tasks/bulk` non-2xx): log the response body and stop. Do not retry
blindly; re-running the skill is idempotent because the dedup check will skip already-queued
rules.
- **No PR-worthy findings**: handled in Step 3 — print the "nothing to queue" message and stop.
- **More than 10 groups**: cap at 10 as described in Step 5. Always queue highest-severity first.
---
## Constraints (Do Not Violate)
- **One task per rule** — never bundle multiple rule violations into one task.
- **Queue only** — this skill never opens PRs and never leaves the base branch. It only writes
tasks to the task store; the actual fix lands later via `dev-task` or `/shipwright:hitl`.
- **Cross-check before deletion** — never enqueue a `dead_exports` / `unreferenced_files` /
`commented_out_blocks` finding without running the 6q.2 task-store cross-check first.
- **No cascade** — only queue what's in the current `entropy-report.md`. Do not re-scan during a run.
- **No principles.md changes** — the fix skill enforces principles, it does not modify them,
and it reads the `**HITL:**` classification from principles.md rather than duplicating it.
- **entropy-report.md is not checked off here** — a queued task only means a fix is scheduled.
The report's findings are checked off when the queued task actually lands its fix, via a
separate mechanism, not by this skill.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!