Repository navigation
Codex, phases 8 to 10b and the 10b review: Bibo's codex.rs, the repair asks Codex, steps, diagnosis and guard per agent, the screens, all hidden while Codex is closed - #57
Draft
undistracted2cha wants to merge 38 commits into
Conversation
…eaves behind goes with it sys::run_isolated_pipes gives each line with the pipe it came from (Codex says its login on stderr, its stream is stdout alone) and, once the program has exited, kills what it left in its group, as the kit's launcher does on macOS and Linux. run_isolated, which Claude's repair uses, does exactly what it did (test, both ways).
… key, the launcher's contract in Rust (phase 8) app/src-tauri/src/codex.rs is the Rust copy of engine/hooks/_headless_codex.py: find() looks where OpenAI's installer puts Codex, then the PATH (an .exe only on Windows); login() says chatgpt, billed, managed, logged_out or unknown from auth.json's mode, plan and refresh token and from `codex login status` compared to fixed lines, never copied; check() and ask() run the launcher's checks in its order, with the same -c, refusals, pauses and words, the same private folder and stream rules, the prompt on stdin only. Nothing calls it yet (repair: phase 9, screens: 10b); Codex stays closed. engine/tests/codex_contract.py holds the scenarios (114 on macOS, 109 on Linux and Windows). The test the_app_and_the_launcher_answer_alike plays each through codex.rs and through the launcher, each in a world of its own, and compares the outcome with its words and everything the fake Codex was given (probes, call, -c in order, environment, folder, stdin, schema), the work folder left empty, Codex's children gone, no token in either record. Another compares the Windows access-list decision (47,580 cases) and SIDs. Without Python both fail. fake_codex.py can plant a %ProgramData% folder at the last probe. plist (macOS) was already in the build through tauri.
… is chosen The installed launcher (~/.claude/hooks/brain/_headless_codex.py) reads codex-features.json next to itself, and neither installer put it there: once Codex opened, it would have stayed paused for everyone. Tests ran the launcher from the repository, where the file is one folder up. install.sh and install.ps1 now place it in their Codex section only; Claude alone installs exactly what it did. test-hooks.sh §16 (220/221 without the fix) and the windows job check the installed file and that the installed launcher reads it.
codex.rs and its parity with the launcher, where the app is stricter, « Arrêter », the group swept, login next to check, codex-features.json installed nowhere until now; local checks.
The windows job's check imported ~\.claude\hooks\brain\_headless_codex.py, and Python
wrote its __pycache__ there: the step's comparison of Claude's install then saw new
files ("Claude's install changed"). Both checks (test-hooks.sh §16 too) now run
python -I -B.
Tests and terrain green on the branch; the first windows run's bench defect (__pycache__) and its fix.
One ASCII JSON line (etat, agents, prefere, codex_home), exit code 0: Bibo reads agents.json through the kit's own reader and gate instead of judging the file itself.
… the kit does (phase 9) agents.rs reads the choice through _agents.py lire (none of it without the file: Claude alone, nothing run; an untrusted or unreadable one: no AI asked). repair.rs asks the preferred engine, then the other one listed after the errors _headless.py falls back on: the same dossier on stdin, the same instructions, the same schema of the same closed lists, the same checks of the answer. Codex goes through codex.rs (ask_heard: told when the question leaves and each stream line). Claude in Terminal only when Claude is listed. With Claude alone, the status, plan, stages, journal and report are what they were: nothing names an engine.
The repair texts come from the AI's name (Claude's words unchanged), from what repair_status, the plan and the stages name; Codex's ChatGPT login button waits for phase 10b. The mock gets &agents=, &secours=1 and &repair=agents.
The repair with Codex: agents.json through _agents.py lire, the same question, the kit's fallback; what differs from the plan, the proof for Claude alone, local checks.
… its instructions byte for byte
agents.rs dropped a codex_home that is not absolute on Windows ("/c") instead of
refusing the line. fake_codex.py wrote the instructions it was given in text mode,
so on Windows their line ends came back as CRLF.
Tests and terrain green on the branch; the two Windows-only test defects of the first run and their fix; codex-mock's known sign-of-life race seen once on macOS.
hooks_muets() of the CODEX_HOME a trusted agents.json records ("non" without one that
lists Codex): Bibo's diagnosis says it without working it out again.
…odex's checks, Claude's only when listed (phase 10a) - detect.rs: the brain's state per agent. Claude's part as before; Codex's: the kit's hook in its hooks.json, the kit's block in the instructions Codex reads, the skills in ~/.agents/skills. Complete for each agent listed; a choice the kit cannot trust is never complete. - steps.rs: the agents' steps where Claude Code's stands, the preferred one first. The Codex step runs the official installer of the pinned release, checked against the SHA-256 install.sh and install.ps1 pin (a test holds them equal), as they run it. The brain step stops on an untrusted choice before downloading anything. - diagnose.rs: Codex, its ChatGPT login (codex::login), its hooks (and whether Codex runs them, _agents.py hooks-muets), instructions and skills; each agent's site in the network check; a red line for an untrusted choice. Codex alone runs nothing of Claude's. - test.yml: the Codex step for real in the app job, on each system. Without agents.json, or with Claude alone, every check, text, step and order is master's.
…oml, only the kit's approvals may appear ~/.codex always (a Codex of the user's own, Claude alone included) and the CODEX_HOME agents.json records: hooks.json as Claude's settings.json; config.toml takes only [hooks.state."<key>"] tables added at its end, one hash each, each naming one of the kit's hooks in that folder's hooks.json (and, in a table of the kit's, its hash and comment); AGENTS.md appended to; the rules and ~/.agents/skills as hooks and skills. Checked against the installers' own wire.py. .codex and .agents join the remedies' NEVER list.
Bibo's brain state, steps, diagnosis and configuration guard per agent: what differs from the plan (the pinned Codex installer, an untrusted choice blocks, the guard wider and config.toml stricter), the proof for Claude alone, the mutations, local checks.
snapshot_of() keeps the system's separator: on Windows ".codex\hooks.json" missed the arm that lets hooks.json gain the kit's hooks, so both tests failed there. The guard itself was right (the other Codex tests passed on Windows).
- test_agents: `_agents.py hooks-muets` prints its word with print(), CRLF on Windows (the app trims it); the test compares the word. - codex_real: the test gave the official installer a CODEX_HOME in Windows' \\?\ form (fs::canonicalize of the temporary folder), which its Join-Path refuses. agents.json records a plain path: the test now gives one.
Tests and terrain green on the branch, the real Codex step on each system; the three Windows-only test defects of the first runs and their fix.
…, and holds the choice until the installer records it (phase 10b) - agents.rs: codex_open() reads the gate in the kit's _agents.py, the copy that reads agents.json (installed, else the sources'), else, before any kit is on disk, the app's own copy of that file (include_str!). Read without Python, which a fresh Mac or PC lacks: only the gate's line as the kit writes it; anything else is closed. A test plays every form of the line against `_agents.py ouvert`. - choose() holds the window's pick for the session, for its home; current() is that pick while agents.json does not record it (etat "choix"), else agents.json. Codex's folder is the one agents.json records, else the one the installer will record ($CODEX_HOME, else ~/.codex, as _agents.codex_home() reads it; a test compares the two). offer(): the gate and the preselection (the choice made, Claude alone on an install of before the choice, else what is installed). - The diagnosis, the steps, the repair and the configuration guard follow current(). A pending choice keeps the brain incomplete and shows « Ton choix d'IA : je le note en installant ton cerveau », and the brain step hands it to install.sh and install.ps1 (BIAB_AGENTS, BIAB_PREFERE); without one nothing is added: the installer runs as before. - steps.rs: Codex's ChatGPT login (`codex -c cli_auth_credentials_store="file" login`, in Terminal or PowerShell) and the onboarding by the preferred agent (`codex "<prompt>"` in the brain); no key of the user's shell reaches Codex there, CODEX_HOME only when it is not Codex's default. - lib.rs: agents_offer, choose_agents, codex_login, open_codex_login. As shipped Codex is closed everywhere: nothing is offered, and Claude alone goes as on master.
…, the onboarding by the preferred AI Only where the kit lets Codex be chosen (agents_offer): the welcome names no AI and leads to « Avec quelle IA veux-tu travailler ? » (Claude, Codex, les deux; for both, who writes the evening summary, said both ways), asked before the diagnosis, whose checks and steps follow it. Codex has its step (« J'installe Codex. »), its login (« Dernière étape : ton compte ChatGPT », the billed key and the workspace account said, never logged out), the end names the agents chosen and the onboarding the preferred one. The repair gets Codex's ChatGPT button. Closed, as shipped: no choice is asked and every screen is master's (the mock's 42 scenarios of Claude alone compared with master's, screen by screen). app/dev/mock.html: &porte=1 (implied by an agents.json that lists Codex), &choix=, &resume=, &codex=. ONBOARDING.md speaks to Claude Code or Codex: the kit's block is in CLAUDE.md or in Codex's AGENTS.md.
…p and runs its tests Before any kit is on disk, the app reads the gate in its own copy of engine/hooks/_agents.py: the PR that opens Codex on a system must publish an app that offers it. release.yml builds on that file too, test.yml's app job runs when it changes, and terrain_app's freshness check of the preview counts it (PUBLIE, next commit). test_release_workflow holds the three together.
…he three systems (app-codex) Each app job of terrain.yml runs two paths (matrix): "app", Claude alone, as before and as users get the app (a choice screen there fails: the app as shipped keeps Codex closed), and "app-codex". There the robot opens Codex in its own copy of the kit, as the other benches do: the tested commit's sources with _agents.py's gate open (zip entries and modes otherwise as git archive gives them), handed to the app (BIAB_ARCHIVE, on master too) and put in ~/.brain-in-a-box/src, where the app reads the gate before installing; the marker stays out, so it still proves the app installed from the archive. The robot clicks « Choisir mon IA », « Codex », skips the Voyage key and the ChatGPT login; Codex's step must be seen, Claude's never, and the end must name Codex. terrain.check_codex: agents.json says Codex alone, Codex answers, its hooks.json runs the six hooks with --agent codex (Windows: the PowerShell form, on winget's Python), the kit approved them, the kit's correction-detector run as Codex runs it leaves its sign of life, Codex's AGENTS.md and the vault's, the skills in ~/.agents/skills, and nothing of Claude Code's. The report expects macos-app-codex, windows-app-codex, linux-app-codex.
…8), not the console's code page agents::tests::codexs_folder_is_the_one_the_installer_records compared the app's Codex folder with _agents.codex_home() printed by Windows' Python, which writes an accented path in the console's code page: from_utf8 failed on « Données é ». The app is unchanged.
…Git Bash, Codex looked for where the driven system puts it - check_git: Claude Code's Git Bash check (git-bash) runs on the path of Claude alone only. With Codex chosen alone there is no Claude Code to ask, and Codex runs its commands through PowerShell: gbq.cmd is check_rest's. The run on the branch stopped there (« Claude Code n'est pas installé »). The path of Claude alone checks the same, in the same order, and says the same. - Codex's place comes from the pilot (Pilote.codex_installe: ~/.local/bin on a Mac or Linux, LOCALAPPDATA on Windows), not from the system the harness runs on: test_terrain_app's fake Mac, run on the Windows runner, looked under LOCALAPPDATA.
… red Each 10b protection of the harness was taken out alone; five left every test green, and now have one: Claude's login screen on the path of Codex alone, the choice asked again at the second start, the robot's marker kept out of the sources planted before the app's first start, agents.json written by the installer of this system (pose_par), and the kit's files checked against the robot's copy (gate open) with codex-features.json.
…eld whole sys::pump reads at most the cap (plus one byte) of a line at a time when run_isolated_pipes is given one, and drops the rest of a longer line up to its newline, as the launcher's _pump does with readline(LINE_CAP + 1). Before, a Codex printing without a newline was held in memory whole until it was killed (150 s, or 60 s for a probe). Claude's repair (run_isolated) reads as before.
…and by the launcher shutil.which puts the current folder first on Windows, even with path= (_win_path_needs_curdir): a codex.exe in the folder a session runs `controle` from would have been run by the night's launcher, Codex being out of its official place. An empty or relative PATH entry is a folder of wherever the task runs from too. _headless_codex.find_codex now looks only in absolute folders, and codex::which (the app) drops relative entries as well as empty ones. Tests: a codex in the current folder and under a relative entry is never found, an absolute one still is. _headless_claude.find_claude has the same flaw on master; it is left to a PR of its own (Claude's path is out of this one).
…e, as on master An install made from a terminal with --agents claude (or BIAB_AGENTS=claude) has agents.json and no sources in ~/.brain-in-a-box/src. Once ~/.claude/hooks/brain is gone (Claude Code reinstalled over ~/.claude), the app had no _agents.py to read the file and gave no agent: a red, blocking « Ton choix d'IA » line, the brain step and the onboarding refused, no Claude in Terminal. Master never read the file and would have reinstalled. - No _agents.py on disk: the app runs its own copy (the one codex_open already falls back on), from a private folder of its own, with the kit's Python. _agents.py stays the only reader. - No Python at all: where the kit's gate keeps Codex closed, Claude alone (the kit trusts no file that lists Codex there, and the installer reads the file itself once Python is in); where Codex can be chosen, no agent, as before (the window asks the choice again anyway).
… the agents With Claude alone the guard watched ~/.codex and ~/.agents/skills too, which master never did: the user's own Codex writing there during the brain step or a repair (a folder trusted, /hooks, a skill) made the step fail (« L'installation touchait à des fichiers à toi ») and was put back. install.sh and install.ps1 do not write there for Claude alone (test-hooks.sh §16, same-as-master.ps1, and the_real_install_sh_cannot_damage_the_users_files, which now compares Codex's files without the guard). When Codex is listed (agents.json, or a choice the installer is about to record), ~/.codex, the CODEX_HOME agents.json records and ~/.agents/skills are guarded as before.
…ell's CODEX_HOME; « not yet » before the brain - start_onboarding (Codex preferred) asks codex::login first and opens nothing unless Codex is logged in with a personal ChatGPT plan: an API key in auth.json billed the whole 15-minute session (taking keys out of the environment does nothing for a key in the file), and a Codex not logged in offered « Provide your own API key » on its first screen. The onboarding's codex runs with -c cli_auth_credentials_store="file", the store codex::login read. - The login and onboarding windows unset CODEX_HOME (Remove-Item on Windows) when agents.json records ~/.codex: Terminal and PowerShell run the user's profile first, whose CODEX_HOME would have sent the login and the session to another folder. - codex::Login::System: Windows' %ProgramData%\OpenAI\Codex open to other accounts, apart from a workspace account (Managed). No login fixes it; the diagnosis says so, and Managed now says a workspace account only (« l'administrateur de ce Mac » matched no case). The API key's line asks for the login button, which rewrites auth.json with the ChatGPT account, not codex logout. - Repair, Codex before the brain step installed codex-features.json: state not_yet (« Codex pourra m'aider une fois ton cerveau installé ») instead of « too old or not supported », nothing run.
app/dev/screens.mjs runs app.js and the mock's fake backend in a small DOM on a clock of its own (each timer in order, nothing waits, the same screens every run) through 43 scenarios with Claude alone: a fresh install on each system, a Voyage key, already installed and the end's two buttons, keywords, blocked, an early login, failures, the repair with Claude, its errors and its states. It compares every screen (title, text, notes, buttons) with app/dev/screens-claude.txt, written from master's app.js and mock.html (e6b8be0): the branch shows the same. A word changed in any of them turns the app job red; --write rewrites the file for a change made on purpose. Before, only a local script compared them, and the job only parsed app.js.
… account says so, change of AI, one account rule Shown only where the kit lets Codex be chosen; with Claude alone every screen is master's (app/dev/screens.mjs). Checked by nine scenarios of the mock, Codex open as the tests open it, each red before this change: - The end, Codex preferred and not logged in with ChatGPT (login put off, an API key, a workspace account): « Connecter Codex à mon compte ChatGPT » instead of « Personnaliser mon cerveau », a note per state (an API key would be billed, its login replaces it; logged out: pick « Sign in with ChatGPT », never a key), the onboarding after the login. Claude preferred: Claude's onboarding, the note says Codex's state. - The login's wait ends when Codex logs in a way the kit does not use (a workspace account, a key, Windows' system settings): the screen says why instead of « J'attends » for ever. A workspace account keeps the button (« ton espace personnel »); the system's settings have none, only « Plus tard ». The same in the repair's login. - An API key: « Clique sur le bouton et connecte-toi avec ChatGPT : ta connexion remplacera la clé », no command to type. - Repair state not_yet: « Codex pourra m'aider une fois ton cerveau installé ». - « Changer d'IA » on the diagnosis and the accounts' screens: back to the choice. - One account rule on the welcome, the choice and the login: « un abonnement ChatGPT personnel (pas un compte d'entreprise) », and « Il te faut les deux comptes » for both. The mock learns &after=, &codex=system, a list for &choix=, and not_yet before the brain step.
…'s path names Claude in the welcome and the end - app-codex: the robot puts the ChatGPT login off, so the end must offer « Connecter Codex à mon compte ChatGPT » and « Ouvrir mon cerveau dans Obsidian », never « Personnaliser mon cerveau », and say to pick « Sign in with ChatGPT ». - app (Claude alone): « pour Claude » in the welcome and « Claude retient » at the end, as the Codex path already checked its own end. Every word of these screens is app/dev/screens.mjs's. - The screens of a Codex logged in otherwise: « Codex est connecté avec un compte ChatGPT d'entreprise », « Codex suit des réglages de ce … », never on either path. Each check turned red first in test_terrain_app.
…fits the system's settings Seen in the mock: « Pas d'autre ordinateur sous la main ? » read oddly under the screen of Windows' system settings; « Pour avancer sans Codex, change d'IA. » there, the account question on the other accounts' screens.
…mes before the prompt The Windows-only test still looked for « codex.exe' 'Read … » right after the program, which the store's -c now separates (run 39043712739, job app on Windows); the two checks are one.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phases 8 to 10b of the Codex plan (
docs/proposals/codex.md, §2.4, §4 and §6): Bibo's phases, all on this branch. Codex stays closed on every system: in production anagents.jsonthat lists Codex is not trusted, so the repair (phase 9) asks no one and the screens (10b) do not show it, and Bibo without Codex behaves exactly as on master.app/src-tauri/src/codex.rsThe Rust copy of the kit's launcher without tools (
engine/hooks/_headless_codex.py), for Bibo:find): the official installer's place first (~/.local/bin/codex,%LOCALAPPDATA%\Programs\OpenAI\Codex\bin\codex.exe), then the PATH and Homebrew's folders; on Windows an.exeonly (npm'scodex.cmdgoes through cmd.exe).login, §4.3):chatgpt,billed,managed,logged_outorunknown.auth.jsonis read for its mode, its plan and whether a refresh token is there, nothing else. Thencodex login status, run as a question would run it (same allow-list environment, same-c), is compared to fixed lines and never copied: its stderr shows part of an API key. No key ever reaches Codex; Bibo never logs anyone out.check,ask): the launcher's checks before a question, in its order (Windows%ProgramData%\OpenAI\Codex, version andexec --help,.env, managed layers,auth.jsonand plan,login status,features listwith the user's and an emptyCODEX_HOME, MCP servers), the same-c, the same refusals and pauses (kind, reason and words), the same private folder, the same reading of the stream, the prompt on stdin only,--ephemeral. « Arrêter » is honoured between the probes and before the question.sys::run_isolated_pipes: each line with its pipe (Codex says its login on stderr), and once Codex exits, what it left in its group is killed, as the launcher does on macOS and Linux.run_isolated(Claude's repair) is unchanged.Where the two differ, the app is stricter (said at the top of
codex.rs): a stream line that opens an object and is not JSON ends the run (Python readsNaN), anid_tokenwith a character outside base64url is unreadable, a profile that is not a regular file counts as unreadable.Parity: one test that fails when the two diverge
engine/tests/codex_contract.pydescribes the scenarios (114 on macOS, 109 on Linux and Windows): a home, aCODEX_HOME, managed layers, an environment andfake_codex.py's settings each. The Rust testthe_app_and_the_launcher_answer_alikeplays each one twice, in a world of its own, throughcodex.rsand through_headless_codex.py, and compares:-cin order, environment, folder, stdin, schema;Scenarios include the order of the checks (two faults at once: the first check says why) and, on Windows, a
%ProgramData%\OpenAI\Codexthat appears after the probes. A second test compares the decision on Windows access lists (47,580 cases, on all three systems) and SIDs. Without Python both fail: no skip.Found:
codex-features.jsonwas installed nowhereThe installed launcher (
~/.claude/hooks/brain/_headless_codex.py) looks forcodex-features.jsonnext to itself: neither installer put it there, so Codex would have stayed paused for everyone once opened. Tests andcodex-mockran the launcher from the repository, where the file is one folder up.install.shandinstall.ps1now place it in their Codex section only (Claude alone: nothing changes); Bibo reads it at the same place.test-hooks.sh§16 and thewindowsjob check the installed file and that the installed launcher reads it.Not hollow
27 protections taken out one at a time, 25 in
codex.rsandsys.rsand 2 in the launcher (a-c, the order of the checks,features listwith an emptyCODEX_HOME,auth.json, a caller's key passed on, a billed login line,login status's line copied, a tool item taken as an answer,--ephemeral, the group sweep, the cage left behind, the login shown, access lists, SIDs, the strict schema, the prompt as an argument, the MCP re-read, theremovedstage, the minimum version, thereprof the words, the.env"CA" rule, the Team plan, an unreadable profile, an MCP server named in a comment; in the launchernotify=[]and thelogin statusline). Each turns at least one test red; two did not at first and got their scenario (a.envsettingLOCALAPPDATA, a profile that is a folder). Without install.sh's new line,test-hooks.shis 220/221.Checked locally (macOS)
cargo test(169),cargo clippy --all-targets -D warningsfor macOS and for the Windows target (compile only, resource compiler stubbed),rustfmtoncodex.rsonly (the repository's style;sys.rs's deviations are master's), Python unit tests (649; the launcher's and the contract also under Python 3.9.6),test-hooks.sh221/221, shellcheck, actionlint,bash -n,py_compile,node --check. No pwsh on this machine: install.ps1's line and thewindowsjob are checked in CI only.CI
758042d), every job green. The parity runs in theappjob on all three systems (Windows: 109 scenarios, about 60 s, including theicaclsones and the folder that appears after the probes).terrain.ymlstarted by hand on the branch: https://github.com/BrainInBox/brain-in-a-box/actions/runs/38978042544 (8ab25a7, the same app), every job green,terrain_appon macOS, Windows and Linux included (Bibo, Claude alone, clicked through in its real window).windowsjob only, a defect of the bench: the new check imported the installed launcher and Python wrote its__pycache__next to the hooks, which the step compares before and after ("Claude's install changed"). Fixed withpython -I -B(intest-hooks.shtoo).Phase 9: the repair with Codex
Bibo's repair asks Codex when
agents.jsonlists it (docs/proposals/codex.md, §2.4, §4.4; section « Étape 9 — fait et écarts »). Codex stays closed on every system: in production anagents.jsonthat lists Codex is not trusted (ferme), and the repair then asks no one.agents.rs):_agents.py lire(new subcommand, one ASCII JSON line of whatlire()says), run with the kit's Python, the installed copy next to the hooks first, else the one of the sources Bibo installed. The gate and the file's checks stay in_agents.py. Without the file: Claude alone, and nothing is run to know it. A file the kit cannot trust, or one Bibo cannot have read (no Python, no_agents.py): no AI is asked (new stateagents).mediumthenhigh, the samevalidate(). Codex throughcodex.rs(ask_heard: told when the question leaves, and each stream line, kept masked incodex-stream.jsonl). Catalogue unchanged._headless.py): the preferred engine, then the other one only when listed, afterstart,unsupported,auth,quota,api; after a timeout only towards Claude; never afterinit,invalidor « Arrêter ». An engine found unavailable before (not logged in, paused, offline) is never run. When none answers, the first one run says why; the others go to the journal (echecs) and the report.terminal_fallback, the last screen's button, andopen_terminalrefuses otherwise.ai()); Rust names the engine (enginein the status and the plan, stagesasking_codex/asking_claude) only when Codex is listed. Codex's ChatGPT login button is phase 10b.Claude alone, exactly as master
agents.jsonnothing is run to read it; the status, the plan and theaskingstage are master's JSON (noengine); the journal entry has master's keys, one by one; the report names no engine; every earlierrepair.rstest passes unchanged.app.jswith master's mock, and the branch's with the branch's, run in a small DOM under Node on 29 Claude-alone repair scenarios (Mac, Windows, Linux words; fixed, user action, low confidence, two rounds, failed down to Terminal, regression, quota, re-login, suspect, offline, Terminal, login put off, each error, each state, « Arrêter »): every screen has the same text, order and buttons. Control: one word changed in a note makes them differ. (No browser: Playwright is phase 10b's.)Not hollow
33 protections taken out one at a time (the fallback rule, Terminal only when Claude is chosen, what the window is offered, Codex's refusals and what counts, the same schema, instructions and effort, the names in the journal and report,
agents.rs's reading): each turns at least one test red. One did not at first (a reader that fails) and got its test.Checked locally (macOS)
cargo test(187),cargo clippy --all-targets -D warningsfor macOS and the Windows target (compile only),rustfmtin the repository's style onagents.rsandcodex.rs(repair.rs: nothing beyond master's deviations), Python unit tests,test-hooks.sh221/221, actionlint,node --check,py_compile.CI
399bfa0), every job green; the phase's tests run in theappjob on all three systems (Windows: 11, without the 6 that need a fakeclaude).terrain.ymlstarted by hand on the branch: https://github.com/BrainInBox/brain-in-a-box/actions/runs/39000240637 (ddd1852), every job green,terrain_appon macOS, Windows and Linux included (Claude alone, in the real window).399bfa0(a relativecodex_homedropped instead of refused; the fake Codex rewrote its instructions with CRLF), and sawcodex-mock(macOS) go red once on the sign of life's known race (session-loggeroverwritten by a hook of the same event, three sessions in a row); green on the next run, unchanged.Phase 10a: Bibo's brain, steps, diagnosis and configuration guard per agent
Bibo's back end follows the agents
agents.jsonlists, read through_agents.py lire(phase 9) (docs/proposals/codex.md, §4.2, §4.5; section « Étape 10a — fait et écarts »). Codex stays closed on every system: in production anagents.jsonthat lists Codex is not trusted, and nothing below shows for Claude alone.detect.rs): the vault and the memory, then each listed agent's part. Claude's as before (hooks insettings.json, skills). Codex's: the kit's hook in itshooks.json, the kit's block in the instructions Codex reads (AGENTS.override.mdwhen it has text, elseAGENTS.md), the skills in~/.agents/skills. Complete for each agent listed; the brain step runs again when a chosen agent is not wired. A choice the kit cannot trust is never complete.steps.rs): where Claude Code's stands, the preferred one first (steps::order). It runs the official installer of therust-v0.162.1release, checked against the SHA-256install.shandinstall.ps1pin (a test holds the three equal), as they run it:CODEX_RELEASE,CODEX_NON_INTERACTIVE=1, theCODEX_HOMEofagents.json, noVOYAGE_API_KEYnor the installer's own settings, Windows'tarfirst on the PATH. A Codex already there is kept; an older one is said, not replaced (asinstall.sh). A script that is not the pinned one never runs. Theappjob runs the step for real on each system (codex_real).diagnose.rs): Codex (version), its login (codex::login: ChatGPT account ok; an API key "would be billed: I don't use it"; a workspace account or imposed settings; not logged in), its hooks (and whether Codex runs them: new_agents.py hooks-muets, Bibo works nothing out itself), instructions and skills; each agent's site in the network check (claude.ai, chatgpt.com). Codex alone runs nothing of Claude's. An untrustedagents.jsongives a red « Ton choix d'IA » line, and the brain step stops before downloading anything (the installers would stop anyway).config_guard.rs):~/.codexalways (a Codex of the user's own, Claude alone included) and theCODEX_HOMEofagents.json, wherever it is.hooks.jsonassettings.json;config.toml: only[hooks.state."<key>"]tables added at its end, one hash each, each naming one of the kit's hooks in that folder'shooks.json(group without a matcher, every command the same kit hook), and in a table of the kit's its hash and comment (another Python); nothing else (no setting, no approval of the user's hook or anotherhooks.json's, no hash slipped into the user's last table).AGENTS.mdappended to; rules and~/.agents/skillsas hooks and skills. Checked against the installers' ownwire.py: nothing to put back.remedies.rs:.codexand.agentsin the catalogue test'sNEVERlist.Claude alone, exactly as master
agents.json.e6b8be0) and the branch,--diagnosein four homes (empty, Claude's brain,agents.json["claude"], a Codex of the user's own without a choice): identical JSON (free space aside). The only difference, by design: anagents.jsonlisting Codex (closed) gives the red « Ton choix d'IA » line.install.sh, Claude alone, next to a~/.codexof the user's: untouched.Not hollow
41 protections taken out one at a time: 40 turn at least one test red. Seven did not at first and got their test (approval keys of the tests named another folder's
hooks.json; a hash appended right after the file's end read as a change in place; the guard's own record already made the kit's skill "ours"; completeness ignored the instructions). The 41st,.codex/.agentsinNEVER, only guards against a future remedy.Checked locally (macOS)
cargo test(205; the real Codex install, ignored, runs in CI),cargo clippy --all-targets -D warningsfor macOS and the Windows target (compile only),rustfmtin the repository's style on the phase's lines only, Python unit tests (651;_agents.py's also under Python 3.9.6),test-hooks.sh221/221, actionlint.CI
ca095a0), all 13 jobs green; the real Codex step (codex_real,appjob) installs Codex 0.162.1 through its pinned installer on macOS, Linux and Windows.terrain.ymlstarted by hand on the branch: https://github.com/BrainInBox/brain-in-a-box/actions/runs/39018558300 (f5fb0d9), every job green,terrain_appon macOS, Windows and Linux included (Claude alone, in the real window);568c32bandca095a0change tests only./;hooks-muets'sprint()ends with CRLF on Windows (the app trims it); the real test gave the official installer a\\?\CODEX_HOME, which itsJoin-Pathrefuses (agents.json records a plain path).Phase 10b: Bibo's screens for Codex (the choice, Codex's step, its ChatGPT login, the onboarding), hidden while the gate is closed
docs/proposals/codex.md, §4.1, §4.3, §4.4; section « Étape 10b — fait et écarts ». Codex stays closed on every system: as shipped the app asks nothing and every screen is master's.agents::codex_open): theCODEX_OUVERTline of the_agents.pythat readsagents.json(installed, else the sources'), else, before any kit is on disk, the app's own copy of that file (include_str!). Read without Python (a fresh Mac or PC has none): only the line as the kit writes it; anything else is closed. A test plays every form against_agents.py ouvert.release.yml, test.yml'sappjob and the terrain's preview freshness follow_agents.py: the PR that opens a system publishes an app that offers it.choose_agents,agents::current); the brain step hands it to install.sh / install.ps1 (BIAB_AGENTS,BIAB_PREFERE), which alone writeagents.json. Preselection: the choice made, else Claude alone on an install of before the choice, else what is installed.codex -c cli_auth_credentials_store="file" loginin Terminal / PowerShell, withoutOPENAI_API_KEY,CODEX_API_KEY,CODEX_ACCESS_TOKEN,OPENAI_BASE_URL; the window pollscodex::login, never Codex's output. An API key or a workspace account is said and not used; Bibo never logs anyone out; « Plus tard ». The repair gets the same button.codex "<prompt>");ONBOARDING.mdspeaks to Claude Code or Codex; the end names the agents chosen.app/dev/mock.html:&porte=1,&choix=,&resume=,&codex=billed|managed|unknown; checked on screen with Playwright (Mac, Windows, Linux words).terrain_app, pathapp-codexon the three systems (terrain.yml matrix): the robot opens Codex in its own copy of the kit (the tested sources, gate open, as the other benches do), handed to the app and put in~/.brain-in-a-box/srcbefore its first start; it clicks « Choisir mon IA », « Codex », skips the Voyage key and the ChatGPT login. Codex's step must be seen, Claude's never; thenterrain.check_codex:agents.json,codex --version, the six hooks with--agent codexapproved, the kit's correction-detector run as Codex runs it, Codex'sAGENTS.mdand the vault's, the skills in~/.agents/skills, nothing of Claude Code's.Claude alone, exactly as master
offer),chooserefuses Codex, no choice is pending, the installer gets what it got; 10a's tests of the diagnosis, steps and guard pass unchanged.app.jswith master's mock and the branch's with the branch's, in a small DOM under Node, on 41 Claude-alone scenarios (fresh install on Mac, Windows, Linux, Voyage key, already installed, keywords, blocked, early login, failures, and phase 9's 29 repair scenarios): 39 give the same screens in the same order; the 2 clock-driven loops give the same screens in an order that varies from run to run, on master too. The only difference: the welcome comes after the gate is read, with the same words. Control: one word changed makes them differ.terrain_app's pathapp(Claude alone, real window) is unchanged, except that a Codex screen there now fails.Not hollow
33 protections taken out one at a time (17 in the app, 16 in the robot): 32 turn at least one test red; five of the robot's did not at first and got their test; the 33rd (an unreadable
_agents.pyread as empty text) is equivalent: no gate line, closed.Checked locally (macOS)
cargo test(211, 5 ignored),cargo clippy --all-targets -D warningsfor macOS and the Windows target,rustfmton the phase's lines, Python unit tests (661),test-hooks.sh221/221, actionlint, the mock on screen (Playwright).CI
900fd32), all 13 jobs green (appandcodex-mockon the three systems,app-windows-install, the bundles).terrain.ymlstarted by hand on the branch: https://github.com/BrainInBox/brain-in-a-box/actions/runs/39036793212 (d4a9b61), every job green, including the six app legs in the real window:app(Claude alone) andapp-codex(Codex chosen alone) on macOS, Windows and Linux.900fd32only adds tests.74dd810) found three Windows-only defects of the benches, nothing in the app or the kit: the Rust test of Codex's folder read Windows' Python output (console code page) as UTF-8 (now-X utf8); test_terrain_app's fake Mac, run on the Windows runner, looked for Codex under LOCALAPPDATA (now the pilot says where); the Windowsapp-codexleg asked Claude Code which Git Bash it takes, with no Claude Code there, after every screen had passed (Claude Code's Git Bash check now runs on the path of Claude alone only).Phase 10b review: fourteen findings, twelve fixed
docs/proposals/codex.md, section « Étape 10b — relecture et corrections ». Every fix has a test that was red before it. Codex stays closed; with Claude alone every screen is master's, now held in CI word for word.Security
codexsession billed on the key inauth.json. Nowstart_onboardingaskscodex::loginand opens nothing but on a personal ChatGPT login, with-c cli_auth_credentials_store="file"; the end offers « Connecter Codex à mon compte ChatGPT » instead, with a note per state.CODEX_HOMEfrom the shell profile (2): the login and onboarding windows unset it whenagents.jsonrecords~/.codex.sys::pumpreads at most the cap of a line and drops the rest, as the launcher's_pump(Codex only; Claude's repair unchanged).find_codex(launcher) andcodex::which(app) look in absolute PATH folders only._headless_claude.find_claudehas the same flaw on master: left to a PR of its own (Claude's path).Non-regression
agents.json ["claude"]with no kit on disk or no Python (5): the app runs its own copy of_agents.py; with no Python at all, Claude alone where the gate keeps Codex closed (master's behaviour).~/.codexand~/.agents/skillsare guarded only when Codex is listed; master never looked there.app/dev/screens.mjsrunsapp.jsand the mock in a small DOM on a clock of its own (deterministic), 43 scenarios compared screen by screen withapp/dev/screens-claude.txt, written from master'sapp.jsand mock (e6b8be0); run by theappjob. Plusterrain_app: « pour Claude » in the welcome, « Claude retient » at the end.Codex's screens (gate open in the mock): a workspace account or Windows' system settings end the login's wait and say why (
codex::Login::Systemsplit fromManaged);not_yetin the repair before the brain step; « Changer d'IA » on the diagnosis and the accounts' screens; one account rule (« un abonnement ChatGPT personnel (pas un compte d'entreprise) », « Il te faut les deux comptes »); the API key's screen asks for the button, no command. Nine mock scenarios check them in the same script;terrain_app'sapp-codexexpects the ChatGPT login at the end, never the onboarding.Checked locally (macOS):
cargo test(217 + 1, 5 ignored), clippy for macOS and the Windows target,rustfmton the lines touched, Python tests (664, 7 skipped;test_headless_codexunder 3.9.6 too),test-hooks.sh221/221, actionlint,node app/dev/screens.mjs, the new screens on Playwright.CI: tests https://github.com/BrainInBox/brain-in-a-box/actions/runs/39044551159 (
087405d), all 13 jobs green (parity on the three systems, the screens' check in theappjob,app-windows-install,codex-mock).terrain.ymlby hand on the branch: https://github.com/BrainInBox/brain-in-a-box/actions/runs/39043712047 (d373d3e, the same app), all green, includingappandapp-codexon macOS, Windows and Linux. The run before (https://github.com/BrainInBox/brain-in-a-box/actions/runs/39043712739) failed on Windows'appjob only: a Windows-only test still expected the prompt right aftercodex.exe, before the store's-c(fixed in087405d, nothing in the app).