From e24012204245f7ad6f512e2c055f558aa512e282 Mon Sep 17 00:00:00 2001 From: geuben Date: Sat, 29 Aug 2026 08:06:57 +0100 Subject: [PATCH 01/15] test: verdict outcome lookup admits it cannot judge an unexecuted id TDD-Run: 14 TDD-Cycle: 1 TDD-Phase: red --- tests/test_advance_adoption.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/test_advance_adoption.py diff --git a/tests/test_advance_adoption.py b/tests/test_advance_adoption.py new file mode 100644 index 0000000..56d7296 --- /dev/null +++ b/tests/test_advance_adoption.py @@ -0,0 +1,10 @@ +from tddcli.adapters.base import Verdict +from tddcli import advance + + +def test_outcome_lookup_returns_none_for_unexecuted_id(): + verdicts = [ + Verdict(passed=["p1::tests/test_x.py::test_a"], failed=[]), + Verdict(passed=[], failed=["p2::tests/test_y.py::test_b"]), + ] + assert advance._outcome_from_verdicts(verdicts, "backend::tests/test_x.py::test_y") is None From e60c9e5e34f9ad854827b2b354c5a381eca169e4 Mon Sep 17 00:00:00 2001 From: geuben Date: Sat, 29 Aug 2026 08:09:10 +0100 Subject: [PATCH 02/15] feat: _outcome_from_verdicts helper (None when the id never ran) TDD-Run: 14 TDD-Cycle: 1 TDD-Phase: green --- src/tddcli/advance.py | 9 +++++++++ tests/test_advance_adoption.py | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/tddcli/advance.py b/src/tddcli/advance.py index efca1e5..44063c4 100644 --- a/src/tddcli/advance.py +++ b/src/tddcli/advance.py @@ -117,6 +117,15 @@ def _stage_and_commit(engine: Engine, cycle, phase: str, declared) -> tuple[str return sha, staged, classification +def _outcome_from_verdicts(verdicts, test_id: str) -> str | None: + for v in verdicts: + if test_id in v.failed: + return FAILED + if test_id in v.passed: + return PASSED + return None + + # -- handlers ------------------------------------------------------------ diff --git a/tests/test_advance_adoption.py b/tests/test_advance_adoption.py index 56d7296..449f401 100644 --- a/tests/test_advance_adoption.py +++ b/tests/test_advance_adoption.py @@ -4,7 +4,7 @@ def test_outcome_lookup_returns_none_for_unexecuted_id(): verdicts = [ - Verdict(passed=["p1::tests/test_x.py::test_a"], failed=[]), - Verdict(passed=[], failed=["p2::tests/test_y.py::test_b"]), + Verdict("p1", "pytest", passed=["p1::tests/test_x.py::test_a"], failed=[]), + Verdict("p2", "pytest", passed=[], failed=["p2::tests/test_y.py::test_b"]), ] assert advance._outcome_from_verdicts(verdicts, "backend::tests/test_x.py::test_y") is None From 6d97ea080d230dd34e97aeb9104b36f5128565e7 Mon Sep 17 00:00:00 2001 From: geuben Date: Sat, 29 Aug 2026 08:10:53 +0100 Subject: [PATCH 03/15] refactor: outcome lookup returns None for an id absent from every verdict TDD-Run: 14 TDD-Cycle: 1 TDD-Phase: refactor --- tests/test_advance_adoption.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_advance_adoption.py b/tests/test_advance_adoption.py index 449f401..c2c7484 100644 --- a/tests/test_advance_adoption.py +++ b/tests/test_advance_adoption.py @@ -1,5 +1,5 @@ -from tddcli.adapters.base import Verdict from tddcli import advance +from tddcli.adapters.base import Verdict def test_outcome_lookup_returns_none_for_unexecuted_id(): From e6de7826b8d66db29bf29bb1746772ab88ab894b Mon Sep 17 00:00:00 2001 From: geuben Date: Sat, 29 Aug 2026 08:14:39 +0100 Subject: [PATCH 04/15] test: adoption of the one new failing test reaches RED without a re-run TDD-Run: 14 TDD-Cycle: 2 TDD-Phase: red --- tests/test_advance_adoption.py | 39 ++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/test_advance_adoption.py b/tests/test_advance_adoption.py index c2c7484..e7dfe83 100644 --- a/tests/test_advance_adoption.py +++ b/tests/test_advance_adoption.py @@ -1,6 +1,32 @@ +from conftest import git, run_cli, write_plan + from tddcli import advance from tddcli.adapters.base import Verdict +PLAN_SINGLE_CANDIDATE = """--- +cycles: + - n: 1 + project: backend + title: "add two numbers" + test: "tests/test_add.py::test_add_two_numbers" + commit_red: "test: add two numbers" + commit_green: "feat: add()" +--- +""" + +TEST_ADDING = """\ +from app.calc import add + + +def test_adding(): + assert add(2, 3) == 5 +""" + +CALC_STUB = """\ +def add(a, b): + raise NotImplementedError +""" + def test_outcome_lookup_returns_none_for_unexecuted_id(): verdicts = [ @@ -8,3 +34,16 @@ def test_outcome_lookup_returns_none_for_unexecuted_id(): Verdict("p2", "pytest", passed=[], failed=["p2::tests/test_y.py::test_b"]), ] assert advance._outcome_from_verdicts(verdicts, "backend::tests/test_x.py::test_y") is None + + +def test_single_new_test_is_adopted_and_evaluated_in_one_advance(repo): + plan = write_plan(repo, PLAN_SINGLE_CANDIDATE) + run_cli(repo, "plan", "register", plan) + run_cli(repo, "run", "start", "--plan", plan) + + (repo / "backend" / "tests" / "test_add.py").write_text(TEST_ADDING) + (repo / "backend" / "app" / "calc.py").write_text(CALC_STUB) + + out = run_cli(repo, "advance") + assert out["next_action"]["verb"] == "write_implementation", out + assert out["run"]["phase"] == "AWAITING_IMPL" From 14aea5202d2c52e5b4abba93d84d77346b4d467d Mon Sep 17 00:00:00 2001 From: geuben Date: Sat, 29 Aug 2026 08:17:18 +0100 Subject: [PATCH 05/15] feat: evaluate the adopted target from the suite run that already happened TDD-Run: 14 TDD-Cycle: 2 TDD-Phase: green --- src/tddcli/advance.py | 34 ++++++++++++++++++++-------------- tests/test_advance_adoption.py | 2 +- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/tddcli/advance.py b/src/tddcli/advance.py index 44063c4..3d1a0ed 100644 --- a/src/tddcli/advance.py +++ b/src/tddcli/advance.py @@ -136,7 +136,7 @@ def _handle_test_phase(engine: Engine, cycle, retried: bool, expect_pass: bool) targets = json.loads(cycle["target_tests"]) phase = cycle["phase"] - outcomes, others, _, failure = engine.run_projects( + outcomes, others, verdicts, failure = engine.run_projects( projects, targets, cycle, phase, retried ) @@ -153,13 +153,18 @@ def _handle_test_phase(engine: Engine, cycle, retried: bool, expect_pass: bool) "cycle", cycle["id"], target_tests=json.dumps(kept + candidates) ) cycle = engine.ledger.one("SELECT * FROM cycle WHERE id = ?", (cycle["id"],)) - return _reply( - engine, cycle, Verb.REFACTOR_OR_ADVANCE, - f"Adopted {candidates[0]} as the target (declared {missing[0]} was not" - " collected). Run `tdd advance` again to evaluate it.", - adopted=candidates, - ) - if len(candidates) > 1: + adopted_outcome = _outcome_from_verdicts(verdicts, candidates[0]) + if adopted_outcome is None: + return _reply( + engine, cycle, Verb.REFACTOR_OR_ADVANCE, + f"Adopted {candidates[0]} as the target (declared {missing[0]} was not" + " collected). Run `tdd advance` again to evaluate it.", + adopted=candidates, + ) + targets = kept + candidates + outcomes = {candidates[0]: adopted_outcome} + others = [t for t in others if t != candidates[0]] + elif len(candidates) > 1: engine.ledger.event( engine.run["id"], cycle["id"], "multiple_new_tests", json.dumps(candidates), @@ -170,12 +175,13 @@ def _handle_test_phase(engine: Engine, cycle, retried: bool, expect_pass: bool) " intended target with `tdd target `.", candidates=candidates, ) - return _reply( - engine, cycle, Verb.WRITE_TEST, - f"Target {missing[0]} was not collected and no new test was found." - " Write the failing test.", - missing=missing, - ) + else: + return _reply( + engine, cycle, Verb.WRITE_TEST, + f"Target {missing[0]} was not collected and no new test was found." + " Write the failing test.", + missing=missing, + ) not_collected = [t for t, o in outcomes.items() if o == NOT_COLLECTED] if not_collected: diff --git a/tests/test_advance_adoption.py b/tests/test_advance_adoption.py index e7dfe83..135068e 100644 --- a/tests/test_advance_adoption.py +++ b/tests/test_advance_adoption.py @@ -1,4 +1,4 @@ -from conftest import git, run_cli, write_plan +from conftest import run_cli, write_plan from tddcli import advance from tddcli.adapters.base import Verdict From 7eb1bfeb09be1ba8bbbe432a80d6f534b658056a Mon Sep 17 00:00:00 2001 From: geuben Date: Sat, 29 Aug 2026 08:19:19 +0100 Subject: [PATCH 06/15] refactor: a single adopted test that failed is evaluated as RED in the same advance TDD-Run: 14 TDD-Cycle: 2 TDD-Phase: refactor --- tests/test_advance_adoption.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_advance_adoption.py b/tests/test_advance_adoption.py index 135068e..8c904fa 100644 --- a/tests/test_advance_adoption.py +++ b/tests/test_advance_adoption.py @@ -1,5 +1,4 @@ from conftest import run_cli, write_plan - from tddcli import advance from tddcli.adapters.base import Verdict From 8df5166dced3c7d242ff242497e5da33e86c6ee6 Mon Sep 17 00:00:00 2001 From: geuben Date: Sat, 29 Aug 2026 08:25:24 +0100 Subject: [PATCH 07/15] refactor: a single adopted test that passed drives sensitivity in the same advance TDD-Run: 14 TDD-Cycle: 3 TDD-Phase: refactor --- tests/test_advance_adoption.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/test_advance_adoption.py b/tests/test_advance_adoption.py index 8c904fa..6b28631 100644 --- a/tests/test_advance_adoption.py +++ b/tests/test_advance_adoption.py @@ -46,3 +46,15 @@ def test_single_new_test_is_adopted_and_evaluated_in_one_advance(repo): out = run_cli(repo, "advance") assert out["next_action"]["verb"] == "write_implementation", out assert out["run"]["phase"] == "AWAITING_IMPL" + + +def test_adopted_passing_test_demands_sensitivity_in_one_advance(repo): + plan = write_plan(repo, PLAN_SINGLE_CANDIDATE) + run_cli(repo, "plan", "register", plan) + run_cli(repo, "run", "start", "--plan", plan) + + (repo / "backend" / "tests" / "test_add.py").write_text(TEST_ADDING) + (repo / "backend" / "app" / "calc.py").write_text("def add(a, b):\n return a + b\n") + + out = run_cli(repo, "advance") + assert out["next_action"]["verb"] == "run_sensitivity_check", out From f9813f28220e194fb91e954e960746ff829d1687 Mon Sep 17 00:00:00 2001 From: geuben Date: Sat, 29 Aug 2026 08:29:20 +0100 Subject: [PATCH 08/15] test: _disambiguate resolves a vitest separator-only mismatch TDD-Run: 14 TDD-Cycle: 4 TDD-Phase: red --- tests/test_advance_adoption.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/test_advance_adoption.py b/tests/test_advance_adoption.py index 6b28631..f93c309 100644 --- a/tests/test_advance_adoption.py +++ b/tests/test_advance_adoption.py @@ -1,6 +1,23 @@ +from pathlib import Path + from conftest import run_cli, write_plan -from tddcli import advance +from tddcli import advance, config as config_mod from tddcli.adapters.base import Verdict +from tddcli.adapters.vitest_adapter import VitestAdapter + +VITEST_TOML = """ +[project.frontend] +root = "frontend" +adapter = "vitest" +test_paths = ["**/*.test.ts"] +""" + + +def _vitest_adapter(tmp_path: Path) -> VitestAdapter: + (tmp_path / "tdd.toml").write_text(VITEST_TOML) + (tmp_path / "frontend").mkdir() + cfg = config_mod.load(tmp_path) + return VitestAdapter(cfg.project("frontend"), tmp_path) PLAN_SINGLE_CANDIDATE = """--- cycles: @@ -48,6 +65,16 @@ def test_single_new_test_is_adopted_and_evaluated_in_one_advance(repo): assert out["run"]["phase"] == "AWAITING_IMPL" +def test_disambiguate_picks_the_normalisation_match(tmp_path): + adapter = _vitest_adapter(tmp_path) + candidates = [ + "frontend::a.test.ts > helper formats a value", + "frontend::b.test.ts > other case", + ] + declared = "frontend::a.test.ts > helper > formats a value" + assert advance._disambiguate(candidates, declared, adapter) == candidates[0] + + def test_adopted_passing_test_demands_sensitivity_in_one_advance(repo): plan = write_plan(repo, PLAN_SINGLE_CANDIDATE) run_cli(repo, "plan", "register", plan) From add252586c6db0ea232609c7207b5d6f789d4b12 Mon Sep 17 00:00:00 2001 From: geuben Date: Sat, 29 Aug 2026 08:31:20 +0100 Subject: [PATCH 09/15] =?UTF-8?q?feat:=20=5Fdisambiguate=20=E2=80=94=20uni?= =?UTF-8?q?que=20normalise-equal=20candidate=20wins?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TDD-Run: 14 TDD-Cycle: 4 TDD-Phase: green --- src/tddcli/advance.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/tddcli/advance.py b/src/tddcli/advance.py index 3d1a0ed..4785473 100644 --- a/src/tddcli/advance.py +++ b/src/tddcli/advance.py @@ -117,6 +117,18 @@ def _stage_and_commit(engine: Engine, cycle, phase: str, declared) -> tuple[str return sha, staged, classification +def _disambiguate(candidates: list[str], declared: str, adapter) -> str | None: + norm_declared = adapter.normalise_id(declared) + matches = [c for c in candidates if adapter.normalise_id(c) == norm_declared] + if len(matches) == 1: + return matches[0] + declared_file = declared.split("::", 1)[-1].split("::")[0].split(" > ")[0] + same_file = [c for c in candidates if c.split("::", 1)[-1].split("::")[0].split(" > ")[0] == declared_file] + if len(same_file) == 1: + return same_file[0] + return None + + def _outcome_from_verdicts(verdicts, test_id: str) -> str | None: for v in verdicts: if test_id in v.failed: From e3c694fd73c7a24575a422f3a47845b34c73c2fe Mon Sep 17 00:00:00 2001 From: geuben Date: Sat, 29 Aug 2026 08:33:15 +0100 Subject: [PATCH 10/15] refactor: disambiguation picks the candidate that normalise-matches the declared id TDD-Run: 14 TDD-Cycle: 4 TDD-Phase: refactor --- tests/test_advance_adoption.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_advance_adoption.py b/tests/test_advance_adoption.py index f93c309..d406889 100644 --- a/tests/test_advance_adoption.py +++ b/tests/test_advance_adoption.py @@ -1,7 +1,8 @@ from pathlib import Path from conftest import run_cli, write_plan -from tddcli import advance, config as config_mod +from tddcli import advance +from tddcli import config as config_mod from tddcli.adapters.base import Verdict from tddcli.adapters.vitest_adapter import VitestAdapter From 537bd6e2a79aa29033f4e4bab9db7e10e9c0c6d6 Mon Sep 17 00:00:00 2001 From: geuben Date: Sat, 29 Aug 2026 08:36:52 +0100 Subject: [PATCH 11/15] test: same-file disambiguation adopts and evaluates without asking TDD-Run: 14 TDD-Cycle: 5 TDD-Phase: red --- tests/test_advance_adoption.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_advance_adoption.py b/tests/test_advance_adoption.py index d406889..5c45908 100644 --- a/tests/test_advance_adoption.py +++ b/tests/test_advance_adoption.py @@ -76,6 +76,20 @@ def test_disambiguate_picks_the_normalisation_match(tmp_path): assert advance._disambiguate(candidates, declared, adapter) == candidates[0] +def test_unique_same_file_candidate_is_adopted_and_evaluated(repo): + plan = write_plan(repo, PLAN_SINGLE_CANDIDATE) + run_cli(repo, "plan", "register", plan) + run_cli(repo, "run", "start", "--plan", plan) + + (repo / "backend" / "tests" / "test_add.py").write_text(TEST_ADDING) + (repo / "backend" / "app" / "calc.py").write_text(CALC_STUB) + (repo / "backend" / "tests" / "test_other.py").write_text("def test_other_thing():\n assert True\n") + + out = run_cli(repo, "advance") + assert out["next_action"]["verb"] == "write_implementation", out + assert out["run"]["phase"] == "AWAITING_IMPL" + + def test_adopted_passing_test_demands_sensitivity_in_one_advance(repo): plan = write_plan(repo, PLAN_SINGLE_CANDIDATE) run_cli(repo, "plan", "register", plan) From 88cb4d3cf0caf9fd23992507b967360d1e223c33 Mon Sep 17 00:00:00 2001 From: geuben Date: Sat, 29 Aug 2026 08:39:13 +0100 Subject: [PATCH 12/15] feat: wire _disambiguate into the multiple-new-tests branch (same-file rule) TDD-Run: 14 TDD-Cycle: 5 TDD-Phase: green --- src/tddcli/advance.py | 45 +++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/src/tddcli/advance.py b/src/tddcli/advance.py index 4785473..a566583 100644 --- a/src/tddcli/advance.py +++ b/src/tddcli/advance.py @@ -177,16 +177,41 @@ def _handle_test_phase(engine: Engine, cycle, retried: bool, expect_pass: bool) outcomes = {candidates[0]: adopted_outcome} others = [t for t in others if t != candidates[0]] elif len(candidates) > 1: - engine.ledger.event( - engine.run["id"], cycle["id"], "multiple_new_tests", - json.dumps(candidates), - ) - return _reply( - engine, cycle, Verb.NAME_TARGET_TEST, - "Several new tests appeared; a cycle covers one behaviour. Name the" - " intended target with `tdd target `.", - candidates=candidates, - ) + owner = missing[0].split("::", 1)[0] + adapter = adapters.build(engine.config.project(owner), engine.worktree) + resolved = _disambiguate(candidates, missing[0], adapter) + if resolved is not None: + engine.ledger.event( + engine.run["id"], cycle["id"], "declared_test_mismatch", + json.dumps({"declared": missing, "adopted": [resolved], "all_candidates": candidates}), + ) + kept = [t for t in targets if t not in missing] + engine.ledger.update( + "cycle", cycle["id"], target_tests=json.dumps(kept + [resolved]) + ) + cycle = engine.ledger.one("SELECT * FROM cycle WHERE id = ?", (cycle["id"],)) + adopted_outcome = _outcome_from_verdicts(verdicts, resolved) + if adopted_outcome is None: + return _reply( + engine, cycle, Verb.REFACTOR_OR_ADVANCE, + f"Adopted {resolved} as the target (declared {missing[0]} was not" + " collected). Run `tdd advance` again to evaluate it.", + adopted=[resolved], + ) + targets = kept + [resolved] + outcomes = {resolved: adopted_outcome} + others = [t for t in others if t != resolved] + else: + engine.ledger.event( + engine.run["id"], cycle["id"], "multiple_new_tests", + json.dumps(candidates), + ) + return _reply( + engine, cycle, Verb.NAME_TARGET_TEST, + "Several new tests appeared; a cycle covers one behaviour. Name the" + " intended target with `tdd target `.", + candidates=candidates, + ) else: return _reply( engine, cycle, Verb.WRITE_TEST, From d4c0d1e1d2d9a367f02de65a74ea76ac6e53c349 Mon Sep 17 00:00:00 2001 From: geuben Date: Sat, 29 Aug 2026 08:42:54 +0100 Subject: [PATCH 13/15] test: pin that two same-file candidates still demand tdd target TDD-Run: 14 TDD-Cycle: 6 TDD-Phase: pin --- tests/test_advance_adoption.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/test_advance_adoption.py b/tests/test_advance_adoption.py index 5c45908..db65008 100644 --- a/tests/test_advance_adoption.py +++ b/tests/test_advance_adoption.py @@ -90,6 +90,30 @@ def test_unique_same_file_candidate_is_adopted_and_evaluated(repo): assert out["run"]["phase"] == "AWAITING_IMPL" +def test_ambiguous_new_tests_still_ask_the_agent(repo): + plan = write_plan(repo, PLAN_SINGLE_CANDIDATE) + run_cli(repo, "plan", "register", plan) + run_cli(repo, "run", "start", "--plan", plan) + + two_tests = """\ +from app.calc import add + + +def test_adding_two(): + assert add(2, 3) == 5 + + +def test_adding_three(): + assert add(1, 2) == 3 +""" + (repo / "backend" / "tests" / "test_add.py").write_text(two_tests) + (repo / "backend" / "app" / "calc.py").write_text(CALC_STUB) + + out = run_cli(repo, "advance") + assert out["next_action"]["verb"] == "name_target_test", out + assert len(out["result"]["candidates"]) == 2 + + def test_adopted_passing_test_demands_sensitivity_in_one_advance(repo): plan = write_plan(repo, PLAN_SINGLE_CANDIDATE) run_cli(repo, "plan", "register", plan) From 371f0c22d87b0d231d86af6273c31dd076307fea Mon Sep 17 00:00:00 2001 From: geuben Date: Sat, 29 Aug 2026 08:47:29 +0100 Subject: [PATCH 14/15] docs: update R8.9 adoption text and event descriptions for same-advance evaluation --- docs/PRD.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/PRD.md b/docs/PRD.md index 3db41b8..7e1a2c0 100644 --- a/docs/PRD.md +++ b/docs/PRD.md @@ -477,10 +477,18 @@ one has no move left but to re-run doctor and read the same output again. - **R8.8** `human_intervention` events are the input to interventions-per-run, the primary autonomy metric. - **R8.9** In `AWAITING_TEST`, `advance` resolves the target by diffing `collect()` against cycle - open. If exactly one new test appeared and it fails, it is adopted as the target and - `declared_test_mismatch` is recorded against the contract. If several appeared, that is the - one-behaviour-per-cycle violation: it is recorded, and `next_action` requires the agent to name - the intended target rather than guessing. + open. When the declared target is `not_found`, the adoption flow runs: + - **Single new test:** adopted as the target; `declared_test_mismatch` is recorded. The verdict + from the run that already happened is evaluated immediately in the same `advance` call — no + extra suite run. If the adopted test failed, the RED commit is made and the cycle moves to + `AWAITING_IMPL`; if it passed, sensitivity is demanded. + - **Multiple new tests — unambiguous:** if exactly one candidate normalise-matches the declared id + (R10.5), or exactly one lives in the declared target's file, it is adopted and evaluated as + above; `declared_test_mismatch` is recorded with the full candidate list. + - **Multiple new tests — ambiguous:** recorded as `multiple_new_tests`; `next_action` requires + the agent to name the intended target with `tdd target ` rather than guessing. + The run that produced `not_found` already executed the new test; its verdict is retrieved from + the in-hand `Verdict` objects without an extra suite run. ### 8.4 Sensitivity checks From e25ac86cddea6335eb521a0577322474cf0a5e85 Mon Sep 17 00:00:00 2001 From: geuben Date: Sat, 29 Aug 2026 08:47:33 +0100 Subject: [PATCH 15/15] docs: friction log for issue-72-adopt-on-first-run --- .../issue-72-adopt-on-first-run-friction.md | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 tasks/friction-logs/issue-72-adopt-on-first-run-friction.md diff --git a/tasks/friction-logs/issue-72-adopt-on-first-run-friction.md b/tasks/friction-logs/issue-72-adopt-on-first-run-friction.md new file mode 100644 index 0000000..1564835 --- /dev/null +++ b/tasks/friction-logs/issue-72-adopt-on-first-run-friction.md @@ -0,0 +1,75 @@ +# Implementation Friction Log: tasks/issue-72-adopt-on-first-run.md + +- Run: 14 +- Executor: claude-sonnet-4-6 (source: transcript) +- Plan blob: `a93211d4ed71c1c73fa1c12447c48495efb0262a` (declared) +- Started: 2026-08-29T07:05:11.715696+00:00 Ended: 2026-08-29T07:46:47.353961+00:00 Outcome: complete +- Baseline failures at start: tddcli=0 + +## Plan fidelity + +- Declared cycles: 6 +- Delivered: 6 Skipped: 0 +- Never reached: none +- Human interventions: 0 + +### Cycle 6: genuinely ambiguous new tests still ask the agent _(pin)_ +- **Target:** `tddcli::tests/test_advance_adoption.py::test_ambiguous_new_tests_still_ask_the_agent` +- **Projects:** `tddcli` +- **Suite runs by phase:** {'AWAITING_PIN': 1, 'SENSITIVITY': 1, 'CLOSE_SWEEP': 1} +- **First run outcome:** passed (as expected) +- **Sensitivity check:** verified, restore byte-identical + - observed: `repo = PosixPath('/private/var/folders/zl/3010c_557g5_2rm9tyqsc03h0000gp/T/pytest-of-headless-coding/pytest-283/test_ambiguous_new_tests_still0/workspace')` +- **Commits:** + - `d4c0d1e1d` [pin] test: pin that two same-file candidates still demand tdd target (1 files) + +### Cycle 5: the unique candidate in the declared file is adopted among several new tests _(standard)_ +- **Target:** `tddcli::tests/test_advance_adoption.py::test_unique_same_file_candidate_is_adopted_and_evaluated` +- **Projects:** `tddcli` +- **Suite runs by phase:** {'AWAITING_TEST': 1, 'AWAITING_IMPL': 1, 'CLOSE_SWEEP': 1} +- **First run outcome:** failed (as expected) +- **Commits:** + - `537bd6e2a` [red] test: same-file disambiguation adopts and evaluates without asking (1 files) + - `88cb4d3cf` [green] feat: wire _disambiguate into the multiple-new-tests branch (same-file rule) (1 files) + +### Cycle 4: disambiguation picks the candidate that normalise-matches the declared id _(standard)_ +- **Target:** `tddcli::tests/test_advance_adoption.py::test_disambiguate_picks_the_normalisation_match` +- **Projects:** `tddcli` +- **Suite runs by phase:** {'AWAITING_TEST': 1, 'AWAITING_IMPL': 1, 'CLOSE_SWEEP': 2} +- **First run outcome:** failed (as expected) +- **Commits:** + - `f9813f282` [red] test: _disambiguate resolves a vitest separator-only mismatch (1 files) + - `add252586` [green] feat: _disambiguate — unique normalise-equal candidate wins (1 files) + - `e3c694fd7` [refactor] refactor: disambiguation picks the candidate that normalise-matches the declared id (1 files) + +### Cycle 3: a single adopted test that passed drives sensitivity in the same advance _(standard)_ +- **Target:** `tddcli::tests/test_advance_adoption.py::test_adopted_passing_test_demands_sensitivity_in_one_advance` +- **Projects:** `tddcli` +- **Suite runs by phase:** {'AWAITING_TEST': 1, 'SENSITIVITY': 1, 'CLOSE_SWEEP': 1} +- **First run outcome:** passed (**passed**) +- **Sensitivity check:** verified, restore byte-identical + - observed: `repo = PosixPath('/private/var/folders/zl/3010c_557g5_2rm9tyqsc03h0000gp/T/pytest-of-headless-coding/pytest-262/test_adopted_passing_test_dema0/workspace')` +- **Commits:** + - `8df5166dc` [refactor] refactor: a single adopted test that passed drives sensitivity in the same advance (1 files) +- **Event — red_first_violation:** ["tddcli::tests/test_advance_adoption.py::test_adopted_passing_test_demands_sensitivity_in_one_advance"] + +### Cycle 2: a single adopted test that failed is evaluated as RED in the same advance _(standard)_ +- **Target:** `tddcli::tests/test_advance_adoption.py::test_single_new_test_is_adopted_and_evaluated_in_one_advance` +- **Projects:** `tddcli` +- **Suite runs by phase:** {'AWAITING_TEST': 1, 'AWAITING_IMPL': 1, 'CLOSE_SWEEP': 2} +- **First run outcome:** failed (as expected) +- **Commits:** + - `e6de7826b` [red] test: adoption of the one new failing test reaches RED without a re-run (1 files) + - `14aea5202` [green] feat: evaluate the adopted target from the suite run that already happened (2 files) + - `7eb1bfeb0` [refactor] refactor: a single adopted test that failed is evaluated as RED in the same advance (1 files) + +### Cycle 1: outcome lookup returns None for an id absent from every verdict _(standard)_ +- **Target:** `tddcli::tests/test_advance_adoption.py::test_outcome_lookup_returns_none_for_unexecuted_id` +- **Projects:** `tddcli` +- **Suite runs by phase:** {'AWAITING_TEST': 1, 'AWAITING_IMPL': 1, 'CLOSE_SWEEP': 2} +- **First run outcome:** failed (as expected) +- **Commits:** + - `e24012204` [red] test: verdict outcome lookup admits it cannot judge an unexecuted id (1 files) + - `e60c9e5e3` [green] feat: _outcome_from_verdicts helper (None when the id never ran) (2 files) + - `6d97ea080` [refactor] refactor: outcome lookup returns None for an id absent from every verdict (1 files) +