Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fc1cd4d
chore(ci): bootstrap hourly DDD contract repair
seonghobae Sep 1, 2026
c3fc24f
chore(ci): isolate hourly DDD bootstrap logic
seonghobae Sep 1, 2026
c02a994
fix(ci): run isolated hourly DDD bootstrap
seonghobae Sep 1, 2026
a328b9e
fix(ci): normalize hourly DDD bootstrap before verification
seonghobae Sep 1, 2026
1241229
fix(ci): publish verified non-workflow DDD changes
seonghobae Sep 1, 2026
ca8b1b4
fix(automation): enforce hourly DDD development contract
github-actions[bot] Sep 1, 2026
90d59c9
fix(automation): restore hourly coordinator App authentication
seonghobae Sep 1, 2026
098f9e6
chore(ci): remove completed hourly DDD bootstrap workflow
seonghobae Sep 1, 2026
38f6422
chore(ci): remove completed hourly DDD bootstrap script
seonghobae Sep 1, 2026
cd872e8
test(automation): add RED DDD prompt-binding regression
seonghobae Sep 1, 2026
e6aaf2c
test(ci): run DDD prompt-binding RED-GREEN repair
seonghobae Sep 1, 2026
1c1532b
chore(ci): remove unsafe temporary DDD source-fix lane
seonghobae Sep 1, 2026
aeba218
fix(ci): normalize DDD parser backslash generation
seonghobae Sep 1, 2026
c355cbf
test(automation): restore RED-GREEN DDD binding bootstrap
seonghobae Sep 1, 2026
c765e6f
chore(ci): remove reintroduced temporary DDD source-fix workflow
seonghobae Sep 1, 2026
6968537
chore(ci): remove reintroduced temporary DDD source-fix helper
seonghobae Sep 1, 2026
f75fbe4
fix(automation): bind DDD contract to executable agent input
seonghobae Sep 1, 2026
fcbd592
docs(automation): align hourly DDD binding contract
seonghobae Sep 1, 2026
76b290c
Merge protected main into hourly DDD contract branch
seonghobae Sep 1, 2026
2906e61
fix(automation): preserve coordinator facade module identity
seonghobae Sep 1, 2026
199cfa5
fix(automation): preserve readiness facade identity
seonghobae Sep 1, 2026
b4fd5d0
fix(automation): reuse readiness core module
seonghobae Sep 1, 2026
cc3b6b8
fix(automation): require executable DDD step binding
seonghobae Sep 1, 2026
0a4d664
fix(automation): scope DDD shell reachability
seonghobae Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ on:
- ".github/workflows/organization-commercial-readiness-loop.yml"
- ".github/workflows/organization-commercial-readiness-loop-quality-ci.yml"
- "scripts/ci/organization_commercial_readiness_loop.py"
- "scripts/ci/organization_commercial_readiness_core.py"
- "scripts/ci/organization_commercial_readiness_ddd_contract.py"
- "organization_commercial_readiness_fixtures.py"
- "tests/test_organization_commercial_readiness_loop*.py"
- "docs/doctoring/organization-commercial-readiness-loop.md"
Expand Down Expand Up @@ -62,11 +64,13 @@ jobs:
--branch \
-m pytest --import-mode=importlib tests/test_organization_commercial_readiness_loop*.py -q
python -m coverage report \
--include='scripts/ci/organization_commercial_readiness_loop.py' \
--include='scripts/ci/organization_commercial_readiness_*.py' \
--show-missing \
--fail-under=100
python -m compileall -q \
scripts/ci/organization_commercial_readiness_loop.py \
scripts/ci/organization_commercial_readiness_core.py \
scripts/ci/organization_commercial_readiness_ddd_contract.py \
organization_commercial_readiness_fixtures.py \
tests/test_organization_commercial_readiness_loop*.py
git diff --exit-code
64 changes: 63 additions & 1 deletion .github/workflows/organization-commercial-readiness-loop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ jobs:
github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
runs-on: ubuntu-24.04
timeout-minutes: 25
permissions:
contents: read
id-token: write
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
ORGANIZATION: ContextualWisdomLab
Expand All @@ -32,6 +35,7 @@ jobs:
egress-policy: block
allowed-endpoints: >-
api.github.com:443
api.opencode.ai:443
github.com:443
objects.githubusercontent.com:443
release-assets.githubusercontent.com:443
Expand All @@ -53,10 +57,68 @@ jobs:
- name: Coordinate one bounded fleet pass
env:
GH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }}
OIDC_AUDIENCE: opencode-github-action
OPENCODE_API_BASE_URL: https://api.opencode.ai
shell: bash --noprofile --norc -e -o pipefail {0}
run: |
set -euo pipefail

