Skip to content

T-WKQD-task-work-ancestry-helper-one-liner

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

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

/sdlc:task-work Step 9 (“Sync with origin/main before opening the PR”) asks the agent to eyeball git log origin/main..HEAD --oneline and identify whether any commit in that range is contamination from a parallel session. The judgment is easy to make wrong when many parallel task-work runs touch local main. A one-line helper that says “contaminated: [list of basenames]” or “clean” would make the contamination-rebase decision deterministic.

Step 9 of plugin/skills/task-work/SKILL.md lists the inspection procedure. The agent runs git log origin/main..HEAD --oneline and scans the output for chore(tasks): start <other-basename> commits that belong to other sessions. Doing this by eye is fine for one or two parallel sessions but degrades fast as the project’s parallelism grows.

A small script (likely plugin/skills/task-work/check_ancestry.py per the co-location convention, or a shared plugin/scripts/check_branch_ancestry.py if a second skill needs it) that, given the current feat branch’s basename and origin/main, prints either:

  • clean — every non-origin/main commit on the branch is owned by the current task (start-commit, verify-commit, work commits), or
  • contaminated: [<other-basename-1>, <other-basename-2>] — list the foreign start-commits that need to be dropped by Step 9’s git rebase --onto origin/main <last-contaminating-sha> operation.

Skill prose at Step 9 then becomes “run this script; if it prints clean, push; if it prints contaminated, run the documented rebase with the listed sha.” Removes the human judgment loop.

  1. Implement the script. Logic: walk git log origin/main..HEAD --format=%H%n%s, parse chore(tasks): start <basename> lines, compare basename to the script’s --this-basename argument, classify.
  2. Wire it into Step 9 of plugin/skills/task-work/SKILL.md as the canonical contamination check.
  3. (Optional, follow-up if multi-skill need emerges) promote the script to plugin/scripts/ per the co-location principle.
  • plugin/skills/task-work/check_ancestry.py (new) — the helper.
  • plugin/skills/task-work/SKILL.md — replace the prose-only inspection in Step 9 with a check_ancestry.py invocation.
  • AC-1: Running the helper on a feat branch whose ancestry contains exactly the start-commit for that basename plus its verify/work commits prints clean.
  • AC-2: Running it on a feat branch that also has a chore(tasks): start <other-basename> commit between its branch-point and its own start-commit prints contaminated: [<other-basename>] along with the sha of the last contaminating commit (suitable for the git rebase --onto invocation).
  • Promoting the helper to plugin/scripts/ until a second skill actually needs it (the co-location convention says wait for the real second caller).
  • Auto-running the rebase. The script reports; task-work prose still runs the rebase command with the listed sha.
  • Detecting contamination from non-start-commit shapes (e.g. a parallel session committing directly to main without a start-commit). The heuristic is chore(tasks): start <basename> only.
  • none

Spawned by /sdlc:task-work post-mortem of T-UBJK-co-locate-skill-specific-scripts on 2026-05-20.

Captured by /sdlc:task-work on 2026-05-21. PR: pending.

  • AC-1: auto — plugin/skills/task-work/test_check_ancestry.py::test_clean_branch builds a fixture repo with only the task’s own start-commit + work commit past origin/main and asserts the helper prints clean.
  • AC-2: auto — plugin/skills/task-work/test_check_ancestry.py::test_contaminated_branch injects a chore(tasks): start other commit on main before branching and asserts the helper prints contaminated: [other] last_sha=<sha> with the sha matching the foreign commit. A third case (test_multiple_contaminants_ordered) covers the two-contaminant ordering so the reported last_sha is unambiguously the most recent foreign start-commit.
  • The co-location convention from T-UBJK-co-locate-skill-specific-scripts paid off immediately: plugin/skills/task-work/check_ancestry.py and its tests landed next to start_task.py / test_start_task.py without a moment of “where does this go”.
  • The existing start_task.py shebang + # /// script PEP-723 header was a clean template; the new script and test file reused the shape verbatim, with dependencies = [] since the classifier needs no third-party packages.
  • Quality checks (run_quality_checks.py) caught nothing because there was nothing to catch — the test runs locally and pegs both ACs, and the SKILL.md edit was prose-only.
  • Step 5b (start_task.py) surfaced a rebase conflict — both main’s start-commit and the feat branch’s verify-commit edited the same frontmatter lines (last_reviewed: and readiness_verified_at:). The script exited 3 and left REBASE state for manual resolution. This is the expected failure mode for the script (and a known carve-out — Step 5b prose says “If conflicts are non-trivial, stop and ask”), but the specific shape (start-commit’s last_reviewed: bump vs verify-commit’s readiness_verified_at: stamp) is mechanical and identical every time. A targeted resolver — “if the only conflict in the rebase is the task’s own frontmatter, auto-merge by union-of-frontmatter-fields with main’s last_reviewed and feat’s readiness_verified_at” — would eliminate one human-in-the-loop touch per task-work run. → T-H0W9-task-work-rebase-frontmatter-conflict
  • The task’s “Approach” said “walk git log origin/main..HEAD --format=%H%n%s” but %H%n%s puts the sha and subject on separate lines, which is awkward to parse robustly when subjects can contain newlines (they can’t, but a parser shouldn’t assume). The implementation switched to --format=%H%x09%s (tab-separated, single line per commit) which is the more idiomatic shape. Minor — the task spec was directionally right — but a brief convention doc on “git log machine-parseable formats” under plugin/conventions/ might prevent this drift the next time a script needs to consume git log output. → T-TH9J-git-log-machine-parseable-format-convention

← Back to Tasks