# ADVERSARIAL PANEL — P30 Wing D
**SP5 (Opus) · 2026-09-03 · READ-ONLY REVIEW**

Scope: D-D1-GRAPH · D-D2-QUESTION · D-D3-MISREADING · D-D4-API · D-D5-CURRICULUM · D-C7-TRAILS + gates G12/G13 in `framework/prepanel_gate.py` + `framework/strata.py`

---

## VERDICT: **FIX-FIRST** (3 numbered findings; trail/question/curriculum/strata pass)

---

## ATTACK LOG

### ATTACK 1 — GRAPH: 10 Edges Sampled, Endpoints Resolved

**File:** `build/P30-SIGHAH/D-D1-GRAPH/meaning_graph.json`  
**Meta claim:** `"integrity: every endpoint names an artifact row that exists; checked at build"` · `"edges": 91`

**Edge count:** 91 counted manually ✅ VERIFIED (36 rule→verse + 7 witness→rule + 48 report→verse).

**Endpoint resolution — all rule:* nodes:** Resolved against `D1-INVENTORY/rules_layer.json`.
| Sample | `from` | `to` | Resolution |
|--------|--------|------|------------|
| E-01 | rule:R1 | verse:55:13 | R1 exists in rules_layer ✅; 55:13 is a valid Quran reference ✅ |
| E-02 | rule:R5 | verse:28:23 | R5 exists ✅; but **28:23 is NOT in R5's exemplar array** ❌ |
| E-03 | rule:R5 | verse:2:60 | R5 exists ✅; 2:60 IS exemplar 2 of R5 ✅ |
| E-04 | rule:R12 | verse:96:1 | R12 exists ✅; 96:1 IS exemplar 1 of R12 ✅ |
| E-05 | rule:R9 | verse:112:3 | R9 exists ✅; 112:3 IS exemplar 1 of R9 ✅ |
| E-06 | report:bukhari:2410 | rule:R1/R2 | **No row `R1/R2` exists in rules_layer** ❌ |
| E-07 | report:bukhari:3476 | rule:R1/R2 | same ❌ |
| E-08 | report:bukhari:5062 | rule:R1/R2 | same ❌ |
| E-09 | report:bukhari:1127 | rule:R2/R12 | **No row `R2/R12` exists in rules_layer** ❌ |
| E-10 | report:bukhari:4724 | rule:R2/R12 | same ❌ |

**Evidence — rules_layer IDs found:**
`build/P30-SIGHAH/D1-INVENTORY/rules_layer.json` → `rules[*].id` = R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12. No compound IDs exist. ✅ VERIFIED.

**Evidence — R5 exemplar array:**
```
rules_layer R5 exemplars: [ayah:2:282, ayah:2:60, ayah:2:66]
graph R5 edges:            [verse:28:23, verse:2:60, verse:2:66]
```
28:23 appears only in R5's `meaning` text field, not in the exemplar array. 2:282 (first exemplar) is absent from the graph. The graph sourced one edge from description prose, not from the exemplar array, and dropped the first exemplar. The integrity claim is false.

---

### FINDING F1 — D-D1-GRAPH: INTEGRITY CLAIM FALSE (TWO DEFECTS)

**Defect F1a — 5 edges with non-existent `to` endpoints:**  
Edges 37–39 target `rule:R1/R2`; edges 42–43 target `rule:R2/R12`. Neither ID exists in `rules_layer.json`. The meta's claim "every endpoint names an artifact row that exists; checked at build" fails for these 5 edges.

**Defect F1b — R5 exemplar substitution:**  
Graph edge `rule:R5 → verse:28:23 (exemplar)` was sourced from the rule's `meaning` prose, not its `exemplars` array. Graph edge `rule:R5 → verse:2:282` — the first exemplar in the rules_layer — is absent. Two errors in one rule: wrong source (prose vs. array) + missed row.

