Skip to content

T-61OI-check-ancestry-flags-stale-base

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

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

plugin/skills/task-work/check_ancestry.ts returns clean whenever no commit in origin/main..HEAD is a chore(tasks): start <other> foreign commit. But that’s only one way the branch can be contaminated — when local main was fast-forwarded mid-session by an externally-merged PR, the task branch’s diff against origin/main can contain restructured upstream files completely unrelated to the task. The helper still prints clean, so the operator skips the rebase, and Step 9’s PR-prep is the first to discover the dirty diff. Cite T-5VN7-pr-check-cursor-bootstrap-misses-existing-comments — the PR’s diff carried a flat docs/decisions/0001-github-ref-leases.md that had been restructured into a folder on origin/main, and the ancestry helper’s clean verdict was misleading.

LocationRole today
plugin/skills/task-work/check_ancestry.tsWalks git log origin/main..HEAD --format=%H%n%s, parses chore(tasks): start <basename> lines, returns clean or contaminated: [...]. Detects parallel-task contamination only.
plugin/skills/task-work/SKILL.md (Step 9)Calls the helper and treats clean as “diff against origin/main is mine alone, push.”
plugin/skills/task-work/tests/check_ancestry.test.tsExercises the parallel-task contamination cases but not the stale-base case.

The helper conflates two different definitions of “clean”:

  1. Parallel-task-clean — no foreign chore(tasks): start commits.
  2. Diff-cleangit diff origin/main...HEAD touches only files the current task owns.

Today’s clean verdict means (1) but Step 9 reads it as (2).

check_ancestry.ts gains a second sub-check (or a separate verdict field) that compares the diff against origin/main to a project- inferred or task-declared scope. Possible shapes:

  • A new verdict clean-with-upstream-drift: <N> non-task files changed when (1) holds but (2) doesn’t.
  • A new flag like --diff-mode that, when set, also asserts the diff set is non-empty and rooted in expected scope.
  • Step 9 prose updates to call out which verdict means “rebase before push.”

Approach intentionally left thin so the implementer can pick the shape that fits the helper’s existing CLI surface.

  1. Reproduce the failure mode: branch off an old main, fast-forward local main to a newer commit on origin/main, run the helper, observe clean, then inspect git diff origin/main...HEAD to see the contamination.
  2. Decide between (a) extending the existing clean/contaminated enum with a new verdict and (b) adding an independent diff-vs-origin-main check that the helper runs alongside the ancestry walk. Both are workable; pick the one that requires the smallest Step 9 prose change.
  3. Implement the diff check. Walk git diff origin/main...HEAD --name-only and classify each file as “mine” (under docs/planning/tasks/<this-basename>.md, or under a paths-touched list this task declares in its frontmatter files_to_touch:) versus “upstream drift.”
  4. Update tests/check_ancestry.test.ts with a fixture that exercises the stale-base case.
  5. Update plugin/skills/task-work/SKILL.md Step 9 prose to read the new verdict.
LocationKindChange
plugin/skills/task-work/check_ancestry.tsmodifyAdd the diff-vs-origin-main scope check; widen the verdict enum or add a parallel field.
plugin/skills/task-work/tests/check_ancestry.test.tsmodifyAdd a fixture exercising stale-base contamination.
plugin/skills/task-work/SKILL.mdmodifyStep 9 prose interprets the new verdict and prescribes the rebase action.
  • AC-1: A test fixture exercises a task branch where local main is behind origin/main such that git diff origin/main...HEAD contains files outside the task’s scope. The helper reports the drift (not clean).
  • AC-2: The existing “parallel-task contamination” path still returns contaminated: [...] unchanged — no regression in the pre-shipped behavior.
  • AC-3: plugin/skills/task-work/SKILL.md Step 9 reads the new verdict and prescribes the rebase action when the helper flags stale-base drift.
  • Auto-rebasing on detection. The helper still reports; Step 9 still performs the rebase. The change is only to the helper’s verdict granularity.
  • Generalizing the helper into a reusable cross-skill branch-health diagnostic. The scope here is the task-work Step 9 contract.
  • none

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

