Skip to content

T-LN0B-orchestrator-tick-race-fixture

Status: closed/obsoleted · Impact: low · Complexity: medium

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

/sdlc:task-close-out Step 4 mitigates the “two orchestrator ticks fire close-out for the same task concurrently” race via git pull --rebase --autostash before git push. The mitigation is currently spec-only — there’s no integration test that demonstrates the race is actually contained. Once T-FQCN-self-driving-orchestrator-loop’s orchestrator starts dispatching close-outs autonomously the race becomes reachable, and the cost of discovering it broken in production (a mangled commit on main, or a push rejection that leaves the task half-closed) is higher than the cost of a small fixture today. Closing this gap turns a load-bearing prose claim into a verified property.

From the originating post-mortem:

Step 4’s “race against concurrent orchestrator ticks via pull --rebase --autostash” remains spec-only. There’s no integration test for the race itself; we’re trusting git’s well-known semantics. Worth a small fixture once the orchestrator is shipping ticks.

plugin/skills/task-close-out/SKILL.md Step 4 pins the --rebase --autostash invocation as load-bearing, but no test in the repo (eval harness, project-check, or otherwise) exercises the race. The closest signal is the invariants linter checking for the literal phrase in the SKILL.md body.

A new case in plugin/skills/task-close-out/tests/run_evals.py (the established eval-harness shape across the plugin — see plugin/skills/entities-migrate/tests/entities_migrate.test.ts etc. for precedent) that:

  1. Spins up a throwaway bare repo and two working clones acting as “tick A” and “tick B.”
  2. Has both clones run the Step 4 sequence (edit frontmatter, commit, pull --rebase --autostash, push) against the same task file at nearly the same time.
  3. Asserts that exactly one push succeeds, the loser’s pull --rebase --autostash reconciles cleanly, and the final state of the task file on the bare repo matches the expected closed-out frontmatter with no merge conflict markers.

The case lives alongside whatever existing cases the file contains. The runner already runs as part of the plugin’s quality-checks pipeline via sdlc.yaml.

  1. Create or extend plugin/skills/task-close-out/tests/run_evals.py to add the race-condition case. Follow the established (name, expected_exit, expected_substrings) shape from sibling eval-suite runners.
  2. The case builds a bare-repo + two-clone fixture in tmp_path with deterministic ordering (file-based sync points, not wall-clock timing). Run both Step-4 sequences, assert the three properties above.
  3. Mark the case as the canonical regression for Step 4’s race-mitigation claim — name it race_orchestrator_concurrent_ticks so it’s findable.
LocationKindChange
plugin/skills/task-close-out/tests/run_evals.pynew*(new file
plugin/skills/task-close-out/invariants.yamlmodifyoptional
  • AC-1: A new case race_orchestrator_concurrent_ticks in plugin/skills/task-close-out/tests/run_evals.py exercises two simultaneous Step-4 sequences against a shared bare repo. The case is picked up by the existing eval-suite runner.
  • AC-2: The case asserts: exactly one push succeeds on the first attempt; the loser’s rebase reconciles without conflict; the final ref on the bare repo contains both close-out edits in a linear history. (auto)
  • AC-3: Deliberately breaking Step 4 (e.g. removing --autostash) makes the case fail. Verified manually once during PR review by mutating the harness and re-running.
  • Exercising the race against the real GitHub remote. The bare repo is enough — we’re testing git semantics, not GitHub’s.
  • Generalizing the harness to other skills’ race-mitigation claims. Start with close-out; extend later if useful.
  • none

Spawned by /sdlc:task-work post-mortem of T-RDKI-extract-task-close-out-skill on 2026-05-20.


← Back to Tasks