Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
853 changes: 190 additions & 663 deletions .github/workflows/opencode-review.yml

Large diffs are not rendered by default.

99 changes: 9 additions & 90 deletions .github/workflows/pr-review-merge-scheduler.yml
Original file line number Diff line number Diff line change
@@ -1,60 +1,6 @@
name: PR Review Merge Scheduler

on:
pull_request_target:
types: [opened, synchronize, reopened, ready_for_review]
workflow_call:
inputs:
dry_run:
description: Print planned actions without mutating PRs
required: false
default: false
type: boolean
max_prs:
description: Maximum open PRs to inspect
required: false
default: "100"
type: string
trigger_reviews:
description: Dispatch OpenCode Review for PR heads without current approval
required: false
default: true
type: boolean
enable_auto_merge:
description: Enable auto-merge for current-head approved PRs
required: false
default: true
type: boolean
merge_mode:
description: "Merge behavior for current-head approved PRs: auto, direct, or disabled"
required: false
default: auto
type: string
update_branches:
description: Update outdated PR branches after OpenCode approval
required: false
default: true
type: boolean
stale_opencode_minutes:
description: Redispatch OpenCode Review when an in-progress OpenCode check is older than this many minutes
required: false
default: "45"
type: string
project_flow:
description: Project flow, usually github-flow or git-flow
required: false
default: ""
type: string
base_branch:
description: Base branch to scan; defaults to the caller repository default branch
required: false
default: ""
type: string
canonical_ref:
description: Ref of ContextualWisdomLab/.github to use for scheduler code
required: false
default: "main"
type: string
schedule:
- cron: "17 */2 * * *"
workflow_dispatch:
Expand All @@ -78,22 +24,14 @@ on:
required: false
default: true
type: boolean
merge_mode:
description: "Merge behavior for current-head approved PRs: auto, direct, or disabled"
required: false
default: auto
update_branches:
description: Update outdated PR branches after OpenCode approval
required: false
default: true
type: boolean
stale_opencode_minutes:
description: Redispatch OpenCode Review when an in-progress OpenCode check is older than this many minutes
required: false
default: "45"

concurrency:
group: central-pr-review-merge-scheduler-${{ github.repository }}
group: pr-review-merge-scheduler
cancel-in-progress: false

jobs:
Expand All @@ -106,24 +44,18 @@ jobs:
pull-requests: write
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
GH_TOKEN: ${{ github.token }}
DEFAULT_BRANCH: ${{ inputs.base_branch || github.event.repository.default_branch }}
DRY_RUN: ${{ inputs.dry_run == true }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run == true }}
MAX_PRS: ${{ inputs.max_prs || '100' }}
PROJECT_FLOW_INPUT: ${{ inputs.project_flow || vars.PROJECT_FLOW || '' }}
PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number || '' }}
TRIGGER_REVIEWS: ${{ github.event_name == 'schedule' || github.event_name == 'pull_request_target' || inputs.trigger_reviews == true }}
ENABLE_AUTO_MERGE: ${{ github.event_name == 'schedule' || github.event_name == 'pull_request_target' || inputs.enable_auto_merge == true }}
MERGE_MODE: ${{ inputs.merge_mode || vars.PR_MERGE_MODE || 'auto' }}
UPDATE_BRANCHES: ${{ github.event_name == 'schedule' || github.event_name == 'pull_request_target' || inputs.update_branches == true }}
STALE_OPENCODE_MINUTES: ${{ inputs.stale_opencode_minutes || vars.STALE_OPENCODE_MINUTES || '45' }}
CANONICAL_REF: ${{ inputs.canonical_ref || 'main' }}
PROJECT_FLOW: ${{ vars.PROJECT_FLOW || 'git-flow' }}
TRIGGER_REVIEWS: ${{ github.event_name != 'workflow_dispatch' || inputs.trigger_reviews == true }}
ENABLE_AUTO_MERGE: ${{ github.event_name != 'workflow_dispatch' || inputs.enable_auto_merge == true }}
UPDATE_BRANCHES: ${{ github.event_name != 'workflow_dispatch' || inputs.update_branches == true }}
steps:
- name: Checkout trusted scheduler
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: ContextualWisdomLab/.github
ref: ${{ env.CANONICAL_REF }}
fetch-depth: 1

