T-K7FR-task-work-worktree-init-language-agnostic
Status: closed/done · Impact: high · Complexity: small
Auto-generated from a /sdlc:task-work post-mortem in rust-path-opener
(2026-05-19-obsidian-vault-id-uri). Review and promote to
open/ready before picking up.
/sdlc:task-work Step 4’s “Initialize the worktree” block assumes
the host project is a JS-flavored repo with pnpm, lefthook, and
mise — it tells the operator to run mise trust && just setup-worktree. On a pure-Rust project (or anything without
just setup-worktree), that command doesn’t exist and the operator
either skips it without confidence or has to invent a justification.
Detect what the project actually needs and run that, or document
clearly that the step is JS-only and skippable for other stacks.
- Step 4 of
plugin/skills/task-work/SKILL.mdincludes a “do not skip” block titled “Initialize the worktree (mandatory — do not skip)”. It callsmise trust && just setup-worktree, which in turn runspnpm run project:init:worktree. - The instructions explain why (lefthook + commitlint armed via
commit-msghook), grounded in a JS-toolchain assumption. - On
sksizer/rust-path-opener(pure-Rust, nopnpm, nolefthook, nomiseconfig, nosetup-worktreerecipe injustfile), Step 4 has no applicable action — the operator skipped it without consequence because the project doesn’t use the JS-flavored commit-msg hook the step is protecting. - The skill has no language/toolchain detection, so the JS-specific block runs as mandatory text regardless of project type.
Proposed
Section titled “Proposed”Step 4’s init block becomes conditional on detected project signals. Examples:
package.json+pnpm-lock.yaml+lefthook.ymlpresent → runmise trust && just setup-worktree.Cargo.tomlpresent and nopackage.json→ no-op (or run a Rust-equivalent hook if one is defined).- Mixed or unknown → fall back to a generic “verify your project’s worktree-init recipe ran” check that consults a per-project manifest.
Alternative: split the JS init into a separate sub-skill
(/sdlc:worktree-init-js) that /sdlc:task-work invokes only when
the JS signals are present, and document the contract for other
toolchains.
Approach
Section titled “Approach”- Add a project-type detector helper (or extend an existing one) in
plugin/lib/that reports{js, rust, mixed, unknown}based on manifest files at the project root. - Update Step 4 to branch on that signal. For
rust, skip the init block silently with a one-line log. Forjs, run the existing pnpm/lefthook commands. Formixed, warn and run JS init. - Optionally, allow projects to override via a
.claude/sdlc/worktree-init.shhook that any toolchain can define. - Update Step 4’s doc text to describe the conditional behavior so operators understand why the init may or may not run.
Files to touch
Section titled “Files to touch”plugin/skills/task-work/SKILL.md— rewrite the Step 4 init block.- Possibly
plugin/lib/project_type.py(new) or similar — language detection helper. - Possibly
plugin/skills/worktree-init-js/SKILL.md(new) if the init logic gets factored out.
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: Running
/sdlc:task-workin a pure-Rust project does not prompt the operator to run a non-existentjust setup-worktreerecipe. - AC-2: Running
/sdlc:task-workin a JS project still arms lefthook before the first feature-branch commit (i.e. the current behavior is preserved). - AC-3: Step 4’s text in
SKILL.mdreflects the new conditional behavior and lists the manifest signals that trigger each branch.
Out of scope
Section titled “Out of scope”- Inventing Rust-side equivalents of
lefthook/commitlint. If a Rust project doesn’t have a commit-msg hook, the skill assumes none is needed. - Toolchain detection for languages beyond JS and Rust. Add others when a real project needs them.
Dependencies
Section titled “Dependencies”- none
Discovery context
Section titled “Discovery context”- Surfaced 2026-05-20 by
/sdlc:task-workrunning2026-05-19-obsidian-vault-id-uriinsksizer/rust-path-opener. Step 4’s mandatory init block had no applicable action on that pure-Rust project; the operator skipped it without consequence because the JS commit-msg hook it’s protecting doesn’t exist there.
Post-mortem
Section titled “Post-mortem”Captured by /sdlc:task-work on 2026-05-20. PR: pending.
Acceptance criteria coverage
Section titled “Acceptance criteria coverage”- AC-1: agent-manual — created
/tmp/rust_demowith nosdlc.yaml, confirmed the Step 4 procedure short-circuits (no executor invocation, no prompt for a non-existent recipe). Then with an emptysdlc.yaml, ran the executor with--key worktree_init --allow-emptyand observedexit=0with a stderr warning only — nojust setup-worktreeattempt. - AC-2: agent-manual — populated
worktree_init: ["echo mise trust simulated", "echo just setup-worktree simulated"]and confirmed the executor ran both verbs in declared order with the existingrun_quality_checks.pymachinery (OK 2/2). The shape is identical to the prior hardcoded recipe — a JS project that ports its two verbs intosdlc.yaml’sworktree_init:keeps the same pre-commit behaviour. - AC-3: auto — Step 4’s rewritten text in
plugin/skills/task-work/SKILL.mdenumerates three project shapes (JS-flavoured with declared verbs, non-JS with absent/empty key, no-sdlc.yamlprojects) and the corresponding behaviour for each; the project-context bullet at the top of the SKILL also lists theworktree_init:slot alongsidequality_checks:.
What worked
Section titled “What worked”- Reusing
run_quality_checks.pyas the executor instead of writing a new one — the--key NAMEflag was a one-line addition and the whole shape (line/json/log modes,--allow-empty, project-root handling) carried over for free. - Reading
plugin/conventions/sdlc-yaml.mdfirst surfaced theworktree_init:slot was already RESERVED with the exact shape intended — implementation became “promote reserved to live” rather than “design from scratch.” plugin/skills/CLAUDE.md’s “co-locate first, promote when shared” rule pointed at the right thing immediately: don’t make a new helper script when an existing shared one extends cleanly.
Friction and automation gaps
Section titled “Friction and automation gaps”- The hardcoded
just setup-worktreesurvived in this project’s own task-work SKILL.md long after the project itself stopped having a justfile — the skill couldn’t self-detect that its own Step 4 was already broken against the very codebase hosting it. A pre-flight check on the live skill against the cwd’s manifests (a “does this Step 4 actually apply here?” lint) would have caught it months earlier. Open follow-up: a skill-runtime drift audit that probes each skill’s hardcoded commands against the current project’s manifests. -> T-E69Y-skill-md-runtime-drift-audit - The Step 4 doc previously declared itself “mandatory — do not
skip” while being silently inapplicable on this project. The
agent (correctly) skipped it, but the contract said otherwise.
Skill prose that gates execution on a hardcoded command should
carry a “applies-when” predicate that the skill or a linter can
evaluate. Open follow-up: add an
applies_when:shape to the invariants.yaml format so mandatory-step claims are checkable. ->2026-05-20-skill-step-applies-when-predicate run_quality_checks.pyand the--keyextension share no test fixtures, just live behavior. The script has no unit tests; this change relied on ad-hoc/tmpsmoke-runs. Open follow-up: introduce aplugin/scripts/tests/test_run_quality_checks.pyfixture suite (config-found, config-missing, key-missing, key-empty, key-non-list, key-with-verbs,--allow-emptypermutations) so future flag additions have a regression net. -> T-SYM2-run-quality-checks-test-fixtures
Spawned follow-up tasks
Section titled “Spawned follow-up tasks”2026-05-20-skill-step-applies-when-predicate— created. New task: declareapplies_when:predicates for conditional skill steps so the runtime can self-skip and prose stops claiming unconditional mandatoriness.- T-SYM2-run-quality-checks-test-fixtures — created. New
task: cover
run_quality_checks.py’s config / key / exit-code matrix with a regression fixture suite. - T-E69Y-skill-md-runtime-drift-audit — linked existing.
Already-drafted auditor for SKILL.md vs. script
--help; covers this run’s “skill couldn’t detect its own broken Step 4” gap.