Skip to content
Open
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
68 changes: 64 additions & 4 deletions .github/workflows/opencode-review-dispatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,9 @@ jobs:
permissions:
contents: read
id-token: write
outputs:
coverage_source_artifact_id: ${{ steps.coverage_source_upload.outputs.artifact-id }}
coverage_source_run_attempt: ${{ steps.coverage_source_attempt.outputs.run_attempt }}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
steps:
Expand Down Expand Up @@ -349,10 +352,23 @@ jobs:
git -C "$COVERAGE_SOURCE_WORKDIR" status --short
tar -cf "$COVERAGE_SOURCE_ARCHIVE" -C "$COVERAGE_SOURCE_WORKDIR" .

- name: Record coverage source workflow attempt
id: coverage_source_attempt
env:
GITHUB_RUN_ATTEMPT: ${{ github.run_attempt }}
shell: bash --noprofile --norc -e -o pipefail {0}
run: |
if ! [[ "$GITHUB_RUN_ATTEMPT" =~ ^[1-9][0-9]*$ ]]; then
echo "::error::Coverage producer workflow attempt is not a positive integer."
exit 1
fi
printf 'run_attempt=%s\n' "$GITHUB_RUN_ATTEMPT" >>"$GITHUB_OUTPUT"

- name: Upload materialized pull request merge tree
id: coverage_source_upload
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: opencode-coverage-source
name: opencode-coverage-source-${{ github.run_attempt }}
path: ${{ runner.temp }}/opencode-coverage-source.tar
if-no-files-found: error
retention-days: 1
Expand Down Expand Up @@ -431,14 +447,55 @@ jobs:
if: needs.coverage-source-tree.result != 'success'
run: |
echo "::error::Coverage source tree could not be materialized; see the coverage-source-tree job log for the exact target repository, base SHA, head SHA, and fetch or merge failure."
exit 1
# Continue to the unified current-attempt recovery gate for bounded
# fail-closed guidance.

- name: Download materialized pull request merge tree
- name: Verify coverage source identity for current workflow attempt
if: always()
id: coverage_source_identity
continue-on-error: true
env:
COVERAGE_SOURCE_ARTIFACT_ID: ${{ needs.coverage-source-tree.outputs.coverage_source_artifact_id }}
COVERAGE_SOURCE_RUN_ATTEMPT: ${{ needs.coverage-source-tree.outputs.coverage_source_run_attempt }}
CURRENT_RUN_ATTEMPT: ${{ github.run_attempt }}
shell: bash --noprofile --norc -e -o pipefail {0}
run: |
if ! [[ "$CURRENT_RUN_ATTEMPT" =~ ^[1-9][0-9]*$ ]] || \
[ "$COVERAGE_SOURCE_RUN_ATTEMPT" != "$CURRENT_RUN_ATTEMPT" ]; then
echo "::error::Coverage source was not produced in current workflow attempt ${CURRENT_RUN_ATTEMPT:-missing}; producer attempt=${COVERAGE_SOURCE_RUN_ATTEMPT:-missing}."
echo "::error::Use a full rerun or a fresh repository dispatch; failed-jobs-only reruns cannot reuse prior-attempt source evidence."
exit 1
fi
if ! [[ "$COVERAGE_SOURCE_ARTIFACT_ID" =~ ^[1-9][0-9]*$ ]]; then
echo "::error::Coverage source artifact ID is missing or malformed for current workflow attempt."
echo "::error::Use a full rerun or a fresh repository dispatch so the producer publishes current-attempt evidence."
exit 1
fi
artifact_id=$COVERAGE_SOURCE_ARTIFACT_ID
printf 'artifact_id=%s\n' "$artifact_id" >>"$GITHUB_OUTPUT"

- name: Download current-attempt materialized pull request merge tree
if: >-
always()
&& needs.coverage-source-tree.result == 'success'
&& steps.coverage_source_identity.outcome == 'success'
id: coverage_source_download
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: opencode-coverage-source
artifact-ids: ${{ steps.coverage_source_identity.outputs.artifact_id }}
path: ${{ runner.temp }}/opencode-coverage-artifact

- name: Report missing current-attempt coverage source
if: always() && (needs.coverage-source-tree.result != 'success' || steps.coverage_source_identity.outcome != 'success' || steps.coverage_source_download.outcome != 'success')
env:
GITHUB_RUN_ATTEMPT: ${{ github.run_attempt }}
shell: bash --noprofile --norc -e -o pipefail {0}
run: |
echo "::error::Coverage source evidence is unavailable for workflow run attempt ${GITHUB_RUN_ATTEMPT}; a failed-jobs-only rerun cannot safely reconstruct or reuse source evidence from another attempt."
echo "::error::Use a full rerun or a fresh repository dispatch so coverage-source-tree uploads exact current-attempt evidence."
exit 1
Comment thread
seonghobae marked this conversation as resolved.

