Skip to content

T-H0W9-task-work-rebase-frontmatter-conflict

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

Auto-generated from a /sdlc:task-work post-mortem in rust-path-opener (2026-05-19-obsidian-vault-id-uri). Review and promote to open/ready before picking up.

/sdlc:task-work Step 5b’s gate-then-flip design produces a deterministic merge conflict on every run: the verify-commit (on the feat branch) adds readiness_verified_at: and the start-commit (on main) changes status: and last_reviewed: — both edits sit in the same YAML frontmatter block, on adjacent lines, so the rebase always conflicts. The conflict’s resolution is mechanical (union the fields) but the skill text says “stop and surface to the user” on conflict. Either pre-resolve automatically or restructure so the conflict never arises.

  • Step 5a (/sdlc:task-ensure-ready) commits readiness_verified_at: <ts> on the feat branch. No status change.
  • Step 5b then edits the task file on main: sets status: in-progress and last_reviewed: <today>. Commits on main.
  • Step 5b.6 runs git rebase main inside the worktree to bring the feat branch onto the updated main. Both commits touched the frontmatter YAML on adjacent lines, so the rebase reliably emits a CONFLICT (content) on the task file.
  • The resolution is always the same: take status and last_reviewed from main, take readiness_verified_at from feat, leave all other fields intact. There is no real ambiguity.
  • The skill text at Step 5b.6 says: “If git rebase main at 5b.6 surfaces a conflict, something is wrong with the local state… Stop and surface to the user; do not auto-resolve.” This is the wrong guidance for the structural conflict, since “something is wrong” is in fact “the design produces this conflict every time.”
  • Reproduced in rust-path-opener /sdlc:task-work run on 2026-05-19-obsidian-vault-id-uri.

Restructure the commits so they don’t touch adjacent lines. /sdlc:task-ensure-ready always writes readiness_verified_at: as the LAST frontmatter key (after related:/tags:). Step 5b’s start-commit only edits the top fields (status, last_reviewed). The two hunks are now physically separated by every other frontmatter key plus the closing ---, well outside git’s ~3-line conflict context, so the rebase sees disjoint hunks and merges without conflict.

Rejected alternatives (kept here for posterity):

  • (b) Custom git merge driver / rerere for docs/planning/tasks/*.md that auto-unions YAML frontmatter. Solves the symptom; more plumbing than the source-structure fix. Revisit if other frontmatter-conflict scenarios surface.
  • (c) Flip commit order — start-commit first, then verify. Architecturally cleaner (no rebase needed) but flips the semantics to mark-then-verify, leaving an orphan in-progress on main if verify fails, and requires reworking ensure-ready’s “skip if input was already in-progress” carve-out. Larger change for the same outcome.
  1. Update /sdlc:task-ensure-ready’s frontmatter-edit logic in plugin/skills/task-ensure-ready/SKILL.md Step 4 so readiness_verified_at: is always Edited in as the LAST key of the frontmatter block, after related: and tags:. If a previous stamp exists not-at-bottom, move it.
  2. Update plugin/skills/task-work/SKILL.md Step 5b.6 to remove the “stop and surface to the user; do not auto-resolve” guidance — the structural conflict is eliminated. Keep the warning only for genuine ancestry conflicts (parallel sessions, manual edits).
  3. Add a regression case to plugin/skills/task-ensure-ready/tests/run_evals.py that runs the gate-flip sequence on a fixture task (verify on feat, start-commit on main, rebase feat onto main) and asserts the rebase exits 0 with no conflict markers in the task file.
  • plugin/skills/task-ensure-ready/SKILL.md — Step 4 frontmatter- write prose: always place readiness_verified_at: last.
  • plugin/skills/task-work/SKILL.md — Step 5b.6 prose: remove the “stop on conflict” structural-conflict guidance.
  • plugin/skills/task-ensure-ready/tests/run_evals.py — add the gate-flip regression case.
  • AC-1: Running /sdlc:task-work <slug> end-to-end on a fresh task produces no rebase conflict at Step 5b.6.
  • AC-2: Step 5b.6’s “stop on conflict” guidance is removed from plugin/skills/task-work/SKILL.md; the warning that remains mentions only genuine ancestry conflicts (parallel sessions / manual edits), not the structural case.
  • AC-3: plugin/skills/task-ensure-ready/tests/run_evals.py contains a case that builds a fixture git repo, runs the verify-then-start-then-rebase sequence, and asserts git rebase exits 0 with no conflict markers in the task file.
  • Conflicts that arise from genuine concurrent edits to the task file by parallel sessions. Those are real and should still stop and surface.
  • none
  • Surfaced 2026-05-20 by /sdlc:task-work running 2026-05-19-obsidian-vault-id-uri in sksizer/rust-path-opener. Step 5b.6 emitted the documented conflict on first attempt; the user had to authorize the union-merge resolution manually.

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

  • AC-1: auto — gate-flip-rebase-clean in plugin/skills/task-ensure-ready/tests/run_evals.py builds a throwaway repo and exercises the verify-then-start-then-rebase sequence end-to-end; the rebase exits 0 with no conflict markers. Note: this very run’s Step 5b.6 still hit the conflict, because the task’s pre-fix stamp lived mid-block — that was expected and is the last time it will fire on a fresh run.
  • AC-2: agent-manual — read the edited prose in plugin/skills/task-work/SKILL.md Step 5b “Exit code 3” bullet; the structural-conflict guidance is gone, the remaining text scopes exit 3 to parallel sessions, manual main edits, and origin/main divergence.
  • AC-3: auto — run_evals.py now reports 9/9 case(s) passed including the new gate-flip-rebase-clean case.
  • The fix surface (one-line behaviour change in apply_passpop + re-insert at the end) was as small as the spec’s “Proposed” section promised. No reordering of other keys, no schema change.
  • The new test exercises real git rebase on a real fixture repo, so it would fail loudly if the structural conflict ever returned — exactly the regression shape the AC asked for.
  • /sdlc:task-work Step 5b.6 hit the very conflict the task was fixing, on the task that was fixing it. Auto-resolution would have been mechanical (union the two YAML hunks), but the skill’s prose says “stop and surface.” A future improvement: have start_task.py detect the structural-conflict shape (only the task file conflicts, only frontmatter, the two hunks are non-overlapping when unioned) and apply the union automatically. Lower priority now that the fix lands, since fresh runs will not hit it — but the next bootstrap task that edits ensure-ready’s stamp logic could still trip it. → T-H98A-last-reviewed-rebase-collision
  • The task’s prior stamp at '2026-05-21T04:02:38Z' was idempotently refreshed by ensure-ready inside the worktree to a fresh '2026-05-21T15:43:38Z' — no friction, but worth noting that re-running ensure-ready bumps the stamp even when the contract has not actually been re-verified against new code. Not a gap to file, just a behaviour to remember.
  • T-H98A-last-reviewed-rebase-collision — existing task already covers the broader “auto-union mechanical frontmatter conflicts in start_task.py” gap; linked rather than duplicated.

← Back to Tasks