SniperGold_ML/docs/P3_S5_ORDER_BLOCK_CONFORMANCE.md

24 KiB

P3-S.5 ORDER BLOCK FORMAL SPECIFICATION & CODE-CONFORMANCE REPORT

Date       : 2026-08-22
Session    : P3-S.5 — Formal SMC Specification + Code-Conformance Audit (Order Block)
Scope      : Order Block (OB) — Engine 2 agents C/E (AF_FindOrderBlock),
             Engine 3 display (AF_CollectOBs), legacy v4.3/v4.4/v4.5
             (ProcessStructure RecordOB + v4.5 MTF RecordOB_mtf),
             legacy entry gate (InpEntryOnFVGorOB / FindEntryOB /
             Sweep->CHoCH->OB chain).
Principle  : SPECIFICATION FIRST, CODE AUDIT SECOND
Status     : NO CODE CHANGES (Phase D) — all findings DOCUMENTED
Artifacts  : docs/SMC_ORDER_BLOCK_SPEC_v1.md (specification)
             ml/p3/smc_semantic/spec_tests_order_block.py (+ .json) (tests)
             ml/p3/smc_semantic/output/spec_tests_order_block_report.json

0. CHECKPOINT & PROVENANCE (session-start verification)

Forge HEAD          : 176880f8f3f17afb7e922a48ed953daa22c1515f (P3-S.4)  VERIFIED
Branch / remote     : main == origin/main (176880f)                      VERIFIED
Working tree        : CLEAN at start                                     VERIFIED
P3-S.4 artifacts    : spec/conformance/tests/report/handover INTACT        VERIFIED
P3-S.3 / P3-S.2 / P3-S.0 / P3-S.1 / P2 checkpoints : in history, intact      VERIFIED
P2.6 model (SHA-256): 06df8452... (verified by file hash in P3-S.4)        VERIFIED
HUMAN VERIFICATION  : CANCELLED                                          VERIFIED

A. SOURCE MATERIAL (Phase A — inventory & reconciliation)

# Source Location Role
1 DESIGN.md (Algo Forge) docs/DESIGN.md:132-133, 206 Intent: C agent "OB (opposite bar before a strong move)"; E agent "OB/FVG zones; ZONE + CONFIRMATION = setup"; display "Order Block
2 Engine 2 detector Include\AlgoForge\AF_Engine2_Agents.mqh:293-322 (AF_FindOrderBlock) opposite-color bar before strong move (body >= 1.5 x avg); zone = full range; NO min size; NO mitigation; returns NEWEST +-1/0; avgN = AF_E2_LOOKBACK_AVG (20)
3 AF_AvgBody AF_Engine2_Agents.mqh:120-127 mean
4 Engine 2 C agent AF_Engine2_Agents.mqh:492-575 ob = AF_FindOrderBlock(20); mInOb = trap(close, zone +- 0.3 ATR); weight 0.30
5 Engine 2 E agent AF_Engine2_Agents.mqh:583-663 ob = AF_FindOrderBlock(20); mZone (OB/FVG); ZONE + CONFIRMATION setup rule
6 Engine 3 display collector Include\AlgoForge\AF_Engine2_Display.mqh:199-250 (AF_CollectOBs) same formation; doji excluded; min 0.15 x ATR; mitigation close-through; dedupe 0.5 x ATR; newest N (default 5)
7 AF_Defines.mqh Include\AlgoForge\AF_Defines.mqh:57,59,60 AF_E3_OB_DEFAULT=5; AF_E3_OB_MIN_ATR=0.15; AF_E3_MOVE_BODY=1.5
8 Engine 3 render/indicator AF_Engine3_Render.mqh; AF_Engine3_Display.mq5 OB boxes; inputs (InpShowOB, InpOBCount=5)
9 v4.3/v4.4 indicator Indicators\Downloads\SniperGold_SMC_ProPlus_v4_3/4_4.mq5 LEGACY structure-break OB: RecordOB on BOS/CHoCH break (ProcessStructure:400-435); orig = last opposite pivot; scan breakBar->orig for last/highest-volume opposite candle (RecordOB:536-556); full range; min 0.15 x ATR; close-through mitigation (FilterAndDrawOBs:559-567); internal + swing OBs; entry FindEntryOB:976-987 + tap +-1 ATR; +15; Sweep->CHoCH->OB chain
10 v4.5 indicator SniperGold_SMC_ProPlus_v4_5.mq5 chart OB identical to v4.4; + MTF OB per-TF (D1/H4/H1) via cache (RecordOB_mtf:385-407; structure-break, InpOBStyle last/volume, 0.15 ATR, close-through mit)
11 FEATURE_CONTRACT docs/FEATURE_CONTRACT.md NO OB feature (1 hit = "prOB" in SGMLProb — false positive)
12 EA runtime Experts\AlgoForge_Backtest_Baseline.mq5 NO OB computation (hits = OrderSend + comment "tanpa draw/OB/alert")
13 Python training ml/**/*.py NO OB (0 hits)
14 P3 docs docs/P3_2_2_..., docs/P3_2_LABEL_FORENSIC "breaker" mentioned ONLY as a future concept, outside this session's mandate
15 External references Indicators\Downloads\SMC Order Block Detector.mq5; XAUUSD_MTF_Liquidity_OB_Reversal (2).mq5 external indicators, NOT part of the project (no wiring)

