Skip to content
Open
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
61f4389
fix(review): isolate untrusted PR source blobs
seonghobae Jul 16, 2026
9548f66
fix(review): bind approvals and isolate Git metadata
seonghobae Jul 17, 2026
469c9eb
fix(review): fail closed before Noema credentials
seonghobae Jul 17, 2026
9e3508f
fix(review): trust isolated coverage worktree
seonghobae Jul 17, 2026
461ddf2
feat(review): scope organization queue sweeps
seonghobae Jul 17, 2026
171ba46
fix(review): close Strix trust-boundary findings
seonghobae Jul 17, 2026
8702d86
Merge remote-tracking branch 'origin/main' into agent/fix-opencode-un…
seonghobae Jul 17, 2026
448b42d
fix(security): fail closed on incomplete Strix scans
seonghobae Jul 17, 2026
0496b9c
Merge remote-tracking branch 'origin/main' into agent/fix-opencode-un…
seonghobae Jul 17, 2026
1cd2e86
Merge remote-tracking branch 'origin/main' into agent/fix-opencode-un…
seonghobae Jul 17, 2026
e7e4ffd
test(review): prove offline coverage paths
seonghobae Jul 17, 2026
f538d6d
test(review): isolate scheduler actor lookup
seonghobae Jul 17, 2026
0acf47f
style(review): format materializer regression tests
seonghobae Jul 20, 2026
e3c8d45
fix(review): 모델 풀 큐 독점과 형식 재시도 제한
seonghobae Jul 20, 2026
8b575e5
fix(review): bind evidence and pin coverage runners
seonghobae Jul 20, 2026
1a826b7
fix(review): install trusted base Python locks
seonghobae Jul 20, 2026
ac73542
fix(review): close privileged execution boundaries
seonghobae Jul 22, 2026
ad0d616
fix(ci): close review trust-boundary gaps
seonghobae Jul 22, 2026
099ce24
fix(ci): harden trusted review execution
seonghobae Jul 22, 2026
674f9ba
fix(review): preserve descriptor and SHA identity
seonghobae Jul 22, 2026
7e3cbcf
fix(review): harden identity and scope contracts
seonghobae Jul 22, 2026
d153584
fix(ci): harden trusted review tooling
seonghobae Jul 22, 2026
746dcbc
fix(ci): harden Strix source materialization
seonghobae Jul 22, 2026
c7b33d2
fix(ci): anchor review identity evidence
seonghobae Jul 22, 2026
bb55850
fix(ci): diff Dependabot lock from merge base
seonghobae Jul 22, 2026
dcc56ac
test(review): isolate autofix actor lookup
seonghobae Jul 22, 2026
0d66caa
fix(review): anchor OpenCode base identity
seonghobae Jul 22, 2026
b3bb41b
test(ci): cover privileged fail-closed paths
seonghobae Jul 22, 2026
6a6e5d4
fix(autofix): require trusted marker actor
seonghobae Jul 22, 2026
b7fe19c
fix(ci): support coverage artifact reruns
seonghobae Jul 22, 2026
4d58323
fix(ci): ignore malformed autofix markers
seonghobae Jul 22, 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
184 changes: 182 additions & 2 deletions .github/workflows/noema-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ run-name: >-

on:
pull_request_target:
types: [opened, synchronize, reopened, ready_for_review, closed]
types: [opened, synchronize, reopened, ready_for_review, edited, closed]
workflow_run:
workflows: ["Required OpenCode Review", "Strix Security Scan"]
types: [completed]
Expand Down Expand Up @@ -66,6 +66,96 @@ jobs:
run: |
echo "::notice::Noema review skipped: no pull request number is associated with this event."

- name: Bind Noema inputs to the live organization pull request
if: env.PR_NUMBER != ''
id: live_pr
env:
GH_TOKEN: ${{ github.token }}
EVENT_NAME: ${{ github.event_name }}
DISPATCH_ACTOR: ${{ github.triggering_actor }}
DISPATCH_SENDER: ${{ github.event.sender.login || '' }}
ALLOWED_DISPATCH_ACTOR: ${{ vars.NOEMA_REPOSITORY_DISPATCH_ACTOR || '' }}
ALLOWED_DISPATCH_TARGETS: ${{ vars.NOEMA_REPOSITORY_DISPATCH_TARGETS || '' }}
SUPPLIED_BASE_REF: ${{ github.event.client_payload.pr_base_ref || '' }}
SUPPLIED_BASE_SHA: ${{ github.event.client_payload.pr_base_sha || '' }}
SUPPLIED_HEAD_REF: ${{ github.event.client_payload.pr_head_ref || '' }}
SUPPLIED_HEAD_SHA: ${{ github.event.client_payload.pr_head_sha || '' }}
run: |
set -euo pipefail

