Repository navigation
feat(engine): host-armed guardrail prompts + authority-transition notice - #131
Conversation
Pinvou tri-mode safety, two registered fork behaviors:
(a) ask_on_guardrail: a session-level flag carried on Op::ChangeMode
(engine + wire, serde-default-absent) and through LiveRuntimeAuthority
like every other posture field. When a host arms it, the guardrail
faces that fail closed under an autonomous posture raise an approval
request instead, because the host has guaranteed a person can answer:
- non-bypassable holds (rlm/rlm_eval/start_mcp_server) under Full
Access prompt with approval_force_prompt instead of the pre-event
block (the ordinary resolver auto-approves non-bypassable tools under
auto-approve, so the hold is set explicitly);
- repo-law ask holds prompt instead of blocking;
- typed ask-rule Prompts are honored instead of silently ignored;
- sandbox escalation takes the Suggest-path approval;
- the execpolicy deny face (denied prefixes + typed denies, full
matching semantics preserved) becomes an approval carrying the
matching evidence; Never posture stays deny regardless;
- the UI disposition resolver resolves an armed forced hold under Full
Access to the modal (CW-7495 consent-card shape, generalized);
Auto-Review and Never denials stay unconditional;
- child gates mirror the main line: an armed held-call hold routes to
the parent's approval surface, and a delegated typed-ask Prompt
refuses even under inherited auto-approve.
Unarmed (the default) behavior is byte-identical to the pre-flag
doctrine; the existing fail-closed pins are annotated with the armed
exception. Composer ('!') provenance stays un-armed (codewhale-hq#5191).
(b) authority-transition notice: the posture each <turn_meta> block
reported is now recorded; when the host changes mode, posture, or
arming between turns, the next block renders exactly one explicit line
('Authority transition (host): ...; reason: host_authority_change').
Mid-turn flips collapse into one transition; unchanged turns emit
nothing so ordinary blocks stay byte-stable (codewhale-hq#3947 doctrine);
/preview-request reads the same value and stays byte-identical.
New forkguard tests: armed/unarmed deny-face and disposition truth
tables, an end-to-end armed Full Access hold that prompts and denies
as an approval, the armed-child delegated refusal, and the transition
contract (first turn/unchanged/change/arming-only/preview wiring).
Signed-off-by: asto <asto@users.noreply.github.com>
Follow-ups from self-review of the host-armed guardrail work: every armed face claimed by the PR now has a dedicated forkguard pin. - armed repo-law ask hold falls through to a forced approval (unarmed stays a hard block, grant-cache cannot consume it silently) - armed sandbox escalation prompts with approval_force_prompt instead of failing closed; unarmed fail-closed face unchanged - armed typed ask-rule Prompt is honored under auto_approve on the model-provenance mainline (composer face stays unarmed) - the preview manifest hashes the exact authority-transition line the live wire body sends; no pending change keeps previews byte-stable - installing a turn authority never rolls back published arming - apply_change_mode status suffixes report arming changes Also merges the duplicated composer-path arming comment. Signed-off-by: asto18089 <asto18089@126.com>
asto18089
left a comment
There was a problem hiding this comment.
Reviewed fresh from scratch at head 41eab46f2 (7 parallel lanes: claims/completeness, unarmed-equivalence + wire/persistence compat, correctness deep-dive, perf/elegance/boundaries, smuggling/DCO, test quality, parent registration codewhale-hq#809; all gates re-run locally and mutations re-executed on a dedicated worktree). Base f1eb10efc equals the current pinvou3-clean tip — no rebase needed, MERGEABLE (BLOCKED is only the gate bot's /lgtm hold).
Verdict
Approve direction. The PR is genuinely useful and root-caused: an external host running the engine autonomously currently gets hard denies (non-bypassable holds, repo-law, sandbox escalation, execpolicy denies) or silently swallowed typed-ask prompts with no way to say "I have a human at the card"; the fix adds an explicit host-intent signal consumed at each decision gate with approval_force_prompt defending every new prompt against the codewhale-hq#3866 auto-approve reversal and grant caches. The transition notice is computed from the same engine state the blocks report and is byte-stable otherwise. 0 blockers, 0 MAJOR code defects found. Three P2-grade items below (all fail safe), plus minors.
Verified
- Gate reproduced:
cargo test -p codewhale-tui --lib --locked forkguard_ -- --test-threads=1→ 253 passed / 0 failed at head; all 13 new pins present exactly once, correctly prefixed. - All 13 body claims verified TRUE against code; two are PARTIAL (findings 2–3). Unarmed equivalence holds: every production
ask_on_guardrailsite is a session-flag gate, a literalfalse/None, or an inert carrier, and each new branch collapses to the base expression at the default; wire compat is safe in both directions (serdedefaultinbound; nodeny_unknown_fieldsin the wire Op, so old engines drop the unknown field; roundtrip fixture updated);SavedSessiondoesn't carry the flag (consistent withauto_approve) so pre-PR session files load identically. - Mutation probes reproduced RED exactly as the body claims: deny-face armed-condition disabled →
forkguard_armed_deny_face_becomes_guardrail_prompt_unarmed_stays_blockred; never-rollback patch →None→forkguard_turn_authority_install_never_rolls_back_published_armingred. The body's "如实记录" of the renderer-only tests is accurate, and the preview-hash test does bite the compare site. - Smuggling CLEAN: 2 commits,
.rsfiles only, every hunk serves (a)/(b)/the declared commit-2 hardening; ~30 mechanical param lines verified mechanical (the relocatedauto_review_auto_resolves_hallucinated_question_without_promptingbody is byte-identical); per-commit DCO sign-offs match author identities; no private data. - Registration (parent PR codewhale-hq#809) satisfied: OPEN against main, pins this exact head,
EXPECTED_COMMITS=88independently verified via rev-list; 13/13 test-name pins match; the copy pinAuthority transition (host): {} -> {}; reason: {}is byte-exact againstrender(); static replay of the guard's grep loop: 220/220 CodeWhale fingerprints green at head. Genuinely new behavior — noask_on_guardrailor(host)transition line at base and no upstream duplication. - Perf clean: unchanged path is allocation-free O(1); no hot-path (streaming/token) changes; no unarmed-path formatting added.
- Correction to prior review lore: the "inherited broken T3 pins at fork-guard.sh:223-224" flag from the #129 review is stale — the MCP_REGISTRY pins were re-pinned in #125's 2026-10-28 batch;
fork-guard --fasthas no inherited red.
P2 findings (fix here or fast-follow; all fail safe)
- [P2][test-net] The transition detector's recorder line is unpinned — deleting it keeps the whole suite green (mutation-verified). Removing
self.last_turn_meta_authority = Some(current_posture);(crates/tui/src/core/engine.rs:5778) leaves 253/0 green: from the second turn on no transition ever renders and feature (b) dies silently. The preview-hash test pre-stuffslast_turn_meta_authorityand runs a single turn, so it pins the compare but not the memory update; the body's mapping ("preview 哈希测咬检测器") is true for the compare lines but incomplete for the detector as a whole. A two-turn E2E (flip → line renders; unchanged posture → byte-stable) or a unit pin on the recorder closes it. - [P2][doc] session.rs:436-440 overclaims "the deterministic floor" as an armed-converted face. The built-in safety gate is hard-Block with no arming consult anywhere (engine.rs:8427-8439 collapses
AskUser+built_in_safety_gatetoBlockunder Auto/Never/Bypass; turn_loop'sBlockarm appliesblocked_errorunconditionally), and the PR body itself never claims this face. Correct the doc (or actually convert the face, with a pin, if that was the intent). - [P2][dead arm] The armed subagent held-call hold mirror is unreachable. subagent/mod.rs:14886-14893 routes an armed
AutoReviewPlanDecision::ForcePromptto the parent approval surface, butForcePromptnever occurs in the Bypass arm it lives in (the plan decision collapses safety-gate holds toBlockunder Bypass). The body presents "武装 held-call hold 路由到父审批面" as a live behavior, yet only the delegated typed-ask refusal is reachable. Either consult arming before the Bypass collapse (making the mirror real, with a test) or annotate the arm as fail-safe future-proofing and adjust the body.
P3 findings (fix, disclose in 已知边界, or accept)
pending_authority_transitionis never cleared after the turn-start message consumes it, so mid-turn turn_meta blocks re-render the same line: steers (engine.rs ~4098), step-budget soft-landing (turn_loop.rs ~1724), budget exhaustion (~1763) — the line can appear in several messages of one turn.take()after the turn-start build fixes it.- Preview reads stale pending: a
ChangeModearriving between turns makes/preview-requestomit the line the live turn will send — divergence in exactly the case the feature targets; theTurnMetadataSnapshotdoc ("preview stays byte-identical") is overstated, and the preview-hash test masks it by injecting both fields manually. Recompute the compare in the preview builder (same O(1)) or scope the doc claim. - Turn start records
last_turn_meta_authoritybefore the late NotStarted early-return (engine.rs ~5968): a send failing exactly there records a posture that is never rendered, so the next change reports the intermediate step. Rare, cosmetic. LiveRuntimeAuthority::from_fieldsnow stores raw fields where base normalized viaagent_approval_mode_for_turn(auto_approve ⇒ Bypass). For non-canonical input (auto_approve=true,approval_mode≠Bypass) the recorded snapshot transiently diverges from the session until the nextrecord_appliedself-heals; unreachable with current callers. Declare it or normalize at intake.- The armed escalation catch-all converts holds for any non-Suggest posture, including Auto-Review — outcome preserved (the resolver's
AutoDenyAutoReviewprecedes the armed branch; verified) but the receipt wording differs, and nothing pins it. Add a pin or exclude Auto explicitly. - Latent mixed-host trap:
approval_routing.rsbuilds the UI authority viafrom_effective_fields(armed forever false), andexec_agent.rs~753 auto-approves everyApprovalRequiredincludingapproval_force_promptevents. Harmless today (no in-crate host arms), but an invariant comment/guard at both sites would keep a future arming toggle from silently auto-denying (TUI) or auto-running (headless) armed holds. turn_meta_block_stays_within_measured_ceilingpassesauthority_transition: Noneonly; a transition-bearing block adds ~148B against the measured ceiling, unmeasured.- 已知边界 additions worth making: arming is process-memory only (lost on restart, like
auto_approve— the host must re-arm); and the session.rs "unarmed behavior is byte-identical" invariant should be scoped to posture-unchanged sessions, since unarmed sessions whose mode/approval posture changes do gain the transition line (that's feature (b) working as intended, but the invariant as worded contradicts it).
Nits (no action required): double registered_tool_blocked_in_full_access call in turn_loop ~3950; AuthorityTransition could derive Copy; publish_turn_authority's 8 params could take LiveRuntimeAuthority and drop one too_many_arguments; the armed escalation card drops the model justification the Suggest card shows; unbounded recv() in the status-suffix test helper would hang rather than fail; no disarm round-trip pin; transition compare uses raw session.approval_mode while the preview standing line renders the effective chip (cosmetic divergence under auto-approve remap); three arming phrasings across surfaces — pin each separately in the parent register if kept.
Every discrepancy above fails in the deny/silent direction — nothing arms an execution path without a human event. Fixing 1–3 (one test, one doc paragraph, one arm decision) plus the two known-boundary additions would make this an easy approve. Engine-side behavior, registration, and provenance are all sound as-is.
Review follow-ups on the host-armed guardrail work: - the authority transition is computed when the turn's metadata block is built (after the NotStarted gate, so a failed send can no longer record a posture the model never saw) and consumed by that block only, so steers and mid-turn runtime notices stop re-rendering the same line; - the preview manifest recomputes the transition from the recorded posture instead of reading the consumed leftover, keeping the preview byte-faithful when a host flips posture between turns; - armed face conversions (execpolicy deny face, repo-law holds, sandbox escalation) now exclude the Auto-Review and Never postures explicitly, making their unconditional denials true at every armed face, and the armed escalation card carries the model's justification; - annotate the fail-safe child ForcePrompt arm and the always-unarmed UI approval authority, document the from_fields raw-field contract, and correct the session.rs face list (the deterministic floor stays a hard deny when armed); - pin the detector end to end (record + consume + second-turn byte-stability) and the armed Auto-Review deny face. Signed-off-by: asto18089 <asto18089@126.com>
|
Fix wave pushed: Behavior fixes
Docs & tripwires
Tests (finding 1 — the mutation-proven hole)
Gates at |
asto18089
left a comment
There was a problem hiding this comment.
复审(fresh re-review)@ 04c1f7843 — 8 评审道 + 全部门禁复跑 + 2 条突变探针
从零重审,不引用此前结论。Rebase 核对:本 PR merge-base == pinvou3-clean tip f1eb10efc(公开维护头未动);父仓 codewhale-hq#809 base == main 3cec34e2b——双双 MERGEABLE,无需 rebase。
结论:approve 方向。本 PR(#131)范围 0 blocker / 0 major;登记侧(codewhale-hq#809)发现 1 个 major,须在合并前修复(见下)。
覆盖与本机门禁(全绿)
- 8 条独立评审道:armed faces(authority.rs/approval_routing/exec_agent)、transition+turn_meta(engine/turn_loop/preview/session)、plumbing+subagent parity、测试网突变分析、smuggling/DCO/数字审计、perf+并发+panic、架构+上游对比、父仓登记一致性。
- 门禁(本机 @
04c1f7843复跑):forkguard_254/0(跑两遍,含全部 14 条新钉——以编译产物--list计 254 核实);--features benchmark-eval-controls6/0;preview 60/0、turn_metadata 24/0、change_mode 4/0、escalation 10/0。父仓 @09393c9707(treedcd7fe5ea本地重建字节一致)fork-guard.sh --fast指纹层全过(行为名实测 269)。 - 突变探针(逐字节还原,收尾
git status干净 + 全套 254/0 复绿):repo-law Auto/Never 豁免子句删除 → RED(该豁免有钉);repo-law 武装臂整臂删除 → RED。 - smuggling:19 文件逐 hunk 分类((a)/(b)/测试钉/注释),无一处越界;无 Cargo/CI/版本/配置改动;3 提交 DCO 齐全、提交信息与内容逐条相符。
声明核对((a)(b) 与全部登记数字)
| 声明 | 判定 |
|---|---|
(a) 五个武装面(forced hold + approval_force_prompt 显式置位反转 codewhale-hq#3866、repo-law、typed-ask、sandbox escalation 带 justification、execpolicy deny face 保匹配语义)逐条实现,Auto-Review/Never 在全部位点无条件拒 |
TRUE(逐位点引证核实) |
会话授予/remember 不能静默消费武装 hold(!approval_force_prompt 门 + E2E 反消费钉) |
TRUE |
composer ! / ACP / 子代理 deny face 硬编码不武装;UI approval authority 恒未武装 + 两处 tripwire |
TRUE |
| 子代理 typed-ask 武装下继承 auto-approve 仍拒(Bypass ForcePrompt 臂为 fail-safe 前瞻,注释与代码一致) | TRUE |
(b) 检测点在 NotStarted 早退之后、record→render→一次性消费无 await 窗口、steer/预算不复渲染、未变轮逐字节稳定、preview 与 live 哈希一致(preview.rs 走同一 authority_transition_between、非读残留)、never-rollback、状态后缀三态(armed/disarmed/None 无后缀) |
TRUE(全部逐条核实;消费窗口 6054→6077 无 await/return 已验证) |
| 未武装逐字节等同 + 唯一 unarmed 协议增量 = 转换行(session.rs 如实披露) | TRUE |
数字:89 提交 / 87 签名(2 个未签名 squash 为既有 #107/#116)/ 269 forkguard 名 / 254 实跑 / 15 指纹(14 测试钉逐字存在 + 1 模型可见行字节精确)/ EXPECTED_HEAD/EXPECTED_COMMITS / drift 526 +93516 −39313 |
全 TRUE |
| 「纯底库行为提交」(登记只在父仓) | TRUE |
发现
P2 minor(不阻塞;建议随合并或紧随修复)
- 子代理 doc 过度声明 repo-law 镜像 —
subagent/mod.rs:2500-2508末句 "children never run what the armed main line would prompt on":子代理委派路径完全不评估 repo-law(subagent/mod.rs零repo_law引用,base 亦然——repo-law 本就是主线 face),故武装会话里子代理对 repo-law 保护路径的委派写会静默执行(武装主线会弹卡)。这是既有边界不是回归,但该句把属性声明到了子代理不设防的面上。建议:把句子收窄到子代理实际设防的 face(typed-ask / execpolicy deny),或把 repo-law 镜像进子代理门。 - 武装静默丢失竞态 —
handle.rs:32-63同步发布 op 快照、publish_runtime_authority(:89-96)全量替换;turn install 发布ask_on_guardrail: None(runtime_threads.rs4 处)。宿主 ChangeMode 发布与引擎 drain 之间若插入 turn install,Some(true)被替换为None,drain 应用快照后武装静默丢失(无事件、无回执)。其余字段无此问题(install 镜像已应用态,None 无害);唯独 arming 的 None 语义是 "不变"。修复方向:turn install 发布携带 live arming,或 drain 合并 None 而非覆盖;至少注释披露。窗口窄,宿主重发即愈。 - preview 转换姿态来源与 live 不一致 —
preview.rs:422-424姿态取self.session.approval_mode,而同一 preview 的 standing 行取input_policy.approval_mode_for_session()(:417),live 比较又发生在apply_runtime_mode_policy安装计划姿态之后(engine.rs:6046-6049)。当PreviewRequestInputs携带与 session 不同的假想姿态时 preview 哈希 ≠ live 哈希——恰是测试 13 钉的属性;内置 TUI 的 FIFO 时序今日掩之。修复:姿态改用input_policy.approval_mode_for_session()。 switch_provider重启引擎静默掉武装 —Engine::new重置为 false,无任何宿主可见信号。session.rs 披露的是"恢复/重启",运行中换 provider 并非重启;建议掉武装时发事件或在状态里回执。- 残余测试缺口(探针后收敛) — (i) composer-
!武装反例无钉:engine.rs:2575把false换成 session 旗标的突变可存活(既有 yolo 测试 unarmed 看不见);(ii) escalation 位点的 Auto/Never 豁免未探针验证(repo-law 位点已实测 RED;escalation 结构不同,建议补一条 armed-Auto 恒拒用例或自跑探针确认)。lane 评审静态分析还报了 repo-law 豁免缺口,已探针证伪(RED)——测试网比静态分析以为的更强。 - 发送失败后的姿态推进 —
engine.rs:6058先记录后发送;提供方 HTTP 失败时已记录姿态模型可能未收到(append-only history 通常随下一成功请求兜底;edit-last-turn 截断会丢)。PR 对此的声明只 scoped 到 NotStarted,措辞准确;可选修复:终态失败回滚last_turn_meta_authority。 from_fields不再归一化(披露于 doc comment)— 非规范发布会在permission_snapshot()短暂暴露 raw 形态,record_applied自愈;方向 fail-closed、在库生产者全规范。记录备查。
已证伪的评审道指控(如实记录,供后续评审参考)
- "body 的 254 过时,head 实为 268":不成立。编译产物
--list= 254(含全部 14 条新钉);268/269 是源码定义计数(含既有 Windows-gated 测试与 config crate),与 "254 过 / 269 名" 双双自洽。 - "
Never+auto_approve=true违反无条件拒绝不变量":不成立。agent_approval_mode_for_turn(authority.rs:408-417)把auto_approve=true一律折叠为 Bypass,组合态经唯一会话写者apply_change_mode不可达;规范态下auto_approve==true ⟺ approval_mode==Bypass。
P3 nit
- arming 谓词在 5 个位点 3 种写法(resolver 结构排除 /
armed && !matches!(Auto|Never)/!auto_approve || armed)——可提armed_guardrail_face(armed, mode)单源化,防未来新 face 漏排除。 TurnMetaPosture字面量 live/preview 双处手写(加第 4 字段时须双改;哈希测试只在校验运行时兜底)——提current_turn_meta_posture()helper。- 武装 escalation 臂与 Suggest 臂主体重复(仅后缀差);3 处新增
too_many_argumentsallow(可收敛为 params struct,沿既有 idiom 亦可);turn_metadata_block读pending_authority_transition生产不可达(潜在双渲染只在未来有人窗外置位时——建议加不变量注释);ops.rs:429"when this op is processed" 措辞(实际 drain 时机更早,效果相同);zh/en 登记 (a) 节两处措辞差(en 丢 "Auto-Review" 与 fail-safe 括注,en 是不完整不是错误);提交 1 与 2/3 作者身份不同(各自 DCO 自洽)。
父仓 codewhale-hq#809 侧 — [MAJOR] 合并前必须修:登记漂移三件套
THIRD_PARTY_NOTICES.md:10、docs/fork-policy.md:3/11/12/14、docs/fork-policy.en.md:3/10/11/13 仍写候选 41eab46f2(88 提交 / 86 签名 / 268 名 / 525 文件 +93293/−39351 净 +53942,drift 区间 dcd4c200f..41eab46f2),与同一棵树的 gitlink 04c1f7843、fork-guard.sh、fork-modifications zh/en(89/87/269/526/+93516/−39313)直接矛盾,且文件头自称 "最后更新 2026-10-31 / Updated: 2026-10-31"。fork-policy.md 是 AGENTS.md 指定的 single source of truth。guard 门禁仍绿(fast 层不校验这三处散文),但登记 PR 的本义就是防漂移——请在 codewhale-hq#809 重钉这三处(至 04c1f7843 或合并后 squash 头)再合。其余登记面(15 指纹逐字、EXPECTED_*、gitlink、safety-modes.md 逐节比对代码、zh/en 主体)全部核实无误。
上游对比(记录,不阻塞)
上游 Hmbown 无等价机制(ask-rules-survive-bypass 之外无 host-arming、无模型可见转换行);与三处上游已收口教义(codewhale-hq#6471 fail-closed、codewhale-hq#6310 server-owned posture、codewhale-hq#3790 mode-is-sole-authority)的张力 PR 内已如实标注,属有意的教义反转而非误实现。上游 2026-11-01 e34f207d2 已把 authority 抽到 crates/engine——下次同步 armed faces 需重接线;本能力是可复用底座关切,届时值得随上游 issue 提出(upstream-first 边界)。CW-7495 锚点公开不可核验(若为内部 spec 号则无妨)。
Disposition:#131 approve 方向可合(M1 建议顺手修);codewhale-hq#809 修完三件套漂移后合。
…d posture Re-review follow-ups on the host-armed guardrail work: - the authority publish path replaces the whole snapshot, and the op loop applies the snapshot rather than the queued op payload, so a posture-only publish (a turn install, or a ChangeMode op carrying no arming) racing a host arm silently dropped the request. `None` now means "keep" at the publish layer too, mirroring apply_change_mode; only an explicit Some may move arming; - the preview recomputes the transition from the same posture helper as the live compare, so hypothetical preview inputs render exactly the body the live turn sends instead of reading the stale session state; - the subagent arming doc now scopes its claim to the faces the child gate actually evaluates (repo-law stays a main-line face), the /provider switch documents that respawning drops session arming, and the ops.rs arming wording matches the drain semantics; - pin the publish stickiness end to end, the armed composer hard-deny face, the armed Auto-Review escalation fail-closed face, and the preview planned-posture parity (preview hash equals the live wire body hash for divergent inputs) - each verified red under its mutant. Signed-off-by: asto18089 <asto18089@126.com>
asto18089
left a comment
There was a problem hiding this comment.
Fix wave pushed: 5c13fc561(re-review 5731536075 的可修复项全部收敛;登记随父仓 codewhale-hq#809 重钉至 ca92bc54f,gitlink → 5c13fc561)。
行为修复
- 武装发布粘滞(re-review M2):publish 路径整体替换快照,而 op 循环应用快照而非 op payload——turn install 或无武装 ChangeMode 与宿主 arm 竞争时会静默吞掉请求。现在
None在 publish 层同样表示"保持"(与apply_change_mode同教义),仅显式Some可动武装。新钉forkguard_pending_arming_survives_arming_free_authority_publishes突变验证转红。 - preview 计划姿态(re-review M3):preview 转换改用与 live 比较同一
turn_meta_posture_for助手(planned approval 来自输入、live arming 来自会话);假想姿态输入下 preview 哈希 == live 线体哈希,新钉以 live parity 断言突变验证转红(首版"双构建不等式"钉曾被本机突变存活、已重设计——如实记录)。
文档/钉子
- 子代理 arming doc 收窄到子代理实际设防面(repo-law 保持主线 face,M1);
/provider重启掉武装补 tripwire(M4,app 池 spawn 时重放武装旗标);ops.rs 措辞对齐 drain 语义;turn_metadata_block的 pending 读取补 await-free 不变量注释;比较点注释补发送失败 residual 边界(M6)。 - 新增 armed composer 硬拒钉(deny-rule 命中经 Prompt 臂放行 deny 命令的突变转红)、armed Auto-Review 升级恒拒钉(豁免子句删除转红)(M5)。
突变核查:4 条新钉全部先证转红再逐字节还原;repo-law Auto/Never 豁免 与 武装臂删除 两条探针再次确认 RED。工作树收尾 git status 干净。
门禁(5c13fc561):forkguard_ 258/0;benchmark 6/0;preview 61/0;turn_metadata 24/0;change_mode 4/0;escalation 11/0;cargo fmt --check 干净。登记数字重测:90 提交 / 88 签名 / drift 526 files +94033/-39383(净 +54650)/ 273 行为名 / 19 指纹。
未采纳(如实):arming 谓词 5 处 3 形态的单源化 helper(对突变验证过的安全面做纯美化重构,收益不抵风险);from_fields 非归一化(已披露、自愈);提交 1 与 2-3 作者身份差异(重写历史不值)。
asto18089
left a comment
There was a problem hiding this comment.
Fresh re-review(第三轮,9 条并行车道,钉在 5c13fc561)
覆盖:9 条车道——(a) 12 项主张逐条对码、(b) 9 项主张逐条对码、arming 状态机与竞态深审(10 场景 + 不变量审计)、走私逐 hunk 对账(含 −107 删除行逐项核对与两个被搬移存量测试的逐字节 diff)、变异测试 14 探针(含 bonus 探针)、设计质量、性能与健壮性、父仓 codewhale-hq#809 登记一致性(283 条指纹逐条验证 + guard 实跑)、门禁复现。本轮从头独立重审,不沿用前两轮结论。
总判定
- (a) 12 项主张:10 TRUE,2 PARTIAL(均为表述口径而非代码缺陷:①「UI disposition 武装后弹卡」实为 resolver 能力——
approval_routing.rs刻意恒未武装(tripwire 注释在案),在库 UI 路径不产卡,武装卡由外部 wire 宿主消费Event::ApprovalRequired { approval_force_prompt };②「未武装逐字节等同」按设计不含 (b) 的 transition 行(session.rsdoc 已如实披露这处唯一 unarmed delta))。 - (b) 9 项主张:8 TRUE,1 PARTIAL(NotStarted 早退不再吞 transition:修复真实、位置正确,但全套测试 0 例行使该路径 → F3)。
- 走私:CLEAN(19 文件全部 hunk 可归入 (a)/(b)/测试钉/doc-tripwire;−107 删除行逐项有对应替换;两个被搬移存量测试正文逐字节一致;无 Cargo/依赖/serde rename/feature/cfg 改动;
crates/外零改动)。 - 变异测试:SOUND(14 探针全部转红或被证明为无效变异;无死 pin、无弱断言。1 处探针 GREEN 经代码证明为无效变异:force_prompt 的
|| armed_full_access_hold被registered_non_bypassable逻辑蕴含,删除不可达差异——该析取项属冗余防御代码,无害)。 - 门禁:7/7 精确复现——forkguard_ 258/0、benchmark 6/0、preview 61/0、turn_metadata 24/0、change_mode 4/0、escalation 11/0、fmt clean。(该分支无 CI checks 报告,以上为本机复现。)
- 父仓 codewhale-hq#809 登记:CONSISTENT——gitlink、EXPECTED_HEAD、EXPECTED_COMMITS=90、283 条指纹逐条命中(19 条新 T2 全中)、273 个 forkguard 名、526 files +94033/−39383、§8 census 18 条、zh/en parity、guard
--fast实跑绿——但发现 1 项文档级 MAJOR(F4)。 - 无需 rebase:merge-base == pinvou3-clean tip
f1eb10efc。
发现
F1 · MAJOR — runtime_threads.rs:9462 审批 pump 丢弃 approval_force_prompt,武装 hold 会被静默自动批准(修复波 3 已带)
pump 以 EngineEvent::ApprovalRequired { id, tool_name, description, intent_summary, .. } 解构丢弃该字段(全文件 0 处引用),authority.auto_approve == true(Full Access)时径直走自动批准:approval.decided { auto: true } → approve_tool_call——无任何人类决定,被守卫动作直接执行。引擎侧 armed face 的注释承诺「force_prompt 阻止 session grants 与 posture shortcut 静默消费本事件」,该承诺对此在库消费者不成立。
可达性核查:当前随附 producer 全部发 None/false,daemon 侧两处 Op::ChangeMode 均为 daemon 自组装(恒 ask_on_guardrail: None),无 client→op 透传——今日不可达(初判 BLOCKER,据此降为 MAJOR)。但本 PR 的功能契约正是「外部宿主经 wire 武装」:任何宿主武装入口抵达该部署的瞬间洞即开;且 PR 已在 exec_agent.rs:754-759 为同类缺口放置 tripwire,唯独漏掉这个在库消费者。附带事实:前存的 non-bypassable 面(CW-7495,force_prompt 先于本 PR 存在)在 pump 下同样被静默自动批准。
修复:绑定字段,auto_approve && !approval_force_prompt 才走自动路径,否则落入既有真人决策路径(register pending → approval.required → 等待决定;Auto-Review fail-closed 臂原样保留)+ tripwire 注释 + forkguard 钉。
F2 · MINOR — ChangeMode status 后缀按「有无 carrier」而非「arming 是否实际变化」门控:首次 turn 之后每次普通 posture 切换都谎称 "(guardrail prompts disarmed)"(修复波 3 已带)
apply_change_mode 的后缀分支是 if let Some(armed);生产上 op 循环应用的永远是经 record_applied_runtime_authority 回填的快照(Some(session.ask_on_guardrail)),后缀分支因此恒 Some——每次普通模式切换的状态行都追加武装宣告。既有钉走的是直调 None 路径(生产不可达),给了假信心。修复:仅在 arming 实际翻转时追加后缀,钉扩展覆盖「carrier 回显未变值」的生产形态。
F3 · MINOR — NotStarted 修复((b) 主张 6)无测试行使(修复波 3 已带)
04c1f7843 把比较移到 NotStarted 早退之后,结构正确,但「turn 启动失败不吞 transition → 下个成功 turn 渲染」0 测试。修复:补回归钉。
F4 · MAJOR(文档级,父仓 codewhale-hq#809)— 概览段仍写评审候选为 04c1f7843
docs/fork-modifications.md L73 与 fork-modifications.en.md L75「指向评审候选 04c1f7843df…」同文件内即与 EXPECTED_HEAD=5c13fc561 矛盾;「第 0 层仅在实际检出 04c1f7843 时为绿」同样过期(zh L76/L455、en L77/L337,共 6 处)。中间波 125fa92b7 的遗留,终波未同步。登记文档是 fork 基线 single source of truth,合并前必须改齐。
F5 · 观察(pre-existing,非本 PR 引入)— tools::subagent 存在确定性顺序依赖失败测试
child_permission_gate::auto_review_guardian_denial_refuses_the_call_with_a_receipt 在同模块 allow 兄弟测试之后运行时,guardian deny 决策不被强制(命令实际执行,tests.rs:22366 panic)。基线 f1eb10efc 复现(12/1)→ 非本 PR 引入,建议独立 follow-up(疑似 guardian mock 状态跨测试泄漏)。
Minor / NIT(记录,不阻断)
LiveRuntimeAuthority::from_fields去掉归一化链(998ffe27e 引入的 refactor churn;全部现役 producer 发送规范形,无可达行为差异;正文已披露,建议 PR 描述补一句)。- armed repo-law 卡缺 arming 标记(其余三个武装面均有 "ask-on-guardrail is armed" / "(armed guardrail approval)"),宿主无法从卡面区分。
- typed-ask armed 臂(
!auto_approve || armed)与其它三面的「源头排除 Auto/Never」形状不一致:armed Auto/Never 会话会收到一个注定被 resolver 自动拒绝的事件(fail-closed,仅噪音;承袭 codewhale-hq#3790 既有形状)。 - armed Full Access 卡在
description为空时文案以 " — " 开头。 - 子代理 registry 构造时冻结 arming;会话中途武装不传播到已在运行的 child(新 spawn 携带)。
- 解除武装不唤醒已 pending 的审批等待(fail 方向安全,turn 挂起至 cancel/interrupt,可接受)。
- narrowing 收据行与 (host) transition 行的 "Authority transition" 前缀同块可共存(字符串可区分,留意混淆)。
四维结论
- 有用且完备:是——(a)/(b) 落地完整、根因清楚、mutation-verified 钉网致密;F1/F2/F3 由修复波 3 收敛。
- 优雅:是——复用 ChangeMode/LiveRuntimeAuthority/审批卡通道,无并行机制;
turn_meta_posture_for单一算法源;三处 tripwire 准确。残留的谓词三处内联重复与卡片文案复制为可后续收敛项。 - 走私:CLEAN。
- 缺陷:F1 为唯一实质缺陷(pump 消费者契约洞),修复波 3 闭合;F5 为 pre-existing 顺序依赖测试,独立跟进。
04c1f78 made AuthorityTransition Copy but left two .clone() calls on Option<AuthorityTransition> in the turn-meta builders. The fork CI clippy gate (-D warnings, no --all-targets needed to see them) fails on both with clippy::clone_on_copy. Copy semantics make the removal byte-identical; forkguard_ suite stays 258 passed / 0 failed and cargo fmt --all -- --check stays clean. Signed-off-by: asto <asto18089@126.com>
…al arming flips Fresh re-review (COMMENT 5732557068) findings F1-F3: the runtime-threads approval pump dropped approval_force_prompt and auto-approved forced holds under Full Access postures; the ChangeMode status suffix keyed off the mere presence of the ask carrier and announced '(guardrail prompts disarmed)' on every ordinary posture switch; and the NotStarted ordering fix had no test exercising it. Three new mutation-verified pins. Signed-off-by: asto18089 <asto18089@users.noreply.github.com>
asto18089
left a comment
There was a problem hiding this comment.
修复波 3(收敛 fresh-review 发现,钉在 f972884e5)
按 fresh re-review(COMMENT 5732557068 @ 5c13fc561)逐条收敛;波已 rebase 到作者新推的 clippy 修复 10b28ef5f 之上。
F1(MAJOR)— runtime pump 尊重 approval_force_prompt
runtime_threads.rs审批 pump 绑定该字段;auto_approve && !approval_force_prompt才走自动批准路径,forced hold 落入既有真人决策路径(register pending →approval.required→ 等待决定;Auto-Review fail-closed 臂原样保留);附 tripwire 注释。- 行为影响披露:前存 non-bypassable 面(CW-7495)在 pump + Full Access 下此前会被静默自动批准,现按 CW-7495 契约浮给客户端决定——与内建 TUI 同面行为一致。
- 钉:
forkguard_pump_never_auto_approves_a_force_prompt_hold(Full Access 线程 + force_prompt 事件 → 必须 pending 等真人、无 auto decided;外部 deny 才传导)。突变验证:删!approval_force_prompt守卫 → 该钉转红(hold 被自动批准)。
F2(MINOR)— status 后缀只在 arming 实际翻转时追加
apply_change_mode以arming_change(实际翻转)替代「有无 carrier」门控后缀;生产形态(op 循环应用回填快照,恒Some)下普通 posture 切换不再谎称 "(guardrail prompts disarmed)"。- 钉扩展:
forkguard_apply_change_mode_status_suffixes_report_the_arming_change增补「carrier 回显未变值不得宣称翻转」断言(生产形态)。突变验证:后缀回退 carrier 门控 → 该钉转红。
F3(MINOR)— NotStarted 顺序补回归钉
- 新增
forkguard_a_not_started_turn_neither_consumes_nor_records_the_transition:无 client turn 失败于块构建之前 → 记录不动、pending 不被吞 → 下个成功 turn 渲染 transition 行。突变验证:在 NotStarted 路径注入 posture 记录(模拟修复前行为)→ 该钉转红。
门禁(本机复现 @ f972884e5)
forkguard_:260 过 / 0 挂(258 + 2 新钉;后缀钉原位扩展);preview 61/0;turn_metadata 24/0;change_mode 4/0;escalation 11/0;--features benchmark-eval-controlsforkguard_benchmark_ 6/0;cargo fmt --check干净。- 本地 clippy 1.99
-D warnings在10b28ef5f与f972884e5报同样的 5 条预存 lint(too_many_arguments ×4 / collapsible_if ×1,全部在 PR 之前的代码上;与 fork CI 工具链口径存在漂移),本波未新增任何 lint。
其余发现处置
- F4(父仓 codewhale-hq#809 登记文档 6 处
04c1f7843陈旧候选 SHA):随父仓登记批改齐(另行推送)。 - F5(
tools::subagent顺序依赖失败,基线f1eb10efc复现):pre-existing,不在本 PR 打包修复,建议独立 follow-up。 - Minor/NIT(armed repo-law 卡标记、typed-ask 臂形状、
|| armed_full_access_hold冗余、空 description " — " 前缀、child registry 冻结 arming 等):记录在案,留待后续收敛;from_fields非归一化已如实披露(见 PR 描述补充)。
登记随父仓 PR codewhale-hq#809 同步
gitlink/EXPECTED_HEAD→f972884e5、提交数/签名数/漂移数字重测、T2 指纹 19→21(+2 新钉)、safety-modes §8 census 18→20、zh/en parity、六处陈旧候选 SHA 改齐。
|
Ready for review. Fix wave 41eab46 -> 04c1f78 -> 5c13fc5 plus two follow-ups re-verified locally; all numbers below are from the current head f972884 (including the F1-F3 pump/status-suffix fix pushed at 06:11Z). Local re-verification at f972884 (single-threaded forkguard suite, shared target dir):
Diff review of the fix wave against 41eab46 confirms the six minors landed with no smuggled changes: transition computed and consumed at the metadata-block build only; CI status (transparency note): GitHub Actions is currently not creating runs for |
asto18089
left a comment
There was a problem hiding this comment.
Round-4 全新评审(9 道 lane,从零开始,不继承任何先前结论)
评审对象 f972884e5dc99c16241941c87a094140e5e7600a(6 提交)。rebase 核实:base f1eb10efc 经 GitHub API 确认仍为 pinvou3-clean 顶端(rebase 为空操作;父仓 main 3cec34e2b 未动,codewhale-hq#809 0 behind,亦无需 rebase)。
覆盖与结果
| Lane | 结果 |
|---|---|
| (a) 武装面逐条对账 | 8 组 claims 全部 TRUE(file:line 级证据);守卫面穷举无遗漏;根因修在决策点(resolver/pump/gate),无旁路路径 |
| (b) 转换通告逐条对账 | 8 组 claims 全部 TRUE(2 处措辞级 caveat);单一权威源比较 + 消费一次 + 无 await 窗口,数据流无漂移面 |
| 状态机对抗审查 | 0 blocker/major;粘滞学说(publish merge + re-record 双层)、NotStarted、未武装逐字节、子代理镜像、serde 兼容全部成立;新 .expect 有既有同型先例且受验证函数保护 |
| 门禁复现 | 9/9 精确一致:forkguard_ 260/0(单线程)、benchmark 6/0、preview 61/0(preview::tests:: 口径)、turn_metadata 24/0、change_mode 4/0、escalation 11/0、runtime_threads 184/0、fmt --check 干净、clippy -D warnings 恰为 5 条存量 lint(全部位于 PR diff 之外) |
| 突变验证 | 5 条行为突变全部转红后逐字节还原:pump 删 !approval_force_prompt、suffix 回退 carrier 门控、NotStarted 注入姿态记录、publish None-keep 移除、子代理门回退 !auto_approve |
| 走私审查 | 子仓 CLEAN:20 文件每个 hunk 均服务 (a)/(b) 或 body 已披露的伴随改动(from_fields 重构、clippy Copy、tripwire 注释、测试钉);零既有断言削弱(两处搬移的既有测试函数体逐字节比对一致;约 30 处单行改动均为签名/字段机械更新);6 提交 message 全部属实(from_fields 披露在 body 而 998ffe2 message 未提,已披露口径) |
| 优雅性 | 通过严格设计评审:全程骑既有载体(Op::ChangeMode/LiveRuntimeAuthority/既有 approval_force_prompt 事件字段/PolicyNarrowingEvent 回执学说),无并行机制;层级正确(execpolicy 未动,engine 层持旗);字节钉符合 codewhale-hq#3947/codewhale-hq#5187 契约非过度钉 |
| 性能 | 全部新增路径 O(1);热路径无新增锁/await/堆分配;无逐帧/逐 chunk 钩子;未武装稳态开销 = 每轮一个 Copy 结构比较 + 两次 Option 存取;生产代码零新增 .clone();无界状态为零 |
| 父仓登记(codewhale-hq#809) | guard --fast PASS(exit 0);EXPECTED_HEAD/gitlink=92 提交计数(rev-list 精确复算)/21 指纹(20 钉全部存在于 fork 树,零缺失零孤儿)/drift 526 files +94377−39438 /275 行为名/census 20/90 DCO 全部精确 TRUE;safety-modes.md 与底库行为逐条一致 |
总评:approve 方向。 四项评审要点全部通过:有用且完整、根因到位;优雅、无重复造轮;无走私;0 blocker / 0 major,性能无可测回归。
发现清单(均 MINOR;1–3 建议小修复波,其余可记录或 follow-up)
- wire 兼容测试缺口(负突变证实) — 删除
Op::ChangeMode.ask_on_guardrail的#[serde(default)]后codewhale-protocol全套 92 测试仍全绿:协议 crate 无「遗留载荷缺字段可反序列化」钉(roundtrip fixture 用Some(true)构造,不咬反序列化;sync_session在op.rs:716-742有同型先例)。建议补一条 legacy-payload 反序列化钉,把「serde 缺省缺席 = 行为不变」从属性正确升级为测试钉咬住。 - runtime_threads 的
approval.requiredJSON 面缺approval_force_prompt—runtime_threads.rs:9503/9600两处载荷只含 id/tool_name/description/intent_summary。宿主实答面(父 app)走Event::ApprovalRequired(带旗标;forwarder.rs:847 → pending_approvals「force_prompt 永远弹卡」)不受影响,执法亦留在底库无旁路;但 pump 部署下消费该 JSON 的外部客户端无法把武装同意卡与普通审批区分开。建议两处载荷补该字段。 Op::SyncSession不重置last_turn_meta_authority— 该字段唯一写点为 init(engine.rs:2427)与轮始记录(:6089),apply_synced_session(:1913-2058)重置 mode/history/workspace 但不含它与session.ask_on_guardrail;进程内切换会话时新会话首轮可能渲染一条 "from" 姿态是新会话从未上报过的转换行(自愈于一轮)。建议 sync 时一并重置(可比照 tool 激活缓存的 process-local-but-conversation-local 学说,session.rs:177-185)。runtime_chat_isolated轮记录但不渲染 — 块构建被 engine.rs:4637/:4718 抑制,而 :6089 记录无条件执行;块-less 轮可吞掉 pending transition(配置门控,潜伏)。- 窄化 provenance 的转换误标
host_authority_change— 嵌入方 provenance(AgentMail/ImportedTranscript/MemoryRecall)在常驻自主姿态下会产出错误 host 归因的转换行;当前全部 in-tree 发送点传 ExternalUser/Runtime/SubAgentHandoff,今日不可达。可考虑独立 reason token。 - pump 部署武装 hold 无界悬挂(已披露,fail-safe 方向)— runtime_threads.rs:9606 决策等待仅由 cancel/interrupt/引擎死亡解除;建议 follow-up:超时或掉武装信号。
- 优雅性打磨 — (i)「武装且有人可答」谓词在 engine.rs:8712 / turn_loop.rs:4234 / :4343 三种拼法,建议依
permission_posture_allows_questions(authority.rs:403)先例提取共享助手;(ii) turn_loop.rs:3982 的\|\| armed_full_access_hold为逻辑死析取(被 registered_non_bypassable 蕴含),删除或加防御注释;(iii) escalation 两臂 ~15 行逐字重复(:4323-4336 vs :4362-4375),可折叠;(iv) 新行Authority transition (host): …与既有窄化行Authority transition: …(engine.rs:4516)近碰撞、可同块共存,建议注释区分;(v) 6 提交横跨 3 个 author 身份,DCO 身份宜归一。 - 测试网备注 — 粘滞双钉分工覆盖不同安装路由(publish-merge 路由仅
forkguard_pending_arming_survives_arming_free_authority_publishes咬住,另一钉守 install 路由)——覆盖互补非缺口;少数测试直接戳私有 detector 状态而非走Op::ChangeMode通道(refactor 敏感)。 - tripwire 注释(approval_routing/exec_agent)无测试伴随 — 已披露,可接受。
已知边界披露(内置 TUI 不可武装、provider 重启掉武装、edit-last-turn 截断丢回执、composer 不武装)逐条核实与 body 一致。突变豁免如实记录(渲染测不咬检测器为设计内,body 已写明)。
Pre-arming hosts emit ChangeMode without the ask_on_guardrail key at all. The absent field must deserialize to None (= keep the session's current arming) and an unarmed op must serialize without the key; a serde(default) or skip_serializing_if regression would hard-require the field on the wire and break every legacy sender. Found by a negative mutation in the round-4 review: removing the attributes shipped green across the whole protocol suite. Signed-off-by: asto18089 <asto18089@users.noreply.github.com>
Two receipt-honesty gaps from the round-4 review: - Op::SyncSession installs a conversation whose model never saw the previous conversation's receipts. The recorded turn_meta posture now resets with the rest of the conversation-boundary state, so the first turn establishes a fresh baseline instead of rendering a transition whose from-side it never reported. Arming itself is host-owned and deliberately survives the sync. - runtime_chat_isolated builds no <turn_meta> block, so a posture recorded on an isolated turn was rendered nowhere and a pending transition could be consumed by a block-less turn. Isolated turns now neither consume nor record; the next block-building turn compares against the last posture a block actually reported. Both faces pinned (forkguard_sync_session_clears_the_recorded_turn_meta_posture, forkguard_an_isolated_turn_neither_consumes_nor_records_the_transition), mutation-verified red. Signed-off-by: asto18089 <asto18089@users.noreply.github.com>
The runtime-thread pump emitted approval.required without the approval_force_prompt flag, so clients on pump deployments could not render an armed consent card as anything but an ordinary approval (enforcement stayed in-repo; this is the signal half). Both emission paths now carry the flag, and the pump pin asserts the payload on the forced (true) and ordinary (false) paths, mutation-verified red. Signed-off-by: asto18089 <asto18089@users.noreply.github.com>
guardrail_prompts_answerable replaces the three inline spellings of "armed and a person can answer" (deny face, repo-law holds, sandbox escalation), mirroring the permission_posture_allows_questions precedent in authority.rs. The two sandbox-escalation arms fold into one: they differed only in the armed marker, and the deny arm becomes the explicit complement. The force_prompt computation drops the armed_full_access_hold disjunct - the hold implies registered_non_bypassable (both are gated on registered_tool_forces_prompt), now documented at the site. The host transition line's docs distinguish it from the narrowing receipt family. No behavior change: the deny/repo-law/escalation pins stay green, and mutating the helper turns the deny-face pin red. Signed-off-by: asto18089 <asto18089@users.noreply.github.com>
asto18089
left a comment
There was a problem hiding this comment.
修复波收官(round-4,4 提交 → b94cc3175)
对 COMMENT 5733839125 全部可修项落盘,body 已同步重写(评审史十提交、262/0、突变清单、修复披露):
| 评审发现 | 处置 |
|---|---|
| 1. wire 兼容测试缺口(负突变证实) | 7e03162d1:协议 crate 补 change_mode_without_ask_on_guardrail_keeps_the_current_arming(缺键反序列化 = None + 未武装序列化不出键;serde 属性删除突变转红——此前全绿) |
2. pump approval.required 缺 force_prompt |
9b1e1cf13:两条载荷补 approval_force_prompt(additive);pump 钉扩展为 forced(true)/ordinary(false) 双路径载荷断言,突变转红 |
| 3. SyncSession 不重置姿态回执 | 5910426c5:sync 重置 last_turn_meta_authority(武装 host-owned 刻意保留);新钉 forkguard_sync_session_clears_the_recorded_turn_meta_posture 突变转红 |
4. runtime_chat_isolated 记录不渲染 |
同上提交:isolated 轮不记录不消费,下个建块轮与最后真实上报姿态比较;新钉 forkguard_an_isolated_turn_neither_consumes_nor_records_the_transition 突变转红 |
| 7. 谓词三拼/死析取/escalation 双臂/行族碰撞 | b94cc3175:guardrail_prompts_answerable 共享助手(permission_posture_allows_questions 先例);escalation 臂合并(deny 臂为显式补集);删 || armed_full_access_hold 死析取(蕴含关系就地注释);转换行 doc 与窄化行族区分注释。无行为变化,deny/repo-law/escalation 钉全绿,谓词突变转红 deny-face 钉 |
| 5/6/8/9 + author 身份 | 记录为 follow-up/已披露/测试网备注;3 身份问题随 squash 合入消失,不改写历史 |
门禁 @ b94cc3175(全部实测):forkguard_ 262/0(单线程)+ benchmark 6/0 + preview 61/0 + turn_metadata 24/0 + change_mode 4/0 + escalation 11/0 + runtime_threads 184/0 + fmt 干净 + 协议 71/0;clippy 恰为 5 条存量 lint(全在 PR diff 外)。5 条新突变全红后逐字节还原。登记批随父仓 codewhale-hq#809 重钉:EXPECTED_HEAD→b94cc3175 / 96 提交 / 23 指纹 / census 22 / 头部文案 89→96 修正;fork-guard.sh --fast 绿。
A provenance-narrowed turn reported its reduced posture through the same turn_meta comparison the host transition uses, so on a raised- posture runtime thread every narrowed turn rendered an 'Authority transition (host)' line that misattributed the per-turn reduction to a host flip, and the next authoritative turn rendered the unwind. The narrowing receipt family already reports the reduction: a narrowed turn now emits no host line and leaves the standing baseline untouched for the next authoritative turn to measure against. SyncSession clears the baseline with the recorded posture. Auto-Review was also standing auto authority that the narrowing downgraded without a receipt; it now records the same structured event the Bypass downgrade carries. Signed-off-by: asto18089 <asto18089@users.noreply.github.com>
Round-5 全新评审(11 道 lane,从零开始,不继承任何先前结论)评审对象 11 道车道结果
修复波 5(
|
JensenChen28
left a comment
There was a problem hiding this comment.
Standards: no actionable finding in the cumulative foundation contract and latest provenance-baseline increment. Spec: live/preview suppression and the distinct standing baseline address false host attribution; Auto-Review narrowing now carries its receipt. Prior foundation source evidence through b94cc31 was reused; the new increment and provider-body/state test oracles were independently reviewed on both axes. Full diff whitespace check passed. No local Cargo/native execution, author test totals, or mutation results are claimed; previously disclosed host-availability and lifecycle boundaries remain.
The legacy text parser restarts its generated ids at 1 for every response, so two adjacent text-encoded tool responses both presented `xml_tool_1`. The turn loop carried that id verbatim into ToolUseState, ToolCallStarted, and every approval event, while the approval receipt log keys receipts by tool-call id and rejects an id that was asked more than once: under decision gating, the second fallback call failed its receipt commit and could never execute. Rewrite each parsed id at the turn-loop ingestion point into a fresh incarnation `<parsed>#<unix millis>x<counter>` backed by a process- global AtomicU64 plus wall-clock millis, so ids stay unique across responses and across engine restarts within the same session. The id is opaque to every downstream consumer and keeps flowing through ToolUseState, so everything downstream automatically uses the rewritten value. Correct the parser's stale module doc, which claimed the engine never invokes it. Signed-off-by: asto18089 <asto18089@126.com>
Handle::approve_tool_call/deny_tool_call were fire-and-forget: they sent ApprovalDecision into the channel and returned Ok(()) meaning only "enqueued". When no turn was awaiting, the idle loop received decisions and silently dropped every non-child id, and inside await_tool_approval the durable receipt commit happened only for the matching id — so a caller (the pinvou3-app approval surface) could believe a decision was accepted while the engine never validated or committed it. Attach an ack oneshot to every ApprovalDecision variant. The handle methods now await the ack under a bounded 15s timeout and return Ok(()) only when the engine consumed the decision and durably committed the outcome for the matching id (or a waiting child took it); Err otherwise. await_tool_approval resolves the ack from the commit result and nacks stale non-child ids instead of dropping them; the idle loop nacks unroutable decisions the same way, mirroring the existing child-approval oneshot style. The test-only mock handle resolves acks in a small pump so mock-based suites keep their enqueue/observe ordering. Tests: the stale-decision and failed-commit cases now assert the handle-level Err and that no receipt was written; the idle-loop child test also covers the parent-shaped nack. Signed-off-by: asto18089 <asto18089@126.com>
f7b5cfd to
47ada18
Compare
|
Round-6 integration-fix pair pushed on top of
Verification at The parent-side registration (pinvou-agent PR codewhale-hq#809 Re-requesting review. |
JensenChen28
left a comment
There was a problem hiding this comment.
复核固定 head eea741fcdd1d0eedeebea8cdec74e750a736cdb9,target/merge-base f1eb10efcfe2468fde42d1724d7f5f19d54b904c。使用子仓独立干净 detached checkout,完整审阅 fec1193→eea 的3文件增量及相关累计链,已读中间93c版本的本人审核5750549837,未重复发布该版本结论。
Standards
retained_user_messages 导入已移至包含其真实调用的测试模块,生产仍使用 origins 版本;这是静态修复核对,不声称 CI 已恢复。Summary shared helper 先用来源图、后用无来源机械位置,三个压缩入口共用政策。新增直接 helper 回归明确验证等长度/角色时4→3、无来源机械4→4;它构造 history/origins,证据范围比真实 provider/取消/编辑链窄,既有真实 builder 来源及普通 Summary wire 回归仍有独立价值。
保留非阻断 P3:legacy status_message 写入与 PruneOnly pre/post 实为 post/post 的测试精度问题。十二项 Fowler 启发式已考虑,无新阻断。
Spec
既有公开 Summary 历史关联 P2 在当前 head 源码关闭。可用 summary_origins 优先于等角色位置推断,B 能4→3;实际带 topology 的形状先按来源5→4,再移除旧载体并插入新载体,条目与实际 B 同为3,编辑 equality 可恢复 prior=A。丢失轮记录被移除,无来源机械路径保位、不可映射替换仍清空。
实际执行来源结构的 Python 模型断言,覆盖等角色有/无 topology、机械回退、丢失轮及不可映射清空;并核对准确源码分支、完整增量与回归 oracle。增量/累计 diff --check 通过。未运行 Cargo/Rust、原生/provider 取消或编辑 E2E,不借作者/同行计数,也不声称旧失败测试已经本机动态通过。无新确认 Spec 缺陷,故批准本次源码修订。
该闭合只属于 eea;父仓 codewhale-hq#809 此刻仍链接93c,不包含此修复。其他审核者、私密事项与 CI 验证保持原处置,不因本批准关闭或豁免。未改业务代码、提交修复、合并或入队。
两轴统计:Standards 保留2项P3;Spec 当前0项确认问题。
|
@/tmp/p131-closure.md |
zhuowp
left a comment
There was a problem hiding this comment.
[P2] 原生 TUI 的会话级授权在读取和写入时使用不同的分组键。子任务现在会发出带有效工作区范围的键,TUI 用它做缓存查询,但打开审批弹窗时只传入精确键,弹窗从父会话上下文重新计算分组键。「本会话批准」随后保存的是这个重算的键。同一子工作区再次请求时仍会提示,保存的授权也偏离了用户审批的作用域。请把事件携带的分组键原样传到弹窗和授权写入,并用真实 TUI 审批往返测试覆盖同范围复用和跨范围隔离。
本轮已静态复核摘要来源重映射及手动、自动、应急三个压缩入口,未确认新增阻断。上述授权问题已通过生产调用链核实。提交本结论时,本地 Windows Rust 测试仍在运行,GitHub 尚未报告该提交的检查;合并资格未满足,本轮未评估发布。
JensenChen28
left a comment
There was a problem hiding this comment.
补充复核固定 head eea741fcdd1d0eedeebea8cdec74e750a736cdb9,最新 base f1eb10efcfe2468fde42d1724d7f5f19d54b904c。已独立核对同行新报告的实际 TUI 消费链;现将本账号对该 head 的批准更新为请求修改。之前的 Summary 来源映射/重定位 P2 仍保持源码关闭,这是另一项会话授权问题。
Standards
[P2] 缓存查询与授权写入没有保持同一分组身份。tui/ui/event_loop.rs:3412–3417 用事件原始 approval_grouping_key 查询会话授权,但 3500–3508 创建审批弹窗仅传 approval_key,丢掉了事件分组键。tui/ui/overlays.rs:494–502 随后用父 app.workspace 构造请求,tui/approval.rs:184–186 重算分组键;它不等于子任务 tools/subagent/mod.rs:15152–15156 已绑定有效工作区的事件分组键。
Spec
正常触发:可提示姿态下,子任务发出需批准操作,用户在原生 TUI 选择“本会话批准”。tui/approval/view.rs:218 回传的是弹窗重算值,tui/ui/apply.rs:2578–2592 将其写入 approval_session_approved。同一子工作区再次发出相同操作时,读取方继续查事件携带的工作区分组键,匹配不到刚才写入的值,因而再次提示;保存的授权身份也偏离了请求携带的作用域。仅检查 key 生成与 ApprovalRequired 字段的既有测试覆盖不到这条真实弹窗往返。
请将事件分组键原样传到弹窗并用于授权写入,补真实 TUI 审批往返测试,验证同范围的操作族复用、不同范围隔离及 force_prompt 仍独立;精确拒绝键和 Summary 修复应保留。本次不扩展为未经验证的执行权限绕过结论。
实际验证:head/base 已 fetch,根及两轴独立阅读子任务生产者→事件路由→弹窗构造→决策事件→授权集合写入/下一次读取源码;未运行 Cargo、原生 UI、Windows、provider 或审批 E2E,同行仍在运行的测试不作本人证据。固定 head 源码未变化,既有 diff 检查结果仅在其原范围内保留。已读最新评论和线程,无需另发重复讨论回复;未改 PR 代码、触发 CI、合并或入队。
zhuowp review 5759493820 / JensenChen28 review 5759735821 (P2): the
session-grant read side queries `approval_session_approved` with the
engine-bound grouping key carried on the ApprovalRequired event, but
the approval dialog recomputed the grouping identity from the parent
workspace. An "approve for session" on a subagent request then stored
the recomputed identity: the next identical sub-workspace request
missed it and prompted again, and the grant lived under an identity
the user never saw.
Carry the event's grouping key into the dialog verbatim
(`push_approval_request_view` -> `ApprovalRequest::new_with_intent`
override) so the decision emission and the session-grant write answer
under the same identity the read side queries. Exact-deny keys, the
Summary origins fix, and force_prompt independence are unchanged.
Regression: a real card round-trip ('2' -> ApproveForSession) on a
workspace-bound subagent event asserts the emitted and stored identity
equal the event key, that the parent-recomputed identity is absent,
same-scope family reuse auto-approves, a different sub-workspace still
prompts, and a forced hold stays on the card. Red-checked by
reintroducing the parent recompute (the card answered
`shell:10:cargo test` instead of the bound identity) and green on the
fix; an earlier red attempt via a bare `None` was a compile error, not
a test failure, and was discarded.
Suites: `tui::approval` 114/0, `tui::ui::tests::approval` 7/0,
`tui::widgets::tests::approval` 18/0, `core::engine` 696/0; fmt clean.
Workspace clippy in the CI shape reports only the known pre-existing
base-lineage `collapsible_if` at `turn_loop.rs` (eviction-notes site,
present on merge-base f1eb10e; disclosed, not fixed here).
Signed-off-by: asto18089 <asto18089@126.com>
|
Both reviews (zhuowp review 5759493820 and JensenChen28 review 5759735821) point at the same P2, and it is fixed on head Verified first. The production chain reproduces exactly as traced: the read side ( Fix — carry, don't recompute. Regression — Suites at Re-review requested from both. |
JensenChen28
left a comment
There was a problem hiding this comment.
批准固定 head 0583e19cb79e50e757c2e6b1638207960b9057ec,最新目标/merge-base pinvou3-clean@f1eb10efcfe2468fde42d1724d7f5f19d54b904c。完整审阅 eea741fc→0583e19c 六文件增量(175+/3−)及实际 TUI 消费链,未变范围继承此前固定源码审核。
Standards
上轮原生 TUI 会话授权分组键 P2 已在源码闭合。事件路由把原 approval_grouping_key 传到 overlay,构造请求以 Some(carried) 原样保存;view 决策、handler、grant writer 均继续透传该值,缓存查询与写入现在使用相同身份。全树调用点已核对,重算 fallback 的 None 仅用于测试辅助/渲染 fixture,生产事件路径显式使用 carried key。精确拒绝键、force_prompt 及先前 Summary 来源映射修复保持原结论;未发现新增确认阻断。
Spec
新增测试使用真实 ApprovalView 按键“2”,先核发出的 ApprovedForSession 与事件分组键一致,再调用生产授权 writer,验证保存原键、不保存父会话重算键、同范围再次请求 AutoApprove、不同子工作区仍 Prompt、force=true 仍 Prompt。测试的 writer 输入另外构造为预期键,虽已通过前一步相等断言连接逻辑,但没有驱动实际 handler/event_loop/子代理完整旅程;同操作族测试复用相同参数,也不能冒称已执行不同 flags 的变体。上述分段 oracle 对本次身份丢失修复有效,验证边界保留。
实际执行:最新 head/base fetch,完整增量和 producer→lookup→popup→decision→writer→lookup 源码及全部新签名调用点审阅;增量/累计 git diff --check 均通过,Signed-off-by 已核。未运行 Cargo、编译/原生、Windows、provider 或 TUI E2E,不采用作者114/7/18/696及负控数字;作者披露的 Clippy 基线归因未独立复验,不声明编译/CI 已全绿。旧非阻断提示写入与 PruneOnly 测试精度建议保留。不可取得的历史私密事项及其他审核者线程不代为关闭。此批准仅覆盖固定代码审核,其他审核者、CI和维护者独立决策;未改 PR 代码、触发 CI、合并、入队或开启 auto-merge。
|
@/tmp/p131-gw.md |
There was a problem hiding this comment.
上一轮原生 TUI 会话授权 P2 已在源码层面关闭。事件携带的工作区分组键现在从事件分发传入审批弹窗,随后原样用于会话授权写入。精确拒绝键与强制审批行为保持原契约。独立裁决核对了本轮 6 文件 17 处差异。新回归覆盖实际弹窗、决策写入及后续查找,包含同作用域复用、其他作用域隔离和强制审批。事件分发到弹窗的接线另以生产源码核对。
固定目标的 Git 合并树检查成功。本条记录已完成的源码复核。补充 Windows 验证:固定版本成功编译,授权弹窗往返、手动裁剪及摘要来源重映射三项独立定向测试通过。全量库测试仍有失败,已出现的 ACP 会话失败在上一个受审提交也出现,归因未完成。GitHub 未报告此提交的检查,合并和发布资格尚未建立。
|
当前 head |
|
On the test-failure note in review 5762102226: reproduced and classified locally at the same head
|
zhuowp
left a comment
There was a problem hiding this comment.
Approved at 0583e19c. The approval event's grouping identity is now carried through the native TUI dialog and session grant write, matching the cache lookup. The round-trip regression verifies same-scope reuse, cross-scope isolation, and forced prompting. The earlier exact-head Windows targeted tests passed for approval, manual pruning, and summary-origin remapping. The now-completed exact-head fork CI gate passed, including workspace nextest (14,915 passed, 15 skipped), and the later issue-link check passed. The prior Windows full-suite discrepancy was recorded separately and was not reproduced by this CI run.
|
This follow-up's condition is already satisfied — closing the loop with the receipts:
zhuowp's final approval already reflects the passing link check, and both reviewers hold approving reviews at head |
Base #131 changed the approval pump to auto_approve && !approval_force_prompt, so on a host that arms ask-on-guardrail guardrails, guardrail-class calls force a real human approval even for an auto_approve=true task. The "no approval prompts" absolute (and the mode row skip parenthetical) now carry that exception, the mode row discloses that trust_mode's effects still apply in plan (the retry path has no mode re-check), and the pins assert the exception clauses and forbid the unscoped absolute from returning. Signed-off-by: asto18089 <asto18089@users.noreply.github.com>
No-Issue: 底座登记批次(tri-mode 安全模型的引擎侧行为契约),不存在单一对应 issue;逐轮评审史、指纹与登记以本描述及父仓 fork-guard 记录为账。
摘要
tri-mode 安全模型(Plan / Auto / YOLO + 会话级 ask-on-guardrail 开关)的两个登记底座行为,评审史二十一个提交(
f1eb10efc之上;逐轮明细见下):998ffe27e(主体,骑f1eb10efc)+ 复审加固提交(补 6 条测试钉,41eab46f2)+ 评审修复提交(收敛 fresh-review 发现,04c1f7843)+ 复审修复二(收敛 re-review 发现:武装发布粘滞、preview 计划姿态、补 4 条突变验证钉,5c13fc561)+ clippy 修复(去 Copy clone,10b28ef5f)+ 复审修复三(收敛第三轮 fresh-review 发现:runtime pump 尊重 force_prompt、status 后缀只报真实翻转、补 2 条突变验证钉,f972884e5)+ 复审修复四(收敛第四轮 fresh-review 发现:协议 crate 遗留 ChangeMode 载荷 serde 钉、session 同步与隔离运行时的诚实姿态回执、pump 审批卡携带 force_prompt 旗标、武装面「可应答」谓词合并与 escalation 臂去重,4 提交7e03162d1/5910426c5/9b1e1cf13/b94cc3175)+ 复审修复五(收敛第五轮 fresh-review 发现:窄化 provenance 轮次不再误报「(host)」转换行——抑制 emits 且不污染 standing baseline;Auto-Review 窄化降级补结构化回执;2 条新钉,提交1785dae15;远端头1785dae15,dcd4c200f之上第 97 个提交。登记以远端 SHA 为准)。(a) ask_on_guardrail:宿主武装的交互式守卫提示
会话级
ask_on_guardrail旗标,经Op::ChangeMode(engine + wire,serde 缺省缺席 = 行为不变)与LiveRuntimeAuthority同 doctrine 携带(Option<bool>,None= 不改变现状;turn 安装重录会话现值,永不回滚已发布武装)。宿主武装后(已保证有真人可以应答),原本在自主姿态下 fail-closed 的守卫面改为升起审批请求:rlm/rlm_eval/start_mcp_server)在 Full Access 下改为带approval_force_prompt的审批(必须显式置位——普通 resolver 在 auto-approve 下会自动放行非旁路工具,feat: LLM can start MCP servers from chat context codewhale-hq/Codewhale#3866 反转语义,落空即静默执行);Never与 Auto-Review 姿态在全部武装面(deny face / repo-law / escalation)无条件保持拒绝——武装豁免显式排除这两个姿态,无人能应答的卡不会升起;未武装(默认)行为逐字节等同旗标前 doctrine;既有 fail-closed pins 就地注释武装例外。Composer(
!)来源保持不武装(codewhale-hq#5191:输入即批准)。(b) turn_meta 权威转换通告
引擎记录每个
<turn_meta>块上报过的姿态;宿主在轮间改变 mode / 姿态 / 武装时,下一块渲染且仅渲染一行:轮中任意次 ChangeMode 翻转折叠为下一块的一次转换;该行由轮块渲染且仅渲染一次(消费后即清,轮中 steer/预算提示不再重复渲染同一行);未变轮零新增行(codewhale-hq#3947 byte-stable doctrine);比较在轮块构建点计算(NotStarted 早退不再吞掉转换);
/preview-request以同一引擎算法重算(preview 与 live wire 哈希一致有专测)。对齐
宿主武装提示 = Claude Code ask-rules-survive-bypassPermissions + CW-7495 consent card 的泛化;模式变更模型通告 = Codex per-policy prompt snippets / Claude system-reminder 的同域实践。设计文档(docs/safety-modes.md)与指纹/登记在父仓 PR codewhale-hq#809 落地。
测试
998ffe27e新增 7 条:forkguard_armed_full_access_forced_hold_resolves_to_prompt(含会话授予不可静默消费武装 hold、Auto-Review/Never 恒拒)、forkguard_armed_deny_face_becomes_guardrail_prompt_unarmed_stays_block、forkguard_armed_full_access_hold_prompts_instead_of_blocking(端到端:武装 hold 弹审批 → 拒绝 → 以 approval denial 收轮)、forkguard_armed_child_refuses_delegated_typed_ask_even_under_auto_approve、forkguard_authority_transition_between_renders_the_host_change、forkguard_turn_meta_authority_transition_renders_only_on_change、forkguard_live_turn_meta_builder_reads_the_pending_transition。forkguard_armed_repo_law_hold_prompts_instead_of_blocking(repo-law 武装面;授予缓存不可静默消费)、forkguard_armed_sandbox_escalation_prompts_instead_of_failing_closed、forkguard_armed_full_access_typed_ask_rule_prompts_instead_of_auto_running(主线 auto_approve 下武装 typed ask;composer!面保持不武装)、forkguard_preview_manifest_hashes_the_same_authority_transition_line_the_wire_sends(preview/wire 哈希一致 + 无转换时逐字节稳定)、forkguard_turn_authority_install_never_rolls_back_published_arming、forkguard_apply_change_mode_status_suffixes_report_the_arming_change;并合并了 composer 路径重复注释。forkguard_pending_arming_survives_arming_free_authority_publishes(武装发布粘滞 E2E:turn install / 无武装 ChangeMode 均不可吞掉 pending arm,显式 disarm 仍生效)、forkguard_armed_composer_shell_keeps_the_hard_deny_face(armed Full Access 下 composer deny-rule 命中仍硬拒,v0.9.4: user-typed!shell commands should not hit the approval modal codewhale-hq/Codewhale#5191 不武装)、forkguard_armed_auto_review_escalation_stays_fail_closed(armed Auto-Review 升级恒拒)、forkguard_preview_transition_uses_the_planned_posture_not_the_stale_session(preview 与 live 线体哈希在假想姿态输入下逐字节一致)。forkguard_turn_start_detector_records_the_posture_and_renders_the_transition_once(端到端咬住 detector 的比较→记录→渲染→一次性消费,两轮 wire 断言逐字节;记录行删除与消费清空删除两条突变均转红),deny-face 钉扩展 Auto-Review 恒拒面。cargo test -p codewhale-tui --lib --locked forkguard_ -- --test-threads=1:264 过 / 0 挂(既有 fail-closed pins 原样绿;--features benchmark-eval-controlsforkguard_benchmark_ 6/0;preview 61/0、turn_metadata 24/0、change_mode 4/0、escalation 11/0、runtime_threads 模块 184/0 全绿;cargo fmt --check干净;协议 crate 71/0 含新增遗留载荷钉)。f972884e5)补 2 条新钉 + 扩展 1 条(全部突变验证转红后逐字节还原):forkguard_pump_never_auto_approves_a_force_prompt_hold(runtime_threads 审批 pump 绑定approval_force_prompt,Full Access 下 forced hold 必须 pending 等真人决定、不自动批准,Auto-Review fail-closed 臂保留;附带修复前存 non-bypassable 面在 pump 下被静默自动批准的缺口)、forkguard_a_not_started_turn_neither_consumes_nor_records_the_transition(NotStarted 早退不吞 transition:失败 turn 不记录姿态,下个成功 turn 渲染该行);后缀钉扩展「carrier 回显未变值不得宣称翻转」的生产形态断言。行为披露:armed hold 在 pump 部署下从「静默自动批准」改为「浮给客户端真人决定」(CW-7495 契约本义)。!auto_approve;复审修复二新增 4 条:publish 粘滞移除(新钉转红)、composer 面传入会话旗标(新钉转红——armed composer 会经 Prompt 臂放行 deny 命令)、escalation 位点 Auto/Never 豁免删除(新钉转红)、preview 姿态回退 session 源(新钉转红);复审修复三新增 3 条:pump 删!approval_force_prompt守卫(新钉转红——hold 被自动批准)、后缀回退 carrier 门控(扩展断言转红)、NotStarted 路径注入姿态记录(新钉转红)。复审修复四新增 5 条:协议 crate 删skip_serializing_if(新钉转红——此前全绿,即负突变证实的缺口;如实更正:单独删#[serde(default)]对Option字段是 serde 无操作、钉不转红,round-5 评审证实并更正此处措辞——保留该属性是为显式声明缺省语义)、pump 载荷删 force_prompt 字段(扩展断言转红;单删任一载荷位点也被同一 pump 钉的两臂分别咬住,round-5 复核)、sync 重置移除(新钉转红)、isolated 门改无条件(新钉转红)、谓词丢 Never 豁免(deny-face 钉转红)。复审修复五新增 3 条:窄化轮抑制门删除(两条新钉转红)、narrowed 轮更新 baseline(新钉转红——下一权威轮渲染无人做过的反向 host 行)、窄化谓词丢 Auto 臂(Auto-Review 新钉转红——降级回执消失、误标 host 行复现)。复审修复四(round-4 fix wave,
b94cc3175)第四轮全新评审(9 道 lane,COMMENT 5733839125 @
f972884e5:claims 全 TRUE、走私 CLEAN、门禁 9/9 精确复现、5 条行为突变全红、注册数字全部精确)确认 0 blocker/major;其 4 项实质发现已由本波 4 提交收敛(全部突变验证转红后逐字节还原):7e03162d1test(protocol):change_mode_without_ask_on_guardrail_keeps_the_current_arming—— 负突变证实测试网缺口后补钉:删除ask_on_guardrail的#[serde(default)]/skip_serializing_if会使该钉转红(此前全套协议测试保持绿);「serde 缺省缺席 = 行为不变」从属性正确升级为测试钉咬住。5910426c5fix(engine):Op::SyncSession重置last_turn_meta_authority(会话切换不再继承上一会话的已上报姿态——新会话首轮不再渲染「from」侧从未上报的转换行;武装本身 host-owned、刻意跨 sync 保留);runtime_chat_isolated轮不再记录/消费转换(此前块-less 轮可吞掉 pending transition)。钉:forkguard_sync_session_clears_the_recorded_turn_meta_posture、forkguard_an_isolated_turn_neither_consumes_nor_records_the_transition(两条均突变转红)。9b1e1cf13fix(runtime):pump 两处approval.requiredJSON 载荷补approval_force_prompt字段(执法本在底库;此前 pump 部署的外部客户端无法把武装同意卡与普通审批区分开)。pump 钉扩展为同时断言 forced(true)与 ordinary(false)两条路径的载荷,突变转红。披露:这是对外 JSON 面的加字段(additive)变更,旧客户端忽略新键不受影响。b94cc3175refactor(engine):guardrail_prompts_answerable合并三处「武装且有人可答」谓词拼写(deny face / repo-law / escalation,依permission_posture_allows_questions先例);escalation 两臂合并为一(仅差 armed 标记,deny 臂为显式补集);删除force_prompt计算中被蕴含的|| armed_full_access_hold死析取(蕴含关系就地注释);host 转换行 doc 注明与窄化回执行族的区分。无行为变化:deny/repo-law/escalation 钉保持绿,谓词突变转红 deny-face 钉。未收敛项(记录在评审线):pump 部署武装 hold 无界悬挂(已披露、fail-safe 方向,follow-up:超时或掉武装信号);tripwire 注释无测试伴随(已披露,可接受);6 提交横跨 3 个 author 身份(squash 合入后消失,不改写历史)。原列的「窄化 provenance 转换误标」一项:round-5 评审证实其在 runtime 线程 AgentMail 输入下实际可达,已由复审修复五修复(见上)。
复审修复五(round-5 fix wave,
1785dae15)第五轮全新评审(11 道 lane,从零开始)确认 round-4 后仍 0 blocker/major、走私 CLEAN、门禁全数精确复现,但推翻了一项 round-4 的不可达判断并暴露两处披露缺口,本波 1 提交收敛:
reason: host_authority_change(宿主什么都没做),下一个权威轮又渲染反向行;Auto-Review 站姿下更连窄化回执都没有(had_auto_authority未把 Auto 计入)。修复:窄化轮不再 emits host 转换行(窄化回执行族本就如实上报该轮降低),也不更新站姿 baseline(last_authoritative_turn_meta_posture,否则下一权威轮把窄化解除渲染成反向 host 行);真正的宿主翻转由下一个权威轮从窄化前站姿出发渲染。Auto→Suggest 降级与 Bypass 降级同样携带结构化窄化事件。钉:forkguard_a_provenance_narrowed_turn_neither_emits_the_host_line_nor_pollutes_the_baseline、forkguard_an_auto_review_turn_narrowed_by_provenance_still_records_its_narrowing(三条突变均转红后逐字节还原)。forkguard_ 264/0;行为名 277→279。 preview 与 live 同步镜像同一 baseline/抑制语义,preview 哈希一致性钉保持绿。登记(随父仓 PR codewhale-hq#809)
fork-guard 指纹(25 条:24 条测试钉 + 1 条
Authority transition (host): …模型可见文案钉)、EXPECTED_HEAD→1785dae15/EXPECTED_COMMITS→97、docs/fork-modifications.md T2 候选期条目、docs/safety-modes.md 设计文档(§7 补窄化归因语义、§8 census 24),均在父仓 PR 落地;本 PR 为纯底座行为提交。已知边界(如实)
Event::ApprovalRequired { approval_force_prompt }消费——"host-armed" 的本义)。approval_routing.rs的 UI authority 恒未武装、exec_agent.rs的 headless auto-approve 已加 tripwire 注释,防止未来武装面接入时静默拒绝/放行。auto_approve同口径,会话恢复后从 disarm 起步,宿主须重新武装(fail-safe)。41eab46f2补齐;detector 端到端钉(记录 + 一次性消费)在04c1f7843补齐。ask_on_guardrail,宿主随后的 arm/disarm 只影响主线与其后新 spawn 的子代理(存活子代理的武装面维持 spawn 时快照;failing-safe 方向视宿主动作而定,已由评审指出、如实披露)。!approval_force_prompt守卫对未武装部署也是行为变化(原 non-bypassable 面在 pump 下被静默自动批准的缺口修复——见复审修复三披露;此即「未武装逐字节等同」的显式例外,方向为 fail-safe)。04c1f7843)确认 0 blocker/major、smuggling CLEAN、全部登记数字属实;其 6 条 minor 已收敛:发布粘滞竞态与 preview 计划姿态(行为修复 + 新钉)、子代理 doc 收窄到子代理实际设防面(repo-law 为主线 face)、/provider重启掉武装补 tripwire、armed composer / armed Auto-Review 升级 / 发布粘滞 3 处补钉、比较点注释补 residual 边界。已知边界补充:provider 切换重启动会话掉武装且无独立事件(app 池在 spawn 时重放武装旗标;外部宿主须重发);记录先于发送的残余窗口(append-only history 兜底,edit-last-turn 截断会丢)。第三轮 fresh review(9 道,COMMENT 5732557068 @5c13fc561):(a) 10 TRUE/2 PARTIAL、(b) 8 TRUE/1 PARTIAL、走私 CLEAN、变异网 SOUND、7 门禁精确复现;其 3 项实质发现已由f972884e5收敛(见上),其余 minor 记录在评审线。披露:LiveRuntimeAuthority::from_fields去掉归一化链为 (a) None-keep 学说的伴随重构,全部现役 producer 发送规范形,无可达行为差异(998ffe27e引入)。pre-existing(基线可复现,不随本 PR 修复):tools::subagent::tests::child_permission_gate::auto_review_guardian_denial_refuses_the_call_with_a_receipt在--test-threads=1下顺序依赖失败,建议独立 follow-up。复审修复六(round-13 fix wave,
8c4e8e7a)第六至十二轮评审推进期间(round-6
fd066bf2+47ada186至 round-123cf2cdff9,登记见父仓 PR codewhale-hq#809),父仓 round-7 评审(10 车道,COMMENT 5746188328 @ 658843d3a)指认 SSE 兼容面缺口,本波 1 提交收敛:8c4e8e7afix(remote):SSE 兼容流approval.required信封补带approval_force_prompt旗标——此前 runtime pump 双载荷位点与 wireEventMsg均携带该旗标,唯独较老的 SSE 兼容面只发{id, approval_id, tool_name, description},由该面驱动的客户端无法把武装同意卡与普通审批区分开(父仓 round-6 评审 M-1 指认、round-7 评审维持 OPEN 的一项)。事件循环早已解构approval_force_prompt(event_loop.rsApprovalRequired 分支),仅record_remote_approval接线缺口:签名补参、信封补字段、事件循环传参。测试:既有 connected-attachment 用例扩展断言 ordinary(false)与 forced(true)两条路径的线上载荷(start_mcp_server强制卡夹具)。加字段为对外 JSON 面 additive 变更,旧客户端忽略新键不受影响。底座门禁复验:remote_control::77/0(--test-threads=1)、cargo fmt --check干净。登记:普通命名回归,不入 T2 指纹普查;行为名 279 不变;父仓 gitlink 与登记随本头重钉(EXPECTED_HEAD→8c4e8e7aa/107 提交/drift 529 files +97522/−39410,见父仓 PR)。No-Issue: issues are disabled on this repository; feature work tracked through the review rounds in this PR.