Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions .github/Indices/CAM.Governance.Model-Terminology.Audit.md

Large diffs are not rendered by default.

1,045 changes: 811 additions & 234 deletions .github/Indices/canonical-code-index.json

Large diffs are not rendered by default.

47 changes: 29 additions & 18 deletions .github/Indices/canonical-code-index.md

Large diffs are not rendered by default.

4 changes: 1 addition & 3 deletions .github/scripts/Maintainer.md
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,6 @@ May be replaced only if local references, cross-document references, ambiguous n
Representative scripts:

```text
.github/scripts/build-symbolic-structures-index.py
.github/scripts/build-canonical-code-index.py
.github/scripts/lint-symbolic-structures.py
```
Expand Down Expand Up @@ -622,7 +621,6 @@ The linter should not mutate files.
**Validation Commands:**

```bash
python .github/scripts/build-symbolic-structures-index.py
python .github/scripts/build-canonical-code-index.py
python .github/scripts/build-canonical-code-index.py --check
python .github/scripts/lint-symbolic-structures.py
Expand Down Expand Up @@ -934,7 +932,7 @@ update-CAM-BS2025-AEON-003-SCH-01.py
update-CAM-BS2025-AEON-003-SCH-03.py
validate_markdown_section_refs.py
lint-symbolic-structures.py
build-symbolic-structures-index.py
build-canonical-code-index.py
package_archive.py
```

Expand Down
Binary file not shown.
Binary file modified .github/scripts/lib/__pycache__/instrument_state.cpython-311.pyc
Binary file not shown.
Binary file not shown.
Binary file not shown.
87 changes: 54 additions & 33 deletions .github/scripts/lint-symbolic-structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parents[2]
INDEX_PATH = REPO_ROOT / ".github" / "Indices" / "CAM.Governance.Symbolic-Structures.Index.json"
CANONICAL_CODE_INDEX_PATH = REPO_ROOT / ".github" / "Indices" / "canonical-code-index.json"
REGISTRY_PATH = REPO_ROOT / ".github" / "Indices" / "CAM.Governance.Symbolic-Structures.Registry.json"

