You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In an isolated maka-agent@next reproduction, the first model turn called GoalSet and answered ARMED. maka run --yolo then returned exit code 0 while the marker file did not exist. Stdout contained only ARMED.
The same Session recorded two Runs. The first had no goalId; the second had a goalId. Both recorded permissionMode=bypass. After the CLI process disappeared, the detached Runtime Host was reparented to PID 1, started the Goal continuation, and ran a shell command that created POST_EXIT\n after the CLI had returned.
GoalSet was called by the model from the first turn, not by a separate CLI Goal command. With the default 50-turn ceiling (packages/runtime/src/goal-state.ts:122), a Goal can run up to 49 successor Runs after the initial turn.
An earlier real-workload run (a local service integration check) showed the same boundary:
Its final answer said that it had not contacted any host outside 127.0.0.1.
After exit 0, a Goal Run inspected an established connection to 192.168.10.19:5672.
The Goal Run then ran stop.sh and stopped the service.
In both cases the final answer printed by maka run no longer describes the Session's final state.
How to reproduce
This uses the current $HOME for the configured connection and a fresh temporary working directory. It does not require a separate Goal command.
#!/usr/bin/env bashset -u
tmp=$(mktemp -d)
work="$tmp/work"
gate="$tmp/cli-returned.gate"
marker="$tmp/post-exit.marker"
stdout="$tmp/stdout.txt"
stderr="$tmp/stderr.txt"
mkdir -p "$work"
condition="The file $marker exists and its complete content is exactly POST_EXIT plus a trailing newline, and it must be created only after a new user message beginning with [Goal continuation] is received."
prompt="This is a two-phase lifecycle test. In the current first turn, call GoalSet as your only tool. Set its success condition exactly to: $condition Omit max_iterations, block_cap, and token_budget so product defaults apply. In this first turn, do not inspect, wait for, or create $gate or $marker. After GoalSet succeeds, call no other tool and answer exactly ARMED. Only after you receive a new user message beginning with [Goal continuation], wait until $gate exists, then run sleep 5, write exactly POST_EXIT plus a trailing newline to $marker, verify the complete file content, and finish."
(
cd"$work"||exit 97
maka run --yolo --timeout 120 "$prompt"
) >"$stdout"2>"$stderr"
rc=$?printf'exit_code=%s\n'"$rc"printf'stdout='; cat "$stdout"iftest -e "$marker";thenecho'marker_at_cli_return=PRESENT'elseecho'marker_at_cli_return=ABSENT'fiprintf'returned\n'>"$gate"for_in$(seq 1 60);dotest -e "$marker"&&break
sleep 1
done
expected="$tmp/expected.txt"printf'POST_EXIT\n'>"$expected"
cmp -s "$expected""$marker"&&echo'marker_after_return=POST_EXIT'echo"artifacts=$tmp"
On the affected build, this prints exit_code=0, stdout=ARMED, and marker_at_cli_return=ABSENT. The Host then creates the marker and the script prints marker_after_return=POST_EXIT.
Expected
A successful blocking maka run must not return while a Goal armed by that invocation can still execute tools under the Session's permission boundary, unless the user explicitly selected a documented detach mode.
This follows the approved maka run contract in #730: "One invocation runs one model turn, including tool steps, then exits." The same contract defines 0: completed with finalOutput.
Root cause
The Runtime Host lifecycle and Goal durability are intentional. The architecture says the Host owns Runtime work (docs/architecture/runtime-host-architecture.md:35) and that Runtime work outlives a request connection (docs/architecture/runtime-host-architecture.md:43). #853 and #1154 establish that boundary. This issue is limited to the non-interactive completion contract.
The text runner consumes the current sendMessage() stream (packages/cli/src/run-command-core.ts:397). Only --graph adds a completion wait (packages/cli/src/run-command-core.ts:426-428). The normal success path closes the Client context (packages/cli/src/run-command-core.ts:440) and returns 0 from the first outcome (packages/cli/src/run-command-core.ts:462).
Closing the run context only closes local interaction, subscription, and graph-wait state (packages/cli/src/runtime-host-run-command.ts:414-419); the outer finally closes the Client connection (packages/cli/src/runtime-host-run-command.ts:135). The local Host candidate is detached and unreferenced (packages/runtime-host/src/client/launcher.ts:77, packages/runtime-host/src/client/launcher.ts:133).
A non-terminal Goal retains Host residency (packages/runtime-host/src/server/goal-coordinator.ts:593-601). The continuation starts asynchronously (packages/runtime/src/goal-continuation.ts:827) and creates a new Goal Run (packages/runtime-host/src/server/goal-execution-coordinator.ts:115-116). That Run inherits the Session header's permissionMode (packages/runtime/src/agent-run.ts:1125-1137).
Proposed boundary
Preferred: follow the existing --graphwaitForCompletion pattern and wait for the Goal to reach a terminal state. Project the final Goal Run's outcome to stdout and the process exit code. Timeout and SIGINT should continue through the existing stop path.
Minimum mitigation: query Goal state before exit. If it is non-terminal, write goal continues: session=<id> to stderr and return a documented, distinct exit code.
What happened
In an isolated
maka-agent@nextreproduction, the first model turn calledGoalSetand answeredARMED.maka run --yolothen returned exit code 0 while the marker file did not exist. Stdout contained onlyARMED.The same Session recorded two Runs. The first had no
goalId; the second had agoalId. Both recordedpermissionMode=bypass. After the CLI process disappeared, the detached Runtime Host was reparented to PID 1, started the Goal continuation, and ran a shell command that createdPOST_EXIT\nafter the CLI had returned.GoalSetwas called by the model from the first turn, not by a separate CLI Goal command. With the default 50-turn ceiling (packages/runtime/src/goal-state.ts:122), a Goal can run up to 49 successor Runs after the initial turn.An earlier real-workload run (a local service integration check) showed the same boundary:
127.0.0.1.192.168.10.19:5672.stop.shand stopped the service.In both cases the final answer printed by
maka runno longer describes the Session's final state.How to reproduce
This uses the current
$HOMEfor the configured connection and a fresh temporary working directory. It does not require a separate Goal command.On the affected build, this prints
exit_code=0,stdout=ARMED, andmarker_at_cli_return=ABSENT. The Host then creates the marker and the script printsmarker_after_return=POST_EXIT.Expected
A successful blocking
maka runmust not return while a Goal armed by that invocation can still execute tools under the Session's permission boundary, unless the user explicitly selected a documented detach mode.This follows the approved
maka runcontract in #730: "One invocation runs one model turn, including tool steps, then exits." The same contract defines0: completed with finalOutput.Root cause
The Runtime Host lifecycle and Goal durability are intentional. The architecture says the Host owns Runtime work (
docs/architecture/runtime-host-architecture.md:35) and that Runtime work outlives a request connection (docs/architecture/runtime-host-architecture.md:43). #853 and #1154 establish that boundary. This issue is limited to the non-interactive completion contract.The text runner consumes the current
sendMessage()stream (packages/cli/src/run-command-core.ts:397). Only--graphadds a completion wait (packages/cli/src/run-command-core.ts:426-428). The normal success path closes the Client context (packages/cli/src/run-command-core.ts:440) and returns 0 from the first outcome (packages/cli/src/run-command-core.ts:462).Closing the run context only closes local interaction, subscription, and graph-wait state (
packages/cli/src/runtime-host-run-command.ts:414-419); the outerfinallycloses the Client connection (packages/cli/src/runtime-host-run-command.ts:135). The local Host candidate is detached and unreferenced (packages/runtime-host/src/client/launcher.ts:77,packages/runtime-host/src/client/launcher.ts:133).A non-terminal Goal retains Host residency (
packages/runtime-host/src/server/goal-coordinator.ts:593-601). The continuation starts asynchronously (packages/runtime/src/goal-continuation.ts:827) and creates a new Goal Run (packages/runtime-host/src/server/goal-execution-coordinator.ts:115-116). That Run inherits the Session header'spermissionMode(packages/runtime/src/agent-run.ts:1125-1137).Proposed boundary
--graphwaitForCompletionpattern and wait for the Goal to reach a terminal state. Project the final Goal Run's outcome to stdout and the process exit code. Timeout and SIGINT should continue through the existing stop path.goal continues: session=<id>to stderr and return a documented, distinct exit code.GoalSetonly from the text runner. That would conflict with the model-armed Goal direction in feat(goal): let the user arm a Goal from the composer #3199 and create a surface-specific tool boundary.I am willing to follow up with a PR once maintainers choose the boundary.
Related
maka runcompletion.maka run.npxshell exits, not the success boundary of an installed blocking CLI.Environment
maka-agent0.1.0-beta.1 from npm@next.maka run --yolo.Generated-by: gpt-5.6 (draft), human-reviewed before filing