**FIX:** Resolve witness edges to compound-rule nodes using a separate `rule_group` entity (e.g., `rule_group:R1-R2`) and register that entity explicitly; OR split into two edges (one per rule). Separately, rebuild R5 edges from the exemplar array only: remove 28:23, add 2:282.

**Severity: PILLAR-BREAKING** — the integrity claim is load-bearing for downstream consumers.

---

### SECONDARY GRAPH OBSERVATION (not FIX-FIRST, recorded for completeness)

5 pairs of duplicate edges are structurally identical objects: R1→2:35 ×2, R2→2:35 ×2, R3→2:36 ×2, R7→2:11 ×2, R8→2:4 ×2. These legitimately reflect multiple morphological tokens at one verse (e.g., two 2D forms in 2:35), but the edge objects are byte-for-byte identical — no token discriminator. Downstream deduplication would silently lose evidence; downstream double-processing would overcount. No breach of the current integrity claim (which says nothing about uniqueness), but a latent data-loss hazard. Recommended: add a `token_index` field before consumers exist.

---

### ATTACK 2 — QUESTION ENGINE: Sourcing Purity Check

**File:** `build/P30-SIGHAH/D-D2-QUESTION/question_engine_v1.json`  
**Claim:** "assembles ONLY sourced strata, oldest first; invents nothing; ABSENT is an answer"

**Spot-check: verse 112:3**
```
strata_present: [prophetic, companion, successor]
oldest_first:
  prophetic  → ibnmajah:31       (graph edge ✅)
  companion  → Al-Tawhid-Saduq:80 (graph edge ✅)
  successor  → Al-Kafi-Volume-2-Kulayni:1689, Maani-al-Akhbar-Saduq:72 (graph edges ✅)
absent: classical, modern — declared ✅
```

**Spot-check: verse 1:5**
```
strata_present: [prophetic]
sources: malik:20, nasai:34
absent: companion, successor, classical, modern — declared ✅
```
`Al-Amali-Saduq:272` is absent from verse 1:5 — consistent with the marfu layer and the graph (which has no Al-Amali-Saduq:272 → verse:1:5 edge). Honest gap, not a silence. ✅

**Spot-check: verse 33:33**
```
strata_present: [companion]
source: Kitab-al-Ghayba-Numani:30, class: deferred-to-panel
```
Deferred is declared. No invented stratum. ✅

**Ordering check — verse 1:2 (multiple eras):**
```
prophetic: malik:20, nasai:34, Al-Amali-Saduq:272 → RANK 0
successor: Al-Kafi-Volume-3-Kulayni:1873, Uyun-akhbar-al-Rida:460 → RANK 2
classical: Man-La-Yahduruh-al-Faqih:1570 → RANK 3
```
Order is oldest-first, consistent with `strata.py` RANK table (prophetic=0 < companion=1 < successor=2 < classical=3). ✅ VERIFIED.

**VERDICT on D-D2:** CLEAN PASS — assembles only sourced strata, absent declared, ordering correct.

---

### ATTACK 3 — MISREADING REGISTER: G16 Satisfied?

**File:** `build/P30-SIGHAH/D-D3-MISREADING/misreading_register.json`