exchange_unavailable() {
echo "::error::OpenCode app token exchange unavailable: $1"
exit 1
}

if [ -z "${GH_TOKEN:-}" ]; then
if [ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" ] || [ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" ]; then
exchange_unavailable "OIDC request environment is missing."
fi

request_url="${ACTIONS_ID_TOKEN_REQUEST_URL}"
separator="&"
case "$request_url" in
*\?*) ;;
*) separator="?" ;;
esac

if ! oidc_response="$(
curl -fsS \
--connect-timeout 10 \
--max-time 30 \
-H "Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \
"${request_url}${separator}audience=${OIDC_AUDIENCE}"
)"; then
exchange_unavailable "OIDC token request did not complete."
fi
if ! oidc_token="$(
jq -er '.value | select(type == "string" and length > 0)' \
<<<"$oidc_response" 2>/dev/null
)"; then
exchange_unavailable "OIDC token response was malformed or empty."
fi
echo "::add-mask::$oidc_token"

if ! token_response="$(
curl -fsS \
--connect-timeout 10 \
--max-time 30 \
-X POST \
-H "Authorization: Bearer ${oidc_token}" \
"${OPENCODE_API_BASE_URL}/exchange_github_app_token"
)"; then
exchange_unavailable "app token request did not complete."
fi
if ! app_token="$(
jq -er '.token | select(type == "string" and length > 0)' \
<<<"$token_response" 2>/dev/null
)"; then
exchange_unavailable "app token response was malformed or empty."
fi
echo "::add-mask::$app_token"
export GH_TOKEN="$app_token"
fi
Comment thread
seonghobae marked this conversation as resolved.

if [ -z "${GH_TOKEN:-}" ]; then
echo "::error::PR_REVIEW_MERGE_TOKEN is required; neither the reviewer credential nor repository-scoped GITHUB_TOKEN is accepted."
echo "::error::PR_REVIEW_MERGE_TOKEN or the job-bound OpenCode App token exchange is required; neither reviewer credentials nor repository-scoped GITHUB_TOKEN are accepted."
exit 1
fi
echo "::add-mask::$GH_TOKEN"
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ this file. The format follows Keep a Changelog, and versioned releases follow
Semantic Versioning where the repository publishes a release.

## [Unreleased]
- Restore the hourly organization commercial-readiness coordinator when the dedicated maintainer secret is absent by exchanging the protected scheduled job's OIDC identity for a short-lived OpenCode App installation token; retain bounded network calls, token masking, and fail-closed parsing. Require every centrally dispatched product-development entrypoint to accept a machine-checked Domain-Driven Design contract, continuously repairing misleading directory ownership and recording larger bounded-context migrations in `docs/product-technical-gap-baseline.md` without duplicating repository-owned schedules.
- Fail closed when the first top-level Noema JSON candidate is malformed,
preventing a later approval object from overriding malformed preface data;
multiple-object output remains supported when its first object is valid.
Expand Down
47 changes: 43 additions & 4 deletions docs/doctoring/organization-commercial-readiness-loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The coordinator may dispatch at most one review-repair workflow and one product-