REQUIRED_FIELDS = [
Expand All @@ -26,7 +26,7 @@
CODE_TOKEN_RE = re.compile(r"\b([A-Z]{1,6}(?:-[A-Z0-9.]+|[0-9][A-Z0-9.]*)?)\b")


def load_json(path: Path) -> dict:
def load_json(path: Path):
return json.loads(path.read_text(encoding="utf-8"))


Expand Down Expand Up @@ -146,8 +146,10 @@ def lint_canonical_codes(*, registry: dict, enforcement: str, strict_release: bo

def main() -> int:
parser = argparse.ArgumentParser(description="Lint symbolic structures index")
parser.add_argument("--index", default=str(INDEX_PATH))
parser.add_argument("--index", default=str(CANONICAL_CODE_INDEX_PATH))
parser.add_argument("--registry", default=str(REGISTRY_PATH))
parser.add_argument("--require-index", action="store_true", help="Fail when --index path is missing")
parser.add_argument("--require-registry", action="store_true", help="Fail when --registry path is missing")
parser.add_argument("--strict-release", action="store_true", help="Treat registry mismatches/collisions as errors")
parser.add_argument(
"--canonical-codes-enforcement",
Expand All @@ -157,43 +159,62 @@ def main() -> int:
)
args = parser.parse_args()

index = load_json(Path(args.index))
registry = load_json(Path(args.registry))
index_path = Path(args.index)
registry_path = Path(args.registry)

errors: list[str] = []
warnings: list[str] = []

errors.extend([f"Malformed registry config: {e}" for e in validate_registry(registry)])

for row in index.get("candidate_unregistered_prefixes", []):
warnings.append(
f"Candidate unregistered prefix '{row.get('prefix','')}' (confidence={row.get('confidence','low')}, reason={row.get('reason','')})"
)

for row in index.get("potential_duplicate_symbolic_structures", []):
warnings.append(
f"Possible duplicate symbolic structure '{row.get('normalized_title','')}' x{row.get('occurrences',0)}"
)

for row in index.get("possible_collisions_requiring_review", []):
msg = (
f"Collision '{row.get('collision_type','unknown')}' for {row.get('prefix','')}:{row.get('value','')} "
f"(severity={row.get('severity','warning')}, reason={row.get('reason','')})"
)
if args.strict_release:
errors.append(msg)
elif row.get("severity") == "error":
errors.append(msg)
else:
warnings.append(msg)

cc_errors, cc_warnings, cc_infos = lint_canonical_codes(
registry=registry,
index_data = None
if index_path.exists():
index_data = load_json(index_path)
elif args.require_index:
errors.append(f"Required index file not found: {index_path}")
else:
print(f"INFO: index file not found; skipping legacy symbolic index checks: {index_path}")

registry = None
if registry_path.exists():
registry = load_json(registry_path)
errors.extend([f"Malformed registry config: {e}" for e in validate_registry(registry)])
elif args.require_registry:
errors.append(f"Required registry file not found: {registry_path}")
else:
print(f"INFO: registry file not found; deriving validation from canonical code index only: {registry_path}")

if isinstance(index_data, dict):
for row in index_data.get("candidate_unregistered_prefixes", []):
warnings.append(
f"Candidate unregistered prefix '{row.get('prefix','')}' (confidence={row.get('confidence','low')}, reason={row.get('reason','')})"
)

for row in index_data.get("potential_duplicate_symbolic_structures", []):
warnings.append(
f"Possible duplicate symbolic structure '{row.get('normalized_title','')}' x{row.get('occurrences',0)}"
)

for row in index_data.get("possible_collisions_requiring_review", []):
msg = (
f"Collision '{row.get('collision_type','unknown')}' for {row.get('prefix','')}:{row.get('value','')} "
f"(severity={row.get('severity','warning')}, reason={row.get('reason','')})"
)
if args.strict_release:
errors.append(msg)
elif row.get("severity") == "error":
errors.append(msg)
else:
warnings.append(msg)

if registry is not None:
cc_errors, cc_warnings, cc_infos = lint_canonical_codes(
registry=registry,
enforcement=args.canonical_codes_enforcement,
strict_release=args.strict_release,
)
errors.extend(cc_errors)
warnings.extend(cc_warnings)
errors.extend(cc_errors)
warnings.extend(cc_warnings)
else:
cc_infos = ["Canonical codes cross-check skipped (symbolic registry not present)"]

for info in cc_infos:
print(f"INFO: {info}")
Expand Down
38 changes: 38 additions & 0 deletions .github/scripts/tests/test_lint_symbolic_structures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import pathlib
import subprocess
import sys


REPO_ROOT = pathlib.Path(__file__).resolve().parents[3]
SCRIPT = REPO_ROOT / ".github" / "scripts" / "lint-symbolic-structures.py"


def run_lint(*args: str) -> subprocess.CompletedProcess[str]:
return subprocess.run(
[sys.executable, str(SCRIPT), *args],
cwd=REPO_ROOT,
text=True,
capture_output=True,
check=False,
)


def test_missing_deprecated_index_is_non_fatal_when_not_required():
result = run_lint(
"--index",
".github/Indices/CAM.Governance.Symbolic-Structures.Index.json",
"--registry",
".github/Indices/CAM.Governance.Symbolic-Structures.Registry.json",
)
assert result.returncode == 0
assert "skipping legacy symbolic index checks" in result.stdout


def test_missing_deprecated_index_fails_when_explicitly_required():
result = run_lint(
"--index",
".github/Indices/CAM.Governance.Symbolic-Structures.Index.json",
"--require-index",
)
assert result.returncode == 1
assert "Required index file not found" in result.stdout
116 changes: 116 additions & 0 deletions .github/scripts/tests/test_update_sch01_registry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import importlib.util
import pathlib


SCRIPT_PATH = pathlib.Path(__file__).resolve().parents[1] / "update-CAM-BS2025-AEON-003-SCH-01.py"
spec = importlib.util.spec_from_file_location("sch01", SCRIPT_PATH)
sch01 = importlib.util.module_from_spec(spec)
assert spec.loader is not None
spec.loader.exec_module(sch01)


def test_build_rows_includes_all_schedule_suffixes():
items = [
{"id": "CAM-BS2025-AEON-100-SCH-01", "title": "S1", "link": "", "version": "1.0", "status": "Active"},
{"id": "CAM-BS2025-AEON-100-SCH-02", "title": "S2", "link": "", "version": "1.0", "status": "Active"},
{"id": "CAM-BS2025-AEON-100-SCH-03", "title": "S3", "link": "", "version": "1.0", "status": "Active"},
{"id": "CAM-BS2025-AEON-100-PLATINUM", "title": "P", "link": "", "version": "1.0", "status": "Active"},
]

rows = sch01.build_rows(items)
assert [r.instrument_id for r in rows] == [
"CAM-BS2025-AEON-100-SCH-01",
"CAM-BS2025-AEON-100-SCH-02",
"CAM-BS2025-AEON-100-SCH-03",
]


def test_model_term_classification_is_conservative():
assert sch01.classify_model_term("Caelestis Architecture Model") == "Architecture Model"
assert sch01.classify_model_term("Runtime Governance Execution Model") == "Execution Model"
assert sch01.classify_model_term("Integrity State Model") == "Security Model"
assert sch01.classify_model_term("pricing models") == "Generic / Non-Canonical Usage"


def test_model_term_review_status_non_blocking_defaults():
assert sch01.classify_review_status("Execution Model") == "Declared / Recognised"
assert sch01.classify_review_status("Security Model") == "Advisory Review"
assert sch01.classify_review_status("Generic / Non-Canonical Usage") == "Generic Usage"
assert sch01.classify_review_status("Unclassified / Review") == "Needs Review"


def test_strip_generated_blocks_ignores_sch01_generated_regions():
text = """
before Runtime Governance Execution Model
<!-- SCH-01:RUNTIME_REGISTRY:START -->
Runtime Governance Execution Model
<!-- SCH-01:RUNTIME_REGISTRY:END -->
middle
<!-- SCH-01:MODEL_TERMINOLOGY_REGISTER:START -->
Integrity State Model
<!-- SCH-01:MODEL_TERMINOLOGY_REGISTER:END -->
after pricing models
"""
cleaned = sch01.strip_generated_blocks_for_scan(text)
assert "before Runtime Governance Execution Model" in cleaned
assert "after pricing models" in cleaned
assert "<!-- SCH-01:RUNTIME_REGISTRY:START -->" not in cleaned
assert "<!-- SCH-01:MODEL_TERMINOLOGY_REGISTER:START -->" not in cleaned
assert "\nRuntime Governance Execution Model\n" not in cleaned
assert "\nIntegrity State Model\n" not in cleaned


def test_render_model_register_suppresses_generic_usage_rows():
rows = [
sch01.ModelTerminologyItem(
instrument_id="CAM-A",
section_heading="H1",
term_used="pricing models",
suggested_classification="Generic / Non-Canonical Usage",
review_status="Generic Usage",
),
sch01.ModelTerminologyItem(
instrument_id="CAM-B",
section_heading="H2",
term_used="Runtime Governance Execution Model",
suggested_classification="Execution Model",
review_status="Declared / Recognised",
),
sch01.ModelTerminologyItem(
instrument_id="CAM-C",
section_heading="H3",
term_used="Attribution & Dependency Model",
suggested_classification="Economic Model",
review_status="Advisory Review",
),
]
out = sch01.render_model_terminology_register(rows)
assert "**Total model-term matches scanned:** 3" in out
assert "**Generic usages suppressed:** 1" in out
assert "Runtime Governance Execution Model" in out
assert "Attribution & Dependency Model" in out
assert "pricing models" not in out


def test_render_model_summary_has_counts_and_audit_path_only():
rows = [
sch01.ModelTerminologyItem("CAM-A", "H1", "pricing models", "Generic / Non-Canonical Usage", "Generic Usage"),
sch01.ModelTerminologyItem("CAM-B", "H2", "Runtime Governance Execution Model", "Execution Model", "Declared / Recognised"),
]
summary = sch01.render_model_terminology_summary(rows)
assert "**Total model-term matches scanned:** 2" in summary
assert "**Generic usages suppressed:** 1" in summary
assert "Instrument | Section / Heading | Term Used" not in summary
assert ".github/Indices/CAM.Governance.Model-Terminology.Audit.md" in summary


def test_render_model_register_is_deterministic():
rows = [
sch01.ModelTerminologyItem("CAM-B", "H2", "Runtime Governance Execution Model", "Execution Model", "Declared / Recognised"),
sch01.ModelTerminologyItem("CAM-A", "H1", "pricing models", "Generic / Non-Canonical Usage", "Generic Usage"),
sch01.ModelTerminologyItem("CAM-C", "H3", "Attribution & Dependency Model", "Economic Model", "Advisory Review"),
]
out1 = sch01.render_model_terminology_register(rows)
out2 = sch01.render_model_terminology_register(rows)
assert out1 == out2
assert "pricing models" not in out1
Loading