name: crystal-cut
description: "Attempt to close (cut) a crystal workitem — transition status:in-progress → status:done. Sweeps all unchecked - [ ] items; blocks if any remain (Decision Log #4). Use when work on a crystal is complete and you're ready to finalize."
license: MIT
crystal-cut - Close a Crystal
Purpose
Transitions a crystal from status: in-progress to status: done. The
crystal-completion-guard PreToolUse hook
(${CLAUDE_PLUGIN_ROOT}/scripts/crystal-completion-guard.sh) enforces the
gate at the tool layer: any - [ ] checkbox in the workitem blocks the
transition (Decision Log #4 generalizes "completion discipline" — every
unchecked checkbox is an open obligation, not only sidetracks).
This skill is the orchestration layer above the gate: it sweeps the workitem, surfaces the open items with the five resolution paths, then performs the flip once the user has addressed each.
Gate behavior (defense in depth)
Three layers enforce the same invariant (Decision Log #7):
- Primary — PreToolUse hook on Write/Edit/MultiEdit for workitem
files. Works in any project regardless of git. It evaluates the
post-edit content, so it also fires on the creating Write: a brand-new
file written straight at
status: donemust contain zero- [ ], or the gate blocks it. (Relevant when importing an already-complete legacy doc — seecrystal-grow→ Migrating legacy docs.) - Stop reminder —
${CLAUDE_PLUGIN_ROOT}/scripts/crystal-stop-reminder.shsurfaces open items at end-of-turn so the assistant doesn't drift. - Backup (git only) — pre-commit hook ships with
vdm-gitfor downstream projects with git. See/vdm-git:guardfor activation.
The gate is intentionally cheap to circumvent only via the five
resolution paths — there is no --force flag and no --no-verify shortcut
in this skill. If the gate seems wrong for a specific case, the right move
is to use one of the five paths (most likely cancelled (reason: ...)),
not to bypass.
Terminal-tier gates (DL #10 in crystal-multi-root)
The done transition triggers the unchecked-items gate above. Two other
terminal statuses behave differently:
| Terminal status | Gate |
|---|---|
done |
Unchecked-items gate (see above) + orphan-sidetracks gate (see below). Five resolution paths apply. |
superseded |
Requires superseded-by: <slug> in frontmatter — names the replacement. Gate blocks transition without it. The replacement workitem should cross-link back (migrated from: <this-slug> in body or superseded: <this-slug> in frontmatter). |
cancelled |
No gate — author explicitly drops the work; unchecked items are no longer obligations. Record rationale inline (typically in a closing ## Cancelled paragraph). |
The superseded-by requirement is enforced by the same PreToolUse hook
(crystal-completion-guard.sh → crystal-completion-guard.py) — see DL #10
for the gate design.
Orphan-sidetracks gate (DL #14 in crystal-multi-root)
A sidetrack card can have **Status:** open without a corresponding inline
- [ ] см. Sidetrack #N marker in the workitem body. Without the marker,
the obligation is invisible to the checkbox-counting unchecked-items
gate — crystal-cut would silently pass over it. DL #14 declares the
inline-marker requirement; this gate enforces it deterministically.
Logic: scan every sidetrack card heading ### #N. <title> followed at some
point by **Status:** open[ ...]. For each, look for a matching marker
line - [ ] ... Sidetrack #N anywhere in the body. Cards lacking a marker
are reported as orphans and block the done-transition with the combined
diagnostic (orphans listed alongside unchecked items if both exist).
Decision-Log entries use a different heading shape (### #N / date / title,
slash separator, no period after N) and never carry **Status:**, so the
parser does not collide with them.
Fix path for orphans: add the marker line (typically inside a
## Pending sidetracks block under ## Next actions, or at the spot of
origin if the побег surfaced in body text), then resolve it via one of the
five paths above. The marker turns the invisible obligation into a visible
checkbox the other gate already understands.
Implementation:
- bash
audit_sidetracks_without_markers <workitem-path>inlib/crystal-path.sh - python
_audit_sidetracks_without_markers(content)incrystal-completion-guard.pyBoth must stay in sync — they implement the same parser.
Five resolution paths (DL #9)
Each unchecked item must be addressed by one of:
| Path | When to use |
|---|---|
[x] resolved |
Done within this workitem — flip the checkbox |
migrated → <slug> |
Belongs elsewhere — move card to an existing crystal or a new sibling you grow on the spot, cross-link both sides |
cancelled (reason: ...) |
Explicitly dropped — record rationale |
deferred (deadline: YYYY-MM-DD) |
Postponed to a date — surfaces again on/after deadline |
promoted-to-stem (→ <sibling>) |
Побег outgrew stem — split into sibling crystal |
For migrated / cancelled / deferred / promoted-to-stem: the
sidetrack card's **Status:** line is updated AND the inline - [ ]
marker is checked ([x]) since the obligation has been addressed in a
defined way. The gate counts checkboxes, not statuses — keeping the two
in sync is required.
Behavioral protocol
When /vdm:crystal-cut [slug] is invoked:
Step 1: Locate the target
If slug is supplied:
- Single-root mode: target
<root>/<slug>/workitem.md(canonical) or<root>/<slug>.md(legacy flat). - Multi-root mode: slug is expected as
<root-qualifier>/<slug>(per DL #6 in crystal-multi-root, e.g.auth/refactor-jwt). If only the short form is supplied and it's unambiguous (matches in exactly one root), use it; if it matches in ≥2 roots, ask which root.
If omitted, target the singleton active workitem (the one with
status: in-progress). Multiple actives → consult
derive_singleton_mode:
globalviolation → warn and ask which one.per-rootmode → one active is allowed per root; if pwd is under a resolved root, target that root's active; otherwise ask.offmode → ask explicitly which slug.
Step 2: Sweep unchecked items
Read the workitem. Collect every - [ ] line with its line number and the
section it appears under (## Next actions, ## Sidetracks → ### #N,
inline Decision-Log discussion, etc.). Group by section for readability.
Step 3: Present the sweep
🔪 crystal-cut sweep: <slug>
## Next actions (3 open):
L478: - [ ] Скаффолд hook scripts
L479: - [ ] Регистрация в hooks.json
L482: - [ ] PROJECT_CHANGELOG.md entry
## Sidetracks (1 open):
#6. Reflexive case — этa сессия как канонический пример (L431)
Resolve each via one of: [x] resolved | migrated → <slug> |
cancelled (...) | deferred (date) | promoted-to-stem (→ <sibling>).
For each open item, propose the most likely resolution path based on
context (e.g. obviously-completed items → suggest [x]; long-deferred
items the user mentioned punting → deferred). Do not auto-flip without
confirmation — the user owns each path decision.
Step 4: Apply resolutions
For each addressed item:
[x]resolved → Edit the checkbox in placemigrated→ create card in target crystal (cross-link both ways), flip this checkbox[x], update sidetrack**Status:**cancelled→ flip checkbox[x], update sidetrack**Status:**with the reasondeferred→ flip checkbox[x], update sidetrack**Status:**with the deadlinepromoted-to-stem→ runcrystal-growfor the sibling, transfer the обязательство there, flip checkbox[x], update sidetrack**Status:**
Step 5: Flip status
Edit the frontmatter: status: in-progress → status: done. The
PreToolUse hook will let this through because the unchecked count is now
zero. If the hook still blocks, you missed an item — re-sweep.
Also bump last-updated: to today.
Step 6: Soft hints (DL #22)
After a successful close, emit two soft hints — text-only, no auto-trigger:
✓ Crystal `<slug>` closed (status:done).
Consider:
- /vdm:changelog — record this workitem's outcome in PROJECT_CHANGELOG.md
If the close addressed N resolved (not migrated/cancelled/deferred)
sidetracks where N > 0, additionally:
- /vdm:learn — N resolved sidetracks may be candidates for knowledge capture
These are suggestions the user runs explicitly. Never auto-invoke.
Examples
Example 1: Clean close
User: /vdm:crystal-cut auth-refactor
Sweep finds zero unchecked items. Assistant flips frontmatter, emits the two soft hints. Done in one round.
Example 2: Resolve via mixed paths
Sweep finds 4 open items. Assistant proposes:
- "Update README" →
[x](already done in the last commit) - "Add tracing" →
migrated → observability-pass - "Fix typo in deprecated API" →
cancelled (API will be removed in v3) - "Mobile UI variant" →
deferred (deadline: 2026-06-01)
User confirms each. Assistant applies edits + flips status + emits hints.
Example 3: Gate fires on premature cut
User flipped status: done manually in IDE without using crystal-cut.
The PreToolUse hook intercepts and emits the blocked-diagnostic. The
assistant explains the five paths and runs the sweep above to bring the
user back on rails.
Example 4: Done with one loose end — split it into a new sibling
The work is feature-complete except a single trailing obligation that has no
existing home — e.g. zero-inbox shipped, but a commented-out cron line
needs a real scheduling pass later. The sweep finds one - [ ]. There is no
"done but for one thing" state — and there shouldn't be; the obligation has
to land somewhere. Decide among three paths:
- date-bound ("revisit after the next release") →
deferred (deadline: ...) - genuinely dropped →
cancelled (reason: ...) - a real follow-up with no home →
migrated → <new-sibling>
The third is the case worth spelling out, because the target doesn't exist yet:
crystal-grow cron-scheduling-pass— create the sibling first (it startsstatus: in-progress; this is fine, the singleton you're closing is about to godone).- Move the obligation into the new crystal's
## Next actions(or a sidetrack card), and cross-link: the source card's**Status:**becomesmigrated → cron-scheduling-pass, the new crystal notesmigrated from: zero-inbox. - Flip the source
- [ ]→[x](the obligation is addressed — it now lives elsewhere). - Re-sweep: zero unchecked → flip
status: done. Cut succeeds.
Don't grow a whole sibling for something that's really a deferred or
cancelled — a one-line follow-up rarely earns its own crystal. Reach for
migrate-to-new only when the loose end is itself a unit of work.
Quality gates
- All
- [ ]checkboxes addressed (gate would block otherwise) - Sidetrack
**Status:**lines match their checkbox resolutions -
migrateditems have cross-links in both source and target - Frontmatter
status: doneANDlast-updated: <today> - Two soft hints emitted (always changelog; learn if N>0 resolved sidetracks)
Configuration
Shares the crystal config section with the other three crystal-*
skills. See /vdm:crystal-grow for sub-commands. Disabling
(crystal off) silences the reminder hooks but does not disable the
PreToolUse gate — the gate is unconditional. To fully disable, uninstall
the plugin.
Reflexive case
docs/tasks/crystal-design/workitem.md was closed via this exact protocol
when the crystal-* suite reached v1. The act of closing it validated the
gate, the sweep, and the hint emission end-to-end. Read its final state
for a worked example of a clean cut.