"Without an AI assistant" never reaches the agent — the choice is dropped between YOU and setup-complete
0.5.203 implements the client half of run-without-AI (CIRISAgent#1149/#1151). Driving it through the real UI for the first time shows the choice does not arrive: the agent receives run_without_ai: false and configures a degraded brain instead of becoming a node.
The evidence
Five-platform gate run 34067627803, Linux desktop leg, first backend boot (incidents_20260906_234914.log):
[SETUP] No usable LLM provider (provider='openai', key_set=False) —
writing CIRIS_SERVICES_DISABLED=true so the next boot degrades instead of aborting
That is the agent's accidental branch, reached only when run_without_ai is false. The deliberate branch logs Owner chose to run without AI and writes CIRIS_RUN_WITHOUT_AI=true. Neither the line nor the key appears anywhere in the run's artifacts, on any platform. Reproduced on Windows, macOS and Linux; still present on 47c9cd5e9.
Consequence today: a user who picks "without an AI assistant" gets a brain with no LLM rather than a node. :8080 keeps serving, nothing hands off, and the app logs in against the backend it was meant to have replaced.
What the automation did, so you can rule our side out
- On YOU it waited for
opt_run_without_ai, found it, clicked it, logged AI preference: opt_run_without_ai. Finding it matters: AiPreferenceSection renders if (hasAgent), so hasAgent was true and the section was live.
- The wizard then skipped the AI screen and went JOIN_FEDERATION → COMPLETE. With
hasAgent true, hasAiStep = hasAgent && !runWithoutAi being false means runWithoutAi was true in client state at that moment.
SetupViewModel sends run_without_ai = currentState.runWithoutAi at both submit sites, unconditionally.
So it was true when the screen rendered and false when the agent read the payload. Everything between is yours. One candidate we noticed without chasing: SetupState.kt:306 sets runWithoutAi = false — if that participates in any re-initialisation between YOU and submit, it produces exactly this.
What we changed on our side
The gate no longer accepts an absent AI screen as proof the choice was made — hasAiStep is false whenever clientMode resolves NODE too, so absence was reading as success. It now asserts the agent's recorded state (CIRIS_RUN_WITHOUT_AI=true in the home's .env, written only by the deliberate branch) on the desktop legs, and reports "not asserted" on mobile where the home is on-device. That is 60c55559b / 47c9cd5e9.
The gate is red on this and should stay red until the choice arrives. The agent half is verified independently: POST /v1/setup/complete with run_without_ai: true records the flag, stops :8080, and the same pid returns as the node on :4243 against the published ciris-server 0.5.199.
Cross-refs: CIRISAgent#1149 (agent side), CIRISAgent#1151 (client half), CIRISAgent#1150 (the gate).
🤖 Generated with Claude Code
"Without an AI assistant" never reaches the agent — the choice is dropped between YOU and setup-complete
0.5.203 implements the client half of run-without-AI (CIRISAgent#1149/#1151). Driving it through the real UI for the first time shows the choice does not arrive: the agent receives
run_without_ai: falseand configures a degraded brain instead of becoming a node.The evidence
Five-platform gate run 34067627803, Linux desktop leg, first backend boot (
incidents_20260906_234914.log):That is the agent's accidental branch, reached only when
run_without_aiis false. The deliberate branch logsOwner chose to run without AIand writesCIRIS_RUN_WITHOUT_AI=true. Neither the line nor the key appears anywhere in the run's artifacts, on any platform. Reproduced on Windows, macOS and Linux; still present on 47c9cd5e9.Consequence today: a user who picks "without an AI assistant" gets a brain with no LLM rather than a node.
:8080keeps serving, nothing hands off, and the app logs in against the backend it was meant to have replaced.What the automation did, so you can rule our side out
opt_run_without_ai, found it, clicked it, loggedAI preference: opt_run_without_ai. Finding it matters:AiPreferenceSectionrendersif (hasAgent), sohasAgentwas true and the section was live.hasAgenttrue,hasAiStep = hasAgent && !runWithoutAibeing false meansrunWithoutAiwas true in client state at that moment.SetupViewModelsendsrun_without_ai = currentState.runWithoutAiat both submit sites, unconditionally.So it was true when the screen rendered and false when the agent read the payload. Everything between is yours. One candidate we noticed without chasing:
SetupState.kt:306setsrunWithoutAi = false— if that participates in any re-initialisation between YOU and submit, it produces exactly this.What we changed on our side
The gate no longer accepts an absent AI screen as proof the choice was made —
hasAiStepis false wheneverclientModeresolves NODE too, so absence was reading as success. It now asserts the agent's recorded state (CIRIS_RUN_WITHOUT_AI=truein the home's.env, written only by the deliberate branch) on the desktop legs, and reports "not asserted" on mobile where the home is on-device. That is60c55559b/47c9cd5e9.The gate is red on this and should stay red until the choice arrives. The agent half is verified independently:
POST /v1/setup/completewithrun_without_ai: truerecords the flag, stops:8080, and the same pid returns as the node on:4243against the published ciris-server 0.5.199.Cross-refs: CIRISAgent#1149 (agent side), CIRISAgent#1151 (client half), CIRISAgent#1150 (the gate).
🤖 Generated with Claude Code