mirror of
https://github.com/towishy/Owen-Graphite.git
synced 2026-07-22 04:40:30 +00:00
626 lines
25 KiB
Python
626 lines
25 KiB
Python
#!/usr/bin/env python3
|
|
"""Audit consistency of Owen Graphite WIKI operating rules."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
|
|
|
|
REQUIRED_FILES = [
|
|
"dev/WIKI/VISUAL-QA.md",
|
|
"dev/WIKI/OWNER-DECISION-TREE.md",
|
|
"dev/WIKI/SELECTOR-OWNER-CHEATSHEET.md",
|
|
"dev/WIKI/MAP/diff-stability.md",
|
|
"dev/WIKI/MAP/coverage-priority-plan.md",
|
|
"dev/WIKI/MAP/route-registry.json",
|
|
"dev/WIKI/MAP/route-registry.md",
|
|
"dev/WIKI/MAP/theme-css-risk-summary.json",
|
|
"dev/WIKI/runtime-evidence-template.md",
|
|
"dev/WIKI/runtime-evidence-schema.json",
|
|
"dev/WIKI/runtime-evidence-storage.md",
|
|
"dev/WIKI/runtime-evidence-registry.json",
|
|
"dev/WIKI/risk-accepted-registry.json",
|
|
"dev/WIKI/RUNTIME/README.md",
|
|
"dev/WIKI/RUNTIME/table.md",
|
|
"dev/WIKI/RUNTIME/chrome.md",
|
|
"dev/WIKI/RUNTIME/pdf.md",
|
|
"dev/WIKI/RUNTIME/plugins.md",
|
|
"dev/WIKI/TOKENS/usage-guide.md",
|
|
"dev/WIKI/TOKENS/state-token-map.md",
|
|
"dev/WIKI/PLUGINS/compatibility-matrix.md",
|
|
"dev/WIKI/PLUGINS/coverage-matrix.md",
|
|
"dev/WIKI/PLUGINS/runtime-dom-notes.md",
|
|
"dev/WIKI/RECIPES/README.md",
|
|
"dev/WIKI/RECIPES/reading-heading-spacing.md",
|
|
"dev/WIKI/RECIPES/live-preview-spacing.md",
|
|
"dev/WIKI/RECIPES/rendered-table-polish.md",
|
|
"dev/WIKI/RECIPES/pdf-label-preset.md",
|
|
"dev/WIKI/RECIPES/top-chrome-state.md",
|
|
"dev/WIKI/RECIPES/style-settings-option.md",
|
|
"dev/WIKI/INCIDENTS/README.md",
|
|
"dev/WIKI/INCIDENTS/incident-template.md",
|
|
"dev/WIKI/INCIDENTS/taxonomy.md",
|
|
"dev/WIKI/PROMPTS/work-summary.md",
|
|
"dev/WIKI/SRC/validation-matrix.md",
|
|
"dev/WIKI/DOCS/v3/pdf-font-size-matrix.md",
|
|
"dev/WIKI/WORKFLOWS/release.md",
|
|
"dev/WIKI/WORKFLOWS/validation-matrix.md",
|
|
"dev/WIKI/WORKFLOWS/wiki-maintenance.md",
|
|
]
|
|
|
|
REQUIRED_TEXT = {
|
|
".github/copilot-instructions.md": [
|
|
"consult `dev/WIKI` first",
|
|
"Owen's explicit instruction",
|
|
"numeric semver",
|
|
"never use a leading `v` prefix",
|
|
],
|
|
"dev/WIKI/README.md": [
|
|
"OWNER-DECISION-TREE.md",
|
|
"SELECTOR-OWNER-CHEATSHEET.md",
|
|
"VISUAL-QA.md",
|
|
"runtime-evidence-template.md",
|
|
"runtime-evidence-schema.json",
|
|
"runtime-evidence-storage.md",
|
|
"Owen can explicitly accept",
|
|
"TOKENS/",
|
|
"PLUGINS/",
|
|
"numeric semver tags only",
|
|
],
|
|
"dev/WIKI/INDEX.md": [
|
|
"OWNER-DECISION-TREE.md",
|
|
"SELECTOR-OWNER-CHEATSHEET.md",
|
|
"VISUAL-QA.md",
|
|
"runtime-evidence-template.md",
|
|
"runtime-evidence-registry.json",
|
|
"risk-accepted-registry.json",
|
|
"SRC/validation-matrix.md",
|
|
"MAP/coverage-priority-plan.md",
|
|
"TOKENS/usage-guide.md",
|
|
"PLUGINS/compatibility-matrix.md",
|
|
"PLUGINS/coverage-matrix.md",
|
|
"RUNTIME/README.md",
|
|
"RECIPES/README.md",
|
|
"INCIDENTS/incident-template.md",
|
|
"INCIDENTS/taxonomy.md",
|
|
"dev/scripts/wiki_route.py",
|
|
"dev/scripts/wiki_route.py <surface> --commands",
|
|
"MAP/route-registry.json",
|
|
"MAP/route-registry.md",
|
|
"dev/scripts/audit_route_registry.py",
|
|
"dev/scripts/build_route_registry_doc.py --check",
|
|
"validation_plan.py --surface",
|
|
"validation_plan.py --surface chrome --surface settings",
|
|
"validation_plan.py --json",
|
|
"finish_work.py --full-check",
|
|
"dev/scripts/start_work.py",
|
|
"dev/scripts/finish_work.py",
|
|
"dev/scripts/validation_plan.py",
|
|
"dev/scripts/run_validation.py",
|
|
"dev/scripts/check_release_status.py",
|
|
"dev/scripts/release_publish.py",
|
|
"dev/scripts/new_runtime_evidence.py",
|
|
"dev/scripts/new_incident.py",
|
|
"dev/scripts/work_summary.py",
|
|
"dev/scripts/build_coverage_priority_plan.py",
|
|
"dev/scripts/audit_mobile_owner.py",
|
|
"dev/scripts/audit_owner_risk_contracts.py",
|
|
"dev/scripts/audit_wiki_route_coverage.py",
|
|
"dev/scripts/audit_selector_owner_cheatsheet.py",
|
|
"dev/scripts/audit_runtime_evidence_requirements.py",
|
|
"dev/scripts/cdp_capture.mjs --status",
|
|
"dev/scripts/promote_evidence.py",
|
|
"PROMPTS/work-summary.md",
|
|
"WORKFLOWS/validation-matrix.md",
|
|
],
|
|
"dev/WIKI/audits.md": [
|
|
"wiki_route.py --list",
|
|
"wiki_route.py mobile",
|
|
"wiki_route.py settings --commands",
|
|
"validation_plan.py --surface",
|
|
"validation_plan.py --surface chrome --surface settings",
|
|
"validation_plan.py --surface chrome --json",
|
|
"validation_plan.py --full-check --run-safe",
|
|
"run_validation.py --preset core",
|
|
"run_validation.py --preset process",
|
|
"check_release_status.py --version <version>",
|
|
"finish_work.py --surface chrome --full-check",
|
|
"finish_work.py --full-check",
|
|
"build_coverage_priority_plan.py --check",
|
|
"validation_plan.py --run-safe",
|
|
"audit_owner_risk_contracts.py",
|
|
"audit_route_registry.py",
|
|
"test_route_workflow.py",
|
|
"audit_selector_owner_cheatsheet.py",
|
|
"build_route_registry_doc.py --check",
|
|
"audit_wiki_consistency.py",
|
|
"cdp_capture.mjs --status",
|
|
"test_direct_owner_guard.py",
|
|
],
|
|
"dev/WIKI/DOCS/v3/pdf-font-size-matrix.md": [
|
|
"12pt = 16px",
|
|
"PDF visibility",
|
|
"PDF customer delivery",
|
|
"--ogd-pdf-surface-text-font-size",
|
|
],
|
|
"dev/WIKI/MAP/diff-stability.md": [
|
|
"sorted keys",
|
|
"theme-css-risk-summary.json",
|
|
"selector-key order",
|
|
"fix the generator ordering",
|
|
],
|
|
"dev/WIKI/MAP/coverage-priority-plan.md": [
|
|
"P0 State And Chrome Runtime",
|
|
"P1 Plugin Runtime",
|
|
"P2 Print And PDF Context",
|
|
"Runtime evidence",
|
|
"build_coverage_priority_plan.py --check",
|
|
],
|
|
"dev/WIKI/VISUAL-QA.md": [
|
|
"Liquid Glass Acceptance",
|
|
"Required View Matrix",
|
|
"runtime-evidence-template.md",
|
|
],
|
|
"dev/WIKI/WORKFLOWS/release.md": [
|
|
"git tag <version>",
|
|
"Never run `git tag v<version>`",
|
|
"release_publish.py --version <version>",
|
|
"check_release_status.py --version <version>",
|
|
"gh release view <version>",
|
|
],
|
|
"dev/WIKI/OWNER-DECISION-TREE.md": [
|
|
"runtime evidence",
|
|
"Obsidian core",
|
|
"Owen explicitly accepts the risk",
|
|
"SELECTOR-OWNER-CHEATSHEET.md",
|
|
"If ownership is still unclear",
|
|
],
|
|
"dev/WIKI/CORE-PRINCIPLES.md": [
|
|
"Owen Risk Acceptance",
|
|
"product-owner risk acceptance",
|
|
"risk-accepted-registry.json",
|
|
"change the boundary documentation and audit rule",
|
|
],
|
|
"dev/WIKI/SELECTOR-OWNER-CHEATSHEET.md": [
|
|
".cm-table-widget",
|
|
"Obsidian core",
|
|
"OWNER-DECISION-TREE.md",
|
|
],
|
|
"dev/WIKI/runtime-evidence-template.md": [
|
|
"Required Evidence",
|
|
"runtime-evidence-schema.json",
|
|
"Matched rules",
|
|
"If this template cannot be filled",
|
|
],
|
|
"dev/WIKI/runtime-evidence-storage.md": [
|
|
"dev/TEMP/runtime-evidence",
|
|
"new_runtime_evidence.py",
|
|
"--strict",
|
|
"promote_evidence.py",
|
|
"Permanent Evidence",
|
|
"Minimum Metadata",
|
|
"runtime-evidence-schema.json",
|
|
"runtime-evidence-registry.json",
|
|
"risk-accepted-registry.json",
|
|
"dev/scripts/cdp_capture.mjs --status",
|
|
"--require-vault Owen-WIKI",
|
|
"--require-theme \"Owen Graphite\"",
|
|
],
|
|
"dev/WIKI/risk-accepted-registry.json": [
|
|
"owen-graphite/risk-accepted-registry/1",
|
|
"cm-table-widget-visibility-2026-05-25",
|
|
"src/base/13-live-preview.css",
|
|
"allowedProperties",
|
|
],
|
|
"dev/WIKI/runtime-evidence-registry.json": [
|
|
"owen-graphite/runtime-evidence-registry/1",
|
|
"src/chrome/32-overlay-popover-dataview.css",
|
|
"src/plugins/60-canvas-graph-link-panes.css",
|
|
"runtime-reserved",
|
|
],
|
|
"dev/WIKI/RUNTIME/README.md": [
|
|
"table.md",
|
|
"chrome.md",
|
|
"pdf.md",
|
|
"plugins.md",
|
|
],
|
|
"dev/WIKI/TOKENS/usage-guide.md": [
|
|
"Use tokens before adding literal colors",
|
|
"Do not create a token to hide a one-off repair",
|
|
],
|
|
"dev/WIKI/TOKENS/state-token-map.md": [
|
|
"Resting glass",
|
|
"Hover",
|
|
"Print",
|
|
],
|
|
"dev/WIKI/PLUGINS/compatibility-matrix.md": [
|
|
"Dataview",
|
|
"coverage-matrix.md",
|
|
"src/chrome/32-overlay-popover-dataview.css",
|
|
"Mermaid",
|
|
"does not transfer ownership",
|
|
],
|
|
"dev/WIKI/WORKFLOWS/validation-matrix.md": [
|
|
"Change Type",
|
|
"Full Release-Confidence Set",
|
|
"wiki_route.py <surface>",
|
|
"--run-safe",
|
|
"--surface <surface>",
|
|
"--surface chrome --surface settings",
|
|
"--json",
|
|
"last-validation.json",
|
|
"finish_work.py --full-check",
|
|
"never builds release ZIPs",
|
|
"pre-commit hook",
|
|
],
|
|
"dev/WIKI/WORKFLOWS/wiki-maintenance.md": [
|
|
"WIKI Maintenance",
|
|
"audit_owner_risk_contracts.py",
|
|
"audit_route_registry.py",
|
|
"test_route_workflow.py",
|
|
"route-registry.json",
|
|
"route-registry.md",
|
|
"build_route_registry_doc.py --check",
|
|
"build_source_usage_map.py --check",
|
|
"build_coverage_priority_plan.py --check",
|
|
"risk-accepted-registry.json",
|
|
"dry-run mode",
|
|
],
|
|
"dev/WIKI/PLUGINS/coverage-matrix.md": [
|
|
"Real DOM Captured",
|
|
"Fixture Exists",
|
|
"Owner Confirmed",
|
|
"Unused CSS Bucket Covered",
|
|
],
|
|
"dev/WIKI/DOCS/docs-map.md": [
|
|
"Purpose Map",
|
|
"Runtime state issue",
|
|
"WIKI/process change",
|
|
],
|
|
"dev/WIKI/PROMPTS/work-summary.md": [
|
|
"WIKI consulted",
|
|
"Owner modules changed",
|
|
"work_summary.py",
|
|
"last-sync.json",
|
|
"changelog candidate",
|
|
"--sync",
|
|
"Obsidian synced",
|
|
],
|
|
"dev/WIKI/RECIPES/README.md": [
|
|
"reading-heading-spacing.md",
|
|
"live-preview-spacing.md",
|
|
"style-settings-option.md",
|
|
],
|
|
"dev/WIKI/INCIDENTS/README.md": [
|
|
"Existing Incidents",
|
|
"Required Fields For New Incidents",
|
|
"incident-template.md",
|
|
"taxonomy.md",
|
|
"new_incident.py",
|
|
"--evidence",
|
|
"promote_evidence.py",
|
|
"Evidence source and runtime schema link.",
|
|
"Owner decision",
|
|
"Forbidden fix pattern.",
|
|
"Regression check.",
|
|
],
|
|
"dev/WIKI/INCIDENTS/taxonomy.md": [
|
|
"runtime-selected-state",
|
|
"owen-risk-accepted",
|
|
"release-process",
|
|
"create an incident",
|
|
],
|
|
"dev/WIKI/SRC/validation-matrix.md": [
|
|
"Source Family",
|
|
"Minimum Checks",
|
|
"Runtime Rule",
|
|
],
|
|
"CONTRIBUTING.md": [
|
|
"WIKI 확인",
|
|
"dev/WIKI/README.md",
|
|
],
|
|
}
|
|
|
|
SRC_DOCS = [
|
|
"dev/WIKI/SRC/base.md",
|
|
"dev/WIKI/SRC/surfaces.md",
|
|
"dev/WIKI/SRC/features.md",
|
|
"dev/WIKI/SRC/chrome.md",
|
|
"dev/WIKI/SRC/plugins.md",
|
|
"dev/WIKI/SRC/themes.md",
|
|
"dev/WIKI/SRC/tokens.md",
|
|
]
|
|
|
|
FORBIDDEN_INDEX_TEXT = [
|
|
"DEV/",
|
|
"dev/MAP/",
|
|
"docs/v3/",
|
|
]
|
|
|
|
RUNTIME_EVIDENCE_STATUSES = {"captured", "partial", "unavailable", "needed"}
|
|
RUNTIME_EVIDENCE_DECISIONS = {"runtime-reserved", "do-not-remove", "needs-capture", "candidate-review"}
|
|
RUNTIME_EVIDENCE_SURFACES = {"chrome", "plugin", "table", "pdf", "live-preview", "mobile", "settings"}
|
|
RISK_ACCEPTED_STATUSES = {"active", "retired"}
|
|
|
|
|
|
def fail(message: str) -> None:
|
|
raise AssertionError(message)
|
|
|
|
|
|
def read(rel: str) -> str:
|
|
return (ROOT / rel).read_text(encoding="utf-8")
|
|
|
|
|
|
def assert_required_files() -> None:
|
|
missing = [path for path in REQUIRED_FILES if not (ROOT / path).is_file()]
|
|
if missing:
|
|
fail("missing WIKI consistency files: " + ", ".join(missing))
|
|
print("OK: WIKI consistency files exist")
|
|
|
|
|
|
def assert_required_text() -> None:
|
|
missing: list[str] = []
|
|
for path, needles in REQUIRED_TEXT.items():
|
|
text = read(path)
|
|
for needle in needles:
|
|
if needle not in text:
|
|
missing.append(f"{path}: {needle!r}")
|
|
if missing:
|
|
fail("required WIKI text missing: " + "; ".join(missing))
|
|
print("OK: WIKI required text present")
|
|
|
|
|
|
def assert_src_docs_have_validation_matrix() -> None:
|
|
missing = [path for path in SRC_DOCS if "SRC/validation-matrix.md" not in read(path)]
|
|
if missing:
|
|
fail("SRC docs missing validation matrix link: " + ", ".join(missing))
|
|
print("OK: SRC docs link validation matrix")
|
|
|
|
|
|
def assert_release_workflow_paths() -> None:
|
|
workflow = read(".github/workflows/release.yml")
|
|
if "dev/MAP/" in workflow:
|
|
fail("release workflow still references dev/MAP")
|
|
if "dev/WIKI/MAP/map-info-classification.md" not in workflow:
|
|
fail("release workflow missing dev/WIKI/MAP artifacts")
|
|
release_check = read("dev/scripts/release_check.py")
|
|
if "leading v is ignored" in release_check:
|
|
fail("release_check help must not imply v-prefixed tags are accepted")
|
|
print("OK: release workflow uses WIKI paths and numeric tag language")
|
|
|
|
|
|
def assert_helper_and_generator_stability() -> None:
|
|
wiki_route = read("dev/scripts/wiki_route.py")
|
|
route_registry = read("dev/WIKI/MAP/route-registry.json")
|
|
build_src_map = read("dev/scripts/build_src_map.py")
|
|
light_tokens = read("src/tokens/00-light-tokens.css")
|
|
required_routes = ["table", "live-preview", "pdf", "chrome", "plugin", "mobile", "tokens", "settings", "docs", "release"]
|
|
missing_routes = [route for route in required_routes if f'"{route}"' not in route_registry]
|
|
if missing_routes:
|
|
fail("route-registry.json missing routes: " + ", ".join(missing_routes))
|
|
if "route_registry" not in wiki_route:
|
|
fail("wiki_route.py must load route registry metadata")
|
|
if "sort_keys=True" not in build_src_map or "stable_selector_entries" not in build_src_map:
|
|
fail("build_src_map.py must keep generated JSON output stable")
|
|
for token in ("--ogd-focus-ring-border", "--ogd-focus-ring-halo", "--ogd-media-frame-bg"):
|
|
if token not in light_tokens:
|
|
fail(f"src/tokens/00-light-tokens.css missing shared token {token}")
|
|
sync_script = read("dev/scripts/sync_obsidian_theme.py")
|
|
if "copy_with_fallback" not in sync_script or "retrying with chunk copy" not in sync_script:
|
|
fail("sync_obsidian_theme.py must keep chunk-copy fallback for WinError 483")
|
|
release_check = read("dev/scripts/release_check.py")
|
|
if "--include-sync" not in release_check or "--sync-target" not in release_check:
|
|
fail("release_check.py must expose optional Obsidian sync validation")
|
|
for script in (
|
|
"dev/scripts/new_runtime_evidence.py",
|
|
"dev/scripts/work_summary.py",
|
|
"dev/scripts/build_coverage_priority_plan.py",
|
|
"dev/scripts/build_route_registry_doc.py",
|
|
"dev/scripts/audit_mobile_owner.py",
|
|
"dev/scripts/run_validation.py",
|
|
"dev/scripts/check_release_status.py",
|
|
"dev/scripts/release_publish.py",
|
|
"dev/scripts/cdp_capture.mjs",
|
|
"dev/scripts/test_route_workflow.py",
|
|
"dev/scripts/test_direct_owner_guard.py",
|
|
):
|
|
if not (ROOT / script).is_file():
|
|
fail(f"missing helper script: {script}")
|
|
for script in (
|
|
"dev/scripts/start_work.py",
|
|
"dev/scripts/finish_work.py",
|
|
"dev/scripts/validation_plan.py",
|
|
"dev/scripts/release_preflight.py",
|
|
"dev/scripts/audit_route_registry.py",
|
|
"dev/scripts/audit_owner_risk_contracts.py",
|
|
"dev/scripts/audit_wiki_route_coverage.py",
|
|
"dev/scripts/audit_selector_owner_cheatsheet.py",
|
|
"dev/scripts/audit_runtime_evidence_requirements.py",
|
|
"dev/scripts/promote_evidence.py",
|
|
):
|
|
if not (ROOT / script).is_file():
|
|
fail(f"missing process script: {script}")
|
|
if "last-sync.json" not in sync_script:
|
|
fail("sync_obsidian_theme.py must record dev/TEMP/last-sync.json")
|
|
coverage_plan = read("dev/scripts/build_coverage_priority_plan.py")
|
|
if "runtime-evidence-registry.json" not in coverage_plan or "Runtime evidence" not in coverage_plan:
|
|
fail("build_coverage_priority_plan.py must read runtime evidence registry")
|
|
if "last_sync_summary" not in read("dev/scripts/work_summary.py"):
|
|
fail("work_summary.py must read last sync state")
|
|
if "--evidence" not in read("dev/scripts/new_incident.py"):
|
|
fail("new_incident.py must support --evidence")
|
|
validation_plan = read("dev/scripts/validation_plan.py")
|
|
if "--run-safe" not in validation_plan or "build_release.py" not in validation_plan:
|
|
fail("validation_plan.py must keep bounded --run-safe behavior")
|
|
if "--surface" not in validation_plan or "--full-check" not in validation_plan or "--json" not in validation_plan or "action=\"append\"" not in validation_plan:
|
|
fail("validation_plan.py must support route-aware and full-check planning")
|
|
if "last-validation.json" not in validation_plan or "validation_summary" not in read("dev/scripts/work_summary.py"):
|
|
fail("validation_plan.py/work_summary.py must maintain validation result summaries")
|
|
if "--full-check" not in read("dev/scripts/finish_work.py"):
|
|
fail("finish_work.py must expose --full-check")
|
|
pre_commit = read("dev/scripts/hooks/pre-commit")
|
|
if "validation_plan.py --run-safe" not in pre_commit or "audit_runtime_evidence_requirements.py --strict" not in pre_commit:
|
|
fail("pre-commit hook must use diff-aware validation and strict runtime evidence checks")
|
|
if "--verify-only" not in read("dev/scripts/sync_obsidian_theme.py"):
|
|
fail("sync_obsidian_theme.py must support --verify-only")
|
|
print("OK: helper routes, shared tokens, and MAP stability checks present")
|
|
|
|
|
|
def assert_wiki_schema() -> None:
|
|
problems: list[str] = []
|
|
for path in sorted((ROOT / "dev" / "WIKI" / "RECIPES").glob("*.md")):
|
|
if path.name == "README.md":
|
|
continue
|
|
text = path.read_text(encoding="utf-8")
|
|
for heading in ("## Route", "## Steps", "## Checks"):
|
|
if heading not in text:
|
|
problems.append(f"{path.relative_to(ROOT).as_posix()} missing {heading}")
|
|
for path in sorted((ROOT / "dev" / "WIKI" / "RUNTIME").glob("*.md")):
|
|
if path.name == "README.md":
|
|
continue
|
|
text = path.read_text(encoding="utf-8")
|
|
if "Evidence" not in text and "Capture" not in text:
|
|
problems.append(f"{path.relative_to(ROOT).as_posix()} missing Evidence/Capture section")
|
|
for path in sorted((ROOT / "dev" / "WIKI" / "WORKFLOWS").glob("*.md")):
|
|
text = path.read_text(encoding="utf-8")
|
|
if "dev\\scripts" not in text and "dev/scripts" not in text:
|
|
problems.append(f"{path.relative_to(ROOT).as_posix()} missing script command")
|
|
if problems:
|
|
fail("WIKI schema issues: " + "; ".join(problems))
|
|
print("OK: WIKI recipe/runtime/workflow schema clean")
|
|
|
|
|
|
def assert_runtime_evidence_registry() -> None:
|
|
path = ROOT / "dev" / "WIKI" / "runtime-evidence-registry.json"
|
|
try:
|
|
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
except Exception as exc:
|
|
fail(f"runtime evidence registry invalid JSON: {exc}")
|
|
if payload.get("schema") != "owen-graphite/runtime-evidence-registry/1":
|
|
fail(f"runtime evidence registry unexpected schema: {payload.get('schema')!r}")
|
|
entries = payload.get("entries")
|
|
if not isinstance(entries, list) or not entries:
|
|
fail("runtime evidence registry must contain non-empty entries list")
|
|
seen: set[str] = set()
|
|
problems: list[str] = []
|
|
for index, entry in enumerate(entries):
|
|
if not isinstance(entry, dict):
|
|
problems.append(f"entry {index} must be object")
|
|
continue
|
|
missing = [key for key in ("module", "surface", "status", "decision", "evidence", "summary") if key not in entry]
|
|
if missing:
|
|
problems.append(f"entry {index} missing {', '.join(missing)}")
|
|
continue
|
|
module = str(entry["module"])
|
|
surface = str(entry["surface"])
|
|
status = str(entry["status"])
|
|
decision = str(entry["decision"])
|
|
evidence = str(entry["evidence"])
|
|
summary = str(entry["summary"])
|
|
if module in seen:
|
|
problems.append(f"duplicate module {module}")
|
|
seen.add(module)
|
|
if not module.startswith("src/") or not (ROOT / module).is_file():
|
|
problems.append(f"entry {module} must reference an existing src module")
|
|
if surface not in RUNTIME_EVIDENCE_SURFACES:
|
|
problems.append(f"entry {module} has unknown surface {surface!r}")
|
|
if status not in RUNTIME_EVIDENCE_STATUSES:
|
|
problems.append(f"entry {module} has unknown status {status!r}")
|
|
if decision not in RUNTIME_EVIDENCE_DECISIONS:
|
|
problems.append(f"entry {module} has unknown decision {decision!r}")
|
|
if not evidence.startswith("dev/TEMP/runtime-evidence/") or not evidence.endswith(".json"):
|
|
problems.append(f"entry {module} evidence must point at dev/TEMP/runtime-evidence/*.json")
|
|
if not summary.strip():
|
|
problems.append(f"entry {module} summary must be non-empty")
|
|
if problems:
|
|
fail("runtime evidence registry issues: " + "; ".join(problems))
|
|
print("OK: runtime evidence registry schema clean")
|
|
|
|
|
|
def assert_risk_accepted_registry() -> None:
|
|
path = ROOT / "dev" / "WIKI" / "risk-accepted-registry.json"
|
|
try:
|
|
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
except Exception as exc:
|
|
fail(f"risk accepted registry invalid JSON: {exc}")
|
|
if payload.get("schema") != "owen-graphite/risk-accepted-registry/1":
|
|
fail(f"risk accepted registry unexpected schema: {payload.get('schema')!r}")
|
|
entries = payload.get("entries")
|
|
if not isinstance(entries, list) or not entries:
|
|
fail("risk accepted registry must contain non-empty entries list")
|
|
seen: set[str] = set()
|
|
problems: list[str] = []
|
|
for index, entry in enumerate(entries):
|
|
if not isinstance(entry, dict):
|
|
problems.append(f"entry {index} must be object")
|
|
continue
|
|
required = ("id", "status", "acceptedBy", "acceptedAt", "surface", "module", "risk", "selectorContains", "allowedProperties", "evidence")
|
|
missing = [key for key in required if key not in entry]
|
|
if missing:
|
|
problems.append(f"entry {index} missing {', '.join(missing)}")
|
|
continue
|
|
risk_id = str(entry["id"])
|
|
status = str(entry["status"])
|
|
module = str(entry["module"])
|
|
surface = str(entry["surface"])
|
|
selectors = entry["selectorContains"]
|
|
properties = entry["allowedProperties"]
|
|
evidence = entry["evidence"]
|
|
if risk_id in seen:
|
|
problems.append(f"duplicate risk id {risk_id}")
|
|
seen.add(risk_id)
|
|
if status not in RISK_ACCEPTED_STATUSES:
|
|
problems.append(f"entry {risk_id} has unknown status {status!r}")
|
|
if not module.startswith("src/") or not (ROOT / module).is_file():
|
|
problems.append(f"entry {risk_id} must reference an existing src module")
|
|
if surface not in RUNTIME_EVIDENCE_SURFACES:
|
|
problems.append(f"entry {risk_id} has unknown surface {surface!r}")
|
|
if not isinstance(selectors, list) or not selectors or not all(isinstance(item, str) and item.strip() for item in selectors):
|
|
problems.append(f"entry {risk_id} selectorContains must be a non-empty string list")
|
|
if not isinstance(properties, list) or not properties or not all(isinstance(item, str) and item.strip() for item in properties):
|
|
problems.append(f"entry {risk_id} allowedProperties must be a non-empty string list")
|
|
if not isinstance(evidence, list) or not evidence:
|
|
problems.append(f"entry {risk_id} evidence must be a non-empty list")
|
|
else:
|
|
for item in evidence:
|
|
item_text = str(item)
|
|
if not item_text.startswith("dev/TEMP/runtime-evidence/") or not item_text.endswith(".json"):
|
|
problems.append(f"entry {risk_id} evidence must point at dev/TEMP/runtime-evidence/*.json")
|
|
if problems:
|
|
fail("risk accepted registry issues: " + "; ".join(problems))
|
|
print("OK: risk accepted registry schema clean")
|
|
|
|
|
|
def assert_index_has_no_legacy_routes() -> None:
|
|
index = read("dev/WIKI/INDEX.md")
|
|
offenders = [text for text in FORBIDDEN_INDEX_TEXT if text in index]
|
|
if offenders:
|
|
fail("WIKI index contains legacy routes: " + ", ".join(offenders))
|
|
print("OK: WIKI index has no legacy routes")
|
|
|
|
|
|
def main() -> int:
|
|
try:
|
|
assert_required_files()
|
|
assert_required_text()
|
|
assert_src_docs_have_validation_matrix()
|
|
assert_release_workflow_paths()
|
|
assert_helper_and_generator_stability()
|
|
assert_wiki_schema()
|
|
assert_runtime_evidence_registry()
|
|
assert_risk_accepted_registry()
|
|
assert_index_has_no_legacy_routes()
|
|
print("OK: WIKI consistency audit clean")
|
|
return 0
|
|
except Exception as exc:
|
|
print(f"FAIL: {exc}", file=sys.stderr)
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|