Layer separation:

Intended semantics   : OB = opposite bar before a strong move (DESIGN.md);
                       C agent context zone; E agent entry zone (ZONE +
                       CONFIRMATION = setup); display zones with MIT filter +
                       dedupe + min size 0.15xATR.
Legacy semantics     : v4.3/v4.4/v4.5: OB = opposite candle BEFORE A STRUCTURE
                       BREAK (BOS/CHoCH on swing len=50 / internal len=5);
                       last or highest-volume opposite candle; full range;
                       min 0.15xATR; close-through mitigation; internal + swing
                       OBs; entry chain Sweep->CHoCH->OB within InpSeqWindow.
Current semantics    : Algo Forge: opposite bar before strong move (1.5 x avg
                       body); zone = full range; Engine 2 = newest-only, NO min
                       size, NO mitigation; display = newest N unmitigated,
                       0.15xATR min, close-through mit, 0.5xATR dedupe.
Training semantics   : N/A — OB is not a training feature (no parity target).
Display-only         : Engine 3 OB boxes; v4.4/v4.5 OB drawing; v4.5 MTF OB.

B. PROJECT SEMANTIC SPECIFICATION (Phase B)

Full document: docs/SMC_ORDER_BLOCK_SPEC_v1.md (PROJECT SEMANTIC SPECIFICATION — not "universal SMC ground truth").

Spec decision summary:

S-1  Formation      : opposite-color candle B immediately before strong-move
                      candle M (|body(M)| >= 1.5 x avg body). Canonical current.
S-2  Candle selection: B = full opposite candle (Close vs Open); doji excluded;
                      zone = FULL RANGE [Low(B), High(B)].
S-3  Zone boundaries: top = High(B), bot = Low(B); no padding; min size none in
                      definition (0.15 ATR consumer filters, A-1).
S-4  Direction      : +1 bullish (bearish B + bullish M), -1 bearish, 0 none;
                      direction = move direction.
S-5  Structural precondition: NONE (canonical); legacy = BOS/CHoCH (A-2).
S-6  Formation timing: observable at M close (B+1 <= t); newest pair (t-1, t);
                      NO 1-bar lag; timestamp = B open.
S-7  Closed-bar     : all inputs <= t; no future visibility.
S-8  Lifecycle      : ZONE + FORMATION EVENT; one zone per pair; no event stream.
S-9  Mitigation     : close-through full fill (Close < Low(B) bull / > High(B)
                      bear); partial/wick NOT mitigation.
S-10 Invalidation   : NOT MODELED.
S-11 Overlap        : separate zones per pair; display dedupe 0.5 ATR (A-3).
S-12 Breaker        : NOT IMPLEMENTED (no code).
S-13 Timeframe      : Engine 2 per-TF independent; canonical = AMBIGUOUS (A-4).
S-14 Consumers      : C/E zone membership (newest OB); legacy entry gate (all
                      unmitigated within tap); display; NO ML feature.
