Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 6 additions & 14 deletions .github/workflows/agent-mention-opencode-dispatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ 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 || '' }}
TRIGGER_REVIEWS: ${{ github.event.client_payload.review_policy.trigger_reviews }}
REVIEW_DISPATCH_LIMIT: ${{ github.event.client_payload.review_policy.review_dispatch_limit || '' }}
ENABLE_AUTO_MERGE: ${{ github.event.client_payload.review_policy.enable_auto_merge }}
UPDATE_BRANCHES: ${{ github.event.client_payload.review_policy.update_branches }}
MERGE_MODE: ${{ github.event.client_payload.review_policy.merge_mode || '' }}
steps:
- name: Validate exact invocation payload
run: |
Expand Down Expand Up @@ -195,10 +195,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: {
Expand All @@ -211,11 +207,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 -
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ Semantic Versioning where the repository publishes a release.

### Fixed

- Kept OpenCode `repository_dispatch` requests within GitHub's ten-property `client_payload` limit by grouping review controls under one validated `review_policy` object and forwarding only the ten scheduler fields the downstream workflow actually consumes.
- Allowed commas and ASCII parentheses in the bounded Strix changed-file path policy so legal tracked Packrat fixtures can receive exact-head security analysis, while rejecting raw `..` components before normalization and keeping controls, backslashes, whitespace ambiguity, and shell punctuation fail-closed.
- Bound each review-agent invocation key to the wrapper's complete canonical payload, including the base branch and requesting actor; altered fields with a valid-format key now fail before durable-leader election or forwarding, and wrapper write permission is job-scoped.
- Bound both trusted-uv quality jobs to `github.event.pull_request.head.sha` and added a permanent two-checkout regression contract so exact-head compatibility, coverage, docstring, and compilation claims cannot silently measure GitHub's generated pull-request merge revision.
- Made Strix treat only a single LiteLLM provider-error line containing NVIDIA NIM context and model-catalog 404 evidence as cross-model fallback evidence, rejecting cross-line signal assembly and provider-like target source literals; moved the public default to Nemotron 3 Super 120B and added a second NVIDIA hosted candidate before GitHub Models without neutralizing reported vulnerabilities.
- Made Strix treat only a single LiteLLM provider-error line containing NVIDIA NIM context and model-catalog 404 evidence as cross-model fallback evidence, rejecting cross-line signal assembly and provider-like target source literals; moved the public default to Nemotron 3 Super 120B and added a second NVIDIA hosted candidate before GitHub Models without neutralizing reported vulnerabilities.
14 changes: 8 additions & 6 deletions scripts/ci/agent_mention_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,11 +402,13 @@ 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"],
"review_policy": {
"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,
Expand Down Expand Up @@ -561,4 +563,4 @@ def main(argv: Sequence[str] | None = None) -> int:


if __name__ == "__main__": # pragma: no cover
raise SystemExit(main())
raise SystemExit(main())
84 changes: 84 additions & 0 deletions tests/test_agent_mention_repository_dispatch_limit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
"""Regression contracts for GitHub repository-dispatch payload limits."""

from __future__ import annotations

import importlib.util
import sys
from pathlib import Path
from types import ModuleType

ROOT = Path(__file__).resolve().parents[1]
ROUTER_PATH = ROOT / "scripts" / "ci" / "agent_mention_router.py"
OPENCODE_WORKFLOW = ROOT / ".github" / "workflows" / "agent-mention-opencode-dispatch.yml"


def _load_router() -> ModuleType:
"""Load the mention router from the repository under test."""

module_name = "agent_mention_repository_dispatch_limit"
spec = importlib.util.spec_from_file_location(module_name, ROUTER_PATH)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
return module


def _request(router: ModuleType):
"""Return one complete OpenCode mention request."""

return router.MentionRequest(
repository="ContextualWisdomLab/example",
pull_request_number=17,
pull_request_head_sha="a" * 40,
pull_request_base_branch="main",
comment_id=91,
actor="maintainer",
agents=("opencode-agent",),
pull_request_base_sha="b" * 40,
)


def test_opencode_repository_dispatch_payload_has_at_most_ten_properties() -> None:
"""GitHub rejects repository-dispatch client payloads with over ten keys."""

router = _load_router()
payload = router.opencode_payload(_request(router))["client_payload"]

assert len(payload) <= 10
assert payload["review_policy"] == {
"trigger_reviews": True,
"review_dispatch_limit": "1",
"enable_auto_merge": False,
"update_branches": False,
"merge_mode": "disabled",
}


def test_opencode_wrapper_reads_nested_policy_and_forwards_a_valid_payload() -> None:
"""The wrapper preserves policy binding without exceeding GitHub's key limit."""

workflow = OPENCODE_WORKFLOW.read_text(encoding="utf-8")

for field in (
"trigger_reviews",
"review_dispatch_limit",
"enable_auto_merge",
"update_branches",
"merge_mode",
):
assert f"github.event.client_payload.review_policy.{field}" in workflow

# The wrapper-only provenance fields are validated before forwarding, but the
# generic merge-scheduler dispatch does not need to duplicate them. Keeping
# them out leaves the forwarded client_payload at GitHub's ten-key ceiling.
forward_block = workflow.split(
"- name: Forward once to the authoritative review-only scheduler", 1
)[1]
for wrapper_only_field in (
"requested_agent:",
"agent_invocation_key:",
"requested_by:",
"source_comment_id:",
):
assert wrapper_only_field not in forward_block
7 changes: 4 additions & 3 deletions tests/test_agent_mention_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,10 @@ def test_eligible_agents_and_payloads() -> None:
assert opencode["event_type"] == "agent-mention-opencode"
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
review_policy = opencode["client_payload"]["review_policy"]
assert review_policy["merge_mode"] == "disabled"
assert review_policy["enable_auto_merge"] is False
assert review_policy["update_branches"] is False


def test_dispatch_uses_central_events_and_acknowledges() -> None:
Expand Down
Loading