fail_validation() {
echo "::error::$1"
exit 1
}

if [[ ! "$TARGET_REPOSITORY" =~ ^ContextualWisdomLab/[A-Za-z0-9_.-]+$ ]]; then
fail_validation "Noema target repository must be an exact ContextualWisdomLab repository name."
fi
if [[ ! "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]]; then
fail_validation "Noema pull request number must be a positive integer."
fi

if [ "$EVENT_NAME" = "repository_dispatch" ]; then
if [ -z "$ALLOWED_DISPATCH_ACTOR" ]; then
fail_validation "NOEMA_REPOSITORY_DISPATCH_ACTOR must be configured before repository_dispatch can mint reviewer credentials."
fi
if [ "$DISPATCH_ACTOR" != "$ALLOWED_DISPATCH_ACTOR" ] || [ "$DISPATCH_SENDER" != "$ALLOWED_DISPATCH_ACTOR" ]; then
fail_validation "Noema repository_dispatch actor and sender must exactly match the configured identity."
fi
target_allowed=false
IFS=',' read -ra allowed_targets <<<"$ALLOWED_DISPATCH_TARGETS"
for allowed_target in "${allowed_targets[@]}"; do
if [ "$TARGET_REPOSITORY" = "${allowed_target//[[:space:]]/}" ]; then
target_allowed=true
break
fi
done
if [ "$target_allowed" != true ]; then
fail_validation "Noema repository_dispatch target is not present in NOEMA_REPOSITORY_DISPATCH_TARGETS."
fi
fi

if ! live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"; then
fail_validation "Noema could not load the requested live pull request."
fi
state="$(jq -r '.state // empty' <<<"$live_pr")"
base_repository="$(jq -r '.base.repo.full_name // empty' <<<"$live_pr")"
head_repository="$(jq -r '.head.repo.full_name // empty' <<<"$live_pr")"
base_ref="$(jq -r '.base.ref // empty' <<<"$live_pr")"
base_sha="$(jq -r '.base.sha // empty' <<<"$live_pr")"
head_ref="$(jq -r '.head.ref // empty' <<<"$live_pr")"
head_sha="$(jq -r '.head.sha // empty' <<<"$live_pr")"

if [ "$state" != "open" ]; then
fail_validation "Noema only reviews open pull requests."
fi
if [ "$base_repository" != "$TARGET_REPOSITORY" ] || [ "$head_repository" != "$TARGET_REPOSITORY" ]; then
fail_validation "Noema reviewer credentials are restricted to same-repository pull requests."
fi
if [[ ! "$base_ref" =~ ^[A-Za-z0-9._/-]+$ ]] || [[ ! "$head_ref" =~ ^[A-Za-z0-9._/-]+$ ]]; then
fail_validation "Noema live pull request refs are invalid."
fi
if [[ ! "$base_sha" =~ ^[0-9a-fA-F]{40}$ ]] || [[ ! "$head_sha" =~ ^[0-9a-fA-F]{40}$ ]]; then
fail_validation "Noema live pull request SHAs are invalid."
fi

if [ "$EVENT_NAME" = "repository_dispatch" ]; then
if [ "$SUPPLIED_BASE_REF" != "$base_ref" ] || [ "$SUPPLIED_BASE_SHA" != "$base_sha" ] || \
[ "$SUPPLIED_HEAD_REF" != "$head_ref" ] || [ "$SUPPLIED_HEAD_SHA" != "$head_sha" ]; then
fail_validation "Noema repository_dispatch payload does not match the live base/head identity."
fi
fi

{
echo "target_repository=$TARGET_REPOSITORY"
echo "repository_name=${TARGET_REPOSITORY#*/}"
echo "pr_number=$PR_NUMBER"
echo "base_ref=$base_ref"
echo "base_sha=$base_sha"
echo "head_ref=$head_ref"
echo "head_sha=$head_sha"
} >>"$GITHUB_OUTPUT"

- name: Resolve trusted Noema review source ref
if: env.PR_NUMBER != ''
id: trusted_source
Expand Down Expand Up @@ -139,6 +229,7 @@ jobs:
if: env.PR_NUMBER != ''
id: noema_credential
env:
TARGET_REPOSITORY: ${{ steps.live_pr.outputs.target_repository }}
NOEMA_GITHUB_APP_CLIENT_ID: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID || '' }}
NOEMA_GITHUB_APP_PRIVATE_KEY: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY || '' }}
TOKEN_EXCHANGE_URL: ${{ vars.NOEMA_TOKEN_EXCHANGE_URL || vars.NOEMA_EXCHANGE_URL || '' }}
Expand Down Expand Up @@ -182,7 +273,7 @@ jobs:
client-id: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID }}
private-key: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY }}
owner: ContextualWisdomLab
repositories: ${{ steps.noema_credential.outputs.repository }}
repositories: ${{ steps.live_pr.outputs.repository_name }}
permission-actions: read
permission-checks: read
permission-contents: read
Expand All @@ -196,6 +287,7 @@ jobs:
if: env.PR_NUMBER != '' && steps.noema_credential.outputs.source == 'oidc'
id: noema_oidc_token
env:
TARGET_REPOSITORY: ${{ steps.live_pr.outputs.target_repository }}
OIDC_AUDIENCE: ${{ vars.NOEMA_OIDC_AUDIENCE || 'cwl-noema-review' }}
TOKEN_EXCHANGE_URL: ${{ vars.NOEMA_TOKEN_EXCHANGE_URL || vars.NOEMA_EXCHANGE_URL || '' }}
run: |
Expand Down Expand Up @@ -250,14 +342,102 @@ jobs:
echo "::add-mask::$app_token"
echo "token=$app_token" >>"$GITHUB_OUTPUT"