A single workflow cannot safely write every repository merely because it runs in the organization `.github` repository. GitHub's default `GITHUB_TOKEN` is scoped to the repository containing the workflow; cross-repository Actions dispatch therefore requires an explicitly provisioned user or GitHub App credential with the required repository and Actions permissions. This control does not make every repository directly writable. It only considers repositories the live API reports as organization-owned, non-fork, enabled, non-archived, default-branch-bearing, and writable by the authenticated installation.

The central job therefore refuses both repository-scoped and reviewer-scoped token fallbacks. It requires the maintainer-scoped `PR_REVIEW_MERGE_TOKEN`; `OPENCODE_APPROVE_TOKEN` remains isolated to the reviewer credential chain and `GITHUB_TOKEN` is not accepted for cross-repository coordination. The maintainer token is exposed only to the final dispatch shell step, not checkout, setup, artifact upload, or other third-party actions. The coordinator itself receives neither `NVIDIA_NIM_API_KEY` nor `COPILOT_GITHUB_TOKEN`. Model credentials remain inside separately reviewed repository-local or central workers.
The central job therefore refuses repository-scoped and reviewer-scoped token fallbacks. It prefers the maintainer-scoped `PR_REVIEW_MERGE_TOKEN`; when that credential is absent, the scheduled default-branch job may exchange its job-bound GitHub OIDC identity for the existing short-lived OpenCode App installation token. Both exchange calls have bounded connection and total timeouts, both returned tokens are masked before reuse, and malformed or empty responses fail closed. `OPENCODE_APPROVE_TOKEN` remains isolated to the reviewer credential chain and `GITHUB_TOKEN` is never accepted for cross-repository coordination. The resulting maintainer credential is exposed only to the final dispatch shell step, not checkout, setup, artifact upload, or other third-party actions. The coordinator itself receives neither `NVIDIA_NIM_API_KEY` nor `COPILOT_GITHUB_TOKEN`. Model credentials remain inside separately reviewed repository-local or central workers.

## Dynamic repository-writer lease

Expand All @@ -30,17 +30,50 @@ The existing organization merge scheduler continues to own review dispatch, bran

## Product-development boundary

Product development is dispatched only when a repository has zero open pull requests and exposes one active, manual-only, explicitly marked workflow:
Product development is dispatched only when a repository has zero open pull requests and exposes one active, manual-only, explicitly marked workflow. The repository owns the human-readable prompt, which may use any language. Eligibility depends on a versioned machine-readable capability set and an executable binding rather than copied English prose.

```yaml
# cwl-org-commercial-entrypoint: v1
# cwl-ddd-architecture-audit: required
on:
workflow_dispatch:

concurrency:
group: product-development

env:
NVIDIA_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }}
CWL_DDD_CONTRACT_VERSION: "1"
CWL_DDD_CONTRACT_CAPABILITIES: >-
aggregate anti_corruption_layer bounded_context context_map
directory_ownership domain_event domain_service entity invariant
minimal_shared_kernel product_gap_baseline repository
subdomain_classification ubiquitous_language value_object
CWL_PRODUCT_AGENT_PROMPT: |
Deliver one buyer-visible increment through the repository-owned product agent.

jobs:
develop:
steps:
- name: Invoke the repository product agent
run: |
# cwl-ddd-prompt-binding: v1
product-agent \
--prompt-env CWL_PRODUCT_AGENT_PROMPT \
--architecture-contract-env CWL_DDD_CONTRACT_CAPABILITIES
```

The entrypoint must contain an explicit `concurrency` contract, use `NVIDIA_NIM_API_KEY`, omit `COPILOT_GITHUB_TOKEN`, have no schedule of its own, and carry a commercial/product-development identity. This opt-in prevents the central coordinator from guessing that an unrelated manual workflow can safely modify product source. Repositories with an existing schedule keep their own lease and are never double-dispatched.
The root workflow `env` mapping must contain exactly one non-empty `CWL_PRODUCT_AGENT_PROMPT`, one exact version-one capability block, and one version value. The capability set is closed for version one; missing, misspelled, duplicated, or unversioned values fail closed. The prompt and capability environment names must reach the same non-comment shell command under the binding marker. Comments, unrelated YAML, nested or duplicate environment scopes, inert block scalars, shell built-ins, malformed quoting, dangling continuations, and flags split across commands do not satisfy the contract.

