mirror of
https://github.com/towishy/Owen-Graphite.git
synced 2026-07-22 04:40:30 +00:00
346 lines
12 KiB
Python
346 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
"""Audit consistency of Owen Graphite WIKI operating rules."""
|
|
|
|
from __future__ import annotations
|
|
|
|
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/theme-css-risk-summary.json",
|
|
"dev/WIKI/runtime-evidence-template.md",
|
|
"dev/WIKI/runtime-evidence-storage.md",
|
|
"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/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/WORKFLOWS/release.md",
|
|
"dev/WIKI/WORKFLOWS/validation-matrix.md",
|
|
]
|
|
|
|
REQUIRED_TEXT = {
|
|
".github/copilot-instructions.md": [
|
|
"consult `dev/WIKI` first",
|
|
"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-storage.md",
|
|
"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",
|
|
"SRC/validation-matrix.md",
|
|
"TOKENS/usage-guide.md",
|
|
"PLUGINS/compatibility-matrix.md",
|
|
"RUNTIME/README.md",
|
|
"RECIPES/README.md",
|
|
"INCIDENTS/incident-template.md",
|
|
"INCIDENTS/taxonomy.md",
|
|
"dev/scripts/wiki_route.py",
|
|
"dev/scripts/start_work.py",
|
|
"dev/scripts/finish_work.py",
|
|
"dev/scripts/validation_plan.py",
|
|
"dev/scripts/new_runtime_evidence.py",
|
|
"dev/scripts/new_incident.py",
|
|
"dev/scripts/work_summary.py",
|
|
"dev/scripts/audit_mobile_owner.py",
|
|
"dev/scripts/audit_wiki_route_coverage.py",
|
|
"dev/scripts/audit_selector_owner_cheatsheet.py",
|
|
"dev/scripts/audit_runtime_evidence_requirements.py",
|
|
"PROMPTS/work-summary.md",
|
|
"WORKFLOWS/validation-matrix.md",
|
|
],
|
|
"dev/WIKI/audits.md": [
|
|
"wiki_route.py --list",
|
|
"wiki_route.py mobile",
|
|
"validation_plan.py --run-safe",
|
|
"audit_selector_owner_cheatsheet.py",
|
|
"audit_wiki_consistency.py",
|
|
],
|
|
"dev/WIKI/MAP/diff-stability.md": [
|
|
"sorted keys",
|
|
"theme-css-risk-summary.json",
|
|
"selector-key order",
|
|
"fix the generator ordering",
|
|
],
|
|
"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>`",
|
|
"gh release view <version>",
|
|
],
|
|
"dev/WIKI/OWNER-DECISION-TREE.md": [
|
|
"runtime evidence",
|
|
"Obsidian core",
|
|
"SELECTOR-OWNER-CHEATSHEET.md",
|
|
"If ownership is still unclear",
|
|
],
|
|
"dev/WIKI/SELECTOR-OWNER-CHEATSHEET.md": [
|
|
".cm-table-widget",
|
|
"Obsidian core",
|
|
"OWNER-DECISION-TREE.md",
|
|
],
|
|
"dev/WIKI/runtime-evidence-template.md": [
|
|
"Required Evidence",
|
|
"Matched rules",
|
|
"If this template cannot be filled",
|
|
],
|
|
"dev/WIKI/runtime-evidence-storage.md": [
|
|
"dev/TEMP/runtime-evidence",
|
|
"new_runtime_evidence.py",
|
|
"--strict",
|
|
"Permanent Evidence",
|
|
"Minimum Metadata",
|
|
],
|
|
"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",
|
|
"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",
|
|
],
|
|
"dev/WIKI/PROMPTS/work-summary.md": [
|
|
"WIKI consulted",
|
|
"Owner modules changed",
|
|
"work_summary.py",
|
|
"--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",
|
|
],
|
|
"dev/WIKI/INCIDENTS/taxonomy.md": [
|
|
"runtime-selected-state",
|
|
"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/",
|
|
]
|
|
|
|
|
|
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")
|
|
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", "docs", "release"]
|
|
missing_routes = [route for route in required_routes if f'"{route}"' not in wiki_route]
|
|
if missing_routes:
|
|
fail("wiki_route.py missing routes: " + ", ".join(missing_routes))
|
|
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/audit_mobile_owner.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_wiki_route_coverage.py",
|
|
"dev/scripts/audit_selector_owner_cheatsheet.py",
|
|
"dev/scripts/audit_runtime_evidence_requirements.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")
|
|
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_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_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())
|