**G16 law (from the register's own meta):**
> "an entry requires a named classical/lexical source that ITSELF records the correction; this register can never hold our opinions — empty is a lawful state"

**M1 examined:**
```json
{
  "id": "M1",
  "verse": "55-series",
  "misreading": "reading the refrain's -kuma as plural 'your'",
  "corrector": "the commentators' own khitab notes (al-thaqalan) - 16/32 editions at 55:13 name the two addressees",
  "trace": "engagement measurement, this session; the census is the transmitted correction in aggregate"
}
```

**Attack on M1 — four prongs:**

**Prong 1 — No named source.** G16 requires "a named classical/lexical source." The corrector field names a category ("the commentators' own khitab notes") and a census figure (16/32 editions) — neither is a named source. Rule: the corrector must be named (e.g., "al-Tabari at verse 55:13 writes: *wa-qila inna al-khitab li...* wa-l-sahih..."). A count of editions noting the two addressees is not a transmitted correction.

**Prong 2 — The trace is our work.** `"trace: engagement measurement, this session"` is an explicit admission that this evidence was assembled by us in this session, not transmitted from a classical source. G16: "this register can never hold our opinions." The "census is the transmitted correction in aggregate" is our interpretive reframing, not a citation.

**Prong 3 — Noting addressees ≠ correcting a misreading.** Classical commentators who state "the addressees are al-jinn wa-al-ins (al-thaqalan)" are giving tafsir of the verse's meaning. They are not identifying a named misreading and explicitly rejecting it. The G16-compliant form is a wa-qila... wa-l-sahih passage ("it has been said [X]; the correct view is [Y]"). M1 contains no such passage.

**Prong 4 — The register's own `awaiting` condemns M1.** The register states: `"awaiting: systematic harvest of classical self-corrections (the 'wa-qila... wa-l-sahih' passages)"`. This is precisely what G16 requires — and M1 is not it. The register knows what it is waiting for; M1 is a premature entry that violates the law the register states for itself.

---

### FINDING F2 — D-D3-MISREADING: M1 VIOLATES G16; REGISTER MUST BE EMPTY

G16 requires a named classical source that itself records a correction. M1 provides an aggregate census framed as "our session" evidence. A commentator census is not a transmitted correction. The entry holds our finding, not a sourced correction.

**FIX:** Remove M1. The register's lawful state is empty until a wa-qila... wa-l-sahih passage is located and named. The M1 finding can be preserved in a program note under `awaiting`, not in `entries`.

**Severity: LAW VIOLATION** — G16 has no softer reading; "empty is a lawful state" and M1 is not a lawful entry.

---

### ATTACK 4 — API: 5 Endpoints Checked, Paths and Keys Audited

**File:** `build/P30-SIGHAH/D-D4-API/p30_api_index.json`  
**Meta claim:** `"count": 36`

**Count:** 36 endpoint objects in the `endpoints` array. ✅ VERIFIED (counted).

**Path existence — 5 sampled:**
| Path (relative to `build/`) | Exists? |
|-----------------------------|---------|
| P30-SIGHAH/D-B1-MARFU/marfu_layer.json | ✅ |
| P30-SIGHAH/D-C7-TRAILS/verification_trails.json | ✅ |
| P30-SIGHAH/D-D1-GRAPH/meaning_graph.json | ✅ |
| P30-SIGHAH/D-D5-CURRICULUM/curriculum_v1.json | ✅ |
| P30-SIGHAH/D-D4-API/p30_api_index.json | ✅ (self-referential — see below) |

**Keys check — 3 sampled:**
| Endpoint | Claimed keys | Actual keys present |
|----------|-------------|---------------------|
| D-C7-TRAILS/verification_trails.json | `["trails"]` | `trails` array ✅ |
| D-D1-GRAPH/meaning_graph.json | `["edges"]` | `edges` array ✅ |
| D-D5-CURRICULUM/curriculum_v1.json | `["path", "teacher_note", "child_layer_carried"]` | all 3 present ✅ |
| D-B8-DOSSIERS/dossiers_v1.json | `["verses", "dossiers"]` | `verses: 90` (int), `dossiers: {...}` (dict) ✅ |

**OBSERVATION — self-referential endpoint (not FIX-FIRST):** Endpoint 16 is the API file itself (`D-D4-API/p30_api_index.json`). The API registers its own discovery record as a data layer. This is harmless but conflates the meta-registry with the data corpus it indexes. Noted; no rule prohibits it.

**VERDICT on D-D4-API:** CLEAN PASS — count verified, paths exist, keys truthful for all sampled endpoints.

---

### ATTACK 5 — TRAILS: 3 Verdicts Re-run from Published Patterns

**File:** `build/P30-SIGHAH/D-C7-TRAILS/verification_trails.json`  
**Pattern source:** `D-C2-FIDELITY/fidelity_matrix.json` · `_meta.patterns`

Published EN detector:
```
\byou two\b|\byou both\b|\bboth of you\b|\bye twain\b|\btwain\b|\byou and you\b|\(jinn?s? and men\)|\(men and jinn?s?\)|two of you|\bO (?:my )?two\b|\bwe are both\b|\bboth of us\b
```

**Trail 1: pickthall · 55:55 · claimed "dropped-or-unclear"**  
Rendering: *"Which is it, of the favours of your Lord, that ye deny?"*  
Pattern run: no match for any dual marker. "your Lord" is not dual-addressed.  
Independent verdict: **DROPPED**. ✅ Matches.

**Trail 2: pickthall · 50:26 · claimed "preserved"**  
Rendering: *"Who setteth up another god along with Allah. Do ye twain hurl him to the dreadful doom."*  
Pattern run: `\btwain\b` matches "ye twain".  
Independent verdict: **PRESERVED**. ✅ Matches.

**Trail 3: english_hilali_khan · 55:21 · claimed "preserved"**  
Rendering: *"21. Then which of the Blessings of your Lord will you both (jinn and men) deny?"*  
Pattern run: `\byou both\b` matches; `\(jinn?s? and men\)` matches "(jinn and men)".  
Independent verdict: **PRESERVED**. ✅ Matches.

All 3 re-run verdicts confirmed. The pattern is correctly published and the detector is reproducible.

**VERDICT on D-C7-TRAILS:** CLEAN PASS — 3/3 verdicts independently reproduced from the published pattern.

---

### ATTACK 6 — GATES G12/G13: Can Either Be Fooled?

**File:** `framework/prepanel_gate.py` lines 131–148

**G12 — machine-derivation freshness check:**

```python
sha = (d.get("_meta") or {}).get("source_sha")
if sha and sha != cur:
    fails.append("G12 ...")
```

**VULNERABILITY — silent bypass on absent `source_sha`:**  
The check `if sha and sha != cur` evaluates to False when `sha` is None (i.e., when `source_sha` is absent from `_meta`, or when `_meta` is absent entirely). A new machine-derived file that omits `source_sha` passes G12 silently — not caught, not reported. The gate catches **stale** SHAs but not **absent** ones.

Current Wing D files all carry `source_sha` ✅. But the gate does not enforce this as a requirement — it is advisory only. Any future machine-derived file added to P30 without a `source_sha` field bypasses G12 entirely, defeating the freshness guarantee the gate is meant to provide.

**LANDING exemption confirmed legitimate:** `if "LANDING" in rel: continue` — the four LANDING directories are integrator-audited raw pools with documented exemption. ✅

**G13 — totality check:**

```python
if rel.replace("\\", "/").endswith("D1-INVENTORY/inventory_layer.json"):
    tot = (d.get("totals") or {})
    if tot.get("segments") != 130030 or tot.get("signatures") != 1065:
        fails.append("G13 ...")
```

G13 cannot be bypassed for the inventory file:
- Missing `totals` key → `tot` = `{}` → `None != 130030` → **fires** ✅
- Wrong `segments` or `signatures` → **fires** ✅
- Path must match exactly → cannot be spoofed by a differently-named file ✅

**G13 is robust. G12 has an absent-SHA bypass.**

---

### FINDING F3 — G12: ABSENT `source_sha` IS A SILENT BYPASS

The gate checks `if sha and sha != cur` — this means `sha = None` (absent field) passes silently. The gate's guarantee ("machine-derivation freshness") requires that the field be present AND current. A two-word fix closes the gap.

**FIX:**
```python
# Current (bypassable):
if sha and sha != cur:
    fails.append("G12 ...")

# Corrected (two conditions):
if sha is None:
    fails.append("G12 %s: source_sha absent (machine-derivation unverifiable)" % rel)
elif sha != cur:
    fails.append("G12 %s: source_sha stale vs the raw morphology (machine-derivation broken)" % rel)
```

**Severity: GATE INTEGRITY** — the gate's stated guarantee is not fully enforced.

---

### ATTACK 7 — strata.py: Logic Review

**File:** `framework/strata.py`

**require_stamp() refusal:** A claim with `era_stamp: null` or missing → raises `ValueError`. A claim with a bad string (e.g., `"our_opinion"`) → `split()[0]` returns `"our_opinion"` which is not in STRATA → raises ValueError. ✅ Proven by the built-in verify() test.

**stamp_report() refusal:** Unknown transmission kind → raises ValueError immediately. ✅

**order() correctness:** Sorts by `RANK` dict. RANK = {prophetic:0, companion:1, successor:2, classical:3, modern:4}. Oldest (lower rank number) sorts first. ✅

**Boundary case — compound era_stamp strings:** `require_stamp` accepts a stamp if `s.split()[0]` is in STRATA. This means a stamp of `"prophetic (via companion chain)"` would pass, since `split()[0]` = `"prophetic"`. This is intentional (era stamps with qualifiers are used in the dossiers). ✅

**VERDICT on strata.py:** CLEAN PASS — logic sound, refusals proven, ordering correct.

---

## FINDINGS SUMMARY

| # | Component | Severity | Finding |
|---|-----------|----------|---------|
| F1 | D-D1-GRAPH | **PILLAR-BREAKING** | Integrity claim false: 5 edges target non-existent compound rule IDs (`R1/R2`, `R2/R12`); R5 exemplar substitution replaces 2:282 (actual exemplar 1) with 28:23 (description prose only) |
| F2 | D-D3-MISREADING | **LAW VIOLATION** | M1 fails G16: no named classical source, trace is our session census, commentators noting addressees ≠ a transmitted correction; register must be empty |
| F3 | G12 gate | **GATE INTEGRITY** | Absent `source_sha` silently bypasses G12; gate only catches stale SHAs, not missing ones |

---

## SECONDARY OBSERVATIONS (no fix required before certification)

| | Component | Note |
|---|-----------|------|
| O1 | D-D1-GRAPH | 5 pairs of structurally identical duplicate edges (R1→2:35 ×2, R2→2:35 ×2, R3→2:36 ×2, R7→2:11 ×2, R8→2:4 ×2) legitimately reflect multiple morphological tokens at one verse but carry no token discriminator; recommend adding `token_index` before consumers exist |
| O2 | D-D4-API | API file lists itself as endpoint 16; harmless but conflates meta-registry with data corpus |

---

## CLEAN PASSES

| Component | Verdict | Evidence |
|-----------|---------|---------|
| D-D2-QUESTION | CLEAN | 3 verses spot-checked; ONLY sourced strata assembled; absent declared; oldest-first ordering confirmed against strata.py RANK table |
| D-D4-API | CLEAN | Count 36 verified; 5 paths exist on disk; 4 key sets confirmed truthful |
| D-D5-CURRICULUM | CLEAN | 4-stage path; all referenced source layers resolvable; no invented content; teacher note and child_layer_carried present |
| D-C7-TRAILS | CLEAN | 3/3 verdicts independently reproduced from the published detector pattern |
| strata.py | CLEAN | require_stamp() refuses unstamped; stamp_report() refuses unknown kinds; order() confirmed oldest-first; boundary case (qualified era stamps) intentional |
| G13 gate | CLEAN | Cannot be bypassed; missing or wrong totals both caught |

---

## RE-ENTRY CONDITIONS

Certification may proceed when:

1. **F1 resolved:** Graph edges pointing to `rule:R1/R2` and `rule:R2/R12` are either (a) split into single-rule edges or (b) pointed to a registered `rule_group` entity. R5 edge to 28:23 replaced with edge to 2:282.

2. **F2 resolved:** M1 removed from `entries`. Any program note about the 55-series misreading framing moved to `awaiting` as a description of what the D3 program seeks.

3. **F3 resolved:** G12 code patched to flag absent `source_sha` as a failure, not a silent pass.

After fixes, run `python prepanel_gate.py build/P30-SIGHAH` to green before re-dispatch.

---

*SP5 · adversarial · read-only · no edits made to any source file*
