T-Y7QU-start-task-fails-loud-on-cache-miss
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.
Make plugin/skills/task-work/start_task.py fail loud when its
lease-cache lookup misses, instead of printing an error to stderr
and exiting 0. The current silent-success path lets /sdlc:task-work
march on past the CAS-REPLACE-to-working transition and the task
branch rebase, leaving the operator to discover the skipped work
only when the next step’s lease-state inspection happens to look.
Cited by T-174G-archive-records-closing-timestamp-for-post-mortem-detector.
| Location | Role today |
|---|---|
plugin/skills/task-work/start_task.py | Looks up the cached lease for the task_id; on miss, prints error: no cached lease for task_id='...'; acquire_lease must have populated the cache before start_task.py runs to stderr and exits 0. The CAS-REPLACE to working and the task branch rebase are silently skipped. |
plugin/lib/lease/cache.py (_find_project_root) | Resolves the project root used to locate the lease cache file. Suspected of using a different resolution path than what populated the cache (the file existed at the operator-expected path; the lookup didn’t find it). |
plugin/skills/task-work/SKILL.md Step 5b | Invokes start_task.py; relies on the script’s exit code as the gate before moving on to Step 6. Step 6 currently does not re-verify lease phase before dispatch. |
Empirical evidence: PR #131’s task-work run hit this exact path. start_task.py reported the
cache-miss error AND exited 0; the operator only caught the silently-skipped CAS-REPLACE because the
next step’s lease-state inspection surfaced phase: acquired where it expected phase: working.
Proposed
Section titled “Proposed”start_task.py exits non-zero on a cache-miss for the given task_id
so /sdlc:task-work halts at Step 5b instead of marching past a
skipped CAS-REPLACE. Separately, the cache-lookup logic is reviewed
to confirm _find_project_root resolves identically for the writer
(acquire_lease) and the reader (start_task.py); if a divergence
exists, normalize on a single resolution path or expose the resolved
path in the error message so future debugging is mechanical.
Approach
Section titled “Approach”- In
start_task.py, change the cache-miss branch tosys.exit(1)(or raise a typedCacheMissError) AFTER printing the diagnostic. The diagnostic should include the resolved project-root path and the expected cache file path so an operator can compare against whatacquire_leasewrote. - Audit
plugin/lib/lease/cache.py’s_find_project_rootand any parallel resolution used byacquire_lease/start_task.py. If they differ, consolidate on one helper. Add a singlepytestcase asserting writer and reader resolve the same root from identical CWD inputs. - Update
plugin/skills/task-work/SKILL.mdStep 5b prose if needed so the operator knows a non-zero exit at this step is a recoverable failure mode (e.g. re-runsdlc lease task acquirethen re-runstart_task.py).
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
plugin/skills/task-work/start_task.py | modify | Non-zero exit on cache-miss; richer diagnostic (resolved project-root + expected cache path). |
plugin/lib/lease/cache.py | modify | Audit _find_project_root; consolidate if reader/writer diverge. |
plugin/skills/task-work/test_start_task.py | modify | New test asserting non-zero exit on cache-miss and a stable diagnostic on stderr. |
plugin/skills/task-work/SKILL.md | modify | Step 5b prose acknowledges cache-miss as a halt condition with a recovery hint. |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: When
start_task.pyruns against a task_id with no entry in the lease cache, the script exits with a non-zero status code. Verified by apytestcase intest_start_task.py. - AC-2: The diagnostic printed on cache-miss includes the resolved project-root path and the expected cache file path. Verified by asserting both substrings appear on stderr.
- AC-3: Reader and writer resolve the same project root from identical CWDs. Verified by a unit
test invoking the resolver from
cache.pyand from whatever pathacquire_leaseuses, asserting equality. - AC-4: All existing tests pass (no regressions).
Out of scope
Section titled “Out of scope”- Reworking the lease cache format or storage location.
- Auto-recovery (re-running
acquire_leasefrom insidestart_task.py).
Dependencies
Section titled “Dependencies”- none
Discovery context
Section titled “Discovery context”Spawned by /sdlc:task-work post-mortem of T-174G-archive-records-closing-timestamp-for-post-mortem-detector on 2026-05-26.
Dedup search (spawn-from-post-mortem)
Section titled “Dedup search (spawn-from-post-mortem)”Bullet: start_task.py reported error: no cached lease for task_id; acquire_lease must have populated the cache before start_task.py runs AND exited 0 — the CAS-REPLACE to working was silently skipped, the task branch rebase didn’t happen, but the operator only knew because the next step’s lease-state inspection caught it. The script should exit non-zero on this branch and the cache-lookup logic should be reviewed (the cache file existed at the expected path; lookup may be using a different project-root resolution than cache.py’s _find_project_root). Keywords searched: find_project_root, acquire_lease, cache-lookup, project-root, cas-replace, lease-state, start_task, inspection Excluded: 2026-05-26-archive-records-closing-timestamp-for-post-mortem-detector Top candidates (score / status / headline):
- 32 / planning/draft / 2026-05-21-start-task-handles-frontmatter-rebase-cleanly — start_task.py auto-merges frontmatter on stamp/start rebase conflict
- 25 / closed/done / 2026-05-24-task-work-lease-integration-model-b — Convert
/sdlc:task-workto lease-aware Model B (exit at PR open) - 21 / planning/draft / 2026-05-24-run-quality-checks-logs-cwd-and-forwards-project-root — run_quality_checks.py logs cwd at start and forwards —project-root to capable verbs
- 18 / closed/done / 2026-05-21-start-task-preserves-readiness-stamp — start_task.py should preserve readiness_verified_at across rebase
- 16 / closed/done / 2026-05-19-extract-start-task-script — Extract task-work Step 5b into scripts/start_task.py Decision: SPAWNED Rationale: Override script’s LINKED-EXISTING (2026-05-21-start-task-handles-frontmatter-rebase-cleanly). Top candidate is about rebase frontmatter conflict resolution (a different start_task.py failure mode); this bullet is about the cache-lookup path silently exit-0’ing when the cache entry is missing, which is an unrelated structural defect. The keyword overlap (start_task) is coincidental.
Dedup search (spawn-from-post-mortem)
Section titled “Dedup search (spawn-from-post-mortem)”Bullet: start_task.py cache-resolution treats the worktree’s own .sdlc/skill-ext/ as the project
root and looks for .sdlc/runtime/lease-cache/ inside the worktree instead of the main repo. Fix:
_find_project_root should treat the .sdlc/worktrees/
- 70 / closed/done / 2026-05-22-move-plugin-runtime-state-to-sdlc-dir — Migrate sdlc plugin runtime state from .claude/ to .sdlc/
- 32 / planning/draft / 2026-05-26-start-task-fails-loud-on-cache-miss — start_task.py exits non-zero and surfaces cache-miss on lease lookup
- 31 / planning/draft / 2026-05-21-start-task-handles-frontmatter-rebase-cleanly — start_task.py auto-merges frontmatter on stamp/start rebase conflict
- 21 / planning/draft / 2026-05-24-run-quality-checks-logs-cwd-and-forwards-project-root — run_quality_checks.py logs cwd at start and forwards —project-root to capable verbs
- 18 / closed/done / 2026-05-21-start-task-preserves-readiness-stamp — start_task.py should preserve
readiness_verified_at across rebase
Decision: LINKED-EXISTING 2026-05-26-start-task-fails-loud-on-cache-miss Rationale: Override
script’s SPAWNED. Rank-#2 candidate’s AC-3 (“Reader and writer resolve the same project root from
identical CWDs”) directly targets this bullet’s root cause — the worktree-vs-main project-root
divergence in
_find_project_rootthat caused the CAS-REPLACE no-op. Linking surfaces the worktree-specific evidence (symlinking.sdlc/runtime/mid-flight was the workaround) inside the existing task instead of fragmenting the fix across two drafts. Originating task: 2026-05-27-task-work-writes-pr-url-to-prs-at-open
Dedup search (spawn-from-post-mortem)
Section titled “Dedup search (spawn-from-post-mortem)”Bullet: The start_task.py cache-resolution bug surfaced last run (see T-Y7QU-start-task-fails-loud-on-cache-miss) required the same .sdlc/runtime symlink workaround at worktree creation. The workaround is now muscle-memory — but it’s still a workaround. The real fix (skip the worktree’s own .sdlc/ when discovering project root) belongs in either the lease library’s _find_project_root or start_task.py’s explicit walk. Keywords searched: start-task-fails-loud-on-cache-miss, find_project_root, cache-resolution, muscle-memory, discovering, start_task, workaround, surfaced Excluded: 2026-05-27-task-close-out-verifies-prs-against-merged-pr Top candidates (score / status / headline):
- 37 / planning/draft / 2026-05-26-start-task-fails-loud-on-cache-miss — start_task.py exits non-zero and surfaces cache-miss on lease lookup
- 33 / planning/draft / 2026-05-21-start-task-handles-frontmatter-rebase-cleanly — start_task.py auto-merges frontmatter on stamp/start rebase conflict
- 19 / closed/done / 2026-05-21-start-task-preserves-readiness-stamp — start_task.py should preserve readiness_verified_at across rebase
- 17 / closed/done / 2026-05-19-extract-start-task-script — Extract task-work Step 5b into scripts/start_task.py
- 14 / planning/draft / 2026-05-23-start-task-drops-orphan-stashes — start_task.py detects pre-existing stashes referencing files absent on the rebase target and prompts to drop them Decision: LINKED-EXISTING 2026-05-26-start-task-fails-loud-on-cache-miss Originating task: 2026-05-27-task-close-out-verifies-prs-against-merged-pr