Repository navigation
feat(tui): offer only the permission modes the agent honours (plan 1.P3 (a), plan r10) - #12
Merged
Merged
Conversation
zy1233
added a commit
that referenced
this pull request
Oct 10, 2026
…ates (plan r9) (#10) ## Summary **Stacked on #9** (base `codex/linux-wsl-r8`). It adds sixteen commits on top of `d84d98a`: the three phase 0 gates of `docs/PLAN/PLAN-RUST-AGENT-RUNTIME-REMOVAL.md` that were still open after r8 (0.3 on the Rust side, 0.7, 0.8), and the parts of phase 1 that need no decision. Section 10.11 and appendix A of the plan have the full record. **No behaviour-changing decision is made here.** Where a decision is needed (the permission-mode mapping, the `--session-id` contract, what happens to `zypi export`, ...) the plan writes a proposal and says what it would cost. Section 10.11 ends with twelve of them, each with my preference; none is implemented. What the branch does: - **0.3, e2e against the real agent.** `python_agent_e2e.rs`: 10 tests drive the pager's ACP client against the real Python agent, and `tui-ci.yml` installs the agent first and sets `PI_E2E_REQUIRED=1` so a missing Python fails instead of passing silently. Its first run found two product bugs, both fixed here: the reason an agent failed to start was swallowed into `channel closed`, and `connect()` hung when the agent exited at once. - **0.7, can the pager lose its own disk reads (ADR1)?** Measured: with 1,000 sessions on Windows the first page of `session/list` went from 2.0 s to 0.11 s and `session/load` from 2.0 s to 0.12 s (a header is read without reading the file, and the repo lists a page at a time). ADR1's override condition did not trigger. The plan inventories the pager's 13 disk-read call sites in 8 files, adds a 13-case PTY matrix of the resume flags (`scripts/tui_pty/resume_matrix.py`: 9 PASS / 4 GAP, the GAPs are the work ADR1 would do) and a landing design with the sandbox ordering problem and a PR slicing. - **0.8, capability matrix and ACP version pairing** (appendix A): every row marked decided / proposed / open, the upgrade rules, and a per-method table of what each end supports. - **1.P1:** `session/list` is paged with an opaque cursor, the pager's picker follows `nextCursor`, and a session is looked up by id without reading every file. - **1.P4:** the pager sends no `mcp_servers`; the agent says which ones a client passed and that it ignored them. - **1.P7:** the permission round trip and `session/cancel` are covered over real stdio. - **Agent exits on its own:** the pager says so and why, instead of a bare `channel closed`. - **1.P3 input, not a change:** `scripts/tui_pty/mode_probe.py` records which permission modes the Python agent honours (12 scenarios on WSL2). Plan mode restricts nothing, Auto is always-approve in the agent, the settings "Default" is dropped, and of the six `--permission-mode` values only `bypassPermissions` does anything. ## Decisions that were open Plan section 10.11, last list. The three that unblock the most, and what became of them: 1. Start ADR1 with PR 1: the Python side only (`_meta.sessionId` honoured by `session/new`, the session's cwd in the `session/load` response). No TUI change, least risk. **Done in #11.** 2. 1.P3: hide Plan, Auto and Default (Shift+Tab becomes Normal <-> Always-Approve) and accept only the `--permission-mode` values that do something. Today Plan gives users a false sense of safety. **Done in #12.** 3. The sandbox ordering (the first of the plan's three options): drop "restore the sandbox profile a session saved", which never applies to a Python session. **Still open.** ## Not verified - The Rust e2e tests and the PTY scripts ran on Linux (WSL2) only; the latency numbers on Windows measure the Python side only. Cold disk, network file systems and more than 1,000 sessions were not measured. - Cross-directory `--resume <id>` (which directory the TUI uses for `@` search, diffs and git info), the settings "Default" path under a pty, and the `-m` / `--model` flag. - The landing design is on paper; everything uses the mock LLM. ## Review guide | Commit | What | |---|---| | `28fed94` | 1.P7: permission round trip and cancel over real stdio | | `54a38fe`, `1b9fb47` | harness: read a header without the file; list a page at a time; find by id | | `e944ed5` | `scripts/session_list_bench.py` | | `c1f02c8`, `9f7bc35` | 1.P1: Python pages `session/list`; the pager follows `nextCursor` | | `935e4b8`, `7becb49`, `9798e1d` | 1.P4: the agent names the ignored MCP servers; the pager sends none; docs | | `affda9e` | the pager notices an agent that exits, and says why a start failed | | `3fc182b` | 0.3: `python_agent_e2e.rs`, `tui-ci.yml`, manifest | | `babd75f` | 0.7: `resume_matrix.py` | | `bee3a36` | manifest: `pi-pager` `reference_passed` 5,602 | | `679cccb`, `d426253` | 1.P3 input: `mode_probe.py` | | `eef675b` | docs: plan 10.11, appendix A, B.4, `docs/baselines/tui.md`, CHANGELOG, AGENTS files | #9 was squash-merged first and this PR's base then moved to `main`. The PR still lists #9's commits, and GitHub could not merge it because files that both sides changed near each other conflict. `b0d001b` merges `main` into the branch with the `ours` strategy: the tree of `main` is the tree of #9's last commit, the base of this branch, so `main` holds nothing the branch lacks, and the branch's tree is unchanged. ## Verification - **CI.** The run on `eef675b` is green (TUI CI [run 38061734062](https://github.com/zy1233/pi-python/actions/runs/38061734062)). The new install step in `tui-ci.yml` ran for the first time, `python_agent_e2e`'s 10 tests really ran (a missing Python would fail them), and the 10 suites add up to **8,659 passed / 0 failed / 22 ignored** with `pi-pager` at 5,602: the numbers measured locally. `cargo test` took 23 m 19 s (32 m 40 s on #9), `Gates + cargo check` 7 m 9 s, Ruff 9 s, pytest on 3.11 / 3.12 / 3.13 about 2 minutes each. `tui-ci.yml` now also runs when `pi_agent_cli/**` changes. After #9 was squash-merged, `main` was merged into this branch (`b0d001b`, keeping the branch's tree, which is the tree of `eef675b`) and CI ran again: green too ([run 38074449935](https://github.com/zy1233/pi-python/actions/runs/38074449935); `cargo test` 21 m 48 s, 8,659 passed again). - Whole manifest on WSL2 (`python scripts/tui_baseline.py test`, scrubbed environment, `PI_E2E_PYTHON` and `PI_E2E_REQUIRED=1`): exit 0, 10 suites, **8,659 passed, 0 failed, 22 ignored** = #9's CI result (8,634) + 25 new `pi-pager` tests (10 e2e, 8 agent-exit, 6 paging, 1 for `mcp_servers`). `pi-pager`: 5,602. - pytest: 2,352 passed / 35 skipped on Linux (WSL2, Python 3.12.3), 2,351 passed / 36 skipped on Windows (Python 3.12.7); `scripts/tests/test_tui_pty.py` with `pyte`: 19 passed. - `ruff check .` and `ruff format --check .`: clean (226 files) with the project's Ruff 0.16.10 and with CI's pinned 0.16.0. - `resume_matrix.py`: 9 PASS / 4 GAP / 0 FAIL. `mode_probe.py`: 12 scenarios.
zy1233
force-pushed
the
codex/adr1-pr1-python
branch
from
October 10, 2026 18:36
1a9a336 to
03961fa
Compare
…P3 (a))
The Python agent does two things about a tool call: it asks, or it approves everything. The TUI
still offered Plan, Auto, a settings "Default" and six --permission-mode values. Measured against
the real zypi on WSL2 (plan section 10.11): Plan restricts nothing (the agent has no
session/set_mode handler, the call fails unseen and the screen goes on saying "plan"); Auto never
asks, because the agent treats `auto` like `always-approve` while the screen promises a classifier;
the settings' Default is a value the agent drops, so after Always-Approve it says Default and the
agent still approves everything; of the six --permission-mode values only bypassPermissions did
anything. All of them err the same way: the user thinks the agent is more careful than it is.
Hidden, not deleted. app/agent_modes.rs holds three capability constants (PLAN, AUTO, DEFAULT, all
false: "the agent honours this"); AppView.{plan,auto,default}_mode_gate and the mirrored
PagerLocalSnapshot gates follow them. Test fixtures open the gates, so the hidden paths and their
old tests still run, and flipping one constant brings its mode back with its tests (1.P3 (b)).
- Shift+Tab cycles Normal <-> Always-Approve (dispatch_cycle_mode_without_plan, next stop from the
pure next_ring_step_without_plan; Auto sits between them only while its gate is on). Everything
else is as before: the pre-session branch stages the flags SessionCreated reads, the session
branch goes through set_yolo_mode_inner, both persist; the managed-policy pin keeps Normal and
shows the policy's warning; a stale Plan flag leaves through Normal with session/set_mode(default).
- Settings: no plan_mode row and no plan-nudge toggle, permission_mode offers Ask and Always
approve only (its description no longer mentions Default, Auto or a classifier), set_plan_mode
does nothing, set_permission_mode turns Auto/Default into Ask, the plan nudge tip is not shown,
and apply_contextual_hints (the one funnel for the hint toggles) clamps plan_mode.
- A saved permission_mode = "auto" or "default" is shown as Ask and the file is left alone; a launch
never starts in Auto (agent_modes::launch_auto).
- --permission-mode takes `default` (no flag) and `bypassPermissions` (--always-approve); plan,
auto, acceptEdits and dontAsk exit 2 with a message that says the flag would change nothing and
lists the valid values. pi-shell's PermissionMode::VALID_VALUES (agent-definition frontmatter) is
untouched.
The Python agent is untouched.
Against the real zypi (scripts/tui_pty/mode_probe.py, now 10 scenarios, the four refused values and
a settings-page check): Shift+Tab goes Normal -> Always-Approve -> Normal -> Always-Approve (asked
in Normal, not asked in Always-Approve); --permission-mode default / bypassPermissions behave as
before; a saved auto or default shows nothing in the status bar and the write is asked about; a
saved always-approve shows "always-approve" and is not asked; plan, auto, acceptEdits, dontAsk exit
2; the settings search for "plan" finds no Plan mode row and the permission picker offers Ask and
Always approve.
Tests: 24 new in pi-pager (21 lib: agent_modes 2, cli 3, dispatch modes 13, settings_modal 3; 3
settings_e2e) and 4 in scripts/tests/test_tui_pty.py. Three old tests that assumed Plan or Default
were visible were adjusted (rows_contain_categories_and_settings_through_pr_14,
expanded_description_wraps_to_modal_width, enum_choice_gated_off_covers_voice_and_permission).
Docs: plan section 10.13, appendix A.1 and B.6, CHANGELOG, packages/pi-agent-cli/AGENTS.md,
scripts/tui_pty/README.md; the baseline reference for pi-pager is 5,626 and docs/baselines/tui.md
records PR #10's CI run (8,659 passed, the numbers r9 had measured locally).
Verified: scripts/tui_baseline.py test on WSL2 (10 suites) 8,683 passed / 0 failed / 22 ignored,
pi-pager 5,626; pytest on Windows 2,382 passed / 36 skipped, on Linux (WSL2, ext4 clone) 2,383
passed / 35 skipped; ruff check and format --check clean with the venv's 0.16.10 and CI's pinned
0.16.0. Twelve one-line mutations of the new code, each caught by at least one test; one of them
(a Default in front of Ask in the picker) was not seen by the e2e picker test, which only pressed
Down, so it now presses Up too. Only mocks and scripted agents, no real LLM; zypi ran on Linux only.
Not pushed: this branch sits on codex/adr1-pr1-python, which sits on PR #10.
…ds the merge order The ADR1 PR 1 branch and the mode_a branch were local when the plan and the baseline report were written, and said "not pushed" in eight places. They are pushed now: #11 (codex/adr1-pr1-python) on top of #10, #12 (codex/phase1-mode-a) on top of #11. Those places name the PRs instead; the r9 statements that were true when they were written stay, as the r10 entry already says. The r10 entry in the revision log gets the last step. The user asked to sync main to origin and merge the processed PRs. The stack has four (#9, #10, #11, #12), and the upper ones have nothing to merge into until the lower ones are in, so all four are merged, bottom-up, one squash each (the convention of #7 and #8), with the base moved to main before each merge and the tree of main compared with the branch's afterwards. Local main is 9 commits ahead of origin/main; all of them except one older docs commit (superseded by the revised one in #9) are already in #9's branch, so main is not pushed: after the merges it is reset to origin/main, and the old main stays on a backup branch. Docs only: the plan, docs/baselines/tui.md.
…rged (plan r10) PR #12's CI run counted what the local WSL2 run had: 8,683 passed / 0 failed / 22 ignored over the ten suites, pi-pager 5,626, each suite equal to its reference_passed. The baseline report and the comment in the manifest said the next CI run would confirm that; they now say it did, with the run. #10 (after main was merged into its branch) and #11 were green too, 8,659 over the ten suites. The r10 entry of the revision log gets what happened when the stack was merged. #9 was a squash, so #10's branch still held #9's original commits and GitHub reported conflicts in files both sides had changed near each other, in the same way. main was merged into #10's branch with the ours strategy (the tree of main is the tree of #9's last commit, so nothing is dropped), #11 and #12 were each rebased onto the new main before their squash, and after every merge the tree of main was compared with the branch's. A squash of the lower PR of a stack makes the next one conflict unless it is brought up to main first. Docs and a comment in the manifest only; the code is the tree CI already ran on.
zy1233
force-pushed
the
codex/phase1-mode-a
branch
from
October 10, 2026 18:38
bd44289 to
78a5a5d
Compare
zy1233
added a commit
that referenced
this pull request
Oct 12, 2026
…L2 (plan r12, part 3) #15, #17 and #14 are squash-merged (59545c1, d57fd9b, 0efd400). The plan and the execution log say so, and main is now 0efd400 in the current-state text. WSL2 works again. On 0efd400: Linux pytest 2,539 passed / 16 skipped / 31 deselected; `zypi --version` prints `zypi 0.5.0 (0efd400)` (a debug build on ext4, not the CI release-dist artifact); the PTY scripts pass, except one check of smoke.py, which is out of date since #12 (22/23). Section 10.15 gets a part 3 with these results and the leftovers; the "not verified" lines that said "WSL does not start" are brought up to date. Only documents change.
zy1233
added a commit
that referenced
this pull request
Oct 12, 2026
…(plan r12) (#16) ## Summary This PR records the V0.5 release in the plan: what the release is, what must be true before the tag, and what is known to be wrong. It is revision r12 of the plan for the removal of the Rust agent runtime. Only documents change. The milestone is on GitHub: [v0.5.0](https://github.com/zy1233/pi-python/milestone/1). Nothing is attached to it, because the conditions are written in one place (the plan). The three PRs that do the work of the conditions are merged before this one: #15 (`59545c1`), #17 (`d57fd9b`) and #14 (`0efd400`), in that order, each as one squash commit. This branch is rebased onto `0efd400`. - `docs/PLAN/PLAN-RUST-AGENT-RUNTIME-REMOVAL.md`: a new section "发布里程碑 V0.5" (what the release is, six exit conditions, known issues, what is not in it). The status, the next steps and the "not verified" list start from `main` at `0efd400`. Decision 16 is new, and #17 did it. - `docs/PLAN/archive/PLAN-RUST-AGENT-RUNTIME-REMOVAL-EXECUTION-LOG.md`: section 10.15, with the reasons, the figures, the follow-up work, your three answers (part "续 2"), and the merges and the WSL2 checks (part "续 3"). - `docs/baselines/tui.md`: the numbers of the new release measurement. ## The six exit conditions | # | Condition | State | |---|---|---| | 1 | CI is green on the commit that gets the tag | Green: `CI` on `0efd400`, `TUI CI` on `d57fd9b` (`0efd400` differs from it only in `release.yml`). Look again on the commit that gets the tag. | | 2 | The Linux release binary is not larger than v0.4.0 | **Done.** 420,767,096 B (v0.4.0: 470,461,776 B, 10.6 % smaller). Run [38221952524](https://github.com/zy1233/pi-python/actions/runs/38221952524). | | 3 | A dry run of the release build on Linux and macOS arm64 | **Done.** Run [38223353755](https://github.com/zy1233/pi-python/actions/runs/38223353755) built both and published nothing (27 min). The manual trigger is in #14, merged. | | 4 | The PTY scripts on the v0.4.0 binary and on the new binary | **Skipped by you.** V0.5 does not wait for it. Which known issues come from v0.4.0 stays a guess. | | 5 | The release notes | **Done** in #15, merged. They list the macOS binary and say that CI built it and nobody has started it. #17 added one entry and removed one known issue. | | 6 | The tag `v0.5.0` | Yours. | ## After the merges, with WSL2 working again You fixed WSL. These runs are on `0efd400` (details: section 10.15, part "续 3"). - Linux `pytest`: 2,539 passed / 16 skipped / 31 deselected. - `zypi --version` prints `zypi 0.5.0 (0efd400)`, exit 0. That is a debug build on ext4. It is not the `release-dist` file that CI builds. - `agentInfo.version` is `0.5.0` in a venv installed from the merged tree. An older editable install keeps the version it had when it was installed (the old `/tmp/wsl-venv`: 0.2.0). - PTY scripts: `exit_matrix.py` 12/12, `print_exit.py` 4/4, `resume_matrix.py` 10 PASS / 3 GAP (the known gaps), `mode_probe.py` as in #12, `status_line_probe.py` 4/4, `net_audit.py --mock` 13/13 and with `--no-network` 13/13. - **`smoke.py` is 22/23.** The failing check, "status line follows the switch", waits for the text `Second model` followed by a `·`. The screen shows the right label, `Second model`, the session file has the `model_change`, and the next turn answers. The check is out of date since #12. My inference, not tested on an old build: the `·` came from the `auto` flag, which #12 no longer shows in the normal mode. It is a script problem, not a TUI problem. It is not fixed here. The fix is one regular expression. ## Decisions for you - The plan decisions 6, 8, 14 and 15 should be made before the tag. If they are not, they go in the release as known issues. - The date of `[0.5.0]` in the changelog is 2026-10-12. Change it on the day of the tag. The known issues become public release text. ## Not verified - Nobody ran the `release-dist` binary that CI builds; only a debug build of `main` ran. Nobody ran the macOS binary. The changelog, the README and the TUI guide say so. - The two changed `if` lines of `release.yml` have run only on their false side. A tag push runs the true side. - This round did not run the Landlock layer (kernel 5.10 has none), `smoke.py --sandbox`, or the whole Rust test manifest on WSL2. CI ran the manifest on `d57fd9b`: 8,665 passed / 0 failed / 22 ignored. - The plan says that the version overrides of `pi-config` could stop working for a user config that needs a version above `0.5.0`. I found no default config that does, and I cannot see user configs. ## Verification - Link check of the plan and of the execution log: plan 149 local and 81 external links, 47 reference definitions; log 43 local and 58 external links; 0 unused, 0 problems. - `ruff check .` and `ruff format --check .`: clean (nothing in Python changes). - The test counts, run ids and times come from the job logs (`gh run view`) and from the output of the runs on WSL2. - The three merges used `--match-head-commit`. #15 and #17 merged with the tree that CI had tested (#17 was rebased onto `main` after #15, and `git diff` against its tested tip was empty). #14 merged onto a newer `main`, so `CI` ran again on the result, `0efd400`, and passed.
zy1233
added a commit
that referenced
this pull request
Oct 13, 2026
The check "status line follows the switch" waited for `Second model\s+·`. The dot came from the `auto` flag. Since #12 the normal mode shows no flag, so the label is `Second model` and the check failed (22/23) on a TUI that works. The check now waits for the name on the border of the input box, with or without a flag after it: `Second model(?: · [^─╯]*)? ─╯`. The picker row has nothing after the name, so it cannot match. The example in the docstring of status_line() is updated. Checked on WSL2 with a debug build of main (b23e14e): smoke.py 23/23 and `--sandbox workspace` 26/26. Before the change, the same build gave 22/23. The pattern was also checked offline against the saved screens of the failing run. The execution log (section 10.15, part 4) and the revision log of the plan say so. They also record the update of the v0.5.0 milestone text.
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.
Summary
Stacked on #11, which is merged first; this branch is then rebased onto
mainand its base moved there. Three commits: plan 1.P3 (a) (the first option of decision 7 in section 10.11), a docs commit that names the PRs of the two r10 branches, and a docs commit that records the CI runs and how the stack was merged. Section 10.13 ofdocs/PLAN/PLAN-RUST-AGENT-RUNTIME-REMOVAL.mdhas the record.This changes what users see. The Python agent does two things about a tool call: it asks, or it approves everything. The TUI still offered Plan, Auto, a settings "Default" and six
--permission-modevalues. Measured against the realzypion WSL2 (section 10.11):session/set_modehandler, the call fails unseen, and the screen goes on saying "plan".autolikealways-approve, while the screen promises a classifier.--permission-modevalues, onlybypassPermissionsdid anything.All of them err the same way: the user thinks the agent is more careful than it is. This PR stops offering what the agent does not honour. The Python agent is untouched.
Hidden, not deleted.
app/agent_modes.rsholds three capability constants (PLAN,AUTO,DEFAULT, allfalse: "the agent honours this");AppView.{plan,auto,default}_mode_gateand the mirroredPagerLocalSnapshotgates follow them. Test fixtures open the gates, so the hidden paths and their old tests still run, and flipping one constant brings its mode back together with its tests (decision 7 (b)).SessionCreatedreads, the session branch goes throughset_yolo_mode_inner, and both persist. The managed-policy pin keeps Normal and shows the policy's warning. A stale Plan flag leaves through Normal withsession/set_mode(default).plan_moderow and no plan-nudge toggle;permission_modeoffers Ask and Always approve only, and its description no longer mentions Default, Auto or a classifier; the plan nudge tip is not shown.set_plan_modedoes nothing, andset_permission_modeturns Auto / Default into Ask.permission_mode = "auto"or"default"is shown as Ask and the file is left alone; a launch never starts in Auto.--permission-modetakesdefault(no flag) andbypassPermissions(--always-approve).plan,auto,acceptEditsanddontAskexit 2 with a message that says the flag would change nothing and lists the valid values.pi-shell'sPermissionMode::VALID_VALUES(agent-definition frontmatter) is untouched.Decision for you
This is (a) of decision 7, the option you picked (
mode_a). A real Plan mode ((b): the agent grows asession/set_modehandler and a read-only toolset) or a real Auto ((c)) would start from the three constants; neither is done here.Not verified, left
zypiran on Linux (WSL2) only, so macOS and Windows runs of the TUI are not done.permission_mode's search keywords still include "classifier" and "auto";--no-planis still in--help; the embedded upstream user guide (extracted to<home>/docs/user-guide/) still describes the old Plan / Auto cycle; the other permission settings (remember_tool_approvals,default_selected_permission) are not checked against the Python agent (candidate 14); the path that leaves a stale Plan flag is covered by a unit test only.Review guide
tui/crates/codegen/pi-pager/src/app/agent_modes.rs(new)permission_mode_flag_values(),launch_auto(...), the module doc with the three measured factsapp/app_view.rs,app/dispatch/modes.rs,dispatch/prompt.rs,dispatch/mod.rs,dispatch/settings/ui.rs,app/event_loop.rs,app/mod.rsnext_ring_step_without_plan,dispatch_cycle_mode_without_plan), the degrade paths, the plan nudgesettings/registry.rs,settings/defs.rs,views/settings_modal/state.rs,actions/defaults.rsCycleModeaction textapp/cli.rs--permission-modevalue parserpi-pageragent_modes2,cli3,dispatch/tests/modes.rs13,settings_modal3,settings_e2e3) and 3 adjusted old ones (rows_contain_categories_and_settings_through_pr_14,enum_choice_gated_off_covers_voice_and_permission,expanded_description_wraps_to_modal_width)scripts/tui_pty/mode_probe.py,scripts/tests/test_tui_pty.pyscripts/tui_baseline.toml,docs/...,CHANGELOG.md,packages/pi-agent-cli/AGENTS.mdpi-pagerreference_passed5,626; plan section 10.13, A.1, B.6; one CHANGELOG entry under ChangedVerification
bd44289) is green (TUI CI run 38074038191): the ten suites add up to 8,683 passed / 0 failed / 22 ignored withpi-pagerat 5,626 (13 ignored), each suite equal to itsreference_passed, so the local numbers below held on the runner, including thesettings_e2etest that was edited after the local manifest run (that binary alone: 254 passed).cargo testtook 29 m 13 s,Gates + cargo check5 m 46 s, Ruff 10 s, pytest on 3.11 / 3.12 / 3.13 about 2 minutes each. The branch was then rebased ontomainafter feat(agent): honour _meta.sessionId on session/new, say where a session works (ADR1 PR 1, plan r10) #11's squash and one docs commit was added; compared with the commit CI ran on, only three files differ (the plan,docs/baselines/tui.md, and a comment inscripts/tui_baseline.toml).python scripts/tui_baseline.py test, scrubbed environment): exit 0, 10 suites, 8,683 passed, 0 failed, 22 ignored = feat: phase 1 contract work that needs no decision, and the phase 0 gates (plan r9) #10's CI result (8,659) + 24 newpi-pagertests.pi-pager: 5,626.scripts/tests/test_tui_pty.pywithpyte: 23 passed.ruff check .andruff format --check .: clean (227 files) with the project's Ruff 0.16.10 and with CI's pinned 0.16.0.zypi(scripts/tui_pty/mode_probe.py): Shift+Tab goes Normal -> Always-Approve -> Normal -> Always-Approve (a write is asked about in Normal, not in Always-Approve);--permission-mode defaultandbypassPermissionsbehave as before; a savedautoordefaultshows nothing in the status bar and the write is asked about; a savedalways-approveshows "always-approve" and is not asked;plan,auto,acceptEditsanddontAskexit 2; the settings search for "plan" finds no Plan mode row, and the permission picker offers Ask and Always approve.