workflow-fix: tighten workflow_lint.py skill-ref regex against /word-in-prose false positives
Highlight text on any card (body, plan, or an event) to anchor a comment, or leave a whole-task comment here. Mention @claude to summon a reply.
No comments yet.
Overview / Motivation
Auto-filed by the workflow-fix-on-bug protocol from a workflow-fix candidate raised on task #812 (emitting agent: experiment-implementer).
Goal
Tighten scripts/workflow_lint.py's skill-reference detector so that ordinary slashed prose tokens (/unset, `false`/unset) inside rule docs do not trip the "unresolved skill reference" false-positive.
Workflow gap
- Bug observed:
workflow_lint.py(no-flags default run) FALSE-POSITIVES an "unresolved skill reference/unset" on.claude/rules/upload-policy.md:171, where the literal prose`false`/unsetis mis-parsed as a/unsetskill reference. - Why it is a workflow gap: The skill-reference detector treats any
/wordtoken in prose as a candidate skill invocation, so an ordinary slashed word in a rule doc (/unset,`false`/unset) trips the check even though it is not a skill reference; the no-flags default run FAILs onmainunrelated to any diff. Any orchestrator / implementer / reviewer running the default lint will see this FAIL. - Confidence (emitter): medium
Proposed change (candidate diff sketch — refine in planning)
# in the skill-ref scanner: only treat `/name` as a skill ref when preceded by
# start-of-line or whitespace, not when it follows a word char / backtick:
- SKILL_REF_RE = re.compile(r"/([a-z][a-z0-9-]+)")
+ SKILL_REF_RE = re.compile(r"(?:(?<=\s)|(?<=^))/([a-z][a-z0-9-]+)")
# OR treat `\`word\`/word` (slash between two backtick-adjacent words) as prose.
Scope / surfaces
- Primary target:
scripts/workflow_lint.py - Also grep the workflow surface for other
/word-in-prose patterns the tightened regex could still miss (grep -rE '\[a-z]+`/[a-z]+' .claude/rules/ CLAUDE.md`).
Constraints / invariants
- Workflow-surface only — never experiment code,
configs/, ortasks/. - The tightened regex must not regress the intended skill-reference detection: an actual
/humanize,/issue,/planreference at the start of a line or after whitespace must still trip. - If
workflow.yamlorCLAUDE.mdchange, they stay consistent with the rule file. - This session runs under
EPM_WORKFLOW_FIX_SESSION=1and carries aworkflow_fix_target:Provenance line — it MUST NOT auto-route any of its own subagents' workflow-fix candidates (recursion guard).
Provenance
- workflow_fix_target: scripts/workflow_lint.py
- fingerprint: 258affc9a4fb
target_file: scripts/workflow_lint.py
bug_observed: workflow_lint.py (no-flags default run) FALSE-POSITIVES an "unresolved skill reference /unset" on .claude/rules/upload-policy.md:171, where the literal prose `false`/unset is mis-parsed as a /unset skill reference.
why_workflow_gap: The skill-reference detector treats any /word token in prose as a candidate skill invocation, so an ordinary slashed word in a rule doc (/unset, `false`/unset) trips the check even though it is not a skill reference; the no-flags default run FAILs on main unrelated to any diff.
proposed_change: Tighten the skill-ref regex to not match a /word immediately preceded by a non-space word-char (e.g. `false`/unset where / follows a backtick-closed word), or add /unset narrative usages to the historical-ref handling; alternatively require a leading word-boundary/space before the /.
diff_sketch: |
in the skill-ref scanner: only treat /name as a skill ref when preceded by
start-of-line or whitespace, not when it follows a word char / backtick:
- SKILL_REF_RE = re.compile(r"/([a-z][a-z0-9-]+)")
- SKILL_REF_RE = re.compile(r"(?:(?<=\s)|(?<=^))/([a-z][a-z0-9-]+)")
OR treat \word`/word` (slash between two backtick-adjacent words) as prose.
confidence: medium related_task: #812