S-15 Parity         : OB NOT an ML feature; PARITY = N/A BY ABSENCE.

C. FORMAL DEFINITIONS (Phase B — extract)

avg_body(t) = mean over the newest min(20, t+1) closed bars of |Close-Open|.

OB_pair(bar b, prices) with b+1 <= t (both closed), move M = b+1:

  Bullish OB (dir=+1): Close(M) > Open(M)  AND |body(M)| >= 1.5*avg_body
                       AND Close(b) < Open(b)      -> zone [Low(b), High(b)]
  Bearish OB (dir=-1): Close(M) < Open(M)  AND |body(M)| >= 1.5*avg_body
                       AND Close(b) > Open(b)      -> zone [Low(b), High(b)]

  One zone per qualifying pair (b, b+1). Zone state (S-9): MITIGATED iff exists
  j > b with bull: Close(j) < Low(b) / bear: Close(j) > High(b).
  Canonical consumer query (S-14): the NEWEST zone at decision t (Engine 2)
  / newest N unmitigated (display/legacy). No invalidation, no age expiry.
  OB is not a Feature Contract feature (S-15).

D. CURRENT CODE BEHAVIOR (Phase C — code audit)

D.1 Engine 2 — AF_FindOrderBlock (AF_Engine2_Agents.mqh:293-322)

int AF_FindOrderBlock(const AFEngine1MTF &e1,int slot,int avgN,double &zoneHi,double &zoneLo)
{
   zoneHi=0.0; zoneLo=0.0;
   if(!e1.IsReady(slot)) return 0;
   int cnt=e1.Count(slot);
   if(cnt<avgN+2) return 0;
   double avg=AF_AvgBody(e1,slot,avgN);           // mean |Close-Open| last avgN bars
   if(avg<=0.0) return 0;
   for(int i=1;i<cnt-1;i++)                        // NEWEST pair first (i=1 -> move=idx0)
   {
      double bodyPrev=MathAbs(e1.Close(slot,i-1)-e1.Open(slot,i-1));
      if(bodyPrev<1.5*avg) continue;               // move body >= 1.5 x avg
      bool upMove=e1.Close(slot,i-1)>e1.Open(slot,i-1);
      if(upMove)
      {
         if(e1.Close(slot,i)<e1.Open(slot,i))      // OB candle (older) bearish
         { zoneHi=e1.High(slot,i); zoneLo=e1.Low(slot,i); return 1; }
      }
      else
      {
         if(e1.Close(slot,i)>e1.Open(slot,i))      // OB candle bullish
         { zoneHi=e1.High(slot,i); zoneLo=e1.Low(slot,i); return -1; }
      }
   }
   return 0;
}

Observations:

  • Formation = opposite-color candle + strong move (body >= 1.5 x avg) — matches S-1.
  • Zone = full range [Low(i), High(i)] — matches S-2/S-3.
  • NEWEST pair: OB candle = index 1, move = index 0 (newest closed bar IS eligible as the move) — NO 1-bar lag (S-6).
  • NO minimum zone size (S-3/A-1), NO mitigation filter (S-9 — BUG-P3S5-001), returns ONLY the newest OB (S-14/A-5).
  • avgN = 20 (AF_E2_LOOKBACK_AVG) in both callers.

D.2 Engine 3 display — AF_CollectOBs (AF_Engine2_Display.mqh:199-250)