- name: Self-test scheduler
Expand All @@ -132,25 +64,13 @@ jobs:
- name: Inspect PR review and merge queue
run: |
set -euo pipefail
project_flow="$PROJECT_FLOW_INPUT"
if [ -z "$project_flow" ]; then
case "$DEFAULT_BRANCH" in
main|master) project_flow="github-flow" ;;
develop) project_flow="git-flow" ;;
*) project_flow="github-flow" ;;
esac
fi
args=(
--repo "$GITHUB_REPOSITORY"
--base-branch "$DEFAULT_BRANCH"
--max-prs "$MAX_PRS"
--project-flow "$project_flow"
--project-flow "$PROJECT_FLOW"
--review-workflow "OpenCode Review"
--stale-opencode-minutes "$STALE_OPENCODE_MINUTES"
)
if [ -n "$PULL_REQUEST_NUMBER" ]; then
args+=(--pr-number "$PULL_REQUEST_NUMBER")
fi
if [ "$DRY_RUN" = "true" ]; then
args+=(--dry-run)
fi
Expand All @@ -164,7 +84,6 @@ jobs:
else
args+=(--no-enable-auto-merge)
fi
args+=(--merge-mode "$MERGE_MODE")
if [ "$UPDATE_BRANCHES" = "true" ]; then
args+=(--update-branches)
else
Expand Down
110 changes: 19 additions & 91 deletions .github/workflows/strix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,10 @@ on:
concurrency:
group: >-
strix-${{ github.repository }}-${{ github.event_name == 'pull_request_target' &&
format('pr-{0}-{1}', github.event.pull_request.number, github.event.pull_request.head.sha) ||
github.event.inputs.pr_number != '' && github.event.inputs.pr_head_sha != '' &&
format('pr-{0}-{1}', github.event.inputs.pr_number, github.event.inputs.pr_head_sha) ||
github.event.inputs.pr_number != '' && format('pr-{0}', github.event.inputs.pr_number) || github.ref }}
format('pr-{0}', github.event.pull_request.number) || github.event.inputs.pr_number != '' &&
format('pr-{0}', github.event.inputs.pr_number) || github.ref }}
# cancel-in-progress deliberately disabled: an attacker could force-push
# a benign commit to cancel an in-progress scan of a malicious commit. The
# head SHA in PR groups prevents stale scans from serializing newer evidence.
# a benign commit to cancel an in-progress scan of a malicious commit.
cancel-in-progress: false

permissions:
Expand All @@ -62,93 +59,25 @@ jobs:
with:
python-version: "3.13"

- name: Resolve trusted Strix source ref
id: trusted_source
env:
JOB_CONTEXT_JSON: ${{ toJSON(job) }}
GITHUB_CONTEXT_JSON: ${{ toJSON(github) }}
run: |
set -euo pipefail
python3 <<'PY' >>"$GITHUB_OUTPUT"
import json
import os
import re
import sys

try:
job_context = json.loads(os.environ.get("JOB_CONTEXT_JSON") or "{}")
github_context = json.loads(os.environ.get("GITHUB_CONTEXT_JSON") or "{}")
except json.JSONDecodeError as exc:
print(f"::error::Could not parse GitHub workflow context JSON: {exc}", file=sys.stderr)
raise SystemExit(1)

trusted_repository = str(
job_context.get("workflow_repository") or "ContextualWisdomLab/.github"
).strip()
trusted_ref = str(
job_context.get("workflow_sha") or github_context.get("workflow_sha") or ""
).strip()
workflow_ref = str(
job_context.get("workflow_ref") or github_context.get("workflow_ref") or ""
).strip()

if not trusted_ref:
trusted_ref = "main"
prefix = "ContextualWisdomLab/.github/.github/workflows/strix.yml@"
if workflow_ref.startswith(prefix):
trusted_ref = workflow_ref.split("@", 1)[1]

if not re.fullmatch(r"[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+", trusted_repository):
print("::error::Trusted workflow repository resolved to an invalid name.", file=sys.stderr)
raise SystemExit(1)
if not re.fullmatch(r"[0-9a-fA-F]{40}|refs/[^\s]+|[A-Za-z0-9._/-]+", trusted_ref):
print("::error::Trusted workflow ref resolved to an invalid value.", file=sys.stderr)
raise SystemExit(1)

