Skip to content
Closed
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
ae86a8c
test(opencode): define attempt-scoped coverage artifact contract
seonghobae Aug 6, 2026
b0f4565
ci(opencode): execute coverage artifact rerun contract
seonghobae Aug 6, 2026
bd9ad77
ci: materialize rerun-safe coverage artifact handoff
seonghobae Aug 6, 2026
3504af2
ci: trigger attempt-scoped coverage artifact materialization
seonghobae Aug 6, 2026
7dd1292
docs(opencode): record attempt-scoped artifact rerun contract
seonghobae Aug 6, 2026
a79e7ca
ci(opencode): apply verified attempt-scoped artifact repair
seonghobae Aug 6, 2026
f939923
ci: trigger verified attempt-scoped artifact repair
seonghobae Aug 6, 2026
5a45220
fix(ci): preserve literal Actions expressions in repair
seonghobae Aug 6, 2026
2431afb
ci: rerun corrected attempt-scoped artifact repair
seonghobae Aug 6, 2026
a7f8901
fix(ci): avoid nested Actions expression parsing
seonghobae Aug 6, 2026
54436c6
ci: rerun expression-safe artifact repair
seonghobae Aug 6, 2026
8fe2148
fix(ci): patch workflow by bounded line anchors
seonghobae Aug 6, 2026
46ec344
fix(ci): publish verified repair with repository token
seonghobae Aug 7, 2026
ff6121b
chore(opencode): add temporary verified patch materializer
seonghobae Aug 7, 2026
17d16cf
ci(opencode): export verified workflow repair payload
seonghobae Aug 7, 2026
3dc3a73
ci(opencode): publish repair with OpenCode app token
seonghobae Aug 7, 2026
76c11d9
fix(opencode): bind coverage source artifacts to attempts
opencode-agent[bot] Aug 7, 2026
86f661c
chore(opencode): remove temporary artifact materializer
seonghobae Aug 7, 2026
e4ae5a3
test(opencode): forbid temporary artifact repair workflows
seonghobae Aug 7, 2026
19fd117
Merge branch 'main' into fix/opencode-attempt-scoped-coverage-artifact
opencode-agent[bot] Aug 7, 2026
321a9af
test(opencode): enforce 100% coverage and docstrings
seonghobae Aug 7, 2026
cae2d8f
test(opencode): distinguish permanent quality gate from repairs
seonghobae Aug 7, 2026
817772b
test(opencode): reject prior-attempt coverage evidence
seonghobae Aug 7, 2026
face57d
chore(opencode): add exact current-attempt repair runner
seonghobae Aug 7, 2026
e45ba8f
fix(opencode): harden exact repair materializer
seonghobae Aug 7, 2026
3569ade
test(opencode): reject empty attempt-scoped artifact IDs
seonghobae Aug 7, 2026
9a86669
fix(opencode): bind repair to exact PR head
seonghobae Aug 7, 2026
952c8d4
chore(opencode): add temporary current-attempt identity materializer
seonghobae Aug 7, 2026
554ad4a
chore(opencode): remove branch-writing coverage repair workflow
seonghobae Aug 7, 2026
df3be69
fix(opencode): satisfy validated artifact identity contract
seonghobae Aug 7, 2026
65f8465
chore(opencode): remove branch-writing artifact repair workflow
seonghobae Aug 7, 2026
61687bb
chore(opencode): remove artifact patch generator
seonghobae Aug 7, 2026
00cafaf
chore(opencode): finalize attempt-scoped artifact handoff
seonghobae Aug 7, 2026
4808ef0
chore(opencode): finalize attempt artifact from reviewed repair
seonghobae Aug 7, 2026
1e7c699
chore(opencode): remove PR-controlled finalizer workflow
seonghobae Aug 7, 2026
5a25032
chore(opencode): remove second PR-controlled finalizer
seonghobae Aug 7, 2026
a600086
chore(opencode): add exact current-attempt repair materializer
seonghobae Aug 7, 2026
12776d2
chore(opencode): finalize reviewed attempt identity repair
seonghobae Aug 7, 2026
f6ae8a8
test(opencode): reject finalizer workflow from permanent tree
seonghobae Aug 7, 2026
96c8881
ci(opencode): finalize current-attempt artifact identity
seonghobae Aug 7, 2026
82d0589
fix(opencode): validate current-attempt artifact identity
opencode-agent[bot] Aug 7, 2026
981aca3
chore(opencode): remove third PR-controlled finalizer
seonghobae Aug 7, 2026
ac87c6a
test(opencode): reject renamed branch repair workflows
seonghobae Aug 7, 2026
97e4f35
fix(opencode): bind quality gate to production config
seonghobae Aug 7, 2026
3291678
test(opencode): require reachable rerun recovery guidance
seonghobae Aug 7, 2026
77e06f5
test(opencode): align rerun contract to fail-closed implementation
seonghobae Aug 7, 2026
600fbb4
test(opencode): scope rerun recovery assertions to exact steps
seonghobae Aug 7, 2026
eb8e721
chore(ci): materialize PR 812 control-flow repair
seonghobae Aug 7, 2026
02da0c7
test(opencode): prove producer failure blocks downstream reuse
seonghobae Aug 7, 2026
8561a37
chore(opencode): remove completed PR 812 repair workflow
seonghobae Aug 7, 2026
2d20a96
test(opencode): keep rerun recovery reachable
seonghobae Aug 7, 2026
94455d2
chore(opencode): stage rerun recovery control flow
seonghobae Aug 7, 2026
a3c6395
chore(opencode): remove unapplied attempt-recovery patch artifact
seonghobae Aug 7, 2026
64d1749
chore(opencode): restage reachable rerun recovery
seonghobae Aug 7, 2026
d0bda16
chore(opencode): remove unapplied attempt-recovery patch artifact
seonghobae Aug 7, 2026
b996029
chore(opencode): restage reachable artifact recovery
seonghobae Aug 7, 2026
552235b
chore(opencode): remove staged recovery patch artifact
seonghobae Aug 7, 2026
2137012
chore(stack): reconcile attempt-scoped coverage evidence with main
seonghobae Aug 7, 2026
e5e4272
fix(opencode): preserve rerun recovery control flow
seonghobae Aug 11, 2026
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: OpenCode Coverage Artifact Rerun Quality CI

