# Plan Review Panel — insuniverse-docs-50-domain-m1

**Plan**: `.claude/plans/insuniverse-docs-50-domain-m1.plan.md` · **Plan version**: `sha256:4e4b4f38deca02073918a90976ee6a4004854b11eb35563c00d608bbb4b1335c`
**Verdict**: `unavailable` via `multi-agent`
**Quorum**: 4/3 responses · 4 distinct roles (of 4 fielded) · passed=false
**Layers**: L1 converged · L2 divergent · L3 not fired
**Halted at**: `5.2e`

> Reason: plan changed after L2 read it: reviewed sha256:4e4b4f38deca… but current is sha256:4c6e58324e6a… — recovery is to rerun L2 against the current plan, NOT to reseal (DD13)

## Findings

| Perspective | Severity | Claim | Evidence |
|---|---|---|---|
| security | HIGH | L2 R4 security judgment J1 narrowed the path-stripping mitigation to only 'strip repo root', but the plan then claims (line 201) temp paths will be folded to '<tmp>'. If the implementation follows the judgment's narrower scope, absolute Windows temp paths like '<home>/AppData/Local/Temp/m1-gates-xyz/docs/...' will leak the username '<user>' in harness output. | Plan line 162 (L2 R4 judgment): 'D출력 경로는 저장소 루트를 벗긴다(선례 verify.sh 2454)'. vs. Plan line 201 & 357-365 (Task 1 & Validation): '임시 트리 경로도 같은 규칙으로 `<tmp>` 로 접는다' and fixture paths shown as `<tmp>/docs/20-site/00-ia.md`. Environment info shows Windows with <home>/ paths. No code shown for how `<tmp>` abstraction is implemented. |
| security | MEDIUM | The plan cites 'verify.sh line 2454' as precedent for path-stripping behavior (line 162), but this line does not exist in the accessible verify.sh file. The citation breaks the evidentiary chain and the actual prior implementation cannot be verified. | Plan line 162: 'select례 `verify.sh` 2454'. Grep search for '2454' in <repo>/docs/_meta/verify/verify.sh returned zero matches. |
| security | MEDIUM | Path abstraction logic for temp files ('<tmp>' folding) is delegated entirely to the external `.claude/plans/insuniverse-docs-50-domain-m1.gates.mjs` file per CLAUDE.md §1's 200-line rule. Without implementation visibility, the plan cannot guarantee that temp paths are correctly abstracted on Windows (where path separators differ) before output, creating a trust boundary gap. | Plan line 201 'Action': 'Task 1 description says to create the .gates.mjs file but does not show the implementation. Plan line 201 asserts 'D임시 트리 경로도 같은 규칙으로 `<tmp>` 로 접는다' but mechanism is not specified. This file mirrors m8 precedent (line 202) but actual implementation code cannot be reviewed. |
| test | MEDIUM | Gate #6 validates bidirectional set equality: 'docs/00-index.md 문서 트리에서 유도한 대상 집합이 PRD §MVP 사본과 같고' | Line 446 Acceptance criterion explicitly claims set equality ('집합...같고'), but the test specification at lines 330-331 only provides a negative test case for one direction: 'PRD §MVP 코드블록에서 파일 하나를 지우면 #6 FAIL: 정본 트리에 있고 PRD 사본에 없는 경로 1건'. The opposite direction (file in PRD but not in tree) has no negative test case shown. This means check6 implementation might only catch missing files in PRD, not extra files in PRD. |
| test | MEDIUM | Gate #8 specifies complete testing of config key effects, including ia-exits key absence branch via temporary probe fixtures | Lines 453-454 state gate #8 tests 'ia-exits 의 키 부재 분기가 이 창에서 1회 실측' with note '같은 분기가 상시 selftest 픽스처를 갖지 못한다는 사실이 PRD 에 남는다 — (미검증)'. The plan defers the actual probe file content generation to the .gates.mjs implementation (lines 359-365 describe what probes SHOULD contain: 'ia 템플릿 · exit 절 보유' and 'policy 템플릿' but do not specify exact markdown/YAML structure). The test cannot be verified as complete without knowing how temporary probes are actually generated. |
| test | MEDIUM | The harness implementation (.claude/plans/insuniverse-docs-50-domain-m1.gates.mjs) exists and can be validated against its specification before approval | The file does not exist (verified via Glob: only 13-relation-contract-m*.gates.mjs files found, no 50-domain-m1 variant). The plan creates this file in Task 1 during the implement phase. While CLAUDE.md §1 permits this (validation blocks moved to external files for large plans), it means the check implementations cannot be verified during /mccp:plan review. The Validation block description at lines 276-423 describes intended behavior but cannot be compared against actual code. |
| invariant | CRITICAL | Check #8 fixture creation preconditions are not verified by any gate. Fixtures are created by harness code (`.mjs` file) which is not shown in the plan due to 200-line limit. Check #13's `--selfprobe` tests 5 table extractors but does not test fixture creation. If fixture code has bugs, check #8 passes with wrong test data. | Lines 356-365 describe fixture structure at high level ('ia probe: <tmp>/docs/20-site/00-ia.md...'), but actual fixture creation code is in Task 1's `.mjs` file (lines 200-205). Lines 405-407 specify `--selfprobe` tests checks #3,#4,#5,#6,#10 (table parsers), not fixture generation. Line 148 (H4) acknowledges harness code is not shown due to CLAUDE.md §1 200-line rule. |
| invariant | HIGH | Check #8 validates tool behavior against manual observation 5 (lines 62-73), which is a one-time measurement on a temporary tree. This observation becomes the gate's expected behavior. If `lint-docs.mjs` (in a different repo) changes silently, check #8 will validate new (wrong) behavior as long as fixtures match the changed tool. | Line 66: '실측(임시 트리 `docs/20-site/policy-example.md` + `docs/_meta/template-config.yaml`)' — observation 5 is manual one-time measurement. Lines 344-372 (check #8 validation) use observation 5's table (lines 68-73) to define expected behavior. Line 176 (L2 R5): fixture matching is tested but observation itself is not re-validated. Plan assumes tool behavior is stable, no gate checks this assumption. |
| invariant | HIGH | The `ia-exits` config key missing branch is tested only once (this milestone) and will not be in future continuous regression tests. Acceptance criterion line 454 explicitly states `(미검증)` — meaning unverified in future runs. This is a one-time gate that erodes. | Lines 453-454: '`ia-exits` 의 키 부재 분기가 **이 창에서 1회 실측**된다... 게이트 #8 은 이 창의 1회 실측만 보증하며 다음 창의 회귀는 보증하지 않는다' and '(미검증) · 픽스처 추가는 `.claude/templates/doc-kit/**` 소유이고 이 PRD 에 그 개정 위임이 없다'. Line 76-77: observation 6 notes selftest fixture for this branch doesn't exist. |
| invariant | MEDIUM | Acceptance criteria mix verified and unverified claims. Lines 453-456 contain multiple `(미검증)` items, meaning they are explicitly unverified for future runs. These unverified claims are nonetheless in Acceptance criteria, weakening the gate guarantees for the plan as a whole. | Line 453: gate #8 tested once only. Line 454: `(미검증)` · not in future selftest. Line 455: R-50-1 has no active owner — `(미검증)` · user judgment required. Line 456: single-pass toggle won't activate — `(미검증)` · header normalization not this PRD's scope. Acceptance criteria should not contain unverified claims without explicit guard language. |
| invariant | MEDIUM | Fixture content specification is only at high level (lines 359-365: 'ia 프로브: <tmp>/docs/20-site/00-ia.md (ia 템플릿 · exit 절 보유)'). Actual fixture generation code is in the harness `.mjs` file, which is not shown. No gate verifies that created fixtures actually match the high-level spec. | Lines 359-365 describe fixtures: '(a) ia probe: (ia 템플릿 · exit 절 보유)' — but what is 'exit 절'? What exact content? Line 201 and line 356 acknowledge code is not shown due to 200-line limit. No fixture validation test in selfprobe (lines 405-407 test extractors, not generators). |
| invariant | MEDIUM | Skip predicate for single-pass toggle: the toggle does not fire because backlog header is missing (line 136-137, 152). If someone later adds the header, the toggle would activate and change gate behavior (auto-recording findings to backlog). Plan does not gate against header addition to prevent unexpected behavior change. | Line 136: '단일통과 토글은 이 저장소에서 발화하지 못한다'. Line 137 mentions 'backlog 적재가 exit 12 로 막혔다: ... has no `\| Date \| Severity \| Source plan \| Finding \|` header row'. Line 152: 'backlog 헤더 부재로 발화하지 못했다'. The gate skips auto-recording because of missing header, but header absence is not gated to prevent conditional behavior activation. |

