diff --git a/.github/workflows/hourly-opencode-commercial-readiness.yml b/.github/workflows/hourly-opencode-commercial-readiness.yml new file mode 100644 index 00000000..31cdb014 --- /dev/null +++ b/.github/workflows/hourly-opencode-commercial-readiness.yml @@ -0,0 +1,430 @@ +name: Hourly ScopeWeave OpenCode Commercial Readiness + +on: + workflow_dispatch: + inputs: + dry_run: + description: Evaluate the gate and prompt without running OpenCode + required: false + default: false + type: boolean + schedule: + - cron: "41 * * * *" + +concurrency: + group: scopeweave-hourly-opencode-commercial-readiness + cancel-in-progress: false + +# The default token is read-only. The single trusted publication step uses the +# job-scoped write token after the OpenCode process has exited. OpenCode receives +# only NVIDIA_API_KEY, which is sourced from NVIDIA_NIM_API_KEY. +permissions: + contents: read + +jobs: + develop-one-bounded-slice: + if: github.repository == 'ContextualWisdomLab/scopeweave' + runs-on: ubuntu-latest + timeout-minutes: 55 + permissions: + contents: write + issues: read + pull-requests: write + env: + DEFAULT_BRANCH: develop + DRY_RUN: ${{ inputs.dry_run || false }} + OPENCODE_VERSION: "1.17.13" + OPENCODE_SHA256: 157afa289d1a8d9372de0ce19ac726119b937a1f6b201808d46f06e4e59bb348 + OPENCODE_MODEL_CANDIDATES: >- + nvidia-nim/nvidia/llama-3.3-nemotron-super-49b-v1.5 + nvidia-nim/nvidia/nemotron-3-super-120b-a12b + nvidia-nim/deepseek-ai/deepseek-v4-pro + OPENCODE_RUN_TIMEOUT_SECONDS: "2400" + steps: + - name: Enforce pull-request-first single-flight gate + id: gate + shell: bash + env: + GH_TOKEN: ${{ github.token }} + NVIDIA_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }} + run: | + set -euo pipefail + + if ! open_prs="$( + gh pr list \ + --repo "$GITHUB_REPOSITORY" \ + --state open \ + --limit 1 \ + --json number,url + )"; then + { + echo "dispatch=false" + echo "reason=pull_request_inventory_unavailable" + } >>"$GITHUB_OUTPUT" + echo "Pull-request inventory was unavailable; failing closed." \ + >>"$GITHUB_STEP_SUMMARY" + exit 0 + fi + + if [ "$(jq 'length' <<<"$open_prs")" -gt 0 ]; then + { + echo "dispatch=false" + echo "reason=open_pull_request" + } >>"$GITHUB_OUTPUT" + echo "An open pull request exists; central PR governance owns this hour." \ + >>"$GITHUB_STEP_SUMMARY" + exit 0 + fi + + if [ -z "${NVIDIA_API_KEY:-}" ]; then + { + echo "dispatch=false" + echo "reason=nim_api_key_unavailable" + } >>"$GITHUB_OUTPUT" + cat >>"$GITHUB_STEP_SUMMARY" <<'SUMMARY' + Autonomous development is disabled because `NVIDIA_NIM_API_KEY` is not configured. + No Copilot subscription, Agent Tasks API, or fine-grained user token is used. + SUMMARY + exit 0 + fi + + { + echo "dispatch=true" + echo "reason=ready" + } >>"$GITHUB_OUTPUT" + + - name: Prepare bounded commercial-quality assignment + if: steps.gate.outputs.dispatch == 'true' + shell: bash + run: | + cat >"$RUNNER_TEMP/scopeweave-agent-prompt.md" <<'PROMPT' + Continue product development for ContextualWisdomLab/scopeweave on the develop branch. + + Start by reading AGENTS.md, README.md, CHANGELOG.md, docs/doctoring, + docs/operations, the trusted open-issue and recently-merged-PR snapshots under + .opencode-context, current source, tests, package contracts, service adapters, + database schema, and release evidence. Select exactly one highest-impact, + buyer-visible product, reliability, interoperability, security, evaluation, + accessibility, or operations gap that can be completed as one bounded pull request. + Do not create another repository or a broad platform rewrite. + + Preserve standalone ScopeWeave operation while keeping framework-independent seams + suitable for ContextualWisdomLab/.github, naruon, Clearfolio, + contextual-orchestrator, and future MSA adapters. Use or improve + contextual-orchestrator for every product LLM path instead of adding a direct, + provider-specific client. Treat all model output as untrusted, bounded, auditable + input that requires deterministic validation before persistence or user-visible use. + + Work test-first: establish a failing realistic regression or executable contract + before production code, confirm that it fails for the intended reason, and then + implement the smallest coherent vertical slice. Never weaken, skip, delete, + suppress, or mark a meaningful release-gate test expected-to-fail merely to obtain + green output. Maintain 100% production statement, branch, function, and line + coverage for each new or materially changed production module. Every public module, + class, function, method, property, exported constant, security invariant, input, + output, and exception must have beginner-readable JSDoc or docstrings. + + New database tables, columns, indexes, triggers, constraints, migration objects, + and durable queues must use descriptive two-word-or-longer snake_case names. Preserve + tenant isolation, least privilege, bounded resource use, idempotency, deterministic + behavior where applicable, migration rollback, immutable audit evidence, and + secret-free logs. Tests must model realistic multi-tenant projects, concurrent edits + or requests, partial downstream failure, restart and migration behavior, large WBS + inputs, accessibility, recovery, and customer-visible outputs as appropriate. + + Use the latest authoritative international standard, primary technical + specification, or peer-reviewed paper for every material decision. Record the + decision, limitations, executable evidence, and APA 7th references under + docs/doctoring/. Distinguish source-supported claims, measurements, assumptions, and + inference. Do not claim certification or compliance beyond executable evidence. + + Use Figma or Product Design only when the selected slice has a real buyer-facing UI. + For such a slice, account for loading, empty, validation, error, keyboard, + screen-reader, touch, narrow-viewport, and permission states. Do not manufacture + design work for a backend-only or library-only increment. + + If an LLM-dependent test is genuinely necessary, use the established + contextual-orchestrator boundary and NVIDIA_NIM_API_KEY in GitHub Actions; keep + deterministic non-LLM tests as the required merge gate. Do not edit, copy, replace, + or weaken CodeRabbit, Noema, OpenCode-review, Strix, security-review, organization + rules, reviewer credentials, `.trivyignore`, or branch protection. + + Run focused tests, npm ci, complete unit and API suites, coverage and docstring + gates, cloud browser E2E for user-visible changes, syntax and dependency checks, + relevant adversarial/security tests, and git diff --check. Update CHANGELOG.md and + all affected product, architecture, operations, security, accessibility, evaluation, + migration, and rollback documentation. Change package versions only when the + integrated repository is genuinely release-ready. Do not merge, publish, release, + push, or commit. Leave one bounded verified working-tree increment and create + PR_MESSAGE.md at the repository root: first line is the proposed title, followed by + a body with buyer impact, exact verification evidence, standards references, risks, + compatibility, and residual gaps. A trusted workflow step will publish the branch; + central PR governance owns review, repair, exact-head checks, approval, and merge. + PROMPT + + - name: Record dry-run decision + if: steps.gate.outputs.dispatch == 'true' && env.DRY_RUN == 'true' + shell: bash + run: | + { + echo "Dry run: one bounded OpenCode/NVIDIA NIM development session would start." + echo + cat "$RUNNER_TEMP/scopeweave-agent-prompt.md" + } >>"$GITHUB_STEP_SUMMARY" + + - name: Check out develop without persisted credentials + if: steps.gate.outputs.dispatch == 'true' && env.DRY_RUN != 'true' + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 + with: + ref: develop + fetch-depth: 0 + persist-credentials: false + + - name: Set up the supported Node.js runtime + if: steps.gate.outputs.dispatch == 'true' && env.DRY_RUN != 'true' + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 + with: + node-version: 22.13.0 + cache: npm + + - name: Materialize trusted repository context + if: steps.gate.outputs.dispatch == 'true' && env.DRY_RUN != 'true' + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + mkdir -p .opencode-context + gh issue list \ + --repo "$GITHUB_REPOSITORY" \ + --state open \ + --limit 100 \ + --json number,title,body,labels,createdAt,updatedAt \ + >.opencode-context/open-issues.json + gh pr list \ + --repo "$GITHUB_REPOSITORY" \ + --state merged \ + --limit 30 \ + --json number,title,body,mergedAt,files \ + >.opencode-context/recent-merged-prs.json + { + echo /opencode.json + echo /.opencode-context/ + } >>.git/info/exclude + + - name: Install the pinned OpenCode CLI + if: steps.gate.outputs.dispatch == 'true' && env.DRY_RUN != 'true' + shell: bash + run: | + set -euo pipefail + archive="${RUNNER_TEMP}/opencode-linux-x64.tar.gz" + install_dir="${HOME}/.opencode/bin" + mkdir -p "$install_dir" + curl -fsSL \ + -o "$archive" \ + "https://github.com/anomalyco/opencode/releases/download/v${OPENCODE_VERSION}/opencode-linux-x64.tar.gz" + printf '%s %s\n' "$OPENCODE_SHA256" "$archive" | sha256sum -c - + tar -xzf "$archive" -C "$RUNNER_TEMP" + install -m 0755 "${RUNNER_TEMP}/opencode" "${install_dir}/opencode" + "${install_dir}/opencode" --version + echo "$install_dir" >>"$GITHUB_PATH" + + - name: Configure OpenCode for NVIDIA NIM + if: steps.gate.outputs.dispatch == 'true' && env.DRY_RUN != 'true' + shell: bash + run: | + set -euo pipefail + cat >"$GITHUB_WORKSPACE/opencode.json" <<'CONFIG' + { + "$schema": "https://opencode.ai/config.json", + "enabled_providers": ["nvidia-nim"], + "model": "nvidia-nim/nvidia/llama-3.3-nemotron-super-49b-v1.5", + "small_model": "nvidia-nim/meta/llama-3.3-70b-instruct", + "provider": { + "nvidia-nim": { + "npm": "@ai-sdk/openai-compatible", + "name": "NVIDIA NIM", + "options": { + "baseURL": "https://integrate.api.nvidia.com/v1", + "apiKey": "{env:NVIDIA_API_KEY}" + }, + "models": { + "nvidia/llama-3.3-nemotron-super-49b-v1.5": { + "name": "NVIDIA Llama 3.3 Nemotron Super 49B v1.5", + "tool_call": true, + "limit": {"context": 131072, "output": 8192} + }, + "nvidia/nemotron-3-super-120b-a12b": { + "name": "NVIDIA Nemotron 3 Super 120B", + "tool_call": true, + "limit": {"context": 131072, "output": 8192} + }, + "deepseek-ai/deepseek-v4-pro": { + "name": "DeepSeek V4 Pro (NIM)", + "tool_call": true, + "limit": {"context": 131072, "output": 8192} + }, + "meta/llama-3.3-70b-instruct": { + "name": "Meta Llama 3.3 70B Instruct (NIM)", + "tool_call": true, + "limit": {"context": 131072, "output": 8192} + } + } + } + } + } + CONFIG + + - name: Run the bounded NVIDIA NIM development agent + if: steps.gate.outputs.dispatch == 'true' && env.DRY_RUN != 'true' + shell: bash + env: + NVIDIA_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }} + run: | + set -euo pipefail + test -n "$NVIDIA_API_KEY" + start_sha="$(git rev-parse HEAD)" + printf '%s\n' "$start_sha" >"$RUNNER_TEMP/start-sha" + prompt="$(cat "$RUNNER_TEMP/scopeweave-agent-prompt.md")" + status=1 + for model in $OPENCODE_MODEL_CANDIDATES; do + echo "::group::opencode $model" + if timeout --kill-after=30s "${OPENCODE_RUN_TIMEOUT_SECONDS}s" \ + env -u GH_TOKEN -u GITHUB_TOKEN -u REPOSITORY_TOKEN \ + -u ACTIONS_ID_TOKEN_REQUEST_TOKEN -u ACTIONS_ID_TOKEN_REQUEST_URL \ + opencode run "$prompt" --model "$model"; then + status=0 + echo "::endgroup::" + echo "Agent session completed with \`$model\`." >>"$GITHUB_STEP_SUMMARY" + break + fi + echo "::endgroup::" + echo "::warning::Model $model failed; discarding partial work and trying the next candidate." + git reset --hard "$start_sha" + git clean -fd + done + if [ "$status" -ne 0 ]; then + echo "::error::Every NVIDIA NIM model candidate failed; no work was proposed." + exit 1 + fi + if [ "$(git rev-parse HEAD)" != "$start_sha" ]; then + echo "::error::The agent created commits; trusted publication requires working-tree-only output." + exit 1 + fi + + - name: Enforce protected workflow and credential boundaries + if: steps.gate.outputs.dispatch == 'true' && env.DRY_RUN != 'true' + shell: bash + run: | + set -euo pipefail + start_sha="$(cat "$RUNNER_TEMP/start-sha")" + changed_files="$( + { + git diff --name-only "$start_sha"...HEAD + git diff --name-only + git ls-files --others --exclude-standard + } | sort -u + )" + if [ -z "$changed_files" ]; then + echo "No product change was produced." >>"$GITHUB_STEP_SUMMARY" + exit 0 + fi + if printf '%s\n' "$changed_files" | grep -E '^\.github/workflows/.*(coderabbit|noema|opencode-review|review-agent|strix|security-review).*\.ya?ml$'; then + echo "::error::The development agent modified a protected review-agent workflow." + exit 1 + fi + if printf '%s\n' "$changed_files" | grep -E '(^|/)(\.trivyignore|\.semgrepignore|\.gitleaksignore)$'; then + echo "::error::The development agent introduced or changed a security-scan suppression file." + exit 1 + fi + if git diff --no-ext-diff "$start_sha" -- . \ + | grep -E '(^|[^A-Za-z])(ghp_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,}|nvapi-[A-Za-z0-9_-]{20,})'; then + echo "::error::A credential-like literal was introduced." + exit 1 + fi + git diff --check + + - name: Run the complete product verification contract + if: steps.gate.outputs.dispatch == 'true' && env.DRY_RUN != 'true' + shell: bash + env: + CI: "true" + run: | + set -euo pipefail + npm ci + npm run test:unit + npm run test:api + npm run coverage + node scripts/ci/static_coverage_evidence.mjs docstrings + if npm run | grep -q 'test:e2e:cloud'; then + npm run test:e2e:cloud + fi + git diff --check + + - name: Revalidate queue ownership and open exactly one pull request + if: steps.gate.outputs.dispatch == 'true' && env.DRY_RUN != 'true' + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + cd "$GITHUB_WORKSPACE" + rm -f opencode.json + rm -rf .opencode-context + + if [ -z "$(git status --porcelain)" ]; then + echo "The agent made no verified source change; nothing to propose this hour." \ + >>"$GITHUB_STEP_SUMMARY" + exit 0 + fi + + if [ "$( + gh pr list \ + --repo "$GITHUB_REPOSITORY" \ + --state open \ + --limit 1 \ + --json number \ + --jq 'length' + )" -ne 0 ]; then + echo "A pull request appeared while the agent was running; refusing duplicate development." \ + >>"$GITHUB_STEP_SUMMARY" + exit 0 + fi + + title="ScopeWeave autonomous commercial-readiness increment" + body_file="$RUNNER_TEMP/pr-body.md" + if [ -f PR_MESSAGE.md ]; then + candidate_title="$(head -n 1 PR_MESSAGE.md | sed 's/^#\+ *//')" + [ -n "$candidate_title" ] && title="$candidate_title" + tail -n +2 PR_MESSAGE.md >"$body_file" + rm -f PR_MESSAGE.md + else + cat >"$body_file" <<'BODY' + Bounded OpenCode/NVIDIA NIM product increment. See the focused diff, + executable verification, CHANGELOG.md, and doctoring record. Central PR + governance remains responsible for independent review, repair, exact-head + checks, approval, and merge. + BODY + fi + + branch="nim-agent/product-dev-${GITHUB_RUN_ID}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git checkout -b "$branch" + git add -A + git commit -m "$title" + git push \ + "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" \ + "HEAD:refs/heads/${branch}" + pr_url="$( + gh pr create \ + --repo "$GITHUB_REPOSITORY" \ + --base "$DEFAULT_BRANCH" \ + --head "$branch" \ + --title "$title" \ + --body-file "$body_file" + )" + { + echo "Opened bounded pull request: $pr_url" + echo "Central PR governance owns review, repair, revalidation, and merge." + } >>"$GITHUB_STEP_SUMMARY" diff --git a/CHANGELOG.md b/CHANGELOG.md index e84f41f8..831351d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added a fail-closed hourly commercial-readiness loop that runs a pinned, + checksum-verified OpenCode agent against NVIDIA hosted NIM models only when + the pull-request queue is empty. The agent receives `NVIDIA_NIM_API_KEY` but + no GitHub mutation credential; a later trusted step performs deterministic + unit, API, coverage, docstring, cloud E2E, and diff verification before + opening exactly one pull request for central review and merge governance. - Added deterministic PM analysis for requirements/RFI/RFP readiness, WBS estimation coverage, dependency risk, and procurement package section checks. - Preserved PM-analysis research papers, NASA WBS handbook, BCP 14, and JSON @@ -32,9 +38,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added regression coverage that prevents array-valued passwords from being coerced into valid credentials. - Updated Hono runtime dependencies to patched supported releases. +- Sanitized Clearfolio submission, status, and artifact-link transport failures + so network details and downstream response text cannot reach browser or + diagnostic payloads; rejected unknown or whitespace-padded conversion states + and malformed, unsupported-scheme, or HTTPS-downgrade artifact links. +- Prevented the product-development agent from modifying reviewer-owned + workflows or scanner-suppression files, inheriting GitHub mutation and OIDC + credentials, committing or publishing directly, or installing an unverified + OpenCode binary. ### Changed +- Attachment-list status refresh now removes the per-row database lookup, + uses a configurable bounded worker pool with per-item abortable timeouts and + a request-wide latency budget, preserves stale status after downstream, + timeout, malformed-response, and persistence failures, excludes internal + conversion identifiers from responses, reports attempted, changed, failed, + skipped-data, and deferred-budget counters separately, and exposes fixed + low-cardinality timeout, lookup, validation, and persistence failure counters. - 프로젝트 이름 입력 필드에 입력 예시(placeholder)를 추가하여 사용자 편의성을 개선했습니다. - 데이터 테이블의 반복되는 액션 버튼에 컨텍스트 정보(작업명)를 포함한 명시적인 ARIA 레이블을 추가하고, 유효성 검사 에러를 폼 필드에 연결하여 접근성을 개선했습니다. - `createGanttBarElement`, `renderGantt`, `buildWeekdayTimeline`에서 반복적으로 호출되던 `compareDateStrings`를 직접적인 문자열 비교 연산(`>=`, `<=`)으로 교체하여 O(N*D) 복잡도의 캐시 스레싱과 정규식 검사를 방지했습니다. @@ -61,4 +82,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.0.1] - 2026-06-25 ### 성능 개선 (Performance) -- 드래그 앤 드롭 동작 중 `dragover` 이벤트에서 발생하는 O(N) 작업 리스트 검색 성능 병목 문제를, O(1) 해시맵(Map) 기반의 캐싱 조회 로직으로 개선하여 큰 크기의 WBS 리스트에서의 버벅임 현상을 해결했습니다. \ No newline at end of file +- 드래그 앤 드롭 동작 중 `dragover` 이벤트에서 발생하는 O(N) 작업 리스트 검색 성능 병목 문제를, O(1) 해시맵(Map) 기반의 캐싱 조회 로직으로 개선하여 큰 크기의 WBS 리스트에서의 버벅임 현상을 해결했습니다. diff --git a/docs/doctoring/hourly-opencode-commercial-readiness.md b/docs/doctoring/hourly-opencode-commercial-readiness.md new file mode 100644 index 00000000..bab06123 --- /dev/null +++ b/docs/doctoring/hourly-opencode-commercial-readiness.md @@ -0,0 +1,161 @@ +# Hourly OpenCode commercial-readiness loop: evidence and design record + +## Decision + +ScopeWeave separates two autonomous responsibilities: + +1. organization-owned workflows review, repair, revalidate, and merge existing + pull requests under the repository rules; and +2. one repository-owned scheduled workflow may create a new product-development + pull request only when the open pull-request queue is empty. + +The product-development workflow runs OpenCode directly inside GitHub Actions and +uses the organization secret named `NVIDIA_NIM_API_KEY`. It does not create a +GitHub Copilot Agent Task, require a Copilot subscription, or use a fine-grained +user token. The OpenCode process receives the NVIDIA key but no GitHub mutation +credential. A later trusted shell step performs the commit, branch push, and pull +request creation only after deterministic verification and a second queue check. + +This boundary preserves the existing CodeRabbit, Noema, OpenCode-review, Strix, +and central merge-scheduler identities. The new workflow neither copies those +reviewers nor changes their keys, permissions, or approval semantics. + +## Authoritative platform and provider constraints + +OpenCode documents custom OpenAI-compatible providers through an explicit +provider identifier, the `@ai-sdk/openai-compatible` package, a `baseURL`, an +explicit model map, and an environment-derived API key. ScopeWeave follows that +contract for the NVIDIA hosted endpoint rather than embedding a credential or +adding a repository-specific provider client. + +NVIDIA documents NIM for large language models as an OpenAI-compatible inference +API. NVIDIA's hosted endpoint uses `https://integrate.api.nvidia.com/v1`, and the +API key is conventionally provided through `NVIDIA_API_KEY`. The workflow maps +the GitHub secret `NVIDIA_NIM_API_KEY` to that process-only environment variable. + +GitHub scheduled workflows execute from the latest commit on the default branch. +The hourly loop therefore remains inert until this workflow is reviewed and +merged into `develop`. GitHub also permits scheduled runs to be delayed or +omitted under load, so the design treats the schedule as a best-effort heartbeat, +not as a durable clock or service-level guarantee. + +GitHub recommends explicit least-privilege token permissions. The workflow-level +token is read-only. The job receives repository-content and pull-request write +permissions only because the trusted publication step must create one branch and +one pull request. The OpenCode command explicitly removes `GH_TOKEN`, +`GITHUB_TOKEN`, repository-token variables, and OIDC request variables from its +environment. The NVIDIA secret is scoped only to the gate and OpenCode steps and +is absent during dependency installation and test execution. + +## Supply-chain and execution controls + +OpenCode is installed from one explicit release archive. The workflow pins both +the release version and a SHA-256 digest and verifies the downloaded archive +before installation. It does not install `latest`, execute an unverified install +script, or allow a pull request to select the agent binary. + +The coding-agent process may edit the checked-out repository but may not commit, +push, open a pull request, merge, publish, or release. A post-agent boundary +rejects: + +- changes to review-agent and security-review workflows; +- changes to `.trivyignore`, `.semgrepignore`, or `.gitleaksignore`; +- credential-like GitHub or NVIDIA token literals; +- commits created by the agent; and +- whitespace or conflict-marker errors detected by `git diff --check`. + +The workflow then runs `npm ci`, all unit and API tests, coverage, the configured +docstring evidence gate, cloud browser E2E when available, and a final diff +check. Publication occurs only after these commands succeed. Current-head GitHub +Checks and independent reviews remain separate protected-branch evidence; a +successful local workflow run does not approve or merge its own pull request. + +## Single-flight and race handling + +The first gate fails closed when pull-request inventory is unavailable, when an +open pull request exists, or when the NVIDIA secret is absent. One repository-wide +non-cancelling concurrency group prevents two scheduled agents from running at +the same time. + +The workflow checks the open pull-request queue again immediately before branch +publication. If another actor created a pull request while the model was working, +the trusted publisher discards publication for that hour. GitHub does not expose +a repository-wide compare-and-create transaction for pull requests, so this does +not claim global atomicity against unrelated clients. Repository operators must +keep this workflow as the sole scheduled product-development producer. + +## Product-quality contract + +The assignment limits each run to one buyer-visible vertical slice. It requires: + +- red-green-refactor test-first development; +- realistic multi-tenant, concurrency, failure, migration, recovery, scale, and + accessibility cases appropriate to the selected feature; +- 100% statement, branch, function, and line coverage for new or materially + changed production modules; +- complete beginner-readable JSDoc or docstrings; +- descriptive two-word-or-longer `snake_case` database objects; +- standalone operation and replaceable MSA adapters; +- `contextual-orchestrator` for any product LLM path; +- deterministic validation of model output before persistence; +- APA 7th standards or peer-reviewed evidence in `docs/doctoring/`; +- Figma or Product Design only for genuine buyer-facing interaction work; and +- CHANGELOG, operations, security, migration, rollback, and release evidence + updates when relevant. + +The prompt explicitly prohibits security suppression, branch-protection changes, +self-merge, self-release, and unrelated refactoring. The model must leave a +working-tree change and a `PR_MESSAGE.md`; a trusted step packages that output as +one pull request against `develop`. + +## Verification contract + +The repository contract test must prove that the workflow: + +- runs hourly with non-cancelling single-flight concurrency; +- fails closed when a pull request exists or the NVIDIA secret is unavailable; +- contains no Copilot Agent Tasks endpoint or `COPILOT_GITHUB_TOKEN`; +- pins and verifies the OpenCode binary; +- uses the OpenAI-compatible NVIDIA endpoint and an environment key; +- strips GitHub mutation credentials from OpenCode; +- blocks reviewer-workflow and scanner-suppression changes; +- performs the complete deterministic verification sequence before publication; +- rechecks the pull-request queue before publishing; and +- leaves independent review, exact-head Checks, and merge authority with central + governance. + +The first live scheduled run additionally provides operational evidence that the +configured OpenCode release, NVIDIA model pool, and repository secret work in the +actual GitHub-hosted environment. Until that run succeeds, the workflow is +implemented and statically verified but not yet operationally proven. + +## Limitations + +The NVIDIA hosted model catalog can change independently of the repository. A +candidate-model failure causes the workflow to discard partial work and try the +next pinned model identifier. Failure of every candidate produces no pull +request. The model pool and OpenCode release should be updated only through a +reviewed pull request with current documentation and checksum evidence. + +The hourly schedule is not a durable job queue. Missed ticks, GitHub Actions +capacity, NVIDIA service availability, repository billing policy, or secret +rotation can postpone a run. The workflow deliberately fails closed instead of +creating overlapping or unverifiable work. + +## References + +Anomaly. (2026). *Providers*. OpenCode documentation. +https://opencode.ai/docs/providers + +GitHub. (2026a). *Events that trigger workflows*. GitHub Docs. +https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows + +GitHub. (2026b). *Security hardening for GitHub Actions*. GitHub Docs. +https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions + +GitHub. (2026c). *Workflow syntax for GitHub Actions*. GitHub Docs. +https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax + +NVIDIA Corporation. (2026). *API reference: NVIDIA NIM for large language +models*. NVIDIA Documentation. +https://docs.nvidia.com/nim/large-language-models/2.0.1/reference/api-reference.html diff --git a/docs/operations/hourly-opencode-commercial-readiness.md b/docs/operations/hourly-opencode-commercial-readiness.md new file mode 100644 index 00000000..dbac92fc --- /dev/null +++ b/docs/operations/hourly-opencode-commercial-readiness.md @@ -0,0 +1,174 @@ +# Hourly OpenCode commercial-readiness operations + +## Ownership + +The repository workflow `.github/workflows/hourly-opencode-commercial-readiness.yml` +creates product-development pull requests. It does not review or merge them. +Organization-central workflows remain responsible for review dispatch, review +feedback repair, exact-head Checks, branch updates, independent approval, and +policy-compliant merge. + +Only one scheduled product-development producer should exist for ScopeWeave. +Do not add a Copilot Agent Tasks scheduler or another repository-local PR merge +scheduler alongside this workflow. + +## Required configuration + +Configure the organization or repository Actions secret: + +- `NVIDIA_NIM_API_KEY`: NVIDIA hosted NIM API credential. + +The workflow maps this value to `NVIDIA_API_KEY` only for the OpenCode gate and +agent process. It does not use `COPILOT_GITHUB_TOKEN`, a Copilot subscription, a +fine-grained user token, or any review-agent credential. + +No additional GitHub token is required. The trusted publication step uses the +job-scoped `GITHUB_TOKEN` with `contents: write` and `pull-requests: write`. The +OpenCode process explicitly removes GitHub mutation and OIDC variables from its +environment. + +## Schedule and activation + +The workflow runs at minute 41 of each UTC hour and also supports a manual dry +run. Scheduled workflows are active only after the workflow exists on the +default branch. Until its pull request merges into `develop`, no hourly agent is +scheduled. + +A scheduled tick performs no coding when: + +- an open pull request exists; +- pull-request inventory cannot be read; +- `NVIDIA_NIM_API_KEY` is absent; +- another hourly run is active; +- every configured NVIDIA model fails; +- the agent creates a commit instead of a working-tree change; +- protected review workflow or scanner-suppression files change; +- deterministic verification fails; +- another pull request appears before publication; or +- the agent produces no source change. + +These outcomes are intentional fail-closed states, not reasons to bypass the +queue or required Checks. + +## Manual dry run + +From the Actions page, select **Hourly ScopeWeave OpenCode Commercial Readiness**, +choose **Run workflow**, and set `dry_run` to `true`. The run evaluates the +single-flight gate and prints the bounded assignment to the step summary without +checking out source, invoking NVIDIA NIM, creating a branch, or opening a pull +request. + +A dry run still requires an empty pull-request queue and a configured NVIDIA +secret because it verifies the same activation preconditions as a live run. + +## OpenCode and model updates + +The workflow pins: + +- the OpenCode release number; +- the SHA-256 digest of the Linux x64 archive; and +- an ordered NVIDIA NIM model-candidate pool. + +To update OpenCode: + +1. inspect the official release and security notes; +2. download the exact `opencode-linux-x64.tar.gz` asset independently; +3. calculate its SHA-256 digest; +4. update both `OPENCODE_VERSION` and `OPENCODE_SHA256` in one reviewed pull + request; +5. update the workflow contract test if the installation contract changes; +6. run a manual dry run and then one live empty-queue run; and +7. retain the previous version and digest in the pull-request evidence for + rollback. + +Never replace the pin with `latest`, an unverified installation script, or a +pull-request-controlled download URL. + +To update models, verify the model identifier against the current NVIDIA hosted +catalog and confirm OpenCode tool-call behavior. Keep at least two independent +candidates so a single model outage does not silently create partial work. A +failed candidate is followed by `git reset --hard` and `git clean -fd`; partial +changes never flow into the next candidate. + +## Verification and publication + +After OpenCode exits successfully, trusted workflow steps: + +1. prove the agent did not create commits; +2. enumerate tracked and untracked changes; +3. reject protected reviewer workflows and scanner-suppression files; +4. reject credential-like literals; +5. run `git diff --check`; +6. run `npm ci`; +7. run the full unit and API suites; +8. run coverage and configured docstring evidence; +9. run cloud browser E2E when available; +10. recheck that the open pull-request queue is still empty; +11. remove temporary OpenCode configuration and context snapshots; +12. create one conventional commit on `nim-agent/product-dev-`; and +13. open one pull request against `develop`. + +The newly opened pull request is not approved by this workflow. Required GitHub +Checks, CodeRabbit, Noema, OpenCode-review, Strix, unresolved-thread rules, and +independent approval remain authoritative. + +## Failure diagnosis + +### `pull_request_inventory_unavailable` + +Confirm that GitHub API access and repository Actions are healthy. Do not convert +this state into an empty queue. Rerun after the API is available. + +### `open_pull_request` + +No action is required. The central PR loop owns review, repair, revalidation, and +merge. The next empty-queue hourly tick may start product development. + +### `nim_api_key_unavailable` + +Verify that `NVIDIA_NIM_API_KEY` is available to this repository through the +organization or repository secret policy. Do not print or copy the secret into a +workflow variable, issue, log, or pull request. + +### Archive checksum failure + +Treat the release artifact as untrusted. Compare the configured version, asset +URL, and independently calculated digest. Update the pin only through a reviewed +pull request. Never disable `sha256sum -c`. + +### All model candidates fail + +Inspect provider availability and fixed OpenCode logs for the operation. Do not +reuse partial working-tree output. Confirm the NVIDIA endpoint, model catalog, +key validity, and rate or quota status before changing the pool. + +### Protected-file boundary failure + +Review the agent diff manually. Do not whitelist changes to CodeRabbit, Noema, +OpenCode-review, Strix, security-review, `.trivyignore`, `.semgrepignore`, or +`.gitleaksignore` in this workflow. Such changes require a separately scoped, +human-authored governance pull request. + +### Deterministic test failure + +The workflow must not publish. Reproduce the failure on a normal development +branch, fix the product or test contract, and rerun the full suite. Do not skip, +ignore, suppress, or lower coverage thresholds. + +### Queue changed before publication + +Another pull request appeared while OpenCode was working. The trusted publisher +leaves the agent output unpublished. The active pull request owns the queue and +must complete through central governance first. + +## Rollback + +Disable the loop without deleting evidence by disabling the workflow in GitHub +Actions. A source rollback reverts the workflow, its contract test, operations +record, doctoring record, and CHANGELOG entry together. Disabling or reverting +the product-development loop does not change the organization-central review +workflows or their credentials. + +If a newly opened agent pull request is unsafe or duplicate, close that pull +request and delete only its `nim-agent/product-dev-*` branch. Do not weaken +branch protection to merge it. diff --git a/package.json b/package.json index 7790e678..6fac2dc5 100644 --- a/package.json +++ b/package.json @@ -10,20 +10,21 @@ }, "scripts": { "check:python-docstrings": "node scripts/ci/static_coverage_evidence.mjs docstrings", - "coverage": "c8 --all --include=app.js --include=cloud-sync.js --include=scripts/ci/static_coverage_evidence.mjs --include=server/app.mjs --include=server/auth.mjs --reporter=json --reporter=json-summary npm run test:coverage", + "coverage": "npm run test:coverage", "server": "node server/server.mjs", - "test:api": "node tests/api/auth-secret.test.mjs && node tests/api/smoke.mjs && node tests/api/ratelimit.test.mjs", - "test:unit": "node tests/unit/analytics.test.mjs && node tests/unit/cpm.test.mjs && node tests/unit/baseline-compare.test.mjs && node tests/unit/workload.test.mjs && node tests/unit/cost-evm.test.mjs && node tests/unit/msproject.test.mjs && node tests/unit/auth-password.test.mjs && node tests/unit/editor-unsaved.test.mjs && node tests/unit/static-coverage-evidence.test.mjs && node tests/unit/dep-types.test.mjs && node tests/unit/weekly-report.test.mjs && node tests/unit/clearfolio.test.mjs && node tests/unit/sprint-stats.test.mjs && node tests/unit/burndown.test.mjs && node tests/unit/pm-analysis.test.mjs && node tests/unit/cloud-sync-security.test.mjs", - "test:coverage": "node tests/unit/msproject.test.mjs && node tests/unit/auth-password.test.mjs && node tests/unit/editor-unsaved.test.mjs && node tests/unit/static-coverage-evidence.test.mjs && npm run test:api", + "test:api": "node tests/api/auth-secret.test.mjs && node tests/api/smoke.mjs && node tests/api/ratelimit.test.mjs && node tests/api/attachment-status.test.mjs", + "test:unit": "node tests/unit/analytics.test.mjs && node tests/unit/cpm.test.mjs && node tests/unit/baseline-compare.test.mjs && node tests/unit/workload.test.mjs && node tests/unit/cost-evm.test.mjs && node tests/unit/msproject.test.mjs && node tests/unit/auth-password.test.mjs && node tests/unit/editor-unsaved.test.mjs && node tests/unit/static-coverage-evidence.test.mjs && node tests/unit/dep-types.test.mjs && node tests/unit/weekly-report.test.mjs && node tests/unit/clearfolio.test.mjs && node tests/unit/clearfolio-adapter-mock-hmac.test.mjs && node tests/unit/sprint-stats.test.mjs && node tests/unit/burndown.test.mjs && node tests/unit/pm-analysis.test.mjs && node tests/unit/cloud-sync-security.test.mjs && node tests/unit/attachment-status.test.mjs && node tests/unit/clearfolio-status-signal.test.mjs && node tests/unit/coverage-script-contract.test.mjs && node tests/config/hourly-opencode-commercial-readiness.test.mjs", + "test:coverage": "c8 --all --include=app.js --include=cloud-sync.js --include=scripts/ci/static_coverage_evidence.mjs --include=server/attachment_status.mjs --include=server/app.mjs --include=server/auth.mjs --include=server/clearfolio.mjs --reporter=json --reporter=json-summary npm run test:coverage:cases", + "test:coverage:cases": "node tests/unit/coverage-script-contract.test.mjs && node tests/unit/attachment-status.test.mjs && node tests/unit/clearfolio-status-signal.test.mjs && node tests/unit/clearfolio-adapter-mock-hmac.test.mjs && node tests/unit/msproject.test.mjs && node tests/unit/auth-password.test.mjs && node tests/unit/editor-unsaved.test.mjs && node tests/unit/static-coverage-evidence.test.mjs && npm run test:api", "test:e2e": "playwright test", "test:e2e:headed": "playwright test --headed", - "test:e2e:cloud": "playwright test tests/e2e/cloud.spec.js", + "test:e2e:cloud": "playwright install chromium && playwright test tests/e2e/cloud.spec.js", "test:fuzz": "playwright install chromium && playwright test tests/e2e/csv_formula_fuzz.spec.js", "fuzz": "node --test tests/fuzz/*.mjs" }, "dependencies": { "@hono/node-server": "^2.0.12", - "hono": "^4.12.32" + "hono": "^4.13.0" }, "devDependencies": { "@playwright/test": "1.61.1", diff --git a/tests/config/hourly-opencode-commercial-readiness.test.mjs b/tests/config/hourly-opencode-commercial-readiness.test.mjs new file mode 100644 index 00000000..19fa0d26 --- /dev/null +++ b/tests/config/hourly-opencode-commercial-readiness.test.mjs @@ -0,0 +1,116 @@ +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; + +const workflowPath = new URL( + '../../.github/workflows/hourly-opencode-commercial-readiness.yml', + import.meta.url, +); +const workflow = readFileSync(workflowPath, 'utf8'); + +assert.match( + workflow, + /cron:\s*["']41 \* \* \* \*["']/, + 'the autonomous product loop runs once per hour', +); +assert.match( + workflow, + /group:\s*scopeweave-hourly-opencode-commercial-readiness/, + 'one repository-wide concurrency group prevents overlapping agents', +); +assert.match( + workflow, + /cancel-in-progress:\s*false/, + 'a later hourly tick never interrupts a running product-development session', +); +assert.match( + workflow, + /permissions:\n\s+contents:\s+read/, + 'the workflow-level token defaults to read-only repository contents', +); +assert.match( + workflow, + /NVIDIA_API_KEY:\s*\$\{\{ secrets\.NVIDIA_NIM_API_KEY \}\}/, + 'the coding agent uses the organization NVIDIA NIM secret', +); +assert.doesNotMatch( + workflow, + /COPILOT_GITHUB_TOKEN|\/agents\/repos\/|create_pull_request/, + 'the scheduler never uses Copilot Agent Tasks or a user-scoped Copilot token', +); +assert.match( + workflow, + /OPENCODE_VERSION:\s*["']1\.17\.13["']/, + 'OpenCode is pinned to a reviewed release rather than latest', +); +assert.match( + workflow, + /OPENCODE_SHA256:\s+[0-9a-f]{64}/, + 'the OpenCode archive has a pinned SHA-256 digest', +); +assert.match( + workflow, + /sha256sum -c -/, + 'the downloaded OpenCode archive is verified before execution', +); +assert.match( + workflow, + /^\s*"baseURL":\s*"https:\/\/integrate\.api\.nvidia\.com\/v1",?$/m, + 'OpenCode targets only the NVIDIA hosted NIM OpenAI-compatible endpoint', +); +assert.match( + workflow, + /"apiKey":\s*"\{env:NVIDIA_API_KEY\}"/, + 'the provider reads its key from the environment instead of the repository', +); +assert.match( + workflow, + /@ai-sdk\/openai-compatible/, + 'the OpenCode provider uses the documented OpenAI-compatible adapter', +); +assert.match( + workflow, + /env -u GH_TOKEN -u GITHUB_TOKEN -u REPOSITORY_TOKEN/, + 'the OpenCode process cannot inherit GitHub mutation credentials', +); +assert.match( + workflow, + /pull-request-first single-flight gate[\s\S]*gh pr list[\s\S]*state open/, + 'an existing pull request prevents another autonomous development slice', +); +assert.match( + workflow, + /Revalidate queue ownership[\s\S]*gh pr list[\s\S]*refusing duplicate development/, + 'the trusted publisher rechecks queue ownership immediately before publication', +); +assert.match( + workflow, + /protected review-agent workflow/, + 'agent changes to reviewer-owned workflows fail closed', +); +assert.match( + workflow, + /\.trivyignore\|\\\.semgrepignore\|\\\.gitleaksignore/, + 'security-scan suppression files are explicitly prohibited', +); +assert.match( + workflow, + /npm ci[\s\S]*npm run test:unit[\s\S]*npm run test:api[\s\S]*npm run coverage[\s\S]*static_coverage_evidence\.mjs docstrings[\s\S]*git diff --check/, + 'the complete deterministic verification contract precedes publication', +); +assert.match( + workflow, + /--base "\$DEFAULT_BRANCH"/, + 'the trusted publisher opens exactly one PR against develop', +); +assert.match( + workflow, + /Do not merge, publish, release,\s+push, or commit/, + 'the coding agent cannot own publication or merge decisions', +); +assert.match( + workflow, + /Central PR governance owns review, repair, revalidation, and merge/, + 'the existing organization review system remains authoritative', +); + +console.log('✓ hourly OpenCode commercial-readiness workflow contract passed');