on:
pull_request:
branches: [main]
paths:
- ".github/workflows/opencode-review-dispatch.yml"
- ".github/workflows/opencode-coverage-artifact-rerun-quality-ci.yml"
- "tests/test_opencode_coverage_artifact_rerun_contract.py"
- "docs/doctoring/opencode-coverage-artifact-reruns.md"
- "CHANGELOG.md"
- "requirements-opencode-review-ci-hashes.txt"
push:
branches: [main]
paths:
- ".github/workflows/opencode-review-dispatch.yml"
- ".github/workflows/opencode-coverage-artifact-rerun-quality-ci.yml"
- "tests/test_opencode_coverage_artifact_rerun_contract.py"
- "docs/doctoring/opencode-coverage-artifact-reruns.md"
- "CHANGELOG.md"
- "requirements-opencode-review-ci-hashes.txt"
Comment thread
seonghobae marked this conversation as resolved.

concurrency:
group: opencode-coverage-artifact-rerun-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

permissions:
contents: read

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

jobs:
exact-head-contract:
name: Python 3.14 attempt-scoped artifact contract
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Harden runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit

- name: Checkout exact source
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
ref: ${{ github.event.pull_request.head.sha || github.sha }}

- name: Set up current stable Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.14"
cache: pip
cache-dependency-path: requirements-opencode-review-ci-hashes.txt

- name: Install hash-locked quality tooling
run: python -m pip install --disable-pip-version-check --require-hashes -r requirements-opencode-review-ci-hashes.txt

