Skip to content

Commit 4b763c5

Browse files
committed
test(config): isolate proxy environment test processes
Run the path and storage-event proxy checks in exact child processes with fixed startup environments. Preserve the original socket/TLS and positive and negative assertions, and join the owned fake-proxy listener. FIX-MTR-12 is the second local child of REL-MTR-ENV-01, following the signed FIX-MTR-13 commit. Preserve original controlled evidence, combined AF1 and all historical STOP records. Only its three test sources and six declared governance paths belong to this commit. MTR-104 remains the sole versioned publisher and still owes its own review and complete final C/D. Local Claude ER03/ER05/H0 PASS: 8c66b333-3ae7-4cc1-a465-6fa2467f85a8. Review output SHA256: 788a42e6ae11eebd5ea00358084f6a7bde472092b85b2ccab567c649393e56d8. Signed-off-by: ELi Ma <eli@patch.sh>
1 parent cca52cc commit 4b763c5

9 files changed

Lines changed: 282 additions & 70 deletions

File tree

‎docs/plan/README.md‎

Lines changed: 3 additions & 1 deletion
Large diffs are not rendered by default.

‎docs/plan/plan-20261015.md‎

Lines changed: 37 additions & 22 deletions
Large diffs are not rendered by default.

‎docs/plan/plan-long.md‎

Lines changed: 4 additions & 2 deletions
Large diffs are not rendered by default.

‎docs/plan/plan-status.md‎

Lines changed: 5 additions & 3 deletions
Large diffs are not rendered by default.

‎docs/refactoring/protocol.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
> **FIX13实际本地提交与FIX12开工前沿(2026-10-25T14:49:01.072868+00:00):** DIAG-MTR-01最终own R2 PASS及signed/DCO本地1a9cd767、组合AF1十阶段/192唯一PASS/root资源后检均已核。FIX-MTR-13实际自己的R4源ER05 PASS/H0 FAIL保留;R5 fd398e89与最终R6 c0ce5ca0四PASS,精确七路径signed/DCO本地提交cca52ccd,实际GPG416E…E0C及完整tree已核。FIX13仍locally-accepted、C/D待MTR104,未push;当前执行前沿为FIX-MTR-12最终增量ER05/精确九路径localcommit→MTR104自身审查/完整C/D→102→103→11→40→13→39→92→12/H1→LTR10→11→12→LTR01/H2,另有95/93→102。正式17/118、0/26、17/144不变;外部资格1/4。Libra FIX04 A1/full10761/21原ignored/2原生flake、源B与own e76e3d02 PASS已核,C/D待;两flake根因未证、资格完成前真实基线及FIX/DEFER条件保持。原REL-MTR-FIX-01退出、FIX3耗尽预算/全部旧STOP、唯一publisher/no-push与H0~H6其它边不变。
2+
13
> **AF1实际终态与子卡提交前沿(2026-10-25T14:33:05.791314+00:00):** DIAG-MTR-01最终自身Claude R2 a3f7bd2d PASS及signed/DCO本地提交1a9cd767已核,未推送。REL-MTR-ENV-01 R7已采纳;组合AF1十阶段exit0、五完整模块101+73+11+4+3=192唯一PASS,root于14:24:46Z核实际新编译产物、原107库名称owner及资源终态,未手工清理。FIX-MTR-13/12各自最终增量ER05与精确localcommit待;MTR-104完整C/D尚未准入。现行链99→DIAG→FIX13→FIX12→104→102→103→11→40→13→39→92→12/H1→LTR10→11→12→LTR01/H2,另有95/93→102;其余H0~H6不变。正式17/118、0/26、17/144不变,外部资格1/4。Libra FIX04 A1全量10761/21原ignored/2原生flake、源B与本地Claude e76e3d02 PASS已核,仅locally-accepted,C/D待;两flake根因未证,资格完成前按具名P2补FIX/DEFER及真实基线证据。FIX02旧C1 STOP/stash与FIX03后继保持;原REL-MTR-FIX-01退出、FIX3耗尽预算及全部旧STOP不复位。
24
35
> **REL-MTR-ENV-01实际采纳与组合源准备(2026-10-25T13:23:46.846168+00:00):** DIAG-MTR-01 D1实际GO,七阶段exit0、root资源/来源后检与ownClaude06c202a4 PASS已核;仅证明受控Request机制,不证明历史Interrupted/交错/C2。REL-MTR-ENV-01切换R1/R2/R3/R4/R5/R6失败原件保留;实际R7 ed5d6a31-7828-40e2-bc13-2442d0932b57 于2026-10-25T13:21:47.060495+00:00 五项PASS,root已采纳;2026-10-25T13:23:18.332588+00:00 在独立legacy clone精确形成四测试文件组合树并同步21份治理。没有新AF1/C claim或提交/push;最终采纳文本自身审查及DIAG精确本地签名提交仍待。现行完整执行链:MTR-99→DIAG-MTR-01→FIX-MTR-13→FIX-MTR-12→MTR-104→MTR-102→MTR-103→MTR-11→MTR-40→MTR-13→MTR-39→MTR-92→MTR-12/H1→LTR-10→LTR-11→LTR-12→LTR-01/H2。另有MTR-95/MTR-93→MTR-102;H0~H6其余原边不变。正式17/118、0/26、17/144不变。Libra外部资格1/4;FIX04恢复R2 session15ccf47d于12:26:41Z四PASS,root采纳及精确恢复12:28:09Z已完成;执行审c8ad29bf于12:37:57Z四PASS,A1于12:38:25.742703Z已claim,终态/自身ER05/C/D尚未取得。FIX02旧C1 STOP及stash988ded20保留;FIX01→FIX04→FIX02→FIX03不替代H1。

