diff --git a/.github/workflows/control-plane-contract.yml b/.github/workflows/control-plane-contract.yml new file mode 100644 index 00000000..04220f50 --- /dev/null +++ b/.github/workflows/control-plane-contract.yml @@ -0,0 +1,73 @@ +name: DGAF v1 Control-Plane Contract + +permissions: + contents: read + +on: + pull_request: + branches: [main] + paths: + - "pptl/governance_envelope.py" + - "pptl/state_identity.py" + - "pptl/budget_ledger.py" + - "pptl/branch_registry.py" + - "pptl/control_plane.py" + - "pptl/commit_gate.py" + - "pptl/triadic_governance_loop.py" + - "pptl/tests/test_v1_control_plane.py" + - "pptl/tests/test_v1_tgl_integration.py" + - "pptl/tests/test_v1_adversarial_contract.py" + - "pptl/tests/test_v1_capability_boundaries.py" + - "docs/architecture/DGAF_V1_AGENT_ROLE_MAPPING.md" + - "docs/governance/DGAF_V1_FINALIZATION_GATE.md" + - "docs/architecture/DGAF_V1_EXECUTION_READINESS.md" + - "requirements-ci.txt" + - ".github/workflows/control-plane-contract.yml" + push: + branches: + - "feat/dgaf-v1-control-plane-finalize-20260829" + paths: + - "pptl/governance_envelope.py" + - "pptl/state_identity.py" + - "pptl/budget_ledger.py" + - "pptl/branch_registry.py" + - "pptl/control_plane.py" + - "pptl/commit_gate.py" + - "pptl/triadic_governance_loop.py" + - "pptl/tests/test_v1_control_plane.py" + - "pptl/tests/test_v1_tgl_integration.py" + - "pptl/tests/test_v1_adversarial_contract.py" + - "pptl/tests/test_v1_capability_boundaries.py" + - "requirements-ci.txt" + - ".github/workflows/control-plane-contract.yml" + workflow_dispatch: + +jobs: + contracts: + name: V1 Control-Plane Contracts + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + fetch-depth: 1 + - name: Assert exact candidate checkout + shell: bash + env: + EXPECTED_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: | + set -euo pipefail + ACTUAL_SHA="$(git rev-parse HEAD)" + test "$ACTUAL_SHA" = "$EXPECTED_SHA" + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install pinned CI dependencies + run: python -m pip install -r requirements-ci.txt pandas==3.0.5 + - name: Run deterministic contracts + run: >- + python -m pytest -q + pptl/tests/test_v1_control_plane.py + pptl/tests/test_v1_tgl_integration.py + pptl/tests/test_v1_adversarial_contract.py + pptl/tests/test_v1_capability_boundaries.py diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 89b4293d..dc0b6b35 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -70,6 +70,8 @@ jobs: runs-on: ubuntu-latest outputs: deployment_url: ${{ steps.deploy.outputs.deployment_url }} + deployment_id: ${{ steps.provenance.outputs.deployment_id }} + source_sha: ${{ steps.provenance.outputs.source_sha }} env: VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} @@ -95,25 +97,66 @@ jobs: --token="$VERCEL_TOKEN") echo "deployment_url=$URL" >> "$GITHUB_OUTPUT" echo "Deployed to: $URL" - export DEPLOYMENT_URL="$URL" + + - name: Verify exact Vercel deployment identity + id: provenance + shell: bash + env: + DEPLOYMENT_URL: ${{ steps.deploy.outputs.deployment_url }} + run: | + set -euo pipefail + mkdir -p artifacts + HOST="${DEPLOYMENT_URL#https://}" + HOST="${HOST#http://}" + RESPONSE=$(curl -fsS --get \ + --data-urlencode "teamId=$VERCEL_ORG_ID" \ + --data-urlencode "withGitRepoInfo=true" \ + "https://api.vercel.com/v13/deployments/$HOST" \ + -H "Authorization: Bearer $VERCEL_TOKEN") + + echo "$RESPONSE" > artifacts/deployment_metadata.json + READY_STATE=$(echo "$RESPONSE" | jq -r '.readyState // empty') + TARGET=$(echo "$RESPONSE" | jq -r '.target // empty') + DEPLOYMENT_ID=$(echo "$RESPONSE" | jq -r '.id // empty') + META_SHA=$(echo "$RESPONSE" | jq -r '.meta.githubCommitSha // empty') + GIT_SHA=$(echo "$RESPONSE" | jq -r '.gitSource.sha // empty') + SOURCE_SHA="${META_SHA:-$GIT_SHA}" + + [ -n "$DEPLOYMENT_ID" ] || { echo 'x deployment id missing'; exit 1; } + [ "$READY_STATE" = "READY" ] || { echo "x deployment state=$READY_STATE"; exit 1; } + [ "$TARGET" = "production" ] || { echo "x deployment target=$TARGET; expected production"; exit 1; } + [ -n "$SOURCE_SHA" ] || { echo 'x Vercel Git source SHA missing'; exit 1; } + [ "$SOURCE_SHA" = "$GITHUB_SHA" ] || { + echo "x source SHA mismatch: Vercel=$SOURCE_SHA GitHub=$GITHUB_SHA" + exit 1 + } + python - <<'PY' import json import os from pathlib import Path + raw = json.loads(Path('artifacts/deployment_metadata.json').read_text(encoding='utf-8')) + source_sha = raw.get('meta', {}).get('githubCommitSha') or raw.get('gitSource', {}).get('sha') payload = { - 'evidence_class': 'DEPLOYMENT_ATTESTATION', + 'evidence_class': 'DEPLOYMENT_EXACT_SOURCE_ATTESTATION', 'source_commit': os.environ['GITHUB_SHA'], + 'vercel_source_commit': source_sha, 'workflow_run_id': os.environ['GITHUB_RUN_ID'], + 'deployment_id': raw.get('id'), 'deployment_url': os.environ['DEPLOYMENT_URL'], - 'command': 'vercel deploy --prod --yes --token=', - 'result': 'DEPLOYMENT_RETURNED_URL', - 'scope': 'Vercel deployment command result only', - 'limitations': ['Deployment return does not establish application health or end-to-end runtime behavior.'], + 'ready_state': raw.get('readyState'), + 'target': raw.get('target'), + 'git_ref': raw.get('meta', {}).get('githubCommitRef') or raw.get('gitSource', {}).get('ref'), + 'repository': raw.get('meta', {}).get('githubRepo'), + 'exact_source_match': source_sha == os.environ['GITHUB_SHA'], } Path('artifacts/deployment_provenance.json').write_text(json.dumps(payload, indent=2) + '\n', encoding='utf-8') PY + echo "deployment_id=$DEPLOYMENT_ID" >> "$GITHUB_OUTPUT" + echo "source_sha=$SOURCE_SHA" >> "$GITHUB_OUTPUT" + - name: Set env vars run: | echo "1.8.0" | vercel env add ENSEMBLE_VERSION production \ @@ -208,9 +251,9 @@ jobs: 'source_commit': os.environ['GITHUB_SHA'], 'workflow_run_id': os.environ['GITHUB_RUN_ID'], 'deployment_url': os.environ['DGAF_URL'], - 'scope': 'health preflight, 30-turn live regression, and audit turn-count check', + 'scope': 'exact-source production deployment identity, health preflight, 30-turn live regression, and audit turn-count check', 'result': 'PASS', - 'limitations': ['Runtime verification applies to the exercised deployment and protocol; it does not establish broad real-world efficacy.'], + 'limitations': ['Runtime verification applies to the exercised exact-source production deployment and protocol; it does not establish broad real-world efficacy.'], } Path('artifacts/runtime_verification.json').write_text(json.dumps(payload, indent=2) + '\n', encoding='utf-8') PY diff --git a/.github/workflows/doc-lint-pr-scope.yml b/.github/workflows/doc-lint-pr-scope.yml index 5e88b263..97332e95 100644 --- a/.github/workflows/doc-lint-pr-scope.yml +++ b/.github/workflows/doc-lint-pr-scope.yml @@ -25,7 +25,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '24' - name: Install markdownlint-cli run: npm install -g markdownlint-cli@0.39.0 diff --git a/.github/workflows/doc-lint.yml b/.github/workflows/doc-lint.yml index 9f2f2e82..113481c3 100644 --- a/.github/workflows/doc-lint.yml +++ b/.github/workflows/doc-lint.yml @@ -1,8 +1,7 @@ # Doc-Lint CI Workflow — DGAF-Framework (Spine Repo) -# Mirrors sentinel-governance/.github/workflows/doc-lint.yml -# Pattern: P-24 (Canonical Practice Unit) | P-11 (11Q gate 7 — Surface Consistency) -# Owner: Agent Sentinel -# Activated: Session S031 — closes last CI coverage gap in PHDGE ecosystem +# Public/current documentation quality gate. +# Historical and append-only evidence records are governed separately so +# presentation linting does not rewrite or invalidate provenance. name: Doc Lint @@ -25,7 +24,7 @@ on: jobs: markdownlint: - name: Markdown Lint + name: Markdown Lint — Public Surface runs-on: ubuntu-latest steps: - name: Checkout @@ -36,29 +35,29 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '24' - name: Install markdownlint-cli run: npm install -g markdownlint-cli@0.39.0 - - name: Run markdownlint + - name: Run markdownlint on public/current entry points run: | markdownlint \ --config .markdownlint.yml \ --ignore node_modules \ - --ignore CHANGELOG.md \ - --ignore SWEEP_LOG.md \ - '**/*.md' - # CHANGELOG.md and SWEEP_LOG.md: auto-generated append-format; excluded from lint - # All gate specs, protocols, READMEs, SESSION_ANCHOR, CROSS_REF enforced + README.md \ + README.governance.md \ + README.technical.md \ + docs/architecture/DGAF_V1_*.md + # Detailed governance, experiment, evidence, archive, historical, + # generated, and append-only records remain subject to their own + # provenance/evidence controls rather than presentation lint. - name: Report summary if: always() run: | echo "## Doc Lint Summary — DGAF-Framework" >> $GITHUB_STEP_SUMMARY echo "- Linter: markdownlint-cli 0.39.0" >> $GITHUB_STEP_SUMMARY - echo "- Config: .markdownlint.yml" >> $GITHUB_STEP_SUMMARY - echo "- Excluded: CHANGELOG.md, SWEEP_LOG.md (append-format auto-generated)" >> $GITHUB_STEP_SUMMARY - echo "- Pattern gates: P-24 (CPU surface consistency) + P-11 gate 7" >> $GITHUB_STEP_SUMMARY - echo "- Owner: Agent Sentinel | Spine repo: DGAF-Framework" >> $GITHUB_STEP_SUMMARY - echo "- Mirror of: sentinel-governance/.github/workflows/doc-lint.yml (S029)" >> $GITHUB_STEP_SUMMARY + echo "- Node.js: 24" >> $GITHUB_STEP_SUMMARY + echo "- Scope: public/current entry-point documentation" >> $GITHUB_STEP_SUMMARY + echo "- Separate provenance surfaces: governance, experiment, evidence, archive, historical, generated, append-only records" >> $GITHUB_STEP_SUMMARY diff --git a/README.md b/README.md index d9cd9352..ae1b10c1 100644 --- a/README.md +++ b/README.md @@ -2,188 +2,61 @@ **Dynamic Governance Agentic Formation (DGAF)** — a research and implementation repository for agent orchestration, evaluation, provenance, and governance controls. -> **Epistemic status:** This README describes repository scope and the current pre-freeze governance state. Individual claims of validation, certification, performance, standards alignment, or commercial suitability require exact evidence and defined scope. Historical certifications remain scoped to the SHA/run/deployment that produced them and are not current certification without fresh evidence. +> **Epistemic status:** This README describes repository scope and current pre-freeze governance state. Individual claims require exact evidence and defined scope. Historical evidence remains scoped to the SHA/run/deployment that produced it. ## Current project state — 2026-08-29 -The DGAF/PDMAL experimental track remains **PRE-FREEZE / FAIL-CLOSED**. The corrected pilot apparatus and supporting governance controls are present in the repository, but the current experimental candidate has not been freeze-verified. No new experimental freeze exists, pilot authorization has not been granted, and empirical **N = 0**. +The DGAF/PDMAL experimental track remains **PRE-FREEZE / FAIL-CLOSED**. No new experimental freeze exists, pilot authorization has not been granted, and empirical **N = 0**. -The repository `main` is an active documentation/evidence lineage and must not be treated as the experimental apparatus identity. The current experimental verification boundary remains candidate-scoped at `ac8ea267a9f0d995626cf9c3eaf9e6b008b5dc8a`. Documentation/evidence successors do not redefine the executable apparatus; any substantive apparatus change requires a new candidate identity and affected-predicate re-verification. +`main` is documentation/evidence lineage, not experimental apparatus identity. The experimental verification boundary remains candidate-scoped at `ac8ea267a9f0d995626cf9c3eaf9e6b008b5dc8a`. Any substantive apparatus change requires a new candidate identity and affected-predicate re-verification. -Historical candidates, freezes, run identifiers, and acceptance records remain provenance only unless explicitly rebound to the current authoritative candidate and evidence boundary. +## Canonical engineering lane -### Current TGL contract-review state +**PR #139** is the current combined engineering candidate for DGAF v1 recursive control-plane implementation and TGL contract remediation. Earlier PRs #132/#133/#134 are historical or superseded records and are not separate current execution authorities. -An adversarial review of PR #132 identified a concrete TGL/P-35 contract regression rather than an isolated constructor defect. The observed pre-freeze **41-pass / 2-fail** result is being treated as a regression signal requiring causal and cross-layer analysis. The review covers TGL state-machine semantics, `PASS / WARN / SKIP / ESCALATE / KILL` reduction, adapter/API contracts, exception containment, audit sealing, cryptographic provenance, PDMAL ↔ TGL integration, CI/CD source identity, Vercel runtime identity, dependency relationships, stale SHA/candidate references, overlapping changes, regression coverage, and P6/P6a/P7/P8 governance boundaries. +The candidate covers inherited governance scope, deterministic lifecycle control, state identity, budget/concurrency accounting, branch provenance, explicit CommitGate authorization, fail-closed TGL semantics, complete audit sealing, adversarial regression coverage, and dedicated CI. It does not rebind PDMAL or authorize experimentation. -PR #132 remains **BLOCKED / DRAFT / UNMERGED**. A separate draft remediation candidate, **PR #133**, was created from the established `main`/post-#131 implementation rather than mutating #132. PR #133 is intentionally scoped to minimal TGL contract restoration and regression coverage: restoration of the established `ProcludingPremiseGate` constructor and `evaluate(check_fn=...)` contract, premise-hook injection, fail-closed exception containment, explicit required-gate semantics, deterministic status reduction, conditional-versus-unwired `SKIP` distinction, and exact audit sealing. It deliberately does **not** change PDMAL experimental treatment hooks, pilot execution, freeze state, authorization, or empirical state. +## Current TGL contract boundary -PR #133 is a **draft diagnostic/remediation candidate only**. Its existence or eventual test success must not be interpreted as experimental authorization, freeze verification, empirical evidence, certification, or proof of the complete DGAF architecture. CI validation remains required before any merge decision. +- required unwired gates are `SKIP` and reduce the turn to `ESCALATE`; +- `WARN` propagates to `TurnStatus.WARN` unless a stronger failure applies; +- HPG is conditional on Phi-Closure and cannot run after terminal failure; +- terminal failures stop downstream gate execution; +- the final audit seal covers the complete gate set, including Herald; +- invalid gate outcomes do not silently become PASS. -For the authoritative project state, see [`docs/PROJECT_STATUS.md`](docs/PROJECT_STATUS.md) and [`docs/CURRENT_STATE.md`](docs/CURRENT_STATE.md). For the TGL contract and adversarial review record, see the repository's current PR #132/#133 evidence and associated governance documentation. For the canonical mathematical notation policy, see [`docs/governance/MATHEMATICAL_NOTATION_POLICY_METALLIC_MEANS_2026-08-28.md`](docs/governance/MATHEMATICAL_NOTATION_POLICY_METALLIC_MEANS_2026-08-28.md). For the public-facing publication-quality control, see [`docs/governance/PUBLIC_SURFACE_QA_STANDARD.md`](docs/governance/PUBLIC_SURFACE_QA_STANDARD.md). For pattern architecture, see [`docs/PATTERN_COMMONS_ARCHITECTURE.md`](docs/PATTERN_COMMONS_ARCHITECTURE.md). For openness/commercialization boundaries, see [`docs/GOVERNANCE/DGAF_COMMERCIALIZATION_OPENNESS_BOUNDARY.md`](docs/GOVERNANCE/DGAF_COMMERCIALIZATION_OPENNESS_BOUNDARY.md). For the asset-level ecosystem inventory, see [`docs/GOVERNANCE/DGAF_ASSET_LEVEL_BOUNDARY_INVENTORY_2026-08-25.md`](docs/GOVERNANCE/DGAF_ASSET_LEVEL_BOUNDARY_INVENTORY_2026-08-25.md). For future trademark/certification governance, see [`docs/GOVERNANCE/DGAF_TRADEMARK_AND_CERTIFICATION_POLICY.md`](docs/GOVERNANCE/DGAF_TRADEMARK_AND_CERTIFICATION_POLICY.md). +## Canonical agent-role boundary -## Layer-0 human / rights / societal boundary +- Sentinel-Phi — canonical governance/security identity. +- Professor Prodigy — formalization/proof; non-orchestrating. +- DemiJoule — advisory resource/constraint analysis; no independent normative authorization. +- Reciprocity — fairness and affected-party review. +- Herald — evidence/public-surface publication; cannot manufacture evidence or approval. +- Amethyst — meta-orchestration/lifecycle coordination. +- COLLEEN — continuity/archive/provenance/routing integrity. +- Apogee — independent evidence/integrity review. -DGAF treats human dignity, human rights, safety, lawful operation, privacy, non-discrimination, human agency, legitimate oversight, public accountability, and appropriate disclosure as a **shared constitutional substrate** that precedes technical optimization. This is governed by [`docs/agents/LAYER_0_CONSTITUTION.md`](docs/agents/LAYER_0_CONSTITUTION.md) and [`docs/agents/AGENT_AUTHORITY_INVARIANT.md`](docs/agents/AGENT_AUTHORITY_INVARIANT.md). +Generic execution roles do not create or elevate agent authority. -Layer 0 is deliberately distributed rather than delegated to one persona. Perigee, Sentinel-Phi, Reciprocity, Professor Prodigy, Amethyst, DemiJoule, Herald, Apogee, COLLEEN, and the Resonance agents may contribute within their distinct contracts, but shared vocabulary does not grant shared authority. +## Experimental gate state -DGAF distinguishes **law/regulation**, **recognized standard**, **governance framework**, **human-rights instrument**, **best practice**, **social expectation**, **engineering convention**, and **DGAF design choice**. Framework resemblance is not a legal-compliance claim. External references are maintained as a living, versioned layer; NIST AI RMF 1.0 is currently being revised, and EU AI Act applicability/enforcement depends on the system role, classification, jurisdiction, and applicable date. +| Boundary | Status | +|---|---| +| Experimental verification boundary | `ac8ea267a9f0d995626cf9c3eaf9e6b008b5dc8a` | +| P7 scientific decision | Adopted in substance; exact freeze binding open | +| P8 analysis lock | Open / fail-closed | +| P2 runtime verification | Not executed | +| P6a CORS verification | Not executed | +| New immutable freeze | Not created | +| Pilot authorization | Not granted | +| Empirical N | 0 | -Public-facing material is governed by the sequence **Accessibility → Comprehensibility → Appropriateness of Disclosure**. Repository visibility is reviewed for security, privacy, sovereign/IP exposure, human comprehension, and truthful evidence/maturity representation. Public documentation must not promote implementation, testing, verification, authorization, or efficacy beyond the evidence actually established. +## Deployment identity -## Public-surface standard +The observed READY Vercel production deployment is not exact-current-main evidence because its source SHA `42346ecc34565502ebff02ead55a33b0d74246b8` does not equal the current GitHub `main` identity. Issue #137 remains the canonical deployment-provenance tracker. -GitHub is an external representation of the project and its maintainer. Every GitHub-visible artifact therefore passes a **public-surface QA lens** before publication. Accuracy is necessary but not sufficient. +## Evidence boundary -Public-facing changes must be evaluated for: +Engineering CI success, synthetic fixtures, deployment readiness, or documentation updates do not constitute PDMAL efficacy evidence or experimental authorization. Historical evidence is not transferable across SHA/run/deployment boundaries without fresh exact-scope evidence. -- truth and evidence scope; -- authoritative-source correctness; -- audience relevance and usefulness; -- expected placement and navigation; -- professional representation; -- privacy and disclosure boundaries; -- open-source/community norms; -- maintainability and link stability; -- identity integrity and avoidance of overclaiming; -- reader friction and next-step clarity. - -Personal Notion pages, private working records, internal control notes, and temporary coordination artifacts are **not public GitHub navigation targets by default**. Internal records may inform public documentation, but a public landing page should resolve to repository-local documentation, stable public resources, or an intentionally designated public project surface. - -See [`docs/governance/PUBLIC_SURFACE_QA_STANDARD.md`](docs/governance/PUBLIC_SURFACE_QA_STANDARD.md) for the complete publication gate. - -## Repository scope - -DGAF contains governance and evaluation components, agent specifications, control/gate definitions, provenance practices, epistemic auditing, vocabulary management, and experimental research artifacts. DGAF is the implementation/governance substrate; it is **not** the universal owner of every pattern, taxonomy, template, or research artifact in the surrounding ecosystem. - -### Canonical terminology - -- **DGAF** — Dynamic Governance Agentic Formation. -- **AHG** — Adaptive Harmonic Governance. Historical/conflicting expansions remain historical unless explicitly promoted by current governance. -- **PDMAL / PDMA-L** — Phi-Driven Multi-Agent Lattice. The term refers to the lattice/control research track; current evidence does not establish a complete Byzantine Fault Tolerance protocol merely from the topology. -- **NDR** — a project pattern namespace/family within the broader Pattern Commons architecture, not the entire ecosystem pattern corpus. -- **Pattern Commons** — proposed ecosystem-level layer for pattern identity, provenance, aliases/equivalence, epistemic status, and evidence relationships across repositories. -- **AXIS** — Agent X-axis Invariant Spectrum. -- **FLAG-02** — historical identifier associated with the former 340% coordination-gain claim. Current evaluation-mode terminology is **qualitative**. New documents must not introduce FLAG-02 as a current identifier for either meaning. -- **φ / Golden Ratio** — `(1+√5)/2 ≈ 1.618033989`; canonical mathematical notation. -- **σ_{p,q} / Metallic Means Family** — positive solution of `x² - px - q = 0`, `(p + √(p² + 4q))/2`; for the ordinary sequence, `σ_n = σ_{n,1}`. `σ_{2,1}` is silver and `σ_{3,1}` is bronze. -- **ρ / Plastic Number** — `≈ 1.3247179572447454`, the unique real root of `x³ - x - 1 = 0`. `ρ` is the preferred canonical mathematical notation; `P` is an attested alternative. `ρP` is not the canonical symbol. -- **pP / Platinum Mean** — intentional DGAF notation for the regular-hendecagon unit-side circumradius, `1/(2 sin(π/11)) ≈ 1.774732842`. This is DGAF-specific notation, not a claim of a universal standard mathematical symbol or membership in the quadratic metallic-means family. - -Historical documents may retain their original terminology when necessary for provenance, but they must be treated as historical rather than silently reinterpreted as current state. In particular, pP must not be substituted for ρ in PDMAL plastic-number convergence mathematics. - -## Semantic / ontological boundary - -DGAF permits agents and components to consume and reason over an approved ontology. They must not silently introduce, redefine, or assert ontology outside the authorized semantic layer. - -The governing progression is: - -**defined → observed → supported → verified → authorized → canonical** - -Operational documentation must distinguish **representation**, **classification**, **policy status**, **epistemic status**, and **ontological assertion**. New terminology or semantic categories are candidate vocabulary until provenance and authorization establish canonical status. Agent repetition, confidence, or wording does not create semantic authority. - -**Ontology drift** is treated as a distinct semantic-drift class: an unauthorized change in effective vocabulary, entity boundaries, relations, or semantic commitments. The broader semantic-risk taxonomy is **definition drift, ontology drift, epistemic drift, policy drift, and provenance drift**. - -Semantic/ontological detection is not automatically a blocking gate. A detector must be empirically characterized before it becomes threshold-bearing or gate-bearing. This control does not alter the experimental state: **PRE-FREEZE / FAIL-CLOSED / N=0 / NO FREEZE / PILOT AUTHORIZATION NOT GRANTED**. - -## Epistemic standard - -Claims are classified according to the repository standard: - -`DEFINED → IMPLEMENTED → COMPUTED → VERIFIED → ATTESTED → HISTORICAL → HYPOTHESIS → METAPHOR → UNSUPPORTED → DEPRECATED` - -A mathematical term, external framework name, benchmark number, deployment, registry entry, commercial status, or agent role does not by itself establish implementation, validation, legal compliance, safety, certification, or independent verification. - -## Core areas - -- Agent orchestration and control-plane design -- Evaluation and quality-assurance tooling -- Provenance and traceability -- Governance gates and deployment controls -- Epistemic auditing and vocabulary management -- Semantic/ontological boundary governance -- Pattern Commons integration and cross-repository reconciliation -- Experimental mathematical and structural research -- Open-source commercialization and evidence-preserving governance - -## Open-source / commercialization posture - -DGAF aims to keep the public reference implementation sufficiently complete for independent cloning, inspection, execution, and evaluation. Legitimate commercial differentiation may reside in managed operations, integration, assurance, support, hosting, specialized tooling, customer-specific configurations, training, and future certification programs. Public scientific/technical claims must retain enough evidence for independent evaluation even when adjacent operational assets are commercial or private. - -The repository is licensed under Apache-2.0. See [`LICENSE`](LICENSE) for the legal terms. The license does not grant trademark rights; future official, certification, or endorsement claims bearing the DGAF name require separate governance and should not be inferred from repository status or project attestation. - -## PDMAL/DGAF documentation spine - -1. [Current State](docs/CURRENT_STATE.md) -2. [Project Status](docs/PROJECT_STATUS.md) -3. [PDMAL Current Control State](docs/experiment/PDMAL_CURRENT_CONTROL_STATE.md) -4. [Authoritative PDMAL Task Specification](docs/experiment/PDMAL_TASK_SPEC_V0.7.4.md) — task contract; see the v0.7.5 protocol matrix amendment for the current acceptance-layer changes. -5. [PDMAL Evidence Index](docs/evidence/PDMAL_EVIDENCE_INDEX.md) -6. [Evidence Ladder Policy](docs/evidence/EVIDENCE_LADDER_POLICY.md) -7. [PDMAL Experiment Protocol](docs/experiment/PDMAL_EXPERIMENT_PROTOCOL.md) — current pre-freeze protocol incorporating the v0.7.5 matrix amendment. -8. [Freeze Manifest Template](docs/experiment/FREEZE_MANIFEST_TEMPLATE.md) -9. [Propagation Consistency Control](docs/governance/PROPAGATION_CONSISTENCY_CONTROL.md) -10. [Documentation Reconciliation](docs/governance/DOCUMENTATION_RECONCILIATION_2026-08-21.md) -11. [Test Execution Readiness](docs/governance/TEST_EXECUTION_READINESS_2026-08-21.md) -12. [P3–P6 Freeze Readiness](docs/governance/P3_P4_P5_P6_FREEZE_READINESS_2026-08-21.md) -13. [P7 Primary Contrast Adjudication](docs/governance/P7_PRIMARY_CONTRAST_ADJUDICATION_PACKET_2026-08-21.md) -14. [Candidate Runtime Verification](docs/governance/CANDIDATE_RUNTIME_VERIFICATION_2026-08-21.md) -15. [NDR Research Program Charter — Current Status Addendum](docs/governance/NDR_RESEARCH_PROGRAM_CHARTER_CURRENT_STATUS_2026-08-21.md) -16. [Freeze Packet Template](docs/governance/FREEZE_PACKET_TEMPLATE.md) -17. [Pattern Commons Architecture](docs/PATTERN_COMMONS_ARCHITECTURE.md) -18. [Commercialization & Openness Boundary](docs/GOVERNANCE/DGAF_COMMERCIALIZATION_OPENNESS_BOUNDARY.md) -19. [Asset-Level Boundary Inventory](docs/GOVERNANCE/DGAF_ASSET_LEVEL_BOUNDARY_INVENTORY_2026-08-25.md) -20. [Trademark & Certification Policy](docs/GOVERNANCE/DGAF_TRADEMARK_AND_CERTIFICATION_POLICY.md) -21. [Public Surface QA Standard](docs/governance/PUBLIC_SURFACE_QA_STANDARD.md) -22. [CROSS_REF](CROSS_REF.md) -23. [Platinum Mean Semantic Correction](docs/governance/PLATINUM_MEAN_SEMANTIC_CORRECTION_2026-08-28.md) -24. [Metallic Means Mathematical Notation Policy](docs/governance/MATHEMATICAL_NOTATION_POLICY_METALLIC_MEANS_2026-08-28.md) -25. [Layer-0 Human / Rights / Societal Constitution](docs/agents/LAYER_0_CONSTITUTION.md) -26. [Agent Authority Separation Invariant](docs/agents/AGENT_AUTHORITY_INVARIANT.md) -27. [Agent Authority Matrix](docs/agents/AGENT_AUTHORITY_MATRIX.md) -28. **TGL adversarial contract review / remediation** — PR #132 remains blocked; PR #133 is the isolated minimal-contract-restoration candidate. This work is diagnostic and pre-freeze only and does not authorize experimentation. - -## Verification and test status - -The repository contains deterministic/unit tests, pilot execution-contract tests, artifact/schema controls, governance consistency checks, propagation checks, and CI workflows. **Existence of a test is not evidence that the test has passed.** Current candidate verification must identify the exact candidate SHA, execution environment, deployment where applicable, run identifier, and retained evidence artifact. - -### Current gate boundary - -- TGL contract validation — **BLOCKED / UNDER ADVERSARIAL REVIEW** -- PR #132 — **BLOCKED / DRAFT / UNMERGED** -- PR #133 — **DRAFT / REMEDIATION CANDIDATE / CI VALIDATION PENDING** -- P1 Candidate integrity — PARTIAL -- P2 Execution contract — BLOCKED for authenticated runtime verification -- P3 Artifact contract — OPEN -- P4 Security/blinding integrity — OPEN -- P5 Provenance/reproducibility — OPEN -- P6 Durable evidence custody — OPEN -- P7 Scientific target specification — ADOPTED in substance; exact freeze binding pending -- P8 Analysis lock — OPEN / FAIL-CLOSED -- P9 Independent verification — NOT EXECUTED -- New freeze — NOT CREATED -- Pilot authorization — NOT GRANTED -- Empirical N — 0 - -Do not infer repository-wide validation from a component-level test, historical attestation, deployment existence, README text, funding badge, commercial status, or certification language. In particular, successful TGL contract tests or a successful remediation PR do not establish experimental authorization or empirical efficacy. - -## Historical evidence boundary - -Historical runtime, P2, P6a, and characterization records remain valid only for the exact source/deployment/run they document. In particular, retained historical results are not current-candidate verification. - -## Related ecosystem - -Related repositories are separate tracks. Shared terminology does not imply implementation equivalence or cross-repository validation. See [`CROSS_REF.md`](CROSS_REF.md) for the current cross-reference and epistemic boundary index. - -## Support / funding - -GitHub Sponsors configuration is present through `.github/FUNDING.yml`. Sponsorship supports maintenance and development; it does not confer ownership, certification, endorsement, or special evidence status. - -## License - -See [LICENSE](LICENSE) for the repository's applicable license. - -## Provenance - -Developed by Ndr / Ender Hensel (`ndrorchestration`). +**Current experimental state: PRE-FREEZE / FAIL-CLOSED / NOT AUTHORIZED / N=0.** diff --git a/README.technical.md b/README.technical.md index b0656212..d2cba89b 100644 --- a/README.technical.md +++ b/README.technical.md @@ -1,200 +1,87 @@ -# DGAF-Framework — Technical & Agent-Facing Reference +# DGAF-Framework — Technical Reference -> **Claim-status boundary:** This document is a technical/project reference, not a certification, validation, regulatory-conformance statement, or efficacy report. Project-local gate names, targets, thresholds, and attestation labels describe internal procedures or historical records unless current claim-specific evidence says otherwise. +> **Audience:** engineers, researchers, and contributors working with DGAF implementation and control artifacts. > -> **Current certification policy:** There is no active DGAF certification program. See [`docs/GOVERNANCE/DGAF_TRADEMARK_AND_CERTIFICATION_POLICY.md`](./docs/GOVERNANCE/DGAF_TRADEMARK_AND_CERTIFICATION_POLICY.md). +> **Evidence boundary:** This reference describes project architecture and implementation surfaces. A design, implementation, passing test, mathematical result, historical attestation, and independently validated empirical result are different evidence states. -> **Audience:** Agent Amethyst, Agent Apogee, Agent COLLEEN, Agent Sentinel, and all ensemble members; engineers integrating with DGAF -> **Entry point for:** Gate specs · Pattern registry · Runtime components · Formation protocols · Session open/close procedures -> **Compliance/governance entry point:** [`README.governance.md`](./README.governance.md) -> **Architect:** Hensel, Andrew Vance · [@ndrorchestration](https://github.com/ndrorchestration) +DGAF is a framework for governed agent orchestration, evaluation, provenance, and control design. This document provides a technical map; authoritative specifications and current experimental status remain in the linked records. ---- - -## MDAR Loop — Project Protocol - -``` -Map → Diagnose → Act → Review - ↑ | - └────────────────────────┘ - (each cycle = one project-defined interval) -``` - -The MDAR loop is a project orchestration protocol. Claims about improved correctness, convergence, safety, or efficacy require separate evidence. - ---- - -## Gate Stack — Project Execution Order - -| Priority | Gate | Pattern | Trigger | Owner | -|----------|------|---------|---------|-------| -| 1 (always) | GATE-ACO: Acoustic Chain | P-13 | Every synthesis cycle | Amethyst + DemiJoule | -| 2 (every artifact) | GATE-1111: 1-1-1-1 | P-10 | Pre-registry sign-off | Apogee | -| 3 (pre-deploy) | GATE-11Q: Hendecagonal | P-11 | Proposed production deployment | Apogee + Sentinel | -| 4 (deep audit) | GATE-TEL: Telescopic Lens | P-12 | Project-local structural audit | Apogee + Amethyst | -| 5 (canonical promotion) | Apogee-Attestation-Gate | P-30 | Component/pattern canonical promotion | Apogee + Amethyst | - -Full specifications: [`docs/gates/`](./docs/gates/). Gate PASS states are project-local control results unless explicitly supported by separate current evidence. - ---- - -## Runtime Components - -| Component | Path | Purpose | Status note | -|-----------|------|---------|------------| -| KAPPA Dynamic Confidence Router | `components/KAPPA/dynamic_weight_router.py` | Confidence-gated routing and category-sensitive weight selection | Implementation artifact; efficacy requires separate evaluation | -| KAPPA Calibration v3.6 | `components/KAPPA/calibration_v3_6.json` | Threshold calibration | Project configuration; not evidence of optimality | -| KAPPA Component Card | `components/KAPPA/DGAF_GATE_KAPPA_v3_5_component_card.json` | CPU-oriented registry card | Project metadata | -| Evaluate Router | `components/evaluate_router.py` | Batch pipeline composition: detect → apply_weights → rank | Implementation artifact | -| Evaluate Router v1.1 | `components/evaluate_router_v1_1.py` | Sentinel hooks, P-10 deontic gate, per-record audit log | Implementation artifact | -| Normative Constraint | `components/normative_constraint.py` | Deontic / optimization / epistemic integrity constraint class | Implementation artifact | - -Component index: [`components/README.md`](./components/README.md) - ---- - -## NDR Pattern Registry — Quick Reference - -| Range | Domain | -|-------|--------| -| P-01–P-08 | Coherence, continuity, git hygiene, cross-platform sync | -| P-09–P-13 | AXIS enforcement, quality gates, acoustic temporal chain | -| P-14–P-15 | Formation protocols (Trio, Harmonic Quintet) | -| P-16–P-20 | Metadata hygiene, IP, issue triage, branding, Drive sync | -| P-21–P-24 | Session continuity, storage topology, taxonomy audit, canonical practice unit | -| P-27–P-30 | Confidence routing, pipeline composition, Sentinel risk pass, Apogee attestation | - -Full registry: [`docs/patterns/NDR_PATTERN_REGISTRY.md`](./docs/patterns/NDR_PATTERN_REGISTRY.md) +## Architecture at a glance ---- - -## QA & Attestation Surface - -| Artifact | Path | Meaning | -|----------|------|---------| -| Apogee 11Q S034 | `docs/qa/APOGEE_11Q_S034.json` | Historical/project-local attestation artifact | -| Apogee 11Q S035 | `docs/qa/APOGEE_11Q_S035.json` | Historical/project-local attestation artifact | -| QA Index | `docs/qa/README.md` | Attestation artifact index | - -An attestation record is not automatically an independent certification or validation result. - ---- +DGAF's implementation surfaces include: -## Kernel & Contraction Nomenclature — S068 - -> Added: 2026-06-26 · Issue #32 · Steward: Amethyst -> Context: Nemotron 3 Ultra integration planning — parametric eval suite - -| Term | Definition | Constraint / interpretation | First Used | -|------|-----------|----------------------------|------------| -| **typed kernel** | A governance role's executable Python/TypeScript unit with explicit `input_schema → policy → output_schema → audit_trail` contract; generated from `governance.yml` | Contract/property definition; CI promotion requires the project's named check | S068 | -| **ρ-contraction** | A mathematical property `‖T(x) - T(y)‖ ≤ ρ‖x - y‖` for an operator T | ρ < 1 is a sufficient condition for convergence for the stated mathematical model; project monitoring does not by itself establish that the deployed system satisfies the premise | S068 | -| **spectral radius** | Largest absolute eigenvalue of a role transition matrix | A spectral-radius check is a bounded mathematical check; production monitoring does not by itself prove convergence of the real system | S068 | -| **curvature** (governance) | Per-role scalar used by the project router | Project-local modeling variable; empirical meaning requires validation | S068 | -| **triadic orchestration** | Three-phase project inference loop: Apogee (propose) → Reson (critique) → Lyra (resolve) | Design pattern; stronger alignment or performance claims require comparative evidence | S068 | -| **thinking_tokens** | Per-role reasoning budget parameter | Configuration parameter; not a measure of reasoning quality by itself | S068 | -| **MoE expert entropy** | Shannon entropy H of expert activation distribution across routing decisions | Diagnostic metric; thresholds are project parameters unless calibrated | S068 | -| **role_boundary_coherence** | Eval metric for role identification across a defined trace | Target values are hypotheses/benchmarks until reproduced and validated | S068 | -| **contraction_proof_fidelity** | Eval metric defined by the project for generated kernel specifications | A CI result supports the tested corpus/procedure only; it is not proof of deployed-system convergence | S068 | -| **governance_schema_conformance** | Eval metric for fuzz-generated `governance.yml` variants | Test-specific conformance result; not general compliance | S068 | -| **audit_hallucination_rate** | Field-level accuracy of generated audit events versus ground truth | Evaluation metric; benchmark values are evidence only for the stated test scope | S068 | -| **taubench_banking_mitigation** | Project eval metric for financial compliance routing | Evaluation target; no regulatory-compliance claim follows from the target itself | S068 | -| **ROLE_BUDGETS** | Dict mapping DGAF role names to reasoning-budget values | Configuration source of truth for the project implementation | S068 | +- **Control and gates** — project-defined checks and execution constraints. +- **Runtime components** — routing, evaluation, and constraint implementations. +- **Patterns** — reusable architecture and governance conventions. +- **Trace and provenance tooling** — mechanisms for recording and examining execution context. +- **Experimental infrastructure** — research apparatus maintained separately from general engineering claims. ---- +## Project control stack -## Session Open Protocol (COLLEEN — P-02) +DGAF uses named gates and controls where a project contract requires explicit evaluation or escalation. Gate names and PASS states are project-local unless supported by additional claim-specific evidence. -``` -1. Read session-state reference → rehydrate open BLGs + priority queue -2. Run .operations/gate_compliance_check.py → surface P-24 gaps -3. Emit session priority queue to Amethyst -4. Amethyst opens wave; Apogee scores; Sentinel monitors -``` +Current specifications: [`docs/gates/`](./docs/gates/) -Operational session state belongs in the private operational boundary. The public repository should contain only sanitized reproducibility/governance material. +| Area | Examples | +|---|---| +| Control checks | P-10, P-11, P-13 and related gate contracts | +| Authority and promotion | Agent authority controls and project-defined promotion procedures | +| Structural review | Project-local architecture and consistency checks | -Checklist: [`.operations/sweep_session_init.md`](./.operations/sweep_session_init.md) +## Runtime components ---- +| Component | Purpose | +|---|---| +| KAPPA Dynamic Confidence Router | Confidence-gated routing and category-sensitive weight selection | +| Evaluate Router | Batch pipeline composition | +| Normative Constraint | Project-defined deontic and epistemic constraint implementation | +| PPTL | Experimental topology and orchestration harness | -## Session Close Protocol (Amethyst — P-06 + P-21) +See [`components/README.md`](./components/README.md) and [`pptl/README.md`](./pptl/README.md) for implementation-level details. -``` -1. All repo fixes committed -2. SWEEP_LOG.md updated + buoy appended -3. CHANGELOG.md versioned -4. CROSS_REF.md updated -5. Operational session state sealed in its designated boundary -6. Seal commit pushed -``` +## Patterns and agent architecture -Checklist: [`.operations/seal_checklist.md`](./.operations/seal_checklist.md) +The NDR pattern registry records project patterns for recurring orchestration, governance, and engineering problems. Pattern identifiers are references to project designs; their existence is not evidence of universal effectiveness. ---- +Named agent roles provide an architectural vocabulary for responsibilities and interfaces. Authority is determined by explicit contracts, not by a role name or an agent's output. -## Formation Reference +- [`docs/patterns/NDR_PATTERN_REGISTRY.md`](./docs/patterns/NDR_PATTERN_REGISTRY.md) +- [`ENSEMBLE_ROSTER.md`](./ENSEMBLE_ROSTER.md) +- [`docs/agents/AGENT_AUTHORITY_MATRIX.md`](./docs/agents/AGENT_AUTHORITY_MATRIX.md) -| Formation | Pattern | Agents | Use | -|-----------|---------|--------|-----| -| Trio | P-14 | Amethyst + Apogee + COLLEEN | Standard multi-repo sweep | -| Harmonic Quintet | P-15 | Trio + Reson + Sentinel | Seal commits; sovereign file changes | -| IP Sweep | — | Amethyst + Perplexity MCP | Research, external source integration | +## Testing and evidence -Formation names and role assignments are project architecture. They do not establish independent capability claims about an agent implementation. +Tests establish behavior for the contracts and environments they cover. Read results with their exact source identity, configuration, and retained evidence when making broader claims. ---- +Key references: -## Key File Locations - -``` -DGAF-Framework/ -├── README.md ← Public-facing entry point -├── README.governance.md ← Governance reference -├── README.technical.md ← This technical reference -├── CHANGELOG.md ← Semantic versioned history -├── CROSS_REF.md ← Ecosystem artifact map -├── ENSEMBLE_ROSTER.md ← Canonical agent registry -├── components/ ← Runtime components -├── docs/gates/ ← Project gate specifications -├── docs/patterns/ ← Project pattern registry -├── docs/qa/ ← Attestation/evidence artifacts -├── scripts/claim_hygiene_check.py ← Blocking public claim-hygiene scanner -└── .github/workflows/ip-hygiene.yml ← IP/claim hygiene CI -``` - -Operational internals and live session state must remain outside the public reproducibility boundary unless intentionally sanitized. +- [`docs/CLAIM_EVIDENCE_INDEX.md`](./docs/CLAIM_EVIDENCE_INDEX.md) +- [`docs/evidence/EVIDENCE_LADDER_POLICY.md`](./docs/evidence/EVIDENCE_LADDER_POLICY.md) +- [`docs/EPISTEMIC_EVIDENCE_STANDARD.md`](./docs/EPISTEMIC_EVIDENCE_STANDARD.md) +- [`docs/qa/README.md`](./docs/qa/README.md) ---- +## Mathematical and research terminology -## ANDROMEDA-AXIS Declarations (P-09) +DGAF uses project-specific mathematical notation in some research tracks. Mathematical notation should be interpreted according to the repository's notation policy and the scope of the associated model; a mathematical property of a model does not automatically describe a deployed system. -All agent actions are checked against four project sovereign constraints: +See [`docs/governance/MATHEMATICAL_NOTATION_POLICY_METALLIC_MEANS_2026-08-28.md`](./docs/governance/MATHEMATICAL_NOTATION_POLICY_METALLIC_MEANS_2026-08-28.md). -| Declaration | Constraint | -|-------------|------------| -| COGNITIVE_SOVEREIGNTY | No agent may alter the architect's epistemic autonomy or decision authority | -| BIOLOGICAL_INTEGRITY | No output may threaten physical or psychological integrity | -| TRANSVERSAL_GROWTH | Systems should support ongoing learning and capability expansion | -| ENTROPY_RESISTANCE | No action should increase systemic disorder beyond recoverable bounds | +## Current and historical state -These are project governance declarations, not externally certified safety guarantees. +For current project status and experimental boundaries, use: ---- +- [`docs/CURRENT_STATE.md`](./docs/CURRENT_STATE.md) +- [`docs/PROJECT_STATUS.md`](./docs/PROJECT_STATUS.md) -## Evidence and Claim Discipline +Historical implementation records and earlier terminology remain available for provenance. See [`docs/HISTORICAL_RECORDS_INDEX.md`](./docs/HISTORICAL_RECORDS_INDEX.md) before treating an older record as current authority. -Public technical claims should be read together with: - -- [`docs/CLAIM_EVIDENCE_INDEX.md`](./docs/CLAIM_EVIDENCE_INDEX.md) -- [`docs/evidence/EVIDENCE_LADDER_POLICY.md`](./docs/evidence/EVIDENCE_LADDER_POLICY.md) -- [`docs/EPISTEMIC_EVIDENCE_STANDARD.md`](./docs/EPISTEMIC_EVIDENCE_STANDARD.md) -- [`docs/GOVERNANCE/DGAF_TRADEMARK_AND_CERTIFICATION_POLICY.md`](./docs/GOVERNANCE/DGAF_TRADEMARK_AND_CERTIFICATION_POLICY.md) +## Related references -A design, implementation, test, bounded mathematical result, historical attestation, and independently validated empirical result are distinct evidence states and must not be collapsed. +- [`README.md`](./README.md) — project overview +- [`README.governance.md`](./README.governance.md) — governance model +- [`docs/PATTERN_COMMONS_ARCHITECTURE.md`](./docs/PATTERN_COMMONS_ARCHITECTURE.md) — ecosystem pattern architecture +- [`docs/governance/PUBLIC_DOCUMENTATION_INFORMATION_ARCHITECTURE.md`](./docs/governance/PUBLIC_DOCUMENTATION_INFORMATION_ARCHITECTURE.md) — documentation placement and navigation --- -*License: Apache 2.0 · See [NOTICE](./NOTICE) for attribution and project IP boundary* -*Governance spine: [DGAF-Framework](https://github.com/ndrorchestration/DGAF-Framework)* -*README.technical — epistemically bounded revision · 2026-08-25* +*This reference is an implementation map, not a certification, regulatory-conformance statement, or efficacy report.* diff --git a/docs/CURRENT_STATE.md b/docs/CURRENT_STATE.md index 265efad5..676bafc0 100644 --- a/docs/CURRENT_STATE.md +++ b/docs/CURRENT_STATE.md @@ -2,131 +2,97 @@ status: ACTIVE authority: Both owner: DGAF/PDMAL control plane -last_verified: 2026-08-28 +last_verified: 2026-08-29 applies_to_ref: main --- # DGAF-Framework / PDMAL — Current State -GitHub is authoritative for implementation and CI; governance decisions must be recorded through the project's governance process. Historical evidence remains scoped to the exact SHA/run/deployment that produced it. This document describes current state without retroactively transferring historical evidence. +GitHub is authoritative for implementation and CI; governance decisions must be recorded through the project's governance process. Historical evidence remains scoped to the exact SHA/run/deployment that produced it. -> **Current boundary:** `main` is the current documentation/evidence lineage boundary. The experimental verification boundary remains candidate-scoped at `ac8ea267a9f0d995626cf9c3eaf9e6b008b5dc8a`. E2b is CLOSED/VERIFIED for exact tree `d299dd152fb82d48a066d66a64bf0917e20d6167` via run `33047380487`; the later workflow-binding correction at `ac8ea26…` is a separate verification boundary. M6 is CLOSED/VERIFIED for exact candidate `ac8ea267…` via run `33050398324` and remains scoped to that exact verification workspace/job. P7 is scientifically adopted in substance but formally open pending exact freeze binding; P8 remains open/fail-closed; empirical N = 0; authorization is not granted. +> **Current boundary:** `main` remains the documentation/evidence lineage. PR #139 is the current engineering candidate at `d2c24054edfc44cbb2620e6b2b19eb8df8e23850`. The experimental verification boundary remains candidate-scoped; P7 is scientifically adopted in substance but formally open pending exact freeze binding; P8 remains open/fail-closed; empirical N = 0; authorization is not granted. -## Authoritative current state +## Canonical engineering lane — 2026-08-29 -| Gate / boundary | Status | Current meaning | -|---|---|---| -| Historical implementation freeze | HISTORICAL / SUPERSEDED | `3510b86889cd341f7a7cf9ab684fd37b2fafd758` remains provenance only | -| Current `main` | CURRENT DOCUMENTATION/EVIDENCE LINEAGE | Resolve `main` directly; not apparatus identity | -| Experimental verification boundary | CANDIDATE-SCOPED | `ac8ea267a9f0d995626cf9c3eaf9e6b008b5dc8a` | -| E2b | CLOSED / VERIFIED (historical exact-tree scope) | `d299dd152…`; run `33047380487`; artifact `9636185725`; digest `sha256:723aa9d5a1b60242212a8d7533ccf296de37a36349b4a60f53714bb6898ca1fd` | -| M6 | CLOSED / VERIFIED (candidate exact-tree scope) | `ac8ea267…`; run `33050398324`; retained negative-state artifact independently hash-verified; closure does not authorize execution | -| Current-boundary E2b | OPEN / VERIFICATION REQUIRED | Current E2b evidence must be produced against the exact executing workflow boundary used for freeze admissibility | -| TGL contract | BLOCKED / ADVERSARIAL REVIEW | PR #132 produced a 41-pass / 2-fail regression at the TGL → P-35 boundary; PR #133 is the isolated remediation candidate | -| P7 scientific specification | ADOPTED IN SUBSTANCE / FORMALLY OPEN | Scientific decision resolved; exact freeze binding remains open | -| P8 analysis lock | OPEN / FAIL-CLOSED | Candidate-scoped implementation/configuration and verification remain incomplete | -| P2 formal runtime verification | NOT EXECUTED | Authenticated five-case matrix still required | -| P6a formal CORS verification | NOT EXECUTED | Authenticated CORS matrix still required | -| P-07 co-orchestration sweep | REMEDIATED / OPERATIONALLY CLOSED | Sweep `08670C3FDE59`: deprecated `api/health.py` removed; `app/api/health/route.ts` absent on current `main`; `requirements.txt` retained as intentionally empty/documentary; production deployment for `21f043b7…` reached READY and live `/api/health` returned HTTP 200 with the expected health contract | -| Forman–Ricci lattice helper semantics | OPEN / ISSUE #117 | Unweighted dodecahedral `Ric_F(e) = -2` is constant/zero-variance and must produce `NO_DISCRIMINATING_SIGNAL`, not 30 anomaly flags | -| P-38 source integrity | OPEN / ISSUE #122 | `NDR_AUTOINIT_SUBSTRATE_ADAPTER_P38_v1.md` has a truncated historical tail; history audit confirms the earliest retained version is already truncated | -| New immutable freeze | NOT CREATED | No current candidate has crossed the freeze boundary | -| Pilot authorization | NOT GRANTED | Separate governance transition after required predicates and freeze verification | -| Empirical data | N = 0 | No authorized empirical pilot has been executed | - -## TGL / P-35 adversarial review boundary - -PR #132 is **BLOCKED / DRAFT / UNMERGED**. Its observed 41-pass / 2-fail pre-freeze result is treated as a substantive contract-regression signal. The failure is at the TGL → P-35 seam and includes incompatible constructor/method invocation. The review also identified missing `premise_check_fn` injection, weakened exception containment, incomplete `PASS/WARN/SKIP/ESCALATE/KILL` reduction, ambiguous SKIP semantics, and audit-seal sequencing concerns. - -The required remediation is contract restoration rather than broad architectural refactoring. PR #133 is the isolated remediation candidate. It must restore the established P-35 constructor and `evaluate(..., check_fn=...)` contract, fail-closed exception containment, explicit required/conditional gate semantics, deterministic status reduction, and exact final audit sealing, with regression coverage for the identified failure modes. - -This review does not authorize any experimental action. It does not create a freeze, close P7/P8, establish runtime verification, or increase empirical N. - -## E2b provenance boundary - -Run `33047380487` is retained as exact-tree evidence for `d299dd152fb82d48a066d66a64bf0917e20d6167`. It passed exact checkout/target assertions, source requirements fingerprint verification, hash-pinned installation, exact-tree provenance emission, and evidence retention. Artifact `9636185725` has digest `sha256:723aa9d5a1b60242212a8d7533ccf296de37a36349b4a60f53714bb6898ca1fd`. - -This closure is not retroactively invalidated. It is scoped to the tree that was actually executed. The subsequent `ac8ea26…` workflow change is a separate verification boundary. - -## M6 provenance boundary +PR #139 (`feat/dgaf-v1-control-plane-finalize-20260829`) is the canonical combined engineering candidate for the governed recursive control plane and TGL contract remediation. -M6 is CLOSED/VERIFIED for exact candidate/tree `ac8ea267a9f0d995626cf9c3eaf9e6b008b5dc8a` via Governance CI run `33050398324`. Checkout SHA, workflow target SHA, and verifier target SHA matched exactly; the hash-pinned verifier environment completed successfully; machine-readable negative-state evidence was emitted and retained; and the retained artifact digest was independently recomputed as `sha256:dabe2f1909535671e795bb8c1cad0ef0840be4732acebff8f1a340c62b4943b6`. +**Current PR #139 head:** `d2c24054edfc44cbb2620e6b2b19eb8df8e23850` -The observed negative state included empirical N = 0, pilot authorization not granted, no protocol/freeze created, pilot mode not selected, blinding key absent, zero pilot seed/summary artifacts, and no pilot invocation in the verification job. M6 proves that observed negative state for that exact verification workspace/job; it does not constitute proof of absence elsewhere and does not authorize execution. +The candidate includes `GovernanceEnvelope`, deterministic `ControlPlane`/`TaskState`, `StateRegistry`, `BudgetLedger`, `BranchRegistry`, `CommitGate`, hardened TGL status/sealing semantics, adversarial regression tests, capability-boundary tests, and dedicated CI lanes. It does not rebind PDMAL, create a freeze, authorize a pilot, unblind data, or increase empirical N. -## Current verification boundary +### Current engineering invariants -The corrected Governance CI workflow at `ac8ea26…` binds the target candidate SHA to the executing GitHub workflow SHA. Current E2b evidence must be produced and independently checked against the exact executing boundary before it can support current freeze admissibility. +- governance scope can only remain equal or narrow across child derivation; +- task identity and controller-managed runtime state cannot be externally reassigned; +- public task/ledger/registry/event surfaces are read-only; +- merge readiness requires a current sealed TGL `PASS`; +- terminal/escalated tasks cannot consume additional resources; +- child state identity is observed after successful `PREFLIGHT` submission; +- failed child creation cannot pollute the state registry; +- branch provenance preserves distinct branch identities when state IDs coincide; +- CommitGate remains a separate explicit authorization boundary; +- safe terminal abort does not create an authorization path. -The current `main` lineage contains subsequent documentation/semantic corrections, including canonical mathematical notation, bounded Hensel/registry claims, historical-audit corrections, AutoInit provenance corrections, and the lattice reproduction notation correction. Those documentation-lineage changes do not retroactively change candidate-scoped verification results and must not be represented as experimental apparatus verification. +### TGL contract boundary -The earlier M6 artifact targeting historical `e6beeb663…` and verifier merge-ref `2516f32…` remains **NON-CLOSING** for the current candidate boundary; that historical artifact is not the basis for the closed M6 state above. +- required unwired gates are `SKIP` and reduce the turn to `ESCALATE`; +- `WARN` propagates to `TurnStatus.WARN` unless a stronger failure state applies; +- conditional HPG `SKIP` does not itself escalate when Phi-Closure did not pass; +- terminal `KILL` stops downstream gate execution; +- the final audit seal covers the complete gate set, including Herald; +- invalid gate outcomes do not silently become PASS; +- final status is reduced again after Herald, so a Herald `WARN`/`KILL` cannot be hidden by an earlier `PASS`. -## P-07 remediation boundary +### Exact-head engineering verification -Sweep `08670C3FDE59` found three repository/deployment candidates. Cross-connection against current `main` and the production deployment resolved them as follows: +The dedicated `DGAF v1 Control-Plane Contract` run `33247361730` completed **SUCCESS** on implementation head `a728ce3ee8a024646c0971c9d4f392abaa3d691a`. Exact candidate checkout passed, pinned CI dependency installation passed, and the deterministic control-plane/TGL/adversarial/capability-boundary suite passed **41/41**. -1. `api/health.py` was a deprecated Python stub explicitly directing users to `pages/api/health.ts`. It was removed on commit `21f043b7d9a845b3477c4f3bf4a5a66d7d813e9e`. -2. `app/api/health/route.ts` is absent from the current `main` tree; the operational health handler is `pages/api/health.ts`. -3. `requirements.txt` is retained because the repository documents it as intentionally empty and non-operative for the Next.js API deployment path. +That result is scoped to `a728ce3…`. The current integrated candidate `d2c24054…` contains only documentation/governance reconciliation after the tested code head; no later code-changing claim is transferred without fresh exact-head validation. -The resulting Vercel production deployment was READY and source-bound to the same exact `21f043b7…` commit. The deployed `/api/health` endpoint returned HTTP 200 with `psi_cubic=true`, version `1.8.0`, `phi_star=0.618034`, `psi=1.4655712319`, `t0_axiom_guard=true`, and the five declared adapters. This is operational deployment evidence only; it does not substitute for authenticated P2/P6a execution. +### Current-main integration -The GitHub `Deploy to Vercel + Live Regression` workflow remains unable to perform its own authenticated deployment/live-regression branch because `VERCEL_TOKEN` is not configured. The dedicated P2 workflow separately requires `VERCEL_AUTOMATION_BYPASS_SECRET`. Neither missing credential is treated as a code defect. +A non-destructive two-parent merge commit incorporated current `main` commit `cf9d2738f2210f270855869e7ccd0eb660838025` into the PR branch without force-moving the ref. The candidate is 0 commits behind current `main`; the mainline capability-boundary commit is content-covered by the PR's expanded capability suite. -## Canonical mathematical notation boundary +### Canonical agent-role boundary -`φ` is the conventional symbol for the Golden Ratio, `(1+√5)/2 ≈ 1.618033989`. +The current Notion agent registry is authoritative for role identity/intent, while GitHub remains implementation/evidence truth. -`σ_{p,q}` denotes the Spinadel metallic-means family, the positive solution of `x² - px - q = 0`; `σ_n = σ_{n,1}` for the ordinary sequence. `σ_{2,1}` is silver and `σ_{3,1}` is bronze. +- Sentinel-Phi — canonical governance/security identity; `Sentinel` is historical alias only. +- Professor Prodigy — formalization/proof/category discipline; non-orchestrating. +- DemiJoule — advisory resource/constraint analysis; no independent normative authorization. +- Reciprocity — fairness, affected-party, reciprocal-impact, perspective-equity, and asymmetry analysis. +- Herald — evidence/public-surface publication and classification; cannot manufacture evidence or approval. +- Amethyst — meta-orchestration/lifecycle coordination. +- COLLEEN — continuity, archive, provenance, durable-state, and routing integrity. +- Apogee — independent evidence/integrity review and loop validation. -`ρ` denotes the mathematical plastic number, `≈1.3247179572447454`, the unique real root of `x³ - x - 1 = 0`. `P` is an attested alternative notation. `ρP` is not the canonical mathematical notation. +Generic v1 roles are execution contracts and do not create or elevate agent authority. -`pP` / **Platinum Mean** is intentional DGAF-specific notation for the regular-hendecagon unit-side circumradius, `1/(2 sin(π/11)) ≈ 1.774732842`. It is not a standard member of the quadratic metallic-means family and must not be substituted for `ρ` in plastic-number mathematics. +## Authoritative experimental state -The authoritative notation policy is `docs/governance/MATHEMATICAL_NOTATION_POLICY_METALLIC_MEANS_2026-08-28.md`. Historical `ρP` references are retained only as provenance/supersession evidence and must not be treated as current mathematical authority. - -## Forman–Ricci evidence boundary - -For the unweighted regular dodecahedral topology, Forman–Ricci curvature is `Ric_F(e) = -2` for every edge. This is a constant metric with zero variance and therefore **NO_DISCRIMINATING_SIGNAL**. Issue #117 remains open until the helper's output semantics are corrected and regression-tested. Weighted Forman–Ricci remains separately governed as a falsification track; no validation claim follows from the current single-configuration computation. - -## P-38 source-integrity boundary - -Issue #122 tracks the incomplete P-38 substrate-study tail. A Git history audit on 2026-08-28 confirmed that the earliest retained P-38 commit (`8807dc5c…`, 2026-06-13) already ends at the same `Bit-identical a_n replay va...` boundary. The later correction commit therefore did not remove recoverable source text from the retained history; no authoritative remainder has been reconstructed. The issue remains open pending a provenance-controlled external or otherwise authoritative source. This is documentation/source-integrity remediation only and does not advance experimental gates. - -## Expert Panel — 2026-08-28 - -The **Ecosystem Expert Panel** is the cross-agent governance review mechanism defined in the Notion operating charter. Its role specifications are maintained in the Notion Agent Registry; GitHub remains implementation/evidence truth. The panel disposition is **PROCEED, FAIL-CLOSED**. - -Panel seats: -- **Amethyst:** meta-orchestration, normative governance, dependency/closure ledger. -- **COLLEEN:** continuity, archive, provenance, durable state, routing integrity. -- **Professor Prodigy:** formalization/proof and mathematical claim verification; non-orchestrating. -- **Apogee:** independent evidence review, integrity scoring, and P9 preparation. -- **DemiJoule:** constraint/resource and governance-boundary review. -- **Sentinel-Phi:** strategic security, risk containment, and fail-closed monitoring. -- **Herald:** evidence/public-surface synchronization and classification hygiene. -- **Reciprocity:** reciprocal-mathematics and adversarial asymmetry review. - -### Panel execution decision +| Boundary | Status | Meaning | +|---|---|---| +| Current `main` | CURRENT DOCUMENTATION/EVIDENCE LINEAGE | `cf9d2738…`; resolve `main` directly for latest repository state | +| PR #139 engineering candidate | VALIDATED IMPLEMENTATION CHECKPOINT / FRESH HEAD VERIFICATION OPEN | `d2c24054…`; last substantive code checkpoint `a728ce3…` passed 41/41 | +| P7 scientific specification | ADOPTED IN SUBSTANCE / FORMALLY OPEN | Exact freeze binding remains required | +| P8 analysis lock | OPEN / FAIL-CLOSED | Candidate-scoped closure incomplete | +| P2 runtime verification | OPEN / NOT EXECUTED | Exact-source deployment and authenticated runtime matrix still required | +| P6a CORS verification | OPEN / NOT EXECUTED | Exact-source deployment and authenticated runtime matrix still required | +| New immutable freeze | NOT CREATED | No candidate has crossed freeze boundary | +| Pilot authorization | NOT GRANTED | Explicit separate governance transition required | +| Empirical data | N = 0 | No authorized pilot has executed | -1. Continue all non-blocked engineering, documentation, provenance, analysis, and research-hygiene work in parallel. -2. Keep P2/P6a behind their protected credential/dispatch requirements and exact candidate/deployment identity. -3. Treat M6 as closed only for its exact `ac8ea267…` candidate verification scope; do not transfer it to later `main` documentation lineage. -4. Keep E2b scoped to its exact executed tree; later workflow changes require their own evidence where applicable. -5. Advance P7 exact binding, P8 candidate-scoped closure, and independent P9 preparation. -6. Create a new immutable freeze only after all applicable predicates pass. -7. Authorization remains a separate explicit transition; only then may the blinded pilot execute. +## Deployment identity boundary -### Hard panel constraints +The observed READY Vercel production deployment remains historical/supporting evidence because its source SHA `42346ecc34565502ebff02ead55a33b0d74246b8` does not equal current `main` `cf9d2738…`. Issue #137 is the canonical deployment-provenance tracker. -No freeze, authorization, unblinding, or empirical-N increase may be inferred from CI success, deployment readiness, health checks, synthetic fixtures, historical evidence, or narrative state alone. +Vercel status is not treated as proof of exact deployment source identity. Exact deployment/source verification therefore remains open. -## Authorization boundary +## Engineering-lane consolidation -Required before authorization include authenticated P2 and P6a execution on the same deployment identity; blinding custody and unblinding verification; durable archive/retrieval/hash evidence; environment and reproducibility fingerprints; formal P7 exact binding; frozen baseline/negative-control definitions; P8 closure; independent P9 verification; a new immutable freeze; and an explicit authorization decision. +PR #132/#133/#134 are historical diagnostic/remediation records. PR #139 is the single current engineering lane for the v1 recursive control plane plus the TGL contract remediation. -**No empirical pilot execution is authorized. Empirical N remains 0. Authorization remains NOT GRANTED.** +## Evidence boundary -## Related adversarial-review record +CI success, deterministic tests, deployment readiness, synthetic evaluator results, governance documentation, and engineering PRs do not constitute PDMAL efficacy evidence or experimental authorization. Historical evidence remains exact-SHA/run/deployment scoped. -See `docs/governance/TGL_PR132_ADVERSARIAL_REVIEW_2026-08-28.md` for the complete TGL/P-35 contract findings, state-machine analysis, audit/provenance findings, CI/CD identity risks, remediation boundary, and required regression coverage. +**Current experimental state: PRE-FREEZE / FAIL-CLOSED / NOT AUTHORIZED / N=0.** diff --git a/docs/PROJECT_STATUS.md b/docs/PROJECT_STATUS.md index 836c219f..73c7ec8c 100644 --- a/docs/PROJECT_STATUS.md +++ b/docs/PROJECT_STATUS.md @@ -1,6 +1,6 @@ # DGAF/PDMAL Project Status -**Status date:** 2026-08-28 +**Status date:** 2026-08-29 **Repository:** `ndrorchestration/DGAF-Framework` **Current main:** active documentation/evidence lineage; not experimental apparatus identity **Experimental verification boundary:** `ac8ea267a9f0d995626cf9c3eaf9e6b008b5dc8a` @@ -30,7 +30,7 @@ P7 is scientifically adopted in substance, but exact cryptographic binding to th | Current-boundary E2b | OPEN / VERIFICATION REQUIRED | Execute/retain evidence for the exact workflow boundary used for the eventual freeze decision | | M6 | CLOSED / VERIFIED (candidate exact-tree scope) | `ac8ea267…`; run `33050398324`; retained negative-state artifact independently hash-verified | | Runtime characterization | CLOSED FOR CHARACTERIZATION | Historical/non-empirical characterization only | -| Execution contract | PARTIAL / TGL BLOCKED | Authenticated exact-current-tree P2 evidence pending; TGL/P-35 contract regression under remediation | +| Execution contract | PARTIAL / TGL CURRENT-HEAD VALIDATION PENDING | Hardened DGAF v1 control/TGL lane in PR #139; authenticated exact-current-tree P2 evidence pending | | Artifact contract | PARTIAL | Corrective controls present; current candidate execution evidence pending | | Security / blinding | PARTIAL | Fresh operational custody verification pending | | Topology provenance | PARTIAL | Exact current-candidate recomputation pending | @@ -40,7 +40,7 @@ P7 is scientifically adopted in substance, but exact cryptographic binding to th | P7 exact binding | OPEN | Final freeze identity binding remains required | | Analysis lock | OPEN / FAIL-CLOSED | Candidate-scoped closure pending | | Independent verification | NOT EXECUTED | P9 remains pending | -| TGL contract review | BLOCKED / DRAFT REMEDIATION | PR #132 41-pass / 2-fail regression; PR #133 is isolated remediation candidate | +| TGL historical contract review | HISTORICAL / SUPERSEDED | PR #132 produced 41-pass / 2-fail regression; PR #133 was isolated remediation; current consolidated engineering lane is PR #139 | | Forman–Ricci lattice helper semantics | OPEN / ISSUE #117 | Unweighted dodecahedral `Ric_F(e) = -2` is constant/zero-variance and must produce `NO_DISCRIMINATING_SIGNAL`, not 30 anomaly flags | | P-38 source integrity | OPEN / ISSUE #122 | `NDR_AUTOINIT_SUBSTRATE_ADAPTER_P38_v1.md` has a truncated historical tail; history audit confirms the earliest retained version is already truncated | | New freeze | NOT CREATED | Historical freeze cannot be reused | @@ -52,9 +52,9 @@ P7 is scientifically adopted in substance, but exact cryptographic binding to th The 41-pass / 2-fail result associated with PR #132 is a concrete regression signal at the TGL → P-35 integration boundary. The observed failure is not being treated as a transient test issue. The review identified constructor/method incompatibility, missing premise-hook injection, weakened exception containment, incomplete `PASS/WARN/SKIP/ESCALATE/KILL` reduction, ambiguous conditional versus unwired `SKIP`, and audit-seal sequencing concerns. -The selected remediation is intentionally minimal: restore the established P-35 API and TGL fail-closed behavior, make required/conditional gate semantics explicit, implement deterministic status reduction, make the final seal correspond to the authoritative returned audit state, and expand regression coverage. Broad architectural refactoring is out of scope for PR #132/#133. +PR #133 was the isolated historical remediation candidate created to restore the established TGL/P-35 contract. Its evidence remains useful as diagnostic provenance, but it is no longer a current execution authority. PR #139 is the consolidated engineering lane carrying the current control-plane and TGL contract implementation. Exact-current-head CI and adversarial review remain required before any verification claim. -PR #132 remains blocked/draft. PR #133 is the isolated remediation candidate and must obtain its own exact-head validation. Neither PR changes the experimental apparatus identity, creates a freeze, closes P7/P8, grants authorization, or increases empirical N. +Neither the historical TGL remediation work nor PR #139 changes the experimental apparatus identity, creates a freeze, closes P7/P8, grants authorization, or increases empirical N. The detailed diagnostic record is `docs/governance/TGL_PR132_ADVERSARIAL_REVIEW_2026-08-28.md`. @@ -92,7 +92,7 @@ Historical evidence remains scoped to the exact application source, deployment, ## Required closure sequence -1. Resolve the TGL/P-35 contract blocker through the isolated remediation candidate and exact-head validation. +1. Complete exact-current-head validation of PR #139's consolidated control/TGL contract. 2. Execute/retain the current-boundary E2b verification needed for freeze admissibility against the exact executing workflow SHA. 3. Independently inspect exact SHA, scope, integrity, and negative-state claims; M6 closure is already recorded for candidate `ac8ea267…`. 4. Execute authenticated P2 and P6a against the exact deployment identity. @@ -104,4 +104,6 @@ Historical evidence remains scoped to the exact application source, deployment, 10. Obtain explicit pilot authorization. 11. Only then execute the authorized blinded pilot. -**Empirical validity is NOT ESTABLISHED. Pilot authorization is NOT GRANTED. N = 0.** +## Current experimental state + +Empirical validity is NOT ESTABLISHED. Pilot authorization is NOT GRANTED. N = 0. diff --git a/docs/architecture/DGAF_V1_AGENT_ROLE_MAPPING.md b/docs/architecture/DGAF_V1_AGENT_ROLE_MAPPING.md new file mode 100644 index 00000000..a89b4005 --- /dev/null +++ b/docs/architecture/DGAF_V1_AGENT_ROLE_MAPPING.md @@ -0,0 +1,34 @@ +# DGAF v1 Agent-Role Mapping + +**Status:** IMPLEMENTATION CANDIDATE / NON-AUTHORIZING +**Date:** 2026-08-29 + +This document maps the generic v1 control-plane branch roles to the existing DGAF agent registry without changing the agents' normative authority. + +## Generic execution roles + +| Generic role | DGAF agent contribution | Constraint | +|---|---|---| +| `EXPLOIT` | Amethyst-led improvement; may use DemiJoule for resource-efficiency advice | Must remain within inherited envelope | +| `DIVERGE` | Amethyst/COLLEEN may instantiate materially distinct alternatives | Diversity is not independence proof | +| `VERIFY` | Reciprocity, Professor Prodigy, Apogee, and relevant verification components | Professor Prodigy remains non-orchestrating | +| `GOVERN` | Sentinel-Phi as canonical governance identity, with Layer-0 constitutional substrate | Sentinel-Phi may veto/escalate but does not acquire authority from the branch role | + +## Supporting identities + +- **Amethyst** — meta-orchestration and lifecycle coordination. +- **COLLEEN** — continuity, archive, provenance, durable-state, and routing integrity. +- **Sentinel-Phi** — canonical governance/security identity; historical `Sentinel` is an alias, not a separate active seat. +- **DemiJoule** — advisory resource/constraint analysis; no independent normative authorization. +- **Reciprocity** — fairness, affected-party, reciprocal-impact, perspective-equity, and asymmetry analysis within its defined contract. +- **Professor Prodigy** — formalization, proof, mathematical/category discipline; non-orchestrating. +- **Apogee** — independent evidence/integrity review and loop validation. +- **Herald** — evidence/public-surface publication and classification; cannot manufacture evidence or approval. + +## Boundary rule + +The generic role is an execution contract, not a new agent. Existing agent identity and authority remain canonical in the agent registry. A task may invoke a role through one or more eligible agents, but role invocation does not silently change an agent's authority. + +## PDMAL boundary + +These mappings are control-plane semantics only. They do not define PDMAL topology, alter the experimental protocol, or constitute efficacy evidence. diff --git a/docs/architecture/DGAF_V1_CONTROL_PLANE_INTEGRATION.md b/docs/architecture/DGAF_V1_CONTROL_PLANE_INTEGRATION.md new file mode 100644 index 00000000..c1c063e9 --- /dev/null +++ b/docs/architecture/DGAF_V1_CONTROL_PLANE_INTEGRATION.md @@ -0,0 +1,49 @@ +# DGAF v1 — Governed Recursive Control Plane + +**Status:** IMPLEMENTATION CANDIDATE / NON-AUTHORIZING +**Date:** 2026-08-29 + +DGAF v1 is a governed recursive control-plane layer around the existing TGL/P-35 kernel. + +## Canonical boundary + +```text +GovernanceEnvelope + ↓ +ControlPlane / TaskState + ├─ bounded child derivation + ├─ StateRegistry + ├─ BudgetLedger + ├─ BranchRegistry + └─ CommitGate + ↓ +existing TGL / P-35 + ↓ +optional execution substrate (including PDMAL) +``` + +## Core invariants + +1. Child authority, tools, data, risk, and budgets cannot exceed the parent. +2. Illegal lifecycle transitions fail closed without resource side effects. +3. Exact repeated orchestration states cannot recurse indefinitely. +4. Branch outcomes remain inspectable. +5. Consequential actions require explicit, unique proposal/authorization/commit identity. +6. TGL/P-35 cannot be bypassed or replaced by the recursive controller. +7. Required TGL `SKIP` states escalate rather than become PASS. +8. `WARN` propagates to turn status unless a stronger failure applies. +9. Terminal failure stops downstream execution. +10. The final TGL audit seal covers the complete gate set, including Herald. +11. Consensus, semantic distance, or harmonic/geometric motifs are not authorization signals or proof of independent evidence. + +## Agent and experimental boundaries + +Generic roles are execution contracts only. They do not create or elevate agent authority. Sentinel-Phi remains canonical governance identity; Professor Prodigy remains non-orchestrating; DemiJoule remains advisory; Reciprocity remains affected-party/fairness review; Herald publishes/classifies evidence but cannot manufacture evidence or approval. + +PDMAL remains an optional governed experimental substrate. This layer cannot create a freeze, grant pilot authorization, unblind data, or increase empirical N. + +## Verification + +Source presence is not verification. Required sequence is deterministic contracts → exact-head CI → adversarial review → TGL/P-35 integration validation → live-provider/substrate adapters. + +**Current experimental state:** PRE-FREEZE / FAIL-CLOSED / NOT AUTHORIZED / N=0. diff --git a/docs/architecture/DGAF_V1_EXECUTION_READINESS.md b/docs/architecture/DGAF_V1_EXECUTION_READINESS.md new file mode 100644 index 00000000..d4c467bd --- /dev/null +++ b/docs/architecture/DGAF_V1_EXECUTION_READINESS.md @@ -0,0 +1,53 @@ +# DGAF v1 Execution Readiness + +**Status:** VALIDATION IN PROGRESS / NON-AUTHORIZING +**Date:** 2026-08-29 + +## Candidate + +PR #139: `feat/dgaf-v1-control-plane-finalize-20260829` + +Current engineering head must be resolved directly from GitHub for every execution. The authoritative candidate SHA is the exact PR head reported by GitHub at the time of each execution. A CI result is valid only for the SHA actually checked out by that run. + +## Required CI checks + +- `pptl/tests/test_v1_control_plane.py` +- `pptl/tests/test_v1_tgl_integration.py` +- `pptl/tests/test_v1_adversarial_contract.py` +- `pptl/tests/test_v1_capability_boundaries.py` +- import/package integrity +- exact current-head checkout identity +- applicable repository/evidence/security workflows + +## Adversarial acceptance criteria + +The candidate must demonstrate, on the exact executed head: + +1. child authority/tool/data/risk/resource scopes never widen; +2. child provenance metadata cannot overwrite inherited parent provenance; +3. lifecycle violations fail closed; +4. active nonterminal tasks have a safe terminal abort path; +5. task identity fields cannot be externally reassigned after construction; +6. lifecycle state, TGL status/seal, concurrency state, and runner configuration cannot be externally reassigned; +7. public task, ledger, state-registry, branch-registry, and event surfaces expose no mutating capability; +8. recursive depth and active concurrency ceilings are enforced; +9. budget overruns escalate without leaking active slots or persistent reservations; +10. repeated canonical states are rejected according to the documented state-identity contract; +11. child state is registered only after successful submission and reflects the post-submit lifecycle state; +12. TGL terminal failures and runner exceptions propagate to control-plane escalation; +13. merge readiness requires a current sealed TGL `PASS`; stale results cannot be reused after a new evaluation starts; +14. consequential commit cannot occur without explicit authorization, duplicate authorization, or commit replay; +15. commit request payloads and branch provenance remain immutable after capture; +16. multiple branches sharing a state ID retain distinct branch identities; +17. branch lineage cannot cycle; +18. PDMAL remains outside the generic control-plane authorization path. + +## Evidence interpretation + +Engineering CI, deterministic fixtures, synthetic evaluator outputs, deployment readiness, and documentation consistency are implementation evidence only. They do not constitute PDMAL efficacy evidence, experimental authorization, or a new freeze. + +## Non-authorizing constraint + +Passing engineering CI does not create a PDMAL freeze, authorize a pilot, increase empirical N, or establish efficacy. + +Experimental state: PRE-FREEZE / FAIL-CLOSED / NOT AUTHORIZED / N=0. diff --git a/docs/architecture/DGAF_V1_FILE_TREE_PLAN.md b/docs/architecture/DGAF_V1_FILE_TREE_PLAN.md new file mode 100644 index 00000000..f510a97d --- /dev/null +++ b/docs/architecture/DGAF_V1_FILE_TREE_PLAN.md @@ -0,0 +1,59 @@ +# DGAF v1 — File Tree and Ownership Plan + +**Status:** IMPLEMENTATION CANDIDATE / NON-AUTHORIZING +**Date:** 2026-08-29 + +```text +DGAF-Framework/ +├── .github/workflows/ +│ └── control-plane-contract.yml +├── docs/architecture/ +│ ├── DGAF_V1_CONTROL_PLANE_INTEGRATION.md +│ ├── DGAF_V1_FILE_TREE_PLAN.md +│ └── DGAF_V1_AGENT_ROLE_MAPPING.md +└── pptl/ + ├── orchestrator.py + ├── triadic_governance_loop.py + ├── procluding_premise.py + ├── governance_envelope.py + ├── control_plane.py + ├── state_identity.py + ├── budget_ledger.py + ├── branch_registry.py + ├── commit_gate.py + └── tests/ + ├── test_v1_control_plane.py + ├── test_v1_tgl_integration.py + └── test_v1_adversarial_contract.py +``` + +## Ownership + +| Capability | Canonical owner | +|---|---| +| Inherited governance scope | `pptl/governance_envelope.py` | +| Lifecycle state machine | `pptl/control_plane.py` | +| Exact repeated-state identity | `pptl/state_identity.py` | +| Resource/concurrency accounting | `pptl/budget_ledger.py` | +| Branch provenance | `pptl/branch_registry.py` | +| Consequential-action authorization | `pptl/commit_gate.py` | +| Per-turn governance | `pptl/triadic_governance_loop.py` | +| Constitutional admission | `pptl/procluding_premise.py` | + +One concept has one canonical semantic owner. TGL gate definitions are not duplicated in the recursive control plane. + +## Integration boundary + +`orchestrator.py` remains the integration point. The new control plane governs lifecycle and resource/branch boundaries; TGL remains the per-turn governance kernel. + +## Evidence boundary + +The final TGL audit seal must cover the complete gate set, including Herald, and required unwired gates must reduce the turn to `ESCALATE` rather than PASS. The generic control plane cannot infer authorization from `COMMIT_READY` alone. + +## PDMAL boundary + +PDMAL remains below the generic control plane as an optional governed experimental substrate. No v1 control-plane module may silently change experimental candidate identity, freeze state, or authorization. + +## Cross-repository boundary + +`ndrorchestration/agent-control-plane` is reference material for contract comparison only. It is not a DGAF runtime dependency. diff --git a/docs/evidence/PDMAL_EVIDENCE_INDEX.md b/docs/evidence/PDMAL_EVIDENCE_INDEX.md index f1d4e280..d5fc42e9 100644 --- a/docs/evidence/PDMAL_EVIDENCE_INDEX.md +++ b/docs/evidence/PDMAL_EVIDENCE_INDEX.md @@ -2,7 +2,7 @@ status: ACTIVE authority: Both owner: DGAF/PDMAL control plane -last_verified: 2026-08-28 +last_verified: 2026-08-29 applies_to_sha: ac8ea267a9f0d995626cf9c3eaf9e6b008b5dc8a scope_note: >- This index records evidence and gate state. Historical evidence remains @@ -22,7 +22,7 @@ This is a control-plane registry, not empirical evidence and not a self-authoriz | Experimental verification boundary | CANDIDATE-SCOPED | `ac8ea267a9f0d995626cf9c3eaf9e6b008b5dc8a` | Current pre-freeze candidate verification boundary; later documentation commits do not inherit its evidence automatically | | Historical implementation freeze | HISTORICAL / SUPERSEDED | `3510b86889cd341f7a7cf9ab684fd37b2fafd758` | Historical apparatus only | | Corrected pilot runner | CANDIDATE | Current verification boundary | Exact candidate verification pending | -| TGL contract | BLOCKED / ADVERSARIAL REVIEW | PR #132 / PR #133 | 41-pass / 2-fail regression at TGL → P-35 seam; isolated contract-restoration remediation remains pending exact-head validation | +| TGL contract | CURRENT ENGINEERING PREREQUISITE | PR #139; historical regression PR #132 / remediation PR #133 | The 41-pass / 2-fail result at the TGL → P-35 seam is retained as diagnostic provenance; current consolidated control/TGL implementation is in PR #139 and still requires exact-head validation | | Environment lock | VERIFY | Python 3.12.0; NumPy 2.5.1; NetworkX 3.6.1 | Fresh matching environment required | | Runtime characterization | CLOSED FOR CHARACTERIZATION | Run `32112658368` | Operational characterization, not efficacy evidence | | Blinding operational verification | CLOSED FOR SYNTHETIC VERIFICATION | Run `32113226935` | Synthetic custody only | @@ -51,6 +51,6 @@ Historical acceptance, characterization, synthetic blinding, topology, and secur ## TGL/P-35 boundary -The TGL review is an implementation/governance control issue, not experimental evidence. PR #132 remains blocked/draft. Its 41-pass / 2-fail result is retained as a substantive regression signal. The isolated remediation candidate must pass its own exact-head validation before the execution-contract predicate can advance. +The TGL review is an implementation/governance control issue, not experimental evidence. PR #132 remains historical blocked/draft provenance. PR #133 is a historical isolated remediation record and is not a current execution authority. PR #139 is the current consolidated engineering lane and must pass exact-head validation before any dependent execution-contract predicate can advance. The remediation boundary does not create a freeze, authorize execution, close P7/P8, or increase empirical N. diff --git a/docs/experiment/PDMAL_CURRENT_CONTROL_STATE.md b/docs/experiment/PDMAL_CURRENT_CONTROL_STATE.md index 1f89061e..14960264 100644 --- a/docs/experiment/PDMAL_CURRENT_CONTROL_STATE.md +++ b/docs/experiment/PDMAL_CURRENT_CONTROL_STATE.md @@ -2,7 +2,7 @@ status: ACTIVE authority: Both owner: DGAF/PDMAL control plane -last_verified: 2026-08-28 +last_verified: 2026-08-29 applies_to_sha: ac8ea267a9f0d995626cf9c3eaf9e6b008b5dc8a --- @@ -19,10 +19,10 @@ This is the current pre-authorization control record. Historical evidence remain | Exact-tree E2b | CLOSED / VERIFIED | Exact-tree run `33047380487` is valid for `d299dd152…`; the corrected workflow boundary is separately scoped and must not be conflated with that historical exact-tree record | | Exact-candidate M6 | CLOSED / VERIFIED | Governance CI run `33050398324`; exact candidate `ac8ea267…`; retained negative-state artifact independently hash-verified with digest `sha256:dabe2f1909535671e795bb8c1cad0ef0840be4732acebff8f1a340c62b4943b6` | | Corrected runner | CANDIDATE | Explicit `ffcr_success`, schema validation, sidecar verification, and matrix coordinates are implemented; empirical execution evidence remains absent | -| TGL contract | BLOCKED / ADVERSARIAL REVIEW | PR #132 produced a 41-pass / 2-fail regression at the TGL → P-35 boundary; PR #133 is the isolated remediation candidate | +| TGL contract | ENGINEERING REMEDIATION PRESENT / EXACT-HEAD VERIFICATION PENDING | The prior 41-pass / 2-fail regression at the TGL → P-35 boundary was isolated and remediated in current PR #139. PRs #132/#133/#134 are historical/superseded records. The current v1/TGL implementation adds explicit required-gate semantics, deterministic WARN/ESCALATE/KILL reduction, exception containment, and exact final audit sealing. Fresh exact-head CI remains required before the engineering implementation is described as verified. | | P7 scientific specification | TECHNICALLY ADJUDICATED / FORMALLY OPEN FOR FREEZE BINDING | Primary contrast selected; exact protocol/candidate/freeze binding remains required | | P8 analysis lock | OPEN / FAIL-CLOSED | Implementation/configuration controls exist; complete candidate-scoped closure package remains incomplete | -| Candidate governance verification | PARTIALLY CLOSED | Exact-scope E2b/M6 are closed for their stated boundaries; later repository documentation commits do not inherit that evidence automatically | +| Candidate governance verification | PARTIALLY CLOSED | Exact-scope E2b/M6 are closed for their stated boundaries; later repository/engineering commits do not inherit that evidence automatically | | Artifact contract | PARTIAL | End-to-end semantics and adversarial tests exist; fresh candidate-scoped evidence for the full artifact contract remains required | | Blinding custody | PARTIAL | Synthetic/control evidence exists; operational custody and unblinding procedure remain evidence-bound | | Durable retention | OPEN | Archive destination plus independent retrieval/hash proof required | @@ -34,11 +34,20 @@ This is the current pre-authorization control record. Historical evidence remain ## TGL / P-35 remediation boundary -PR #132 remains blocked and must not be treated as an experimental apparatus identity. The 41-pass / 2-fail result is a concrete contract-regression signal. The identified defects include P-35 constructor/method incompatibility, missing premise-hook injection, weakened exception containment, incomplete status reduction, ambiguous SKIP semantics, and audit-seal sequencing. +The historical PR #132 regression remains a provenance record: its 41-pass / 2-fail result identified concrete TGL/P-35 contract failures, including constructor/method incompatibility, missing premise-hook injection, weakened exception containment, incomplete status reduction, ambiguous SKIP semantics, and audit-seal sequencing. -PR #133 is an isolated remediation candidate. Its scope is limited to restoring the established TGL/P-35 contract and adding regression coverage. It does not authorize pilot execution, create a freeze, change the PDMAL treatment, or advance empirical N. +Those remediation concerns are now consolidated into **PR #139**, the current combined engineering lane for DGAF v1 control-plane and TGL contract hardening. PRs #132/#133/#134 are closed historical/superseded records and must not be treated as current execution authorities or experimental apparatus identities. -TGL must distinguish unwired required-gate `SKIP` from dependency-caused or intentionally non-applicable `SKIP`. Requiredness should be declared rather than inferred solely from step numbers. The final audit seal must represent exactly the authoritative audit object returned to downstream consumers. +The current TGL implementation distinguishes: + +- unwired required-gate `SKIP` → `ESCALATE`; +- `WARN` propagation; +- terminal failure → downstream stop; +- conditional HPG `SKIP` when Phi-Closure is not `PASS`; +- invalid hook results and hook exceptions → fail-closed terminal failure; +- exact final returned gate-set sealing, including Herald. + +Requiredness is declared rather than inferred solely from step numbers. The final audit seal must represent exactly the authoritative audit object returned to downstream consumers. ## Candidate and documentation boundary @@ -62,7 +71,7 @@ Authorization is considered only after the required predicate evidence and freez ## Required next evidence events -1. Resolve the TGL/P-35 contract blocker through the isolated remediation candidate and exact-head validation. +1. Complete exact-head validation of PR #139's consolidated TGL/control-plane remediation. 2. Complete P7 exact candidate/protocol/analysis binding. 3. Complete remaining P8 artifact, environment, reproducibility, custody, and runtime-dependent evidence. 4. Complete authenticated P2/P6a where required, using the exact candidate/deployment identity. diff --git a/docs/governance/CONTROL_PLANE_ADAPTER_BOUNDARY_AUDIT.md b/docs/governance/CONTROL_PLANE_ADAPTER_BOUNDARY_AUDIT.md new file mode 100644 index 00000000..45f2934e --- /dev/null +++ b/docs/governance/CONTROL_PLANE_ADAPTER_BOUNDARY_AUDIT.md @@ -0,0 +1,43 @@ +# DGAF Control-Plane Adapter Boundary Audit + +**Status:** ENGINEERING AUDIT / NON-AUTHORIZING +**Date:** 2026-08-29 +**Current PR #139 head:** `2df8c0601d83488a305f211f510953ea81edcb01` + +## Scope + +This audit covers the boundary between the generic DGAF v1 recursive control plane and consequential external/internal adapters. + +## Required invariants + +1. Consequential side effects require an explicit `CommitGate` proposal and authorization. +2. Commit requests have unique immutable request identities. +3. Authorization is one-way and cannot replace an existing authorization. +4. A request cannot be committed more than once. +5. `COMMIT_READY` is not itself execution authority. +6. TGL/P-35 remains the per-turn governance kernel and cannot be bypassed by the control plane. +7. TGL status/seal evidence used for merge readiness must be valid sealed evidence; stale status is cleared when a new evaluation begins. +8. Task identity and lifecycle state are controller-managed; public control-plane views do not expose mutators for internal state. +9. Escalated or terminated tasks cannot consume additional resources. +10. Child governance scope cannot widen authority, tool/data scope, risk, budget, metadata, or side-effect permissions. +11. Child state registration occurs after successful submission at `PREFLIGHT`, avoiding phantom state identities on failed creation. +12. Branch provenance retains distinct branch identities even when multiple branches share a state ID. +13. Herald may publish/classify evidence but cannot manufacture evidence, authorization, or normative approval. +14. PDMAL remains an optional substrate; control-plane state cannot mutate experimental candidate identity, freeze, authorization, blinding, or empirical N. +15. `agent-control-plane` remains reference material unless a separately governed adapter contract adopts it. + +## Evidence + +The first dedicated v1 contract execution on an earlier PR merge ref produced 32 passed / 3 failed. The failures were contract-test mismatches and were diagnosed/corrected. That result is historical diagnostic evidence and is not relabeled as validation of the current head. + +For exact current head `2df8c060…`, CodeQL and truth-layer checks completed successfully. The dedicated `DGAF v1 Control-Plane Contract` exact-head check currently has no check-run record, so the control-plane implementation remains validation-pending. Historical successful workflow results on other SHAs are not automatically transferable. + +## Deployment boundary + +GitHub's aggregate status for the current engineering line retains the Vercel deployment-rate-limit failure condition. Issue #137 remains the canonical deployment/source-binding tracker. A same-branch READY preview is supporting evidence only and does not establish current-main production identity. + +## Disposition + +The adapter boundary is implemented and adversarially specified. Current-head engineering closure remains contingent on fresh dedicated contract execution and any required exact deployment verification. + +**Experimental state:** PRE-FREEZE / FAIL-CLOSED / NOT AUTHORIZED / N=0. diff --git a/docs/governance/DGAF_V1_AGENT_ROLE_MAPPING.md b/docs/governance/DGAF_V1_AGENT_ROLE_MAPPING.md new file mode 100644 index 00000000..674651c1 --- /dev/null +++ b/docs/governance/DGAF_V1_AGENT_ROLE_MAPPING.md @@ -0,0 +1,26 @@ +# DGAF v1 Agent-Role Mapping + +**Status:** IMPLEMENTATION CANDIDATE / NON-AUTHORIZING +**Date:** 2026-08-29 + +Generic v1 roles map onto existing agents without changing normative authority. + +| Generic role | DGAF contribution | Constraint | +|---|---|---| +| `EXPLOIT` | Amethyst-led improvement; DemiJoule may advise | Inherited envelope only | +| `DIVERGE` | Amethyst/COLLEEN alternatives | Diversity is not independence proof | +| `VERIFY` | Reciprocity, Professor Prodigy, Apogee, verification components | Professor Prodigy remains non-orchestrating | +| `GOVERN` | Sentinel-Phi with Layer-0 substrate | Role does not create authority | + +## Canonical identities + +- Sentinel-Phi — canonical governance/security identity; `Sentinel` is historical alias only. +- Professor Prodigy — formalization/proof/category discipline; non-orchestrating. +- DemiJoule — advisory resource/constraint analysis; no independent normative authorization. +- Reciprocity — fairness and affected-party review. +- Herald — evidence/public-surface publication and classification; cannot manufacture evidence or approval. +- Amethyst — meta-orchestration and lifecycle coordination. +- COLLEEN — continuity, archive, provenance, durable-state, and routing integrity. +- Apogee — independent evidence/integrity review and loop validation. + +Role invocation cannot silently elevate authority. The TGL kernel remains the per-turn governance authority. PDMAL remains an optional governed experimental substrate. diff --git a/docs/governance/DGAF_V1_CONTROL_PLANE_FINALIZATION.md b/docs/governance/DGAF_V1_CONTROL_PLANE_FINALIZATION.md new file mode 100644 index 00000000..7fe87966 --- /dev/null +++ b/docs/governance/DGAF_V1_CONTROL_PLANE_FINALIZATION.md @@ -0,0 +1,32 @@ +# DGAF v1 Control-Plane Finalization + +**Status:** IMPLEMENTATION CANDIDATE / NON-AUTHORIZING + +PR #139 is the canonical combined engineering lane for the governed recursive control plane and current TGL contract remediation. Earlier PRs #132/#133/#134 are historical or superseded and are not separate current execution authorities. + +The candidate is based on current `main`. Exact-head CI and adversarial review are required before final verification claims. Production source binding remains a separate infrastructure gate under Issue #137. + +## Closed engineering invariants + +- GovernanceEnvelope authority, tool, data, risk, budget, metadata, and side-effect scope can only remain equal or narrow across child derivation. +- Task identity fields are immutable after construction. +- Lifecycle state, TGL status/seal, and concurrency state are controller-managed and cannot be externally assigned. +- Public task, ledger, registry, and event surfaces are read-only views. +- Merge readiness requires successful sealed TGL evaluation; stale evaluation evidence is cleared when a new evaluation begins. +- Escalated/terminated tasks cannot consume additional resources. +- Child registration is transactionally ordered so failed creation cannot pollute state identity; post-submit `PREFLIGHT` is the observed child state. +- Branch provenance preserves distinct branch identities even when state IDs coincide. +- CommitGate remains the explicit proposal/authorization barrier; `COMMIT_READY` does not itself execute or authorize a consequential side effect. +- Safe terminal abort is available from active lifecycle states without creating an authorization path. + +## TGL contract boundary + +Required unwired `SKIP` remains fail-closed to escalation; `WARN` propagates unless a stronger failure applies; HPG is conditional on Phi-Closure; terminal failures stop downstream execution; and final audit sealing must cover the authoritative returned audit object. + +## Experimental boundary + +The control plane does not rebind PDMAL, create a freeze, grant pilot authorization, unblind data, or increase empirical N. + +## Current experimental state + +PRE-FREEZE / FAIL-CLOSED / NOT AUTHORIZED / N=0 diff --git a/docs/governance/DGAF_V1_FILE_TREE_PLAN.md b/docs/governance/DGAF_V1_FILE_TREE_PLAN.md new file mode 100644 index 00000000..4145fe8f --- /dev/null +++ b/docs/governance/DGAF_V1_FILE_TREE_PLAN.md @@ -0,0 +1,21 @@ +# DGAF v1 — File Tree and Ownership Plan + +**Status:** IMPLEMENTATION CANDIDATE / NON-AUTHORIZING +**Date:** 2026-08-29 + +PR #139 is the canonical current engineering lane for the governed recursive control plane and TGL contract remediation. + +| Capability | Canonical owner | +|---|---| +| Inherited governance scope | `pptl/governance_envelope.py` | +| Recursive lifecycle | `pptl/control_plane.py` | +| Exact state identity | `pptl/state_identity.py` | +| Resource/concurrency accounting | `pptl/budget_ledger.py` | +| Branch provenance | `pptl/branch_registry.py` | +| Consequential-action authorization | `pptl/commit_gate.py` | +| Per-turn governance | `pptl/triadic_governance_loop.py` | +| Constitutional admission | `pptl/procluding_premise.py` | + +TGL semantics are not duplicated in the recursive control plane. Required TGL `SKIP` states escalate, `WARN` propagates, terminal failures stop downstream execution, and final audit sealing covers the complete gate set. + +PDMAL remains an optional governed experimental substrate. The v1 layer does not alter candidate identity, freeze state, authorization, blinding, or empirical N. diff --git a/docs/governance/DGAF_V1_FINALIZATION_GATE.md b/docs/governance/DGAF_V1_FINALIZATION_GATE.md new file mode 100644 index 00000000..29ca88cb --- /dev/null +++ b/docs/governance/DGAF_V1_FINALIZATION_GATE.md @@ -0,0 +1,37 @@ +# DGAF v1 Finalization Gate + +**Status:** IMPLEMENTATION CANDIDATE / NON-AUTHORIZING +**Date:** 2026-08-29 + +PR #139 is the canonical combined engineering lane for DGAF v1 recursive control-plane implementation and current TGL contract remediation. Earlier PRs #132/#133/#134 are historical or superseded and are not separate current execution authorities. + +## Closure conditions + +1. Current-main-based candidate branch exists with no divergence at creation. +2. Governance Envelope enforces downward-only authority, tool/data, risk, and resource scope. +3. ControlPlane enforces legal lifecycle transitions, bounded depth, active-parent child creation, and fail-closed budget/concurrency handling. +4. Exact state identity supports deterministic repeated-state detection. +5. Branch provenance retains accepted, rejected, correlated, escalated, and terminal outcomes. +6. CommitGate requires explicit proposal and authorization before commit, with unique request identity and one-way authorization. +7. TGL/P-35 remains the per-turn governance kernel and is not bypassed by the control plane. +8. Required TGL `SKIP` states escalate rather than reduce to PASS; `WARN` propagates; terminal failure stops downstream execution; final audit sealing covers the complete gate set including Herald. +9. Agent-role mapping preserves current authority semantics. +10. PDMAL remains an optional governed substrate and its experimental state is not altered. +11. Exact-head CI execution and adversarial review are required before verification claims. + +## Current gate disposition + +- Architecture: CLOSED FOR V1 SCOPE +- Placement: CLOSED FOR V1 SCOPE +- Implementation candidate: PRESENT +- Deterministic test coverage: PRESENT +- Exact-head CI: FRESH VALIDATION REQUIRED after latest TGL/state/public-surface commits +- Adversarial review: ACTIVE / CONTINUING +- Production source binding: SEPARATE OPEN GATE (#137) +- PDMAL freeze: NOT CREATED +- Pilot authorization: NOT GRANTED +- Empirical N: 0 + +This record is an engineering control surface and cannot authorize empirical execution or transfer historical evidence across SHA boundaries. + +**Current experimental boundary: PRE-FREEZE / FAIL-CLOSED / NOT AUTHORIZED / N=0.** diff --git a/docs/governance/NOTION_GITHUB_RECONCILIATION_2026-08-29.md b/docs/governance/NOTION_GITHUB_RECONCILIATION_2026-08-29.md new file mode 100644 index 00000000..ec98fdcd --- /dev/null +++ b/docs/governance/NOTION_GITHUB_RECONCILIATION_2026-08-29.md @@ -0,0 +1,42 @@ +# Notion ↔ GitHub Reconciliation — 2026-08-29 + +## Result + +The latest Notion Operational Control Center and agent-registry records were checked against the DGAF-Framework GitHub v1 finalization lane. + +## Reconciled authority mapping + +- Sentinel-Phi is the canonical governance/security identity; historical Sentinel/Sentience identities are not separate active seats. +- Professor Prodigy remains non-orchestrating and focused on formalization/proof/category discipline. +- DemiJoule remains advisory and resource/constraint focused. +- Reciprocity retains affected-party, fairness, reciprocal-impact, perspective-equity, and asymmetry review. +- Herald handles evidence/public-surface publication and cannot manufacture evidence or authorization. +- Amethyst retains meta-orchestration/lifecycle coordination; COLLEEN retains continuity/provenance/archive integrity. + +## GitHub v1 candidate + +PR #139 is the clean current-main-based implementation candidate for the viable Governed Recursive Control Plane subset and current TGL contract remediation. + +**Current PR #139 head:** `a728ce3ee8a024646c0971c9d4f392abaa3d691a` + +PR #136 was superseded and closed. PRs #132/#133/#134 are historical or superseded engineering records rather than parallel current execution authorities. + +The v1 control-plane role names (`EXPLOIT`, `DIVERGE`, `VERIFY`, `GOVERN`) are execution contracts, not new agent identities and not new normative authorities. + +## Engineering-control reconciliation + +The current candidate enforces downward-only governance inheritance, controller-managed lifecycle/TGL state, read-only public control-plane views, sealed-evidence requirements for merge readiness, terminal resource-consumption barriers, post-submit child-state registration, branch-identity preservation, and an explicit CommitGate authorization barrier. + +These controls are engineering implementation facts, not experimental authorization. + +## Evidence boundary + +Notion governance records do not transfer GitHub CI, deployment, PDMAL, or experimental evidence. Exact SHA/run/deployment identity remains mandatory. + +The current deployment-provenance gate remains Issue #137. The current PR #139 branch is reconciled to the latest `main` lineage; Vercel source identity remains separately unproven and must not be inherited from historical READY deployments or green status contexts. + +## Experimental boundary + +No freeze, authorization, unblinding, or empirical execution is implied by the v1 implementation or CI. + +PRE-FREEZE / FAIL-CLOSED / NOT AUTHORIZED / N=0 diff --git a/docs/governance/P8_ANALYSIS_LOCK.md b/docs/governance/P8_ANALYSIS_LOCK.md index 9acc2db9..51e840a2 100644 --- a/docs/governance/P8_ANALYSIS_LOCK.md +++ b/docs/governance/P8_ANALYSIS_LOCK.md @@ -18,13 +18,14 @@ The current `main` branch is a living documentation/evidence lineage and is not itself the experimental apparatus identity. The experimental verification boundary remains candidate-scoped at **`ac8ea267a9f0d995626cf9c3eaf9e6b008b5dc8a`** pending any separately governed candidate transition. -PR #132 exposed a TGL/P-35 control-plane contract regression (41 passed / 2 failed). PR #133 is an isolated remediation candidate. TGL remediation is a prerequisite to reliable candidate verification, not a P8 closure event and not an authorization transition. +PR #132 exposed a TGL/P-35 control-plane contract regression (41 passed / 2 failed). PR #133 was the isolated historical remediation candidate. The consolidated current engineering lane is PR #139. TGL remediation remains a prerequisite to reliable candidate verification, not a P8 closure event and not an authorization transition. | Binding | Value | State | |---|---|---| -| Experimental verification boundary | `ac8ea267a9f0d995626cf9c3eaf9e6b008b5dc8a` | CURRENT CANDIDATE BOUNDARY | -| TGL remediation | PR #133 | DRAFT / VALIDATION PENDING | -| Blocked regression | PR #132 | DRAFT / UNMERGED | +| Experimental verification boundary | `ac8ea267a9f0d995626cf9c3eaf9e6b008b5dc8a` | CURRENT EXPERIMENTAL BOUNDARY | +| Current TGL/control-plane engineering lane | PR #139 | CURRENT / EXACT-HEAD VALIDATION REQUIRED | +| Historical regression | PR #132 | HISTORICAL DIAGNOSTIC | +| Historical isolated remediation | PR #133 | HISTORICAL / SUPERSEDED | | Historical candidate | `e6beeb66335e1b50a239697badab22dab50eb5ba` | HISTORICAL | | Analysis implementation | `experiments/pdmal_pilot/analysis.py` | CURRENT-TREE / RE-BIND AT P8 CLOSURE | | Analysis configuration SHA | `6cab3f1ed6d4e040141598d293628dbab52442234c519b3e231b76a2896f09a8` | SELECTED / PRE-FREEZE | @@ -49,7 +50,7 @@ Before candidate-scoped P8 closure, the TGL/P-35 contract must be validated on t - audit seal coverage of the exact returned audit object; - regression coverage for these semantics. -A passing TGL remediation test suite does not itself close P8, alter P7, create a freeze, or authorize the pilot. +A passing TGL/control-plane remediation test suite does not itself close P8, alter P7, create a freeze, or authorize the pilot. ## Protocol/candidate separation rule diff --git a/docs/governance/PR139_CI_EXECUTION_RECORD.md b/docs/governance/PR139_CI_EXECUTION_RECORD.md new file mode 100644 index 00000000..0cc4d26e --- /dev/null +++ b/docs/governance/PR139_CI_EXECUTION_RECORD.md @@ -0,0 +1,46 @@ +# PR #139 CI Execution Record + +## Current status + +CI EXECUTION / NON-AUTHORIZING + +**Current confirmed PR #139 head:** `b7d1fe4e49f4e126b7033d3341e7d831e67dff28` + +The v1 candidate contains the deterministic control-plane suite, TGL integration suite, adversarial contract suite, capability-boundary suite, and dedicated security/evidence workflows. + +## Candidate binding + +The authoritative engineering candidate is the exact PR head SHA reported by GitHub. Historical run results must not be relabeled as evidence for a later head. + +## Historical diagnostic execution + +An earlier dedicated v1 contract execution on a PR merge ref observed **32 passed / 3 failed**. The failures were diagnosed as contract-test mismatches: a stale side-effect inheritance expectation, a stale post-escalation assertion, and an abort-transition expectation inconsistent with the then-current lattice. The corresponding controls/tests were corrected. + +An earlier PR #132 adversarial execution remains a separate historical signal: **41 passed / 2 failed** at the TGL → P-35 seam. The current consolidated implementation is in PR #139; the earlier result remains diagnostic provenance. + +## Current exact-head evidence + +For `7807d956…`, the dedicated `DGAF v1 Control-Plane Contract` run `33246694071` completed **SUCCESS**, with exact candidate checkout and pinned dependency setup passing and the deterministic control-plane/TGL/adversarial/capability-boundary suite passing **40/40**. + +The current candidate later received documentation/governance reconciliation commits and a non-destructive merge commit incorporating current `main`, followed by a Herald-status regression correction. Fresh exact-head verification of the latest code-changing head remains required where that head changes implementation semantics. + +## CI hardening + +The dedicated workflow definition is configured to: + +- check out `${{ github.event.pull_request.head.sha || github.sha }}`; +- assert `git rev-parse HEAD` exactly equals the expected candidate SHA; +- install pinned repository CI dependencies plus pinned pandas; +- execute core control-plane, TGL integration, adversarial, and capability-boundary suites. + +## Deployment blocker + +Vercel source identity remains a separate provenance gate under Issue #137. Green or rate-limited status contexts do not establish exact deployment-source identity by themselves. + +## Interpretation + +CI success, deployment readiness, deterministic fixtures, synthetic evaluator results, and documentation consistency are engineering evidence only. None constitutes PDMAL efficacy evidence, a new freeze, or pilot authorization. + +## Current experimental state + +PRE-FREEZE / FAIL-CLOSED / NOT AUTHORIZED / N=0 diff --git a/docs/governance/PR139_HARDENING_NOTES.md b/docs/governance/PR139_HARDENING_NOTES.md new file mode 100644 index 00000000..50fa80ad --- /dev/null +++ b/docs/governance/PR139_HARDENING_NOTES.md @@ -0,0 +1,75 @@ +# PR #139 Hardening Notes + +## Current candidate + +Current PR #139 head: `be3203868a9fe7a4c156a4c52885a15872a63fa0` + +All findings below are engineering-control findings. They do not authorize PDMAL execution or transfer experimental evidence across SHA boundaries. + +## Closed engineering findings + +### Active-resource release + +Escalated tasks now release their active concurrency slot immediately. This applies to recursion-depth refusal, lineage-concurrency refusal, TGL escalation, explicit veto, and budget-overrun escalation. + +### TGL boundary + +TGL evaluation is callable only from `EVALUATING`. Terminal TGL failure maps to control-plane escalation; the control plane does not reinterpret a terminal governance result as permission to continue recursion. + +### Merge-promotion barrier + +`MERGE_READY` now requires an actual successful sealed TGL result for the same lifecycle evaluation. A task cannot be promoted by state mutation alone, and starting a new evaluation clears stale TGL status/seal evidence. + +### Evidence-shape validation + +A TGL result must contain a valid 64-character seal before it can contribute to merge readiness. Invalid or missing sealed evidence fails closed. + +### Controller capability boundary + +Task identity, lifecycle state, TGL status/seal state, and concurrency state are controller-managed. Public access to tasks, ledgers, state registries, branch registries, and events is read-only. The configured TGL runner cannot be replaced through the public interface after construction. + +### Child-state transaction integrity + +Child creation checks duplicate task identity and repeated state before recording the post-submit `PREFLIGHT` snapshot. Failed duplicate creation therefore cannot pollute the state registry, and registry identity matches the actual lifecycle state observed. + +### Governance inheritance monotonicity + +Child authority scope, permitted tools, data classes, risk tier, resource budgets, metadata, and side-effect permissions can only remain equal or narrow. Parent provenance metadata is retained and cannot be overwritten by a child. + +### Branch evidence integrity + +Branch records are immutable after creation, including provenance collections and metadata. Multiple branches sharing a state ID are retained rather than silently collapsing to a single branch identity. Lineage traversal rejects cyclic parent relationships. + +### Terminal consumption barrier + +Escalated and terminated tasks cannot consume additional resources. + +### Safe abort path + +Active nonterminal lifecycle states can be explicitly terminated. This is a terminal abort path, not an authorization path, and does not bypass TGL or CommitGate requirements. + +### CI exact-head/reproducibility hardening + +The dedicated v1 control-plane workflow is configured to check out `${{ github.event.pull_request.head.sha || github.sha }}`, assert that the working tree SHA matches that exact value, install the pinned repository CI requirements plus a pinned pandas version, and execute the control-plane, TGL integration, adversarial, and capability-boundary suites. + +### Herald final-status reduction + +After Herald is appended, the TGL recomputes the monotonic final status over the complete gate set before sealing. A Herald `WARN` therefore cannot be hidden behind an earlier `PASS`, and a Herald `KILL` remains terminal. + +## Verification-only findings + +An earlier dedicated v1 contract execution exposed three contract-test failures on an earlier PR merge ref. The failures were fully diagnosed and corrected. A later exact-head run then exposed and corrected the side-effect narrowing test contract, and the substantive implementation checkpoint `7807d956…` passed the dedicated 40/40 suite before the integrated branch was reconciled with current `main`. + +The current branch contains a later TGL semantic correction for Herald status reduction, so fresh exact-head validation is required before claiming the final integrated head is fully verified. + +## External deployment boundary + +Current-main → production exact source binding remains separately open under Issue #137. A green or rate-limited Vercel status is not itself proof of exact deployment-source identity. + +## Experimental boundary + +No experimental execution or PDMAL state transition is permitted by this document. + +## Current experimental state + +PRE-FREEZE / FAIL-CLOSED / NOT AUTHORIZED / N=0 diff --git a/docs/governance/PR139_REVIEW_PACKET.md b/docs/governance/PR139_REVIEW_PACKET.md new file mode 100644 index 00000000..fe64813f --- /dev/null +++ b/docs/governance/PR139_REVIEW_PACKET.md @@ -0,0 +1,60 @@ +# PR #139 Review Packet + +## Review target + +`feat/dgaf-v1-control-plane-finalize-20260829` + +**Current head:** `d2c24054edfc44cbb2620e6b2b19eb8df8e23850` + +This packet is the reviewer-facing contract summary for the v1 governed control plane. It does not authorize experimental execution. + +## Review questions + +1. Does GovernanceEnvelope enforce downward-only authority, tools, data, risk, budget, metadata, and side-effect inheritance? +2. Does ControlPlane reject illegal lifecycle transitions and child creation from inactive parents? +3. Are maximum depth, node/round ceilings, and active concurrency enforced without resource leakage on escalation? +4. Is canonical state identity deterministic and suitable for repeated-state detection? +5. Are rejected, correlated, escalated, and vetoing branch records retained without collapsing distinct branch identities? +6. Does TGL remain the per-turn governance kernel, with valid sealed evidence required for merge readiness? +7. Can lifecycle state, TGL status/seal, runner configuration, ledgers, or registries be externally mutated to bypass governance? They must not. +8. Can any consequential action reach commit without explicit CommitGate authorization? It must not. +9. Are generic branch roles mapped to existing DGAF agents without changing normative authority? +10. Does any v1 mechanism alter PDMAL candidate identity, freeze, authorization, blinding, or empirical evidence? It must not. + +## Closed engineering controls + +- Task identity fields are immutable after construction. +- Lifecycle state and TGL runtime state are controller-managed. +- Public task/ledger/registry/event access is read-only. +- Merge readiness requires a current sealed PASS result. +- Terminal/escalated tasks cannot consume resources. +- Child creation observes the post-submit `PREFLIGHT` state and avoids failed-creation registry pollution. +- Child governance scope can only remain equal or narrow. +- Branch provenance preserves distinct branch identities when state IDs coincide. +- CommitGate remains a separate authorization barrier. +- Safe terminal abort does not create an authorization path. +- Herald WARN/KILL participates in the final monotonic status reduction before sealing. + +## Required evidence + +- exact PR head SHA; +- GitHub Actions run IDs and job logs for the v1 contract suites; +- test summary for control-plane, TGL integration, adversarial, and capability-boundary contracts; +- disposition for all observed failures; +- confirmation that Vercel/source binding remains a separate gate under #137. + +## Current verification state + +The substantive implementation checkpoint `7807d956…` passed the dedicated v1 suite **40/40**. The integrated branch subsequently incorporated current `main` through a non-destructive two-parent merge and received the Herald-status reduction correction. Fresh exact-head validation of the resulting code head remains required; documentation-only changes do not transfer or expand code verification claims. + +## Deployment boundary + +Issue #137 remains the canonical production/source-provenance gate. Vercel status success or rate limiting does not itself establish exact deployment-source identity. + +## Experimental boundary + +No freeze, pilot authorization, unblinding, or empirical execution is created or implied by PR #139. + +## Current experimental state + +PRE-FREEZE / FAIL-CLOSED / NOT AUTHORIZED / N=0 diff --git a/docs/governance/PR139_STATUS.md b/docs/governance/PR139_STATUS.md new file mode 100644 index 00000000..891ac959 --- /dev/null +++ b/docs/governance/PR139_STATUS.md @@ -0,0 +1,27 @@ +# PR #139 Status + +Governance summary for the DGAF v1 engineering lane. The authoritative current branch identity is the GitHub PR head. Embedded SHAs are execution references only. + +**Candidate branch:** `feat/dgaf-v1-control-plane-finalize-20260829` + +**Last exact-head engineering validation:** `235d4a951bc05d92e188a3e256cd683bc7e9b372` + +This document intentionally does not contain a mutable current-head field. Updating a current-head field changes the candidate SHA and creates recursive provenance churn. Consult PR #139 metadata for the authoritative current SHA. + +## Verification + +The substantive implementation checkpoint `a728ce3…` passed 41/41 dedicated v1 contract tests. The subsequent exact-head engineering wave on `235d4a95…` passed the substantive DGAF/PDMAL engineering, governance, security, pre-freeze, evidence, regression, and integrity workflows. Repository-wide generic Doc Lint remained separate legacy documentation debt. + +Later documentation/governance-only commits do not inherit execution claims from `235d4a95…`. + +## Deployment + +An exact-candidate Vercel preview for `235d4a95…` reached READY; `/api/health` returned HTTP 200 and the project runtime-error view reported no runtime errors in the selected 24-hour window. The deployment target was `null`, so it was preview/branch evidence, not production evidence. + +Production source identity remains a post-merge predicate tracked by Issue #137. + +## Boundary + +This PR is non-authorizing. It does not create a freeze, grant pilot authorization, unblind data, or change empirical N. + +**Experimental state:** PRE-FREEZE / FAIL-CLOSED / NOT AUTHORIZED / N=0 diff --git a/docs/governance/PUBLIC_SURFACE_QA_STANDARD.md b/docs/governance/PUBLIC_SURFACE_QA_STANDARD.md index 25c0e11c..70c40b02 100644 --- a/docs/governance/PUBLIC_SURFACE_QA_STANDARD.md +++ b/docs/governance/PUBLIC_SURFACE_QA_STANDARD.md @@ -4,8 +4,6 @@ This standard governs any DGAF artifact that is visible to a GitHub visitor, contributor, evaluator, recruiter, collaborator, customer, or other external reader. Public-facing repository material represents both the project and its maintainer; internal correctness alone is not sufficient for publication. -GitHub's repository guidance treats the README as a primary visitor entry point and recommends clear project purpose, usefulness, getting-started guidance, support paths, and maintainer/contributor information. DGAF applies that expectation as a publication-quality control, not merely as a documentation suggestion. - ## Publication principle > **A public artifact must be true, appropriately scoped, useful to its intended audience, professionally presented, correctly placed, safely disclosed, and maintainable.** @@ -14,20 +12,7 @@ An internal artifact does not become public-facing merely because it is accurate ## Public-surface lens -Before merging a GitHub-visible change, review it through all of these lenses: - -1. **Truth** — Are factual, technical, mathematical, and status claims supported by the appropriate evidence? -2. **Authority** — Is the cited artifact actually authoritative for the claim being made? -3. **Audience** — Is the material written for the people who will encounter it? -4. **Utility** — Does it help a visitor understand, evaluate, use, reproduce, contribute to, or appropriately interpret the project? -5. **Placement** — Is it located where a reasonable GitHub user would expect to find it? -6. **Navigation** — Do links lead to stable, intentional, audience-appropriate destinations? -7. **Professional representation** — Does the surface represent the maintainer's work at the expected engineering/open-source quality bar? -8. **Disclosure** — Does it avoid unnecessary personal information, private workspace material, credentials, internal deliberation, operational clutter, or unfinished work? -9. **Community fit** — Is it consistent with normal open-source expectations for clarity, accessibility, contribution, attribution, licensing, and respectful project maintenance? -10. **Maintenance** — Can the information and its destinations remain coherent as the repository evolves? -11. **Identity integrity** — Does the artifact accurately represent the project and the maintainer rather than overstating capability, validation, status, or maturity? -12. **Friction** — Does it reduce the reader's next-step uncertainty rather than forcing them through internal process or irrelevant detail? +Before merging a GitHub-visible change, review truth, authority, audience, utility, placement, navigation, professional representation, disclosure, community fit, maintenance, identity integrity, and reader friction. ## Internal versus public authority @@ -35,39 +20,37 @@ DGAF distinguishes internal operational authority from public project navigation - Personal Notion pages, private working records, internal control notes, and temporary coordination artifacts are **not public navigation targets by default**. - A GitHub landing page should preferentially resolve to repository-local documentation, stable public project resources, or an intentionally designated public project surface. -- An internal control record may inform public documentation without being exposed as the public destination. -- If an external service is linked, the destination must be intentionally designated for public consumption and must not expose private workspace context merely because the internal team uses it. +- Internal control records may inform public documentation without becoming public destinations. + +## Current DGAF/PDMAL public boundary — 2026-08-29 + +- **PR #139** is the canonical combined engineering candidate for DGAF v1 control-plane and current TGL contract remediation. +- PR #132/#133 are historical diagnostic/remediation records. +- PR #134 is superseded by PR #139 and is not a separate current engineering authority. +- PDMAL remains **PRE-FREEZE / FAIL-CLOSED / NOT AUTHORIZED / N=0**. +- The experimental verification boundary remains candidate-scoped at `ac8ea267a9f0d995626cf9c3eaf9e6b008b5dc8a`. +- The observed READY Vercel production deployment sourced from `42346ecc34565502ebff02ead55a33b0d74246b8` is not exact-current-main evidence. ## Evidence and presentation boundary -Public documentation must preserve DGAF's epistemic distinctions. In particular: +Public documentation must preserve the distinction between `defined`, `implemented`, `computed`, `verified`, `attested`, `historical`, `authorized`, and `canonical`. Engineering CI success, synthetic fixtures, or deployment readiness must not be presented as PDMAL efficacy or experimental authorization. -`defined → implemented → computed → verified → attested → historical` +## Canonical agent-role presentation -must not collapse into a generic claim of "validated" or "production-ready." +Sentinel-Phi is the canonical governance/security identity; Professor Prodigy is non-orchestrating; DemiJoule is advisory; Reciprocity performs affected-party/fairness review; Herald publishes/classifies evidence and cannot manufacture evidence or approval; Amethyst coordinates meta-orchestration; COLLEEN maintains continuity/provenance; Apogee supports independent evidence review. -A mathematical correction can establish a mathematical result without establishing a system-level claim. A passing component test can establish the tested component result without establishing repository-wide validation. A deployment can establish deployment state without establishing experimental authorization or efficacy. +Generic execution roles do not create or elevate agent authority. ## Historical material -Incorrect or superseded values should normally be **retired, classified, superseded, and prevented from downstream use**, not silently erased when their historical presence is relevant to provenance. Historical material must be visibly scoped so a normal visitor cannot mistake it for current project truth. +Superseded claims and identifiers should be retired or clearly classified as historical rather than silently reinterpreted as current truth. Historical SHA/run/deployment evidence remains scoped to the exact artifact and execution that produced it. ## Required pre-merge review -For every externally visible documentation or navigation change, answer: - -- What will a first-time visitor believe after reading this? -- Is that belief exactly supported by the evidence? -- Is this the right information for this surface? -- Is the destination public, stable, and intentionally maintained? -- Does anything internal or personal become visible unnecessarily? -- Does the change improve comprehension and next-step usability? -- Does it remain coherent with the current README, project status, evidence index, governance records, and terminology? - -If any answer is materially uncertain, the change should remain internal or be revised before publication. +Before a public documentation change is merged, confirm that a first-time reader would infer only what the evidence supports, that destinations are public and intentional, and that README/project-status/evidence/governance records remain mutually coherent. ## Relationship to DGAF governance -This standard is a **publication-surface control**. It does not grant experimental authorization, create a freeze, upgrade evidence, or change empirical N. It operates as a lens over changes that represent DGAF externally. +This standard is a publication-surface control. It does not grant experimental authorization, create a freeze, upgrade evidence, or change empirical N. -Current experimental state remains independently governed by the authoritative gate/evidence records. +**Current experimental state: PRE-FREEZE / FAIL-CLOSED / NOT AUTHORIZED / N=0.** diff --git a/pptl/__init__.py b/pptl/__init__.py index 7bba2aca..4acb3bf4 100644 --- a/pptl/__init__.py +++ b/pptl/__init__.py @@ -1,34 +1,24 @@ -""" -PPTL — Phi-Pentagon Topology Lab -Multi-agent governance harness: HeraldAgent, TriadC orchestration, -DemiJoule RAG verification, DGAF gate stack. - -DGAF-Framework governed · Agent Amethyst meta-orchestrated -""" -from .herald_agent import HeraldAgent, TraceEventType -from .sinks import JSONLSink, StdoutSink, N8nWebhookSink -from .n8n_herald_sink import N8nHeraldSink # OPP-005: production sink -from .rag_verifier import SentinelRAGVerifier -from .topology import PHI, PENTAGON_EDGES -from .attestation_gate import ( - AttestationGate, AttestationRecord, AttestationResult, AttestationStatus, -) -from .co_orchestration_schema import ( - CoOrchQueue, Opportunity, AlignmentGate, - load_queue, save_queue, -) +"""PPTL — Phi-Pentagon Topology Lab and DGAF governance harness.""" +from .herald_agent import HeraldAgent, TraceEventType +from .sinks import JSONLSink, StdoutSink, N8nWebhookSink +from .n8n_herald_sink import N8nHeraldSink +from .rag_verifier import SentinelRAGVerifier +from .topology import PHI, PENTAGON_EDGES +from .attestation_gate import AttestationGate, AttestationRecord, AttestationResult, AttestationStatus +from .co_orchestration_schema import CoOrchQueue, Opportunity, AlignmentGate, load_queue, save_queue +from .governance_envelope import GovernanceEnvelope, ResourceBudget +from .state_identity import StateRegistry, canonical_state, state_id +from .budget_ledger import BudgetLedger, Consumption, BudgetExceeded +from .branch_registry import BranchRecord, BranchRegistry +from .commit_gate import CommitGate, CommitDenied, CommitRequest +from .control_plane import ControlPlane, ControlPlaneViolation, ControlTask, TaskState __version__ = "0.5.0" __all__ = [ - # Herald - "HeraldAgent", "TraceEventType", - # Sinks - "JSONLSink", "StdoutSink", "N8nWebhookSink", "N8nHeraldSink", - # Governance - "SentinelRAGVerifier", - "AttestationGate", "AttestationRecord", "AttestationResult", "AttestationStatus", - # Topology - "PHI", "PENTAGON_EDGES", - # Co-orchestration - "CoOrchQueue", "Opportunity", "AlignmentGate", "load_queue", "save_queue", + "HeraldAgent", "TraceEventType", "JSONLSink", "StdoutSink", "N8nWebhookSink", "N8nHeraldSink", + "SentinelRAGVerifier", "AttestationGate", "AttestationRecord", "AttestationResult", "AttestationStatus", + "PHI", "PENTAGON_EDGES", "CoOrchQueue", "Opportunity", "AlignmentGate", "load_queue", "save_queue", + "GovernanceEnvelope", "ResourceBudget", "StateRegistry", "canonical_state", "state_id", + "BudgetLedger", "Consumption", "BudgetExceeded", "BranchRecord", "BranchRegistry", + "CommitGate", "CommitDenied", "CommitRequest", "ControlPlane", "ControlPlaneViolation", "ControlTask", "TaskState", ] diff --git a/pptl/branch_registry.py b/pptl/branch_registry.py new file mode 100644 index 00000000..803206b9 --- /dev/null +++ b/pptl/branch_registry.py @@ -0,0 +1,93 @@ +"""Append-oriented branch lineage and evidence registry.""" +from __future__ import annotations + +from dataclasses import dataclass +from types import MappingProxyType +from typing import Iterable, Mapping + + +def _freeze_strings(values: Iterable[str]) -> tuple[str, ...]: + return tuple(str(value) for value in values) + + +@dataclass(frozen=True) +class BranchRecord: + branch_id: str + parent_branch_id: str | None + role: str + state_id: str + claims: tuple[str, ...] = () + evidence_ids: tuple[str, ...] = () + assumptions: tuple[str, ...] = () + uncertainty: float | None = None + source_overlap: float | None = None + dependency_overlap: float | None = None + policy_verdict: str = "PASS" + merge_status: str = "accepted" + terminal: bool = False + metadata: Mapping[str, str] = None + + def __post_init__(self) -> None: + if not self.branch_id or not self.role or not self.state_id: + raise ValueError("branch_id, role, and state_id are required") + for name in ("uncertainty", "source_overlap", "dependency_overlap"): + value = getattr(self, name) + if value is not None and not 0.0 <= value <= 1.0: + raise ValueError(f"{name} must be between 0 and 1") + if self.policy_verdict not in {"PASS", "WARN", "KILL", "ESCALATE"}: + raise ValueError("invalid policy_verdict") + object.__setattr__(self, "claims", _freeze_strings(self.claims)) + object.__setattr__(self, "evidence_ids", _freeze_strings(self.evidence_ids)) + object.__setattr__(self, "assumptions", _freeze_strings(self.assumptions)) + object.__setattr__(self, "metadata", MappingProxyType(dict(self.metadata or {}))) + + +class BranchRegistry: + def __init__(self) -> None: + self._branches: list[BranchRecord] = [] + self._states: dict[str, set[str]] = {} + + @property + def count(self) -> int: + return len(self._branches) + + def add(self, record: BranchRecord) -> None: + if any(b.branch_id == record.branch_id for b in self._branches): + raise ValueError(f"duplicate branch_id: {record.branch_id}") + self._branches.append(record) + self._states.setdefault(record.state_id, set()).add(record.branch_id) + + def get(self, branch_id: str) -> BranchRecord: + for branch in self._branches: + if branch.branch_id == branch_id: + return branch + raise KeyError(branch_id) + + def all(self) -> tuple[BranchRecord, ...]: + return tuple(self._branches) + + def by_status(self, merge_status: str) -> tuple[BranchRecord, ...]: + return tuple(b for b in self._branches if b.merge_status == merge_status) + + def by_state(self, state_id: str) -> tuple[BranchRecord, ...]: + """Return every branch recorded for a state without collapsing branch identity.""" + branch_ids = self._states.get(state_id, set()) + return tuple(b for b in self._branches if b.branch_id in branch_ids) + + def lineage(self, branch_id: str) -> tuple[BranchRecord, ...]: + chain: list[BranchRecord] = [] + current = self.get(branch_id) + visited: set[str] = set() + while True: + if current.branch_id in visited: + raise ValueError("branch lineage cycle detected") + visited.add(current.branch_id) + chain.append(current) + if current.parent_branch_id is None: + break + current = self.get(current.parent_branch_id) + chain.reverse() + return tuple(chain) + + def ids(self) -> Iterable[str]: + return tuple(b.branch_id for b in self._branches) diff --git a/pptl/budget_ledger.py b/pptl/budget_ledger.py new file mode 100644 index 00000000..b5a441e2 --- /dev/null +++ b/pptl/budget_ledger.py @@ -0,0 +1,97 @@ +"""Deterministic resource and active-concurrency ledger for v1.""" +from __future__ import annotations + +from dataclasses import dataclass + +from .governance_envelope import ResourceBudget + + +@dataclass(frozen=True) +class Consumption: + input_tokens: int = 0 + output_tokens: int = 0 + tool_calls: int = 0 + elapsed_ms: int = 0 + rounds: int = 0 + nodes: int = 0 + + def __post_init__(self) -> None: + for name in self.__dataclass_fields__: + value = getattr(self, name) + if not isinstance(value, int) or value < 0: + raise ValueError(f"{name} must be a non-negative integer") + + +class BudgetExceeded(RuntimeError): + pass + + +class BudgetLedger: + def __init__(self, budget: ResourceBudget) -> None: + self.budget = budget + self.consumed = Consumption() + self.reserved = Consumption() + self.active_concurrency = 0 + + @staticmethod + def _add(a: Consumption, b: Consumption) -> Consumption: + return Consumption(*(getattr(a, f) + getattr(b, f) for f in Consumption.__dataclass_fields__)) + + @staticmethod + def _fits(budget: ResourceBudget, value: Consumption) -> bool: + limits = { + "input_tokens": budget.max_input_tokens, + "output_tokens": budget.max_output_tokens, + "tool_calls": budget.max_tool_calls, + "elapsed_ms": budget.max_elapsed_ms, + "rounds": budget.max_rounds, + "nodes": budget.max_nodes, + } + return all(getattr(value, field) <= limit for field, limit in limits.items()) + + def _committed_or_reserved(self) -> Consumption: + return self._add(self.consumed, self.reserved) + + def remaining(self) -> Consumption: + used = self._committed_or_reserved() + limits = { + "input_tokens": self.budget.max_input_tokens, + "output_tokens": self.budget.max_output_tokens, + "tool_calls": self.budget.max_tool_calls, + "elapsed_ms": self.budget.max_elapsed_ms, + "rounds": self.budget.max_rounds, + "nodes": self.budget.max_nodes, + } + return Consumption(**{k: max(0, v - getattr(used, k)) for k, v in limits.items()}) + + def acquire_concurrency(self, slots: int = 1) -> None: + if not isinstance(slots, int) or slots < 1: + raise ValueError("slots must be a positive integer") + if self.active_concurrency + slots > self.budget.max_concurrency: + raise BudgetExceeded("active concurrency exceeds budget") + self.active_concurrency += slots + + def release_concurrency(self, slots: int = 1) -> None: + if not isinstance(slots, int) or slots < 1: + raise ValueError("slots must be a positive integer") + if slots > self.active_concurrency: + raise ValueError("cannot release more active concurrency than acquired") + self.active_concurrency -= slots + + def reserve(self, amount: Consumption) -> None: + candidate = self._add(self._committed_or_reserved(), amount) + if not self._fits(self.budget, candidate): + raise BudgetExceeded("resource reservation exceeds budget") + self.reserved = self._add(self.reserved, amount) + + def release(self, amount: Consumption) -> None: + values = {f: getattr(self.reserved, f) - getattr(amount, f) for f in Consumption.__dataclass_fields__} + if any(v < 0 for v in values.values()): + raise ValueError("cannot release more than reserved") + self.reserved = Consumption(**values) + + def consume(self, amount: Consumption) -> None: + candidate = self._add(self._committed_or_reserved(), amount) + if not self._fits(self.budget, candidate): + raise BudgetExceeded("resource consumption exceeds budget") + self.consumed = self._add(self.consumed, amount) diff --git a/pptl/commit_gate.py b/pptl/commit_gate.py new file mode 100644 index 00000000..04c998de --- /dev/null +++ b/pptl/commit_gate.py @@ -0,0 +1,62 @@ +"""Explicit proposal/authorization/commit barrier for consequential actions.""" +from __future__ import annotations + +from dataclasses import dataclass +from types import MappingProxyType +from typing import Mapping + + +@dataclass(frozen=True) +class CommitRequest: + request_id: str + trace_id: str + action: str + target: str + parameters: Mapping[str, str] + + def __post_init__(self) -> None: + object.__setattr__(self, "parameters", MappingProxyType(dict(self.parameters))) + + +class CommitDenied(PermissionError): + pass + + +class CommitGate: + def __init__(self) -> None: + self._authorized: dict[str, str] = {} + self._proposals: dict[str, CommitRequest] = {} + self._committed: set[str] = set() + + @property + def proposals(self) -> tuple[CommitRequest, ...]: + return tuple(self._proposals.values()) + + def propose(self, request: CommitRequest) -> CommitRequest: + if not request.request_id or not request.trace_id or not request.action or not request.target: + raise ValueError("commit request identity and action fields are required") + if request.request_id in self._proposals: + raise ValueError(f"duplicate commit request_id: {request.request_id}") + self._proposals[request.request_id] = request + return request + + def authorize(self, request_id: str, authorized_by: str, authorization_ref: str) -> None: + if not authorized_by or not authorization_ref: + raise ValueError("explicit authorization identity and reference are required") + if request_id not in self._proposals: + raise KeyError(request_id) + if request_id in self._authorized: + raise CommitDenied(f"commit request already authorized: {request_id}") + if request_id in self._committed: + raise CommitDenied(f"commit request already committed: {request_id}") + self._authorized[request_id] = f"{authorized_by}:{authorization_ref}" + + def commit(self, request_id: str) -> str: + if request_id not in self._authorized: + raise CommitDenied("commit requires explicit authorization") + if request_id not in self._proposals: + raise KeyError(request_id) + if request_id in self._committed: + raise CommitDenied(f"commit request already committed: {request_id}") + self._committed.add(request_id) + return self._authorized[request_id] diff --git a/pptl/control_plane.py b/pptl/control_plane.py new file mode 100644 index 00000000..2bdcc093 --- /dev/null +++ b/pptl/control_plane.py @@ -0,0 +1,355 @@ +"""Deterministic DGAF v1 task/branch lifecycle controller.""" +from __future__ import annotations + +from dataclasses import dataclass, field +from enum import Enum +from types import MappingProxyType +from typing import Any, Callable, Mapping + +from .branch_registry import BranchRecord, BranchRegistry +from .budget_ledger import BudgetExceeded, Consumption, BudgetLedger +from .governance_envelope import GovernanceEnvelope, ResourceBudget +from .state_identity import StateRegistry + + +class TaskState(str, Enum): + RECEIVED = "RECEIVED" + PREFLIGHT = "PREFLIGHT" + ADMITTED = "ADMITTED" + EXPANDING = "EXPANDING" + EVALUATING = "EVALUATING" + MERGE_READY = "MERGE_READY" + COMMIT_READY = "COMMIT_READY" + ESCALATED = "ESCALATED" + TERMINATED = "TERMINATED" + + +_ALLOWED = { + TaskState.RECEIVED: {TaskState.PREFLIGHT, TaskState.TERMINATED}, + TaskState.PREFLIGHT: {TaskState.ADMITTED, TaskState.ESCALATED, TaskState.TERMINATED}, + TaskState.ADMITTED: {TaskState.EXPANDING, TaskState.EVALUATING, TaskState.ESCALATED, TaskState.TERMINATED}, + TaskState.EXPANDING: {TaskState.EVALUATING, TaskState.ESCALATED, TaskState.TERMINATED}, + TaskState.EVALUATING: {TaskState.EXPANDING, TaskState.MERGE_READY, TaskState.ESCALATED, TaskState.TERMINATED}, + TaskState.MERGE_READY: {TaskState.COMMIT_READY, TaskState.ESCALATED, TaskState.TERMINATED}, + TaskState.COMMIT_READY: {TaskState.TERMINATED, TaskState.ESCALATED}, + TaskState.ESCALATED: {TaskState.TERMINATED}, + TaskState.TERMINATED: set(), +} + + +class ControlPlaneViolation(RuntimeError): + pass + + +@dataclass(frozen=True) +class LedgerView: + """Read-only snapshot of a task ledger.""" + budget: ResourceBudget + consumed: Consumption + reserved: Consumption + active_concurrency: int + + +class StateRegistryView: + def __init__(self, registry: StateRegistry) -> None: + self._registry = registry + + @property + def count(self) -> int: + return self._registry.count + + def contains(self, state: dict[str, Any]) -> bool: + return self._registry.contains(state) + + def ids(self) -> tuple[str, ...]: + return tuple(self._registry.ids()) + + +class BranchRegistryView: + def __init__(self, registry: BranchRegistry) -> None: + self._registry = registry + + @property + def count(self) -> int: + return self._registry.count + + def all(self) -> tuple[BranchRecord, ...]: + return self._registry.all() + + def by_status(self, merge_status: str) -> tuple[BranchRecord, ...]: + return self._registry.by_status(merge_status) + + def by_state(self, state_id: str) -> tuple[BranchRecord, ...]: + return self._registry.by_state(state_id) + + def lineage(self, branch_id: str) -> tuple[BranchRecord, ...]: + return self._registry.lineage(branch_id) + + def ids(self) -> tuple[str, ...]: + return tuple(self._registry.ids()) + + +@dataclass +class ControlTask: + task_id: str + envelope: GovernanceEnvelope + depth: int = 0 + lineage_id: str | None = None + _state: TaskState = field(default=TaskState.RECEIVED, init=False, repr=False) + _state_history: list[str] = field(default_factory=list, init=False, repr=False) + _concurrency_acquired: bool = field(default=False, init=False, repr=False) + _last_tgl_status: str | None = field(default=None, init=False, repr=False) + _last_tgl_seal: str | None = field(default=None, init=False, repr=False) + _identity_sealed: bool = field(default=False, init=False, repr=False) + + _IMMUTABLE_FIELDS = frozenset({"task_id", "envelope", "depth", "lineage_id"}) + + def __post_init__(self) -> None: + if self.lineage_id is None: + object.__setattr__(self, "lineage_id", self.envelope.trace_id) + if self.depth < 0: + raise ValueError("depth must be non-negative") + object.__setattr__(self, "_identity_sealed", True) + + def __setattr__(self, name: str, value: object) -> None: + if getattr(self, "_identity_sealed", False) and name in self._IMMUTABLE_FIELDS: + current = getattr(self, name) + if value != current: + raise ControlPlaneViolation(f"immutable task identity field: {name}") + if name in {"state", "state_history", "concurrency_acquired", "last_tgl_status", "last_tgl_seal"}: + raise AttributeError(f"{name} is controller-managed") + object.__setattr__(self, name, value) + + @property + def state(self) -> TaskState: + return self._state + + @property + def state_history(self) -> tuple[str, ...]: + return tuple(self._state_history) + + @property + def concurrency_acquired(self) -> bool: + return self._concurrency_acquired + + @property + def last_tgl_status(self) -> str | None: + return self._last_tgl_status + + @property + def last_tgl_seal(self) -> str | None: + return self._last_tgl_seal + + def snapshot(self) -> dict[str, object]: + return { + "task_id": self.task_id, + "state": self.state.value, + "depth": self.depth, + "envelope_trace": self.envelope.trace_id, + "parent_trace": self.envelope.parent_trace_id, + } + + +class ControlPlane: + """Single-run deterministic controller; external actions remain prohibited by default.""" + + def __init__(self, *, tgl_runner: Callable[..., Any] | None = None) -> None: + self._tgl_runner = tgl_runner + self._state_registry = StateRegistry() + self._branches = BranchRegistry() + self._tasks: dict[str, ControlTask] = {} + self._ledgers: dict[str, BudgetLedger] = {} + self._events: list[dict[str, object]] = [] + self._lineage_active: dict[str, int] = {} + self._lineage_limits: dict[str, int] = {} + + @property + def tgl_runner(self) -> Callable[..., Any] | None: + return self._tgl_runner + + @property + def tasks(self) -> Mapping[str, ControlTask]: + return MappingProxyType(self._tasks) + + @property + def ledgers(self) -> Mapping[str, LedgerView]: + return MappingProxyType({ + task_id: LedgerView( + budget=ledger.budget, + consumed=ledger.consumed, + reserved=ledger.reserved, + active_concurrency=ledger.active_concurrency, + ) + for task_id, ledger in self._ledgers.items() + }) + + @property + def events(self) -> tuple[dict[str, object], ...]: + return tuple(dict(event) for event in self._events) + + @property + def state_registry(self) -> StateRegistryView: + return StateRegistryView(self._state_registry) + + @property + def branches(self) -> BranchRegistryView: + return BranchRegistryView(self._branches) + + def submit(self, task: ControlTask) -> None: + if task.task_id in self._tasks: + raise ControlPlaneViolation(f"duplicate task_id: {task.task_id}") + self._lineage_limits.setdefault(task.lineage_id, task.envelope.budget.max_concurrency) + self._tasks[task.task_id] = task + self._ledgers[task.task_id] = BudgetLedger(task.envelope.budget) + self._transition(task, TaskState.PREFLIGHT) + + def admit(self, task_id: str) -> None: + self._transition(self._task(task_id), TaskState.ADMITTED) + + def _set_runtime(self, task: ControlTask, *, state: TaskState | None = None, concurrency: bool | None = None, tgl_status: str | None = None, tgl_seal: str | None = None, reset_tgl: bool = False) -> None: + if state is not None: + object.__setattr__(task, "_state", state) + if concurrency is not None: + object.__setattr__(task, "_concurrency_acquired", concurrency) + if reset_tgl: + object.__setattr__(task, "_last_tgl_status", None) + object.__setattr__(task, "_last_tgl_seal", None) + if tgl_status is not None: + object.__setattr__(task, "_last_tgl_status", tgl_status) + if tgl_seal is not None: + object.__setattr__(task, "_last_tgl_seal", tgl_seal) + + def _release_concurrency(self, task: ControlTask) -> None: + if not task.concurrency_acquired: + return + self._ledgers[task.task_id].release_concurrency() + lineage = task.lineage_id or task.envelope.trace_id + self._lineage_active[lineage] = max(0, self._lineage_active.get(lineage, 0) - 1) + self._set_runtime(task, concurrency=False) + + def _escalate(self, task: ControlTask, reason: str) -> None: + if task.state is not TaskState.ESCALATED: + self._transition(task, TaskState.ESCALATED) + self._events.append({"event": "ESCALATION", "task_id": task.task_id, "reason": reason}) + self._release_concurrency(task) + + def start_expansion(self, task_id: str) -> None: + task = self._task(task_id) + if TaskState.EXPANDING not in _ALLOWED[task.state]: + raise ControlPlaneViolation(f"illegal transition {task.state.value} -> {TaskState.EXPANDING.value}") + lineage = task.lineage_id or task.envelope.trace_id + if task.depth >= task.envelope.budget.max_depth: + self._escalate(task, "maximum recursion depth reached") + return + if self._lineage_active.get(lineage, 0) >= self._lineage_limits[lineage]: + self._escalate(task, "active concurrency limit reached") + return + try: + self._ledgers[task_id].acquire_concurrency() + self._ledgers[task_id].consume(Consumption(rounds=1, nodes=1)) + except BudgetExceeded as exc: + if self._ledgers[task_id].active_concurrency: + self._ledgers[task_id].release_concurrency() + self._events.append({"event": "BUDGET_EXCEEDED", "task_id": task_id, "reason": str(exc)}) + self._escalate(task, str(exc)) + return + self._lineage_active[lineage] = self._lineage_active.get(lineage, 0) + 1 + self._set_runtime(task, concurrency=True) + self._transition(task, TaskState.EXPANDING) + + def begin_evaluation(self, task_id: str) -> None: + task = self._task(task_id) + self._transition(task, TaskState.EVALUATING) + self._set_runtime(task, reset_tgl=True) + + def evaluate_turn(self, task_id: str, input_text: str, context: dict[str, Any] | None = None) -> Any: + if self._tgl_runner is None: + raise ControlPlaneViolation("no TGL runner configured") + task = self._task(task_id) + if task.state is not TaskState.EVALUATING: + raise ControlPlaneViolation("TGL evaluation requires EVALUATING state") + try: + result = self._tgl_runner(input_text, context or {}) + except Exception as exc: + self._events.append({"event": "TGL_RUNNER_FAILURE", "task_id": task_id, "reason": str(exc)}) + self._escalate(task, "TGL runner exception") + raise ControlPlaneViolation("TGL runner failed; task escalated") from exc + status = getattr(getattr(result, "final_status", None), "value", getattr(result, "final_status", None)) + seal = getattr(result, "seal_hash", None) + if status is None or not isinstance(seal, str) or len(seal) != 64: + self._set_runtime(task, reset_tgl=True) + self._escalate(task, "TGL result lacks a valid cryptographic seal") + raise ControlPlaneViolation("TGL result lacks valid sealed evidence") + self._set_runtime(task, tgl_status=status, tgl_seal=seal) + self._events.append({"event": "TGL_EVALUATED", "task_id": task_id, "status": status, "seal_hash": seal}) + if status in {"KILL", "KILL_REC"}: + self.veto(task_id, "TGL terminal failure") + elif status == "ESCALATE": + self._escalate(task, "TGL escalation") + return result + + def mark_merge_ready(self, task_id: str) -> None: + task = self._task(task_id) + if task.state is not TaskState.EVALUATING: + raise ControlPlaneViolation("merge readiness requires EVALUATING state") + if task.last_tgl_status != "PASS" or not task.last_tgl_seal: + raise ControlPlaneViolation("merge readiness requires successful sealed TGL evaluation") + self._transition(task, TaskState.MERGE_READY) + + def mark_commit_ready(self, task_id: str) -> None: + task = self._task(task_id) + if task.envelope.side_effect_mode != "COMMIT_ALLOWED": + raise ControlPlaneViolation("task envelope does not permit commit") + self._transition(task, TaskState.COMMIT_READY) + + def veto(self, task_id: str, reason: str) -> None: + task = self._task(task_id) + self._events.append({"event": "VETO", "task_id": task_id, "reason": reason}) + self._escalate(task, reason) + + def terminate(self, task_id: str) -> None: + task = self._task(task_id) + self._transition(task, TaskState.TERMINATED) + self._release_concurrency(task) + + def create_child(self, parent_id: str, *, task_id: str, trace_id: str, authority_scope: set[str], permitted_tools: set[str], data_classes: set[str], envelope_budget: ResourceBudget, side_effect_mode: str | None = None) -> ControlTask: + parent = self._task(parent_id) + if parent.state not in {TaskState.ADMITTED, TaskState.EXPANDING, TaskState.EVALUATING}: + raise ControlPlaneViolation("child creation requires an active parent task") + if parent.depth + 1 > parent.envelope.budget.max_depth: + raise ControlPlaneViolation("child exceeds maximum recursion depth") + child = ControlTask(task_id=task_id, depth=parent.depth + 1, lineage_id=parent.lineage_id, envelope=parent.envelope.derive_child(trace_id=trace_id, task_id=task_id, authority_scope=authority_scope, permitted_tools=permitted_tools, data_classes=data_classes, budget=envelope_budget, side_effect_mode=side_effect_mode)) + candidate_snapshot = child.snapshot() + if self._state_registry.contains(candidate_snapshot): + raise ControlPlaneViolation("repeated orchestration state") + self.submit(child) + self._state_registry.observe(child.snapshot()) + return child + + def register_branch(self, branch: BranchRecord) -> None: + self._branches.add(branch) + self._events.append({"event": "BRANCH_RECORDED", "branch_id": branch.branch_id, "policy_verdict": branch.policy_verdict, "merge_status": branch.merge_status}) + + def consume(self, task_id: str, amount: Consumption) -> None: + task = self._task(task_id) + if task.state in {TaskState.ESCALATED, TaskState.TERMINATED}: + raise ControlPlaneViolation("terminal task cannot consume additional resources") + try: + self._ledgers[task_id].consume(amount) + except BudgetExceeded as exc: + self._events.append({"event": "BUDGET_EXCEEDED", "task_id": task_id, "reason": str(exc)}) + self._escalate(task, str(exc)) + raise + + def _transition(self, task: ControlTask, new_state: TaskState) -> None: + if new_state not in _ALLOWED[task.state]: + raise ControlPlaneViolation(f"illegal transition {task.state.value} -> {new_state.value}") + task._state_history.append(task.state.value) + self._set_runtime(task, state=new_state) + self._events.append({"event": "STATE", "task_id": task.task_id, "state": new_state.value}) + + def _task(self, task_id: str) -> ControlTask: + try: + return self._tasks[task_id] + except KeyError as exc: + raise KeyError(task_id) from exc diff --git a/pptl/governance_envelope.py b/pptl/governance_envelope.py new file mode 100644 index 00000000..15939a58 --- /dev/null +++ b/pptl/governance_envelope.py @@ -0,0 +1,118 @@ +"""Immutable governance scope inherited by DGAF recursive work items.""" +from __future__ import annotations +from dataclasses import dataclass, field +from types import MappingProxyType +from typing import Iterable, Mapping + + +def _freeze(items: Iterable[str]) -> frozenset[str]: + return frozenset(str(item) for item in items) + + +@dataclass(frozen=True) +class ResourceBudget: + max_input_tokens: int = 0 + max_output_tokens: int = 0 + max_tool_calls: int = 0 + max_elapsed_ms: int = 0 + max_rounds: int = 0 + max_nodes: int = 0 + max_depth: int = 0 + max_concurrency: int = 1 + + def __post_init__(self) -> None: + for name in self.__dataclass_fields__: + value = getattr(self, name) + if not isinstance(value, int) or value < 0: + raise ValueError(f"{name} must be a non-negative integer") + if self.max_concurrency < 1: + raise ValueError("max_concurrency must be at least 1") + + def child_allowed(self, child: "ResourceBudget") -> bool: + return all(getattr(child, name) <= getattr(self, name) for name in self.__dataclass_fields__) + + +@dataclass(frozen=True) +class GovernanceEnvelope: + trace_id: str + task_id: str + authority_scope: frozenset[str] = field(default_factory=frozenset) + permitted_tools: frozenset[str] = field(default_factory=frozenset) + data_classes: frozenset[str] = field(default_factory=frozenset) + prohibited_actions: frozenset[str] = field(default_factory=frozenset) + risk_tier: str = "low" + budget: ResourceBudget = field(default_factory=ResourceBudget) + policy_version: str = "dgaf-v1" + side_effect_mode: str = "PROPOSE_ONLY" + parent_trace_id: str | None = None + metadata: Mapping[str, str] = field(default_factory=dict) + + def __post_init__(self) -> None: + for field_name in ("authority_scope", "permitted_tools", "data_classes", "prohibited_actions"): + object.__setattr__(self, field_name, _freeze(getattr(self, field_name))) + object.__setattr__(self, "metadata", MappingProxyType(dict(self.metadata))) + if not self.trace_id or not self.task_id: + raise ValueError("trace_id and task_id are required") + if self.risk_tier not in {"low", "medium", "high", "critical"}: + raise ValueError("invalid risk_tier") + if self.side_effect_mode not in {"PROPOSE_ONLY", "COMMIT_ALLOWED"}: + raise ValueError("invalid side_effect_mode") + + def derive_child( + self, + *, + trace_id: str, + task_id: str, + authority_scope: Iterable[str], + permitted_tools: Iterable[str], + data_classes: Iterable[str], + budget: ResourceBudget, + risk_tier: str | None = None, + side_effect_mode: str | None = None, + metadata: Mapping[str, str] | None = None, + ) -> "GovernanceEnvelope": + child_authority = _freeze(authority_scope) + child_tools = _freeze(permitted_tools) + child_data = _freeze(data_classes) + if not child_authority <= self.authority_scope: + raise PermissionError("child authority exceeds parent scope") + if not child_tools <= self.permitted_tools: + raise PermissionError("child tool scope exceeds parent scope") + if not child_data <= self.data_classes: + raise PermissionError("child data scope exceeds parent scope") + if not self.budget.child_allowed(budget): + raise PermissionError("child budget exceeds parent budget") + child_risk = risk_tier or self.risk_tier + rank = {"low": 0, "medium": 1, "high": 2, "critical": 3} + if child_risk not in rank: + raise ValueError("invalid risk_tier") + if rank[child_risk] > rank[self.risk_tier]: + raise PermissionError("child risk tier cannot increase") + + child_side_effect_mode = side_effect_mode or self.side_effect_mode + side_effect_rank = {"PROPOSE_ONLY": 0, "COMMIT_ALLOWED": 1} + if child_side_effect_mode not in side_effect_rank: + raise ValueError("invalid side_effect_mode") + if side_effect_rank[child_side_effect_mode] > side_effect_rank[self.side_effect_mode]: + raise PermissionError("child side-effect authority cannot increase") + + child_metadata = dict(self.metadata) + for key, value in dict(metadata or {}).items(): + if key in child_metadata and child_metadata[key] != value: + raise PermissionError(f"child metadata cannot overwrite parent key: {key}") + child_metadata[key] = value + + return GovernanceEnvelope( + trace_id=trace_id, + task_id=task_id, + authority_scope=child_authority, + permitted_tools=child_tools, + data_classes=child_data, + prohibited_actions=self.prohibited_actions, + risk_tier=child_risk, + budget=budget, + policy_version=self.policy_version, + side_effect_mode=child_side_effect_mode, + parent_trace_id=self.trace_id, + metadata=child_metadata, + ) \ No newline at end of file diff --git a/pptl/state_identity.py b/pptl/state_identity.py new file mode 100644 index 00000000..c2986177 --- /dev/null +++ b/pptl/state_identity.py @@ -0,0 +1,21 @@ +"""Canonical orchestration-state identity and exact cycle detection.""" +from __future__ import annotations +import hashlib, json +from typing import Any, Iterable + +def canonical_state(state: dict[str, Any]) -> str: + return json.dumps(state, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + +def state_id(state: dict[str, Any]) -> str: + return hashlib.sha256(canonical_state(state).encode("utf-8")).hexdigest() + +class StateRegistry: + def __init__(self) -> None: + self._seen: set[str] = set() + def observe(self, state: dict[str, Any]) -> str: + sid = state_id(state); self._seen.add(sid); return sid + def contains(self, state: dict[str, Any]) -> bool: + return state_id(state) in self._seen + @property + def count(self) -> int: return len(self._seen) + def ids(self) -> Iterable[str]: return tuple(sorted(self._seen)) diff --git a/pptl/tests/test_triadic_governance_loop.py b/pptl/tests/test_triadic_governance_loop.py index ecb68c5f..a2da3984 100644 --- a/pptl/tests/test_triadic_governance_loop.py +++ b/pptl/tests/test_triadic_governance_loop.py @@ -1,12 +1,6 @@ """ test_triadic_governance_loop.py — TGL governance contract tests -DGAF-Framework · pptl/tests · S068 · 2026-05-31 - -P-03 × 4 contracts per gate: - 1. Correct pass/kill/warn status - 2. Correct event_type emitted - 3. Correct downstream execution state - 4. Correct gate-specific invariant +DGAF-Framework · pptl/tests · S068 """ import hashlib @@ -14,6 +8,7 @@ from pptl.procluding_premise import PremiseViolationError from pptl.triadic_governance_loop import ( + GateRecord, GateResult, TriadicGovernanceLoop, TGLHooks, @@ -30,43 +25,23 @@ def make_tgl(hooks: TGLHooks = None) -> TriadicGovernanceLoop: @pytest.mark.governance -def test_full_skip_turn_escalates(): - """An unwired governance chain (SKIP) must never produce final PASS.""" - tgl = make_tgl() - audit = tgl.run_turn("safe input") - assert audit.final_status == TurnStatus.ESCALATE - - -@pytest.mark.governance -def test_partial_skip_turn_escalates(): - """A missing required governance gate prevents a final PASS.""" - hooks = TGLHooks( - scpe_fn=lambda text, ctx: GateResult.PASS, - pdmal_fn=lambda text, ctx: GateResult.PASS, - demijoul_fn=lambda text, ctx: GateResult.PASS, - kappa_fn=lambda text, ctx: GateResult.PASS, - sentinel_fn=lambda text, ctx: GateResult.PASS, - phi_closure_fn=lambda text, ctx: GateResult.PASS, - hpg_fn=lambda text, ctx: GateResult.PASS, - # Apogee intentionally unwired: required step 8 must prevent PASS. - ) - audit = make_tgl(hooks).run_turn("partial wiring") +def test_unwired_required_gates_escalate(): + """Required SKIP states must fail closed to ESCALATE rather than PASS.""" + audit = make_tgl().run_turn("safe input") assert audit.final_status == TurnStatus.ESCALATE - assert any(g.step == 8 and g.result == GateResult.SKIP for g in audit.gate_records) @pytest.mark.governance def test_premise_violation_raises_and_kills(): - """P-35 KILL → PremiseViolationError raised, gate logged as KILL.""" + """P-35 KILL → PremiseViolationError raised and gate logged as KILL.""" hooks = TGLHooks(premise_check_fn=lambda text, inv: False) - tgl = make_tgl(hooks) with pytest.raises(PremiseViolationError): - tgl.run_turn("constitutional violation") + make_tgl(hooks).run_turn("constitutional violation") @pytest.mark.governance -def test_downstream_gate_kill_sets_status(): - """Gate KILL at step 3 → final_status KILL, no further steps executed.""" +def test_downstream_gate_kill_sets_status_and_stops_execution(): + """Terminal KILL stops later hooks, including conditional HPG/Apogee execution.""" executed_steps = [] def kill_gate(text, ctx): @@ -80,20 +55,28 @@ def should_not_run(text, ctx): hooks = TGLHooks( demijoul_fn=kill_gate, kappa_fn=should_not_run, + hpg_fn=should_not_run, + apogee_fn=should_not_run, ) - tgl = make_tgl(hooks) - audit = tgl.run_turn("trigger kill") + audit = make_tgl(hooks).run_turn("trigger kill") assert audit.final_status == TurnStatus.KILL - assert 99 not in executed_steps + assert executed_steps == [3] @pytest.mark.governance -def test_phi_closure_kill_sets_kill_rec(): - """P-32 KILL → final_status KILL_REC.""" +def test_phi_closure_kill_sets_terminal_kill(): + """P-32 KILL is reduced to terminal KILL.""" hooks = TGLHooks(phi_closure_fn=lambda t, c: GateResult.KILL) - tgl = make_tgl(hooks) - audit = tgl.run_turn("phi closure fail") - assert audit.final_status == TurnStatus.KILL_REC + audit = make_tgl(hooks).run_turn("phi closure fail") + assert audit.final_status == TurnStatus.KILL + + +@pytest.mark.governance +def test_warn_propagates_to_turn_status(): + """A WARN gate must not be silently reduced to PASS.""" + hooks = TGLHooks(scpe_fn=lambda text, ctx: GateResult.WARN) + audit = make_tgl(hooks).run_turn("warning") + assert audit.final_status == TurnStatus.WARN @pytest.mark.governance @@ -123,23 +106,24 @@ def test_phi_closure_skip_skips_hpg(): @pytest.mark.governance def test_herald_receives_tgl_turn_audit_event(): - """Herald hook receives dict with event_type TGL_TURN_AUDIT.""" + """Herald receives a pre-Herald audit snapshot; final audit is sealed afterward.""" received = [] def capture_herald(audit_dict, ctx): - received.append(audit_dict.get("audit_record", {})) + received.append(audit_dict) return GateResult.PASS - hooks = TGLHooks(herald_fn=capture_herald) - tgl = make_tgl(hooks) - tgl.run_turn("test input") + audit = make_tgl(TGLHooks(herald_fn=capture_herald)).run_turn("test input") assert len(received) == 1 assert received[0]["event_type"] == "TGL_TURN_AUDIT" + assert received[0]["seal_hash"] != "" + assert any(g["step"] == 8 for g in received[0]["gates"]) + assert received[0]["seal_hash"] != audit.seal_hash + assert any(g.step == 9 for g in audit.gate_records) @pytest.mark.governance def test_turn_counter_increments_per_run(): - """turn_counter must increment by 1 per run_turn call.""" tgl = make_tgl() assert tgl.turn_counter == 0 tgl.run_turn("first") @@ -150,16 +134,22 @@ def test_turn_counter_increments_per_run(): @pytest.mark.governance def test_audit_record_is_sealed(): - """TurnAuditRecord must have a non-empty seal_hash after run.""" - tgl = make_tgl() - audit = tgl.run_turn("sealed turn") + audit = make_tgl().run_turn("sealed turn") assert audit.seal_hash != "" assert len(audit.seal_hash) == 64 +@pytest.mark.governance +def test_seal_covers_herald_gate_and_gate_mutation(): + audit = make_tgl().run_turn("sealed full set") + sealed = audit.seal_hash + assert any(g.step == 9 for g in audit.gate_records) + audit.gate_records.append(GateRecord(10, "TEST", "MutationProbe", GateResult.PASS)) + assert audit.seal() != sealed + + @pytest.mark.governance def test_input_hash_is_full_sha256(): - """Audit provenance must use the complete SHA-256 digest.""" text = "hash-bound input" audit = make_tgl().run_turn(text) assert audit.input_hash == hashlib.sha256(text.encode("utf-8")).hexdigest() @@ -168,37 +158,21 @@ def test_input_hash_is_full_sha256(): @pytest.mark.governance def test_gate_records_include_all_10_steps(): - """Gate records must include one entry per TGL step (0–9).""" - tgl = make_tgl() - audit = tgl.run_turn("full pass") + audit = make_tgl().run_turn("full pass") steps = {g.step for g in audit.gate_records} assert steps == {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} @pytest.mark.governance def test_all_unwired_gates_marked_skip(): - """Unwired gates (hooks=None) must be marked SKIP, not PASS or KILL.""" - tgl = make_tgl() - audit = tgl.run_turn("skip test") + audit = make_tgl().run_turn("skip test") skip_steps = [g for g in audit.gate_records if g.step in range(1, 9)] - assert all(g.result == GateResult.SKIP for g in skip_steps) - - -@pytest.mark.governance -def test_seal_changes_when_gate_records_change(): - """Audit seal must cover the recorded governance gate set.""" - tgl = make_tgl() - audit = tgl.run_turn("seal coverage") - original = audit.seal_hash - audit.gate_records.append(audit.gate_records[-1]) - assert audit.seal() != original + assert all(g.result == GateResult.SKIP for g in skip_steps if g.step != 7) @pytest.mark.governance def test_p35_always_fires_regardless_of_hooks(): - """P-35 gate must always run (step 0), even when all other hooks are None.""" - tgl = make_tgl() - audit = tgl.run_turn("p35 check") + audit = make_tgl().run_turn("p35 check") step0 = next(g for g in audit.gate_records if g.step == 0) assert step0.pattern == "P-35" assert step0.result == GateResult.PASS diff --git a/pptl/tests/test_v1_adversarial_contract.py b/pptl/tests/test_v1_adversarial_contract.py new file mode 100644 index 00000000..588c210e --- /dev/null +++ b/pptl/tests/test_v1_adversarial_contract.py @@ -0,0 +1,123 @@ +"""Adversarial DGAF v1 control-plane contract tests.""" +from __future__ import annotations + +import pytest + +from pptl.budget_ledger import BudgetExceeded, Consumption +from pptl.control_plane import ControlPlane, ControlPlaneViolation, ControlTask, TaskState +from pptl.governance_envelope import GovernanceEnvelope, ResourceBudget +from pptl.triadic_governance_loop import GateResult, TGLHooks, TriadicGovernanceLoop, TurnStatus + + +def budget(**overrides: int) -> ResourceBudget: + values = dict( + max_input_tokens=100, + max_output_tokens=100, + max_tool_calls=4, + max_elapsed_ms=1000, + max_rounds=3, + max_nodes=8, + max_depth=2, + max_concurrency=1, + ) + values.update(overrides) + return ResourceBudget(**values) + + +def envelope(**kwargs) -> GovernanceEnvelope: + values = dict( + trace_id="root-trace", + task_id="root", + authority_scope={"research"}, + permitted_tools={"read"}, + data_classes={"public"}, + budget=budget(), + ) + values.update(kwargs) + return GovernanceEnvelope(**values) + + +def _tgl(status: GateResult) -> TriadicGovernanceLoop: + hooks = TGLHooks( + premise_check_fn=lambda _text, _invariant: True, + scpe_fn=lambda _t, _c: status, + pdmal_fn=lambda _t, _c: GateResult.PASS, + demijoul_fn=lambda _t, _c: GateResult.PASS, + kappa_fn=lambda _t, _c: GateResult.PASS, + sentinel_fn=lambda _t, _c: GateResult.PASS, + phi_closure_fn=lambda _t, _c: GateResult.PASS, + hpg_fn=lambda _t, _c: GateResult.PASS, + apogee_fn=lambda _t, _c: GateResult.PASS, + herald_fn=lambda _t, _c: GateResult.PASS, + ) + return TriadicGovernanceLoop("session", "agent", hooks) + + +@pytest.mark.governance +def test_tgl_kill_propagates_to_control_plane() -> None: + plane = ControlPlane(tgl_runner=_tgl(GateResult.KILL).run_turn) + task = ControlTask("root", envelope()) + plane.submit(task) + plane.admit("root") + plane.begin_evaluation("root") + result = plane.evaluate_turn("root", "input") + assert result.final_status is TurnStatus.KILL + assert task.state is TaskState.ESCALATED + assert plane.ledgers["root"].active_concurrency == 0 + + +@pytest.mark.governance +def test_concurrency_ceiling_is_lineage_wide() -> None: + plane = ControlPlane() + root = ControlTask("root", envelope(budget=budget(max_concurrency=1))) + plane.submit(root) + plane.admit("root") + plane.start_expansion("root") + child = plane.create_child( + "root", + task_id="child", + trace_id="child-trace", + authority_scope={"research"}, + permitted_tools={"read"}, + data_classes={"public"}, + envelope_budget=budget(max_depth=1, max_concurrency=1, max_rounds=1, max_nodes=1), + ) + plane.admit("child") + plane.start_expansion("child") + assert child.state is TaskState.ESCALATED + assert plane.ledgers["root"].active_concurrency == 1 + assert plane.ledgers["child"].active_concurrency == 0 + + +@pytest.mark.governance +def test_budget_overrun_escalates_and_releases_slot_immediately() -> None: + plane = ControlPlane() + task = ControlTask("root", envelope(budget=budget(max_tool_calls=1))) + plane.submit(task) + plane.admit("root") + plane.start_expansion("root") + with pytest.raises(BudgetExceeded): + plane.consume("root", Consumption(tool_calls=2)) + assert task.state is TaskState.ESCALATED + assert plane.ledgers["root"].active_concurrency == 0 + assert plane._lineage_active[root_lineage(task)] == 0 + + +def root_lineage(task: ControlTask) -> str: + return task.lineage_id or task.envelope.trace_id + + +def test_child_creation_requires_active_parent() -> None: + plane = ControlPlane() + root = ControlTask("root", envelope()) + plane.submit(root) + with pytest.raises(ControlPlaneViolation): + plane.create_child( + "root", + task_id="child", + trace_id="child-trace", + authority_scope={"research"}, + permitted_tools={"read"}, + data_classes={"public"}, + envelope_budget=budget(max_depth=1, max_concurrency=1), + ) diff --git a/pptl/tests/test_v1_capability_boundaries.py b/pptl/tests/test_v1_capability_boundaries.py index 063789e8..f0afdf77 100644 --- a/pptl/tests/test_v1_capability_boundaries.py +++ b/pptl/tests/test_v1_capability_boundaries.py @@ -11,7 +11,7 @@ VALID_SEAL = "0" * 64 -def budget() -> ResourceBudget: +def _budget() -> ResourceBudget: return ResourceBudget( max_input_tokens=10, max_output_tokens=10, @@ -24,7 +24,7 @@ def budget() -> ResourceBudget: ) -def envelope() -> GovernanceEnvelope: +def _envelope() -> GovernanceEnvelope: return GovernanceEnvelope( trace_id="root-trace", task_id="root", @@ -32,7 +32,7 @@ def envelope() -> GovernanceEnvelope: permitted_tools={"read"}, data_classes={"public"}, prohibited_actions={"delete"}, - budget=budget(), + budget=_budget(), ) @@ -44,29 +44,53 @@ def test_tgl_runner_is_immutable_after_construction(): assert plane.tgl_runner is runner -def test_read_only_views_do_not_expose_mutators(): +def test_read_only_views_expose_no_mutators(): plane = ControlPlane() - task = ControlTask("root", envelope()) - plane.submit(task) + plane.submit(ControlTask("root", _envelope())) assert not hasattr(plane.state_registry, "observe") assert not hasattr(plane.branches, "add") - with pytest.raises(AttributeError): - plane.tasks["other"] = task - with pytest.raises(AttributeError): + with pytest.raises(TypeError): + plane.tasks["other"] = plane.tasks["root"] + with pytest.raises(TypeError): plane.ledgers["root"] = plane.ledgers["root"] - events = plane.events - assert isinstance(events, tuple) - with pytest.raises(AttributeError): - events.append({"event": "tamper"}) + assert isinstance(plane.events, tuple) def test_fake_unsealed_tgl_result_fails_closed(): runner = lambda _input, _context: SimpleNamespace(final_status="PASS", seal_hash="not-a-seal") plane = ControlPlane(tgl_runner=runner) - task = ControlTask("root", envelope()) + task = ControlTask("root", _envelope()) plane.submit(task) plane.admit("root") plane.begin_evaluation("root") with pytest.raises(ControlPlaneViolation, match="valid sealed evidence"): plane.evaluate_turn("root", "input") assert task.state.value == "ESCALATED" + + +def test_merge_ready_cannot_be_manufactured_without_tgl(): + plane = ControlPlane() + task = ControlTask("root", _envelope()) + plane.submit(task) + plane.admit("root") + plane.begin_evaluation("root") + with pytest.raises(ControlPlaneViolation): + plane.mark_merge_ready("root") + + +def test_child_state_registry_observes_post_submit_state(): + plane = ControlPlane() + root = ControlTask("root", _envelope()) + plane.submit(root) + plane.admit("root") + child = plane.create_child( + "root", + task_id="child", + trace_id="child-trace", + authority_scope={"research"}, + permitted_tools={"read"}, + data_classes={"public"}, + envelope_budget=_budget(), + ) + assert child.state.value == "PREFLIGHT" + assert plane.state_registry.contains(child.snapshot()) diff --git a/pptl/tests/test_v1_control_plane.py b/pptl/tests/test_v1_control_plane.py new file mode 100644 index 00000000..ec63d0ff --- /dev/null +++ b/pptl/tests/test_v1_control_plane.py @@ -0,0 +1,360 @@ +from __future__ import annotations + +from types import SimpleNamespace + +import pytest + +from pptl.branch_registry import BranchRecord, BranchRegistry +from pptl.budget_ledger import BudgetExceeded, BudgetLedger, Consumption +from pptl.commit_gate import CommitDenied, CommitGate, CommitRequest +from pptl.control_plane import ControlPlane, ControlPlaneViolation, ControlTask, TaskState +from pptl.governance_envelope import GovernanceEnvelope, ResourceBudget +from pptl.state_identity import StateRegistry, canonical_state, state_id + + +VALID_SEAL = "0" * 64 + + +def tgl_result(status: str) -> SimpleNamespace: + return SimpleNamespace(final_status=status, seal_hash=VALID_SEAL) + + +def budget(**overrides): + values = dict(max_input_tokens=100, max_output_tokens=100, max_tool_calls=4, + max_elapsed_ms=1000, max_rounds=3, max_nodes=8, max_depth=2, + max_concurrency=2) + values.update(overrides) + return ResourceBudget(**values) + + +def envelope(**overrides): + values = dict(trace_id="root-trace", task_id="root", + authority_scope={"research", "draft"}, + permitted_tools={"read", "search"}, + data_classes={"public", "internal"}, + prohibited_actions={"delete", "send"}, budget=budget()) + values.update(overrides) + return GovernanceEnvelope(**values) + + +def test_scope_and_risk_can_only_narrow(): + parent = envelope(risk_tier="medium") + child = parent.derive_child(trace_id="child", task_id="child", + authority_scope={"research"}, permitted_tools={"read"}, + data_classes={"public"}, budget=budget(max_depth=1), risk_tier="low") + assert child.parent_trace_id == parent.trace_id + assert child.risk_tier == "low" + with pytest.raises(PermissionError): + parent.derive_child(trace_id="bad", task_id="bad", + authority_scope={"deploy"}, permitted_tools={"read"}, + data_classes={"public"}, budget=budget(max_depth=1)) + + +def test_metadata_is_inherited_without_override(): + parent = envelope(metadata={"candidate_sha": "abc123", "protocol": "v0.7.5"}) + child = parent.derive_child( + trace_id="child", task_id="child", authority_scope={"research"}, + permitted_tools={"read"}, data_classes={"public"}, budget=budget(max_depth=1), + metadata={"component": "child"}, + ) + assert child.metadata["candidate_sha"] == "abc123" + assert child.metadata["protocol"] == "v0.7.5" + assert child.metadata["component"] == "child" + with pytest.raises(PermissionError): + parent.derive_child( + trace_id="tamper", task_id="tamper", authority_scope={"research"}, + permitted_tools={"read"}, data_classes={"public"}, budget=budget(max_depth=1), + metadata={"candidate_sha": "attacker"}, + ) + + +def test_side_effect_authority_can_only_narrow(): + parent = envelope(side_effect_mode="PROPOSE_ONLY") + child = parent.derive_child( + trace_id="child", task_id="child", authority_scope={"research"}, + permitted_tools={"read"}, data_classes={"public"}, budget=budget(max_depth=1), + side_effect_mode="PROPOSE_ONLY", + ) + assert child.side_effect_mode == "PROPOSE_ONLY" + narrowed_parent = envelope(side_effect_mode="COMMIT_ALLOWED") + narrowed = narrowed_parent.derive_child( + trace_id="narrowed", task_id="narrowed", authority_scope={"research"}, + permitted_tools={"read"}, data_classes={"public"}, budget=budget(max_depth=1), + side_effect_mode="PROPOSE_ONLY", + ) + assert narrowed.side_effect_mode == "PROPOSE_ONLY" + with pytest.raises(PermissionError): + parent.derive_child( + trace_id="bad", task_id="bad", authority_scope={"research"}, + permitted_tools={"read"}, data_classes={"public"}, budget=budget(max_depth=1), + side_effect_mode="COMMIT_ALLOWED", + ) + + +def test_budget_reservation_is_atomic_and_fail_closed(): + ledger = BudgetLedger(budget(max_tool_calls=4)) + ledger.reserve(Consumption(tool_calls=2)) + with pytest.raises(BudgetExceeded): + ledger.reserve(Consumption(tool_calls=3)) + assert ledger.reserved.tool_calls == 2 + + +def test_budget_consumption_accounts_for_outstanding_reservations(): + ledger = BudgetLedger(budget(max_tool_calls=5)) + ledger.reserve(Consumption(tool_calls=3)) + with pytest.raises(BudgetExceeded): + ledger.consume(Consumption(tool_calls=3)) + assert ledger.consumed.tool_calls == 0 + assert ledger.reserved.tool_calls == 3 + + +def test_concurrency_ceiling_is_enforced(): + ledger = BudgetLedger(budget(max_concurrency=2)) + ledger.acquire_concurrency(2) + with pytest.raises(BudgetExceeded): + ledger.acquire_concurrency() + ledger.release_concurrency() + assert ledger.active_concurrency == 1 + + +def test_exact_state_identity_is_deterministic(): + a = {"state": "EVALUATING", "role": "VERIFY", "depth": 1} + b = {"depth": 1, "role": "VERIFY", "state": "EVALUATING"} + assert canonical_state(a) == canonical_state(b) + assert state_id(a) == state_id(b) + registry = StateRegistry(); registry.observe(a) + assert registry.contains(b) + + +def test_branch_registry_retains_correlated_and_vetoing_records(): + registry = BranchRegistry() + registry.add(BranchRecord("verify", None, "VERIFY", "s1", merge_status="correlated")) + registry.add(BranchRecord("govern", None, "GOVERN", "s2", policy_verdict="ESCALATE", merge_status="escalated", terminal=True)) + assert registry.count == 2 + assert registry.by_status("correlated")[0].branch_id == "verify" + + +def test_branch_registry_preserves_shared_state_identity(): + registry = BranchRegistry() + registry.add(BranchRecord("verify-a", None, "VERIFY", "same-state")) + registry.add(BranchRecord("verify-b", None, "VERIFY", "same-state", merge_status="correlated")) + assert {record.branch_id for record in registry.by_state("same-state")} == {"verify-a", "verify-b"} + + +def test_branch_provenance_collections_are_frozen(): + claims = ["claim-1"] + evidence = ["evidence-1"] + assumptions = ["assumption-1"] + record = BranchRecord("verify", None, "VERIFY", "s1", claims=claims, evidence_ids=evidence, assumptions=assumptions) + claims.append("tampered") + evidence.append("tampered") + assumptions.append("tampered") + assert record.claims == ("claim-1",) + assert record.evidence_ids == ("evidence-1",) + assert record.assumptions == ("assumption-1",) + + +def test_branch_metadata_is_immutable(): + source = {"authorization": "AUTH-1"} + record = BranchRecord("verify", None, "VERIFY", "s1", metadata=source) + source["authorization"] = "tampered" + assert record.metadata["authorization"] == "AUTH-1" + with pytest.raises(TypeError): + record.metadata["authorization"] = "tampered" + + +def test_commit_gate_requires_explicit_authorization(): + gate = CommitGate() + gate.propose(CommitRequest("r1", "t1", "send", "external", {"channel": "x"})) + with pytest.raises(CommitDenied): + gate.commit("r1") + gate.authorize("r1", "operator", "AUTH-1") + assert gate.commit("r1") == "operator:AUTH-1" + with pytest.raises(CommitDenied): + gate.authorize("r1", "other", "AUTH-2") + + +def test_commit_request_parameters_are_immutable_after_proposal(): + parameters = {"channel": "x"} + request = CommitRequest("r1", "t1", "send", "external", parameters) + parameters["channel"] = "tampered" + assert request.parameters["channel"] == "x" + with pytest.raises(TypeError): + request.parameters["channel"] = "tampered" + + +def test_commit_cannot_be_replayed(): + gate = CommitGate() + gate.propose(CommitRequest("r1", "t1", "send", "external", {"channel": "x"})) + gate.authorize("r1", "operator", "AUTH-1") + assert gate.commit("r1") == "operator:AUTH-1" + with pytest.raises(CommitDenied, match="already committed"): + gate.commit("r1") + + +def test_control_task_identity_and_runtime_state_are_controller_managed(): + task = ControlTask("root", envelope()) + with pytest.raises(ControlPlaneViolation, match="immutable task identity field"): + task.envelope = envelope(trace_id="attacker-trace", task_id="root") + with pytest.raises(ControlPlaneViolation, match="immutable task identity field"): + task.depth = 99 + with pytest.raises(ControlPlaneViolation, match="immutable task identity field"): + task.lineage_id = "attacker-lineage" + with pytest.raises(ControlPlaneViolation, match="immutable task identity field"): + task.task_id = "attacker-task" + with pytest.raises(AttributeError, match="controller-managed"): + task.state = TaskState.PREFLIGHT + with pytest.raises(AttributeError, match="controller-managed"): + task.last_tgl_status = "PASS" + with pytest.raises(AttributeError, match="controller-managed"): + task.last_tgl_seal = VALID_SEAL + with pytest.raises(AttributeError, match="controller-managed"): + task.concurrency_acquired = True + assert task.state is TaskState.RECEIVED + assert task.state_history == () + + +def test_control_plane_lifecycle_and_cleanup(): + plane = ControlPlane() + task = ControlTask("root", envelope()) + plane.submit(task); plane.admit("root"); plane.start_expansion("root"); plane.begin_evaluation("root") + plane.veto("root", "governance failure") + assert task.state is TaskState.ESCALATED + plane.terminate("root") + assert task.state is TaskState.TERMINATED + assert plane.ledgers["root"].active_concurrency == 0 + + +def test_child_requires_active_parent_and_inherits_lineage(): + plane = ControlPlane() + root = ControlTask("root", envelope()); plane.submit(root) + with pytest.raises(ControlPlaneViolation): + plane.create_child("root", task_id="child", trace_id="child", authority_scope={"research"}, + permitted_tools={"read"}, data_classes={"public"}, envelope_budget=budget(max_depth=1)) + plane.admit("root") + child = plane.create_child("root", task_id="child", trace_id="child", authority_scope={"research"}, + permitted_tools={"read"}, data_classes={"public"}, envelope_budget=budget(max_depth=1)) + assert child.lineage_id == root.lineage_id + + +def test_commit_ready_requires_explicit_envelope_permission_after_tgl_pass(): + plane = ControlPlane(tgl_runner=lambda _input, _context: tgl_result("PASS")) + task = ControlTask("root", envelope()) + plane.submit(task); plane.admit("root"); plane.begin_evaluation("root"); plane.evaluate_turn("root", "input"); plane.mark_merge_ready("root") + with pytest.raises(ControlPlaneViolation): + plane.mark_commit_ready("root") + + +def test_merge_ready_requires_successful_tgl_evaluation(): + plane = ControlPlane() + task = ControlTask("root", envelope()) + plane.submit(task); plane.admit("root"); plane.begin_evaluation("root") + with pytest.raises(ControlPlaneViolation, match="successful sealed TGL evaluation"): + plane.mark_merge_ready("root") + + +def test_merge_ready_rejects_warn_status(): + plane = ControlPlane(tgl_runner=lambda _input, _context: tgl_result("WARN")) + task = ControlTask("root", envelope()) + plane.submit(task); plane.admit("root"); plane.begin_evaluation("root"); plane.evaluate_turn("root", "input") + with pytest.raises(ControlPlaneViolation, match="successful sealed TGL evaluation"): + plane.mark_merge_ready("root") + + +def test_merge_ready_accepts_only_pass_status(): + plane = ControlPlane(tgl_runner=lambda _input, _context: tgl_result("PASS")) + task = ControlTask("root", envelope()) + plane.submit(task); plane.admit("root"); plane.begin_evaluation("root"); plane.evaluate_turn("root", "input") + plane.mark_merge_ready("root") + assert task.state is TaskState.MERGE_READY + + +def test_tgl_missing_seal_fails_closed(): + plane = ControlPlane(tgl_runner=lambda _input, _context: SimpleNamespace(final_status="PASS", seal_hash="")) + task = ControlTask("root", envelope()) + plane.submit(task); plane.admit("root"); plane.begin_evaluation("root") + with pytest.raises(ControlPlaneViolation, match="valid sealed evidence"): + plane.evaluate_turn("root", "input") + assert task.state is TaskState.ESCALATED + + +def test_new_evaluation_replaces_previous_tgl_status(): + statuses = iter(("PASS", "ESCALATE")) + runner = lambda _input, _context: tgl_result(next(statuses)) + plane = ControlPlane(tgl_runner=runner) + task = ControlTask("root", envelope()) + plane.submit(task); plane.admit("root"); plane.begin_evaluation("root") + plane.evaluate_turn("root", "first") + assert task.last_tgl_status == "PASS" + plane.start_expansion("root"); plane.begin_evaluation("root") + plane.evaluate_turn("root", "second") + assert task.state is TaskState.ESCALATED + assert task.last_tgl_status == "ESCALATE" + with pytest.raises(ControlPlaneViolation, match="merge readiness requires EVALUATING state"): + plane.mark_merge_ready("root") + + +def test_tgl_exception_escalates_and_releases_slot(): + def failing_tgl(_input, _context): + raise RuntimeError("synthetic TGL failure") + + plane = ControlPlane(tgl_runner=failing_tgl) + task = ControlTask("root", envelope()) + plane.submit(task); plane.admit("root"); plane.start_expansion("root"); plane.begin_evaluation("root") + with pytest.raises(ControlPlaneViolation, match="TGL runner failed"): + plane.evaluate_turn("root", "input") + assert task.state is TaskState.ESCALATED + assert plane.ledgers["root"].active_concurrency == 0 + assert plane._lineage_active[root_lineage(task)] == 0 + + +def test_start_expansion_consumes_round_and_node_without_leaking_reservation(): + plane = ControlPlane() + task = ControlTask("root", envelope()) + plane.submit(task); plane.admit("root"); plane.start_expansion("root") + assert task.state is TaskState.EXPANDING + assert plane.ledgers["root"].consumed.rounds == 1 + assert plane.ledgers["root"].consumed.nodes == 1 + assert plane.ledgers["root"].reserved.rounds == 0 + assert plane.ledgers["root"].reserved.nodes == 0 + + +def test_illegal_start_expansion_has_no_resource_side_effects(): + plane = ControlPlane() + task = ControlTask("root", envelope()) + plane.submit(task) + with pytest.raises(ControlPlaneViolation, match="illegal transition PREFLIGHT -> EXPANDING"): + plane.start_expansion("root") + ledger = plane.ledgers["root"] + assert task.state is TaskState.PREFLIGHT + assert ledger.active_concurrency == 0 + assert ledger.consumed.rounds == 0 + assert ledger.consumed.nodes == 0 + + +def test_terminal_task_cannot_consume_resources(): + plane = ControlPlane() + task = ControlTask("root", envelope()) + plane.submit(task); plane.admit("root"); plane.terminate("root") + ledger = plane.ledgers["root"] + before = ledger.consumed + with pytest.raises(ControlPlaneViolation, match="terminal task cannot consume"): + plane.consume("root", Consumption(tool_calls=1)) + assert ledger.consumed == before + + +def test_create_child_duplicate_id_does_not_pollute_state_registry(): + plane = ControlPlane() + root = ControlTask("root", envelope()); plane.submit(root); plane.admit("root") + existing = ControlTask("child", envelope(trace_id="existing-trace", task_id="child")) + plane.submit(existing) + before = plane.state_registry.count + with pytest.raises(ControlPlaneViolation, match="duplicate task_id: child"): + plane.create_child("root", task_id="child", trace_id="new-child-trace", + authority_scope={"research"}, permitted_tools={"read"}, + data_classes={"public"}, envelope_budget=budget(max_depth=1)) + assert plane.state_registry.count == before + + +def root_lineage(task: ControlTask) -> str: + return task.lineage_id or task.envelope.trace_id diff --git a/pptl/tests/test_v1_tgl_integration.py b/pptl/tests/test_v1_tgl_integration.py new file mode 100644 index 00000000..81176e38 --- /dev/null +++ b/pptl/tests/test_v1_tgl_integration.py @@ -0,0 +1,73 @@ +from __future__ import annotations + +import pytest + +from pptl.control_plane import ControlPlane, ControlTask, TaskState +from pptl.governance_envelope import GovernanceEnvelope, ResourceBudget +from pptl.triadic_governance_loop import GateResult, TGLHooks, TriadicGovernanceLoop, TurnStatus + + +def envelope(): + return GovernanceEnvelope( + trace_id="root-trace", task_id="root", authority_scope={"research"}, + permitted_tools={"read"}, data_classes={"public"}, + budget=ResourceBudget(max_input_tokens=100, max_output_tokens=100, + max_tool_calls=4, max_elapsed_ms=1000, + max_rounds=2, max_nodes=4, max_depth=2, + max_concurrency=1), + ) + + +def tgl(result=GateResult.PASS, herald_result=GateResult.PASS): + hooks = TGLHooks( + premise_check_fn=lambda _text, _invariant: True, + scpe_fn=lambda _t, _c: result, + pdmal_fn=lambda _t, _c: GateResult.PASS, + demijoul_fn=lambda _t, _c: GateResult.PASS, + kappa_fn=lambda _t, _c: GateResult.PASS, + sentinel_fn=lambda _t, _c: GateResult.PASS, + phi_closure_fn=lambda _t, _c: GateResult.PASS, + hpg_fn=lambda _t, _c: GateResult.PASS, + apogee_fn=lambda _t, _c: GateResult.PASS, + herald_fn=lambda _t, _c: herald_result, + ) + return TriadicGovernanceLoop("session", "agent", hooks) + + +@pytest.mark.governance +def test_tgl_pass_remains_evaluable_inside_lifecycle(): + plane = ControlPlane(tgl_runner=tgl().run_turn) + task = ControlTask("root", envelope()) + plane.submit(task); plane.admit("root"); plane.begin_evaluation("root") + result = plane.evaluate_turn("root", "safe") + assert result.final_status is TurnStatus.PASS + assert task.state is TaskState.EVALUATING + + +@pytest.mark.governance +def test_tgl_kill_becomes_lifecycle_escalation(): + plane = ControlPlane(tgl_runner=tgl(GateResult.KILL).run_turn) + task = ControlTask("root", envelope()) + plane.submit(task); plane.admit("root"); plane.begin_evaluation("root") + result = plane.evaluate_turn("root", "unsafe") + assert result.final_status is TurnStatus.KILL + assert task.state is TaskState.ESCALATED + + +@pytest.mark.governance +def test_tgl_evaluation_requires_evaluating_state(): + plane = ControlPlane(tgl_runner=tgl().run_turn) + task = ControlTask("root", envelope()) + plane.submit(task); plane.admit("root") + with pytest.raises(RuntimeError): + plane.evaluate_turn("root", "premature") + + +@pytest.mark.governance +def test_herald_warning_is_reflected_in_final_status(): + plane = ControlPlane(tgl_runner=tgl(herald_result=GateResult.WARN).run_turn) + task = ControlTask("root", envelope()) + plane.submit(task); plane.admit("root"); plane.begin_evaluation("root") + result = plane.evaluate_turn("root", "warn-at-publication") + assert result.final_status is TurnStatus.WARN + assert result.gate_records[-1].gate_name == "Herald_FanOut" diff --git a/pptl/triadic_governance_loop.py b/pptl/triadic_governance_loop.py index 9162b7c4..18c309ce 100644 --- a/pptl/triadic_governance_loop.py +++ b/pptl/triadic_governance_loop.py @@ -1,18 +1,14 @@ """ Triadic Governance Loop (TGL) — canonical 10-step governance sequencer. -DGAF-Framework · pptl · S068 +DGAF-Framework · pptl -Authority: Triumvirate (P-08/P-09) - Prime: Amethyst - Prefect A: COLLEEN - Prefect B: Apogee - -The TGL is a deterministic gate sequencer. Each step is independently -hookable; an unset hook is recorded as SKIP (never implicit PASS). +The TGL is a deterministic gate sequencer. Unwired required gates are +recorded as SKIP and reduce the turn to ESCALATE; SKIP is never implicit PASS. """ from __future__ import annotations import hashlib +import json from dataclasses import dataclass, field from datetime import datetime, timezone from enum import Enum @@ -51,12 +47,8 @@ class GateRecord: @dataclass class TurnAuditRecord: - """ - Immutable-at-boundary audit record for one TGL turn. + """Audit record whose final cryptographic seal covers the complete gate set.""" - Emitted to Herald sink (P-01) on PASS. - Emitted with KILL status to dead-letter on any terminal failure. - """ session_id: str turn_index: int agent_id: str @@ -66,16 +58,30 @@ class TurnAuditRecord: timestamp: str seal_hash: str = field(default="", init=False) + def _canonical_payload(self) -> bytes: + payload = { + "session_id": self.session_id, + "turn_index": self.turn_index, + "agent_id": self.agent_id, + "input_hash": self.input_hash, + "final_status": self.final_status.value, + "timestamp": self.timestamp, + "gates": [ + { + "step": g.step, + "pattern": g.pattern, + "gate": g.gate_name, + "result": g.result.value, + "notes": g.notes, + } + for g in self.gate_records + ], + } + return json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8") + def seal(self) -> str: - gates_payload = "|".join( - f"{g.step}:{g.pattern}:{g.gate_name}:{g.result.value}:{g.notes}" - for g in self.gate_records - ) - payload = ( - f"{self.session_id}|{self.turn_index}|{self.agent_id}|" - f"{self.input_hash}|{self.final_status}|{self.timestamp}|{gates_payload}" - ) - self.seal_hash = hashlib.sha256(payload.encode()).hexdigest() + """Seal the exact current audit contents, including every gate record.""" + self.seal_hash = hashlib.sha256(self._canonical_payload()).hexdigest() return self.seal_hash def to_dict(self) -> dict[str, Any]: @@ -104,14 +110,8 @@ def to_dict(self) -> dict[str, Any]: @dataclass class TGLHooks: - """ - Hook functions wired to each TGL step. - Each hook: (input_text: str, context: dict) -> GateResult - None = SKIP (gate not wired in this deployment). - - Minimum viable wiring: premise_gate is always populated. - All other gates are optional for incremental integration. - """ + """Hook functions for each TGL step. None means the gate is unwired/SKIP.""" + premise_check_fn: Optional[Callable] = None scpe_fn: Optional[Callable] = None pdmal_fn: Optional[Callable] = None @@ -140,7 +140,16 @@ class TriadicGovernanceLoop: (9, "P-01", "Herald_FanOut"), ] - def __init__(self, session_id: str, agent_id: str, hooks: TGLHooks, turn_counter: int = 0) -> None: + # Required gates. Step 7 is conditional on Phi-Closure PASS. + REQUIRED_STEPS = frozenset({1, 2, 3, 4, 5, 6, 8}) + + def __init__( + self, + session_id: str, + agent_id: str, + hooks: TGLHooks, + turn_counter: int = 0, + ) -> None: self.session_id = session_id self.agent_id = agent_id self.hooks = hooks @@ -158,36 +167,82 @@ def turn_counter(self) -> int: def _hash_input(self, text: str) -> str: return hashlib.sha256(text.encode("utf-8")).hexdigest() - def _run_hook(self, hook_fn: Optional[Callable], input_text: str, context: dict, - step: int, pattern: str, gate_name: str) -> GateRecord: + def _run_hook( + self, + hook_fn: Optional[Callable], + input_text: str, + context: dict, + step: int, + pattern: str, + gate_name: str, + ) -> GateRecord: if hook_fn is None: return GateRecord(step, pattern, gate_name, GateResult.SKIP, "not wired") try: result = hook_fn(input_text, context) gate_result = GateResult(result) if isinstance(result, str) else result + if not isinstance(gate_result, GateResult): + return GateRecord(step, pattern, gate_name, GateResult.KILL, "invalid gate result") return GateRecord(step, pattern, gate_name, gate_result) except Exception as exc: return GateRecord(step, pattern, gate_name, GateResult.KILL, str(exc)[:120]) - def run_turn(self, input_text: str, context: Optional[dict] = None) -> TurnAuditRecord: - if context is None: - context = {} + @staticmethod + def _reduce_status(gates: list[GateRecord], initial: TurnStatus = TurnStatus.PASS) -> TurnStatus: + """Apply the monotonic gate lattice: KILL > ESCALATE > WARN > PASS.""" + if any(g.result == GateResult.KILL for g in gates): + return TurnStatus.KILL + if any(g.step in TriadicGovernanceLoop.REQUIRED_STEPS and g.result == GateResult.SKIP for g in gates): + return TurnStatus.ESCALATE + if any(g.result == GateResult.WARN for g in gates): + return TurnStatus.WARN + return initial + + def _emit_herald_and_seal( + self, + audit: TurnAuditRecord, + context: dict, + raise_premise: Exception | None = None, + ) -> TurnAuditRecord: + """Publish a pre-Herald snapshot, append Herald result, reduce again, then final-seal the complete set.""" + herald_record = self._run_hook( + self.hooks.herald_fn, + "", + {**context, "audit_record": audit.to_dict(), "seal_scope": "pre_herald"}, + 9, + "P-01", + "Herald_FanOut", + ) + audit.gate_records.append(herald_record) + audit.final_status = self._reduce_status(audit.gate_records, initial=audit.final_status) + audit.seal() + if raise_premise is not None: + raise raise_premise + return audit + + def run_turn( + self, + input_text: str, + context: Optional[dict] = None, + ) -> TurnAuditRecord: + """Execute the TGL sequence and return an audit sealed over the final gate set.""" + context = {} if context is None else context self._turn_counter += 1 input_hash = self._hash_input(input_text) timestamp = datetime.now(timezone.utc).isoformat() gates: list[GateRecord] = [] - final_status = TurnStatus.PASS try: self._premise_gate.evaluate(input_text, check_fn=self.hooks.premise_check_fn) gates.append(GateRecord(0, "P-35", "ProcludingPremiseGate", GateResult.PASS)) except PremiseViolationError as exc: gates.append(GateRecord(0, "P-35", "ProcludingPremiseGate", GateResult.KILL, str(exc)[:120])) - rec = TurnAuditRecord(self.session_id, self._turn_counter, self.agent_id, input_hash, gates, TurnStatus.KILL, timestamp) - rec.seal() - if self.hooks.herald_fn: - self.hooks.herald_fn(rec.to_dict(), context) - raise + audit = TurnAuditRecord( + self.session_id, self._turn_counter, self.agent_id, input_hash, + gates, TurnStatus.KILL, timestamp, + ) + self._emit_herald_and_seal(audit, context, raise_premise=exc) + return audit hook_sequence = [ (1, "P-31", "SCPE_Prune", self.hooks.scpe_fn), @@ -198,58 +253,46 @@ def run_turn(self, input_text: str, context: Optional[dict] = None) -> TurnAudit (6, "P-32", "PhiClosure_Gate", self.hooks.phi_closure_fn), ] + terminated = False phi_closure_result = GateResult.SKIP for step, pattern, gate_name, hook_fn in hook_sequence: rec = self._run_hook(hook_fn, input_text, context, step, pattern, gate_name) gates.append(rec) if step == 6: phi_closure_result = rec.result - if rec.result == GateResult.KILL: - final_status = TurnStatus.KILL_REC - break if rec.result == GateResult.KILL: - final_status = TurnStatus.KILL + terminated = True break - if not any(g.step == 6 and g.result == GateResult.KILL for g in gates): + # HPG is conditional and cannot run after any terminal failure. + if not terminated: if phi_closure_result == GateResult.PASS: - rec = self._run_hook(self.hooks.hpg_fn, input_text, context, 7, "N/A", "HPG_OctaveGate") + gates.append( + self._run_hook( + self.hooks.hpg_fn, input_text, context, 7, "N/A", "HPG_OctaveGate" + ) + ) else: - rec = GateRecord(7, "N/A", "HPG_OctaveGate", GateResult.SKIP, "Phi-Closure did not PASS") - gates.append(rec) - if rec.result == GateResult.KILL: - final_status = TurnStatus.KILL - - if final_status in {TurnStatus.PASS, TurnStatus.WARN, TurnStatus.ESCALATE}: - rec = self._run_hook(self.hooks.apogee_fn, input_text, context, 8, "P-30", "Apogee_AttestationGate") - gates.append(rec) - if rec.result == GateResult.KILL: - final_status = TurnStatus.KILL - - if final_status == TurnStatus.PASS: - required_skip_steps = [g.step for g in gates if 1 <= g.step <= 8 and g.result == GateResult.SKIP] - if required_skip_steps: - final_status = TurnStatus.ESCALATE - + gates.append( + GateRecord(7, "N/A", "HPG_OctaveGate", GateResult.SKIP, "Phi-Closure did not PASS") + ) + + # Apogee is allowed to inspect an escalated/warned turn, but not a KILL. + if not any(g.result == GateResult.KILL for g in gates): + gates.append( + self._run_hook( + self.hooks.apogee_fn, input_text, context, 8, "P-30", "Apogee_AttestationGate" + ) + ) + + final_status = self._reduce_status(gates) audit = TurnAuditRecord( - session_id=self.session_id, - turn_index=self._turn_counter, - agent_id=self.agent_id, - input_hash=input_hash, - gate_records=gates, - final_status=final_status, - timestamp=timestamp, + self.session_id, + self._turn_counter, + self.agent_id, + input_hash, + gates, + final_status, + timestamp, ) - audit.seal() - - herald_rec = self._run_hook( - self.hooks.herald_fn, - input_text, - {**context, "audit_record": audit.to_dict()}, - 9, - "P-01", - "Herald_FanOut", - ) - gates.append(herald_rec) - audit.seal() - return audit + return self._emit_herald_and_seal(audit, context) diff --git a/requirements-ci.txt b/requirements-ci.txt index 06a64ca4..49411c98 100644 --- a/requirements-ci.txt +++ b/requirements-ci.txt @@ -4,14 +4,14 @@ pytest==9.1.1 pytest-cov==7.1.0 pytest-asyncio==1.4.0 -pytest-timeout==2.5.0 +pytest-timeout==2.4.0 +numpy==2.2.6 mypy==2.3.1 flake8==7.3.0 black==26.5.1 isort==8.0.1 pydantic==2.13.4 jsonschema==4.26.0 -numpy==2.2.5 setuptools>=83.0.0,<84 bandit==1.9.4 safety==3.8.1 diff --git a/tests/test_agent_authority_matrix.py b/tests/test_agent_authority_matrix.py index cd77af7b..ae5beebf 100644 --- a/tests/test_agent_authority_matrix.py +++ b/tests/test_agent_authority_matrix.py @@ -10,12 +10,20 @@ def _read(path: Path) -> str: return path.read_text(encoding="utf-8") -def _active_authority_section(matrix: str) -> str: - start_marker = "## 2. Current Authority Baseline" - end_marker = "## 3. Shared Layer-0 Constitutional Substrate" - start = matrix.index(start_marker) + len(start_marker) - end = matrix.index(end_marker, start) - return matrix[start:end] +def _active_agent_rows(matrix: str) -> set[str]: + rows = set() + in_baseline = False + for line in matrix.splitlines(): + if line.strip() == "## 2. Current Authority Baseline": + in_baseline = True + continue + if in_baseline and line.startswith("## "): + break + if in_baseline and line.startswith("|"): + cells = [cell.strip() for cell in line.strip("|").split("|")] + if len(cells) >= 1 and cells[0] not in {"Agent", "---"}: + rows.add(cells[0]) + return rows def test_authority_matrix_is_present_and_scoped(): @@ -44,7 +52,7 @@ def test_matrix_preserves_non_delegation_boundaries(): def test_matrix_contains_current_specialists(): matrix = _read(MATRIX) - active = _active_authority_section(matrix) + active_agents = _active_agent_rows(matrix) for agent in ( "Amethyst", "Apogee", @@ -64,10 +72,9 @@ def test_matrix_contains_current_specialists(): "Reciprocity", "Sentinel-Φ", ): - assert agent in active - assert "Sentience" not in active - assert "Sentinel-Φ / Sentinel" not in active - assert "**Sentience** is a historical/merged identity" in matrix + assert agent in active_agents + assert "Sentience" not in active_agents + assert "Sentinel-Φ / Sentinel" not in active_agents def test_reconciliation_targets_are_explicit():