From faf178b8fd3493a143c8bb098eb409351b68902f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 01:35:14 +0900 Subject: [PATCH 1/9] test(agent-mention): require cross-repository dispatch credential --- tests/test_agent_mention_downstream_idempotency.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_agent_mention_downstream_idempotency.py b/tests/test_agent_mention_downstream_idempotency.py index 23634f293d..42e6531dd5 100644 --- a/tests/test_agent_mention_downstream_idempotency.py +++ b/tests/test_agent_mention_downstream_idempotency.py @@ -19,7 +19,8 @@ def test_router_can_read_durable_central_artifacts() -> None: assert "permissions:\n actions: read" in local assert "permissions:\n actions: read" in sweep assert "AGENT_DISPATCH_TOKEN: ${{ github.token }}" in local - assert "AGENT_DISPATCH_TOKEN: ${{ github.token }}" in sweep + assert "AGENT_DISPATCH_TOKEN: ${{ github.token }}" not in sweep + assert 'export AGENT_DISPATCH_TOKEN="$TARGET_REPOSITORY_TOKEN"' in sweep def test_downstream_workflows_claim_artifacts_and_bind_exact_key() -> None: From 32767bfc304e95ad46fb8031b75f10059062e801 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 01:35:53 +0900 Subject: [PATCH 2/9] fix(agent-mention): dispatch sibling reviews with selected credential --- .github/workflows/agent-mention-router.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/agent-mention-router.yml b/.github/workflows/agent-mention-router.yml index 43fb163975..3a388aa66a 100644 --- a/.github/workflows/agent-mention-router.yml +++ b/.github/workflows/agent-mention-router.yml @@ -158,7 +158,6 @@ jobs: env: PR_REVIEW_MERGE_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }} OPENCODE_APPROVE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN }} - AGENT_DISPATCH_TOKEN: ${{ github.token }} run: | set -euo pipefail if [ -n "$PR_REVIEW_MERGE_TOKEN" ]; then @@ -172,6 +171,7 @@ jobs: TARGET_REPOSITORY_SOURCE="${TARGET_REPOSITORY_TOKEN:+installation}" fi export TARGET_REPOSITORY_TOKEN + export AGENT_DISPATCH_TOKEN="$TARGET_REPOSITORY_TOKEN" if [ -z "$TARGET_REPOSITORY_TOKEN" ] || [ -z "$TARGET_REPOSITORY_SOURCE" ]; then echo "::error::Agent mention sweep requires PR_REVIEW_MERGE_TOKEN, OPENCODE_APPROVE_TOKEN, or the OpenCode app token exchange." exit 1 From b092040abef7d6259536e85c7975e5e47463bb1a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 01:36:36 +0900 Subject: [PATCH 3/9] docs(agent-mention): correct dispatch credential boundary --- docs/automation/review-agent-comment-invocation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/automation/review-agent-comment-invocation.md b/docs/automation/review-agent-comment-invocation.md index a886caa967..6cf747afa1 100644 --- a/docs/automation/review-agent-comment-invocation.md +++ b/docs/automation/review-agent-comment-invocation.md @@ -1,6 +1,6 @@ # Review-agent comment invocation -Updated: 2026-08-22 +Updated: 2026-09-02 ## Purpose @@ -45,7 +45,7 @@ This preserves the central MSA boundary without copying privileged workflow code - The two agent-specific wrapper workflows receive only job-scoped `actions: read` and `contents: write`; their workflow defaults remain `contents: read`. - `actions: read` permits exact-name artifact inventory checks. Artifact upload uses the workflow artifact service and is pinned to immutable `actions/upload-artifact` v7.0.1. - `contents: write` is intentionally retained only on jobs that call GitHub's create-repository-dispatch endpoint. GitHub documents that endpoint as requiring Contents repository permission at write level. Removing it would disable the bounded central dispatch path; broad workflow-default write access is not granted. -- The organization sweep uses the established cross-repository credential chain for reading target comments, while the central repository's own short-lived job token dispatches the central workflows. +- The organization sweep uses the same established cross-repository credential selected for target-repository reads when it dispatches the central review workflows. A repository-scoped `GITHUB_TOKEN` from `ContextualWisdomLab/.github` is not used as sibling-repository dispatch authority. - OpenCode dispatch is restricted to the exact `OPENCODE_REPOSITORY_DISPATCH_TARGETS` allowlist. - An invocation cannot merge: `enable_auto_merge=false`, `update_branches=false`, and `merge_mode=disabled` are bound into the OpenCode invocation claim and hardcoded in the wrapper. GitHub's create-repository-dispatch endpoint allows at most 10 top-level `client_payload` properties (HTTP 422 otherwise), so those review-only constants are not copied onto the first-hop mention payload. The wrapper's merge-scheduler forward keeps those three flags and explicitly sends `trigger_reviews=true`, together with repository, PR, head/base SHA, base branch, and the invocation key. The source comment remains bound and auditable in the verified invocation claim and durable ledger; it is not repeated to the scheduler, which does not consume it. - Every dispatch is bound to live PR number, current head SHA, base branch, source comment, requested agent, and requesting actor metadata fetched or validated immediately before dispatch. @@ -73,7 +73,7 @@ Rollback is deletion of the four mention-router workflows, the two Python helper ## References -GitHub. (n.d.). *Available rules for rulesets*. GitHub Docs. Retrieved August 6, 2026, from https://docs.github.com/en/enterprise-cloud@latest/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets +GitHub. (n.d.). *Available rules for rulesets*. GitHub Docs. Retrieved August 6, 2026, from https://docs.github.com/en/enterprise-cloud@latest/repositories/configuring-branches-and-merges/in-your-repository/managing-rulesets/available-rules-for-rulesets GitHub. (n.d.). *Events that trigger workflows*. GitHub Docs. Retrieved August 6, 2026, from https://docs.github.com/en/enterprise-cloud@latest/actions/reference/workflows-and-actions/events-that-trigger-workflows From 7ca6ecf4fc136afd01e1311552bd6c958bd2c7b7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 02:00:53 +0900 Subject: [PATCH 4/9] test(agent-mention): align sweep credential contract --- tests/test_agent_mention_workflow_contract.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_agent_mention_workflow_contract.py b/tests/test_agent_mention_workflow_contract.py index c5fc4cae54..6659ba7c7a 100644 --- a/tests/test_agent_mention_workflow_contract.py +++ b/tests/test_agent_mention_workflow_contract.py @@ -47,7 +47,8 @@ def test_workflow_uses_local_event_and_central_sweep_with_job_scoped_writes() -> assert "secrets.PR_REVIEW_MERGE_TOKEN" in sweep assert "secrets.OPENCODE_APPROVE_TOKEN" in sweep assert "TARGET_REPOSITORY_SOURCE" in sweep - assert "AGENT_DISPATCH_TOKEN: ${{ github.token }}" in sweep + assert "AGENT_DISPATCH_TOKEN: ${{ github.token }}" not in sweep + assert 'export AGENT_DISPATCH_TOKEN="$TARGET_REPOSITORY_TOKEN"' in sweep assert "agent_mention_sweep.py" in sweep From 02e09148d6a5f418dfd527e7101e0d8ece942211 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 02:05:46 +0900 Subject: [PATCH 5/9] fix(review): preserve central dispatch authority in mention sweep --- .github/workflows/agent-mention-router.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/agent-mention-router.yml b/.github/workflows/agent-mention-router.yml index 3a388aa66a..43fb163975 100644 --- a/.github/workflows/agent-mention-router.yml +++ b/.github/workflows/agent-mention-router.yml @@ -158,6 +158,7 @@ jobs: env: PR_REVIEW_MERGE_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }} OPENCODE_APPROVE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN }} + AGENT_DISPATCH_TOKEN: ${{ github.token }} run: | set -euo pipefail if [ -n "$PR_REVIEW_MERGE_TOKEN" ]; then @@ -171,7 +172,6 @@ jobs: TARGET_REPOSITORY_SOURCE="${TARGET_REPOSITORY_TOKEN:+installation}" fi export TARGET_REPOSITORY_TOKEN - export AGENT_DISPATCH_TOKEN="$TARGET_REPOSITORY_TOKEN" if [ -z "$TARGET_REPOSITORY_TOKEN" ] || [ -z "$TARGET_REPOSITORY_SOURCE" ]; then echo "::error::Agent mention sweep requires PR_REVIEW_MERGE_TOKEN, OPENCODE_APPROVE_TOKEN, or the OpenCode app token exchange." exit 1 From 7c1d255f6b840143071a6a4bec0907e07983dbf5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 02:06:03 +0900 Subject: [PATCH 6/9] test(review): lock split target and central dispatch credentials --- tests/test_agent_mention_workflow_contract.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_agent_mention_workflow_contract.py b/tests/test_agent_mention_workflow_contract.py index 6659ba7c7a..f5d6e13272 100644 --- a/tests/test_agent_mention_workflow_contract.py +++ b/tests/test_agent_mention_workflow_contract.py @@ -47,8 +47,8 @@ def test_workflow_uses_local_event_and_central_sweep_with_job_scoped_writes() -> assert "secrets.PR_REVIEW_MERGE_TOKEN" in sweep assert "secrets.OPENCODE_APPROVE_TOKEN" in sweep assert "TARGET_REPOSITORY_SOURCE" in sweep - assert "AGENT_DISPATCH_TOKEN: ${{ github.token }}" not in sweep - assert 'export AGENT_DISPATCH_TOKEN="$TARGET_REPOSITORY_TOKEN"' in sweep + assert "AGENT_DISPATCH_TOKEN: ${{ github.token }}" in sweep + assert 'export AGENT_DISPATCH_TOKEN="$TARGET_REPOSITORY_TOKEN"' not in sweep assert "agent_mention_sweep.py" in sweep From 94af35fdfc8a4b4139ea35b44d38c2023e4bdee7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 02:06:27 +0900 Subject: [PATCH 7/9] test(review): preserve central dispatch token boundary --- tests/test_agent_mention_downstream_idempotency.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_agent_mention_downstream_idempotency.py b/tests/test_agent_mention_downstream_idempotency.py index 42e6531dd5..e3f64fc021 100644 --- a/tests/test_agent_mention_downstream_idempotency.py +++ b/tests/test_agent_mention_downstream_idempotency.py @@ -12,15 +12,15 @@ def test_router_can_read_durable_central_artifacts() -> None: - """Both local routing and sibling sweeping receive actions read access.""" + """Target reads and central dispatch keep distinct least-privilege authorities.""" text = ROUTER_WORKFLOW.read_text(encoding="utf-8") local, sweep = text.split("\n sweep-organization-agent-mentions:\n", 1) assert "permissions:\n actions: read" in local assert "permissions:\n actions: read" in sweep assert "AGENT_DISPATCH_TOKEN: ${{ github.token }}" in local - assert "AGENT_DISPATCH_TOKEN: ${{ github.token }}" not in sweep - assert 'export AGENT_DISPATCH_TOKEN="$TARGET_REPOSITORY_TOKEN"' in sweep + assert "AGENT_DISPATCH_TOKEN: ${{ github.token }}" in sweep + assert 'export AGENT_DISPATCH_TOKEN="$TARGET_REPOSITORY_TOKEN"' not in sweep def test_downstream_workflows_claim_artifacts_and_bind_exact_key() -> None: From 7cd06a325eb4aa739f786f43b521e617f7588455 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 02:07:04 +0900 Subject: [PATCH 8/9] docs(review): restore central dispatch authority contract --- docs/automation/review-agent-comment-invocation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/automation/review-agent-comment-invocation.md b/docs/automation/review-agent-comment-invocation.md index 6cf747afa1..a886caa967 100644 --- a/docs/automation/review-agent-comment-invocation.md +++ b/docs/automation/review-agent-comment-invocation.md @@ -1,6 +1,6 @@ # Review-agent comment invocation -Updated: 2026-09-02 +Updated: 2026-08-22 ## Purpose @@ -45,7 +45,7 @@ This preserves the central MSA boundary without copying privileged workflow code - The two agent-specific wrapper workflows receive only job-scoped `actions: read` and `contents: write`; their workflow defaults remain `contents: read`. - `actions: read` permits exact-name artifact inventory checks. Artifact upload uses the workflow artifact service and is pinned to immutable `actions/upload-artifact` v7.0.1. - `contents: write` is intentionally retained only on jobs that call GitHub's create-repository-dispatch endpoint. GitHub documents that endpoint as requiring Contents repository permission at write level. Removing it would disable the bounded central dispatch path; broad workflow-default write access is not granted. -- The organization sweep uses the same established cross-repository credential selected for target-repository reads when it dispatches the central review workflows. A repository-scoped `GITHUB_TOKEN` from `ContextualWisdomLab/.github` is not used as sibling-repository dispatch authority. +- The organization sweep uses the established cross-repository credential chain for reading target comments, while the central repository's own short-lived job token dispatches the central workflows. - OpenCode dispatch is restricted to the exact `OPENCODE_REPOSITORY_DISPATCH_TARGETS` allowlist. - An invocation cannot merge: `enable_auto_merge=false`, `update_branches=false`, and `merge_mode=disabled` are bound into the OpenCode invocation claim and hardcoded in the wrapper. GitHub's create-repository-dispatch endpoint allows at most 10 top-level `client_payload` properties (HTTP 422 otherwise), so those review-only constants are not copied onto the first-hop mention payload. The wrapper's merge-scheduler forward keeps those three flags and explicitly sends `trigger_reviews=true`, together with repository, PR, head/base SHA, base branch, and the invocation key. The source comment remains bound and auditable in the verified invocation claim and durable ledger; it is not repeated to the scheduler, which does not consume it. - Every dispatch is bound to live PR number, current head SHA, base branch, source comment, requested agent, and requesting actor metadata fetched or validated immediately before dispatch. @@ -73,7 +73,7 @@ Rollback is deletion of the four mention-router workflows, the two Python helper ## References -GitHub. (n.d.). *Available rules for rulesets*. GitHub Docs. Retrieved August 6, 2026, from https://docs.github.com/en/enterprise-cloud@latest/repositories/configuring-branches-and-merges/in-your-repository/managing-rulesets/available-rules-for-rulesets +GitHub. (n.d.). *Available rules for rulesets*. GitHub Docs. Retrieved August 6, 2026, from https://docs.github.com/en/enterprise-cloud@latest/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets GitHub. (n.d.). *Events that trigger workflows*. GitHub Docs. Retrieved August 6, 2026, from https://docs.github.com/en/enterprise-cloud@latest/actions/reference/workflows-and-actions/events-that-trigger-workflows From 9c2352a757092c8825d6bfe83d41661e023e56ac Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 02:08:53 +0900 Subject: [PATCH 9/9] chore(fuzz): preserve protected-main duplicate removal --- fuzz/fuzz_opencode_normalize_output.py | 47 -------------------------- 1 file changed, 47 deletions(-) delete mode 100644 fuzz/fuzz_opencode_normalize_output.py diff --git a/fuzz/fuzz_opencode_normalize_output.py b/fuzz/fuzz_opencode_normalize_output.py deleted file mode 100644 index 0e034a2ee2..0000000000 --- a/fuzz/fuzz_opencode_normalize_output.py +++ /dev/null @@ -1,47 +0,0 @@ -"""Atheris fuzz harness for OpenCode review-output normalization.""" - -from __future__ import annotations - -import importlib.util -import pathlib -import sys - -import atheris - - -REPO_ROOT = pathlib.Path(__file__).resolve().parents[1] -NORMALIZER_PATH = REPO_ROOT / "scripts" / "ci" / "opencode_review_normalize_output.py" - - -def _load_normalizer(): - """Load the normalizer module without requiring package installation.""" - spec = importlib.util.spec_from_file_location( - "opencode_review_normalize_output", NORMALIZER_PATH - ) - if spec is None or spec.loader is None: - raise RuntimeError("Could not load OpenCode normalizer module") - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - return module - - -NORMALIZER = _load_normalizer() - - -def TestOneInput(data: bytes) -> None: - """Feed arbitrary model text into the JSON extraction path.""" - try: - text = data.decode("utf-8", errors="ignore") - NORMALIZER.extract_json_object(text) - except (ValueError, UnicodeError): - return - - -def main() -> None: - """Run the Atheris entry point.""" - atheris.Setup(sys.argv, TestOneInput) - atheris.Fuzz() - - -if __name__ == "__main__": - main()