Skip to content

feat(automation): run naruon commercial readiness hourly - #709

Draft
seonghobae wants to merge 21 commits into
mainfrom
ops/naruon-hourly-commercial-readiness-20260803
Draft

feat(automation): run naruon commercial readiness hourly#709
seonghobae wants to merge 21 commits into
mainfrom
ops/naruon-hourly-commercial-readiness-20260803

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add a fixed-target hourly loop for ContextualWisdomLab/naruon:develop
  • dispatch central review-feedback fixes and current-head review/merge processing every hour
  • when the live open PR queue is zero, run one bounded OpenCode buyer-gap implementation slice
  • validate backend/frontend changes, block control-plane and secret edits, and publish only through a normal PR
  • add design, implementation plan, and static workflow trust-boundary tests

Safety boundaries

  • no direct write to naruon:develop
  • no workflow_dispatch entrypoint for privileged workflows
  • exact repository/base validation on every dispatch
  • one development worker at a time and one product gap per run
  • current-head required checks and independent approval remain mandatory
  • agent has no shell, web, task, or external-directory capability
  • maximum 12 changed files / 1,200 changed lines
  • product code requires tests and CHANGELOG.md

Verification requested

python -m pytest -q \
  tests/test_naruon_commercial_readiness_hourly_contract.py \
  tests/test_required_workflow_queue_contract.py
python -m pytest -q
actionlint \
  .github/workflows/naruon-commercial-readiness-hourly.yml \
  .github/workflows/naruon-commercial-readiness-development.yml

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

  • 새 기능
    • 매시간 상용화 준비 작업을 자동으로 점검하고 실행합니다.
    • 열린 PR의 리뷰 수정과 병합을 순차적으로 처리하며, 작업이 없을 때만 새로운 제품 개선 PR을 생성합니다.
    • 변경 범위, 민감한 파일, 테스트 및 브랜치 상태를 자동 검증합니다.
  • 문서
    • 상용화 준비 자동화의 운영 방식, 보안 기준 및 처리 절차를 문서화했습니다.
  • 테스트
    • 자동화 트리거, 작업 순서, 변경 제한 및 검증 조건을 확인하는 계약 테스트를 추가했습니다.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a814c8dc-1b39-43e2-bbbf-b801140dd259

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

고정된 develop 브랜치에서 시간별 PR 처리와 단일 제품 개발을 수행하는 GitHub Actions 루프를 추가했습니다. 제한된 자격 증명, OpenCode 권한, 변경 범위 검증, 플랫폼별 테스트, 복구 워크플로 및 정적 계약 테스트를 포함합니다.

Changes

Naruon 상업적 준비도 자동화 루프

Layer / File(s) Summary
루프 설계와 실행 계약
docs/superpowers/plans/..., docs/superpowers/specs/...
시간별 실행 순서, 고정 대상, PR 조건, 보안 경계, 변경 제한, 검증 명령 및 수용 기준을 정의했습니다.
시간별 PR 오케스트레이션
.github/workflows/naruon-commercial-readiness-hourly.yml, tests/test_naruon_commercial_readiness_hourly_contract.py
매시간 열린 PR을 조회하고 리뷰 수정·병합을 디스패치합니다. PR 큐가 비었고 활성 개발 워커가 없을 때만 제품 개발을 디스패치합니다.
제한된 제품 개발 워커
.github/workflows/naruon-commercial-readiness-development.yml
대상과 기준 SHA를 재검증하고 제한된 OpenCode 실행을 수행합니다. 변경 범위와 테스트를 검증한 뒤 하나의 제품 PR을 생성하고 후속 병합 처리를 디스패치합니다.
자동화 하드닝과 계약 검증
scripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.py
워크플로와 계약 테스트에 최소 권한, App 토큰 우선순위, 경로·콘텐츠 예산, 민감 경로 차단 및 네트워크 제한을 적용합니다.
PR 709 복구 실행
.github/workflows/pr709-commercial-readiness-hardening-v2.yml, .github/workflows/pr709-least-privilege-repair.yml
복구 브랜치에서 하드닝과 계약 검증을 실행합니다. 검증된 변경만 커밋하고 푸시합니다.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related issues

  • ContextualWisdomLab/.github#713: 고정 대상 시간별 루프, PR 우선 처리, 빈 큐에서의 단일 개발 워커라는 구현 목표가 일치합니다.
  • ContextualWisdomLab/noema#29: 시간별 상업적 준비도 워크플로와 쓰기 자격 증명 및 Maintainer App 프로비저닝의 코드 경로가 연결됩니다.

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
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 Naruon 상용화 준비 자동화를 매시간 실행하는 핵심 변경을 명확하고 간결하게 설명합니다.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ops/naruon-hourly-commercial-readiness-20260803

