Skip to content

T-UBJK-co-locate-skill-specific-scripts

Status: closed/done · Impact: medium · Complexity: small

Today plugin/scripts/ holds a mix of “shared across multiple skills” and “used by exactly one skill” scripts, with no signal at the filesystem layer to distinguish them. Move single-skill scripts into their owning skill’s directory (plugin/skills/<skill>/<script>.py) and keep plugin/scripts/ for the actually-shared ones. Codify the rule as a principle (“co-locate first, promote when shared”) so future scripts land in the right place by default.

Audit of current callers:

ScriptSkills invoking itCurrent homeTarget home
setup_planning.pysetup (only)plugin/scripts/plugin/skills/setup/
new_epic.pyepic-new (only)plugin/scripts/plugin/skills/epic-new/
migrate_entities.pyentities-migrate (only)plugin/scripts/plugin/skills/entities-migrate/
audit_entities.pyentities-audit, setup, entities-migrate (subprocess)plugin/scripts/stays shared — 3 callers
new_task.pytask-new, backlog-triage, review-todosplugin/scripts/stays shared — 3 callers
new_milestone.pymilestone-new, milestones-from-file, backlog-triageplugin/scripts/stays shared — 3 callers
new_backlog.pyreview-todos, backlog-triageplugin/scripts/stays shared — 2 callers

Three scripts (setup_planning.py, new_epic.py, migrate_entities.py) have exactly one caller and belong with their skill. The other four are genuinely shared and stay in plugin/scripts/.