- name: Materialize exact OpenCode approval evidence
if: env.PR_NUMBER != ''
id: primary_approval_evidence
env:
GH_TOKEN: ${{ secrets.NOEMA_REVIEW_TOKEN || steps.noema_github_app_token.outputs.token || steps.noema_oidc_token.outputs.token }}
GH_REPOSITORY: ${{ steps.live_pr.outputs.target_repository }}
PR_BASE_REF: ${{ steps.live_pr.outputs.base_ref }}
PR_BASE_SHA: ${{ steps.live_pr.outputs.base_sha }}
PR_HEAD_REF: ${{ steps.live_pr.outputs.head_ref }}
PR_HEAD_SHA: ${{ steps.live_pr.outputs.head_sha }}
OPENCODE_SOURCE_GIT_DIR: ${{ runner.temp }}/noema-pr-objects.git
OPENCODE_SOURCE_WORKDIR: ${{ runner.temp }}/noema-pr-head
OPENCODE_SOURCE_MANIFEST: ${{ runner.temp }}/noema-pr-source-manifest.json
OPENCODE_CHANGED_FILES_FILE: ${{ runner.temp }}/opencode-changed-files.txt
OPENCODE_ARTIFACT_MANIFEST_FILE: ${{ runner.temp }}/opencode-artifact-manifest.json
run: |
set -euo pipefail
if [ -z "${GH_TOKEN:-}" ]; then
echo "::error::Noema reviewer token is unavailable for exact approval evidence."
exit 1
fi
git check-ref-format "refs/heads/$PR_BASE_REF"
git check-ref-format "refs/heads/$PR_HEAD_REF"
gh auth setup-git
rm -rf "$OPENCODE_SOURCE_GIT_DIR" "$OPENCODE_SOURCE_WORKDIR"
rm -f "$OPENCODE_SOURCE_MANIFEST" "$OPENCODE_CHANGED_FILES_FILE" \
"$OPENCODE_ARTIFACT_MANIFEST_FILE"
git init --bare "$OPENCODE_SOURCE_GIT_DIR"
git --git-dir="$OPENCODE_SOURCE_GIT_DIR" remote add pr-source \
"$GITHUB_SERVER_URL/$GH_REPOSITORY.git"
git --git-dir="$OPENCODE_SOURCE_GIT_DIR" fetch --no-tags --no-recurse-submodules \
pr-source "+refs/heads/${PR_BASE_REF}:refs/remotes/pr-source/base"
git --git-dir="$OPENCODE_SOURCE_GIT_DIR" fetch --no-tags --no-recurse-submodules \
pr-source "+refs/heads/${PR_HEAD_REF}:refs/remotes/pr-source/head"
[ "$(git --git-dir="$OPENCODE_SOURCE_GIT_DIR" rev-parse refs/remotes/pr-source/base)" = "$PR_BASE_SHA" ]
[ "$(git --git-dir="$OPENCODE_SOURCE_GIT_DIR" rev-parse refs/remotes/pr-source/head)" = "$PR_HEAD_SHA" ]
git --git-dir="$OPENCODE_SOURCE_GIT_DIR" update-ref refs/heads/noema-review "$PR_HEAD_SHA"
git --git-dir="$OPENCODE_SOURCE_GIT_DIR" symbolic-ref HEAD refs/heads/noema-review
python3 scripts/ci/materialize_pr_review_source.py \
--git-dir "$OPENCODE_SOURCE_GIT_DIR" \
--head-sha "$PR_HEAD_SHA" \
--output-dir "$OPENCODE_SOURCE_WORKDIR" \
--manifest "$OPENCODE_SOURCE_MANIFEST"
merge_base="$(git -C "$OPENCODE_SOURCE_WORKDIR" merge-base "$PR_BASE_SHA" "$PR_HEAD_SHA")"
git -C "$OPENCODE_SOURCE_WORKDIR" diff --name-only --find-renames \
"$merge_base" "$PR_HEAD_SHA" >"$OPENCODE_CHANGED_FILES_FILE"
if [ ! -s "$OPENCODE_CHANGED_FILES_FILE" ]; then
echo "::error::Noema cannot validate an approval without a non-empty exact-head changed-file manifest."
exit 1
fi
python3 <<'PY'
import hashlib
import json
import os
from pathlib import Path

