Skip to content

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.

LocationRole today
plugin/skills/task-work/start_task.pyLooks 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 5bInvokes 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.

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.

  1. In start_task.py, change the cache-miss branch to sys.exit(1) (or raise a typed CacheMissError) 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 what acquire_lease wrote.
  2. Audit plugin/lib/lease/cache.py’s _find_project_root and any parallel resolution used by acquire_lease / start_task.py. If they differ, consolidate on one helper. Add a single pytest case asserting writer and reader resolve the same root from identical CWD inputs.
  3. Update plugin/skills/task-work/SKILL.md Step 5b prose if needed so the operator knows a non-zero exit at this step is a recoverable failure mode (e.g. re-run sdlc lease task acquire then re-run start_task.py).
LocationKindChange
plugin/skills/task-work/start_task.pymodifyNon-zero exit on cache-miss; richer diagnostic (resolved project-root + expected cache path).
plugin/lib/lease/cache.pymodifyAudit _find_project_root; consolidate if reader/writer diverge.
plugin/skills/task-work/test_start_task.pymodifyNew test asserting non-zero exit on cache-miss and a stable diagnostic on stderr.
plugin/skills/task-work/SKILL.mdmodifyStep 5b prose acknowledges cache-miss as a halt condition with a recovery hint.
  • AC-1: When start_task.py runs against a task_id with no entry in the lease cache, the script exits with a non-zero status code. Verified by a pytest case in test_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.py and from whatever path acquire_lease uses, asserting equality.
  • AC-4: All existing tests pass (no regressions).
  • Reworking the lease cache format or storage location.
  • Auto-recovery (re-running acquire_lease from inside start_task.py).
  • none

Spawned by /sdlc:task-work post-mortem of T-174G-archive-records-closing-timestamp-for-post-mortem-detector on 2026-05-26.

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-work to 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.

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// ancestor specially or start_task.py should pass an explicit —project-root. Keywords searched: find_project_root, cache-resolution, project-root, lease-cache, start_task, skill-ext, worktrees, specially Excluded: 2026-05-27-task-work-writes-pr-url-to-prs-at-open Top candidates (score / status / headline):

  • 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_root that 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

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

← Back to Tasks