PRINCIPLES.md and plugin/skills/CLAUDE.md say nothing about where scripts should live; the implicit “drop it in plugin/scripts/” pattern is a default by inertia.

  • setup_planning.py, new_epic.py, migrate_entities.py live next to their owning skill.
  • Every ${CLAUDE_PLUGIN_ROOT}scripts/<x>.py reference in skill prose, in test runners, and in cross-script defaults (e.g. migrate_entities.py’s --audit-script default) is updated to the new path.
  • PRINCIPLES.md carries a “Co-locate first, promote when shared” engineering principle.
  • plugin/skills/CLAUDE.md carries the operational form of the same rule: single-skill scripts live in plugin/skills/<name>/, multi-skill scripts in plugin/scripts/, “promotion” is an explicit refactor when a second caller appears.
  • All existing eval suites still pass. /sdlc:entities-audit and /sdlc:entities-migrate continue to behave identically (the --audit-script default just resolves differently).
  1. Confirm the audit. Grep plugin/skills/*/SKILL.md and plugin/scripts/*.py for invocations of each script in the audit table. Flag any caller this task doesn’t already list. Adjust the target-home column if the actual call graph differs from the table.

  2. Add the principle to PRINCIPLES.md. A short section: scripts start co-located with their skill; promotion to a shared home is an explicit refactor triggered by a second skill needing the script. The principle exists so the default behavior pushes toward co-location, with promotion as a deliberate event.

  3. Add the operational convention to plugin/skills/CLAUDE.md. Mirror the principle in the skill-authoring context: when adding a new script, ask “is any other skill plausibly going to call this in the next few iterations?” — if not, put it in plugin/skills/<your-skill>/. Reference PRINCIPLES.md for the why.

  4. Move setup_planning.pyplugin/skills/setup/setup_planning.py. Update plugin/skills/setup/SKILL.md to invoke the new path. git mv to preserve history. Run setup against a tmp project root to verify.

  5. Move new_epic.pyplugin/skills/epic-new/new_epic.py. Update plugin/skills/epic-new/SKILL.md. Verify by creating an epic against a tmp project root.

  6. Move migrate_entities.pyplugin/skills/entities-migrate/migrate_entities.py. Update plugin/skills/entities-migrate/SKILL.md. Update the script’s own default_audit_script resolution — it currently uses (here / "audit_entities.py"), which after the move points at the wrong location; change to (here / ".." / ".." / "scripts" / "audit_entities.py") or similar. Update the migrate eval runner’s SCRIPT path.

  7. Test every touched skill. Run plugin/skills/entities-audit/tests/run_evals.py, plugin/skills/entities-migrate/tests/run_evals.py, and an end-to-end smoke (setup_planning.py against a fresh tmp dir then audit_entities.py against it). All must pass.

  8. Grep for stale path references. grep -rEn 'plugin/scripts/(setup_planning|new_epic|migrate_entities)\.py' across the whole repo (skipping .git and node_modules) should return no live references — only docs that intentionally describe the migration (this task, post-mortems, etc.).

  • PRINCIPLES.md — add “Co-locate first, promote when shared” principle.
  • plugin/skills/CLAUDE.md — add operational convention referencing the principle.
  • plugin/scripts/setup_planning.pyplugin/skills/setup/setup_planning.py (move).
  • plugin/scripts/new_epic.pyplugin/skills/epic-new/new_epic.py (move).
  • plugin/scripts/migrate_entities.pyplugin/skills/entities-migrate/migrate_entities.py (move).
  • plugin/skills/setup/SKILL.md — update script-path reference.
  • plugin/skills/epic-new/SKILL.md — update script-path reference.
  • plugin/skills/entities-migrate/SKILL.md — update script-path reference.
  • plugin/skills/entities-migrate/migrate_entities.py — update internal default_audit_script resolution after the move.
  • plugin/skills/entities-migrate/tests/run_evals.py — update SCRIPT path.
  • Any other SKILL.md / doc that grep turns up referencing the moved scripts.
  • AC-1: PRINCIPLES.md contains a “Co-locate first, promote when shared” section that names what triggers promotion (a second skill needing the script).
  • AC-2: plugin/skills/CLAUDE.md contains a section that operationalizes the principle for skill authors and points at PRINCIPLES.md.
  • AC-3: ls plugin/scripts/ does NOT contain setup_planning.py, new_epic.py, or migrate_entities.py. ls plugin/skills/setup/, plugin/skills/epic-new/, and plugin/skills/entities-migrate/ contain the moved scripts respectively.
  • AC-4: grep -rEn 'plugin/scripts/(setup_planning|new_epic|migrate_entities)\.py' --include='*.md' --include='*.py' outside this task file returns no live references.
  • AC-5: plugin/skills/entities-migrate/tests/run_evals.py 8/8 and plugin/skills/entities-audit/tests/run_evals.py 8/8 still pass.
  • AC-6: End-to-end smoke — fresh tmp project root → plugin/skills/setup/setup_planning.pyplugin/scripts/audit_entities.py reports no drift. (The audit invocation does NOT need to change because audit_entities.py stays in plugin/scripts/.)
  • AC-7: git log --follow plugin/skills/setup/setup_planning.py shows the history of the file from before the move (i.e., git mv was used, not delete + add).
  • Re-shaping plugin/conventions/ (commit-messages, branch-naming) — those are durable cross-skill conventions and stay where they are.
  • Moving audit_entities.py, new_task.py, new_milestone.py, new_backlog.py — they’re shared by 2+ skills and stay in plugin/scripts/.
  • Renaming plugin/scripts/ to something more explicit like plugin/scripts/shared/. The implicit “this dir = shared” works once the principle is documented; renaming is cosmetic churn.
  • The validator at plugin/validators/validate_frontmatter.py — it’s already in its own directory and used by every skill that touches an entity; not in scope here.
  • none

Raised during the SDLC:Mini session on 2026-05-19 while reviewing the T-XPG7-validator-skill-examples-no-pipe-tail follow-up task. The conversation about wrapping the validator (rejected) led to a broader observation: plugin/scripts/ mixes single-skill and shared scripts with no signal to tell them apart. Establishes the co-location principle so future scripts default to the right place.

Captured by /sdlc:task-work on 2026-05-20. PR: pending.

  • AC-1: auto — verified by command grep "Co-locate" PRINCIPLES.md in the relevance check; principle landed in an earlier pass and is already on main.

  • AC-2: auto — verified by command grep "Co-locate" plugin/skills/CLAUDE.md in the relevance check; operational convention already on main.

  • AC-3: auto — ls plugin/scripts/ confirms the three scripts are gone; ls plugin/skills/{setup,epic-new,entities-migrate}/ confirms each lands next to its owning skill.

  • AC-4: agent-manual —

    command grep -rEn 'plugin/scripts/(setup_planning|new_epic|migrate_entities)\.py' --include='*.md' --include='*.py'

    returns zero results in plugin/ and site/. Hits remain inside docs/planning/tasks/*.md — these are historical task documents recounting the original creation/extension of those scripts at their old plugin/scripts/ home, not live references. The task’s relevance_note already carved out “docs that intentionally describe the migration.”

  • AC-5: auto — ${CLAUDE_PLUGIN_ROOT}scripts/run_quality_checks.py --config sdlc.yaml --line reported OK 5/5, which covers both entities-audit/tests/run_evals.py and entities-migrate/tests/run_evals.py.

  • AC-6: agent-manual — ran plugin/skills/setup/setup_planning.py --project-root <tmp> (created 4 entity dirs + sdlc.yaml), then plugin/scripts/audit_entities.py --project-root <tmp> (exit=0, “No drift.”).

  • AC-7: auto — git log --follow --oneline plugin/skills/setup/setup_planning.py shows the pre-move history (cc4b2f4 feat(setup): scaffold top-level sdlc.yaml during /sdlc:setup, etc.), confirming git mv preserved provenance.

  • Pre-task relevance check immediately confirmed AC-1 / AC-2 were already satisfied on main, so the scope reduced cleanly to the mechanical move. The task body’s relevance_note had already telegraphed this.
  • git mv for all three scripts + targeted edits to relative-path resolution kept the surface area small (14 files changed, mostly one-line updates).
  • The default_audit_script resolution in migrate_entities.py was called out explicitly in the task body’s Approach step 6 and in the orchestrator’s brief, so the cross-script default was easy to fix without surprises.
  • site/scripts/regen.mjs only walks plugin/scripts/ for script reference pages, so the three moved scripts had their auto-generated reference pages deleted with no replacement. The site reference now under-covers those scripts. Gap: regen.mjs should also walk plugin/skills/<skill>/*.py so co-located scripts keep their reference pages. This is the natural follow-up of the co-location principle landing. → T-5PLQ-regen-walks-skill-co-located-scripts
  • site/scripts/regen.mjs also surfaced unrelated drift on this run (newly-created find-quality-checks.md, run_quality_checks.md, detect_quality_runners.md reference pages plus four updates to skill reference pages) that wasn’t caused by this task. Had to manually git restore those to keep the PR scope tight. Gap: the orchestrator should not run regen.mjs for unrelated scope reasons during a mechanical-refactor task, or /dev-update-docs should be a separate scheduled pass so per-task PRs don’t end up coupled to ambient regen drift. → T-ETML-decouple-regen-from-task-work-prs
  • The historical docs/planning/tasks/*.md references to plugin/scripts/<moved>.py remain. They’re frozen historical artifacts (each task documents the world as it was when written), but a literal reading of AC-4’s grep counts them. Gap: AC wording could be tightened to “no live references in plugin/ or site/” so the historical-artifact carve-out is explicit and machine-checkable. (skipped — meta/doc-only)
  • The orchestrator’s brief mentioned “Step 9’s contamination rebase will need to drop the chore(tasks): start ... commit from main between your branch-point and your start-commit if it lands first.” That parallel session’s commit was already on main when this run started (it landed before this task-work fired). I’ll need to confirm the rebase is clean against origin/main at Step 9; gap: a small helper that prints “your feat branch ancestry vs origin/main” in one line would make the contamination-rebase decision a one-liner instead of eyeballing git log. → T-WKQD-task-work-ancestry-helper-one-liner
  • The Skill tool was denied at Step 5a so I had to inline the ensure-ready logic (read implementation-ready.md, evaluate, stamp, validate, commit). The skill is supposed to be invoked as a sub-call from task-work. Gap: when invoked via /sdlc:orchestrate, the nested Skill tool isn’t authorized — the dispatch contract should either grant Skill to sub-agents or task-work’s documentation should call out the manual fallback explicitly. → T-QJY7-orchestrate-grants-skill-tool-to-subagents

← Back to Tasks