for(int i=0; i<maxIdx; i++)              // i = move candle (0 = newest closed)
{
   int obBar=i+1;                        // OB candle immediately before the move
   double moveBody=MathAbs(Close(i)-Open(i));
   if(moveBody<AF_E3_MOVE_BODY*avg) continue;       // 1.5 x avg
   bool moveUp=(Close(i)>Open(i));
   bool prevBear=(Close(obBar)<Open(obBar));
   bool prevBull=(Close(obBar)>Open(obBar));
   if(!prevBear && !prevBull) continue;             // doji excluded
   if(moveUp && prevBear) bull=true;
   else if(!moveUp && prevBull) bull=false;
   else continue;
   top=High(obBar); bot=Low(obBar);
   if((top-bot)<AF_E3_OB_MIN_ATR*atr) continue;    // min 0.15 x ATR
   // mitigation: any subsequent CLOSE beyond the far boundary
   for(int b=obBar-1; b>=0; b--) { bull? Close<bot : Close>top -> mit; }
   if(mit) continue;
   // dedupe: skip zone within 0.5 x ATR of an existing zone
   ...
}

Observations: same formation (S-1); adds doji exclusion (S-2 consistent), min 0.15 x ATR (A-1), close-through mitigation (S-9), 0.5 x ATR dedupe (A-3), newest N unmitigated zones (S-14). Timestamp = Time(obBar) = OB candle (S-6).

D.3 Legacy v4.3/v4.4 — structure-break OB (v4_4.mq5:400-435, 536-556)

// in ProcessStructure, on a bullish break (close[i]>upTarget):
int orig=(lastLowBar>=0&&lastLowBar<i)?lastLowBar:upBar;
if(allow) RecordOB(true,internal,orig,i,open,high,low,close,vol);
// on a bearish break: orig=(lastHighBar...); RecordOB(false,...)

void RecordOB(bool bull,bool internal,int fromBar,int toBar,...)
{
   for(int b=toBar;b>=fromBar;b--) {                 // break bar -> last pivot bar
      bool oppo = bull ? (close[b]<open[b]) : (close[b]>open[b]);
      if(!oppo) continue;
      if(InpVolumeOB){ if(vol[b]>bestVol){bestVol=vol[b]; found=b;} }  // highest volume
      else { found=b; break; }                                         // last opposite
   }
   if(found<0) return;
   if((high[found]-low[found])<0.15*g_atr) return;
   g_obs[n].top=high[found]; g_obs[n].bot=low[found]; ...
}
// mitigation (FilterAndDrawOBs): close[b]<bot (bull) / close[b]>top (bear) -> mit

Observations: LEGACY definition — OB recorded ONLY at a STRUCTURE BREAK (BOS/CHoCH), the OB candle = last (or highest-volume) opposite candle between the last opposite pivot and the break bar; full range; min 0.15 x ATR; close-through mitigation. This is the A-2 second definition. v4.5 MTF OB = same rule per-TF.

D.4 Entry consumption (v4.4)

int FindEntryOB(bool bull,double price)  // unmitigated OB, mid within +-0.2 ATR of price
   ... if(bull && mid>price+0.2*g_atr) continue; ...
buyOBTap = (obB>=0 && price<=g_obs[obB].top+tap && price>=g_obs[obB].bot-tap); // tap=1 ATR
buy += 15 (OB tap); signal "Sweep->CHoCH->Bullish OB (discount)";
pending: "wait pullback to bullish OB" at zone top.

Observations: legacy entry consumes ALL unmitigated OBs within tap (S-14/A-5); the Engine-2 C/E agents consume only the NEWEST OB without a mitigation check.


E. CONFORMANCE MATRIX (Phase C)

Verdicts: CONFORMING / PARTIALLY CONFORMING / NON-CONFORMING / UNVERIFIABLE / SPECIFICATION AMBIGUOUS.

