Skip to content

T-407I-subagent-invariant-preflight-lint

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

Auto-generated from a /sdlc:task-work post-mortem. Review and promote to open/ready before picking up.

plugin/scripts/lint_skill_prose.py already exists and is wired into the project-check gate, but it only fires post-hoc — after a task-work sub-agent has finished writing. When the sub-agent’s edits happen to introduce a forbidden phrase that the skill’s own invariants.yaml declares illegal (e.g. pr-check forbids prose mentioning the raw GitHub-CLI comment command), the violation surfaces during the quality-check gate at the end of the task, not during the edit loop. Running the linter inline after each Edit that touches an instrumented SKILL.md would catch the violation seconds after it lands, while the sub-agent still has full context to rephrase. Cite T-5VN7-pr-check-cursor-bootstrap-misses-existing-comments for the live near-miss this surfaced.

LocationRole today
plugin/scripts/lint_skill_prose.pyLints a passed SKILL.md against its co-located invariants.yaml; called by project_check.py’s skill-doc suite.
plugin/skills/pr-check/invariants.yamlDeclares forbidden_phrases that include the raw GitHub-CLI comment command pattern.
plugin/skills/task-work/SKILL.mdStep 6 dispatches an implementation sub-agent with a brief; the brief does not require running any linter after Edits.
plugin/scripts/run_quality_checks.pySurfaces lint failures only at Step 7 (end-of-task quality-check gate).

So today: a sub-agent can land a forbidden phrase in a SKILL.md during Step 6, finish all its edits, and only discover the violation at Step 7 when the gate fails. The sub-agent then has to retrace the edits to find which one introduced the phrase.

The task-work Step 6 sub-agent brief acquires a “lint after Edit” clause: whenever the sub-agent edits any file matching plugin/skills/*/SKILL.md (or any other file that has a co-located invariants.yaml), the next operation in its loop is to run lint_skill_prose.py on that file and address any reported violations before moving on. If the linter is clean, the sub-agent proceeds normally. If not, the sub-agent rephrases and re-runs until clean.

The check is inline (no new infrastructure) — it leverages the linter that already exists and the sub-agent’s existing Bash affordance. The deliverable is a documentation change in plugin/skills/task-work/SKILL.md (and possibly its sub-agent brief template) that makes this loop explicit.

  1. Identify the exact location in plugin/skills/task-work/SKILL.md (or the sub-agent brief it ships) where the implementation sub-agent receives its tool/loop instructions. Add a “lint after SKILL.md edit” bullet that names lint_skill_prose.py and the trigger condition.
  2. Optionally add a thin helper at plugin/skills/task-work/lint_touched_skill_docs.sh that, given the set of files just edited, picks out the SKILL.md ones and invokes the linter against each — so the sub-agent’s loop only has to invoke one command rather than discovering touched files itself.
  3. Add a test under plugin/skills/task-work/test_*.py (or a fixture in the eval harness) that exercises the loop: sub-agent edits a SKILL.md introducing a forbidden phrase, the loop runs the linter, the linter reports the violation, the sub-agent rephrases.
LocationKindChange
plugin/skills/task-work/SKILL.mdmodifyAdd an inline-lint loop bullet to Step 6’s sub-agent brief.
plugin/skills/task-work/lint_touched_skill_docs.shnewOptional convenience wrapper around lint_skill_prose.py for the sub-agent’s loop.
plugin/skills/task-work/test_*.pymodifyAdd a regression test that pins the inline-lint behavior.
  • AC-1: plugin/skills/task-work/SKILL.md’s Step 6 sub-agent brief instructs the sub-agent to run lint_skill_prose.py immediately after any Edit to a file matching plugin/skills/*/SKILL.md, before continuing the loop.
  • AC-2: A regression test demonstrates that introducing a known forbidden_phrases entry into a SKILL.md fixture during a sub-agent loop is caught inline by the linter (not at the end-of-task gate).
  • AC-3: If a helper wrapper is added, it accepts a list of recently-edited files, filters to SKILL.md instances with co-located invariants.yaml, and exits non-zero if any linter run reports violations.
  • Inventing a generic “lint after edit” framework for arbitrary linters. The deliverable is specifically wiring the existing lint_skill_prose.py into the task-work sub-agent loop.
  • Lifting the invariants.yaml schema into a shared cross-skill format. Today each skill carries its own; the inline-lint loop works with whatever shape exists.
  • none

Spawned by /sdlc:task-work post-mortem of T-5VN7-pr-check-cursor-bootstrap-misses-existing-comments on 2026-05-23.

Bullet: The sub-agent’s first-pass attempt to wire gh pr comment… mentions into plugin/skills/pr-check/SKILL.md would have tripped pr-check’s invariants.yaml forbidden-phrase rule. The sub-agent caught it manually and rephrased around the raw GitHub-CLI comment command, but a pre-flight lint check during authoring (the linter that already exists, just run inline in the sub-agent’s loop after each Edit) would catch this class of accidental invariant violation faster than the post-hoc gate. Keywords searched: forbidden-phrase, first-pass, invariants, github-cli, pre-flight, accidental, sub-agent, rephrased Excluded: 2026-05-21-pr-check-cursor-bootstrap-misses-existing-comments Top candidates (score / status / headline):

  • 33 / closed/done / 2026-05-20-task-work-sub-agent-verdict-contract-clarity — Tighten task-work sub-agent verdict contract so ensure-ready’s READY marker isn’t mistaken for task-work’s final verdict
  • 22 / closed/done / 2026-05-19-skill-prose-invariant-linter — Doc-linter that asserts required invariants in skill prose
  • 18 / closed/done / 2026-05-19-add-orchestrate-skill — Add /sdlc:orchestrate skill — one-tick reconcile + dispatch loop
  • 16 / closed/done / 2026-05-20-orchestrate-enforces-one-line-verdict — Orchestrate enforces one-line verdicts from sub-agents
  • 16 / planning/draft / 2026-05-21-orchestrated-sub-agent-design-call-gap — Surface design questions before orchestrated /sdlc:task-work commits to an approach Decision: SPAWNED

← Back to Tasks