Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
62497e5
fix: keep cross-repo OpenCode evidence healthy
Aug 11, 2026
c68a795
test(opencode): require formal review before status skip
seonghobae Aug 12, 2026
f97b9a4
fix(opencode): prove formal review before status skip
seonghobae Aug 12, 2026
4242588
fix: fail closed on provider scan evidence
seonghobae Aug 12, 2026
db88ea6
test: align Strix outage contract
seonghobae Aug 12, 2026
ac56651
docs: align Strix outage gate contract
seonghobae Aug 12, 2026
ec72661
docs(review): record cross-repo OpenCode evidence fail-closed
seonghobae Aug 13, 2026
56dbb09
fix(coverage): accept only bounded relative requirement includes
seonghobae Aug 13, 2026
8c79abd
ci: repair bounded Strix scope guidance
seonghobae Aug 14, 2026
6d916c6
chore(ci): remove failed one-shot repair workflow
seonghobae Aug 14, 2026
b49f36e
ci: correct Strix scope repair anchors
seonghobae Aug 14, 2026
454ec96
ci: fix Strix scope repair test source
seonghobae Aug 14, 2026
d012c19
ci: repair Strix scope guidance with literal test fixture
seonghobae Aug 14, 2026
329c496
fix(strix): orient bounded CI scope inside sandbox
opencode-agent[bot] Aug 14, 2026
a0b48d2
docs(strix): record bounded PR scope and CI recursion contract
seonghobae Aug 14, 2026
2fc94f6
ci: repair nested requirements lock discovery
seonghobae Aug 14, 2026
4470fc9
ci: route lock-path repair to ubuntu-24.04
seonghobae Aug 14, 2026
912313f
ci: execute lock-path regression from repository root
seonghobae Aug 14, 2026
0d90922
fix(ci): discover nested requirements lock paths
github-actions[bot] Aug 14, 2026
38cb3b9
docs(ci): record base requirements lock discovery contract
seonghobae Aug 14, 2026
5756a20
merge: integrate main hourly repair without weakening OpenCode gates
cursoragent Aug 16, 2026
aae3d9e
fix(ci): stop cancelled required checks from looking failed
cursoragent Aug 16, 2026
f74e4f7
test(ci): pin merge scheduler to keep required queue scans
cursoragent Aug 16, 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
31 changes: 29 additions & 2 deletions .github/workflows/opencode-review-dispatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7851,8 +7851,35 @@ jobs:
exit 1
fi
if [ "${GH_REPOSITORY:-}" != "${GITHUB_REPOSITORY:-}" ] &&
[ "${OPENCODE_STATUS_TOKEN_SOURCE:-}" = "github-token" ]; then
echo "::notice::OpenCode repository_dispatch status publication is unavailable because only the same-repository github.token can access cross-repository target ${GH_REPOSITORY}. The exact-head formal review remains authoritative; configure PR_REVIEW_MERGE_TOKEN or OPENCODE_APPROVE_TOKEN to publish the optional commit status."
{ [ "${OPENCODE_STATUS_TOKEN_SOURCE:-}" = "github-token" ] ||
[ "${OPENCODE_STATUS_TOKEN_SOURCE:-}" = "opencode-app" ]; }; then
formal_review_file="$(mktemp)"
cleanup_formal_review_evidence() {
rm -f "$formal_review_file"
}
trap cleanup_formal_review_evidence EXIT
if ! gh api "repos/${GH_REPOSITORY}/pulls/${PR_NUMBER}/reviews" --paginate --slurp |
jq 'flatten' >"$formal_review_file"; then
echo "::error::OpenCode repository_dispatch status publication could not prove an exact-head formal OpenCode review before skipping unavailable cross-repository commit-status publication."
exit 1
fi
if ! jq -e --arg head "$PR_HEAD_SHA" '
any(.[];
(.commit_id // "") == $head
and (
(.user.login // "") == "opencode-agent[bot]"
or (.user.login // "") == "opencode-agent"
)
and (
((.state // "") | ascii_upcase) == "APPROVED"
or ((.state // "") | ascii_upcase) == "CHANGES_REQUESTED"
)
)
' "$formal_review_file" >/dev/null; then
echo "::error::OpenCode repository_dispatch status publication could not prove an exact-head formal OpenCode review; refusing to hide a cross-repository review-evidence gap."
exit 1
fi
echo "::notice::OpenCode repository_dispatch status publication is unavailable because only the same-repository github.token can access cross-repository target ${GH_REPOSITORY}, and the OpenCode App token has no cross-repository commit-status permission in this deployment. A verified exact-head formal review remains authoritative; configure PR_REVIEW_MERGE_TOKEN or OPENCODE_APPROVE_TOKEN to publish the optional commit status."
exit 0
fi

Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/opencode-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ jobs:

coverage-source-tree:
name: coverage-source-tree
needs: [required-workflow-bootstrap]
runs-on: ubuntu-latest
steps:
- run: >-
Expand All @@ -42,7 +41,6 @@ jobs:

coverage-evidence:
name: coverage-evidence
needs: [coverage-source-tree]
runs-on: ubuntu-latest
steps:
- run: >-
Expand All @@ -51,7 +49,6 @@ jobs:

opencode-review-target:
name: opencode-review
needs: [coverage-evidence]
runs-on: ubuntu-latest
steps:
- run: >-
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/pr-review-merge-scheduler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ concurrency:
github.event_name == 'repository_dispatch' && github.event.client_payload.pr_number != '' && format('pr-{0}', github.event.client_payload.pr_number) ||
github.event_name == 'repository_dispatch' && github.run_id ||
github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request_target' || github.event_name == 'pull_request_review' || github.event_name == 'repository_dispatch' }}
# scan-pr-queue is a required check. Cancelling an in-flight same-head run
# leaves a CANCELLED required conclusion that stays in the rollup after a
# later success, so the PR looks failed with zero failed jobs.
cancel-in-progress: false

# Scorecard Token-Permissions (alert #9): declare a least-privilege default at
# the workflow level. The scan-pr-queue job that actually needs write access
Expand Down
32 changes: 13 additions & 19 deletions .github/workflows/strix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -839,9 +839,10 @@ jobs:
# exit 1 both for genuine blocking vulnerabilities AND for
# LLM-backend-unavailable outcomes (GitHub Models "Too many requests"
# rate limits, OpenAI quota starvation, 413 tokens_limit_reached
# token-cap, connection/warm-up failures) that could not complete a scan. A backend outage is CI
# infrastructure noise, not a security finding, so it must not fail
# the required check and block merges.
# token-cap, connection/warm-up failures) that could not complete a
# scan. Incomplete provider evidence is not a clean security result,
# so the required check stays failed until a later scheduler pass or
# explicit rerun obtains complete evidence.
strix_run_log="$RUNNER_TEMP/strix_gate_console.log"
strix_rc=0
set +e
Expand All @@ -854,28 +855,21 @@ jobs:
fi

# Preserve configuration failures (exit 2) and any unexpected exit
# code as hard failures — only the scan-failure code (1) can be an
# infrastructure/backend-unavailability outcome.
# code as hard failures. A scan-failure code (1), including provider
# or backend unavailability, is incomplete security evidence and
# remains a hard failure for the required check.
if [ "$strix_rc" -ne 1 ]; then
exit "$strix_rc"
fi

# Recognized signals that the LLM backend was unavailable / starved.
backend_unavailable_signal='RateLimitError|Too many requests\. For more on scraping GitHub|exceeded your current quota|insufficient_quota|billing details|"status"[[:space:]]*:[[:space:]]*"RESOURCE_EXHAUSTED"|tokens_limit_reached|Request body too large|Max size:[[:space:]]*[0-9]+[[:space:]]+tokens|Error code:[[:space:]]*413|LLM CONNECTION FAILED|Could not establish connection to the language model|LLM warm-up failed|Configured model and fallback models were unavailable|Configured Vertex model and fallback models were unavailable|emitted provider infrastructure or failure-signal output|before provider infrastructure failure|litellm(\.exceptions)?\.NotFoundError[^[:cntrl:]]*Nvidia_nimException[^[:cntrl:]]*Error code:[[:space:]]*404'
# Any evidence that a vulnerability was actually reported. Its presence
# forces a hard failure so real findings are NEVER downgraded. Keep the
# severity branch anchored away from identifiers so environment lines
# such as STRIX_FAIL_ON_MIN_SEVERITY do not look like findings.
reported_vulnerability_signal='Vulnerabilities[[:space:]]+[1-9]|(^|[^A-Za-z0-9_])severity[[:space:]]*:'

# Neutral skip only when ALL hold: a backend-unavailability signal is
# present and no vulnerability was reported anywhere. This preserves
# real security gating while keeping uncontrollable provider outages
# from blocking current-head merge progress.
if grep -Eiq "$backend_unavailable_signal" "$strix_run_log" \
&& ! grep -Eiq "$reported_vulnerability_signal" "$strix_run_log"; then
echo "::warning title=Strix backend unavailable::Strix could not complete because its LLM backend was unavailable (rate limit / token cap / connection or warm-up failure) before producing a vulnerability report. Treating as a neutral skip so an infrastructure outage does not block merges; genuine findings still fail the check. See the strix-reports artifact and the run log."
exit 0
# Provider/backend failures are not a clean scan. Keep the required
# check red and let the scheduler or an explicit rerun recover after
# provider capacity returns; never convert missing evidence to a pass.
if grep -Eiq "$backend_unavailable_signal" "$strix_run_log"; then
echo "::error title=Strix backend unavailable::Strix could not complete because its LLM backend was unavailable before producing complete security evidence. The required check remains failed; rerun after provider capacity recovers. See the strix-reports artifact and the run log."
exit "$strix_rc"
fi

echo "Strix reported security findings or failed for a non-backend reason; failing the required check (gate exit ${strix_rc})." >&2
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
<!-- CWL-ENTRY -->
> **Agents: read the master context FIRST.** Before any work, read [`docs/CWL-MASTER-CONTEXT.md`](docs/CWL-MASTER-CONTEXT.md) (mission · naruon-as-platform + inter-component UML · cross-cutting disciplines · conventions · roadmap · current state), the live **GitHub Project #1** <https://github.com/orgs/ContextualWisdomLab/projects/1> (work/roadmap source of truth), the full spec **ContextualWisdomLab/naruon#974**, and operate the Project per [`docs/agent-github-project-protocol.md`](docs/agent-github-project-protocol.md). The repo/Project — not any private agent memory — is the source of truth.

Materialize accepts only exact SHA-256 pins or a bounded relative `-r` include (no `.`/`..`); a lone `--require-hashes` directive is not trust evidence. See [`docs/doctoring/hourly-nvidia-nim-autofix.md`](docs/doctoring/hourly-nvidia-nim-autofix.md).
Conflict-scope roots fail closed when the immediate parent directory is a symbolic link.
Materialize accepts only exact SHA-256 pins or a bounded relative `-r` include (no `.`/`..`); a lone `--require-hashes` directive is not trust evidence. See [`docs/doctoring/cross-repo-opencode-evidence.md`](docs/doctoring/cross-repo-opencode-evidence.md).
Conflict-scope roots fail closed when the immediate parent directory is a symbolic link. See [`docs/doctoring/hourly-nvidia-nim-autofix.md`](docs/doctoring/hourly-nvidia-nim-autofix.md).
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ Semantic Versioning where the repository publishes a release.
### Fixed

- Materialized base Python locks only when every package line is an exact SHA-256 pin or a bounded relative `-r`/`--requirement` include. A lone `--require-hashes` directive, a dotted include such as `./lock.txt`, or `-r other-hashes.txt` no longer enters the trusted build context.
- Stopped cancelling in-flight required `scan-pr-queue` runs and stopped serializing the OpenCode required-workflow stub jobs, so a later same-head success is not hidden behind a cancelled or queued required check.
- Sent a static User-Agent on the pinned trusted-uv archive request, required an exact-head formal OpenCode review before skipping unavailable cross-repository commit-status publication, and kept Strix failed when provider evidence is incomplete instead of neutralizing an outage into a pass.
- Refused a conflict-scope repository root whose immediate parent is a symbolic link, so a swapped parent cannot redirect the canonical worktree after the last-component check (CWE-367).
- Bounded the Strix quality self-test's deterministic timeout fixtures to 3-second process and 5-second fake-sleep budgets so exact-head policy evidence completes inside the existing job limit without changing production Strix scanner timeouts, providers, credentials, or review semantics.
- Allowed commas and ASCII parentheses in the bounded Strix changed-file path policy so legal tracked Packrat fixtures can receive exact-head security analysis, while rejecting raw `..` components before normalization and keeping controls, backslashes, whitespace ambiguity, and shell punctuation fail-closed.
Expand Down
59 changes: 59 additions & 0 deletions docs/doctoring/base-requirements-lock-discovery.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Base requirements lock discovery contract

## Purpose

This doctoring record defines how the central review and coverage workflows discover hash-pinned Python requirement locks from an authenticated pull-request base commit. It records the nested-path regression repaired in pull request #939 and preserves the security boundary already developed in pull request #785.

## Incident

The materializer intentionally recognizes two candidate forms:

- conventional file names such as `requirements.txt`, `requirements-dev.txt`, and `requirements.lock`; and
- direct `.txt` children of any directory named `requirements`, such as `requirements/ci.txt` and `service/requirements/package.txt`.

The path predicate implemented both forms, but `base_hash_locks()` still called the basename-only predicate. As a result, a direct child such as `requirements/ci.txt` was rejected before its authenticated base blob and hash-pinned content could be evaluated. The implementation advertised path-aware eligibility while the collector enforced only legacy basename eligibility.

The repair changes the collector to call `_is_candidate_lock_path(candidate)` with the already parsed `PurePosixPath`. It does not broaden the accepted Git object types or relax content validation.

## Trust boundary

A candidate enters the generated build context only when every applicable condition holds:

1. The base revision is an exact 40-character hexadecimal commit SHA.
2. `git ls-tree` reports a regular `100...` blob in that exact base tree.
3. The repository-relative path is non-absolute and contains no `..` component.
4. The path is either a conventional requirements lock name or a direct `.txt` child of a directory named `requirements`.
5. Every substantive requirement is an exact `==` pin with complete SHA-256 hashes, or a separately bounded relative requirements include.
6. Symlinks, gitlinks, malformed tree entries, unpinned files, unsafe includes, and pull-request-only content remain excluded.
7. `uv.lock` follows its separate trusted export path and still requires the corresponding base-owned `pyproject.toml`.

Path eligibility is candidate discovery, not dependency trust. The existing hash, include, export, and downstream closure checks remain authoritative.

## Test-first evidence

Temporary repair workflow run `31787913977` executed the following sequence on head `912313ff92cdcee6f240e9584f79ca37615ee5a2`:

1. Created a temporary Git repository containing hash-pinned `requirements/ci.txt` and `service/requirements/package.txt` blobs.
2. Confirmed the regression test failed before the implementation change because neither path was collected.
3. Replaced the basename-only collector predicate with the repository-relative path predicate.
4. Confirmed both paths were returned in deterministic repository order.
5. Compiled the implementation and regression test and ran `git diff --check`.
6. Deleted the temporary writer workflow before committing the production change.

An earlier repair attempt failed before exercising the assertion because direct script execution omitted the repository root from `sys.path`. The corrected workflow ran both RED and GREEN phases with the same explicit `PYTHONPATH=.` environment, so the observed transition is attributable to the collector change rather than import setup.

## Permanent regression command

```bash
PYTHONPATH=. python3 tests/test_materialize_base_python_requirement_paths.py
python3 -m compileall -q \
scripts/ci/materialize_base_python_requirements.py \
tests/test_materialize_base_python_requirement_paths.py
git diff --check
```

The repository quality workflow must also run the full materializer and Strix regression suites on the exact pull-request head. Focused repair evidence cannot replace protected-branch checks, semantic review, or required independent approvals.

## Change-management rule

Future changes to candidate naming, path parsing, Git tree filtering, requirement includes, `uv.lock` export, or materialized manifests must update the path-discovery tests and the broader materializer suite together. A path predicate and its collector call site must not evolve independently.
33 changes: 33 additions & 0 deletions docs/doctoring/cross-repo-opencode-evidence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Cross-repository OpenCode evidence

## Incident and buyer impact

Materialize accepts only exact SHA-256 pins or a bounded relative `-r`
include; a lone `--require-hashes` line is not lock evidence.

Sibling-repo reviews (for example `ContextualWisdomLab/naruon#1317`) lost
coverage-evidence because `releases.astral.sh` rejected the default Python
User-Agent, and the OpenCode App token could not publish a commit status
across repositories. A later Strix provider outage was also converted into
a green required check, so incomplete security evidence looked like a pass.

## Decision

1. Send a static `User-Agent: cwl-trusted-uv-materializer/1` on the fixed
Astral HTTPS URL. The URL, no-redirect opener, size bound, checksum, and
executable version checks stay unchanged.
2. Before skipping cross-repository status publication, prove an exact-head
formal OpenCode review (`APPROVED` or `CHANGES_REQUESTED`). Missing proof
fails closed.
3. Keep Strix red when the backend is unavailable. Incomplete provider
evidence is not a clean scan.

## References

Fielding, R., Nottingham, M., & Reschke, J. (Eds.). (2022). *HTTP semantics*
(RFC 9110). Internet Engineering Task Force.
https://doi.org/10.17487/RFC9110

National Institute of Standards and Technology. (2020). *Security and privacy
controls for information systems and organizations* (NIST Special Publication
800-53 Rev. 5). https://doi.org/10.6028/NIST.SP.800-53r5
12 changes: 8 additions & 4 deletions docs/doctoring/hourly-nvidia-nim-autofix.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,10 +226,14 @@ and repository policy.

## Independent review-agent boundary

`.github/workflows/opencode-review-dispatch.yml` is not modified by this slice.
The regression contract pins that workflow's Git blob SHA byte-for-byte rather
than inferring independence from provider-name strings. The existing reviewer
retains its own separately reviewed identity, model pool, and credential chain.
`.github/workflows/opencode-review-dispatch.yml` is not modified by the hourly
NVIDIA NIM autofix slice. Pull request #939 later required an exact-head formal
OpenCode review before skipping unavailable cross-repository commit-status
publication; that change does not share autofix credentials or give the
write-capable worker review authority. The regression contract pins the current
reviewed Git blob SHA byte-for-byte rather than inferring independence from
provider-name strings. The existing reviewer retains its own separately reviewed
identity, model pool, and credential chain.

This is a control separation, not naming convention. Review produces a verdict
that may gate merge; autofix proposes branch changes. Their credentials,
Expand Down
Loading
Loading