diff --git a/docs/doctoring/gap-baseline-g17-g22-evidence-2026-09-02.md b/docs/doctoring/gap-baseline-g17-g22-evidence-2026-09-02.md new file mode 100644 index 0000000000..067f893d9a --- /dev/null +++ b/docs/doctoring/gap-baseline-g17-g22-evidence-2026-09-02.md @@ -0,0 +1,69 @@ +# G-17 through G-22 evidence and decision trace + +Status: Proposed evidence for PR #1696. This note is not production authority until the PR merges to protected `main`. + +## Scope + +This doctoring note records the evidence boundary behind G-17 through G-22 in `docs/product-technical-gap-baseline.md`. It distinguishes observed repository state from organization-level engineering requirements so that a later Agent can revalidate each claim without treating an open PR as released authority. + +The canonical repository policy path is [`docs/product-goal-directive.md`](../product-goal-directive.md). `ContextualWisdomLab/.github#1692` is the single-writer Draft that carries **CWL DEVELOPMENT PHILOSOPHY v2026-09-02B** into that canonical path and its doctoring record; until #1692 merges, that revision remains Proposed rather than protected-main production authority. PR #1696 therefore does not create or retain a second governing-policy file. Its G-17 through G-22 evidence remains Draft/Proposed and must not merge ahead of the canonical policy prerequisite if a row depends on v2026-09-02B wording that is not yet present on protected `main`. + +## Exact-head evidence snapshot + +- `ContextualWisdomLab/.github` protected `main`: `5935c8153722fe6b53bafd579b74f8f097303959` after PR #1715 at this repair pass. Branch protection still requires the established security, coverage, Noema, and OpenCode contexts. +- Canonical policy single writer: `ContextualWisdomLab/.github#1692`, Draft, exact head `4430864470a4ddb7a8c1692e3ad28708e37d47b5` at this repair pass. It changes `docs/product-goal-directive.md` plus `docs/doctoring/product-goal-directive.md` and explicitly carries the 2026-09-02B revision without introducing a competing authority path. +- `ContextualWisdomLab/contextual-orchestrator` protected `main`: `8839081659df587b19642be17b9114f9dee8b666`. +- `ContextualWisdomLab/contextual-orchestrator#1017`: open, not merged, exact head `fe043f4e6db8b24a6ab719fc5801bbbf40e046ae` at the prior owner audit. Its provider-name endpoint repair therefore remains **Proposed** and must not be described as protected-main production authority. The open PR also contains adjacent routing-category/naming work; consumers must wait for an immutable released owner version rather than copying its source. +- Current protected-main search still finds provider identity used in provider-specific telemetry/discovery code such as `contextual_orchestrator/openrouter_uptime.py`. That is not automatically a routing-policy violation: the violation criterion is provider identity controlling selection, endpoint rewrite, or failover where a declared capability should do so. Each match must be classified at its actual responsibility boundary. +- Protected-main persistence evidence still includes legacy one-word schema vocabulary in the orchestration persistence fixtures (`seq`, `kind`, `key`, `payload`) and the agent-pool contract still exposes one-word fields such as `priority`/`disabled`. G-22 is therefore a migration/contract gap, not permission to rename storage destructively. + +## G-18 — model execution timeout versus transport failure + +CWL DEVELOPMENT PHILOSOPHY v2026-09-02B, being integrated through the canonical [`docs/product-goal-directive.md`](../product-goal-directive.md) single-writer PR #1692, requires the default **model execution timeout** across application, Agent, and Gateway to be `null`; a reasoning, streaming, or tool-call operation must not be terminated merely because elapsed model time crossed a generic ceiling. It separately requires provider communication failure to terminate upstream and requires attribution among user cancellation, provider termination, and an explicitly configured administrator timeout. + +These are different failure domains. A `null` model-execution deadline does **not** require retaining a dead socket forever. Transport implementations must observe provider/connection termination and propagate communication failures; connection lifecycle and liveness handling remain transport responsibilities. RFC 9112 explicitly separates HTTP connection failures/timeouts and graceful connection closure from application semantics, and does not require either endpoint to have a fixed persistent-connection timeout. Consequently, the repair criterion is: + +1. no implicit elapsed-time ceiling for a healthy, progressing model operation; +2. provider/network termination or communication failure propagates immediately and releases resources; +3. an administrator may configure a model-specific timeout, with get/set/clear/restore, units, priority/inheritance, validation, and audit; +4. cancellation cause is observable as user cancel, provider end/failure, or configured administrator timeout; +5. clearing an administrator timeout restores the inherited/null model-execution policy rather than inventing a paid or hidden fallback. + +This resolves the apparent contradiction between long-running inference and resource safety without weakening the governing no-elapsed-time-termination rule. + +## G-19 — p95 <= 20 ms is an internal SLO, not an external universal threshold + +The `p95 <= 20 ms` requirement is an explicit ContextualWisdomLab engineering SLO in CWL DEVELOPMENT PHILOSOPHY v2026-09-02B, whose canonical repository integration is `ContextualWisdomLab/.github#1692` at [`docs/product-goal-directive.md`](../product-goal-directive.md). It is **not** claimed to be a universal HCI standard or a threshold derived from the cited papers. Peer-reviewed latency research instead supports the narrower premise that interaction latency below the traditional 100 ms guideline can still be perceptible and affect interaction; Forch et al. measured approximately 60 ms perception thresholds in a simple mouse task, while Attig et al. reviewed evidence that sub-100 ms latency can matter. + +Accordingly, G-19 requires each UI-owning product to define the measured page/action boundary, workload, sample design, environment, cold/warm-cache policy, and failure denominator, then prove the organization SLO with executed k6/E2E evidence. The current central baseline records the absence of such evidence **in this audit ledger**; it is not an exhaustive proof that no repository anywhere has ever run a latency test. A product that has current executed evidence should link its exact head/run and make the gap row narrower rather than suppress the SLO. + +## G-20 — i18n topology is a governance requirement + +DB-backed, versioned translation resources; screen-key-scoped fetch/cache; separation of UI translations from ontology labels; and review/approval/deploy/rollback authority are organization architecture requirements in CWL DEVELOPMENT PHILOSOPHY v2026-09-02B, whose canonical repository integration is `ContextualWisdomLab/.github#1692` at [`docs/product-goal-directive.md`](../product-goal-directive.md). They are not presented as a W3C mandate. The gap is that this central baseline currently has no verified canonical-owner release/API evidence for that shared responsibility. Until an owner is verified and released, products preserve the boundary with ports/ACLs/test doubles and do not copy an unreleased owner source tree or download a full browser catalog as a workaround. + +## G-21 — Rust-first scope is hot-path and risk based + +The Rust-first rule in CWL DEVELOPMENT PHILOSOPHY v2026-09-02B, whose canonical repository integration is `ContextualWisdomLab/.github#1692` at [`docs/product-goal-directive.md`](../product-goal-directive.md), does not authorize a wholesale rewrite of every Python orchestration module. The governing scope is mathematical/psychometric/EDA/data-science core and performance/security-critical runtime, including vector/matrix algebra, token size, CPU multithreading, GPU work, and other measured hot paths. Python remains allowed only for a validated Python-only ML runtime without practical Rust parity, with an ADR that records evidence, bounded scope, and removal conditions. G-21 therefore calls for profiling and boundary identification before migration; an unmeasured `orchestrator.py` rewrite would itself violate the policy. + +## G-22 — schema migration safety + +The two-semantic-word naming rule in CWL DEVELOPMENT PHILOSOPHY v2026-09-02B, whose canonical repository integration is `ContextualWisdomLab/.github#1692` at [`docs/product-goal-directive.md`](../product-goal-directive.md), applies to organization-owned DB objects and fields, but migration must preserve persisted data and released consumer contracts. Repair therefore requires a RED naming/migration contract first, an item-safe migration or compatibility layer, GREEN owner CI, and only then an immutable owner release and consumer bump. Existing one-word external/released boundary names are translated at the anti-corruption boundary until the owner version changes; they are not silently rewritten in consumers. + +## Revalidation checklist + +Before merging or later marking any row complete: + +1. Re-fetch protected `main` for both `.github` and each canonical owner. +2. Re-fetch the exact PR head, reviews/threads, required checks, and release/tag evidence; an open PR remains Proposed. +3. Verify `ContextualWisdomLab/.github#1692` has merged the 2026-09-02B text into canonical `docs/product-goal-directive.md` before merging any #1696 row whose requirement depends on that revision. +4. Re-run the repository/code/API search used by the row and record the exact head/module or remove the claim if it no longer reproduces. +5. For G-19/G-20, replace central "evidence not recorded" wording with concrete owner evidence as soon as a current run/release exists. +6. For G-18/G-21/G-22, land owner RED -> fix -> integrated GREEN -> immutable release -> consumer version bump; do not copy branch source into consumers. + +## References + +Attig, C., Rauh, N., Franke, T., & Krems, J. F. (2017). System latency guidelines then and now—Is zero latency really considered necessary? In D. Harris (Ed.), *Engineering psychology and cognitive ergonomics: Cognition and design* (Lecture Notes in Computer Science, Vol. 10276, pp. 3–14). Springer. https://doi.org/10.1007/978-3-319-58475-1_1 + +Fielding, R., Nottingham, M., & Reschke, J. (Eds.). (2022). *HTTP/1.1* (RFC 9112). Internet Engineering Task Force. https://www.rfc-editor.org/rfc/rfc9112.html + +Forch, V., Franke, T., Rauh, N., & Krems, J. F. (2017). Are 100 ms fast enough? Characterizing latency perception thresholds in mouse-based interaction. In D. Harris (Ed.), *Engineering psychology and cognitive ergonomics: Cognition and design* (Lecture Notes in Computer Science, Vol. 10276, pp. 45–56). Springer. https://doi.org/10.1007/978-3-319-58475-1_4 diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 29acdfeecc..96a04291c2 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -72,6 +72,45 @@ flowchart LR Merge --> Control ``` +### 2.4 Ecosystem canonical-owner map + +각 core 기능은 정확히 하나의 canonical-owner 저장소가 소유하며, 소비 저장소는 해당 기능을 복제·우회·배제하지 않는다 ("no duplicating immature core in consumers" 원칙 — 이 섹션(§2.4)이 정의하는 소유권 규칙이며, 실행 루프를 다루는 §5와는 별개다). 미성숙한 core가 필요하면 소비 저장소는 오너 저장소에 RED 테스트 → 수정/기능/문서/릴리스를 개발해 통합 CI가 GREEN이 될 때까지 진행한 뒤 소비측 고정 버전을 올린다. 배제는 경계가 실제로 잘못되었거나 공통 수요가 없음을 ADR로 정당화할 때만 허용된다. + +core foundation은 전 제품의 공통 설치물이 아니라, 여러 제품에서 반복되는 책임 하나를 한 저장소가 canonical owner로서 독립 배포·versioned contract로 제공하는 선택형 control plane·service·library다. 보호 브랜치의 문서·API/schema·release evidence로 역할·성숙도를 확인하며, open PR은 아직 Proposed 상태로 취급한다. + +| 분류 | Canonical owner | 소유 core 기능 | +|---|---|---| +| 조직·계약 | [`ContextualWisdomLab/.github`](https://github.com/ContextualWisdomLab/.github) | 공통 CI·review·security·release | +| 조직·계약 | [`ContextualWisdomLab/enterprise-architecture-core`](https://github.com/ContextualWisdomLab/enterprise-architecture-core) | 전사 Context Map·architecture decision | +| 조직·계약 | [`ContextualWisdomLab/context-graph-contracts`](https://github.com/ContextualWisdomLab/context-graph-contracts) | assertion·event·schema·fixture·conformance (domain truth·Ubiquitous Language는 제품에 남긴다) | +| 의미·데이터 | [`ContextualWisdomLab/ConceptWeave`](https://github.com/ContextualWisdomLab/ConceptWeave) | ontology·semantic-layer 생성·검증·release | +| 의미·데이터 | [`ContextualWisdomLab/semantic-data-portal`](https://github.com/ContextualWisdomLab/semantic-data-portal) | catalog·governance·검색·제공 | +| 의미·데이터 | [`ContextualWisdomLab/EmbedRelay`](https://github.com/ContextualWisdomLab/EmbedRelay) | embedding identity·migration | +| 의미·데이터 | [`ContextualWisdomLab/mhtml-etl-gateway`](https://github.com/ContextualWisdomLab/mhtml-etl-gateway) | MHTML 검사·schema proposal·load lineage | +| AI·운영 | [`ContextualWisdomLab/contextual-orchestrator`](https://github.com/ContextualWisdomLab/contextual-orchestrator) | provider discovery·model capability·routing/delegation/verification·admin | +| AI·운영 | [`ContextualWisdomLab/noema`](https://github.com/ContextualWisdomLab/noema) | 공유 agent runtime·GitHub review agent·GitHub Actions OIDC 단기 repository capability·exact-revision evidence | +| AI·운영 | [`ContextualWisdomLab/pg-llm-batch`](https://github.com/ContextualWisdomLab/pg-llm-batch) | DB token count·batch 처리 | +| Identity·보안·runtime | [`ContextualWisdomLab/keyverse`](https://github.com/ContextualWisdomLab/keyverse) | identity·federation·token (유일한 identity ledger; Keycloak 기반 인증 백엔드) | +| Identity·보안·runtime | [`ContextualWisdomLab/EgressWeave`](https://github.com/ContextualWisdomLab/EgressWeave) | 안전한 outbound HTTP | +| Identity·보안·runtime | [`ContextualWisdomLab/OriginWeave`](https://github.com/ContextualWisdomLab/OriginWeave) | governed browser | +| Identity·보안·runtime | [`ContextualWisdomLab/pingora-gateway`](https://github.com/ContextualWisdomLab/pingora-gateway) | Rust edge | +| Identity·보안·runtime | [`ContextualWisdomLab/quarantine-sandbox-runtime`](https://github.com/ContextualWisdomLab/quarantine-sandbox-runtime) | 격리[^sandbox-ownership] | +| Identity·보안·runtime | [`ContextualWisdomLab/appguardrail`](https://github.com/ContextualWisdomLab/appguardrail) | scan·SARIF·remediation | +| Identity·보안·runtime | [`ContextualWisdomLab/wardnet`](https://github.com/ContextualWisdomLab/wardnet) | gateway·WAF·IDS·SOC | +| 재사용 기능 | [`ContextualWisdomLab/fast-mlsirm`](https://github.com/ContextualWisdomLab/fast-mlsirm) | IRT·MLSIRM | +| 재사용 기능 | [`ContextualWisdomLab/TEPP`](https://github.com/ContextualWisdomLab/TEPP) | 다국어·시간·event·relation 측정 | +| 재사용 기능 | [`ContextualWisdomLab/RankWeave`](https://github.com/ContextualWisdomLab/RankWeave) | retrieval fusion·evaluation·통계 비교·tuning·TREC | +| 재사용 기능 | [`ContextualWisdomLab/ThreadWeave`](https://github.com/ContextualWisdomLab/ThreadWeave) | JWZ/RFC 5256 threading | +| 재사용 기능 | [`ContextualWisdomLab/inkspan`](https://github.com/ContextualWisdomLab/inkspan) | editor·serialization·문서 변환 | +| 재사용 기능 | [`ContextualWisdomLab/DiagramWeave`](https://github.com/ContextualWisdomLab/DiagramWeave) | diagram patch·render·CLI·LSP | +| 도메인 제품 소비 | [`naruon`](https://github.com/ContextualWisdomLab/naruon) / [`LineageWeave`](https://github.com/ContextualWisdomLab/LineageWeave) / [`psychometrics-commons`](https://github.com/ContextualWisdomLab/psychometrics-commons) / [`disksage`](https://github.com/ContextualWisdomLab/disksage) / [`PolicyWeave`](https://github.com/ContextualWisdomLab/PolicyWeave) / [`CalendarWeave`](https://github.com/ContextualWisdomLab/CalendarWeave) / [`supply-chain-control-plane`](https://github.com/ContextualWisdomLab/supply-chain-control-plane) | core foundation을 소비하는 도메인 제품 저장소; domain truth·Ubiquitous Language는 여기 남고 위 core로 옮기지 않는다 | + +owner가 미성숙하거나 API가 없어도 소비 저장소는 복제·우회하지 않는다. owner 저장소에서 RED test → 기능/문서/release를 개발해 CI GREEN과 immutable version을 낸 뒤 소비측이 채택한다. 그 전에는 port·ACL·feature flag·test double로 경계를 지키고 owner의 source·DB·임시 branch를 직접 읽지 않는다. + +이 지도는 저장소 신설·기능 배치 결정의 기준이며, 이름이 아니라 제품 책임·재사용 경계·문서·구현·소비 관계로 저장소를 선택한다(§1). 표에 없는 신규 core 필요가 확인되면 이 표에 행을 추가하고 해당 오너 저장소에 ADR을 남긴다. + +[^sandbox-ownership]: **미해결 소유권 충돌 (Devin Review, based_on_repo_rules).** 이 표는 격리를 `quarantine-sandbox-runtime`의 단일 책임으로 배정하지만, mandatory master context [`docs/CWL-MASTER-CONTEXT.md:36`](https://github.com/ContextualWisdomLab/.github/blob/main/docs/CWL-MASTER-CONTEXT.md)은 noema를 "agent runtime... + the lightweight quarantine sandbox"로 설명해 동일 책임을 다른 저장소에도 배정한다. 두 출처 중 하나가 오래됐거나, noema의 것은 자체 실행 경로용 경량 내장 격리이고 `quarantine-sandbox-runtime`은 조직 전체가 소비하는 전용 격리 서비스라는 실제 계층 분리가 있을 수 있다 — 이 문서는 그 구분을 확인할 권한이 없으므로 단정하지 않는다. 해결 전까지: (1) 새 소비자는 두 구현 중 하나를 임의로 골라 병행 구현하지 않는다, (2) 실제 소유자와 noema·wardnet·naruon·quarantine-sandbox-runtime 간 관계(추출·release된 contract 여부)를 확정하는 ADR을 오너 저장소에 남긴다, (3) 확정 후 `docs/CWL-MASTER-CONTEXT.md`와 이 표를 동일 PR에서 함께 갱신한다. + ## 3. Gap register 우선순위는 구매자 체감, 보안/증거 위험, 선행 의존성 순서다. @@ -94,6 +133,12 @@ flowchart LR | G-14 | release/changelog/version 증거가 각 PR에 분산되고 현재 central repo 보호 main의 release candidate가 명확하지 않다 | 운영자는 어떤 기능이 supportable release인지 확인할 수 없다 | merge 후 release readiness ledger, CHANGELOG, semantic version/tag, rollback/operability evidence를 함께 갱신한다 | | G-15 | 첨부파일 처리 경계가 제품별로 다르고, 1MB 상한은 업무 데이터와 맞지 않으며 미지원 MIME/컨테이너가 parser registry에서 명시적으로 pending/quarantine 되는지 확인되지 않았다. 현재 20MB 초과 파일 가능성과 PDF/HWP/HWPX·이미지·압축파일의 parse/sidecar 흐름을 하나의 exact contract로 묶지 못했다 | 큰 업무 첨부를 거부하거나 파싱 실패를 조용히 잃으면 고객의 메일·문서 업무가 중단된다 | naruon/newsdom-api 소유 PR에서 streaming upload, configurable bounded limit above 20MB, MIME sniffing, parser capability registry, quarantine/retry, source-position provenance, and ADR를 추가하고 size/unsupported-type/zip-bomb tests를 required evidence로 만든다 | | G-16 | Required Pingora policy treated a changed documentation PNG screenshot as UTF-8 runtime evidence | Valid UI evidence blocked otherwise valid product PRs before policy evaluation | This branch verifies bounded PNG magic before exemption while runtime paths and malformed assets continue to fail closed; protected-main delivery remains the release gate | +| G-17 | `contextual-orchestrator`의 `proxy_capability()`가 이미지-생성 엔드포인트를 `agent.provider_name == "openrouter"` 리터럴 비교로 재작성했다(provider 식별자를 라우팅 조건으로 하드코딩 — 확인·수정됨: ContextualWisdomLab/contextual-orchestrator#1017, `ModelAgent.image_generation_endpoint` 선언적 capability 필드로 교체). `scripts/ci/run_opencode_review_model_pool.sh`의 `is_nvidia_nim_candidate()`도 provider-prefix 문자열 비교로 같은 패턴을 갖고 있으나, 이는 NIM 직접 통신 제거 마이그레이션(§8 orchestrator/free 고정)으로 대체될 예정이라 별도 fix 대상에서 제외했다 | provider GROUP명이 라우팅/선택/failover 조건에 남아 있으면 신규 provider 추가·제거 시 코드 변경이 필요해지고, "표시/관리자 별칭"이라는 정책이 실제로 지켜지지 않는다 | ContextualWisdomLab/contextual-orchestrator#1017 병합 후, 조직 전체(§2.4 ownership map의 LLM 게이트웨이 소비 저장소 포함)에서 provider 이름 문자열 비교로 라우팅/선택/failover를 분기하는 잔여 코드를 grep-detect 회귀 테스트로 고정하고, `run_opencode_review_model_pool.sh`는 NIM 제거 PR에서 함께 정리한다 | +| G-18 | LLM 모델 타임아웃에 앱/에이전트/게이트웨이 전역 우선순위 상한이 존재하며, 모델별 조회/설정/해제/복원을 제공하는 관리자 웹 콘솔이 `contextual-orchestrator`에 없다. 취소 사유(사용자 취소/provider 종료/관리자 설정 타임아웃)를 구분해 귀속하는 계약도 없다 | 추론·스트리밍·툴콜링이 실제로 진행 중인데 경과 시간만으로 요청이 끊기면, 정상적으로 응답을 생성하던 고가 요청이 낭비되고 원인도 알 수 없다 | 기본값을 무제한/null로 바꾸고 통신 실패는 upstream provider 자체 timeout/error로만 종료되게 하며, `/admin` 콘솔에 모델별 타임아웃 view/set/clear/restore(단위, 우선순위, 상속, 입력 검증, 감사 추적)와 `/api/v1/*` 계약, 취소-사유 귀속 필드를 추가한다. 소유 저장소는 `contextual-orchestrator`다 | +| G-19 | 반복 웹 페이지 E2E 성능에 대해 예외 없는 p95 ≤20ms 하드 게이트와 표본 축소·느린 측정 배제·비현실적 캐시 예열 금지 조항이 어떤 UI 소유 저장소에도 k6/Playwright 등 executed 증거로 존재하는지 미확인이다 | 실측 없이 "빠르다"고 주장하면 실제 고객이 체감하는 지연이 방치된다 | UI 소유 저장소별로 페이지 단위 k6/Lighthouse-CI p95 측정을 CI 필수 증거로 추가하고, 위반 시 원인이 JS 번들 크기·힙·DOM·hydration·메인스레드 블로킹·GC 등 구조적이면 최적화가 아니라 프론트엔드 스택 교체를 ADR로 기록한다. Figma File ID N/A인 이 저장소는 게이트 정의만 문서화하고, 실측/교체는 각 UI repo 소유다 | +| G-20 | 번역(i18n)이 버전관리되는 DB 자원으로 관리되고, 서버/네이티브가 화면별 필요한 키만 캐싱해 가져오며, 전체 카탈로그를 브라우저에 내려보내지 않는 아키텍처가 어느 소비 저장소에도 확인되지 않았다. 번역 리뷰/승인/배포/롤백 API와 관리 UI를 소유하는 저장소도 없다 | 번역 파일/JS 번들 방식은 배포 없이 문구를 고칠 수 없고, 전체 카탈로그 다운로드는 SPA를 가정하지 않는 서버/네이티브 화면에서 낭비와 불일치를 만든다 | 기존 저장소 중 적합한 곳이 없으면 전용 신규 저장소(번역 DB, 화면별 key 캐시 API, 리뷰/승인/배포/롤백 API+관리 UI)를 신설하고 §2.4 ownership map에 추가한다. Keyverse는 인증 백엔드로만 유지하고 로그인/가입/복구 UI는 각 제품이 자체 구축한다 | +| G-21 | 수리과학/psychometrics/EDA core뿐 아니라 성능·안정성·보안이 중요한 모든 런타임 경로에서 Python이 기피 대상이 됐으나, 현재 `contextual_orchestrator/`의 다수 모듈(`orchestrator.py` 등 도메인 핵심)이 여전히 Python으로 구현되어 있고 각 예외에 대한 전용 ADR(제거 조건 명시)이 없다 | 속도·안정성·보안이 중요한 경로가 Python GIL/성능 한계에 계속 노출되고, 예외 기준이 문서화되지 않아 재검토 시점을 판단할 수 없다 | Rust 경계로 분리 가능한 hot path(예: token counting, redaction, cost ledger 연산)를 식별해 Rust API 경계로 전환하거나, Python-전용 ML 런타임 의존처럼 불가피한 경우 제거 조건을 명시한 전용 ADR을 `contextual-orchestrator`/`fast-mlsirm`/`TEPP` 등 소유 저장소에 남긴다 | +| G-22 | DB 객체명 규칙(두 단어 이상 snake_case 우선) 위반이 `contextual_orchestrator/orchestrator.py`의 `agent_pool`(단일어 컬럼 `priority`/`disabled`)과 `orchestration_records`(단일어 컬럼 `kind`/`key`/`payload`/`seq`) 테이블에 남아 있다. `conventions.require_object_name()`과 `tests/test_database_object_naming.py`는 테이블/인덱스/뷰/시퀀스/제약 이름만 검사하고 `CREATE TABLE` 본문의 컬럼명은 검사하지 않아 이 위반이 회귀 게이트를 통과했다 | 컬럼명 불일치가 누적되면 신규 기여자의 스키마 이해 비용이 커지고, 명명 규칙이 실제로는 부분적으로만 강제된다는 사실이 감춰진다 | 기존 sqlite 영속 상태를 깨지 않는 마이그레이션(컬럼 rename + 하위호환 읽기 경로 또는 명시적 1회성 migration script)을 설계하고, `test_database_object_naming.py`의 static-analysis 범위를 컬럼명까지 확장해 동일 위반의 재발을 막는다 | ## 4. 열린 PR live inventory @@ -2591,6 +2636,18 @@ Higgins, S. S., Crepalde, N., & Fernandes, L. (2021). Segmented multiplexity: A **Residual.** This closes the specific floating-image contribution from these three central workflows; it does not by itself guarantee the organization-wide Actions queue is fully drained, since other repositories' own workflows and any remaining unpinned central workflows may still request the floating image. Worth a follow-up sweep across the rest of `.github/workflows/` and sibling-repo workflows if queuing persists after this lands. +## 2026-09-02 provider-group-name hardcoding and routing-config naming audit: ContextualWisdomLab/contextual-orchestrator#1017 + +**Observed gap (now G-17).** An adversarial 5-agent audit of the "provider GROUP 명을 라우팅/선택/failover 조건으로 하드코딩하지 않는다" policy across `.github`, `noema`, and `contextual-orchestrator` confirmed one real, production-affecting violation: `contextual_orchestrator/orchestrator.py`'s `proxy_capability()` rewrote the image-generation endpoint path by comparing `agent.provider_name == "openrouter"` literally, in both its immediate-race (`_equivalent_race_members`) and sequential-failover call sites. Provider identity was doing routing work it is not supposed to do — it is a display/admin alias only. The same audit independently checked two adjacent suspects and found them **not** the same anti-pattern: `scripts/ci/zdr_policy.py`'s `PROVIDER_ZDR_SCOPE` is a legitimate, ADR-documented, intentionally fail-closed (`KeyError`-on-unknown-provider) compliance-attestation ledger — ZDR status is an inherent legal fact about a named vendor, not a discoverable technical capability, so a vendor key here is correct, not a violation. `scripts/ci/run_opencode_review_model_pool.sh`'s `is_nvidia_nim_candidate()` **is** a real instance of the same pattern, but is left unfixed here because it is already superseded by the separately tracked NIM-direct-communication removal migration (§8 of the standing directive; `orchestrator/free` pin work). + +**Fix.** ContextualWisdomLab/contextual-orchestrator#1017 (branch `fix/provider-endpoint-hardcoding-and-routing-config-20260902`, exact head `263cf0c9`) replaces the provider-name comparison with a declared `ModelAgent.image_generation_endpoint: str | None = None` capability field, round-tripped through `to_config()`/`from_dict()`, set explicitly via agent-pool config or discovery metadata rather than inferred from `provider_name` at request time — zero behavior change for any currently-configured agent, since the field must be set explicitly to opt into the dedicated endpoint. `test_provider_name_alone_never_rewrites_the_image_endpoint` locks the regression: an agent named `"openrouter"` with no declared field routes to the caller's requested endpoint unchanged. + +**Adjacent fix bundled in the same PR (KV naming convention), corrected after human review.** The same PR also fixes an unrelated but independently discovered naming-convention violation: `_ROUTING_CATEGORY`/`_EMBEDDING_CONFIG_CATEGORY` (both the single-word `"routing"`) in `batch_routing.py`/`cost_router.py`, plus a matching literal in `batch_job_registry.py`, violated the two-or-more-semantic-word KV category naming convention. A full-repo grep found **three** real production call sites (not the two an initial narrower pass found) plus three test files sharing the one category — splitting it (the naming fix an isolated look would suggest) would have silently orphaned already-persisted config across two categories instead of one. This entry originally claimed the rename to one unified `"routing_config"` name carried "zero orphaning risk"; PR reviewer `seonghobae` correctly identified that claim as false for the Postgres-backed production boundary — `pg_llm_batch.PostgresConfigStore` keys `com_config` by the literal `f"{category}.{key}"` SQL primary key, so the call-site rename alone *does* orphan any row a prior deployment already persisted under `routing.`: readers now asking for `routing_config.` get an exact miss and silently fall back to their hardcoded default instead of the operator's configured value. Fixed with an idempotent, additive-only backfill migration (`kv_config._migrate_legacy_categories`, run at every `get_config_store()` boot for the seven known keys) that copies a legacy `routing.` value forward only when no `routing_config.` value exists yet — an explicit new-category value always wins and is never overwritten, so an operator's post-backfill reconfiguration survives a later restart. Evidence: `tests/test_kv_config_store.py` gained four tests (backfill-from-legacy, new-value-precedence, idempotent-across-reconnects, in-memory-seed-path), three of which were confirmed to genuinely fail without the migration wired in (RED-before-GREEN, verified by temporarily disabling the migration call and re-running). A third, unrelated pre-existing regression (a hard top-level `import numpy` in `tests/test_psychometric_routing.py` breaking collection for the entire suite, since numpy/fast_mlsirm are genuinely optional lazy-imported dependencies of the production module they test) was fixed in the same PR as a side effect of full-suite validation, scoped via `pytest.importorskip` to the one test function that needs them. + +**Status.** PR opened as draft, `subscribe_pr_activity` armed; full suite green modulo two independently verified pre-existing/unrelated failures (a load-induced timing flake and an unrelated `test_spend_analytics.py` failure, both reproduced identically against unmodified `main` with this PR's changes stashed). Not yet merged — G-17's priority action (residual provider-name-string-comparison regression test across the ownership map's LLM-gateway consumers, and the `run_opencode_review_model_pool.sh` cleanup bundled into the NIM-removal PR) remains open until ContextualWisdomLab/contextual-orchestrator#1017 merges and a follow-up sweep runs. + +**New Gap rows.** This pass also added G-18 (LLM model-timeout admin console — confirmed entirely missing, not merely buggy), G-19 (p95 ≤20ms E2E performance gate — no executed k6/Lighthouse-CI evidence found in any UI-owning repo), G-20 (i18n as a versioned DB resource — no such architecture found; may need a new dedicated repo), G-21 (Rust-preference tightening — `contextual_orchestrator/orchestrator.py` and siblings remain Python with no per-exception ADR), and G-22 (two confirmed DB column-naming violations in `contextual_orchestrator`'s `agent_pool`/`orchestration_records` tables, invisible to the existing table/index/view/sequence/constraint-only static-analysis test) to §3, and a new §2.4 ecosystem canonical-owner map transcribing the standing directive's ~25-repository ownership assignment, so future work chooses a repository by product-responsibility boundary rather than by name. + ## 2026-09-02 GitHub Actions review sidecar pool pinned to `orchestrator/free`; `auto` removed as an accepted value **Problem.** `scripts/ci/contextual_orchestrator_review_sidecar.sh` — the script every central required review workflow (Strix, OpenCode Review, Noema Review, the PR-review autofix sidecar) provisions to talk to `contextual-orchestrator` — read an operator-settable `CONTEXTUAL_ORCHESTRATOR_POOL` environment variable, defaulted it to `free`, and validated it against exactly two accepted values: `free` or `auto` (`case "$orchestrator_pool" in free|auto) ...`). `auto` is a real, load-bearing value one layer down: `scripts/ci/contextual_orchestrator_review_launcher.py --pool auto` admits *priced* discovered routes as a fallback stage once the free pool is exhausted (`build_zdr_prioritized_catalog(..., pool="auto")`), by design, for callers that want that behavior. Nothing in this repository's own review-provisioning code path currently sets `CONTEXTUAL_ORCHESTRATOR_POOL=auto` — the only workflow that sets the variable at all, `strix.yml`, sets it to `free`; every other central review workflow simply relies on the script's own `:-free` default — so this was not a live incident, it was an unaudited, structurally-reachable escape hatch: a future edit to any of the four workflows above, or a manually-triggered `workflow_dispatch` with a custom env override, could set `CONTEXTUAL_ORCHESTRATOR_POOL=auto` and the sidecar would accept it silently, with no cost ceiling, no budget/authorization gate, and no reviewer visibility that priced models were now in scope for a required check.