Skip to content

feat(parity): Wave E — settings persistence, exit_plan_mode, --fork-session, fallback signature strip, hermetic tests (v1.30.0)#119

Open
sweetcornna wants to merge 1 commit into
mainfrom
feat/waveE-parity-batch
Open

feat(parity): Wave E — settings persistence, exit_plan_mode, --fork-session, fallback signature strip, hermetic tests (v1.30.0)#119
sweetcornna wants to merge 1 commit into
mainfrom
feat/waveE-parity-batch

Conversation

@sweetcornna

Copy link
Copy Markdown
Owner

Wave E — claude-code parity 小批量五连 (v1.30.0)

Gap ledger Wave E,五项独立小改动打包成一个 PR(每项都有自己的 TDD 红→绿轨迹)。

E1 — 分层权限设置持久化 + 控制台 persist 答案

问题:PermissionGate.from_layered_sources 零生产调用方——所有部署只从 env 建 gate,durable 规则只能写环境变量;控制台 "always" 授权随会话蒸发。

  • 新模块 corlinman_agent.permission_settings:build_permission_gate()<data_dir>/settings.json(用户层)→ <项目>/.corlinman/settings.local.json(项目层)→ CORLINMAN_AGENT_PERMISSIONS env(最终话语权)叠层;mode/strict 同 precedence;无设置文件时与旧 from_env() 字节等价(含 first-match-wins 默认)。坏文件/坏块跳过不崩溃。
  • ApprovalGate + servicer 默认 gate 构造切到新加载器。
  • 控制台审批新增第三答案 p/persist:等同 always 并经 persist_allow_rule 原子写入(tmp+rename、幂等)用户层 settings;persist 钩子失败降级为会话级授权而非拒绝a 保持会话级(Codex Console permission surface: /permissions, /plan, --permission-mode + interactive approval (v1.23.0) #104——授权不越上下文边界)。

E2 — exit_plan_mode 内置工具

plan mode 拒掉全部 mutating 工具,此前只能人肉 /permissions 放行。现在模型计划就绪自己调用:

  • plan → default 翻转 + 交互审批缓存重置(两个 resolver 来源——set_approval_resolverapp_state.approval_resolver——都重置);非 plan mode 干净 no-op;可选 plan 摘要回显。
  • 二阶边缘预堵(本 parity 程序的教训:权限面每修一处开一个二阶边缘):权限 mode 是 servicer 级全局的,plan 模式下 spawn 工具不属 mutating、子代理可被派生——child executor 现在与递归 spawn / 后台 shell 同款拒绝子代理调用 exit_plan_mode,只有顶层回合能结束 plan mode。
  • skill allowed-tools 控制工具直通名单加入本工具,防 skill 把模型困死在 plan mode。

E3 — --fork-session

  • AgentJournal.fork_session:仅复制 completed 回合、按时间序、消息/tool_calls 保真;in_progress(在别处直播)与 errored(T4.4 面包屑)不复制;单回合损坏 log+跳过;源会话全程只读。Postgres 后端 list_session_turns 为 stub → 干净降级 0-turn fork(docstring 注明,含 limit 截断语义)。
  • 控制台 --fork-session:恢复前铸新 console:<id> key 并 fork,原会话零污染;attach 模式回显不支持说明后继续不 fork。

E4 — fallback 跨模型 thinking 签名剥离

thinking 块签名按模型铸造;OpenAI 兼容入口带入的客户端历史在 model-not-found / 持续过载两条 fallback 分支切模型重放时会把可恢复错误变成硬 400。两条分支重放前剥 thinking/redacted_thinking 块与 signature 键(纯 thinking 内容折叠为 "")。

E5 — 测试封闭性

conftest.py autouse fixture 对非 live 测试清洗 ANTHROPIC_BASE_URL + 6 个代理变量——宿主泄漏的路由 env 曾把 respx-mock 的 provider 套件重路由到真网络造成假失败。live_llm/live_transport 不受影响;测试自身 monkeypatch.setenv 仍生效。

验证

  • ruff / mypy(595 files)/ lint-imports 全绿
  • pytest 非 live 全量:6708 passed, 4 skipped
  • ui typecheck + lint + build 全绿;gen-proto.sh diff-clean
  • 新增测试 30+:13(settings 分层/持久化)+ 8(console 审批含 persist 契约)+ 6(exit_plan_mode 含子代理拒绝、app_state resolver 重置)+ 10(journal fork + console fork)+ 3(签名剥离)+ 2(封闭性契约)

备注

…ession, fallback signature strip, hermetic tests (v1.30.0)

E1 Layered permission settings + console persist answer:
- corlinman_agent.permission_settings: build_permission_gate() stacks
  <data_dir>/settings.json (user) -> ./.corlinman/settings.local.json
  (project) -> CORLINMAN_AGENT_PERMISSIONS env (final word); mode/strict
  follow the same precedence; byte-identical to from_env() when no file
  contributes. persist_allow_rule() writes durable allow rules atomically
  (tmp+rename, idempotent). ApprovalGate + servicer default gates now use
  the loader.
- Console approval gains a third answer p/persist: allow + session cache
  + durable settings rule; a failing persist hook degrades to a session
  grant, never a deny. "a" stays session-scoped (Codex #104).

E2 exit_plan_mode builtin (claude-code parity):
- Model-callable plan->default flip with approval-cache reset across BOTH
  resolver sources (set_approval_resolver + app_state.approval_resolver);
  clean no-op outside plan mode; optional plan summary echo.
- Child tool executor refuses exit_plan_mode: permission mode is
  servicer-global, only the top-level turn may end plan mode.
- Skill allowed-tools control passthrough includes it so a skill can't
  strand the model in plan mode.

E3 --fork-session (claude-code parity):
- AgentJournal.fork_session copies only completed turns chronologically
  onto a fresh key (in_progress/errored skipped; per-turn best-effort;
  source read-only). Console flag mints a new console:<id> key and forks
  before resume; attach mode degrades with a note.

E4 Cross-model thinking-signature strip on fallback:
- Both reasoning-loop fallback branches (model-not-found, sustained
  overload) strip thinking/redacted_thinking blocks and signature keys
  from replayed history — per-model signatures otherwise turn a
  recoverable failover into a hard 400.

E5 Hermetic tests:
- Root conftest.py scrubs ANTHROPIC_BASE_URL + proxy env for non-live
  tests so host routing config can't reroute respx-mocked suites.

Gate: ruff + mypy (595 files) + lint-imports + pytest 6708 passed /
4 skipped + ui typecheck/lint/build + gen-proto diff-clean.
@github-actions github-actions Bot added codex:needs-review A Codex review is needed for the current PR head. codex:review-requested A Codex review was requested or should be running automatically. status: 🔁 re-review loop A fresh Codex review was requested after the latest change or comment. labels Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex:needs-review A Codex review is needed for the current PR head. codex:review-requested A Codex review was requested or should be running automatically. status: 🔁 re-review loop A fresh Codex review was requested after the latest change or comment.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant