From 7d95ca2ced67896ebb29ec7682d6df459de43ff4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 18 Aug 2026 07:03:16 +0000 Subject: [PATCH 1/3] docs(readme): keep the default README customer/operator facing Rewrite the root README around product purpose, standalone run/deploy, host HTTP/OIDC calls, the orchestrator gateway contract, naruon/gyeot hub-and-leaf paths, GitHub App permissions, and operator configuration. Move hourly-loop, acquisition, saleable-program, and KPI collect links to docs/internal/README.md without deleting those files. Co-authored-by: Seongho Bae --- CHANGELOG.md | 2 +- README.md | 164 +++++++++------------------------------- docs/internal/README.md | 54 +++++++++++++ 3 files changed, 90 insertions(+), 130 deletions(-) create mode 100644 docs/internal/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md index ffa6eead5..8dd3e0262 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Changelog ## Unreleased -- 루트 `README.md`를 구매자/운영자 표면으로 재작성한다. 제품 정의, 단독 실행, 호스트 호출(HTTP API·`contracts/orchestrator-gateway.json`), 운영 설정을 앞에 두고 naruon·곁(gyeot) 허브-리프 호출을 지원 경로로 문서화한다. CloudAgent/OpenCode 절차, PR 스택, exact-head CI, writer/agent 경계는 `CONTRIBUTING.md`와 `docs/development/contributor-and-agent-procedure.md`로 옮기며 삭제하지 않는다. `test/readme-customer-facing.test.ts`가 README에 봇 매뉴얼 잔여가 남지 않는지 고정한다. +- 루트 `README.md`를 구매자/운영자 표면으로 재작성한다. 제품 정의, 단독 실행, 호스트 호출(`/health`·`/ready`·`/exchange`, `contracts/orchestrator-gateway.json`), GitHub App 권한, 운영자가 설정하는 Worker 변수, naruon·곁(gyeot) 허브-리프 호출을 앞에 둔다. 시간당 상업 루프, 시간당 제품 개발 절차, 매각/판매 가능 프로그램, KPI provenance 수집 레시피, readiness-scan 내부는 README에서 빼고 `docs/internal/README.md`로 안내한다. CloudAgent/OpenCode 절차, PR 스택, exact-head CI, writer/agent 경계는 기존 `CONTRIBUTING.md`와 `docs/development/contributor-and-agent-procedure.md`에 남기며 삭제하지 않는다. `test/readme-customer-facing.test.ts`가 README에 봇 매뉴얼 잔여가 남지 않는지 고정한다. - 비리뷰 LLM 작업인 `hourly-product-development`를 리뷰와 동일한 `contextual-orchestrator` 게이트웨이 계약(`NOEMA_LLM_API_URL` `/v1`, 모델 별칭 `contextual-orchestrator`, 전용 `NOEMA_LLM_API_KEY`)으로 전환한다. Llama Nemotron → Nemotron Super → DeepSeek 순차 NIM 후보 폴백과 `NVIDIA_NIM_API_KEY` 직접 호출을 제거하고, 공유 `scripts/verify-orchestrator-gateway.mjs`가 `/healthz` 신원과 직접 공급자 호스트를 실패-폐쇄한다. 리뷰어의 `NOEMA_FALLBACK_*` / PydanticAI `FallbackModel` 순차 폴백도 제거해 남은 설정은 실패-폐쇄한다. 동일 계약을 `contracts/orchestrator-gateway.json`으로 공개해 `ContextualWisdomLab/naruon` 판단·결정 에이전트가 1급 소비자로 재사용할 수 있게 한다. naruon 배선은 별도 저장소 PR이다. 상위 공급자 키는 오케스트레이터 KV에 남기며 OIDC 토큰 중개·App 신원·3-runner 샌드박스 경계는 유지한다. - 검증된 active-orphan 워크플로 하나를 운영자가 호출할 수 있는 `operations:workflow-registry-disable` 경로를 추가한다. 저장소와 워크플로 ID를 `NOEMA_MAINTAINER_TOKEN_PATH` 위임 토큰 파일 읽기 전에 검사하고, 신선한 전체 레지스트리 감사·즉시 live refresh·프로세스 로컬 plan·보호된 main/워크플로 재검증·사후 전체 감사 봉투(`schema_version` 1, `PASS`/`FAIL`, `remaining_failure_codes`, `remaining_active_orphan_ids`)를 통과한 뒤에만 영수증을 유지한다. 성공 종료와 `post_audit_status: FAIL`은 해당 ID만 `disabled_manually`가 되었고 레지스트리는 아직 더럽을 수 있음을 뜻하므로, 운영자는 영수증의 `remaining_active_orphan_ids`로 다음 단일 호출을 이어간다. 배치 비활성화·자가 수리 워크플로·거버넌스 완화는 추가하지 않으며 호출 계약은 doctoring에 기록한다. - 읽기 전용 `operations:runner-assignment` audit를 추가해 exact workflow run/source head에 대한 runner assignment를 완전 pagination으로 진단하고, 신선한 unassigned queue는 bounded grace 이후 실패-폐쇄한다. 이 증빙은 runner assignment와 required Check/CI, formal review, merge, release, deployment authority를 분리하며 assigned runner 이후 workflow failure를 성공으로 승격하지 않는다. diff --git a/README.md b/README.md index 2e96a5fad..113e9067b 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,13 @@ calls it through a published HTTP API and a secret-free LLM gateway contract. That hub-and-leaf call is the supported MSA path — **따로 또 같이** — not a reason to merge repositories. -It deploys as a Cloudflare Worker (Free tier) with two jobs: +It deploys as a [Cloudflare Worker](https://developers.cloudflare.com/workers/) +(Free tier) with two jobs: -1. **Token exchange.** GitHub Actions presents an OIDC JWT (audience - `cwl-noema-review`). Noema verifies issuer, audience, organization owner, - and the exact trusted central workflow identity, then returns a +1. **Token exchange.** GitHub Actions presents an + [OIDC](https://docs.github.com/en/actions/reference/security/oidc) JWT + (audience `cwl-noema-review`). Noema verifies issuer, audience, organization + owner, and the exact trusted central workflow identity, then returns a repository-scoped GitHub App installation token (`pull_requests: write`, `contents: read`, `checks: read`). 2. **Review.** The default-branch @@ -19,11 +21,16 @@ It deploys as a Cloudflare Worker (Free tier) with two jobs: analysis stays in a separate sandbox; see [`docs/noema-agent-sandbox-plan.md`](./docs/noema-agent-sandbox-plan.md). -Every Noema LLM job — production review, hourly product development, and +Every Noema LLM job — production review, hourly-product-development, and host-side judgments — calls `ContextualWisdomLab/contextual-orchestrator`. Upstream provider keys stay in the orchestrator credential KV. Noema does not walk a sequential model list or fall back to a direct provider. +OIDC, Worker binding, and Durable Object trust rules are recorded in +[Architecture doctoring](./docs/doctoring/architecture-trust-boundaries.md) +and the [ADR index](./docs/adr/README.md). Do not treat those records as a new +paper list. + ## Composition hubs Leaf products stay independently deployable. Composition hubs call them as @@ -87,7 +94,8 @@ Do not reuse `OPENAI_API_KEY` as Noema's gateway token. ## How a host calls it -The public HTTP surface is documented in [`openapi.json`](./openapi.json) and +The public HTTP surface is documented in [`openapi.json`](./openapi.json) +([OpenAPI 3.1](https://spec.openapis.org/oas/v3.1.1.html)) and [API 명세](./docs/api-spec.md). Every JSON response is `{ ok: true, data, trace_id }` or `{ ok: false, error_code, message, details, trace_id }`. @@ -131,19 +139,15 @@ The Python reviewer package (`reviewer/`) is the judgement plane. It consumes a bounded PR manifest and can publish a `ReviewVerdict`. See [`reviewer/README.md`](./reviewer/README.md). -## Operator-facing review dispatch - -`central-review` is default-branch-only. Bind the SHA from a fresh PR read: +After deploy, confirm the HTTP contract: ```bash -gh api repos/ContextualWisdomLab/noema/dispatches -X POST --input - <<'JSON' -{"event_type":"noema-review","client_payload":{"target_repository":"ContextualWisdomLab/example","pr_number":1,"pr_head_sha":"0123456789abcdef0123456789abcdef01234567"}} -JSON +NOEMA_EXCHANGE_URL=https://.../exchange npm run smoke:check ``` -Runtime isolation, wait policy, and sandbox controls live in -[`docs/noema-agent-sandbox-plan.md`](./docs/noema-agent-sandbox-plan.md) and -[`docs/development/contributor-and-agent-procedure.md`](./docs/development/contributor-and-agent-procedure.md). +`npm run smoke:check` checks `/health`, `/ready`, and `/exchange` schema, +trace/latency headers, runtime readiness, the unauthenticated 401 Bearer +challenge, and no-store/nosniff headers. ## Required GitHub App permissions @@ -166,8 +170,9 @@ Public Worker vars (defaults in `wrangler.toml`): | `ALLOWED_AUDIENCE` | `cwl-noema-review` | OIDC audience | | `ALLOWED_REPOSITORY_OWNER` | `ContextualWisdomLab` | Allowed org | | `ALLOWED_WORKFLOW_REPOSITORY` | `ContextualWisdomLab/.github` | Trusted workflow repo | -| `ALLOWED_WORKFLOW_REF_PREFIX` | `…/noema-review.yml@refs/heads/main` | Exact trusted workflow ref (name kept; matching is exact, not prefix) | +| `ALLOWED_WORKFLOW_REF_PREFIX` | `ContextualWisdomLab/.github/.github/workflows/noema-review.yml@refs/heads/main` | Exact trusted workflow ref (name kept; matching is exact, not prefix) | | `GITHUB_API_BASE` | `https://api.github.com` | GitHub Cloud API origin | +| `GITHUB_APP_SLUG` | `noema` | GitHub App slug | | `NOEMA_RATE_LIMIT_PER_MINUTE` | `60` | `/exchange` fixed-window budget | | `NOEMA_OIDC_JWKS_CACHE_TTL_SECONDS` | `300` | OIDC JWKS cache | | `NOEMA_INSTALLATION_CACHE_TTL_SECONDS` | `600` | Installation-id cache | @@ -176,123 +181,24 @@ Public Worker vars (defaults in `wrangler.toml`): across Worker isolates, then keeps the isolate-local limiter as defense in depth. Missing or malformed distributed decisions fail closed; see [Distributed rate limiting](./docs/distributed-rate-limiting.md). -OIDC `jti` values are consumed once by a Durable Object replay guard. +OIDC `jti` values are consumed once by a Durable Object replay guard; see +[OIDC replay protection](./docs/oidc-replay-protection.md). -## 판매/운영 패키지 +## Operator documentation -- [API 명세](./docs/api-spec.md) -- [안정성 계약](./docs/api-stability-contract.md) -- [Orchestrator gateway consumer contract](./docs/orchestrator-gateway-consumer-contract.md) - [온보딩 가이드](./docs/onboarding.md) - [운영 Runbook](./docs/runbook.md) -- [Distributed Rate Limiting](./docs/distributed-rate-limiting.md) -- [Runtime readiness](./docs/runtime-readiness.md) -- [Hourly Commercial-Readiness Loop](./docs/hourly-commercial-readiness-loop.md) -- [Hourly Orchestrator Product Development](./docs/operations/hourly-product-development.md) -- [SLA/지원 정책](./docs/sla-and-support.md) -- [가격 초안](./docs/pricing-draft.md) -- [관측성 KPI](./docs/observability-kpi.md) +- [API 명세](./docs/api-spec.md) +- [안정성 계약](./docs/api-stability-contract.md) +- [OpenAPI](./openapi.json) - [보안/위협 모델](./docs/threat-model.md) -- [이용약관 초안](./docs/terms-draft.md) - [배포 가이드](./docs/deployment-guide.md) -- [보안 검증 체크리스트](./docs/security-validation-checklist.md) -- [파일럿 온보딩 체크리스트](./docs/pilot-readiness-checklist.md) -- [출시 준비 감사서](./docs/release-readiness-audit.md) -- [Buyer Pitch Deck Outline](./docs/buyer-pitch-deck-outline.md) -- [판매 가능 Goal 등록서](./docs/saleable-program-goal-registry.md) -- [판매 가능 프로그램 Goal](./docs/saleable-program-readiness.md) -- [목표 완료 감사서](./docs/goal-completion-audit.md) -- [20억 매각 가능성 Goal 등록서](./docs/acquisition-readiness-2b.md) -- [Buyer Due Diligence Index](./docs/buyer-due-diligence-index.md) -- [Transfer Readiness Plan](./docs/transfer-readiness-plan.md) -- [Library Boundary Decision](./docs/library-boundary-decision.md) -- [Contributing](./CONTRIBUTING.md) - -## KPI 계산 - -```bash -npm run kpi:compute -npm run kpi:check -npm run kpi:alerts -npm run kpi:verify -NOEMA_KPI_TAIL_COMMAND='timeout 30s wrangler tail noema --env production --format json' \ - NOEMA_KPI_LOG_PATH=exchange-30d.ndjson \ - NOEMA_KPI_SOURCE_KIND=production \ - NOEMA_KPI_SOURCE_ID=cloudflare-logpush:noema-production \ - npm run kpi:collect -# 또는 Logpush/아카이브 URL 직접 사용 -NOEMA_KPI_LOG_URL=https://.../exchange-30d.ndjson \ - NOEMA_KPI_LOG_PATH=exchange-30d.ndjson \ - NOEMA_KPI_SOURCE_KIND=production \ - NOEMA_KPI_SOURCE_ID=cloudflare-logpush:noema-production \ - npm run kpi:collect -NOEMA_KPI_REQUIRE_WINDOW_DAYS=30 npm run kpi:verify:strict -``` - -`exchange-30d.ndjson`은 운영 30일 로그 집계용 파일입니다. -`kpi:collect`는 `exchange-30d.ndjson.provenance.json`을 함께 생성하며, strict KPI 게이트는 `sourceKind=production`, `sourceId`, `records`, `collectedAt`이 있는 provenance 파일을 요구합니다. -`wrangler tail`은 실시간 수집이므로, 30일 집계는 Logpush/외부 파이프라인 또는 임시 저장본을 `exchange-30d.ndjson`로 구성해야 합니다. - -## 배포 전 스모크 체크 - -```bash -NOEMA_EXCHANGE_URL=https://.../exchange npm run smoke:check -``` - -`npm run smoke:check`는 `/health`, `/ready`, `/exchange`의 스키마, 추적/지연 헤더, runtime readiness, 401 Bearer challenge, no-store/nosniff 보안 헤더를 확인하고 실패 내역을 JSON으로 출력하며, -배포에서 `NOEMA_SMOKE_EVIDENCE_PATH`를 지정하면 `noema-smoke-evidence.json` 형태로 증빙을 저장할 수 있습니다. - -CI/CD의 `cd` 워크플로우는 동일 스크립트를 실행해 `/health`/`/ready`/`/exchange` 계약을 검증합니다. - -운영 증빙 수집 전에는 다음 preflight로 production URL과 KPI 로그 수집 입력이 준비됐는지 확인합니다. - -```bash -NOEMA_EXCHANGE_URL=https://.../exchange \ -NOEMA_KPI_SOURCE_KIND=production \ -NOEMA_KPI_SOURCE_ID=cloudflare-logpush:noema-production \ -NOEMA_KPI_LOG_URL=https://.../exchange-30d.ndjson \ -npm run production:preflight -``` - -`production:preflight`는 증빙을 생성하지 않으며, smoke/KPI evidence 수집 전 누락된 입력을 fail-fast로 알려줍니다. - -## 판매 가능성 자동 감사 - -```bash -npm run readiness:audit -NOEMA_EXCHANGE_URL=https://.../exchange npm run readiness:audit -``` - -- `npm run readiness:audit`는 기술게이트, 보안/테스트, KPI strict(가능한 경우), smoke 증빙(환경변수 지정 시), 파일럿 완료 증빙까지 한 번에 검사합니다. -- `docs/security-validation-checklist.md`의 미체크 항목은 readiness audit 실패로 처리됩니다. -- 결과는 `artifacts/saleable-readiness//goal-audit.json`에 저장됩니다. -- 파일럿 완료 증빙은 production HTTPS `NOEMA URL`, `증빙 출처: production`, `계약/매출 증빙 경로`가 있어야 인정됩니다. -- `readiness-scan` 워크플로우(`.github/workflows/readiness-scan.yml`)는 UTC 01:00 기준으로 정기 `readiness:audit`를 실행해 증빙을 `saleable-readiness-audit` 아티팩트로 보존합니다. -- 정기 `schedule` 실행은 누락된 production evidence를 `NOT_READY` status, warning, artifact로 남기는 감시 작업이며, `workflow_dispatch`와 로컬 `npm run readiness:audit`는 동일한 누락을 실패로 유지합니다. - -## 20억 매각 가능성 감사 - -```bash -npm run acquisition:manifest -npm run acquisition:audit -``` - -- `npm run acquisition:manifest`는 buyer data room 파일, 명령, 외부 Figma 자산, 최종 evidence 경로를 `artifacts/acquisition-readiness//data-room-manifest.json`으로 해시/색인합니다. -- manifest의 최종 evidence 항목은 파일 존재를 색인하며, 증빙 내용의 유효성은 `validatedBy`에 적힌 `npm run acquisition:audit` 통과로 판정합니다. -- `npm run acquisition:audit`는 `KRW 2,000,000,000` 매각 협상 기준의 실사 패키지를 검사합니다. -- `npm run security:evidence`는 보안 체크리스트와 reviewed security evidence만 단독 검증합니다. -- 기본 evidence path는 `artifacts/acquisition/revenue-evidence.json`, `artifacts/acquisition/transfer-evidence.json`, `docs/pilot-readiness-log.md`, 그리고 가장 최신 `artifacts/saleable-readiness//goal-audit.json`입니다. -- `NOEMA_PILOT_LOG_PATH`로 별도 production 파일럿 로그를 지정할 수 있습니다. -- ARR/LOI/weighted pipeline, production 유료 파일럿, IP/license/권한 이전성, saleable readiness가 모두 증빙되지 않으면 실패합니다. -- revenue/transfer evidence는 `owner`, `source_documents`, 기본 45일 이내 `updated_at` 메타데이터가 없으면 실패합니다. -- `acquisition-readiness-audit` 워크플로우(`.github/workflows/acquisition-readiness-scan.yml`)는 매일 `acquisition:manifest`와 `acquisition:audit`를 실행하고 evidence artifact를 보존합니다. -- 정기 `schedule` 실행은 production/acquisition evidence 누락을 `NOT_READY` status, warning, artifact로 남기는 감시 작업이며, `workflow_dispatch`와 로컬 `npm run acquisition:audit`는 동일한 누락을 실패로 유지합니다. -- submodule은 현재 사용하지 않으며, `docs/library-boundary-decision.md`의 split trigger가 충족될 때 npm workspace package 분리를 검토합니다. - -## 릴리스 검증 - -```bash -npm run release:verify -``` +- [SLA/지원 정책](./docs/sla-and-support.md) +- [Runtime readiness](./docs/runtime-readiness.md) +- [Distributed Rate Limiting](./docs/distributed-rate-limiting.md) +- [Orchestrator gateway consumer contract](./docs/orchestrator-gateway-consumer-contract.md) -운영/프로덕션 배포 경로는 `.github/workflows/cd.yml`에서 `npm run release:verify:strict`를 사용하며, 실패 시 KPI 증빙(`noema-kpi-evidence.json`)과 provenance(`exchange-30d.ndjson.provenance.json`)는 워크플로우 Artifact로 저장됩니다. +Maintainers and coding agents: start at +[`docs/internal/README.md`](./docs/internal/README.md) +(contributor and agent procedure: +[`docs/development/contributor-and-agent-procedure.md`](./docs/development/contributor-and-agent-procedure.md)). diff --git a/docs/internal/README.md b/docs/internal/README.md new file mode 100644 index 000000000..67acaac88 --- /dev/null +++ b/docs/internal/README.md @@ -0,0 +1,54 @@ +# Internal maintainer index + +This index is for maintainers and coding agents. Buyers and operators should +start at [`README.md`](../../README.md). The files listed here stay on disk; +they are not the customer/operator README surface. + +## Contributor and agent procedure + +- [Contributing](../../CONTRIBUTING.md) +- [Contributor and agent procedure](../development/contributor-and-agent-procedure.md) +- [AGENTS.md](../../AGENTS.md) +- [Review sandbox](../noema-agent-sandbox-plan.md) + +## Hourly loops + +- [Hourly commercial-readiness loop](../hourly-commercial-readiness-loop.md) +- [Hourly product development](../operations/hourly-product-development.md) +- [Hourly product-development prerequisites](../operations/hourly-product-development-prerequisites.md) + +## Saleable program and acquisition internals + +- [Saleable program readiness](../saleable-program-readiness.md) +- [Saleable program goal registry](../saleable-program-goal-registry.md) +- [Goal completion audit](../goal-completion-audit.md) +- [Acquisition readiness](../acquisition-readiness-2b.md) +- [Buyer due diligence index](../buyer-due-diligence-index.md) +- [Buyer pitch deck outline](../buyer-pitch-deck-outline.md) +- [Transfer readiness plan](../transfer-readiness-plan.md) +- [Library boundary decision](../library-boundary-decision.md) +- [Pilot readiness checklist](../pilot-readiness-checklist.md) +- [Release readiness audit](../release-readiness-audit.md) +- [Pricing draft](../pricing-draft.md) +- [Terms draft](../terms-draft.md) + +## KPI collect and readiness-scan internals + +Operator-facing SLA numbers stay in +[SLA/지원 정책](../sla-and-support.md). The recipes below are maintainer +evidence collection, not buyer documentation. + +- [관측성 KPI](../observability-kpi.md) +- `npm run kpi:compute` / `kpi:check` / `kpi:alerts` / `kpi:verify` +- `npm run kpi:collect` writes `exchange-30d.ndjson` plus + `exchange-30d.ndjson.provenance.json`. Strict KPI requires + `sourceKind=production`, `sourceId`, `records`, and `collectedAt`. +- `npm run production:preflight` fail-fast-checks production URL and KPI + collect inputs; it does not create evidence. +- `npm run readiness:audit` and `.github/workflows/readiness-scan.yml` +- `npm run acquisition:manifest` / `acquisition:audit` and + `.github/workflows/acquisition-readiness-scan.yml` +- `npm run security:evidence` + +Do not copy these loops, sale-process audits, or agent procedures back into +`README.md`. From 2aba56badf13aa9b66cb92828cba1d05526f1a78 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 30 Aug 2026 02:52:39 +0000 Subject: [PATCH 2/3] docs(readme): keep the default README customer/operator facing Reapply PR #425's content onto current main after an unrelated git history rewrite orphaned the branch's original base commit (a7793751, pre-#450) from main's history (root ab3ebde, from #450 onward). README.md was byte-identical between the old base and current main, so the PR's own README.md diff applies verbatim. - Rewrite README.md as a customer/operator surface: product purpose, standalone run/deploy, /health /ready /exchange, orchestrator gateway contract, naruon/gyeot hub table, GitHub App permissions, operator vars, and links to onboarding/runbook/API spec/threat-model/deployment guide/SLA/OpenAPI. - Add docs/internal/README.md so maintainers still find the hourly-loop, acquisition, saleable-program, and KPI collect internals that moved out of the customer README. - Update the CHANGELOG.md Unreleased entry to the PR's refined wording, replacing the earlier draft of the same bullet already present on main. test/readme-customer-facing.test.ts continues to pass. --- CHANGELOG.md | 2 +- README.md | 164 +++++++++------------------------------- docs/internal/README.md | 54 +++++++++++++ 3 files changed, 90 insertions(+), 130 deletions(-) create mode 100644 docs/internal/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d71ee7f3..102c705d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ - 외부 스케줄러 운영 증거의 시간 권한을 fail-closed로 강화한다. canonical UTC `scheduled_at`/`started_at`이 검증 시점보다 미래인 retained evidence는 `scheduler_timestamp_future`로 거부해 아직 실행되지 않은 hourly run이 운영·인수 준비 증거를 제조하지 못하게 한다. - strict production KPI provenance를 fail-closed로 강화한다. `collectedAt`은 canonical UTC 시각만 허용하고 미래·존재하지 않는 달력값을 거부하며, `records`는 coercion 없는 양의 safe-integer JSON number로 authenticated log record count와 일치해야 한다. production log는 `O_NOFOLLOW`로 연 verified regular-file descriptor의 identity·size·mode·mtime·ctime을 재검증하면서 동일 verified bytes로 snapshot을 생성해 symlink·pathname replacement·descriptor drift가 KPI authority를 제조하지 못하게 한다. - 인수 준비성 감사의 buyer/data-room 증거 경계를 실패-폐쇄로 강화한다. `updated_at`은 canonical ISO date 또는 timezone-bearing timestamp만 허용하고 존재하지 않는 달력값·비정규 형식·미래 시각을 거부하며, 명시된 evidence max-age 설정과 매출·고객·LOI·비율 지표는 coercion 없이 유효 도메인을 만족해야 한다. immutable release asset의 `bytes`도 실제 양의 safe-integer JSON number만 인정해 문자열·범위 밖 값이 매각 준비 증거를 제조하지 못하게 한다. -- 루트 `README.md`를 구매자/운영자 표면으로 재작성한다. 제품 정의, 단독 실행, 호스트 호출(HTTP API·`contracts/orchestrator-gateway.json`), 운영 설정을 앞에 두고 naruon·곁(gyeot) 허브-리프 호출을 지원 경로로 문서화한다. CloudAgent/OpenCode 절차, PR 스택, exact-head CI, writer/agent 경계는 `CONTRIBUTING.md`와 `docs/development/contributor-and-agent-procedure.md`로 옮기며 삭제하지 않는다. `test/readme-customer-facing.test.ts`가 README에 봇 매뉴얼 잔여가 남지 않는지 고정한다. +- 루트 `README.md`를 구매자/운영자 표면으로 재작성한다. 제품 정의, 단독 실행, 호스트 호출(`/health`·`/ready`·`/exchange`, `contracts/orchestrator-gateway.json`), GitHub App 권한, 운영자가 설정하는 Worker 변수, naruon·곁(gyeot) 허브-리프 호출을 앞에 둔다. 시간당 상업 루프, 시간당 제품 개발 절차, 매각/판매 가능 프로그램, KPI provenance 수집 레시피, readiness-scan 내부는 README에서 빼고 `docs/internal/README.md`로 안내한다. CloudAgent/OpenCode 절차, PR 스택, exact-head CI, writer/agent 경계는 기존 `CONTRIBUTING.md`와 `docs/development/contributor-and-agent-procedure.md`에 남기며 삭제하지 않는다. `test/readme-customer-facing.test.ts`가 README에 봇 매뉴얼 잔여가 남지 않는지 고정한다. - repository-owned patch-validator image는 Node.js 24.19.0을 fully static으로 빌드한 `scratch` runtime을 사용하고, `process.versions` 기반 embedded dependency inventory, exact image SHA-256, CycloneDX/Syft/Grype/Trivy evidence, no-network/read-only/non-root smoke를 동일 exact-head receipt에 결합한다. - 비리뷰 LLM 작업인 `hourly-product-development`를 리뷰와 동일한 `contextual-orchestrator` 게이트웨이 계약(`NOEMA_LLM_API_URL` `/v1`, 모델 별칭 `contextual-orchestrator`, 전용 `NOEMA_LLM_API_KEY`)으로 전환한다. Llama Nemotron → Nemotron Super → DeepSeek 순차 NIM 후보 폴백과 `NVIDIA_NIM_API_KEY` 직접 호출을 제거하고, 공유 `scripts/verify-orchestrator-gateway.mjs`가 `/healthz` 신원과 직접 공급자 호스트를 실패-폐쇄한다. 리뷰어의 `NOEMA_FALLBACK_*` / PydanticAI `FallbackModel` 순차 폴백도 제거해 남은 설정은 실패-폐쇄한다. 동일 계약을 `contracts/orchestrator-gateway.json`으로 공개해 `ContextualWisdomLab/naruon` 판단·결정 에이전트가 1급 소비자로 재사용할 수 있게 한다. naruon 배선은 별도 저장소 PR이다. 상위 공급자 키는 오케스트레이터 KV에 남기며 OIDC 토큰 중개·App 신원·3-runner 샌드박스 경계는 유지한다. - 검증된 active-orphan 워크플로 하나를 운영자가 호출할 수 있는 `operations:workflow-registry-disable` 경로를 추가한다. 저장소와 워크플로 ID를 `NOEMA_MAINTAINER_TOKEN_PATH` 위임 토큰 파일 읽기 전에 검사하고, 신선한 전체 레지스트리 감사·즉시 live refresh·프로세스 로컬 plan·보호된 main/워크플로 재검증·사후 전체 감사 봉투(`schema_version` 1, `PASS`/`FAIL`, `remaining_failure_codes`, `remaining_active_orphan_ids`)를 통과한 뒤에만 영수증을 유지한다. 성공 종료와 `post_audit_status: FAIL`은 해당 ID만 `disabled_manually`가 되었고 레지스트리는 아직 더럽을 수 있음을 뜻하므로, 운영자는 영수증의 `remaining_active_orphan_ids`로 다음 단일 호출을 이어간다. 배치 비활성화·자가 수리 워크플로·거버넌스 완화는 추가하지 않으며 호출 계약은 doctoring에 기록한다. diff --git a/README.md b/README.md index 2e96a5fad..113e9067b 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,13 @@ calls it through a published HTTP API and a secret-free LLM gateway contract. That hub-and-leaf call is the supported MSA path — **따로 또 같이** — not a reason to merge repositories. -It deploys as a Cloudflare Worker (Free tier) with two jobs: +It deploys as a [Cloudflare Worker](https://developers.cloudflare.com/workers/) +(Free tier) with two jobs: -1. **Token exchange.** GitHub Actions presents an OIDC JWT (audience - `cwl-noema-review`). Noema verifies issuer, audience, organization owner, - and the exact trusted central workflow identity, then returns a +1. **Token exchange.** GitHub Actions presents an + [OIDC](https://docs.github.com/en/actions/reference/security/oidc) JWT + (audience `cwl-noema-review`). Noema verifies issuer, audience, organization + owner, and the exact trusted central workflow identity, then returns a repository-scoped GitHub App installation token (`pull_requests: write`, `contents: read`, `checks: read`). 2. **Review.** The default-branch @@ -19,11 +21,16 @@ It deploys as a Cloudflare Worker (Free tier) with two jobs: analysis stays in a separate sandbox; see [`docs/noema-agent-sandbox-plan.md`](./docs/noema-agent-sandbox-plan.md). -Every Noema LLM job — production review, hourly product development, and +Every Noema LLM job — production review, hourly-product-development, and host-side judgments — calls `ContextualWisdomLab/contextual-orchestrator`. Upstream provider keys stay in the orchestrator credential KV. Noema does not walk a sequential model list or fall back to a direct provider. +OIDC, Worker binding, and Durable Object trust rules are recorded in +[Architecture doctoring](./docs/doctoring/architecture-trust-boundaries.md) +and the [ADR index](./docs/adr/README.md). Do not treat those records as a new +paper list. + ## Composition hubs Leaf products stay independently deployable. Composition hubs call them as @@ -87,7 +94,8 @@ Do not reuse `OPENAI_API_KEY` as Noema's gateway token. ## How a host calls it -The public HTTP surface is documented in [`openapi.json`](./openapi.json) and +The public HTTP surface is documented in [`openapi.json`](./openapi.json) +([OpenAPI 3.1](https://spec.openapis.org/oas/v3.1.1.html)) and [API 명세](./docs/api-spec.md). Every JSON response is `{ ok: true, data, trace_id }` or `{ ok: false, error_code, message, details, trace_id }`. @@ -131,19 +139,15 @@ The Python reviewer package (`reviewer/`) is the judgement plane. It consumes a bounded PR manifest and can publish a `ReviewVerdict`. See [`reviewer/README.md`](./reviewer/README.md). -## Operator-facing review dispatch - -`central-review` is default-branch-only. Bind the SHA from a fresh PR read: +After deploy, confirm the HTTP contract: ```bash -gh api repos/ContextualWisdomLab/noema/dispatches -X POST --input - <<'JSON' -{"event_type":"noema-review","client_payload":{"target_repository":"ContextualWisdomLab/example","pr_number":1,"pr_head_sha":"0123456789abcdef0123456789abcdef01234567"}} -JSON +NOEMA_EXCHANGE_URL=https://.../exchange npm run smoke:check ``` -Runtime isolation, wait policy, and sandbox controls live in -[`docs/noema-agent-sandbox-plan.md`](./docs/noema-agent-sandbox-plan.md) and -[`docs/development/contributor-and-agent-procedure.md`](./docs/development/contributor-and-agent-procedure.md). +`npm run smoke:check` checks `/health`, `/ready`, and `/exchange` schema, +trace/latency headers, runtime readiness, the unauthenticated 401 Bearer +challenge, and no-store/nosniff headers. ## Required GitHub App permissions @@ -166,8 +170,9 @@ Public Worker vars (defaults in `wrangler.toml`): | `ALLOWED_AUDIENCE` | `cwl-noema-review` | OIDC audience | | `ALLOWED_REPOSITORY_OWNER` | `ContextualWisdomLab` | Allowed org | | `ALLOWED_WORKFLOW_REPOSITORY` | `ContextualWisdomLab/.github` | Trusted workflow repo | -| `ALLOWED_WORKFLOW_REF_PREFIX` | `…/noema-review.yml@refs/heads/main` | Exact trusted workflow ref (name kept; matching is exact, not prefix) | +| `ALLOWED_WORKFLOW_REF_PREFIX` | `ContextualWisdomLab/.github/.github/workflows/noema-review.yml@refs/heads/main` | Exact trusted workflow ref (name kept; matching is exact, not prefix) | | `GITHUB_API_BASE` | `https://api.github.com` | GitHub Cloud API origin | +| `GITHUB_APP_SLUG` | `noema` | GitHub App slug | | `NOEMA_RATE_LIMIT_PER_MINUTE` | `60` | `/exchange` fixed-window budget | | `NOEMA_OIDC_JWKS_CACHE_TTL_SECONDS` | `300` | OIDC JWKS cache | | `NOEMA_INSTALLATION_CACHE_TTL_SECONDS` | `600` | Installation-id cache | @@ -176,123 +181,24 @@ Public Worker vars (defaults in `wrangler.toml`): across Worker isolates, then keeps the isolate-local limiter as defense in depth. Missing or malformed distributed decisions fail closed; see [Distributed rate limiting](./docs/distributed-rate-limiting.md). -OIDC `jti` values are consumed once by a Durable Object replay guard. +OIDC `jti` values are consumed once by a Durable Object replay guard; see +[OIDC replay protection](./docs/oidc-replay-protection.md). -## 판매/운영 패키지 +## Operator documentation -- [API 명세](./docs/api-spec.md) -- [안정성 계약](./docs/api-stability-contract.md) -- [Orchestrator gateway consumer contract](./docs/orchestrator-gateway-consumer-contract.md) - [온보딩 가이드](./docs/onboarding.md) - [운영 Runbook](./docs/runbook.md) -- [Distributed Rate Limiting](./docs/distributed-rate-limiting.md) -- [Runtime readiness](./docs/runtime-readiness.md) -- [Hourly Commercial-Readiness Loop](./docs/hourly-commercial-readiness-loop.md) -- [Hourly Orchestrator Product Development](./docs/operations/hourly-product-development.md) -- [SLA/지원 정책](./docs/sla-and-support.md) -- [가격 초안](./docs/pricing-draft.md) -- [관측성 KPI](./docs/observability-kpi.md) +- [API 명세](./docs/api-spec.md) +- [안정성 계약](./docs/api-stability-contract.md) +- [OpenAPI](./openapi.json) - [보안/위협 모델](./docs/threat-model.md) -- [이용약관 초안](./docs/terms-draft.md) - [배포 가이드](./docs/deployment-guide.md) -- [보안 검증 체크리스트](./docs/security-validation-checklist.md) -- [파일럿 온보딩 체크리스트](./docs/pilot-readiness-checklist.md) -- [출시 준비 감사서](./docs/release-readiness-audit.md) -- [Buyer Pitch Deck Outline](./docs/buyer-pitch-deck-outline.md) -- [판매 가능 Goal 등록서](./docs/saleable-program-goal-registry.md) -- [판매 가능 프로그램 Goal](./docs/saleable-program-readiness.md) -- [목표 완료 감사서](./docs/goal-completion-audit.md) -- [20억 매각 가능성 Goal 등록서](./docs/acquisition-readiness-2b.md) -- [Buyer Due Diligence Index](./docs/buyer-due-diligence-index.md) -- [Transfer Readiness Plan](./docs/transfer-readiness-plan.md) -- [Library Boundary Decision](./docs/library-boundary-decision.md) -- [Contributing](./CONTRIBUTING.md) - -## KPI 계산 - -```bash -npm run kpi:compute -npm run kpi:check -npm run kpi:alerts -npm run kpi:verify -NOEMA_KPI_TAIL_COMMAND='timeout 30s wrangler tail noema --env production --format json' \ - NOEMA_KPI_LOG_PATH=exchange-30d.ndjson \ - NOEMA_KPI_SOURCE_KIND=production \ - NOEMA_KPI_SOURCE_ID=cloudflare-logpush:noema-production \ - npm run kpi:collect -# 또는 Logpush/아카이브 URL 직접 사용 -NOEMA_KPI_LOG_URL=https://.../exchange-30d.ndjson \ - NOEMA_KPI_LOG_PATH=exchange-30d.ndjson \ - NOEMA_KPI_SOURCE_KIND=production \ - NOEMA_KPI_SOURCE_ID=cloudflare-logpush:noema-production \ - npm run kpi:collect -NOEMA_KPI_REQUIRE_WINDOW_DAYS=30 npm run kpi:verify:strict -``` - -`exchange-30d.ndjson`은 운영 30일 로그 집계용 파일입니다. -`kpi:collect`는 `exchange-30d.ndjson.provenance.json`을 함께 생성하며, strict KPI 게이트는 `sourceKind=production`, `sourceId`, `records`, `collectedAt`이 있는 provenance 파일을 요구합니다. -`wrangler tail`은 실시간 수집이므로, 30일 집계는 Logpush/외부 파이프라인 또는 임시 저장본을 `exchange-30d.ndjson`로 구성해야 합니다. - -## 배포 전 스모크 체크 - -```bash -NOEMA_EXCHANGE_URL=https://.../exchange npm run smoke:check -``` - -`npm run smoke:check`는 `/health`, `/ready`, `/exchange`의 스키마, 추적/지연 헤더, runtime readiness, 401 Bearer challenge, no-store/nosniff 보안 헤더를 확인하고 실패 내역을 JSON으로 출력하며, -배포에서 `NOEMA_SMOKE_EVIDENCE_PATH`를 지정하면 `noema-smoke-evidence.json` 형태로 증빙을 저장할 수 있습니다. - -CI/CD의 `cd` 워크플로우는 동일 스크립트를 실행해 `/health`/`/ready`/`/exchange` 계약을 검증합니다. - -운영 증빙 수집 전에는 다음 preflight로 production URL과 KPI 로그 수집 입력이 준비됐는지 확인합니다. - -```bash -NOEMA_EXCHANGE_URL=https://.../exchange \ -NOEMA_KPI_SOURCE_KIND=production \ -NOEMA_KPI_SOURCE_ID=cloudflare-logpush:noema-production \ -NOEMA_KPI_LOG_URL=https://.../exchange-30d.ndjson \ -npm run production:preflight -``` - -`production:preflight`는 증빙을 생성하지 않으며, smoke/KPI evidence 수집 전 누락된 입력을 fail-fast로 알려줍니다. - -## 판매 가능성 자동 감사 - -```bash -npm run readiness:audit -NOEMA_EXCHANGE_URL=https://.../exchange npm run readiness:audit -``` - -- `npm run readiness:audit`는 기술게이트, 보안/테스트, KPI strict(가능한 경우), smoke 증빙(환경변수 지정 시), 파일럿 완료 증빙까지 한 번에 검사합니다. -- `docs/security-validation-checklist.md`의 미체크 항목은 readiness audit 실패로 처리됩니다. -- 결과는 `artifacts/saleable-readiness//goal-audit.json`에 저장됩니다. -- 파일럿 완료 증빙은 production HTTPS `NOEMA URL`, `증빙 출처: production`, `계약/매출 증빙 경로`가 있어야 인정됩니다. -- `readiness-scan` 워크플로우(`.github/workflows/readiness-scan.yml`)는 UTC 01:00 기준으로 정기 `readiness:audit`를 실행해 증빙을 `saleable-readiness-audit` 아티팩트로 보존합니다. -- 정기 `schedule` 실행은 누락된 production evidence를 `NOT_READY` status, warning, artifact로 남기는 감시 작업이며, `workflow_dispatch`와 로컬 `npm run readiness:audit`는 동일한 누락을 실패로 유지합니다. - -## 20억 매각 가능성 감사 - -```bash -npm run acquisition:manifest -npm run acquisition:audit -``` - -- `npm run acquisition:manifest`는 buyer data room 파일, 명령, 외부 Figma 자산, 최종 evidence 경로를 `artifacts/acquisition-readiness//data-room-manifest.json`으로 해시/색인합니다. -- manifest의 최종 evidence 항목은 파일 존재를 색인하며, 증빙 내용의 유효성은 `validatedBy`에 적힌 `npm run acquisition:audit` 통과로 판정합니다. -- `npm run acquisition:audit`는 `KRW 2,000,000,000` 매각 협상 기준의 실사 패키지를 검사합니다. -- `npm run security:evidence`는 보안 체크리스트와 reviewed security evidence만 단독 검증합니다. -- 기본 evidence path는 `artifacts/acquisition/revenue-evidence.json`, `artifacts/acquisition/transfer-evidence.json`, `docs/pilot-readiness-log.md`, 그리고 가장 최신 `artifacts/saleable-readiness//goal-audit.json`입니다. -- `NOEMA_PILOT_LOG_PATH`로 별도 production 파일럿 로그를 지정할 수 있습니다. -- ARR/LOI/weighted pipeline, production 유료 파일럿, IP/license/권한 이전성, saleable readiness가 모두 증빙되지 않으면 실패합니다. -- revenue/transfer evidence는 `owner`, `source_documents`, 기본 45일 이내 `updated_at` 메타데이터가 없으면 실패합니다. -- `acquisition-readiness-audit` 워크플로우(`.github/workflows/acquisition-readiness-scan.yml`)는 매일 `acquisition:manifest`와 `acquisition:audit`를 실행하고 evidence artifact를 보존합니다. -- 정기 `schedule` 실행은 production/acquisition evidence 누락을 `NOT_READY` status, warning, artifact로 남기는 감시 작업이며, `workflow_dispatch`와 로컬 `npm run acquisition:audit`는 동일한 누락을 실패로 유지합니다. -- submodule은 현재 사용하지 않으며, `docs/library-boundary-decision.md`의 split trigger가 충족될 때 npm workspace package 분리를 검토합니다. - -## 릴리스 검증 - -```bash -npm run release:verify -``` +- [SLA/지원 정책](./docs/sla-and-support.md) +- [Runtime readiness](./docs/runtime-readiness.md) +- [Distributed Rate Limiting](./docs/distributed-rate-limiting.md) +- [Orchestrator gateway consumer contract](./docs/orchestrator-gateway-consumer-contract.md) -운영/프로덕션 배포 경로는 `.github/workflows/cd.yml`에서 `npm run release:verify:strict`를 사용하며, 실패 시 KPI 증빙(`noema-kpi-evidence.json`)과 provenance(`exchange-30d.ndjson.provenance.json`)는 워크플로우 Artifact로 저장됩니다. +Maintainers and coding agents: start at +[`docs/internal/README.md`](./docs/internal/README.md) +(contributor and agent procedure: +[`docs/development/contributor-and-agent-procedure.md`](./docs/development/contributor-and-agent-procedure.md)). diff --git a/docs/internal/README.md b/docs/internal/README.md new file mode 100644 index 000000000..67acaac88 --- /dev/null +++ b/docs/internal/README.md @@ -0,0 +1,54 @@ +# Internal maintainer index + +This index is for maintainers and coding agents. Buyers and operators should +start at [`README.md`](../../README.md). The files listed here stay on disk; +they are not the customer/operator README surface. + +## Contributor and agent procedure + +- [Contributing](../../CONTRIBUTING.md) +- [Contributor and agent procedure](../development/contributor-and-agent-procedure.md) +- [AGENTS.md](../../AGENTS.md) +- [Review sandbox](../noema-agent-sandbox-plan.md) + +## Hourly loops + +- [Hourly commercial-readiness loop](../hourly-commercial-readiness-loop.md) +- [Hourly product development](../operations/hourly-product-development.md) +- [Hourly product-development prerequisites](../operations/hourly-product-development-prerequisites.md) + +## Saleable program and acquisition internals + +- [Saleable program readiness](../saleable-program-readiness.md) +- [Saleable program goal registry](../saleable-program-goal-registry.md) +- [Goal completion audit](../goal-completion-audit.md) +- [Acquisition readiness](../acquisition-readiness-2b.md) +- [Buyer due diligence index](../buyer-due-diligence-index.md) +- [Buyer pitch deck outline](../buyer-pitch-deck-outline.md) +- [Transfer readiness plan](../transfer-readiness-plan.md) +- [Library boundary decision](../library-boundary-decision.md) +- [Pilot readiness checklist](../pilot-readiness-checklist.md) +- [Release readiness audit](../release-readiness-audit.md) +- [Pricing draft](../pricing-draft.md) +- [Terms draft](../terms-draft.md) + +## KPI collect and readiness-scan internals + +Operator-facing SLA numbers stay in +[SLA/지원 정책](../sla-and-support.md). The recipes below are maintainer +evidence collection, not buyer documentation. + +- [관측성 KPI](../observability-kpi.md) +- `npm run kpi:compute` / `kpi:check` / `kpi:alerts` / `kpi:verify` +- `npm run kpi:collect` writes `exchange-30d.ndjson` plus + `exchange-30d.ndjson.provenance.json`. Strict KPI requires + `sourceKind=production`, `sourceId`, `records`, and `collectedAt`. +- `npm run production:preflight` fail-fast-checks production URL and KPI + collect inputs; it does not create evidence. +- `npm run readiness:audit` and `.github/workflows/readiness-scan.yml` +- `npm run acquisition:manifest` / `acquisition:audit` and + `.github/workflows/acquisition-readiness-scan.yml` +- `npm run security:evidence` + +Do not copy these loops, sale-process audits, or agent procedures back into +`README.md`. From dacce76d0687548efd6e49843eea1af40135d689 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 31 Aug 2026 20:09:31 +0900 Subject: [PATCH 3/3] docs(readme): remove unused app slug variable --- README.md | 1 - test/readme-customer-facing.test.ts | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 113e9067b..4f6ceb585 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,6 @@ Public Worker vars (defaults in `wrangler.toml`): | `ALLOWED_WORKFLOW_REPOSITORY` | `ContextualWisdomLab/.github` | Trusted workflow repo | | `ALLOWED_WORKFLOW_REF_PREFIX` | `ContextualWisdomLab/.github/.github/workflows/noema-review.yml@refs/heads/main` | Exact trusted workflow ref (name kept; matching is exact, not prefix) | | `GITHUB_API_BASE` | `https://api.github.com` | GitHub Cloud API origin | -| `GITHUB_APP_SLUG` | `noema` | GitHub App slug | | `NOEMA_RATE_LIMIT_PER_MINUTE` | `60` | `/exchange` fixed-window budget | | `NOEMA_OIDC_JWKS_CACHE_TTL_SECONDS` | `300` | OIDC JWKS cache | | `NOEMA_INSTALLATION_CACHE_TTL_SECONDS` | `600` | Installation-id cache | diff --git a/test/readme-customer-facing.test.ts b/test/readme-customer-facing.test.ts index f89e71582..2de4f62fe 100644 --- a/test/readme-customer-facing.test.ts +++ b/test/readme-customer-facing.test.ts @@ -35,6 +35,7 @@ describe("README stays customer/operator facing", () => { }); it("does not keep bot-manual leftover in README", () => { + expect(readme).not.toContain("GITHUB_APP_SLUG"); for (const leftover of [ "CloudAgent", "OpenCode 1.17.13",