T-DHGL-build-import-planning-skill
Status: closed/done · Impact: medium · Complexity: medium
Adopting the SDLC plugin in a project that already has planning
documentation requires re-keying that prior work by hand. This skill
scans the repo for existing planning-shaped content (READMEs, design
docs, brain-dump notes, issue archives, etc.) and produces draft
task/milestone entities under docs/planning/, so a project can adopt
the SDLC system without throwing away its history. The conversion is
LLM-driven because the source shape is unpredictable — the value is in
interpretation, not regex.
The SDLC plugin ships skills that create entities one at a time
(sdlc:task-new, sdlc:milestone-new) and one bulk-extract skill that
operates on a single supplied filepath (sdlc:milestones-from-file at
plugin/skills/milestones-from-file/SKILL.md). There is no skill that
walks a repo and identifies what’s worth converting — the user has to
know which file to point at and which entity type to extract.
plugin/scripts/new_task.py and plugin/scripts/new_milestone.py
already own the mechanics of frontmatter/slug/date generation, so any
new skill can shell out to them rather than reimplementing entity
creation.
Proposed
Section titled “Proposed”A new skill at plugin/skills/import-planning/SKILL.md invoked as
/sdlc:import-planning. It walks the project root (excluding
docs/planning/ itself, node_modules, .git, dist, build artifacts,
etc.), identifies files that look like planning content (markdown notes,
design docs, READMEs with roadmap-ish sections, issue dumps), and
proposes a batched plan: “found N candidates → would produce M tasks,
K milestones, L skipped.” For each candidate the user approves, the
skill drafts the entity body (Goal/Today/Proposed/ACs for tasks;
Goal/Success criteria/Out of scope for milestones), shells out to
new_task.py / new_milestone.py to create the file, then Edits the
body in. Defaults to running inside a fresh worktree and opening a PR,
mirroring milestones-from-file. --auto skips per-candidate
confirmation. --dry-run stops at the plan. --scope <path> restricts
the scan to a subdirectory.
Approach
Section titled “Approach”- Discovery script (deterministic). Add
plugin/scripts/scan_planning_candidates.py(PEP-723uv run). Walks the project root respecting.gitignore, applies path-based exclusions (docs/planning/,node_modules,.git,dist,build,target, etc.), and applies cheap heuristics to filter candidates: file extension (.md,.txt,.org), size bounds, optional content sniff for planning-shaped headers (## TODO,## Roadmap,## Milestones, dated checklists). Emits JSON:{candidates: [{path, size, mtime, heuristic_hints: [...]}]}. Read-only. - Skill scaffolding. Add
plugin/skills/import-planning/SKILL.mdwith standard frontmatter (description,allowed-tools: [Read, Bash, Edit, Agent, AskUserQuestion]). Wire up the worktree+PR flow (copy the boilerplate frommilestones-from-file). - Per-candidate interpretation. For each candidate from Step 1,
dispatch one sub-agent (via
Agenttool) that reads the file and returns a strict-JSON verdict:{entity_type: task | milestone | skip, headline, slug_suggestion, body_sections: {...}, evidence: [...]}. Parallel dispatch in one message. - Batched plan UI. Aggregate verdicts, render a markdown plan
grouped by
entity_type, ask the user viaAskUserQuestion: run-as-shown / walk-one-by-one / cancel.--autoskips this prompt. - Creation pass. For each approved candidate, shell out to
new_task.py/new_milestone.pywith the verdict’s headline/slug/ status, then Edit the body sections into the resulting file. Validate each viavalidate_frontmatter.py. - Eval suite. Add
plugin/skills/import-planning/tests/run_evals.pyand fixtures, mirroring theentities-migrate/tests/shape. Fixtures: a repo with mixed planning + non-planning files, a repo with nothing planning-shaped, a repo where a candidate looks like a milestone vs. a task, a repo where the scan must respect.gitignore. - Cross-reference cleanup. Update
sdlc:setupSKILL.md to mention/sdlc:import-planningas the bootstrap path when adopting in a non-empty project. Updatemilestones-from-filedescription to point atimport-planningas the repo-wide alternative.
Files to touch
Section titled “Files to touch”plugin/scripts/scan_planning_candidates.py(new) — deterministic repo-walker, emits JSON candidate list.plugin/skills/import-planning/SKILL.md(new) — orchestrator skill.plugin/skills/import-planning/tests/run_evals.py(new) — eval harness, mirrorsentities-migrate/tests/shape.plugin/skills/import-planning/tests/fixtures/(new) — fixture trees per case.plugin/skills/import-planning/tests/README.md(new) — fixture docs.plugin/skills/setup/SKILL.md— cross-reference/sdlc:import-planningas the bootstrap path for non-empty projects.plugin/skills/milestones-from-file/SKILL.md— noteimport-planningas the repo-wide alternative.
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
/sdlc:import-planningappears in the skill list after/reload-plugins. - AC-2:
scan_planning_candidates.py --jsonagainst a tmp repo with mixed planning + non-planning files emits the planning candidates and excludes non-planning paths. - AC-3: Scanner excludes
docs/planning/,.git,node_modules,dist, and conventional build dirs by default; respects.gitignore. - AC-4:
--dry-runmode prints the plan and exits without creating worktree, branch, or entity files. - AC-5:
--automode against the mixed-candidates fixture creates the expected number of entity files vianew_task.py/new_milestone.pyand each new file passesvalidate_frontmatter.py. - AC-6: For each created entity, the body sections (Goal / Today /
Proposed / Approach / ACs for tasks; Goal / Success criteria / Out of
scope for milestones) are populated, not left as
<...>placeholders. - AC-7: Per-candidate verdicts come from sub-agents dispatched in a single message (parallel), verifiable by inspecting the skill’s Step 3 prose and via test-double if feasible.
- AC-8:
plugin/skills/import-planning/tests/run_evals.pypasses against at least four fixtures (mixed, nothing-planning, milestone-vs- task disambiguation, gitignore-respect). - AC-9:
/project-checkstill passes. - AC-10:
plugin/skills/setup/SKILL.mdmentions/sdlc:import-planning;plugin/skills/milestones-from-file/SKILL.mdcross-references it.
Out of scope
Section titled “Out of scope”- Auto-detection of entity types beyond
taskandmilestone(bug/epic/feature) — those don’t have schemas in the plugin yet. - De-duplication against already-imported content under
docs/planning/. Imports are additive; collisions surface as validation errors fromnew_task.py/new_milestone.py. - Non-text source formats (PDFs, images, Notion exports). Markdown, plaintext, and org-mode only.
- Migrating prose inside existing SDLC entities — that’s
/sdlc:entities-migrate’s concern, not this skill’s.
Dependencies
Section titled “Dependencies”- none
Discovery context
Section titled “Discovery context”- Surfaced while reviewing how the SDLC plugin onboards into a project
with prior planning history:
/sdlc:setupinitializes empty directories, and/sdlc:milestones-from-filehandles one supplied file, but neither walks a repo. Adoption friction = re-keying. This skill closes that gap.
Post-mortem
Section titled “Post-mortem”Captured by /sdlc:task-work on 2026-05-19. PR: pending.
Acceptance criteria coverage
Section titled “Acceptance criteria coverage”- AC-1: agent-manual — confirmed
plugin/skills/import-planning/SKILL.mdexists with valid YAML frontmatter (description:+allowed-tools). - AC-2: auto —
mixedeval case asserts planning files surface in--jsonoutput; non-planning paths do not. - AC-3: auto —
gitignore-respecteval case asserts.gitignore-matched paths are excluded; default-exclusion list verified by inspection + targeted run against/tmp/ac3-test/. - AC-4: agent-manual — SKILL.md Step 3 skips worktree creation under
--dry-run; Step 6 explicitly stops at the plan render. - AC-5: auto —
plugin/skills/import-planning/invariants.yamldeclaresrequired_tool_refs:fornew_task.py,new_milestone.py, andvalidate_frontmatter.py;plugin/scripts/lint_skill_prose.pyasserts all three are cited in the SKILL.md. End-to-end execution still requires a live sub-agent fan-out, but the prose-shell-out claim is mechanically pinned. See T-9CI4-skill-prose-invariant-linter. - AC-6: auto —
plugin/skills/import-planning/invariants.yamldeclaresrequired_phrases:for the literal phraseapplies under --auto tooin section7c; the linter asserts it. A regression that drops the--autocarve-out trips Step 0 of/project-check. See T-9CI4-skill-prose-invariant-linter. - AC-7: auto —
plugin/skills/import-planning/invariants.yamldeclaresrequired_phrases:for the literal phraseone sub-agent in a single messagein section5.andParallelism comes from one message.in the Notes section; the linter asserts both. See T-9CI4-skill-prose-invariant-linter. - AC-8: auto —
plugin/skills/import-planning/tests/run_evals.pyreports4/4 eval case(s) passed. Cases: mixed, nothing-planning, milestone-vs-task, gitignore-respect. - AC-9: auto —
.claude/skills/project-check/check_entities.pyexits 0 (3 entity dir(s) checked, no drift.). Adjacent suites also re-checked clean: entities-migrate 7/7, project-check 7/7. - AC-10: auto —
grep "import-planning"returns one hit each insetup/SKILL.mdandmilestones-from-file/SKILL.md.
What worked
Section titled “What worked”- The
/sdlc:milestones-from-fileandproject-cleanupskills were strong pattern donors — worktree+PR flow and parallel sub-agent dispatch transferred almost verbatim, keeping the new skill’s prose internally consistent with the rest of the plugin. - Shelling out to
new_task.py/new_milestone.pyrather than reimplementing entity creation kept the skill thin and side-stepped the schema-drift risk an inline reimplementation would carry. - Eval suite mirroring
entities-migrate/tests/(PEP-723 runner, self-contained fixture trees, tmp-copy-and-mutate) plugged into the existing pattern without invention.
Friction and automation gaps
Section titled “Friction and automation gaps”/sdlc:task-ensure-ready’s contract literally disqualifiesstatus: in-progress, but/sdlc:task-workStep 3 setsin-progressbefore invoking ensure-ready in Step 5. Applied the contract pragmatically (spec quality verified, status check soft-skipped) — but this is a real design contradiction inside the SDLC plugin. Fix options: (a) move Step 3’s status flip to after ensure-ready passes, or (b) widen the contract’s allowedstatusset to includein-progresswhen called by task-work. Either fix removes the judgment call. → T-OHT5-resolve-ensure-ready-in-progress-contract- AC-5 / AC-6 / AC-7 are orchestration claims that can only be fully exercised by a live sub-agent
fan-out, which the eval harness can’t simulate (sub-agents only exist at skill-invocation time).
Marked
agent-manualagainst SKILL.md prose. A future affordance — a--self-testmode that uses fixture sub-agent responses, or a doc-linter that asserts required invariants are present in skill prose — would make theseauto. → T-9CI4-skill-prose-invariant-linter - The eval suite for an LLM-orchestration skill is unavoidably scanner-only. The skill’s actual
end-to-end behavior (per-candidate verdicts, body fills, PR opening) cannot be reached without a
live invocation. Worth tracking as a pattern-level gap: skills whose value is in interpretation
will always carry an irreducible
agent-manualband of ACs. gitignorehandling settled on thepathspeclibrary (top-level.gitignoreonly) rather than shelling out togit check-ignore. Trade-off: works on bare directory trees and test fixtures without agit initstep, but nested.gitignorefiles are not consulted. Acceptable for the haystack-narrowing role the scanner plays; a future scope expansion (deep.gitignoresemantics,.git/info/exclude, globally-configured excludes) would warrant a swap togit check-ignore. → T-K603-scan-candidates-deep-gitignore- Branch naming for the spec PR vs the implementation PR required a conscious split
(
docs/<basename>for the spec,feat/<basename>for the implementation,chore/import-planningfor skill-runtime worktrees) to avoid future collisions. Worth codifying — task-work’s docs assumefeat/<basename>is always the implementation branch but don’t speak to spec-only PRs. A short addition to task-work / task-new’s Notes section would lock it in. → T-UNUH-codify-task-branch-naming - Bash invocations of
grepwere intercepted by a shell alias (looks likergwith--no-heading --with-filenamerewrites) that fails on BSD-flag arguments — used/usr/bin/grepto bypass. Cosmetic, but it broke a verification step until noticed. → T-Y7IN-document-grep-alias-bypass - The
Bashtool’sPC: cached from ...annotation returned a stale prior-command error when re-issuing a similar git command — required tweaking the command shape to dodge the cache. Not a correctness issue but caused a confusing detour. - One Bash invocation of
git commit -m "$(cat <<EOF...EOF)"failed because zsh expandeddocs(tasks):as a glob inside the command-substitution. Worked around by writing the message to a temp file and passing-F. Worth documenting as a pattern in any SDLC skill that constructs commit messages from the model. → T-UEU9-document-commit-message-pattern
Spawned follow-up tasks
Section titled “Spawned follow-up tasks”- T-OHT5-resolve-ensure-ready-in-progress-contract — close the task-ensure-ready vs task-work in-progress flip contradiction, created
- T-9CI4-skill-prose-invariant-linter — doc-linter to auto-verify required phrases/sections in skill prose, created
- T-K603-scan-candidates-deep-gitignore — swap pathspec for
git check-ignoreto honor nested.gitignore, created - T-UNUH-codify-task-branch-naming — codify
docs/vsfeat/vschore/branch prefixes across SDLC skills, created - T-Y7IN-document-grep-alias-bypass — switch SDLC SKILL.md grep examples to
command grep//usr/bin/grep, created - T-UEU9-document-commit-message-pattern — canonical
mktemp+git commit -Fsnippet across SDLC skills, created