Skip to content
Draft
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: 4 additions & 1 deletion .github/workflows/strix-changed-path-quality-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@ on:
pull_request:
branches: [main]
paths:
- ".github/workflows/strix.yml"
- ".github/workflows/strix-changed-path-quality-ci.yml"
- "CHANGELOG.md"
- "docs/doctoring/strix-legal-git-paths.md"
- "docs/doctoring/strix-quality-timeout-fixtures.md"
- "docs/doctoring/strix-scan-mode-dual-flow.md"
- "scripts/ci/strix_quick_gate.sh"
- "scripts/ci/test_strix_quick_gate.sh"
- "tests/test_strix_changed_path_policy.py"
- "tests/test_strix_workflow_dependency_hashes.py"
- "tests/test_strix_quality_timeout_fixture_budget.py"
- "tests/test_strix_scan_mode_policy.py"

permissions:
contents: read
Expand Down Expand Up @@ -66,6 +69,6 @@ jobs:
test "$(git rev-parse HEAD)" = "${{ github.event.pull_request.head.sha || github.sha }}"
python -m coverage run -m pytest tests -q
bash scripts/ci/test_strix_quick_gate.sh
python -m compileall -q tests/test_strix_changed_path_policy.py tests/test_strix_workflow_dependency_hashes.py tests/test_strix_quality_timeout_fixture_budget.py
python -m compileall -q tests/test_strix_changed_path_policy.py tests/test_strix_workflow_dependency_hashes.py tests/test_strix_quality_timeout_fixture_budget.py tests/test_strix_scan_mode_policy.py
bash -n scripts/ci/strix_quick_gate.sh
git diff --exit-code
32 changes: 25 additions & 7 deletions .github/workflows/strix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,18 @@ on:
- '.github/ISSUE_TEMPLATE/**'
schedule:
# Weekly scan on protected branches (Mondays at 03:00 UTC).
# Official Strix standard mode: 30–60 min; current 120/100/90/95 budget
# is honest. CWL has no consistent RC-tag or prerelease convention, so
# this weekly pass is the scheduled pre-release depth. Do not invent a
# GitHub release event or RC tag pattern to compensate.
- cron: '0 3 * * 1'
# Default-branch-only retry entrypoint; no caller-selected workflow ref.
# Remains PR-metadata-bound same-head evidence (quick). It cannot scan a
# branch or tag release candidate. Do not restore workflow_dispatch here:
# GitHub's manual UI/API lets the caller select a branch, and that revision
# supplies the workflow YAML (id-token + statuses:write) before any in-job
# trusted-source checkout can run. Deep stays unwired on this privileged
# file until a separately reviewed default-branch-only dispatcher exists.
repository_dispatch:
types: [strix-scan]

Expand Down Expand Up @@ -92,11 +102,9 @@ jobs:

strix:
if: github.event_name != 'pull_request_target' || github.event.action != 'closed'
# Large repositories can require a legitimate full-hour review. The scanner
# gets a 90-minute process budget and a 95-minute total retry budget; the
# 100-minute step and 120-minute job leave deterministic time to preserve
# partial reports and publish a concrete failure reason. Hitting any cap is
# fail-closed and never turns an incomplete scan into an approval.
# Quick and standard share the honest 120/100/90/95 budget (standard is
# 30–60 min). Deep is not selected on this privileged workflow. Do not
# raise the required pull_request_target / repository_dispatch path to 360.
timeout-minutes: 120
runs-on: ubuntu-latest
# Least-privilege token scoped to this job (Scorecard alert #43): the scan
Expand All @@ -110,6 +118,13 @@ jobs:
statuses: write
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
# Official CLI modes only (quick|standard|deep). Dual-flow mapping:
# required PR / repository_dispatch / push develop → quick;
# push main|master and weekly schedule → standard.
# repository_dispatch must not inherit standard from github.ref
# (dispatch SHA is the default branch, often main). This privileged
# workflow never selects deep and never exposes workflow_dispatch.
STRIX_SCAN_MODE: ${{ github.event_name == 'schedule' && 'standard' || github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master') && 'standard' || 'quick' }}
steps:
- name: Harden runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
Expand Down Expand Up @@ -781,7 +796,7 @@ jobs:
esac
echo "STRIX_LLM_FILE=$strix_llm_file" >> "$GITHUB_ENV"

- name: Run Strix (quick)
- name: Run Strix
if: steps.gate.outputs.enabled == 'true'
timeout-minutes: 100
# Security invariant for pull_request_target: execute only from the
Expand Down Expand Up @@ -835,10 +850,13 @@ jobs:
run: |
budget_suffix="TIME""OUT"
process_budget_seconds="5400"
total_budget_seconds="5700"
printf 'Using official Strix scan mode %s with process budget %ss.\n' \
"${STRIX_SCAN_MODE}" "$process_budget_seconds"
export "LLM_${budget_suffix}=900"
export "STRIX_MEMORY_COMPRESSOR_${budget_suffix}=300"
export "STRIX_PROCESS_${budget_suffix}_SECONDS=$process_budget_seconds"
export "STRIX_TOTAL_${budget_suffix}_SECONDS=5700"
export "STRIX_TOTAL_${budget_suffix}_SECONDS=$total_budget_seconds"

# Capture the gate exit code plus its console output. The gate returns
# exit 1 both for genuine blocking vulnerabilities AND for
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Semantic Versioning where the repository publishes a release.

### Changed

- Map the organization-required Strix workflow to official CLI modes (`quick`, `standard`) from the GitHub event instead of inheriting Quick on every trigger. Required `pull_request_target` and `repository_dispatch` `strix-scan` evidence stay Quick; `push` to `develop` stays Quick; `push` to `main`/`master` and the weekly Monday 03:00 UTC schedule use Standard with the existing 120/100/90/95 budget. Deep stays unwired on this privileged file: restoring `workflow_dispatch` would let a caller-selected branch supply `id-token` and `strix` status publication before any trusted-source checkout. CWL's dual GitHub Flow / Git Flow setup has no consistent RC-tag or prerelease convention, so this change does not invent `release:`, `v*-rc*`, or `client_payload.scan_mode`. The gate now allowlists official mode names (`quick|standard|deep`) and rejects `normal`.
- Require the hourly repair worker to establish an exact-head root cause, enumerate the smallest remediation candidates, and prove writer authority, sealed-path scope, credentials, dependency order, verifiability, and causal effect before editing; infeasible or external blockers leave the tree unchanged while the broader loop continues with another eligible PR or buyer-visible product gap.
- Run the bounded Quarantine Sandbox Runtime heartbeat at minute 14 without granting the caller model secrets, repository mutation permissions, approval, merge, release, artifact-execution, or final security-verdict authority.
- Run the bounded Clearfolio PR review-feedback repair caller at minute 23 of every hour while keeping the shared scheduler free of product-specific timers and repository names for modular reuse by naruon, contextual-orchestrator, Inkspan, and other CWL services.
Expand Down
71 changes: 71 additions & 0 deletions docs/doctoring/strix-scan-mode-dual-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Strix official scan-mode mapping for dual GitHub / Git Flow

근거 기준일: **2026-08-16**

## Incident

The organization-required Strix workflow always ran Quick. `scripts/ci/strix_quick_gate.sh` already forwarded `STRIX_SCAN_MODE` (default `quick`) as `strix -n -t . --scan-mode $SCAN_MODE`, but `.github/workflows/strix.yml` never set the environment variable. Every current event therefore inherited Quick: `pull_request_target`, `push` to `main`/`develop`/`master`, the Monday 03:00 UTC `schedule`, and `repository_dispatch` type `strix-scan`.

Official Strix CLI modes are only `quick`, `standard`, and `deep` (Strix, n.d.-a). There is no `normal` alias. Quick is the CI/PR path (minutes). Standard is pre-release / weekly (30 min–1 h). Deep is pre-production (1–4 h) and is the CLI default; CWL must keep choosing explicitly so required PR evidence never inherits Deep.

The previous 120 / 100 / 90 / 95-minute budget is honest for Quick and Standard. It cannot finish Deep. GitHub-hosted jobs max out at 360 minutes.

## Decision

ContextualWisdomLab runs both GitHub Flow (`main`/`master` is the base) and Git Flow (`develop` is the base). That dual-flow setup is incomplete: there is no consistent RC-tag, prerelease, or GitHub `release` event convention, and RankWeave forbids prerelease GitHub Releases. This change therefore does **not** invent `release:` or `v*-rc*` triggers.

Confirmed event → official mode mapping:

| Event | Mode | Job / step / process / total |
|---|---|---|
| `pull_request_target` | `quick` | 120 / 100 / 5400 / 5700 |
| `repository_dispatch` `strix-scan` | `quick` | 120 / 100 / 5400 / 5700 |
| `push` to `develop` | `quick` | 120 / 100 / 5400 / 5700 |
| `push` to `main` or `master` | `standard` | 120 / 100 / 5400 / 5700 |
| `schedule` (Monday 03:00 UTC) | `standard` | 120 / 100 / 5400 / 5700 |

`repository_dispatch` remains the default-branch-only, PR-metadata-bound same-head retry. It cannot scan a branch or tag release candidate. The mapping requires `github.event_name == 'push'` before treating `refs/heads/main` or `refs/heads/master` as Standard, because a `repository_dispatch` SHA is the default branch and is often `main`.

Deep is **not** selected on this privileged workflow. Restoring `workflow_dispatch` on `.github/workflows/strix.yml` would let a writer choose a feature-branch workflow revision. That revision supplies the YAML—`id-token: write` plus `statuses: write` for the `strix` commit-status context—before any in-job trusted-source checkout can run (GitHub, n.d.-b; National Institute of Standards and Technology, 2022). A malicious or confused branch could skip the scan and publish a fake passing `strix` status. The same class of defect already failed `test_no_central_workflow_exposes_branch_selected_manual_dispatch` when `workflow_dispatch` was added to the quality job (see `docs/doctoring/strix-legal-git-paths.md`).

Do **not** restore `workflow_dispatch` on this file as an RC convenience. Do **not** add `client_payload.scan_mode` to the privileged `strix-scan` retry. A later Deep path must be a separately reviewed default-branch-only dispatcher that treats target repository, pull-request number, and exact head SHA as untrusted bounded data.

`require_safe_scan_mode` allowlists `quick|standard|deep` and rejects `normal` and every other string, including charset-valid aliases. The gate may still accept `deep` for local or future dispatcher use; this workflow never sets it.

Fail-closed behavior is unchanged: missing artifact, unmapped findings, infrastructure errors, PR scoping, and severity gating stay as they were. The hashed-lock installer line is not part of this change.

`pull_request_target` continues to execute trusted base scripts only.

## Verification contract

`tests/test_strix_scan_mode_policy.py` and `scripts/ci/test_strix_quick_gate.sh` fail if:

1. the event → mode expression is reverted or `repository_dispatch` can select a mode;
2. Deep job/step/process budgets appear on the required PR path;
3. a GitHub release event, RC-tag trigger, or `workflow_dispatch` is added to this privileged file;
4. `require_safe_scan_mode` accepts `normal` or any unofficial name;
5. `client_payload.scan_mode` or `github.event.inputs.scan_mode` appears in `strix.yml`.

The quality workflow trigger includes this record, the mapping test, and `.github/workflows/strix.yml` so later edits re-run exact-head evidence.

## Rollback

Roll back the mapping and this record together only if a required `pull_request_target` job is observed running Deep or a 360-minute budget. Do not restore unconditional Quick by deleting `STRIX_SCAN_MODE`. Do not restore `workflow_dispatch` on this file to recover Deep.

## Next operator action

Merge this mapping after current-head quality, security, and review evidence pass. If a buyer needs a pre-production Deep scan, open a separate default-branch-only dispatcher design; do not add inputs to `strix.yml`.

## References (APA 7th)

GitHub. (n.d.-a). *Workflow syntax for GitHub Actions*. GitHub Docs. Retrieved August 16, 2026, from https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax

GitHub. (n.d.-b). *Manually running a workflow*. GitHub Docs. Retrieved August 16, 2026, from https://docs.github.com/en/actions/how-tos/manage-workflow-runs/manually-run-a-workflow

MITRE. (n.d.). *CWE-345: Insufficient verification of data authenticity*. Retrieved August 16, 2026, from https://cwe.mitre.org/data/definitions/345.html

National Institute of Standards and Technology. (2022). *Secure software development framework (SSDF) version 1.1: Recommendations for mitigating the risk of software vulnerabilities* (NIST Special Publication 800-218). https://doi.org/10.6028/NIST.SP.800-218

Strix. (n.d.-a). *Scan modes*. Strix Docs. Retrieved August 16, 2026, from https://docs.strix.ai/usage/scan-modes

Strix. (n.d.-b). *Command-line interface*. usestrix/strix. Retrieved August 16, 2026, from https://github.com/usestrix/strix
12 changes: 9 additions & 3 deletions scripts/ci/strix_quick_gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -357,10 +357,16 @@ require_positive_integer() {

require_safe_scan_mode() {
local scan_mode="$1"
if [ -z "$scan_mode" ] || [[ ! "$scan_mode" =~ ^[[:alnum:]_.-]+$ ]]; then
echo "ERROR: STRIX_SCAN_MODE contains unsupported characters: '$scan_mode'." >&2
# Official Strix CLI modes only. There is no `normal` alias.
case "$scan_mode" in
quick | standard | deep)
return 0
;;
*)
echo "ERROR: STRIX_SCAN_MODE must be one of quick, standard, or deep; got '$scan_mode'." >&2
exit 2
fi
;;
esac
}

validate_raw_target_path_input() {
Expand Down
5 changes: 5 additions & 0 deletions scripts/ci/strix_required_workflow_smoke.sh
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ assert_file_contains "$workflow_file" 'bash "$TRUSTED_STRIX_GATE"' "Strix workfl
assert_file_contains "$workflow_file" "Self-test Strix required workflow contract" "Strix workflow uses bounded required-path smoke test"
assert_file_contains "$workflow_file" 'bash "$TRUSTED_STRIX_REQUIRED_SMOKE"' "Strix workflow executes bounded smoke test"
assert_file_contains "$workflow_file" "timeout-minutes: 2" "Strix required-path smoke test has a short timeout"
assert_file_not_contains "$workflow_file" "workflow_dispatch:" "Strix privileged workflow must not load a caller-selected workflow ref"
assert_file_contains "$workflow_file" "STRIX_SCAN_MODE:" "Strix workflow sets official scan mode from the event"
assert_file_contains "$workflow_file" "github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')" "Strix standard push mapping requires the push event"
assert_file_contains "$workflow_file" "timeout-minutes: 120" "Strix required path keeps the 120-minute job budget"
assert_file_contains "$gate_script" "quick | standard | deep)" "Strix gate allowlists official scan modes"
assert_status_permissions_scoped
assert_file_contains "$workflow_file" 'context="strix"' "Strix workflow publishes the strix commit status context"
assert_file_contains "$workflow_file" "Existing current-run Strix success status is already present" "Strix manual follow-up status publisher accepts already-published same-run evidence"
Expand Down
Loading
Loading