- name: Run attempt-scoped artifact regression
run: python -m pytest tests/test_opencode_coverage_artifact_rerun_contract.py -q

- name: Enforce complete central test and branch coverage
run: |
set -euo pipefail
python -m coverage erase
python -m coverage run -m pytest tests -q
python -m coverage report --show-missing

- name: Enforce production docstring coverage
run: python -m interrogate .
Comment thread
seonghobae marked this conversation as resolved.
Outdated

- name: Compile permanent contracts
run: python -m compileall -q scripts tests

- name: Reject uncommitted generated state
run: git diff --exit-code --check && test -z "$(git status --porcelain)"
21 changes: 18 additions & 3 deletions .github/workflows/opencode-review-dispatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ jobs:
permissions:
contents: read
id-token: write
outputs:
coverage_source_artifact_id: ${{ steps.coverage_source_upload.outputs.artifact-id }}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
steps:
Expand Down Expand Up @@ -350,9 +352,10 @@ jobs:
tar -cf "$COVERAGE_SOURCE_ARCHIVE" -C "$COVERAGE_SOURCE_WORKDIR" .

- 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 @@ -433,12 +436,24 @@ jobs:
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
Comment thread
seonghobae marked this conversation as resolved.
Outdated

- name: Download materialized pull request merge tree
- name: Download current-attempt materialized pull request merge tree
id: coverage_source_download
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: opencode-coverage-source
artifact-ids: ${{ needs.coverage-source-tree.outputs.coverage_source_artifact_id }}
path: ${{ runner.temp }}/opencode-coverage-artifact
Comment thread
seonghobae marked this conversation as resolved.

- name: Report missing current-attempt coverage source
if: steps.coverage_source_download.outcome != 'success'
env:
GITHUB_RUN_ATTEMPT: ${{ github.run_attempt }}
run: |
set -euo pipefail
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
coderabbitai[bot] 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
Expand Down
102 changes: 102 additions & 0 deletions .github/workflows/pr812-finalize-attempt-artifact-v3.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
name: PR 812 Finalize Attempt Artifact Handoff V3

on:
push:
branches:
- fix/opencode-attempt-scoped-coverage-artifact
paths:
- .github/workflows/pr812-finalize-attempt-artifact-v3.yml

concurrency:
group: pr812-finalize-attempt-artifact-v3
cancel-in-progress: false

permissions:
contents: write
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

jobs:
finalize:
if: >-
github.repository == 'ContextualWisdomLab/.github' &&
github.ref == 'refs/heads/fix/opencode-attempt-scoped-coverage-artifact'
runs-on: ubuntu-24.04
timeout-minutes: 35
steps:
- name: Harden runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit

- name: Checkout exact trigger head
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.sha }}
fetch-depth: 2
persist-credentials: true

- name: Refuse concurrent branch movement
env:
EXPECTED_SHA: ${{ github.sha }}
EXPECTED_BRANCH: fix/opencode-attempt-scoped-coverage-artifact
shell: bash --noprofile --norc -e -o pipefail {0}
run: |
test "$(git rev-parse HEAD)" = "$EXPECTED_SHA"
remote_sha="$(git ls-remote origin "refs/heads/$EXPECTED_BRANCH" | awk '{print $1}')"
test "$remote_sha" = "$EXPECTED_SHA"

- name: Set up Python 3.14
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.14"
cache: pip
cache-dependency-path: requirements-opencode-review-ci-hashes.txt

- name: Install exact hash-locked quality tooling
run: >-
python -m pip install --disable-pip-version-check --require-hashes
-r requirements-opencode-review-ci-hashes.txt

- name: Apply reviewed permanent repair and remove transient source
shell: bash --noprofile --norc -e -o pipefail {0}
run: |
python scripts/ci/prepare_opencode_attempt_artifact_patch.py
rm -- scripts/ci/prepare_opencode_attempt_artifact_patch.py
rm -- .github/workflows/pr812-finalize-attempt-artifact-v3.yml
git diff --check

