From 53eb26b6c04bc63839aa2717061dea1529534519 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 4 Aug 2026 21:17:53 +0900 Subject: [PATCH 1/9] test(ci): define hourly product-development safety contract --- .../hourly-product-development.test.mjs | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 tests/config/hourly-product-development.test.mjs diff --git a/tests/config/hourly-product-development.test.mjs b/tests/config/hourly-product-development.test.mjs new file mode 100644 index 00000000..9bd654a3 --- /dev/null +++ b/tests/config/hourly-product-development.test.mjs @@ -0,0 +1,109 @@ +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; + +const workflowPath = '.github/workflows/hourly-product-development.yml'; +const workflow = readFileSync(workflowPath, 'utf8'); + +assert.match( + workflow, + /schedule:\s*\n\s*- cron: ["']41 \* \* \* \*["']/, + 'the bounded product-development gate runs once per hour at minute 41', +); +assert.match(workflow, /workflow_dispatch:/, 'operators can invoke the same gate manually'); +assert.match( + workflow, + /group: scopeweave-hourly-product-development/, + 'one repository-wide concurrency group prevents overlapping product tasks', +); +assert.match( + workflow, + /cancel-in-progress: false/, + 'an active gate is not cancelled halfway through its duplicate-prevention checks', +); +assert.match( + workflow, + /COPILOT_GITHUB_TOKEN/, + 'agent-task creation requires a separately scoped user token', +); +assert.match( + workflow, + /\/pulls\?state=open&per_page=1/, + 'the workflow refuses development while any pull request owns the queue', +); +assert.match( + workflow, + /\/agents\/repos\/\$\{TARGET_REPOSITORY\}\/tasks\?per_page=100/, + 'the workflow inventories existing agent tasks before creating another', +); +assert.match( + workflow, + /active_states = \{"queued", "in_progress", "idle", "waiting_for_user"\}/, + 'known active task states remain fail-closed', +); +assert.match( + workflow, + /terminal_states = \{"completed", "failed", "timed_out", "cancelled"\}/, + 'only explicit terminal task states release the single-flight gate', +); +assert.match( + workflow, + /\/agents\/repos\/\$\{TARGET_REPOSITORY\}\/tasks["']/, + 'the eligible path creates exactly one repository-scoped agent task', +); +assert.match( + workflow, + /create_pull_request: true/, + 'the bounded task must return work through one reviewable pull request', +); +assert.match( + workflow, + /100% production statement, branch, function, and line coverage/, + 'the agent prompt preserves the production coverage contract', +); +assert.match( + workflow, + /complete beginner-readable JSDoc\/docstrings/, + 'the agent prompt preserves the documentation contract', +); +assert.match( + workflow, + /two-or-more-word snake_case database object names/, + 'the agent prompt preserves the canonical database naming contract', +); +assert.match( + workflow, + /APA 7th references under docs\/doctoring/, + 'the agent prompt preserves standards traceability', +); +assert.match( + workflow, + /NVIDIA_NIM_API_KEY/, + 'LLM-dependent validation is routed through the repository secret contract', +); +assert.match( + workflow, + /contextual-orchestrator/, + 'LLM work reuses the modular orchestration boundary when applicable', +); +assert.match( + workflow, + /Use Figma or Product Design only when the selected slice has an actual buyer-facing UI/, + 'visual tooling is required only for genuine product-interface work', +); +assert.match( + workflow, + /Do not merge your own pull request/, + 'the development agent cannot bypass independent review', +); +assert.doesNotMatch( + workflow, + /contents:\s*write|pull-requests:\s*write|secrets:\s*inherit/, + 'the scheduler itself keeps read-only repository permissions and does not inherit all secrets', +); +assert.doesNotMatch( + workflow, + /pr-review-merge-scheduler|pr-review-fix-scheduler/, + 'repository scheduling does not duplicate the organization-owned PR maintenance loops', +); + +console.log('✓ hourly product-development workflow contract tests passed'); From 6427dee7ac46df6f82d0c5ff6fe3eb586bd92331 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 4 Aug 2026 21:20:02 +0900 Subject: [PATCH 2/9] ci: add fail-closed hourly product-development gate --- .../workflows/hourly-product-development.yml | 240 ++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 .github/workflows/hourly-product-development.yml diff --git a/.github/workflows/hourly-product-development.yml b/.github/workflows/hourly-product-development.yml new file mode 100644 index 00000000..298e2810 --- /dev/null +++ b/.github/workflows/hourly-product-development.yml @@ -0,0 +1,240 @@ +name: Hourly ScopeWeave Product Development + +on: + schedule: + - cron: "41 * * * *" + workflow_dispatch: + +permissions: + contents: read + pull-requests: read + +concurrency: + group: scopeweave-hourly-product-development + cancel-in-progress: false + +jobs: + create-one-bounded-product-task: + runs-on: ubuntu-latest + timeout-minutes: 10 + env: + TARGET_REPOSITORY: ContextualWisdomLab/scopeweave + BASE_BRANCH: develop + AGENT_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + steps: + - name: Determine whether product development may start + id: gate + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + + if [ -z "${AGENT_TOKEN:-}" ]; then + echo "::warning::COPILOT_GITHUB_TOKEN is not configured; product development remains fail-closed." + echo "eligible=false" >>"$GITHUB_OUTPUT" + exit 0 + fi + + open_pr_count="$( + gh api "/repos/${TARGET_REPOSITORY}/pulls?state=open&per_page=1" \ + --jq 'length' + )" + if [ "$open_pr_count" -ne 0 ]; then + echo "An open pull request already owns the development queue." + echo "eligible=false" >>"$GITHUB_OUTPUT" + exit 0 + fi + + if ! tasks_json="$( + GH_TOKEN="$AGENT_TOKEN" gh api \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2026-03-10" \ + --paginate \ + --slurp \ + "/agents/repos/${TARGET_REPOSITORY}/tasks?per_page=100" + )"; then + echo "::warning::Unable to list Copilot agent tasks; refusing to create another." + echo "eligible=false" >>"$GITHUB_OUTPUT" + exit 0 + fi + + if ! active_task_count="$( + TASKS_JSON="$tasks_json" python3 - <<'PY' + import json + import os + import sys + + payload = json.loads(os.environ["TASKS_JSON"]) + + def extract_tasks(value): + """Return a flat task list or None for an unknown response shape.""" + if isinstance(value, list): + if all(isinstance(item, dict) and "state" in item for item in value): + return value + extracted = [] + for page in value: + page_tasks = extract_tasks(page) + if page_tasks is None: + return None + extracted.extend(page_tasks) + return extracted + if isinstance(value, dict): + tasks_value = value.get("tasks", value.get("items")) + if isinstance(tasks_value, list): + return extract_tasks(tasks_value) + return None + + tasks = extract_tasks(payload) + if tasks is None: + print("Unsupported agent-task response shape", file=sys.stderr) + raise SystemExit(1) + + active_states = {"queued", "in_progress", "idle", "waiting_for_user"} + terminal_states = {"completed", "failed", "timed_out", "cancelled"} + active_count = 0 + for task in tasks: + if not isinstance(task, dict): + active_count += 1 + continue + state = task.get("state") + if state in active_states or state not in terminal_states: + active_count += 1 + print(active_count) + PY + )"; then + echo "::warning::Unable to interpret Copilot agent tasks; refusing to create another." + echo "eligible=false" >>"$GITHUB_OUTPUT" + exit 0 + fi + + if [ "$active_task_count" -ne 0 ]; then + echo "A Copilot agent task already owns the development queue." + echo "eligible=false" >>"$GITHUB_OUTPUT" + exit 0 + fi + + echo "eligible=true" >>"$GITHUB_OUTPUT" + + - name: Revalidate the single-flight gate and create one product task + if: steps.gate.outputs.eligible == 'true' + env: + GH_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + shell: bash + run: | + set -euo pipefail + + open_pr_count="$( + GH_TOKEN="${{ github.token }}" gh api \ + "/repos/${TARGET_REPOSITORY}/pulls?state=open&per_page=1" \ + --jq 'length' + )" + if [ "$open_pr_count" -ne 0 ]; then + echo "A pull request appeared after the first gate; refusing duplicate development." + exit 0 + fi + + tasks_json="$( + gh api \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2026-03-10" \ + --paginate \ + --slurp \ + "/agents/repos/${TARGET_REPOSITORY}/tasks?per_page=100" + )" + TASKS_JSON="$tasks_json" python3 - <<'PY' + import json + import os + + payload = json.loads(os.environ["TASKS_JSON"]) + + def extract_tasks(value): + """Return a flat task list and fail for any unknown shape.""" + if isinstance(value, list): + if all(isinstance(item, dict) and "state" in item for item in value): + return value + tasks = [] + for page in value: + tasks.extend(extract_tasks(page)) + return tasks + if isinstance(value, dict): + value = value.get("tasks", value.get("items")) + if isinstance(value, list): + return extract_tasks(value) + raise SystemExit("Unsupported agent-task response shape") + + active_states = {"queued", "in_progress", "idle", "waiting_for_user"} + terminal_states = {"completed", "failed", "timed_out", "cancelled"} + for task in extract_tasks(payload): + state = task.get("state") if isinstance(task, dict) else None + if state in active_states or state not in terminal_states: + raise SystemExit("An active or unknown agent task already owns the queue") + PY + + prompt="$(cat <<'PROMPT' + Continue commercializing ContextualWisdomLab/scopeweave on the develop branch. + + The organization-owned PR maintenance schedulers already review, repair, + revalidate, and merge open pull requests more frequently than this hourly + gate. This task is created only after both pull requests and active agent + tasks are confirmed to be zero. Select exactly one highest-impact + buyer-visible product gap that fits one bounded pull request. + + First inspect AGENTS.md, README.md, CHANGELOG.md, docs/doctoring, + operations documentation, open issues, recent commits, package and service + boundaries, database schema, tests, security posture, accessibility, + interoperability with ContextualWisdomLab/.github, naruon, Clearfolio, and + contextual-orchestrator, and the end-to-end project-management user journey. + Preserve standalone operation while keeping seams suitable for modular MSA + extraction and provider-neutral adapters. + + Work test-first: write a failing realistic test before production code and + verify that it fails for the intended reason. Implement the smallest coherent + vertical slice, then run focused and full validation. Require 100% production + statement, branch, function, and line coverage for every new or changed + production module and complete beginner-readable JSDoc/docstrings. Preserve + fail-closed input contracts, tenant isolation, bounded resource use, + deterministic behavior where applicable, immutable audit evidence, and + two-or-more-word snake_case database object names. Do not silently create or + retain single-word database objects. + + Use the latest authoritative international standard, official primary + specification, or peer-reviewed paper for every material technical or + methodological decision. Record the decision, limitations, and APA 7th + references under docs/doctoring/. Do not claim compliance or certification + beyond executable evidence. + + Tests must represent realistic ScopeWeave behavior: multi-tenant projects, + concurrent edits or requests, partial downstream failure, restart and + migration behavior, large WBS inputs, accessibility, rollback, and customer- + visible output as appropriate to the selected slice. If an LLM-dependent test + is genuinely necessary, use the NVIDIA_NIM_API_KEY repository secret and the + contextual-orchestrator boundary rather than embedding a provider-specific + client. Keep deterministic non-LLM tests as the required merge gate. + + Use Figma or Product Design only when the selected slice has an actual buyer-facing UI; + capture loading, empty, error, keyboard, screen-reader, touch, narrow viewport, + and permission states before implementation. Do not add cosmetic UI to a + backend-only or library-only slice. + + Update CHANGELOG.md, operator and architecture documentation, migrations, + rollback evidence, package smoke tests, and version metadata when the slice is + genuinely release-ready. Open exactly one focused pull request explaining + buyer impact, standards evidence, compatibility, risks, and executable + verification. Do not merge your own pull request, publish a release, weaken + branch protection, or bypass required checks and independent review. + PROMPT + )" + + payload="$( + jq -n \ + --arg prompt "$prompt" \ + --arg base_ref "$BASE_BRANCH" \ + '{prompt: $prompt, base_ref: $base_ref, create_pull_request: true}' + )" + gh api \ + --method POST \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2026-03-10" \ + "/agents/repos/${TARGET_REPOSITORY}/tasks" \ + --input - <<<"$payload" From 761dac4291bc2c1d2656125a6bdb9043f1ed348b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 4 Aug 2026 21:21:35 +0900 Subject: [PATCH 3/9] test(ci): run hourly development contract in unit suite --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7790e678..8803327b 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "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", "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:unit": "node tests/config/hourly-product-development.test.mjs && 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:e2e": "playwright test", "test:e2e:headed": "playwright test --headed", From 4cfcadfb5bf5a35aec17ddb3362bd91aa0e190e2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 4 Aug 2026 21:23:21 +0900 Subject: [PATCH 4/9] docs(operations): document hourly product-development gate --- docs/operations/hourly-product-development.md | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 docs/operations/hourly-product-development.md diff --git a/docs/operations/hourly-product-development.md b/docs/operations/hourly-product-development.md new file mode 100644 index 00000000..c4428639 --- /dev/null +++ b/docs/operations/hourly-product-development.md @@ -0,0 +1,100 @@ +# Hourly product-development gate + +## Purpose + +ScopeWeave's organization-owned workflows already inspect, repair, revalidate, +and merge pull requests more frequently than once per hour. The repository-level +workflow at `.github/workflows/hourly-product-development.yml` therefore does +**not** duplicate those privileged maintenance schedulers. It runs at minute 41 +of every hour and creates one bounded product-development agent task only after +the pull-request queue and the agent-task queue are both empty. + +This separates queue governance from product generation: + +1. `ContextualWisdomLab/.github` owns review dispatch, feedback repair, current- + head checks, branch updates, and policy-compliant merge behavior. +2. ScopeWeave owns the product-specific definition of commercial quality, + realistic verification, modular architecture, standards evidence, database + naming, and UI-design requirements. +3. A product task can create one focused pull request, but it cannot merge, + publish, release, weaken protections, or declare itself approved. + +## Schedule and single-flight behavior + +The workflow runs with the cron expression `41 * * * *` and supports manual +`workflow_dispatch` for the same fail-closed gate. One repository-wide +concurrency group prevents two scheduled invocations from checking the queue at +the same time. An in-progress gate is not cancelled because interruption between +inventory and task creation could undermine duplicate prevention. + +The gate checks twice: + +- no open pull request exists; and +- no Copilot agent task is active or has an unknown state. + +Known active states are `queued`, `in_progress`, `idle`, and +`waiting_for_user`. Known terminal states are `completed`, `failed`, +`timed_out`, and `cancelled`. Any unknown response shape, unknown task state, +non-dictionary item, API error, or missing credential keeps the gate closed. + +## Credentials and permissions + +The ordinary workflow token is read-only and is used only to count open pull +requests. GitHub's Agent Tasks API cannot be driven by the ordinary Actions +`GITHUB_TOKEN`; repository administrators must configure a fine-grained user +token with the necessary Agent Tasks read/write access as the +`COPILOT_GITHUB_TOKEN` repository secret. + +The secret is passed only to the two steps that inventory or create agent tasks. +The workflow does not inherit all repository secrets. The generated task is +instructed to use `NVIDIA_NIM_API_KEY` only when an LLM-dependent test is truly +necessary and to access models through the provider-neutral +`contextual-orchestrator` boundary where applicable. + +## Product-task contract + +An eligible invocation creates exactly one task against `develop` with +`create_pull_request: true`. The prompt requires the task to: + +- select one highest-impact buyer-visible Gap; +- write and observe a failing realistic test before production code; +- retain standalone operation and modular MSA extraction seams; +- require complete beginner-readable JSDoc/docstrings; +- require 100% statement, branch, function, and line coverage for changed + production modules; +- preserve tenant isolation, bounded resources, fail-closed validation, + determinism where applicable, and immutable audit evidence; +- use two-or-more-word `snake_case` database object names; +- use current authoritative standards or peer-reviewed evidence and record APA + 7th references under `docs/doctoring/`; +- use Figma or Product Design only for an actual buyer-facing interface and + cover loading, empty, error, keyboard, screen-reader, touch, narrow viewport, + and permission states; +- update CHANGELOG, migration, rollback, package, and version evidence when the + slice is genuinely release-ready; and +- open one focused PR without self-merging or bypassing required checks. + +## Failure and recovery + +A scheduled run that cannot prove eligibility exits successfully with a warning +and creates no task. This avoids repeated failures when the optional agent token +is not configured while remaining fail-closed against duplicate development. +Operators should inspect the run summary for one of these expected reasons: + +- open PR owns the queue; +- active or unknown agent task owns the queue; +- Agent Tasks API inventory failed or changed shape; or +- `COPILOT_GITHUB_TOKEN` is not configured. + +After correcting credentials or an API contract, invoke `workflow_dispatch` to +exercise the same gate. Do not work around the inventory by deleting active task +records or weakening the terminal-state allowlist. + +## Verification + +`tests/config/hourly-product-development.test.mjs` statically enforces the +schedule, single-flight rules, least-privilege token boundary, duplicate +prevention, bounded prompt, database naming, coverage, documentation, +standards, Figma, LLM-provider, and no-self-merge contracts. It also rejects any +future attempt to copy the organization PR schedulers or grant repository write +permissions to this orchestration workflow. From a8d0de300dc3d8210994346c6cc26bb44e9b35f6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 4 Aug 2026 21:24:41 +0900 Subject: [PATCH 5/9] docs(changelog): record hourly product-development gate --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e84f41f8..4a727a52 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 product-development gate that creates at most one + buyer-visible Copilot agent task only when both the pull-request queue and + active/unknown agent-task queue are empty. The repository workflow keeps + read-only GitHub permissions, requires a separately scoped + `COPILOT_GITHUB_TOKEN`, and leaves PR review, repair, revalidation, and merge + ownership in the organization-central `.github` workflows. - 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 @@ -61,4 +67,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 리스트에서의 버벅임 현상을 해결했습니다. From 72d0e2c69fcbdca82dfd80f98613d621b3f20fa6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 4 Aug 2026 21:27:36 +0900 Subject: [PATCH 6/9] docs(doctoring): record hourly agent-task governance evidence --- docs/doctoring/hourly-product-development.md | 128 +++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 docs/doctoring/hourly-product-development.md diff --git a/docs/doctoring/hourly-product-development.md b/docs/doctoring/hourly-product-development.md new file mode 100644 index 00000000..36652116 --- /dev/null +++ b/docs/doctoring/hourly-product-development.md @@ -0,0 +1,128 @@ +# Hourly product-development orchestration: evidence and design record + +## Decision + +ScopeWeave separates privileged pull-request maintenance from product-task +creation. Organization-central workflows own review dispatch, feedback repair, +exact-head checks, branch updates, and policy-compliant merge behavior. The +repository-level hourly gate creates one development task only after it proves +that no pull request and no active or unknown agent task owns the queue. + +This boundary prevents three failure modes: + +1. duplicated repository and organization PR schedulers consuming the same + Actions and reviewer capacity; +2. overlapping coding agents creating competing pull requests for the same + buyer Gap; and +3. an orchestration token receiving repository write permissions that it does + not require. + +## Authoritative platform constraints + +GitHub documents the Agent Tasks API as a public-preview interface that can +create, list, and inspect Copilot cloud-agent tasks. The API accepts +`base_ref` and `create_pull_request`, but supports user-to-server credentials +rather than the ordinary workflow `GITHUB_TOKEN`. ScopeWeave therefore uses a +separately scoped `COPILOT_GITHUB_TOKEN`, inventories tasks before creation, +and fails closed when the credential, API call, response shape, or task state +cannot be trusted. + +GitHub scheduled workflows execute from the latest commit on the default branch. +Consequently, the hourly gate does not become active merely because its pull +request exists: the workflow must first pass protected-branch review and merge +into `develop`. + +GitHub Actions concurrency groups prevent multiple runs with the same key from +running simultaneously. ScopeWeave uses one repository-wide group and sets +`cancel-in-progress: false` so a later hourly tick cannot interrupt a running +inventory between the first and second duplicate-prevention checks. Because +GitHub may replace an older pending run, the workflow does not depend on strict +cron ordering or on every scheduled tick being executed. + +GitHub's workflow-security guidance recommends explicit minimum permissions. +The hourly workflow declares only read access to repository contents and pull +requests. It does not run a third-party action, inherit all secrets, or grant +write access through `GITHUB_TOKEN`; the user token is exposed only to the two +Agent Tasks API steps. + +## Fail-closed task inventory + +The gate recognizes these active states: + +- `queued` +- `in_progress` +- `idle` +- `waiting_for_user` + +It recognizes only these terminal states: + +- `completed` +- `failed` +- `timed_out` +- `cancelled` + +Every unknown state, non-object entry, unsupported pagination shape, parse +failure, HTTP failure, missing token, or open PR is treated as ownership of the +queue. A second inventory immediately before creation narrows the race window. +The API does not expose a repository-level compare-and-create primitive in the +documented public-preview contract, so the workflow does not claim globally +atomic task creation against unrelated external clients. Repository operators +must use this workflow as the single scheduled producer. + +## Development contract + +The task prompt is a merge gate rather than a marketing statement. It requires +one bounded buyer-visible vertical slice, failing-test-first development, +realistic customer and failure cases, standalone and modular MSA operation, +provider-neutral adapters, two-or-more-word `snake_case` database objects, +complete JSDoc/docstrings, and 100% statement, branch, function, and line +coverage for changed production modules. + +Material decisions require current authoritative standards or peer-reviewed +evidence recorded with APA 7th references under `docs/doctoring/`. LLM-dependent +tests are optional rather than default; when necessary they use the repository's +`NVIDIA_NIM_API_KEY` through the `contextual-orchestrator` seam, while +deterministic tests remain required. Figma or Product Design is required only +when the selected slice includes an actual buyer-facing interface. + +The generated task must open one focused pull request and is expressly forbidden +from merging itself, publishing a release, weakening branch protection, or +bypassing checks and independent review. + +## Verification contract + +`tests/config/hourly-product-development.test.mjs` statically proves: + +- the hourly schedule and manual entry point; +- one non-cancelling concurrency group; +- read-only repository permissions; +- absence of central PR-scheduler duplication; +- required user-token and Agent Tasks API boundaries; +- open-PR and active/unknown-task rejection; +- one reviewable pull request per eligible task; +- coverage, documentation, database naming, realistic testing, standards, + Figma, LLM-provider, and no-self-merge prompt requirements; and +- absence of broad secret inheritance or repository write permissions. + +After merge, an operator must exercise `workflow_dispatch` once with no open PR +and a controlled terminal task inventory, confirm exactly one task is created, +then repeat with that task active and confirm no second task is created. The +production schedule remains fail-closed until `COPILOT_GITHUB_TOKEN` is +configured with the documented user-to-server Agent Tasks permissions. + +## References + +GitHub. (n.d.-a). *Concurrency*. GitHub Docs. Retrieved August 4, 2026, from +https://docs.github.com/en/actions/concepts/workflows-and-actions/concurrency + +GitHub. (n.d.-b). *Events that trigger workflows*. GitHub Docs. Retrieved August +4, 2026, from +https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows + +GitHub. (n.d.-c). *Protecting against security threats*. GitHub Docs. Retrieved +August 4, 2026, from +https://docs.github.com/en/code-security/tutorials/secure-your-organization/protect-against-threats + +GitHub. (n.d.-d). *Using Copilot cloud agent via the API*. GitHub Docs. +Retrieved August 4, 2026, from +https://docs.github.com/en/copilot/how-tos/use-copilot-agents/cloud-agent/use-cloud-agent-via-the-api From d4bf3467aaaef629e31767b22af9f08b79449893 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 4 Aug 2026 21:44:39 +0900 Subject: [PATCH 7/9] test(ci): pin Agent Tasks calls to documented API version --- tests/config/hourly-product-development.test.mjs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/config/hourly-product-development.test.mjs b/tests/config/hourly-product-development.test.mjs index 9bd654a3..71cfa7c5 100644 --- a/tests/config/hourly-product-development.test.mjs +++ b/tests/config/hourly-product-development.test.mjs @@ -25,6 +25,16 @@ assert.match( /COPILOT_GITHUB_TOKEN/, 'agent-task creation requires a separately scoped user token', ); +assert.match( + workflow, + /X-GitHub-Api-Version:\s*2022-11-28/g, + 'Agent Tasks calls use the API version shown by the current official endpoint documentation', +); +assert.doesNotMatch( + workflow, + /X-GitHub-Api-Version:\s*2026-03-10/, + 'the preview Agent Tasks integration does not silently opt into an undocumented endpoint-version contract', +); assert.match( workflow, /\/pulls\?state=open&per_page=1/, From 0a31292c0d61a0eb2a7f066a5b31fa6154b76022 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 4 Aug 2026 21:46:19 +0900 Subject: [PATCH 8/9] fix(ci): use documented Agent Tasks API version --- .github/workflows/hourly-product-development.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/hourly-product-development.yml b/.github/workflows/hourly-product-development.yml index 298e2810..2cd2a5d2 100644 --- a/.github/workflows/hourly-product-development.yml +++ b/.github/workflows/hourly-product-development.yml @@ -49,7 +49,7 @@ jobs: if ! tasks_json="$( GH_TOKEN="$AGENT_TOKEN" gh api \ -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2026-03-10" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ --paginate \ --slurp \ "/agents/repos/${TARGET_REPOSITORY}/tasks?per_page=100" @@ -137,7 +137,7 @@ jobs: tasks_json="$( gh api \ -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2026-03-10" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ --paginate \ --slurp \ "/agents/repos/${TARGET_REPOSITORY}/tasks?per_page=100" @@ -235,6 +235,6 @@ jobs: gh api \ --method POST \ -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2026-03-10" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ "/agents/repos/${TARGET_REPOSITORY}/tasks" \ --input - <<<"$payload" From 3cf609d122da4447817dfd47a1a0b80ee3c033d5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 4 Aug 2026 21:47:42 +0900 Subject: [PATCH 9/9] docs(doctoring): pin documented Agent Tasks API contract --- docs/doctoring/hourly-product-development.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/doctoring/hourly-product-development.md b/docs/doctoring/hourly-product-development.md index 36652116..a20979b1 100644 --- a/docs/doctoring/hourly-product-development.md +++ b/docs/doctoring/hourly-product-development.md @@ -25,7 +25,10 @@ create, list, and inspect Copilot cloud-agent tasks. The API accepts rather than the ordinary workflow `GITHUB_TOKEN`. ScopeWeave therefore uses a separately scoped `COPILOT_GITHUB_TOKEN`, inventories tasks before creation, and fails closed when the credential, API call, response shape, or task state -cannot be trusted. +cannot be trusted. The current Agent Tasks endpoint examples explicitly send +`X-GitHub-Api-Version: 2022-11-28`; the workflow follows that endpoint-specific +documented contract instead of opting the preview integration into the newer +general REST version without endpoint evidence. GitHub scheduled workflows execute from the latest commit on the default branch. Consequently, the hourly gate does not become active merely because its pull @@ -97,7 +100,7 @@ bypassing checks and independent review. - one non-cancelling concurrency group; - read-only repository permissions; - absence of central PR-scheduler duplication; -- required user-token and Agent Tasks API boundaries; +- required user-token, documented API-version, and Agent Tasks API boundaries; - open-PR and active/unknown-task rejection; - one reviewable pull request per eligible task; - coverage, documentation, database naming, realistic testing, standards,