#!/usr/bin/env python3
"""Verify the TEACH educator Hermes functional-build kit.

This standard-library verifier treats the download as a supply-chain and
implementation-contract artifact.  Passing it never means that Hermes has
built, installed, institutionally approved, or operationally tested the
target application.
"""

from __future__ import annotations

import argparse
import csv
import hashlib
import json
import os
import re
import stat
import sys
import unicodedata
import uuid
import zipfile
from datetime import datetime
from pathlib import Path, PurePosixPath
from typing import Any


SCRIPT = Path(__file__).resolve()
PACKAGE_NAME = "TEACH-Nurse-Educator-Instructional-Designer-Mission-Control-Hermes-Build-Kit-v1.0.0"
PACKAGE_ALIAS = "TEACH-Nurse-Educator-Instructional-Designer-Mission-Control-Hermes-Build-Kit.zip"
EXTRACTED = SCRIPT.parent.name == "tools" and (SCRIPT.parents[1] / "README-FIRST.md").is_file()
WORKSPACE = None if EXTRACTED else SCRIPT.parents[1]
DEFAULT_PACKAGE = SCRIPT.parents[1] if EXTRACTED else SCRIPT.parents[1] / "deliverables" / PACKAGE_NAME

BUILD_ID = "NAIO-NEID-TEACH-FUNCTIONAL-BUILD-KIT-1.0.0"
PRODUCT = "TEACH — Nurse Educator & Instructional Designer Mission Control"
PRODUCT_ID = "nurse-educator-instructional-designer-mission-control"
LANE = "nurse_educator_instructional_designer"
ROUTE = "/nurse-educators-designers/dashboard"
NAMESPACE = "neid_teach.*"
HOME = "My TEACH Command Studio"
READINESS = "not_operational_build_required"

EXPECTED_MATRIX_ROWS = 216
EXPECTED_INTEGRATION_ROWS = 48
EXPECTED_TARGET_ROWS = 264
EXPECTED_CANONICAL_ROWS = 169
EXPECTED_TOTAL_EXECUTION_RECORDS = 433

# These are patched only when the frozen authored contracts change.  They are
# intentionally independent of RELEASE-MANIFEST.json so changing a contract
# and regenerating the manifest cannot make an altered kit self-validating.
EXPECTED_CONTROL_ID_DIGEST = "2f1ae6e99e36903122e2356707f87659087bebbc06de142d7308d2762a4d975e"
EXPECTED_MATRIX_SEMANTIC_DIGEST = "fc4e7735322374c94d9349644a705d8ab6c74bd981e24085954f637541122168"
EXPECTED_INTEGRATION_SEMANTIC_DIGEST = "360b7ff9cf91d0b07d0123c32fc40def4eab66c299177e9b6c57b0b5b585b842"
EXPECTED_CAPABILITY_CONFIG_DIGEST = "a54a14f125bfddeb172e98b6011c015798aca074e97f9a5ce19ff2544c9d3be1"
EXPECTED_SOURCE_REGISTRY_DIGEST = "0e0732eb8897a29e58c6726addac94863072616ec181880570cb461b452174e0"

EXPECTED_MATRIX_COLUMNS = (
    "control_id", "screen", "control", "intended_behavior",
    "implementation_target", "persisted_data", "required_permission",
    "offline_behavior", "error_behavior", "verification_test", "status",
)
EXPECTED_CONTROL_LEDGER_COLUMNS = (
    "Test ID", "Area", "Priority", "Verification target",
    "Required evidence", "Result",
)
EXPECTED_INTEGRATION_LEDGER_COLUMNS = (
    "Test ID", "Scenario", "Priority", "Expected result",
    "Required evidence", "Result",
)
CRITICAL_CONTROL_PREFIXES = {
    "AGENT", "AI", "AUTH", "CTX", "EVID", "GOV", "MEM", "PWR", "REC", "TEACH",
}
CRITICAL_CONTROL_IDS = {"APP-004", "DIAG-008", "PKG-005", "PKG-006", "PKG-010"}

TEACH_CANONICAL_SECTIONS = (
    "Installation and recovery",
    "Role, authority, and human decisions",
    "Privacy, security, and context separation",
    "Evidence, sources, and truth",
    "Accessibility, inclusion, and learner dignity",
    "Learning design and engagement",
    "Assessment integrity and fairness",
    "Feedback and learner support",
    "Clinical, skills-lab, and simulation",
    "Copyright, authorship, and scholarship",
    "AI literacy and responsible use",
    "Learning technology and automation",
    "Curriculum, accreditation, and program evaluation",
    "Dashboard, workload, career, and whole life",
    "Outcomes, stewardship, and honest claims",
)

EXPECTED_AGENTS = {
    "TEACH-AGT-01": "Educator Life, Capacity & Career Planning Coach",
    "TEACH-AGT-02": "Learning Design & Alignment Architect",
    "TEACH-AGT-03": "Accessibility, Inclusion & Cognitive-Load Reviewer",
    "TEACH-AGT-04": "Assessment, Rubric & Feedback Design Coach",
    "TEACH-AGT-05": "Learner Communication & Human-Support Rehearsal Coach",
    "TEACH-AGT-06": "Clinical, Skills-Lab & Simulation Safety and Source Scout",
    "TEACH-AGT-07": "Curriculum, Competency & Accreditation Evidence Mapper",
    "TEACH-AGT-08": "Sources, Rights, OER & Scholarship Integrity Librarian",
    "TEACH-AGT-09": "AI Literacy & Governed Learning-Technology Workflow Designer",
    "TEACH-AGT-10": "Independent Agent Auditor & Kill Sentinel",
}

EXPECTED_EVIDENCE_TYPES = [
    "evaluated_mission", "reviewed_artifact", "source_rights_audit",
    "correction_record", "minimized_process_reflection",
    "assessment_design_record", "safety_drill", "supervised_workflow_run",
    "accessibility_review", "agent_run_receipt", "validated_project_outcome",
]
EXPECTED_EVIDENCE_EXCLUSIONS = [
    "synthetic_or_starter_activity",
    "clicks_page_views_or_time_in_app",
    "ai_self_assessment_or_praise",
    "deleted_expired_superseded_revoked_or_conflicted_evidence",
    "unreviewed_evidence_when_review_is_required",
    "phi_patient_event_or_identifiable_clinical_information",
    "live_care_or_clinical_decision_activity",
    "student_record_submission_grade_feedback_or_identifiable_learner_information",
    "secure_exam_item_bank_answer_key_or_restricted_assessment_content",
    "fabricated_outcome_measure_authorship_hour_credential_or_evidence",
    "restricted_employer_course_lms_personnel_research_or_secret_information",
    "private_wellbeing_relationship_financial_health_schedule_transport_caregiving_or_life_information_in_any_badge_evidence",
    "unbound_or_unauthorized_institutional_education_activity",
    "person_level_learner_workforce_or_program_data",
    "external_release_action_or_production_automation",
]
EXPECTED_RECOMPUTE_TRIGGERS = [
    "correction", "deletion", "expiry", "supersession", "revocation",
    "criteria_version_change", "integrity_rights_or_authority_conflict",
]

PROMPT_SHA = "ce884cfc6345cc93a6baec0852cf44feb44d859234ef1dc2b5ac2138df18ebe2"
BASELINE_ZIP_SHA = "69a4dd86659b41136ce9ceb2ceb52512ab567637ab0be29fdc6f6ab6573223c1"
BASE_PROGRAM_SHA = "ee424bcd15c66ee01b706ce5697d7f188d3f49562aa79a43093374d899487e2f"
BASE_GUIDE_SHA = "c5ddfac2c161161257603d9e4d91c68226a921e350c70a14f895222ee684481d"
LEGACY_ZIP_SHA = "841e4b363d1de91560a786e37811c28011b3ee33741e8bfebe1670d30a3e8c92"
BASELINE_MANIFEST_SHA = "22fed55fd789c933f4e7eb50266f88f8dda47f9fba91ebbe9c2cbe4f0ab99702"
BASELINE_CHECKSUMS_SHA = "e178993d6185b126cc915f0d3026ddb3b07e7c5c7dd432aaf581152db8a5a40e"

TEACH_TREE_HASHES = {
    "README.md": "c88eff8b0a32903ca51ab6d4e4a0e4d956e307cb85adfa4d432271e1e3fe2dec",
    "core/00-Educator-Learner-and-Institution-Trust-Shield.md": "dd45ba8ae9d28868413591cc9614d57e21cbd50de28afa7b74559f3942c97689",
    "core/01-TEACH-Operating-Core.md": "bb04992c24ee044bd077b94c4df6bd9ca2b60169a2290e72a6f0fa8e14856174",
    "manifest.md": "f6f98be6da742ab712c7e0f5f8f4c5498326c20b36b911cd2dba34ce97fea82b",
    "teach/01-T-Transform-Learning.md": "339bb3ee9024af3cb7d8ab3c41fa87f96876bc7cd86192aeda9e639a3e15bcc0",
    "teach/02-E-Engage-Every-Learner.md": "ce1a714fdd639878ecd7a17d5d2e10ac96a876ee70b24c71f2d20446c55af82d",
    "teach/03-A-Assess-Authentically.md": "376833a71fbb6d08694cb71c26fb294ffae70356efeeebcae41b9223a7396488",
    "teach/04-C-Curate-Safely.md": "72d0fa06ff6f6e3eaf04ebc8a35167dbb882d71074d530357b3abb2523da7b10",
    "teach/05-H-Harness-AI-and-Sustain-Humans.md": "e090970c015ee93f10e17781d349fd010e8194ffac537a0a16a0a3807779c4cc",
    "templates/TEACH-Cards-and-Templates.md": "19e6679d88aa08a810ad6fad343d514b939fc68fc397248fc19e0002992741ae",
    "tests/Acceptance-Tests.md": "e7c48a67993d2806fd3342b4f2153e8a0fcf65f0e39a1b570b63bdb6d5f2bb49",
    "workflows/TEACH-Command-Studio.md": "f7ecf39fdd6634c764ff488d7b58f00b685eabf9714c037608154a32bc6b4c76",
    "workflows/TEACH-Launch-and-Impact-Plan.md": "a343cbc9ac5ce80d75e783da1c4d8890dd2842c8f70148aa72bb643bfcf74cd6",
}
TEACH_TREE_DIGEST = "b2738d9abedafe0c6b3ebe0ba05c8241b57bb316badd42ae2da482f5ddf5711e"