- name: Verify focused and complete GREEN evidence
shell: bash --noprofile --norc -e -o pipefail {0}
run: |
python -m pytest -q tests/test_opencode_coverage_artifact_rerun_contract.py
python -m coverage erase
python -m coverage run -m pytest tests -q
python -m coverage report --show-missing --fail-under=100
python -m interrogate .
python -m compileall -q scripts tests
git diff --check
expected="$(printf '%s\n' \
'.github/workflows/opencode-review-dispatch.yml' \
'.github/workflows/pr812-finalize-attempt-artifact-v3.yml' \
'CHANGELOG.md' \
'docs/doctoring/opencode-coverage-artifact-reruns.md' \
'scripts/ci/prepare_opencode_attempt_artifact_patch.py' | sort)"
actual="$(git diff --name-only HEAD | sort)"
test "$actual" = "$expected"

- name: Publish verified five-file repair
env:
EXPECTED_SHA: ${{ github.sha }}
EXPECTED_BRANCH: fix/opencode-attempt-scoped-coverage-artifact
shell: bash --noprofile --norc -e -o pipefail {0}
run: |
remote_sha="$(git ls-remote origin "refs/heads/$EXPECTED_BRANCH" | awk '{print $1}')"
test "$remote_sha" = "$EXPECTED_SHA"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add -A
git commit -m "fix(opencode): validate current-attempt artifact identity before download"
test "$(git rev-parse HEAD^)" = "$EXPECTED_SHA"
git push origin "HEAD:refs/heads/$EXPECTED_BRANCH"
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ Semantic Versioning where the repository publishes a release.

### Fixed

- Bound OpenCode coverage source artifacts to one workflow attempt and immutable artifact ID, retained one-day source evidence, and made failed-jobs-only reruns fail closed with full-rerun or fresh-dispatch guidance instead of searching for expired or prior-attempt artifacts.
- 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.
91 changes: 91 additions & 0 deletions docs/doctoring/opencode-coverage-artifact-reruns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# OpenCode coverage artifact rerun contract

## Decision

The central OpenCode review workflow binds every materialized pull-request merge tree to one workflow-run attempt and one immutable GitHub Actions artifact identifier. The credential-free `coverage-evidence` job may consume only that exact artifact identifier. It never searches by a mutable artifact name and never falls back to an artifact produced by another run or attempt.

The source artifact retains the existing one-day retention period. A failed-jobs-only rerun that does not rerun the successful producer is therefore expected to fail closed once that producer artifact expires. The operator response is a **full rerun or a fresh repository dispatch**, both of which rerun `coverage-source-tree` and create current-attempt evidence. Increasing retention or reusing prior-attempt source evidence is not an accepted repair.

## Incident

On August 7, 2026, failed-jobs-only rerun attempt 2 of OpenCode workflow run `31022108085` retried `coverage-evidence` for `ContextualWisdomLab/pg-llm-batch#53` without retrying the successful `coverage-source-tree` producer. The attempt-1 artifact `opencode-coverage-source` had a one-day retention period and was already expired. `actions/download-artifact` therefore returned `Artifact not found` before any current-head tests or docstring checks could run.

The product pull request was not the source of this failure. The failing boundary was the central producer/consumer lifecycle: a static name did not prove that the consumer received evidence uploaded by the current attempt.

## Contract

```mermaid
sequenceDiagram
participant D as Repository dispatch
participant V as validate-pr-metadata
participant P as coverage-source-tree
participant A as Immutable Actions artifact
participant C as coverage-evidence

D->>V: Exact repository, PR, base SHA, head SHA
V->>P: Validated current-head metadata
P->>P: Materialize exact merge tree
P->>A: Upload attempt-scoped name
A-->>P: artifact-id
P-->>C: Immutable artifact-id job output
C->>A: Download exact artifact-id
alt Artifact belongs to current producer attempt
A-->>C: Merge-tree archive
C->>C: Validate archive, sandbox tests, coverage, docstrings
else Producer was omitted or evidence expired
A-->>C: Download failure
C-->>D: Fail closed; require full rerun or fresh dispatch
end
```

