add strict external parity release gate

This commit is contained in:
732642856
2026-07-14 19:57:46 +08:00
parent ba0e836a70
commit bd6a9e84d6
5 changed files with 29 additions and 5 deletions
+1
View File
@@ -92,6 +92,7 @@ For large architecture or release work, also read:
| ERR-059 | Gulika was either an approximate fallback or falsely implied as a chart module output. | mitigated 2026-07-14 | `scripts/gulika.py` computes Prasna Marga Ghatika segment Ascendant with Swiss sunrise/sunset and Lahiri sidereal houses. It is exposed only as `prashna_context.supporting_indicators.gulika`, remains `partial`, and cannot unlock Sphuta or verdict layers until external numeric parity exists. |
| ERR-060 | Legacy Sphuta functions combined approximate Gulika with interpretive signals, while the exact formula could not be inspected in the production question context. | mitigated 2026-07-14 | `prashna_sphuta.py` exposes formula-only Trisphuta/Catusphuta/Pancasphuta from the partial Gulika evidence. It is supporting-only; Kunda, life-sensitive Sphutas and Prashna verdicts remain blocked pending external numeric parity. |
| ERR-061 | Full-reading passed longitude-only data to the Tajika layer, permanently blocking its speed-dependent seven-planet interaction evidence. | mitigated 2026-07-14 | Pass actual Swiss longitude/speed pairs. The output may expose only partial Ithasala/Easarapha candidates; named chains and event verdicts stay blocked pending golden cases. |
| ERR-062 | A release gate could validate a parity manifest's shape without requiring all external engines to actually match, allowing “contract valid” to be mistaken for “oracle verified.” | mitigated 2026-07-14 | `three_engine_parity_replay_validator.py --require-pass` fails unless parity status is pass; `run_quality_gate.py --require-external-parity` exposes this as an explicit high-standard release requirement. |
## Fragment Sweep Command Set
+7 -3
View File
@@ -378,7 +378,7 @@ def git_untracked_files() -> set[str]:
return {line.strip() for line in completed.stdout.splitlines() if line.strip()}
def release_hygiene_check() -> None:
def release_hygiene_check(require_external_parity: bool = False) -> None:
print("\n== Release hygiene check ==")
untracked = git_untracked_files()
critical = [path for path in RELEASE_CRITICAL_UNTRACKED_PATHS if path in untracked]
@@ -393,7 +393,10 @@ def release_hygiene_check() -> None:
raise SystemExit(1)
run([PYTHON, "scripts/public_release_privacy_scan.py", "--json"])
run([PYTHON, "scripts/report_renderer_isolation_poc.py", "--strict"])
run([PYTHON, "scripts/three_engine_parity_replay_validator.py", "references/oracle/three_engine_parity_replay_manifest.json"])
parity_command = [PYTHON, "scripts/three_engine_parity_replay_validator.py", "references/oracle/three_engine_parity_replay_manifest.json"]
if require_external_parity:
parity_command.append("--require-pass")
run(parity_command)
print("release_hygiene_check ok: no release-critical product files are untracked")
@@ -496,6 +499,7 @@ def main() -> int:
parser.add_argument("--frontend-click-mode", choices=["core", "mobile", "offline", "pdf", "workspace", "mobile-trust", "import-files", "all"], default=None, help="Browser click smoke mode for browser/release profiles")
parser.add_argument("--frontend-click-timeout", type=int, default=240, help="Timeout seconds for browser click smoke")
parser.add_argument("--all-tests", action="store_true", help="Run every pytest file, including optional-dependency suites")
parser.add_argument("--require-external-parity", action="store_true", help="Fail the release gate unless the three-engine raw parity manifest passes.")
args = parser.parse_args()
profile = run_profile(args)
@@ -525,7 +529,7 @@ def main() -> int:
run([PYTHON, "scripts/character_level_inventory_manifest.py", "--scope", "project", "--no-write", "--summary-only"])
run([PYTHON, "scripts/deployment_preflight.py"])
if profile["check_release_hygiene"]:
release_hygiene_check()
release_hygiene_check(require_external_parity=args.require_external_parity)
run([PYTHON, "scripts/validate_bphs_invariants.py"])
if args.all_tests:
pytest_targets = ["tests"]
@@ -119,9 +119,11 @@ def validate_manifest(path: str | Path) -> dict[str, Any]:
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("manifest", nargs="?", default="references/oracle/three_engine_parity_replay_manifest.json")
parser.add_argument("--require-pass", action="store_true", help="Return nonzero unless all comparison rows pass.")
args = parser.parse_args(argv)
print(json.dumps(validate_manifest(args.manifest), ensure_ascii=False, indent=2, sort_keys=True))
return 0
report = validate_manifest(args.manifest)
print(json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True))
return 0 if not args.require_pass or report["status"] == "pass" else 1
if __name__ == "__main__":
+2
View File
@@ -6,3 +6,5 @@ def test_release_profile_requires_privacy_and_renderer_probes() -> None:
assert '"scripts/public_release_privacy_scan.py", "--json"' in source
assert '"scripts/report_renderer_isolation_poc.py", "--strict"' in source
assert '"scripts/three_engine_parity_replay_validator.py"' in source
assert '"--require-external-parity"' in source
assert 'parity_command.append("--require-pass")' in source
@@ -2,6 +2,9 @@ from __future__ import annotations
import hashlib
import json
import subprocess
import sys
from pathlib import Path
from pathlib import Path
from scripts.three_engine_parity_replay_validator import validate_manifest
@@ -54,3 +57,15 @@ def test_three_engine_parity_validator_accepts_one_same_chart_row(tmp_path: Path
assert result["tested"] is True
assert result["comparison_row_count"] == 1
assert result["match_count"] == 1
def test_validator_require_pass_rejects_blocked_manifest() -> None:
root = Path(__file__).resolve().parents[1]
completed = subprocess.run(
[sys.executable, "scripts/three_engine_parity_replay_validator.py", "--require-pass"],
cwd=root,
capture_output=True,
text=True,
check=False,
)
assert completed.returncode == 1