Comment @coderabbitai help to get the list of available commands.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two least-privilege issues must be fixed before merge:

  1. The target-repository write credential is exported as GITHUB_TOKEN to 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/gh publish and queue-validation steps.
  2. Resolve target credential currently 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.

Copy link
Copy Markdown
Contributor Author

@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.

Comment thread .github/workflows/naruon-commercial-readiness-development.yml Fixed
Comment thread .github/workflows/naruon-commercial-readiness-development.yml Fixed
Comment thread .github/workflows/naruon-commercial-readiness-hourly.yml Fixed
Comment thread .github/workflows/naruon-commercial-readiness-hourly.yml Fixed
Comment thread .github/workflows/pr709-commercial-readiness-hardening-v2.yml Fixed
@opencode-agent
opencode-agent Bot disabled auto-merge August 3, 2026 07:40

Copy link
Copy Markdown
Contributor Author

@jules Apply the least-privilege fix directly on the current PR head (do not add or depend on a self-modifying repair workflow): in naruon-commercial-readiness-development.yml, change the development job to contents: read while retaining only the exact permissions required for dispatch/OIDC, prefer the short-lived scoped App token before PAT fallback, and remove GITHUB_TOKEN/USE_GITHUB_TOKEN from the OpenCode implementation subprocess. In the hourly workflow, retain read-only contents and use write-capable credentials only in bounded dispatch/merge steps. Add/retain exact contract tests for these invariants. Delete .github/workflows/pr709-least-privilege-repair.yml, .github/workflows/pr709-commercial-readiness-hardening-v2.yml, and scripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.py. Run the focused contract tests, YAML/actionlint validation, and git diff --check, then push the minimal verified result.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 success with required evidence or explicit no-source not-applicable evidence.

  • Regression test: Keep the approval branch checking needs.coverage-evidence.result == success before 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 head e616816d5a471d16e719ba6ed0dee6b3c3b3aa3e.

  • 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"]
Loading

@opencode-agent

opencode-agent Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

OpenCode Review Overview

  • Head SHA: 4896dac870b5fb6cee5ac712bc010526f77adb47
  • Workflow run: 30848587964
  • Workflow attempt: 1
  • Gate result: REQUEST_CHANGES (approval step)

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 success with required evidence or explicit no-source not-applicable evidence.

  • Regression test: Keep the approval branch checking needs.coverage-evidence.result == success before 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 head 4896dac870b5fb6cee5ac712bc010526f77adb47.

  • 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"]
Loading

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 success with required evidence or explicit no-source not-applicable evidence.

  • Regression test: Keep the approval branch checking needs.coverage-evidence.result == success before 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 head e616816d5a471d16e719ba6ed0dee6b3c3b3aa3e.

  • 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"]
Loading

@opencode-agent
opencode-agent Bot force-pushed the ops/naruon-hourly-commercial-readiness-20260803 branch from e616816 to 4896dac Compare August 3, 2026 19:57

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 success with required evidence or explicit no-source not-applicable evidence.

  • Regression test: Keep the approval branch checking needs.coverage-evidence.result == success before 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 head 4896dac870b5fb6cee5ac712bc010526f77adb47.

  • 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"]
Loading

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_TOKENPAT_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-endpointsapi.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

📥 Commits

Reviewing files that changed from the base of the PR and between 3f65dbe and 4896dac.

📒 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.yml
  • docs/superpowers/plans/2026-08-03-naruon-hourly-commercial-readiness-loop.md
  • docs/superpowers/specs/2026-08-03-naruon-hourly-commercial-readiness-loop-design.md
  • scripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.py
  • tests/test_naruon_commercial_readiness_hourly_contract.py

Comment on lines +131 to +148
- 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"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 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.

Suggested change
- 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.

Comment on lines +428 to +437
- 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"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 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 300

Repository: 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 250

Repository: 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:


🏁 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],
})
PY

Repository: ContextualWisdomLab/.github

Length of output: 216