‎docs/refactoring/traces.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
> **FIX13实际本地提交与FIX12开工前沿(2026-10-25T14:49:01.072868+00:00):** DIAG-MTR-01最终own R2 PASS及signed/DCO本地1a9cd767、组合AF1十阶段/192唯一PASS/root资源后检均已核。FIX-MTR-13实际自己的R4源ER05 PASS/H0 FAIL保留;R5 fd398e89与最终R6 c0ce5ca0四PASS,精确七路径signed/DCO本地提交cca52ccd,实际GPG416E…E0C及完整tree已核。FIX13仍locally-accepted、C/D待MTR104,未push;当前执行前沿为FIX-MTR-12最终增量ER05/精确九路径localcommit→MTR104自身审查/完整C/D→102→103→11→40→13→39→92→12/H1→LTR10→11→12→LTR01/H2,另有95/93→102。正式17/118、0/26、17/144不变;外部资格1/4。Libra FIX04 A1/full10761/21原ignored/2原生flake、源B与own e76e3d02 PASS已核,C/D待;两flake根因未证、资格完成前真实基线及FIX/DEFER条件保持。原REL-MTR-FIX-01退出、FIX3耗尽预算/全部旧STOP、唯一publisher/no-push与H0~H6其它边不变。
2+
13
> **AF1实际终态与子卡提交前沿(2026-10-25T14:33:05.791314+00:00):** DIAG-MTR-01最终自身Claude R2 a3f7bd2d PASS及signed/DCO本地提交1a9cd767已核,未推送。REL-MTR-ENV-01 R7已采纳;组合AF1十阶段exit0、五完整模块101+73+11+4+3=192唯一PASS,root于14:24:46Z核实际新编译产物、原107库名称owner及资源终态,未手工清理。FIX-MTR-13/12各自最终增量ER05与精确localcommit待;MTR-104完整C/D尚未准入。现行链99→DIAG→FIX13→FIX12→104→102→103→11→40→13→39→92→12/H1→LTR10→11→12→LTR01/H2,另有95/93→102;其余H0~H6不变。正式17/118、0/26、17/144不变,外部资格1/4。Libra FIX04 A1全量10761/21原ignored/2原生flake、源B与本地Claude e76e3d02 PASS已核,仅locally-accepted,C/D待;两flake根因未证,资格完成前按具名P2补FIX/DEFER及真实基线证据。FIX02旧C1 STOP/stash与FIX03后继保持;原REL-MTR-FIX-01退出、FIX3耗尽预算及全部旧STOP不复位。
24
35
> **REL-MTR-ENV-01实际采纳与组合源准备(2026-10-25T13:23:46.846168+00:00):** DIAG-MTR-01 D1实际GO,七阶段exit0、root资源/来源后检与ownClaude06c202a4 PASS已核;仅证明受控Request机制,不证明历史Interrupted/交错/C2。REL-MTR-ENV-01切换R1/R2/R3/R4/R5/R6失败原件保留;实际R7 ed5d6a31-7828-40e2-bc13-2442d0932b57 于2026-10-25T13:21:47.060495+00:00 五项PASS,root已采纳;2026-10-25T13:23:18.332588+00:00 在独立legacy clone精确形成四测试文件组合树并同步21份治理。没有新AF1/C claim或提交/push;最终采纳文本自身审查及DIAG精确本地签名提交仍待。现行完整执行链:MTR-99→DIAG-MTR-01→FIX-MTR-13→FIX-MTR-12→MTR-104→MTR-102→MTR-103→MTR-11→MTR-40→MTR-13→MTR-39→MTR-92→MTR-12/H1→LTR-10→LTR-11→LTR-12→LTR-01/H2。另有MTR-95/MTR-93→MTR-102;H0~H6其余原边不变。正式17/118、0/26、17/144不变。Libra外部资格1/4;FIX04恢复R2 session15ccf47d于12:26:41Z四PASS,root采纳及精确恢复12:28:09Z已完成;执行审c8ad29bf于12:37:57Z四PASS,A1于12:38:25.742703Z已claim,终态/自身ER05/C/D尚未取得。FIX02旧C1 STOP及stash988ded20保留;FIX01→FIX04→FIX02→FIX03不替代H1。
@@ -168,7 +170,7 @@ Libra 将工作期间捕获的脱敏记录写入 traces Git 历史,Mega2 接
168170

