Skip to content

Commit 86245fa

Browse files
authored
Merge pull request #8 from full-stack-plugins/feat/rust-hook-prompt-guidance
Expose CodeGraph WASM coverage and explicit integration gaps
2 parents 781529e + 1e8fba1 commit 86245fa

14 files changed

Lines changed: 547 additions & 1 deletion

File tree

‎README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ Version output includes `cli_version`, `target`, and protocol information. Disco
9696

9797
The workspace requires `std`. The opt-in `wasm-precheck` build feature uses a bounded Rust worker with pinned Java/TypeScript grammar candidates. It currently reaches only single-file `lint java` and `lint typescript` requests without explicit native context; it is absent from the published npm `0.1.2` package. No `no_std`, completed project-wide WASM fallback, zero-unsafe, or fastest-runtime claim is made. Runtime OS calls require safety review; a comprehensive security audit is not claimed.
9898

99+
The current source-built CLI's `codeguard grammar status --format=json` reports the coverage gap without loading grammars or running lint: the pinned CodeGraph source has 30 vendored WASM files plus two distinct grammars resolved through its dependency; CodeGuard has two unqualified candidates and zero released syntax capabilities. The separate [coverage inventory](grammars/codegraph-coverage.json) is not an approved asset manifest. COBOL's 16.4 MB source asset also exceeds the current Rust loader's 8 MiB limit.
100+
99101
### npm installation and one-off use
100102

101103
On Apple Silicon macOS, the published `0.1.2` package was verified with a fresh npm cache:

‎README.zh-CN.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ cargo build --locked -p codeguard-cli
9696

9797
工作区要求 `std`。可选 `wasm-precheck` 构建特性已用固定的 Java/TypeScript grammar 候选资产与有界 Rust worker,为缺少显式原生上下文的 `lint java`、`lint typescript` 单文件请求提供疑似语法观察;公开 npm `0.1.2` 不含该路径。不声称 `no_std`、已完成项目级 WASM 兜底、零 unsafe 或性能最快。运行层系统调用需要安全审查,当前不宣称已完成整体安全审计。
9898

99+
当前源码构建的 CLI 可用 `codeguard grammar status --format=json` 只读展示来源覆盖缺口,不加载 grammar 或运行 lint:固定 CodeGraph 来源含 30 份随仓 WASM,另有两种独立 grammar 从其依赖取得;CodeGuard 当前只有两份未验收候选,已发行语法能力为零。[覆盖库存](grammars/codegraph-coverage.json)不是已批准资产清单。COBOL 来源资产约 16.4 MB,还超过当前 Rust 加载器的 8 MiB 限额。
100+
99101
### npm 安装与一次性调用
100102