print(f"repository={trusted_repository}")
print(f"ref={trusted_ref}")
PY

- name: Checkout trusted Strix source
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ${{ steps.trusted_source.outputs.repository }}
fetch-depth: 1
persist-credentials: false
ref: ${{ steps.trusted_source.outputs.ref }}
path: trusted-strix-source

- name: Export trusted Strix source paths
run: |
set -euo pipefail
trusted_strix_source="$GITHUB_WORKSPACE/trusted-strix-source"
test -f "$trusted_strix_source/scripts/ci/strix_quick_gate.sh"
test -f "$trusted_strix_source/scripts/ci/test_strix_quick_gate.sh"
test -f "$trusted_strix_source/scripts/ci/strix_required_workflow_smoke.sh"
{
echo "TRUSTED_STRIX_SOURCE=$trusted_strix_source"
echo "TRUSTED_STRIX_GATE=$trusted_strix_source/scripts/ci/strix_quick_gate.sh"
echo "TRUSTED_STRIX_GATE_TEST=$trusted_strix_source/scripts/ci/test_strix_quick_gate.sh"
echo "TRUSTED_STRIX_REQUIRED_SMOKE=$trusted_strix_source/scripts/ci/strix_required_workflow_smoke.sh"
} >> "$GITHUB_ENV"

- name: Materialize target workspace
- name: Materialize trusted workspace
env:
GH_TOKEN: ${{ github.token }}
REPOSITORY: ${{ github.repository }}
TARGET_WORKSPACE_SHA: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.base.sha || github.sha }}
TRUSTED_WORKSPACE_SHA: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.base.sha || github.sha }}
run: |
set -euo pipefail
trusted_workspace="$RUNNER_TEMP/trusted-workspace"
mkdir -p "$trusted_workspace"
git init -q "$trusted_workspace"
gh auth setup-git
git -C "$trusted_workspace" remote add origin "$GITHUB_SERVER_URL/$REPOSITORY.git"
git -C "$trusted_workspace" fetch --no-tags --depth=1 origin "$TARGET_WORKSPACE_SHA"
git -C "$trusted_workspace" checkout --detach --quiet "$TARGET_WORKSPACE_SHA"
git -C "$trusted_workspace" cat-file -e "$TARGET_WORKSPACE_SHA^{commit}"
git -C "$trusted_workspace" fetch --no-tags --depth=1 origin "$TRUSTED_WORKSPACE_SHA"
git -C "$trusted_workspace" checkout --detach --quiet "$TRUSTED_WORKSPACE_SHA"
git -C "$trusted_workspace" cat-file -e "$TRUSTED_WORKSPACE_SHA^{commit}"
{
echo "TRUSTED_WORKSPACE=$trusted_workspace"
echo "TRUSTED_STRIX_GATE=$trusted_workspace/scripts/ci/strix_quick_gate.sh"
echo "TRUSTED_STRIX_GATE_TEST=$trusted_workspace/scripts/ci/test_strix_quick_gate.sh"
} >> "$GITHUB_ENV"

- name: Fetch pull request head for trusted scan
Expand Down Expand Up @@ -211,13 +140,13 @@ jobs:
echo "::error::PR head ref did not resolve to expected commit $PR_HEAD_SHA after retries." >&2
exit 1

- name: Self-test Strix required workflow contract
timeout-minutes: 2
working-directory: trusted-strix-source
- name: Self-test Strix gate script
timeout-minutes: 10
working-directory: ${{ runner.temp }}/trusted-workspace
run: |
set -euo pipefail
printf 'Running bounded Strix required-workflow smoke test.\n'
bash "$TRUSTED_STRIX_REQUIRED_SMOKE"
printf 'Running Strix gate self-test with a 10-minute step timeout.\n'
bash "$TRUSTED_STRIX_GATE_TEST"

- name: Gate Strix secrets
id: gate
Expand Down Expand Up @@ -282,7 +211,7 @@ jobs:

- name: Install Strix
if: steps.gate.outputs.enabled == 'true'
working-directory: trusted-strix-source
working-directory: ${{ runner.temp }}/trusted-workspace
run: |
python3 -m pip install --disable-pip-version-check --no-cache-dir --require-hashes -r requirements-strix-ci-hashes.txt