## Refutation attempted

| Perspective | Verdict | What was attacked |
|---|---|---|
| architect | pass | Verified bootstrap_permitted boundary (Status-only check at line 2974 of verify.sh vs. file-creation check #11); verified template assignment logic by elimination against lint-docs.mjs (exactly 3 templates defined); verified config key behavior distinction (surface_exit_kinds unconditional at 1240-1243 vs. deferred_ref_targets conditional at 1434); confirmed mirror pattern exists in m8.gates.mjs (TEMPS registry 45-68); traced task sequence for circular dependencies (none found); confirmed seam to M2 clean (no docs/ changes in Files to Change table); verified 7 backlog rows with 50-domain match observation 11 claim. |
| security | fail | Attempted to verify: (1) Whether the security judgment's narrower path-stripping scope ('repo root only') would fully address Windows username leakage in temp paths — confirmed it does not. (2) Whether the plan's claimed `<tmp>` folding is actually implemented — could not verify due to .mjs code being external and unreviewed. (3) Whether the verify.sh line 2454 precedent actually exists — confirmed it does not. (4) Whether CRLF and zero-failure semantics for parsing adequately guard against injection — confirmed adequate for local files (no untrusted input source). (5) Whether temp file cleanup actually happens — precedent from m8 shows TEMPS registry and exit hooks, but m1 implementation not shown. The core issue is that the security judgment (J1 narrowed mitigation) commits to less than what the plan later claims, and the implementation of the fuller claim is not visible. |
| test | fail | I attacked the test specifications for the plan's core validation gates (check #6, #8, #13) and the gate harness implementation. (1) For gate #6 (line 446 Acceptance), I read the test specification at lines 326-333 and found that the negative test case (line 330-331) only demonstrates detection of 'tree has file, PRD missing file', with no negative case shown for 'PRD has file, tree missing file'. The Acceptance criterion explicitly claims 'set equality' but the test may be one-directional. (2) For gate #8, I verified that the plan defers probe fixture generation to the .gates.mjs implementation without specifying exact content (lines 356-365 describe structure but not concrete markdown/YAML templates). (3) For harness presence, I confirmed via Glob that .claude/plans/insuniverse-docs-50-domain-m1.gates.mjs does not exist at plan review time, consistent with CLAUDE.md deferral rules. I examined 80-index.md tree and PRD §MVP section (lines 70-83 vs 117-129) and confirmed they currently match (20 files), but the incomplete test specification could miss future regressions in one direction. |
| invariant | fail | Attacked fail-closed gate structure of check #8 (config key verification) by tracing fixture creation path and discovering preconditions not verified by any gate. Examined #13's `--selfprobe` coverage and found it tests table extractors but not fixture generators. Checked observation 5 (manual baseline) for anchoring strength and found it unanchored to any persistent verification. Searched for one-time test erosion (ia-exits branch) and found explicit `(미검증)` admission that future regression is not protected. Reviewed Acceptance criteria for unverified claims that weaken guarantees. Examined skip predicates around single-pass toggle activation and found missing gate against header addition. Examined Task order dependencies and Validation block sequencing. Checked for receipt anchoring but found only "run gates once and they work" with no persistent artifact binding. |

## 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": "unavailable",
  "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": 477733,
  "halt_stage": "5.2e",
  "backlog_appended": null,
  "backlog_skipped_nonblocking": null,
  "granted": 4,
  "reviewed_plan_hash": "sha256:4e4b4f38deca02073918a90976ee6a4004854b11eb35563c00d608bbb4b1335c",
  "plan_path": ".claude/plans/insuniverse-docs-50-domain-m1.plan.md",
  "recorded_at": "2026-08-24T07:55:07.870Z"
}
```
