-
Notifications
You must be signed in to change notification settings - Fork 0
feat(automation): route trusted review-agent mentions #787
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
139 commits
Select commit
Hold shift + click to select a range
a3f4c83
ci: materialize focused review-agent mention router
seonghobae 0cdc975
chore(automation): remove transient mention-router materializer
seonghobae e8adc56
test(automation): preserve trusted mention-router contracts
seonghobae 39afd62
test(automation): preserve organization mention sweep contracts
seonghobae 57a6b9b
test(automation): preserve least-privilege mention workflow contract
seonghobae 6c5038c
feat(automation): add trusted review-agent mention router
seonghobae fab2bf9
feat(automation): add bounded organization mention sweep
seonghobae 1867cc8
test(automation): pin mention-router runner and checkout source
seonghobae 8cd9be3
feat(automation): add trusted review-agent mention workflow
seonghobae 7e53010
ci(automation): enforce mention-router quality gates
seonghobae f6aaa6e
docs(automation): document review-agent mention routing
seonghobae 580909f
docs(changelog): record trusted mention-router slice
seonghobae b0d2d11
docs(automation): define mention-router activation gate
seonghobae b67b99e
test(automation): isolate CLI environment contract
seonghobae 95f5e54
test(automation): require path-based coverage measurement
seonghobae cf4400f
ci(automation): measure exact mention-router script paths
seonghobae 588ea68
test(agent-router): require per-agent durable dispatch idempotency
seonghobae ee1e33e
test(agent-router): require downstream exact-key idempotency
seonghobae cb08d05
test(agent-router): separate workflow trigger and pytest contracts
seonghobae 6008bac
test(agent-router): execute durable idempotency contracts
seonghobae d7c2335
fix(agent-router): deduplicate exact per-agent dispatches
seonghobae 109a5fa
fix(agent-router): accept empty workflow-run inventory
seonghobae 9e11bb3
test(agent-router): model durable run inventory reads
seonghobae 236fd71
fix(agent-router): grant durable workflow-run read access
seonghobae 78af2e0
test(agent-router): require durable dispatch wrappers
seonghobae f0522c4
feat(agent-router): add durable Noema dispatch wrapper
seonghobae 88b5960
feat(agent-router): add durable OpenCode dispatch wrapper
seonghobae d4caec6
feat(agent-router): route through durable dispatch wrappers
seonghobae 68877ff
test(agent-router): expect durable wrapper events
seonghobae ec201df
test(agent-router): cover wrapper event idempotency
seonghobae c1171d7
test(agent-router): reject unsupported Bash lookahead
seonghobae 316246c
fix(agent-router): use portable Bash base validation
seonghobae d69a767
ci(agent-router): track durable dispatch wrappers
seonghobae 7a5db21
test(agent-router): make central run ledger authoritative
seonghobae 75df0cd
ci(agent-router): execute receipt authority contract
seonghobae d3afa23
test(agent-router): make production paths receipt-independent
seonghobae f384e2e
fix(agent-router): remove local receipt authority
seonghobae 7689c3a
fix(agent-router): make sweep receipt-independent
seonghobae b86250d
test: prevent no-op sweep requests from exhausting budget
seonghobae 22e4c18
test: assert job permissions independently
seonghobae 1fd0119
test: accept wildcard quality-gate path coverage
seonghobae 9056842
fix: preserve sweep capacity for newly queued work
seonghobae 9427718
test: cover durable run-ledger failure boundaries
seonghobae ff02c87
docs: record durable dispatch ledger and permission boundary
seonghobae 678be8b
fix(automation): remove branch-selected mention sweep dispatch
seonghobae c1e28b2
test(automation): prohibit branch-selected mention sweep dispatch
seonghobae 9027938
docs(automation): document protected mention sweep trigger
seonghobae 8aa7101
fix(automation): preserve app-token fallback semantics
seonghobae e0f2457
test(ci): cover scheduler package import fallback
seonghobae 84bb106
test(automation): require payload-bound agent invocation keys
seonghobae ef456e3
ci: materialize payload-bound review-agent invocations
seonghobae af1b35c
ci: add bounded PR 787 payload-binding materializer
seonghobae b4105d9
ci(pr787): trigger payload-bound invocation repair
seonghobae b731c29
ci: trigger bounded PR 787 payload-binding repair
seonghobae 521ce30
ci(pr787): stage validated payload-binding repair
seonghobae eb73298
ci(pr787): run payload-bound repair v2
seonghobae 4427fe1
ci: retrigger verified PR 787 payload-binding repair
seonghobae 550a869
ci(pr787): build verified payload candidate
seonghobae 53c5dc6
ci(pr787): upload verified payload candidate
seonghobae 55152c6
ci(pr787): export verified reviewed files
seonghobae 306a082
ci(pr787): export reviewed workflow files
seonghobae 5605489
fix(ci): materialize complete PR 787 payload binding
seonghobae 722c942
fix(automation): document payload-bound invocation keys
seonghobae 5313ea7
ci(automation): repair Noema invocation payload binding
seonghobae 51e6ec4
ci(pr787): repair payload-bound invocation keys
seonghobae 7e85114
ci(pr787): finalize payload-bound review dispatch
seonghobae 7fa3fa2
chore(ci): remove PR 787 repair trigger
seonghobae 438dfc6
chore(ci): remove PR 787 export repair workflow
seonghobae 0c72ab2
chore(ci): remove PR 787 workflow export helper
seonghobae 874ecf7
chore(ci): remove PR 787 finalizer workflow
seonghobae 97a14bc
chore(ci): remove PR 787 Noema repair workflow
seonghobae d47359b
chore(ci): remove PR 787 payload push workflow
seonghobae b8b5bb2
chore(ci): remove PR 787 invocation repair workflow
seonghobae ecb93aa
ci(pr787): run payload-binding product finalizer
seonghobae a7b1bee
chore(ci): remove PR 787 v2 repair workflow
seonghobae f1024b3
chore(ci): remove PR 787 candidate repair workflow
seonghobae 2002f9b
chore(ci): remove PR 787 digest repair workflow
seonghobae 660cef3
chore(ci): remove PR 787 payload upload workflow
seonghobae 0c88eee
ci(pr787): retrigger payload-binding finalizer
seonghobae c64b98b
chore(ci): remove PR 787 patch helper
seonghobae adebafa
chore(ci): remove PR 787 one-shot repair helper
seonghobae f62dfb1
chore(ci): remove PR 787 payload finalizer
seonghobae 3bef8ad
fix(automation): verify payload-bound invocation keys
seonghobae 85c5091
ci(pr787): reconcile Noema payload binding
seonghobae 92c83f4
chore(ci): remove PR 787 payload reconcile workflow
seonghobae b2bd9d4
ci(pr787): finalize payload binding and remove repair artifacts
seonghobae 04d715f
ci: remove accidental placeholder issues
seonghobae fa71834
chore: remove placeholder cleanup workflow
github-actions[bot] 41e6598
ci(pr787): finalize repaired payload tree v2
seonghobae 1cf29e5
fix(automation): bind invocation keys to complete payloads
github-actions[bot] 712dc72
ci: clean exact accidental placeholder issues v2
seonghobae cb1733a
ci(pr787): finalize durable payload tree v3
seonghobae 0cec3e0
fix(automation): bind invocation keys to complete payloads
github-actions[bot] 6e85bd6
test(automation): prove rejected mentions are mutation-free
seonghobae 3ea8586
ci(automation): execute complete exact-range quality gate
seonghobae 26c859a
chore(automation): stage deterministic PR 787 review repairs
seonghobae 692147f
ci(automation): run verified final PR 787 repair
seonghobae 22bf2d4
ci(automation): verify transient writer removal correctly
seonghobae 49f381c
ci(automation): make PR 787 finalizer observable
seonghobae b9ed228
fix(automation): repair transient transformer matching
seonghobae 89fd7d0
ci(automation): repair and authenticate final state writer
seonghobae 6cb7b20
fix(automation): tolerate typed function end markers
seonghobae eaeadd9
fix(automation): match indented workflow blocks
seonghobae 7ddbc7a
fix(automation): preserve generated test escape sequences
seonghobae 440143b
fix(automation): preserve generated router newline escapes
seonghobae 576141e
test(automation): close remaining router coverage branches
seonghobae 384b1b8
ci(automation): expose final uncovered sweep branch
seonghobae 50b039f
test(automation): cover empty pagination termination
seonghobae 107617f
ci(automation): split verified runtime and workflow publication
seonghobae 7ba61cb
fix(automation): close mention-router runtime findings
github-actions[bot] 2744718
ci(automation): remove nonexistent router path trigger
seonghobae 100203d
fix(automation): make Noema wrapper leader election resilient
seonghobae c24721d
fix(automation): make OpenCode wrapper leader election resilient
seonghobae e028648
fix(automation): harden sweep token exchange and handoff
seonghobae f572094
chore(automation): remove transient PR 787 finalizer
seonghobae 11a6a8d
test(automation): specify exact-name artifact ledger
seonghobae 613718e
fix(automation): use exact-name artifact dispatch ledger
seonghobae 7e4ba8c
fix(automation): share exact artifact ledger cache in sweep
seonghobae 8846c52
fix(automation): claim Noema dispatches with exact artifacts
seonghobae bada44c
fix(automation): claim OpenCode dispatches with exact artifacts
seonghobae ff37f98
test(automation): migrate idempotency coverage to artifacts
seonghobae 0e95e15
test(automation): migrate review regressions to artifacts
seonghobae f732cdb
test(automation): assert artifact-first wrapper claims
seonghobae 1282c01
test(automation): assert shared artifact cache in sweep
seonghobae 76d79d7
docs(automation): document exact artifact ledger
seonghobae d2c7d41
test(automation): return artifact inventories in router fakes
seonghobae 29ff8b5
style(automation): annotate intentional failure isolation
seonghobae b49d982
ci(pr787): finalize verified payload binding
seonghobae 545caf0
Merge branch 'main' into feat/review-agent-mention-router-main
opencode-agent[bot] 0e5e597
chore(automation): remove PR-controlled router repair workflow
seonghobae a32efd5
ci(pr787): run final workflow-authorized payload repair
seonghobae 75e21fb
chore(automation): remove unsafe PR-specific branch writer
seonghobae d22976c
test(automation): bind complete review invocation payload
seonghobae 0ffbb19
fix(automation): bind complete agent invocation claim
seonghobae 82fc692
fix(automation): bind Noema wrapper to exact base identity
seonghobae 8ec28b8
fix(automation): bind OpenCode wrapper to complete payload
seonghobae ae304b7
test(automation): align router fixtures with base SHA binding
seonghobae 305a64d
test(automation): complete review regression request metadata
seonghobae 4170e07
test(automation): enrich sweep PR fixtures with base SHA
seonghobae File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,196 @@ | ||
| name: Agent Mention Noema Dispatch | ||
| run-name: >- | ||
| Agent Mention Noema ${{ github.event.client_payload.target_repository }}#${{ | ||
| github.event.client_payload.pr_number }} [cwl-agent-invocation:${{ | ||
| github.event.client_payload.agent_invocation_key }}] | ||
|
|
||
| on: | ||
| repository_dispatch: | ||
| types: [agent-mention-noema] | ||
|
|
||
| concurrency: | ||
| group: agent-mention-noema-${{ github.event.client_payload.agent_invocation_key || github.run_id }} | ||
| cancel-in-progress: false | ||
| queue: max | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| validate-and-forward: | ||
| if: github.repository == 'ContextualWisdomLab/.github' | ||
| runs-on: ubuntu-24.04 | ||
| timeout-minutes: 5 | ||
| permissions: | ||
| actions: read | ||
| contents: write | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| REQUESTED_AGENT: "cwl-noema-review" | ||
| PAYLOAD_AGENT: ${{ github.event.client_payload.requested_agent || '' }} | ||
| INVOCATION_KEY: ${{ github.event.client_payload.agent_invocation_key || '' }} | ||
| TARGET_REPOSITORY: ${{ github.event.client_payload.target_repository || '' }} | ||
| PR_NUMBER: ${{ github.event.client_payload.pr_number || '' }} | ||
| PR_HEAD_SHA: ${{ github.event.client_payload.pr_head_sha || '' }} | ||
| PR_BASE_SHA: ${{ github.event.client_payload.pr_base_sha || '' }} | ||
| 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 || '' }} | ||
| steps: | ||
| - name: Validate exact invocation payload | ||
| run: | | ||
| set -euo pipefail | ||
| if [ "$PAYLOAD_AGENT" != "$REQUESTED_AGENT" ] || | ||
| ! [[ "$INVOCATION_KEY" =~ ^[0-9a-f]{64}$ ]] || | ||
| ! [[ "$TARGET_REPOSITORY" =~ ^ContextualWisdomLab/[A-Za-z0-9_.-]+$ ]] || | ||
| ! [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || | ||
| ! [[ "$PR_HEAD_SHA" =~ ^[0-9a-f]{40}$ ]] || | ||
| ! [[ "$PR_BASE_SHA" =~ ^[0-9a-f]{40}$ ]] || | ||
| ! [[ "$BASE_BRANCH" =~ ^[A-Za-z0-9._/-]+$ ]] || | ||
| [[ "$BASE_BRANCH" == -* ]] || | ||
| ! [[ "$SOURCE_COMMENT_ID" =~ ^[1-9][0-9]*$ ]] || | ||
| ! [[ "$REQUESTED_BY" =~ ^[A-Za-z0-9-]+$ ]]; then | ||
| echo "::error::Rejected malformed or mismatched Noema agent invocation payload." | ||
| exit 1 | ||
| fi | ||
|
|
||
| python3 - <<'PYTHON' | ||
| import hashlib | ||
| import hmac | ||
| import json | ||
| import os | ||
|
|
||
| canonical = json.dumps( | ||
| { | ||
| "actor": os.environ["REQUESTED_BY"], | ||
| "agent": os.environ["REQUESTED_AGENT"], | ||
| "base_branch": os.environ["BASE_BRANCH"], | ||
| "base_sha": os.environ["PR_BASE_SHA"], | ||
| "comment_id": int(os.environ["SOURCE_COMMENT_ID"]), | ||
| "head_sha": os.environ["PR_HEAD_SHA"], | ||
| "pr_number": int(os.environ["PR_NUMBER"]), | ||
| "repository": os.environ["TARGET_REPOSITORY"], | ||
| }, | ||
| ensure_ascii=True, | ||
| separators=(",", ":"), | ||
| sort_keys=True, | ||
| ).encode("utf-8") | ||
| expected = hashlib.sha256(canonical).hexdigest() | ||
| if not hmac.compare_digest(expected, os.environ["INVOCATION_KEY"]): | ||
| raise SystemExit("invocation key does not match canonical payload") | ||
| PYTHON | ||
|
|
||
| - name: Inspect exact-name Actions artifact ledger | ||
| id: ledger | ||
| run: | | ||
| set -euo pipefail | ||
| LEDGER_ARTIFACT_NAME="cwl-agent-invocation-${INVOCATION_KEY}" | ||
| export LEDGER_ARTIFACT_NAME | ||
| echo "LEDGER_ARTIFACT_NAME=$LEDGER_ARTIFACT_NAME" >>"$GITHUB_ENV" | ||
| response_file="${RUNNER_TEMP}/agent-mention-artifacts.json" | ||
| gh api "repos/${GITHUB_REPOSITORY}/actions/artifacts" \ | ||
| -X GET \ | ||
| -f "name=${LEDGER_ARTIFACT_NAME}" \ | ||
| -f "per_page=100" >"$response_file" | ||
| python3 - "$response_file" <<'PYTHON' | ||
| import json | ||
| import os | ||
| from pathlib import Path | ||
| import sys | ||
|
|
||
| response_path = Path(sys.argv[1]) | ||
| payload = json.loads(response_path.read_text(encoding="utf-8")) | ||
| expected_name = os.environ["LEDGER_ARTIFACT_NAME"] | ||
| if not isinstance(payload, dict): | ||
| raise SystemExit("artifact response must be an object") | ||
| total_count = payload.get("total_count") | ||
| artifacts = payload.get("artifacts") | ||
| if type(total_count) is not int or total_count < 0: | ||
| raise SystemExit("artifact response has an invalid total_count") | ||
| if not isinstance(artifacts, list): | ||
| raise SystemExit("artifact response has an invalid artifacts collection") | ||
| if total_count != len(artifacts): | ||
| raise SystemExit("artifact response is truncated or inconsistent") | ||
| live = False | ||
| for artifact in artifacts: | ||
| if not isinstance(artifact, dict): | ||
| raise SystemExit("artifact response contains a non-object record") | ||
| artifact_id = artifact.get("id") | ||
| name = artifact.get("name") | ||
| expired = artifact.get("expired") | ||
| if type(artifact_id) is not int or artifact_id < 1: | ||
| raise SystemExit("artifact response contains an invalid artifact id") | ||
| if not isinstance(name, str) or name != expected_name: | ||
| raise SystemExit("artifact response contains a mismatched artifact name") | ||
| if type(expired) is not bool: | ||
| raise SystemExit("artifact response contains an invalid expired flag") | ||
| live = live or not expired | ||
|
|
||
| output_path = Path(os.environ["GITHUB_OUTPUT"]) | ||
| if live: | ||
| with output_path.open("a", encoding="utf-8") as handle: | ||
| handle.write("claim=false\n") | ||
| raise SystemExit(0) | ||
|
|
||
| claim_dir = Path(os.environ["RUNNER_TEMP"]) / "cwl-agent-invocation-ledger" | ||
| claim_dir.mkdir(mode=0o700, parents=True, exist_ok=True) | ||
| claim = { | ||
| "actor": os.environ["REQUESTED_BY"], | ||
| "agent": os.environ["REQUESTED_AGENT"], | ||
| "base_branch": os.environ["BASE_BRANCH"], | ||
| "base_sha": os.environ["PR_BASE_SHA"], | ||
| "comment_id": int(os.environ["SOURCE_COMMENT_ID"]), | ||
| "head_sha": os.environ["PR_HEAD_SHA"], | ||
| "invocation_key": os.environ["INVOCATION_KEY"], | ||
| "pr_number": int(os.environ["PR_NUMBER"]), | ||
| "repository": os.environ["TARGET_REPOSITORY"], | ||
| } | ||
| (claim_dir / "claim.json").write_text( | ||
| json.dumps(claim, ensure_ascii=True, indent=2, sort_keys=True) + "\n", | ||
| encoding="utf-8", | ||
| ) | ||
| with output_path.open("a", encoding="utf-8") as handle: | ||
| handle.write("claim=true\n") | ||
| PYTHON | ||
|
|
||
| - name: Claim exact invocation in the durable artifact ledger | ||
| if: steps.ledger.outputs.claim == 'true' | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: cwl-agent-invocation-${{ env.INVOCATION_KEY }} | ||
| path: ${{ runner.temp }}/cwl-agent-invocation-ledger/claim.json | ||
| if-no-files-found: error | ||
| retention-days: 30 | ||
| compression-level: 0 | ||
| overwrite: false | ||
| include-hidden-files: false | ||
|
|
||
| - name: Forward once to the authoritative Noema workflow | ||
| if: steps.ledger.outputs.claim == 'true' | ||
| run: | | ||
| set -euo pipefail | ||
| jq -n \ | ||
| --arg target_repository "$TARGET_REPOSITORY" \ | ||
| --argjson pr_number "$PR_NUMBER" \ | ||
| --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: "noema-review", | ||
| client_payload: { | ||
| target_repository: $target_repository, | ||
| pr_number: $pr_number, | ||
| pr_head_sha: $pr_head_sha, | ||
| pr_base_sha: $pr_base_sha, | ||
| base_branch: $base_branch, | ||
| requested_agent: $requested_agent, | ||
| agent_invocation_key: $agent_invocation_key, | ||
| requested_by: $requested_by, | ||
| source_comment_id: $source_comment_id | ||
| } | ||
| }' \ | ||
| | gh api "repos/${GITHUB_REPOSITORY}/dispatches" -X POST --input - | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.