feat(automation): run naruon commercial readiness hourly - #709
feat(automation): run naruon commercial readiness hourly#709seonghobae wants to merge 22 commits into
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthrough고정된 ChangesNaruon 상업적 준비도 자동화 루프
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related issues
Possibly related PRs
Sequence Diagram(s)sequenceDiagram
participant HourlyWorkflow as 시간별 오케스트레이터
participant ReviewMergeScheduler as 리뷰·병합 스케줄러
participant DevelopmentWorkflow as 개발 워커
participant TargetRepository as 대상 저장소
HourlyWorkflow->>TargetRepository: develop 열린 PR 조회
HourlyWorkflow->>ReviewMergeScheduler: 리뷰 수정 및 병합 디스패치
alt 열린 PR과 활성 개발 워커가 없음
HourlyWorkflow->>DevelopmentWorkflow: 제품 개발 디스패치
DevelopmentWorkflow->>TargetRepository: 변경 검증 후 단일 PR 생성
DevelopmentWorkflow->>ReviewMergeScheduler: 현재 헤드 리뷰·병합 디스패치
end
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
seonghobae
left a comment
There was a problem hiding this comment.
Two least-privilege issues must be fixed before merge:
- The target-repository write credential is exported as
GITHUB_TOKENto the untrusted-context-driven OpenCode subprocess. The implementation agent does not need repository write access while editing the local worktree. Remove that environment variable from the agent step and keep the write token available only to the tightly bounded Git/ghpublish and queue-validation steps. Resolve target credentialcurrently prefers the broad PAT secrets over the short-lived OpenCode App token. Prefer the scoped App token first; use a PAT only as an explicitly documented fallback when app exchange is unavailable. Add contract tests that prevent either regression.
Re-run the focused workflow trust-boundary tests and actionlint after the change.
|
@jules Fix the two current least-privilege findings on this branch with minimal changes: remove the target-repository write credential from the OpenCode agent subprocess environment, and make credential resolution prefer the short-lived scoped OpenCode App token over broad PAT fallbacks. Keep target writes available only to bounded queue validation, Git push, PR creation, and dispatch steps. Add exact workflow contract tests that reject reintroducing a write token into the agent step and assert App-first credential selection. Run the focused contract suite, full tests with required coverage/docstrings, actionlint, and git diff checks, then push the fix. |
|
@jules Apply the least-privilege fix directly on the current PR head (do not add or depend on a self-modifying repair workflow): in |
There was a problem hiding this comment.
Pull request overview
OpenCode cannot approve yet because required coverage evidence did not pass.
Review outcome
1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence
-
Problem: The required coverage-evidence job result was
failure, so OpenCode cannot establish approval sufficiency for this head. -
Root cause: Automated approval is only valid when the same-head coverage-evidence job proves supported repository test suites passed and configured docstring gates passed or were advisory, or reports not applicable because no supported source files or package manifests exist. Missing, failed, skipped, unavailable, or unsupported-tooling test evidence is a blocker.
-
Fix: Install or configure the repository test/docstring evidence tooling when source files or package manifests exist, rerun the current-head coverage-evidence job, and approve only after it reports
successwith required evidence or explicit no-source not-applicable evidence. -
Regression test: Keep the approval branch checking
needs.coverage-evidence.result == successbefore posting APPROVE, and publish REQUEST_CHANGES when coverage-evidence blocker states such as cancelled, skipped, failed, unsupported-tooling, or below-100 evidence are present. -
Result: REQUEST_CHANGES
-
Reason: coverage-evidence result was
failure, so required test/docstring evidence was not proven for current heade616816d5a471d16e719ba6ed0dee6b3c3b3aa3e. -
Head SHA:
e616816d5a471d16e719ba6ed0dee6b3c3b3aa3e -
Workflow run: 30813108073
-
Workflow attempt: 1
Coverage evidence
Coverage Decision
- Result: FAIL
- Test evidence: not proven passing
- Docstring evidence: not proven passing when configured
- Failure count: 1
Changed-File Evidence Map
flowchart LR
PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
Evidence --> S1["Workflow (4 files)"]
S1 --> I1["GitHub Actions review job"]
I1 --> R1["Review risk: Workflow (4 files)"]
R1 --> V1["actionlint plus required checks"]
Evidence --> S2["Docs (2 files)"]
S2 --> I2["operator or user guidance"]
I2 --> R2["Review risk: Docs (2 files)"]
R2 --> V2["docs review"]
Evidence --> S3["CI script: bootstrap_naruon_commercial_readiness_hardening_v2.py"]
S3 --> I3["review and security gate shell path"]
I3 --> R3["Review risk: CI script: bootstrap_naruon_commercial_readiness_hardening_v2.py"]
R3 --> V3["bash -n plus Strix self-test"]
Evidence --> S4["Test: test_naruon_commercial_readiness_hourly_contract.py"]
S4 --> I4["regression suite"]
I4 --> R4["Review risk: Test: test_naruon_commercial_readiness_hourly_contract.py"]
R4 --> V4["targeted test run"]
OpenCode Review Overview
Pull request overviewOpenCode cannot approve yet because required coverage evidence did not pass. Review outcome1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence
Coverage evidenceCoverage Decision
Changed-File Evidence Mapflowchart LR
PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
Evidence --> S1["Workflow (4 files)"]
S1 --> I1["GitHub Actions review job"]
I1 --> R1["Review risk: Workflow (4 files)"]
R1 --> V1["actionlint plus required checks"]
Evidence --> S2["Docs (2 files)"]
S2 --> I2["operator or user guidance"]
I2 --> R2["Review risk: Docs (2 files)"]
R2 --> V2["docs review"]
Evidence --> S3["CI script: bootstrap_naruon_commercial_readiness_hardening_v2.py"]
S3 --> I3["review and security gate shell path"]
I3 --> R3["Review risk: CI script: bootstrap_naruon_commercial_readiness_hardening_v2.py"]
R3 --> V3["bash -n plus Strix self-test"]
Evidence --> S4["Test: test_naruon_commercial_readiness_hourly_contract.py"]
S4 --> I4["regression suite"]
I4 --> R4["Review risk: Test: test_naruon_commercial_readiness_hourly_contract.py"]
R4 --> V4["targeted test run"]
|
There was a problem hiding this comment.
Pull request overview
OpenCode cannot approve yet because required coverage evidence did not pass.
Review outcome
1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence
-
Problem: The required coverage-evidence job result was
failure, so OpenCode cannot establish approval sufficiency for this head. -
Root cause: Automated approval is only valid when the same-head coverage-evidence job proves supported repository test suites passed and configured docstring gates passed or were advisory, or reports not applicable because no supported source files or package manifests exist. Missing, failed, skipped, unavailable, or unsupported-tooling test evidence is a blocker.
-
Fix: Install or configure the repository test/docstring evidence tooling when source files or package manifests exist, rerun the current-head coverage-evidence job, and approve only after it reports
successwith required evidence or explicit no-source not-applicable evidence. -
Regression test: Keep the approval branch checking
needs.coverage-evidence.result == successbefore posting APPROVE, and publish REQUEST_CHANGES when coverage-evidence blocker states such as cancelled, skipped, failed, unsupported-tooling, or below-100 evidence are present. -
Result: REQUEST_CHANGES
-
Reason: coverage-evidence result was
failure, so required test/docstring evidence was not proven for current heade616816d5a471d16e719ba6ed0dee6b3c3b3aa3e. -
Head SHA:
e616816d5a471d16e719ba6ed0dee6b3c3b3aa3e -
Workflow run: 30813108073
-
Workflow attempt: 2
Coverage evidence
Coverage Decision
- Result: FAIL
- Test evidence: not proven passing
- Docstring evidence: not proven passing when configured
- Failure count: 1
Changed-File Evidence Map
flowchart LR
PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
Evidence --> S1["Workflow (4 files)"]
S1 --> I1["GitHub Actions review job"]
I1 --> R1["Review risk: Workflow (4 files)"]
R1 --> V1["actionlint plus required checks"]
Evidence --> S2["Docs (2 files)"]
S2 --> I2["operator or user guidance"]
I2 --> R2["Review risk: Docs (2 files)"]
R2 --> V2["docs review"]
Evidence --> S3["CI script: bootstrap_naruon_commercial_readiness_hardening_v2.py"]
S3 --> I3["review and security gate shell path"]
I3 --> R3["Review risk: CI script: bootstrap_naruon_commercial_readiness_hardening_v2.py"]
R3 --> V3["bash -n plus Strix self-test"]
Evidence --> S4["Test: test_naruon_commercial_readiness_hourly_contract.py"]
S4 --> I4["regression suite"]
I4 --> R4["Review risk: Test: test_naruon_commercial_readiness_hourly_contract.py"]
R4 --> V4["targeted test run"]
e616816 to
4896dac
Compare
There was a problem hiding this comment.
Pull request overview
OpenCode cannot approve yet because required coverage evidence did not pass.
Review outcome
1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence
-
Problem: The required coverage-evidence job result was
failure, so OpenCode cannot establish approval sufficiency for this head. -
Root cause: Automated approval is only valid when the same-head coverage-evidence job proves supported repository test suites passed and configured docstring gates passed or were advisory, or reports not applicable because no supported source files or package manifests exist. Missing, failed, skipped, unavailable, or unsupported-tooling test evidence is a blocker.
-
Fix: Install or configure the repository test/docstring evidence tooling when source files or package manifests exist, rerun the current-head coverage-evidence job, and approve only after it reports
successwith required evidence or explicit no-source not-applicable evidence. -
Regression test: Keep the approval branch checking
needs.coverage-evidence.result == successbefore posting APPROVE, and publish REQUEST_CHANGES when coverage-evidence blocker states such as cancelled, skipped, failed, unsupported-tooling, or below-100 evidence are present. -
Result: REQUEST_CHANGES
-
Reason: coverage-evidence result was
failure, so required test/docstring evidence was not proven for current head4896dac870b5fb6cee5ac712bc010526f77adb47. -
Head SHA:
4896dac870b5fb6cee5ac712bc010526f77adb47 -
Workflow run: 30848587964
-
Workflow attempt: 1
Coverage evidence
Coverage Decision
- Result: FAIL
- Test evidence: not proven passing
- Docstring evidence: not proven passing when configured
- Failure count: 1
Changed-File Evidence Map
flowchart LR
PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
Evidence --> S1["Workflow (4 files)"]
S1 --> I1["GitHub Actions review job"]
I1 --> R1["Review risk: Workflow (4 files)"]
R1 --> V1["actionlint plus required checks"]
Evidence --> S2["Docs (2 files)"]
S2 --> I2["operator or user guidance"]
I2 --> R2["Review risk: Docs (2 files)"]
R2 --> V2["docs review"]
Evidence --> S3["CI script: bootstrap_naruon_commercial_readiness_hardening_v2.py"]
S3 --> I3["review and security gate shell path"]
I3 --> R3["Review risk: CI script: bootstrap_naruon_commercial_readiness_hardening_v2.py"]
R3 --> V3["bash -n plus Strix self-test"]
Evidence --> S4["Test: test_naruon_commercial_readiness_hourly_contract.py"]
S4 --> I4["regression suite"]
I4 --> R4["Review risk: Test: test_naruon_commercial_readiness_hourly_contract.py"]
R4 --> V4["targeted test run"]
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/test_naruon_commercial_readiness_hourly_contract.py (1)
1-118: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win대상 저장소 쓰기 자격 증명 노출 방지와 App 우선 선택을 검증하는 계약 테스트가 없습니다.
PR의 코멘트 요약은 "Add exact contract tests preventing write-token exposure in the agent step and asserting App-first credential selection"을 명시적으로 요구합니다. 그러나 이 파일에는
Run one commercial-readiness implementation slice단계의 env 블록에GITHUB_TOKEN또는USE_GITHUB_TOKEN이 없음을 확인하는 assert도 없고,Resolve target credential단계에서APP_TOKEN이PAT_TOKEN보다 우선 사용됨을 확인하는 assert도 없습니다. 이 요구사항이 코드로 실행되지 않으면 향후 회귀를 막을 수 없습니다.✅ 제안된 추가 테스트
def test_development_worker_does_not_expose_write_token_to_agent_and_prefers_app_token() -> None: """The OpenCode subprocess must never receive a target-repo write token.""" workflow = workflow_text("naruon-commercial-readiness-development.yml") agent_step = workflow.split("Run one commercial-readiness implementation slice", 1)[1] agent_env = agent_step.split("run: |", 1)[0] assert "GITHUB_TOKEN:" not in agent_env assert "USE_GITHUB_TOKEN" not in agent_env assert 'target_token="$APP_TOKEN"' in workflow🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_naruon_commercial_readiness_hourly_contract.py` around lines 1 - 118, Extend the contract tests in tests/test_naruon_commercial_readiness_hourly_contract.py with a test targeting the “Run one commercial-readiness implementation slice” step in naruon-commercial-readiness-development.yml. Extract that step’s env block and assert it contains neither GITHUB_TOKEN nor USE_GITHUB_TOKEN, and assert the “Resolve target credential” logic assigns target_token from APP_TOKEN before falling back to PAT_TOKEN, preserving App-first credential selection.
🧹 Nitpick comments (2)
.github/workflows/pr709-commercial-readiness-hardening-v2.yml (1)
119-121: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win전체 테스트 스위트를 실행하십시오.
이 워크플로는 계약 테스트 대상 워크플로 2개와 계약 테스트 파일을 수정합니다. 현재 검증은 테스트 파일 2개만 실행합니다. 다른 계약 테스트가 정확한 문자열을 단정하므로 회귀를 놓칠 수 있습니다.
As per coding guidelines: "Run the full test suite when editing contract-tested workflows, configuration, or governance prose; tests assert exact strings and structures in those files."
♻️ 제안 수정
python3 -m pytest -q \ tests/test_naruon_commercial_readiness_hourly_contract.py \ tests/test_required_workflow_queue_contract.py + python3 -m pytest -q🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/pr709-commercial-readiness-hardening-v2.yml around lines 119 - 121, Update the pytest command in the workflow step to run the complete test suite instead of only the two explicitly listed contract tests, while preserving the existing quiet execution option.Source: Coding guidelines
.github/workflows/naruon-commercial-readiness-development.yml (1)
36-39: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win두 워크플로 모두 harden-runner를 감사(audit) 모드로만 사용합니다.
egress-policy: audit는 아웃바운드 트래픽을 기록만 할 뿐 차단하지 않습니다. 이 PR의 핵심 목표가 최소 권한 강화이고,naruon-commercial-readiness-development.yml에는 반자율 에이전트에 자격 증명이 노출되는 문제도 있으므로(위 참조), 네트워크 아웃바운드를 실제로 제한하면 심층 방어 효과를 얻을 수 있습니다.
.github/workflows/naruon-commercial-readiness-development.yml#L36-L39:egress-policy: block으로 전환하고allowed-endpoints에api.github.com,github.com,api.opencode.ai,models.github.ai,registry.npmjs.org등 실제로 필요한 엔드포인트만 명시하십시오..github/workflows/naruon-commercial-readiness-hourly.yml#L30-L33: 동일하게egress-policy: block으로 전환하고 이 워크플로가 실제로 호출하는 GitHub API 엔드포인트만 허용하십시오.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/naruon-commercial-readiness-development.yml around lines 36 - 39, Switch the harden-runner configuration to blocking mode in both .github/workflows/naruon-commercial-readiness-development.yml:36-39 and .github/workflows/naruon-commercial-readiness-hourly.yml:30-33. In the development workflow, add only the required endpoints such as api.github.com, github.com, api.opencode.ai, models.github.ai, and registry.npmjs.org; in the hourly workflow, allow only the GitHub API endpoints it actually calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/naruon-commercial-readiness-development.yml:
- Around line 131-148: Reverse the credential selection order in the “Resolve
target credential” step: initialize target_token from APP_TOKEN first, then fall
back to PAT_TOKEN only when APP_TOKEN is empty. Preserve the existing
missing-credential failure, masking, and GITHUB_OUTPUT behavior.
- Around line 428-437: Remove the GITHUB_TOKEN and USE_GITHUB_TOKEN environment
entries from the “Run one commercial-readiness implementation slice” workflow
step. Keep the subsequent validation and publishing steps’ GH_TOKEN
configuration unchanged, while preserving the other environment variables in
this step.
In @.github/workflows/naruon-commercial-readiness-hourly.yml:
- Around line 17-18: Set a short job-level timeout for the orchestrate job by
adding timeout-minutes alongside runs-on, using a value appropriate for the
hourly workflow such as 10–15 minutes, so stalled gh api calls cannot occupy the
concurrency group until GitHub’s default limit.
In @.github/workflows/pr709-commercial-readiness-hardening-v2.yml:
- Around line 1-4: Delete
.github/workflows/pr709-commercial-readiness-hardening-v2.yml entirely; its
hardening changes must be committed directly to the target workflow and contract
tests. Also delete .github/workflows/pr709-least-privilege-repair.yml entirely,
including its comment stub, so neither temporary workflow remains as a
repository-local governance drift source.
- Around line 55-106: Fix the YAML indentation around the inline Python in
“Normalize squash-safe autonomous branch guard” so the closing triple-quoted
replacement string remains inside the run block and the workflow parses
successfully. Also remove the duplicated guard-replacement logic and reuse the
existing implementation from
bootstrap_naruon_commercial_readiness_hardening_v2.py instead of embedding a
second copy.
In `@scripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.py`:
- Around line 246-248: Update the changed-files exclusion regex in the bootstrap
hardening check to match extensionless CODEOWNERS files alongside AGENTS.md and
CLAUDE.md, while preserving the existing .md matching for those two files.
Ensure CODEOWNERS paths outside .github/ are blocked.
- Around line 332-349: In the hourly job permissions replacement handled by
replace_once_or_accept, retain or restore pull-requests: read alongside
contents: read so the GH_TOKEN fallback used by the queue-reading step allows gh
pr list to succeed. Keep the hourly job-wide credential removal unchanged.
- Around line 200-208: Update the changed-file accounting around changed_files
and changed_lines to register every untracked file with Git intent-to-add before
calculating statistics, then use the cached diff for both the binary guard and
line-count aggregation. Preserve the existing binary rejection and changed-line
budget behavior while ensuring newly added files are included.
- Around line 366-383: Update the replacement logic in the step-name loop to
preserve existing step keys when matching workflow steps: support the env: key
already present in “Decide whether product development may run” and the if: key
already present in “Dispatch one buyer-visible product gap,” then insert
GH_TOKEN after those existing keys without triggering replace_once_or_accept
RuntimeError.
- Around line 488-497: Update the bootstrap module configuration so this
one-time script is explicitly excluded from both coverage and interrogate
checks, or add dedicated tests covering it. In main, revise the docstring’s
“atomically” claim to accurately describe the sequential workflow writes and
cleanup.
In `@tests/test_naruon_commercial_readiness_hourly_contract.py`:
- Around line 106-117: Update
test_development_worker_opens_one_pr_and_dispatches_review to assert the quoted
JSON keys "review_dispatch_limit" and "merge_mode", matching the actual
development workflow text and the corresponding correction in the other contract
checks.
- Around line 33-44: Update the assertions in
test_hourly_loop_dispatches_fix_merge_and_zero_queue_development so the
review_dispatch_limit and stale_opencode_minutes JSON keys include the missing
quotation mark before the colon, matching the exact payload strings emitted by
naruon-commercial-readiness-hourly.yml. Keep the existing key and value
assertions otherwise unchanged.
---
Outside diff comments:
In `@tests/test_naruon_commercial_readiness_hourly_contract.py`:
- Around line 1-118: Extend the contract tests in
tests/test_naruon_commercial_readiness_hourly_contract.py with a test targeting
the “Run one commercial-readiness implementation slice” step in
naruon-commercial-readiness-development.yml. Extract that step’s env block and
assert it contains neither GITHUB_TOKEN nor USE_GITHUB_TOKEN, and assert the
“Resolve target credential” logic assigns target_token from APP_TOKEN before
falling back to PAT_TOKEN, preserving App-first credential selection.
---
Nitpick comments:
In @.github/workflows/naruon-commercial-readiness-development.yml:
- Around line 36-39: Switch the harden-runner configuration to blocking mode in
both .github/workflows/naruon-commercial-readiness-development.yml:36-39 and
.github/workflows/naruon-commercial-readiness-hourly.yml:30-33. In the
development workflow, add only the required endpoints such as api.github.com,
github.com, api.opencode.ai, models.github.ai, and registry.npmjs.org; in the
hourly workflow, allow only the GitHub API endpoints it actually calls.
In @.github/workflows/pr709-commercial-readiness-hardening-v2.yml:
- Around line 119-121: Update the pytest command in the workflow step to run the
complete test suite instead of only the two explicitly listed contract tests,
while preserving the existing quiet execution option.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: aa0c7f58-2742-4d7c-abd9-c7b33640e212
📒 Files selected for processing (8)
.github/workflows/naruon-commercial-readiness-development.yml.github/workflows/naruon-commercial-readiness-hourly.yml.github/workflows/pr709-commercial-readiness-hardening-v2.yml.github/workflows/pr709-least-privilege-repair.ymldocs/superpowers/plans/2026-08-03-naruon-hourly-commercial-readiness-loop.mddocs/superpowers/specs/2026-08-03-naruon-hourly-commercial-readiness-loop-design.mdscripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.pytests/test_naruon_commercial_readiness_hourly_contract.py
| - name: Resolve target credential | ||
| id: target_credential | ||
| env: | ||
| PAT_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || '' }} | ||
| APP_TOKEN: ${{ steps.target_app_token.outputs.token || '' }} | ||
| run: | | ||
| set -euo pipefail | ||
| target_token="$PAT_TOKEN" | ||
| if [ -z "$target_token" ]; then | ||
| target_token="$APP_TOKEN" | ||
| fi | ||
| if [ -z "$target_token" ]; then | ||
| echo "::error::No scoped target-repository write credential is available." | ||
| exit 1 | ||
| fi | ||
| echo "::add-mask::$target_token" | ||
| echo "token=$target_token" >>"$GITHUB_OUTPUT" | ||
|
|
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
자격 증명 우선순위가 요구사항과 반대로 되어 있습니다.
target_token은 먼저 $PAT_TOKEN(넓은 범위의 개인 액세스 토큰)을 시도하고, 비어 있을 때만 $APP_TOKEN(OIDC로 교환한 짧은 수명의 범위 제한 OpenCode App 토큰, 69-129번 줄)으로 대체합니다. PR 코멘트 요약은 명시적으로 "Prefer a short-lived scoped OpenCode App token over broad PAT fallbacks"를 요구합니다. 현재 순서는 이 요구사항과 정반대입니다. PR_REVIEW_MERGE_TOKEN 또는 OPENCODE_APPROVE_TOKEN 시크릿이 설정되어 있는 한, 범위가 더 넓은 PAT가 항상 우선 사용됩니다.
우선순위를 뒤집어 APP_TOKEN을 먼저 시도하십시오.
🔒 제안된 수정
- name: Resolve target credential
id: target_credential
env:
PAT_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || '' }}
APP_TOKEN: ${{ steps.target_app_token.outputs.token || '' }}
run: |
set -euo pipefail
- target_token="$PAT_TOKEN"
+ target_token="$APP_TOKEN"
if [ -z "$target_token" ]; then
- target_token="$APP_TOKEN"
+ target_token="$PAT_TOKEN"
fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Resolve target credential | |
| id: target_credential | |
| env: | |
| PAT_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || '' }} | |
| APP_TOKEN: ${{ steps.target_app_token.outputs.token || '' }} | |
| run: | | |
| set -euo pipefail | |
| target_token="$PAT_TOKEN" | |
| if [ -z "$target_token" ]; then | |
| target_token="$APP_TOKEN" | |
| fi | |
| if [ -z "$target_token" ]; then | |
| echo "::error::No scoped target-repository write credential is available." | |
| exit 1 | |
| fi | |
| echo "::add-mask::$target_token" | |
| echo "token=$target_token" >>"$GITHUB_OUTPUT" | |
| - name: Resolve target credential | |
| id: target_credential | |
| env: | |
| PAT_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || '' }} | |
| APP_TOKEN: ${{ steps.target_app_token.outputs.token || '' }} | |
| run: | | |
| set -euo pipefail | |
| target_token="$APP_TOKEN" | |
| if [ -z "$target_token" ]; then | |
| target_token="$PAT_TOKEN" | |
| fi | |
| if [ -z "$target_token" ]; then | |
| echo "::error::No scoped target-repository write credential is available." | |
| exit 1 | |
| fi | |
| echo "::add-mask::$target_token" | |
| echo "token=$target_token" >>"$GITHUB_OUTPUT" |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/naruon-commercial-readiness-development.yml around lines
131 - 148, Reverse the credential selection order in the “Resolve target
credential” step: initialize target_token from APP_TOKEN first, then fall back
to PAT_TOKEN only when APP_TOKEN is empty. Preserve the existing
missing-credential failure, masking, and GITHUB_OUTPUT behavior.
| - name: Run one commercial-readiness implementation slice | ||
| if: steps.target_state.outputs.eligible == 'true' | ||
| env: | ||
| STRIX_GITHUB_MODELS_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }} | ||
| GITHUB_TOKEN: ${{ steps.target_credential.outputs.token }} | ||
| MODEL: github-models/openai/gpt-5 | ||
| USE_GITHUB_TOKEN: "true" | ||
| SHARE: "false" | ||
| NPM_CONFIG_IGNORE_SCRIPTS: "true" | ||
| NO_COLOR: "1" |
There was a problem hiding this comment.
🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
file=".github/workflows/naruon-commercial-readiness-development.yml"
printf '%s\n' '--- target workflow section ---'
sed -n '380,465p' "$file"
printf '%s\n' '--- credential and token references ---'
rg -n -C 3 'target_credential|GITHUB_TOKEN|USE_GITHUB_TOKEN|opencode|OpenCode|permissions:' "$file" .github 2>/dev/null | head -n 500
printf '%s\n' '--- workflow files and relevant configuration files ---'
git ls-files | rg '(^|/)(opencode|.*commercial-readiness.*|.*workflow.*|package\.json|.*lock.*|.*jsonc?)$' | head -n 300Repository: ContextualWisdomLab/.github
Length of output: 50383
🏁 Script executed:
#!/bin/bash
set -eu
file=".github/workflows/naruon-commercial-readiness-development.yml"
printf '%s\n' '--- credential resolution ---'
sed -n '1,145p' "$file"
printf '%s\n' '--- OpenCode invocation and post-run handling ---'
sed -n '470,545p' "$file"
printf '%s\n' '--- hardening assertions for the agent step ---'
sed -n '125,160p' ".github/workflows/pr709-commercial-readiness-hardening-v2.yml"
printf '%s\n' '--- repository references to the requested removal ---'
rg -n -C 4 'Remove target-repository write credentials|agent step still receives a repository token|GITHUB_TOKEN.*USE_GITHUB_TOKEN|USE_GITHUB_TOKEN' .github README.md scripts 2>/dev/null | head -n 250Repository: ContextualWisdomLab/.github
Length of output: 17687
🌐 Web query:
OpenCode CLI v1.17.13 GITHUB_TOKEN USE_GITHUB_TOKEN GitHub integration documentation
💡 Result:
In the context of the OpenCode CLI (including version 1.17.13), the GITHUB_TOKEN and USE_GITHUB_TOKEN environment variables are primarily utilized for managing authentication and comment behavior when running within GitHub Actions [1][2][3]. GitHub Integration Overview The OpenCode CLI provides a github command group to automate tasks in repository workflows, such as automated PR reviews and CI processing [1][4]. The command opencode github run is typically used within a GitHub Actions environment [1][5][2]. Environment Variables 1. GITHUB_TOKEN: This environment variable is used to provide the GitHub personal access token (or the automatically generated token in Actions) required for the agent to interact with the repository, such as posting comments or reviewing pull requests [1][6][3]. 2. USE_GITHUB_TOKEN: This is a configuration flag, often set as an environment variable (USE_GITHUB_TOKEN=true or false), that determines how the CLI handles GitHub interactions [3]. Specifically, it controls whether the CLI posts per-reviewer comments to a pull request [3]. When set to true, the CLI is enabled to post comments directly using the provided token [3]. If set to false, it may restrict or modify this behavior—for example, preventing individual reviewer comments in favor of a single coordinator summary—to manage noise in pull request threads [3]. Technical Context for v1.17.13 In version 1.17.13 of the OpenCode CLI, users running automation scripts or custom GitHub Actions wrappers may explicitly export these variables to ensure the CLI operates with the correct permissions and comment-posting logic [7][3]. If you are encountering issues or managing CI workflows, ensure that your Action's environment includes GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} and that USE_GITHUB_TOKEN is configured according to your desired PR feedback verbosity [2][3]. Documentation Note Official CLI documentation for GitHub integration is available via the OpenCode CLI reference pages, which detail the opencode github install and opencode github run commands [1][5][4]. Configurations involving these environment variables are frequently found in third-party or custom GitHub Actions implementations that invoke the CLI [2][3].
Citations:
- 1: https://opencode.ai/docs/cli/
- 2: https://deepwiki.com/anomalyco/opencode/6.4-github-action-integration
- 3: sun-praise/opencode-actions@0fdbbb4
- 4: https://opencodeguide.com/en/cli-commands/
- 5: https://dev.opencode.ai/docs/cli/
- 6: https://github.com/sst/opencode/blob/9ad6588f/packages/web/src/content/docs/cli.mdx
- 7: sleep2agi/agent-network@f797d08
🏁 Script executed:
#!/bin/bash
set -eu
api="https://api.github.com/repos/anomalyco/opencode"
ref_json="$(curl -fsSL "$api/git/ref/tags/v1.17.13")"
printf '%s\n' '--- tag resolution ---'
printf '%s\n' "$ref_json" | jq '{ref, object}'
object_type="$(printf '%s\n' "$ref_json" | jq -r '.object.type')"
object_sha="$(printf '%s\n' "$ref_json" | jq -r '.object.sha')"
if [ "$object_type" = tag ]; then
tag_json="$(curl -fsSL "$api/git/tags/$object_sha")"
object_sha="$(printf '%s\n' "$tag_json" | jq -r '.object.sha')"
fi
printf '%s\n' '--- resolved commit ---'
curl -fsSL "$api/commits/$object_sha" | jq '{sha, message: .commit.message}'
printf '%s\n' '--- source paths related to GitHub and environment handling ---'
tree="$(curl -fsSL "$api/git/trees/$object_sha?recursive=1")"
printf '%s\n' "$tree" | jq -r '.tree[] | select(.type=="blob" and (.path|test("github|env|config"; "i"))) | .path' | head -n 300
printf '%s\n' '--- local policy assertions ---'
python3 - <<'PY'
from pathlib import Path
p = Path(".github/workflows/naruon-commercial-readiness-development.yml")
text = p.read_text()
agent = text.split("- name: Run one commercial-readiness implementation slice", 1)[1].split("- name: Validate bounded changed-file", 1)[0]
print({
"agent_has_GITHUB_TOKEN": "GITHUB_TOKEN:" in agent,
"agent_has_USE_GITHUB_TOKEN": "USE_GITHUB_TOKEN:" in agent,
"post_agent_uses_target_credential": "GH_TOKEN: ${{ steps.target_credential.outputs.token }}" in text.split("- name: Validate bounded changed-file", 1)[1],
})
PYRepository: ContextualWisdomLab/.github
Length of output: 216
OpenCode 구현 단계에서 대상 저장소 쓰기 자격 증명을 제거하십시오.
GITHUB_TOKEN과 USE_GITHUB_TOKEN은 신뢰할 수 없는 입력을 처리하는 OpenCode 프로세스에 대상 저장소 쓰기 경로를 제공합니다. 두 변수를 제거하고, 후속 검증 및 게시 단계의 GH_TOKEN만 유지하십시오.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/naruon-commercial-readiness-development.yml around lines
428 - 437, Remove the GITHUB_TOKEN and USE_GITHUB_TOKEN environment entries from
the “Run one commercial-readiness implementation slice” workflow step. Keep the
subsequent validation and publishing steps’ GH_TOKEN configuration unchanged,
while preserving the other environment variables in this step.
| orchestrate: | ||
| runs-on: ubuntu-latest |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
작업에 타임아웃이 없습니다.
orchestrate 작업에 timeout-minutes가 없습니다. concurrency.cancel-in-progress: false(11번 줄)와 결합되면, gh api 호출 하나가 멈출 경우 작업이 GitHub Actions 기본 최대 시간(6시간)까지 실행될 수 있고, 그동안 동시성 그룹이 점유되어 후속 시간별 실행이 모두 차단됩니다. 이 워크플로는 시간마다 실행되도록 설계되었으므로 지연에 특히 취약합니다.
짧은 timeout-minutes 값(예: 10-15분)을 작업에 추가하십시오.
⏱️ 제안된 수정
jobs:
orchestrate:
runs-on: ubuntu-latest
+ timeout-minutes: 15
permissions:
actions: write
contents: write
pull-requests: read📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| orchestrate: | |
| runs-on: ubuntu-latest | |
| orchestrate: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/naruon-commercial-readiness-hourly.yml around lines 17 -
18, Set a short job-level timeout for the orchestrate job by adding
timeout-minutes alongside runs-on, using a value appropriate for the hourly
workflow such as 10–15 minutes, so stalled gh api calls cannot occupy the
concurrency group until GitHub’s default limit.
| name: PR 709 Commercial Readiness Hardening v2 | ||
|
|
||
| # One-shot repair for the PR branch. The bootstrap deletes this workflow after | ||
| # the requested least-privilege changes and contract tests pass. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
임시 PR 709 복구 산출물이 저장소에 남아 있습니다. 두 파일 모두 실행 시점의 자기 삭제에 의존합니다. 부트스트랩이 실행되지 않거나 실패하면 임시 워크플로가 그대로 남고, 조직 전역 governance 정본과 별개의 drift 소스가 됩니다. PR 요청도 두 파일의 삭제를 요구합니다.
.github/workflows/pr709-commercial-readiness-hardening-v2.yml#L1-L4: 이 워크플로 파일을 PR에서 삭제하십시오. 필요한 하드닝 변경은 대상 워크플로와 계약 테스트에 직접 커밋하십시오..github/workflows/pr709-least-privilege-repair.yml#L1-L1: 주석 스텁을 남기지 말고 파일을 삭제하십시오. 현재 상태는 GitHub Actions에서 "Invalid workflow file" 오류를 발생시킵니다.
As per coding guidelines: "Treat workflows in .github/workflows/ as the canonical organization-wide PR governance, security scanning, and merge-automation implementation; do not maintain repository-local drift sources."
📍 Affects 2 files
.github/workflows/pr709-commercial-readiness-hardening-v2.yml#L1-L4(this comment).github/workflows/pr709-least-privilege-repair.yml#L1-L1
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/pr709-commercial-readiness-hardening-v2.yml around lines 1
- 4, Delete .github/workflows/pr709-commercial-readiness-hardening-v2.yml
entirely; its hardening changes must be committed directly to the target
workflow and contract tests. Also delete
.github/workflows/pr709-least-privilege-repair.yml entirely, including its
comment stub, so neither temporary workflow remains as a repository-local
governance drift source.
Source: Coding guidelines
| - name: Normalize squash-safe autonomous branch guard | ||
| run: | | ||
| set -euo pipefail | ||
| python3 - <<'PY' | ||
| from pathlib import Path | ||
|
|
||
| path = Path('.github/workflows/naruon-commercial-readiness-development.yml') | ||
| text = path.read_text(encoding='utf-8') | ||
| marker = 'Ignoring completed autonomous branch:' | ||
| if marker not in text: | ||
| start = text.index(' autonomous_branch_count=0\n') | ||
| end = text.index( | ||
| ' if [ "$autonomous_branch_count" -ne 0 ]; then\n', | ||
| start, | ||
| ) | ||
| replacement = ''' autonomous_branch_count=0 | ||
| while IFS= read -r autonomous_ref; do | ||
| [ -n "$autonomous_ref" ] || continue | ||
| autonomous_sha="$(git -C "$target_workspace" rev-parse "$autonomous_ref")" | ||
| if git -C "$target_workspace" merge-base --is-ancestor \\ | ||
| "$autonomous_sha" "$BASE_SHA"; then | ||
| continue | ||
| fi | ||
|
|
||
| autonomous_branch="${autonomous_ref#refs/remotes/origin/}" | ||
| branch_pr_state="$( | ||
| gh pr list \\ | ||
| --repo "$TARGET_REPOSITORY" \\ | ||
| --state all \\ | ||
| --base "$TARGET_BASE_BRANCH" \\ | ||
| --head "$autonomous_branch" \\ | ||
| --limit 20 \\ | ||
| --json state,mergedAt \\ | ||
| --jq 'if length == 0 then "missing" elif any(.[]; .state == "OPEN") then "open" elif any(.[]; .mergedAt != null) then "merged" else "closed" end' | ||
| )" | ||
| case "$branch_pr_state" in | ||
| merged | closed) | ||
| echo "Ignoring completed autonomous branch: ${autonomous_branch} (${branch_pr_state})." | ||
| ;; | ||
| *) | ||
| autonomous_branch_count=$((autonomous_branch_count + 1)) | ||
| echo "Unfinished autonomous branch: ${autonomous_branch} (${branch_pr_state})." | ||
| ;; | ||
| esac | ||
| done < <( | ||
| git -C "$target_workspace" for-each-ref \\ | ||
| --format='%(refname)' \\ | ||
| 'refs/remotes/origin/autonomous/commercial-readiness-*' | ||
| ) | ||
| ''' | ||
| path.write_text(text[:start] + replacement + text[end:], encoding='utf-8') | ||
| PY |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: 문제 워크플로의 YAML 파싱과 actionlint 결과를 확인한다.
python3 -m pip install --quiet yamllint >/dev/null 2>&1
python3 - <<'PY'
from pathlib import Path
import yaml
p = Path('.github/workflows/pr709-commercial-readiness-hardening-v2.yml')
doc = yaml.safe_load(p.read_text(encoding='utf-8'))
print('parsed type:', type(doc).__name__)
print('top-level keys:', list(doc) if isinstance(doc, dict) else doc)
PY
python3 -m yamllint -f parsable .github/workflows/pr709-commercial-readiness-hardening-v2.yml || trueRepository: ContextualWisdomLab/.github
Length of output: 325
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- workflow excerpt ---'
nl -ba .github/workflows/pr709-commercial-readiness-hardening-v2.yml | sed -n '45,120p'
printf '%s\n' '--- matching guard implementations ---'
rg -n -C 4 'Normalize squash-safe autonomous branch guard|autonomous_branch_count=0|Ignoring completed autonomous branch|replace_once_or_accept' .github/workflows .github/scripts 2>/dev/null || true
printf '%s\n' '--- available YAML/action validators ---'
command -v ruby || true
command -v node || true
command -v actionlint || true
command -v yamllint || true
ruby -e 'begin; require "yaml"; puts "ruby_yaml_available"; rescue LoadError; puts "ruby_yaml_unavailable"; end'
node -e 'for (const p of ["yaml","js-yaml"]) { try { console.log(p + "_available:" + require.resolve(p)); } catch (_) { console.log(p + "_unavailable"); } }'Repository: ContextualWisdomLab/.github
Length of output: 6299
🏁 Script executed:
#!/bin/bash
set +e
printf '%s\n' '--- workflow excerpt ---'
sed -n '45,120p' .github/workflows/pr709-commercial-readiness-hardening-v2.yml
printf '%s\n' '--- development guard ---'
sed -n '205,270p' .github/workflows/naruon-commercial-readiness-development.yml
printf '%s\n' '--- bootstrap candidates ---'
find . -type f -iname '*bootstrap*naruon*' -o -type f -iname '*commercial*hardening*' | sort
printf '%s\n' '--- validators ---'
ruby -e 'require "yaml"; p YAML.safe_load(File.read(".github/workflows/pr709-commercial-readiness-hardening-v2.yml"), aliases: true).class'
yamllint -f parsable .github/workflows/pr709-commercial-readiness-hardening-v2.yml
actionlint .github/workflows/pr709-commercial-readiness-hardening-v2.ymlRepository: ContextualWisdomLab/.github
Length of output: 9573
🏁 Script executed:
#!/bin/bash
set +e
printf '%s\n' '--- workflow lines 100-108 with visible whitespace ---'
sed -n '100,108p' .github/workflows/pr709-commercial-readiness-hardening-v2.yml | sed -n 'l'
printf '%s\n' '--- bootstrap guard references ---'
rg -n -C 8 'squash|autonomous_branch_count|Ignoring completed autonomous branch|replace_once_or_accept' scripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.py
printf '%s\n' '--- actionlint ---'
actionlint .github/workflows/pr709-commercial-readiness-hardening-v2.yml
printf '%s\n' '--- Python syntax check for the embedded script body ---'
python3 - <<'PY'
from pathlib import Path
import re
p = Path('.github/workflows/pr709-commercial-readiness-hardening-v2.yml')
text = p.read_text(encoding='utf-8')
m = re.search(r'(?ms)^ python3 - <<'PY'\n(.*?)^ PY$', text)
print('embedded_script_found:', bool(m))
if m:
script = '\n'.join(line[10:] if line.startswith(' ') else line for line in m.group(1).splitlines()) + '\n'
compile(script, str(p) + ':embedded-python', 'exec')
print('embedded_script_syntax: valid')
PYRepository: ContextualWisdomLab/.github
Length of output: 12900
YAML 구문 오류를 수정하십시오.
104행의 닫는 '''가 run 블록의 들여쓰기 밖에 있습니다. 이 파일은 YAML로 파싱되지 않으므로 워크플로를 실행할 수 없습니다. 인라인 Python의 가드 치환 문자열도 bootstrap_naruon_commercial_readiness_hardening_v2.py와 중복됩니다.
🧰 Tools
🪛 YAMLlint (1.37.1)
[error] 105-105: syntax error: could not find expected ':'
(syntax)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/pr709-commercial-readiness-hardening-v2.yml around lines
55 - 106, Fix the YAML indentation around the inline Python in “Normalize
squash-safe autonomous branch guard” so the closing triple-quoted replacement
string remains inside the run block and the workflow parses successfully. Also
remove the duplicated guard-replacement logic and reuse the existing
implementation from bootstrap_naruon_commercial_readiness_hardening_v2.py
instead of embedding a second copy.
Source: Linters/SAST tools
| text = replace_once_or_accept( | ||
| text, | ||
| """ permissions: | ||
| actions: write | ||
| contents: write | ||
| pull-requests: read | ||
| """, | ||
| """ permissions: | ||
| actions: write | ||
| contents: read | ||
| """, | ||
| "hourly job permissions", | ||
| ) | ||
| text = remove_once_or_accept( | ||
| text, | ||
| " GH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || github.token }}\n", | ||
| "hourly job-wide credential", | ||
| ) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
pull-requests: read 제거가 큐 읽기 폴백 경로를 깨뜨립니다.
큐 읽기 스텝의 GH_TOKEN은 secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || github.token 체인을 유지합니다. 두 시크릿이 없으면 github.token이 사용됩니다. 이때 pull-requests: read 권한이 없으므로 gh pr list가 403으로 실패합니다. 읽기 전용 권한은 최소 권한 목표와 충돌하지 않으므로 복원하십시오.
🔧 제안 수정
""" permissions:
actions: write
contents: read
+ pull-requests: read
""",
"hourly job permissions",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| text = replace_once_or_accept( | |
| text, | |
| """ permissions: | |
| actions: write | |
| contents: write | |
| pull-requests: read | |
| """, | |
| """ permissions: | |
| actions: write | |
| contents: read | |
| """, | |
| "hourly job permissions", | |
| ) | |
| text = remove_once_or_accept( | |
| text, | |
| " GH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || github.token }}\n", | |
| "hourly job-wide credential", | |
| ) | |
| text = replace_once_or_accept( | |
| text, | |
| """ permissions: | |
| actions: write | |
| contents: write | |
| pull-requests: read | |
| """, | |
| """ permissions: | |
| actions: write | |
| contents: read | |
| pull-requests: read | |
| """, | |
| "hourly job permissions", | |
| ) | |
| text = remove_once_or_accept( | |
| text, | |
| " GH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || github.token }}\n", | |
| "hourly job-wide credential", | |
| ) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.py` around
lines 332 - 349, In the hourly job permissions replacement handled by
replace_once_or_accept, retain or restore pull-requests: read alongside
contents: read so the GH_TOKEN fallback used by the queue-reading step allows gh
pr list to succeed. Keep the hourly job-wide credential removal unchanged.
| for step_name in ( | ||
| "Dispatch review feedback fixes", | ||
| "Dispatch current-head review and merge processing", | ||
| "Decide whether product development may run", | ||
| "Dispatch one buyer-visible product gap", | ||
| ): | ||
| text = replace_once_or_accept( | ||
| text, | ||
| f""" - name: {step_name} | ||
| run: | | ||
| """, | ||
| f""" - name: {step_name} | ||
| env: | ||
| GH_TOKEN: ${{{{ github.token }}}} | ||
| run: | | ||
| """, | ||
| f"{step_name} workflow-token scope", | ||
| ) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: hourly 워크플로의 대상 4개 스텝 정의를 확인해 기존 env/shell 키 존재 여부를 검사한다.
fd -t f 'naruon-commercial-readiness-hourly.yml' -x rg -n -A 6 \
'name: (Dispatch review feedback fixes|Dispatch current-head review and merge processing|Decide whether product development may run|Dispatch one buyer-visible product gap)' {}Repository: ContextualWisdomLab/.github
Length of output: 165
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- candidate workflow files ---'
fd -t f -i 'naruon-commercial-readiness.*\.ya?ml$' . || true
printf '%s\n' '--- bootstrap references ---'
rg -n -A 12 -B 4 'Dispatch review feedback fixes|Dispatch current-head review and merge processing|Decide whether product development may run|Dispatch one buyer-visible product gap|replace_once_or_accept' scripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.py
printf '%s\n' '--- tracked files with target step names ---'
rg -l 'Dispatch review feedback fixes|Dispatch current-head review and merge processing|Decide whether product development may run|Dispatch one buyer-visible product gap' . || trueRepository: ContextualWisdomLab/.github
Length of output: 10700
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- workflow files ---'
git ls-files '.github/workflows/*' | sort
printf '%s\n' '--- relevant workflow fragments in tracked files ---'
rg -n -A 10 -B 3 'Dispatch review feedback fixes|Dispatch current-head review and merge processing|Decide whether product development may run|Dispatch one buyer-visible product gap' .github scripts tests 2>/dev/null || true
printf '%s\n' '--- bootstrap test and workflow-text helpers ---'
cat -n scripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.py | sed -n '1,35p;320,390p;430,490p'
printf '%s\n' '--- tracked references to hourly workflow ---'
rg -n 'naruon-commercial-readiness-hourly|pr709-commercial-readiness-hardening-v2' . || trueRepository: ContextualWisdomLab/.github
Length of output: 19499
🏁 Script executed:
#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path
path = Path(".github/workflows/naruon-commercial-readiness-hourly.yml")
text = path.read_text(encoding="utf-8")
names = (
"Dispatch review feedback fixes",
"Dispatch current-head review and merge processing",
"Decide whether product development may run",
"Dispatch one buyer-visible product gap",
)
for name in names:
start = text.index(f" - name: {name}\n")
next_step = text.find("\n - name:", start + 1)
block = text[start:] if next_step == -1 else text[start:next_step]
header = block.split(" run: |", 1)[0]
old = f" - name: {name}\n run: |\n"
new = (
f" - name: {name}\n"
" env:\n"
" GH_TOKEN: ${{ github.token }}\n"
" run: |\n"
)
print(f"{name}")
print(f" old_count={text.count(old)} new_present={new in text}")
print(" keys_before_run=" + repr(header.splitlines()[1:]))
PYRepository: ContextualWisdomLab/.github
Length of output: 704
기존 스텝 키를 보존하도록 치환 로직을 수정하십시오.
Decide whether product development may run에는 env:가 있고, Dispatch one buyer-visible product gap에는 if:가 있습니다. 두 스텝 모두 패턴과 일치하지 않아 replace_once_or_accept가 RuntimeError를 발생시키고 부트스트랩을 중단합니다. 기존 키 뒤에 GH_TOKEN을 추가하십시오.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.py` around
lines 366 - 383, Update the replacement logic in the step-name loop to preserve
existing step keys when matching workflow steps: support the env: key already
present in “Decide whether product development may run” and the if: key already
present in “Dispatch one buyer-visible product gap,” then insert GH_TOKEN after
those existing keys without triggering replace_once_or_accept RuntimeError.
| def main() -> None: | ||
| """Patch both workflows and tests atomically, then remove bootstrap artifacts.""" | ||
| development = patch_development(DEVELOPMENT.read_text(encoding="utf-8")) | ||
| hourly = patch_hourly(HOURLY.read_text(encoding="utf-8")) | ||
| contract = patch_contract(CONTRACT.read_text(encoding="utf-8")) | ||
| DEVELOPMENT.write_text(development, encoding="utf-8") | ||
| HOURLY.write_text(hourly, encoding="utf-8") | ||
| CONTRACT.write_text(contract, encoding="utf-8") | ||
| for path in (OLD_BOOTSTRAP, NEW_BOOTSTRAP, SELF): | ||
| path.unlink(missing_ok=True) |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: 부트스트랩 스크립트에 대응하는 테스트와 커버리지 제외 설정을 확인한다.
rg -n 'bootstrap_naruon_commercial_readiness_hardening_v2' --glob '!scripts/ci/**'
echo '--- coverage/interrogate config ---'
fd -t f -g 'pyproject.toml' -x rg -n -A 12 '\[tool\.(coverage|interrogate)' {}Repository: ContextualWisdomLab/.github
Length of output: 165
🏁 Script executed:
#!/bin/bash
set -u
printf '%s\n' '--- target file ---'
wc -l scripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.py
sed -n '1,80p' scripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.py
sed -n '430,520p' scripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.py
printf '%s\n' '--- matching tests and references ---'
rg -n -i 'naruon_commercial_readiness|hardening_v2|bootstrap' --glob '*.py' --glob 'pyproject.toml' --glob '*.ini' --glob '*.cfg' --glob '*.yaml' --glob '*.yml' . || true
printf '%s\n' '--- repository configuration files ---'
git ls-files | rg '(^|/)(pyproject\.toml|\.coveragerc|setup\.cfg|tox\.ini|pytest\.ini|.*coverage.*|.*interrogate.*)$' || trueRepository: ContextualWisdomLab/.github
Length of output: 9816
🏁 Script executed:
#!/bin/bash
set -u
printf '%s\n' '--- scripts/ci files ---'
git ls-files scripts/ci
printf '%s\n' '--- test files ---'
git ls-files | rg '(^|/)(test[^/]*|tests?)/|(^|/)test_.*\.py$|_test\.py$' || true
printf '%s\n' '--- target module structure ---'
ast-grep outline scripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.py --lang python || trueRepository: ContextualWisdomLab/.github
Length of output: 4977
🏁 Script executed:
#!/bin/bash
set -u
printf '%s\n' '--- coverage and interrogate configuration ---'
cat -n pyproject.toml | sed -n '1,240p'
printf '%s\n' '--- references to coverage/interrogate and test discovery ---'
rg -n -i 'coverage|interrogate|pytest|scripts/ci' .github pyproject.toml setup.cfg tox.ini pytest.ini .coveragerc 2>/dev/null || true
printf '%s\n' '--- workflow files that run quality gates ---'
rg -l -i 'coverage|interrogate|pytest' .github/workflows | head -40 | while read -r file; do
echo "--- $file"
rg -n -i -A 8 -B 3 'coverage|interrogate|pytest' "$file"
doneRepository: ContextualWisdomLab/.github
Length of output: 50384
🏁 Script executed:
#!/bin/bash
set -u
printf '%s\n' '--- target history/status metadata ---'
git status --short -- scripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.py tests pyproject.toml .github/workflows || true
git log --oneline -5 -- scripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.py || true
printf '%s\n' '--- all references to target path and deletion behavior ---'
rg -n 'bootstrap_naruon_commercial_readiness_hardening_v2\.py|SELF\.unlink|unlink\(missing_ok|remove.*bootstrap|bootstrap.*remove' . --glob '!scripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.py' || trueRepository: ContextualWisdomLab/.github
Length of output: 417
🏁 Script executed:
#!/bin/bash
set -u
printf '%s\n' '--- bootstrap workflow ---'
cat -n .github/workflows/pr709-commercial-readiness-hardening-v2.yml | sed -n '1,180p'
printf '%s\n' '--- repository-owned coverage commands ---'
rg -n -A 18 -B 8 'run_python_test_coverage\(\)|run_python_docstring_coverage\(\)|coverage run -m pytest|coverage report|interrogate' .github/workflows/opencode-review-dispatch.yml | sed -n '1,220p'Repository: ContextualWisdomLab/.github
Length of output: 15252
🏁 Script executed:
#!/bin/bash
set -u
python3 - <<'PY'
import ast
from pathlib import Path
path = Path("scripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.py")
tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
main = next(
node for node in tree.body
if isinstance(node, ast.FunctionDef) and node.name == "main"
)
writes = [
(node.lineno, ast.unparse(node))
for node in ast.walk(main)
if isinstance(node, ast.Call)
and isinstance(node.func, ast.Attribute)
and node.func.attr == "write_text"
]
deletes = [
(node.lineno, ast.unparse(node))
for node in ast.walk(main)
if isinstance(node, ast.Call)
and isinstance(node.func, ast.Attribute)
and node.func.attr == "unlink"
]
print("main.write_text_calls =", writes)
print("main.unlink_calls =", deletes)
print("main_docstring =", ast.get_docstring(main))
PYRepository: ContextualWisdomLab/.github
Length of output: 519
테스트 또는 명시적 제외 설정을 추가하십시오.
pyproject.toml은 scripts/ci를 커버리지 소스로 지정하고 100%를 요구하지만, 이 모듈을 제외하는 설정과 대응 테스트가 없습니다. 현재 부트스트랩 워크플로도 모듈을 실행한 뒤 삭제하고 두 계약 테스트만 실행합니다. 테스트를 추가하거나, 일회성 모듈이면 coverage와 interrogate에서 모두 명시적으로 제외하십시오. main의 "atomically"도 세 번의 순차 write_text 호출과 일치하지 않으므로 수정하십시오.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.py` around
lines 488 - 497, Update the bootstrap module configuration so this one-time
script is explicitly excluded from both coverage and interrogate checks, or add
dedicated tests covering it. In main, revise the docstring’s “atomically” claim
to accurately describe the sequential workflow writes and cleanup.
Source: Coding guidelines
| def test_hourly_loop_dispatches_fix_merge_and_zero_queue_development() -> None: | ||
| """Every hourly run must drain PRs before it is allowed to develop.""" | ||
| workflow = workflow_text("naruon-commercial-readiness-hourly.yml") | ||
|
|
||
| assert '"event_type": "pr-review-fix-scheduler"' in workflow | ||
| assert '"event_type": "merge-scheduler"' in workflow | ||
| assert '"event_type": "naruon-commercial-readiness-development"' in workflow | ||
| assert 'if [ "$OPEN_PR_COUNT" -ne 0 ]; then' in workflow | ||
| assert 'review_dispatch_limit: "-1"' in workflow | ||
| assert 'stale_opencode_minutes: "60"' in workflow | ||
| assert "actions/workflows/naruon-commercial-readiness-development.yml/runs" in workflow | ||
| assert '.status == "queued" or .status == "in_progress"' in workflow |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
JSON 페이로드의 키 인용 형식이 일치하지 않아 테스트가 실패합니다.
41번 줄과 42번 줄의 문자열은 review_dispatch_limit: "-1"과 stale_opencode_minutes: "60"을 검사합니다. 그러나 naruon-commercial-readiness-hourly.yml의 실제 내용(108번 줄, 113번 줄)은 "review_dispatch_limit": "-1"과 "stale_opencode_minutes": "60"입니다. JSON 키 자체가 인용부호로 둘러싸여 있으므로, 콜론 앞에 테스트 문자열에는 없는 " 문자가 하나 더 있습니다. 이 페이로드는 <<'JSON' 인용 구분자로 작성되어 있어 셸 이스케이프의 모호함이 없습니다. 따라서 이 두 assert는 실제 파일 내용과 정확히 일치하지 않아 실패합니다.
37번 줄과 38번 줄은 키와 값을 모두 인용부호로 감싸는 올바른 형식('"event_type": "pr-review-fix-scheduler"')을 사용하고 있으므로, 이는 의도된 다른 스타일이 아니라 단순한 누락으로 보입니다.
As per coding guidelines, tests/**/*.py: "tests assert exact strings and structures in those files" — 정확한 문자열 일치가 이 계약의 전제이므로 이 불일치는 해당 요구사항을 위반합니다.
🐛 제안된 수정
- assert 'review_dispatch_limit: "-1"' in workflow
- assert 'stale_opencode_minutes: "60"' in workflow
+ assert '"review_dispatch_limit": "-1"' in workflow
+ assert '"stale_opencode_minutes": "60"' in workflow📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def test_hourly_loop_dispatches_fix_merge_and_zero_queue_development() -> None: | |
| """Every hourly run must drain PRs before it is allowed to develop.""" | |
| workflow = workflow_text("naruon-commercial-readiness-hourly.yml") | |
| assert '"event_type": "pr-review-fix-scheduler"' in workflow | |
| assert '"event_type": "merge-scheduler"' in workflow | |
| assert '"event_type": "naruon-commercial-readiness-development"' in workflow | |
| assert 'if [ "$OPEN_PR_COUNT" -ne 0 ]; then' in workflow | |
| assert 'review_dispatch_limit: "-1"' in workflow | |
| assert 'stale_opencode_minutes: "60"' in workflow | |
| assert "actions/workflows/naruon-commercial-readiness-development.yml/runs" in workflow | |
| assert '.status == "queued" or .status == "in_progress"' in workflow | |
| def test_hourly_loop_dispatches_fix_merge_and_zero_queue_development() -> None: | |
| """Every hourly run must drain PRs before it is allowed to develop.""" | |
| workflow = workflow_text("naruon-commercial-readiness-hourly.yml") | |
| assert '"event_type": "pr-review-fix-scheduler"' in workflow | |
| assert '"event_type": "merge-scheduler"' in workflow | |
| assert '"event_type": "naruon-commercial-readiness-development"' in workflow | |
| assert 'if [ "$OPEN_PR_COUNT" -ne 0 ]; then' in workflow | |
| assert '"review_dispatch_limit": "-1"' in workflow | |
| assert '"stale_opencode_minutes": "60"' in workflow | |
| assert "actions/workflows/naruon-commercial-readiness-development.yml/runs" in workflow | |
| assert '.status == "queued" or .status == "in_progress"' in workflow |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tests/test_naruon_commercial_readiness_hourly_contract.py` around lines 33 -
44, Update the assertions in
test_hourly_loop_dispatches_fix_merge_and_zero_queue_development so the
review_dispatch_limit and stale_opencode_minutes JSON keys include the missing
quotation mark before the colon, matching the exact payload strings emitted by
naruon-commercial-readiness-hourly.yml. Keep the existing key and value
assertions otherwise unchanged.
Source: Coding guidelines
| def test_development_worker_opens_one_pr_and_dispatches_review() -> None: | ||
| """Successful development is published only through a normal reviewed PR.""" | ||
| workflow = workflow_text("naruon-commercial-readiness-development.yml") | ||
|
|
||
| assert 'development_branch="autonomous/commercial-readiness-${GITHUB_RUN_ID}"' in workflow | ||
| assert 'git push origin "HEAD:${DEVELOPMENT_BRANCH}"' in workflow | ||
| assert "gh pr create" in workflow | ||
| assert '"event_type": "merge-scheduler"' in workflow | ||
| assert 'review_dispatch_limit: "-1"' in workflow | ||
| assert 'merge_mode: "direct_or_auto"' in workflow | ||
| assert "--draft" not in workflow | ||
| assert "git push origin HEAD:develop" not in workflow |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
동일한 키 인용 문제가 development.yml 검사에도 있습니다.
114번 줄과 115번 줄은 review_dispatch_limit: "-1"과 merge_mode: "direct_or_auto"(따옴표 없는 키)를 검사하지만, naruon-commercial-readiness-development.yml의 실제 714번 줄과 717번 줄은 "review_dispatch_limit": "-1"과 "merge_mode": "direct_or_auto"(따옴표 있는 키)입니다. 33-44번 줄에서 지적한 것과 같은 근본 원인입니다. 두 파일 모두 수정해야 python -m pytest -q tests/test_naruon_commercial_readiness_hourly_contract.py가 통과합니다.
🐛 제안된 수정
- assert 'review_dispatch_limit: "-1"' in workflow
- assert 'merge_mode: "direct_or_auto"' in workflow
+ assert '"review_dispatch_limit": "-1"' in workflow
+ assert '"merge_mode": "direct_or_auto"' in workflow📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def test_development_worker_opens_one_pr_and_dispatches_review() -> None: | |
| """Successful development is published only through a normal reviewed PR.""" | |
| workflow = workflow_text("naruon-commercial-readiness-development.yml") | |
| assert 'development_branch="autonomous/commercial-readiness-${GITHUB_RUN_ID}"' in workflow | |
| assert 'git push origin "HEAD:${DEVELOPMENT_BRANCH}"' in workflow | |
| assert "gh pr create" in workflow | |
| assert '"event_type": "merge-scheduler"' in workflow | |
| assert 'review_dispatch_limit: "-1"' in workflow | |
| assert 'merge_mode: "direct_or_auto"' in workflow | |
| assert "--draft" not in workflow | |
| assert "git push origin HEAD:develop" not in workflow | |
| def test_development_worker_opens_one_pr_and_dispatches_review() -> None: | |
| """Successful development is published only through a normal reviewed PR.""" | |
| workflow = workflow_text("naruon-commercial-readiness-development.yml") | |
| assert 'development_branch="autonomous/commercial-readiness-${GITHUB_RUN_ID}"' in workflow | |
| assert 'git push origin "HEAD:${DEVELOPMENT_BRANCH}"' in workflow | |
| assert "gh pr create" in workflow | |
| assert '"event_type": "merge-scheduler"' in workflow | |
| assert '"review_dispatch_limit": "-1"' in workflow | |
| assert '"merge_mode": "direct_or_auto"' in workflow | |
| assert "--draft" not in workflow | |
| assert "git push origin HEAD:develop" not in workflow |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tests/test_naruon_commercial_readiness_hourly_contract.py` around lines 106 -
117, Update test_development_worker_opens_one_pr_and_dispatches_review to assert
the quoted JSON keys "review_dispatch_limit" and "merge_mode", matching the
actual development workflow text and the corresponding correction in the other
contract checks.
Source: Coding guidelines
|
@jules Rebuild this PR into a reviewable, least-privilege hourly commercialization loop. Address every current unresolved review finding, use tests first, do not merge, and do not leave one-shot/bootstrap workflows or scripts in the final tree. Final allowed runtime files:
Delete entirely from the final diff:
Required production fixes:
Verification required on the final exact head:
Update the PR body to describe the final exact file set and verification. Leave the PR Draft only if any condition is still incomplete; otherwise mark it ready for independent review, but do not merge it. |
|
Closing this 22-commit draft to stop stale workflow fan-out. The Naruon hourly commercial-readiness requirement is preserved in the consolidated post-#731 replay queue. Rebuild it from repaired current main as the smallest immutable-source workflow set, with PR work taking priority and product-gap development only when the live queue is empty. |
Summary
ContextualWisdomLab/naruon:developSafety boundaries
naruon:developworkflow_dispatchentrypoint for privileged workflowsCHANGELOG.mdVerification requested
Operating cadence
The scheduler runs at minute 7 of every hour. Open PRs always take priority; product development is suppressed until the live PR count reaches zero.
Summary by CodeRabbit