The capability IDs cover the strategic and tactical Domain-Driven Design obligations required by the organization: core/supporting/generic subdomain classification, Bounded Context, Context Map, Ubiquitous Language, Aggregate, Entity, Value Object, Domain Service, Repository, Domain Event, Invariant, Anti-Corruption Layer, minimal Shared Kernel, directory ownership, and product-gap baseline traceability. Human-readable instructions can evolve independently as long as the repository product-agent adapter consumes both bound inputs and implements the declared version.

Each hourly product increment must identify the owning product responsibility before selecting a repository, then compare the live directory tree, module/package names, API, database objects, tests, and documentation with that responsibility. Misleading directory paths, generic `utils`/`common` dumping grounds that own domain behavior, infrastructure imports inside the domain model, cross-context database access, obsolete product names, or customer-visible implementation boundaries are architecture defects, not cosmetic debt. When one can be corrected safely in the bounded increment, the agent moves the code and updates imports, package manifests, call sites, migrations, tests, ADRs, diagrams, and compatibility adapters in the same pull request.

The contract does not impose one universal folder template. A move is justified by domain ownership and dependency direction, not by directory aesthetics. Aggregate boundaries remain the smallest consistency boundary; external and legacy systems are isolated behind an Anti-Corruption Layer; the Shared Kernel remains minimal; and cross-context integration uses explicit versioned contracts. If a coherent move exceeds the current pull request's safe scope, the agent must record the exact owner, callers, target context, migration sequence, and acceptance evidence in `docs/product-technical-gap-baseline.md` and select it as the next bounded architecture increment rather than silently leaving the drift unresolved.

The repository-local entrypoint remains responsible for its own bounded editable paths, tests, 100% production statement and branch coverage, public docstrings, package and security verification, exact-head publication, and pull-request creation. A missing compliant entrypoint is a deliberate no-op, not permission to inject a generic writer into that repository.
This opt-in prevents the central coordinator from guessing that an unrelated manual workflow can safely modify product source. Repositories with an existing hourly or more frequent dedicated writer keep their own lease and are never double-dispatched; those schedules may share the same DDD contract and should adopt it without adding another cron.

The repository-local entrypoint remains responsible for bounded editable paths, tests, 100% production statement and branch coverage, public docstrings, package and security verification, exact-head publication, and pull-request creation. A missing compliant entrypoint is a deliberate no-op, not permission to inject a generic writer into that repository.

## Failure, evidence, and operations

