From d513468d0e1f383429e73d7cdc18bd92fbac5d28 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 17 Aug 2026 06:54:06 +0900 Subject: [PATCH 1/3] feat(automation): run contextual-orchestrator hourly NVIDIA NIM review repair Add a thin minute-17 caller so the LLM routing hub receives the same bounded review-repair heartbeat as Clearfolio, DiskSage, and fast-mlsirm without copying scheduler, model, or credential logic. NVIDIA_NIM_API_KEY stays on the separately reviewed worker. The caller never receives COPILOT_GITHUB_TOKEN. --- ...tual-orchestrator-hourly-review-repair.yml | 27 ++++++ .../hourly-nvidia-nim-review-repair.yml | 7 ++ AGENTS.md | 1 + ARCHITECTURE.md | 11 ++- CHANGELOG.md | 2 + CLAUDE.md | 3 +- docs/automation/hourly-review-repair.md | 25 ++++++ ...xtual-orchestrator-hourly-review-caller.md | 82 +++++++++++++++++++ ...xtual_orchestrator_hourly_review_caller.py | 81 ++++++++++++++++++ 9 files changed, 234 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/contextual-orchestrator-hourly-review-repair.yml create mode 100644 docs/doctoring/contextual-orchestrator-hourly-review-caller.md create mode 100644 tests/test_contextual_orchestrator_hourly_review_caller.py diff --git a/.github/workflows/contextual-orchestrator-hourly-review-repair.yml b/.github/workflows/contextual-orchestrator-hourly-review-repair.yml new file mode 100644 index 000000000..fb18fd4b4 --- /dev/null +++ b/.github/workflows/contextual-orchestrator-hourly-review-repair.yml @@ -0,0 +1,27 @@ +name: Contextual Orchestrator Hourly Review Repair + +on: + schedule: + # Minute 17 avoids Clearfolio (23), DiskSage (37), and fast-mlsirm (49). + - cron: "17 * * * *" + +concurrency: + group: contextual-orchestrator-hourly-review-repair + # A later heartbeat must not cancel an in-flight queue decision or repair. + cancel-in-progress: false + +permissions: + contents: read + +jobs: + dispatch-review-repair: + uses: ./.github/workflows/pr-review-fix-scheduler.yml + with: + target_repository: ContextualWisdomLab/contextual-orchestrator + base_branch: main + max_prs: "50" + max_dispatches: "1" + retry_hours: "1" + secrets: + PR_REVIEW_MERGE_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }} + OPENCODE_APPROVE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN }} diff --git a/.github/workflows/hourly-nvidia-nim-review-repair.yml b/.github/workflows/hourly-nvidia-nim-review-repair.yml index 702942708..c2043e47f 100644 --- a/.github/workflows/hourly-nvidia-nim-review-repair.yml +++ b/.github/workflows/hourly-nvidia-nim-review-repair.yml @@ -12,6 +12,7 @@ on: - .github/workflows/fast-mlsirm-hourly-review-repair.yml - .github/workflows/github-hourly-review-repair.yml - .github/workflows/governance-risk-compliance-hourly-review-repair.yml + - .github/workflows/contextual-orchestrator-hourly-review-repair.yml - .github/workflows/hourly-nvidia-nim-review-repair.yml - .github/workflows/nonnest2-hourly-review-repair.yml - .github/workflows/originweave-hourly-review-repair.yml @@ -23,6 +24,7 @@ on: - tests/test_fast_mlsirm_hourly_review_caller.py - tests/test_github_hourly_conflict_repair.py - tests/test_governance_risk_compliance_hourly_review_caller.py + - tests/test_contextual_orchestrator_hourly_review_caller.py - tests/test_hourly_scheduler_runtime_budget.py - tests/test_nonnest2_hourly_review_caller.py - tests/test_originweave_hourly_review_caller.py @@ -47,6 +49,7 @@ on: - docs/doctoring/fast-mlsirm-hourly-review-caller.md - docs/doctoring/github-hourly-conflict-repair.md - docs/doctoring/governance-risk-compliance-hourly-review-caller.md + - docs/doctoring/contextual-orchestrator-hourly-review-caller.md - docs/doctoring/hourly-nvidia-nim-autofix.md - docs/doctoring/nonnest2-hourly-review-caller.md - docs/doctoring/originweave-hourly-review-caller.md @@ -62,6 +65,7 @@ on: - .github/workflows/fast-mlsirm-hourly-review-repair.yml - .github/workflows/github-hourly-review-repair.yml - .github/workflows/governance-risk-compliance-hourly-review-repair.yml + - .github/workflows/contextual-orchestrator-hourly-review-repair.yml - .github/workflows/hourly-nvidia-nim-review-repair.yml - .github/workflows/nonnest2-hourly-review-repair.yml - .github/workflows/originweave-hourly-review-repair.yml @@ -73,6 +77,7 @@ on: - tests/test_fast_mlsirm_hourly_review_caller.py - tests/test_github_hourly_conflict_repair.py - tests/test_governance_risk_compliance_hourly_review_caller.py + - tests/test_contextual_orchestrator_hourly_review_caller.py - tests/test_hourly_scheduler_runtime_budget.py - tests/test_nonnest2_hourly_review_caller.py - tests/test_originweave_hourly_review_caller.py @@ -97,6 +102,7 @@ on: - docs/doctoring/fast-mlsirm-hourly-review-caller.md - docs/doctoring/github-hourly-conflict-repair.md - docs/doctoring/governance-risk-compliance-hourly-review-caller.md + - docs/doctoring/contextual-orchestrator-hourly-review-caller.md - docs/doctoring/hourly-nvidia-nim-autofix.md - docs/doctoring/nonnest2-hourly-review-caller.md - docs/doctoring/originweave-hourly-review-caller.md @@ -153,6 +159,7 @@ jobs: tests/test_fast_mlsirm_hourly_review_caller.py \ tests/test_github_hourly_conflict_repair.py \ tests/test_governance_risk_compliance_hourly_review_caller.py \ + tests/test_contextual_orchestrator_hourly_review_caller.py \ tests/test_hourly_scheduler_runtime_budget.py \ tests/test_nonnest2_hourly_review_caller.py \ tests/test_originweave_hourly_review_caller.py \ diff --git a/AGENTS.md b/AGENTS.md index bd6a96a11..3ff20a81e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,3 +7,4 @@ Materialize accepts only exact SHA-256 pins or a bounded relative `-r` include ( Conflict-scope roots fail closed when the immediate parent directory is a symbolic link. OriginWeave hourly NVIDIA NIM repair is a thin caller at minute 10. See [`docs/doctoring/originweave-hourly-review-caller.md`](docs/doctoring/originweave-hourly-review-caller.md). nonnest2 hourly NVIDIA NIM repair is a thin caller at minute 16. See [`docs/doctoring/nonnest2-hourly-review-caller.md`](docs/doctoring/nonnest2-hourly-review-caller.md). +The contextual-orchestrator hourly repair caller runs at minute 17. See [`docs/doctoring/contextual-orchestrator-hourly-review-caller.md`](docs/doctoring/contextual-orchestrator-hourly-review-caller.md). diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 3e2e70b58..9b75ee3f6 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -66,9 +66,10 @@ The worker checks out helpers at `${{ github.sha }}` so a later default-branch push cannot replace privileged scripts after dispatch (CWE-367). Repair binds `NVIDIA_NIM_API_KEY`, never `COPILOT_GITHUB_TOKEN`. -Product callers stagger Clearfolio at minute 23, DiskSage at minute 37, and -fast-mlsirm at minute 49. Each caller is read-only, dispatches at most one -repair, and delegates all privileged logic to the same sealed scheduler. +Product callers stagger contextual-orchestrator at minute 17, Clearfolio at +minute 23, DiskSage at minute 37, and fast-mlsirm at minute 49. Each caller +is read-only, dispatches at most one repair, and delegates all privileged +logic to the same sealed scheduler. ## Control-plane data flow @@ -123,4 +124,6 @@ trusted `uv` exporter is downloaded from the literal GitHub Releases URL for - [`docs/doctoring/hourly-nvidia-nim-autofix.md`](docs/doctoring/hourly-nvidia-nim-autofix.md) — current increment's repair-worker decision and APA 7th citations. - [`docs/doctoring/fast-mlsirm-hourly-review-caller.md`](docs/doctoring/fast-mlsirm-hourly-review-caller.md) - — product-specific psychometric repair heartbeat and scientific gates. \ No newline at end of file + — product-specific psychometric repair heartbeat and scientific gates. +- [`docs/doctoring/contextual-orchestrator-hourly-review-caller.md`](docs/doctoring/contextual-orchestrator-hourly-review-caller.md) + — LLM routing-hub hourly caller, allowlist, and CWE-269 boundary. \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index fd1aebf43..3de76cf0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Semantic Versioning where the repository publishes a release. - Added a dedicated Clearfolio hourly caller that invokes the product-neutral central scheduler with the exact repository, protected base branch, one-dispatch budget, one-hour retry floor, single-flight concurrency, and only the established scheduler credentials. - Added a dedicated DiskSage hourly caller that invokes the same product-neutral RCA and remediation-feasibility scheduler with an exact repository target, one-dispatch budget, two-hour same-head retry floor, non-cancelling single-flight heartbeat, and explicit established scheduler credentials. - Added a dedicated fast-mlsirm hourly caller that preserves Rust-owned psychometric arithmetic while dispatching at most one exact-head, root-cause-driven repair with a two-hour same-head retry floor. +- Added a dedicated contextual-orchestrator hourly caller that invokes the product-neutral RCA scheduler for `ContextualWisdomLab/contextual-orchestrator` at minute 17 with a one-dispatch budget, one-hour same-head retry floor, non-cancelling single-flight heartbeat, and only the established scheduler credentials. ### Changed @@ -26,6 +27,7 @@ Semantic Versioning where the repository publishes a release. - Run the bounded Clearfolio PR review-feedback repair caller at minute 23 of every hour while keeping the shared scheduler free of product-specific timers and repository names for modular reuse by naruon, contextual-orchestrator, Inkspan, and other CWL services. - Run the bounded DiskSage repair heartbeat at minute 37 of every hour, dispatch no more than one exact-head repair, and wait two hours before redispatching an unchanged head so legitimate OpenCode or NVIDIA NIM latency does not create duplicate writers. - Run the bounded fast-mlsirm repair heartbeat at minute 49 of every hour with one-dispatch scope and a two-hour same-head floor, without weakening true-parameter recovery, CPU/GPU parity, skipped-test, or Rust-ownership gates. +- Run the bounded contextual-orchestrator repair heartbeat at minute 17 of every hour so the LLM routing hub receives NVIDIA NIM review repair without sharing a runner burst with Clearfolio, DiskSage, or fast-mlsirm. - Use NVIDIA NIM `mistralai/mistral-small-4-119b-2603` with explicit high reasoning for scheduled repair and `nvidia/nemotron-3-nano-30b-a3b` for bounded helper work instead of GitHub Models in the write-capable autofix worker. - Apply one NUL-delimited exact-path and complete pre/post-worktree verification contract to both ordinary review repair and merge-conflict repair rather than relying on a visible post-model diff for the ordinary path. diff --git a/CLAUDE.md b/CLAUDE.md index d73a5c169..688b012ed 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -68,7 +68,8 @@ Details: `README.md` and `PR_GOVERNANCE_AUDIT.md`. - `docs/` — master context, Project protocol, `org-required-workflow-rollout.md`, `scorecard-governance.md`, SBOM inventory. Doctoring records live under `docs/doctoring/`. [`ARCHITECTURE.md`](ARCHITECTURE.md) is the control-plane - diagram for review, hourly NVIDIA NIM repair, and merge trust boundaries. + diagram for review, hourly NVIDIA NIM repair (including contextual-orchestrator + minute 17), and merge trust boundaries. - `.jules/` — recorded performance (`bolt.md`) and security (`sentinel.md`) learnings from past work on `scripts/ci/`; worth scanning before optimizing or hardening those scripts. diff --git a/docs/automation/hourly-review-repair.md b/docs/automation/hourly-review-repair.md index 7f15e42c3..8a165e99a 100644 --- a/docs/automation/hourly-review-repair.md +++ b/docs/automation/hourly-review-repair.md @@ -5,6 +5,8 @@ engine**. - `clearfolio-hourly-review-repair.yml` owns Clearfolio's heartbeat at minute 23 of every hour. +- `contextual-orchestrator-hourly-review-repair.yml` owns the LLM routing hub's + heartbeat at minute 17 with a one-hour same-head retry floor. - `pr-review-fix-scheduler.yml` is the reusable, product-neutral scheduler module. It has no product-specific timer and can be called by naruon, contextual-orchestrator, Inkspan, or another CWL service with an explicit @@ -39,6 +41,26 @@ The caller passes only the established `PR_REVIEW_MERGE_TOKEN` and `NVIDIA_NIM_API_KEY`; the model credential is scoped exclusively to the two OpenCode execution steps in the separately reviewed autofix worker. +## contextual-orchestrator execution contract + +The contextual-orchestrator caller is a thin consumer of the same reusable +scheduler: + +```yaml +target_repository: ContextualWisdomLab/contextual-orchestrator +base_branch: main +max_prs: "50" +max_dispatches: "1" +retry_hours: "1" +``` + +It runs at `17 * * * *`, uses its own non-cancelling single-flight group, and +passes only the two established scheduler credentials. Before activation on +protected `main`, `OPENCODE_REPOSITORY_DISPATCH_TARGETS` must include the exact +contextual-orchestrator repository. A missing target mapping fails before any +mutation credential is materialized; it never falls back to a broader +repository scope. + ## Reusable target-selection contract The shared scheduler resolves its target in this order: @@ -195,6 +217,9 @@ Permanent tests prove: - the Clearfolio caller owns exactly one hourly schedule and names the exact repository and protected base branch; +- the contextual-orchestrator caller owns its distinct minute-17 schedule, + exact repository, protected base, non-cancelling concurrency, and explicit + secret contract; - the shared scheduler contains no product-specific timer or repository name; - the dispatch budget and same-head retry floor remain one; - caller and reusable-workflow secrets are explicit and never use diff --git a/docs/doctoring/contextual-orchestrator-hourly-review-caller.md b/docs/doctoring/contextual-orchestrator-hourly-review-caller.md new file mode 100644 index 000000000..0e5390013 --- /dev/null +++ b/docs/doctoring/contextual-orchestrator-hourly-review-caller.md @@ -0,0 +1,82 @@ +# contextual-orchestrator hourly review-repair caller + +Materialize accepts only exact SHA-256 pins or a bounded relative `-r` include; a lone `--require-hashes` line is not lock evidence. + +## Decision and operating boundary + +The central repository owns a thin scheduled caller for +`ContextualWisdomLab/contextual-orchestrator`, the LLM token-cost optimizer, +upstream load balancer, and routing hub. It runs at minute 17 each hour so it +does not share a runner burst with Clearfolio (23), DiskSage (37), or +fast-mlsirm (49). It calls the product-neutral `pr-review-fix-scheduler.yml` +with protected base `main`, a one-dispatch budget, and a one-hour same-head +retry floor. + +The caller contains product identity, cadence, and explicit reusable-workflow +inputs only. Queue classification, exact-head binding, root-cause analysis, +remediation feasibility, retry markers, and repair dispatch remain central. +contextual-orchestrator continues to own routing, cost review, and release +semantics and remains independently operable as a standalone module. + +## Authority and secret contract + +The caller keeps `GITHUB_TOKEN` at `contents: read` and maps only +`PR_REVIEW_MERGE_TOKEN` and `OPENCODE_APPROVE_TOKEN`. It never uses +`secrets: inherit`, receives `NVIDIA_NIM_API_KEY`, or introduces +`COPILOT_GITHUB_TOKEN`. CWE-269 forbids granting the caller the worker's +write or model privileges (MITRE, 2026). Model credentials remain scoped +to the separately reviewed repair worker. The worker cannot approve, +merge, release, change protection, or manufacture passing evidence. + +Before protected-main activation, the repository variable +`OPENCODE_REPOSITORY_DISPATCH_TARGETS` must contain the exact +`ContextualWisdomLab/contextual-orchestrator` target. Missing or mismatched +configuration fails before mutation credential materialization. GitHub App +installation and both mapped credentials must also remain limited to approved +repositories. + +## Failure and recovery + +A missing target allowlist entry, credential, or exact-head evidence is a +non-passing configuration state. Operators correct the bounded configuration +and rerun the unchanged protected source; they do not widen a token, inherit all +secrets, bypass protection, or perturb a clean PR head merely to trigger review. +A newer heartbeat does not cancel an in-flight decision. Same-head retry +markers and the per-PR writer lease prevent duplicate repair writers. + +After source integration, closure requires a scheduled or manual protected-main +consumer run that proves the exact contextual-orchestrator target, read-only +caller token, bounded dispatch decision, and fail-closed credential behavior. +Source checks alone are not protected-main operational acceptance. Merge also +retains every required security check, zero unresolved valid findings, and a +qualifying independent non-author approval. + +## Verification and rollback + +Machine-checkable contracts require the exact target, minute 17 cadence, +non-cancelling single-flight group, one dispatch, one-hour retry floor, explicit +secret mapping, read-only permissions, central path-filter coverage, and +absence of model or Copilot credentials. The full owned suite and hosted +security/review gates must pass on the unchanged exact head. + +Rollback removes the contextual-orchestrator caller, its focused test, +doctoring, and the associated central path-filter/documentation entries. It +must not remove the reusable scheduler's dispatch validation or affect the +independent Clearfolio, DiskSage, and fast-mlsirm callers. + +## APA 7th references + +MITRE. (2026). *CWE-269: Improper privilege management*. +https://cwe.mitre.org/data/definitions/269.html + +GitHub, Inc. (n.d.-a). *Events that trigger workflows*. GitHub Docs. Retrieved +August 16, 2026, from +https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#schedule + +GitHub, Inc. (n.d.-b). *Reuse workflows*. GitHub Docs. Retrieved August 16, +2026, from +https://docs.github.com/en/actions/how-tos/sharing-automations/reuse-workflows + +GitHub, Inc. (n.d.-c). *Workflow syntax for GitHub Actions: Permissions*. +GitHub Docs. Retrieved August 16, 2026, from +https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#permissions diff --git a/tests/test_contextual_orchestrator_hourly_review_caller.py b/tests/test_contextual_orchestrator_hourly_review_caller.py new file mode 100644 index 000000000..037cc45ad --- /dev/null +++ b/tests/test_contextual_orchestrator_hourly_review_caller.py @@ -0,0 +1,81 @@ +"""Contract tests for contextual-orchestrator's bounded hourly review-repair caller.""" + +from pathlib import Path + + +CALLER = Path(".github/workflows/contextual-orchestrator-hourly-review-repair.yml") +DOCTORING = Path("docs/doctoring/contextual-orchestrator-hourly-review-caller.md") +QUALITY_WORKFLOW = Path(".github/workflows/hourly-nvidia-nim-review-repair.yml") +SCHEDULER = Path(".github/workflows/pr-review-fix-scheduler.yml") + + +def _read(path: Path) -> str: + """Return one repository contract file as UTF-8 text.""" + return path.read_text(encoding="utf-8") + + +def test_orchestrator_caller_is_hourly_bounded_and_non_cancelling() -> None: + """The orchestrator receives one bounded repair opportunity without cancellation.""" + caller = _read(CALLER) + + assert 'cron: "17 * * * *"' in caller + assert "group: contextual-orchestrator-hourly-review-repair" in caller + assert "cancel-in-progress: false" in caller + assert "uses: ./.github/workflows/pr-review-fix-scheduler.yml" in caller + assert "target_repository: ContextualWisdomLab/contextual-orchestrator" in caller + assert "base_branch: main" in caller + assert 'max_prs: "50"' in caller + assert 'max_dispatches: "1"' in caller + assert 'retry_hours: "1"' in caller + + +def test_orchestrator_caller_preserves_read_only_explicit_secret_scope() -> None: + """The queue scanner maps established credentials without model secrets.""" + caller = _read(CALLER) + workflow_scope, jobs_scope = caller.split("\njobs:\n", maxsplit=1) + + assert "\npermissions:\n contents: read\n" in workflow_scope + assert "\n permissions:\n" not in jobs_scope + assert "PR_REVIEW_MERGE_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }}" in caller + assert "OPENCODE_APPROVE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN }}" in caller + assert "secrets: inherit" not in caller + assert "NVIDIA_NIM_API_KEY" not in caller + assert "COPILOT_GITHUB_TOKEN" not in caller + for forbidden in ( + "actions: write", + "contents: write", + "issues: write", + "pull-requests: write", + "statuses: write", + ): + assert forbidden not in caller + + +def test_orchestrator_target_is_not_hard_coded_in_shared_scheduler() -> None: + """Product identity remains in the thin caller rather than the engine.""" + assert "ContextualWisdomLab/contextual-orchestrator" not in _read(SCHEDULER) + + +def test_orchestrator_doctoring_records_fail_closed_activation() -> None: + """Operators retain target-allowlist and acceptance prerequisites.""" + doctoring = _read(DOCTORING) + + for phrase in ( + "ContextualWisdomLab/contextual-orchestrator", + "OPENCODE_REPOSITORY_DISPATCH_TARGETS", + "independent non-author approval", + "NVIDIA_NIM_API_KEY", + "COPILOT_GITHUB_TOKEN", + "protected-main operational acceptance", + "APA 7th references", + ): + assert phrase in doctoring + + +def test_focused_quality_workflow_tracks_orchestrator_contracts() -> None: + """Caller, test, and doctoring edits always rerun the focused gate.""" + quality = _read(QUALITY_WORKFLOW) + + assert quality.count(".github/workflows/contextual-orchestrator-hourly-review-repair.yml") == 2 + assert quality.count("docs/doctoring/contextual-orchestrator-hourly-review-caller.md") == 2 + assert quality.count("tests/test_contextual_orchestrator_hourly_review_caller.py") == 3 From 7e3994624dc033989f31974b4d23212468d2f4d8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 09:47:15 +0900 Subject: [PATCH 2/3] fix(automation): grant scheduler job scoped OIDC --- .../workflows/contextual-orchestrator-hourly-review-repair.yml | 3 +++ docs/doctoring/contextual-orchestrator-hourly-review-caller.md | 3 ++- tests/test_contextual_orchestrator_hourly_review_caller.py | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/contextual-orchestrator-hourly-review-repair.yml b/.github/workflows/contextual-orchestrator-hourly-review-repair.yml index fb18fd4b4..de5ecbdec 100644 --- a/.github/workflows/contextual-orchestrator-hourly-review-repair.yml +++ b/.github/workflows/contextual-orchestrator-hourly-review-repair.yml @@ -15,6 +15,9 @@ permissions: jobs: dispatch-review-repair: + permissions: + contents: read + id-token: write uses: ./.github/workflows/pr-review-fix-scheduler.yml with: target_repository: ContextualWisdomLab/contextual-orchestrator diff --git a/docs/doctoring/contextual-orchestrator-hourly-review-caller.md b/docs/doctoring/contextual-orchestrator-hourly-review-caller.md index 0e5390013..e267fbfb7 100644 --- a/docs/doctoring/contextual-orchestrator-hourly-review-caller.md +++ b/docs/doctoring/contextual-orchestrator-hourly-review-caller.md @@ -20,7 +20,8 @@ semantics and remains independently operable as a standalone module. ## Authority and secret contract -The caller keeps `GITHUB_TOKEN` at `contents: read` and maps only +The caller keeps `GITHUB_TOKEN` at `contents: read`, grants job-scoped OIDC for +the reusable scheduler's app-token exchange, and maps only `PR_REVIEW_MERGE_TOKEN` and `OPENCODE_APPROVE_TOKEN`. It never uses `secrets: inherit`, receives `NVIDIA_NIM_API_KEY`, or introduces `COPILOT_GITHUB_TOKEN`. CWE-269 forbids granting the caller the worker's diff --git a/tests/test_contextual_orchestrator_hourly_review_caller.py b/tests/test_contextual_orchestrator_hourly_review_caller.py index 037cc45ad..9f0e96a84 100644 --- a/tests/test_contextual_orchestrator_hourly_review_caller.py +++ b/tests/test_contextual_orchestrator_hourly_review_caller.py @@ -35,7 +35,7 @@ def test_orchestrator_caller_preserves_read_only_explicit_secret_scope() -> None workflow_scope, jobs_scope = caller.split("\njobs:\n", maxsplit=1) assert "\npermissions:\n contents: read\n" in workflow_scope - assert "\n permissions:\n" not in jobs_scope + assert "\n permissions:\n contents: read\n id-token: write\n" in jobs_scope assert "PR_REVIEW_MERGE_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }}" in caller assert "OPENCODE_APPROVE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN }}" in caller assert "secrets: inherit" not in caller From ac4271ada604f382b31605d6c18ba20626e1d1ef Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 03:35:28 +0900 Subject: [PATCH 3/3] fix(security): remove unnecessary caller oidc elevation --- ...tual-orchestrator-hourly-review-repair.yml | 1 - ...xtual-orchestrator-hourly-review-caller.md | 20 ++++++++++--------- ...xtual_orchestrator_hourly_review_caller.py | 5 ++++- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/.github/workflows/contextual-orchestrator-hourly-review-repair.yml b/.github/workflows/contextual-orchestrator-hourly-review-repair.yml index de5ecbdec..2b3b85f3a 100644 --- a/.github/workflows/contextual-orchestrator-hourly-review-repair.yml +++ b/.github/workflows/contextual-orchestrator-hourly-review-repair.yml @@ -17,7 +17,6 @@ jobs: dispatch-review-repair: permissions: contents: read - id-token: write uses: ./.github/workflows/pr-review-fix-scheduler.yml with: target_repository: ContextualWisdomLab/contextual-orchestrator diff --git a/docs/doctoring/contextual-orchestrator-hourly-review-caller.md b/docs/doctoring/contextual-orchestrator-hourly-review-caller.md index e267fbfb7..f253faa76 100644 --- a/docs/doctoring/contextual-orchestrator-hourly-review-caller.md +++ b/docs/doctoring/contextual-orchestrator-hourly-review-caller.md @@ -20,13 +20,14 @@ semantics and remains independently operable as a standalone module. ## Authority and secret contract -The caller keeps `GITHUB_TOKEN` at `contents: read`, grants job-scoped OIDC for -the reusable scheduler's app-token exchange, and maps only -`PR_REVIEW_MERGE_TOKEN` and `OPENCODE_APPROVE_TOKEN`. It never uses -`secrets: inherit`, receives `NVIDIA_NIM_API_KEY`, or introduces -`COPILOT_GITHUB_TOKEN`. CWE-269 forbids granting the caller the worker's -write or model privileges (MITRE, 2026). Model credentials remain scoped -to the separately reviewed repair worker. The worker cannot approve, +The caller keeps `GITHUB_TOKEN` at `contents: read` and maps only +`PR_REVIEW_MERGE_TOKEN` and `OPENCODE_APPROVE_TOKEN`. It deliberately does not +grant `id-token: write`: the established mapped scheduler credentials remain +the authority, while the optional OIDC exchange stays unavailable and +fail-closed. It never uses `secrets: inherit`, receives `NVIDIA_NIM_API_KEY`, +or introduces `COPILOT_GITHUB_TOKEN`. CWE-269 forbids granting the caller the +worker's write or model privileges (MITRE, 2026). Model credentials remain +scoped to the separately reviewed repair worker. The worker cannot approve, merge, release, change protection, or manufacture passing evidence. Before protected-main activation, the repository variable @@ -56,8 +57,9 @@ qualifying independent non-author approval. Machine-checkable contracts require the exact target, minute 17 cadence, non-cancelling single-flight group, one dispatch, one-hour retry floor, explicit -secret mapping, read-only permissions, central path-filter coverage, and -absence of model or Copilot credentials. The full owned suite and hosted +secret mapping, read-only permissions, central path-filter coverage, absence +of OIDC elevation, absence of model or Copilot credentials, and absence of +any target checkout or pull-request execution trigger. The full owned suite and security/review gates must pass on the unchanged exact head. Rollback removes the contextual-orchestrator caller, its focused test, diff --git a/tests/test_contextual_orchestrator_hourly_review_caller.py b/tests/test_contextual_orchestrator_hourly_review_caller.py index 9f0e96a84..5e006807a 100644 --- a/tests/test_contextual_orchestrator_hourly_review_caller.py +++ b/tests/test_contextual_orchestrator_hourly_review_caller.py @@ -35,12 +35,15 @@ def test_orchestrator_caller_preserves_read_only_explicit_secret_scope() -> None workflow_scope, jobs_scope = caller.split("\njobs:\n", maxsplit=1) assert "\npermissions:\n contents: read\n" in workflow_scope - assert "\n permissions:\n contents: read\n id-token: write\n" in jobs_scope + assert "\n permissions:\n contents: read\n" in jobs_scope + assert "id-token: write" not in caller assert "PR_REVIEW_MERGE_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }}" in caller assert "OPENCODE_APPROVE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN }}" in caller assert "secrets: inherit" not in caller assert "NVIDIA_NIM_API_KEY" not in caller assert "COPILOT_GITHUB_TOKEN" not in caller + assert "actions/checkout" not in caller + assert "pull_request:" not in caller for forbidden in ( "actions: write", "contents: write",