Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
05b52e7
fix(opencode): use same-repo status credential
seonghobae Aug 22, 2026
5974bee
merge: bring #1227 onto current main without dropping same-repo statu…
seonghobae Aug 23, 2026
1a197fe
Merge remote-tracking branch 'origin/main' into fix/opencode-same-rep…
claude Aug 30, 2026
c959957
Merge remote-tracking branch 'origin/main' into fix/opencode-same-rep…
claude Aug 30, 2026
95e93f5
Merge remote-tracking branch 'origin/main' into fix-opencode-same-rep…
claude Sep 1, 2026
b910a15
fix(test): pin the recomputed review-dispatch blob SHA after the merge
claude Sep 1, 2026
199ef94
Merge remote-tracking branch 'origin/main' into fix-opencode-same-rep…
claude Sep 1, 2026
c1b4075
test(ci): close main's post-#1546 scheduler coverage regression
claude Sep 1, 2026
9b5fc06
test(ci): document nested REST fixture helpers
seonghobae Sep 1, 2026
933cf53
fix(tests): drain dispatch fixture stdin to break CI dependency cycle
seonghobae Sep 1, 2026
cb3fc86
Merge remote-tracking branch 'origin/main' into fix-opencode-same-rep…
claude Sep 1, 2026
547fcc8
test(ci,noema): repair stale #1564 fixtures (#1599)
seonghobae Sep 1, 2026
5f81d8e
fix(strix): keep model preflight timeout positive (#1601)
seonghobae Sep 1, 2026
6eb93bc
fix(ci): align review-repair quality gate with contextual orchestrato…
seonghobae Sep 1, 2026
f59bad1
fix(strix): preserve unbounded orchestrator inference on 1.5.3 (#1604)
seonghobae Sep 1, 2026
5d1b9b2
fix(sbom): enforce hourly non-fork commercial inventory (#1603)
seonghobae Sep 1, 2026
c70b081
test(strix): align installer publication contract with GITHUB_ENV (#1…
seonghobae Sep 1, 2026
196deb8
test(ci): add one-shot scheduler runner TDD repair
seonghobae Sep 1, 2026
452f4ba
ci: trigger merge scheduler runner TDD repair
seonghobae Sep 1, 2026
dcd739b
fix(ci): pin merge scheduler to ubuntu-24.04
opencode-agent[bot] Sep 1, 2026
c4f3ba3
fix(ci): pin merge scheduler queue-drain jobs to ubuntu-24.04 (#1609)
seonghobae Sep 1, 2026
a86177e
test(strix): restore compatibility entrypoint coverage (#1610)
seonghobae Sep 1, 2026
fc335f8
fix(noema): isolate trusted review head bindings (#1500)
seonghobae Sep 1, 2026
7ffb771
fix(docs): remove fabricated owner authorization claims (#1478)
seonghobae Sep 1, 2026
94ca307
Merge remote-tracking branch 'origin/main' into fix-opencode-same-rep…
claude Sep 1, 2026
5c7eb11
Merge branch 'main' into fix/opencode-same-repo-status-token
opencode-agent[bot] Sep 1, 2026
aa150dd
Merge pull request #1227 from ContextualWisdomLab/fix/opencode-same-r…
seonghobae Sep 1, 2026
4349658
perf(review): bound verification label scanning (#1615)
seonghobae Sep 1, 2026
176ae54
fix(actions): pin required security runners to Ubuntu 24.04 (#1618)
seonghobae Sep 1, 2026
827a6c9
fix(noema): refresh reviewer App token before publication (#1616)
seonghobae Sep 1, 2026
cb38cc3
feat(metadata): reconcile fleet repository public surfaces
seonghobae Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
262 changes: 262 additions & 0 deletions .github/actions/noema-review/two_phase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
#!/usr/bin/env python3
"""Prepare and publish Noema verdicts across short-lived reviewer credentials.

The model phase can legitimately outlive a one-hour GitHub App installation
credential. This trusted helper therefore seals the already validated model
verdict to a runner-local file, then a later workflow step reopens that file
only after the reviewer credential has been refreshed. Publication always
re-fetches the live pull request and verifies its exact head and base before
submitting any review evidence.
"""

from __future__ import annotations

import argparse
import json
import os
import re
import stat
import sys
from pathlib import Path
from typing import Any

ROOT = Path(__file__).resolve().parents[3]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))

from scripts.ci import noema_review_gate as gate # noqa: E402

ENVELOPE_SCHEMA_VERSION = 1
MAX_ENVELOPE_BYTES = 2 * 1024 * 1024


def _canonical_head(value: str) -> str:
"""Return one canonical lowercase Git SHA or fail closed."""
head = value.strip().lower()
if not re.fullmatch(r"[0-9a-f]{40}", head):
raise RuntimeError("Noema two-phase handoff requires a canonical 40-character Git SHA")
return head


def _canonical_base(pull_request: dict[str, Any]) -> str:
"""Return the exact base commit that defined the reviewed diff/context."""
base = str(pull_request.get("baseRefOid") or "").strip().lower()
if not re.fullmatch(r"[0-9a-f]{40}", base):
raise RuntimeError("Noema two-phase handoff requires a canonical 40-character base SHA")
return base


def _reviewer_actor() -> str:
"""Return a verified independent reviewer actor for the active token."""
actor = gate.current_actor()
if not actor:
raise RuntimeError("Noema reviewer identity could not be verified")
if actor in gate.PRIMARY_REVIEW_AUTHORS:
raise RuntimeError(
f"Current token actor {actor!r} is already a primary review actor; "
"Noema requires an independent reviewer credential."
)
return actor


def _write_envelope(path: Path, payload: dict[str, Any]) -> None:
"""Create one private, non-following runner-local verdict envelope."""
encoded = (json.dumps(payload, separators=(",", ":"), sort_keys=True) + "\n").encode("utf-8")
if len(encoded) > MAX_ENVELOPE_BYTES:
raise RuntimeError("Noema verdict envelope exceeds the bounded handoff size")
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
if hasattr(os, "O_NOFOLLOW"):
flags |= os.O_NOFOLLOW
fd = os.open(path, flags, 0o600)
try:
file_stat = os.fstat(fd)
if not stat.S_ISREG(file_stat.st_mode) or file_stat.st_nlink != 1:
raise RuntimeError("Noema verdict envelope target is not a private regular file")
view = memoryview(encoded)
written = 0
while written < len(view):
count = os.write(fd, view[written:])
if count <= 0:
raise RuntimeError("Noema verdict envelope write made no forward progress")
written += count
os.fsync(fd)
except BaseException:
os.close(fd)
path.unlink(missing_ok=True)
raise
else:
os.close(fd)


def _read_envelope(path: Path) -> dict[str, Any]:
"""Read and validate one sealed runner-local verdict envelope."""
flags = os.O_RDONLY
if hasattr(os, "O_NOFOLLOW"):
flags |= os.O_NOFOLLOW
try:
fd = os.open(path, flags)
except OSError as exc:
raise RuntimeError("Noema verdict envelope is unavailable for publication") from exc
try:
file_stat = os.fstat(fd)
if not stat.S_ISREG(file_stat.st_mode) or file_stat.st_nlink != 1:
raise RuntimeError("Noema verdict envelope is not a regular single-link file")
if file_stat.st_mode & 0o077:
raise RuntimeError("Noema verdict envelope permissions are broader than owner-only")
if file_stat.st_size <= 0 or file_stat.st_size > MAX_ENVELOPE_BYTES:
raise RuntimeError("Noema verdict envelope size is outside the bounded contract")
chunks: list[bytes] = []
remaining = MAX_ENVELOPE_BYTES + 1
while remaining > 0:
chunk = os.read(fd, min(65536, remaining))
if not chunk:
break
chunks.append(chunk)
remaining -= len(chunk)
raw = b"".join(chunks)
if len(raw) > MAX_ENVELOPE_BYTES:
raise RuntimeError("Noema verdict envelope exceeded the bounded read limit")
finally:
os.close(fd)
try:
payload = json.loads(raw.decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise RuntimeError("Noema verdict envelope is malformed") from exc
if not isinstance(payload, dict):
raise RuntimeError("Noema verdict envelope root must be an object")
return payload


def prepare_verdict(repo: str, number: int, expected_head: str, path: Path) -> int:
"""Run model review and seal its verdict without publishing GitHub evidence."""
expected = _canonical_head(expected_head)
pull_request = gate.fetch_pr(repo, number)
try:
gate.require_expected_head(pull_request, expected)
except RuntimeError:
print("Pull request is closed or stale; Noema verdict preparation skipped.")
return 0
expected_base = _canonical_base(pull_request)
actor = _reviewer_actor()
if pull_request.get("isDraft"):
print("PR is draft; Noema verdict preparation skipped.")
return 0
if gate.existing_noema_review(pull_request, actor):
print("Current head already has a Noema review; verdict preparation skipped.")
return 0

diff, truncated = gate.fetch_diff(repo, number)
changed_files = gate.fetch_changed_files(repo, number)
changed_paths = tuple(file_path for file_path, _status in changed_files)
review_context = gate.build_review_context(repo, number, pull_request, changed_files)
try:
verdict = gate.call_llm(
repo,
number,
pull_request,
diff,
truncated,
expected,
review_context,
changed_paths,
)
except gate.StaleHeadDuringRepairRetryError:
print("Pull request head changed during model repair retry; verdict was not sealed.")
return 0

_write_envelope(
path,
{
"schema_version": ENVELOPE_SCHEMA_VERSION,
"repository": repo,
"pull_request_number": number,
"expected_head": expected,
"expected_base": expected_base,
"verdict": verdict,
},
)
print(
f"Prepared Noema verdict for {repo}#{number} at head {expected} / base {expected_base}; "
"publication is deferred."
)
return 0


def publish_verdict(repo: str, number: int, expected_head: str, path: Path) -> int:
"""Publish a prepared verdict only with fresh exact-head/base reviewer authority."""
expected = _canonical_head(expected_head)
try:
payload = _read_envelope(path)
required_keys = {
"schema_version",
"repository",
"pull_request_number",
"expected_head",
"expected_base",
"verdict",
}
if set(payload) != required_keys:
raise RuntimeError("Noema verdict envelope fields do not match the trusted schema")
if payload["schema_version"] != ENVELOPE_SCHEMA_VERSION:
raise RuntimeError("Noema verdict envelope schema version is unsupported")
if payload["repository"] != repo or payload["pull_request_number"] != number:
raise RuntimeError("Noema verdict envelope target identity does not match publication")
if payload["expected_head"] != expected:
raise RuntimeError("Noema verdict envelope head does not match publication")
expected_base = str(payload["expected_base"]).strip().lower()
if not re.fullmatch(r"[0-9a-f]{40}", expected_base):
raise RuntimeError("Noema verdict envelope base does not contain a canonical Git SHA")
verdict = payload["verdict"]
if not isinstance(verdict, dict):
raise RuntimeError("Noema verdict envelope verdict must be an object")

current_pull_request = gate.fetch_pr(repo, number)
try:
gate.require_expected_head(current_pull_request, expected)
except RuntimeError:
print("Pull request closed or advanced after model review; prepared verdict was not published.")
return 0
if _canonical_base(current_pull_request) != expected_base:
print("Pull request base advanced after model review; stale prepared verdict was not published.")
return 0
actor = _reviewer_actor()
if current_pull_request.get("isDraft"):
print("PR became draft after model review; prepared verdict was not published.")
return 0
if gate.existing_noema_review(current_pull_request, actor):
print("Current head already has a Noema review; duplicate publication skipped.")
return 0
gate.submit_review(repo, number, current_pull_request, actor, verdict)
return 0
finally:
path.unlink(missing_ok=True)


def parse_args(argv: list[str]) -> argparse.Namespace:
"""Parse the trusted two-phase handoff command line."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo", required=True)
parser.add_argument("--pr-number", required=True, type=int)
parser.add_argument("--expected-head", required=True)
modes = parser.add_mutually_exclusive_group(required=True)
modes.add_argument("--prepare-verdict-file", type=Path)
modes.add_argument("--publish-verdict-file", type=Path)
return parser.parse_args(argv)


def main(argv: list[str]) -> int:
"""Execute the selected prepare or publication phase."""
args = parse_args(argv)
if args.pr_number <= 0:
raise SystemExit("--pr-number must be positive")
if args.prepare_verdict_file is not None:
return prepare_verdict(args.repo, args.pr_number, args.expected_head, args.prepare_verdict_file)
return publish_verdict(args.repo, args.pr_number, args.expected_head, args.publish_verdict_file)


if __name__ == "__main__":
try:
raise SystemExit(main(sys.argv[1:]))
except RuntimeError as exc:
print(f"::error::{exc}", file=sys.stderr)
raise SystemExit(1) from exc
25 changes: 20 additions & 5 deletions .github/workflows/hourly-nvidia-nim-review-repair.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
name: Hourly NVIDIA NIM Review Repair
name: Contextual Orchestrator Review Repair Quality CI

# Compatibility boundary: keep this historical file path so the existing GitHub
# Actions workflow registry identity is updated in place instead of leaving an
# orphaned enabled workflow ID. The display name and executable responsibility
# are authoritative: this is a read-only PR/push quality gate, not an hourly
# writer and not a direct NVIDIA NIM executor.
#
# Hourly execution is owned by the thin product callers and the reusable
# scheduler; write-capable repair is owned by pr-review-autofix.yml, whose model
# execution is routed through contextual-orchestrator/orchestrator/free.
on:
pull_request:
paths:
Expand Down Expand Up @@ -32,6 +41,9 @@ on:
- tests/test_contextual_orchestrator_review_sidecar_contract.py
- docs/doctoring/contextual-orchestrator-vendored-sidecar.md
- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md
- docs/doctoring/review-repair-quality-workflow-identity.md
- docs/product-technical-gap-baseline.md
- CHANGELOG.md
- tests/test_bandscope_hourly_review_caller.py
- tests/test_disksage_hourly_review_caller.py
- tests/test_inkspan_hourly_review_caller.py
Expand Down Expand Up @@ -106,6 +118,9 @@ on:
- tests/test_contextual_orchestrator_review_sidecar_contract.py
- docs/doctoring/contextual-orchestrator-vendored-sidecar.md
- docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md
- docs/doctoring/review-repair-quality-workflow-identity.md
- docs/product-technical-gap-baseline.md
- CHANGELOG.md
- tests/test_bandscope_hourly_review_caller.py
- tests/test_disksage_hourly_review_caller.py
- tests/test_inkspan_hourly_review_caller.py
Expand Down Expand Up @@ -154,12 +169,12 @@ permissions:
contents: read

concurrency:
group: hourly-nvidia-nim-review-repair-${{ github.event.pull_request.number || github.ref }}
group: contextual-orchestrator-review-repair-quality-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
contract:
name: Hourly cadence, immutable source, NIM credential, and conflict scope
name: Scheduler, contextual-orchestrator, writer, and conflict-scope contracts
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
Expand All @@ -180,7 +195,7 @@ jobs:
run: >-
python -m pip install --disable-pip-version-check --require-hashes
-r requirements-opencode-review-ci-hashes.txt
- name: Verify hourly scheduler and NVIDIA NIM autofix contracts
- name: Verify scheduler and contextual-orchestrator review-repair contracts
run: |
set -euo pipefail
python -m pytest -q \
Expand Down Expand Up @@ -232,4 +247,4 @@ jobs:
tests/test_pr_review_autofix_context_head_binding.py \
tests/test_pr_review_autofix_nvidia_nim_contract.py \
tests/test_pr_review_autofix_writer_security_contract.py
git diff --check
git diff --check
Loading
Loading