cawdev-cli 1.0.3-beta → 1.0.5-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/runner/README.md CHANGED
@@ -101,7 +101,7 @@ with the window — there `q` stops it, and asks first when sessions are live.
101
101
  machine is configured, most changes are smaller than that. **Inside the
102
102
  terminal, press `c`** (or type `/config`): a picker over what this machine does
103
103
  — add a project, add a workspace, each agent with its current state ("Disable
104
- claude", "Enable agy"), the console-rules switch, and the config itself with the
104
+ claude", "Enable codex"), the console-rules switch, and the config itself with the
105
105
  token cut short. Every row is one of the shell commands below, driven through
106
106
  the terminal's own line editor and picker; `esc` anywhere in a step changes
107
107
  nothing. The screen edits the file the daemon says it booted from (the hello
@@ -126,7 +126,7 @@ is for scripts and for a machine with no daemon to attach to:
126
126
  cawdev config a menu, the same Select widget as everywhere else
127
127
  cawdev config add-project serve one more project you can write to
128
128
  cawdev config add-workspace another checkout for a project already served
129
- cawdev config agent enable|disable <claude|agy>
129
+ cawdev config agent enable|disable <claude|agy|codex>
130
130
  cawdev config rules on|off accept permission rules from the console — R126
131
131
  cawdev config show this machine's config, token redacted
132
132
  ```
@@ -331,6 +331,166 @@ browser anywhere and the terminal collects the session when you approve.
331
331
  error — it is what an expired session looks like, and the answer is the same as
332
332
  having none.
333
333
 
334
+ ### Advanced mode: one window per session
335
+
336
+ `/advanced-mode` splits the terminal the way emacs does: **one window per
337
+ session** this machine is running — or ran since you attached — above **the
338
+ prompt, which stays at the bottom** the way a shell's does. Each window's title says what kind of
339
+ session it is (`plan`, `dev`, `review`, `ask`…), where it stands, the card and
340
+ the project, and its body is that session's output as it arrives. A frame's
341
+ colour is its session's state — **red while it runs, green once it finished**
342
+ (a plan planned, a dev developed), amber if it stopped any other way — and
343
+ the focused window is drawn in heavy lines; `tab` and `shift+tab` move the focus, `1`–`9`
344
+ jump to a window, and `enter` prompts the focused session exactly as it does
345
+ outside the mode. `/advanced-mode` again (or `/advanced-mode off`) leaves.
346
+
347
+ It is drawn on the terminal's **alternate screen**, like nano or emacs, and
348
+ holds still the way they do: auto-wrap is off, so nothing ever scrolls the
349
+ screen, and the wheel is sent to the program as arrow keys instead of sliding
350
+ the terminal up. The normal screen and its scrollback are exactly as you left
351
+ them — R81's reason
352
+ for printing into the scrollback still holds, and this mode is something you
353
+ ask for rather than the default. On the way out, what the watched session said
354
+ meanwhile is laid into the scrollback, so nothing was lost by watching it here.
355
+
356
+ **The PLANNED cards run across the top, grouped by project** — a line with
357
+ each project's name over its cards and a rule between two projects, every
358
+ project this machine serves included ("nothing planned" is an answer) — one
359
+ small box each, titled with the card and its status — `R3 (Planned)` — the cards of
360
+ every project this machine serves that are ready to be developed, read again
361
+ every thirty seconds. **`d`** lists them and develops the one you pick, on this
362
+ machine and on the card's own branch; `/new dev` with nothing after it does the
363
+ same anywhere, and `/new dev R3` goes straight to one.
364
+
365
+ **Four windows at most.** Past four, the rest are named on one line above the
366
+ prompt — number, kind, state, card, in the state's colour — and `tab` (or the
367
+ number) onto one brings it up in place of another. The focused session always
368
+ has a window, then the ones still running, then the ended ones, each most
369
+ recently looked at first.
370
+
371
+ A window stays up when its session ends, and its last line says what can be
372
+ done next:
373
+
374
+ - **`/develop`** on a finished plan starts the development of its card, on
375
+ this machine (the branch is the card's own).
376
+ - **`/pr`** on a finished session that wrote a branch opens its pull request
377
+ (`POST runs/{id}/pull-request` — through the card's work item, or from the
378
+ run itself for a session started from a sentence): this machine pushes the
379
+ branch if origin does not have it and runs `gh pr create` (so `gh` has to be
380
+ installed and signed in here). The POST answers with the workspace REQUEST,
381
+ and the terminal follows it (`GET /api/runners/{id}/workspace-requests/{id}`)
382
+ to its result: the URL — which the daemon confirms with `gh pr view` after
383
+ `gh pr create`, so "made" is the host's word and not gh's — or why there is
384
+ none, printed in full (no `gh` comes with its install line and the compare
385
+ link to open it by hand). It watched the run's `prUrl` before, which a
386
+ failure never writes, and said "asked for…" for as long as it was open.
387
+ A checkout cloned on demand is served like any other: the set of paths a
388
+ request may name is read per request, not taken at boot.
389
+ - **`/continue`** on a failed or stopped session is the console's *Carry on*,
390
+ on this machine (`POST runs/{id}/carry-on` with this runner): the same run
391
+ re-queued, its window coming back to life when the daemon takes it. A
392
+ session that stopped BEFORE this terminal attached — the daemon forgets a
393
+ run as it ends, so after a restart nothing here knew of it — is found on
394
+ attach and on sign-in (`findStopped`: the first page of each served
395
+ project's runs, kept when `carriesOnHere` — yours, `canCarryOn`, and its
396
+ checkout on this machine or none), listed in the scrollback, and given a
397
+ window that opens on why it stopped.
398
+ - **`/open`** opens the pull request in your browser.
399
+ - **`/drop`** deletes the checkout the session was cloned into, when it was one
400
+ made on demand (below): asked first, naming the path, and refused while a
401
+ run is in it or holds it, with uncommitted files, or with a commit origin
402
+ does not have. The checkouts in the config are never offered — a project
403
+ always keeps them.
404
+ - **`/close`** takes the window away.
405
+
406
+ **`/new`** starts a session from the prompt, aimed at this machine so its
407
+ window opens here: `/new plan R3`, `/new dev R3`, `/new ask <question>` — or a
408
+ task in words with no card: `/new plan add a CSV export of the invoices` writes
409
+ its card first and plans it (R150), and `/new dev fix the sort on the client
410
+ list` writes its card too (`cardTitleFor`: the sentence, cut between words
411
+ near a hundred characters, the whole text in the body) and then develops it
412
+ exactly as `/new dev R12` would — a number, a work item on the development
413
+ board, a branch of its own, and the card session's instruction to commit. It
414
+ was a manual session on a `task-…` branch with no card, which never reached
415
+ the board and was never told to commit. A card reference on its own is that card; anything
416
+ else is the task in words. **The project is always asked** — a list of what
417
+ this machine serves — unless the command names it with `@project`, or
418
+ advanced mode is narrowed to ONE project (`/projects`), which is a choice
419
+ already made rather than a guess. Never guessed from the focused window, nor
420
+ from a machine serving just one, since a session on the wrong project is a
421
+ card planned or a branch cut in the wrong repository. `/new dev` alone and `d` list the planned cards of every project,
422
+ each with its own, so they ask nothing more. A usage window over this machine's threshold is refused in a
423
+ sentence; `--anyway` starts it regardless, the same decision the console's
424
+ panel asks for. All four commands need you signed in (`/login`), and they work
425
+ outside advanced mode too, on the session you are watching.
426
+
427
+ **Which projects it shows is yours to pick.** `/advanced-mode` on a machine
428
+ serving several projects opens a picker with every one ticked — space (or a
429
+ digit) ticks and unticks, `a` all or none, enter confirms — and only the
430
+ projects ticked are drawn: their windows, their roadmap on top, and the ones
431
+ `/new` asks between. `/advanced-mode @saasng @ngsign` names them and asks
432
+ nothing; `/projects` asks again from inside the mode; `/reload` keeps the
433
+ pick. A prompt never talks to a session of a project that is not shown.
434
+
435
+ **Across the top, the roadmap** (`roadmapStrip`): every project this machine
436
+ serves as a small board — Considering, Planned, In development, Merged side
437
+ by side, one line per card, `●` on a card with a session running now;
438
+ Shipped and Declined, which only grow, are counts beside the project's name,
439
+ as on the console's board. As tall as the fullest column, never more than a
440
+ third of the screen, and gone before the windows are on a short terminal.
441
+ When the room runs out it is SAID: a column ends in `+N more`, empty columns
442
+ go first when the width does, then whole columns (Merged, then Considering,
443
+ then Planned — In development stays longest), named in the header as
444
+ `merged 3 hidden`, then projects, counted as `+N projects`. Read every 30 s.
445
+
446
+ **A window says which agent and which model** — `Claude · Opus 5.5`,
447
+ `Codex · GPT-5-codex` — bold and underlined at the head of its second line
448
+ (`agentModelFor`, `modelLabel`). A live session's is the daemon's word, what
449
+ it actually spawned: the model asked for, replaced by the one Claude Code
450
+ announces on `init`, and per STAGE on a walked run, since each stage is its
451
+ own process. An ended one's is the platform's. No model named reads
452
+ `default model` — the CLI's own choice — rather than a guess.
453
+
454
+ **A dev window says where it is working**: its second line is the project and
455
+ the absolute path of the checkout (`subtitleFor`) — on a machine with
456
+ `saasng` and its `-auto-N` clones, the one directory to open. Cut from the
457
+ left when narrow, so the end of the path survives.
458
+
459
+ **The prompt shows what is open, not what was.** A question is the prompt's
460
+ banner alone, whole and wrapped between words, and it goes the moment it is
461
+ answered — here or in the console. A permission request's lines are tagged
462
+ with it (`sayAbout`) and leave when it is decided.
463
+
464
+ ### Checkouts made on demand: `autoWorkspaces`
465
+
466
+ `"autoWorkspaces": 3` in the machine's config lets the daemon CLONE up to three
467
+ more checkouts of a project by itself, when a coding run would otherwise wait
468
+ for "no free workspace" — so `/new dev R4` starts while R3 is still being
469
+ developed. Absent or 0 is none, as it has always been. The clone sits beside
470
+ the project's first checkout as `<first>-auto-2`, `-auto-3`…; it is a local
471
+ clone of the first (no credential, no download) with `origin` put back to the
472
+ project's remote. Nothing is written to the config: the daemon finds these by
473
+ their name at boot and on SIGHUP, so deleting one — `/drop`, or by hand — is
474
+ the whole of removing it. A clone and not a `git worktree`, on purpose: the
475
+ daemon's reset checks the default branch out, which git refuses in a second
476
+ worktree. Changed without a restart, like `agentCommands`.
477
+
478
+ ### `/reload`
479
+
480
+ Quitting and starting again, in one word — for when the client's code changed
481
+ on disk. The same command line runs again in a fresh process. When nothing is
482
+ running here the runner is stopped first (as `q` does), so the relaunch starts
483
+ it on the new code too; when sessions are live it is kept, and the terminal
484
+ says so — restarting it would end them. Under `runner.mjs --attach` the runner
485
+ is the terminal's own process, so `/reload` says to quit and start again.
486
+
487
+ It comes back **as you left it**: advanced mode if it was on, the same window
488
+ focused and the same session watched, the daemon's log shown or not, and the
489
+ windows of sessions that had ended — with their `/develop` and `/pr` — except
490
+ the ones you closed. The state travels to the new process in
491
+ `CAWDEV_RELOAD_STATE`, which that process reads once and removes, so nothing it
492
+ spawns inherits it.
493
+
334
494
  ### Where the socket is
335
495
 
336
496
  `~/.cawdev/run/<runner name>.sock`, in a `0700` directory, removed when the
@@ -370,6 +530,23 @@ How many coding runs go at once is `min(workspaces, maxSessions)`. A run that
370
530
  waits now says **"no free workspace in cawdev (2 here, all busy)"** instead of
371
531
  "that project already has a run here", which was a proxy for it.
372
532
 
533
+ **A free checkout with uncommitted work in it is not preferred over a clean
534
+ one** (R71/i258). A coding run with no pinned checkout and no free preferred
535
+ one now takes the first free checkout whose tree reads clean, checking each
536
+ free checkout in `workspaces` order until one does. It only falls back to a
537
+ free checkout somebody stranded work in — and today's *Start anyway* flow —
538
+ when every free checkout of that project is dirty; the log says so when it
539
+ happens. A *preferred* checkout (R212) is still taken whether it is clean or
540
+ not: it is the checkout the branch last ran in, so its uncommitted work is
541
+ plausibly that branch's own.
542
+
543
+ **Unless the run asked to wait for a clean one instead** (`waitForClean`,
544
+ R270). Then a preferred checkout is taken only when it reads clean, and when
545
+ nothing free reads clean the run is left queued — with a reason saying so —
546
+ rather than falling back to the first free checkout regardless. See "Prepares
547
+ the working copy" below for where that flag comes from and what the daemon
548
+ says about knowing it.
549
+
373
550
  **This number caps coding and nothing else** (R70). `ASK`, `ROADMAP`, `AUDIT`
374
551
  and `SCOPE` runs take no workspace, so a project whose checkouts are all busy
375
552
  still starts a question, an entry-writing session, an audit and a scoping
@@ -599,7 +776,24 @@ files at all. R47 removes the need for this by giving each run a workspace.
599
776
  uncommitted before the run is started, and the decision arrives with the
600
777
  claim as `allowDirty`. Without it the runner still refuses, with the file
601
778
  list, and fails the run saying so: a run that arrives with no flag is one
602
- nobody was warned about.
779
+ nobody was warned about. **Except when the dirt is the branch's own**: the
780
+ platform NAMED the checkout (a carried-on run's held workspace, R80, or a
781
+ pinned work item's next run, R86) and it is already on the run's branch.
782
+ Then the work is kept and not cleaned — refusing it made every *Carry on*
783
+ a second failure about work the run itself had written.
784
+
785
+ **Or the run may have said, before it got this far, to wait instead**
786
+ (`waitForClean`, R270) — the console's third way out of the dirty-checkout
787
+ panel, alongside *Start anyway* and *Cancel*. That is decided earlier, in
788
+ `pickWorkspace`, at poll time: with `waitForClean` set, the preferred
789
+ checkout is taken only when it reads clean, and when nothing free reads
790
+ clean the run is left queued with a reason rather than claimed at all — so
791
+ by the time a claim reaches this step, the checkout it names is already
792
+ clean and this whole paragraph does not apply. The daemon says
793
+ `waitForClean: true` in its capabilities, the same way it says
794
+ `baseBranches: true`, and an older daemon that does not is not offered such
795
+ a run — the platform tells it apart rather than letting it take a dirty
796
+ checkout the person explicitly asked it not to.
603
797
 
604
798
  With it, the edits are **carried onto the branch** — stashed, checked out,
605
799
  popped (R57). `git checkout` refuses outright when a locally modified file
@@ -662,9 +856,115 @@ files at all. R47 removes the need for this by giving each run a workspace.
662
856
  8. **Reports the lifecycle back** and, if the session dies without saying
663
857
  anything, ends the run rather than leaving it `RUNNING` forever.
664
858
 
859
+ **What each stage used, and what the run used** (R76, R305). Every `result`
860
+ event the CLI prints carries the process's cumulative token counts, and the
861
+ daemon files two absolute readings off each: the *stage's* (`POST
862
+ …/stages/{stage}/usage` — the stage's own processes, across its attempts,
863
+ so a stage re-run after a refused gate keeps adding to the first attempt's
864
+ total) and the *run's* (`POST …/usage`). Each stage is its own process
865
+ counting from zero, so the daemon keeps the run's running total itself and
866
+ adds each child's figure to it: **the run's total is the sum of its stages'
867
+ processes**, and a resumed run starts from whatever the claim already
868
+ carried. The platform keeps the larger reading on both, so a lost report is
869
+ healed by the next one. Tokens, never money, and cache reads are not
870
+ counted — `tools/lib/usage.mjs` says why. A daemon older than R305 reports
871
+ the run and never a stage, and every stage after the first on a staged run
872
+ was ignored as a reading that went backwards; restart the daemon to get
873
+ the per-stage figures and the summed total.
874
+
665
875
  Cancelling a run reaches the daemon on its next poll and takes down the child's
666
876
  whole process group — an agent that started a build should not leave it running.
667
877
 
878
+ ### Scanning every branch (R300)
879
+
880
+ A project with a `quality` block has its branch scanned with SonarQube
881
+ **after a coding run's TEST stage settles and before the next stage begins**,
882
+ on the tree the tests just ran against:
883
+
884
+ ```json
885
+ "cawdev": {
886
+ "workspaces": ["/Users/you/code/cawdev"],
887
+ "quality": { "command": "sonar-scanner", "tokenEnv": "SONAR_TOKEN" }
888
+ }
889
+ ```
890
+
891
+ Both keys have those defaults, so `"quality": {}` is enough. `cawdev config
892
+ quality on <slug>` writes the block (and `off` removes it), as does the
893
+ `c` screen inside the attached terminal. The block is read once at boot
894
+ like the rest of `projects`, so **restart the daemon** after adding or
895
+ changing it — the daemon says so itself when it is signalled.
896
+
897
+ **It is the daemon that scans, not the session.** The scanner is a subprocess
898
+ this process starts and nothing the spawned agent can call — a session that
899
+ reported its own gate could lie about it. It runs in the run's checkout with
900
+ the project's key and the branch's name from the claim
901
+ (`-Dsonar.projectKey=`, `-Dsonar.branch.name=`), the repository's own
902
+ `sonar-project.properties` for everything else, and a working directory in the
903
+ daemon's temp dir rather than `.scannerwork/` inside the checkout, which the
904
+ next reset would otherwise clean and the working-copy watcher report. Then it
905
+ waits for the server to finish the analysis, reads the branch's quality gate
906
+ and its new-code measures, and tells the platform:
907
+ `POST /api/runners/{id}/quality/{slug}`, on the machine's `runner:operate`
908
+ token, the same door as the git survey. The run page and the development
909
+ board show the result — *Quality gate: failed · 3 new problems · Open in
910
+ SonarQube* — found by the branch's name.
911
+
912
+ **The token stays on this machine.** `tokenEnv` names the environment variable
913
+ the daemon reads the scanner token from — export it where the daemon runs;
914
+ it is never written to the config file, never sent to the platform, and goes
915
+ to the scanner in its environment, never on its command line. It is **not**
916
+ the token the project's owner entered under the project's Settings →
917
+ SonarQube: that one is the platform's reader credential and never leaves the
918
+ platform. Two credentials, two custodians. The scanner has to read
919
+ `SONAR_TOKEN` from its environment, which `sonar-scanner` does from 4.7 on;
920
+ older scanners want `-Dsonar.login`, which would put the token in `ps`, so
921
+ this daemon does not offer it. The block may also name `url`, for a machine
922
+ that reaches the server by another name than the platform does (a LAN
923
+ hostname), and `timeoutSeconds` (600) bounding the scan and the wait.
924
+
925
+ **Community Edition.** A server that refuses `sonar.branch.name` — every
926
+ Community Edition without the plugin below — is *skipped by default*: the
927
+ reading carries an error saying so, and nothing is analysed.
928
+ `"whenBranchesRefused"` in the block opts into one of two fallbacks; `skip`
929
+ stays the default and nothing opts in for you.
930
+
931
+ - `"main"` re-scans the branch as the project's **main** analysis and marks
932
+ the reading `MAIN` with a warning. Understand what that trades: the
933
+ project's own dashboard, and the platform's project reading with it, then
934
+ describe whichever branch scanned last.
935
+ - `"project"` scans the branch as its **own scratch SonarQube project**
936
+ instead — `<key>:branch:<branch>-<hash>`, overwritten on each run of the
937
+ branch, and never touching the project's own dashboard. A fresh project has
938
+ no new-code period, so "new" is redefined as *on a line this branch
939
+ changed*; the reading is marked `PROJECT`, its gate is always `NONE`
940
+ (nothing to gate a scratch project on), and there is no new-code coverage.
941
+ This needs the scanner's token to hold **Create Projects** on the server —
942
+ check its default project visibility too, since *public* would publish the
943
+ code. cawdev never deletes these scratch projects; cleaning them up is the
944
+ operator's own housekeeping.
945
+
946
+ The project itself can say which of these it needs: its Settings →
947
+ SonarQube card offers *Developer Edition or above (or Community with the
948
+ [sonarqube-community-branch-plugin](https://github.com/mc1arke/sonarqube-community-branch-plugin)
949
+ installed)*, *Community Edition*, or *Not sure — find out on the first scan*.
950
+ Set correctly, the daemon skips the doomed branch attempt and goes straight
951
+ to the chosen fallback; left at "not sure", behaviour is exactly as above.
952
+ **Restart the daemon after upgrading it to use `"project"`**: an older
953
+ runner does not know the value and refuses to boot with it in its config.
954
+
955
+ **A failed scan never fails a run.** A machine with no block scans nothing and
956
+ says nothing. A machine with a block and a project with no key logs `not
957
+ scanned` and reports nothing. Everything after that reports *something*: no
958
+ scanner on this machine, no token exported, no server, a server that refused —
959
+ each is an `error` reading with the sentence on it, so the run page says the
960
+ scan could not run rather than showing a branch nobody looked at, and the
961
+ run goes on to its next stage exactly as it would have. Only a staged run
962
+ with a TEST stage is scanned: a lifecycle without one, an unstaged run, a run
963
+ whose gate refused TEST twice or one that reported done inside TEST has no
964
+ settled TEST stage to scan after. The log line is
965
+ `quality: <branch> gate OK · 0 new problems`, or `quality: could not scan
966
+ <branch>: <why>`.
967
+
668
968
  ## What a project's rules can ask of it (R40)
669
969
 
670
970
  A project may decide that a finished run **pushes its branch**, **opens a pull
@@ -674,11 +974,18 @@ with the claim, and the daemon logs them when it takes a run, so you find out
674
974
  what is going to happen before it happens rather than afterwards. `auto_merge`
675
975
  gets a shouted line of its own.
676
976
 
677
- The daemon does not decide any of this. The platform queues `PUSH`, `OPEN_PR`
678
- and `MERGE` on the same `run_action` queue the console's *Commit* button uses,
977
+ The daemon does not decide any of this. The platform queues `PUSH` and
978
+ `OPEN_PR` on the same `run_action` queue the console's *Commit* button uses,
679
979
  and the daemon performs them in `settleActions` — one pass right after the
680
980
  session ends, because the working-copy watcher stopped with the child and the
681
- rules queue their work at exactly that moment.
981
+ rules queue their work at exactly that moment. `auto_merge` is asked for
982
+ later, when the branch reaches Done on the development board (R306): the
983
+ platform queues a `MERGE` **workspace request** attributed to the rule —
984
+ `requestedByRule: "auto_merge"` on the claim — to the machine that wrote the
985
+ branch, and the daemon logs `MERGING <branch> — by auto_merge, nobody is
986
+ reading this diff` when it takes it. It is the same `gh pr merge` the board's
987
+ button runs. (A platform older than R306 still queues a `MERGE` run action,
988
+ and the daemon still performs it.)
682
989
 
683
990
  **cawdev still holds no git-host credential.** This machine does, which is why
684
991
  the work happens here and why R19 and R25's division is unchanged. All that is
@@ -693,6 +1000,11 @@ What it actually runs:
693
1000
  conflict — the rule wanted one to exist and one does.
694
1001
  - `gh pr merge <url> --squash --delete-branch`, and only if a real pull request
695
1002
  is found. A `/compare/` URL is not one, and it refuses rather than guessing.
1003
+ Before merging it asks `gh pr view --json mergeable,mergeStateStatus` and,
1004
+ while the host answers `UNKNOWN` — mergeability is computed lazily, and a
1005
+ merge seconds after `gh pr create` used to fail for no reason a person could
1006
+ act on — waits up to six times five seconds (R306). Any other answer merges
1007
+ at once; the merge is what says whether it could.
696
1008
 
697
1009
  Every one of them reports a result or a reason, and a machine with no `gh`, no
698
1010
  remote or no credentials simply says so. **A rule is a request, not a grant** —
@@ -1000,3 +1312,42 @@ the prompt.
1000
1312
  The prompt deliberately teaches the **method**, not the task. The task is in the
1001
1313
  roadmap entry, which the agent reads for itself with `task_current` — putting it
1002
1314
  in the prompt too would be a second copy that can disagree with the entry.
1315
+
1316
+ ### Codex CLI
1317
+
1318
+ `codex` in `agentCommands` (`cawdev config agent enable codex`) lets a run name
1319
+ `codex` as its agent. It is spawned by `spawnCodex`, one `codex exec --json`
1320
+ per turn like Antigravity, and every flag it is given is in
1321
+ `../lib/codex.mjs` with what was checked against the real CLI (0.155). Sign in
1322
+ once with `codex login`; the run uses that sign-in and **not** your
1323
+ `~/.codex/config.toml` (`--ignore-user-config`, this CLI's `--setting-sources
1324
+ ''`), so a model or MCP server you configured for yourself does not reach it.
1325
+
1326
+ Codex has a sandbox the operating system enforces, and that decides what it
1327
+ may do:
1328
+
1329
+ - **A run that must change nothing** — ASK, AUDIT, SCOPE, REVIEW, PLAN, and the
1330
+ PLAN, VERIFY and MEMORY stages of any run — gets `--sandbox read-only`. Its
1331
+ shell reads and cannot write, and it needs nothing from the machine. Only
1332
+ cawdev's server is attached, narrowed with `enabled_tools` to the list Claude
1333
+ Code would be handed, and no stage ever holds `report`.
1334
+ - **A run that writes** — CODE, INTERVIEW, MERGE, RELEASE and their IMPLEMENT
1335
+ and TEST — must commit and push, which the sandbox refuses, and Codex has no
1336
+ allow-list for its shell. So it runs with
1337
+ `--dangerously-bypass-approvals-and-sandbox`, and **only on a machine that
1338
+ allows everything** (R126); anywhere else the run fails before anything is
1339
+ spawned, saying so.
1340
+
1341
+ Every MCP server is marked `default_tools_approval_mode = "approve"` — in `exec`
1342
+ the approval policy is *never*, and without it every call to cawdev is refused.
1343
+ The run's token reaches cawdev's server through `env_vars`, never the command
1344
+ line, and is blanked for the model's own commands
1345
+ (`shell_environment_policy.set`).
1346
+
1347
+ Not reached on Codex: experts (the session is told it cannot delegate), a
1348
+ project's tool rules and R51's permission prompt (both are Claude Code's
1349
+ `--allowedTools` grammar and its `approve` handler), the command half of R110's
1350
+ shield (the transcript half — secrets taken out of a result — runs in the daemon
1351
+ and applies), and live prompting (the process ends with its turn; a follow-up is
1352
+ `codex exec resume`). Skills are inlined whole, as for Antigravity. No usage
1353
+ windows are read: Codex has no `-p /usage`.