OpenCode 구현 단계에서 대상 저장소 쓰기 자격 증명을 제거하십시오.

GITHUB_TOKENUSE_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.

Comment on lines +17 to +18
orchestrate:
runs-on: ubuntu-latest

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 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.

Suggested change
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.

Comment on lines +1 to +4
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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 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

Comment on lines +55 to +106
- 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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 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 || true

Repository: 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.yml

Repository: 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')
PY

Repository: 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

Comment on lines +332 to +349
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",
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

pull-requests: read 제거가 큐 읽기 폴백 경로를 깨뜨립니다.

큐 읽기 스텝의 GH_TOKENsecrets.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.

Suggested change
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.

Comment on lines +366 to +383
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",
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 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' . || true

Repository: 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' . || true

Repository: 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:]))
PY

Repository: ContextualWisdomLab/.github

Length of output: 704


기존 스텝 키를 보존하도록 치환 로직을 수정하십시오.
Decide whether product development may run에는 env:가 있고, Dispatch one buyer-visible product gap에는 if:가 있습니다. 두 스텝 모두 패턴과 일치하지 않아 replace_once_or_acceptRuntimeError를 발생시키고 부트스트랩을 중단합니다. 기존 키 뒤에 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.

Comment on lines +488 to +497
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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 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.*)$' || true

Repository: 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 || true

Repository: 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"
done

Repository: 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' || true

Repository: 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))
PY

Repository: ContextualWisdomLab/.github

Length of output: 519


테스트 또는 명시적 제외 설정을 추가하십시오.

pyproject.tomlscripts/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

Comment on lines +33 to +44
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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.

Suggested change
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

Comment on lines +106 to +117
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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.

Suggested change
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

@seonghobae
seonghobae marked this pull request as draft August 4, 2026 00:04

Copy link
Copy Markdown
Contributor Author

@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:

  • .github/workflows/naruon-commercial-readiness-hourly.yml
  • .github/workflows/naruon-commercial-readiness-development.yml
  • the design/implementation documentation already in this PR
  • tests/test_naruon_commercial_readiness_hourly_contract.py

Delete entirely from the final diff:

  • .github/workflows/pr709-commercial-readiness-hardening-v2.yml
  • .github/workflows/pr709-least-privilege-repair.yml
  • scripts/ci/bootstrap_naruon_commercial_readiness_hardening_v2.py

Required production fixes:

  1. In the development workflow, prefer the short-lived scoped App token. Fall back to the PAT only for trusted post-agent validation/publication when no App token is available.
  2. Remove GITHUB_TOKEN and USE_GITHUB_TOKEN from the untrusted OpenCode implementation step. The implementation process must have no target-repository write credential. Keep write credentials only in subsequent trusted validation/push/PR/dispatch steps.
  3. Add a 15-minute job timeout to the hourly orchestrate job so a stalled API call cannot occupy the hourly concurrency group for GitHub's default maximum.
  4. Keep hourly permissions at the minimum needed for its real paths: actions: write, contents: read, and pull-requests: read; do not restore job-wide write credentials.
  5. Materialize the bounded-change guard directly in the development workflow. Register untracked files with git add -A -N, use the cached numstat for binary and line-budget checks, count new files, and reject extensionless CODEOWNERS anywhere as well as AGENTS.md and CLAUDE.md.
  6. Preserve existing env: and if: keys when adding step-scoped tokens. The decision and dispatch steps must keep their original control flow.
  7. Correct the contract tests to match the exact quoted JSON keys ("review_dispatch_limit", "stale_opencode_minutes", and "merge_mode").
  8. Keep the fixed target exactly ContextualWisdomLab/naruon:develop, minute-7 hourly cadence, PR-first behavior, single-flight product development, one buyer-visible slice, normal-PR-only publication, no direct develop push, no privileged workflow_dispatch, no secret/control-plane edits, and the 12-file/1,200-line bounds.
  9. Ensure the zero-PR product worker cannot run when the PR/task inventory is unknown or incomplete, and that completion or publication failures remain fail-closed.

Verification required on the final exact head:

  • YAML safe-load for both workflows
  • actionlint for both workflows
  • full repository pytest suite
  • focused hourly and queue-contract tests
  • 100% statement/branch coverage and docstrings for all new retained production code (there should be no retained bootstrap module)
  • Ruff and git diff --check
  • security review confirming no target write token reaches the implementation agent and no temporary workflow remains

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants