Skip to content

Commit 33aa17b

Browse files
committed
接通C和C++文档原任务复检并保留输入失稳记录
1 parent 8a80420 commit 33aa17b

32 files changed

Lines changed: 3618 additions & 47 deletions

‎README.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1125,4 +1125,6 @@ Rust CVE observations now check same-run stability of RustSec crates/rust conten
11251125

11261126
`codeguard comments c api.c --clang-tool /absolute/clang --standard c11 --format=json`; C++ selects `comments cpp api.cpp --standard c++17` with the same tool argument. The measured Apple Clang 21 documentation profile requires an explicit tool and matching standard, without installation. Three precise native SARIF rules cover empty command descriptions, unknown parameter names and return tags on void functions, with repair steps, comment-only scope and original-tool verification argv. Unknown rules remain separate. Invalid reports, changed inputs, cancellation and unresolved preprocessing cannot become source violations.
11271127

1128-
The dedicated `c_family_comments_feedback` 0.1 observation leaves project configuration unknown and detailed-contract qualification ungranted. Completely missing comments may yield zero native diagnostics. Without a workspace, feedback remains 0.1 with `next=null`. An existing workspace now receives feedback0.2, stable file/language/standard/native-rule tasks and next0.28 with all current positions and original-tool rescan argv. Input/tool changes withdraw repair locations; clean rescans keep tasks open. Dedicated task verification, attempt journals, project checks and Hooks remain unintegrated. Exit3 or cancellation130 persists, without automatic closure or delivery permission. See the [16-case native evidence, execution path and remaining gaps](tests/acceptance/c-family-comments-native.md).
1128+
The dedicated `c_family_comments_feedback` 0.1 observation leaves project configuration unknown and detailed-contract qualification ungranted. Completely missing comments may yield zero native diagnostics. Without a workspace, feedback remains 0.1 with `next=null`. An existing workspace now receives feedback0.3, stable file/language/standard/native-rule tasks and next0.29 with all current positions and original-tool rescan argv. Input/tool changes withdraw repair locations; clean rescans keep tasks open. Dedicated task verification now binds the first tool, standard and rule and records local observations while keeping tasks open. Dedicated attempt journals, project checks and Hooks remain unintegrated. Exit3 or cancellation130 persists, without automatic closure or delivery permission. See the [16-case native evidence, execution path and remaining gaps](tests/acceptance/c-family-comments-native.md).
1129+
1130+
See [C/C++ original-task recheck acceptance](tests/acceptance/c-family-comments-task-recheck.md) for current closed protocols and evidence. Full four-core production qualification remains ungranted.

‎README.zh-CN.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1102,4 +1102,6 @@ Rust CVE局部观察现在核对同轮RustSec crates/rust内容、成员及物
11021102

11031103
`codeguard comments c api.c --clang-tool /absolute/clang --standard c11 --format=json`;C++ 使用 `comments cpp api.cpp --standard c++17` 并保留同一工具参数。当前仅适配已实测的 Apple Clang 21 独立文档警告档案,明确工具与标准,不隐式安装。原生 SARIF 的空命令描述、错误参数名及 void 返回标签三项精确规则带有修复步骤、仅文档允许范围和原工具复检 argv;未知规则单独保留。坏报告、输入变化、取消和未解析预处理不能成为源码违规。
11041104

