M001 — A blocking question survives a handover
An unresolved question keeps refusing a completed finish after the work moves to a new task, or the alternative is chosen deliberately and the reason is recorded where the next worker will read it.
DONE 4 of 4 issues done
- ready
- 0
- blocked
- 0
- active
- 0
- verify
- 0
- done
- 4
- cancelled
- 0
Problem
An open question is the mechanism for stopping acceptance when work depends on a person. It is attached to the task that opened it, and the gate only reads questions belonging to the active task. Handing over and starting a new task therefore leaves the question unresolved and no longer blocking anything, so the one thing that was supposed to refuse a completed finish refuses nothing. The gap was found by running the documented end-to-end sequence, not by review, and it is published as a planned claim with no implementation and no test.
Outcome
An unresolved question keeps refusing a completed finish after the work moves to a new task, or the alternative is chosen deliberately and the reason is recorded where the next worker will read it.
Current state
docs/CLAIMS.yaml carries blocker-survives-handover as planned, with implementation and test both '-'. lib/question.sh matches unresolved lines by the active task id; lib/check.sh reads only those. Nothing detects that a handover has orphaned a blocker.
Desired state
The behaviour is decided between two named alternatives, implemented, proved by a behavioural case, and the claim moves from planned to guaranteed with its implementation and test filled in.
Scope
- lib/question.sh
- lib/check.sh
- share/skeleton/templates/open-questions.md
- docs/CLAIMS.yaml
- docs/claims
- docs/CONTINUITY.md
- test/cases
Out of scope
- A question that blocks across repositories or worktrees
- Assigning a question to a person, or notifying anybody
- Any change to the finish contract's other lines
Acceptance criteria
- A behavioural case demonstrates the current escape, so the gap is executable rather than asserted
- The choice between transferring the question and widening the gate is recorded as a decision with what was rejected and why
- The chosen behaviour is implemented and a case fails when it is removed
- The claim in docs/CLAIMS.yaml moves to guaranteed with a real implementation path and a real test path
- The claim detail page states what the behaviour does not cover
Validation
- bash test/run.sh
- bin/majordomus doctor
- scripts/generate-site-data --check
Evidence required
- gap_reproduced
- decision_recorded
- behaviour_tested
Risks
- Transferring a question with a handover makes it a property of the work rather than of the task, which is right in the common case and wrong when the handover is to a different piece of work. Widening the gate to every unresolved question blocks tasks that never opened one. The decision has to name the case it is choosing to be wrong in.
Evidence
A milestone is never done because its issues are; the last step is its own acceptance, and it is gated on the rows below.
| covers | type | command | result |
|---|---|---|---|
| gap_reproduced | test | bash test/run.sh 50_blocker_across_handover | the case runs the documented sequence and the mutation section reproduces the escape from a probe that narrows the gate back to the active task, so the gap is executable rather than described |
| decision_recorded | manual | bin/majordomus decision list | the choice, both rejected alternatives with the case each gets wrong, and the two conditions the choice imposed on the implementation |
| behaviour_tested | test | bash test/run.sh 50_blocker_across_handover && bash test/run.sh 25_continuity_lifecycle && bin/majordomus doctor | exit 0, 85s — recorded as this task's verification; an inherited blocker refuses a completed finish, any task can clear it, and the shipped template is not an entry |
Issues
Each one is an execution contract with its own acceptance criteria, validation command and required evidence. The wave column is the layer of the dependency graph it sits in.
| issue | status | wave | depends on | title |
|---|---|---|---|---|
| I0101 | DONE | 0 | — | Reproduce the escaped blocker as an executable case |
| I0102 | DONE | 1 | I0101 | Decide whether a question transfers or the gate widens |
| I0103 | DONE | 2 | I0102 | Implement the decision and prove it holds |
| I0104 | DONE | 3 | I0103 | Move the claim from planned to guaranteed |
The graph
flowchart LR
I0101["I0101<br/>Reproduce the escaped blocker as an executable case"]:::done
I0102["I0102<br/>Decide whether a question transfers or the gate widens"]:::done
I0103["I0103<br/>Implement the decision and prove it holds"]:::done
I0104["I0104<br/>Move the claim from planned to guaranteed"]:::done
I0101 --> I0102
I0102 --> I0103
I0103 --> I0104
classDef done stroke:#16a34a,fill:#052e16,stroke-width:2px
classDef active stroke:#2563eb,fill:#eff6ff,stroke-width:2px
classDef verify stroke:#7c3aed,fill:#f5f3ff,stroke-width:2px
classDef ready stroke:#0891b2,fill:#ecfeff,stroke-width:2px
classDef blocked stroke:#b45309,fill:#fffbeb,stroke-width:2px
classDef cancelled stroke:#6b7280,fill:#f9fafb,stroke-width:2pxCanonical record: .ai/repo/project/milestones/M001.yaml. Read it back with majordomus plan show M001.