Skip to content

END transitions skip push_branch/create_pull_request despite declaring them as automatic effects #207

Description

@ccisnedev

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:

  1. execute those effects before leaving END, or
  2. 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

  1. Complete a cycle until the FSM reaches END.
  2. Confirm END advertises PR creation as a mechanical step.
  3. Run:
iq fsm transition --event pr_ready_no_evolution
  1. Observe that the FSM returns to IDLE.
  2. 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:

  1. implement push_branch and create_pull_request as real END effects in the runtime,
  2. keep them delegated, but make END transitions fail closed unless the delegated action is confirmed,
  3. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions