# Plan Review Panel — insuniverse-docs-40-runtime-vendor-legal-m4

**Plan**: `.claude/plans/insuniverse-docs-40-runtime-vendor-legal-m4.plan.md` · **Plan version**: `sha256:8f94905be762e7368b8decce140ee5f27872cfb61aaf16b9323ac2ce589f69ca`
**Verdict**: `divergent` via `multi-agent`
**Quorum**: 4/3 responses · 4 distinct roles (of 4 fielded) · passed=false
**Layers**: L1 converged · L2 divergent · L3 not fired

> Reason: L2 quorum not satisfied: 2 blocking finding(s): architect/HIGH, architect/FAIL — MCCP_REVIEW_SINGLE_PASS=deadline_pressure 로 진행한다. verdict는 divergent 그대로 봉인된다.

## Findings

| Perspective | Severity | Claim | Evidence |
|---|---|---|---|
| architect | HIGH | Gate #2d prevents Axis 2 (code-only policy) omissions by scanning configuration.ts vendor keys. However, only 3 of 10 Axis 2 policies have configuration.ts anchors (30% coverage). The other 7 policies (70%) have no configuration.ts references, leaving them unverifiable via gate #2d. | Axis 2 table lines 179-190: policies WITH configuration.ts anchors: `treat-provider:__pool__:membership` (line 181), `treat-provider:__car__:scope` (line 183), `messaging:fcm:credentials` (line 185). Policies WITHOUT: `treat-provider:__pool__:routing`, `payment:__env__:mode`, `messaging:fcm:token-lifecycle`, `messaging:fcm:exception`, `messaging:push.scheduler:cron`, `messaging:aligo:local-mock`, `messaging:__bridge__:platform`. Gate #2d validation (line 359-360) scans: 'configuration.ts 의 벤더성 설정 블록 키'. Plan claims at line 203: '이 검사가 보지 못하는 형태(설정 블록을 안 쓰는 연동)는 출력에 사각으로 고지한다' — but treats this as minor blind spot, not as 70% of Axis 2. |
| architect | MEDIUM | Axis 2 (10 code-only policies) is hand-authored without machine derivation, yet the plan claims at line 249 'policies are derived and not hand-made' (정책 id 는 손으로 만들지 않는다). This is contradictory: Axis 1 is machine-derived from vendor-behaviors.yaml, but Axis 2 policies are human-identified from code inspection and hard-coded in the plan table (lines 179-190). | Line 249: '정책 id 는 §정책 층 로케이터 축 1·2 에서 유도하고 손으로 만들지 않는다.' Lines 177-190: Axis 2 is 'plan-declared' (계획서 선언 10건) 'policies that index has no layer for, code-only'. Lines 179-190: 10 policies hard-listed with hand-identified anchors. Plan admits incompleteness at line 500 ('축 2 가 코드 전용 정책을 하나도 빠뜨리지 않았다 — (미검증) 열린 집합이라'). |
| architect | MEDIUM | If Axis 2 is discovered wrong during implementation, the plan is structurally stuck: Axis 2 table is embedded in both plan body and harness constant; plan body cannot be edited (receipt binding, line 201); but harness can be edited, creating divergence. Gate #2b requires plan table and harness constant to match exactly; fixing Axis 2 in harness breaks gate #2b. | Line 201: 'Axis 2 표가 틀린 것으로 드러나면 계획서를 고치지 않는다... 중단 후 /mccp:plan 재진입해야 한다.' Line 242 Task 0: '축 2 의 10건은 하네스 안에 상수 표로 박고'. Line 358-359 gate #2b: '계획서 축 2 표 재파싱 <D>행 == 하네스 상수 표 <D>행'. Line 205: '#2b 는 계획서 표와 하네스 상수가 같은 창에서 저작된 두 사본이라 둘이 같이 틀리면 일치한다' — acknowledges but doesn't mitigate binding. |
| architect | MEDIUM | Acceptance item line 499 marks gate #2d ('configuration block keys bounded to index vendors ∪ Axis 2 ∪ NOT Building') as verifiable, but Acceptance item line 500 marks Axis 2 completeness as `(미검증)`. These are contradictory: if Axis 2 completeness is unverifiable, gate #2d cannot verify configuration key coverage since 70% of Axis 2 policies don't touch configuration. | Line 499: '설정 블록 벤더성 키 전건이... 귀속된다 — 게이트 `#2`' (presented as verifiable constraint). Line 500: '축 2 가 코드 전용 정책을 하나도 빠뜨리지 않았다 — `(미검증)`' (marked unverified). Combined: gate #2d can verify that configuration keys are accounted for, but cannot verify that all code-only policies (70% of which lack config keys) are present. |
| invariant | MEDIUM | Preflight validation of document-dependent checks is one-time-only; after documents are created in Task 1, no gate re-verifies that checks #1-14 actually fail when their required documents are absent. | Line 242 describes preflight as '일회용' (one-time-only), and line 302 shows `all` subcommand excludes preflight ('전사 오기를 Task 8 까지 미루지 않고 하네스가 서는 자리에서 잡는다'). After Task 1, if check #3 were accidentally modified to pass on missing documents, checks #0 through #15 in subsequent tasks would not detect this regression. |
| invariant | LOW | Axis 2 completeness (detecting all code-only policies) is marked as unverified and relies on manual review, creating a documentation-gate asymmetry where the plan asserts completeness in Summary (line 11) without a corresponding automated gate. | Line 203 admits 'Acceptance 의 완전성 항목은 `(미검증)` 으로 남는다' and Acceptance line 500 marks it explicitly unverified. Contrast with Summary line 9 which claims '이 창의 산출물' includes all 35 index-derived policies + 10 code-only policies. This creates a gap between stated scope and verified scope. |
| invariant | LOW | Frozen tree pointer (server 4d798f55, site 767d1e44) used as third independent source for validating policies is static at plan authoring time. If frozen tree is later found to be stale or mismatched against actual code branches, no gate catches this until checks #5 or #8 run and find missing anchors. | Lines 57, 212-214 reference fixed commit SHAs for frozen trees. The plan assumes these trees are correct at execution time but does not include a gate that verifies frozen tree pointers still match the intended code state (e.g., by checking against git refs or manifest). Check #0 validates prior harnesses pass but does not independently verify frozen tree validity. |

## Refutation attempted

| Perspective | Verdict | What was attacked |
|---|---|---|
| architect | fail | Examined: (1) whether Axis 1 and 2 are truly "derived" vs hand-authored — found Axis 2 is hand-curated despite claim otherwise. (2) gate #2d's coverage by analyzing Axis 2 table (lines 179-190) for configuration.ts anchors — found only 30% covered. (3) receipt binding against mid-implementation Axis 2 correction — confirmed plan forbids body edits but allows harness edits, creating divergence risk. (4) coherence between gate #2d verifiability claim and Axis 2 completeness unverifiability marking — found contradiction in Acceptance section. Did NOT find: evidence that gates #5 or #8 can catch omissions of non-configuration-block policies; evidence that the 70% blind spot is acceptable; counter-evidence that the design handles the binding trap. |
| security | pass | Examined trust boundaries: DB vs env fallback hierarchy (observations 5-6), DB failure modes (fail-closed on auto-car scoping). Checked secret/credential handling: plan explicitly prohibits transcribing credential values, numeric constants, or vendor codes; gates #6 and #13 verify zero occurrences. Analyzed partial-state trust: status fields are auto-derived from frozen tree scanning (gates #3, #5), not manually chosen—mismatch triggers exit 1. Traced path handling: git cat-file calls use execFileSync array args (prevents injection), anchors validated against frozen trees for path/line existence (#8). Examined the documented dual-polarity issue on enableTreatCar (observation 6): plan lists both anchors without judgment, correctly notes this is a code defect not the plan's to fix. Verified no new dependencies added (gate #11), no new attack surface, no leakage of secrets or machine-specific paths. All validation gates re-verify rather than trust input. Could not construct concrete input→consequence path to HIGH/CRITICAL defect. |
| test | pass | Attacked the plan's load-bearing claims systematically: 1. **Axis 2 table (10 code-only policies) falsifiability**: Verified that check#2b re-parses the plan table and compares to harness constants; checks #5 and #8 independently re-scan frozen tree for gate kinds and anchor interpretation, creating three independent verification sources against collusion. Circular copy problem is addressed. 2. **Dual unknown-status claim**: Verified check#3a explicitly requires both data-dependent-unknown ≥1 AND conditional-unknown ≥1, with calculation showing 8 + 37 instances respectively (treat fallback=both gate, 3 db-only axis 2 policies guaranteeing data-dependent; timeout/env/exception/normalization layers guaranteeing conditional). If either count is 0, execution halts. 3. **Preflight document-dependency verification**: Confirmed check#0-15 list shows 13 document-dependent checks (1-10, 12-14) that should fail without files, and 3 document-independent checks (0, 11, 15) that should pass. Preflight's ability to catch a check passing without documents is testable. 4. **PRD update timing**: Noted that check#15 runs in Task 0 Validate and assumes `/mccp:plan` has already updated the PRD Delivery Milestones row. While this depends on mccp framework behavior outside this plan's scope, the test itself (git diff HEAD showing hunk in correct table section) is falsifiable. 5. **M1/M2/M3 regression assumptions**: Confirmed check#0 explicitly runs all three harnesses and demands exit 0 + complete check execution (numerator==denominator, denominator ≥ declared count). Not just "passed" but "completed all checks". 6. **Status derivation rule hardness**: Traced the status ← gate-kind derivation rules (lines 194-197, 207) against axis 1 layer definitions (Observation 3) and axis 2 table (lines 179-190). Structure is determinate given vendor-behaviors.yaml structure. 7. **Acceptance items coverage**: Scanned all 24 acceptance criteria (lines 477-502); 21 are tied to named gates #0-#15, 3 are marked (미검증) as appropriately unverifiable (axis 2 completeness as open set, PRD evidence re-measurement as manual, onboarding question coverage as end-user judgment). 8. **Test ordering and dependencies**: Verified check#1 (lint) runs only at Task 7 after all content created (avoiding premature failure), while structural checks (#2-#6) run as documents materialize. No circular wait detected. 9. **Negative case recording**: Confirmed Validation block includes 음성 (negative) case expectations for each check, showing what failure looks like (e.g., check#3 failing if status doesn't match derivation, check#5 failing if gate kind re-derivation differs from table). |
| invariant | pass | Investigated fail-closed gate erosion by tracing check #0 (prior harness regression), #2b (circular dependency on plan table + harness constants), #5/#8 (frozen tree scanning), and #15 (PRD diff validation). Examined preflight's one-time guard against checks passing when documents don't exist, verifying it runs in Task 0 but is not repeated in later task validations. Confirmed rollback safety is implicit (delete documents + revert PRD + remove harness) but not explicitly tested. Verified receipt anchoring to plan_hash via plan_hash locking (line 201) and confirmed PRD updates are within plan scope. Found no fail-open gates (all exit 1 on error), no unanchored receipts, and acceptable documented limitations on axis 2 completeness verification." |

## Measurement

<!-- Written by plan-review/cli.js record on EVERY exit path, pass or halt.
     Machine-readable; do not hand-edit. A null field means the axis was
     not observed, never that it was zero. -->

```json
{
  "verdict": "divergent",
  "source": "multi-agent",
  "layers": {
    "l1": "converged",
    "l2": "divergent",
    "l3": "not fired"
  },
  "quorum": {
    "responded": 4,
    "required": 3,
    "roles": 4,
    "of": 4,
    "passed": false
  },
  "wall_clock_ms": 370787,
  "halt_stage": null,
  "backlog_appended": 2,
  "backlog_skipped_nonblocking": 6,
  "granted": 4,
  "reviewed_plan_hash": "sha256:8f94905be762e7368b8decce140ee5f27872cfb61aaf16b9323ac2ce589f69ca",
  "plan_path": ".claude/plans/insuniverse-docs-40-runtime-vendor-legal-m4.plan.md",
  "recorded_at": "2026-08-26T06:07:43.664Z"
}
```
