From 698ffde2a609f620f2baedc0c130df8d4b22e225 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 30 Aug 2026 10:51:58 +0000 Subject: [PATCH 1/3] fix(strix): add missing github-token fallback for self-target status publish The publish-manual-pr-evidence-status job's target-app-token (an OpenCode app-token exchange) is scoped for sibling repositories and always 403s when .github is the repository_dispatch target of its own Strix run -- observed live on PR #1434's own repository_dispatch run (ContextualWisdomLab/.github/actions/runs/33306963425): "target-app-token did not succeed: gh: Resource not accessible by integration (HTTP 403)", then a hard failure since the publisher only tolerates an unpublishable status when the scan result was itself success. The job already declares permissions: statuses: write, so github.token has exactly the scope this self-referential case needs. A near-identical status-publish block ~40 lines earlier in the same file already has this exact fallback (GITHUB_STATUS_TOKEN, conditioned on target_repository == github.repository so it's never attempted -- and never a source of new 403 noise -- for a genuine cross-repo target); this second block had simply drifted without it. Mirrors that existing pattern exactly. Co-Authored-By: Claude --- .github/workflows/strix.yml | 12 ++++++++++ CHANGELOG.md | 9 +++++++ docs/product-technical-gap-baseline.md | 33 ++++++++++++++++++-------- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/.github/workflows/strix.yml b/.github/workflows/strix.yml index 93e493aae..040b10c8c 100644 --- a/.github/workflows/strix.yml +++ b/.github/workflows/strix.yml @@ -1087,6 +1087,15 @@ jobs: - name: Publish same-head manual Strix status env: TARGET_APP_STATUS_TOKEN: ${{ steps.target_app_token.outputs.token || '' }} + # Same conditional as the strix job's own GITHUB_STATUS_TOKEN above: + # github.token only has statuses:write on THIS repository, so it is + # only worth offering as a publish credential when the dispatch + # target is this repository itself (the self-referential case + # target-app-token cannot cover -- the OpenCode app is scoped to + # sibling repos, not to ContextualWisdomLab/.github as its own + # target). Empty for a cross-repo target, matching post_strix_status's + # existing empty-token skip. + GITHUB_STATUS_TOKEN: ${{ (github.event.client_payload.target_repository == '' || github.event.client_payload.target_repository == github.repository) && github.token || '' }} GITHUB_STATUS_READ_TOKEN: ${{ github.token }} PR_REVIEW_MERGE_STATUS_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || '' }} OPENCODE_APPROVE_STATUS_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN || '' }} @@ -1198,6 +1207,9 @@ jobs: if post_strix_status "target-app-token" "$TARGET_APP_STATUS_TOKEN"; then exit 0 fi + if post_strix_status "github-token" "$GITHUB_STATUS_TOKEN"; then + exit 0 + fi if post_strix_status "pr-review-merge-token" "$PR_REVIEW_MERGE_STATUS_TOKEN"; then exit 0 fi diff --git a/CHANGELOG.md b/CHANGELOG.md index fc84661ed..4ae27e931 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ this file. The format follows Keep a Changelog, and versioned releases follow Semantic Versioning where the repository publishes a release. ## [Unreleased] +- Fix `strix.yml`'s `publish-manual-pr-evidence-status` job failing to + publish a manual Strix status back to `.github` when it is the + `repository_dispatch` target of its own Strix run: `target-app-token` is + scoped for sibling repositories and always 403s for this self-referential + case, and the job was missing the `github.token` fallback (conditioned on + `target_repository == github.repository`, matching the job's own + already-declared `statuses: write` permission) that a near-identical + block elsewhere in the same file already had. No behavior change for + genuine cross-repo targets. - Raise `contextual_orchestrator_review_sidecar.sh`'s `ORCHESTRATOR_CATALOG_FAMILY_CAP` default from 4 to 8: root-caused the live "no provider route passed the Strix plain-chat preflight" outage diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 794dc9de9..45ea78adf 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1133,20 +1133,33 @@ then a 502 on the actual gateway request). has not been extended to PR #1434 specifically, so this pass did not self-authorize one) or a `repository_dispatch` targeting a *different* repository that does not itself edit these trusted files. -- **Secondary, separate finding on the same run**: the follow-up - `publish-manual-pr-evidence-status` job also failed — +- **Secondary, separate finding on the same run — now fixed.** The + follow-up `publish-manual-pr-evidence-status` job also failed — `target-app-token` got `HTTP 403: Resource not accessible by integration` publishing the (correctly non-success, per the self-test failure above) Strix status back to `.github`'s own PR #1434. The publisher's own logic only tolerates a publish failure silently when `STRIX_RESULT=success`; a - non-success result that also cannot be published hard-fails by design, so - this is arguably correct fail-closed behavior surfacing a real, - previously-unobserved token-scoping gap, not a logic bug. Plausibly an - edge case specific to `.github` being the `target_repository` of its own - `repository_dispatch` Strix run (this central repo normally dispatches - Strix *to* sibling repos, not to itself) rather than a gap sibling repos - would hit; not investigated further or fixed this pass given it is - downstream of, and only surfaced by, the self-test failure above. + non-success result that also cannot be published hard-fails by design, + which is correct fail-closed behavior, not a logic bug — but the actual + token-scoping gap it surfaced was real: `target-app-token` (an OpenCode + app-token exchange) is scoped for *sibling* repositories, not for + `.github` as the target of its own `repository_dispatch` (this central + repo normally dispatches Strix *to* siblings, not to itself), so it can + never succeed for this specific self-referential case. The job's own + `permissions: statuses: write` block already grants `github.token` + exactly the scope this case needs, and the sibling status-publish block + earlier in the same file (the `strix` job's own inline publish step, ~40 + lines above) already had a `github-token` fallback conditioned on + `target_repository == '' || target_repository == github.repository` — + this second, near-identical block had simply drifted without it. Fixed + by adding the same conditional `GITHUB_STATUS_TOKEN` env var and a + matching `post_strix_status "github-token" "$GITHUB_STATUS_TOKEN"` call + to this job's fallback chain, mirroring the existing pattern exactly; + empty (never attempted) for a genuine cross-repo target, so no behavior + change for the normal sibling-repo case. Full local suite unaffected + (1882 passed, 100% interrogate); not verified on a live hosted run since + reproducing it requires a `repository_dispatch` Strix run targeting + `.github` itself, the same rare trigger path that surfaced the bug. ## 2026-08-30 ZDR/NIM-routing architecture review (owner-directed) From bec60cab35e794169c965d3c34d1909ca48a4e61 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 30 Aug 2026 11:18:57 +0000 Subject: [PATCH 2/3] test(strix): update GITHUB_STATUS_TOKEN smoke assertions for the second declaration Devin Review finding on the prior commit: the smoke test's status_token_count assertion still expected exactly one GITHUB_STATUS_TOKEN declaration, which the prior commit's second declaration (in publish-manual-pr-evidence-status) broke. Fixed by: - Updating the declaration-count assertion from 1 to 2 (one per status-publishing job). - Adding a new assertion pinning both declarations to the identical same-repository conditional expression, so a future divergent copy (e.g. a typo'd comparison) fails closed here. - Adding a new assertion on how many times post_strix_status is actually invoked with the github-token fallback (3: the strix job's own inline publish tries it twice -- mid-chain and as the final last-resort retry -- while publish-manual-pr-evidence-status tries it once), so a declared-but-unused token would also fail closed. Verified against the actual file content, not assumed: ran the full scripts/ci/test_strix_quick_gate.sh (PASS) and the full pytest suite (1882 passed) locally before pushing. Co-Authored-By: Claude --- scripts/ci/test_strix_quick_gate.sh | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index b528e8baf..94dfe107a 100644 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -206,8 +206,29 @@ assert_strix_workflow_pr_trigger_hardened() { assert_file_contains "$workflow_file" "default-branch repository_dispatch evidence cannot cancel" "strix workflow documents manual evidence isolation from branch protection contexts" assert_file_contains "$workflow_file" "re-dispatches exact-head evidence" "strix workflow documents current-head queue recovery" assert_file_contains "$workflow_file" "refs/pull//head has already advanced before this queued run starts" "strix workflow documents stale scan queue avoidance" + # Two declarations as of 2026-08-30: the strix job's own inline status + # publish, and publish-manual-pr-evidence-status's -- the latter added a + # github-token fallback for the self-referential case where .github is + # the repository_dispatch target of its own Strix run (target-app-token + # is scoped for sibling repositories and always 403s there). Both must + # stay byte-identical same-repository conditionals, not merely + # same-named: a third assertion below pins the exact conditional + # expression to the same count so a divergent copy (e.g. a typo'd + # comparison, or one job's copy missing the || github.repository + # fallback) fails closed here instead of only in the field the next + # time .github dispatches Strix at itself. status_token_count="$(grep -c '^[[:space:]]*GITHUB_STATUS_TOKEN:' "$workflow_file")" - assert_equals "1" "$status_token_count" "strix workflow defines GITHUB_STATUS_TOKEN once so GitHub can parse repository_dispatch" + assert_equals "2" "$status_token_count" "strix workflow defines GITHUB_STATUS_TOKEN exactly twice, once per status-publishing job" + status_token_conditional_count="$(grep -c "GITHUB_STATUS_TOKEN: \${{ (github.event.client_payload.target_repository == '' || github.event.client_payload.target_repository == github.repository) && github.token || '' }}" "$workflow_file")" + assert_equals "2" "$status_token_conditional_count" "both GITHUB_STATUS_TOKEN declarations use the identical same-repository conditional" + # Three invocations, not two: the strix job's own inline publish tries + # the github-token fallback twice (once mid-chain, once as the final + # last-resort retry after every other credential has failed), while + # publish-manual-pr-evidence-status tries it once. Both jobs actually + # consuming their own declared GITHUB_STATUS_TOKEN (not just declaring + # and ignoring it) is the property under test here. + github_status_token_fallback_count="$(grep -c 'post_strix_status "github-token" "\$GITHUB_STATUS_TOKEN"' "$workflow_file")" + assert_equals "3" "$github_status_token_fallback_count" "both status-publishing jobs actually invoke their own GITHUB_STATUS_TOKEN fallback, not just declare it" assert_file_not_contains "$workflow_file" "github.event.pull_request.number == 240" "strix workflow must not hard-code repository-specific PR bypasses" assert_file_contains "$workflow_file" "models: read" "strix workflow grants only the GitHub Models read permission needed for Strix" assert_file_contains "$workflow_file" "actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0" "strix workflow pins actions/setup-python" From 3bca264d7cacfabf8853578bc019480a62a7d297 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 1 Sep 2026 00:54:14 +0000 Subject: [PATCH 3/3] fix(ci): bound required-workflow-bootstrap awk extraction to its own job Ports the identical fix already applied to this session's #1476 and #1488 (commit 4323e270 pattern; root-caused in .github#1506, which targets main but has not merged there yet). This PR's exact-head-path-policy check runs its own head-branch copy of scripts/ci/test_strix_quick_gate.sh (plain pull_request trigger, not pull_request_target), and the merge commit onto this branch carried forward the pre-existing bug from both sides (neither main nor this branch's own prior head had the fix), so it needs porting here directly -- confirmed live: run 33455130248/job 99693376191 failed with exactly this assertion. Root cause: assert_opencode_review_uses_codegraph_and_contextual_orchestrator extracted the required-workflow-bootstrap job block from opencode-review.yml with awk '/^ required-workflow-bootstrap:$/,/^[^ ]/'. Every job key in that workflow is indented 2 spaces (never column 0), so the end pattern never matched until EOF, sweeping an unrelated if: line from a later job into the "block" and failing the assertion on unrelated content. Fixed by using an explicit state flag so the end pattern (^ [A-Za-z0-9_-]+:) is only tested starting on the line after the start match, correctly bounding the block to just its own lines. Verified directly: the old awk swept in line 219's `if: github.event.action != 'closed'` from a later job; the new awk captures zero if: lines. Full local run: bash scripts/ci/test_strix_quick_gate.sh -> PASS, exit 0 (was FAIL/exit 1 before this commit). coverage run -m pytest tests -q -> 2126 passed, 1 skipped, 21 subtests. coverage report -> 100%. interrogate -> 100%. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_015Gs7KmNvH75nxz1sL8mKjw --- scripts/ci/test_strix_quick_gate.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 9e22ba11c..a29c3f0ac 100644 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -543,7 +543,7 @@ assert_opencode_review_uses_codegraph_and_contextual_orchestrator() { assert_file_not_contains "$workflow_file" "Wait for trusted OpenCode approval review" "opencode pull_request bridge was removed to avoid duplicate required-check resource use" assert_file_not_contains "$workflow_file" "Trusted OpenCode requested changes for head" "opencode pull_request bridge no longer reconsumes stale trusted review state" assert_file_not_contains "$workflow_file" "github.event.pull_request.number == 240" "opencode review workflow must not hard-code repository-specific PR bypasses" - if awk '/^ required-workflow-bootstrap:$/,/^[^ ]/' "$bootstrap_file" | grep -q '^[[:space:]]*if:'; then + if awk '/^ required-workflow-bootstrap:$/{p=1; print; next} p && /^ [A-Za-z0-9_-]+:/{exit} p' "$bootstrap_file" | grep -q '^[[:space:]]*if:'; then record_failure "opencode required workflow bootstrap must not depend on required-workflow event payload fields" fi assert_file_contains "$workflow_file" 'github.event.client_payload.target_repository || github.repository' "opencode review scopes concurrency by target repository"