Skip to content
Closed
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ Semantic Versioning where the repository publishes a release.

### Fixed

- Include the exact `backend/app/*.py` package context in PR-scoped Strix
scans when a backend application module changes. This prevents a real local
import from being reported as missing merely because the dependency was
unchanged and therefore outside the changed-file attribution set.

- Parsed `opencode.jsonc` as JSONC (stripping `//` and `/* */` comments outside string literals) in the reasoning-effort guard and its contract tests, instead of raw `json.loads`, which rejected the file the moment it carried its first explanatory comment (added for the `contextual-orchestrator` provider block) with `Expecting property name enclosed in double quotes`. Comment markers inside string values, such as the `$schema` URL, are left untouched.
- Download the pinned `uv` 0.12.1 exporter from the official GitHub Releases URL instead of `releases.astral.sh`, which now returns HTTP 403 and blocks org-wide OpenCode `coverage-evidence`. The SHA-256 pin is unchanged. The opener may follow one hop onto `release-assets.githubusercontent.com` or `objects.githubusercontent.com` and still rejects every other host, userinfo, non-HTTPS scheme, and nondefault port (ContextualWisdomLab/.github#1109).
- Compared the trusted `uv` executable's post-install `--version` output against the real GitHub Releases build's full string, `uv 0.12.1 (x86_64-unknown-linux-gnu)`, instead of the bare `uv 0.12.1` the prior check required; the genuine release binary always prints the target triple, so every installation was failing the pin check immediately after the archive download itself was fixed (ContextualWisdomLab/.github#1109).
Expand Down
43 changes: 43 additions & 0 deletions docs/doctoring/strix-pr-head-context-boundary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Strix PR-head dependency context boundary

Status: accepted 2026-08-21

## Incident

The Strix run for LineageWeave PR #192 materialized changed Python files but
not the unchanged local `backend/app` dependency package. The scanner then
reported `backend.app.post_eligibility` as missing even though that module was
present in the PR head and base repository. Earlier attempts also encountered
NVIDIA NIM rate limits; those provider failures must remain visible and must
not be confused with a source finding.

## Decision

When a PR changes a Python module under `backend/app`, the trusted Strix scope
resolver enumerates every Python file under `backend/app` from the exact PR
head tree. The scope builder copies changed files from that head and unchanged
context from the trusted base checkout. The changed-file list remains the
finding-attribution boundary; this does not turn a context file into a changed
finding. The scan still executes only trusted scanner code and treats PR-head
blobs as non-executable data.

This is a product-neutral extension of the existing backend context contract;
it does not replace the repository-specific context list for other backend
layouts and does not downgrade provider or vulnerability failures.

## Evidence and rollback

The regression fixture creates a changed `backend/app/knowledge_graph.py` that
imports an unchanged `backend/app/post_eligibility.py`, then asserts that the
production scope contains the dependency and the trusted content. Roll back
this change only with an equivalent exact-head dependency-context contract;
removing the context or weakening the Strix gate is not an acceptable rollback.

## References

National Institute of Standards and Technology. (2008). *Technical guide to
information security testing and assessment* (Special Publication 800-115).
https://doi.org/10.6028/NIST.SP.800-115

OWASP Foundation. (n.d.). *Web security testing guide*. Retrieved August 21,
2026, from https://owasp.org/www-project-web-security-testing-guide/
28 changes: 28 additions & 0 deletions scripts/ci/strix_quick_gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1184,6 +1184,7 @@ is_scannable_changed_file() {

pull_request_scope_context_files() {
local needs_backend_python=0
local needs_backend_app_python=0
local needs_frontend_email_api_context=0
local needs_deployment_context=0
local changed_file normalized_changed_file
Expand All @@ -1194,6 +1195,9 @@ pull_request_scope_context_files() {
if [[ "$normalized_changed_file" =~ ^backend/.+\.py$ ]]; then
needs_backend_python=1
fi
if [[ "$normalized_changed_file" =~ ^backend/app/.+\.py$ ]]; then
needs_backend_app_python=1
fi
;;
# The app shell, email components, threading URL builder, and API client can
# shape frontend email retrieval flows; include backend auth context with them.
Expand Down Expand Up @@ -1257,6 +1261,30 @@ backend/services/threading_service.py
EOF
fi

if [ "$needs_backend_app_python" -eq 1 ]; then
# Some products use backend/app rather than the central backend/api layout.
# Include every Python module in that exact PR-head package so a changed
# module's local imports are available to Strix without scanning the whole
# repository. Unchanged files are copied from the trusted base checkout;
# changed files are copied from PR_HEAD_SHA by the scope builder.
local backend_app_head_sha
backend_app_head_sha="$(trim_whitespace "${PR_HEAD_SHA:-}")"
if [ -n "$backend_app_head_sha" ] && is_valid_git_commit_sha "$backend_app_head_sha"; then
local backend_app_files
if ! backend_app_files="$(git -c core.quotepath=false ls-tree -r --name-only "$backend_app_head_sha" -- backend/app)"; then
echo "ERROR: backend/app PR-head context could not be enumerated; failing closed." >&2
return 2
fi
while IFS= read -r context_file; do
case "$context_file" in
backend/app/*.py)
printf '%s\n' "$context_file"
;;
esac
done <<<"$backend_app_files"
fi
fi

if [ "$needs_frontend_email_api_context" -eq 1 ]; then
cat <<'EOF'
backend/api/auth.py
Expand Down
24 changes: 22 additions & 2 deletions scripts/ci/test_strix_quick_gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6951,6 +6951,20 @@ if [ -f "$target_path/backend/services/email_parser.py" ]; then
matched_backend_context=1
fi

if [ -f "$target_path/backend/app/knowledge_graph.py" ]; then
if [ ! -f "$target_path/backend/app/post_eligibility.py" ]; then
echo "Error: backend/app local import context missing from PR scope ($target_path)" >&2
exit 78
fi
if ! grep -Fq -- 'BASE_POST_ELIGIBILITY_SHOULD_BE_SCANNED' "$target_path/backend/app/post_eligibility.py"; then
echo "Error: backend/app dependency context did not use trusted base content" >&2
cat -- "$target_path/backend/app/post_eligibility.py" >&2
exit 79
fi
echo "scan ok with backend/app local import context"
matched_backend_context=1
fi

if [ "$matched_backend_context" -eq 1 ]; then
exit 0
fi
Expand All @@ -6967,11 +6981,12 @@ EOF
git config user.name 'Strix Test'
git config user.email 'strix-test@example.invalid'
echo 'seed' >README.md
mkdir -p backend/api backend/services
mkdir -p backend/api backend/app backend/services
printf '%s\n' 'BASE_AUTH_CONTENT_SHOULD_NOT_BE_SCANNED' >backend/api/auth.py
printf '%s\n' 'BASE_EMAILS_CONTENT_SHOULD_NOT_BE_SCANNED' >backend/api/emails.py
printf '%s\n' 'BASE_CALENDAR_SERVICE_SHOULD_BE_SCANNED' >backend/services/calendar_service.py
printf '%s\n' 'BASE_LLM_PROVIDER_URLS_SHOULD_NOT_BE_SCANNED' >backend/services/llm_provider_urls.py
printf '%s\n' 'BASE_POST_ELIGIBILITY_SHOULD_BE_SCANNED' >backend/app/post_eligibility.py
git add .
git commit -qm 'base commit'
)
Expand Down Expand Up @@ -7020,6 +7035,10 @@ EOF
cat >backend/api/runner_config.py <<'EOF'
def require_workspace_admin():
return 'HEAD_RUNNER_CONFIG_SHOULD_BE_SCANNED'
EOF
cat >backend/app/knowledge_graph.py <<'EOF'
from .post_eligibility import SOURCE_POST_ELIGIBILITY_SQL
HEAD_KNOWLEDGE_GRAPH_SHOULD_BE_SCANNED
EOF
git add .
git commit -qm 'head commit'
Expand All @@ -7037,7 +7056,7 @@ EOF
STRIX_INPUT_FILE_ROOT="$tmp_dir" \
GITHUB_EVENT_NAME="pull_request_target" \
PR_BASE_SHA="$base_sha" \
PR_HEAD_SHA="$head_sha" \
PR_HEAD_SHA=" $head_sha " \
STRIX_DISABLE_PR_SCOPING="0" \
FAKE_STRIX_CALL_LOG="$call_log" \
STRIX_LLM_FILE="$strix_llm_file" \
Expand All @@ -7054,6 +7073,7 @@ EOF
assert_file_contains "$output_log" "scan ok with PR-head backend dependency context" "case=pull-request-target-changed-backend-context-uses-head-blob output"
assert_file_contains "$output_log" "scan ok with PR-head LLM provider URL validation context" "case=pull-request-target-changed-backend-context-includes-llm-provider-url-validation output"
assert_file_contains "$output_log" "scan ok with PR-head email parser text safety context" "case=pull-request-target-changed-backend-context-includes-email-parser-text-safety output"
assert_file_contains "$output_log" "scan ok with backend/app local import context" "case=pull-request-target-changed-backend-context-includes-backend-app-local-import output"
assert_equals "1" "$(wc -l <"$call_log" | tr -d ' ')" "case=pull-request-target-changed-backend-context-uses-head-blob strix call count"

rm -rf "$tmp_dir"
Expand Down
Loading