# Requirement Specification Code (Function/Line) Actual Behavior Verdict
E-1 Formation opposite candle B before strong move M (body >= 1.5 x avg) (S-1) AF_FindOrderBlock:309-318; AF_CollectOBs:210-220 matches; legacy uses structure-break rule instead (A-2) CONFORMING (current) / A-2 legacy divergence
E-2 Candle selection full opposite candle; doji excluded; FULL RANGE (S-2) AF_FindOrderBlock:313,318; AF_CollectOBs:215-223; v4.4 RecordOB:544-555 full range [Low,High]; doji excluded CONFORMING
E-3 Zone boundaries top=High(B), bot=Low(B); no padding (S-3) all: top/bot = High/Low of OB candle exact boundaries; 0.15 ATR min filter differs by consumer (A-1) CONFORMING (+ AMBIGUOUS A-1)
E-4 Direction +1 bullish / -1 bearish / 0 none (S-4) AF_FindOrderBlock:313-318; AF_CollectOBs:218-220 direction = move direction; symmetric CONFORMING
E-5 Structural precondition NONE (canonical) (S-5) AF_FindOrderBlock: no break/BOS/CHoCH check current detector = opposite+strong-body only CONFORMING (current); legacy = structure-break (A-2, PARTIALLY CONFORMING at project level)
E-6 Formation timing observable at M close; newest pair (t-1, t); NO 1-bar lag (S-6) AF_FindOrderBlock: i=1 (move=idx0) newest closed bar IS the move candle; OB candle = idx 1 CONFORMING
E-7 Closed-bar / no future all inputs <= t (S-7) Engine-1 lock; v4.4 tc=total-1 no future visibility; deterministic CONFORMING
E-8 Lifecycle ZONE + FORMATION EVENT; one zone per pair; no event stream (S-8) zone arrays (g_obs / dd.ob); newest-query one zone per pair; no duplication (OB-T09) CONFORMING
E-9 Mitigation close-through full fill (S-9) AF_CollectOBs:227-231; v4.4 FilterAndDrawOBs:563-566; AF_FindOrderBlock: NONE display/legacy apply close-through mit; Engine-2 detector NONE NON-CONFORMING (Engine 2, BUG-P3S5-001)
E-10 Invalidation NOT MODELED (S-10) no invalidation code none exists CONFORMING
E-11 Overlap / multiple separate zones per pair; dedupe only in display (S-11/A-3) AF_CollectOBs:234-239 dedupe 0.5 ATR; AF_FindOrderBlock newest-only; v4.4 keep-all keep-all vs dedupe intent not adjudicated PARTIALLY CONFORMING (A-3)
E-12 Breaker NOT IMPLEMENTED (S-12) grep breaker = 0 project code matches no Breaker/flipped-OB code CONFORMING (no claim)
E-13 Timeframe Engine 2 per-TF; canonical AMBIGUOUS (S-13) AF_FindOrderBlock per slot (H4/M30/M15/M3); v4.5 MTF cache per-TF independent; HTF role not defined SPECIFICATION AMBIGUOUS (A-4)
E-14 Consumer behavior C/E zone membership (newest OB); legacy entry all-unmitigated (S-14/A-5) C:AF_Engine2_Agents:511-512,539-541; E:607-608,621-623; legacy FindEntryOB Engine-2 consumes newest-ANY (no mit/age); legacy all-unmitigated within tap PARTIALLY CONFORMING (BUG-P3S5-001, A-5)
E-15 Runtime/training parity OB NOT an ML feature; PARITY = N/A (S-15) FEATURE_CONTRACT/EA/ml: no OB no training-side OB; no parity target CONFORMING (N/A by absence)

F. CONFIRMED BUGS (Phase D — DOCUMENT ONLY, no code changes)

BUG-P3S5-001
  SEVERITY     : MEDIUM
  SOURCE       : AF_FindOrderBlock — AF_Engine2_Agents.mqh:293-322 (Engine 2 C/E agents)
  SPEC REQ     : S-9 — mitigation is part of the OB zone lifecycle; the canonical
                 consumer query should use unmitigated zones (display/legacy do)
  ACTUAL CODE  : AF_FindOrderBlock returns the newest OB with NO mitigation check
                 and NO age/freshness bound; it returns ONLY the newest zone
                 (older unmitigated OBs invisible to the agents)
  IMPACT       : C agent "price in OB zone" (weight 0.30) and E agent "zone tap"
                 can fire on a FULLY-FILLED (mitigated) OB, and on an OB that may
                 be hundreds of bars old (no freshness). The legacy entry path
                 (FindEntryOB) filters mitigation; the current Engine-2 path does
                 not. Same bug class as BUG-P3S4-001 (FVG side).
                 NOT the f7/f9/f18 duplication class — OB is a zone, not an event
                 stream (verified OB-T09).
  PROPOSED FIX : apply the close-through mitigation filter (S-9) to
                 AF_FindOrderBlock (or let the agents filter mitigated zones) and
                 add a zone-age bound if consumers require freshness; document the
                 zone-selection rule (newest vs newest-unmitigated).