1105-
这是 `c_family_comments_feedback` 0.1 的局部观察:项目原配置未知,完整详细契约未授予资格;Clang完全缺失注释也可能零诊断。未绑定工作台时保留0.1/`next=null`;已有工作台现在提供0.2反馈、按文件/语言标准/原规则归并的稳定任务和next0.28,保留全部当前位置及原工具复扫argv。输入或工具变化撤回修复定位,清洁复扫保留开放任务。专用task verify、尝试日志、项目检查和Hook仍待接通;退出3,取消130,不自动关闭任务或授予门禁。真实16例、执行路径和剩余缺口见[原生文档验收](tests/acceptance/c-family-comments-native.md)。
1105+
这是 `c_family_comments_feedback` 0.1 的局部观察:项目原配置未知,完整详细契约未授予资格;Clang完全缺失注释也可能零诊断。未绑定工作台时保留0.1/`next=null`;已有工作台现在提供0.3反馈、按文件/语言标准/原规则归并的稳定任务和next0.29,保留全部当前位置及原工具复扫argv。输入或工具变化撤回修复定位,清洁复扫保留开放任务。专用task verify已沿首次工具/标准/规则接通,记录局部事实并保持任务开放;专用尝试日志、项目检查和Hook仍待接通;退出3,取消130,不自动关闭任务或授予门禁。真实16例、执行路径和剩余缺口见[原生文档验收](tests/acceptance/c-family-comments-native.md)。
1106+
1107+
C/C++ 原任务复检与新封闭协议的当前证据见[复检验收](tests/acceptance/c-family-comments-task-recheck.md);完整四核心生产资格仍未授予。
Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
//! C/C++原生文档任务的固定工具复检;本地观察不能签发关闭。
2+
use crate::work_sync::c_family_comments_report as contract;
3+
use codeguard_runtime::{SourceSnapshot, read_bounded_regular_file};
4+
use serde_json::{Value, json};
5+
use sha2::{Digest, Sha256};
6+
use std::{
7+
path::{Path, PathBuf},
8+
sync::atomic::{AtomicBool, AtomicU64, Ordering},
9+
time::{Instant, SystemTime, UNIX_EPOCH},
10+
};
11+
static NEXT: AtomicU64 = AtomicU64::new(0);
12+
const EXTRA: &[&str] = &[
13+
"task_id",
14+
"task_kind",
15+
"task_rule",
16+
"original_ref",
17+
"input_stable",
18+
];
19+
20+
/// 核验首次报告及消费收据;参数为工作区和精确run/摘要引用,返回原始局部观察。
21+
pub(crate) fn original(root: &Path, reference: &Value) -> Result<Value, &'static str> {
22+
let run = reference["run_id"]
23+
.as_str()
24+
.filter(|s| {
25+
s.starts_with("clangdoc-")
26+
&& s.len() <= 120
27+
&& s.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'-')
28+
})
29+
.ok_or("clang_documentation_origin_invalid")?;
30+
if !reference
31+
.as_object()
32+
.is_some_and(|o| o.len() == 2 && o.contains_key("sha256"))
33+
{
34+
return Err("clang_documentation_origin_invalid");
35+
}
36+
let bytes = read_bounded_regular_file(
37+
&root.join(format!(".codeguard/reports/{run}.json")),
38+
128 * 1024,
39+
)
40+
.map_err(|_| "clang_documentation_origin_unavailable")?;
41+
let digest = format!("{:x}", Sha256::digest(&bytes));
42+
if reference["sha256"] != digest {
43+
return Err("clang_documentation_origin_changed");
44+
}
45+
let r = codeguard_adapters::parse_unique_json(&bytes)
46+
.map_err(|_| "clang_documentation_origin_invalid")?;
47+
if !contract::valid_shape(&r) || r["run_id"] != run {
48+
return Err("clang_documentation_origin_invalid");
49+
}
50+
let marker = serde_json::to_vec_pretty(&json!({"schema_version":"0.1.0","workspace_id":r["workspace_id"],"run_id":run,"report_sha256":digest})).map_err(|_| "clang_documentation_marker_invalid")?;
51+
if read_bounded_regular_file(
52+
&root.join(format!(".codeguard/state/consumed/{run}.json")),
53+
4096,
54+
)
55+
.map_err(|_| "clang_documentation_marker_invalid")?
56+
!= marker
57+
{
58+
return Err("clang_documentation_marker_invalid");
59+
}
60+
Ok(r)
61+
}
62+
63+
fn reference(brief: &Value) -> Value {
64+
json!({"run_id":brief["evidence_ref"]["first_run_id"],"sha256":brief["evidence_ref"]["first_report_sha256"]})
65+
}
66+
fn tool_current(r: &Value) -> bool {
67+
r["selected_tool"]
68+
.as_str()
69+
.and_then(|s| Path::new(s).canonicalize().ok())
70+
.and_then(|p| read_bounded_regular_file(&p, 256 * 1024 * 1024).ok())
71+
.is_some_and(|b| r["native"]["tool_sha256"] == format!("{:x}", Sha256::digest(b)))
72+
}
73+
74+
/// 租约与进程启动前核对原工具;不允许参数替换路径或首次已核对制品。
75+
pub(crate) fn preflight(
76+
root: &Path,
77+
brief: &Value,
78+
tool: Option<&Path>,
79+
) -> Result<(), &'static str> {
80+
let first = original(root, &reference(brief))?;
81+
if tool.is_some_and(|p| Some(p) != first["selected_tool"].as_str().map(Path::new)) {
82+
return Err("clang_documentation_original_tool_required");
83+
}
84+
if !first["native"]["tool_sha256"].is_null() && !tool_current(&first) {
85+
return Err("clang_documentation_original_tool_changed");
86+
}
87+
Ok(())
88+
}
89+
90+
/// 冻结当前任务源码,沿首次标准与工具执行原生文档检查;返回封闭局部复检观察。
91+
pub(crate) fn run(root: &Path, brief: &Value, deadline: Instant) -> Result<Value, &'static str> {
92+
preflight(root, brief, None)?;
93+
let first = original(root, &reference(brief))?;
94+
let path = first["path"]
95+
.as_str()
96+
.ok_or("clang_documentation_target_invalid")?;
97+
let snapshot =
98+
SourceSnapshot::capture(root, [PathBuf::from(path)], 1, 1024 * 1024, 1024 * 1024)
99+
.map_err(|_| "clang_documentation_source_unavailable")?;
100+
let bytes = snapshot
101+
.files()
102+
.get(&PathBuf::from(path))
103+
.ok_or("clang_documentation_source_unavailable")?;
104+
let native = crate::clang_syntax_probe::observe_documentation(
105+
Path::new(
106+
first["selected_tool"]
107+
.as_str()
108+
.ok_or("clang_documentation_tool_invalid")?,
109+
),
110+
first["language"]
111+
.as_str()
112+
.ok_or("clang_documentation_language_invalid")?,
113+
first["standard"]
114+
.as_str()
115+
.ok_or("clang_documentation_standard_invalid")?,
116+
bytes,
117+
deadline,
118+
&AtomicBool::new(false),
119+
);
120+
let mut r = first.clone();
121+
r["report_type"] = json!("clang_documentation_task_recheck");
122+
r["run_id"] = json!(format!(
123+
"clangdoc-{}-{}-{}",
124+
std::process::id(),
125+
NEXT.fetch_add(1, Ordering::Relaxed),
126+
SystemTime::now()
127+
.duration_since(UNIX_EPOCH)
128+
.map_or(0, |d| d.as_nanos())
129+
));
130+
r["source_sha256"] = json!(format!("{:x}", Sha256::digest(bytes)));
131+
r["local_scan_complete"] = json!(
132+
matches!(
133+
native["status"].as_str(),
134+
Some("completed" | "diagnostics_observed")
135+
) && !native["diagnostics"]
136+
.as_array()
137+
.into_iter()
138+
.flatten()
139+
.any(|d| d["level"] == "error")
140+
);
141+
r["native"] = native;
142+
r["task_id"] = brief["task_id"].clone();
143+
r["task_kind"] = brief["kind"].clone();
144+
r["task_rule"] = brief["native_rule_id"].clone();
145+
r["original_ref"] = reference(brief);
146+
r["input_stable"] = json!(
147+
contract::current(root, &normal(&r))
148+
&& (first["native"]["tool_sha256"].is_null() || tool_current(&first))
149+
);
150+
if !valid_shape(root, &r) {
151+
return Err("clang_documentation_recheck_invalid");
152+
}
153+
Ok(r)
154+
}
155+
156+
/// 去掉任务字段以复用观察形状;不改变run或输入身份。
157+
pub(crate) fn normal(r: &Value) -> Value {
158+
let mut v = r.clone();
159+
if let Some(o) = v.as_object_mut() {
160+
for k in EXTRA {
161+
o.remove(*k);
162+
}
163+
o.insert(
164+
"report_type".into(),
165+
json!("clang_documentation_workbench_observation"),
166+
);
167+
}
168+
v
169+
}
170+
/// 核验任务、首次报告、语言与原规则绑定;不依赖可编辑任务Markdown。
171+
pub(crate) fn valid_shape(root: &Path, r: &Value) -> bool {
172+
if !r
173+
.as_object()
174+
.is_some_and(|o| o.len() == 21 && EXTRA.iter().all(|k| o.contains_key(*k)))
175+
|| r["report_type"] != "clang_documentation_task_recheck"
176+
|| !r["input_stable"].is_boolean()
177+
|| !contract::valid_shape(&normal(r))
178+
{
179+
return false;
180+
}
181+
let Ok(first) = original(root, &r["original_ref"]) else {
182+
return false;
183+
};
184+
let rule = match r["task_kind"].as_str() {
185+
Some("blocker") if r["task_rule"].is_null() => "environment",
186+
Some("finding") => match r["task_rule"].as_str().filter(|rule| {
187+
codeguard_adapters::clang_documentation_guidance(rule).is_some()
188+
&& first["local_scan_complete"] == true
189+
&& first["native"]["diagnostics"]
190+
.as_array()
191+
.into_iter()
192+
.flatten()
193+
.any(|d| d["rule_id"] == *rule)
194+
}) {
195+
Some(rule) => rule,
196+
None => return false,
197+
},
198+
_ => return false,
199+
};
200+
let id = format!(
201+
"CG-{}{}",
202+
if r["task_kind"] == "blocker" {
203+
"B-"
204+
} else {
205+
""
206+
},
207+
&contract::fingerprint(&first, rule)[..32]
208+
);
209+
if r["task_id"] != id
210+
|| [
211+
"workspace_id",
212+
"path",
213+
"language",
214+
"standard",
215+
"profile",
216+
"selected_tool",
217+
]
218+
.iter()
219+
.any(|k| r[*k] != first[*k])
220+
{
221+
return false;
222+
}
223+
let Ok(b) = read_bounded_regular_file(
224+
&root.join(format!(".codeguard/findings/{id}/finding.json")),
225+
128 * 1024,
226+
) else {
227+
return false;
228+
};
229+
let Ok(fact) = codeguard_adapters::parse_unique_json(&b) else {
230+
return false;
231+
};
232+
fact["id"] == id
233+
&& fact["workspace_id"] == first["workspace_id"]
234+
&& fact["state"] == "open"
235+
&& fact["authority"] == "local_unverified"
236+
&& fact["delivery_decision"] == "not_evaluated"
237+
&& fact[if r["task_kind"] == "blocker" {
238+
"scope"
239+
} else {
240+
"path"
241+
}] == first["path"]
242+
&& (r["task_kind"] == "blocker"
243+
|| (fact["native_rule_id"] == r["task_rule"]
244+
&& fact["first_source_sha256"] == first["source_sha256"]))
245+
&& fact["kind"] == r["task_kind"]
246+
&& fact["checker_id"] == contract::checker(&first)
247+
&& fact["first_run_id"] == first["run_id"]
248+
&& fact["first_report_sha256"] == r["original_ref"]["sha256"]
249+
&& fact["fingerprint"] == contract::fingerprint(&first, rule)
250+
}
251+
/// 持久化前再次核对输入与首次制品;变化不记录完整复检。
252+
pub(crate) fn inputs_current(root: &Path, r: &Value) -> bool {
253+
valid_shape(root, r)
254+
&& contract::current(root, &normal(r))
255+
&& original(root, &r["original_ref"])
256+
.is_ok_and(|first| first["native"]["tool_sha256"].is_null() || tool_current(&first))
257+
&& (r["native"]["tool_sha256"].is_null() || tool_current(r))
258+
}
259+
/// 分类原任务复检;任何结果均保持本地非权威、任务未关闭。
260+
pub(crate) fn classify(brief: &Value, r: &Value) -> &'static str {
261+
if r["task_id"] != brief["task_id"]
262+
|| r["input_stable"] != true
263+
|| r["local_scan_complete"] != true
264+
{
265+
return if brief["kind"] == "blocker" {
266+
"still_blocked"
267+
} else {
268+
"incomplete"
269+
};
270+
}
271+
if brief["kind"] == "blocker" {
272+
return if r["native"]["diagnostics"]
273+
.as_array()
274+
.into_iter()
275+
.flatten()
276+
.any(|d| {
277+
d["rule_id"]
278+
.as_str()
279+
.is_some_and(|s| codeguard_adapters::clang_documentation_guidance(s).is_none())
280+
}) {
281+
"still_blocked"
282+
} else {
283+
"environment_restored_unverified_policy"
284+
};
285+
}
286+
if r["native"]["diagnostics"]
287+
.as_array()
288+
.into_iter()
289+
.flatten()
290+
.any(|d| d["rule_id"] == brief["native_rule_id"])
291+
{
292+
"still_present"
293+
} else {
294+
"candidate_absent_unverified_policy"
295+
}
296+
}