Expand All @@ -56,6 +89,12 @@ Rollback is removal or disabling of `.github/workflows/organization-commercial-r

## APA 7 references

Evans, E. (2004). *Domain-driven design: Tackling complexity in the heart of software*. Addison-Wesley.

Evans, E. (2015). *Domain-driven design reference: Definitions and pattern summaries*. Domain Language. https://www.domainlanguage.com/ddd/reference/

International Organization for Standardization, International Electrotechnical Commission, & Institute of Electrical and Electronics Engineers. (2022). *Software, systems and enterprise—Architecture description* (ISO/IEC/IEEE Standard 42010:2022). https://www.iso.org/standard/74393.html

GitHub. (n.d.). *Automatic token authentication*. GitHub Docs. Retrieved August 8, 2026, from https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication

GitHub. (n.d.). *Events that trigger workflows*. GitHub Docs. Retrieved August 8, 2026, from https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows
Expand Down
8 changes: 8 additions & 0 deletions docs/product-technical-gap-baseline.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@

이 문서는 제품·기술·운영 Gap을 현재 문서와 현재 GitHub 상태에 묶어 두는 기준선이다. 새 작업은 먼저 이 문서의 Gap ID를 PR 설명과 테스트 증거에 연결하고, PR의 정확한 exact HEAD·Checks·리뷰를 다시 수집한 뒤 구현한다. 표의 상태는 작성 시점의 관측값이므로, 병합 판단에는 재사용하지 않는다. 이 인벤토리는 스냅샷이며 merge authorization이 아니다.

## 2026-09-01 시간별 DDD 실행 계약 보강

- **관측:** 조직 상용화 루프는 매시 7분 실행되고 기존 전용 writer 예약을 존중하지만, 중앙 제품개발 opt-in은 DDD 및 디렉터리 소유권 감사를 요구하지 않았다. 또한 maintainer secret이 없는 예약 환경에서는 cross-repository dispatch 전에 중단될 수 있었다.
- **Gap `G-DDD-01`:** Bounded Context와 실제 디렉터리·패키지·API·DB 소유권이 어긋나도 시간별 Agent가 이를 필수 결함으로 선택한다는 기계 검증 계약이 없었다.
- **조치:** 수동 제품개발 진입점에 `# cwl-ddd-architecture-audit: required`와 전략·전술 DDD 용어, directory-path repair, `docs/product-technical-gap-baseline.md` 갱신을 요구한다. 기존 전용 예약은 writer lease를 유지해 중복 실행하지 않는다.
- **가용성 조치:** `PR_REVIEW_MERGE_TOKEN`을 우선 사용하되 없으면 protected-default-branch job의 OIDC identity를 short-lived OpenCode App installation token으로 교환한다. repository `GITHUB_TOKEN`, reviewer credential, model provider key는 fallback으로 사용하지 않는다.
- **완료 증거:** exact-head focused policy tests, statement/branch coverage 100%, Python docstring 100%, workflow security checks, independent review, protected merge. 병합 전 상태는 구현 중이며 운영 완료로 간주하지 않는다.

## 1. 근거와 범위

### 1.1 우선순위가 높은 근거
Expand Down
24 changes: 23 additions & 1 deletion organization_commercial_readiness_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,32 @@ def manual_workflow(*, workflow_id: int = 9) -> WorkflowRecord:
path=".github/workflows/commercial-product-development.yml",
content=(
"# cwl-org-commercial-entrypoint: v1\n"
"# cwl-ddd-architecture-audit: required\n"
"on:\n workflow_dispatch:\n"
"concurrency:\n group: product-development\n"
"permissions:\n contents: write\n"
"NVIDIA_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }}\n"
"env:\n"
" NVIDIA_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }}\n"
" CWL_DDD_CONTRACT_VERSION: \"1\"\n"
" CWL_DDD_CONTRACT_CAPABILITIES: >-\n"
" aggregate anti_corruption_layer bounded_context context_map\n"
" directory_ownership domain_event domain_service entity invariant\n"
" minimal_shared_kernel product_gap_baseline repository\n"
" subdomain_classification ubiquitous_language value_object\n"
" CWL_PRODUCT_AGENT_PROMPT: |\n"
" 제품 책임과 재사용 경계를 먼저 확인하고 구매자가 체감할 한 단위를 개발한다.\n"
"\n"
" 디렉터리, 패키지, API, 데이터베이스, 테스트와 문서의 소유권을 함께 맞춘다.\n"
"jobs:\n"
" develop:\n"
" runs-on: ubuntu-24.04\n"
" steps:\n"
" - name: Invoke the repository product agent\n"
" run: |\n"
" # cwl-ddd-prompt-binding: v1\n"
" python scripts/automation/commercial_product_development.py \\\n"
" --prompt-env CWL_PRODUCT_AGENT_PROMPT \\\n"
" --architecture-contract-env CWL_DDD_CONTRACT_CAPABILITIES\n"
),
)

Expand Down
Loading
Loading