BUG-P3S5-002
  SEVERITY     : LOW
  SOURCE       : two OB definitions in one project — AF_FindOrderBlock
                 (opposite + strong move) vs v4.3/v4.4/v4.5 RecordOB
                 (opposite before STRUCTURE BREAK)
  SPEC REQ     : S-5/A-2 — the canonical definition must be explicit
  ACTUAL CODE  : the current Algo Forge detector (DESIGN.md rule) and the legacy
                 indicator (BOS/CHoCH rule) can select DIFFERENT candles as "the
                 OB" for the same price sequence
  IMPACT       : runtime Engine-2 outputs are NOT directly comparable with the
                 legacy v4.4/v4.5 OB displays; the legacy volume-weighted mode
                 (InpVolumeOB / InpOBStyle=OB_HIGHEST_VOLUME) selects a different
                 candle again (A-6)
  PROPOSED FIX : document the current rule as canonical and mark the legacy
                 structure-break/volume rule as historical (no code change to the
                 legacy indicators this session).

BUG-P3S5-003
  SEVERITY     : INFO (specification ambiguity)
  SOURCE       : minimum zone size — AF_Defines.mqh:59 (0.15 ATR) vs
                 AF_FindOrderBlock (none) vs v4.4:549 (0.15 ATR)
  SPEC REQ     : S-3/A-1 — a canonical project minimum zone size
  ACTUAL CODE  : Engine-2 detector has no filter; display/legacy use 0.15 x ATR
  IMPACT       : the same candle can be "OB" in Engine 2 but filtered by
                 display/legacy
  PROPOSED FIX : adjudicate a single threshold (NOT optimized here) or explicitly
                 scope the filters to their consumers.

BUG-P3S5-004
  SEVERITY     : INFO (documentation)
  SOURCE       : display dedupe — AF_Engine2_Display.mqh:234-239 (0.5 x ATR)
                 vs Engine-2 newest-only vs legacy keep-all
  SPEC REQ     : S-11/A-3 — overlap handling intent
  ACTUAL CODE  : three different zone-selection/overlap policies
  IMPACT       : display may hide an OB the Engine-2 agents still use
  PROPOSED FIX : adjudicate keep-all vs dedupe (documented; not fixed here).

G. SEMANTIC AMBIGUITIES (Phase F — consistency)

G-1  MINIMUM ZONE SIZE (A-1)
     Engine 2 none / display 0.15 x ATR / legacy 0.15 x ATR. No canonical value.
G-2  TWO OB DEFINITIONS (A-2)
     current = opposite candle before strong move (DESIGN.md, Algo Forge);
     legacy = opposite candle before structure break (v4.3/v4.4/v4.5, BOS/CHoCH,
     volume mode). Different candles can be selected.
G-3  OVERLAP / DEDUPE (A-3)
     display dedupes zones within 0.5 x ATR; Engine 2 and legacy keep all.
G-4  CANONICAL TIMEFRAME (A-4)
     Engine-2 M15 slot explicit; HTF OB as M15 context / M3 execution not defined.
     Same class as CHoCH G-4 / sweep G-3 / FVG A-3.
G-5  CONSUMER ZONE SCOPE (A-5)
     Engine 2 newest-ANY (no mit/age) vs legacy all-unmitigated within tap vs
     display newest-N-unmitigated. BUG-P3S5-001.
G-6  VOLUME-WEIGHTED OB (A-6)
     legacy-only (InpVolumeOB / OB_HIGHEST_VOLUME); current = last candle only.

H. SEMANTIC RELATIONSHIP WITH THE SMC CHAIN (Phase C)

Detection : OB detection is independent (canonical current: opposite + strong
            body; no sweep/CHoCH/FVG required) — CONFORMING S-1/S-5.
