Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e95fed3
ci(strix): bootstrap orchestrator gateway patch
seonghobae Aug 28, 2026
e98d08e
fix(ci): make Strix bootstrap workflow parseable
seonghobae Aug 28, 2026
f73486e
ci(strix): bootstrap orchestrator gateway patch
seonghobae Aug 28, 2026
2e1db52
ci(strix): rerun orchestrator gateway patch
seonghobae Aug 28, 2026
3af96b4
ci(strix): rerun orchestrator gateway patch
seonghobae Aug 28, 2026
6a2eddb
fix(strix): route default scans through contextual-orchestrator
seonghobae Aug 28, 2026
2a904b1
fix(strix): hash-lock orchestrator sidecar dependencies
seonghobae Aug 28, 2026
198db58
fix(strix): preserve dispatch and private-source routing boundaries
seonghobae Aug 28, 2026
5ff0e96
fix(strix): exclude the requested NVIDIA diagnostic model
seonghobae Aug 28, 2026
5b37c9b
fix(strix): restore exact quick-gate source bytes
seonghobae Aug 28, 2026
48f893b
fix(strix): correct diagnostic fallback warning
seonghobae Aug 28, 2026
6beae6b
fix(strix): mask gateway token before sidecar startup
seonghobae Aug 28, 2026
4c2bdcb
fix(strix): syntax-check every required smoke script
seonghobae Aug 28, 2026
5f1c1da
Merge main into Strix remediation branch
seonghobae Aug 28, 2026
2ef49da
Merge protected main and retain Strix gateway hardening
seonghobae Aug 28, 2026
30540d2
Merge branch 'main' into feat/strix-orchestrator-free-zdr
seonghobae Aug 28, 2026
cc6bb05
test(strix): bind syntax regression to sidecar check
seonghobae Aug 28, 2026
4a694d5
docs(strix): record integrated catalog fix state
seonghobae Aug 28, 2026
4112970
fix(review): keep orchestrator bearer out of step env
seonghobae Aug 28, 2026
dd46e76
fix(strix): qualify gateway child model
seonghobae Aug 28, 2026
0dabda5
fix(ci): restore complete Strix gate fixture
seonghobae Aug 28, 2026
d9ecfaf
test(strix): isolate malformed-sidecar smoke failure
seonghobae Aug 28, 2026
986cfc2
Merge protected main into gateway credential repair
seonghobae Aug 28, 2026
7d24923
fix(review): isolate token loader shell state
seonghobae Aug 28, 2026
dc03010
fix(review): mask gateway tokens only on Actions
seonghobae Aug 28, 2026
7b3c3b2
test(review): isolate non-Actions mask fixture
seonghobae Aug 28, 2026
2c9588e
Merge branch 'main' into feat/strix-orchestrator-free-zdr
seonghobae Aug 28, 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
67 changes: 61 additions & 6 deletions .github/workflows/strix.yml
Comment thread
seonghobae marked this conversation as resolved.
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Comment thread
seonghobae marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,18 @@ jobs:
printf 'Materialized central Strix dependency lock from same-repository PR head.\n'
fi

- name: Provision contextual-orchestrator Strix sidecar
if: github.event_name != 'repository_dispatch' || github.event.client_payload.strix_llm == ''
env:
BYTEZ_API_KEY: ${{ secrets.BYTEZ_API_KEY }}
NVIDIA_NIM_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }}
NVIDIA_NIM_API_KEY_SUB: ${{ secrets.NVIDIA_NIM_API_KEY_SUB }}
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
set -euo pipefail
bash "$TRUSTED_STRIX_SOURCE/scripts/ci/contextual_orchestrator_review_sidecar.sh"
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Comment thread
seonghobae marked this conversation as resolved.