169171
| 交接 | 生产方 | 消费方与先后约束 | 放行凭据 |
170172
|---|---|---|---|
171-
| H0 契约冻结 | 两仓 root 当前联合治理审查;Mega2 MTR-01 登记 | 两仓实现前置;LTR-00 已取消后续验收,不再作为生产者 | 本会话按用户明确单审指令,两份计划及本契约由真实本地Claude对最终冻结输入明确PASS;冻结revision/hash与两仓真实基线。只覆盖reviewer数量和工具,保留所有来源/自身ER05/完整C/D/预算门;本轮REL-MTR-ENV-01已由R7 ed5d6a31真实五PASS/root采纳,DIAG最终文本R2 a3f7bd2d PASS并本地提交1a9cd767;组合AF1已核192唯一PASS,两个child当前最终输入的增量ER05/H0和精确localcommit仍待,MTR104 C/D未准入。 **历史适用性快照(13:15:51Z之前):** 本轮REL-MTR-ENV-01与H0由Mega计划ENV-R1具名登记,未取得终态不得放行。 **历史列(R6审查终态2026-10-25T13:15:51.139351Z之前的原文):** 两份计划及本契约联合 Claude 与隔离 Codex PASS;冻结 revision/hash 与两仓基线 |
173+
| H0 契约冻结 | 两仓 root 当前联合治理审查;Mega2 MTR-01 登记 | 两仓实现前置;LTR-00 已取消后续验收,不再作为生产者 | 本会话按用户明确单审指令,两份计划及本契约由真实本地Claude对最终冻结输入明确PASS;冻结revision/hash与两仓真实基线。只覆盖reviewer数量和工具,保留所有来源/自身ER05/完整C/D/预算门;本轮REL-MTR-ENV-01已由R7 ed5d6a31真实五PASS/root采纳,DIAG最终文本R2 a3f7bd2d PASS并本地提交1a9cd767;组合AF1已核192唯一PASS,FIX13当前自身最终R6 c0ce5ca0四PASS及signed/DCO本地cca52ccd已核;FIX12当前最终own/localcommit仍待,MTR104 C/D未准入。 **历史(FIX13提交前):** 两个child当前最终输入的增量ER05/H0和精确localcommit仍待,MTR104 C/D未准入。 **历史适用性快照(13:15:51Z之前):** 本轮REL-MTR-ENV-01与H0由Mega计划ENV-R1具名登记,未取得终态不得放行。 **历史列(R6审查终态2026-10-25T13:15:51.139351Z之前的原文):** 两份计划及本契约联合 Claude 与隔离 Codex PASS;冻结 revision/hash 与两仓基线 |
172174
| H1 Git 服务可用 | Mega2 MTR-12,前置 MTR-13 普通 clone 隔离 | Libra LTR-10/11/12 实现 scoped push,由 LTR-01 发布;LTR-02 clone 验收 | HTTP+SSH scope/auth/FF/对象隔离验收及发布凭据,或明确的受审本地双传输 fixture;不得以最后收口作为服务启动条件 |
173175
| H2 scoped push 可用 | Libra LTR-01 | Mega2 MTR-14 起以真实 Libra 输入重建索引及验收 | 客户端发布凭据或受审本地 fixture;持久 source/epoch 和失败重试收据 |
174176
| H3 查询服务可用 | Mega2 MTR-20 | Libra LTR-13/19 capability/readiness、LTR-24 target resolve、LTR-14/15/20 导航筛选、LTR-03/04/25/05 checkpoint/search/准入/read | 列表、关键词、预算读取与水位契约的实际验收;服务版本/fixture 明确 |

