v0.4.8-pre.007
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# file: scripts/audit_khadhroony_workspace_rules.py
|
||||
# version: 14
|
||||
# version: 17
|
||||
|
||||
"""Audit mechanically verifiable rules specific to khadhroony-bot3."""
|
||||
|
||||
@@ -8,6 +8,7 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import dataclasses
|
||||
import json
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
@@ -100,29 +101,31 @@ def audit_kb_lib_tracing(root: pathlib.Path) -> list[Violation]:
|
||||
if match.group(1).startswith("TRACING_TARGET_"):
|
||||
re_exports.add(match.group(1))
|
||||
target_pattern = re.compile(
|
||||
r'^pub\(crate\) const (TRACING_TARGET_[A-Z0-9_]+): &str = "([^"]+)";$',
|
||||
r'pub\(crate\) const (TRACING_TARGET_[A-Z0-9_]+): &str\s*=\s*"([^"]+)";',
|
||||
re.MULTILINE,
|
||||
)
|
||||
declared_targets: dict[str, tuple[str, int]] = {}
|
||||
for constants in sorted((crate / "src").rglob("constants.rs")):
|
||||
text = constants.read_text(encoding="utf-8")
|
||||
match = target_pattern.search(text)
|
||||
if match is None:
|
||||
continue
|
||||
component = constants.parent.relative_to(crate / "src")
|
||||
expected = "kb-lib." + ".".join(component.parts)
|
||||
relative = constants.relative_to(root).as_posix()
|
||||
if match.group(2) != expected:
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_TRACE101",
|
||||
relative,
|
||||
text[: match.start()].count("\n") + 1,
|
||||
f"`{match.group(1)}` must use hierarchical target `{expected}`",
|
||||
for match in target_pattern.finditer(text):
|
||||
line = text[: match.start()].count("\n") + 1
|
||||
declared_targets[match.group(1)] = (relative, line)
|
||||
if match.group(2) != expected:
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_TRACE101",
|
||||
relative,
|
||||
line,
|
||||
f"`{match.group(1)}` must use hierarchical target `{expected}`",
|
||||
)
|
||||
)
|
||||
)
|
||||
macro_pattern = re.compile(
|
||||
r"tracing::(?:debug|error|info|trace|warn)!\(\s*target:\s*crate::(TRACING_TARGET_[A-Z0-9_]+)"
|
||||
)
|
||||
used_targets: set[str] = set()
|
||||
for path in sorted((crate / "src").rglob("*.rs")):
|
||||
relative = path.relative_to(root).as_posix()
|
||||
text = path.read_text(encoding="utf-8")
|
||||
@@ -137,6 +140,7 @@ def audit_kb_lib_tracing(root: pathlib.Path) -> list[Violation]:
|
||||
)
|
||||
)
|
||||
for match in macro_pattern.finditer(text):
|
||||
used_targets.add(match.group(1))
|
||||
if match.group(1) not in re_exports:
|
||||
violations.append(
|
||||
Violation(
|
||||
@@ -146,6 +150,16 @@ def audit_kb_lib_tracing(root: pathlib.Path) -> list[Violation]:
|
||||
f"missing crate-root re-export for `{match.group(1)}`",
|
||||
)
|
||||
)
|
||||
for target, (relative, line) in sorted(declared_targets.items()):
|
||||
if target not in used_targets:
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_TRACE104",
|
||||
relative,
|
||||
line,
|
||||
f"declared operational tracing target `{target}` is not used by a real tracing event",
|
||||
)
|
||||
)
|
||||
return violations
|
||||
|
||||
|
||||
@@ -358,6 +372,326 @@ def audit_reserved_materializer_scaffolds(root: pathlib.Path) -> list[Violation]
|
||||
"reserved materializer must return an ignored API result",
|
||||
)
|
||||
)
|
||||
if "std::result::Result::Ok(std::vec::Vec::new())" not in text:
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_MT007",
|
||||
relative,
|
||||
1,
|
||||
"reserved materializer legacy contract must return an empty event list",
|
||||
)
|
||||
)
|
||||
if not re.search(r"return crate::MT_[A-Z0-9_]+_ACCEPTED_FAMILIES;", text):
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_MT008",
|
||||
relative,
|
||||
1,
|
||||
"reserved materializer must use its exported accepted-family constant",
|
||||
)
|
||||
)
|
||||
return violations
|
||||
|
||||
|
||||
def iter_rust_json_object_macros(text: str) -> list[tuple[int, str]]:
|
||||
"""Return balanced `serde_json::json!({ ... })` object macro bodies."""
|
||||
|
||||
blocks: list[tuple[int, str]] = []
|
||||
needle = "serde_json::json!({"
|
||||
offset = 0
|
||||
while True:
|
||||
start = text.find(needle, offset)
|
||||
if start < 0:
|
||||
break
|
||||
brace = text.find("{", start)
|
||||
depth = 0
|
||||
in_string = False
|
||||
escaped = False
|
||||
end = brace
|
||||
while end < len(text):
|
||||
character = text[end]
|
||||
if in_string:
|
||||
if escaped:
|
||||
escaped = False
|
||||
elif character == "\\":
|
||||
escaped = True
|
||||
elif character == '"':
|
||||
in_string = False
|
||||
elif character == '"':
|
||||
in_string = True
|
||||
elif character == "{":
|
||||
depth += 1
|
||||
elif character == "}":
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
blocks.append((start, text[start: end + 1]))
|
||||
offset = end + 1
|
||||
break
|
||||
end += 1
|
||||
else:
|
||||
offset = start + len(needle)
|
||||
return blocks
|
||||
|
||||
|
||||
def audit_materializer_conventions(root: pathlib.Path) -> list[Violation]:
|
||||
"""Audit materializer names, constants and component facade structure."""
|
||||
|
||||
violations: list[Violation] = []
|
||||
materializer_root = root / "kb-lib/src/materializer"
|
||||
component_pattern = re.compile(
|
||||
r'pub\(crate\) const (MT_[A-Z0-9_]+)_COMPONENT_NAME: &str\s*=\s*"([^"]+)";'
|
||||
)
|
||||
processor_pattern = re.compile(
|
||||
r'pub\(crate\) const (MT_[A-Z0-9_]+)_PROCESSOR_NAME: &str\s*=\s*"([^"]+)";'
|
||||
)
|
||||
component_names: dict[str, tuple[str, str, int]] = {}
|
||||
processor_names: dict[str, tuple[str, str, int]] = {}
|
||||
for path in sorted(materializer_root.rglob("constants.rs")):
|
||||
relative = path.relative_to(root).as_posix()
|
||||
text = path.read_text(encoding="utf-8")
|
||||
for match in component_pattern.finditer(text):
|
||||
prefix = match.group(1)
|
||||
line = text[: match.start()].count("\n") + 1
|
||||
component_names[prefix] = (match.group(2), relative, line)
|
||||
for match in processor_pattern.finditer(text):
|
||||
prefix = match.group(1)
|
||||
line = text[: match.start()].count("\n") + 1
|
||||
processor_names[prefix] = (match.group(2), relative, line)
|
||||
for prefix, (component_name, relative, line) in sorted(component_names.items()):
|
||||
processor = processor_names.get(prefix)
|
||||
if processor is None:
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_MT009",
|
||||
relative,
|
||||
line,
|
||||
f"`{prefix}` component name requires one matching processor name",
|
||||
)
|
||||
)
|
||||
continue
|
||||
processor_name = processor[0]
|
||||
if not component_name.startswith("kb-lib.materializer."):
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_MT010",
|
||||
relative,
|
||||
line,
|
||||
f"materializer component name `{component_name}` must start with `kb-lib.materializer.`",
|
||||
)
|
||||
)
|
||||
expected_processor = component_name.removeprefix("kb-lib.")
|
||||
if processor_name != expected_processor:
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_MT011",
|
||||
processor[1],
|
||||
processor[2],
|
||||
f"processor name `{processor_name}` must equal `{expected_processor}`",
|
||||
)
|
||||
)
|
||||
for prefix, (_, relative, line) in sorted(processor_names.items()):
|
||||
if prefix not in component_names:
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_MT012",
|
||||
relative,
|
||||
line,
|
||||
f"`{prefix}` processor name requires one matching component name",
|
||||
)
|
||||
)
|
||||
for path in sorted(materializer_root.rglob("constants.rs")):
|
||||
relative = path.relative_to(root).as_posix()
|
||||
text = path.read_text(encoding="utf-8")
|
||||
for match in component_pattern.finditer(text):
|
||||
symbol = f"{match.group(1)}_COMPONENT_NAME"
|
||||
used_by_implementation = any(
|
||||
symbol in candidate.read_text(encoding="utf-8")
|
||||
for candidate in path.parent.rglob("*.rs")
|
||||
if candidate.name != "constants.rs"
|
||||
)
|
||||
if not used_by_implementation:
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_MT025",
|
||||
relative,
|
||||
text[: match.start()].count("\n") + 1,
|
||||
f"materializer component constant `{symbol}` is not consumed by its implementation",
|
||||
)
|
||||
)
|
||||
component_values = {value[0] for value in component_names.values()}
|
||||
processor_values = {value[0] for value in processor_names.values()}
|
||||
if len(component_values) != len(component_names):
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_MT017",
|
||||
"kb-lib/src/materializer",
|
||||
1,
|
||||
"materializer component names must be unique",
|
||||
)
|
||||
)
|
||||
if len(processor_values) != len(processor_names):
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_MT018",
|
||||
"kb-lib/src/materializer",
|
||||
1,
|
||||
"materializer processor names must be unique",
|
||||
)
|
||||
)
|
||||
tracing_pattern = re.compile(
|
||||
r'pub\(crate\) const TRACING_TARGET_MATERIALIZER_[A-Z0-9_]+: &str\s*=\s*"([^"]+)";'
|
||||
)
|
||||
for path in sorted(materializer_root.rglob("constants.rs")):
|
||||
relative = path.relative_to(root).as_posix()
|
||||
text = path.read_text(encoding="utf-8")
|
||||
for match in tracing_pattern.finditer(text):
|
||||
if match.group(1) not in component_values:
|
||||
line = text[: match.start()].count("\n") + 1
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_MT019",
|
||||
relative,
|
||||
line,
|
||||
f"materializer tracing target `{match.group(1)}` must equal one component name",
|
||||
)
|
||||
)
|
||||
config_path = root / "config/example.config.json"
|
||||
if config_path.exists():
|
||||
try:
|
||||
config = json.loads(config_path.read_text(encoding="utf-8"))
|
||||
except json.JSONDecodeError:
|
||||
config = None
|
||||
pending: list[object] = [config] if config is not None else []
|
||||
while pending:
|
||||
value = pending.pop()
|
||||
if isinstance(value, dict):
|
||||
targets = value.get("targets")
|
||||
if isinstance(targets, list):
|
||||
for target in targets:
|
||||
if (
|
||||
isinstance(target, str)
|
||||
and target.startswith("kb-lib.materializer.")
|
||||
and target not in component_values
|
||||
):
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_MT020",
|
||||
"config/example.config.json",
|
||||
1,
|
||||
f"logging target `{target}` is not a declared materializer component",
|
||||
)
|
||||
)
|
||||
pending.extend(value.values())
|
||||
elif isinstance(value, list):
|
||||
pending.extend(value)
|
||||
for path in sorted(materializer_root.rglob("*.rs")):
|
||||
relative = path.relative_to(root).as_posix()
|
||||
text = path.read_text(encoding="utf-8")
|
||||
if path.name != "constants.rs" and re.search(r'"processorName"\s*:\s*"', text):
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_MT021",
|
||||
relative,
|
||||
1,
|
||||
"persisted processor names must come from exported component constants",
|
||||
)
|
||||
)
|
||||
for offset, block in iter_rust_json_object_macros(text):
|
||||
if '"projectionVersion"' not in block:
|
||||
continue
|
||||
line = text[:offset].count("\n") + 1
|
||||
if '"processorName"' not in block:
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_MT022",
|
||||
relative,
|
||||
line,
|
||||
"versioned materializer output must persist its processor provenance",
|
||||
)
|
||||
)
|
||||
if re.search(
|
||||
r'"projectionVersion"\s*:\s*crate::MT_[A-Z0-9_]+_PROJECTION_VERSION',
|
||||
block,
|
||||
) is None:
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_MT023",
|
||||
relative,
|
||||
line,
|
||||
"materializer projection version must come from an exported component constant",
|
||||
)
|
||||
)
|
||||
if re.search(
|
||||
r'"processorName"\s*:\s*crate::MT_[A-Z0-9_]+_PROCESSOR_NAME',
|
||||
block,
|
||||
) is None:
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_MT024",
|
||||
relative,
|
||||
line,
|
||||
"materializer processor provenance must come from an exported component constant",
|
||||
)
|
||||
)
|
||||
if path.name != "constants.rs" and re.search(
|
||||
r"^\s*(?:pub(?:\(crate\))?\s+)?const\s+[A-Z][A-Z0-9_]*\b",
|
||||
text,
|
||||
re.MULTILINE,
|
||||
):
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_MT013",
|
||||
relative,
|
||||
1,
|
||||
"materializer constants must be declared in the component `constants.rs`",
|
||||
)
|
||||
)
|
||||
if path.name == "materializer.rs" and re.search(
|
||||
r"^pub struct Mt[A-Za-z0-9]+Materializer;", text, re.MULTILINE
|
||||
):
|
||||
constants_path = path.parent / "constants.rs"
|
||||
if not constants_path.exists():
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_MT014",
|
||||
relative,
|
||||
1,
|
||||
"materializer implementation requires a sibling `constants.rs`",
|
||||
)
|
||||
)
|
||||
if re.search(r'"(?:kb-lib\.)?materializer\.[^"]+"', text):
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_MT015",
|
||||
relative,
|
||||
1,
|
||||
"materializer identities must come from exported component constants",
|
||||
)
|
||||
)
|
||||
module_lines = [
|
||||
line
|
||||
for line in text.splitlines()
|
||||
if re.fullmatch(r"mod [A-Za-z_][A-Za-z0-9_]*;", line.strip()) is not None
|
||||
]
|
||||
if not module_lines:
|
||||
continue
|
||||
for index, line in enumerate(text.splitlines(), 1):
|
||||
stripped = line.strip()
|
||||
if not stripped or stripped.startswith("//") or stripped == "#[cfg(test)]":
|
||||
continue
|
||||
if re.fullmatch(r"mod [A-Za-z_][A-Za-z0-9_]*;", stripped) is not None:
|
||||
continue
|
||||
if stripped.startswith("pub use ") or stripped.startswith("pub(crate) use "):
|
||||
continue
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_MT016",
|
||||
relative,
|
||||
index,
|
||||
"materializer facade may contain only submodule declarations and re-exports",
|
||||
)
|
||||
)
|
||||
break
|
||||
return violations
|
||||
|
||||
|
||||
@@ -440,13 +774,13 @@ def audit_reserved_executor_scaffolds(root: pathlib.Path) -> list[Violation]:
|
||||
"reserved executor must build only an explicit zero-instruction plan",
|
||||
)
|
||||
)
|
||||
if reserved_count != 104:
|
||||
if reserved_count != 103:
|
||||
violations.append(
|
||||
Violation(
|
||||
"KH_EX007",
|
||||
"kb-lib/src/executor",
|
||||
1,
|
||||
f"expected 104 reserved executor boundaries, found {reserved_count}",
|
||||
f"expected 103 reserved executor boundaries, found {reserved_count}",
|
||||
)
|
||||
)
|
||||
return violations
|
||||
@@ -596,15 +930,16 @@ def main() -> int:
|
||||
root = pathlib.Path(arguments.root).resolve()
|
||||
violations = (
|
||||
audit_tracing(root)
|
||||
+ audit_kb_lib_tracing(root)
|
||||
+ audit_kb_lib_symbol_prefixes(root)
|
||||
+ audit_reserved_decoder_scaffolds(root)
|
||||
+ audit_reserved_materializer_scaffolds(root)
|
||||
+ audit_reserved_executor_scaffolds(root)
|
||||
+ audit_token_2022_naming(root)
|
||||
+ audit_private_kb_lib_paths_in_active_docs(root)
|
||||
+ audit_solana_types(root)
|
||||
+ audit_wincode_resolution(root)
|
||||
+audit_kb_lib_tracing(root)
|
||||
+audit_kb_lib_symbol_prefixes(root)
|
||||
+audit_reserved_decoder_scaffolds(root)
|
||||
+audit_reserved_materializer_scaffolds(root)
|
||||
+audit_materializer_conventions(root)
|
||||
+audit_reserved_executor_scaffolds(root)
|
||||
+audit_token_2022_naming(root)
|
||||
+audit_private_kb_lib_paths_in_active_docs(root)
|
||||
+audit_solana_types(root)
|
||||
+audit_wincode_resolution(root)
|
||||
)
|
||||
violations.sort(key=lambda item: (item.code, item.path, item.line, item.message))
|
||||
if not violations:
|
||||
|
||||
Reference in New Issue
Block a user