feat(seedgo): feat: seedgo proof system + checklist directory mode

Co-Authored-By: @seedgo <seedgo@aipass>
This commit is contained in:
AIOSAI
2026-03-22 23:31:34 -07:00
co-authored by @seedgo
parent 873df4e062
commit 3edef2a95a
19 changed files with 3058 additions and 2 deletions
@@ -0,0 +1,2 @@
# aipass_proof — Proof handlers for the aipass standards pack.
# Discovery: seedgo_proof module finds *_proof/ dirs, runs *_proof.py files inside.
@@ -0,0 +1,89 @@
# Content Naming Proof — Function Name Convention
**Version:** 1.0.0
**Created:** 2026-03-22
**Modified:** 2026-03-22
**Status:** Placeholder
---
## Purpose
The content naming proof verifies that every `*_content.py` file exports a function whose name matches the convention the `standards_query` module expects. The query system derives the function name from the filename — if the name doesn't match, the query fails silently.
---
## The Naming Convention
The convention is deterministic: the standard name is extracted from the filename, then used to construct the function name.
```
Filename: {name}_content.py
Function: get_{name}_standards()
```
### Correct Examples
| File | Expected Function |
|------|-------------------|
| `architecture_content.py` | `get_architecture_standards()` |
| `imports_content.py` | `get_imports_standards()` |
| `log_handler_content.py` | `get_log_handler_standards()` |
| `error_handling_content.py` | `get_error_handling_standards()` |
| `json_structure_content.py` | `get_json_structure_standards()` |
### Incorrect Examples
| File | Wrong Function | Why |
|------|----------------|-----|
| `architecture_content.py` | `get_architecture_content()` | Suffix must be `_standards`, not `_content` |
| `architecture_content.py` | `get_arch_standards()` | Name must match full prefix, no abbreviations |
| `imports_content.py` | `get_import_standards()` | Must be `imports` (plural), exactly as in filename |
---
## How standards_query Resolves Function Names
1. Discover all `*_content.py` files via glob
2. For each file, extract the name: strip `_content.py` suffix to get `{name}`
3. Construct expected function name: `get_{name}_standards`
4. Import the module and call `getattr(module, function_name)`
5. If the attribute doesn't exist, the standard silently has no queryable content
<!-- TODO: Document the exact standards_query resolution code path
and error handling behavior -->
---
## What the Scan Checks
1. **Glob** all `*_content.py` files in the pack
2. **Extract** the standard name from each filename
3. **Import** the module (or parse with AST)
4. **Verify** the expected `get_{name}_standards` function exists
5. **Report** any mismatches
---
## Common Failures
- **Wrong suffix**: `get_{name}_content()` instead of `get_{name}_standards()`
- **Abbreviated name**: `get_arch_standards()` instead of `get_architecture_standards()`
- **Missing function**: File exists but contains no matching function at all
- **Typo in name**: `get_arcitecture_standards()` — close but not exact
---
## How to Fix
1. Open the content file
2. Extract the standard name from the filename (everything before `_content.py`)
3. Ensure the file contains exactly: `def get_{name}_standards() -> str:`
4. Verify the function returns a string of formatted content
---
## Related
- **DPLAN-0044**: Self-audit tooling design
- **tools/content_naming_scanner.py**: Original prototype
- **Checker**: `content_naming.py` in this directory
@@ -0,0 +1,141 @@
# =================== AIPass ====================
# Name: content_naming.py
# Description: Verify content file naming convention (get_{name}_standards)
# Version: 1.0.0
# Created: 2026-03-22
# Modified: 2026-03-22
# =============================================
"""Content Naming Proof -- Verify every *_content.py has correctly named get_{name}_standards().
Convention: {name}_content.py must provide a get_{name}_standards() function at module level.
This is how standards_query discovers and calls content providers.
Interface:
scan(pack_dir: Path) -> dict
Returns: {"passed": bool, "total": int, "correct": list, "incorrect": list,
"issues": list, "summary": str}
Reference: tools/content_naming_scanner.py (original prototype)
"""
from __future__ import annotations
import ast
from pathlib import Path
from aipass.prax import logger
from aipass.seedgo.apps.handlers.json import json_handler
# Directories to skip during scanning
_SKIP_DIRS = {".archive", ".sorting_unprocessed", "__pycache__"}
def _parse_public_functions(file_path: Path) -> list[str]:
"""Parse a Python file and return module-level public function names.
Uses ast.parse to inspect the file without importing it. Only returns
top-level FunctionDef nodes whose names do not start with underscore.
Args:
file_path: Path to the Python file to parse.
Returns:
List of public function names found at module level.
"""
source = file_path.read_text(encoding="utf-8")
tree = ast.parse(source, filename=str(file_path))
return [
node.name
for node in ast.iter_child_nodes(tree)
if isinstance(node, ast.FunctionDef) and not node.name.startswith("_")
]
def scan(pack_dir: Path) -> dict:
"""Scan all *_content.py files and verify naming conventions.
For each content file, derives the expected function name
(get_{stem}_standards) and checks whether it exists at module level
via AST parsing.
Args:
pack_dir: Path to the standards pack directory (e.g. handlers/aipass_standards/).
Returns:
Dict with keys: passed, total, correct, incorrect, issues, summary.
"""
logger.info(f"[content_naming] Scanning {pack_dir}")
correct: list[str] = []
incorrect: list[str] = []
issues: list[dict[str, str | list[str]]] = []
if not pack_dir.is_dir():
return {
"passed": True,
"total": 0,
"correct": [],
"incorrect": [],
"issues": [{"file": str(pack_dir), "issue": "Directory does not exist"}],
"summary": "0 content files (directory missing)",
}
for content_file in sorted(pack_dir.glob("*_content.py")):
# Skip files inside excluded directories
if any(part in _SKIP_DIRS for part in content_file.relative_to(pack_dir).parts):
continue
# Skip underscore-prefixed files (__init__.py, _helpers.py, etc.)
if content_file.name.startswith("_"):
continue
# Derive expected function name: {name}_content.py -> get_{name}_standards
stem = content_file.stem.removesuffix("_content")
expected_fn = f"get_{stem}_standards"
# Parse via AST -- no imports
try:
public_fns = _parse_public_functions(content_file)
except SyntaxError as exc:
incorrect.append(content_file.name)
issues.append({
"file": content_file.name,
"expected": expected_fn,
"issue": f"SyntaxError: {exc}",
})
continue
if expected_fn in public_fns:
correct.append(content_file.name)
else:
incorrect.append(content_file.name)
issues.append({
"file": content_file.name,
"expected": expected_fn,
"found_functions": public_fns,
"issue": f"Missing expected function {expected_fn}()",
})
total = len(correct) + len(incorrect)
passed = len(incorrect) == 0
# Build human-readable summary
parts = [f"{len(correct)} correct"]
if incorrect:
parts.append(f"{len(incorrect)} incorrect")
parts.append(f"{total} total")
summary = " | ".join(parts)
logger.info(f"[content_naming] Result: {summary}")
json_handler.log_operation("proof_scan", {"proof": "content_naming", "passed": passed})
return {
"passed": passed,
"total": total,
"correct": correct,
"incorrect": incorrect,
"issues": issues,
"summary": summary,
}
@@ -0,0 +1,81 @@
# =================== AIPass ====================
# Name: content_naming_content.py
# Description: Queryable content for the content naming proof
# Version: 1.0.0
# Created: 2026-03-22
# Modified: 2026-03-22
# =============================================
"""
Content Naming Proof Content Handler
Provides formatted content naming proof content for the query system.
Module orchestrates, handler implements.
"""
from aipass.seedgo.apps.handlers.json import json_handler
def get_content_naming_proof() -> str:
"""Return content naming proof content for query system.
Returns:
str: Formatted proof text with Rich styling
"""
sep = "\u2500" * 70
lines = [
f"[dim]{sep}[/dim]",
"[bold red]CONTENT NAMING PROOF \u2014 Function Name Convention[/bold red]",
"[dim]Every *_content.py must have get_{name}_standards() matching its filename.[/dim]",
f"[dim]{sep}[/dim]",
"",
"[bold cyan]WHAT THIS CHECKS:[/bold cyan]",
" Each content file [dim]{name}_content.py[/dim] must export a function named",
" [dim]get_{name}_standards()[/dim] that the standards_query module can call.",
"",
"[bold cyan]THE NAMING CONVENTION:[/bold cyan]",
" [yellow]File:[/yellow] [dim]architecture_content.py[/dim]",
" [yellow]Function:[/yellow] [dim]get_architecture_standards()[/dim]",
"",
" [yellow]File:[/yellow] [dim]imports_content.py[/dim]",
" [yellow]Function:[/yellow] [dim]get_imports_standards()[/dim]",
"",
" [yellow]File:[/yellow] [dim]log_handler_content.py[/dim]",
" [yellow]Function:[/yellow] [dim]get_log_handler_standards()[/dim]",
"",
"[bold cyan]WHY IT MATTERS:[/bold cyan]",
" The [dim]standards_query[/dim] module calls this exact function name to retrieve",
" content. It derives the function name from the filename:",
" [dim]{name}_content.py[/dim] \u2192 [dim]get_{name}_standards()[/dim]",
"",
" Wrong name = query fails silently. The standard appears to have no",
" content even though the file exists.",
"",
f"[dim]{sep}[/dim]",
"[bold cyan]COMMON FAILURES:[/bold cyan]",
"",
"[yellow]Wrong function name:[/yellow]",
" [red]\u2717[/red] [dim]def get_architecture_content()[/dim] \u2014 wrong suffix",
" [red]\u2717[/red] [dim]def get_arch_standards()[/dim] \u2014 abbreviated name",
" [green]\u2713[/green] [dim]def get_architecture_standards()[/dim]",
"",
"[yellow]Missing function entirely:[/yellow]",
" [red]\u2717[/red] File exists but has no function matching the convention",
"",
# TODO: Expand with details on how standards_query resolves
# function names and what happens on resolution failure.
"",
f"[dim]{sep}[/dim]",
"[bold cyan]HOW TO FIX:[/bold cyan]",
" [yellow]1.[/yellow] Extract the standard name from the filename: [dim]{name}_content.py[/dim] \u2192 [dim]{name}[/dim]",
" [yellow]2.[/yellow] Ensure the file contains: [dim]def get_{name}_standards() -> str:[/dim]",
" [yellow]3.[/yellow] Verify the function returns a string of formatted content",
"",
"[bold cyan]RELATED:[/bold cyan]",
" [dim]DPLAN-0044: Self-audit tooling design[/dim]",
" [dim]tools/content_naming_scanner.py: Original prototype[/dim]",
f"[dim]{sep}[/dim]",
]
json_handler.log_operation("proof_content_queried", {"proof": "content_naming"})
return "\n".join(lines)
@@ -0,0 +1,116 @@
# Interface Proof — Checker Interface Compliance
**Version:** 1.0.0
**Created:** 2026-03-22
**Modified:** 2026-03-22
**Status:** Placeholder
---
## Purpose
The interface proof verifies that every checker in a pack declares the correct `AUDIT_SCOPE` variable and uses the function signature that the audit engine expects. Without these, the audit engine cannot scope or call the checker correctly.
---
## AUDIT_SCOPE
Every checker must declare `AUDIT_SCOPE` at module level. This tells the audit engine what to pass to the checker.
| Value | Meaning | What the checker receives |
|-------|---------|--------------------------|
| `"all_files"` | Checker runs once per Python file in the branch | `file_path`, `module_name`, `branch_path` |
| `"entry_point"` | Checker runs only on the main entry point | `file_path`, `module_name`, `branch_path` |
| `"branch_level"` | Checker runs once for the whole branch | `branch_path`, `module_name` |
```python
# Example declaration
AUDIT_SCOPE = "all_files"
```
---
## Function Signatures
The audit engine calls a specific function depending on the AUDIT_SCOPE:
### File-Level Checkers (`all_files` / `entry_point`)
```python
def check_module(file_path: str, module_name: str, branch_path: str) -> dict:
"""Validate a single file against the standard.
Args:
file_path: Absolute path to the file being checked
module_name: Name of the module (derived from filename)
branch_path: Absolute path to the branch root
Returns:
dict with at minimum: {"passed": bool, "issues": list}
"""
```
### Branch-Level Checkers (`branch_level`)
```python
def check_branch(branch_path: str, module_name: str) -> dict:
"""Validate the branch as a whole against the standard.
Args:
branch_path: Absolute path to the branch root
module_name: Name of the standard being checked
Returns:
dict with at minimum: {"passed": bool, "issues": list}
"""
```
---
## How the Audit Engine Discovers and Calls Checkers
1. **Discovery**: Glob `*_check.py` in the pack directory
2. **Import**: `importlib.import_module()` on each discovered file
3. **Scope read**: Read `AUDIT_SCOPE` from the module
4. **Dispatch**: Based on scope, call `check_module()` or `check_branch()` with the correct arguments
5. **Collect results**: Aggregate return dicts into the audit report
If `AUDIT_SCOPE` is missing, the engine cannot determine how to call the checker. If the function signature is wrong, the call raises `TypeError` at runtime.
---
## What the Scan Checks
1. **AUDIT_SCOPE exists** as a module-level variable in every `*_check.py`
2. **AUDIT_SCOPE value** is one of the three valid strings
3. **Function name** matches the scope: `check_module` for file-level, `check_branch` for branch-level
4. **Parameter count** matches the expected signature
<!-- TODO: Detail the AST inspection approach vs import-based validation
once the scanner implementation is finalized -->
---
## Common Failures
| Failure | Impact |
|---------|--------|
| Missing `AUDIT_SCOPE` | Audit engine skips or crashes on the checker |
| Wrong function name (`check` instead of `check_module`) | `AttributeError` at runtime |
| Missing parameters | `TypeError` when audit engine calls with expected args |
| Invalid scope value | Engine doesn't know how to route the checker |
---
## How to Fix
1. Add `AUDIT_SCOPE = "all_files"` (or `"entry_point"` / `"branch_level"`) at the top of the checker
2. Ensure the function name is `check_module` (file-level) or `check_branch` (branch-level)
3. Ensure all parameters are present in the signature
---
## Related
- **DPLAN-0044**: Self-audit tooling design
- **tools/interface_scanner.py**: Original prototype
- **Checker**: `interface.py` in this directory
@@ -0,0 +1,238 @@
# =================== AIPass ====================
# Name: interface.py
# Description: Verify checker interface (AUDIT_SCOPE + function signature)
# Version: 1.0.0
# Created: 2026-03-22
# Modified: 2026-03-22
# =============================================
"""Interface Proof -- Verify each checker declares AUDIT_SCOPE and correct function signature.
Every *_check.py in a standards pack must:
1. Declare AUDIT_SCOPE at module level (one of: all_files, entry_point, branch_level)
2. Implement the correct entry function (check_module or check_branch)
3. Have the correct first parameter (module_path or branch_path) plus bypass_rules
Interface:
scan(pack_dir: Path) -> dict
Returns: {"passed": bool, "total": int, "pass_count": int, "fail_count": int,
"results": list[dict], "issues": list, "summary": str}
Reference: tools/interface_scanner.py (original prototype)
"""
from __future__ import annotations
import ast
from pathlib import Path
from aipass.prax import logger
from aipass.seedgo.apps.handlers.json import json_handler
VALID_SCOPES = {"all_files", "entry_point", "branch_level"}
# Directories / files to skip inside the pack
_SKIP_DIRS = frozenset({".archive", ".sorting_unprocessed"})
# -- AST helpers ---------------------------------------------------------------
def _extract_audit_scope(tree: ast.Module) -> str | None:
"""Return the string value of AUDIT_SCOPE if defined at module level."""
for node in ast.iter_child_nodes(tree):
if not isinstance(node, ast.Assign):
continue
for target in node.targets:
if isinstance(target, ast.Name) and target.id == "AUDIT_SCOPE":
if isinstance(node.value, ast.Constant) and isinstance(
node.value.value, str
):
return node.value.value
return None
def _find_function(tree: ast.Module, name: str) -> ast.FunctionDef | None:
"""Return the FunctionDef node for *name* if it exists at module level."""
for node in ast.iter_child_nodes(tree):
if isinstance(node, ast.FunctionDef) and node.name == name:
return node
return None
def _check_params(func: ast.FunctionDef, expected_first: str) -> tuple[bool, str]:
"""Verify the function has (expected_first, bypass_rules=None).
Returns (ok, detail_message).
"""
args = func.args
positional = [a.arg for a in args.posonlyargs] + [a.arg for a in args.args]
if not positional:
return False, "no parameters"
if positional[0] != expected_first:
return False, f"first param is '{positional[0]}', expected '{expected_first}'"
if len(positional) < 2 or positional[1] != "bypass_rules":
# Also accept bypass_rules as a keyword-only arg
kw_names = [a.arg for a in args.kwonlyargs]
if "bypass_rules" not in kw_names:
return False, "missing 'bypass_rules' parameter"
return True, "ok"
# -- Skip logic ----------------------------------------------------------------
def _should_skip(path: Path) -> bool:
"""Return True if the file should be excluded from scanning."""
if path.name == "__init__.py":
return True
# Skip underscore-prefixed files (but not dunder)
if path.name.startswith("_") and not path.name.startswith("__"):
return True
for part in path.parts:
if part in _SKIP_DIRS:
return True
return False
# -- Core scan -----------------------------------------------------------------
def scan(pack_dir: Path) -> dict:
"""Scan all *_check.py files in *pack_dir* and verify interface compliance.
Args:
pack_dir: Path to the standards pack directory (e.g. handlers/aipass_standards/).
Returns:
Dict with keys: passed, total, pass_count, fail_count, results, issues, summary.
"""
results: list[dict] = []
issues: list[str] = []
if not pack_dir.is_dir():
msg = f"Pack directory not found: {pack_dir}"
logger.warning(msg)
return {
"passed": False,
"total": 0,
"pass_count": 0,
"fail_count": 0,
"results": results,
"issues": [msg],
"summary": msg,
}
check_files = sorted(pack_dir.glob("*_check.py"))
for check_file in check_files:
if _should_skip(check_file):
continue
name = check_file.stem # e.g. "cli_check"
standard_name = name.removesuffix("_check") # e.g. "cli"
entry: dict = {
"file": check_file.name,
"standard": standard_name,
"audit_scope": None,
"scope_valid": False,
"has_function": False,
"expected_function": None,
"params_ok": False,
"params_detail": "",
"compliant": False,
"issues": [],
}
# Parse the file
try:
source = check_file.read_text(encoding="utf-8")
tree = ast.parse(source, filename=str(check_file))
except SyntaxError as exc:
err = f"{check_file.name}: SyntaxError: {exc.msg} (line {exc.lineno})"
entry["issues"].append(err)
issues.append(err)
results.append(entry)
continue
# 1. Check AUDIT_SCOPE
scope = _extract_audit_scope(tree)
entry["audit_scope"] = scope
if scope is None:
err = f"{check_file.name}: AUDIT_SCOPE not defined"
entry["issues"].append(err)
issues.append(err)
elif scope not in VALID_SCOPES:
err = f"{check_file.name}: AUDIT_SCOPE='{scope}' not in {VALID_SCOPES}"
entry["issues"].append(err)
issues.append(err)
else:
entry["scope_valid"] = True
# 2. Determine expected function based on scope
if scope in ("all_files", "entry_point", None):
expected_func = "check_module"
expected_first_param = "module_path"
else: # branch_level
expected_func = "check_branch"
expected_first_param = "branch_path"
entry["expected_function"] = expected_func
# 3. Check function exists
func_node = _find_function(tree, expected_func)
if func_node is None:
err = f"{check_file.name}: missing {expected_func}()"
entry["has_function"] = False
entry["issues"].append(err)
issues.append(err)
else:
entry["has_function"] = True
# 4. Check parameters
ok, detail = _check_params(func_node, expected_first_param)
entry["params_ok"] = ok
entry["params_detail"] = detail
if not ok:
err = f"{check_file.name}: {expected_func}() params: {detail}"
entry["issues"].append(err)
issues.append(err)
# Final compliance
entry["compliant"] = (
entry["scope_valid"] and entry["has_function"] and entry["params_ok"]
)
results.append(entry)
pass_count = sum(1 for r in results if r["compliant"])
fail_count = len(results) - pass_count
total = len(results)
passed = fail_count == 0 and total > 0
if passed:
summary = f"All {total} checkers comply with the interface."
elif total == 0:
summary = "No *_check.py files found in pack directory."
else:
summary = f"{fail_count}/{total} checkers have interface issues."
logger.info(f"interface proof: {summary}")
json_handler.log_operation("proof_scan", {"proof": "interface", "passed": passed})
return {
"passed": passed,
"total": total,
"pass_count": pass_count,
"fail_count": fail_count,
"results": results,
"issues": issues,
"summary": summary,
}
@@ -0,0 +1,83 @@
# =================== AIPass ====================
# Name: interface_content.py
# Description: Queryable content for the interface proof
# Version: 1.0.0
# Created: 2026-03-22
# Modified: 2026-03-22
# =============================================
"""
Interface Proof Content Handler
Provides formatted interface proof content for the query system.
Module orchestrates, handler implements.
"""
from aipass.seedgo.apps.handlers.json import json_handler
def get_interface_proof() -> str:
"""Return interface proof content for query system.
Returns:
str: Formatted proof text with Rich styling
"""
sep = "\u2500" * 70
lines = [
f"[dim]{sep}[/dim]",
"[bold red]INTERFACE PROOF \u2014 Checker Interface Compliance[/bold red]",
"[dim]Every checker must declare AUDIT_SCOPE and use the correct function signature.[/dim]",
f"[dim]{sep}[/dim]",
"",
"[bold cyan]WHAT THIS CHECKS:[/bold cyan]",
" [yellow]1.[/yellow] AUDIT_SCOPE variable is declared at module level",
" [yellow]2.[/yellow] Function signature matches the expected pattern",
" [yellow]3.[/yellow] Parameters match the audit engine's calling convention",
"",
"[bold cyan]AUDIT_SCOPE VALUES:[/bold cyan]",
' [green]"all_files"[/green] \u2014 Checker receives every file in the branch',
' [green]"entry_point"[/green] \u2014 Checker receives only the main entry point',
' [green]"branch_level"[/green] \u2014 Checker receives the branch root path',
"",
"[bold cyan]FUNCTION SIGNATURES:[/bold cyan]",
' [yellow]all_files / entry_point:[/yellow] [dim]def check_module(file_path, module_name, branch_path)[/dim]',
' [yellow]branch_level:[/yellow] [dim]def check_branch(branch_path, module_name)[/dim]',
"",
"[bold cyan]WHY IT MATTERS:[/bold cyan]",
" Without AUDIT_SCOPE, the audit engine can't scope the checker.",
" It won't know whether to pass individual files or the branch root.",
" Without the correct function signature, the checker crashes at runtime.",
"",
f"[dim]{sep}[/dim]",
"[bold cyan]COMMON FAILURES:[/bold cyan]",
"",
"[yellow]Missing AUDIT_SCOPE:[/yellow]",
" [red]\u2717[/red] Checker has no AUDIT_SCOPE declaration at all",
" [green]\u2713[/green] [dim]AUDIT_SCOPE = \"all_files\"[/dim]",
"",
"[yellow]Wrong function name:[/yellow]",
" [red]\u2717[/red] [dim]def check(file_path, module_name, branch_path)[/dim]",
" [green]\u2713[/green] [dim]def check_module(file_path, module_name, branch_path)[/dim]",
"",
"[yellow]Wrong parameters:[/yellow]",
" [red]\u2717[/red] [dim]def check_module(file_path)[/dim] (missing module_name, branch_path)",
" [green]\u2713[/green] [dim]def check_module(file_path, module_name, branch_path)[/dim]",
"",
# TODO: Expand with actual validation logic details once the
# interface scanner is fully implemented.
"",
f"[dim]{sep}[/dim]",
"[bold cyan]HOW TO FIX:[/bold cyan]",
' [yellow]1.[/yellow] Add [dim]AUDIT_SCOPE = "all_files" | "entry_point" | "branch_level"[/dim]',
" [yellow]2.[/yellow] Ensure function matches expected interface:",
" [dim]check_module(file_path, module_name, branch_path)[/dim] for file-level",
" [dim]check_branch(branch_path, module_name)[/dim] for branch-level",
"",
"[bold cyan]RELATED:[/bold cyan]",
" [dim]DPLAN-0044: Self-audit tooling design[/dim]",
" [dim]tools/interface_scanner.py: Original prototype[/dim]",
f"[dim]{sep}[/dim]",
]
json_handler.log_operation("proof_content_queried", {"proof": "interface"})
return "\n".join(lines)
@@ -0,0 +1,139 @@
# Plugin Integrity Proof — Dynamic Discovery Contract
**Version:** 1.0.0
**Created:** 2026-03-22
**Modified:** 2026-03-22
**Status:** Placeholder
---
## Purpose
The plugin integrity proof verifies that core audit modules do not hardcode standard names. The seedgo audit system uses a plugin architecture — standards are discovered dynamically via `glob` + `importlib`. If core modules hardcode standard names, the "drop-in" contract breaks: adding a new standard by dropping a file into the pack no longer works without also editing core code.
---
## Modules Scanned
These are the core audit modules that must stay clean of hardcoded standard names:
| Module | Role |
|--------|------|
| `standards_audit.py` | Discovers and executes checkers via glob + importlib |
| `branch_audit.py` | Orchestrates branch-level audit runs |
| `audit_display.py` | Formats and displays audit results to the terminal |
| `seedgo.py` | Main entry point for the seedgo system |
---
## What Counts as Hardcoded
A standard name is "hardcoded" when it appears as a string literal inside a core module in a context that affects logic or routing. The scanner checks for known standard names (e.g., `"architecture"`, `"encapsulation"`, `"imports"`) appearing in these modules.
### The AMBIGUOUS_NAMES Filter
Some standard names are common English words that appear in non-standard contexts. The scanner maintains an `AMBIGUOUS_NAMES` set to filter false positives:
<!-- TODO: Document the full AMBIGUOUS_NAMES list once scanner is finalized -->
Examples of ambiguous names that may need filtering:
- `"meta"` — appears in Python metadata contexts
- `"naming"` — appears in general discussions of naming
- `"testing"` — appears in general testing references
- `"trigger"` — appears in event/action contexts
---
## Cosmetic vs Structural Hardcoding
Not all hardcoded references have the same severity:
### Structural (must fix)
Standard names used in logic that affects which standards run or how results are processed:
```python
# BAD — hardcoded routing
if standard_name == "architecture":
result = check_architecture(branch_path)
elif standard_name == "encapsulation":
result = check_encapsulation(branch_path)
```
```python
# GOOD — dynamic iteration
for checker in discovered_checkers:
result = checker.check_module(file_path, module_name, branch_path)
```
### Cosmetic (upgrade target)
Standard names in display strings, section headers, or help text:
```python
# Cosmetic — doesn't affect logic, but still couples display to specific standards
console.print("[bold]Architecture:[/bold]")
console.print(results["architecture"]["summary"])
```
**`audit_display.py`** has known cosmetic hardcoding. This is tracked as **DPLAN-0047** — the upgrade path is to iterate result dicts dynamically instead of hardcoding section order.
---
## Known Special Cases
### branch_audit.py — 3 Legitimate Cases
`branch_audit.py` has 3 standards that require special handling because they operate at a different scope than file-level checkers:
1. **architecture** — Template baseline check requires branch-level context
2. **meta** — Passport/trinity validation requires branch-level context
3. **testing** — Test discovery requires branch-level context
These are not bugs — they are architectural necessities documented in the checker interfaces. They use `AUDIT_SCOPE = "branch_level"` which requires distinct calling logic.
### audit_display.py — Cosmetic References
Display formatting references specific standard names for section headers and ordering. This is functional but not ideal. The upgrade path (DPLAN-0047) would replace hardcoded sections with dynamic iteration over whatever results the audit returns.
---
## What the Scan Checks
1. **Read** each core module's source code
2. **Extract** all string literals
3. **Compare** against the known standard names list
4. **Filter** through AMBIGUOUS_NAMES to remove false positives
5. **Classify** remaining matches as structural or cosmetic
6. **Report** violations with file, line number, and context
<!-- TODO: Document the exact regex/AST approach and threshold for
pass/fail once scanner is implemented -->
---
## How to Fix
1. **Structural hardcoding**: Replace with dynamic iteration over discovered checkers or result dicts
2. **If/elif chains**: Convert to loop over `glob('*_check.py')` results
3. **Dict key references**: Use `.items()` iteration instead of hardcoded key access
4. **Display sections**: Iterate result keys dynamically (DPLAN-0047 upgrade)
5. **Document special cases**: If a reference is truly necessary, document why in the code and in this proof
---
## The Drop-In Contract
The fundamental promise of the plugin architecture:
> To add a new standard to a pack, drop `{name}_check.py` into the pack directory. The audit engine discovers it automatically. No other files need to change.
Every hardcoded standard name in a core module is a violation of this contract. It means adding a standard requires editing core code, which defeats the purpose of plugin discovery.
---
## Related
- **DPLAN-0044**: Self-audit tooling design
- **DPLAN-0047**: audit_display.py cosmetic hardcoding upgrade
- **tools/plugin_integrity_scanner.py**: Original prototype
- **Checker**: `plugin_integrity.py` in this directory
@@ -0,0 +1,518 @@
# =================== AIPass ====================
# Name: plugin_integrity.py
# Description: Verify core audit modules don't hardcode standard names
# Version: 1.0.0
# Created: 2026-03-22
# Modified: 2026-03-22
# =============================================
"""Plugin Integrity Proof -- Verify plugin architecture stays clean.
The plugin architecture depends on dynamic discovery (glob + importlib).
Core audit modules should never reference specific standard names in code
logic. This handler scans target modules with AST + regex and reports
any hardcoded standard-name references.
Interface:
scan(pack_dir: Path) -> dict
Returns: {"passed": bool, "issues": list, "summary": str, ...}
Note: audit_display.py cosmetic refs are a known upgrade target (DPLAN-0047).
Reference: tools/plugin_integrity_scanner.py (original prototype)
"""
from __future__ import annotations
import ast
import re
from pathlib import Path
from aipass.prax import logger
from aipass.seedgo.apps.handlers.json import json_handler
# =============================================================================
# CONFIGURATION
# =============================================================================
# Modules whose hardcoded refs are cosmetic (display layer, not routing).
COSMETIC_MODULES: set[str] = {"audit_display.py"}
# Standard names that double as common infrastructure/English words.
# These appear as directory names, import paths, variable names, Rich markup
# labels, etc. -- not as hardcoded standard routing. Never flag bare AST
# string literals for these; only the regex patterns catch truly suspicious use.
AMBIGUOUS_NAMES: set[str] = {
"architecture",
"cli",
"cli_flags",
"documentation",
"encapsulation",
"error_handling",
"handlers",
"imports",
"introspection",
"meta",
"modules",
"naming",
"readme",
"testing",
"trigger",
"shebang",
"permission_flags",
}
# =============================================================================
# TARGET MODULE RESOLUTION
# =============================================================================
def _resolve_target_modules(
pack_dir: Path,
) -> list[dict[str, Path | str | bool]]:
"""Build the list of core modules to scan, resolved from pack_dir.
Path layout (from pack_dir = .../apps/handlers/aipass_standards/):
seedgo_root = pack_dir.parent.parent.parent (.../seedgo/)
modules_dir = seedgo_root / "apps" / "modules"
audit_dir = pack_dir.parent / "audit" (sibling handler dir)
entry_point = seedgo_root / "apps" / "seedgo.py"
"""
seedgo_root = pack_dir.parent.parent.parent
modules_dir = seedgo_root / "apps" / "modules"
audit_dir = pack_dir.parent / "audit"
entry_point = seedgo_root / "apps" / "seedgo.py"
return [
{
"path": modules_dir / "standards_audit.py",
"label": "standards_audit.py",
"cosmetic": False,
},
{
"path": audit_dir / "branch_audit.py",
"label": "branch_audit.py",
"cosmetic": False,
},
{
"path": audit_dir / "audit_display.py",
"label": "audit_display.py",
"cosmetic": True,
},
{
"path": modules_dir / "standards_query.py",
"label": "standards_query.py",
"cosmetic": False,
},
{
"path": entry_point,
"label": "seedgo.py",
"cosmetic": False,
},
]
# =============================================================================
# STANDARD NAME DISCOVERY
# =============================================================================
def _discover_standard_names(pack_dir: Path) -> list[str]:
"""Discover standard names from *_check.py files in the pack directory.
Returns:
Sorted list of standard names (e.g. ["architecture", "cli", ...]).
"""
if not pack_dir.is_dir():
return []
names: list[str] = []
for check_file in sorted(pack_dir.glob("*_check.py")):
name = check_file.stem.removesuffix("_check")
names.append(name)
return names
# =============================================================================
# AST HELPERS
# =============================================================================
def _enclosing_context(node: ast.AST, parents: dict[int, ast.AST]) -> str:
"""Walk up the parent chain to find the enclosing function/class name."""
parts: list[str] = []
current = node
while id(current) in parents:
current = parents[id(current)]
if isinstance(current, ast.FunctionDef | ast.AsyncFunctionDef):
parts.append(f"def {current.name}()")
elif isinstance(current, ast.ClassDef):
parts.append(f"class {current.name}")
if parts:
return " > ".join(reversed(parts))
return "<module level>"
def _is_docstring(node: ast.Constant, tree: ast.Module) -> bool:
"""Check if a string constant is a docstring."""
for parent in ast.walk(tree):
body = getattr(parent, "body", None)
if not isinstance(body, list) or not body:
continue
first = body[0]
if (
isinstance(first, ast.Expr)
and isinstance(first.value, ast.Constant)
and first.value is node
):
return True
return False
def _is_display_string(
node: ast.Constant,
parents: dict[int, ast.AST],
) -> bool:
"""Check if a string constant is used in a display/print context.
Strings passed to console.print(), header(), logger.*, error(), warning()
or used inside f-strings are display text, not hardcoded routing.
"""
parent = parents.get(id(node))
if parent is None:
return False
# Inside an f-string -> display formatting
if isinstance(parent, ast.JoinedStr):
return True
# Direct argument to a display/logging call
if isinstance(parent, ast.Call):
func = parent.func
if isinstance(func, ast.Attribute) and func.attr in (
"print", "info", "error", "warning", "debug",
):
return True
if isinstance(func, ast.Name) and func.id in (
"header", "error", "warning",
):
return True
# Keyword arg inside a display call
grandparent = parents.get(id(parent))
if isinstance(grandparent, ast.Call):
func = grandparent.func
if isinstance(func, ast.Attribute) and func.attr in (
"print", "log_operation",
):
return True
if isinstance(func, ast.Name) and func.id in (
"header", "error", "warning",
):
return True
return False
def _is_dict_key_access(
node: ast.Constant,
parents: dict[int, ast.AST],
) -> bool:
"""Check if a string is a dict key inside generic data-structure access.
Patterns like result.get('key') or result['key'] when iterating over
dynamic data are not hardcoded routing.
"""
parent = parents.get(id(node))
if parent is None:
return False
# .get('key', default)
if isinstance(parent, ast.Call):
func = parent.func
if isinstance(func, ast.Attribute) and func.attr == "get":
return True
# ['key'] subscript
if isinstance(parent, ast.Subscript):
return True
return False
# =============================================================================
# AST SCANNER
# =============================================================================
def _scan_file_ast(
file_path: Path,
standard_names: list[str],
) -> list[dict[str, str | int]]:
"""Parse a file with AST and find string literals matching standard names.
Filters out docstrings, display strings, dict-key access, and
ambiguous names. Only flags string literals that look like hardcoded
routing.
"""
source = file_path.read_text(encoding="utf-8")
try:
tree = ast.parse(source, filename=str(file_path))
except SyntaxError:
return []
# Build child -> parent map
parents: dict[int, ast.AST] = {}
for parent in ast.walk(tree):
for child in ast.iter_child_nodes(parent):
parents[id(child)] = parent
name_set = set(standard_names)
findings: list[dict[str, str | int]] = []
for node in ast.walk(tree):
if not isinstance(node, ast.Constant):
continue
if not isinstance(node.value, str):
continue
value = node.value
if _is_docstring(node, tree):
continue
if _is_display_string(node, parents):
continue
if _is_dict_key_access(node, parents):
continue
if value not in name_set:
continue
if value in AMBIGUOUS_NAMES:
continue
context = _enclosing_context(node, parents)
findings.append({
"line": getattr(node, "lineno", 0),
"name": value,
"context": context,
"value": value,
"kind": "ast_string_literal",
})
return findings
# =============================================================================
# REGEX SCANNER
# =============================================================================
def _scan_file_regex(
file_path: Path,
standard_names: list[str],
) -> list[dict[str, str | int]]:
"""Regex scan for standard names used as hardcoded identifiers.
Patterns detected:
- check_<standard>( -- direct checker function call
- <standard>_violations -- hardcoded violation key construction
- == '<standard>' -- hardcoded branching
"""
source = file_path.read_text(encoding="utf-8")
lines = source.splitlines()
findings: list[dict[str, str | int]] = []
in_docstring = False
docstring_delim: str | None = None
for lineno, line in enumerate(lines, start=1):
stripped = line.strip()
# Track triple-quoted docstrings (simple heuristic)
for delim in ('"""', "'''"):
count = stripped.count(delim)
if in_docstring:
if delim == docstring_delim and count >= 1:
in_docstring = False
docstring_delim = None
continue
if count == 1:
in_docstring = True
docstring_delim = delim
break
if in_docstring:
continue
# Skip pure comment lines
if stripped.startswith("#"):
continue
# Extract code portion (before inline comment)
code_part = line.split("#")[0] if "#" in line else line
for sn in standard_names:
# Pattern 1: check_<standard>( -- direct checker call
fn_pat = rf"\bcheck_{re.escape(sn)}\s*\("
if re.search(fn_pat, code_part):
findings.append({
"line": lineno,
"name": sn,
"context": stripped[:80],
"value": stripped[:80],
"kind": "hardcoded_function_call",
})
# Pattern 2: <standard>_violations -- hardcoded violation key
viol_pat = rf"\b{re.escape(sn)}_violations\b"
if re.search(viol_pat, code_part):
findings.append({
"line": lineno,
"name": sn,
"context": stripped[:80],
"value": stripped[:80],
"kind": "hardcoded_violation_key",
})
# Pattern 3: == '<standard>' -- hardcoded branching
branch_pat = rf"""==\s*['"]{re.escape(sn)}['"]"""
if re.search(branch_pat, code_part):
findings.append({
"line": lineno,
"name": sn,
"context": stripped[:80],
"value": stripped[:80],
"kind": "hardcoded_branch",
})
return findings
# =============================================================================
# PUBLIC SCAN INTERFACE
# =============================================================================
def scan(pack_dir: Path) -> dict:
"""Run the full plugin integrity scan.
Args:
pack_dir: Path to the standards pack directory
(e.g. handlers/aipass_standards/).
Returns:
Dict with keys:
passed: True if flagged_count == 0 (cosmetic modules don't fail)
standard_names: list of discovered standard names
modules: list of per-module result dicts
clean_count: number of clean modules
flagged_count: modules with unexpected hardcoded references
cosmetic_count: modules with only cosmetic references
missing_count: modules whose file was not found
issues: list of human-readable issue strings
summary: one-line summary string
"""
logger.info("plugin_integrity: scanning pack_dir=%s", pack_dir)
standard_names = _discover_standard_names(pack_dir)
target_modules = _resolve_target_modules(pack_dir)
module_results: list[dict] = []
issues: list[str] = []
for module_info in target_modules:
file_path = Path(str(module_info["path"]))
label = str(module_info["label"])
is_cosmetic = bool(module_info.get("cosmetic", False)) or label in COSMETIC_MODULES
result: dict = {
"label": label,
"path": str(file_path),
"exists": file_path.is_file(),
"cosmetic_module": is_cosmetic,
"findings": [],
"status": "clean",
}
if not file_path.is_file():
result["status"] = "missing"
issues.append(f"{label}: file not found at {file_path}")
module_results.append(result)
continue
# Run both AST and regex scans
ast_findings = _scan_file_ast(file_path, standard_names)
regex_findings = _scan_file_regex(file_path, standard_names)
# Deduplicate by (line, name, kind) -- AST findings take priority
seen: set[tuple[int, str, str]] = set()
combined: list[dict] = []
for finding in ast_findings:
key = (int(finding["line"]), str(finding["name"]), str(finding["kind"]))
if key not in seen:
seen.add(key)
combined.append(finding)
for finding in regex_findings:
key = (int(finding["line"]), str(finding["name"]), str(finding["kind"]))
if key not in seen:
seen.add(key)
combined.append(finding)
# Sort by line number
combined.sort(key=lambda x: int(x["line"]))
result["findings"] = combined
if combined:
if is_cosmetic:
result["status"] = "cosmetic"
else:
result["status"] = "flagged"
# Build issue strings for flagged (non-cosmetic) modules
for finding in combined:
kind_label = {
"ast_string_literal": "string literal",
"hardcoded_function_call": "function call",
"hardcoded_violation_key": "violation key",
"hardcoded_branch": "branch condition",
}.get(str(finding["kind"]), str(finding["kind"]))
issues.append(
f"{label} L{finding['line']}: hardcoded {kind_label} "
f"referencing standard '{finding['name']}'"
)
else:
result["status"] = "clean"
module_results.append(result)
clean_count = sum(1 for m in module_results if m["status"] == "clean")
flagged_count = sum(1 for m in module_results if m["status"] == "flagged")
cosmetic_count = sum(1 for m in module_results if m["status"] == "cosmetic")
missing_count = sum(1 for m in module_results if m["status"] == "missing")
passed = flagged_count == 0
# Build summary
total = len(module_results)
parts: list[str] = [f"{clean_count}/{total} clean"]
if cosmetic_count:
parts.append(f"{cosmetic_count} cosmetic")
if flagged_count:
parts.append(f"{flagged_count} FLAGGED")
if missing_count:
parts.append(f"{missing_count} missing")
if passed:
summary = f"Plugin integrity clean: {', '.join(parts)}"
else:
summary = f"Plugin integrity FAILED: {', '.join(parts)}"
logger.info("plugin_integrity: passed=%s, summary=%s", passed, summary)
json_handler.log_operation("proof_scan", {"proof": "plugin_integrity", "passed": passed})
return {
"passed": passed,
"standard_names": standard_names,
"modules": module_results,
"clean_count": clean_count,
"flagged_count": flagged_count,
"cosmetic_count": cosmetic_count,
"missing_count": missing_count,
"issues": issues,
"summary": summary,
}
@@ -0,0 +1,81 @@
# =================== AIPass ====================
# Name: plugin_integrity_content.py
# Description: Queryable content for the plugin integrity proof
# Version: 1.0.0
# Created: 2026-03-22
# Modified: 2026-03-22
# =============================================
"""
Plugin Integrity Proof Content Handler
Provides formatted plugin integrity proof content for the query system.
Module orchestrates, handler implements.
"""
from aipass.seedgo.apps.handlers.json import json_handler
def get_plugin_integrity_proof() -> str:
"""Return plugin integrity proof content for query system.
Returns:
str: Formatted proof text with Rich styling
"""
sep = "\u2500" * 70
lines = [
f"[dim]{sep}[/dim]",
"[bold red]PLUGIN INTEGRITY PROOF \u2014 Dynamic Discovery Contract[/bold red]",
"[dim]Core audit modules must not hardcode standard names.[/dim]",
f"[dim]{sep}[/dim]",
"",
"[bold cyan]WHAT THIS CHECKS:[/bold cyan]",
" Core audit modules must use dynamic discovery (glob + importlib),",
" not hardcoded standard names. The plugin architecture depends on it.",
"",
"[bold cyan]MODULES SCANNED:[/bold cyan]",
" [yellow]\u2022[/yellow] [dim]standards_audit.py[/dim] \u2014 Runs checker discovery and execution",
" [yellow]\u2022[/yellow] [dim]branch_audit.py[/dim] \u2014 Orchestrates branch-level audits",
" [yellow]\u2022[/yellow] [dim]audit_display.py[/dim] \u2014 Formats and displays audit results",
" [yellow]\u2022[/yellow] [dim]seedgo.py[/dim] \u2014 Main entry point for seedgo",
"",
"[bold cyan]WHY IT MATTERS:[/bold cyan]",
" The plugin architecture depends on dynamic discovery. When a new standard",
" is added (drop a [dim]*_check.py[/dim] file in the pack), it should be automatically",
" picked up by glob + importlib. Hardcoded names break this contract:",
" [red]\u2717[/red] New standard added but not in the hardcoded list \u2192 silently skipped",
" [red]\u2717[/red] Standard renamed but hardcoded ref not updated \u2192 crash or stale results",
"",
f"[dim]{sep}[/dim]",
"[bold cyan]WHAT COUNTS AS HARDCODED:[/bold cyan]",
" Any string literal matching a known standard name inside the core modules.",
" The scanner uses an AMBIGUOUS_NAMES filter to exclude false positives",
' (e.g. "meta", "naming" may appear in non-standard contexts).',
"",
"[bold cyan]COSMETIC vs STRUCTURAL:[/bold cyan]",
" [yellow]Structural:[/yellow] Standard name used in logic (if/elif chains, dict keys for routing)",
" [yellow]Cosmetic:[/yellow] Standard name in display strings, help text, comments",
"",
" [dim]audit_display.py[/dim] cosmetic refs are a known upgrade target ([dim]DPLAN-0047[/dim]).",
" [dim]branch_audit.py[/dim] has 3 legitimate special cases (architecture, meta, testing)",
" that require branch-level handling distinct from file-level standards.",
"",
# TODO: Expand with AMBIGUOUS_NAMES list, detection regex patterns,
# and full accounting of known special cases once scanner is built.
"",
f"[dim]{sep}[/dim]",
"[bold cyan]HOW TO FIX:[/bold cyan]",
" [yellow]1.[/yellow] Replace hardcoded standard names with dynamic iteration over results",
" [yellow]2.[/yellow] Use [dim]glob('*_check.py')[/dim] + [dim]importlib.import_module()[/dim] pattern",
" [yellow]3.[/yellow] For display: iterate result dicts instead of hardcoding section order",
" [yellow]4.[/yellow] For special cases: document why they exist, track in DPLAN-0047",
"",
"[bold cyan]RELATED:[/bold cyan]",
" [dim]DPLAN-0044: Self-audit tooling design[/dim]",
" [dim]DPLAN-0047: audit_display.py cosmetic hardcoding upgrade[/dim]",
" [dim]tools/plugin_integrity_scanner.py: Original prototype[/dim]",
f"[dim]{sep}[/dim]",
]
json_handler.log_operation("proof_content_queried", {"proof": "plugin_integrity"})
return "\n".join(lines)
@@ -0,0 +1,93 @@
# README Currency Proof — Documentation Accuracy
**Version:** 1.0.0
**Created:** 2026-03-22
**Modified:** 2026-03-22
**Status:** Placeholder
---
## Purpose
The README currency proof verifies that a pack's `README.md` accurately reflects the actual state of the pack. README files are the first thing branches and humans read to understand pack coverage. Stale READMEs erode trust and cause confusion.
---
## What Gets Checked
### 1. Checker Count Accuracy
The README typically states how many standards the pack contains (e.g., "24 active standards"). This proof compares that stated count against the actual number of `*_check.py` files in the pack.
```
README says: "24 active standards"
Actual count: glob("*_check.py") -> 26 files
Result: FAIL — count mismatch (off by 2)
```
### 2. Undocumented Standards
Every `*_check.py` in the pack should have a corresponding mention in the README. A standard that exists in the pack but is never mentioned in the README is "undocumented."
```
Pack contains: stderr_routing_check.py
README mentions: (no reference to stderr_routing)
Result: FAIL — undocumented standard
```
### 3. Stale References
The README should not reference standards that no longer exist in the pack. If a standard was archived or removed, its README entry must also be removed.
```
README mentions: diagnostics_check.py
Pack contains: (no such file — moved to .archive/)
Result: FAIL — stale reference
```
---
## How Detection Works
<!-- TODO: Document the exact detection algorithm once scanner is built -->
1. **Count extraction**: Parse the README for numeric patterns near keywords like "standards", "checkers", "active"
2. **Standard enumeration**: Glob `*_check.py` in the pack directory, extract standard names
3. **Cross-reference**: For each standard name, search the README text for a mention
4. **Stale detection**: For each standard name found in the README, verify the corresponding `*_check.py` exists
---
## What "Undocumented" Means
A standard is undocumented if:
- Its `*_check.py` file exists in the pack directory
- The README.md contains no reference to that standard's name
- It is not listed in any standards table or section of the README
This does not require a full section per standard — a mention in a list or table is sufficient. But total absence means the README is incomplete.
---
## Common Failures
| Failure | Cause | Fix |
|---------|-------|-----|
| Count mismatch | Standards added/removed without updating README count | Update the count |
| Undocumented standard | New checker added, README not updated | Add entry to README |
| Stale reference | Standard archived/removed, README not updated | Remove the entry |
---
## How to Fix
1. **Count**: Run `ls *_check.py | wc -l` in the pack directory, update the README count
2. **Undocumented**: Add a line or section for each missing standard
3. **Stale**: Remove or mark as archived any references to non-existent standards
---
## Related
- **DPLAN-0044**: Self-audit tooling design
- **tools/readme_currency_scanner.py**: Original prototype
- **Checker**: `readme_currency.py` in this directory
@@ -0,0 +1,237 @@
# =================== AIPass ====================
# Name: readme_currency.py
# Description: Verify README.md accuracy against actual pack state
# Version: 1.0.0
# Created: 2026-03-22
# Modified: 2026-03-22
# =============================================
"""README Currency Proof -- Compare README checker counts and standard lists against actual pack.
Checks that the seedgo branch README.md accurately reflects the current state
of the standards pack: correct checker count, all standards documented, no stale
references to removed standards.
Interface:
scan(pack_dir: Path) -> dict
Returns: {"passed": bool, "readme_found": bool, "actual_check_count": int,
"readme_counts": list, "undocumented": list, "stale_refs": list,
"issues": list, "summary": str}
Reference: tools/readme_currency_scanner.py (original prototype)
"""
from __future__ import annotations
import re
from pathlib import Path
from aipass.prax import logger
from aipass.seedgo.apps.handlers.json import json_handler
# -- Helpers -------------------------------------------------------------------
def _standard_name_from_file(filename: str) -> str:
"""Extract the standard name from a *_check.py filename.
Example: 'cli_flags_check.py' -> 'cli_flags'
"""
return filename.removesuffix("_check.py")
def _normalize_name(name: str) -> str:
"""Normalize a standard name for flexible matching.
Lowercases, replaces spaces/hyphens with underscores, strips
surrounding whitespace, and collapses repeated underscores.
"""
name = name.lower().strip()
name = re.sub(r"[\s\-]+", "_", name)
name = re.sub(r"_+", "_", name)
return name
def _extract_readme_count_references(
readme_text: str,
) -> list[dict[str, str | int]]:
"""Find lines that reference a number followed by checker/standard/etc.
Returns a list of dicts with 'line', 'number', and 'context' keys.
"""
results: list[dict[str, str | int]] = []
pattern = re.compile(
r"\b(\d+)\s+"
r"(checker|checkers|standard|standards|check|checks)",
re.IGNORECASE,
)
for lineno, line in enumerate(readme_text.splitlines(), start=1):
for match in pattern.finditer(line):
results.append(
{
"line": lineno,
"number": int(match.group(1)),
"context": line.strip(),
}
)
return results
def _extract_readme_standard_names(readme_text: str) -> set[str]:
"""Extract standard names mentioned in the README.
Looks for the 'Checker Packs' section listing. Handles comma-separated
lists like 'architecture, CLI, CLI flags, ... and diagnostics patterns'.
"""
names: set[str] = set()
# Parse the comma/and-separated list in the Checker Packs section.
checks_pattern = re.compile(
r"pack\s+checks?:\s*(.+?)(?:\.|$)", re.IGNORECASE | re.DOTALL
)
match = checks_pattern.search(readme_text)
if match:
raw_list = match.group(1)
# Split on commas and ", and "
raw_list = re.sub(r",?\s+and\s+", ",", raw_list)
for item in raw_list.split(","):
item = item.strip().rstrip(".")
if item:
names.add(_normalize_name(item))
return names
# -- Core scan -----------------------------------------------------------------
def scan(pack_dir: Path) -> dict:
"""Compare README.md checker counts and standard lists against actual pack state.
Args:
pack_dir: Path to the standards pack directory (e.g. handlers/aipass_standards/).
The seedgo README.md is located at pack_dir.parent.parent.parent / "README.md".
Returns:
Dict with keys: passed, readme_found, actual_check_count, readme_counts,
undocumented, stale_refs, issues, summary.
"""
issues: list[str] = []
# -- Gather actual standards from pack_dir --
if not pack_dir.is_dir():
msg = f"Pack directory not found: {pack_dir}"
logger.warning(msg)
return {
"passed": False,
"readme_found": False,
"actual_check_count": 0,
"readme_counts": [],
"undocumented": [],
"stale_refs": [],
"issues": [msg],
"summary": msg,
}
check_files = sorted(pack_dir.glob("*_check.py"))
actual_names: set[str] = {_standard_name_from_file(f.name) for f in check_files}
actual_check_count = len(check_files)
# -- Locate README --
# pack_dir is e.g. .../seedgo/apps/handlers/aipass_standards/
# seedgo root = pack_dir.parent.parent.parent
seedgo_root = pack_dir.parent.parent.parent
readme_path = seedgo_root / "README.md"
if not readme_path.is_file():
msg = f"README not found: {readme_path}"
issues.append(msg)
return {
"passed": False,
"readme_found": False,
"actual_check_count": actual_check_count,
"readme_counts": [],
"undocumented": sorted(actual_names),
"stale_refs": [],
"issues": issues,
"summary": msg,
}
readme_text = readme_path.read_text(encoding="utf-8")
# -- Count references --
count_refs = _extract_readme_count_references(readme_text)
count_mismatch = any(ref["number"] != actual_check_count for ref in count_refs)
if count_mismatch:
for ref in count_refs:
if ref["number"] != actual_check_count:
issues.append(
f"Line {ref['line']}: README says {ref['number']} but "
f"actual count is {actual_check_count}"
)
# -- Name references --
readme_names = _extract_readme_standard_names(readme_text)
# Build lookup tables for normalized matching
actual_lookup: dict[str, str] = {_normalize_name(n): n for n in actual_names}
readme_lookup: dict[str, str] = {_normalize_name(n): n for n in readme_names}
actual_norm = set(actual_lookup.keys())
readme_norm = set(readme_lookup.keys())
# Flexible matching: a README name matches if it is a substring of or
# equal to any actual normalized name, and vice-versa.
matched_actual: set[str] = set()
matched_readme: set[str] = set()
for rn in readme_norm:
for an in actual_norm:
if rn == an or rn in an or an in rn:
matched_actual.add(an)
matched_readme.add(rn)
stale_norm = readme_norm - matched_readme
missing_norm = actual_norm - matched_actual
stale_refs = sorted(readme_lookup[n] for n in stale_norm)
undocumented = sorted(actual_lookup[n] for n in missing_norm)
if stale_refs:
issues.append(f"Stale references in README: {', '.join(stale_refs)}")
if undocumented:
issues.append(f"Undocumented standards: {', '.join(undocumented)}")
passed = not count_mismatch and not stale_refs and not undocumented
# -- Summary --
if passed:
summary = (
f"README is current. {actual_check_count} checkers, "
f"all documented, no stale references."
)
else:
parts: list[str] = []
if count_mismatch:
parts.append("count mismatch")
if stale_refs:
parts.append(f"{len(stale_refs)} stale reference(s)")
if undocumented:
parts.append(f"{len(undocumented)} undocumented standard(s)")
summary = f"README is stale: {', '.join(parts)}."
logger.info(f"readme_currency proof: {summary}")
json_handler.log_operation("proof_scan", {"proof": "readme_currency", "passed": passed})
return {
"passed": passed,
"readme_found": True,
"actual_check_count": actual_check_count,
"readme_counts": count_refs,
"undocumented": undocumented,
"stale_refs": stale_refs,
"issues": issues,
"summary": summary,
}
@@ -0,0 +1,73 @@
# =================== AIPass ====================
# Name: readme_currency_content.py
# Description: Queryable content for the README currency proof
# Version: 1.0.0
# Created: 2026-03-22
# Modified: 2026-03-22
# =============================================
"""
README Currency Proof Content Handler
Provides formatted README currency proof content for the query system.
Module orchestrates, handler implements.
"""
from aipass.seedgo.apps.handlers.json import json_handler
def get_readme_currency_proof() -> str:
"""Return README currency proof content for query system.
Returns:
str: Formatted proof text with Rich styling
"""
sep = "\u2500" * 70
lines = [
f"[dim]{sep}[/dim]",
"[bold red]README CURRENCY PROOF \u2014 Documentation Accuracy[/bold red]",
"[dim]README.md must reflect actual pack state at all times.[/dim]",
f"[dim]{sep}[/dim]",
"",
"[bold cyan]WHAT THIS CHECKS:[/bold cyan]",
" [yellow]1.[/yellow] Checker count in README matches actual [dim]*_check.py[/dim] file count",
" [yellow]2.[/yellow] All standards in the pack are documented in the README",
" [yellow]3.[/yellow] No stale references to removed or renamed standards",
"",
"[bold cyan]WHY IT MATTERS:[/bold cyan]",
" A stale README misleads everyone about what the pack actually contains.",
" Branches query README to understand pack coverage. Humans read it to",
" orient themselves. Wrong counts and missing standards erode trust.",
"",
f"[dim]{sep}[/dim]",
"[bold cyan]COMMON FAILURES:[/bold cyan]",
"",
"[yellow]Wrong checker count:[/yellow]",
' [red]\u2717[/red] README says "24 standards" but pack has 26 [dim]*_check.py[/dim] files',
" [green]\u2713[/green] Count matches actual glob of [dim]*_check.py[/dim]",
"",
"[yellow]Undocumented standards:[/yellow]",
" [red]\u2717[/red] [dim]stderr_routing_check.py[/dim] exists but README never mentions it",
" [green]\u2713[/green] Every checker has a corresponding entry in the README",
"",
"[yellow]Stale references:[/yellow]",
" [red]\u2717[/red] README mentions [dim]diagnostics_check.py[/dim] which was archived",
" [green]\u2713[/green] All referenced standards actually exist in the pack",
"",
# TODO: Expand with detection algorithm details, regex patterns
# for count extraction, and stale reference identification logic.
"",
f"[dim]{sep}[/dim]",
"[bold cyan]HOW TO FIX:[/bold cyan]",
" [yellow]1.[/yellow] Update the checker count to match [dim]len(glob('*_check.py'))[/dim]",
" [yellow]2.[/yellow] Add entries for all undocumented standards",
" [yellow]3.[/yellow] Remove references to standards that no longer exist",
"",
"[bold cyan]RELATED:[/bold cyan]",
" [dim]DPLAN-0044: Self-audit tooling design[/dim]",
" [dim]tools/readme_currency_scanner.py: Original prototype[/dim]",
f"[dim]{sep}[/dim]",
]
json_handler.log_operation("proof_content_queried", {"proof": "readme_currency"})
return "\n".join(lines)
@@ -0,0 +1,94 @@
# Triplet Proof — Standard File Completeness
**Version:** 1.0.0
**Created:** 2026-03-22
**Modified:** 2026-03-22
**Status:** Placeholder
---
## Purpose
The triplet proof verifies that every standard in a pack exists as a complete set of 3 files. A standard is only fully realized when all three components are present — the runtime checker, the queryable content, and the human-readable documentation.
---
## The 3-File Convention
Every standard `{name}` in a pack must have:
| File | Role | Consumer |
|------|------|----------|
| `{name}_check.py` | Runtime checker — validates branches against the standard | Audit engine |
| `{name}_content.py` | Queryable content — returns formatted text for the query system | standards_query module |
| `{name}.md` | Full documentation — complete specification for humans | Developers, branch managers |
---
## What the Scan Checks
1. **Glob all `*_check.py` files** in the pack directory to establish the standard names
2. **For each standard name**, verify that `{name}_content.py` and `{name}.md` also exist
3. **Detect orphans** — content or md files that exist without a matching checker
4. **Report completeness** — count of complete triplets vs incomplete standards
---
## Examples
### Complete Triplet (passing)
```
architecture_check.py -- checker
architecture_content.py -- queryable content
architecture.md -- documentation
```
All three files present. The standard is fully realized.
### Incomplete Standard (failing)
```
naming_check.py -- checker exists
-- naming_content.py MISSING
-- naming.md MISSING
```
The checker runs, but branches cannot query the standard and humans have no documentation.
### Orphaned Content (failing)
```
-- bypass_check.py MISSING
bypass_content.py -- content exists with no checker
```
Content exists but there is no checker to enforce it.
---
## How to Fix Failures
1. **Missing content file**: Create `{name}_content.py` with a `get_{name}_standards()` function that returns formatted Rich text
2. **Missing md file**: Create `{name}.md` with full documentation (purpose, rules, examples, how to fix violations)
3. **Orphaned content/md**: Either create the missing checker or remove the orphan if the standard was intentionally dropped
Use existing complete triplets as templates. The `architecture` standard is a reliable reference.
---
## Scan Interface
```python
scan(pack_dir: Path) -> dict
# Returns: {"passed": bool, "issues": list, "summary": str, ...}
```
<!-- TODO: Document return dict shape in detail once scanner is implemented -->
---
## Related
- **DPLAN-0044**: Self-audit tooling design
- **tools/triplet_scanner.py**: Original prototype that this proof is based on
- **Checker**: `triplet.py` in this directory
@@ -0,0 +1,165 @@
# =================== AIPass ====================
# Name: triplet.py
# Description: Verify standard triplet completeness (check + content + md)
# Version: 1.0.0
# Created: 2026-03-22
# Modified: 2026-03-22
# =============================================
"""Triplet Proof -- Verify every standard has all 3 files: *_check.py, *_content.py, *.md
Interface:
scan(pack_dir: Path) -> dict
Returns: {"passed": bool, "total": int, "complete": list, "check_only": list,
"missing_check": list, "other_incomplete": list, "orphaned": list,
"issues": list, "summary": str}
Reference: tools/triplet_scanner.py (original prototype)
"""
from __future__ import annotations
from pathlib import Path
from aipass.prax import logger
from aipass.seedgo.apps.handlers.json import json_handler
# Directories to skip entirely
_SKIP_DIRS = {".archive", ".sorting_unprocessed", "__pycache__"}
def _top_level_files(pack_dir: Path) -> list[Path]:
"""Return all regular files directly in *pack_dir*, skipping hidden/skip dirs."""
if not pack_dir.is_dir():
return []
return [
p
for p in pack_dir.iterdir()
if p.is_file()
and p.name not in _SKIP_DIRS
and not p.name.startswith("_")
]
def scan(pack_dir: Path) -> dict:
"""Run triplet completeness scan on a standards pack directory.
Every standard should have three files: {name}_check.py, {name}_content.py, {name}.md.
Files that don't fit any triplet pattern are flagged as orphaned.
Args:
pack_dir: Path to the standards pack directory (e.g. handlers/aipass_standards/).
Returns:
Dict with keys: passed, total, complete, check_only, missing_check,
other_incomplete, orphaned, issues, summary.
"""
logger.info(f"[triplet] Scanning {pack_dir}")
checks: set[str] = set()
contents: set[str] = set()
docs: set[str] = set()
orphaned_files: list[str] = []
for filepath in _top_level_files(pack_dir):
name = filepath.name
if name.endswith("_check.py"):
checks.add(name.removesuffix("_check.py"))
elif name.endswith("_content.py"):
contents.add(name.removesuffix("_content.py"))
elif name.endswith(".md"):
# Only lowercase-starting .md files participate in triplet matching.
# Uppercase .md (e.g. README.md, SOP docs) are ancillary -- not orphaned.
stem = filepath.stem
if stem[0:1].islower():
docs.add(stem)
elif name.endswith(".py"):
# .py files that are neither _check nor _content -- orphaned
orphaned_files.append(name)
elif name.endswith(".json"):
# Config / ancillary JSON files -- not orphaned
pass
else:
orphaned_files.append(name)
# Union of all discovered standard names
all_names = sorted(checks | contents | docs)
complete: list[str] = []
check_only: list[str] = []
missing_check: list[str] = []
other_incomplete: list[str] = []
issues: list[dict[str, str | bool]] = []
for std_name in all_names:
has_check = std_name in checks
has_content = std_name in contents
has_md = std_name in docs
entry = {
"name": std_name,
"has_check": has_check,
"has_content": has_content,
"has_md": has_md,
}
if has_check and has_content and has_md:
complete.append(std_name)
elif has_check and not has_content and not has_md:
check_only.append(std_name)
issues.append({**entry, "issue": "check only -- missing content + md"})
elif not has_check:
missing_check.append(std_name)
missing = []
if not has_check:
missing.append("check")
if not has_content:
missing.append("content")
if not has_md:
missing.append("md")
issues.append({**entry, "issue": f"missing: {', '.join(missing)}"})
else:
other_incomplete.append(std_name)
missing = []
if not has_content:
missing.append("content")
if not has_md:
missing.append("md")
issues.append({**entry, "issue": f"missing: {', '.join(missing)}"})
passed = len(issues) == 0 and len(orphaned_files) == 0
# Build human-readable summary
parts = [f"{len(complete)} complete"]
if check_only:
parts.append(f"{len(check_only)} check-only")
if missing_check:
parts.append(f"{len(missing_check)} missing-check")
if other_incomplete:
parts.append(f"{len(other_incomplete)} other-incomplete")
if orphaned_files:
parts.append(f"{len(orphaned_files)} orphaned")
parts.append(f"{len(all_names)} total")
summary = " | ".join(parts)
logger.info(f"[triplet] Result: {summary}")
json_handler.log_operation("proof_scan", {"proof": "triplet", "passed": passed})
return {
"passed": passed,
"total": len(all_names),
"complete": complete,
"check_only": check_only,
"missing_check": missing_check,
"other_incomplete": other_incomplete,
"orphaned": orphaned_files,
"issues": issues,
"summary": summary,
}
@@ -0,0 +1,72 @@
# =================== AIPass ====================
# Name: triplet_content.py
# Description: Queryable content for the triplet proof
# Version: 1.0.0
# Created: 2026-03-22
# Modified: 2026-03-22
# =============================================
"""
Triplet Proof Content Handler
Provides formatted triplet proof content for the query system.
Module orchestrates, handler implements.
"""
from aipass.seedgo.apps.handlers.json import json_handler
def get_triplet_proof() -> str:
"""Return triplet proof content for query system.
Returns:
str: Formatted proof text with Rich styling
"""
sep = "\u2500" * 70
lines = [
f"[dim]{sep}[/dim]",
"[bold red]TRIPLET PROOF \u2014 Standard File Completeness[/bold red]",
"[dim]Every standard in a pack must have 3 files.[/dim]",
f"[dim]{sep}[/dim]",
"",
"[bold cyan]WHAT THIS CHECKS:[/bold cyan]",
" Every standard must exist as a complete triplet:",
" [yellow]1.[/yellow] [dim]{name}_check.py[/dim] \u2014 The checker (runtime validation)",
" [yellow]2.[/yellow] [dim]{name}_content.py[/dim] \u2014 Queryable content (for standards_query)",
" [yellow]3.[/yellow] [dim]{name}.md[/dim] \u2014 Full documentation (for humans)",
"",
"[bold cyan]WHY IT MATTERS:[/bold cyan]",
" A checker without content means branches can't query the standard to understand it.",
" A checker without documentation means humans can't read the full spec.",
" Incomplete standards create knowledge gaps in the system.",
"",
f"[dim]{sep}[/dim]",
"[bold cyan]COMMON FAILURES:[/bold cyan]",
"",
"[yellow]Check-only standards:[/yellow]",
" Checker exists but content and md are missing.",
" [red]\u2717[/red] [dim]naming_check.py exists, naming_content.py missing, naming.md missing[/dim]",
"",
"[yellow]Orphaned content:[/yellow]",
" Content file exists with no matching checker.",
" [red]\u2717[/red] [dim]bypass_content.py exists, no bypass_check.py[/dim]",
"",
f"[dim]{sep}[/dim]",
"[bold cyan]HOW TO FIX:[/bold cyan]",
" [yellow]1.[/yellow] Create the missing [dim]*_content.py[/dim] with a [dim]get_{name}_standards()[/dim] function",
" [yellow]2.[/yellow] Create the missing [dim]*.md[/dim] with full documentation",
" [yellow]3.[/yellow] Follow existing triplets as examples:",
" [green]\u2713[/green] [dim]architecture_check.py + architecture_content.py + architecture.md[/dim]",
"",
# TODO: Expand with actual scan logic details, threshold counts,
# and per-pack statistics once the scanner is implemented.
"",
f"[dim]{sep}[/dim]",
"[bold cyan]RELATED:[/bold cyan]",
" [dim]DPLAN-0044: Self-audit tooling design[/dim]",
" [dim]tools/triplet_scanner.py: Original prototype[/dim]",
f"[dim]{sep}[/dim]",
]
json_handler.log_operation("proof_content_queried", {"proof": "triplet"})
return "\n".join(lines)
+17 -2
View File
@@ -269,7 +269,21 @@ def handle_command(command: str, args: List[str]) -> bool:
resolved = Path.cwd() / resolved
resolved = resolved.resolve()
# Run checklist
# Directory mode — run checklist on all .py files in directory
if resolved.is_dir():
py_files = sorted(resolved.glob("*.py"))
py_files = [f for f in py_files if not f.name.startswith("_")]
if not py_files:
error("No .py files found in directory", suggestion=f"Directory: {resolved}")
return True
console.print(f"\n[bold cyan]Checklist — {resolved.name}/[/bold cyan] [dim]({len(py_files)} files)[/dim]\n")
for f in py_files:
results = run_checklist(str(f), pack_name=pack_name)
_print_results(results, str(f))
console.print()
return True
# Single file mode
results = run_checklist(str(resolved), pack_name=pack_name)
# Print results
@@ -333,7 +347,8 @@ def print_help() -> None:
console.print()
console.print("[yellow]USAGE:[/yellow]")
console.print(" [green]drone @seedgo checklist <file>[/green] [dim]# Check file (aipass pack)[/dim]")
console.print(" [green]drone @seedgo checklist <file>[/green] [dim]# Check single file[/dim]")
console.print(" [green]drone @seedgo checklist <directory>[/green] [dim]# Check all .py files in directory[/dim]")
console.print(" [green]drone @seedgo checklist --pack <pack> <file>[/green] [dim]# Check with specific pack[/dim]")
console.print(" [green]drone @seedgo checklist --help[/green] [dim]# This help message[/dim]")
console.print()
@@ -0,0 +1,322 @@
# =================== AIPass ====================
# Name: proof_query.py
# Description: Proof Query Module
# Version: 1.0.0
# Created: 2026-03-22
# Modified: 2026-03-22
# =============================================
"""
Proof Query Module
Auto-discovering content query module for proof packs.
Mirrors standards_query.py pattern but for proof content.
Run: seedgo proof_query
"""
import importlib.util
from pathlib import Path
from typing import List
# =============================================================================
# INFRASTRUCTURE SETUP
# =============================================================================
# IMPORTS
# =============================================================================
# Prax logger (system-wide, always first)
from aipass.prax import logger
# CLI services (display/output formatting)
from aipass.cli import console, header
from aipass.cli.apps.modules import error, warning
# JSON handler for tracking
from aipass.seedgo.apps.handlers.json import json_handler
# =============================================================================
# PACK DISCOVERY
# =============================================================================
def _discover_proof_packs() -> dict:
"""Discover available proof packs from handlers/ directory.
Convention: directories named *_proof/ containing *_content.py files.
Pack identifier is the full directory name (e.g., "aipass_proof").
Returns:
Dict mapping pack name to Path, e.g. {"aipass_proof": Path("handlers/aipass_proof")}
"""
handlers_dir = Path(__file__).parent.parent / "handlers"
packs = {}
if not handlers_dir.exists():
return packs
for d in sorted(handlers_dir.iterdir()):
if not d.is_dir():
continue
if not d.name.endswith("_proof"):
continue
# Must contain at least one *_content.py file
content_files = list(d.glob("*_content.py"))
if content_files:
packs[d.name] = d
return packs
# =============================================================================
# PROOF CONTENT DISCOVERY
# =============================================================================
def _discover_proof_content(pack_dir: Path) -> dict:
"""Discover available proof content files within a pack.
Globs *_content.py from the pack directory.
Args:
pack_dir: Path to the *_proof/ directory
Returns:
Dict mapping proof name to Path,
e.g. {"triplet": Path("triplet_content.py"), ...}
"""
proofs = {}
for f in sorted(pack_dir.glob("*_content.py")):
# Strip _content.py suffix to get proof name
proof_name = f.stem.removesuffix("_content")
proofs[proof_name] = f
return proofs
# =============================================================================
# CONTENT LOADING
# =============================================================================
def _load_proof_content(content_file: Path, proof_name: str) -> str | None:
"""Load and return formatted proof content from a content handler.
Imports the content file and calls get_{proof_name}_proof().
Args:
content_file: Path to the *_content.py file
proof_name: Name of the proof (e.g., "triplet")
Returns:
Rich-formatted string, or None on failure
"""
try:
spec = importlib.util.spec_from_file_location(content_file.stem, content_file)
if spec is None or spec.loader is None:
logger.error(f"[proof_query] Failed to create spec for {content_file}")
return None
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
fn_name = f"get_{proof_name}_proof"
fn = getattr(mod, fn_name, None)
if fn is None:
logger.error(f"[proof_query] No {fn_name}() in {content_file.name}")
console.print(f"[red]Content handler missing:[/red] {fn_name}() not found in {content_file.name}")
return None
return fn()
except Exception as e:
logger.error(f"[proof_query] Failed to load {content_file.name}: {e}")
console.print(f"[red]Failed to load content:[/red] {e}")
return None
# =============================================================================
# DISPLAY HELPERS
# =============================================================================
def _show_query_introspection() -> None:
"""No-args display: list available proof packs with content counts."""
console.print()
console.print("[bold cyan]proof_query Module[/bold cyan]")
console.print("Pack-aware content query — browse proof content by pack and name")
console.print()
# Show discovered packs
packs = _discover_proof_packs()
console.print("[yellow]Discovered Proof Packs:[/yellow]")
for name, pack_path in packs.items():
proofs = _discover_proof_content(pack_path)
console.print(f" [cyan]{name}[/cyan] ({len(proofs)} proof{'s' if len(proofs) != 1 else ''})")
if not packs:
console.print(" [dim]No proof packs found[/dim]")
console.print()
console.print("[yellow]Connected Handlers:[/yellow]")
for name, pack_path in packs.items():
console.print(f" [cyan]handlers/{name}/[/cyan]")
proofs = _discover_proof_content(pack_path)
for proof_name, proof_path in proofs.items():
console.print(f" [dim]- {proof_path.name} (get_{proof_name}_proof)[/dim]")
console.print()
# Navigation hints with drone commands
if packs:
console.print("[yellow]Next:[/yellow] Pick a pack to see its proofs")
for name in packs:
console.print(f" [green]drone @seedgo proof_query {name}[/green]")
console.print()
def _list_pack_proofs(pack_name: str, pack_dir: Path) -> None:
"""List all proofs available in a pack.
Args:
pack_name: Full pack directory name (e.g., "aipass_proof")
pack_dir: Path to the pack directory
"""
proofs = _discover_proof_content(pack_dir)
console.print()
header(f"PROOFS IN {pack_name.upper()}")
console.print()
if not proofs:
warning("No content handlers found.")
console.print(f"[dim]Add *_content.py files to handlers/{pack_name}/[/dim]")
console.print()
return
console.print(f"[yellow]Available Proofs:[/yellow] ({len(proofs)})")
console.print()
for name in proofs:
console.print(f" [cyan]{name}[/cyan]")
console.print()
console.print("[yellow]Next:[/yellow] Pick a proof to see its content")
first_proof = next(iter(proofs))
console.print(f" [green]drone @seedgo proof_query {pack_name} {first_proof}[/green]")
console.print()
def _show_proof_content(pack_name: str, pack_dir: Path, proof_name: str) -> None:
"""Display specific proof content.
Args:
pack_name: Full pack directory name (e.g., "aipass_proof")
pack_dir: Path to the pack directory
proof_name: Name of the proof (e.g., "triplet")
"""
proofs = _discover_proof_content(pack_dir)
if proof_name not in proofs:
console.print(f"[red]Unknown proof:[/red] '{proof_name}'")
console.print()
warning(f"Available proofs in {pack_name}:")
for name in proofs:
console.print(f" [cyan]{name}[/cyan]")
console.print()
return
content = _load_proof_content(proofs[proof_name], proof_name)
json_handler.log_operation("proof_queried", {"pack": pack_name, "proof": proof_name})
if content:
console.print()
# Handle both str and List[str] return types from content handlers
if isinstance(content, list):
for line in content:
console.print(line)
else:
console.print(content)
console.print()
# =============================================================================
# COMMAND HANDLER
# =============================================================================
def handle_command(command: str, args: List[str]) -> bool:
"""
Handle 'proof_query' command with pack-aware drill-down.
Args:
command: Command name
args: Additional arguments
[] → show introspection (available packs)
["aipass_proof"] → list proofs in pack
["aipass_proof", "triplet"] → show proof content
["--help"] → help
Returns:
True if handled, False if not this module's command
"""
if command != "proof_query":
return False
if not args:
print_introspection()
return True
if args[0] in ["--help", "-h", "help"]:
print_help()
return True
# First arg = pack name
packs = _discover_proof_packs()
pack_name = args[0]
if pack_name not in packs:
console.print(f"[red]Unknown pack:[/red] '{pack_name}'")
console.print()
console.print("[yellow]Available packs:[/yellow]")
for name in packs:
console.print(f" [cyan]{name}[/cyan]")
console.print()
return True
# No second arg = list proofs in pack
if len(args) < 2:
_list_pack_proofs(pack_name, packs[pack_name])
return True
# Second arg = proof name
_show_proof_content(pack_name, packs[pack_name], args[1])
return True
# =============================================================================
# INTROSPECTION & HELP
# =============================================================================
def print_introspection() -> None:
"""Display module info for seedgo introspection system."""
_show_query_introspection()
def print_help() -> None:
"""Print help information."""
console.print()
console.print("[bold cyan]Proof Query Module[/bold cyan]")
console.print("Pack-aware content query — browse and display proof content")
console.print()
console.print("[yellow]COMMANDS:[/yellow]")
console.print(" [green]drone @seedgo proof_query[/green] [dim]List available proof packs[/dim]")
console.print(" [green]drone @seedgo proof_query <pack>[/green] [dim]List proofs in pack[/dim]")
console.print(" [green]drone @seedgo proof_query <pack> <proof>[/green] [dim]Show proof content[/dim]")
console.print(" [green]drone @seedgo proof_query --help[/green] [dim]This help message[/dim]")
console.print()
console.print("[yellow]EXAMPLES:[/yellow]")
console.print(" [dim]# List all proof packs[/dim]")
console.print(" [green]drone @seedgo proof_query[/green]")
console.print()
console.print(" [dim]# List proofs in aipass proof pack[/dim]")
console.print(" [green]drone @seedgo proof_query aipass_proof[/green]")
console.print()
console.print(" [dim]# Show triplet proof content[/dim]")
console.print(" [green]drone @seedgo proof_query aipass_proof triplet[/green]")
console.print()
console.print("[yellow]REFERENCE:[/yellow]")
console.print(" Auto-discovers *_content.py handlers from proof pack directories.")
console.print(" Each content handler provides get_<name>_proof() returning Rich-formatted text.")
console.print()
console.print("[dim]Commands: proof_query, --help[/dim]")
console.print()
@@ -0,0 +1,497 @@
# =================== AIPass ====================
# Name: seedgo_proof.py
# Description: Self-proof orchestrator — discovers and runs proof packs
# Version: 1.0.0
# Created: 2026-03-22
# Modified: 2026-03-22
# =============================================
"""Seedgo Proof — Self-check orchestrator for standards pack integrity.
Discovers *_proof/ directories in handlers/, runs all proof handler .py files
inside, aggregates results, and reports CERTIFIED or NOT CERTIFIED.
Commands:
seedgo proof — List available proof packs
seedgo proof aipass — Run all proofs for aipass pack
seedgo proof aipass --json — Machine-readable JSON output
Discovery pattern mirrors standards_audit.py:
handlers/*_proof/ dirs → handler .py files → scan(pack_dir) interface
Interface per proof handler:
scan(pack_dir: Path) -> dict with keys: passed, issues, summary
Related:
DPLAN-0048 (seedgo self-check system)
tools/ (original prototypes — standalone, not connected)
"""
import json
import importlib.util
from pathlib import Path
from typing import List
from rich.table import Table
# =============================================================================
# INFRASTRUCTURE SETUP
# =============================================================================
# IMPORTS
# =============================================================================
# Prax logger (system-wide, always first)
from aipass.prax import logger
# CLI services (display/output formatting)
from aipass.cli import console, header
from aipass.cli.apps.modules import error, warning
# JSON handler for tracking
from aipass.seedgo.apps.handlers.json import json_handler
# =============================================================================
# PACK DISCOVERY
# =============================================================================
def _discover_proof_packs() -> dict:
"""Discover available proof packs from handlers/ directory.
Convention: directories named *_proof/ containing at least one .py handler file.
Pack display name strips the _proof suffix.
Excludes from handler counting:
- __init__.py
- *_content.py
- files starting with _
- non-.py files
Returns:
Dict mapping pack name to Path, e.g. {"aipass": Path("handlers/aipass_proof")}
"""
handlers_dir = Path(__file__).parent.parent / "handlers"
packs = {}
if not handlers_dir.exists():
return packs
for d in sorted(handlers_dir.iterdir()):
if not d.is_dir():
continue
if not d.name.endswith("_proof"):
continue
# Must contain at least one valid handler .py file
handler_files = _discover_proof_handlers(d)
if handler_files:
pack_name = d.name.removesuffix("_proof")
packs[pack_name] = d
return packs
def _discover_proof_handlers(pack_dir: Path) -> List[Path]:
"""Find all handler .py files in a proof pack directory.
Skips:
- __init__.py
- *_content.py
- *.md files
- files starting with _
Args:
pack_dir: Path to the *_proof/ directory
Returns:
Sorted list of handler Paths
"""
handlers = []
if not pack_dir.exists():
return handlers
for f in sorted(pack_dir.iterdir()):
if not f.is_file():
continue
if f.suffix != ".py":
continue
if f.name.startswith("_"):
continue
if f.name.endswith("_content.py"):
continue
handlers.append(f)
return handlers
def _resolve_target_pack(proof_pack_name: str) -> Path | None:
"""Resolve the standards pack directory that a proof pack targets.
Mapping: strip '_proof' suffix from proof dir name, add '_standards'.
E.g., handlers/aipass_proof/ -> handlers/aipass_standards/
Args:
proof_pack_name: The display name of the proof pack (e.g. "aipass")
Returns:
Path to the target standards pack, or None if it does not exist
"""
handlers_dir = Path(__file__).parent.parent / "handlers"
target_dir = handlers_dir / f"{proof_pack_name}_standards"
if target_dir.is_dir():
return target_dir
return None
# =============================================================================
# PROOF EXECUTION
# =============================================================================
def _load_and_run_proof(handler_path: Path, pack_dir: Path) -> dict:
"""Import a proof handler module dynamically and call its scan() function.
Args:
handler_path: Path to the handler .py file
pack_dir: Path to the target standards pack directory (passed to scan())
Returns:
Result dict from scan(), or error dict on failure
"""
handler_name = handler_path.stem
try:
spec = importlib.util.spec_from_file_location(handler_name, handler_path)
if spec is None or spec.loader is None:
logger.error(f"[seedgo_proof] Failed to create spec for {handler_path}")
return {
"passed": False,
"issues": [f"Failed to create import spec for {handler_path.name}"],
"summary": "Import error",
"error": True,
}
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
scan_fn = getattr(mod, "scan", None)
if scan_fn is None:
logger.warning(f"[seedgo_proof] No scan() in {handler_path.name} — skipping")
return {
"passed": False,
"issues": [f"No scan() function found in {handler_path.name}"],
"summary": "Missing scan() interface",
"error": True,
"not_implemented": True,
}
result = scan_fn(pack_dir)
if not isinstance(result, dict):
return {
"passed": False,
"issues": [f"scan() returned {type(result).__name__}, expected dict"],
"summary": "Invalid return type",
"error": True,
}
return result
except Exception as e:
logger.error(f"[seedgo_proof] Error running {handler_name}: {e}")
return {
"passed": False,
"issues": [f"Exception: {e}"],
"summary": f"Error: {e}",
"error": True,
}
def _run_proof_pack(pack_name: str, pack_dir: Path) -> dict:
"""Run all proof handlers in a pack against the target standards pack.
For each handler: load, run scan(), collect results.
Aggregate: count passed/failed/errors.
Args:
pack_name: Display name of the proof pack (e.g. "aipass")
pack_dir: Path to the proof pack directory (e.g. handlers/aipass_proof/)
Returns:
Aggregated dict with keys:
pack_name, target_dir, results (per-handler), passed, failed,
errors, total, certified
"""
handlers = _discover_proof_handlers(pack_dir)
target_dir = _resolve_target_pack(pack_name)
if target_dir is None:
logger.error(f"[seedgo_proof] No target standards pack found for '{pack_name}'")
return {
"pack_name": pack_name,
"target_dir": None,
"results": {},
"passed": 0,
"failed": 0,
"errors": 1,
"total": 0,
"certified": False,
"error": f"No matching standards pack: {pack_name}_standards",
}
results = {}
passed_count = 0
failed_count = 0
error_count = 0
for handler_path in handlers:
handler_name = handler_path.stem
result = _load_and_run_proof(handler_path, target_dir)
results[handler_name] = result
if result.get("error"):
error_count += 1
elif result.get("passed"):
passed_count += 1
else:
failed_count += 1
total = len(handlers)
certified = (failed_count == 0 and error_count == 0 and total > 0)
return {
"pack_name": pack_name,
"target_dir": str(target_dir),
"results": results,
"passed": passed_count,
"failed": failed_count,
"errors": error_count,
"total": total,
"certified": certified,
}
# =============================================================================
# DISPLAY
# =============================================================================
def _display_proof_results(pack_name: str, results: dict) -> None:
"""Rich console output for proof pack results.
Shows header, per-proof results table, and final verdict.
Args:
pack_name: Display name of the proof pack
results: Aggregated results dict from _run_proof_pack()
"""
console.print()
header(f"SEEDGO PROOF — {pack_name.upper()}")
console.print()
if results.get("error") and not results.get("results"):
error(results["error"])
console.print()
return
target_dir = results.get("target_dir", "unknown")
console.print(f"[dim]Target pack: {target_dir}[/dim]")
console.print(f"[dim]Proof handlers: {results['total']}[/dim]")
console.print()
# Per-proof results table
table = Table(
title="Proof Results",
show_header=True,
header_style="bold cyan",
show_lines=False,
pad_edge=True,
)
table.add_column("Proof", style="cyan", min_width=20)
table.add_column("Status", justify="center", min_width=12)
table.add_column("Summary", min_width=30)
for proof_name, result in results.get("results", {}).items():
if result.get("not_implemented"):
status = "[yellow]PENDING[/yellow]"
elif result.get("error"):
status = "[red]ERROR[/red]"
elif result.get("passed"):
status = "[green]PASSED[/green]"
else:
status = "[red]FAILED[/red]"
summary = result.get("summary", "No summary")
issue_count = len(result.get("issues", []))
if issue_count > 0 and not result.get("not_implemented"):
summary += f" ({issue_count} issue{'s' if issue_count != 1 else ''})"
table.add_row(proof_name, status, summary)
console.print(table)
console.print()
# Aggregate summary
console.print(
f" [green]Passed:[/green] {results['passed']} "
f"[red]Failed:[/red] {results['failed']} "
f"[yellow]Errors:[/yellow] {results['errors']} "
f"[dim]Total:[/dim] {results['total']}"
)
console.print()
# Final verdict
if results["certified"]:
console.print("[bold green] CERTIFIED[/bold green]")
else:
console.print("[bold red] NOT CERTIFIED[/bold red]")
console.print()
# =============================================================================
# INTROSPECTION
# =============================================================================
def _show_proof_introspection() -> None:
"""Show available proof packs and example commands when proof is run with no args."""
packs = _discover_proof_packs()
console.print()
header("SEEDGO PROOF")
console.print()
if not packs:
warning("No proof packs found.")
console.print("[dim]Add handler .py files to handlers/*_proof/ directories.[/dim]")
console.print()
return
console.print("[yellow]Available Proof Packs:[/yellow]")
console.print()
for name, pack_path in packs.items():
handler_files = _discover_proof_handlers(pack_path)
target = _resolve_target_pack(name)
target_status = "[green]target found[/green]" if target else "[red]no target[/red]"
console.print(
f" [cyan]{name}[/cyan] "
f"({len(handler_files)} proof{'s' if len(handler_files) != 1 else ''}, "
f"{target_status})"
)
console.print()
console.print("[yellow]Next:[/yellow] Pick a pack to run proofs")
first_pack = next(iter(packs))
console.print(f" [green]drone @seedgo proof {first_pack}[/green] [dim]# Run all proofs[/dim]")
console.print(f" [green]drone @seedgo proof {first_pack} --json[/green] [dim]# JSON output[/dim]")
console.print()
def print_introspection() -> None:
"""Display module info and connected handlers."""
_show_proof_introspection()
# =============================================================================
# HELP
# =============================================================================
def print_help() -> None:
"""Print help information."""
console.print()
console.print("[bold cyan]Seedgo Proof Module[/bold cyan]")
console.print("Self-check orchestrator — verifies standards pack integrity")
console.print()
console.print("[yellow]COMMANDS:[/yellow]")
console.print(" [green]drone @seedgo proof[/green] [dim]Show available proof packs[/dim]")
console.print(" [green]drone @seedgo proof aipass[/green] [dim]Run all proofs for aipass pack[/dim]")
console.print(" [green]drone @seedgo proof aipass --json[/green] [dim]JSON output[/dim]")
console.print(" [green]drone @seedgo proof --help[/green] [dim]This help message[/dim]")
console.print()
console.print("[yellow]PROOF HANDLER INTERFACE:[/yellow]")
console.print(" Each handler .py file must define:")
console.print(" [cyan]scan(pack_dir: Path) -> dict[/cyan]")
console.print(" Return keys: passed (bool), issues (list), summary (str)")
console.print()
console.print("[yellow]REFERENCE:[/yellow]")
console.print(" Proof pack = handlers/*_proof/ directory")
console.print(" Target = handlers/*_standards/ directory (auto-resolved)")
console.print(" Verdict: CERTIFIED (all pass) or NOT CERTIFIED (any fail/error)")
console.print()
console.print("[dim]Commands: proof, seedgo_proof, --help[/dim]")
console.print()
# =============================================================================
# COMMAND HANDLER
# =============================================================================
def handle_command(command: str, args: List[str]) -> bool:
"""Handle 'proof' command with pack-aware routing.
Args:
command: Command name
args: Additional arguments
[] -> show proof introspection (available packs)
["aipass"] -> run aipass proof pack
["aipass", "--json"] -> JSON output
["--help"] -> help
Returns:
True if handled, False if not this module's command
"""
if command not in ("proof", "seedgo_proof"):
return False
# No args -> show proof introspection (available packs)
if not args:
print_introspection()
return True
# --help -> help
if args[0] in ["--help", "-h", "help"]:
print_help()
return True
# Parse pack name and flags
pack_name: str | None = None
json_output = False
for arg in args:
if arg == "--json":
json_output = True
elif not arg.startswith("-"):
if pack_name is None:
pack_name = arg
# Validate pack name
packs = _discover_proof_packs()
if pack_name is None or pack_name not in packs:
available = ", ".join(packs.keys()) if packs else "(none)"
error(
f"Unknown proof pack: '{pack_name}'",
suggestion=f"Available packs: {available}. Usage: drone @seedgo proof {next(iter(packs), '<pack>')}"
)
return True
pack_dir = packs[pack_name]
# Log proof start
json_handler.log_operation(
"proof_started",
{"pack": pack_name}
)
# Run the proof pack
results = _run_proof_pack(pack_name, pack_dir)
# Output
if json_output:
console.print_json(json.dumps(results, indent=2, default=str))
else:
_display_proof_results(pack_name, results)
# Log completion
json_handler.log_operation(
"proof_completed",
{
"pack": pack_name,
"passed": results["passed"],
"failed": results["failed"],
"errors": results["errors"],
"certified": results["certified"],
}
)
return True