- name: Resolve live NVIDIA NIM Strix models
id: resolve_nvidia_models
env:
Expand All @@ -557,7 +569,7 @@ jobs:
NVIDIA_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }}
run: |
set -euo pipefail
if [ -n "$STRIX_MODEL_REQUESTED" ] || [ "$TARGET_REPOSITORY_PRIVATE" != "false" ] || [ -z "${NVIDIA_API_KEY:-}" ]; then
if [ -z "$STRIX_MODEL_REQUESTED" ] || [ "$TARGET_REPOSITORY_PRIVATE" != "false" ] || [ -z "${NVIDIA_API_KEY:-}" ]; then
Comment thread
github-actions[bot] marked this conversation as resolved.
Outdated
printf 'primary=\nfallback=\n' >> "$GITHUB_OUTPUT"
exit 0
fi
Expand Down Expand Up @@ -591,18 +603,39 @@ jobs:
- name: Gate Strix secrets
id: gate
env:
STRIX_MODEL: ${{ github.event.client_payload.strix_llm || (steps.target_visibility.outputs.is_private == 'false' && steps.resolve_nvidia_models.outputs.primary || 'gpt-5.4') }}
STRIX_MODEL: ${{ github.event.client_payload.strix_llm || 'contextual-orchestrator/orchestrator/free' }}
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Outdated
STRIX_MODEL_REQUESTED: ${{ github.event.client_payload.strix_llm || '' }}
STRIX_OPENAI_API_KEY: ${{ secrets.STRIX_OPENAI_API_KEY || secrets.OPENAI_API_KEY }}
STRIX_OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
STRIX_NVIDIA_NIM_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }}
STRIX_VERTEX_CREDENTIALS: ${{ secrets.GCP_SA_KEY }}
STRIX_GITHUB_MODELS_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }}
CONTEXTUAL_ORCHESTRATOR_BASE_URL: ${{ env.CONTEXTUAL_ORCHESTRATOR_BASE_URL }}
CONTEXTUAL_ORCHESTRATOR_TOKEN: ${{ env.CONTEXTUAL_ORCHESTRATOR_TOKEN }}
TARGET_REPOSITORY_PRIVATE: ${{ steps.target_visibility.outputs.is_private }}
run: |
strix_model="$(printf '%s' "$STRIX_MODEL" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
echo "strix_model=$strix_model" >> "$GITHUB_OUTPUT"
case "$strix_model" in
contextual-orchestrator/orchestrator/free)
echo 'enabled=true' >> "$GITHUB_OUTPUT"
echo 'provider_mode=contextual_orchestrator' >> "$GITHUB_OUTPUT"
if ! [[ "$CONTEXTUAL_ORCHESTRATOR_BASE_URL" =~ ^http://127\.0\.0\.1:[0-9]{1,5}$ ]]; then
echo '::error::The contextual-orchestrator Strix sidecar must use an exact IPv4 loopback URL and explicit port.'
exit 1
fi
sidecar_port="${CONTEXTUAL_ORCHESTRATOR_BASE_URL##*:}"
if [ "$sidecar_port" -lt 1 ] || [ "$sidecar_port" -gt 65535 ]; then
echo '::error::The contextual-orchestrator Strix sidecar port must be between 1 and 65535.'
exit 1
fi
sanitized_orchestrator_token="$(printf '%s' "$CONTEXTUAL_ORCHESTRATOR_TOKEN" | tr -d '\r\n')"
trimmed_orchestrator_token="$(printf '%s' "$sanitized_orchestrator_token" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
if [ -z "$trimmed_orchestrator_token" ] || [ "$trimmed_orchestrator_token" != "$CONTEXTUAL_ORCHESTRATOR_TOKEN" ]; then
echo '::error::The contextual-orchestrator Strix sidecar requires one non-empty, line-safe bearer token.'
exit 1
fi
;;
openai/gpt-5-mini* | openai/gpt-5-nano* | \
openai/openai/gpt-5-mini* | openai/openai/gpt-5-nano* | \
github_models/openai/gpt-5-mini* | github_models/openai/gpt-5-nano*)
Expand Down Expand Up @@ -677,7 +710,7 @@ jobs:
fi
;;
*)
echo '::error::STRIX_LLM must select NVIDIA NIM Nemotron, GitHub Models openai/gpt-5 or newer, direct OpenAI GPT-5.4 or newer, OpenRouter openrouter/free, or an approved organization Vertex AI model.'
echo '::error::STRIX_LLM must select contextual-orchestrator/orchestrator/free, NVIDIA NIM Nemotron, GitHub Models openai/gpt-5 or newer, direct OpenAI GPT-5.4 or newer, OpenRouter openrouter/free, or an approved organization Vertex AI model.'
exit 1
;;
esac
Expand Down Expand Up @@ -752,7 +785,7 @@ jobs:
- name: Mask LLM API key
if: steps.gate.outputs.enabled == 'true'
env:
LLM_API_KEY: ${{ steps.gate.outputs.provider_mode == 'github_models' && (secrets.STRIX_GITHUB_MODELS_TOKEN || github.token) || steps.gate.outputs.provider_mode == 'openai_direct' && (secrets.STRIX_OPENAI_API_KEY || secrets.OPENAI_API_KEY) || steps.gate.outputs.provider_mode == 'openrouter' && secrets.OPENROUTER_API_KEY || steps.gate.outputs.provider_mode == 'nvidia_nim' && secrets.NVIDIA_NIM_API_KEY || '' }}
LLM_API_KEY: ${{ steps.gate.outputs.provider_mode == 'contextual_orchestrator' && env.CONTEXTUAL_ORCHESTRATOR_TOKEN || steps.gate.outputs.provider_mode == 'github_models' && (secrets.STRIX_GITHUB_MODELS_TOKEN || github.token) || steps.gate.outputs.provider_mode == 'openai_direct' && (secrets.STRIX_OPENAI_API_KEY || secrets.OPENAI_API_KEY) || steps.gate.outputs.provider_mode == 'openrouter' && secrets.OPENROUTER_API_KEY || steps.gate.outputs.provider_mode == 'nvidia_nim' && secrets.NVIDIA_NIM_API_KEY || '' }}
run: |
# Sanitize CR/LF before masking to prevent broken ::add-mask::
# commands and potential workflow command injection.
Expand All @@ -768,11 +801,15 @@ jobs:
- name: Prepare LLM API key input file
if: steps.gate.outputs.enabled == 'true'
env:
LLM_API_KEY_SECRET: ${{ steps.gate.outputs.provider_mode == 'github_models' && (secrets.STRIX_GITHUB_MODELS_TOKEN || github.token) || steps.gate.outputs.provider_mode == 'openai_direct' && (secrets.STRIX_OPENAI_API_KEY || secrets.OPENAI_API_KEY) || steps.gate.outputs.provider_mode == 'openrouter' && secrets.OPENROUTER_API_KEY || steps.gate.outputs.provider_mode == 'nvidia_nim' && secrets.NVIDIA_NIM_API_KEY || '' }}
LLM_API_KEY_SECRET: ${{ steps.gate.outputs.provider_mode == 'contextual_orchestrator' && env.CONTEXTUAL_ORCHESTRATOR_TOKEN || steps.gate.outputs.provider_mode == 'github_models' && (secrets.STRIX_GITHUB_MODELS_TOKEN || github.token) || steps.gate.outputs.provider_mode == 'openai_direct' && (secrets.STRIX_OPENAI_API_KEY || secrets.OPENAI_API_KEY) || steps.gate.outputs.provider_mode == 'openrouter' && secrets.OPENROUTER_API_KEY || steps.gate.outputs.provider_mode == 'nvidia_nim' && secrets.NVIDIA_NIM_API_KEY || '' }}
PROVIDER_MODE: ${{ steps.gate.outputs.provider_mode }}
run: |
sanitized="$(printf '%s' "$LLM_API_KEY_SECRET" | tr -d '\r\n')"
trimmed="$(printf '%s' "$sanitized" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
if [ -z "$trimmed" ] && [ "$PROVIDER_MODE" = "contextual_orchestrator" ]; then
echo '::error::CONTEXTUAL_ORCHESTRATOR_TOKEN is required for gateway-backed Strix scans.'
exit 1
fi
if [ -z "$trimmed" ] && [ "$PROVIDER_MODE" = "github_models" ]; then
echo '::error::STRIX_GITHUB_MODELS_TOKEN is required for GitHub Models Strix scans.'
exit 1
Expand All @@ -794,6 +831,21 @@ jobs:
printf '%s' "$trimmed" > "$llm_api_key_file"
echo "LLM_API_KEY_FILE=$llm_api_key_file" >> "$GITHUB_ENV"

