Summary
END currently declares pull-request publication as a mechanical, automatic step, but the CLI transition does not actually execute it.
In practice, iq fsm transition --event pr_ready and iq fsm transition --event pr_ready_no_evolution can advance the FSM out of END even when no branch was pushed, no PR was created, and no PR artifact was written.
Expected behavior
When END transitions declare:
effects: [push_branch, create_pull_request]
commit_policy: push_and_pr
then the runtime should do one of the following, consistently:
- execute those effects before leaving END, or
- fail closed and refuse the transition unless those effects are completed by a delegated command path.
The current contract and state copy strongly imply option 1.
Actual behavior
The FSM transitions successfully from END to IDLE / EVOLUTION, but:
- no upstream branch is configured,
- no PR exists for the branch,
- no
pull_request.md artifact is created,
- the repo is left in a state that looks complete to the FSM even though the mechanical END contract did not happen.
Reproduction
- Complete a cycle until the FSM reaches END.
- Confirm END advertises PR creation as a mechanical step.
- Run:
iq fsm transition --event pr_ready_no_evolution
- Observe that the FSM returns to IDLE.
- Check branch state and GitHub:
- no upstream configured for the branch,
- no PR exists,
- no local PR artifact was produced.
Evidence
Contract says END should publish the branch and create the PR
code/cli/assets/fsm/states/end.yaml
- "Push the branch and create the pull request. This is mechanical"
git push -u origin <branch>
gh pr create --title "<issue-title>" --body "Closes #<issue>"
code/cli/assets/fsm/transition_contract.yaml
- END -> EVOLUTION (
pr_ready) declares effects: [push_branch, create_pull_request]
- END -> IDLE (
pr_ready_no_evolution) declares effects: [push_branch, create_pull_request]
- both transitions declare
commit_policy: push_and_pr
Runtime explicitly skips those effects
code/cli/lib/modules/fsm/effect_executor.dart
- says
push_branch and create_pull_request are "skill-side" and "NOT executed here"
executeAll() silently skips unknown effects instead of failing closed
Suspected root cause
The END contract was upgraded to treat PR publication as an automatic runtime obligation, but the effect executor still implements the older split where Git/PR effects are external to the CLI.
That creates contract drift:
- the YAML says the transition performs push + PR,
- the CLI state machine marks the transition as successful,
- the effect executor does neither.
Why this matters
This is a silent false-success bug:
- the cycle can leave END,
- the FSM can reset to IDLE,
- downstream automation and users can believe the cycle is closed,
- but the branch/PR publication step never actually happened.
Suggested fixes
Any of these would resolve the drift, but the implementation should pick one consistently:
- implement
push_branch and create_pull_request as real END effects in the runtime,
- keep them delegated, but make END transitions fail closed unless the delegated action is confirmed,
- split END into explicit deterministic subcommands that the transition invokes and verifies.
Affected files
code/cli/assets/fsm/states/end.yaml
code/cli/assets/fsm/transition_contract.yaml
code/cli/lib/modules/fsm/effect_executor.dart
code/cli/lib/modules/fsm/commands/transition.dart
Summary
END currently declares pull-request publication as a mechanical, automatic step, but the CLI transition does not actually execute it.
In practice,
iq fsm transition --event pr_readyandiq fsm transition --event pr_ready_no_evolutioncan advance the FSM out of END even when no branch was pushed, no PR was created, and no PR artifact was written.Expected behavior
When END transitions declare:
effects: [push_branch, create_pull_request]commit_policy: push_and_prthen the runtime should do one of the following, consistently:
The current contract and state copy strongly imply option 1.
Actual behavior
The FSM transitions successfully from END to IDLE / EVOLUTION, but:
pull_request.mdartifact is created,Reproduction
Evidence
Contract says END should publish the branch and create the PR
code/cli/assets/fsm/states/end.yamlgit push -u origin <branch>gh pr create --title "<issue-title>" --body "Closes #<issue>"code/cli/assets/fsm/transition_contract.yamlpr_ready) declareseffects: [push_branch, create_pull_request]pr_ready_no_evolution) declareseffects: [push_branch, create_pull_request]commit_policy: push_and_prRuntime explicitly skips those effects
code/cli/lib/modules/fsm/effect_executor.dartpush_branchandcreate_pull_requestare "skill-side" and "NOT executed here"executeAll()silently skips unknown effects instead of failing closedSuspected root cause
The END contract was upgraded to treat PR publication as an automatic runtime obligation, but the effect executor still implements the older split where Git/PR effects are external to the CLI.
That creates contract drift:
Why this matters
This is a silent false-success bug:
Suggested fixes
Any of these would resolve the drift, but the implementation should pick one consistently:
push_branchandcreate_pull_requestas real END effects in the runtime,Affected files
code/cli/assets/fsm/states/end.yamlcode/cli/assets/fsm/transition_contract.yamlcode/cli/lib/modules/fsm/effect_executor.dartcode/cli/lib/modules/fsm/commands/transition.dart