CR-012/PR-012a: DAG-2026 foundation — single-source flag, DagSettings, ground-truth addendum - #260
Merged
Merged
Conversation
…ound-truth addendum DAG-2026 foundation (ADR-RT-004, CR-012 s4.5/s5.1). Adds graqle.assurance with is_dag_enabled() (positive allowlist, default OFF), DagSettings (every DAG symbol typed, GRAQLE_DAG_ prefix, frozen, 0 rejected for gate thresholds and cap, required-when-enabled fail-closed), load_dag_settings() (env > private file > safe default, cached only on success, drift lock on unknown GRAQLE_DAG_* names), config_version() (joint HMAC commitment, secret values never in clear), validate_flag_consistency() wired into GraqleConfig.from_yaml(). GraqleConfig gains a read-only assurance.enabled derived from the env flag; assurance.enabled in graqle.yaml is rejected before env interpolation. Adds .env.example (placeholders only) and docs/dag/ground-truth-addendum.md with line-anchor verification notes. Flag off => GovernanceMiddleware.check() untouched. 84 new tests; tests/test_config unchanged (217 pass). Deviations from the CR and the D1 P0 reproduction are in the PR body and issue #338. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit 93adcced88dcc332e988535e32e1571c0e214a4e)
…C binding, M-3 number-free hints, from_yaml contract) graq_reason sentinel pass 1 (81%) REQUEST CHANGES: B-1 cache critical section now under an RLock with double-checked freshness; M-1 secrets digest message bound to the non-secret payload under a domain label (key = secret values, never serialised); M-3 ValidationError conversion maps pydantic error types to number-free operator hints and names the env var; B-2 documented as the intended fail-closed startup boundary in from_yaml's Raises (policy question stays with issue #338 N5). Refuted: m-4 (drift scan already case-insensitive). M-2 (computed_field vs property) left for the #338 N3 ruling — two sentinel passes disagreed. 301 tests pass. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit 5204add5e30c770085f9adff33cc35ab06b0e372)
…k-is-absent, M2 cache key, M3 path/file safety, N1/N3/N4 (rulings N2/N3/N4; PR-339 round 1) B1: new DagSettings.config_salt (GRAQLE_DAG_CONFIG_SALT, REQUIRED_WHEN_ENABLED, >=32 bytes, SecretStr, never logged/serialised, excluded from the payload); config_version HMAC key = salt, message = label | canon(secret values) | canon(non-secret payload); keying tag deployment_salt_v1 / joint_canonical_v1 in config_provenance (ruling N2). M1: '', whitespace and None are absent for every REQUIRED_WHEN_ENABLED field. M2: cache token = (flag snapshot, sha256 of all GRAQLE_DAG_* env values, resolved secrets path, mtime_ns, size); flag snapshotted once per load; reset clears the token under the lock. M3: path-typed fields excluded from the fingerprint; secrets file resolve(), regular-file check, POSIX 0o077 refusal with chmod 600 message, Windows WARNING. N1: core.rbac ImportError -> ConfigurationError. N3: tautology documented. N4: DEBUG log of exception type before from None. Tests: +22 (salt/blank/cache/path/mode/rbac/debug); 393 pass across tests/test_assurance tests/test_docs tests/test_config tests/test_public_api tests/test_packaging. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit df5434443e0babb133999eb29aa8c00afed6b960)
…descriptor (O_NOFOLLOW + fstat), symlink test Post-implementation sentinel pass 1 on df543444 (graq_reason tool 0.685 / synth 81%) flagged the resolve->stat->open sequence as a TOCTOU/symlink race. Fix: _open_secrets_file opens with O_RDONLY|O_NOFOLLOW|O_CLOEXEC on POSIX and runs the regular-file and 0o077 mode checks on fstat() of the open descriptor before reading from it; ELOOP/IsADirectory -> ConfigurationError naming the path; Windows keeps the best-effort path + WARNING. Refuted after verification (not changed): MAJ-1 'salt rotation invisible to the cache token' (the env fingerprint covers GRAQLE_DAG_CONFIG_SALT; test_b1_identical_secrets_different_salts_differ reloads without force), MAJ-2 'keying tag is an oracle' (with the salt required when enabled the tag is a function of the public flag state; the tag is required by ruling N2). 393 pass, 7 platform skips. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit 8828d6e067d661ce84b29464dc2d6bc311f6100d)
…t_ino in cache token, operator docs) Research-Team rounds 2+3 APPROVED conditional on C1-C3 (pull/339#issuecomment-5656126399). C1: the value-keyed HMAC fallback is removed - a MAC keyed by the data it authenticates must not exist as a precedent even when unreachable. With a salt the digest is HMAC-SHA256 under GRAQLE_DAG_CONFIG_SALT (keying=deployment_salt_v1); without one it is an explicitly unkeyed sha256 over label | canon(secrets) | canon(public) (keying=unkeyed_checksum_v1). KEYING_JOINT_CANONICAL deleted; CR-018 note recorded that anchoring must refuse any keying other than deployment_salt_v1. C2: st_ino added to the cache token - an atomic rename-replacement can preserve mtime_ns and size. C3: .env.example requires a CSPRNG-generated salt (openssl rand -base64 48, never a passphrase); new 'Operating the DAG flag' section in docs/dag/ground-truth-addendum.md covers salt generation and rotation, the Kubernetes 0644 refusal and its defaultMode 0400 remedy, and the Windows residual. +4 tests (unkeyed digest recomputed and shown not to be the old MAC, source grep against the precedent, inode-change reload with mtime_ns and size held equal). 395 passed, 7 skipped; ruff clean; governance/drace/rbac/config-settings diff empty. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit 5aa4ae78e8790c190e11114e45ff9178af52c12b)
🛡️ GraQle PR Guardian💥 Blast Radius: 9 modules affected
Total blast radius: 9 🏛️ Governance Verdict🚫 FAIL
🔍 SHACL ViolationsNo SHACL violations detected. ✅ 🔐 Approval RequirementsThis PR requires approval from:
❌ Approval requirement NOT yet satisfied.
🔬 Powered by GraQle PR Guardian v0.1.0 · Scan completed 2026-09-15T06:35:27.775398+00:00 |
harishquantamix
approved these changes
Sep 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Public port of the DAG-2026 foundation, cherry-picked after the private PR merged.
Private PR: quantamixsol/research-development-graqle#339 — APPROVED by the Research Team (rounds 1, 2+3, and the C1–C3 conditional approval), merged 2026-09-14 as
f4a53b87.Cherry-picked commits (feature commits only, never the merge commit):
93adcced,5204add5,df543444,8828d6e0,5aa4ae78→ replayed here asbe70039d,b143035e,a25a94ba,d6fb4229,ffe77e01.What this is
The foundation layer for the Decision Assurance Gate programme (ADR-RT-004). It ships configuration plumbing and types only — no gate logic, no routing, no scoring.
graqle.assurancepackage —is_dag_enabled()(single-source flag reader; positive allowlist1|true|yes|on, anything else OFF),DagSettings(one typed home for every DAG configuration symbol,GRAQLE_DAG_*prefix, frozen, unknown names rejected),ConfigurationError,load_dag_settings()(environment > private file atGRAQLE_DAG_SECRETS_PATH> safe default; cached only on success),config_version()(deterministic fingerprint; private values never in clear),config_provenance(),validate_flag_consistency().GraqleConfig.assurance—enabledis derived from the environment flag and is read-only. Settingassurance.enabledingraqle.yamlraisesConfigurationErrorbefore environment interpolation..env.exampleat the repo root — every DAG variable NAME with placeholders. No tuning values.docs/dag/ground-truth-addendum.md— the charter-correction record, plus an operator section covering CSPRNG salt generation and rotation, the KubernetesdefaultMode: 0400requirement, and the Windows residual.Three statements the Research Team asked for explicitly
GRAQLE_DAG_ENABLEDdefaults to unset, which resolves to OFF.assurancesection onGraqleConfig.GovernanceMiddleware.check()is untouched. Verified by diff guard:graqle/core/governance.py,graqle/governance/*,graqle/intelligence/governance/drace.pyandgraqle/core/rbac.pyare all byte-identical to master on this branch.Verification on this branch
Rebased onto current master after CR-README-02 (#259) landed, so the diff is exactly the nine PR-012a paths and nothing else.
CHANGELOG.mdis deliberately unchanged: master already carries the## Unreleased — 0.84.0 (DAG-2026 CR-012 foundation)block, and the released## 0.84.0section must not be disturbed.Version note
This does not ship in a new version. Per the Research-Team ruling, the DAG foundation is released only once the gate vocabulary (PR-012b:
GateOutcome, the reason-code registry,GateVerdict, trace schema v3) lands alongside it. The published 0.84.0 artifact does not contain this code, and the CHANGELOG entry stays under Unreleased until a version carries it.🤖 Generated with Claude Code