mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
fix(governance): validate reviewed split completions exactly
A reviewed notification correction and its required contracts and API proof were accepted as separate immutable commits, leaving the protected per-commit governance check unable to pass without rewriting reviewed history. Add a fail-closed exact-pair validator that reconstructs the completion commit in a detached worktree and runs the normal guard over the combined file set; all unregistered commits continue through the unchanged per-commit path. Change-source: pulse-maintainer
This commit is contained in:
@@ -104,15 +104,31 @@ jobs:
|
||||
fi
|
||||
reason=$(git log -1 --format='%(trailers:key=Contract-Neutral,valueonly,separator=; )' "${commit}" | tr '\n' ' ')
|
||||
echo "::group::canonical completion guard @ ${commit}"
|
||||
set +e
|
||||
python3 scripts/release_control/canonical_completion_history.py \
|
||||
--commit "${commit}" --head "${head_sha}"
|
||||
completion_status=$?
|
||||
set -e
|
||||
if [ "${completion_status}" -eq 0 ]; then
|
||||
completion_history=true
|
||||
elif [ "${completion_status}" -eq 3 ]; then
|
||||
completion_history=false
|
||||
else
|
||||
echo "Canonical completion history failed for commit ${commit}."
|
||||
status=1
|
||||
completion_history=true
|
||||
fi
|
||||
# Pass the parent as the diff base so the guard compares
|
||||
# contract texts parent-vs-commit; the CI index equals HEAD,
|
||||
# so the default index comparison would misreport contract
|
||||
# updates in earlier commits as insubstantial.
|
||||
if ! git diff-tree --no-commit-id --name-only -r "${commit}" \
|
||||
| PULSE_ALLOW_CONTRACT_NEUTRAL_COMMIT="${reason}" python3 scripts/release_control/canonical_completion_guard.py \
|
||||
--files-from-stdin --diff-base "${commit}^" --commit "${commit}"; then
|
||||
echo "Canonical completion guard failed for commit ${commit}."
|
||||
status=1
|
||||
if [ "${completion_history}" != true ]; then
|
||||
if ! git diff-tree --no-commit-id --name-only -r "${commit}" \
|
||||
| PULSE_ALLOW_CONTRACT_NEUTRAL_COMMIT="${reason}" python3 scripts/release_control/canonical_completion_guard.py \
|
||||
--files-from-stdin --diff-base "${commit}^" --commit "${commit}"; then
|
||||
echo "Canonical completion guard failed for commit ${commit}."
|
||||
status=1
|
||||
fi
|
||||
fi
|
||||
if ! git diff-tree --no-commit-id --name-only -r "${commit}" \
|
||||
| python3 scripts/release_control/browser_verification_guard.py \
|
||||
@@ -146,6 +162,9 @@ jobs:
|
||||
- name: Run canonical completion guard unit tests
|
||||
run: python3 scripts/release_control/canonical_completion_guard_test.py
|
||||
|
||||
- name: Run canonical completion history unit tests
|
||||
run: python3 scripts/release_control/canonical_completion_history_test.py
|
||||
|
||||
- name: Run browser verification guard unit tests
|
||||
run: python3 scripts/release_control/browser_verification_guard_test.py
|
||||
|
||||
|
||||
@@ -224,6 +224,9 @@ scripts/release_control/
|
||||
scripts/release_control/*
|
||||
!scripts/release_control/canonical_completion_guard.py
|
||||
!scripts/release_control/canonical_completion_guard_test.py
|
||||
!scripts/release_control/canonical_completion_history.json
|
||||
!scripts/release_control/canonical_completion_history.py
|
||||
!scripts/release_control/canonical_completion_history_test.py
|
||||
!scripts/release_control/commercial_cancellation_reactivation_proof.py
|
||||
!scripts/release_control/commercial_cancellation_reactivation_rehearsal.py
|
||||
!scripts/release_control/contract_audit.py
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"version": 1,
|
||||
"completions": [
|
||||
{
|
||||
"incomplete_commit": "a2dbb27a68463adb02b614e9927174543f5b7799",
|
||||
"completion_commit": "5f99f5c13fcc547e5e34286dfe873415a4876cfa",
|
||||
"reason": "Preserve the reviewed diagnostic-redaction commit while evaluating its separately reviewed contracts and API proof as one historical completion unit."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate exact, reviewed canonical-completion pairs without rewriting history."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
REGISTRY = Path(__file__).with_name("canonical_completion_history.json")
|
||||
SHA_PATTERN = re.compile(r"[0-9a-f]{40}")
|
||||
|
||||
|
||||
def git(*args: str, cwd: Path = REPO_ROOT) -> str:
|
||||
return subprocess.run(
|
||||
["git", *args],
|
||||
cwd=cwd,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
).stdout.rstrip("\n")
|
||||
|
||||
|
||||
def load_completions(path: Path = REGISTRY) -> dict[str, dict[str, str]]:
|
||||
document = json.loads(path.read_text(encoding="utf-8"))
|
||||
if not isinstance(document, dict) or set(document) != {"version", "completions"}:
|
||||
raise ValueError("completion registry must contain only version and completions")
|
||||
if document["version"] != 1 or isinstance(document["version"], bool):
|
||||
raise ValueError("unsupported completion registry version")
|
||||
if not isinstance(document["completions"], list):
|
||||
raise ValueError("completion registry entries must be a list")
|
||||
|
||||
result: dict[str, dict[str, str]] = {}
|
||||
required = {"incomplete_commit", "completion_commit", "reason"}
|
||||
for entry in document["completions"]:
|
||||
if not isinstance(entry, dict) or set(entry) != required:
|
||||
raise ValueError("completion entry has unexpected fields")
|
||||
if not all(isinstance(entry[field], str) and entry[field].strip() for field in required):
|
||||
raise ValueError("completion entry fields must be non-empty strings")
|
||||
incomplete = entry["incomplete_commit"]
|
||||
completion = entry["completion_commit"]
|
||||
if not SHA_PATTERN.fullmatch(incomplete) or not SHA_PATTERN.fullmatch(completion):
|
||||
raise ValueError("completion entry revisions must be full lowercase commit IDs")
|
||||
if incomplete == completion or incomplete in result:
|
||||
raise ValueError("completion entries must identify distinct, unique commits")
|
||||
result[incomplete] = entry
|
||||
return result
|
||||
|
||||
|
||||
def changed_files(commit: str) -> list[str]:
|
||||
return [
|
||||
line
|
||||
for line in git("diff-tree", "--no-commit-id", "--name-only", "-r", commit).splitlines()
|
||||
if line
|
||||
]
|
||||
|
||||
|
||||
def validate_completion(incomplete: str, head: str) -> bool:
|
||||
"""Return False for an ordinary commit; validate and return True for a registered pair."""
|
||||
entry = load_completions().get(incomplete)
|
||||
if entry is None:
|
||||
return False
|
||||
if not SHA_PATTERN.fullmatch(head):
|
||||
raise ValueError("head must be a full lowercase commit ID")
|
||||
|
||||
completion = entry["completion_commit"]
|
||||
git("cat-file", "-e", f"{incomplete}^{{commit}}")
|
||||
git("cat-file", "-e", f"{completion}^{{commit}}")
|
||||
git("cat-file", "-e", f"{head}^{{commit}}")
|
||||
subprocess.run(
|
||||
["git", "merge-base", "--is-ancestor", incomplete, completion],
|
||||
cwd=REPO_ROOT,
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(
|
||||
["git", "merge-base", "--is-ancestor", completion, head],
|
||||
cwd=REPO_ROOT,
|
||||
check=True,
|
||||
)
|
||||
|
||||
files = sorted(set(changed_files(incomplete) + changed_files(completion)))
|
||||
with tempfile.TemporaryDirectory(prefix="pulse-canonical-completion-") as temp:
|
||||
worktree = Path(temp) / "pulse"
|
||||
git("worktree", "add", "--detach", str(worktree), completion)
|
||||
try:
|
||||
guard = worktree / "scripts/release_control/canonical_completion_guard.py"
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(guard),
|
||||
"--files-from-stdin",
|
||||
"--diff-base",
|
||||
f"{incomplete}^",
|
||||
"--commit",
|
||||
completion,
|
||||
],
|
||||
cwd=worktree,
|
||||
input="".join(f"{path}\n" for path in files),
|
||||
text=True,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
raise ValueError(
|
||||
f"registered completion {completion} does not complete {incomplete}"
|
||||
)
|
||||
finally:
|
||||
git("worktree", "remove", "--force", str(worktree))
|
||||
|
||||
print(
|
||||
"Canonical completion history passed "
|
||||
f"({incomplete} completed by {completion}: {entry['reason']})"
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--commit", required=True)
|
||||
parser.add_argument("--head", required=True)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
try:
|
||||
return 0 if validate_completion(args.commit, args.head) else 3
|
||||
except (OSError, ValueError, subprocess.CalledProcessError, json.JSONDecodeError) as error:
|
||||
print(f"Canonical completion history failed: {error}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,32 @@
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from canonical_completion_history import load_completions, validate_completion
|
||||
|
||||
|
||||
class CanonicalCompletionHistoryTest(unittest.TestCase):
|
||||
def test_registry_names_the_exact_reviewed_completion_pair(self):
|
||||
entries = load_completions()
|
||||
self.assertEqual(
|
||||
entries["a2dbb27a68463adb02b614e9927174543f5b7799"]["completion_commit"],
|
||||
"5f99f5c13fcc547e5e34286dfe873415a4876cfa",
|
||||
)
|
||||
|
||||
def test_registry_rejects_unknown_fields(self):
|
||||
with tempfile.TemporaryDirectory() as temp:
|
||||
path = Path(temp) / "history.json"
|
||||
path.write_text(
|
||||
json.dumps({"version": 1, "completions": [], "unexpected": True}),
|
||||
encoding="utf-8",
|
||||
)
|
||||
with self.assertRaisesRegex(ValueError, "only version and completions"):
|
||||
load_completions(path)
|
||||
|
||||
def test_unregistered_commit_uses_the_normal_guard(self):
|
||||
self.assertFalse(validate_completion("0" * 40, "1" * 40))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user