- name: Prepare pull request merge tree for coverage measurement
env:
COVERAGE_SOURCE_ARCHIVE: ${{ runner.temp }}/opencode-coverage-artifact/opencode-coverage-source.tar
Comment thread
seonghobae marked this conversation as resolved.
Expand Down Expand Up @@ -2507,12 +2564,15 @@ jobs:
ContextualWisdomLab/.github:scripts/ci/pr_head_replay_guard.py | \
ContextualWisdomLab/.github:scripts/ci/pr_review_merge_scheduler.py | \
ContextualWisdomLab/.github:scripts/ci/run_opencode_review_model_pool.sh | \
ContextualWisdomLab/.github:scripts/ci/adversarial_evidence.py | \
Comment thread
seonghobae marked this conversation as resolved.
ContextualWisdomLab/.github:scripts/ci/opencode_review_normalize_output.py | \
ContextualWisdomLab/.github:scripts/ci/strix_quick_gate.sh | \
ContextualWisdomLab/.github:scripts/ci/validate_opencode_failed_check_review.sh | \
ContextualWisdomLab/.github:tests/test_changed_file_syntax_gate.py | \
ContextualWisdomLab/.github:tests/test_javascript_coverage_gate.py | \
ContextualWisdomLab/.github:tests/test_materialize_base_javascript_packages.py | \
ContextualWisdomLab/.github:tests/test_adversarial_evidence.py | \
ContextualWisdomLab/.github:tests/test_opencode_coverage_artifact_rerun_contract.py | \
ContextualWisdomLab/.github:tests/test_opencode_agent_contract.py | \
ContextualWisdomLab/.github:tests/test_opencode_model_pool_runner.py | \
ContextualWisdomLab/.github:tests/test_pr_head_replay_guard.py | \
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ Semantic Versioning where the repository publishes a release.

### Fixed

- Synchronized the Strix quick-gate contract with the current-attempt OpenCode
coverage-artifact download step, keeping the self-test aligned with the
exact-head artifact provenance boundary.
- Bound OpenCode coverage source evidence to the current workflow attempt and
immutable upload artifact ID, failing closed with full-rerun guidance when a
failed-jobs-only rerun cannot obtain exact current-attempt evidence.
- Kept the extracted adversarial-evidence gate and its contract test inside the
central OpenCode review-process fallback scope, so changes to that trust
boundary retain the same exact-file eligibility and adversarial review path.
- Publish only the sanitized cumulative Strix report tree, avoiding a later
copy of relative scanner output that could reintroduce known internal warning
text into uploaded security evidence.
Expand Down
44 changes: 44 additions & 0 deletions docs/doctoring/opencode-adversarial-fallback-scope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# OpenCode adversarial fallback scope

## Incident boundary

The central OpenCode fallback allowlist omitted `adversarial_evidence.py` and
its contract test after the gate was extracted from an already-allowlisted
review helper. A pull request that changed the extracted trust-boundary code
therefore lost the bounded central review-process fallback solely because of
the refactor.

## Decision

Treat the extracted gate and its test as the same review-process unit as the
normalizer and approval gate by adding their exact repository paths to
`fallback_changed_file_allowed`. The existing
`fallback_changed_file_counts_as_core` function already classifies every
allowlisted central path except `.jules/bolt.md` as core, so no new classifier,
provider rule, credential, or approval path is needed.

This keeps the changed control and its regression evidence inside one
assessment scope. NIST SP 800-53 Rev. 5, control SA-11, requires ongoing unit,
integration, system, or regression evaluation and evidence at the defined
depth and coverage (National Institute of Standards and Technology, 2020).
The stable OWASP Web Security Testing Guide likewise includes positive and
negative security-control requirements in the security test suite (OWASP
Foundation, 2020).

## Verification

`tests/test_opencode_agent_contract.py` pins both exact paths in the workflow.
The allowlist stays closed: unrelated files remain ineligible, and every merge
still requires the existing exact-head checks and independent review policy.
The Strix quick-gate self-test names the current-attempt coverage artifact
download step, so a coverage-artifact hardening rename cannot silently leave
the protected workflow contract stale.

## References

National Institute of Standards and Technology. (2020). *Security and privacy
controls for information systems and organizations* (NIST Special Publication
800-53, Revision 5). https://doi.org/10.6028/NIST.SP.800-53r5

OWASP Foundation. (2020). *OWASP web security testing guide* (Version 4.2).
https://owasp.org/www-project-web-security-testing-guide/v42/
44 changes: 44 additions & 0 deletions docs/doctoring/opencode-coverage-artifact-reruns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# OpenCode coverage artifact reruns