‎src/commands/path.rs‎

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -584,32 +584,42 @@ mod tests {
584584
/// reached directly; the control client (no bypass) goes to the proxy.
585585
#[test]
586586
fn loopback_requests_skip_a_configured_proxy() {
587-
use crate::config::testing::{EnvVarGuard, env_lock};
588-
589-
let lock = env_lock();
587+
use crate::config::testing::{proxy_test_child_phase, run_proxy_test_child};
588+
589+
const TEST: &str = "commands::path::tests::loopback_requests_skip_a_configured_proxy";
590+
if let Some(phase) = proxy_test_child_phase(TEST) {
591+
let server = std::env::var("MEGA2_PROXY_TEST_SERVER").expect("child server");
592+
match phase.as_str() {
593+
"direct" => {
594+
let direct = http_client(Duration::from_secs(5), true).unwrap();
595+
assert_eq!(
596+
provision_request(&direct, &server, "/p", None).unwrap(),
597+
"already exists /p"
598+
);
599+
}
600+
"control" => {
601+
let proxied = http_client(Duration::from_secs(5), false).unwrap();
602+
assert!(provision_request(&proxied, &server, "/p", None).is_err());
603+
}
604+
_ => panic!("unknown proxy test phase"),
605+
}
606+
return;
607+
}
590608
let proxy = serve(json_response("502 Bad Gateway", "{}"));
591609
let server = serve(json_response(
592610
"200 OK",
593611
r#"{"req_result":true,"data":{"path":"/p","created":false,"commit_id":null},"err_message":""}"#,
594612
));
595-
let _guards = [
596-
EnvVarGuard::set(&lock, "HTTP_PROXY", &proxy.url),
597-
EnvVarGuard::set(&lock, "http_proxy", &proxy.url),
598-
EnvVarGuard::remove(&lock, "NO_PROXY"),
599-
EnvVarGuard::remove(&lock, "no_proxy"),
600-
EnvVarGuard::remove(&lock, "ALL_PROXY"),
601-
EnvVarGuard::remove(&lock, "all_proxy"),
613+
let values = [
614+
("HTTP_PROXY", proxy.url.as_str()),
615+
("http_proxy", proxy.url.as_str()),
616+
("MEGA2_PROXY_TEST_SERVER", server.url.as_str()),
602617
];
603-
let direct = http_client(Duration::from_secs(5), true).unwrap();
604-
assert_eq!(
605-
provision_request(&direct, &server.url, "/p", None).unwrap(),
606-
"already exists /p"
607-
);
618+
run_proxy_test_child(TEST, "direct", &values);
608619
assert_eq!(proxy.hits(), 0);
609620
assert_eq!(server.hits(), 1);
610621

611-
let proxied = http_client(Duration::from_secs(5), false).unwrap();
612-
assert!(provision_request(&proxied, &server.url, "/p", None).is_err());
622+
run_proxy_test_child(TEST, "control", &values);
613623
assert_eq!(proxy.hits(), 1, "control goes through the proxy");
614624
assert_eq!(server.hits(), 1);
615625
}

‎src/config/testing.rs‎

Lines changed: 142 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,8 @@ pub type EnvLockGuard = MutexGuard<'static, ()>;
3737
pub fn env_lock() -> EnvLockGuard {
3838
// Recover from a poisoned lock instead of cascading a single test's panic
3939
// into PoisonError failures across every other env-serialized test. The
40-
// lock only serializes process-wide env access; `EnvVarGuard`'s Drop restores
41-
// any mutated variable during unwinding, so the env is consistent by the time
42-
// the next test acquires the lock.
40+
// lock only serializes participating callers; it does not protect unrelated
41+
// environment readers. Proxy tests use child startup environments instead.
4342
ENV_LOCK
4443
.lock()
4544
.unwrap_or_else(|poisoned| poisoned.into_inner())
@@ -56,8 +55,8 @@ pub struct EnvVarGuard<'a> {
5655
impl<'a> EnvVarGuard<'a> {
5756
pub fn set(lock: &'a EnvLockGuard, key: &'static str, value: &str) -> Self {
5857
let previous = std::env::var_os(key);
59-
// SAFETY: tests that mutate MEGA_* variables must hold the shared config env lock,
60-
// and this guard restores the previous value before that lock is released.
58+
// SAFETY: callers must exclude concurrent environment readers as well as
59+
// writers. This lock alone only serializes participating tests.
6160
unsafe {
6261
std::env::set_var(key, value);
6362
}
@@ -102,6 +101,144 @@ impl Drop for EnvVarGuard<'_> {
102101
}
103102
}
104103

104+
#[cfg(test)]
105+
pub fn proxy_test_child_phase(test_name: &str) -> Option<String> {
106+
(std::env::var("MEGA2_PROXY_TEST_CHILD").as_deref() == Ok(test_name))
107+
.then(|| std::env::var("MEGA2_PROXY_TEST_PHASE").expect("child phase"))
108+
}
109+
110+
#[cfg(test)]
111+
pub fn run_proxy_test_child(test_name: &str, phase: &str, values: &[(&str, &str)]) {
112+
use std::{
113+
io::Read,
114+
process::{Child, Command, Stdio},
115+
sync::atomic::{AtomicBool, Ordering},
116+
thread,
117+
time::{Duration, Instant},
118+
};
119+
120+
const PROXY_KEYS: [&str; 8] = [
121+
"HTTP_PROXY",
122+
"http_proxy",
123+
"HTTPS_PROXY",
124+
"https_proxy",
125+
"ALL_PROXY",
126+
"all_proxy",
127+
"NO_PROXY",
128+
"no_proxy",
129+
];
130+
const OUTPUT_LIMIT: u64 = 1024 * 1024;
131+
struct ChildGuard(Child);
132+
impl Drop for ChildGuard {
133+
fn drop(&mut self) {
134+
let _ = self.0.kill();
135+
let _ = self.0.wait();
136+
}
137+
}
138+
139+
let before = PROXY_KEYS.map(std::env::var_os);
140+
let mut command = Command::new(std::env::current_exe().expect("test binary"));
141+
command
142+
.args([
143+
"--exact",
144+
test_name,
145+
"--nocapture",
146+
"--test-threads=1",
147+
"--color=never",
148+
])
149+
.env("MEGA2_PROXY_TEST_CHILD", test_name)
150+
.env("MEGA2_PROXY_TEST_PHASE", phase)
151+
.stdout(Stdio::piped())
152+
.stderr(Stdio::piped());
153+
for key in PROXY_KEYS {
154+
command.env_remove(key);
155+
}
156+
for (key, value) in values {
157+
command.env(key, value);
158+
}
159+
let mut child = ChildGuard(command.spawn().expect("spawn isolated proxy test"));
160+
let stdout = child.0.stdout.take().expect("child stdout");
161+
let stderr = child.0.stderr.take().expect("child stderr");
162+
let too_large = AtomicBool::new(false);
163+
let deadline = Instant::now() + Duration::from_secs(20);
164+
let read = |stream: Box<dyn Read + Send>| {
165+
let mut bytes = Vec::new();
166+
stream
167+
.take(OUTPUT_LIMIT + 1)
168+
.read_to_end(&mut bytes)
169+
.expect("read child output");
170+
if bytes.len() as u64 > OUTPUT_LIMIT {
171+
too_large.store(true, Ordering::Release);
172+
}
173+
bytes
174+
};
175+
let (status, stdout, stderr) = thread::scope(|scope| {
176+
let stdout = scope.spawn(|| read(Box::new(stdout)));
177+
let stderr = scope.spawn(|| read(Box::new(stderr)));
178+
let result = loop {
179+
if too_large.load(Ordering::Acquire) {
180+
break Err("isolated proxy test output limit exceeded");
181+
}
182+
if Instant::now() >= deadline {
183+
break Err("isolated proxy test exceeded 20 seconds");
184+
}
185+
if !PROXY_KEYS
186+
.iter()
187+
.zip(&before)
188+
.all(|(key, value)| std::env::var_os(key) == *value)
189+
{
190+
break Err("isolated proxy test changed parent proxy environment");
191+
}
192+
match child.0.try_wait() {
193+
Ok(Some(status)) => break Ok(status),
194+
Ok(None) => thread::sleep(Duration::from_millis(5)),
195+
Err(_) => break Err("could not wait for isolated proxy test"),
196+
}
197+
};
198+
// Close pipes before joining readers, including every failure path.
199+
if result.is_err() {
200+
let _ = child.0.kill();
201+
let _ = child.0.wait();
202+
}
203+
(
204+
result,
205+
stdout.join().expect("stdout reader"),
206+
stderr.join().expect("stderr reader"),
207+
)
208+
});
209+
assert!(
210+
stdout.len() as u64 <= OUTPUT_LIMIT && stderr.len() as u64 <= OUTPUT_LIMIT,
211+
"child output limit"
212+
);
213+
let stdout = String::from_utf8_lossy(&stdout);
214+
let stderr = String::from_utf8_lossy(&stderr);
215+
assert!(
216+
status.expect("isolated proxy test completion").success(),
217+
"{stdout}\n{stderr}"
218+
);
219+
assert_eq!(
220+
stdout
221+
.lines()
222+
.filter(|line| *line == format!("test {test_name} ... ok"))
223+
.count(),
224+
1,
225+
"{stdout}"
226+
);
227+
assert!(
228+
stdout
229+
.lines()
230+
.any(|line| line.starts_with("test result: ok. 1 passed; 0 failed; 0 ignored;")),
231+
"{stdout}"
232+
);
233+
assert!(
234+
PROXY_KEYS
235+
.iter()
236+
.zip(&before)
237+
.all(|(key, value)| std::env::var_os(key) == *value),
238+
"parent proxy environment changed"
239+
);
240+
}
241+
105242
#[derive(Debug, Clone)]
106243
pub struct TestConfigBuilder {
107244
base_dir: PathBuf,

0 commit comments

Comments
 (0)