Expand Down Expand Up @@ -431,7 +360,6 @@ jobs:
working-directory: ${{ runner.temp }}/trusted-workspace
env:
STRIX_LLM_FILE: ${{ env.STRIX_LLM_FILE }}
STRIX_REPO_ROOT: ${{ runner.temp }}/trusted-workspace
LLM_API_BASE_FILE: ${{ env.LLM_API_BASE_FILE }}
STRIX_LLM_DEFAULT_PROVIDER: ${{ steps.gate.outputs.provider_mode == 'vertex_ai' && 'vertex_ai' || 'openai' }}
LLM_API_KEY_FILE: ${{ env.LLM_API_KEY_FILE }}
Expand All @@ -449,9 +377,9 @@ jobs:
STRIX_SOURCE_DIRS: ". backend frontend"
STRIX_REASONING_EFFORT: low
STRIX_LLM_MAX_RETRIES: 1
STRIX_TRANSIENT_RETRY_PER_MODEL: 2
STRIX_TRANSIENT_RETRY_PER_MODEL: 5
STRIX_TRANSIENT_RETRY_BACKOFF_SECONDS: 60
STRIX_FALLBACK_MODELS: ${{ steps.gate.outputs.provider_mode == 'github_models' && 'github_models/deepseek/deepseek-v3-0324 github_models/deepseek/deepseek-r1-0528' || '' }}
STRIX_FALLBACK_MODELS: ${{ steps.gate.outputs.provider_mode == 'github_models' && 'github_models/deepseek/deepseek-r1-0528 github_models/deepseek/deepseek-v3-0324' || '' }}
STRIX_FAIL_ON_PROVIDER_SIGNAL: "1"
STRIX_VERTEX_FALLBACK_MODELS: ""
NPM_CONFIG_IGNORE_SCRIPTS: "true"
Expand Down Expand Up @@ -512,7 +440,7 @@ jobs:
publish-manual-pr-evidence-status:
name: publish-manual-pr-evidence-status
needs: strix
if: ${{ always() && !cancelled() && github.event_name == 'workflow_dispatch' && github.event.inputs.pr_head_sha != '' }}
if: ${{ always() && github.event_name == 'workflow_dispatch' && github.event.inputs.pr_head_sha != '' }}
runs-on: ubuntu-latest
permissions:
statuses: write
Expand Down
4 changes: 0 additions & 4 deletions .gitignore

This file was deleted.

6 changes: 0 additions & 6 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
## 2024-06-21 - Python JSON Decoding Optimization
**Learning:** In Python, string slicing `text[index:]` inside a loop can cause O(N^2) complexity and severe memory copying overhead. When decoding JSON incrementally from a large text blob, `json.JSONDecoder().raw_decode(text, index)` can parse from a given index without slicing. Combining this with `text.find("{", index)` to skip irrelevant characters is significantly faster than `enumerate(text)`.
**Action:** Always prefer `raw_decode(text, index)` and `string.find()` over string slicing and character-by-character iteration when scanning large files for JSON objects.
## 2024-06-23 - `iter_json_objects` 최적화
**Learning:** Python의 `json.JSONDecoder().raw_decode()`를 사용할 때 문자열을 하나씩 순회하며 슬라이싱(`text[index:]`)을 수행하면, O(N^2)의 메모리 할당 및 복사 작업이 발생하여 매우 큰 병목(Bottleneck)이 될 수 있습니다.
**Action:** `str.find("{", index)`를 사용하여 JSON 객체의 시작 위치를 빠르게 건너뛰고, `raw_decode(text, index)`에서 제공하는 `idx` 인자를 활용해 슬라이싱 없이 직접 파싱을 수행하여 최적화합니다.
## 2024-11-20 - JSON Decoding Performance - Index Advancement
**Learning:** Even when avoiding string slicing using `json.JSONDecoder().raw_decode(text, index)`, failing to correctly advance the index by ignoring the returned `end` index (`value, _ = decoder.raw_decode(...)`) forces the search loop to repeatedly attempt to decode nested JSON structures (e.g., inner braces `{`) sequentially. This leads to massive O(N^2) time complexity and redundant parsing for large, deeply nested JSON objects.
**Action:** Always capture and use the new end index returned by `raw_decode` (e.g., `value, next_idx = decoder.raw_decode(text, index)`) to jump over the completely parsed object and proceed efficiently.
Loading
Loading