runner_temp = Path(os.environ["RUNNER_TEMP"]).resolve(strict=True)
changed_files = Path(os.environ["OPENCODE_CHANGED_FILES_FILE"]).resolve(strict=True)
if changed_files != runner_temp / "opencode-changed-files.txt":
raise SystemExit("changed-file evidence escaped runner temp")
changed_files.chmod(0o600)
manifest = Path(os.environ["OPENCODE_ARTIFACT_MANIFEST_FILE"])
manifest.write_text(
json.dumps(
{
"schema": 1,
"artifacts": {
changed_files.name: hashlib.sha256(
changed_files.read_bytes()
).hexdigest()
},
},
sort_keys=True,
),
encoding="utf-8",
)
manifest.chmod(0o600)
digest = hashlib.sha256(manifest.read_bytes()).hexdigest()
with Path(os.environ["GITHUB_OUTPUT"]).open("a", encoding="utf-8") as output:
output.write(f"manifest_sha256={digest}\n")
PY

- name: Run Noema LLM review and submit verdict
if: env.PR_NUMBER != ''
env:
TARGET_REPOSITORY: ${{ steps.live_pr.outputs.target_repository }}
PR_NUMBER: ${{ steps.live_pr.outputs.pr_number }}
GH_TOKEN: ${{ secrets.NOEMA_REVIEW_TOKEN || steps.noema_github_app_token.outputs.token || steps.noema_oidc_token.outputs.token }}
NOEMA_REVIEW_TOKEN_SOURCE: ${{ steps.noema_credential.outputs.source == 'pat' && 'noema-review-pat' || steps.noema_credential.outputs.source == 'github-app' && 'noema-review-github-app' || 'noema-review-app-oidc' }}
NOEMA_LLM_API_URL: ${{ vars.NOEMA_LLM_API_URL || '' }}
NOEMA_LLM_MODEL: ${{ vars.NOEMA_LLM_MODEL || '' }}
NOEMA_LLM_API_KEY: ${{ secrets.NOEMA_LLM_API_KEY || secrets.OPENAI_API_KEY || '' }}
OPENCODE_REQUIRE_ADVERSARIAL_VALIDATION: "true"
OPENCODE_SOURCE_WORKDIR: ${{ runner.temp }}/noema-pr-head
OPENCODE_CHANGED_FILES_FILE: ${{ runner.temp }}/opencode-changed-files.txt
OPENCODE_ARTIFACT_MANIFEST_SHA256: ${{ steps.primary_approval_evidence.outputs.manifest_sha256 }}
run: |
set -euo pipefail
if [ -z "${PR_NUMBER:-}" ]; then
Expand Down
Loading
Loading