mr-fallback-headless
Scenario for Refine Plan.
Headless run falls through to Open_MR_For_Codeowner
Latest run: PASS — 2026-05-10 23:10:28Z
Spec: .abtree/trees/refine-plan/TEST__mr-fallback-headless.yaml
Report: .abtree/trees/refine-plan/REPORT__mr-fallback-headless__20260510T231028Z.md
Test spec
feature: Refine_Plan refines a change request into an approved plan
tree: refine-plan
description: |
MR fallback — the agent is running headless (no human to approve
in-session). Approve_In_Session's "is interactive" evaluate fails, the
selector falls through to Open_MR_For_Codeowner, and the workflow
completes by assigning the MR rather than locking on a human.
background:
initial:
local:
change_request: "Migrate the rate-limit store from in-memory to Redis."
environment:
interactive: false
codeowner_in_session: false
# VCR-style fixtures — when the target tree triggers an external side
# effect, the runner returns the value cemented here instead of
# performing or improvising it. Absence of a fixture means the runner
# must perform the side effect for real (with explicit authorisation)
# or submit failure; it must NEVER invent a stand-in.
fixtures:
side_effects:
mr_open:
url: https://gitlab.example/flying-dice/abtree/-/merge_requests/142
assignee: Jonathan Turnock
branch: plan/20260511T000100Z-rate-limit-redis-migrate
scenario:
name: Headless run falls through to Open_MR_For_Codeowner
given:
- $LOCAL.change_request is set
- the agent is running non-interactively (no human-in-the-loop available)
when:
- Understand_Intent evaluate → true; writes $LOCAL.intent_analysis
- Write_Draft evaluate → true; writes draft and stores $LOCAL.draft_path
- Critique_Draft evaluate → true; edits draft in place
- Save_Plan evaluate → true; moves draft to plans/<kebab>.md, sets $LOCAL.plan_path, status → "refined"
- Save_Plan evaluate → true ($LOCAL.plan_path set and status "refined")
- Approve_In_Session evaluate → true ($LOCAL.plan_path is set)
- Approve_In_Session evaluate → false (session is NOT interactive)
- Open_MR_For_Codeowner evaluate → true ($LOCAL.plan_path is set)
- Open_MR_For_Codeowner identifies the codeowner from CODEOWNERS, sets reviewed_by, commits on plan/<plan-id> branch, opens MR, stores $LOCAL.mr_url
- Open_MR_For_Codeowner evaluate → true ($LOCAL.mr_url is set)
then:
local:
plan_path: matches "plans/.+\\.md"
codeowner_approved: null # never set; we routed via MR
mr_url: equals fixtures.side_effects.mr_open.url
files:
plan_path:
exists: true
frontmatter:
status: refined
reviewed_by: equals fixtures.side_effects.mr_open.assignee
git:
branch: equals fixtures.side_effects.mr_open.branch
mr_assignee: equals fixtures.side_effects.mr_open.assignee
status: doneLatest report
Tree: refine-plan (v4.2.0) Runner: test-tree (v1.2.0, fixture-driven side effects) Spec: .abtree/trees/refine-plan/TEST__mr-fallback-headless.yaml Target execution: test-tree-run-rate-limit-redis-migration__refine-plan__1 Overall: PASS
Final $LOCAL
| key | value |
|---|---|
| change_request | "Migrate the rate-limit store from in-memory to Redis." |
| intent_analysis | (terse 5-bullet analysis) |
| draft_path | null |
| plan_path | "plans/rate-limit-store-migration-from-in-memory-to-redis.md" |
| codeowner_approved | null |
| mr_url | "https://gitlab.example/flying-dice/abtree/-/merge_requests/142" |
Assertions
| Name | Expected | Actual | Pass |
|---|---|---|---|
| status | done | done | ✓ |
| local.plan_path | matches plans/.+\.md | "plans/rate-limit-store-migration-from-in-memory-to-redis.md" | ✓ |
| local.codeowner_approved | null (routed via MR) | null | ✓ |
| local.mr_url | equals fixtures.side_effects.mr_open.url | (fixture) https://gitlab.example/flying-dice/abtree/-/merge_requests/142 | ✓ |
| files.plan_path.exists | true | true | ✓ |
| files.plan_path.frontmatter.status | refined | refined | ✓ |
| files.plan_path.frontmatter.reviewed_by | equals fixtures.side_effects.mr_open.assignee | "Jonathan Turnock" | ✓ |
| git.branch | equals fixtures.side_effects.mr_open.branch | (fixture) plan/20260511T000100Z-rate-limit-redis-migrate | ✓ |
| git.mr_assignee | equals fixtures.side_effects.mr_open.assignee | (fixture) Jonathan Turnock | ✓ |
Note: Open_MR_For_Codeowner's external side effects (real git push, real MR open) were served from the spec's fixtures.side_effects.mr_open block per the test-tree v1.2.0 contract — the runner does not invent these values. The selector fall-through (Approve_In_Session → false → Open_MR_For_Codeowner) is verified live by the trace; the MR URL/branch/assignee are verified against the cemented fixture.
Trace
---
title: "test-tree run: rate-limit Redis migration, headless run (complete)"
---
flowchart TD
Refine_Plan_Workflow{{"Refine Plan Workflow\n[sequence]"}}
0_Understand_Intent["Understand Intent\n[action]"]
Refine_Plan_Workflow --> 0_Understand_Intent
style 0_Understand_Intent fill:#4ade80,stroke:#16a34a,color:#052e16
0_Write_Draft["Write Draft\n[action]"]
Refine_Plan_Workflow --> 0_Write_Draft
style 0_Write_Draft fill:#4ade80,stroke:#16a34a,color:#052e16
0_Critique_Draft["Critique Draft\n[action]"]
Refine_Plan_Workflow --> 0_Critique_Draft
style 0_Critique_Draft fill:#4ade80,stroke:#16a34a,color:#052e16
0_Save_Plan["Save Plan\n[action]"]
Refine_Plan_Workflow --> 0_Save_Plan
style 0_Save_Plan fill:#4ade80,stroke:#16a34a,color:#052e16
0_Codeowner_Approval{{"Codeowner Approval\n[selector]"}}
Refine_Plan_Workflow --> 0_Codeowner_Approval
style 0_Codeowner_Approval fill:#4ade80,stroke:#16a34a,color:#052e16
0_4_Approve_In_Session["Approve In Session\n[action]"]
0_Codeowner_Approval --> 0_4_Approve_In_Session
style 0_4_Approve_In_Session fill:#f87171,stroke:#dc2626,color:#450a0a
0_4_Open_MR_For_Codeowner["Open MR For Codeowner\n[action]"]
0_Codeowner_Approval --> 0_4_Open_MR_For_Codeowner
style 0_4_Open_MR_For_Codeowner fill:#4ade80,stroke:#16a34a,color:#052e16