‎crates/codeguard-cli/src/c_family_comments_workbench.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub(crate) fn resolve_root(
3131
}
3232
/// 保存并同步局部观察;中断后不开始新的持久化阶段。
3333
pub(crate) fn connect(root: &Path, tool: &Path, report: &mut Value, deadline: Instant) {
34-
report["schema_version"] = json!("0.2.0");
34+
report["schema_version"] = json!("0.3.0");
3535
report["task_workflow_status"] = json!("partial");
3636
let argv = report["verification_command"]
3737
.as_array_mut()

‎crates/codeguard-cli/src/lib.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,8 @@ pub mod c_family_comments_command;
513513
#[cfg(unix)]
514514
mod c_family_comments_workbench;
515515
#[cfg(unix)]
516+
mod c_family_comments_task_recheck;
517+
#[cfg(unix)]
516518
mod clang_lint_feedback;
517519

518520
#[cfg(feature = "wasm-precheck")]

‎crates/codeguard-cli/src/next_command.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2429,7 +2429,7 @@ fn run_sequence(run_id: &str) -> Option<u128> {
24292429

24302430
fn view(disposition: &str, reason: &str, brief: Value, actions: Value) -> Value {
24312431
json!({
2432-
"schema_version":if brief["schema_version"] == "0.28.0" {json!("0.28.0")}else if brief["schema_version"] == "0.27.0" {json!("0.27.0")}else if brief["schema_version"] == "0.26.0" {json!("0.26.0")}else if brief["schema_version"] == "0.25.0" {json!("0.25.0")}else if brief["schema_version"] == "0.24.0" {json!("0.24.0")}else if brief["schema_version"] == "0.23.0" {json!("0.23.0")}else if brief["schema_version"] == "0.22.0" {json!("0.22.0")}else if brief["schema_version"] == "0.21.0" {json!("0.21.0")}else if brief["schema_version"] == "0.20.0" {json!("0.20.0")}else if brief["checker_id"] == "shell.shellcheck" {json!("0.17.0")} else if brief["checker_id"] == "go.vet" {json!("0.13.0")} else if brief["checker_id"] == "syntax.native_confirmation" {brief["schema_version"].clone()} else {json!("0.1.0")}, "report_type":"repair_brief_preview",
2432+
"schema_version":if brief["schema_version"] == "0.29.0" {json!("0.29.0")}else if brief["schema_version"] == "0.28.0" {json!("0.28.0")}else if brief["schema_version"] == "0.27.0" {json!("0.27.0")}else if brief["schema_version"] == "0.26.0" {json!("0.26.0")}else if brief["schema_version"] == "0.25.0" {json!("0.25.0")}else if brief["schema_version"] == "0.24.0" {json!("0.24.0")}else if brief["schema_version"] == "0.23.0" {json!("0.23.0")}else if brief["schema_version"] == "0.22.0" {json!("0.22.0")}else if brief["schema_version"] == "0.21.0" {json!("0.21.0")}else if brief["schema_version"] == "0.20.0" {json!("0.20.0")}else if brief["checker_id"] == "shell.shellcheck" {json!("0.17.0")} else if brief["checker_id"] == "go.vet" {json!("0.13.0")} else if brief["checker_id"] == "syntax.native_confirmation" {brief["schema_version"].clone()} else {json!("0.1.0")}, "report_type":"repair_brief_preview",
24332433
"operation":"next", "command_status":"complete", "exit_code":0,
24342434
"disposition":disposition, "reason":reason,
24352435
"repair_brief":brief, "next_actions":actions,

0 commit comments

Comments
 (0)