| name | refine-deck |
| description | Deck hygiene pass — retag stale cards, prune 90-day unverified parks, surface defunct references, orphaned dependencies, and jargon titles. AUTO-INVOKE on "tidy up the deck", "hygiene pass", "clean up the queue", or /refine-deck. The board itself gets refactored each iteration. |
Codex GoC Command
When this skill says goc ..., resolve the executable before running the
command:
- In the
game-of-cards source checkout, use uv run goc ....
- If
goc is already on PATH, use goc ....
- If this skill is loaded from the Game of Cards Codex plugin, use the
bundled helper at
<plugin-root>/skills/_goc-bootstrap.sh ...; the plugin
root is the parent directory that contains both skills/ and bin/.
- If the plugin root is not obvious from the loaded skill path, locate the
helper with:
GOC_BOOTSTRAP=$(find "$HOME/.codex/plugins/cache" -path '*/game-of-cards/*/skills/_goc-bootstrap.sh' -type f -perm -111 -exec ls -t {} + 2>/dev/null | head -n 1)
test -n "$GOC_BOOTSTRAP" || { echo "GoC Codex plugin bootstrap not found" >&2; exit 127; }
"$GOC_BOOTSTRAP" --help
Use that helper path in place of bare goc for the rest of the skill. Do not
edit deck files directly just because goc is not on PATH.
When to invoke
Invoke when the user says "tidy up the deck", "check for stale cards", "hygiene pass", "clean up the queue", "archive old", "audit the deck", or invokes /refine-deck. Covers retagging stale cards, pruning 90-day unverified parks, surfacing defunct file:line references, surfacing orphaned dependencies (epics with no children, meta-fix families not wired, log.md migration TODOs), surfacing engineer-jargon titles for retitling, and proposing new canonical tags (XP refactor mercilessly + Kanban continuous improvement).
Preflight
If any ! block below shows goc: command not found, Permission for this action has been denied, or no such file or directory: .game-of-cards/deck/, stop and invoke Skill(kickoff) first. Kickoff detects which setup step is missing (CLI not installed, Bash allowance not granted, project state not scaffolded) and walks the user through it. Re-invoke this skill only after kickoff completes.
Context (project-local extension)
!cat .game-of-cards/hooks/refine-deck.md 2>/dev/null || true
Refine the deck
Every iteration the BOARD gets better, not just the code on it: this
skill is the recurring hygiene tax that keeps the deck's read-pattern
guarantee alive as filing slows down and rot accumulates. The hook
above may extend the flow with project-specific categories or
thresholds (scope rules in reference.md § Rationale).
Surface rot and act on it before commit. Two action paths depending
on the finding's nature:
- Hygiene findings (mechanical: stale
unverified parks, defunct
file:line cites, missing summaries, orphaned-edge mechanical wires)
— apply the edit directly; a non-firing tag row is the one
exception, reported and never edited.
- Structural findings (epic-shaped clusters, missing
canonical-reference families, contribution-recall proposals,
meta-decision umbrellas, newly-emergent tag candidates surfaced
by a project hook's pattern-discovery pass) — file via
Skill(create-card), disprove via
Skill(advance-card) <title> disproved, or park
--tag unverified per Step 4.5. "Surfaced and discussed in chat"
is not a disposition.
Long-form material lives in reference.md — a sibling file in
this skill's directory. Read the named section only when the
situation actually applies:
| Situation | reference.md section |
|---|
| Why this pass exists; hook scope rules | Rationale |
| Running the four orphaned-dependency sub-checks | Orphaned-dependency sub-check scripts |
| Anchoring cites; what the recipe declines | Citation anchor check |
goc quality-pass --llm | Quality-pass --llm flag |
| What the Step 4 report should look like | Example Step 4 output |
| Which findings Step 4.5 covers, escape valve | Step 4.5 scope notes |
Step 1 — sanity floor
!b=.claude/skills/_goc-bootstrap.sh; if [ -f $b ]; then sh $b validate; else goc validate; fi 2>&1 || echo "[refine-deck] validate found rot; the skill body below will route you through fixing it"
If validate fails with half-edge errors, run goc repair-edges to
preview the missing reverse-edge writes, then goc repair-edges --apply and re-run goc validate. If repair reports a structural
cycle, park that card for human review instead of guessing which edge
is wrong. Fix unknown tags / missing required fields FIRST too.
Hygiene runs on a valid deck. The precondition above is intentionally
soft-gated so a failing validator surfaces its output into this
skill rather than blocking the skill load.
Step 2 — survey by category
Stale unverified parks
!b=.claude/skills/_goc-bootstrap.sh; if [ -f $b ]; then sh $b --tag unverified -v; else goc --tag unverified -v; fi 2>&1 || true
For each entry: check created against today's date. Cards parked
90 days that nobody has reproduced or refuted are decay
candidates. Options:
- Retry the falsifying recipe. If the body's
"what-evidence-would-falsify-it" recipe is now feasible (infra
exists, sweep budget available), run it. On evidence: drop the
unverified tag (promote) or flip to disproved.
- Demote to disproved. If three independent rounds have failed
to reproduce, the lead is dead.
Skill(advance-card) <title> disproved with a one-line "Three rounds attempted; no
reproduction" rebuttal.
- Keep parked. Add a one-line note in
log.md explaining why
this round didn't have the budget; the 90-day clock resets.
Stale-open cards (no log activity)
!b=.claude/skills/_goc-bootstrap.sh; if [ -f $b ]; then sh $b --status open --json; else goc --status open --json; fi 2>&1 | head -100
Cards with status: open whose log.md has no entries in 60+
days are at risk of being forgotten. For each: read the body,
decide if the lead is still real, and either:
- Re-prioritize via
Skill(next-card) (recommend it on the next
loop iteration).
- Escalate the gate from
none to decision if blocking on a
framing question.
- Flip to
disproved if the original evidence has rotted away.
Defunct file:line citations
Check each open card's cites against current code with an ANCHOR
test, not a bounds test. An in-range line number is no evidence the
cite is current: a file that grew keeps every old number valid while
the code that was there moved down, so line ≤ EOF can only fire on
a file that SHRANK. Compare what is AT the cited line now against
what the card says is there. Per cite (long form: reference.md
§ Citation anchor check):
- Resolve the path — cards write
engine.py:N for goc/engine.py:N;
prefer a non-mirror match. Ranges map both endpoints.
- Anchor = that line's text at the commit that last WROTE the number:
walk
git log --follow --format=%H -- <card>/README.md oldest →
newest and take the newest commit where the cite token turns from
absent to present — the filing commit for a virgin cite, the repair
commit for one an earlier pass rewrote. Anchoring a repaired number
at the filing commit reads unrelated code and moves the cite onto it.
- Anchor ≠ the line in HEAD → the cite is defunct.
- Relocate the anchor text in HEAD and rewrite the number only on
a unique match of a non-trivial line (>~12 chars, not a blank or a
bare brace). Never guess.
Cites step 4 declines — anchor gone, ambiguous, or trivial — are
REPORTED for a human to read, never silently skipped. Anchor text that
exists nowhere usually means the cited code was refactored away:
re-read the card and, if the refactor also fixed the defect, close via
Skill(finish-card) with a note "fixed incidentally by ".
Missing summaries
Pre-2026-05-01 cards may have empty or absent summary: fields.
Surface these:
goc --status all --json | \
jq '.[] | select(.summary == "" or .summary == null) | .title'
For each surfaced card: read the body, write a ≤3-sentence
summary into the frontmatter. Mechanical doc edit; no status
change.
Tags without firing predicates
Per Skill(card-schema), a tag must satisfy its own row, not a
fixed text window; the row's check column says whether that is
scorable or a judgment (reference.md § Tag sweeps). Survey 5–10
random cards per round. Report, never strip — a non-firing row
costs a line of output, not curated grouping.
Orphaned dependencies
Relational rot the validator cannot see: it enforces edge SYMMETRY
at commit time but not edge ABSENCE — epics with zero linked
children; meta-fix cards whose body lists a family roster but carry
zero edges; open cards with legacy **Depends on:** / **Next:** / **Part of:** body markers but empty schema arrays; unactioned
log.md migration TODOs (formerly parent: X / formerly spawned_from: X). Run the four sub-checks in reference.md
§ Orphaned-dependency sub-check scripts, judge each surfaced card's
edge direction, and wire it via goc advance X --by Y
(symmetric-by-construction, so the validator stays happy). A card
whose family members are code sites has nothing to wire — leave it.
Card metadata quality pass
Title antipatterns + missing-summary scan via:
goc quality-pass --status all
What it surfaces:
- Title antipatterns — same regex predicates
goc new uses to
reject filings (engineer-jargon: r88, path-2, phase-3,
bug-140, _md_/_py_ infixes, camelCase tokens, math symbols).
Catches legacy cards filed before the antipattern guard was wired.
For each surfaced title: rename via goc move <old> <new> so
cross-references rewrite atomically.
- Missing summaries — pre-2026-05-01 cards may lack the
summary: frontmatter field that triage views (goc -v)
depend on. For each: read the body, write a ≤3-sentence summary
into the YAML.
Step 3 — file new canonical tag candidates
When a coherent body of work emerges that isn't covered by an
existing tag (e.g., a sprint of 6 cards all about a specific
research front), file via Skill(create-card) a card whose DoD is
the SCHEMA.md PR adding the new tag + its predicate. Adding the
tag itself remains a SCHEMA.md PR per the schema's "Adding new
tags" rule; the filing that schedules that PR is imperative. Like
every other structural finding, the candidate either becomes a
card here, gets disproved (the proposed predicate doesn't fire on
a sufficient set), or parks --tag unverified per Step 4.5 — not
a chat-only proposal.
Step 4 — surface and act
For each surfaced issue, output one line documenting the action
taken (hygiene) or the card filed / disprove flip / park
(structural):
<title>: <issue> → <action>
Sample lines in reference.md § Example Step 4 output.
Step 4.5 — Park-or-disprove unfollowed structural candidates (mandatory)
Project hooks may extend Step 2 with a pattern-discovery pass that
surfaces more structural candidates than this round can verify and
file. Structural candidates that didn't get applied this round
MUST go somewhere durable before commit:
- Filed as a new card via
Skill(create-card).
- Disproved via
Skill(advance-card) <title> disproved — when
you re-read the cited code and the candidate is wrong on its
face.
- Unverified via
Skill(create-card) ... --tag unverified —
when the candidate has substance but no verification budget
this round. Body must include: the candidate's hypothesis with
file:line (verbatim quote), why deferred, falsification recipe,
the category (Step 2 sub-section) that surfaced it.
Scope, minimum-vs-maximum bound, and the noise escape valve:
reference.md § Step 4.5 scope notes.
Cross-references
Skill(advance-card) — for status flips (disproved / re-open
/ unblock).
Skill(card-schema) — tag application predicates and the schema
PR contract for new tags.
Skill(create-card) — when a hygiene issue surfaces a NEW
defect (e.g., the defunct citation reveals a real bug, not just
rot), file via create-card.
- Project commit workflow — to land the hygiene edits as a
chore(deck): hygiene pass — <date> commit.