BASE_PROGRAM = Path("source/base-reference/Nursing-Educator-Life-and-Practice-Hermes-Program.md")
BASE_GUIDE = Path("source/base-reference/Nursing-Educator-AI-OS-Life-and-Practice-Guide.docx")
PROMPT = Path("source/original-functional-build-master-prompt.md")
BASELINE = Path("source/baseline-application")
BASELINE_ARCHIVE = Path("source/archives/DISCOVER-Nurse-AI-OS-Mission-Control-v2.0.0.zip")
TEACH_SOURCE = Path("source/teach-domain-pack")
LEGACY = Path("source/legacy-reference")
LEGACY_ARCHIVE = Path("source/archives/Nurse-Educator-and-Instructional-Designer-Complete-AI-OS-with-TEACH-SuperPowers-Package-v1.0.zip")
LEGACY_FILES = (
    "Nurse-Educator-and-Instructional-Designer-Complete-AI-OS-with-TEACH-SuperPowers-Hermes-Program.md",
    "Nurse-Educator-and-Instructional-Designer-Complete-AI-OS-with-TEACH-SuperPowers-Setup-Guide.md",
    "Nurse-Educator-and-Instructional-Designer-Complete-AI-OS-with-TEACH-SuperPowers-Setup-Guide.docx",
)

EXPECTED_SOURCE_RECOMMENDATIONS = {
    "NEID-SRC-01": (BASE_PROGRAM.as_posix(), BASE_PROGRAM_SHA),
    "NEID-SRC-02": (BASE_GUIDE.as_posix(), BASE_GUIDE_SHA),
    "NEID-SRC-03": ("source/teach-domain-pack/core/00-Educator-Learner-and-Institution-Trust-Shield.md", TEACH_TREE_HASHES["core/00-Educator-Learner-and-Institution-Trust-Shield.md"]),
    "NEID-SRC-04": ("source/teach-domain-pack/core/01-TEACH-Operating-Core.md", TEACH_TREE_HASHES["core/01-TEACH-Operating-Core.md"]),
    "NEID-SRC-05": ("source/teach-domain-pack/teach/01-T-Transform-Learning.md", TEACH_TREE_HASHES["teach/01-T-Transform-Learning.md"]),
    "NEID-SRC-06": ("source/teach-domain-pack/teach/02-E-Engage-Every-Learner.md", TEACH_TREE_HASHES["teach/02-E-Engage-Every-Learner.md"]),
    "NEID-SRC-07": ("source/teach-domain-pack/teach/03-A-Assess-Authentically.md", TEACH_TREE_HASHES["teach/03-A-Assess-Authentically.md"]),
    "NEID-SRC-08": ("source/teach-domain-pack/teach/04-C-Curate-Safely.md", TEACH_TREE_HASHES["teach/04-C-Curate-Safely.md"]),
    "NEID-SRC-09": ("source/teach-domain-pack/teach/05-H-Harness-AI-and-Sustain-Humans.md", TEACH_TREE_HASHES["teach/05-H-Harness-AI-and-Sustain-Humans.md"]),
    "NEID-SRC-10": ("source/teach-domain-pack/workflows/TEACH-Launch-and-Impact-Plan.md", TEACH_TREE_HASHES["workflows/TEACH-Launch-and-Impact-Plan.md"]),
}

REQUIRED = (
    ".env.example", "README-FIRST.md", "README.md", "START_HERE.md",
    "GIVE-THIS-PACKAGE-TO-HERMES.md", "BUILD-STATUS.md", "INSTALL.md",
    "OPERATOR_HANDOFF.md", "PROCESSING_MESSAGE.md", "INPUT-PRECEDENCE.md",
    "SOURCE-NOTES.md", "CHANGELOG.md", "LICENSE-NOTICE.md", "VERSION",
    "RELEASE-MANIFEST.json", "SHA256SUMS.txt", "SOURCE-INVENTORY.json",
    "implementation/EDUCATOR-Functional-Build-Master-Prompt.md",
    "implementation/EDUCATOR-Product-Specification.md",
    "implementation/EDUCATOR-Architecture-and-Data-Model.md",
    "implementation/EDUCATOR-Governance-EDENA-and-Data-Boundaries.md",
    "implementation/EDUCATOR-Agent-Team-and-Routing.md",
    "implementation/EDUCATOR-Personalization-Mapping-Crosswalk.md",
    "implementation/EDUCATOR-Starter-Workspace-Crosswalk.md",
    "implementation/EDUCATOR-Capability-and-Badge-Evidence-Model.md",
    "implementation/EDUCATOR-Guide-Page-Content.md",
    "implementation/EDUCATOR-Baseline-Gap-Report.md",
    "implementation/EDUCATOR-Technical-Implementation-Guide.md",
    "implementation/EDUCATOR-User-Installation-Guide.md",
    "implementation/EDUCATOR-Security-and-Privacy-Checklist.md",
    "implementation/EDUCATOR-Synthetic-Sample-Mission.md",
    "implementation/EDUCATOR-Control-Completeness-Matrix.csv",
    "implementation/EDUCATOR-Acceptance-and-Test-Ledger.md",
    "implementation/GAP_ANALYSIS.md",
    "implementation/HERMES-FINAL-HANDOFF-REPORT-TEMPLATE.md",
    "personalization/README.md",
    "personalization/EDUCATOR-Discover-Packet.synthetic.example.json",
    "personalization/EDUCATOR-Soul-Profile.synthetic.example.json",
    "personalization/EDUCATOR-Mission-Profile.synthetic.example.json",
    "personalization/input-schemas/discover-packet-input.schema.json",
    "personalization/input-schemas/soul-profile-input.schema.json",
    "schemas/EDUCATOR-Discover-Packet.schema.json",
    "schemas/EDUCATOR-Soul-Profile.schema.json",
    "schemas/EDUCATOR-Mission-Profile.schema.json",
    "config/EDUCATOR-Capability-Mastery-Criteria.v1.json",
    "config/EDUCATOR-Source-Recommendation-Registry.v1.json",
    PROMPT.as_posix(), BASE_PROGRAM.as_posix(), BASE_GUIDE.as_posix(),
    BASELINE_ARCHIVE.as_posix(), LEGACY_ARCHIVE.as_posix(),
    "tools/verify-build-kit.py",
    *(f"source/legacy-reference/{name}" for name in LEGACY_FILES),
    *(f"source/teach-domain-pack/{name}" for name in TEACH_TREE_HASHES),
)

PLACEHOLDERS = (
    "[PACKAGE_NAME]", "[SOURCE_DIRECTORY_OR_REPOSITORY]",
    "[DISCOVER_PACKET_PATH_OR_SOURCE]", "[SOUL_QUIZ_RESULTS_PATH_OR_SOURCE]",
    "[HERMES_PROFILE_NAME_OR_AUTO_DETECT]", "[USER_TYPE]", "[ROLE_LANES]",
    "[DOMAIN]", "[AGENT_LIST]", "[DATA_CLASSIFICATION]",
    "[DOMAIN_GUARDRAILS]", "[MACOS_WINDOWS_LINUX]",
    "[ZIP_INSTALLER_DESKTOP_APP_LOCAL_WEB_APP]",
)


class Checks:
    def __init__(self) -> None:
        self.passed: list[str] = []
        self.failed: list[str] = []
        self.warnings: list[str] = []

    def check(self, condition: bool, label: str, detail: Any = None) -> bool:
        if condition:
            self.passed.append(label)
            print(f"PASS  {label}")
            return True
        suffix = "" if detail in (None, "", [], {}) else f" — {detail}"
        self.failed.append(label + suffix)
        print(f"FAIL  {label}{suffix}")
        return False

    def warn(self, label: str, detail: Any = None) -> None:
        suffix = "" if detail in (None, "", [], {}) else f" — {detail}"
        self.warnings.append(label + suffix)
        print(f"WARN  {label}{suffix}")