The implementation must preserve all of the following properties:

- `coverage-source-tree` remains the only job with repository-read and OIDC credentials for target-repository materialization.
- `coverage-evidence` remains limited to `actions: read`; it receives no repository-content token, OIDC credential, model secret, or review-write credential.
- The upload name includes `github.run_attempt` for operator diagnostics and collision resistance.
- The upload step exports the immutable `artifact-id`; the consumer downloads with `artifact-ids` rather than `name`.
- Retention remains one day to minimize retention of private source evidence.
- Missing current-attempt evidence produces a bounded diagnostic containing the run attempt and the required recovery action.
- Exact-head metadata validation, same-repository validation, merge-tree construction, archive-member validation, isolated execution, coverage, docstring, security, and approval gates remain unchanged.

## Rerun operations

| Operator action | Producer behavior | Consumer behavior | Accepted outcome |
|---|---|---|---|
| Fresh repository dispatch | Producer runs and uploads a new attempt-scoped artifact | Downloads the producer's immutable artifact ID | Accepted |
| Full workflow rerun | Producer reruns and uploads a new attempt-scoped artifact | Downloads the new immutable artifact ID | Accepted |
| Failed-jobs-only rerun while producer is omitted | No current-attempt producer output exists | Fails closed with recovery guidance | Expected failure |
| Attempt to reuse an earlier artifact by name | Current-attempt identity is not proven | Rejected by contract | Rejected |
| Increase retention to hide missing producer execution | Stale source remains available longer | Does not repair attempt identity | Rejected |

## Security and privacy rationale

Artifact immutability prevents later jobs from mutating a successfully uploaded archive, but immutability alone does not identify which workflow attempt produced the archive. The producer's exact `artifact-id` closes that ambiguity. Attempt-qualified naming remains useful for diagnostics, while ID-based selection is the authoritative binding.

The one-day retention period is intentionally short because the archive can contain proprietary or otherwise sensitive source code. Recovery must create fresh, exact-head evidence rather than preserve source archives for a longer period. No product test executes in the credentialed producer. No trusted follow-up consumes command files after untrusted coverage execution begins.

## Rollback

Rollback consists of reverting the attempt-scoped producer output and exact-ID consumer selection together. Reverting only one side leaves the workflow unable to exchange evidence. A rollback must preserve one-day retention, credential separation, and fail-closed behavior; it must not restore mutable-name fallback across attempts.

## Verification

The permanent regression suite must verify:

1. attempt-scoped artifact naming and immutable `artifact-id` producer output;
2. exact-ID download by the consumer;
3. actionable failure for a missing current-attempt artifact;
4. one-day retention; and
5. absence of repository, OIDC, secret, and review-write credentials from `coverage-evidence`.

The complete repository test suite, Python compilation, production statement and branch coverage, public docstring gate, security and supply-chain checks, current-head review, independent approval, and protected merge remain required.

## References

GitHub. (2026a). *Downloading workflow artifacts*. GitHub Actions documentation. https://docs.github.com/en/actions/how-tos/manage-workflow-runs/download-workflow-artifacts

GitHub. (2026b). *Re-running workflows and jobs*. GitHub Actions documentation. https://docs.github.com/en/actions/how-tos/manage-workflow-runs/re-run-workflows-and-jobs

GitHub. (2026c). *actions/download-artifact* [Computer software]. GitHub. https://github.com/actions/download-artifact

GitHub. (2026d). *actions/upload-artifact* [Computer software]. GitHub. https://github.com/actions/upload-artifact
Loading
Loading