From 02a86f342ff779145ae7d023c7f096ede4148b04 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 13:21:46 +0900 Subject: [PATCH 1/4] test(agent-mention): enforce repository-dispatch property cap --- ..._agent_mention_complete_payload_binding.py | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tests/test_agent_mention_complete_payload_binding.py b/tests/test_agent_mention_complete_payload_binding.py index 04562e93f..fae5d9874 100644 --- a/tests/test_agent_mention_complete_payload_binding.py +++ b/tests/test_agent_mention_complete_payload_binding.py @@ -182,6 +182,72 @@ def test_wrappers_recompute_complete_claim_before_ledger_access() -> None: assert opencode.count(field) >= 2 +def test_repository_dispatch_payloads_stay_within_github_property_limit() -> None: + """Keep both OpenCode dispatch hops at GitHub's ten-property API boundary.""" + + router = _load_router() + request = router.parse_event(_event()) + assert request is not None + noema_payload = router.noema_payload(request)["client_payload"] + opencode_payload = router.opencode_payload(request)["client_payload"] + + assert len(noema_payload) <= 10 + assert set(opencode_payload) == { + "target_repository", + "pr_number", + "pr_head_sha", + "pr_base_sha", + "base_branch", + "requested_agent", + "agent_invocation_key", + "requested_by", + "source_comment_id", + } + assert len(opencode_payload) <= 10 + + workflow = OPENCODE_WORKFLOW.read_text(encoding="utf-8") + for default in ( + "github.event.client_payload.trigger_reviews || 'true'", + "github.event.client_payload.review_dispatch_limit || '1'", + "github.event.client_payload.enable_auto_merge || 'false'", + "github.event.client_payload.update_branches || 'false'", + "github.event.client_payload.merge_mode || 'disabled'", + ): + assert default in workflow + + forward = workflow.split( + " - name: Forward once to the authoritative review-only scheduler\n", 1 + )[1] + payload_literal = forward.split("client_payload: {\n", 1)[1].split( + "\n }\n }'", 1 + )[0] + forwarded_keys = { + line.strip().split(":", 1)[0] + for line in payload_literal.splitlines() + if ":" in line + } + assert forwarded_keys == { + "target_repository", + "pr_number", + "pr_head_sha", + "pr_base_sha", + "base_branch", + "trigger_reviews", + "review_dispatch_limit", + "enable_auto_merge", + "update_branches", + "merge_mode", + } + assert len(forwarded_keys) <= 10 + for wrapper_identity in ( + "requested_agent", + "agent_invocation_key", + "requested_by", + "source_comment_id", + ): + assert f"{wrapper_identity}:" not in payload_literal + + def test_no_pr_specific_writer_workflow_remains() -> None: """Complete binding is implemented in canonical files, never a branch writer.""" From dc7ba23e5af765f1cad7fbd199e36b94eb889f7e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 13:31:07 +0900 Subject: [PATCH 2/4] fix(agent-mention): reconstruct fixed review controls in trusted wrapper --- .../agent-mention-opencode-dispatch.yml | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/.github/workflows/agent-mention-opencode-dispatch.yml b/.github/workflows/agent-mention-opencode-dispatch.yml index 160b4723d..8bee4a3ac 100644 --- a/.github/workflows/agent-mention-opencode-dispatch.yml +++ b/.github/workflows/agent-mention-opencode-dispatch.yml @@ -36,11 +36,15 @@ jobs: BASE_BRANCH: ${{ github.event.client_payload.base_branch || '' }} REQUESTED_BY: ${{ github.event.client_payload.requested_by || '' }} SOURCE_COMMENT_ID: ${{ github.event.client_payload.source_comment_id || '' }} - TRIGGER_REVIEWS: ${{ github.event.client_payload.trigger_reviews }} - REVIEW_DISPATCH_LIMIT: ${{ github.event.client_payload.review_dispatch_limit || '' }} - ENABLE_AUTO_MERGE: ${{ github.event.client_payload.enable_auto_merge }} - UPDATE_BRANCHES: ${{ github.event.client_payload.update_branches }} - MERGE_MODE: ${{ github.event.client_payload.merge_mode || '' }} + # The router intentionally omits these immutable review-only controls so + # the repository_dispatch payload stays under GitHub's 10-property cap. + # The trusted wrapper reconstructs the canonical values before validating + # the invocation key, so transport minimization cannot weaken claim binding. + TRIGGER_REVIEWS: ${{ github.event.client_payload.trigger_reviews || 'true' }} + REVIEW_DISPATCH_LIMIT: ${{ github.event.client_payload.review_dispatch_limit || '1' }} + ENABLE_AUTO_MERGE: ${{ github.event.client_payload.enable_auto_merge || 'false' }} + UPDATE_BRANCHES: ${{ github.event.client_payload.update_branches || 'false' }} + MERGE_MODE: ${{ github.event.client_payload.merge_mode || 'disabled' }} steps: - name: Validate exact invocation payload run: | @@ -195,10 +199,6 @@ jobs: --arg pr_head_sha "$PR_HEAD_SHA" \ --arg pr_base_sha "$PR_BASE_SHA" \ --arg base_branch "$BASE_BRANCH" \ - --arg requested_agent "$REQUESTED_AGENT" \ - --arg agent_invocation_key "$INVOCATION_KEY" \ - --arg requested_by "$REQUESTED_BY" \ - --argjson source_comment_id "$SOURCE_COMMENT_ID" \ '{ event_type: "merge-scheduler", client_payload: { @@ -211,11 +211,7 @@ jobs: review_dispatch_limit: "1", enable_auto_merge: false, update_branches: false, - merge_mode: "disabled", - requested_agent: $requested_agent, - agent_invocation_key: $agent_invocation_key, - requested_by: $requested_by, - source_comment_id: $source_comment_id + merge_mode: "disabled" } }' \ - | gh api "repos/${GITHUB_REPOSITORY}/dispatches" -X POST --input - + | gh api "repos/${GITHUB_REPOSITORY}/dispatches" -X POST --input - \ No newline at end of file From f0bb2b75283fe56f3d5cce1e82885fca7b0a4996 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 13:32:01 +0900 Subject: [PATCH 3/4] fix(agent-mention): cap OpenCode wrapper dispatch payload --- scripts/ci/agent_mention_router.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/scripts/ci/agent_mention_router.py b/scripts/ci/agent_mention_router.py index bdb8ac3db..04f090394 100644 --- a/scripts/ci/agent_mention_router.py +++ b/scripts/ci/agent_mention_router.py @@ -390,10 +390,14 @@ def noema_payload(request: MentionRequest) -> dict[str, Any]: def opencode_payload(request: MentionRequest) -> dict[str, Any]: - """Return the durable review-only OpenCode wrapper dispatch body.""" + """Return the capped review-only OpenCode wrapper dispatch body. + + Repository-dispatch payloads carry only identity and durable-claim fields. + The trusted wrapper reconstructs the immutable review-only controls before + recomputing the invocation key and before forwarding the scheduler payload. + """ agent = "opencode-agent" - claim = agent_invocation_claim(request, agent) return { "event_type": "agent-mention-opencode", "client_payload": { @@ -402,11 +406,6 @@ def opencode_payload(request: MentionRequest) -> dict[str, Any]: "pr_head_sha": request.pull_request_head_sha, "pr_base_sha": request.pull_request_base_sha, "base_branch": request.pull_request_base_branch, - "trigger_reviews": claim["trigger_reviews"], - "review_dispatch_limit": claim["review_dispatch_limit"], - "enable_auto_merge": claim["enable_auto_merge"], - "update_branches": claim["update_branches"], - "merge_mode": claim["merge_mode"], "requested_agent": agent, "agent_invocation_key": agent_invocation_key(request, agent), "requested_by": request.actor, @@ -561,4 +560,4 @@ def main(argv: Sequence[str] | None = None) -> int: if __name__ == "__main__": # pragma: no cover - raise SystemExit(main()) + raise SystemExit(main()) \ No newline at end of file From 4e6ecf5c7e0dc88db8f75b8b1faaf9beeb6e1d9b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 13:37:55 +0900 Subject: [PATCH 4/4] test(agent-mention): align initial dispatch with capped transport --- tests/test_agent_mention_router.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/tests/test_agent_mention_router.py b/tests/test_agent_mention_router.py index 4509d43f0..c1ca41ef9 100644 --- a/tests/test_agent_mention_router.py +++ b/tests/test_agent_mention_router.py @@ -201,7 +201,7 @@ def test_receipt_and_allowlist_helpers() -> None: def test_eligible_agents_and_payloads() -> None: - """Eligibility and event bodies preserve the bounded review contract.""" + """Eligibility and wrapper transport preserve the bounded review contract.""" module = load_module() request = module.parse_event(event("@cwl-noema-review @opencode-agent")) @@ -218,13 +218,30 @@ def test_eligible_agents_and_payloads() -> None: assert noema["event_type"] == "agent-mention-noema" assert noema["client_payload"]["pr_head_sha"] == "a" * 40 assert noema["client_payload"]["pr_base_sha"] == "b" * 40 + opencode = module.opencode_payload(request) assert opencode["event_type"] == "agent-mention-opencode" + assert set(opencode["client_payload"]) == { + "target_repository", + "pr_number", + "pr_head_sha", + "pr_base_sha", + "base_branch", + "requested_agent", + "agent_invocation_key", + "requested_by", + "source_comment_id", + } + assert len(opencode["client_payload"]) == 9 assert opencode["client_payload"]["base_branch"] == "develop" assert opencode["client_payload"]["pr_base_sha"] == "b" * 40 - assert opencode["client_payload"]["merge_mode"] == "disabled" - assert opencode["client_payload"]["enable_auto_merge"] is False - assert opencode["client_payload"]["update_branches"] is False + + claim = module.agent_invocation_claim(request, "opencode-agent") + assert claim["trigger_reviews"] is True + assert claim["review_dispatch_limit"] == "1" + assert claim["merge_mode"] == "disabled" + assert claim["enable_auto_merge"] is False + assert claim["update_branches"] is False def test_dispatch_uses_central_events_and_acknowledges() -> None: @@ -251,6 +268,7 @@ def test_dispatch_uses_central_events_and_acknowledges() -> None: args[0] == "repos/ContextualWisdomLab/.github/dispatches" for args, _ in dispatches ) + assert len(dispatches[1][1]["client_payload"]) == 9 assert target.calls[0][1] == {"content": "eyes"} assert "cwl-agent-mention-receipt:91" in target.calls[1][1]["body"] assert "exact-name Actions artifacts" in target.calls[1][1]["body"]