Bullet: Local main got fast-forwarded mid-session (PR #97 merged externally), leaving the task branch rooted off a stale main commit with foreign files in the diff against origin/main. check_ancestry.py reported clean because it only flags chore(tasks): start foreign commits, not unrelated upstream restructures. Step 9’s rebase resolved it cleanly, but the helper’s clean verdict was misleading — diff-against-origin-main was non-trivial yet the verdict didn’t say so. The helper could grow a second mode that distinguishes clean from parallel task-work contamination from task branch’s diff vs origin/main contains only this task’s files. Keywords searched: diff-against-origin-main, fast-forwarded, check_ancestry, distinguishes, contamination, restructures, mid-session, non-trivial Excluded: 2026-05-21-pr-check-cursor-bootstrap-misses-existing-comments Top candidates (score / status / headline):

  • 9 / closed/done / 2026-05-20-task-work-ancestry-helper-one-liner — Helper that prints feat-branch ancestry diff vs origin/main
  • 6 / open/ready / 2026-05-21-task-work-step-4-branches-from-origin-main — Step 4 worktree-add branches from origin/main when local main is contaminated
  • 5 / planning/draft / 2026-05-21-git-log-machine-parseable-format-convention — Convention doc for machine-parseable git log formats
  • 3 / closed/done / 2026-05-19-co-locate-skill-specific-scripts — Co-locate skill-specific scripts under their skill dir; promote when shared
  • 3 / closed/done / 2026-05-19-migrate-reorders-frontmatter-keys — Shared reorder_frontmatter_keys helper produces consistent key order across migrate paths Decision: SPAWNED

Captured by /sdlc:task-work on 2026-06-04. PR: pending.

  • AC-1: auto — bun test plugin/skills/task-work/tests/check_ancestry.test.ts; new stale base: ... fixture asserts classify returns stale-base with the restructured upstream pair as driftFiles, and the CLI prints stale-base: 2 upstream-drift files; rebase onto origin/main.
  • AC-2: auto — same suite; the three pre-shipped parallel-task cases (clean, single contaminant, multiple contaminants) pass unchanged, plus a new contaminated takes precedence over stale-base drift case proves the verdict order.
  • AC-3: agent-manual — read the rendered plugin/skills/task-work/SKILL.md Step 9: the clean bullet notes the diff-scope condition, a new stale-base: bullet explains the fast-forwarded-base mode, and item 3 splits into 3a (contaminatedrebase --onto) and 3b (stale-base → plain rebase origin/main).
  • The two-dot-minus-three-dot diff heuristic was confirmed with a throwaway repro repo before any production code was written, so the implementation matched the verified mechanism on the first pass.
  • Baseline-gated quality checks correctly subtracted out the 356 pre-existing corpus/test findings; the only “new-drift” flagged was spurious corpus-listing churn, none of it touching the three changed files.
  • run_quality_checks.ts --diff-against-baseline defaulted its --baseline-dir to the worktree’s .sdlc/quality-baselines/, but Step 3a captured the baseline under the main repo’s dir, so the first gate run exited 2 with “baseline not found” — the gate should resolve the baseline dir against the main checkout (worktree superproject) by default, the same way start_task.ts/append_pr_url.ts resolve the main repo, so the Step 7 invocation doesn’t need a manual --baseline-dir override. → T-44OO-plugin-scripts-self-discover-project-root
  • audit_entities.ts audits the live on-disk corpus, so uncommitted task-file edits in the main checkout shift its per-file - OK listing and surface as phantom new-drift: lines against a baseline captured from committed origin/main state — the baseline gate flags benign - OK reordering as drift. The entity audit’s baseline diff should compare on the set of genuine DRIFT/ERROR findings (or the drift summary count), not on every per-file status line. → T-BCNP-quality-gate-ignores-summary-and-corpus-lines
  • ensure-ready’s --cleanup-on-fail is rejected by ensure_ready_mutate.ts on --mode pass (“only meaningful with —mode fail”), but task-work Step 5a’s prose says to pass both flags together — the skill prose and the script contract disagree, forcing the operator to drop the flag on the pass path. Either the script should accept-and-ignore --cleanup-on-fail on pass, or Step 5a should only add it on the fail branch. → T-BOZ6-ensure-ready-cleanup-flag-pass-mode-contract

← Back to Tasks