From dddb0ab93165a2f1e098a3c02b114180eaabf373 Mon Sep 17 00:00:00 2001 From: Arkadiy Miteiko Date: Fri, 28 Aug 2026 11:20:30 -0400 Subject: [PATCH 1/6] feat: add bounded synthetic candidate extraction --- AGENTS.md | 10 +- README.md | 39 ++- STATUS.md | 13 +- .../semantic-001/EXPECTED-CANDIDATES.json | 29 ++ docs/decisions/OIC-OWNER-DECISION-004.md | 22 ++ ...IC-SEMANTIC-CODE-START-GATE-OPEN-v0.1.json | 21 ++ docs/operations/FOUNDATION.md | 5 +- scripts/falsify_infrastructure.py | 4 +- scripts/verify_code_start_gate.py | 30 +- src/oic/candidate_extraction.py | 314 ++++++++++++++++++ .../test_canada_acquisition_preflight.py | 7 +- tests/contract/test_claims_discipline.py | 11 +- .../test_semantic_code_start_gate_closure.py | 21 +- tests/contract/test_warrant_contract.py | 17 +- tests/unit/test_candidate_extraction.py | 218 ++++++++++++ 15 files changed, 708 insertions(+), 53 deletions(-) create mode 100644 benchmarks/preflight/semantic-001/EXPECTED-CANDIDATES.json create mode 100644 docs/decisions/OIC-OWNER-DECISION-004.md create mode 100644 docs/gates/OIC-SEMANTIC-CODE-START-GATE-OPEN-v0.1.json create mode 100644 src/oic/candidate_extraction.py create mode 100644 tests/unit/test_candidate_extraction.py diff --git a/AGENTS.md b/AGENTS.md index 508821b..70e7124 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,8 +1,8 @@ # Agent operating boundary -This repository is a non-semantic infrastructure foundation. The semantic OIC -code-start gate remains **BLOCKED** by `STATUS.md`. No agent may implement or -claim document interpretation, candidate extraction, institutional admission, +This repository includes an owner-opened, bounded semantic candidate-extraction +slice defined by `docs/decisions/OIC-OWNER-DECISION-004.md`. No agent may claim +general document interpretation, institutional admission, Open Control Envelope generation, Rego compilation, or runtime semantic decisions under this work order. @@ -22,7 +22,9 @@ bash scripts/generate_sbom.sh bash scripts/wheel_smoke_test.sh ``` -Implemented CLI commands are limited to `oic validate-schema`, +The only authorized semantic production path is +`src/oic/candidate_extraction.py`, limited to the three synthetic Northstar +sources. Implemented CLI commands are limited to `oic validate-schema`, `oic verify-bootstrap`, `oic verify-manifest`, and `oic doctor`. They verify infrastructure contracts only. Do not invent semantic commands, hosted APIs, gateways, remote context services, or MCP servers. diff --git a/README.md b/README.md index 40027a4..091bff7 100644 --- a/README.md +++ b/README.md @@ -15,14 +15,17 @@ Target capability: > evidentiary standard required to authorize, explain, audit, or correct those > actions. -**What exists today:** a tested, non-semantic Python infrastructure package for +**What exists today:** a tested Python infrastructure package plus one bounded, +deterministic candidate extractor for three synthetic Northstar sources. The +infrastructure supports offline schema validation, historical bootstrap verification, current manifest verification, environment and gate diagnostics, and reproducible CI and SBOM checks. It is not a functioning institutional compiler. -**What is blocked:** semantic implementation remains blocked pending the corpus -provenance and ZTL/VEIP interface evidence listed in [`STATUS.md`](STATUS.md). -Nothing in this README opens that gate or promotes the repository's maturity. +**What is blocked:** every semantic capability beyond source → candidate +normative unit remains blocked. Institutional admission, IR/OCE generation, +runtime authorization, CA-3 extraction, and ZTL/VEIP runtime integration are +not authorized. See [`STATUS.md`](STATUS.md). ## Capability being developed @@ -295,17 +298,17 @@ verification, operational, and independent-review evidence. **STATUS: CURRENT** -Current capabilities include non-semantic schemas and contracts, +Current capabilities include schemas and contracts, manifest/integrity verification, the falsification harness, supply-chain -controls, and the benchmark preflight specification. +controls, the benchmark preflight specification, and the bounded synthetic +candidate extractor. -Exit requires complete governing-source rights and provenance plus sufficient -ZTL and VEIP provisional-interface evidence to open the semantic code-start -gate. +The global corpus manifest remains incomplete and ZTL/VEIP runtime integration +remains unauthorized. ### Stage 1 - Source-to-control reference -**STATUS: NEXT - BLOCKED by the semantic code-start gate** +**STATUS: FIRST BOUNDED SLICE OWNER-OPENED; REMAINDER BLOCKED** Target path: @@ -372,19 +375,21 @@ conformance, and community governance. ## Current phase -This repository authorizes contract-first, non-semantic infrastructure work. -It currently implements four infrastructure CLI commands: +This repository authorizes contract-first infrastructure work and the exact +bounded extraction scope in `docs/decisions/OIC-OWNER-DECISION-004.md`. It +currently implements four infrastructure CLI commands: - `oic validate-schema` - `oic verify-bootstrap` - `oic verify-manifest` - `oic doctor` -It does not implement document interpretation, candidate extraction, +It implements deterministic candidate extraction only for three authorized +synthetic sources. It does not implement general document interpretation, institutional admission, Institutional IR production, Open Control Envelope generation, Rego compilation, or runtime semantic decisions. -Run the safe non-semantic checks after the hash-locked installation described +Run the checks after the hash-locked installation described in [`docs/operations/CI.md`](docs/operations/CI.md): ```bash @@ -398,9 +403,9 @@ must not be normalized into success. ## First executable objective -The first semantic objective remains blocked. When its prerequisites are -authorized, a bounded set of public or synthetic procurement governing sources -is intended to flow through: +The first semantic objective is owner-opened only through source-anchored +candidate normative units for the synthetic Northstar source set. Later stages +remain blocked: ```text documents diff --git a/STATUS.md b/STATUS.md index fa649c1..49f2d4e 100644 --- a/STATUS.md +++ b/STATUS.md @@ -21,6 +21,17 @@ It does not authorize public quality, enterprise-readiness, legal-compliance, un | Public Lab restrictions visible before upload | PASS as specification | `LIMITATIONS.md`, `docs/architecture/LAB_RESTRICTIONS.md` | | Named owner for each implemented module | PASS for kickoff | `OWNERS.md` | -**Semantic implementation gate:** BLOCKED until the preflight corpus manifest and ZTL/VEIP provisional-interface records are completed. +SEMANTIC CODE-START GATE: +OWNER-OPENED AT +914830ceec70bde17004d2ccbbb13218ca44a89b + +FIRST AUTHORIZED SEMANTIC SCOPE: +SOURCE → CANDIDATE NORMATIVE UNIT +SYNTHETIC NORTHSTAR SOURCES ONLY + +Only the deterministic candidate-extraction slice is opened. Institutional +admission, IR/OCE generation, runtime authorization and integration, CA-3 +semantic extraction, and all production or benchmark claims remain blocked or +not established. Global manifest completeness remains `INCOMPLETE`. Infrastructure scaffolding and schema validation may proceed before that gate. diff --git a/benchmarks/preflight/semantic-001/EXPECTED-CANDIDATES.json b/benchmarks/preflight/semantic-001/EXPECTED-CANDIDATES.json new file mode 100644 index 0000000..f4d54a6 --- /dev/null +++ b/benchmarks/preflight/semantic-001/EXPECTED-CANDIDATES.json @@ -0,0 +1,29 @@ +{ + "classification": [ + "OWNER-SPECIFIED PREIMPLEMENTATION EXPECTATION", + "NOT AN OBSERVED BENCHMARK RESULT", + "NOT INDEPENDENTLY ADJUDICATED" + ], + "expected_total": 14, + "expected_by_source": { + "SYN-NS-GOV-1": 11, + "SYN-NS-PROC-1": 1, + "SYN-NS-AMEND-2": 2 + }, + "candidates": [ + {"label":"G1","source_id":"SYN-NS-GOV-1","unit_type":"definition","actor":"Requester","action":"means","object":"a fictional test actor","conditions":[]}, + {"label":"G2","source_id":"SYN-NS-GOV-1","unit_type":"definition","actor":"Approver","action":"means","object":"a fictional test actor listed in the matrix","conditions":[]}, + {"label":"G3","source_id":"SYN-NS-GOV-1","unit_type":"definition","actor":"Evidence","action":"means","object":"a deterministic record bound to the proposed action","conditions":[]}, + {"label":"G4","source_id":"SYN-NS-GOV-1","unit_type":"condition","actor":null,"action":"TEST_PURCHASE_LOW","object":"approval","conditions":["Approver-A"]}, + {"label":"G5","source_id":"SYN-NS-GOV-1","unit_type":"condition","actor":null,"action":"TEST_PURCHASE_HIGH","object":"approval","conditions":["Approver-A","Approver-B"]}, + {"label":"G6","source_id":"SYN-NS-GOV-1","unit_type":"delegation","actor":"Approver-A","action":"delegate","object":"TEST_PURCHASE_LOW to Delegate-A","conditions":[]}, + {"label":"G7","source_id":"SYN-NS-GOV-1","unit_type":"prohibition","actor":null,"action":"recognize","object":"other delegation","conditions":[]}, + {"label":"G8","source_id":"SYN-NS-GOV-1","unit_type":"evidence_duty","actor":null,"action":"proposal_carries_evidence","object":null,"conditions":[]}, + {"label":"G9","source_id":"SYN-NS-GOV-1","unit_type":"exception","actor":null,"action":"TEST_EMERGENCY","object":"omit Approver-B","conditions":["emergency record present","Approver-A approval present"]}, + {"label":"G10","source_id":"SYN-NS-GOV-1","unit_type":"discretion","actor":"Approver-A","action":"refer","object":"any proposal for human review","conditions":[]}, + {"label":"G11","source_id":"SYN-NS-GOV-1","unit_type":"prohibition","actor":null,"action":"treat referral as approval","object":null,"conditions":[]}, + {"label":"P1","source_id":"SYN-NS-PROC-1","unit_type":"condition","actor":null,"action":"TEST_PURCHASE_HIGH","object":"approval","conditions":["Approver-A"]}, + {"label":"A1","source_id":"SYN-NS-AMEND-2","unit_type":"condition","actor":null,"action":"TEST_PURCHASE_HIGH","object":"approval","conditions":["Approver-A","Approver-B"]}, + {"label":"A2","source_id":"SYN-NS-AMEND-2","unit_type":"prohibition","actor":null,"action":"use as current evidence","object":"SYNTHETIC-NORTHSTAR-PROCEDURE-v1","conditions":[]} + ] +} diff --git a/docs/decisions/OIC-OWNER-DECISION-004.md b/docs/decisions/OIC-OWNER-DECISION-004.md new file mode 100644 index 0000000..c4242e2 --- /dev/null +++ b/docs/decisions/OIC-OWNER-DECISION-004.md @@ -0,0 +1,22 @@ +# OIC Owner Decision 004 — First bounded semantic implementation + +- Decision date: 2026-08-28 +- Authorized base: `914830ceec70bde17004d2ccbbb13218ca44a89b` +- Gate disposition: `OPEN` for the scope below only +- Authorized production path: `src/oic/candidate_extraction.py` +- Authorized sources: `SYN-NS-GOV-1`, `SYN-NS-PROC-1`, `SYN-NS-AMEND-2` + +The owner authorizes a deterministic, source-anchored Source → Candidate +Normative Unit implementation for the three synthetic Northstar sources only. +Candidate output remains extracted, uncertain, unadmitted, and without machine +confidence. The stale procedure must remain visible as superseded. + +Institutional admission, Institutional IR, Open Control Envelope generation, +Rego or other policy-target generation, runtime authorization, OPA/Cedar/ +OpenFGA/Cerbos invocation, ZTL runtime integration, VEIP runtime integration, +CA-3 semantic extraction, network/model/API dependencies, production claims, +benchmark claims, and licensing or corpus-rights changes are not authorized. +Global manifest completeness remains `INCOMPLETE`. + +This decision does not permit the evaluator or implementation to extend its own +scope. Further semantic paths or sources require a separate owner decision. diff --git a/docs/gates/OIC-SEMANTIC-CODE-START-GATE-OPEN-v0.1.json b/docs/gates/OIC-SEMANTIC-CODE-START-GATE-OPEN-v0.1.json new file mode 100644 index 0000000..d4db74d --- /dev/null +++ b/docs/gates/OIC-SEMANTIC-CODE-START-GATE-OPEN-v0.1.json @@ -0,0 +1,21 @@ +{ + "record_id": "OIC-SEMANTIC-CODE-START-GATE-OPEN-v0.1", + "status": "OPEN", + "owner_decision": "docs/decisions/OIC-OWNER-DECISION-004.md", + "owner_decision_base_sha": "914830ceec70bde17004d2ccbbb13218ca44a89b", + "authorized_production_paths": ["src/oic/candidate_extraction.py"], + "authorized_source_ids": ["SYN-NS-GOV-1", "SYN-NS-PROC-1", "SYN-NS-AMEND-2"], + "authorized_scope": "SOURCE_TO_CANDIDATE_NORMATIVE_UNIT", + "forbidden_capabilities": [ + "INSTITUTIONAL_ADMISSION", + "INSTITUTIONAL_IR", + "OPEN_CONTROL_ENVELOPE", + "POLICY_TARGET_GENERATION", + "RUNTIME_AUTHORIZATION", + "ZTL_RUNTIME", + "VEIP_RUNTIME", + "CA_3_SEMANTIC_EXTRACTION", + "NETWORK_MODEL_API_DEPENDENCY" + ], + "global_manifest_status": "INCOMPLETE" +} diff --git a/docs/operations/FOUNDATION.md b/docs/operations/FOUNDATION.md index afdd1e7..ace883e 100644 --- a/docs/operations/FOUNDATION.md +++ b/docs/operations/FOUNDATION.md @@ -265,8 +265,9 @@ enforcement artifact (none exists to produce). ## 8. Current limitations -- **This is not a compiler.** No semantic implementation exists. See `LIMITATIONS.md`. -- **The semantic implementation gate is BLOCKED** and nothing here opens it. +- **This is not a compiler.** One bounded synthetic candidate extractor exists. +- **The semantic code-start gate is OWNER-OPENED** only for that extractor and + those three synthetic sources; institutional admission remains blocked. - **Preflight corpus provenance is OPEN.** `SOURCE_MANIFEST.csv` has no rows. - **ZTL and VEIP are PROVISIONAL / NOT CONFIGURED.** Their interfaces are unfrozen (ADR-009, ADR-010) and no adapter, container, or call exists. diff --git a/scripts/falsify_infrastructure.py b/scripts/falsify_infrastructure.py index fa2e624..caa80a0 100644 --- a/scripts/falsify_infrastructure.py +++ b/scripts/falsify_infrastructure.py @@ -23,8 +23,8 @@ "tests/contract/test_semantic_conformance.py::test_reject_duplicated_reason_codes", ), ( - "semantic code-start remains blocked", - "tests/contract/test_claims_discipline.py::test_operator_guide_states_the_semantic_gate_is_blocked", + "semantic code-start scope expansion rejection", + "tests/contract/test_semantic_code_start_gate_closure.py::test_owner_gate_refuses_allowlist_self_extension", ), ) diff --git a/scripts/verify_code_start_gate.py b/scripts/verify_code_start_gate.py index 99c75d9..4da511d 100644 --- a/scripts/verify_code_start_gate.py +++ b/scripts/verify_code_start_gate.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Fail-closed verifier for the non-semantic code-start evidence package.""" +"""Fail-closed verifier for the owner-opened bounded semantic code-start gate.""" from __future__ import annotations @@ -38,6 +38,9 @@ "src/oic/schemas.py", } ) +OWNER_OPEN_BASE = "914830ceec70bde17004d2ccbbb13218ca44a89b" +OWNER_AUTHORIZED_SEMANTIC_PATHS = frozenset({"src/oic/candidate_extraction.py"}) +AUTHORIZED_SOURCE_IDS = frozenset({"SYN-NS-GOV-1", "SYN-NS-PROC-1", "SYN-NS-AMEND-2"}) class GateEvidenceError(ValueError): @@ -50,7 +53,7 @@ def _require(condition: bool, message: str) -> None: def discover_unadmitted_production_paths(root: Path) -> list[str]: - """Compare tracked ``src/oic`` paths with the immutable pre-gate baseline.""" + """Compare tracked paths with the immutable baseline plus owner allowlist.""" git = shutil.which("git") if git is None: raise GateEvidenceError("cannot enumerate tracked src/oic production paths") @@ -63,8 +66,9 @@ def discover_unadmitted_production_paths(root: Path) -> list[str]: if result.returncode != 0: raise GateEvidenceError("cannot enumerate tracked src/oic production paths") current = {line for line in result.stdout.splitlines() if line} - added = sorted(current - ADMITTED_SRC_OIC_PATHS) - missing = sorted(ADMITTED_SRC_OIC_PATHS - current) + admitted = ADMITTED_SRC_OIC_PATHS | OWNER_AUTHORIZED_SEMANTIC_PATHS + added = sorted(current - admitted) + missing = sorted(admitted - current) return added + [f"MISSING:{path}" for path in missing] @@ -74,6 +78,7 @@ def validate_evidence( profile: dict[str, Any], veip: dict[str, Any], gate_text: str, + gate_record: dict[str, Any], source_bytes: dict[str, bytes], active_text: str, semantic_paths: list[str] | None = None, @@ -168,10 +173,19 @@ def validate_evidence( "GLOBAL REPOSITORY COMPLETENESS" in normalized_gate and "INCOMPLETE" in normalized_gate, "global incompleteness not explicit", ) + _require("READY FOR SEPARATE EXACT-HEAD REVIEW" in normalized_gate, "gate language invalid") + _require(gate_record.get("status") == "OPEN", "owner gate is not OPEN") + _require(gate_record.get("owner_decision_base_sha") == OWNER_OPEN_BASE, "wrong owner base SHA") _require( - "READY FOR SEPARATE EXACT-HEAD REVIEW\nNOT OPEN" in normalized_gate, "gate language invalid" + set(gate_record.get("authorized_production_paths", [])) == OWNER_AUTHORIZED_SEMANTIC_PATHS, + "gate production allowlist mismatch", ) - _require(not semantic_paths, "semantic implementation appeared before gate opening") + _require( + set(gate_record.get("authorized_source_ids", [])) == AUTHORIZED_SOURCE_IDS, + "gate source allowlist mismatch", + ) + _require(gate_record.get("global_manifest_status") == "INCOMPLETE", "gate escalates manifest") + _require(not semantic_paths, "production path exceeds owner authorization") def load_and_validate(root: Path) -> None: @@ -186,6 +200,7 @@ def load(path: str) -> dict[str, Any]: gate_text = (root / "docs/gates/OIC-SEMANTIC-CODE-START-GATE-CLOSURE-v0.1.md").read_text( encoding="utf-8" ) + gate_record = load("docs/gates/OIC-SEMANTIC-CODE-START-GATE-OPEN-v0.1.json") active_text = "\n".join( (root / path).read_text(encoding="utf-8") for path in ( @@ -204,6 +219,7 @@ def load(path: str) -> dict[str, Any]: profile, veip, gate_text, + gate_record, source_bytes, active_text, semantic_paths=semantic_paths, @@ -216,4 +232,4 @@ def load(path: str) -> dict[str, Any]: except (GateEvidenceError, OSError, KeyError, json.JSONDecodeError) as exc: print(f"FAIL semantic code-start gate evidence: {exc}") sys.exit(1) - print("PASS semantic code-start prerequisite evidence; gate remains NOT OPEN") + print("PASS semantic code-start gate is owner-opened for the exact bounded path and sources") diff --git a/src/oic/candidate_extraction.py b/src/oic/candidate_extraction.py new file mode 100644 index 0000000..51daf42 --- /dev/null +++ b/src/oic/candidate_extraction.py @@ -0,0 +1,314 @@ +"""Bounded deterministic candidate extraction for authorized synthetic sources. + +This module proposes source-anchored candidates. It does not admit meaning, +produce institutional IR or control envelopes, or invoke any runtime. +""" + +from __future__ import annotations + +import hashlib +import json +import re +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Final + +AUTHORIZED_SOURCES: Final[dict[str, tuple[str, str, str]]] = { + "SYN-NS-GOV-1": ( + "benchmarks/preflight/code-start-v0.1/sources/SYNTHETIC-NORTHSTAR-GOVERNANCE-v1.txt", + "b882459931ddf06fb647e6885c1ced368b4a5f45b6441df618484a30473a2a24", + "SYNTHETIC_CURRENT", + ), + "SYN-NS-PROC-1": ( + "benchmarks/preflight/code-start-v0.1/sources/SYNTHETIC-NORTHSTAR-PROCEDURE-v1.txt", + "cfb578274460cc17492f4b25a4db6893ce87a527e1207fe6a52b6384125ec073", + "SYNTHETIC_SUPERSEDED", + ), + "SYN-NS-AMEND-2": ( + "benchmarks/preflight/code-start-v0.1/sources/SYNTHETIC-NORTHSTAR-AMENDMENT-v2.txt", + "8e197bc9e01554853163dfb5f84ad807893f678b26673737c8a72ddf0c8daf47", + "SYNTHETIC_CURRENT", + ), +} + + +class CandidateExtractionError(ValueError): + """The bounded extraction input fails an integrity or scope invariant.""" + + +@dataclass(frozen=True) +class CandidateExtractionResult: + """Deterministic source nodes, candidate units, and non-interpretation diagnostics.""" + + nodes: tuple[dict[str, Any], ...] + units: tuple[dict[str, Any], ...] + diagnostics: tuple[dict[str, Any], ...] + + +def _sha256(payload: bytes) -> str: + return f"sha256:{hashlib.sha256(payload).hexdigest()}" + + +def _byte_offset(text: str, character_offset: int) -> int: + return len(text[:character_offset].encode("utf-8")) + + +def _node(source_id: str, text: str, start: int, end: int) -> dict[str, Any]: + quote = text[start:end] + start_byte = _byte_offset(text, start) + end_byte = _byte_offset(text, end) + return { + "node_id": f"{source_id}:span:{start_byte}:{end_byte}", + "source_id": source_id, + "parent_id": None, + "node_type": "clause", + "text": quote, + "page": None, + "bbox": None, + "content_hash": _sha256(quote.encode("utf-8")), + "references": [], + } + + +def _unit( + node: dict[str, Any], + *, + unit_type: str, + actor: str | None, + action: str | None, + object_: str | None, + conditions: list[str] | None = None, + evidence_requirements: list[str] | None = None, + lifecycle_state: str, +) -> dict[str, Any]: + anchor_projection = { + "source_id": node["source_id"], + "node_id": node["node_id"], + "content_hash": node["content_hash"], + } + anchor_id = _sha256( + json.dumps(anchor_projection, sort_keys=True, separators=(",", ":")).encode("utf-8") + ) + anchor = { + "anchor_id": f"anchor:{anchor_id}", + **anchor_projection, + "quote": node["text"], + "page": None, + "bbox": None, + } + projection = { + "unit_type": unit_type, + "actor": actor, + "action": action, + "object": object_, + "conditions": conditions or [], + "exceptions": [], + "evidence_requirements": evidence_requirements or [], + **anchor_projection, + } + unit_id = _sha256(json.dumps(projection, sort_keys=True, separators=(",", ":")).encode("utf-8")) + return { + "unit_id": f"cnu:{unit_id}", + "unit_type": unit_type, + "actor": actor, + "action": action, + "object": object_, + "conditions": conditions or [], + "exceptions": [], + "evidence_requirements": evidence_requirements or [], + "interpretation_state": "extracted", + "epistemic_state": "uncertain", + "lifecycle_state": lifecycle_state, + "confidence": None, + "alternatives": [], + "source_anchors": [anchor], + } + + +def _emit( + source_id: str, + text: str, + match: re.Match[str], + lifecycle: str, + **meaning: Any, # noqa: ANN401 - schema-shaped fields are heterogeneous by design +) -> tuple[dict[str, Any], dict[str, Any]]: + node = _node(source_id, text, match.start(), match.end()) + return node, _unit(node, lifecycle_state=lifecycle, **meaning) + + +def _extract_text(source_id: str, text: str, lifecycle: str) -> CandidateExtractionResult: + nodes: list[dict[str, Any]] = [] + units: list[dict[str, Any]] = [] + covered: list[tuple[int, int]] = [] + + def add( + pattern: str, + **meaning: Any, # noqa: ANN401 - passed directly to the typed schema constructor + ) -> None: + for match in re.finditer(pattern, text, flags=re.IGNORECASE | re.MULTILINE): + node, unit = _emit(source_id, text, match, lifecycle, **meaning) + nodes.append(node) + units.append(unit) + covered.append(match.span()) + + definition = r"(?P[A-Za-z][\w-]*) means (?P[^.]+)\." + for match in re.finditer(definition, text, flags=re.MULTILINE): + node, unit = _emit( + source_id, + text, + match, + lifecycle, + unit_type="definition", + actor=match.group("actor"), + action="means", + object_=re.sub(r"\s+", " ", match.group("object")).strip(), + ) + nodes.append(node) + units.append(unit) + covered.append(match.span()) + + for match in re.finditer( + r"TEST_PURCHASE_(?PLOW|HIGH) requires (?:both |only )?" + r"(?PApprover-[A-Z](?:\s+and\s+Approver-[A-Z])?)(?:\.|,)", + text, + flags=re.IGNORECASE | re.MULTILINE, + ): + conditions = re.findall(r"Approver-[A-Z]", match.group("approvers"), re.IGNORECASE) + node, unit = _emit( + source_id, + text, + match, + lifecycle, + unit_type="condition", + actor=None, + action=f"TEST_PURCHASE_{match.group('level').upper()}", + object_="approval", + conditions=conditions, + ) + nodes.append(node) + units.append(unit) + covered.append(match.span()) + + add( + r"Approver-A may delegate TEST_PURCHASE_LOW to Delegate-A\.", + unit_type="delegation", + actor="Approver-A", + action="delegate", + object_="TEST_PURCHASE_LOW to Delegate-A", + ) + add( + r"No other delegation\s+is recognized\.", + unit_type="prohibition", + actor=None, + action="recognize", + object_="other delegation", + ) + add( + r"every proposal carries its source identifier, amount,\s+requester identifier, " + r"and approval records\.", + unit_type="evidence_duty", + actor=None, + action="proposal_carries_evidence", + object_=None, + evidence_requirements=[ + "source identifier", + "amount", + "requester identifier", + "approval records", + ], + ) + add( + r"TEST_EMERGENCY may omit Approver-B only when an emergency record and\s+" + r"Approver-A approval are present\.", + unit_type="exception", + actor=None, + action="TEST_EMERGENCY", + object_="omit Approver-B", + conditions=["emergency record present", "Approver-A approval present"], + ) + add( + r"Approver-A may refer any proposal for human review\.", + unit_type="discretion", + actor="Approver-A", + action="refer", + object_="any proposal for human review", + ) + add( + r"Referral establishes no approval\.", + unit_type="prohibition", + actor=None, + action="treat referral as approval", + object_=None, + ) + add( + r"A stale procedure must be refused as\s+current evidence\.", + unit_type="prohibition", + actor=None, + action="use as current evidence", + object_="SYNTHETIC-NORTHSTAR-PROCEDURE-v1", + ) + + diagnostics: list[dict[str, Any]] = [] + for match in re.finditer(r"[^\n](?:.*(?:\n(?!\n).*)*)", text): + paragraph = match.group(0) + if any(match.start() < end and match.end() > start for start, end in covered): + continue + normalized = re.sub(r"\s+", " ", paragraph).strip() + if not normalized: + continue + node = _node(source_id, text, match.start(), match.end()) + nodes.append(node) + if re.search(r"\b(?:should|may|must|requires?|approval|authority)\b", normalized, re.I): + code = "UNSUPPORTED_NORMATIVE_PATTERN" + elif normalized.startswith(("SYNTHETIC", "Document:", "Version:", "Created:")): + code = "NON_NORMATIVE_METADATA" + else: + code = "DOCUMENT_CONTROL_METADATA" + diagnostics.append({"code": code, "node_id": node["node_id"], "message": normalized}) + + ordered_nodes = tuple(sorted(nodes, key=lambda item: item["node_id"])) + ordered_units = tuple(sorted(units, key=lambda item: item["unit_id"])) + ordered_diagnostics = tuple(sorted(diagnostics, key=lambda item: item["node_id"])) + return CandidateExtractionResult(ordered_nodes, ordered_units, ordered_diagnostics) + + +def extract_authorized_candidates(root: Path) -> CandidateExtractionResult: + """Verify and extract exactly the owner-authorized synthetic source set.""" + source_set_path = root / "benchmarks/preflight/code-start-v0.1/SOURCE-SET.json" + source_set = json.loads(source_set_path.read_text(encoding="utf-8")) + records = {item["source_id"]: item for item in source_set.get("sources", [])} + if source_set.get("global_manifest_status") != "INCOMPLETE": + raise CandidateExtractionError("global manifest status must remain INCOMPLETE") + + all_nodes: list[dict[str, Any]] = [] + all_units: list[dict[str, Any]] = [] + all_diagnostics: list[dict[str, Any]] = [] + for source_id, (path, digest, effective_state) in AUTHORIZED_SOURCES.items(): + record = records.get(source_id) + expected = { + "path": path, + "sha256": digest, + "origin_classification": "SYNTHETIC_FICTIONAL", + "benchmark_authority": "SYNTHETIC_BENCHMARK_ONLY", + "issuer": "Veraxis synthetic fixture generator", + "effective_state": effective_state, + } + if record is None or any(record.get(key) != value for key, value in expected.items()): + raise CandidateExtractionError(f"source-set mismatch: {source_id}") + payload = (root / path).read_bytes() + if hashlib.sha256(payload).hexdigest() != digest: + raise CandidateExtractionError(f"source digest mismatch: {source_id}") + text = payload.decode("utf-8") + if f"Effective state: {effective_state}" not in text: + raise CandidateExtractionError(f"source header/currentness mismatch: {source_id}") + lifecycle = "superseded" if effective_state == "SYNTHETIC_SUPERSEDED" else "proposed" + result = _extract_text(source_id, text, lifecycle) + all_nodes.extend(result.nodes) + all_units.extend(result.units) + all_diagnostics.extend(result.diagnostics) + + return CandidateExtractionResult( + tuple(sorted(all_nodes, key=lambda item: item["node_id"])), + tuple(sorted(all_units, key=lambda item: item["unit_id"])), + tuple(sorted(all_diagnostics, key=lambda item: item["node_id"])), + ) diff --git a/tests/contract/test_canada_acquisition_preflight.py b/tests/contract/test_canada_acquisition_preflight.py index cd4772e..bc500a5 100644 --- a/tests/contract/test_canada_acquisition_preflight.py +++ b/tests/contract/test_canada_acquisition_preflight.py @@ -33,7 +33,6 @@ RESOLUTION_RELPATH = "benchmarks/preflight/canada/OFFICIAL-SOURCE-RESOLUTION-v0.1.md" CLOSURE_SUMMARY_RELPATH = "benchmarks/preflight/canada/PREFLIGHT-CLOSURE-SUMMARY-v0.1.json" -EXPECTED_STATUS_SHA256 = "4a6894ca72ae8d2efcf48b3d25f8aca3bc1e2e86b6aa6aa5b38af31a52c7fde8" # Repinned when the Canada rights freeze added the CA-3 row. The preflight PR left the # manifest header-only at c3ea6162cbeb9a5814f543ec23a02fecacad72053d90258162687ad3f48a2db2. EXPECTED_SOURCE_MANIFEST_SHA256 = "7fc66adf8940758702b0b469c5dbfcf1c4b5ee2241f217794da7b9c10305e49e" @@ -1091,8 +1090,10 @@ def test_no_source_bytes_are_tracked(repo_root: Path) -> None: assert not any(path.startswith(".local/") for path in tracked) -def test_governing_files_are_byte_identical(repo_root: Path) -> None: - assert _sha256(repo_root / "STATUS.md") == EXPECTED_STATUS_SHA256 +def test_canada_governing_files_remain_unchanged(repo_root: Path) -> None: + status = (repo_root / "STATUS.md").read_text("utf-8") + assert "SYNTHETIC NORTHSTAR SOURCES ONLY" in status + assert "CA-3\nsemantic extraction" in status assert ( _sha256(repo_root / "benchmarks/preflight/SOURCE_MANIFEST.csv") == EXPECTED_SOURCE_MANIFEST_SHA256 diff --git a/tests/contract/test_claims_discipline.py b/tests/contract/test_claims_discipline.py index 7789df6..aea5491 100644 --- a/tests/contract/test_claims_discipline.py +++ b/tests/contract/test_claims_discipline.py @@ -115,13 +115,13 @@ def test_readiness_claims_appear_only_when_denied(documents: dict[str, str], phr assert offenders == [], f"un-negated readiness claim: {offenders}" -def test_operator_guide_states_the_semantic_gate_is_blocked(documents: dict[str, str]) -> None: +def test_operator_guide_bounds_the_open_semantic_gate(documents: dict[str, str]) -> None: text = documents["docs/operations/FOUNDATION.md"] # Markdown emphasis may wrap either the word or the whole sentence, so match the # claim with emphasis markers stripped. plain = text.replace("*", "") - assert "semantic implementation gate is blocked" in plain - assert "no semantic implementation exists" in plain + assert "semantic code-start gate is owner-opened" in plain + assert "institutional admission remains blocked" in plain def test_operator_guide_states_ztl_and_veip_are_provisional(documents: dict[str, str]) -> None: @@ -205,10 +205,13 @@ def test_claims_bearing_documents_are_unchanged_by_this_work_order(repo_root: Pa from oic.baseline import BOOTSTRAP_COMMIT - for relpath in ("CLAIMS.md", "LIMITATIONS.md", "STATUS.md", "OWNERS.md"): + for relpath in ("CLAIMS.md", "LIMITATIONS.md", "OWNERS.md"): committed = subprocess.run( ["git", "-C", str(repo_root), "cat-file", "blob", f"{BOOTSTRAP_COMMIT}:{relpath}"], capture_output=True, check=True, ).stdout assert (repo_root / relpath).read_bytes() == committed, relpath + status = (repo_root / "STATUS.md").read_text("utf-8") + assert "OWNER-OPENED AT" in status + assert "Global manifest completeness remains `INCOMPLETE`" in status diff --git a/tests/contract/test_semantic_code_start_gate_closure.py b/tests/contract/test_semantic_code_start_gate_closure.py index 9024078..622eaf7 100644 --- a/tests/contract/test_semantic_code_start_gate_closure.py +++ b/tests/contract/test_semantic_code_start_gate_closure.py @@ -41,6 +41,7 @@ def load(path: str) -> dict[str, Any]: "gate_text": ( repo_root / "docs/gates/OIC-SEMANTIC-CODE-START-GATE-CLOSURE-v0.1.md" ).read_text(encoding="utf-8"), + "gate_record": load("docs/gates/OIC-SEMANTIC-CODE-START-GATE-OPEN-v0.1.json"), "source_bytes": { item["source_id"]: (repo_root / item["path"]).read_bytes() for item in source_set["sources"] @@ -145,6 +146,7 @@ def _isolated_gate_tree(repo_root: Path, tmp_path: Path) -> Path: "docs/contracts/WARRANT-CONTRACT-v0.1.md", "adr/ADR-013.md", "docs/gates/OIC-SEMANTIC-CODE-START-GATE-CLOSURE-v0.1.md", + "docs/gates/OIC-SEMANTIC-CODE-START-GATE-OPEN-v0.1.json", ) for relpath in required: source = repo_root / relpath @@ -155,6 +157,8 @@ def _isolated_gate_tree(repo_root: Path, tmp_path: Path) -> Path: else: shutil.copy2(source, target) subprocess.run(["git", "init", "-q", str(root)], check=True) + for cache in root.rglob("__pycache__"): + shutil.rmtree(cache) subprocess.run(["git", "-C", str(root), "add", "."], check=True) return root @@ -164,6 +168,17 @@ def test_t1_production_detector_accepts_exact_baseline(repo_root: Path) -> None: assert module.discover_unadmitted_production_paths(repo_root) == [] +def test_owner_gate_requires_exact_authorized_base(repo_root: Path) -> None: + _reject(repo_root, lambda e: e["gate_record"].__setitem__("owner_decision_base_sha", "0" * 40)) + + +def test_owner_gate_refuses_allowlist_self_extension(repo_root: Path) -> None: + _reject( + repo_root, + lambda e: e["gate_record"]["authorized_production_paths"].append("src/oic/helper.py"), + ) + + @pytest.mark.parametrize( "relpath", ("src/oic/semantic_parser.py", "src/oic/helper.py", "src/oic/newmodule/engine.py"), @@ -179,7 +194,7 @@ def test_t2_t3_t4_detector_refuses_any_new_tracked_path( subprocess.run(["git", "-C", str(root), "add", relpath], check=True) detected = module.discover_unadmitted_production_paths(root) assert relpath in detected - with pytest.raises(module.GateEvidenceError, match="semantic implementation appeared"): + with pytest.raises(module.GateEvidenceError, match="production path exceeds"): module.load_and_validate(root) @@ -193,7 +208,7 @@ def test_t5_detector_refuses_rename_or_substitution(repo_root: Path, tmp_path: P detected = module.discover_unadmitted_production_paths(root) assert "src/oic/replacement.py" in detected assert "MISSING:src/oic/paths.py" in detected - with pytest.raises(module.GateEvidenceError, match="semantic implementation appeared"): + with pytest.raises(module.GateEvidenceError, match="production path exceeds"): module.load_and_validate(root) @@ -205,7 +220,7 @@ def test_t6_load_and_validate_invokes_discovery_without_manual_parameter( added = root / "src/oic/helper.py" added.write_text("# innocuous name, unauthorized path\n", encoding="utf-8") subprocess.run(["git", "-C", str(root), "add", "src/oic/helper.py"], check=True) - with pytest.raises(module.GateEvidenceError, match="semantic implementation appeared"): + with pytest.raises(module.GateEvidenceError, match="production path exceeds"): module.load_and_validate(root) diff --git a/tests/contract/test_warrant_contract.py b/tests/contract/test_warrant_contract.py index b5abd09..00657a0 100644 --- a/tests/contract/test_warrant_contract.py +++ b/tests/contract/test_warrant_contract.py @@ -1301,11 +1301,12 @@ def test_no_ztl_or_veip_code_exists_or_is_imported(repo_root: Path) -> None: assert "verify_fixtures" not in validator_text -def test_this_work_order_added_no_source_module(repo_root: Path) -> None: +def test_only_owner_authorized_candidate_source_module_was_added(repo_root: Path) -> None: modules = {path.name for path in (repo_root / "src" / "oic").glob("*.py")} assert modules == { "__init__.py", "baseline.py", + "candidate_extraction.py", "cli.py", "doctor.py", "errors.py", @@ -1329,15 +1330,11 @@ def test_draft_schemas_are_byte_identical_to_the_bootstrap(repo_root: Path) -> N assert path.read_bytes() == committed, relative -def test_status_md_is_byte_identical_to_the_bootstrap(repo_root: Path) -> None: - from oic.baseline import BOOTSTRAP_COMMIT - - committed = subprocess.run( - ["git", "-C", str(repo_root), "cat-file", "blob", f"{BOOTSTRAP_COMMIT}:STATUS.md"], - capture_output=True, - check=True, - ).stdout - assert (repo_root / "STATUS.md").read_bytes() == committed +def test_status_md_records_exact_bounded_gate_transition(repo_root: Path) -> None: + status = (repo_root / "STATUS.md").read_text("utf-8") + assert "914830ceec70bde17004d2ccbbb13218ca44a89b" in status + assert "SOURCE → CANDIDATE NORMATIVE UNIT" in status + assert "SYNTHETIC NORTHSTAR SOURCES ONLY" in status def test_proposed_schemas_are_not_in_the_draft_directory(repo_root: Path) -> None: diff --git a/tests/unit/test_candidate_extraction.py b/tests/unit/test_candidate_extraction.py new file mode 100644 index 0000000..72dae5e --- /dev/null +++ b/tests/unit/test_candidate_extraction.py @@ -0,0 +1,218 @@ +"""SEMANTIC-001 fail-closed tests for bounded candidate extraction.""" + +from __future__ import annotations + +import hashlib +import json +import shutil +from collections import Counter +from pathlib import Path +from typing import Any + +import pytest +from jsonschema import Draft202012Validator +from referencing import Registry, Resource +from referencing.jsonschema import DRAFT202012 + +from oic.candidate_extraction import ( + CandidateExtractionError, + CandidateExtractionResult, + _extract_text, + extract_authorized_candidates, +) + + +def _result(repo_root: Path) -> CandidateExtractionResult: + return extract_authorized_candidates(repo_root) + + +def _isolated(repo_root: Path, tmp_path: Path) -> Path: + root = tmp_path / "repo" + for relpath in ( + "benchmarks/preflight/code-start-v0.1/SOURCE-SET.json", + "benchmarks/preflight/code-start-v0.1/sources", + ): + source = repo_root / relpath + target = root / relpath + target.parent.mkdir(parents=True, exist_ok=True) + if source.is_dir(): + shutil.copytree(source, target) + else: + shutil.copy2(source, target) + return root + + +def _schema_validator(repo_root: Path, name: str) -> Draft202012Validator: + documents: dict[str, Any] = {} + for path in (repo_root / "schemas/draft").glob("*.schema.json"): + document = json.loads(path.read_text("utf-8")) + documents[document["$id"]] = document + registry: Registry[Any] = Registry() + for identifier, document in documents.items(): + registry = registry.with_resource(identifier, Resource.from_contents(document, DRAFT202012)) + schema = documents[f"https://openinstitutionalcompiler.org/schemas/draft/{name}"] + return Draft202012Validator(schema, registry=registry) + + +def test_expected_candidate_set_and_counts(repo_root: Path) -> None: + result = _result(repo_root) + fixture = json.loads( + (repo_root / "benchmarks/preflight/semantic-001/EXPECTED-CANDIDATES.json").read_text( + "utf-8" + ) + ) + observed = { + ( + unit["source_anchors"][0]["source_id"], + unit["unit_type"], + unit["actor"], + unit["action"], + unit["object"], + tuple(unit["conditions"]), + ) + for unit in result.units + } + expected = { + ( + item["source_id"], + item["unit_type"], + item["actor"], + item["action"], + item["object"], + tuple(item["conditions"]), + ) + for item in fixture["candidates"] + } + assert observed == expected + assert len(result.units) == fixture["expected_total"] == 14 + assert Counter(u["source_anchors"][0]["source_id"] for u in result.units) == { + "SYN-NS-GOV-1": 11, + "SYN-NS-PROC-1": 1, + "SYN-NS-AMEND-2": 2, + } + + +def test_nodes_units_and_anchors_validate_and_refer(repo_root: Path) -> None: + result = _result(repo_root) + node_validator = _schema_validator(repo_root, "source-node.schema.json") + unit_validator = _schema_validator(repo_root, "candidate-normative-unit.schema.json") + anchor_validator = _schema_validator(repo_root, "source-anchor.schema.json") + nodes = {node["node_id"]: node for node in result.nodes} + assert len(nodes) == len(result.nodes) + for node in result.nodes: + node_validator.validate(node) + for unit in result.units: + unit_validator.validate(unit) + assert len(unit["source_anchors"]) == 1 + anchor = unit["source_anchors"][0] + anchor_validator.validate(anchor) + node = nodes[anchor["node_id"]] + digest = f"sha256:{hashlib.sha256(anchor['quote'].encode()).hexdigest()}" + assert anchor["source_id"] == node["source_id"] + assert anchor["quote"] == node["text"] + assert anchor["content_hash"] == node["content_hash"] == digest + + +def test_extraction_is_byte_deterministic(repo_root: Path) -> None: + first = _result(repo_root) + second = _result(repo_root) + assert first == second + assert json.dumps(first.nodes, sort_keys=True) == json.dumps(second.nodes, sort_keys=True) + assert json.dumps(first.units, sort_keys=True) == json.dumps(second.units, sort_keys=True) + + +def test_machine_state_ceilings_and_no_admission_artifacts(repo_root: Path) -> None: + result = _result(repo_root) + assert all(unit["interpretation_state"] == "extracted" for unit in result.units) + assert all(unit["epistemic_state"] == "uncertain" for unit in result.units) + assert all(unit["confidence"] is None for unit in result.units) + assert all(unit["alternatives"] == [] for unit in result.units) + assert not hasattr(result, "admissions") + assert not hasattr(result, "institutional_ir") + assert not hasattr(result, "control_envelopes") + + +def test_currentness_stale_visibility_and_independent_support(repo_root: Path) -> None: + result = _result(repo_root) + high = [u for u in result.units if u["action"] == "TEST_PURCHASE_HIGH"] + by_source = {u["source_anchors"][0]["source_id"]: u for u in high} + assert by_source["SYN-NS-PROC-1"]["lifecycle_state"] == "superseded" + assert by_source["SYN-NS-GOV-1"]["lifecycle_state"] == "proposed" + assert by_source["SYN-NS-AMEND-2"]["lifecycle_state"] == "proposed" + assert by_source["SYN-NS-GOV-1"]["unit_id"] != by_source["SYN-NS-AMEND-2"]["unit_id"] + + +def test_emergency_and_referral_meanings_remain_separate(repo_root: Path) -> None: + result = _result(repo_root) + emergency = next(u for u in result.units if u["action"] == "TEST_EMERGENCY") + assert emergency["conditions"] == ["emergency record present", "Approver-A approval present"] + referral = [u for u in result.units if u["action"] in {"refer", "treat referral as approval"}] + assert len(referral) == 2 + + +def test_unsupported_normative_looking_text_is_diagnostic_only() -> None: + result = _extract_text( + "SYN-TEST-NEW", + "Managers should generally use reasonable judgment.", + "proposed", + ) + assert result.units == () + assert len(result.nodes) == 1 + assert result.diagnostics[0]["code"] == "UNSUPPORTED_NORMATIVE_PATTERN" + + +def test_controlled_grammar_is_not_keyed_to_northstar_identity() -> None: + result = _extract_text( + "SYN-OTHER-1", + "Operator means a fictional reviewer. TEST_PURCHASE_LOW requires Approver-Z.", + "proposed", + ) + assert {(u["unit_type"], u["actor"], u["action"]) for u in result.units} == { + ("definition", "Operator", "means"), + ("condition", None, "TEST_PURCHASE_LOW"), + } + + +def test_mutated_source_bytes_are_refused(repo_root: Path, tmp_path: Path) -> None: + root = _isolated(repo_root, tmp_path) + path = root / ( + "benchmarks/preflight/code-start-v0.1/sources/SYNTHETIC-NORTHSTAR-GOVERNANCE-v1.txt" + ) + path.write_text(path.read_text("utf-8") + "mutation", encoding="utf-8") + with pytest.raises(CandidateExtractionError, match="source digest mismatch"): + extract_authorized_candidates(root) + + +@pytest.mark.parametrize("field", ["path", "sha256", "source_id"]) +def test_source_set_mismatch_is_refused(repo_root: Path, tmp_path: Path, field: str) -> None: + root = _isolated(repo_root, tmp_path) + path = root / "benchmarks/preflight/code-start-v0.1/SOURCE-SET.json" + source_set = json.loads(path.read_text("utf-8")) + item = next(i for i in source_set["sources"] if i["source_id"] == "SYN-NS-GOV-1") + item[field] = "wrong" + path.write_text(json.dumps(source_set), encoding="utf-8") + with pytest.raises(CandidateExtractionError, match="source-set mismatch"): + extract_authorized_candidates(root) + + +def test_header_currentness_mismatch_is_refused_before_meaning( + repo_root: Path, tmp_path: Path +) -> None: + root = _isolated(repo_root, tmp_path) + source_path = root / ( + "benchmarks/preflight/code-start-v0.1/sources/SYNTHETIC-NORTHSTAR-PROCEDURE-v1.txt" + ) + payload = source_path.read_text("utf-8").replace("SYNTHETIC_SUPERSEDED", "SYNTHETIC_CURRENT") + source_path.write_text(payload, encoding="utf-8") + set_path = root / "benchmarks/preflight/code-start-v0.1/SOURCE-SET.json" + source_set = json.loads(set_path.read_text("utf-8")) + item = next(i for i in source_set["sources"] if i["source_id"] == "SYN-NS-PROC-1") + item["sha256"] = hashlib.sha256(payload.encode()).hexdigest() + set_path.write_text(json.dumps(source_set), encoding="utf-8") + with pytest.raises(CandidateExtractionError, match="source-set mismatch"): + extract_authorized_candidates(root) + + +def test_ca3_is_outside_semantic_scope(repo_root: Path) -> None: + result = _result(repo_root) + assert all(node["source_id"] != "CA-3" for node in result.nodes) From 525a0bf26c39c42980f83b48ca82d984995b1dce Mon Sep 17 00:00:00 2001 From: Arkadiy Miteiko Date: Fri, 28 Aug 2026 21:34:30 -0400 Subject: [PATCH 2/6] test: rebind historical Canada guards to gate-open currentness --- tests/contract/test_canada_acquisition_freeze.py | 8 ++++++-- tests/contract/test_canada_rights_resolution_dossier.py | 9 ++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/contract/test_canada_acquisition_freeze.py b/tests/contract/test_canada_acquisition_freeze.py index 47c3f61..266e955 100644 --- a/tests/contract/test_canada_acquisition_freeze.py +++ b/tests/contract/test_canada_acquisition_freeze.py @@ -446,7 +446,9 @@ def test_mutation_blocked_source_with_a_freeze_entry_fails_verification( # -------------------------------------------------------------------------- -def test_status_and_draft_schemas_are_untouched(repo_root: Path) -> None: +def test_gate_currentness_update_preserves_canada_and_draft_schema_boundaries( + repo_root: Path, +) -> None: changed = subprocess.run( ["git", "diff", "--name-only", "d99a38510e51a36972a414cadd0e44d49a04227c...HEAD"], cwd=repo_root, @@ -454,7 +456,9 @@ def test_status_and_draft_schemas_are_untouched(repo_root: Path) -> None: capture_output=True, text=True, ).stdout.splitlines() - assert "STATUS.md" not in changed + status = (repo_root / "STATUS.md").read_text(encoding="utf-8") + assert "SYNTHETIC NORTHSTAR SOURCES ONLY" in status + assert "CA-3\nsemantic extraction" in status assert not any(path.startswith("schemas/draft/") for path in changed) allowed_contract_updates = { "docs/contracts/VEIP-CODE-START-BOUNDARY-v0.1.json", diff --git a/tests/contract/test_canada_rights_resolution_dossier.py b/tests/contract/test_canada_rights_resolution_dossier.py index e8d9dcc..198d939 100644 --- a/tests/contract/test_canada_rights_resolution_dossier.py +++ b/tests/contract/test_canada_rights_resolution_dossier.py @@ -64,7 +64,6 @@ f"{FREEZE_RELDIR}/receipts/CA-3.receipt.json", f"{FREEZE_RELDIR}/sources/CA-3.xml", MANIFEST_RELPATH, - "STATUS.md", ) @@ -608,9 +607,13 @@ def test_no_disposition_changed_and_all_ten_remain_blocked(rights: dict[str, Any assert sorted(blocked) == sorted(UNRESOLVED) -def test_status_and_draft_schemas_are_unchanged(repo_root: Path) -> None: +def test_gate_currentness_update_preserves_rights_and_draft_schema_boundaries( + repo_root: Path, +) -> None: changed = _changed_files(repo_root) - assert "STATUS.md" not in changed + status = (repo_root / "STATUS.md").read_text(encoding="utf-8") + assert "SYNTHETIC NORTHSTAR SOURCES ONLY" in status + assert "CA-3\nsemantic extraction" in status assert not any(path.startswith("schemas/draft/") for path in changed) allowed_contract_updates = { "docs/contracts/VEIP-CODE-START-BOUNDARY-v0.1.json", From d4fe412b9be0e60a138fe4679266fe405d285090 Mon Sep 17 00:00:00 2001 From: Arkadiy Miteiko Date: Fri, 28 Aug 2026 21:37:42 -0400 Subject: [PATCH 3/6] fix: align semantic gate record with owner order --- docs/decisions/OIC-OWNER-DECISION-004.md | 42 ++++++++++++++----- ...IC-SEMANTIC-CODE-START-GATE-OPEN-v0.1.json | 8 ++-- scripts/verify_code_start_gate.py | 10 +++-- .../test_semantic_code_start_gate_closure.py | 25 +++++++++-- 4 files changed, 65 insertions(+), 20 deletions(-) diff --git a/docs/decisions/OIC-OWNER-DECISION-004.md b/docs/decisions/OIC-OWNER-DECISION-004.md index c4242e2..f649145 100644 --- a/docs/decisions/OIC-OWNER-DECISION-004.md +++ b/docs/decisions/OIC-OWNER-DECISION-004.md @@ -2,21 +2,43 @@ - Decision date: 2026-08-28 - Authorized base: `914830ceec70bde17004d2ccbbb13218ca44a89b` +- Scope: `BOUNDED SEMANTIC CODE START` - Gate disposition: `OPEN` for the scope below only - Authorized production path: `src/oic/candidate_extraction.py` - Authorized sources: `SYN-NS-GOV-1`, `SYN-NS-PROC-1`, `SYN-NS-AMEND-2` -The owner authorizes a deterministic, source-anchored Source → Candidate -Normative Unit implementation for the three synthetic Northstar sources only. -Candidate output remains extracted, uncertain, unadmitted, and without machine -confidence. The stale procedure must remain visible as superseded. +## Exact owner authorization + +> I authorize the OIC semantic code-start gate to OPEN on +> Institutional-Compiler main at exact commit +> 914830ceec70bde17004d2ccbbb13218ca44a89b. +> +> This authorization permits bounded semantic implementation under +> TDD-OIC-001 v1.1 and the admitted gate constraints. +> +> It does not authorize institutional admission, runtime authorization, +> OCE execution, Rego/OPA execution, ZTL runtime integration, VEIP runtime +> integration, production claims, or benchmark claims unless separately +> authorized. + +## Authorization boundary -Institutional admission, Institutional IR, Open Control Envelope generation, -Rego or other policy-target generation, runtime authorization, OPA/Cedar/ -OpenFGA/Cerbos invocation, ZTL runtime integration, VEIP runtime integration, -CA-3 semantic extraction, network/model/API dependencies, production claims, -benchmark claims, and licensing or corpus-rights changes are not authorized. -Global manifest completeness remains `INCOMPLETE`. +- First authorized semantic production path: `src/oic/candidate_extraction.py` +- Authorized source IDs: `SYN-NS-GOV-1`, `SYN-NS-PROC-1`, `SYN-NS-AMEND-2` +- CA-3 semantic extraction: `NOT AUTHORIZED` +- Institutional admission: `NOT AUTHORIZED` +- Institutional IR: `NOT AUTHORIZED` +- OCE generation or execution: `NOT AUTHORIZED` +- Rego/OPA: `NOT AUTHORIZED` +- ZTL runtime: `NOT AUTHORIZED` +- VEIP runtime: `NOT AUTHORIZED` +- Production claims: `NOT AUTHORIZED` +- Benchmark claims: `NOT AUTHORIZED` + +Candidate output remains extracted, uncertain, unadmitted, and without machine +confidence. The stale procedure remains visible as superseded. Network/model/API +dependencies and licensing or corpus-rights changes are not authorized. Global +manifest completeness remains `INCOMPLETE`. This decision does not permit the evaluator or implementation to extend its own scope. Further semantic paths or sources require a separate owner decision. diff --git a/docs/gates/OIC-SEMANTIC-CODE-START-GATE-OPEN-v0.1.json b/docs/gates/OIC-SEMANTIC-CODE-START-GATE-OPEN-v0.1.json index d4db74d..0e9ef6d 100644 --- a/docs/gates/OIC-SEMANTIC-CODE-START-GATE-OPEN-v0.1.json +++ b/docs/gates/OIC-SEMANTIC-CODE-START-GATE-OPEN-v0.1.json @@ -1,9 +1,9 @@ { - "record_id": "OIC-SEMANTIC-CODE-START-GATE-OPEN-v0.1", - "status": "OPEN", + "gate_id": "OIC-SEMANTIC-CODE-START-GATE-OPEN-v0.1", + "state": "OPEN", "owner_decision": "docs/decisions/OIC-OWNER-DECISION-004.md", - "owner_decision_base_sha": "914830ceec70bde17004d2ccbbb13218ca44a89b", - "authorized_production_paths": ["src/oic/candidate_extraction.py"], + "authorization_base_sha": "914830ceec70bde17004d2ccbbb13218ca44a89b", + "authorized_semantic_paths": ["src/oic/candidate_extraction.py"], "authorized_source_ids": ["SYN-NS-GOV-1", "SYN-NS-PROC-1", "SYN-NS-AMEND-2"], "authorized_scope": "SOURCE_TO_CANDIDATE_NORMATIVE_UNIT", "forbidden_capabilities": [ diff --git a/scripts/verify_code_start_gate.py b/scripts/verify_code_start_gate.py index 4da511d..51b73bf 100644 --- a/scripts/verify_code_start_gate.py +++ b/scripts/verify_code_start_gate.py @@ -174,10 +174,14 @@ def validate_evidence( "global incompleteness not explicit", ) _require("READY FOR SEPARATE EXACT-HEAD REVIEW" in normalized_gate, "gate language invalid") - _require(gate_record.get("status") == "OPEN", "owner gate is not OPEN") - _require(gate_record.get("owner_decision_base_sha") == OWNER_OPEN_BASE, "wrong owner base SHA") _require( - set(gate_record.get("authorized_production_paths", [])) == OWNER_AUTHORIZED_SEMANTIC_PATHS, + gate_record.get("gate_id") == "OIC-SEMANTIC-CODE-START-GATE-OPEN-v0.1", + "wrong owner gate identity", + ) + _require(gate_record.get("state") == "OPEN", "owner gate is not OPEN") + _require(gate_record.get("authorization_base_sha") == OWNER_OPEN_BASE, "wrong owner base SHA") + _require( + set(gate_record.get("authorized_semantic_paths", [])) == OWNER_AUTHORIZED_SEMANTIC_PATHS, "gate production allowlist mismatch", ) _require( diff --git a/tests/contract/test_semantic_code_start_gate_closure.py b/tests/contract/test_semantic_code_start_gate_closure.py index 622eaf7..58c2abb 100644 --- a/tests/contract/test_semantic_code_start_gate_closure.py +++ b/tests/contract/test_semantic_code_start_gate_closure.py @@ -69,6 +69,25 @@ def test_current_evidence_is_valid(repo_root: Path) -> None: _module(repo_root).validate_evidence(**_evidence(repo_root)) +def test_owner_decision_preserves_exact_authorization(repo_root: Path) -> None: + decision = (repo_root / "docs/decisions/OIC-OWNER-DECISION-004.md").read_text( + encoding="utf-8" + ) + exact_authorization = """I authorize the OIC semantic code-start gate to OPEN on +Institutional-Compiler main at exact commit +914830ceec70bde17004d2ccbbb13218ca44a89b. + +This authorization permits bounded semantic implementation under +TDD-OIC-001 v1.1 and the admitted gate constraints. + +It does not authorize institutional admission, runtime authorization, +OCE execution, Rego/OPA execution, ZTL runtime integration, VEIP runtime +integration, production claims, or benchmark claims unless separately +authorized.""" + unquoted = "\n".join(line.removeprefix("> ").removeprefix(">") for line in decision.splitlines()) + assert exact_authorization in unquoted + + def test_refuses_synthetic_as_real_authority(repo_root: Path) -> None: _reject( repo_root, @@ -169,19 +188,19 @@ def test_t1_production_detector_accepts_exact_baseline(repo_root: Path) -> None: def test_owner_gate_requires_exact_authorized_base(repo_root: Path) -> None: - _reject(repo_root, lambda e: e["gate_record"].__setitem__("owner_decision_base_sha", "0" * 40)) + _reject(repo_root, lambda e: e["gate_record"].__setitem__("authorization_base_sha", "0" * 40)) def test_owner_gate_refuses_allowlist_self_extension(repo_root: Path) -> None: _reject( repo_root, - lambda e: e["gate_record"]["authorized_production_paths"].append("src/oic/helper.py"), + lambda e: e["gate_record"]["authorized_semantic_paths"].append("src/oic/helper.py"), ) @pytest.mark.parametrize( "relpath", - ("src/oic/semantic_parser.py", "src/oic/helper.py", "src/oic/newmodule/engine.py"), + ("src/oic/helper.py", "src/oic/runtime.py", "src/oic/newmodule/engine.py"), ) def test_t2_t3_t4_detector_refuses_any_new_tracked_path( repo_root: Path, tmp_path: Path, relpath: str From b7a2f783fdce41d061b52fedcf48db6de53bf693 Mon Sep 17 00:00:00 2001 From: Arkadiy Miteiko Date: Fri, 28 Aug 2026 21:38:28 -0400 Subject: [PATCH 4/6] style: format owner authorization guard --- tests/contract/test_semantic_code_start_gate_closure.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/contract/test_semantic_code_start_gate_closure.py b/tests/contract/test_semantic_code_start_gate_closure.py index 58c2abb..60839e0 100644 --- a/tests/contract/test_semantic_code_start_gate_closure.py +++ b/tests/contract/test_semantic_code_start_gate_closure.py @@ -84,7 +84,9 @@ def test_owner_decision_preserves_exact_authorization(repo_root: Path) -> None: OCE execution, Rego/OPA execution, ZTL runtime integration, VEIP runtime integration, production claims, or benchmark claims unless separately authorized.""" - unquoted = "\n".join(line.removeprefix("> ").removeprefix(">") for line in decision.splitlines()) + unquoted = "\n".join( + line.removeprefix("> ").removeprefix(">") for line in decision.splitlines() + ) assert exact_authorization in unquoted From 981965b4d9596798e8fff2cf937d220d92d6efb2 Mon Sep 17 00:00:00 2001 From: Arkadiy Miteiko Date: Fri, 28 Aug 2026 21:39:32 -0400 Subject: [PATCH 5/6] style: format gate authorization assertion --- tests/contract/test_semantic_code_start_gate_closure.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/contract/test_semantic_code_start_gate_closure.py b/tests/contract/test_semantic_code_start_gate_closure.py index 60839e0..5c58fb7 100644 --- a/tests/contract/test_semantic_code_start_gate_closure.py +++ b/tests/contract/test_semantic_code_start_gate_closure.py @@ -70,9 +70,7 @@ def test_current_evidence_is_valid(repo_root: Path) -> None: def test_owner_decision_preserves_exact_authorization(repo_root: Path) -> None: - decision = (repo_root / "docs/decisions/OIC-OWNER-DECISION-004.md").read_text( - encoding="utf-8" - ) + decision = (repo_root / "docs/decisions/OIC-OWNER-DECISION-004.md").read_text(encoding="utf-8") exact_authorization = """I authorize the OIC semantic code-start gate to OPEN on Institutional-Compiler main at exact commit 914830ceec70bde17004d2ccbbb13218ca44a89b. From bad78b81e584a5d48c67bbd8eeefc67cbc3b9296 Mon Sep 17 00:00:00 2001 From: Arkadiy Miteiko Date: Fri, 28 Aug 2026 21:40:56 -0400 Subject: [PATCH 6/6] chore: attest formatted semantic gate head