#289·skills

Windows packaging: CRLF makes every packaged Workflow unrunnable, and fp-check's Stop hook deadlocks the session (#205's fix never merged)

Author: rhansen04Created Aug 28, 2026Updated Aug 28, 2026

Two packaging defects hit on Windows today, both in plugins whose skills worked well. Reporting them together because the fix for one is a one-line .gitattributes and the other is a reopen.

Environment: Claude Code on Windows 11, Git Bash, core.autocrlf=true (the Git for Windows default), marketplace at d1f1575.


1. Every packaged Workflow is unrunnable on Windows — CRLF

Workflow invocation is refused before the script runs:

script contains control characters that would be hidden in the approval dialog

It is not the caller's arguments. It is the plugin file: with core.autocrlf=true the checkout writes workflows/*.js with CRLF, and \r is a control character to the permission handler.

$ git ls-files --eol plugins/insecure-defaults/workflows/audit.js
i/lf    w/crlf  attr/    plugins/insecure-defaults/workflows/audit.js

$ python -c "d=open('audit.js','rb').read(); print(d.count(b'\r\n'))"
713

Confirmed on insecure-defaults:audit-pipeline and variant-analysis:variants. Both are unusable as shipped — variant-analysis had to be run by hand before the cause was found, and insecure-defaults only ran after copying the script and replacing \r\n with \n (content otherwise byte-identical, and it then completed normally: 11 agents, 1448 files, 6 candidates, all refuted).

Fix: add a .gitattributes at the repo root. The repo has none today.

*.js text eol=lf
*.mjs text eol=lf

Worth considering * text=auto eol=lf for anything else a permission handler or shebang might read.


2. fp-check's Stop hook deadlocks the session — and #205's fix never landed

The hook re-blocks the end of every turn until Claude Code's consecutive-block cap (9 here) trips and force-ends it. Once triggered, the session cannot end normally no matter what the agent says, and the repeated checklist reads as an agent failure rather than a plugin one.

Two causes:

(a) No stop_hook_active clause. The prompt has no rule releasing the turn after it has already blocked once, so it fires indefinitely. This is not covered by #205.

(b) Scope, which #205 already diagnosed — the prompt demands all 7 phases "for EVERY bug that was being verified", with no path for a user who asked to verify one finding out of a set. In my session the user asked for exactly one candidate (V2) out of 13 from a variant-analysis sweep; that verification was complete, with all 6 gates and a TRUE POSITIVE verdict, and the hook still blocked 9 times.

#205 was closed on the assumption that #188 would land, and #188 was closed unmerged (mergedAt: null). plugins/fp-check/hooks/hooks.json still carries the Stop block at HEAD, unchanged. So the fix that justified closing #205 is not in the tree.

Local workaround: delete the Stop block, keep SubagentStop. That lives in cache/ and is lost on the next plugin update.

Suggested fix, on top of what @GotenJBZ proposed in #190/#205:

  1. Return {"ok": true} when stop_hook_active is set — a completeness gate should state its case once, not trap the session.
  2. Skip when the conversation is not fp-check.
  3. Skip when work was delegated to subagents/workflows (they have their own per-agent checks).
  4. Check only the phases the path actually used (standard vs deep).
  5. Scope to the findings the user asked about, not to every candidate that appeared earlier in the conversation.

Rule 0 is the one that turns this from noisy into blocking.


The skills themselves were good, which is why the packaging is worth fixing: fp-check corrected an overstated impact claim twice in three rounds — including downgrading an alleged cross-tenant leak to same-tenant, and refuting an RSA-SHA1 finding by tracing that the value never reaches the accept/reject decision.