Skip to content

fix(cosh-ng): host injection save-compose-restore contract (#2598 U2) - #2632

Open
SunnyQjm wants to merge 4 commits into
mainfrom
fix/cosh-2598-host-injection
Open

fix(cosh-ng): host injection save-compose-restore contract (#2598 U2)#2632
SunnyQjm wants to merge 4 commits into
mainfrom
fix/cosh-2598-host-injection

Conversation

@SunnyQjm

@SunnyQjm SunnyQjm commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

Umbrella #2598 (U2, host-injection non-destruction contract): converge four injection-layer contract breaks with one save-compose-restore design, instead of per-issue point fixes. Design record: specs/cosh-2598-host-injection-contract/ in the dev workspace (G1 confirmed 2026-08-17); key invariants and matrices are also carried by code comments, test names and commit bodies in this PR.

Fixes #2540 (NS-005), Fixes #2539 (NS-009); Refs #2598, #2541 (sub-item B / SEM-019), #2537 (termios anchor M5).

Changes

  • M2 dispatch-chain failure isolation (SEM-019 root point)_cosh_utf8_han_status returns 1 for plain ASCII by design; its bare calls in _cosh_begin_attempt and _cosh_classify_missing leaked an internal "failure" into every dispatch, killing user set -e sessions and polluting the session exit code. All enum-status helper calls are now guarded conditional-context calls (tri-state preserved); _cosh_token_fingerprint in command_not_found_handle degrades to the native-delegate path on failure.
  • M3 PROMPT_COMMAND attribute fidelity (NS-005) — record the declare -p flags token before the wholesale hijack, restore -x afterwards; the hijack value is now a guard form (declare -F ... && ...) that stays a silent no-op when the exported copy leaks into a nested bash, and stages $? into _COSH_PROMPT_STATUS so declare -F cannot clobber the user's status before the prompt chain reads it (ledger exit codes depend on this).
  • M1 frame-level errexit protection (SEM-019) — preexec/prompt frames suspend errexit on entry and restore on exit; the veto path defers restoration to the next frame entry; the prompt wrapper returns 0 under errexit because a non-zero PROMPT_COMMAND list status kills an interactive session (probed on bash 3.2 and 5.2; bash itself restores the user's $? at the prompt boundary — also probed on both).
  • M4 line-execution DEBUG trap exit (NS-009) — the first DEBUG firing of a line completes the preexec work and leaves the trap disarmed for the rest of the line (per-command trap execution opens bash's mid-line job reap window — the mechanical cause of background-job serialization and [1] slot reuse). The prompt boundary re-arms through an ownership matrix: user-cleared / dormant / combined user trap (kept as-is, path generation stays fails-closed) / foreign trap (absorbed into the OLD chain). _cosh_now_ms drops the external date exec (fork hygiene, with a bash <4.2 fallback).
  • M5 termios lifecycle anchor ([P0][cosh-ng] 交互 TUI 替换 PS1/PS2 prompt 合同、termios 未恢复、--norc 未生效 #2537 RC-4) — new Linux-gated PTY integration tests pin the termios roundtrip across exit/EOF/SIGTERM with a real cosh-shell binary; SIGKILL is an explicit non-goal (physically uninterceptable).
  • Organization — the inline marker script crossed the 1000-line blocking bar, so the split plan already recorded in the large-file inventory was executed: script body extracted to marker/bash_marker.sh via include_str! (byte-identical; same asset pattern as input_intent.sh), inventory row updated.

Tests

  • tests/shell_host/marker.rs: +6 regressions — exported PROMPT_COMMAND array/scalar reassignment keeps -x, nested-bash guard-value silence, set -e session survives dispatch with $?/$- fidelity, background jobs keep concurrent job numbers, user DEBUG trap installed mid-session keeps firing.
  • tests/raw_cli/termios_lifecycle.rs (Linux-only): termios roundtrip over exit/EOF/SIGTERM. macOS libtest intermittently reports ENOTTY on a fresh pty slave (stable in a standalone process), so the module is gated to the shipping target.

Verification

Focused (green, macOS host unless noted):

  • cargo test -p cosh-shell --test shell_host -- marker:: 57/57; --lib -- osc golden 36/36 (byte identity across the asset split); new regressions 6/6.
  • cargo clippy --workspace -- -D warnings; cargo fmt --check; check-layout.sh; check-test-inventory.sh — all green (cosh-lab preflight data.ok=true).
  • Container FAIL→PASS against the S2 baselines (Apple container):
    • SEM-019 (anolisos:23.5, bash 5.2): candidate session exit code 1→0, framed payload parity kept.
    • NS-009 (same): baseline 9/10 SERIALIZED → fix 10/10 CONCURRENT under the un-anchored classifier (v1 column-0 anchoring undercounts merged first lines; both sides re-judged with the same v2 classifier).
    • NS-005 (alinux3 arm64, real cosh-shell raw cosh-core build of this branch): declare -adeclare -ax, driver exit 1→0, oracle parity.
  • Rules-alignment + behavior review (CodeReview agent with the auto-review rule set): no blocking findings. L3 security review: 0 findings.

Excluded scope (not run here, as-is):

  • Core-107 formal dual-arch re-run (NS-005/NS-009/SEM-019 interactive variants) — external validation harness; container-level FAIL→PASS is the pre-merge verdict.
  • tests/shell_host full suite on macOS: relay::routing_c3 shows pre-existing flaky/hang behavior under full parallel runs on this host (bare-base comparison: base fails/flakes the same or worse); hooks::engine lib/bins failures reproduce on bare base (pre-existing, macOS-only). Linux CI is the authoritative gate.
  • PTY-013 full termios family PASS additionally depends on U1 [Umbrella][P0][cosh-ng] 调用透明合同:默认 exec bash 透传 + TUI 白名单倒置 + 编译型 launcher shim #2597 removing the hang triggers (launcher/flag contract), out of this PR's scope.

Known deviations (recorded, not regressions):

Evidence

Assets on fork orphan branch pr-2632-assets (commit-pinned):

@github-actions github-actions Bot added component:cosh-ng src/cosh-ng scope:documentation ./docs/|./*.md|./NOTICE labels Aug 18, 2026
@SunnyQjm

SunnyQjm commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator Author

PR number: #2632
head_sha: 736b115
reviewed_at: 2026-08-18T05:07:36Z

Findings

未发现 blocking package/module/public API 组织问题。以下为非阻断项:

  • [P3] src/cosh-ng/crates/cosh-shell/tests/raw_cli.rs:77-85 — Linux-only 说明注释(5 行)写在 mod support; 之上,而真正被 #[cfg(target_os = "linux")] 门控的 mod termios_lifecycle; 在其后 3 行。注释与被说明对象分离,建议移到 #[cfg(...)] 正上方,避免读者误以为 support 被门控。
  • [P3] specs/cosh-ng-code-organization/large-file-inventory.md:52src/shell_host/marker/bash.rs 拆分后实测 10 行,已远低于 700 阈值。按台账自身规则"仅超限文件可登记",该行更适合移除或改写为闭环记录,而非继续以 Registered files 行存在。当前保留不影响 check-layout.sh(按路径匹配超限文件),属文档口径问题,不阻断。

结构合规确认

  • 范围:代码改动全部在 crates/cosh-shell/ 内;large-file-inventory.md 属 SDD 验收证据更新,符合 Scope Fence。
  • Ownermarker/bash.rsmarker/bash_marker.shinput_intent.sh 均归 shell_host/(OSC marker、shell 级输入意图分类),单一 owner,无跨 owner 混杂;测试落在既有 tests/shell_host/marker.rs 与新增 tests/raw_cli/termios_lifecycle.rs topic 文件,沿用既有 #[path] 挂载模式。
  • 资产拆分:脚本体迁到 bash_marker.sh + include_str! 正是台账中登记的拆分计划(镜像 input_intent.sh 先例),由行为修改把内联脚本推过 1000 行阻断线触发,属阈值驱动的必要配套,非"顺手整理"。拆分后 bash.rs 按 diff 增量推算约 10 行,与台账更新一致。
  • Public API:无 lib.rs 改动,无新增 pub mod/pub usebash_marker_script 保持 pub(in crate::shell_host);无新增 self-crate cosh_shell:: 路径。
  • 依赖方向src/ 无新增 Rust import,未触碰 forbidden dependency 清单。
  • Root src / 大文件:无新增 root 实现文件;bash.rs 从 934 行登记债务降为 10 行,系本 PR 的结构收益。新建 bash_marker.sh 为 1093 行 .sh 静态资产,当前 check-layout.sh 与台账只治理 *.rs,且该拆分是登记计划的原样执行,不构成越线。

Open Questions

  • 是否需要为超大 shell 资产(如 1093 行的 bash_marker.sh)建立独立大小治理口径(按函数分区拆成多个 asset),可作为后续 SDD 议题;当前 input_intent.sh 先例同样未登记此类治理。

Validation

  • 未运行任何验证(本评审为纯静态输入评审,不执行工具)。
  • 行数/计数类判定以 CI gate 实测为准。注意快照时 Test cosh-ng fast checksFAILURETest cosh-ngCANCELLED,建议合并前确认失败原因与本 PR 无关并等待转绿。
  • 剩余风险:M1–M5 的 bash 行为契约(errexit 框架保护、DEBUG trap 所有权矩阵、PROMPT_COMMAND 属性保真、termios 恢复)依赖 PR 描述的容器 FAIL→PASS 证据,本评审无法独立复核;macOS 上 tests/shell_host 全量与 PTY-013 家族按 PR 说明属已记录的排除范围,权威门禁为 Linux CI。

@SunnyQjm
SunnyQjm force-pushed the fix/cosh-2598-host-injection branch from 736b115 to c83f0eb Compare August 18, 2026 05:46
@SunnyQjm

Copy link
Copy Markdown
Collaborator Author

PR number: #2632
head_sha: c83f0eb
reviewed_at: 2026-08-18T05:51:16Z

Findings

未发现 blocking package/module/public API 组织问题。以下为非阻断项(与上一 head 的评审一致,本轮 patch 中仍存在):

  • [P3] src/cosh-ng/crates/cosh-shell/tests/raw_cli.rs:77 — Linux-only 说明注释(5 行)写在 mod support; 正上方,而真正被 #[cfg(target_os = "linux")] 门控的 mod termios_lifecycle; 在其后 3 行。注释与被说明对象分离,建议移到 #[cfg(...)] 正上方,避免读者误以为 support 被门控。
  • [P3] specs/cosh-ng-code-organization/large-file-inventory.md:52src/shell_host/marker/bash.rs 拆分后按 diff 推算为 10 行,已远低于 700 阈值。按台账自身规则"仅超限文件可登记",该行更适合移除或改写为闭环记录,而非继续以 Registered files 行存在。台账 Lines 列是 advisory 快照、审计按路径匹配,保留不影响 check-layout.sh,属文档口径问题,不阻断。

结构合规确认

  • 范围:代码改动全部在 crates/cosh-shell/ 内;large-file-inventory.md 属 SDD 验收证据同步,符合 Scope Fence。
  • Ownermarker/bash.rsmarker/bash_marker.shinput_intent.sh 均归 shell_host/(OSC marker、shell 级输入意图分类),单一 owner。slash/registry.rs:485 仅把测试的 include_str! 路径随资产拆分改指 bash_marker.sh,是拆分的必要配套且 PR 说明已解释,非混入无关 cleanup。
  • 大文件bash.rs 从 934 行登记债务降为 10 行,执行的正是台账登记的拆分计划(脚本体迁 bash_marker.sh + include_str!,镜像 input_intent.sh 先例),由行为修改推过 1000 行阻断线触发,属阈值驱动的必要配套。新建 bash_marker.sh 为 1093 行 .sh 静态资产,当前 check-layout.sh 与台账只治理 *.rs,不构成越线。
  • Public API:无 lib.rs 改动,无新增 pub mod/pub usebash_marker_script 保持 pub(in crate::shell_host);patch 内无新增 cosh_shell:: self-crate 路径。
  • 依赖方向src/ 无新增 Rust import,forbidden dependency 清单不受影响。
  • Root src:无新增 root 实现文件。
  • 测试位置:落在既有 tests/shell_host/marker.rs 与新增 Linux 门控的 tests/raw_cli/termios_lifecycle.rs,沿用既有 #[path] 挂载模式。

Open Questions

  • 是否为超大 shell 资产(如 1093 行的 bash_marker.sh)建立独立大小治理口径(按函数分区拆成多个 asset),可作后续 SDD 议题;input_intent.sh 先例同样未登记此类治理。

Validation

  • 未运行任何验证(纯静态输入评审,不执行工具)。
  • 行数/计数类判定以 CI gate 实测为准。快照时 Test cosh-ngBuild cosh-ng release 为 IN_PROGRESS、Test cosh-ng fast checks 为 QUEUED;上一快照曾出现 fast checks FAILURE,建议合并前确认其与本 PR 无关并等待全部转绿。
  • 剩余风险:M1–M5 的 bash 行为契约(errexit 框架保护、DEBUG trap 所有权矩阵、PROMPT_COMMAND 属性保真、termios 恢复)依赖 PR 描述的容器 FAIL→PASS 证据,本评审无法独立复核;macOS 上 tests/shell_host 全量与 PTY-013 家族按 PR 说明属已记录排除范围,权威门禁为 Linux CI。PR 当前为 draft。

@SunnyQjm
SunnyQjm force-pushed the fix/cosh-2598-host-injection branch from c83f0eb to a9d6f08 Compare August 18, 2026 05:52
@SunnyQjm

Copy link
Copy Markdown
Collaborator Author

Review round 1 disposition (head moved to the current SHA):

  • P3 comment/module separation in tests/raw_cli.rs — adopted. The Linux-only rationale block now sits directly above the #[cfg(target_os = "linux")] gate it describes, with mod support; moved ahead of it (fixed up into the test commit).
  • P3 inventory row for the 10-line bash.rs — adopted: the row is removed per the inventory's own "registered = over-threshold" scope; the split history stays in the refactor commit body and this PR's description (fixed up into the refactor commit).
  • Open question (size governance for large .sh assets) — agreed this is a follow-up SDD topic (applies equally to the pre-existing input_intent.sh); not expanded in this PR.
  • CI fast-checks failure at the reviewed snapshot — root-caused and fixed in the same refactor commit: slash::registry routing_c4 parses the marker source for the authoritative slash case list, and its include_str! pointer had to follow the script body to bash_marker.sh. slash::registry suite is green locally (8/8); the remaining macOS-local lib/bins reds reproduce on bare base (pre-existing hooks-domain + parallel-flaky adapter cases, solo-green), Linux CI stays the authoritative gate.

@SunnyQjm

SunnyQjm commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator Author

PR number: #2632
head_sha: a9d6f08
reviewed_at: 2026-08-18T06:11:01Z

Findings

未发现 blocking package/module/public API 组织问题。此前轮次的两条 P3(tests/raw_cli.rs 注释与 #[cfg] 门控分离、台账中已低于阈值的 934 行登记行保留)在当前 patch 中均已闭环,本轮无新增非阻断项。

结构合规确认

  • 范围:代码改动全部在 crates/cosh-shell/ 内;large-file-inventory.md 为 SDD 台账同步(按台账"仅超限文件可登记"规则删除拆分后已低于阈值的登记行),符合 Scope Fence。未触碰非 cosh-shell crate、lockfile 或环境文件。
  • Ownermarker/bash.rsmarker/bash_marker.shinput_intent.sh 均归 shell_host/(OSC marker、shell 级输入意图分类),归属单一。slash/registry.rs:485 仅将 routing_c4 测试的 include_str! 跟随脚本体改指 bash_marker.sh,是资产拆分的必要配套(也是此前 fast checks FAILURE 的根因修复),跨 owner 边界已在 PR 说明和处置记录中解释,非混入无关 cleanup。
  • 大文件bash.rs 从登记 934 行降为约 10 行(按 diff 增量推算,以 CI 实测为准),执行的正是台账登记的拆分计划(脚本体迁 .sh + include_str!,镜像 input_intent.sh 先例),由行为修改推过 1000 行阻断线触发,属阈值驱动的必要配套。新建 bash_marker.sh 为 1093 行 .sh 静态资产,当前 check-layout.sh 与台账只治理 *.rs,不构成越线;首行空行保留旧内联字面量的起始换行,字节一致性按 PR 说明由 osc golden 测试(36/36)钉住。
  • Public API:无 lib.rs 改动,无新增 pub mod/pub usebash_marker_script 保持 pub(in crate::shell_host);patch 内无新增 cosh_shell:: self-crate 路径。
  • 依赖方向src/ 无新增 Rust import,forbidden dependency 清单不受影响。
  • Root src:无新增 root 实现文件。
  • 测试位置:回归落在既有 tests/shell_host/marker.rs(+176 行,测试文件行数归测试 SDD 治理),新增 Linux 门控的 tests/raw_cli/termios_lifecycle.rs,沿用既有 #[path] 挂载模式,Linux-only 注释已紧贴 #[cfg(target_os = "linux")] 门控。

Open Questions

  • 超大 shell 资产(bash_marker.sh 1093 行,先例 input_intent.sh)是否需要独立大小治理口径(按函数分区拆成多个 asset),已确认为后续 SDD 议题,本 PR 不展开。
  • slash/registry.rs:486 zsh 侧仍 include_str!("../shell_host/marker/zsh.rs")(内联脚本),与 bash 侧拆分后形态不对称;zsh 未超阈值,属既有状态,后续若 zsh 同样拆资产需同步该测试。

Validation

  • 未运行任何验证(纯静态输入评审,不执行工具)。
  • 行数/计数类判定以 CI gate 实测为准。本快照 Test cosh-ng fast checksBuild cosh-ng release、Docs/Commit/PR Checks、license/cla 均 SUCCESS,但 Test cosh-ng 为 CANCELLED(06:08:48 完成),建议合并前确认取消原因(是否被新 push 取代或失败中断)并等待该检查转绿。
  • 剩余风险:M1–M5 的 bash 行为契约(errexit 框架保护、DEBUG trap 所有权矩阵、PROMPT_COMMAND 属性保真、termios 恢复)依赖 PR 描述的容器 FAIL→PASS 证据,本评审无法独立复核;termios_lifecycle.rs 的 import 解析(wait_timeoutnix/libc 用法)与 CI 稳定性未经静态评审验证;macOS 上 tests/shell_host 全量与 PTY-013 家族按 PR 说明属已记录排除范围,权威门禁为 Linux CI。PR 当前为 draft。

@SunnyQjm
SunnyQjm force-pushed the fix/cosh-2598-host-injection branch from a9d6f08 to 7a8023f Compare August 18, 2026 06:40
@SunnyQjm

Copy link
Copy Markdown
Collaborator Author

CI follow-up (head 7a8023f3): the previous Test cosh-ng CANCELLED was caused by this PR — the three new termios_lifecycle tests deadlocked on Linux. Root cause: the test joined its pty-master drain thread after drop(master), but the read only reaches EOF once every slave fd closes; the test function still held its own slave fd (and the SIGTERM path can leave the inner bash holding its inherited slave), so the join never returned and the job hit the workflow cancel. macOS never reached that code path (the module is gated there), which is why local runs missed it.

Fix (fixed up into the test commit): the drain thread is now deliberately detached with the rationale documented inline, and the test drops both pty fds explicitly after the assertions. Verified on Linux (alinux3 arm64 container, real binary): termios_lifecycle 3/3 pass in 2.4s — which also lands the M5 termios roundtrip contract green on the shipping target for the first time (exit/EOF/SIGTERM all restore the outer termios field-by-field).

@SunnyQjm

SunnyQjm commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator Author

PR number: #2632
head_sha: 7a8023f
reviewed_at: 2026-08-18T07:00:22Z

Findings

未发现 blocking package/module/public API 组织问题。本轮 head 与上一轮已评审 head 相同、patch 无变化;此前两条 P3(tests/raw_cli.rs 注释与 #[cfg] 门控分离、台账保留已低于阈值的登记行)在当前 patch 中保持闭环,本轮无新增阻断或非阻断结构项。

结构合规确认

  • 范围:代码改动全部在 crates/cosh-shell/ 内;large-file-inventory.md 为 SDD 台账同步(按台账"仅超限文件可登记"规则删除拆分后已低于阈值的登记行),符合 Scope Fence。未触碰非 cosh-shell crate、lockfile 或环境文件。
  • Ownermarker/bash.rsmarker/bash_marker.shinput_intent.sh 均归 shell_host/(OSC marker、shell 级输入意图分类),归属单一。slash/registry.rs:485 仅将 routing_c4 测试的 include_str! 跟随脚本体改指 bash_marker.sh,是资产拆分的必要配套(也是此前 fast checks FAILURE 的根因修复),跨 owner 边界已在 PR 说明与处置记录中解释,非混入无关 cleanup。
  • 大文件bash.rs 从登记 934 行降为约 10 行(按 diff 增量推算,以 CI 实测为准),执行的正是台账登记的拆分计划(脚本体迁 .sh + include_str!,镜像 input_intent.sh 先例),由行为修改推过 1000 行阻断线触发,属阈值驱动的必要配套。新建 bash_marker.sh 为 1093 行 .sh 静态资产,当前 check-layout.sh 与台账只治理 *.rs,不构成越线;首行空行保留旧内联字面量起始换行,字节一致性按 PR 说明由 osc golden 测试钉住。
  • Public API:无 lib.rs 改动,无新增 pub mod/pub usebash_marker_script 保持 pub(in crate::shell_host);patch 内无新增 cosh_shell:: self-crate 路径。
  • 依赖方向src/ 无新增 Rust import,forbidden dependency 清单不受影响。
  • Root src:无新增 root 实现文件。
  • 测试位置:回归落在既有 tests/shell_host/marker.rs(+176 行,测试文件行数归测试 SDD 治理),新增 Linux 门控的 tests/raw_cli/termios_lifecycle.rs 沿用既有 #[path] 挂载模式,Linux-only 注释已紧贴 #[cfg(target_os = "linux")] 门控;drain 线程按处置记录改为刻意 detach 并内联说明死锁根因。
  • 行为改动审读.sh 资产内,供参考):guarded call 改写(|| var=$?)在条件上下文中保留 0/1/2 三态且不再向用户 errexit 泄漏内部非零状态;_cosh_preexec_marker/_cosh_prompt_command 包装器的 errexit suspend/deferred-restore、_cosh_debug_trap_exit 行内撤防与 _cosh_rearm_debug_trap 所有权矩阵、PROMPT_COMMAND -x 属性记录/恢复与 guard-form hijack 值,均与 PR 描述的 M1–M4 契约一致,未发现静态层面矛盾。

Open Questions

  • 超大 shell 资产(bash_marker.sh 1093 行,先例 input_intent.sh)是否需要独立大小治理口径(按函数分区拆成多个 asset),已在此前轮次确认为后续 SDD 议题,本 PR 不展开。
  • slash/registry.rs:486 zsh 侧仍 include_str!("../shell_host/marker/zsh.rs")(内联脚本),与 bash 侧拆分后形态不对称;zsh 未超阈值,属既有状态,后续若 zsh 同样拆资产需同步该测试。

Validation

  • 未运行任何验证(纯静态输入评审,不执行工具)。
  • 行数/计数类判定以 CI gate 实测为准。
  • CI 快照(本输入新到,需合并前处理)Test cosh-ng fast checks SUCCESS(06:46:55)、Build cosh-ng release SUCCESS(06:45:30)、Docs/Commit/PR Checks 与 license/cla 均 SUCCESS,但 Test cosh-ng 为 FAILURE(06:50:00 完成)——这正是 PR 声明的权威 Linux 全量门禁。作者 06:40 的处置说明称 termios 死锁修复已在 alinux3 容器实测 3/3 通过,但该修复已含在当前 head 内、CI 仍失败,上一轮"import 解析未经静态验证"的疑虑虽由 fast checks 转绿基本消解,全量测试失败原因(是否仍由本 PR 新增用例引入)必须在合并前查明并等待转绿。
  • 剩余风险:M1–M5 的 bash 行为契约(errexit 框架保护、DEBUG trap 所有权矩阵、PROMPT_COMMAND 属性保真、termios 恢复)依赖 PR 描述的容器 FAIL→PASS 证据,本评审无法独立复核;macOS 上 tests/shell_host 全量与 PTY-013 家族按 PR 说明属已记录排除范围。PR 当前为 draft。

@SunnyQjm

Copy link
Copy Markdown
Collaborator Author

CI round-2 disposition (head moved to the current SHA): the Test cosh-ng FAILURE (marker::shell_host_linux_bash_natural_language_routes_directly_to_agent, second input delegated to native command-not-found) was a real regression introduced by this PR's M1×M4 interaction, root-caused down to the mechanism and fixed at the design level, not spot-patched:

  • Mechanism (probed on Linux, 5/5 deterministic, container two-arm + bare-base control + in-script probes): the errexit wrapper's tail statements run under the freshly re-armed DEBUG trap and produce junk dispatches at the end of every prompt frame. The stale-history containment check compares both directions, so an internal statement whose text contains the history entry false-matches — concretely _cosh_debug_trap_exit contains exit, the preloaded shared-HISTFILE tail was exit, the false match ran _cosh_begin_attempt "exit", and the poisoned attempt state made the next real command's cnf dispatch delegate natively.
  • Fix (fixed up into the behavior commit): an internal-namespace ownership guard — any BASH_COMMAND carrying the reserved _cosh_/_COSH_ namespace never enters the user dispatch path (early exit through the aggregate re-arm invariant). One obligation survives the early exit: trap … DEBUG lines still flag MAY_CHANGE, otherwise a user-installed combined trap would lose its ownership snapshot and path generation would stay trusted (pinned by the existing combined_debug_trap_fails_closed test, which went red→green during this fix).
  • Known deviation, recorded in the design doc: a user command line carrying the reserved namespace degrades to native execution (fail-safe, same as a stale miss; the internal namespace is not a supported surface).
  • Verification: Linux container — the failing CI test 1/1, combined_debug_trap 2/2, termios_lifecycle 3/3, and the shared-HISTFILE two-round repro flips to intercepted; macOS — marker:: 57/57. Preflight green (fmt/clippy/gates/drift).

@SunnyQjm
SunnyQjm force-pushed the fix/cosh-2598-host-injection branch from 7a8023f to f1a0117 Compare August 18, 2026 10:07
@SunnyQjm
SunnyQjm marked this pull request as ready for review August 18, 2026 10:23
@SunnyQjm
SunnyQjm requested a review from casparant as a code owner August 18, 2026 10:23

@qoderai qoderai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • [P1] termios 生命周期测试对 bash 存在隐式前置依赖,在极简 Linux 环境下可能整体被静默跳过,削弱恢复合同的回归覆盖。
  • [P2] NS-009 回归测试目前只校验 jobs 输出的并发 job number,未覆盖 $! 的作业号语义,存在残余差异未被捕获的空间。
  • [P2] termios 恢复轮询的 5 秒固定上限在资源紧张或噪声较大的 CI 环境中可能产生一次性假阴性,建议提供更弹性的恢复窗口或非致命降级路径。

🤖 Generated by QoderView workflow run

Comment thread src/cosh-ng/crates/cosh-shell/tests/shell_host/termios_lifecycle.rs Outdated
Comment thread src/cosh-ng/crates/cosh-shell/tests/shell_host/marker.rs Outdated
Comment thread src/cosh-ng/crates/cosh-shell/tests/raw_cli/termios_lifecycle.rs Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f1a0117e86

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/cosh-ng/crates/cosh-shell/src/shell_host/marker/bash_marker.sh Outdated
Comment thread src/cosh-ng/crates/cosh-shell/tests/raw_cli.rs Outdated
Comment thread src/cosh-ng/crates/cosh-shell/src/shell_host/marker/bash_marker.sh Outdated
@SunnyQjm

SunnyQjm commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator Author

PR number: #2632
head_sha: f1a0117
reviewed_at: 2026-08-18T11:01:37Z

Findings

  • [P2] src/cosh-ng/crates/cosh-shell/src/shell_host/marker/bash_marker.sh 约 L993–1001(_cosh_prompt_command 包装器尾部 set -e/return 0)— 与上一轮(同一 head)所报一致,本 head 未修:尾部语句不在内部命名空间 guard(约 L767)覆盖内。errexit 会话中 _cosh_rearm_debug_trap 重装 trap 后、_COSH_IN_PROMPT_COMMAND=0 已执行,尾部 set -e 触发 trap 时 _COSH_AT_PROMPT=1;只要上一条用户命令紧凑文本含 set-e(最典型即开启 errexit 的 set -e/set -euo pipefail 本身),stale-history 双向包含即命中并进入真实 dispatch:重复 emit 该命令 preexec、generation 递增、_COSH_AT_PROMPT 清零且 trap 保持撤防,下一行用户命令失去 preexec/begin_attempt,未知命令 cnf 因 attempt 指向 set -e 而降级 native delegate(与 CI round-2 失败同构的残留暴露面)。建议探针:errexit 会话 set -e 后紧跟未知命令,断言 (a) set -e preexec 仅出现一次,(b) 未知命令仍被拦截。修复方向:成功路径同样采用 veto 路径的 deferred restore(_COSH_RESTORE_ERREXIT=1),把 set -e 挪出已重挂 trap 的帧尾。
  • [P2] bash_marker.sh _cosh_rearm_debug_trap(约 L720 起)与 MAY_CHANGE 检测点 — 行级撤防(NS-009)使用户在复合命令/多命令行中执行的 trap - DEBUG 完全不可观测(trap 已撤不产生触发,history 行首 trap*DEBUG* 前缀匹配也不命中),prompt 边界 re-arm 将"用户清空"误判为"自身休眠"(current 空 + may_change=0)并重装 marker trap:echo hi; trap - DEBUG 即可复现,违背 re-arm 注释自述的 user-cleared 契约。旧实现无 prompt 边界 re-arm,被动尊重清空,故属行为回退(与 codex 行内评论一致,本评审独立推演确认;清空作为行首命令时仍被尊重)。修复方向:dispatch 分支对 history 行改用子串 *trap*DEBUG* 检测并权衡误报,或在 impl 撤防处打点以区分两种空 trap。
  • [P3] bash_marker.sh 约 L1086 guard 值 — 嵌套 bash 中 declare -F 失败使列表整体退出状态为 1;errexit 经 SHELLOPTS 导出与 PROMPT_COMMAND -x 泄漏同时成立时,按本 PR 自探契约(非零 PROMPT_COMMAND 列表状态杀死 errexit 交互会话),嵌套交互 bash 首个 prompt 可能被杀。嵌套×errexit 未测,属 INV-2 边角(前轮已报)。
  • [P3] tests/shell_host/marker.rs 约 L2960 exported_prompt_command_guard_value_is_silent_in_nested_bashbash -i -c 'cmd' 不打印主 prompt,PROMPT_COMMAND 不会求值,嵌套静默断言可能不依赖 guard 形态即恒真,INV-2 或未真正被覆盖;建议改为向嵌套交互 bash 喂 stdin 的形态复测。静态推断,置信度中等。
  • [P3] tests/raw_cli/termios_lifecycle.rs 约 L46 bash 探测静默 return(同 qoderai;与套件既有探测模式同风格,可显式 ignore/log 改善信号)与约 L143 恢复窗口固定 5s(同 qoderai)— 测试健壮性边角,不阻断。

结构维度:未发现 blocking package/module/public API 组织问题。本 head 与 10:32 已评审 head 完全相同、patch 无增量,上述为未闭环残留与本轮新增静态推断。

结构合规确认

  • 范围:代码改动全部在 crates/cosh-shell/ 内;台账同步符合 Scope Fence;未触碰非 cosh-shell crate、lockfile、环境文件。
  • Ownermarker/bash.rsbash_marker.shinput_intent.sh 均属 shell_host/ 单一 owner;slash/registry.rs:485 仅跟随资产拆分改指 include_str! 路径(前轮确认为必要配套)。
  • 大文件bash.rs 942→约 10 行,执行的正是台账登记的 .sh 资产拆分计划;台账按"仅超限文件可登记"删行,口径自洽。新建 bash_marker.sh 为 1114 行 .sh 静态资产,check-layout.sh 与台账只治理 *.rs,不构成越线;codex 所提"拆分超限 .sh 资产"按现行规则文本不成立(规则治理对象为 src/**/*.rs,台账拆分计划本身即迁 .shinput_intent.sh 先例在先),大 .sh 资产治理已登记为后续 SDD 议题。
  • Public API / 依赖方向 / Root src:无 lib.rs 改动,无新增 pub,无新增 cosh_shell:: self-crate 路径,src/ 无新增 Rust import,无新增 root 实现文件。
  • 测试位置termios_lifecycle.rs#[cfg(target_os = "linux")] 门控并沿用 #[path] 挂载;codex 所引 AGENTS.md 测试布局条款不在本评审输入内,无法核对归属判定,从 harness(raw_cli_command + support::raw_cli)看现放置自洽,留 open question。

Open Questions

  • qoderai 所提 NS-009 用例未覆盖 $! 作业号语义:jobs 输出 [2]/[3] 已证非串行,$! 校验可作加强项,非验收缺口。
  • zsh 侧 include_str! 形态不对称、超大 .sh 资产治理口径:延续前轮结论,留后续 SDD。

Validation

  • 未运行任何验证(纯静态输入评审,不执行工具);行数/计数判定以 CI gate 实测为准。
  • 本 head CI 全绿:Test cosh-ng SUCCESS(10:18:48,head 推送后的检查)、fast checks / release build / Docs / Commit / PR Checks / cla 均 SUCCESS;前轮 CI 阻塞疑虑已解除。PR 已转 non-draft,合并前剩余门槛即上述 P2 的处置。
  • 剩余风险:P2/P3 均为基于 functrace/DEBUG 触发语义的静态推断,需 marker 级探针证实或证伪;M1–M5 行为契约依赖 PR 容器 FAIL→PASS 证据,本评审无法独立复核。

@SunnyQjm
SunnyQjm force-pushed the fix/cosh-2598-host-injection branch 2 times, most recently from 9250783 to c65e4ff Compare August 18, 2026 11:59
@SunnyQjm

Copy link
Copy Markdown
Collaborator Author

PR number: #2632
head_sha: c65e4ff
reviewed_at: 2026-08-18T12:50:09Z

Findings

  • [P2] src/cosh-ng/crates/cosh-shell/src/shell_host/marker/bash_marker.sh _cosh_prompt_command 包装器尾部(约 L1000 附近的 set -e/return 0)— 与上一轮(f1a0117)所报一致,本 head 仍未修:尾部非命名空间语句不在内部命名空间 guard(*_cosh_*/*_COSH_*)覆盖内。errexit 会话中 _cosh_rearm_debug_trap 重装 trap、_COSH_IN_PROMPT_COMMAND=0 之后,尾部 set -e 触发 trap 时 _COSH_AT_PROMPT=1;只要上一条用户命令紧凑文本含 set-e(最典型即刚执行的 set -e/set -euo pipefail 本身),stale-history 双向包含即命中并进入真实 dispatch:重复 emit 该命令 preexec、generation 递增、_COSH_AT_PROMPT 清零且 trap 随帧尾撤防——下一条用户命令整行失去 preexec/begin_attempt,未知命令 cnf 因 attempt 指向 set -e 而降级 native delegate(与 CI round-2 已修复回归同构的残留暴露面,guard 只堵住了命名空间语句)。建议探针:errexit 会话 set -e 后紧跟未知命令,断言 (a) set -e preexec 仅出现一次,(b) 未知命令仍被拦截。修复方向:成功路径同样采用 veto 路径的 deferred restore(_COSH_RESTORE_ERREXIT=1),把 set -e 挪出已重挂 trap 的帧尾。
  • [P2] bash_marker.sh _cosh_rearm_debug_trap(约 L720 起)与 _cosh_preexec_marker_impltrap*DEBUG* 行首检测 — 行级撤防(NS-009)使用户在复合/多命令行中执行的 trap - DEBUG 完全不可观测(trap 已撤不产生触发,history 行首前缀匹配也不命中),prompt 边界 re-arm 将"用户清空"误判为"自身休眠"(current 空 + may_change=0)并重装 marker trap:echo hi; trap - DEBUG 即可复现,违背 re-arm 注释自述的 user-cleared 契约;旧实现无 prompt 边界 re-arm、被动尊重清空,属行为回退(清空作为行首命令时仍被尊重)。修复方向:dispatch 分支对 history 行改用子串 *trap*DEBUG* 检测并权衡误报,或在 impl 撤防处打点以区分两种空 trap。
  • [P3] tests/shell_host/marker.rs 约 L2960 exported_prompt_command_guard_value_is_silent_in_nested_bashbash -i -c 'cmd' 不打印主 prompt,PROMPT_COMMAND 不会求值,嵌套静默断言可能不依赖 guard 形态即恒真,INV-2 或未真正被覆盖;建议改为向嵌套交互 bash 喂 stdin 的形态复测。静态推断,置信度中等。
  • [P3] bash_marker.sh 尾部 PROMPT_COMMAND guard 值 — 嵌套 bash 中 declare -F 失败使列表整体退出状态为 1;errexit 经 SHELLOPTS 导出与 PROMPT_COMMAND -x 泄漏同时成立时,按本 PR 自探契约(非零 PROMPT_COMMAND 列表状态杀死 errexit 交互会话),嵌套交互 bash 首个 prompt 可能被杀。嵌套×errexit 未测,属 INV-2 已知取舍的边角。

结构维度:未发现 blocking package/module/public API 组织问题。

结构合规确认

  • 范围:代码改动全部在 crates/cosh-shell/ 内;large-file-inventory.md 为 SDD 台账同步(按"仅超限文件可登记"规则删除拆分后已低于阈值的登记行),符合 Scope Fence;未触碰非 cosh-shell crate、lockfile、环境文件。
  • Ownermarker/bash.rsbash_marker.shinput_intent.sh 均属 shell_host/ 单一 owner;slash/registry.rs:485 仅跟随资产拆分改指 include_str! 路径,跨 owner 配套已解释,非混入无关 cleanup。
  • 大文件bash.rs 942→约 10 行(按 diff 推算,以 CI 实测为准),执行的正是台账登记的 .sh 资产拆分计划;新建 bash_marker.sh 为 1114 行 .sh 静态资产,check-layout.sh 与台账治理对象为 *.rs,不构成越线;首行空行保留旧内联字面量起始换行,字节一致性按 PR 说明由 osc golden 测试钉住。
  • Public API / 依赖方向 / Root src:无 lib.rs 改动,无新增 pub/pub mod,无新增 cosh_shell:: self-crate 路径,src/ 无新增 Rust import,无新增 root 实现文件。
  • 测试位置termios_lifecycle.rs#[cfg(target_os = "linux")] 门控并沿用 #[path] 挂载,Linux-only 注释已紧贴门控;drain 线程刻意 detach 并内联说明死锁根因。测试布局归属条款(测试 SDD/AGENTS.md)不在本评审输入内,从 harness(raw_cli_command + support::raw_cli)看现放置自洽,留 open question。
  • 前轮闭环确认$! 作业语义(uniq-pids-3)与恢复窗口具名常量(RAW_ENGAGE_DEADLINE=20s/RESTORE_DEADLINE=10s)已按 qoderai 意见采纳;bash 探测前置经作者论证(inner shell 即 bash,属功能前置)不予采纳可接受。

Open Questions

  • 超大 shell 资产(bash_marker.sh 1114 行,先例 input_intent.sh)是否需要独立大小治理口径,已确认为后续 SDD 议题,本 PR 不展开。
  • zsh 侧 include_str!("../shell_host/marker/zsh.rs")(内联脚本)与 bash 侧拆分后形态不对称;zsh 未超阈值,属既有状态。

Validation

  • 未运行任何验证(纯静态输入评审,不执行工具);行数/计数判定以 CI gate 实测为准。
  • CI 快照Test cosh-ng fast checks SUCCESS(12:14:56)、Build cosh-ng release SUCCESS(12:10:03)、Docs/Commit/PR Checks 与 license/cla 均 SUCCESS,但 Test cosh-ng 为 CANCELLED(12:17:45)——这是 PR 声明的权威 Linux 全量门禁,且本快照可能对应的是 12:31 fixup 前的中间 head;合并前必须确认取消原因并等待当前 head 全量检查转绿。
  • 剩余风险:两条 P2 与 P3 均为基于 functrace/DEBUG 触发语义的静态推断,需 marker 级探针证实或证伪;M1–M5 行为契约依赖 PR 描述的容器 FAIL→PASS 证据,本评审无法独立复核;macOS 上 tests/shell_host 全量与 PTY-013 家族按 PR 说明属已记录排除范围。

@SunnyQjm
SunnyQjm force-pushed the fix/cosh-2598-host-injection branch from c65e4ff to edee581 Compare August 18, 2026 13:06
@SunnyQjm

SunnyQjm commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator Author

PR number: #2632
head_sha: edee581
reviewed_at: 2026-08-19T02:47:47Z

Findings

  • [P1] src/cosh-ng/crates/cosh-shell/tests/shell_host/marker.rs 约 L3007 errexit_session_survives_dispatch_and_exits_cleanly — 与 BryanHeBY 评审一致并经本评审静态确认:三条断言全部针对终端输出,测试未发送 exit、未断言会话退出码。而本 PR 宣称修复的 SEM-019([P1][cosh-ng] 信号与退出状态传播:SIGPIPE 继承处置 + set -e 会话退出码(IO-009/SEM-019) #2541 子项 B,引述其原文)实测症状是两侧 framed 输出一致、唯会话退出码 oracle=0/candidate=1 不同——退出码这根轴当前零覆盖,测试名中的 "exits_cleanly" 名实不符(probe-two 覆盖的是"会话未被杀"半边,属真实但不同的格子)。payload 与 SEM-019 原文亦有三处偏离:false && : 而非 false && printf bad、逐行输入而非 { …; } 复合命令、无 exit 收尾。harness 侧据 BryanHeBY 引用 shell_host/model.rs:213pub exit_status: Option<i32>(该文件不在本输入 patch 内,未能独立核对)。建议 payload 对齐 SEM-019 原文并以 exit 收尾、断言 exit_status == Some(0);若暂不覆盖,应改测试名并在 PR 中显式声明该轴由容器验收兜底。
  • [P2] src/cosh-ng/crates/cosh-shell/src/shell_host/marker/bash_marker_frames.sh 约 L63–L85(_cosh_prompt_command 成功路径尾部 set -e,约 L76)— 自 f1a0117 起连续多轮未闭环,本 head 仍未修。机制(静态推演,与 CI round-2 已探针证实的回归同构):impl 尾部 _cosh_rearm_debug_trap(约 L104)重装 trap、_COSH_IN_PROMPT_COMMAND=0 之后,包装器尾部 set -e 触发 trap 时 _COSH_AT_PROMPT=1set -e 不含 _cosh_/_COSH_ 命名空间、内部 guard 不拦截;只要上一条用户命令紧凑文本与 set-e 互为子串(最典型即刚执行的 set -e/set -euo pipefail),stale-history 双向包含即通过并进入真实 dispatch:重复 emit 该命令 preexec、generation 递增、_COSH_AT_PROMPT 清零且 trap 经 _cosh_debug_trap_exit 置 dormant 撤防——下一条用户命令整行失去 preexec/begin_attempt,未知命令 cnf 因 attempt 指向 set -e 降级 native delegate。建议探针:errexit 会话 set -euo pipefail 后紧跟未知命令,断言 (a) 该行 preexec 仅出现一次,(b) 未知命令仍被拦截。修复方向:成功路径同样采用 veto 路径的 deferred restore(_COSH_RESTORE_ERREXIT=1),恢复落入下一帧 trap handler 内(DEBUG trap 不递归,天然无触发)。
  • [P2] NS-005 的 PS2 半边无覆盖(BryanHeBY 提出)— 本 patch 全文无任何 PS2 保存/恢复/透传逻辑(可静态确认),而 BryanHeBY 引述 [P1][cosh-ng] exported 变量数组再赋值丢失 -x 属性(NS-005) #2540 验收含「环境 PS2 被采用」、[Umbrella][P0][cosh-ng] 宿主注入非破坏合同:PS1/PS2/PROMPT_COMMAND/trap/termios 保存-组合-恢复语义 #2598 将 NS-005 整格列为转 PASS。[P1][cosh-ng] exported 变量数组再赋值丢失 -x 属性(NS-005) #2540 issue 原文不在本评审输入内,该验收前提无法独立核对;若属实,本 PR 对 NS-005 只覆盖 -x 属性半边,建议要么补 PS2 处理与机制记录([Umbrella][P0][cosh-ng] 宿主注入非破坏合同:PS1/PS2/PROMPT_COMMAND/trap/termios 保存-组合-恢复语义 #2598 自述"强假设须先确证"),要么把 PS2 从本 PR 的 Fixes 范围显式摘出(降级为 Refs/部分修复)。
  • [P3] src/cosh-ng/crates/cosh-shell/tests/shell_host.rs 约 L66–L76 — Linux-only 说明注释(5 行)写在未门控的 mod termios; 正上方,而真正被 #[cfg(target_os = "linux")] 门控的 mod termios_lifecycle; 在其后 3 行。注释与被说明对象分离,系此前 raw_cli.rs 已修复的同类问题在套件迁移时再次引入(前轮已报,未闭环);建议移到 #[cfg(...)] 正上方。
  • [P3] tests/shell_host/marker.rs 约 L2974 exported_prompt_command_guard_value_is_silent_in_nested_bashbash --noprofile --norc -i -c 'cmd' 不进入交互读取循环,PROMPT_COMMAND 不会求值,!contains("command not found") 断言可能不依赖 guard 形态即恒真,INV-2 或未真正被覆盖(前轮已报,未闭环);建议改为向嵌套交互 bash 喂 stdin 的形态复测。静态推断,置信度中等。
  • [P3] bash_marker_frames.sh 约 L172 guard 值 × 嵌套 × errexit — 嵌套 bash 中 declare -F 失败使列表整体退出状态为 1;errexit 经 SHELLOPTS 继承与 PROMPT_COMMAND -x 泄漏同时成立时,按本 PR 自探契约(非零 PROMPT_COMMAND 列表状态杀死 errexit 交互会话),嵌套交互 bash 首个 prompt 可能被杀。嵌套×errexit 未测,属 INV-2 已知取舍的边角(前轮已报)。
  • [P3] guard 值导出偏差(BryanHeBY 列为 P2,本评审降为 P3)— export PROMPT_COMMAND 恢复后 cosh guard 串进入所有子进程环境,与裸 bash(子进程见用户原值)存在可观测差异;属注入机制固有代价,且 PR 描述 "Known deviations" 已记录该取舍。剩余动作是按 BryanHeBY 建议同步进 [Umbrella][P0][cosh-ng] 宿主注入非破坏合同:PS1/PS2/PROMPT_COMMAND/trap/termios 保存-组合-恢复语义 #2598 非目标清单,避免后续被当新 bug 重开。
  • [P3] src/cosh-ng/crates/cosh-shell/src/shell_host/marker/bash.rs:10OnceLock<String> + 运行时 .concat() 可用编译期 concat!(include_str!(...), ...) 直接得到 &'static str,省去一次性约 35KB 堆分配与懒初始化(前轮已报,纯简化建议,不阻断)。

结构维度:未发现 blocking package/module/public API 组织问题。本 head 与 13:26 已评审 head 相同、patch 无增量;本轮触发为 BryanHeBY 的新人工评审意见(02:34),已逐条静态复核并并入上述 Findings,其余为前轮未闭环残留项的复核确认。

结构合规确认

  • 范围:代码改动全部在 crates/cosh-shell/ 内;large-file-inventory.md 为 SDD 台账同步(按台账"仅超限文件可登记"规则删除拆分后已低于阈值的登记行),符合 Scope Fence;未触碰非 cosh-shell crate、lockfile、环境文件。
  • Ownerbash.rs 与三个 marker 资产、input_intent.sh 均属 shell_host/ 单一 owner;core(init+emit)/dispatch(attempt/cnf 链)/frames(precmd/prompt 帧+install)按职责切分。slash/registry.rs 仅将 routing_c4 测试的 include_str! 跟随脚本体改指 bash_marker_core.sh(slash case 列表所在资产),是拆分的必要配套,跨 owner 边界已解释。
  • 大文件bash.rs 942→约 21 行(按 diff 推算,以 CI 实测为准),执行的正是台账登记的 .sh 资产拆分计划;三个新建资产 437/517/181 行均低于 700,新建文件走拆分而非 waiver,符合台账规则;check-layout.sh 与台账治理对象为 *.rs.sh 资产不构成越线。
  • Public API / 依赖方向 / Root src:无 lib.rs 改动,无新增 pub/pub modbash_marker_script 保持 pub(in crate::shell_host);无新增 cosh_shell:: self-crate 路径;src/ 无新增 Rust import(nix/wait_timeout 仅出现于 tests,且文件清单无 Cargo.toml 变更,无新增依赖);无新增 root 实现文件。
  • 测试位置termios_lifecycle.rsshell_host integration target 且自包含(codex P1 已采纳),#[cfg(target_os = "linux")] 门控 + #[path] 挂载沿用既有模式;marker.rs +180 行测试归测试 SDD 治理。
  • 前轮闭环确认:用户清空 trap(dormant 台账 + re-arm 三向判定 + *trap*DEBUG* 包含式检测)、NS-009 的 $! 三 distinct pid 断言、恢复窗口具名常量(RAW_ENGAGE_DEADLINE/RESTORE_DEADLINE)均已落地;termios 测试 bash 前置经作者论证(inner shell 即 bash,属功能前置)可接受。

Open Questions

Validation

  • 未运行任何验证(纯静态输入评审,不执行工具);行数/计数判定以 CI gate 实测为准。
  • CI 快照(本 head,全绿)Test cosh-ng SUCCESS(13:17:41)、fast checks SUCCESS(13:13:01)、release build SUCCESS(13:12:42)、Docs/Commit/PR Checks 与 license/cla 均 SUCCESS;权威 Linux 全量门禁已绿。注意 BryanHeBY 评审意见(02:34)晚于该批检查,head 未变故结论仍适用。
  • 剩余风险:P1 的退出码覆盖缺口与 P2 的包装器尾部 set -e 暴露面为静态推演(后者与 round-2 已探针证实机制同构),需 marker 级探针证实或证伪;M1–M5 行为契约(errexit 帧保护、DEBUG trap 所有权矩阵、PROMPT_COMMAND 属性保真、termios 恢复)依赖 PR 容器 FAIL→PASS 证据,本评审无法独立复核;shell_host/model.rsexit_status 字段不在本输入 patch 内,按 BryanHeBY 引用采纳;macOS 上 tests/shell_host 全量与 PTY-013 家族属已记录排除范围。PR 已转 non-draft。

@BryanHeBY

Copy link
Copy Markdown
Collaborator

审阅 @ edee5819:1 处 P1(SEM-019 覆盖缺口),2 处 P2

注入层的保存-组合-恢复实现质量很高:bash_marker_frames.sh:124 只解析 declare -p 的 flags token(注释明确排除「值里含 x」的假阳性),bash_marker_dispatch.sh:229 对 NS-009 的 reap 窗口机制说明到位,marker.rs:2927「INV-1 reverse cell is not constructible under this harness」主动披露覆盖限制 —— 这三点都很好。

以下三条建议在合并前处置。

P1 — 以 SEM-019 命名的测试覆盖的是另一格,退出码这根轴无覆盖

marker.rs:3007 errexit_session_survives_dispatch_and_exits_cleanly 的全部断言:

assert!(terminal.contains("probe-one"));
assert!(terminal.contains("probe-two"));
assert!(terminal.contains("errexit-on"));

三条都针对终端内容。但 #2541 子项 B 原文写的是:

payload { set -e; false && printf bad; printf errexit-context } 两侧 framed 输出完全一致
(payload sha 相同、ready 均见),唯一差异:会话退出码 oracle=0 / candidate=1

#2598 的现状根因把可观测后果分成两半:「直接杀死交互 shell / 污染会话退出码」。这条测试的 probe-two 断言覆盖的是前一半(会话没被 DEBUG trap 里的内部失败杀掉),这是真实覆盖,不是空转。

但它没有覆盖后一半,而后一半才是 SEM-019 实测到的那一格 —— #2541-B 明确写了该场景下两侧 framed 输出完全一致,即 shell 没有死,唯一差异是退出码。#2541 的验收也是照这一格写的:「不污染会话退出码(与 bash 一致 exit 0)」。

所以问题不是测试无效,而是以 SEM-019 命名的测试断言的不是 SEM-019 测的那根轴,该轴目前无任何覆盖。

不是 harness 限制:shell_host/model.rs:213 就有 pub exit_status: Option<i32>

另外 payload 与 SEM-019 有三处偏离:false && : 而非 false && printf bad;逐行交互输入而非 { …; } 复合命令;没有 exit,因此完全没走会话退出路径 —— 而 #2541 的疑似根因写的正是「B 在会话退出/last-status 传播路径」。

oracle=0 这一侧我核过手册,是对的。bash(1) set -e:

The shell does not exit if the command that fails is … part of any command executed in a
&& or || list except the command following the final && or ||

失败的 false 不是 final && 之后的命令,不触发退出;随后 printf 返回 0。实测 bash -c '{ set -e; false && printf bad; printf errexit-context; }' → exit 0。

建议:payload 改用 SEM-019 原文并以 exit 收尾,加断言 output.exit_status == Some(0)

P2 — 完全没有 PS2 处理,而 NS-005 的验收含 PS2

grep -rn PS2bash_marker_*.shcosh-shell/src/ 下只命中讲续行状态的注释,没有任何保存/恢复/透传逻辑。而 #2540 的验收含「环境 PS2 被采用」,#2598 把 NS-005 整格列为转 PASS。

一条容易踩的口径先放在这里:环境 PS2 只被交互 bash 采用

$ PS2='__ENVPS2__ ' bash -c 'printf "[%s]" "$PS2"'   → []
   交互 pty 下同样环境                                 → __ENVPS2__

bash(1) 的 PS2 default 是 `> ' —— #2540 证据里 candidate 的 > 恰好就是这个默认值,所以诊断方向是对的,只是机制未确证。

#2598 自己要求「强假设(须先确证)… 修复前先在隔离 worktree 复现并确证机制,不带假设写码」。NS-005 的 -x 半边做到了(机制进注释、有测试),PS2 半边既无改动也无机制记录。建议要么补,要么在本 PR 里把 PS2 从 NS-005 的验收范围显式摘出。

P2 — 导出的 guard value 是一处真实的可观测偏差,建议显式声明

bash_marker_frames.sh:176-177-x 恢复后,cosh 自己的 guard 串就进了每个子进程的环境。marker.rs 的 INV-2 测的是它无害(不刷 command not found),这没问题;但它不是等价:

cosh 会话:  env | grep PROMPT_COMMAND  →  cosh 的 guard 串
裸 bash:     env | grep PROMPT_COMMAND  →  用户自己的值

#2598 的不变量写的是「用户可观测的 shell 状态 … 必须与裸 bash 交互会话等价」。

同名变量无法既导出用户值又承载 cosh 的钩子,所以这是注入这个手段的固有代价,不是实现缺陷。但按 #2598 的裁决口径它属于偏差,建议在 PR 描述或 #2598 的非目标清单里写明,免得后续被当成新 bug 重开。

手册引文经 bash(1) 原文核对;PS2 与 set -e 探针为本机 bash 5.2 实测。优先级层面的另一条(NS-005 的 -x 影响面)与本 diff 无关,我提到 #2540 去。

@SunnyQjm

Copy link
Copy Markdown
Collaborator Author

Round-4 disposition (head d0c8593)

All five findings from the 2026-08-18T13:44 auto-review are closed:

[P2] prompt-frame tail set -e residual exposure — adopted, probe-confirmed first. Reproduced your exact scenario on the shipping target (real pty): set -euo pipefail followed by an unknown command showed the predicted duplicate preexec emit (count 2). Fix is the design-level convergence you suggested: the success path now uses the same deferred restore as the veto path (_COSH_RESTORE_ERREXIT=1, consumed at the next frame entry inside the trap handler where DEBUG does not recurse), plus one cell your sketch did not need to cover: if the marker trap is idle (user cleared it, no next frame to consume the flag), the wrapper restores in place — with no trap armed the bare statement cannot dispatch. Post-fix probe: set-line preexec count = 1, and NL interception stays healthy across set -e / -u / -o pipefail / -euo pipefail (identical to no-set baseline). One clarification on the suggested assertion (b): a single-word unknown command delegates to native cnf by design (cnf-layer scope), so the interception assertion was run in NL form. Spec updated (design.md S5-补强 clause 4 records the falsified earlier reading and the corrected mechanism). Remaining recorded non-goal: the wrapper-tail return 0 text (mutual-substring odds with a user command are negligible).

[P3] comment placement — adopted; the Linux-only note now sits directly above the #[cfg] gate (a stray duplicate mount that crept in during an earlier fixup squash was also removed; caught by compile).

[P3] nested-bash test never entering the read loop — adopted; the regression now pipes the script through stdin to bash --noprofile --norc -i, so the nested shell serves real prompts and evaluates the leaked guard value at each one. Green on the shipping target (65/65 shell_host marker+termios).

[P3] nested × errexit corner — acknowledged as the recorded INV-2 trade-off; not expanded here.

[P3] OnceLock<String>concat! — adopted; you were right that concat!(include_str!(...), ...) composes at compile time. bash_marker_script() now returns the concatenation as a true &'static str with no runtime allocation.

Also rebased onto latest main (9 commits) before push; full preflight green (fmt/clippy/layout/test-inventory), Linux container suite 65/65.

@SunnyQjm

SunnyQjm commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator Author

PR number: #2632
head_sha: d0c8593
reviewed_at: 2026-08-19T03:53:37Z

Findings

  • [P1] src/cosh-ng/crates/cosh-shell/tests/shell_host/marker.rs 约 L3007 errexit_session_survives_dispatch_and_exits_cleanly — SEM-019 退出码轴仍无覆盖(跨轮未闭环,本 patch 中该测试与上轮逐字一致)。三条断言全部针对终端输出;未发送 exit、未断言会话退出码,而 SEM-019 实测症状([P1][cosh-ng] 信号与退出状态传播:SIGPIPE 继承处置 + set -e 会话退出码(IO-009/SEM-019) #2541 子项 B,按 PR 描述引述)是两侧 framed 输出一致、唯退出码 oracle=0/candidate=1 不同——测试名中的 "exits_cleanly" 名实不符。payload 与原文仍有三处偏离:false && : vs false && printf bad、逐行输入 vs { …; } 复合命令、无 exit 收尾。建议 payload 对齐 SEM-019 原文并以 exit 收尾、断言 exit_status == Some(0)(harness 字段 shell_host/model.rsexit_status 不在本输入 patch 内,未能独立核对);若暂不覆盖,应改测试名并在 PR 显式声明该轴由容器验收兜底。
  • [P2] NS-005 的 PS2 半边无覆盖(跨轮未闭环)— 本 patch 全文无任何 PS2 保存/恢复/透传逻辑(可静态确认),而人工评审引述 [P1][cosh-ng] exported 变量数组再赋值丢失 -x 属性(NS-005) #2540 验收含「环境 PS2 被采用」、umbrella 将 NS-005 整格列为转 PASS。[P1][cosh-ng] exported 变量数组再赋值丢失 -x 属性(NS-005) #2540 原文不在本输入内,该验收前提无法独立核对;若属实,本 PR 对 NS-005 只覆盖 -x 属性半边,建议补 PS2 处理与机制记录,或把 PS2 从本 PR 的 Fixes 范围显式摘出(降级 Refs/部分修复)。
  • [P3] tests/shell_host/marker.rs 约 L2984 嵌套 bash 测试 payload 转义 — Rust 源码 '%s\\\\n' 经 Rust→shell 双层转义后外层 printf 实际收到 %s\\n,输出字面 \n 两字符而非换行:内层 bash 实际收到单行(exit 从未作为独立命令送达,内层靠 EOF 退出),与注释自述的"逐行脚本"意图不符。因内层提示符在读取该行前后均出现、guard 值仍被真实求值,nested-done!contains("command not found") 两条断言依然有效,INV-2 覆盖结论不被颠覆;建议改为 '%s\\n'(Rust 层两个反斜杠)使 payload 与意图一致。纯静态推演,机制确定,置信度中高。
  • [P3] guard 值导出偏差(人工评审建议显式声明)— PR 描述 "Known deviations" 已记录该取舍(INV-2),剩余动作是按其建议同步进 umbrella 非目标清单,避免后续被当新 bug 重开。

结构维度:未发现 blocking package/module/public API 组织问题。本 head 与上一轮已评审 head 相同、patch 无增量,上述为未闭环残留项的静态复核确认。

结构合规确认

  • 范围:代码改动全部在 crates/cosh-shell/ 内;large-file-inventory.md 为 SDD 台账同步(按台账"仅超限文件可登记"规则删除拆分后已低于阈值的登记行),符合 Scope Fence;未触碰非 cosh-shell crate、lockfile、环境文件。
  • Ownerbash.rs 与三个 marker 资产、input_intent.sh 均属 shell_host/ 单一 owner;core(init+emit)/dispatch(attempt/cnf 链)/frames(precmd/prompt 帧+install)按职责切分。slash/registry.rs 仅将 routing_c4 测试的 include_str! 跟随脚本体改指 bash_marker_core.sh(slash case 列表所在资产),是资产拆分的必要配套,跨 owner 边界已在 PR 说明与处置记录中解释。
  • 大文件bash.rs 942→17 行(按 diff 实见,以 CI 实测为准),执行的正是台账登记的 .sh 资产拆分计划;三个新建资产 437/517/195 行均低于 700,新建文件走拆分而非 waiver,符合台账规则;check-layout.sh 与台账治理对象为 *.rs.sh 资产不构成越线。concat!(include_str!(...), ...) 编译期拼接为 &'static strbash_marker_script 签名与 pub(in crate::shell_host) 可见性不变;core 资产首行空行保留旧内联字面量起始换行,字节一致性按 PR 说明由 osc golden 测试钉住。
  • Public API / 依赖方向 / Root src:无 lib.rs 改动,无新增 pub/pub mod;patch 内无新增 cosh_shell:: self-crate 路径;src/ 无新增 Rust import(nix/wait_timeout 仅出现于 tests,文件清单无 Cargo.toml 变更,无新增依赖);无新增 root 实现文件。
  • 测试位置termios_lifecycle.rsshell_host integration target、Linux 门控 + #[path] 挂载沿用既有模式且自包含;tests/shell_host.rs 的 Linux-only 注释已紧贴 #[cfg] 门控;marker.rs +185 行测试归测试 SDD 治理。
  • 前轮闭环确认:prompt 帧尾 set -e 暴露面(成功路径 deferred restore + idle-trap 就地恢复)、用户清空 trap 三向判定、嵌套 bash 测试改 stdin 管道形态、OnceLockconcat!、注释贴门控——均已落地且与本 patch 静态一致。

Open Questions

Validation

  • 未运行任何验证(纯静态输入评审,不执行工具);行数/计数判定以 CI gate 实测为准。
  • CI 快照(本 head,全绿)Test cosh-ng SUCCESS(03:11:02)、Test cosh-ng fast checks SUCCESS(03:07:08)、Build cosh-ng release SUCCESS(03:05:27)、Docs/Commit/PR Checks 与 license/cla 均 SUCCESS,且均晚于当前 head 推送,权威 Linux 全量门禁已绿。
  • 剩余风险:P1 的退出码覆盖缺口与 P2 的 PS2 范围为跨轮次未闭环项,合并前需处置或显式声明兜底;M1–M5 行为契约(errexit 帧保护、DEBUG trap 所有权矩阵、PROMPT_COMMAND 属性保真、termios 恢复)依赖 PR 描述的容器 FAIL→PASS 证据与作者探针结论,本评审无法独立复核;macOS 上 tests/shell_host 全量与 PTY-013 家族属已记录排除范围。PR 已转 non-draft。

@SunnyQjm
SunnyQjm force-pushed the fix/cosh-2598-host-injection branch from d0c8593 to ae90a91 Compare August 19, 2026 09:22
@SunnyQjm

Copy link
Copy Markdown
Collaborator Author

人工评审处置(@BryanHeBY,head 移至 ae90a91

三条全部核实成立并处置;另致歉:这条评论发出时间在我上一轮机器评审处置窗口之前 26 分钟,活动过滤窗口把它漏掉了一轮,本轮补上。

P1 — SEM-019 退出码轴:采纳,新增按原文 payload 的主轴回归

您的判读完全正确:既有 errexit_session_survives_dispatch_and_exits_cleanly 钉的是「会话存活」半边(V-B4 格,保留),退出码这根轴(#2541-B 实测格)确实无覆盖。已新增 errexit_compound_payload_keeps_session_exit_code_clean

Shipping target(Linux 容器,真实 PTY)实测:断言通过——即 M2 守卫修复真实钉住了该轴,不再只是终端内容侧写。

P2 — PS2:机制已探针确证,显式摘出本 PR 验收范围

按伞单「先确证机制再写码」条款,先做了真实 PTY A/B 探针(容器,PS2='__ENVPS2__ ' 环境下 injected rcfile 会话 vs 裸 bash -i):两侧 $PS2 均为环境值——注入层不触碰 PS2,对环境 PS2 的采用与裸交互 bash 逐点一致。因此 #2540 证据中 candidate 显示 > 默认值的偏差不源于本 diff 的注入层;疑似机制在宿主 env 构造层,未确证,归 #2540 继续追踪。已在 PR body 的 Known deviations 里显式声明「PS2 摘出本 PR 的 NS-005 范围」,并注明:若您倾向更严格的口径,可把 Fixes #2540 降级为 Refs,待 PS2 半边落地后再关——请裁决。

P2 — exported guard value 可观测偏差:采纳,已显式声明

同意您的定性:INV-2 回归钉的是「无害」而非「等价」,env | grep PROMPT_COMMAND 的差异是注入手段的固有代价(同名变量无法两全)。已按 #2598 裁决口径写入 PR body Known deviations(非目标声明),并同步进 spec,防止后续被当新 bug 重开。

另谢三处正面确认(flags token 假阳性排除、NS-009 reap 窗口说明、INV-1 反向格披露)——这三处的写法会保持。

@SunnyQjm

SunnyQjm commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator Author

PR number: #2632
head_sha: ae90a91
reviewed_at: 2026-08-19T09:52:29Z

Findings

  • [P3] src/cosh-ng/crates/cosh-shell/tests/shell_host/marker.rs 约 L2990(exported_prompt_command_guard_value_is_silent_in_nested_bash 的嵌套 printf payload,测试函数约 L2974;跨两轮未闭环)— 本 head patch 中仍为 Rust 源码 '%s\\\\n'(4 反斜杠)。经 Rust→shell 双层解码,外层 printf 实际拿到格式 %s\\n,其中 \\ 被 printf 解释为字面反斜杠:输出的是字面 \n 两字符而非换行。内层 bash 因此只收到单行,exit 被吞进 printf 参数(内层靠 EOF 退出),只服务一个 prompt,与注释自述的"逐行脚本、内层逐 prompt 求值"意图不符。核心断言仍偶然成立:guard 值在内层唯一 prompt 前仍被真实求值且保持静默,nested-done 子串命中、!contains("command not found") 有效,INV-2 覆盖结论不被颠覆,但覆盖强度低于意图。佐证:同测试内 hooks 行用的是正确的 \\n(Rust 2 反斜杠)。建议改为 '%s\\n'(Rust 层两反斜杠)使 payload 与意图一致。纯静态推演,机制确定,置信度中高。

结构维度:未发现 blocking package/module/public API 组织问题。本 head 与上一轮已评审 head 相同、patch 无增量;本轮变化来自状态检查推进(CI 已转绿),上述 P3 为残留项复核确认。

结构合规确认

  • 范围:代码改动全部在 crates/cosh-shell/ 内;large-file-inventory.md 为 SDD 台账同步(按台账"仅超限文件可登记"规则删除拆分后已低于阈值的登记行),符合 Scope Fence;未触碰非 cosh-shell crate、lockfile、环境文件。
  • Ownerbash.rs 与三个 marker 资产、input_intent.sh 均属 shell_host/ 单一 owner,core/dispatch/frames 按职责切分;slash/registry.rs 仅将 routing_c4 测试的 include_str! 跟随脚本体改指 bash_marker_core.sh(slash case 列表所在资产),是资产拆分的必要配套,跨 owner 边界已解释。
  • 大文件bash.rs 942→17 行(diff 实见),执行的正是台账登记的 .sh 资产拆分计划;三个新建资产 437/517/195 行均低于 700,新建文件走拆分而非 waiver,符合台账规则;check-layout.sh 与台账治理对象为 *.rs.sh 资产不构成越线。concat!(include_str!(...), ...) 编译期拼接,bash_marker_script 保持 pub(in crate::shell_host)&'static str 签名。
  • Public API / 依赖方向 / Root src:无 lib.rs 改动,无新增 pub/pub mod;patch 内无新增 cosh_shell:: self-crate 路径;src/ 无新增 Rust import(nix/wait_timeout 仅出现于 tests,无 Cargo.toml 变更,无新增依赖);无新增 root 实现文件。
  • 测试位置termios_lifecycle.rsshell_host integration target、Linux 门控 + #[path] 挂载且自包含;Linux-only 注释已紧贴 #[cfg] 门控;marker.rs 测试行数归测试 SDD 治理。

前轮闭环确认

  • P1(SEM-019 退出码轴):新增 errexit_compound_payload_keeps_session_exit_code_clean[P1][cosh-ng] 信号与退出状态传播:SIGPIPE 继承处置 + set -e 会话退出码(IO-009/SEM-019) #2541-B 原文复合 payload + exit_status == Some(0) 断言钉住该轴;负断言 SEM019-leaked 设计正确(回显中 %s 未展开,不会误命中)。
  • P2(prompt 帧尾 set -e 暴露面):成功路径已改 deferred restore(_COSH_RESTORE_ERREXIT=1 由下一帧 trap handler 消费)+ idle-trap 就地恢复,与本 patch 静态一致。
  • P2(PS2 范围 / guard 值导出偏差):已在 PR body Known deviations 显式摘出与声明。
  • 其余:M2 guarded call 三态保留、cnf 指纹失败降级、M4 行内撤防 + re-arm 三向判定、内部命名空间 guard、$! 三 distinct pid 断言、恢复窗口具名常量、OnceLockconcat!、注释贴门控——均保持闭环。

Open Questions

  • Fixes #2540 口径:PS2 半边已声明摘出,作者请维护者裁决是否降级为 Refs,待裁决。
  • 超大 .sh 资产治理口径(先例 input_intent.sh):已确认为后续 SDD 议题,三分资产形态下紧迫性下降。
  • zsh 侧 include_str!("../shell_host/marker/zsh.rs") 内联形态与 bash 侧三分资产不对称:zsh 未超阈值,属既有状态,后续若 zsh 同样拆资产需同步 routing_c4 测试。

Validation

  • 未运行任何验证(纯静态输入评审,不执行工具);行数/计数判定以 CI gate 实测为准。
  • CI 快照(本 head,全绿)Test cosh-ng SUCCESS(09:32:27)、Test cosh-ng fast checks SUCCESS(09:29:53)、Build cosh-ng release SUCCESS(09:29:21)、Docs/Commit/PR Checks 与 license/cla 均 SUCCESS,且均晚于当前 head 推送;上一轮"等待权威 Linux 全量门禁转绿"的合并前门槛已满足。PR 已转 non-draft。
  • 剩余风险:M1–M5 行为契约(errexit 帧保护、DEBUG trap 所有权矩阵、PROMPT_COMMAND 属性保真、termios 恢复)依赖 PR 容器 FAIL→PASS 证据与作者探针结论,本评审无法独立复核;shell_host/model.rsexit_status 字段不在本输入 patch 内,按人工评审引用采纳;P3 转义项为静态推演(机制确定,置信度中高);嵌套 × errexit 下 guard 值列表退出状态为 1 的边角已由作者记录为 INV-2 已知取舍。

The intent classifier (_cosh_classify_missing) runs inside $(...)
substitutions whose subshells inherit the user's errexit. The bare
_cosh_utf8_han_status calls return 1 for plain-ASCII input by design,
which aborts the subshell before printf under `set -e` and drifts the
classification. Guarded conditional-context calls keep the 0/1/2
tri-state intact (#2598 M2 sweep, umbrella for #2541 sub-item B).

Assisted-by: Qoder:1.22.0
Signed-off-by: SunnyQjm <mfeng@linux.alibaba.com>
Converge the four injection-layer contract breaks tracked by umbrella
issue #2598 (save-compose-restore semantics, one design):

- M2 dispatch-chain failure isolation (SEM-019, #2541-B): guard the
  bare _cosh_utf8_han_status call in _cosh_begin_attempt and the
  _cosh_token_fingerprint substitution in command_not_found_handle so
  internal enum statuses never leak into a user `set -e` session.
- M3 PROMPT_COMMAND attribute fidelity (NS-005, Fixes #2540): record
  the declare -p flags token before the wholesale replacement, restore
  the export attribute afterwards, and switch the hijack value to a
  guard form that stays a silent no-op in nested shells. The guard
  value stages $? into _COSH_PROMPT_STATUS because `declare -F` would
  otherwise clobber the user's status before the prompt chain reads it.
- M1 frame-level errexit protection (SEM-019): wrap the preexec and
  prompt frames to suspend errexit on entry; the preexec veto path
  defers restoration to the next frame entry (2541-D4), and the prompt
  wrapper returns 0 under errexit because a non-zero PROMPT_COMMAND
  list status kills an interactive session (probed on bash 3.2/5.2;
  bash itself restores the user's $? at the prompt boundary).
- M4 line-execution trap exit (NS-009, Fixes #2539): the first DEBUG
  firing of a line finishes the preexec work and leaves the trap
  disarmed for the rest of the line - per-command trap execution is
  what opens bash's mid-line job reap window that serializes
  background jobs. The prompt boundary re-arms via an ownership
  matrix (user-cleared / dormant / combined user trap / foreign trap),
  and _cosh_now_ms drops the external `date` exec (fork hygiene).

Container FAIL->PASS evidence (Apple container, anolisos:23.5 + real
binaries on alinux3): SEM-019 session exit 1->0 with payload parity,
NS-009 10/10 concurrent job numbers under the unanchored classifier,
NS-005 declare -a -> declare -ax with a real cosh-shell/cosh-core
run. Design: specs/cosh-2598-host-injection-contract/ (G1 confirmed
2026-08-17).

Refs #2598, #2541
Fixes #2540
Fixes #2539

Assisted-by: Qoder:1.22.0
Signed-off-by: SunnyQjm <mfeng@linux.alibaba.com>
- marker.rs: NS-005 export-attribute cells (array/scalar, nested-bash
  silence), SEM-019 errexit dispatch survival, NS-009 concurrent job
  numbers, user DEBUG trap chain parity. The unexported reverse cell
  is documented as container-acceptance scope (the harness always
  injects an exported PROMPT_COMMAND).
- raw_cli/termios_lifecycle.rs (Linux-only): termios roundtrip over
  exit/EOF/SIGTERM with a real cosh-shell in a pty (#2598 M5 anchor,
  #2537 RC-4). macOS libtest intermittently reports ENOTTY on a fresh
  pty slave, so the module is gated to the shipping target.

Refs #2598, #2537

Assisted-by: Qoder:1.22.0
Signed-off-by: SunnyQjm <mfeng@linux.alibaba.com>
The #2598 host-injection changes pushed marker/bash.rs past the
1000-line blocking bar in the large-file inventory. Execute the split
plan already recorded there: move the embedded script body into
marker/bash_marker.sh loaded via include_str! (the same asset pattern
as shell_host/input_intent.sh). include_str! preserves the exact bytes
of the former raw-string literal; the OSC golden tests and the marker
PTY suite pin the emitted protocol. Inventory row updated in the same
change. The routing_c4 registry test parses the marker source text for
the authoritative slash case list; its include_str! pointer follows the
script body to the new asset (caught by CI fast checks).

Refs #2598

Assisted-by: Qoder:1.22.0
Signed-off-by: SunnyQjm <mfeng@linux.alibaba.com>
@SunnyQjm

Copy link
Copy Markdown
Collaborator Author

Round-6 disposition (head bc84468)

Single remaining [P3] from the 2026-08-19T09:52 auto-review — adopted, verified on the shipping target. Your double-decoding trace was exact: the outer printf format was %s\\\\n at the shell layer, so each argument was joined by a literal backslash-n and the nested bash received one line with exit swallowed into the printf argument (single prompt served, EOF exit). Fixed to %s\\n (real newline per argument, matching the hooks line's convention you cited); the nested interactive bash now receives the two-line script and serves a prompt per line, evaluating the leaked guard value at each — the test's stated intent. Green on Linux (real pty).

All other items in your review are closure confirmations; the two open questions (Fixes #2540 vs Refs downgrade, oversized-.sh governance) stay with the maintainer / follow-up SDD as noted.

@SunnyQjm

SunnyQjm commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator Author

PR number: #2632
head_sha: bc84468
reviewed_at: 2026-08-20T04:02:19Z

Findings

本 head 与上一轮(03:20 已评审)相同、patch 无增量;本轮触发为 kongche-jbw 的新人工评审(03:41),三条发现已逐条对照 patch 静态复核,均成立:

  • [P1] src/cosh-ng/crates/cosh-shell/src/shell_host/marker/bash_marker_frames.sh 尾部 install 段(PROMPT_COMMAND guard 值,约 L189)× bash_marker_dispatch.sh _cosh_debug_trap_exit(约 L229–244)— 用户重赋值 PROMPT_COMMAND(数组或标量)后 marker 链整体失活,静态复核确认:重挂 authority 只有 _cosh_prompt_command_impl 尾部的 _cosh_rearm_debug_trap_COSH_AT_PROMPT=1 只能由 _cosh_precmd_marker 设置;guard 值被 wholesale 替换后,prompt 边界不再经过 cosh wrapper——无 precmd、无 re-arm,撤防的 DEBUG trap(dormant 台账)永不恢复,后续 preexec/precmd/intercept 全部停止。两点细化:(a) 包装器被用户替换本身属既有/原生对齐行为(旧实现同样丢失),但本 PR 的 NS-009 行内撤防设计使"无 prompt wrapper"同时意味着 trap 永不重挂,暴露面扩大;(b) 本 PR 新增的两个属性保真测试(marker.rs 约 L2930/L2952)正好踩进该终态却不断言链存活,scripted.rssend_command_line 丢弃 read_until 返回值(该文件不在本 patch 内,按评审引用采纳)使缺失的 prompt_ready 只表现为静默超时。建议:显式裁定重赋值后的契约(原生对齐=停止拦截并文档化,或 cosh 检测重装),并在重赋值后再执行一条命令断言新一代 marker 是否到达。
  • [P2] src/cosh-ng/crates/cosh-shell/tests/shell_host/termios_lifecycle.rs termios_flags(约 L73–95)— 只快照 c_iflag/c_oflag/c_cflag/c_lflag,但生产路径 cfmakeraw 还改写 c_cc[VMIN]/c_cc[VTIME];若退出路径只恢复 flags 而遗留 raw 读取参数,exit/EOF/SIGTERM 三条仍全绿,与同文件"partial restore cannot pass"的注释自述矛盾。建议启动前设置非默认 VMIN/VTIME 并比较完整 c_cc,覆盖该平台其余可观察字段。
  • [P2] src/cosh-ng/crates/cosh-shell/src/shell_host/input_intent.sh 约 L343–355(_cosh_classify_missing guarded call)— 修复格未经目标调用链覆盖:新增 errexit 用例(errexit_session_survives_dispatch_and_exits_cleanlyerrexit_compound_payload_keeps_session_exit_code_clean)全部执行已存在命令,不进入 _cosh_classify_missing;且未启用 inherit_errexit$(...) 子shell不继承 errexit,被修路径根本不触发,guard 回退后测试仍绿。建议:真实 marker 会话开 shopt -s inherit_errexit + set -e,输入缺失的 ASCII 自然语言命令,断言拦截发生、会话存活、退出码为 0。

结构维度:未发现 blocking package/module/public API 组织问题。

结构合规确认

  • 范围:代码改动全部在 crates/cosh-shell/ 内;large-file-inventory.md 为 SDD 台账同步(按台账"仅超限文件可登记"规则删除拆分后已低于阈值的登记行),符合 Scope Fence;未触碰非 cosh-shell crate、lockfile、环境文件。
  • Ownerbash.rs 与三个 marker 资产、input_intent.sh 均属 shell_host/ 单一 owner,core/dispatch/frames 按职责切分;slash/registry.rs 仅将 routing_c4 测试的 include_str! 跟随脚本体改指 bash_marker_core.sh,是资产拆分的必要配套,跨 owner 边界已解释。
  • 大文件bash.rs 942→17 行(diff 实见,以 CI 实测为准),执行台账登记的 .sh 资产拆分计划;三个新建资产 437/517/195 行均低于 700,新建文件走拆分而非 waiver,符合台账规则;check-layout.sh 与台账治理对象为 *.rs.sh 资产不构成越线。
  • Public API / 依赖方向 / Root src:无 lib.rs 改动,无新增 pub/pub modbash_marker_script 保持 pub(in crate::shell_host)&'static str 签名;无新增 cosh_shell:: self-crate 路径;src/ 无新增 Rust import,无 Cargo.toml 变更,无新增 root 实现文件。
  • 测试位置termios_lifecycle.rsshell_host integration target、Linux 门控 + #[path] 挂载且自包含;Linux-only 注释紧贴 #[cfg] 门控;marker.rs 测试行数归测试 SDD 治理。
  • 跨轮闭环总览:SEM-019 退出码轴、prompt 帧尾 set -e deferred restore、用户清空 trap 三向判定、内部命名空间 guard、$! 三 distinct pid、恢复窗口具名常量、concat! 编译期拼接、嵌套 bash stdin 管道与转义修正、PS2 范围与 guard 导出偏差显式声明——均保持闭环。

Open Questions

  • P1 的契约取向(原生对齐 vs 重装 wrapper)需维护者裁决;Fixes #2540 口径(PS2 半边已声明摘出)同待裁决。
  • 超大 .sh 资产治理口径已确认为后续 SDD 议题;zsh 侧内联形态与 bash 三分资产不对称为既有状态。

Validation

  • 未运行任何验证(纯静态输入评审,不执行工具);行数/计数判定以 CI gate 实测为准。
  • CI 快照(本 head,全绿)Test cosh-ng SUCCESS(02:30:52)、fast checks SUCCESS(02:25:42)、release build SUCCESS(02:25:30)、Docs/Commit/PR Checks 与 license/cla 均 SUCCESS,均晚于当前 head 推送;kongche-jbw 评审(03:41)晚于该批检查但 head 未变,结论仍适用。
  • 剩余风险:P1/P2 三条为合并前待处置项(P1 的机制部分与已探针证实的帧设计自洽,P2 两条为静态可确认的覆盖缺口);M1–M5 行为契约依赖 PR 容器 FAIL→PASS 证据,本评审无法独立复核;scripted.rsshell_host/model.rs 不在本输入 patch 内,按评审引用采纳;macOS 排除范围已记录。PR 已转 non-draft。

@kongche-jbw
kongche-jbw self-requested a review August 20, 2026 03:25

@kongche-jbw kongche-jbw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review baseline: f94cf16d1f68...bc84468e6d24

[P1] PROMPT_COMMAND 重赋值后 marker 链会失活

src/cosh-ng/crates/cosh-shell/src/shell_host/marker/bash_marker_dispatch.sh:238
把 DEBUG trap 留在撤防状态,并依赖 _cosh_prompt_command 在 prompt 边界重挂。
执行本 PR 新增用例里的 PROMPT_COMMAND=(...) 或标量赋值时,赋值会先替换
bash_marker_frames.sh:189 的 guard,随后 prompt 不再调用 cosh wrapper,
所以 _COSH_AT_PROMPT 和 trap 都无法恢复,之后不再产生 preexec/precmd/intercept。
现有属性测试仍会通过,因为 scripted.rssend_command_line 丢弃了
read_until 返回的 false;本地单跑这三个用例实际累计等待了 20.35 秒超时。
Possible direction: 让边界恢复不依赖可被用户覆盖的 PROMPT_COMMAND,并在数组、
标量重赋值后再执行一条命令,断言新一代 preexec/precmd/intercept 事件按时到达。

[P2] termios 往返断言遗漏控制字符状态

src/cosh-ng/crates/cosh-shell/tests/shell_host/termios_lifecycle.rs:73
只快照四个 flag 字段,但生产路径的 cfmakeraw 还会改写 c_cc[VMIN]
c_cc[VTIME]。若退出路径只恢复 flags、遗留 raw 读取参数,exit/EOF/SIGTERM
三条测试仍会全部通过,与“恢复原值”和注释中的“partial restore cannot pass”不符。
Possible direction: 启动前设置非默认 VMIN/VTIME,并比较完整 c_cc;同时覆盖
该平台可观察的其余 termios 字段,证明三种退出路径确实恢复整份快照。

[P2] classifier 的 errexit 修复没有经过目标调用链

src/cosh-ng/crates/cosh-shell/src/shell_host/input_intent.sh:347
修复的是 set -e 继承到命令替换后,ASCII helper 返回 1 导致分类提前退出;
但新增 errexit 用例只执行已有命令,不会进入 _cosh_classify_missing,现有
input_intent 用例也未启用 inherit_errexit。因此这两处 guard 回退后测试仍会绿,
ASCII 自然语言缺失命令会重新降级到 native command-not-found 或终止会话。
Possible direction: 通过真实 marker 会话启用 inherit_errexitset -e,输入
缺失的 ASCII 自然语言命令,断言发生 natural-language intercept、会话存活且退出码为 0。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component:cosh-ng src/cosh-ng scope:documentation ./docs/|./*.md|./NOTICE

Projects

None yet

3 participants