- name: Prepare contextual-orchestrator API base
if: steps.gate.outputs.provider_mode == 'contextual_orchestrator'
env:
CONTEXTUAL_ORCHESTRATOR_BASE_URL: ${{ env.CONTEXTUAL_ORCHESTRATOR_BASE_URL }}
run: |
set -euo pipefail
if ! [[ "$CONTEXTUAL_ORCHESTRATOR_BASE_URL" =~ ^http://127\.0\.0\.1:[0-9]{1,5}$ ]]; then
echo '::error::The contextual-orchestrator API base must remain on exact IPv4 loopback.'
exit 1
fi
umask 077
llm_api_base_file="$RUNNER_TEMP/llm_api_base.txt"
printf '%s' "${CONTEXTUAL_ORCHESTRATOR_BASE_URL}/v1" > "$llm_api_base_file"
echo "LLM_API_BASE_FILE=$llm_api_base_file" >> "$GITHUB_ENV"

- name: Prepare OpenRouter API base
if: steps.gate.outputs.provider_mode == 'openrouter'
run: |
Expand Down Expand Up @@ -929,6 +981,9 @@ jobs:
strix_llm_file="$RUNNER_TEMP/strix_llm.txt"
strix_model="$(printf '%s' "$STRIX_MODEL" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
case "$strix_model" in
contextual-orchestrator/orchestrator/free)
printf '%s' 'openai/orchestrator/free' > "$strix_llm_file"
;;
openai/gpt-5-mini* | openai/gpt-5-nano* | \
openai/openai/gpt-5-mini* | openai/openai/gpt-5-nano* | \
github_models/openai/gpt-5-mini* | github_models/openai/gpt-5-nano*)
Expand Down Expand Up @@ -959,7 +1014,7 @@ jobs:
printf '%s' "$strix_model" > "$strix_llm_file"
;;
*)
echo '::error::STRIX_LLM must select NVIDIA NIM Nemotron, GitHub Models openai/gpt-5 or newer, direct OpenAI GPT-5.4 or newer, OpenRouter openrouter/free, or an approved organization Vertex AI model.'
echo '::error::STRIX_LLM must select contextual-orchestrator/orchestrator/free, NVIDIA NIM Nemotron, GitHub Models openai/gpt-5 or newer, direct OpenAI GPT-5.4 or newer, OpenRouter openrouter/free, or an approved organization Vertex AI model.'
exit 1
;;
esac
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ this file. The format follows Keep a Changelog, and versioned releases follow
Semantic Versioning where the repository publishes a release.

