T-WC1T-lefthook-gate-ports
Status: closed/done · Impact: high · Complexity: medium
Three pre-commit gates are capabilities living as scripts — and two
lefthook lines point at deleted .py files, so those gates are silently
broken today. A third mechanism (check_skill_prose.ts) subprocess-spawns
the very script being moved. Port the gates to ops and repoint every gate
mechanism so the pre-commit surface is live again and survives the
scripts/ deletion.
| Location | Role today |
|---|---|
plugin/scripts/lint_skill_prose.ts | SKILL.md prose linter vs per-skill invariants.yaml |
plugin/scripts/lint_task_state_commit_origin.ts | task-state frontmatter commits only on main |
plugin/scripts/worktree_scope_guard.ts | staged paths must stay in the active worktree scope |
lefthook.yml:37 | runs plugin/scripts/worktree_scope_guard.py — file no longer exists (broken gate) |
lefthook.yml:89 | runs plugin/scripts/lint_task_state_commit_origin.py — same |
.claude/skills/project-check/check_skill_prose.ts | lefthook wrapper whose DEFAULT_LINTER spawns plugin/scripts/lint_skill_prose.ts |
Proposed
Section titled “Proposed”services/gate/ops/{skill-prose,worktree-scope}.ts and
model/entities/task/ops/lint-state-origin.ts (all hidden; the task gate
sits on its entity per resolved design question a). lefthook.yml runs
bun run "$CLAUDE_PLUGIN_ROOT"cli/sdlc.ts gate skill-prose | gate worktree-scope | task lint-state-origin; check_skill_prose.ts is
thinned to call the verb (or lefthook calls it directly); every
project-check spawn target referencing plugin/scripts/ or
plugin/validators/ is repointed.
Approach
Section titled “Approach”- Goldens: violation fixtures per gate (red) + clean tree (green), capturing
<path>:<line>citation format and exit codes. - Port the three gate cores; render hooks preserve citation output.
- Repoint lefthook lines 37 and 89 (the stale
.pyrefs) and line 58’s wrapper; audit all.claude/skills/project-check/*.tsforscripts//validators/spawn targets and repoint each. - Prove the gates fire red-on-fixture through a real
git commitattempt.
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
plugin/lib/services/gate/ops/skill-prose.ts | new | from lint_skill_prose.ts |
plugin/lib/services/gate/ops/worktree-scope.ts | new | from worktree_scope_guard.ts |
plugin/lib/model/entities/task/ops/lint-state-origin.ts | new | from lint_task_state_commit_origin.ts |
lefthook.yml | modify | three gate recipes → sdlc CLI; fixes the stale .py lines |
.claude/skills/project-check/check_skill_prose.ts | modify | DEFAULT_LINTER → sdlc gate skill-prose (or delete the wrapper) |
plugin/scripts/lint_skill_prose.ts | modify | thins to forwarder until deletion |
plugin/scripts/lint_task_state_commit_origin.ts | modify | same |
plugin/scripts/worktree_scope_guard.ts | modify | same |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: each gate fires red on its violation fixture through lefthook (a real pre-commit run), with citation output matching the golden.
- AC-2:
grep -n '\.py' lefthook.ymlreturns nothing; no lefthook recipe referencesplugin/scripts/orplugin/validators/. - AC-3:
grep -rn "plugin/scripts/\|plugin/validators/" .claude/skills/project-check/returns zero spawn targets. - AC-4: the three ops are hidden (absent from default
--help, present under--advanced); fullbun testgreen.
Out of scope
Section titled “Out of scope”- invariants.yaml content changes beyond paths (T-QL5F-skill-prose-codemod owns the pins).
- New gate rules or rule changes.
- The legacy-path guard itself (T-YBKU-shim-deletion-guard).
Dependencies
Section titled “Dependencies”- T-NV49-op-path-substrate — the
gatehidden noun + per-verb hidden rendering must exist before lefthook lines can point at it.
Discovery context
Section titled “Discovery context”- Planning-phase drift finding: lefthook.yml:37,89 invoke deleted
.pyscripts; adversarial refutation expanded scope to the 6 live.tsgate shims (check_skill_prose wrapper). docs/plans/op-substrate-full-sweep.md §Context + T8 amendment.