101103
已在 Apple Silicon macOS 上通过全新 npm 缓存验证公开 `0.1.2` 包:
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
//! CodeGraph grammar 来源覆盖清单的只读投影;不加载 WASM,也不签发检查结果。
2+
3+
use codeguard_adapters::bundled_grammar_candidates;
4+
use serde::Deserialize;
5+
use serde_json::{Value, json};
6+
use std::collections::BTreeSet;
7+
use std::process::ExitCode;
8+
9+
const SOURCE_COMMIT: &str = "40f112453583a2304c4b605a3a9d6545919662bd";
10+
const LANGUAGES: [&str; 32] = [
11+
"arkts",
12+
"c",
13+
"cfml",
14+
"cfquery",
15+
"cfscript",
16+
"cobol",
17+
"cpp",
18+
"csharp",
19+
"dart",
20+
"erlang",
21+
"go",
22+
"java",
23+
"javascript",
24+
"kotlin",
25+
"lua",
26+
"luau",
27+
"nix",
28+
"objc",
29+
"pascal",
30+
"php",
31+
"python",
32+
"r",
33+
"ruby",
34+
"rust",
35+
"scala",
36+
"solidity",
37+
"swift",
38+
"terraform",
39+
"tsx",
40+
"typescript",
41+
"vbnet",
42+
"zig",
43+
];
44+
45+
#[derive(Deserialize)]
46+
#[serde(deny_unknown_fields)]
47+
struct CoverageInventory {
48+
schema_version: String,
49+
source_repository: String,
50+
source_commit: String,
51+
scope: String,
52+
note: String,
53+
assets: Vec<CoverageAsset>,
54+
}
55+
56+
#[derive(Deserialize)]
57+
#[serde(deny_unknown_fields)]
58+
struct CoverageAsset {
59+
language: String,
60+
provider: String,
61+
source_path: Option<String>,
62+
sha256: Option<String>,
63+
bytes: Option<usize>,
64+
}
65+
66+
/// 展示 CodeGraph 固定来源与 CodeGuard 已验证候选之间的覆盖差距。
67+
/// 参数仅接受 `--format human|json`;返回 3 表示仍有未接入或未验收的 grammar。
68+
pub fn run(args: &[String]) -> ExitCode {
69+
let format = match args {
70+
[] => "human",
71+
[option, value] if option == "--format" && matches!(value.as_str(), "human" | "json") => {
72+
value.as_str()
73+
}
74+
[option] if option == "--format=human" => "human",
75+
[option] if option == "--format=json" => "json",
76+
_ => {
77+
eprintln!("grammar status 仅支持 --format human|json");
78+
return ExitCode::from(2);
79+
}
80+
};
81+
let report = match coverage_report() {
82+
Ok(report) => report,
83+
Err(reason) => {
84+
eprintln!("grammar 来源清单未完成:{reason}");
85+
return ExitCode::from(4);
86+
}
87+
};
88+
if format == "json" {
89+
println!("{report}");
90+
} else {
91+
println!(
92+
"CodeGraph grammar 32 种独立资产:随仓固定 30;CodeGuard 候选 {};已验收发行 0。",
93+
report["candidate_count"]
94+
);
95+
println!("全部语法初检覆盖未完成;原生 lint 义务不变。使用 --format=json 查看逐语言缺口。");
96+
}
97+
ExitCode::from(3)
98+
}
99+
100+
fn coverage_report() -> Result<Value, String> {
101+
let raw = include_bytes!("../../../grammars/codegraph-coverage.json");
102+
let strict = codeguard_adapters::parse_unique_json(raw).map_err(str::to_owned)?;
103+
let inventory: CoverageInventory =
104+
serde_json::from_value(strict).map_err(|error| error.to_string())?;
105+
validate_inventory(&inventory)?;
106+
let candidates = bundled_grammar_candidates()?;
107+
let candidate_languages = candidates
108+
.assets
109+
.iter()
110+
.map(|asset| asset.language.as_str())
111+
.collect::<BTreeSet<_>>();
112+
let inventory_languages = inventory
113+
.assets
114+
.iter()
115+
.map(|asset| asset.language.as_str())
116+
.collect::<BTreeSet<_>>();
117+
if !candidate_languages.is_subset(&inventory_languages) {
118+
return Err("CodeGuard 候选不在 CodeGraph 来源清单中".into());
119+
}
120+
let assets = inventory
121+
.assets
122+
.iter()
123+
.map(|asset| {
124+
let candidate = candidates
125+
.assets
126+
.iter()
127+
.find(|candidate| candidate.language == asset.language);
128+
json!({
129+
"language":asset.language,
130+
"provider":asset.provider,
131+
"source_path":asset.source_path,
132+
"source_sha256":asset.sha256,
133+
"source_bytes":asset.bytes,
134+
"integration_status":candidate.map_or("not_integrated", |item| item.release_status.as_str()),
135+
"runtime_observation":candidate.map(|item| item.codeguard_runtime_validation.as_str()),
136+
"released":false,
137+
"gap":if asset.provider == "tree_sitter_wasms_dependency" {"dependency_bytes_not_pinned"}
138+
else if asset.bytes.is_some_and(|bytes| bytes > 8 * 1024 * 1024) {"current_loader_size_limit"}
139+
else if candidate.is_some() {"language_qualification_and_release_pending"}
140+
else {"asset_provenance_license_and_loader_validation_pending"},
141+
})
142+
})
143+
.collect::<Vec<_>>();
144+
Ok(json!({
145+
"schema_version":"1.0.0",
146+
"report_type":"grammar_coverage_inventory",
147+
"source_repository":inventory.source_repository,
148+
"source_commit":inventory.source_commit,
149+
"codegraph_grammar_count":inventory.assets.len(),
150+
"codegraph_vendored_count":inventory.assets.iter().filter(|asset| asset.provider == "codegraph_vendored").count(),
151+
"candidate_count":candidates.assets.len(),
152+
"released_count":0,
153+
"authority":"source_inventory_only",
154+
"parser_capability":"unverified",
155+
"gate_effect":"none",
156+
"execution":"not_run",
157+
"delivery_decision":"not_evaluated",
158+
"next_action":"固定各缺口资产的上游来源与许可证,逐语言验证 Rust 加载、版本语料及原生对照;不得凭此库存跳过原生 lint。",
159+
"assets":assets,
160+
}))
161+
}
162+
163+
fn validate_inventory(inventory: &CoverageInventory) -> Result<(), String> {
164+
if inventory.schema_version != "1.0.0"
165+
|| inventory.source_repository != "https://github.com/partme-ai/codegraph"
166+
|| inventory.source_commit != SOURCE_COMMIT
167+
|| inventory.scope != "CodeGraph grammar file map excluding the jsx alias of javascript"
168+
|| inventory.note.is_empty()
169+
|| inventory.assets.len() != LANGUAGES.len()
170+
{
171+
return Err("CodeGraph 来源清单版本、来源或范围不符".into());
172+
}
173+
let mut seen = BTreeSet::new();
174+
let mut vendored = 0;
175+
for asset in &inventory.assets {
176+
if !seen.insert(asset.language.as_str()) {
177+
return Err("CodeGraph grammar 重复".into());
178+
}
179+
match asset.provider.as_str() {
180+
"codegraph_vendored" => {
181+
vendored += 1;
182+
let filename = if asset.language == "csharp" {
183+
"c_sharp"
184+
} else {
185+
&asset.language
186+
};
187+
if asset.source_path.as_deref()
188+
!= Some(format!("src/extraction/wasm/tree-sitter-{filename}.wasm").as_str())
189+
|| !asset.sha256.as_deref().is_some_and(valid_sha256)
190+
|| asset.bytes.is_none_or(|bytes| bytes < 8)
191+
{
192+
return Err("CodeGraph 随仓资产身份无效".into());
193+
}
194+
}
195+
"tree_sitter_wasms_dependency" => {
196+
if !matches!(asset.language.as_str(), "objc" | "solidity")
197+
|| asset.source_path.is_some()
198+
|| asset.sha256.is_some()
199+
|| asset.bytes.is_some()
200+
{
201+
return Err("CodeGraph 依赖资产身份无效".into());
202+
}
203+
}
204+
_ => return Err("CodeGraph 资产来源未知".into()),
205+
}
206+
}
207+
if vendored != 30 || seen != LANGUAGES.into_iter().collect() {
208+
return Err("CodeGraph grammar 覆盖集合不符".into());
209+
}
210+
Ok(())
211+
}
212+
213+
fn valid_sha256(value: &str) -> bool {
214+
value.len() == 64
215+
&& value
216+
.bytes()
217+
.all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
218+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ pub mod git_index_safety;
9797
pub mod git_index_safety_command;
9898
#[cfg(unix)]
9999
pub mod go_lint_command;
100+
pub mod grammar_status_command;
100101
#[cfg(unix)]
101102
pub mod hook_execute_command;
102103
pub mod hook_plan_command;

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ fn main() -> ExitCode {
6363
println!(
6464
"规则目录:rules list <language|all> [path] [--format human|json];只读配置声明、候选规则来源及目录缺口,不执行检查或批准白名单。"
6565
);
66+
println!(
67+
"语法资产覆盖:grammar status [--format human|json];只读列出固定 CodeGraph 来源的 32 种独立 grammar、CodeGuard 候选与未接入项;不加载 WASM、不运行 lint。"
68+
);
6669
println!(
6770
"检查预算:check all 支持 --jobs 1–64 和 CODEGUARD_JOBS;--timeout 优先于 CODEGUARD_TIMEOUT,默认 30m。项目默认值见 .codeguard/runtime.json 1.1;当前仅原生执行受截止时间约束。"
6871
);
@@ -76,6 +79,9 @@ fn main() -> ExitCode {
7679
[command, rest @ ..] if command == "init" => codeguard_cli::init_command::run(rest),
7780
[command, rest @ ..] if command == "config" => codeguard_cli::config_command::run(rest),
7881
[command, rest @ ..] if command == "tools" => codeguard_cli::tools_command::run(rest),
82+
[command, operation, rest @ ..] if command == "grammar" && operation == "status" => {
83+
codeguard_cli::grammar_status_command::run(rest)
84+
}
7985
#[cfg(unix)]
8086
[command, rest @ ..] if command == "doctor" => codeguard_cli::doctor_command::run(rest),
8187
[command, rest @ ..] if command == "plan" => codeguard_cli::plan_command::run(rest),
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use serde_json::Value;
2+
use std::process::Command;
3+
4+
#[test]
5+
fn codegraph_coverage_is_explicit_and_never_claims_parser_or_gate_completion() {
6+
let output = Command::new(env!("CARGO_BIN_EXE_codeguard"))
7+
.args(["grammar", "status", "--format=json"])
8+
.output()
9+
.expect("grammar status");
10+
assert_eq!(output.status.code(), Some(3));
11+
let report: Value = serde_json::from_slice(&output.stdout).expect("JSON report");
12+
assert_eq!(report["report_type"], "grammar_coverage_inventory");
13+
assert_eq!(report["codegraph_grammar_count"], 32);
14+
assert_eq!(report["codegraph_vendored_count"], 30);
15+
assert_eq!(report["candidate_count"], 2);
16+
assert_eq!(report["released_count"], 0);
17+
assert_eq!(report["authority"], "source_inventory_only");
18+
assert_eq!(report["parser_capability"], "unverified");
19+
assert_eq!(report["gate_effect"], "none");
20+
assert_eq!(report["execution"], "not_run");
21+
assert_eq!(report["delivery_decision"], "not_evaluated");
22+
let assets = report["assets"].as_array().unwrap();
23+
assert_eq!(assets.len(), 32);
24+
for asset in assets {
25+
assert_eq!(asset["released"], false);
26+
assert!(!asset["gap"].as_str().unwrap().is_empty());
27+
}
28+
for language in ["java", "typescript"] {
29+
let asset = assets
30+
.iter()
31+
.find(|row| row["language"] == language)
32+
.unwrap();
33+
assert_eq!(asset["integration_status"], "candidate_unvalidated");
34+
assert_eq!(asset["runtime_observation"], "rust_loader_smoke_passed");
35+
}
36+
let cobol = assets
37+
.iter()
38+
.find(|row| row["language"] == "cobol")
39+
.unwrap();
40+
assert_eq!(cobol["gap"], "current_loader_size_limit");
41+
assert!(cobol["source_bytes"].as_u64().unwrap() > 8 * 1024 * 1024);
42+
for language in ["objc", "solidity"] {
43+
let asset = assets
44+
.iter()
45+
.find(|row| row["language"] == language)
46+
.unwrap();
47+
assert_eq!(asset["gap"], "dependency_bytes_not_pinned");
48+
assert!(asset["source_sha256"].is_null());
49+
}
50+
}
51+
52+
#[test]
53+
fn grammar_status_rejects_unexpected_arguments_without_scanning() {
54+
let output = Command::new(env!("CARGO_BIN_EXE_codeguard"))
55+
.args(["grammar", "status", "--check"])
56+
.output()
57+
.expect("invalid grammar command");
58+
assert_eq!(output.status.code(), Some(2));
59+
assert!(output.stdout.is_empty());
60+
}

‎docs/Codeguard-Architecture.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,8 @@ A completed native violation never selects fallback to obtain a clean result. A
241241

242242
Reuse pinned CodeGraph grammar WASM artifacts and applicable upstream licenses, source references, patches and corpus cases. Do not copy CodeGraph's graph extraction or source-blanking heuristics into a syntax verdict. CodeGraph's `src/extraction/grammars.ts` loads language artifacts through `web-tree-sitter`; successful loading there does not prove compatibility with Codeguard's Rust runtime. The local source review on 2026-09-28 found modified grammar artifacts and about 65 MB of grammar files, so a live directory is not a release input.
243243

244+
The read-only `codeguard grammar status` command now projects a [pinned CodeGraph coverage inventory](../grammars/codegraph-coverage.json): 30 vendored WASM files and two dependency-provided grammars, with Java and TypeScript as CodeGuard candidates and none qualified for release. This inventory is deliberately separate from the pinned [candidate asset manifest](../grammars/manifest.json). In particular, the COBOL source file exceeds the current loader's 8 MiB input limit. Inventory rows never trigger loading or count as supported parser capability.
245+
244246
```text
245247
grammars/ # proposed release assets, not project state
246248
├── manifest.json # immutable source, digest, ABI and capability map

‎docs/Codeguard-Architecture.zh_CN.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,8 @@ flowchart TD
241241

242242
复用固定版本的 CodeGraph grammar WASM,以及适用的上游许可证、源码引用、补丁和语料。不能把 CodeGraph 的图提取或源码遮盖启发式直接用于语法判定。CodeGraph 的 `src/extraction/grammars.ts` 通过 `web-tree-sitter` 加载语言资产,在那里可加载不证明兼容 Codeguard 的 Rust 运行时。发行时必须固定不可变的来源与资产清单,不能把正在修改的本地目录直接当作发行输入。
243243

244+
只读 `codeguard grammar status` 现投影[固定来源覆盖库存](../grammars/codegraph-coverage.json):CodeGraph 随仓 30 份 WASM,依赖提供另两种独立 grammar;CodeGuard 只有 Java、TypeScript 两份候选,尚无已验收发行能力。覆盖库存与[候选资产清单](../grammars/manifest.json)分开,不能凭库存行自动加载或宣称支持。尤其 COBOL 来源资产超过当前加载器 8 MiB 限额。
245+
244246
```text
245247
grammars/ # 规划中的发行资产,不是项目状态
246248
├── manifest.json # 固定来源、摘要、ABI 及能力映射

‎docs/Codeguard-Technical-Design.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ The opt-in CLI build now also emits [ESLint feedback 0.3.0](../schemas/eslint-lo
191191

192192
Import only pinned WASM bytes with upstream license, commit/patch provenance, SHA-256, ABI, tested runtime and language/dialect ranges, corpus reference and known gaps. Verify each copied CodeGraph artifact in the selected Rust Tree-sitter runtime; do not copy TypeScript extraction logic or assume CodeGraph support equals syntax acceptance. A release manifest belongs to distribution assets, not writable `.codeguard/` project state.
193193

194+
Use `codeguard grammar status --format=json` to query the separate source coverage inventory. It reports 32 distinct CodeGraph grammar assets, two CodeGuard candidates, and zero released capabilities without running a parser. The two dependency-provided assets lack pinned bytes, and COBOL exceeds the current 8 MiB loader limit; these remain explicit gaps before any per-language qualification.
195+
194196
Use a parent-controlled parser worker with a total deadline, per-file input bound, memory/process limit and capped diagnostics. Lazy-load selected grammars, parse without general network/filesystem imports, and terminate a stuck worker without losing native results from other modules. Host-specific enforcement and Rust MSRV compatibility must be tested; numeric production budgets remain measurement-driven rather than invented promises.
195197

196198
The opt-in Rust CLI now has a private [candidate worker](../crates/codeguard-cli/src/syntax_worker_command.rs) and [parent verifier](../crates/codeguard-cli/src/syntax_worker_runner.rs). On Unix they use the existing process-group runner with a 1 MiB source limit, 64 KiB output limit, caller deadline and cancellation, then recheck source/grammar digests and recovery positions. This is a measured contract slice, not the finished isolation design: a verified memory bound, cross-platform enforcement, formal lint/check routing and release packaging remain open. Candidate grammar observations remain `incomplete` even when parsing finds no recovery nodes.

0 commit comments

Comments
 (0)