## Customer-operability decision

OpenCode coverage now accepts only the immutable artifact identifier emitted by
`actions/upload-artifact` in the current GitHub Actions workflow attempt. The
producer exports both that identifier and `github.run_attempt`; the
credential-free coverage consumer checks that the producer attempt equals its
own attempt before downloading by ID.

This closes an evidence-integrity gap in failed-jobs-only reruns. GitHub can
reuse successful job outputs from the earlier attempt, while artifacts can be
expired or unavailable independently. A static artifact name therefore cannot
prove that coverage inspected the exact source produced for the current
attempt. Missing, malformed, expired, or prior-attempt evidence now fails
closed and tells an operator to use a full rerun or a fresh repository
dispatch. The existing one-day retention window remains bounded; no lookup or
fallback to an earlier attempt is permitted.

## Verification and rollback

Repository tests parse the complete producer and consumer job blocks. They
require the attempt-scoped artifact name, immutable upload output, current
attempt comparison, exact-ID download, credential-free consumer permissions,
and actionable recovery message. The central workflow's ordinary full quality
gate exercises the new contract at 100% statement, branch, and docstring
coverage.

Rollback is a normal revert of the workflow, contract test, and fallback-scope
entry. Operators must not restore name-based or prior-attempt artifact lookup;
until a replacement contract is available, a missing producer must continue to
fail closed.

## References

GitHub. (n.d.). *Re-running workflows and jobs*. Retrieved August 24, 2026,
from https://docs.github.com/en/actions/how-tos/manage-workflow-runs/re-run-workflows-and-jobs

GitHub. (n.d.). *REST API endpoints for GitHub Actions artifacts*. Retrieved
August 24, 2026, from
https://docs.github.com/en/rest/actions/artifacts?apiVersion=2026-03-10

GitHub. (n.d.). *upload-artifact* [Computer software]. Retrieved August 24,
2026, from https://github.com/actions/upload-artifact/blob/main/README.md
4 changes: 4 additions & 0 deletions scripts/ci/strix_quick_gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3119,6 +3119,10 @@ is_timeout_error() {
return 0
fi

if grep -Fq 'litellm.Timeout' "$STRIX_LOG"; then
return 0
fi
Comment thread
seonghobae marked this conversation as resolved.

if grep -Fq 'Strix run timed out after' "$STRIX_LOG"; then
return 0
fi
Expand Down
2 changes: 1 addition & 1 deletion scripts/ci/test_strix_quick_gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -929,7 +929,7 @@ assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" '
assert_file_contains "$workflow_file" "Materialize pull request merge tree for coverage measurement" "required OpenCode reviews measure coverage instead of approving skipped coverage evidence"
assert_file_contains "$workflow_file" "Exchange OpenCode app token for target repository coverage reads" "coverage source materialization can read private target repositories during central manual dispatch"
assert_file_contains "$workflow_file" "Upload materialized pull request merge tree" "coverage source materialization passes only a prepared merge tree artifact to the PR-head coverage job"
assert_file_contains "$workflow_file" "Download materialized pull request merge tree" "coverage evidence consumes the prepared merge tree artifact without target-repository credentials"
assert_file_contains "$workflow_file" "Download current-attempt materialized pull request merge tree" "coverage evidence consumes the prepared current-attempt merge tree artifact without target-repository credentials"
assert_file_contains "$workflow_file" "Report coverage source materialization failure" "coverage evidence logs source materialization failures as the coverage blocker"
local coverage_merge_tree_step
coverage_merge_tree_step="$(
Expand Down
12 changes: 12 additions & 0 deletions tests/test_opencode_agent_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -1551,6 +1551,10 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent():
"ContextualWisdomLab/.github:scripts/ci/opencode_review_approve_gate.sh | \\"
in workflow
)
assert (
"ContextualWisdomLab/.github:scripts/ci/adversarial_evidence.py | \\"
in workflow
)
assert "scripts/ci/run_opencode_review_model_pool.sh | \\" in workflow
assert (
"ContextualWisdomLab/.github:tests/test_javascript_coverage_gate.py | \\"
Expand All @@ -1560,6 +1564,14 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent():
"ContextualWisdomLab/.github:tests/test_materialize_base_javascript_packages.py | \\"
in workflow
)
assert (
"ContextualWisdomLab/.github:tests/test_adversarial_evidence.py | \\"
in workflow
)
assert (
"ContextualWisdomLab/.github:tests/test_opencode_coverage_artifact_rerun_contract.py | \\"
in workflow
)
assert "tests/test_opencode_agent_contract.py | \\" in workflow
assert (
"ContextualWisdomLab/appguardrail:scripts/ci/collect_org_security_failures.py"
Expand Down
Loading
Loading