def sha256(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as stream:
        for chunk in iter(lambda: stream.read(1024 * 1024), b""):
            digest.update(chunk)
    return digest.hexdigest()


def normalized_digest(value: Any) -> str:
    payload = json.dumps(value, ensure_ascii=False, separators=(",", ":"), sort_keys=True)
    return hashlib.sha256((payload + "\n").encode()).hexdigest()


def strict_json_loads(text: str) -> Any:
    def pairs_hook(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
        result: dict[str, Any] = {}
        for key, value in pairs:
            if key in result:
                raise ValueError(f"duplicate JSON key: {key!r}")
            result[key] = value
        return result

    def reject_constant(value: str) -> Any:
        raise ValueError(f"non-finite JSON number: {value}")

    return json.loads(text, object_pairs_hook=pairs_hook, parse_constant=reject_constant)


def safe_name(name: str) -> bool:
    if not name or "\x00" in name or "\\" in name or name.startswith("/") or "//" in name:
        return False
    candidate = name[:-1] if name.endswith("/") else name
    if not candidate:
        return False
    parts = candidate.split("/")
    pure = PurePosixPath(candidate)
    return (
        all(part not in {"", ".", ".."} and ":" not in part for part in parts)
        and not pure.is_absolute() and pure.parts == tuple(parts)
    )


def files(root: Path) -> dict[str, Path]:
    if not root.is_dir():
        return {}
    return {
        path.relative_to(root).as_posix(): path
        for path in sorted(root.rglob("*"))
        if path.is_file() and not path.is_symlink()
    }


def tree_digest(hashes: dict[str, str]) -> str:
    payload = "".join(f"{digest}  {name}\n" for name, digest in sorted(hashes.items()))
    return hashlib.sha256(payload.encode()).hexdigest()


def load_json(c: Checks, path: Path, label: str) -> Any | None:
    try:
        value = strict_json_loads(path.read_text(encoding="utf-8"))
    except Exception as error:
        c.check(False, label, error)
        return None
    c.check(True, label)
    return value


def parse_checksums(path: Path) -> tuple[dict[str, str], list[str]]:
    result: dict[str, str] = {}
    errors: list[str] = []
    folded: set[str] = set()
    for number, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
        digest, separator, relative = line.partition("  ")
        key = unicodedata.normalize("NFC", relative).casefold()
        if (
            separator != "  " or not re.fullmatch(r"[0-9a-f]{64}", digest)
            or not safe_name(relative) or relative.endswith("/")
            or relative in result or key in folded
        ):
            errors.append(f"line {number}")
            continue
        folded.add(key)
        result[relative] = digest
    return result, errors


def markdown_table_rows(text: str) -> list[list[str]]:
    rows: list[list[str]] = []
    for line in text.splitlines():
        stripped = line.strip()
        if stripped.startswith("|") and stripped.endswith("|"):
            rows.append([cell.strip() for cell in stripped[1:-1].split("|")])
    return rows


def expected_control_priority(control_id: str) -> str:
    prefix = control_id.partition("-")[0]
    return "Critical" if prefix in CRITICAL_CONTROL_PREFIXES or control_id in CRITICAL_CONTROL_IDS else "Required"


def expected_canonical_rows() -> list[list[str]]:
    foundation_sections = (
        (range(1, 6), "Functional", 0),
        (range(6, 11), "Safety and authority", 5),
        (range(11, 17), "Privacy and isolation", 10),
        (range(17, 21), "Assessment fairness", 16),
        (range(21, 25), "Evidence and sources", 20),
        (range(25, 30), "Accessibility and usability", 24),
        (range(30, 34), "Whole-life", 29),
    )
    rows = [
        [f"FOUNDATION-{index:03d}", "Foundation", f"`{BASE_PROGRAM.name}` → Acceptance tests → {section} → source-order bullet {index - offset}", "Not Run"]
        for indices, section, offset in foundation_sections for index in indices
    ]
    rows.extend(
        [f"TEACH-{letter}{index}", "TEACH overlay", f"`tests/Acceptance-Tests.md` → {section} → source ID {letter}{index}", "Not Run"]
        for letter, section in zip("ABCDEFGHIJKLMNO", TEACH_CANONICAL_SECTIONS)
        for index in range(1, 9)
    )
    rows.extend(
        [f"INTEGRATION-INT{index:02d}", "Complete Edition integration", f"`tests/Acceptance-Tests.md` → source ID INT{index:02d}", "Not Run"]
        for index in range(1, 17)
    )
    return rows


def json_type_matches(value: Any, expected: str) -> bool:
    if expected == "object":
        return isinstance(value, dict)
    if expected == "array":
        return isinstance(value, list)
    if expected == "string":
        return isinstance(value, str)
    if expected == "boolean":
        return isinstance(value, bool)
    if expected == "null":
        return value is None
    if expected == "integer":
        return isinstance(value, int) and not isinstance(value, bool)
    if expected == "number":
        return isinstance(value, (int, float)) and not isinstance(value, bool)
    return True


def resolve_ref(root: dict[str, Any], reference: str) -> Any:
    if not reference.startswith("#/"):
        raise ValueError(f"external or malformed $ref is unsupported: {reference}")
    value: Any = root
    for raw in reference[2:].split("/"):
        key = raw.replace("~1", "/").replace("~0", "~")
        if not isinstance(value, dict) or key not in value:
            raise ValueError(f"unresolved $ref: {reference}")
        value = value[key]
    return value


def validate_json_schema(instance: Any, schema: dict[str, Any]) -> list[str]:
    """Validate the schema subset used by the three bundled adapters."""

    errors: list[str] = []

    def walk(value: Any, rule: Any, path: str, depth: int = 0) -> None:
        if depth > 100:
            errors.append(f"{path}: schema recursion limit exceeded")
            return
        if isinstance(rule, bool):
            if not rule:
                errors.append(f"{path}: disallowed by schema")
            return
        if not isinstance(rule, dict):
            errors.append(f"{path}: malformed schema node")
            return
        if "$ref" in rule:
            try:
                target = resolve_ref(schema, rule["$ref"])
            except Exception as error:
                errors.append(f"{path}: {error}")
                return
            walk(value, target, path, depth + 1)
            siblings = {key: item for key, item in rule.items() if key != "$ref"}
            if siblings:
                walk(value, siblings, path, depth + 1)
            return
        if "const" in rule and value != rule["const"]:
            errors.append(f"{path}: expected const {rule['const']!r}")
        if "enum" in rule and value not in rule["enum"]:
            errors.append(f"{path}: value is outside enum")
        if "not" in rule:
            before = len(errors)
            walk(value, rule["not"], path, depth + 1)
            matched_forbidden_schema = len(errors) == before
            del errors[before:]
            if matched_forbidden_schema:
                errors.append(f"{path}: value matches a forbidden schema")
        expected_type = rule.get("type")
        if expected_type is not None:
            choices = expected_type if isinstance(expected_type, list) else [expected_type]
            if not any(json_type_matches(value, item) for item in choices):
                errors.append(f"{path}: expected type {expected_type!r}")
                return
        for branch in rule.get("allOf", []):
            walk(value, branch, path, depth + 1)
        if "anyOf" in rule:
            branch_errors: list[list[str]] = []
            for branch in rule["anyOf"]:
                before = len(errors)
                walk(value, branch, path, depth + 1)
                branch_errors.append(errors[before:])
                del errors[before:]
            if all(branch_errors):
                errors.append(f"{path}: no anyOf branch matched")
        if "oneOf" in rule:
            matches = 0
            for branch in rule["oneOf"]:
                before = len(errors)
                walk(value, branch, path, depth + 1)
                if len(errors) == before:
                    matches += 1
                else:
                    del errors[before:]
            if matches != 1:
                errors.append(f"{path}: expected one oneOf match, observed {matches}")
        if "if" in rule:
            before = len(errors)
            walk(value, rule["if"], path, depth + 1)
            condition = len(errors) == before
            del errors[before:]
            branch = rule.get("then") if condition else rule.get("else")
            if branch is not None:
                walk(value, branch, path, depth + 1)
        if isinstance(value, dict):
            required = rule.get("required", [])
            for key in required:
                if key not in value:
                    errors.append(f"{path}: missing required property {key!r}")
            properties = rule.get("properties", {})
            patterns = rule.get("patternProperties", {})
            for key, item in value.items():
                matched = False
                if key in properties:
                    walk(item, properties[key], f"{path}.{key}", depth + 1)
                    matched = True
                for pattern, branch in patterns.items():
                    if re.search(pattern, key):
                        walk(item, branch, f"{path}.{key}", depth + 1)
                        matched = True
                if not matched:
                    additional = rule.get("additionalProperties", True)
                    if additional is False:
                        errors.append(f"{path}: unknown property {key!r}")
                    elif isinstance(additional, dict):
                        walk(item, additional, f"{path}.{key}", depth + 1)
            minimum = rule.get("minProperties")
            maximum = rule.get("maxProperties")
            if minimum is not None and len(value) < minimum:
                errors.append(f"{path}: too few properties")
            if maximum is not None and len(value) > maximum:
                errors.append(f"{path}: too many properties")
        if isinstance(value, list):
            if "minItems" in rule and len(value) < rule["minItems"]:
                errors.append(f"{path}: too few items")
            if "maxItems" in rule and len(value) > rule["maxItems"]:
                errors.append(f"{path}: too many items")
            if rule.get("uniqueItems"):
                frozen = [json.dumps(item, sort_keys=True, ensure_ascii=False) for item in value]
                if len(frozen) != len(set(frozen)):
                    errors.append(f"{path}: items are not unique")
            prefix_rules = rule.get("prefixItems", [])
            if isinstance(prefix_rules, list):
                for index, prefix_rule in enumerate(prefix_rules[:len(value)]):
                    walk(value[index], prefix_rule, f"{path}[{index}]", depth + 1)
            item_rule = rule.get("items")
            item_start = len(prefix_rules) if isinstance(prefix_rules, list) else 0
            if isinstance(item_rule, (dict, bool)):
                for index in range(item_start, len(value)):
                    walk(value[index], item_rule, f"{path}[{index}]", depth + 1)
            if "contains" in rule:
                matched = 0
                for item in value:
                    before = len(errors)
                    walk(item, rule["contains"], path, depth + 1)
                    if len(errors) == before:
                        matched += 1
                    else:
                        del errors[before:]
                minimum = rule.get("minContains", 1)
                maximum = rule.get("maxContains")
                if matched < minimum or (maximum is not None and matched > maximum):
                    errors.append(f"{path}: contains matched {matched}")
        if isinstance(value, str):
            if "minLength" in rule and len(value) < rule["minLength"]:
                errors.append(f"{path}: string is too short")
            if "maxLength" in rule and len(value) > rule["maxLength"]:
                errors.append(f"{path}: string is too long")
            if "pattern" in rule:
                try:
                    matched = re.search(rule["pattern"], value)
                except re.error as error:
                    errors.append(f"{path}: invalid schema pattern: {error}")
                else:
                    if not matched:
                        errors.append(f"{path}: string does not match pattern")
            if rule.get("format") == "uuid":
                try:
                    parsed = uuid.UUID(value)
                except (AttributeError, ValueError):
                    errors.append(f"{path}: string is not a valid UUID")
                else:
                    if str(parsed) != value.casefold():
                        errors.append(f"{path}: UUID is not in canonical hyphenated form")
            elif rule.get("format") == "date-time":
                candidate = value[:-1] + "+00:00" if value.endswith("Z") else value
                try:
                    parsed_datetime = datetime.fromisoformat(candidate)
                except ValueError:
                    errors.append(f"{path}: string is not a valid RFC 3339 date-time")
                else:
                    if parsed_datetime.tzinfo is None:
                        errors.append(f"{path}: date-time must include a UTC offset")

    walk(instance, schema, "$")
    return errors


def check_controlled_placeholders(c: Checks, package: Path) -> None:
    excluded = {"source", "RELEASE-MANIFEST.json", "SHA256SUMS.txt"}
    unresolved: list[str] = []
    residue: list[str] = []
    stale_tokens = (
        "staff_nurse_life_practice", "/staff-nurses/dashboard", "/educators/dashboard",
        "snqi_shift.*", "NAIO-SNQI", "NAIO-SN-LPEF", "SNLP-SRC-",
        "SHIFT-AGT-", "SHIFT-PWR-", "SHIFT-WF-", "SHIFT-TPL-",
        "Staff-Nurse-Life-and-Practice", "Private Personal Mode",
        "Institution-Approved Quality Workspace",
    )
    for relative, path in files(package).items():
        if relative.split("/", 1)[0] in excluded or path.suffix.lower() not in {".md", ".json", ".csv", ".example"}:
            continue
        try:
            text = path.read_text(encoding="utf-8")
        except UnicodeDecodeError:
            continue
        for placeholder in PLACEHOLDERS:
            if placeholder in text:
                unresolved.append(f"{relative}:{placeholder}")
        if re.search(r"(?im)^\s*(?:[-*]\s*)?(?:TODO|TBD|FIXME)(?:\b|:)", text):
            unresolved.append(f"{relative}:TODO/TBD/FIXME")
        for token in stale_tokens:
            if token in text:
                residue.append(f"{relative}:{token}")
    c.check(not unresolved, "Controlled contracts contain no unresolved placeholders", unresolved[:30])
    c.check(not residue, "Controlled contracts contain no stale Staff/route identity residue", residue[:30])


def check_package_inventory(c: Checks, package: Path) -> Any | None:
    checksum_path = package / "SHA256SUMS.txt"
    manifest_path = package / "RELEASE-MANIFEST.json"
    if not c.check(checksum_path.is_file(), "Release checksum file exists"):
        return None
    parsed, errors = parse_checksums(checksum_path)
    c.check(not errors, "Checksum syntax and paths are safe", errors)
    actual = files(package)
    expected_checksum_names = set(actual) - {"SHA256SUMS.txt"}
    c.check(set(parsed) == expected_checksum_names, "Checksum inventory is exact", {
        "missing": sorted(expected_checksum_names - set(parsed)),
        "extra": sorted(set(parsed) - expected_checksum_names),
    })
    mismatches = [name for name, digest in parsed.items() if name in actual and sha256(actual[name]) != digest]
    c.check(not mismatches, "Every package checksum matches", mismatches)

    manifest = load_json(c, manifest_path, "Release manifest parses") if c.check(manifest_path.is_file(), "Release manifest exists") else None
    if not isinstance(manifest, dict):
        return manifest
    raw = manifest.get("files_excluding_manifest_and_checksums")
    entries: dict[str, dict[str, Any]] = {}
    malformed: list[Any] = []
    if isinstance(raw, list):
        for item in raw:
            if not isinstance(item, dict) or not isinstance(item.get("path"), str) or not safe_name(item["path"]):
                malformed.append(item)
            elif item["path"] in entries:
                malformed.append(item["path"])
            else:
                entries[item["path"]] = item
    else:
        malformed.append("missing list")
    c.check(not malformed, "Manifest file entries are well formed", malformed[:5])
    expected_manifest_names = set(actual) - {"RELEASE-MANIFEST.json", "SHA256SUMS.txt"}
    c.check(set(entries) == expected_manifest_names, "Manifest inventory is exact", {
        "missing": sorted(expected_manifest_names - set(entries)),
        "extra": sorted(set(entries) - expected_manifest_names),
    })
    bad = [
        name for name, item in entries.items() if name in actual and
        (item.get("sha256") != sha256(actual[name]) or item.get("bytes") != actual[name].stat().st_size)
    ]
    c.check(not bad, "Manifest hashes and byte counts match", bad)
    return manifest


def archive_files(c: Checks, path: Path, label: str) -> tuple[str | None, dict[str, bytes]]:
    try:
        archive = zipfile.ZipFile(path)
    except Exception as error:
        c.check(False, f"{label} opens", error)
        return None, {}
    with archive:
        infos = archive.infolist()
        names = [item.filename for item in infos]
        safe = all(safe_name(name) for name in names)
        c.check(safe, f"{label} paths are safe")
        c.check(len(names) == len(set(names)), f"{label} has no duplicate names")
        normalized = [unicodedata.normalize("NFC", name).casefold() for name in names]
        c.check(len(normalized) == len(set(normalized)), f"{label} has no case/Unicode collisions")
        roots = {PurePosixPath(name).parts[0] for name in names if safe_name(name)}
        root = next(iter(roots)) if len(roots) == 1 else None
        c.check(root is not None, f"{label} has one root", sorted(roots))
        symlinks = [item.filename for item in infos if stat.S_ISLNK((item.external_attr >> 16) & 0xFFFF)]
        c.check(not symlinks, f"{label} contains no symlinks", symlinks)
        c.check(archive.testzip() is None, f"{label} CRC passes")
        data = {item.filename: archive.read(item) for item in infos if not item.is_dir()}
        return root, data


def compare_tree_archive(c: Checks, tree: Path, archive_path: Path, label: str) -> None:
    _root, archived = archive_files(c, archive_path, label)
    disk = files(tree)
    matched: dict[str, bytes] = {}
    ambiguous: list[str] = []
    for archived_name, data in archived.items():
        candidates = [name for name in disk if archived_name == name or archived_name.endswith("/" + name)]
        if len(candidates) == 1:
            if candidates[0] in matched:
                ambiguous.append(archived_name)
            matched[candidates[0]] = data
        elif candidates:
            ambiguous.append(archived_name)
    c.check(not ambiguous and set(matched) == set(disk) and len(matched) == len(archived), f"{label} unpacked inventory matches", {
        "unmatched_disk": sorted(set(disk) - set(matched)),
        "archive_file_count": len(archived), "matched_count": len(matched),
        "ambiguous": ambiguous,
    })
    mismatches = [name for name in set(disk) & set(matched) if disk[name].read_bytes() != matched[name]]
    c.check(not mismatches, f"{label} unpacked bytes match", mismatches)


def check_required(c: Checks, package: Path, preassembly: bool) -> None:
    generated_or_copied = {
        "RELEASE-MANIFEST.json", "SHA256SUMS.txt", "SOURCE-INVENTORY.json",
        "implementation/EDUCATOR-Functional-Build-Master-Prompt.md",
        "tools/verify-build-kit.py",
        "personalization/input-schemas/discover-packet-input.schema.json",
        "personalization/input-schemas/soul-profile-input.schema.json",
    }
    expected = [
        name for name in REQUIRED
        if not (preassembly and (name in generated_or_copied or name.startswith("source/")))
    ]
    missing = [name for name in expected if not (package / name).is_file()]
    empty = [name for name in expected if (package / name).is_file() and (package / name).stat().st_size == 0]
    c.check(not missing, "All required contracts and sources exist", missing)
    c.check(not empty, "All required contracts and sources are nonempty", empty)
    version = package / "VERSION"
    if version.is_file():
        c.check(version.read_text(encoding="utf-8").strip() == "1.0.0", "Build-kit VERSION is 1.0.0")


def check_sources(c: Checks, package: Path, manifest: Any | None) -> None:
    expected_files = (
        (PROMPT, PROMPT_SHA, "Original functional prompt hash is exact"),
        (BASELINE_ARCHIVE, BASELINE_ZIP_SHA, "Baseline ZIP hash is exact"),
        (BASE_PROGRAM, BASE_PROGRAM_SHA, "Educator foundation program hash is exact"),
        (BASE_GUIDE, BASE_GUIDE_SHA, "Educator foundation guide hash is exact"),
        (LEGACY_ARCHIVE, LEGACY_ZIP_SHA, "Legacy TEACH ZIP hash is exact"),
    )
    for relative, expected_hash, label in expected_files:
        path = package / relative
        c.check(path.is_file() and sha256(path) == expected_hash, label, sha256(path) if path.is_file() else "missing")

    baseline_zip = package / BASELINE_ARCHIVE
    if (package / BASELINE).is_dir() and baseline_zip.is_file():
        compare_tree_archive(c, package / BASELINE, baseline_zip, "Baseline source archive")
    else:
        c.check(False, "Baseline unpacked source and archive are present")
    legacy_zip = package / LEGACY_ARCHIVE
    if (package / LEGACY).is_dir() and legacy_zip.is_file():
        compare_tree_archive(c, package / LEGACY, legacy_zip, "Legacy TEACH source archive")
    else:
        c.check(False, "Legacy TEACH unpacked source and archive are present")

    teach = package / TEACH_SOURCE
    observed = {name: sha256(teach / name) for name in TEACH_TREE_HASHES if (teach / name).is_file()}
    c.check(observed == TEACH_TREE_HASHES and set(files(teach)) == set(TEACH_TREE_HASHES), "TEACH source tree has exact 13 files and hashes", {
        "missing": sorted(set(TEACH_TREE_HASHES) - set(observed)),
        "extra": sorted(set(files(teach)) - set(TEACH_TREE_HASHES)),
        "wrong": sorted(name for name in observed if observed[name] != TEACH_TREE_HASHES[name]),
    })
    c.check(tree_digest(observed) == TEACH_TREE_DIGEST, "TEACH deterministic source-tree digest is exact", tree_digest(observed))

    base_manifest = package / BASELINE / "RELEASE-MANIFEST.json"
    base_sums = package / BASELINE / "SHA256SUMS.txt"
    c.check(base_manifest.is_file() and sha256(base_manifest) == BASELINE_MANIFEST_SHA, "Baseline manifest hash is exact")
    c.check(base_sums.is_file() and sha256(base_sums) == BASELINE_CHECKSUMS_SHA, "Baseline checksum hash is exact")

    inventory = load_json(c, package / "SOURCE-INVENTORY.json", "Source inventory parses")
    if isinstance(inventory, dict):
        c.check(inventory.get("schema") == "NAIO-NEID-TEACH-BUILD-KIT-SOURCE-INVENTORY-1", "Source inventory schema is exact")
        source_text = json.dumps(inventory, sort_keys=True)
        pinned = (PROMPT_SHA, BASELINE_ZIP_SHA, BASE_PROGRAM_SHA, BASE_GUIDE_SHA, LEGACY_ZIP_SHA, TEACH_TREE_DIGEST, *TEACH_TREE_HASHES.values())
        c.check(all(value in source_text for value in pinned), "Source inventory records every pinned hash")
        personal = inventory.get("personalization", {})
        c.check(isinstance(personal, dict) and all(personal.get(key) is False for key in (
            "real_discover_packet_bundled", "real_soul_quiz_result_bundled",
            "raw_soul_or_quiz_answers_bundled", "synthetic_examples_are_personal_facts",
            "synthetic_examples_are_badge_evidence",
        )), "Source inventory truthfully labels personalization")
        institutional = inventory.get("institutional_education_workspace", {})
        c.check(isinstance(institutional, dict) and all(institutional.get(key) is False for key in (
            "provisioned_by_this_kit", "authorized_by_this_kit", "real_student_or_institutional_data_bundled",
        )), "Source inventory truthfully labels institutional readiness")
    if isinstance(manifest, dict):
        c.check(manifest.get("sources") == {
            "original_functional_build_prompt": PROMPT_SHA,
            "baseline_application_zip": BASELINE_ZIP_SHA,
            "educator_base_program": BASE_PROGRAM_SHA,
            "educator_base_guide": BASE_GUIDE_SHA,
            "teach_legacy_outer_zip": LEGACY_ZIP_SHA,
        }, "Release manifest records the exact pinned source inputs", manifest.get("sources"))


def section_numbers(text: str, start_heading: str, end_heading: str | None) -> list[int]:
    start = text.find(start_heading)
    if start < 0:
        return []
    body = text[start + len(start_heading):]
    if end_heading:
        end = body.find(end_heading)
        if end >= 0:
            body = body[:end]
    return [int(item) for item in re.findall(r"(?m)^\s*(\d+)\.\s+", body)]


def check_domain(c: Checks, package: Path) -> None:
    source = package / TEACH_SOURCE
    power_files = [source / f"teach/{name}" for name in (
        "01-T-Transform-Learning.md", "02-E-Engage-Every-Learner.md",
        "03-A-Assess-Authentically.md", "04-C-Curate-Safely.md",
        "05-H-Harness-AI-and-Sustain-Humans.md",
    )]
    power_texts = [path.read_text(encoding="utf-8") for path in power_files]
    powers = [int(item) for text in power_texts for item in re.findall(r"(?m)^## Power (\d+) \u2014", text)]
    c.check(powers == list(range(1, 21)), "Exactly 20 ordered canonical TEACH powers", powers)
    c.check(all("install inactive" in text.casefold() and "synthetic preview mode" in text.casefold() for text in power_texts), "Every TEACH power family installs inactive in synthetic Preview")

    workflow_text = (source / "workflows/TEACH-Launch-and-Impact-Plan.md").read_text(encoding="utf-8")
    workflows = section_numbers(workflow_text, "## Eighteen ready-to-launch workflows", "## Seven-Day TEACH Launch")
    c.check(workflows == list(range(1, 19)), "Exactly 18 ordered canonical TEACH workflows", workflows)

    template_text = (source / "templates/TEACH-Cards-and-Templates.md").read_text(encoding="utf-8")
    templates = section_numbers(template_text, "## Required library", "## Optional Power Activation Card")
    c.check(templates == list(range(1, 25)), "Exactly 24 ordered canonical TEACH templates", templates)

    tests_text = (source / "tests/Acceptance-Tests.md").read_text(encoding="utf-8")
    overlay_ids = re.findall(r"(?m)^- \*\*([A-O][1-8]):\*\*", tests_text)
    integration_ids = re.findall(r"(?m)^- \*\*(INT\d{2}):\*\*", tests_text)
    expected_overlay = [f"{letter}{index}" for letter in "ABCDEFGHIJKLMNO" for index in range(1, 9)]
    c.check(overlay_ids == expected_overlay, "Canonical overlay checks are exactly A1..O8", overlay_ids[:10] if overlay_ids != expected_overlay else None)
    c.check(integration_ids == [f"INT{i:02d}" for i in range(1, 17)], "Canonical integration checks are exactly INT01..INT16", integration_ids)

    program_text = (package / BASE_PROGRAM).read_text(encoding="utf-8")
    acceptance_start = program_text.find("# 20. Acceptance tests")
    acceptance_end = program_text.find("# 21.", acceptance_start + 1) if acceptance_start >= 0 else -1
    foundation_text = program_text[acceptance_start:acceptance_end] if acceptance_start >= 0 and acceptance_end > acceptance_start else ""
    expected_sections = (
        ("Functional", 5), ("Safety and authority", 5), ("Privacy and isolation", 6),
        ("Assessment fairness", 4), ("Evidence and sources", 4),
        ("Accessibility and usability", 5), ("Whole-life", 4),
    )
    observed_counts: list[tuple[str, int]] = []
    for index, (heading, _count) in enumerate(expected_sections):
        start = foundation_text.find(f"## {heading}")
        next_heading = expected_sections[index + 1][0] if index + 1 < len(expected_sections) else None
        end = foundation_text.find(f"## {next_heading}", start + 1) if next_heading and start >= 0 else len(foundation_text)
        section = foundation_text[start:end] if start >= 0 and end > start else ""
        observed_counts.append((heading, len(re.findall(r"(?m)^- ", section))))
    c.check(observed_counts == list(expected_sections), "Canonical educator foundation acceptance sections are exactly 5/5/6/4/4/5/4", observed_counts)
    c.check(sum(count for _, count in observed_counts) == 33, "Canonical educator foundation has exactly 33 source-order acceptance bullets", sum(count for _, count in observed_counts))


def validate_test_inventories(c: Checks, package: Path) -> dict[str, Any]:
    matrix_path = package / "implementation/EDUCATOR-Control-Completeness-Matrix.csv"
    try:
        with matrix_path.open(newline="", encoding="utf-8") as stream:
            reader = csv.DictReader(stream)
            matrix = list(reader)
            columns = tuple(reader.fieldnames or ())
    except Exception as error:
        c.check(False, "Control matrix parses", error)
        return {}
    c.check(columns == EXPECTED_MATRIX_COLUMNS, "Control matrix has the exact 11-column contract", columns)
    c.check(len(matrix) == EXPECTED_MATRIX_ROWS, "Control matrix has exactly 216 rows", len(matrix))
    ids = [row.get("control_id", "") for row in matrix]
    c.check(len(set(ids)) == len(ids) == EXPECTED_MATRIX_ROWS and all(re.fullmatch(r"[A-Z][A-Z0-9_]{1,15}-[0-9]{3}", item) for item in ids), "Control-matrix IDs are unique and well formed")
    teach_ids = [item for item in ids if item.startswith("TEACH-")]
    c.check(teach_ids == [f"TEACH-{i:03d}" for i in range(1, 19)], "Educator domain controls are exactly TEACH-001..TEACH-018", teach_ids)
    c.check(not [index for index, row in enumerate(matrix, 1) if any(not str(row.get(column, "")).strip() for column in EXPECTED_MATRIX_COLUMNS)], "Control matrix has no empty required cell")
    c.check({row.get("status") for row in matrix} == {"Not Run"}, "Every control-matrix row starts Not Run", sorted({str(row.get('status')) for row in matrix}))

    control_digest = hashlib.sha256(("\n".join(ids) + "\n").encode()).hexdigest()
    semantic_digest = normalized_digest([[row[column] for column in EXPECTED_MATRIX_COLUMNS] for row in matrix])
    if EXPECTED_CONTROL_ID_DIGEST:
        c.check(control_digest == EXPECTED_CONTROL_ID_DIGEST, "Control-ID digest is frozen", control_digest)
    if EXPECTED_MATRIX_SEMANTIC_DIGEST:
        c.check(semantic_digest == EXPECTED_MATRIX_SEMANTIC_DIGEST, "Control-matrix semantic digest is frozen", semantic_digest)

    ledger_path = package / "implementation/EDUCATOR-Acceptance-and-Test-Ledger.md"
    rows = markdown_table_rows(ledger_path.read_text(encoding="utf-8"))
    c.check(rows.count(list(EXPECTED_CONTROL_LEDGER_COLUMNS)) == 1, "Acceptance ledger has one exact control header")
    c.check(rows.count(list(EXPECTED_INTEGRATION_LEDGER_COLUMNS)) == 1, "Acceptance ledger has one exact integration header")
    ctl = [row for row in rows if row and row[0].startswith("CTL-")]
    integration = [row for row in rows if row and re.fullmatch(r"INT-[0-9]{3}", row[0])]
    by_id = {row["control_id"]: row for row in matrix}
    c.check([row[0] for row in ctl] == [f"CTL-{item}" for item in ids], "CTL ledger order and IDs exactly match the matrix")
    bad_ctl: list[str] = []
    for row in ctl:
        control_id = row[0].removeprefix("CTL-")
        source_row = by_id.get(control_id)
        if not source_row or len(row) != 6 or row[1] != source_row["screen"] or row[2] != expected_control_priority(control_id) or row[3] != source_row["verification_test"] or row[-1] != "Not Run":
            bad_ctl.append(row[0])
    c.check(not bad_ctl, "Every CTL ledger row preserves matrix semantics and priority", bad_ctl)
    expected_integration = [f"INT-{i:03d}" for i in range(1, EXPECTED_INTEGRATION_ROWS + 1)]
    c.check([row[0] for row in integration] == expected_integration and all(len(row) == 6 and row[-1] == "Not Run" for row in integration), "Integration ledger has exact INT-001..INT-048 Not Run rows")
    integration_digest = normalized_digest(integration)
    if EXPECTED_INTEGRATION_SEMANTIC_DIGEST:
        c.check(integration_digest == EXPECTED_INTEGRATION_SEMANTIC_DIGEST, "Integration-scenario semantic digest is frozen", integration_digest)

    canonical = [
        row for row in rows if row and (
            row[0].startswith("FOUNDATION-")
            or re.fullmatch(r"TEACH-[A-O][1-8]", row[0])
            or row[0].startswith("INTEGRATION-")
        )
    ]
    c.check(canonical == expected_canonical_rows(), "Canonical ledger exactly maps 33 foundation + 120 TEACH + 16 integration checks", {
        "observed": len(canonical), "expected": EXPECTED_CANONICAL_ROWS,
    })
    c.check(len(ctl) + len(integration) == EXPECTED_TARGET_ROWS and len(canonical) == EXPECTED_CANONICAL_ROWS, "Ledger counts are exactly 264 target and 169 canonical")
    text = ledger_path.read_text(encoding="utf-8")
    c.check(all(token in text for token in ("**216**", "**48**", "**264**", "**169**", "**433**")), "Ledger states the exact 216 + 48 + 169 = 433 execution accounting")
    return {
        "control_ids": control_digest,
        "control_matrix_semantics": semantic_digest,
        "integration_scenarios": integration_digest,
    }


def check_capabilities(c: Checks, package: Path) -> tuple[Any | None, str | None]:
    cap = load_json(c, package / "config/EDUCATOR-Capability-Mastery-Criteria.v1.json", "Capability criteria parse")
    if not isinstance(cap, dict):
        return cap, None
    digest = normalized_digest(cap)
    c.check(cap.get("schema") == "NAIO-NEID-TEACH-CAPABILITY-MASTERY-CRITERIA-1", "Capability criteria schema ID is exact")
    c.check(cap.get("version") == "1.0.0" and cap.get("status") == "normative_build_layer_contract", "Capability criteria version and status are exact")
    c.check(cap.get("legacy_identity") == {
        "program": "NAIO-NEID-COMPLETE-TEACH-1.0",
        "foundation": "NAIO-NE-LPEF-1.0",
        "expansion": "NAIO-NEID-TEACH-1.0",
        "namespace": NAMESPACE,
        "notice": "The four-level catalog, CAP IDs, JSON shape, criteria and agent permissions are build-layer additions, not identifiers from the legacy corpus.",
    }, "Capability criteria preserve exact legacy identity")
    c.check(cap.get("counts") == {
        "domains": 17, "levels": 4, "domain_criteria": 68,
        "capstone_criteria": 9, "total_criteria": 77,
    }, "Capability criteria counts are 17/4/68+9=77", cap.get("counts"))
    expected_names = [
        "AI literacy", "Prompt design", "Evidence-informed research",
        "Critical thinking and reasoning", "Structured problem-solving",
        "Workflow and learning-design design",
        "Course, project, and capacity management",
        "Learner-data and privacy stewardship",
        "Ethical AI, integrity, and authorship", "EDENA and TEACH governance",
        "Agent supervision", "Multi-agent orchestration",
        "Knowledge-base, source, and rights development", "Automation design",
        "Accessible Human Teaching artifact creation",
        "Evaluation and program improvement",
        "Nurse educator and instructional-design practice",
    ]
    domains = cap.get("domains", [])
    c.check(
        isinstance(domains, list)
        and [item.get("id") for item in domains] == [f"CAP-{index:02d}" for index in range(1, 18)]
        and [item.get("name") for item in domains] == expected_names,
        "Capability domains have exact CAP-01..CAP-17 names",
    )
    levels = cap.get("levels", [])
    expected_levels = [
        ("L1", "Basic", None, "P1"),
        ("L2", "Intermediate", "L1", "P1"),
        ("L3", "Advanced", "L2", "P2"),
        ("L4", "AI Agent Orchestration Master", "L3", "P2"),
    ]
    observed_levels = [
        (item.get("id"), item.get("name"), item.get("prerequisite_level"), item.get("agent_permission_ceiling"))
        for item in levels if isinstance(item, dict)
    ]
    c.check(observed_levels == expected_levels, "Capability mastery chain and P1/P2 ceilings are exact", observed_levels)
    criteria = [criterion for domain in domains if isinstance(domain, dict) for criterion in domain.get("criteria", []) if isinstance(criterion, dict)]
    expected_criteria_ids = [f"CAP-{domain:02d}-L{level}-01" for domain in range(1, 18) for level in range(1, 5)]
    c.check([item.get("id") for item in criteria] == expected_criteria_ids, "Domain criteria are exactly 68 ordered IDs")
    bad_criteria = []
    for item in criteria:
        level_match = re.fullmatch(r"CAP-\d{2}-(L[1-4])-01", str(item.get("id", "")))
        if not level_match:
            bad_criteria.append(str(item.get("id")))
            continue
        level = level_match.group(1)
        if (
            item.get("required") is not True
            or item.get("agent_permission_ceiling") != ("P1" if level in {"L1", "L2"} else "P2")
            or item.get("requires_global_orchestration_capstone") is not (level == "L4")
            or item.get("prerequisite_level") != {"L1": None, "L2": "L1", "L3": "L2", "L4": "L3"}[level]
            or item.get("minimum_distinct_real_missions") != {"L1": 1, "L2": 2, "L3": 3, "L4": 1}[level]
            or item.get("requires_human_review") is not (level != "L1")
            or item.get("human_review_rule") != ("contextual_policy" if level == "L1" else "unconditional_plus_contextual_policy")
            or not str(item.get("description", "")).strip()
        ):
            bad_criteria.append(str(item.get("id")))
    c.check(not bad_criteria, "Every capability criterion preserves prerequisites, review metadata and permission ceilings", bad_criteria)
    eligibility = cap.get("eligibility", {})
    c.check(eligibility.get("allowed_evidence_types") == EXPECTED_EVIDENCE_TYPES, "Eligible evidence types are exact")
    c.check(eligibility.get("excluded") == EXPECTED_EVIDENCE_EXCLUSIONS, "Evidence exclusions are exact")
    c.check(eligibility.get("recompute_triggers") == EXPECTED_RECOMPUTE_TRIGGERS, "Recompute triggers are exact")
    policy = cap.get("contextual_human_review_policy", {})
    c.check(
        isinstance(policy, dict) and policy.get("applies_to_all_levels") is True
        and policy.get("required_result") == "current_scope_matched_named_human_process_review_before_award"
        and "edena_red" in str(policy.get("red_rule", "")),
        "Contextual named-human review policy applies to all levels",
    )
    capstone = cap.get("global_orchestration_capstone", {})
    capstone_criteria = capstone.get("criteria", []) if isinstance(capstone, dict) else []
    c.check(
        isinstance(capstone, dict)
        and capstone.get("id") == "CAPSTONE-AI-ORCH-01"
        and capstone.get("required_for_level") == "L4"
        and capstone.get("agent_permission_ceiling") == "P2"
        and capstone.get("external_actions") == "unsupported"
        and [item.get("id") for item in capstone_criteria] == [f"CAPSTONE-{i:02d}" for i in range(1, 10)],
        "Global orchestration capstone is exact and has nine criteria",
    )
    c.check(cap.get("teach_alignment", {}).get("core_four") == ["Plan My Day", "Design & Align", "Assess & Support", "Review & Release"], "Capability catalog preserves the exact Core Four")
    notice = str(cap.get("noncredential_notice", "")).casefold()
    c.check(all(term in notice for term in ("not continuing-education credit", "licensure", "teaching or clinical competence", "grading", "accreditation", "institutional authorization")), "Capability catalog has a complete noncredential notice")
    if EXPECTED_CAPABILITY_CONFIG_DIGEST:
        c.check(digest == EXPECTED_CAPABILITY_CONFIG_DIGEST, "Capability configuration digest is frozen", digest)
    return cap, digest


def check_source_registry(c: Checks, package: Path) -> tuple[Any | None, str | None]:
    registry = load_json(c, package / "config/EDUCATOR-Source-Recommendation-Registry.v1.json", "Source recommendation registry parses")
    if not isinstance(registry, dict):
        return registry, None
    digest = normalized_digest(registry)
    c.check(registry.get("schema") == "NAIO-NEID-SOURCE-RECOMMENDATION-REGISTRY-1", "Source registry schema ID is exact")
    entries = registry.get("entries", [])
    observed: dict[str, tuple[Any, Any]] = {}
    if isinstance(entries, list):
        for item in entries:
            if isinstance(item, dict):
                observed[str(item.get("id"))] = (item.get("path"), item.get("sha256"))
    c.check(observed == EXPECTED_SOURCE_RECOMMENDATIONS and registry.get("count") == 10, "Source registry is exact NEID-SRC-01..10 with pinned paths and hashes", observed)
    rules = registry.get("rules", {})
    c.check(
        isinstance(rules, dict)
        and rules.get("exact_id_set") == "NEID-SRC-01..NEID-SRC-10"
        and rules.get("unknown_id_behavior") == "reject_and_show_content_free_configuration_error"
        and rules.get("recommendation_grants_authority") is False
        and rules.get("citation_or_link_grants_reproduction_rights") is False,
        "Source registry rejects unknown IDs and grants no authority or rights",
    )
    if EXPECTED_SOURCE_REGISTRY_DIGEST:
        c.check(digest == EXPECTED_SOURCE_REGISTRY_DIGEST, "Source registry digest is frozen", digest)
    return registry, digest


def check_schemas_and_fixtures(c: Checks, package: Path, preassembly: bool) -> None:
    c.check(
        bool(validate_json_schema("blocked", {"not": {"const": "blocked"}})),
        "Schema engine enforces not",
    )
    c.check(
        bool(validate_json_schema(["first", "extra"], {"type": "array", "prefixItems": [True], "items": False})),
        "Schema engine enforces prefixItems and Boolean items false",
    )
    c.check(
        bool(validate_json_schema("not-a-uuid", {"type": "string", "format": "uuid"}))
        and not validate_json_schema("22222222-2222-4222-8222-222222222222", {"type": "string", "format": "uuid"}),
        "Schema engine enforces UUID format",
    )
    c.check(
        bool(validate_json_schema("2026-07-20 10:00:00", {"type": "string", "format": "date-time"}))
        and not validate_json_schema("2026-07-20T10:00:00Z", {"type": "string", "format": "date-time"}),
        "Schema engine enforces offset RFC 3339 date-time format",
    )
    schema_specs = (
        ("EDUCATOR-Discover-Packet.schema.json", "EDUCATOR-Discover-Packet.synthetic.example.json", "NAIO-NEID-DISCOVER-PACKET-ADAPTER-1"),
        ("EDUCATOR-Soul-Profile.schema.json", "EDUCATOR-Soul-Profile.synthetic.example.json", "NAIO-NEID-SOUL-PROFILE-ADAPTER-1"),
        ("EDUCATOR-Mission-Profile.schema.json", "EDUCATOR-Mission-Profile.synthetic.example.json", "NAIO-NEID-MISSION-PROFILE-1"),
    )
    loaded: dict[str, tuple[Any, Any]] = {}
    for schema_name, fixture_name, schema_id in schema_specs:
        schema = load_json(c, package / "schemas" / schema_name, f"{schema_name} parses")
        fixture = load_json(c, package / "personalization" / fixture_name, f"{fixture_name} parses")
        loaded[schema_name] = (schema, fixture)
        if isinstance(schema, dict) and isinstance(fixture, dict):
            c.check(schema.get("properties", {}).get("schema", {}).get("const") == schema_id, f"{schema_name} freezes {schema_id}")
            errors = validate_json_schema(fixture, schema)
            c.check(not errors, f"{fixture_name} validates against its adapter schema", errors[:20])
            c.check(fixture.get("schema") == schema_id and fixture.get("demo") is True, f"{fixture_name} is an unmistakable synthetic demonstration")
            c.check(schema.get("type") == "object" and schema.get("additionalProperties") is False, f"{schema_name} rejects unknown top-level fields")
            references: list[str] = []
            stack: list[Any] = [schema]
            while stack:
                node = stack.pop()
                if isinstance(node, dict):
                    if isinstance(node.get("$ref"), str):
                        references.append(node["$ref"])
                    stack.extend(node.values())
                elif isinstance(node, list):
                    stack.extend(node)
            unresolved: list[str] = []
            for reference in references:
                try:
                    resolve_ref(schema, reference)
                except ValueError as error:
                    unresolved.append(str(error))
            c.check(not unresolved, f"{schema_name} has only resolvable local references", unresolved)

            unknown = json.loads(json.dumps(fixture))
            unknown["student_records"] = [{"learner": "forbidden"}]
            c.check(bool(validate_json_schema(unknown, schema)), f"{schema_name} rejects student-record-like unknown payloads")
            wrong_schema = json.loads(json.dumps(fixture))
            wrong_schema["schema"] = "NAIO-WRONG-ADAPTER-1"
            c.check(bool(validate_json_schema(wrong_schema, schema)), f"{schema_name} rejects a wrong adapter identity")

            if "raw_answers_present" in fixture:
                raw_flag = json.loads(json.dumps(fixture))
                raw_flag["raw_answers_present"] = True
                c.check(bool(validate_json_schema(raw_flag, schema)), f"{schema_name} rejects raw-answer admission")
            if "Discover-Packet" in schema_name or "Soul-Profile" in schema_name:
                raw_payload = json.loads(json.dumps(fixture))
                raw_payload["raw_answers"] = {"q1": "forbidden raw response"}
                c.check(bool(validate_json_schema(raw_payload, schema)), f"{schema_name} rejects a raw-answer payload field")

    if not preassembly:
        pairs = (
            ("schemas/EDUCATOR-Discover-Packet.schema.json", "personalization/input-schemas/discover-packet-input.schema.json"),
            ("schemas/EDUCATOR-Soul-Profile.schema.json", "personalization/input-schemas/soul-profile-input.schema.json"),
        )
        mismatches = [(left, right) for left, right in pairs if (package / left).read_bytes() != (package / right).read_bytes()]
        c.check(not mismatches, "First-run input schemas are exact byte copies of the NEID adapters", mismatches)

    mission_schema, mission = loaded.get("EDUCATOR-Mission-Profile.schema.json", (None, None))
    if isinstance(mission_schema, dict) and isinstance(mission, dict):
        definitions = mission_schema.get("$defs", {})
        c.check(definitions.get("roleAdapter", {}).get("enum") == ["nurse_educator", "instructional_designer", "hybrid_faculty_developer", "unresolved"], "Mission schema has exact three role adapters plus unresolved")
        workspace_types = [
            "life_capacity_career", "teaching_learning_design_accessibility",
            "assessment_feedback_learner_support", "clinical_skills_lab_simulation",
            "curriculum_program_evaluation_scholarship_faculty_development",
        ]
        c.check(definitions.get("workspaceType", {}).get("enum") == workspace_types, "Mission schema has exactly five protected workspace types")
        data_classes = ["TEACH-DATA-0", "TEACH-DATA-1", "TEACH-DATA-2", "TEACH-DATA-M", "TEACH-DATA-C", "TEACH-DATA-A", "TEACH-DATA-R", "TEACH-DATA-X"]
        c.check(definitions.get("dataClass", {}).get("enum") == data_classes, "Mission schema has the complete TEACH-DATA-0/1/2/M/C/A/R/X model")
        c.check(definitions.get("templateId", {}).get("pattern") == r"^TEACH-TPL-(?:0[1-9]|1[0-9]|2[0-4])$", "Template schema permits exactly TEACH-TPL-01..24")
        identity = mission.get("identity", {})
        c.check((identity.get("product_id"), identity.get("lane"), identity.get("canonical_route"), identity.get("home")) == (PRODUCT_ID, LANE, ROUTE, HOME), "Synthetic Mission Profile target identity is exact")
        workspaces = mission.get("workspaces", [])
        c.check(isinstance(workspaces, list) and [item.get("workspace_type") for item in workspaces] == workspace_types, "Synthetic Mission Profile instantiates all five protected workspaces")
        authority = mission.get("authority_context", {})
        c.check(authority.get("deployment_context") == "private_educator_designer_mode" and authority.get("institutional_runtime_bind_required") is True and authority.get("institutional_bind") is None, "Synthetic Mission Profile requires a separate institutional runtime bind")
        safety = mission.get("safety_boundaries", {})
        c.check(isinstance(safety, dict) and safety and all(value is True for value in safety.values()), "Every synthetic Mission Profile safety boundary is enabled")
        ai = mission.get("ai_preferences", {})
        c.check(ai.get("agent_permission_state") == "PERM-P0 Disabled" and ai.get("p4_available") is False and ai.get("external_actions") == "off" and ai.get("connectors") == "off" and ai.get("background_automation") == "off", "Synthetic Mission Profile keeps agents P0 and all execution paths off")
        assets = mission.get("asset_recommendations", {})
        c.check(assets.get("activation_state") == "recommendations_only_inactive" and assets.get("agent_permission_state") == "PERM-P0 Disabled" and assets.get("tool_state") == "disabled" and assets.get("tool_ids") == [], "Synthetic recommendations remain inactive, P0 and tool-free")
        c.check(all(item in EXPECTED_SOURCE_RECOMMENDATIONS for item in mission.get("source_recommendation_ids", [])), "Synthetic source recommendations resolve through NEID-SRC-01..10")

        wrong_route = json.loads(json.dumps(mission))
        wrong_route["identity"]["canonical_route"] = "/educators/dashboard"
        c.check(bool(validate_json_schema(wrong_route, mission_schema)), "Mission schema rejects a stale or wrong route")
        wrong_workspace_order = json.loads(json.dumps(mission))
        wrong_workspace_order["workspaces"][0], wrong_workspace_order["workspaces"][1] = wrong_workspace_order["workspaces"][1], wrong_workspace_order["workspaces"][0]
        c.check(bool(validate_json_schema(wrong_workspace_order, mission_schema)), "Mission schema rejects a wrong protected-workspace order")
        invalid_profile_id = json.loads(json.dumps(mission))
        invalid_profile_id["profile_id"] = "not-a-uuid"
        c.check(bool(validate_json_schema(invalid_profile_id, mission_schema)), "Mission schema rejects a malformed profile UUID")
        bad_bind = json.loads(json.dumps(mission))
        bad_bind["demo"] = False
        bad_bind["status"] = "proposed_for_owner_review"
        bad_bind["authority_context"]["deployment_context"] = "institution_approved_education_workspace"
        bad_bind["authority_context"]["authority_status"] = "not_established"
        bad_bind["authority_context"]["institutional_bind"] = None
        c.check(bool(validate_json_schema(bad_bind, mission_schema)), "Mission schema rejects an institutional context without an externally verified runtime bind")
        widened = json.loads(json.dumps(mission))
        widened["ai_preferences"]["p4_available"] = True
        c.check(bool(validate_json_schema(widened, mission_schema)), "Mission schema rejects P4 availability")

    discover = loaded.get("EDUCATOR-Discover-Packet.schema.json", (None, None))[1]
    soul = loaded.get("EDUCATOR-Soul-Profile.schema.json", (None, None))[1]
    if isinstance(discover, dict):
        discover_keys = {str(key).casefold() for key in discover}
        c.check("raw_answers" not in discover_keys and "quiz_answers" not in discover_keys, "Synthetic Discover adapter contains no raw-answer payload")
    if isinstance(soul, dict):
        c.check(soul.get("raw_answers_present") is False, "Synthetic Soul adapter contains no raw quiz answers")


def check_agent_contract(c: Checks, package: Path) -> None:
    path = package / "implementation/EDUCATOR-Agent-Team-and-Routing.md"
    text = path.read_text(encoding="utf-8")
    ids = sorted(set(re.findall(r"TEACH-AGT-(?:0[1-9]|10)", text)))
    c.check(ids == list(EXPECTED_AGENTS), "Exactly ten TEACH agent IDs are frozen", ids)
    missing_names = [f"{agent_id} {name}" for agent_id, name in EXPECTED_AGENTS.items() if agent_id not in text or name not in text]
    c.check(not missing_names, "Every TEACH agent has its exact frozen name", missing_names)
    c.check("**Installed agent state:** `PERM-P0 Disabled`" in text and text.count("`PERM-P0 Disabled`") >= 11, "All ten agents install PERM-P0 Disabled")
    permission_section = text[text.find("## 3. Permission and automation model"):text.find("## 4. One-run state machine")]
    c.check(all(level in permission_section for level in ("`PERM-P0`", "`PERM-P1`", "`PERM-P2`", "`PERM-P3`", "`PERM-P4`")) and "**Absent.**" in permission_section, "Permission contract has P1/P2 one-run ceilings, reminder-only P3 and absent P4")
    c.check("one invocation" in text.casefold() and "zero hidden retries" in text.casefold() and "no agent action exists" in text.casefold(), "Agent contract forbids hidden retries and every external action")


def check_contracts(c: Checks, package: Path, manifest: Any | None, preassembly: bool) -> None:
    prompt = package / "implementation/EDUCATOR-Functional-Build-Master-Prompt.md"
    if not preassembly and prompt.is_file():
        text = prompt.read_text(encoding="utf-8")
        c.check(not [placeholder for placeholder in PLACEHOLDERS if placeholder in text], "Resolved master prompt has no unresolved placeholders")
        for needle in (BUILD_ID, PRODUCT_ID, LANE, ROUTE, HOME, NAMESPACE, READINESS, "NAIO-NEID-DISCOVER-PACKET-ADAPTER-1", "NAIO-NEID-SOUL-PROFILE-ADAPTER-1", "Private Educator/Designer Mode", "TEACH-DATA-0/1/2/M/C/A/R/X"):
            c.check(needle in text, f"Resolved master prompt contains {needle}")

    json_errors: list[str] = []
    for path in package.rglob("*.json"):
        try:
            strict_json_loads(path.read_text(encoding="utf-8"))
        except Exception as error:
            json_errors.append(f"{path.relative_to(package)}: {error}")
    c.check(not json_errors, "Every JSON file strictly parses without duplicate keys or non-finite numbers", json_errors)

    check_agent_contract(c, package)
    cap, capability_digest = check_capabilities(c, package)
    registry, source_digest = check_source_registry(c, package)
    check_schemas_and_fixtures(c, package, preassembly)
    tests = validate_test_inventories(c, package)

    handoff = (package / "implementation/HERMES-FINAL-HANDOFF-REPORT-TEMPLATE.md").read_text(encoding="utf-8")
    readiness = (
        "**Operational:** All acceptance criteria passed, including a genuine end-to-end streamed response through the downloaded application.",
        "**Core operational; AI setup pending:** All offline/core acceptance criteria passed, but a live configured backend was not available for the genuine-streaming acceptance test.",
        "**Not operational:** One or more core acceptance criteria failed; list each blocker.",
    )
    c.check(all(line in handoff for line in readiness), "Handoff contains the three exact readiness statements")

    boundary_files = (
        "README-FIRST.md", "GIVE-THIS-PACKAGE-TO-HERMES.md", "BUILD-STATUS.md",
        "implementation/EDUCATOR-Product-Specification.md",
        "implementation/EDUCATOR-Governance-EDENA-and-Data-Boundaries.md",
        "implementation/EDUCATOR-Agent-Team-and-Routing.md",
        "implementation/EDUCATOR-Guide-Page-Content.md",
    )
    boundary_text = "\n".join((package / name).read_text(encoding="utf-8") for name in boundary_files).casefold()
    boundary_groups = {
        "learner and clinical data": ("no-phi", "learner record", "secure assessment", "teach-data-x"),
        "human authority": ("human grading", "accommodation", "accreditation", "professional judgment"),
        "accessibility and rights": ("accessibility", "copyright", "rights", "authorship"),
        "simulation and integrity": ("clinical", "simulation", "detector", "surveillance"),
        "memory and action": ("session only", "external action", "pause all", "perm-p0"),
        "readiness": ("not operational", "institution-approved", "runtime bind"),
    }
    for label, needles in boundary_groups.items():
        c.check(all(needle in boundary_text for needle in needles), f"Core contracts document {label} boundaries", [needle for needle in needles if needle not in boundary_text])

    if not isinstance(manifest, dict):
        return
    c.check(manifest.get("schema") == "NAIO-NEID-TEACH-HERMES-BUILD-KIT-1", "Release manifest schema is exact")
    build = manifest.get("build_kit", {})
    target = manifest.get("target", {})
    c.check(build.get("id") == BUILD_ID and build.get("version") == "1.0.0", "Manifest build identity is exact")
    c.check((target.get("product"), target.get("product_id"), target.get("version"), target.get("lane"), target.get("route"), target.get("namespace"), target.get("home"), target.get("readiness")) == (PRODUCT, PRODUCT_ID, "2.0.0", LANE, ROUTE, NAMESPACE, HOME, READINESS), "Manifest target identity and readiness are exact", target)
    expected_counts = {
        "role_adapters": 3, "deployment_contexts": 2, "protected_workspaces": 5,
        "core_launchers": 4, "superpowers": 20, "workflows": 18,
        "templates": 24, "agents": 10, "mastery_levels": 4,
        "capability_domains": 17, "capability_criteria_including_capstone": 77,
        "canonical_foundation_checks": 33, "canonical_overlay_checks": 120,
        "canonical_integration_checks": 16, "canonical_compatibility_checks": 169,
        "control_matrix_rows": 216, "acceptance_ledger_explicit_target_test_rows": 264,
        "target_control_tests": 216, "cross_cutting_full_stack_scenarios": 48,
        "total_target_full_stack_tests": 264, "total_required_execution_records": 433,
    }
    c.check(manifest.get("counts") == expected_counts, "Manifest counts are exact", manifest.get("counts"))
    defaults = manifest.get("defaults", {})
    c.check(defaults == {
        "mode": "private_educator_designer_synthetic_demonstration",
        "data": "TEACH-DATA-0/1/2/M/C/A/R/X with TEACH-DATA-X rejected before echo or persistence",
        "memory": "session_only", "powers": "Available Inactive",
        "workflows": "Preview Only", "agents": "PERM-P0 Disabled",
        "connectors_schedules_sharing_external_actions_background_agents": "Off",
        "optional_fifth_launcher": "Empty",
        "institution_approved_education_workspace": "Unavailable until separately provisioned and bound",
    }, "Manifest safe defaults are exact", defaults)
    digests = manifest.get("contract_digests", {})
    expected_digests = {
        **tests,
        "capability_configuration": capability_digest,
        "source_recommendation_registry": source_digest,
    }
    c.check(digests == expected_digests, "Manifest contract digests match every frozen staged contract", digests)
    c.check(isinstance(cap, dict) and isinstance(registry, dict), "Manifest-referenced capability and source contracts are valid objects")
    notice = str(manifest.get("implementation_notice", "")).casefold()
    c.check(all(needle in notice for needle in ("build-layer additions", "student-data", "grading", "accreditation", "external action")), "Manifest implementation notice rejects borrowed authority")


def check_modes(c: Checks, package: Path, enforce_modes: bool) -> None:
    symlinks: list[str] = []
    special: list[str] = []
    bad: list[str] = []
    for path in package.rglob("*"):
        relative = path.relative_to(package).as_posix()
        if path.is_symlink():
            symlinks.append(relative)
            continue
        if not path.is_dir() and not path.is_file():
            special.append(relative)
            continue
        mode = stat.S_IMODE(path.stat().st_mode)
        expected = 0o755 if path.is_dir() or relative == "tools/verify-build-kit.py" else 0o644
        if mode != expected:
            bad.append(f"{relative}:{oct(mode)} expected {oct(expected)}")
        if mode & 0o022:
            bad.append(f"{relative}:{oct(mode)} group/world writable")
    c.check(not symlinks, "Package contains no symlinks", symlinks)
    c.check(not special, "Package contains no special files", special)
    if enforce_modes:
        c.check(not bad, "Package modes are normalized and not group/world writable", bad[:30])
    elif bad:
        c.warn("Extracted filesystem modes are non-authoritative; outer-ZIP modes remain mandatory", bad[:30])
    else:
        c.check(True, "Package modes are normalized and not group/world writable")


def check_outer_zip(c: Checks, package: Path, zip_path: Path) -> None:
    if not c.check(zip_path.is_file() and zip_path.stat().st_size > 0, "Final downloadable ZIP exists", zip_path):
        return
    root, archived = archive_files(c, zip_path, "Final downloadable ZIP")
    c.check(root == PACKAGE_NAME, "Final ZIP root name is exact", root)
    if root is None:
        return
    stripped = {name[len(root) + 1:]: data for name, data in archived.items() if name.startswith(root + "/")}
    disk = files(package)
    c.check(set(stripped) == set(disk), "Final ZIP inventory equals release directory", {
        "missing": sorted(set(disk) - set(stripped)),
        "extra": sorted(set(stripped) - set(disk)),
    })
    mismatches = [name for name in set(stripped) & set(disk) if stripped[name] != disk[name].read_bytes()]
    c.check(not mismatches, "Final ZIP bytes equal release directory", mismatches)
    try:
        with zipfile.ZipFile(zip_path) as archive:
            bad_modes: list[str] = []
            for info in archive.infolist():
                if info.is_dir():
                    continue
                relative = info.filename[len(root) + 1:]
                mode = stat.S_IMODE((info.external_attr >> 16) & 0xFFFF)
                expected = 0o755 if relative == "tools/verify-build-kit.py" else 0o644
                if mode != expected or mode & 0o022:
                    bad_modes.append(f"{relative}:{oct(mode)} expected {oct(expected)}")
            c.check(not bad_modes, "Final ZIP preserves normalized safe file modes", bad_modes[:30])
    except Exception as error:
        c.check(False, "Final ZIP mode check completes", error)


def check_release_companions(c: Checks, package: Path, zip_path: Path, required: bool) -> None:
    if not required:
        return
    alias = zip_path.parent / PACKAGE_ALIAS
    sidecar = zip_path.parent / f"{PACKAGE_NAME}.zip.sha256"
    c.check(alias.is_file(), "Unversioned download alias exists", alias)
    if alias.is_file() and zip_path.is_file():
        c.check(sha256(alias) == sha256(zip_path) and alias.read_bytes() == zip_path.read_bytes(), "Unversioned alias is byte-identical to the versioned ZIP")
    c.check(sidecar.is_file(), "Outer ZIP checksum sidecar exists", sidecar)
    if sidecar.is_file() and zip_path.is_file():
        expected = f"{sha256(zip_path)}  {zip_path.name}\n"
        c.check(sidecar.read_text(encoding="utf-8") == expected, "Outer ZIP checksum sidecar is exact")


def summary(c: Checks, *, preassembly: bool = False) -> int:
    print("\nValidation summary")
    print(f"PASS={len(c.passed)} FAIL={len(c.failed)} WARN={len(c.warnings)}")
    if c.failed:
        print("Failures:")
        for failure in c.failed:
            print(f"- {failure}")
        return 1
    if preassembly:
        print("PRE-ASSEMBLY CONTRACT CHECK PASSED — no release or operational status is asserted.")
    else:
        print("VERIFIED EDUCATOR BUILD KIT — the target application remains not operational until Hermes builds it and executes all 433 required records.")
    return 0


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--package", type=Path, default=DEFAULT_PACKAGE)
    parser.add_argument("--zip", dest="zip_path", type=Path)
    parser.add_argument("--pre-assembly", action="store_true")
    parser.add_argument("--require-release-companions", action="store_true")
    args = parser.parse_args(argv)

    package = args.package.resolve()
    c = Checks()
    c.check(package.is_dir(), "Package directory exists", package)
    if not package.is_dir():
        return summary(c, preassembly=args.pre_assembly)
    if not args.pre_assembly:
        c.check(package.name == PACKAGE_NAME, "Versioned package directory name is exact", package.name)
    check_required(c, package, args.pre_assembly)
    check_controlled_placeholders(c, package)

    if args.pre_assembly:
        check_contracts(c, package, None, True)
        return summary(c, preassembly=True)

    manifest = check_package_inventory(c, package)
    check_sources(c, package, manifest)
    check_domain(c, package)
    check_contracts(c, package, manifest, False)
    check_modes(c, package, enforce_modes=args.zip_path is None and os.name != "nt")

    zip_path = args.zip_path
    if zip_path is None and WORKSPACE is not None:
        candidate = WORKSPACE / "deliverables" / f"{PACKAGE_NAME}.zip"
        zip_path = candidate if candidate.exists() else None
    if zip_path is not None:
        resolved_zip = zip_path.resolve()
        check_outer_zip(c, package, resolved_zip)
        check_release_companions(c, package, resolved_zip, args.require_release_companions)
    elif args.require_release_companions:
        c.check(False, "Release companions require an explicit or discoverable outer ZIP")
    elif EXTRACTED:
        c.warn("Outer ZIP was not supplied; extracted directory was fully verified")
    else:
        c.warn("Outer ZIP was not found; release directory was fully verified")
    return summary(c)


if __name__ == "__main__":
    sys.exit(main())