## [Unreleased]
- Required Strix security evidence now uses the existing vendored
`contextual-orchestrator` sidecar and its fail-closed `orchestrator/free`
ZDR-first zero-cost pool for normal scans. Direct NVIDIA NIM, OpenRouter,
GitHub Models, OpenAI, and Vertex paths remain available only when an
authorized `repository_dispatch` explicitly supplies `strix_llm` for
diagnosis. Gateway startup, loopback binding, bearer-token masking, and an
isolated `--target` dependency tree fail closed; Strix receives only the
loopback OpenAI-compatible token/base while provider credentials stay inside
the sidecar process. The gateway route owns provider/model failover, so the
scanner does not append a second direct-provider fallback chain.
- Central review now routes through the vendored `contextual-orchestrator`
gateway sidecar: the write-capable PR autofix and the shared `opencode.jsonc`
default use the fail-closed zero-cost pool `orchestrator/free`, with
Expand Down
61 changes: 61 additions & 0 deletions docs/adr/0004-strix-contextual-orchestrator-authority.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# ADR-0004: contextual-orchestrator owns normal Strix provider routing

- Status: Proposed
- Date: 2026-08-28
- Owners: ContextualWisdomLab central CI maintainers
- Figma File ID: N/A (workflow/control-plane change; no customer UI)

## Context

Required Strix scans were serialized per repository, but each scan still owned a
hard-coded direct provider chain. A live DiskSage exact-head scan exhausted four
independent paths in one run: NVIDIA rate limiting, an unavailable NVIDIA model,
an OpenRouter upstream error, and exhausted direct OpenAI credit. No authoritative
vulnerability report existed, so the required check correctly failed closed, but
consumer product PRs could not repair the shared authority boundary.

The central repository already vendors a pinned contextual-orchestrator sidecar.
It registers the five organization provider credentials in a process-local KV,
performs live discovery, applies the reviewed zero-cost/ZDR policy, and exposes
`orchestrator/free` through an authenticated OpenAI-compatible loopback API.

## Decision

Normal Strix scans SHALL provision that sidecar and call
`openai/orchestrator/free` through exact IPv4 loopback. The sidecar owns
provider/model discovery and fallback. Strix SHALL NOT add a second direct
fallback chain for the gateway-backed route.

A caller MAY use `repository_dispatch.strix_llm` to select an existing direct
provider model for bounded diagnosis. That override is explicit, auditable, and
does not change the normal default.

The sidecar dependency tree SHALL be installed into an isolated `--target`
directory so it cannot rewrite the hash-locked Strix runtime. Its generated
bearer token SHALL be line-safe, masked before export, and passed to Strix only
through a mode-specific file. Missing credentials, unhealthy startup, non-loopback
base URLs, invalid ports, and missing tokens fail closed.

## Consequences

- Shared provider outages are handled by one routing authority instead of nested
retry/fallback loops.
- Provider credentials remain inside the gateway process; the scanner sees only
a short-lived loopback credential.
- A gateway outage remains non-passing security evidence.
- Existing direct-provider diagnostic contracts and their tests remain supported.
- After merge, consumer PRs require a fresh exact-head Strix run; predecessor
outage evidence is not transferred.

