T-ZQ4H-task-ensure-ready-accepts-in-progress
Status: closed/done · Impact: low · Complexity: small
/sdlc:task-ensure-ready’s allowed-status set is {draft, proposed, backlog, ready}. When a task is already in-progress (set by
/sdlc:task-work’s Step 3) and someone re-runs task-ensure-ready
against it — directly, or via task-work’s own Step 5a — the contract
downshifts the doc to proposed/needs-definition and clears
readiness_verified_at, even when the body hasn’t drifted and the
stamp is still fresh. That silently destroys a valid in-progress
state. Closing this gap removes the only foot-gun preventing a
strict by-the-book run of /sdlc:task-work on a task that was
already started in a prior session.
The friction surfaced in a /sdlc:task-work run on the pumice repo
(originating task: pumice’s
docs/planning/tasks/2026-05-19-engine-review-update-completed-rows-affected.md,
PR sksizer/pumice#220). The relevant bullet from that post-mortem:
/sdlc:task-workStep 3 setsstatus: in-progress, but Step 5’s/sdlc:task-ensure-readycontract requiresstatusin{draft, proposed, backlog, ready}. Re-running task-ensure-ready in Step 5 would clobber the Step 3 commit by flipping status back toproposed/needs-definition. I skipped the re-run because thereadiness_verified_atstamp was fresh from earlier this session and the body hadn’t drifted — but a strict-by-the-book run breaks here.
plugin/skills/task-ensure-ready/SKILL.md and the contract at
plugin/entities/task/implementation-ready.md both enforce the
4-status allowlist. There is no carve-out for in-progress or
in-progress/blocked even when the doc clearly carries a valid
readiness_verified_at stamp.
(Step ordering in plugin/skills/task-work/SKILL.md puts Step 3
“flip to in-progress” before Step 5 “ensure ready”. The friction
would persist even if those steps were reversed, because the contract
itself excludes in-progress from valid input states — any re-run on
a previously-started task hits the same downshift.)
Proposed
Section titled “Proposed”/sdlc:task-ensure-ready treats in-progress and
in-progress/blocked as valid input states. On pass it refreshes
readiness_verified_at and leaves status untouched. On fail it
records a definition_gap and clears the stamp, but does NOT
downshift status away from in-progress — the task is already
mid-flight; downshifting would corrupt task-work state. The skill’s
spec, contract doc, and any validators are updated together so a
strict run of /sdlc:task-work against a previously-started task no
longer requires a manual skip.
Approach
Section titled “Approach”- Update
plugin/entities/task/implementation-ready.mdto widen the allowed-status set to includein-progressandin-progress/blocked. Document the fail-mode carve-out (no downshift away fromin-progress*). - Update
plugin/skills/task-ensure-ready/SKILL.mdto match — accept the new statuses on input; refreshreadiness_verified_aton pass without touching status; on fail leave status alone forin-progress*inputs (thedefinition_gapstill gets recorded and the stamp still gets cleared). - Audit
plugin/validators/*.pyfor hardcoded{draft, proposed, backlog, ready}checks; widen them to the 6-status superset where the readiness gate runs. - Confirm
/sdlc:task-work’s Step 5a behaviour stays correct (Step 5a’s initial ensure-ready call still hits a{draft, proposed, backlog, ready}task — the relaxation doesn’t affect that flow). Add a one-line note toplugin/skills/task-work/SKILL.mdsaying that re-running ensure-ready against anin-progresstask is now a safe no-op when the stamp is fresh.
Files to touch
Section titled “Files to touch”plugin/entities/task/implementation-ready.md— widen the allowed-status set; document the in-progress fail-mode carve-out.plugin/skills/task-ensure-ready/SKILL.md— update the input contract and fail-path behaviour.plugin/validators/validate_frontmatter.py(and any other validator with a hardcoded allowlist) — audit and widen as needed.plugin/skills/task-work/SKILL.md— clarify in Step 5/8 that re-running ensure-ready against an in-progress task is safe.
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: Running
/sdlc:task-ensure-ready <basename>against a task withstatus: in-progressand a body that satisfies the contract returnsREADY:and leavesstatus: in-progressunchanged;readiness_verified_atis refreshed. - AC-2: Running it against a
status: in-progresstask whose body has drifted records adefinition_gap, clearsreadiness_verified_at, and leavesstatus: in-progressunchanged (no downshift toproposed/needs-definition). - AC-3: Existing happy-path behaviour for
{draft, proposed, backlog, ready}inputs is unchanged. - AC-4:
/sdlc:task-workcan complete an end-to-end run without the implementer skipping Step 5’s ensure-ready re-run on a previously-started task.
Out of scope
Section titled “Out of scope”- Adding new in-progress sub-states.
- Re-architecting the task lifecycle (separate larger work).
Dependencies
Section titled “Dependencies”- none
Discovery context
Section titled “Discovery context”Spawned from a /sdlc:task-work post-mortem on
sksizer/pumice#220 (originating task:
docs/planning/tasks/2026-05-19-engine-review-update-completed-rows-affected.md
in the pumice repo). The post-mortem documents the foot-gun in
context — see the “Friction and automation gaps” section there.
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: auto —
plugin/skills/task-ensure-ready/tests/run_evals.pycasein-progress-pass-refreshes-stampsimulates the documented Step-4 (pass) mutation against anin-progressfixture and assertsreadiness_verified_atrefreshes whilestatus: in-progressstays put. - AC-2: auto — same suite, cases
in-progress-fail-preserves-statusandin-progress-blocked-fail-preserves-status. Both apply the Step-5 (fail) mutation and assertdefinition_gaprecorded,readiness_verified_atcleared,statusuntouched. - AC-3: auto — same suite, cases
ready-pass-keeps-status,ready-fail-downshifts,proposed-fail-downshiftsexercise the original four-status happy and fail paths. - AC-4: agent-manual — verified by reading task-work SKILL.md Step
5a’s success condition: it now accepts
status:in the six-status set, and the new “if the resolved task was alreadyin-progress*, skip Step 5b” branch closes the previously-broken resumed-session path. Spot-check across a real resumed run still pending.
What worked
Section titled “What worked”- The schema (
plugin/entities/task/schema.json) already permittedin-progressandin-progress/blocked, so the change was purely contract / SKILL.md prose plus one new test harness. No validator edits needed. - The test harness pattern was easy to lift from sibling skills (entities-migrate, import-planning). PEP-723 self-bootstrapping scripts mean no setup friction.
Friction and automation gaps
Section titled “Friction and automation gaps”- The orchestrator dispatched task-work to a sub-agent that stopped
at
READY:from ensure-ready (Step 5a) instead of continuing through Steps 6-10. Happened twice. task-work’s Step 5 narrative should call out explicitly that a sub-agent picking up this skill must NOT treat theREADY:marker as the end of the flow — that marker only completes Step 5a; Steps 5b through 10 still need to run. Possible automation: emit a distinct task-work final marker (e.g.TASK-WORK COMPLETE:) whose absence is unambiguous evidence the sub-agent stopped early. → T-Y0SQ-task-work-emit-completion-marker - The orchestrator that handed off this brief also skipped Step 5b
(flipping the task to
status: in-progresson main and rebasing the feat branch onto that start-commit). The task file on main was stillstatus: readywhen this PR opened; Step 11a will reconcile by going straight fromready→closed/done. Possible automation: lift Step 5b into a script (plugin/scripts/start_task.py) the orchestrator must shell out to — same shape asnew_task.py. A prose-only “now do this” step is too easy for a sub-agent to skip. → T-VFGF-extract-start-task-script - ensure-ready’s implementation lives entirely in SKILL.md prose, so
there is no executable to unit-test directly. The new test harness
re-encodes the prose’s mutations in Python — useful, but it drifts
silently if SKILL.md changes and the re-encoding doesn’t. Possible
automation: extract the frontmatter-mutation step of ensure-ready
into a
plugin/scripts/ensure_ready_mutate.pythat both the skill and the test suite call. That would collapse the spec and the test’s “implementation” into one source. → T-4W64-extract-ensure-ready-mutate-script
Spawned follow-up tasks
Section titled “Spawned follow-up tasks”- T-Y0SQ-task-work-emit-completion-marker — task-work emits a distinctive final stdout marker so orchestrators can detect early exit, created.
- T-VFGF-extract-start-task-script — lift task-work Step 5b
into
scripts/start_task.py, created. - T-4W64-extract-ensure-ready-mutate-script — collapse ensure-ready’s frontmatter mutations into a single shared script, created.