vt-d-quickfix¶
Apply a quick-fix with automatic audit trail. For small, self-contained changes (typos, label corrections, minor UI tweaks) that do not warrant a full spec workflow. Creates an audit file in intake/quick-fixes/, branches, applies the fix, and optionally creates a PR.
Plugin: vt-product-dev
Category: Other
Command: /vt-d-quickfix
/vt-d-quickfix¶
Quick-Fix Intake Path with Audit Trail (SPEC-133).
Invoke as: /vt-d-quickfix "Fix typo in dashboard heading"
When to Use¶
- Small, self-contained changes (typos, label corrections, color tweaks, copy fixes)
- Change is obvious and unambiguous — no design decision required
- Fix takes less than 30 minutes to implement
- No risk of breaking other features
A change that is more than trivial but not spec-worthy runs as class small-change (Step 3a): this
skill writes its lane record and branch, then hands over to the build, review and finalize skills.
When NOT to use: if the fix requires architectural decisions, affects multiple systems, or has unknowns → use /vt-d-spec-from-requirements instead.
The boundary between quickfix, small-change and the full workflow is stated once, in constitution
Principle II, "Sanctioned lane" (.specify/memory/constitution.md). This skill links to it and does not
restate it — reason: two statements of one boundary drift apart (SPEC-164 FR-5).
Execution¶
Step 0.5: Project Registration Check¶
Run the shared registration check — single source of truth. Do NOT inline the logic here (duplicating it re-introduces the cross-copy drift SPEC-150 removed):
bash ~/.claude/skills/vt-c-project-register/scripts/check_registration.sh
Act on the emitted status line (stdout); any line other than UNREGISTERED … means
continue silently (keeps the gate soft even if the vocabulary grows):
- REGISTERED, TOOLKIT_REPO, NOT_GIT, or DEGRADED … → continue silently.
- UNREGISTERED skips=N escalate=<bool> → note it and continue (soft gate — never block):
- Do NOT ask — reason: registration writes to the toolkit's registry in another repo and is
its own interactive flow, so it must not interrupt this workflow. Print one notice line instead:
"ℹ This project isn't registered in the toolkit, so its learnings won't reach the intake
pipeline. Run /vt-c-project-register when convenient."
- If escalate=true, strengthen the wording: note that learnings have not been
reaching the toolkit across N sessions.
- Only if the user explicitly asks to register → invoke /vt-c-project-register.
- Then continue. Do NOT --record-skip here — reason: a skip records that the user
DECLINED registration, and this phase does not ask. The counter feeds the session-start
escalation ("You have skipped this several times"), so bumping it where nobody was asked
makes that sentence false. Recording follows the ASK, not the phase
(docs/solutions/patterns/fork-safe-single-source-stub.md).
Step 1: Parse Argument¶
Read the argument passed to /vt-d-quickfix. This becomes the initial title for the audit entry and the git commit message.
If no argument was provided, ask for a description in Step 2.
Step 2: Collect Context¶
Derive these from the argument and the conversation — do not ask (reason: a quick fix that opens with a questionnaire is no longer quick). Use AskUserQuestion only when there is no argument and no description anywhere in the conversation: - description — one-line description of what to fix (pre-filled from argument if provided) - trigger — what prompted this fix (e.g., "PM noticed typo during demo", "spotted in QA review") - reason — short human-readable rationale (e.g., "Dashboard heading misspelled") - domain — optional product area tag (e.g., "VisiMatch", "visitrans.de"; blank = omit) - affected files — optional list of files to edit (blank = Claude identifies candidates from description)
trigger and reason default to what the user said (e.g. "user request in session"); domain stays blank unless obvious.
Step 3: Route Decision¶
Decide the class yourself and print one decision line. Judge it against Principle II, "Sanctioned lane"
(.specify/memory/constitution.md):
- "Fix now" — class
quickfix(default) — a trivial request: small and local, a few lines, no new behavior, no data model or API change → continue to Steps 4–9. A trivial request stays classquickfix; do not send it through the lane (EC-1). - "Small change" — class
small-change— more than trivial, but not spec-worthy: within Principle II's lane bound and not architectural → go to Step 3a. Steps 4–9 do not apply to this class. - "Create a Spec instead" — spec-worthy: beyond the lane bound, architectural, or the user asked for a spec → invoke
/vt-d-spec-from-requirementsand exit
If you route to "Create a Spec", hand off to /vt-d-spec-from-requirements with the description collected in Step 2. EXIT after handoff.
Developer override (EC-3). If the developer names a class, honour it and say so in the decision line.
An override up to small-change still gets Step 3a's intake-check and diff-check. An override down to quickfix is not re-checked by anything, because that class never reaches review — Principle II names this as a stated residual, so say it in the decision line too.
Step 3a: The small-change lane¶
The lane runs this skill (record + branch) → /vt-d-3-build → /vt-d-4-review → /vt-d-5-finalize → /vt-c-session-journal.
This skill writes the lane record and the branch, then hands over. It does not build, review or finalize
itself — reason: those skills own their gates, and a copy of their steps here would drift from them (FR-3).
Calling the lane helper. Every lane-detect.sh call below is written in the cross-plugin form
"$CLAUDE_PLUGIN_ROOT/../vt-base/bin/lane-detect.sh": the helper lives in the base plugin, while
$CLAUDE_PLUGIN_ROOT names this skill's plugin. If $CLAUDE_PLUGIN_ROOT is unset or that file does not
exist, run the copy relative to this skill file's real directory, ../../../vt-base/bin/lane-detect.sh.
If neither exists, stop and report both paths — never decide the lane from memory, because the helper is
the one place the rule is applied (review and finalize call it too).
- Read the bound. Run
"$CLAUDE_PLUGIN_ROOT/../vt-base/bin/lane-detect.sh" bound. Exit 2 → stop and report that Principle II's bound marker is missing or broken. If the output ends inrecalibrate=due, tell the developer in one line that Principle II's provisional bound is due for re-evaluation (D11), then continue. - Write the lane record at
intake/quick-fixes/{filename}, using Step 4's slug, filename andfixed_byrules and its path sanitizing:
---
id: YYYY-MM-DD-HH-MM-{slug}
title: {title from Step 2}
date: {ISO 8601 timestamp}
trigger: {trigger from Step 2}
reason: {reason from Step 2}
class: small-change
goal: {what the change achieves, in one line}
intended_files:
- {repo-relative path}
bound: {the output of step 1, verbatim}
architectural: {true|false}
commit: ""
files: []
fixed_by: {git config user.name}
branch: fix/qf-YYYY-MM-DD-{slug}
---
intended_files is written now, before the build: the diff re-check compares the real diff with it.
3. Check the record before any branch exists. Run "$CLAUDE_PLUGIN_ROOT/../vt-base/bin/lane-detect.sh" intake-check intake/quick-fixes/{filename}.
On exit 3, delete the record, print the helper's reason, and hand the collected context over to /vt-d-spec-from-requirements — EXIT (EC-2, FR-4).
On exit 2, stop and report what the helper printed.
4. A bug goal? If the goal fixes a defect whose cause is not yet known, point the developer first to /vt-d-systematic-debugging or /vt-d-root-cause-tracing, or to /vt-d-6-operate for a production incident. This skill does not investigate — reason: those skills own the method (FR-6).
5. Create the branch.
git add intake/quick-fixes/{filename}
git commit -m "chore: lane record for {title}" -m "Quick-Fix-Ref: intake/quick-fixes/{filename}"
/vt-d-3-build for the change described by goal.
8. Re-check the diff before review. Run "$CLAUDE_PLUGIN_ROOT/../vt-base/bin/lane-detect.sh" diff-check intake/quick-fixes/{filename}.
- Exit 3 → stop: the diff outgrew the lane, or the record is architectural. Hand over to /vt-d-spec-from-requirements; do not review it as a lane run (EC-4).
- Exit 4 → there is no base the remote confirms. Apply the fix the helper prints on stderr (for example git fetch origin) and re-run; never treat it as zero files.
- Paths listed after outside= were not in intended_files: name them to the developer. They do not escalate on their own.
9. Review, finalize, journal. Run /vt-d-4-review, then /vt-d-5-finalize, then /vt-c-session-journal. Review runs spec compliance with this record as the stand-in spec, and finalize fails a spec-less branch that has no lane record.
Step 4: Generate Slug and Create Audit File¶
Derive slug from title:
1. Lowercase the title
2. Replace all non-alphanumeric characters with -
3. Collapse consecutive - to single -
4. Strip leading and trailing -
5. Truncate to 40 characters max
6. Example: "Fix typo in Dashboard Heading" → fix-typo-in-dashboard-heading
Derive filename from current date/time (local time):
Example:2026-05-14-22-05-fix-typo-in-dashboard-heading.md
Derive fixed_by automatically (no user prompt):
If intake/quick-fixes/ does not exist, create it:
Write audit file at intake/quick-fixes/{filename}:
---
id: YYYY-MM-DD-HH-MM-{slug}
title: {title from Step 2}
date: {ISO 8601 timestamp}
trigger: {trigger from Step 2}
reason: {reason from Step 2}
domain: {domain from Step 2, or omit field if blank}
commit: "" # see Step 8 — stays empty on no-upstream path; populated on upstream path
files: []
fixed_by: {git config user.name}
branch: fix/qf-YYYY-MM-DD-{slug}
---
<!-- Auto-generated by /vt-d-quickfix. Do not edit. -->
Security note: Sanitize the files: field values — strip any path traversal patterns (../, ~/, absolute paths). Only relative paths within the repo are valid.
Step 5: Create Quickfix Branch¶
If already on main or master, note: "Creating quick-fix branch from main." (advisory only, not blocking).
Step 6: Apply the Fix¶
Claude applies the fix directly using its edit tools.
If files were specified in Step 2: edit those files directly.
If no files were specified: identify candidate files from the description and edit them — no confirmation step (reason: the fix lives on its own branch and is reviewed in the PR).
After edits, present a diff summary and continue to Step 7. If the user objects later, re-examine the description and try again. After 3 rejected attempts, hand off to a spec.
Step 7: Stage and Commit¶
Stage the audit file and all changed fix files:
Commit with trailer:
The Quick-Fix-Ref: trailer makes this fix discoverable via:
Step 8: Update Audit File With File List¶
Patch the audit file:
- files: [] → files: list of actually changed files (from git diff --name-only HEAD~1)
Leave commit: "" as-is — see Why no commit hash in the audit file below.
Amend safety check — check if branch has a remote upstream:
- If no upstream (exit code non-zero): branch is local only → safe to amend the audit file's
files:list into the fix commit: - If upstream exists: branch was already pushed → do NOT amend (rewrites published history). Create a second commit. In this path the fix-commit hash is already final, so it can be recorded:
Why no commit hash in the audit file (no-upstream path)¶
A commit cannot contain its own hash. After git commit --amend, the hash changes, so any hash patched in before the amend points to a dead commit. There is no fixpoint — amending again just produces another dead hash. The skill historically tried to record this hash, which never worked cleanly.
Discovery of the canonical commit instead uses the Quick-Fix-Ref: trailer that Step 7 writes into the commit message:
Recovery: If amend fails for any reason, the audit file simply retains commit: "" and files: []. The user can re-run git add intake/quick-fixes/{filename} && git commit --amend --no-edit once the files list is patched in.
Step 9: Offer PR Creation¶
Do not ask — take Option A (pushing the fix/qf- branch and opening a PR is reversible and is how the fix gets reviewed). Use Option B or C only if the user said so, or if gh is not available/authenticated (then fall back to B, and to C if the push fails).
Option A: "Create PR now" (default)
gh pr create \
--title "fix: {title}" \
--body "$(cat <<'PRBODY'
## Summary
Quick-fix applied via `/vt-d-quickfix`.
## Quick-Fix Audit Entries
This PR contains 1 quick fix. See full context in the audit file below.
| Date | Title | Reason | Audit File |
|------|-------|--------|------------|
| {date} | {title} | {reason} | intake/quick-fixes/{filename} |
---
🔧 Applied via [/vt-d-quickfix](plugins/vt-product-dev/skills/quickfix/SKILL.md)
PRBODY
)"
Option B: "Push branch, I'll create PR manually"
Print: "Branch pushed. Create PR at your convenience."Option C: "Leave local — I'll handle it"
Print: "Reminder: run git push -u origin fix/qf-{date}-{slug} when ready."
Edge Cases¶
Slug collision (two fixes in the same minute): append -2, -3 etc. to the filename if it already exists.
On main/master branch: warn "You're on main. A quickfix branch will be created from main." — advisory only, not blocking.
Audit file accumulation: all files in intake/quick-fixes/ are permanent. No cut/archive mechanism — files are small and git history is the ledger.
Escalation: if the fix turns out to be more complex than expected at any step, hand off to /vt-d-spec-from-requirements with the already-collected context and say why in one line.
Success Criteria¶
For class small-change (Step 3a):
- the lane record carries
class: small-change,goal,intended_files,boundandarchitectural, and is the branch's first commit intake-checkran before the branch was created, anddiff-checkran after the build and before review- the run handed over to
/vt-d-3-build,/vt-d-4-review,/vt-d-5-finalizeand/vt-c-session-journalinstead of doing their work here
For class quickfix (Steps 4–9):
intake/quick-fixes/{filename}.mdexists with all required frontmatter fieldsfiles:field is populated (not[]) after Step 8commit:field is populated only on the upstream-exists path (no-upstream path leaves it empty — see Step 8 "Why no commit hash")Quick-Fix-Ref:trailer present in the commit message — this is the canonical discovery path, independent of thecommit:fieldfix/qf-branch exists with the fix committed- PR body (if created) contains
## Quick-Fix Audit Entriessection