Legacy chain : Sweep -> CHoCH -> OB/FVG within InpSeqWindow (v4.4) — OB is the
            final ENTRY ZONE of the chain; ordering/freshness handled by the
            sweep/CHoCH gates (P3-S.2/P3-S.3 audits); the OB zone itself has no
            age rule (mitigation is its only termination).
Engine 2 : C agent = OB as CONTEXT (price-in-zone, weight 0.30); E agent = OB as
            ENTRY ZONE with ZONE + CONFIRMATION = setup (DESIGN.md). Consistent
            with the design intent.
ML       : OB ABSENT from f0-f18 — the chain is runtime/display only.
NO production dependency between OB and f7/f8/f9/f10/f11/FVG/CHoCH was changed.

I. RUNTIME/TRAINING PARITY (Phase C — diagnostic, not a retrain)

OB is not a Feature Contract feature (f0-f18) and has no Python training
representation (verified: no OB in FEATURE_CONTRACT.md, EA runtime, ml/**/*.py).
-> PARITY = N/A BY ABSENCE (S-15). No mismatch exists; the absence itself is a
   documented fact, not a defect.
If OB is later promoted to an ML feature, a Feature Contract entry and a parity
harness are required first (out of scope).

J. VERDICT

PARTIALLY CONFORMING

  Formation (E-1, E-2, E-4, E-6, E-7)      : CONFORMING (current definition)
  Zone boundaries (E-3)                    : CONFORMING (+ AMBIGUOUS A-1 min size)
  Structural precondition (E-5)            : CONFORMING (current = none);
                                             legacy BOS/CHoCH rule = A-2
  Lifecycle (E-8)                          : CONFORMING (zone; no event duplication)
  Mitigation (E-9)                         : NON-CONFORMING (Engine 2, BUG-P3S5-001)
  Invalidation (E-10)                      : CONFORMING (not modeled)
  Overlap (E-11)                           : PARTIALLY CONFORMING (A-3)
  Breaker (E-12)                           : NOT IMPLEMENTED (no claim)
  Timeframe (E-13)                         : SPECIFICATION AMBIGUOUS (A-4)
  Consumer semantics (E-14)                : PARTIALLY CONFORMING (BUG-P3S5-001, A-5)
  Runtime/training parity (E-15)           : CONFORMING (N/A by absence)

Verdict = conformance to the PROJECT SEMANTIC SPECIFICATION, NOT profitability/edge.
The DETECTOR core (formation, candle selection, direction, closed-bar, zone
lifecycle) is conforming; the gaps are consumer-side (Engine-2 mitigation/age
filter missing, zone selection scope, canonical threshold/timeframe) plus the
legacy structure-break second definition.

K. NEXT GATE

After the Order Block verdict (PARTIALLY CONFORMING):
  Displacement -> MTF Alignment -> Candidate Setup.
DO NOT return to ML; DO NOT build a meta-label; DO NOT create new event labels;
DO NOT fix production bugs before the audit chain completes (BUG-P3S5-00x stay
documented until the semantic audit chain is finished).

L. CHECKPOINT & PROVENANCE (P3-S.5)

Forge HEAD (session start)   : 176880f (P3-S.4) — VERIFIED
P3-S.4 spec (immutable)      : docs/SMC_FVG_SPEC_v1.md
P3-S.3 spec (immutable)      : docs/SMC_CHOCH_MSS_SPEC_v1.md
P3-S.2 spec (immutable)      : docs/SMC_LIQUIDITY_SWEEP_SPEC_v1.md
FEATURE_CONTRACT             : C44CC6F2... (P2-era) / 7b908b12... (committed)
symbol/tf                    : XAUUSD / M15 (2017-01-01 .. 2026-08-20)
Human verification           : CANCELLED
No production change         : AF_FindOrderBlock / AF_CollectOBs / v4.3/v4.4/v4.5 /
                               Engine 2 / Engine 3 / f7-f18 / FVG / CHoCH /
                               Displacement / ML : NOT touched