## Verification

- RED/GREEN static contract for the workflow, model namespace, loopback and token.
- Bounded required-workflow smoke contract.
- Bash syntax and YAML parse.
- Existing full organization Checks, independent review, and protected merge.

## Rollback

Revert this ADR and its workflow commit. Do not partially restore a direct default
while leaving gateway key/base files active. Re-run the complete required Strix
contract and affected consumer exact heads after rollback.
51 changes: 51 additions & 0 deletions docs/doctoring/strix-contextual-orchestrator-gateway.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Strix contextual-orchestrator gateway doctoring

## Failure evidence

The triggering consumer scan produced no vulnerability artifact. Its terminal
log recorded provider infrastructure failures across NVIDIA NIM, OpenRouter, and
direct OpenAI, followed by the existing fail-closed
`STRIX_PROVIDER_UNAVAILABLE` classification. Repository Test, Release, SAST, and
Security workflows were independently successful on the same consumer head.

## Causal boundary

The defect is not in the consumer product tree. It is the duplicated routing
authority in central Strix: the scanner selected and retried direct providers even
though the organization already had a pinned contextual-orchestrator gateway with
model discovery, ZDR policy, and provider-family diversity.

## Corrective control

```text
five provider credentials
→ process-local contextual-orchestrator KV
→ live discovery + ZDR-first zero-cost catalog
→ authenticated 127.0.0.1 OpenAI-compatible API
→ Strix openai/orchestrator/free
→ authoritative report or fail-closed required check
```

Direct providers are retained only for an explicit diagnostic override. The
normal gateway route has no scanner-owned fallback list.

## Security and operability

- The sidecar is pinned by commit SHA.
- Provider credentials never become Strix key files in gateway mode.
- The bearer token is generated per job, rejects line breaks, and is masked.
- The base URL must be exact IPv4 loopback with a valid port.
- Sidecar packages use an isolated target directory rather than the scanner's
hash-locked environment.
- Health failure, empty discovery, missing credentials, and provider exhaustion
remain non-passing.
- Consumer PRs are rechecked on unchanged exact heads after the central fix.

## Traceability

- ADR: `docs/adr/0004-strix-contextual-orchestrator-authority.md`
- Workflow: `.github/workflows/strix.yml`
- Sidecar: `scripts/ci/contextual_orchestrator_review_sidecar.sh`
- Required smoke: `scripts/ci/strix_required_workflow_smoke.sh`
- Contract: `tests/test_strix_contextual_orchestrator_contract.py`
- Predecessor gateway ADR: `docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md`
16 changes: 16 additions & 0 deletions docs/product-technical-gap-baseline.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@

이 문서는 제품·기술·운영 Gap을 현재 문서와 현재 GitHub 상태에 묶어 두는 기준선이다. 새 작업은 먼저 이 문서의 Gap ID를 PR 설명과 테스트 증거에 연결하고, PR의 정확한 exact HEAD·Checks·리뷰를 다시 수집한 뒤 구현한다. 표의 상태는 작성 시점의 관측값이므로, 병합 판단에는 재사용하지 않는다. 이 인벤토리는 스냅샷이며 merge authorization이 아니다.

## 2026-08-28 live delta — Strix provider authority

- DiskSage #264의 exact-head 제품·Release·SAST·Security 검증은 성공했지만,
중앙 Strix는 NVIDIA 429, NVIDIA fallback 404, OpenRouter 502, OpenAI
`insufficient_quota`가 연속 발생해 권위 있는 취약점 보고서를 만들지 못하고
`STRIX_PROVIDER_UNAVAILABLE`로 fail-closed 종료했다. 이 결과를 제품 결함이나
성공 증거로 오인하지 않는다.
- 정상 Strix 실행의 provider/model 선택 권한은 vendored
`contextual-orchestrator`의 `orchestrator/free` ZDR-first pool로 이동한다.
명시적 `repository_dispatch.strix_llm`만 기존 direct-provider 진단 경로를
선택할 수 있다. Gateway 실패는 direct fallback으로 위장하지 않고 required
Check를 실패시킨다.
- 소비 저장소 PR은 중앙 provider outage를 고치기 위한 no-op commit이나 반복
rerun을 만들지 않는다. 중앙 수정이 병합된 뒤 unchanged exact head에 새 Strix
evidence를 dispatch하고, 독립 승인과 모든 required Check를 다시 요구한다.

## 1. 근거와 범위

### 1.1 우선순위가 높은 근거
Expand Down
Loading
Loading