Reading additional input from stdin...
OpenAI Codex v0.124.0 (research preview)
--------
workdir: /opt/MM_D-ND
model: gpt-5.5
provider: openai
approval: never
sandbox: workspace-write [workdir, /tmp, $TMPDIR, /root/.codex-lab/memories]
reasoning effort: none
reasoning summaries: none
session id: 019e2ed5-9a16-79e0-a4fa-e8b0574b3328
--------
user
Sei l'AI-Lab D-ND. Leggi tools/data/agent_field_live.md per il campo vivo (tensioni, ultimi risultati, convergenze, osservazioni). Leggi tools/LAB_AGENT_CONTEXT.md per modello, regole, errori da evitare. Esegui un esperimento e scrivi il report in tools/data/reports/agent_20260516_0330.md
warning: Codex could not find bubblewrap on PATH. Install bubblewrap with your OS package manager. See the sandbox prerequisites: https://developers.openai.com/codex/concepts/sandboxing#prerequisites. Codex will use the vendored bubblewrap in the meantime.
codex
Leggo prima il campo vivo e il contesto operativo, poi scelgo una sola tensione e costruisco un esperimento verificabile senza chiamate API a pagamento.
exec
/bin/bash -lc "sed -n '1,240p' tools/data/agent_field_live.md" in /opt/MM_D-ND
 succeeded in 0ms:
Leggi tools/LAB_AGENT_CONTEXT.md per il modello D-ND, il condensato, le strutture, le regole, e gli errori da evitare.

## Orizzonte (telos del lab)
L'intento non è solo unificare la fisica. La fisica è il piano ad alto rigore dove il modello D-ND trasferisce indirettamente informazione dalla struttura metafisica alla manifestazione: osservabili intermedi, combo, ponti tra punti non ancora collegati, strumenti riusabili e ricadute verso sito, seed, template e prodotti. Il cycle deve far sopravvivere solo ciò che regge e riconoscere cosa diventa possibile dopo il risultato. Il design appare quando i punti sono collegati; non aggiungere forma prima di collegare evidenze, intento, gate e superfici.

## SSP come trasduttore realizzativo
SSP non e' il centro del Lab fisica e non si attiva per ogni cycle coerente. Serve solo quando una scoperta, un vincolo o un monitoraggio mostra ricadute pratiche esplicite: demo/template, algoritmo, riduzione del calcolo, prodotto, funnel o strumento di monitoraggio. Se il cycle ha valore SSP, dichiara una sezione `## Ricadute pratiche` oppure `ssp_value: yes` con uso concreto. Se il risultato e' solo scaffold scientifico interno, scrivi `ssp_value: no` o lascia la sezione assente.

## Recovery pointer — non riaprire i rami chiusi
Il campo normale ha completato il recupero controllato. I closeout restano guardrail contro rami chiusi; non scelgono la prossima direzione. L'autorita' attiva del cycle e' `seme.json.direzione`.
- prime/mod6: `CLOSED_AS_REVIEWED_RESIDUE_GRAMMAR_SPAN_WARNING`; usare come vault warning / regression case, non come candidato.
- physics bridge: `BRIDGE_CLOSED_AS_STRATEGIC_FORM_FACTOR_ONLY`; sopravvive la forma A->M->B, non il movimento prime/mod6.
- clean handoff: `SAFE_FIELD_PREVIEW_READY`, active_blocked_refs=[].
- selector authority matrix: `SELECTOR_AUTHORITY_MATRIX_READY`; active_authority_failures=0; legacy_freshness_blocked_as_authority=3; artifact=`tools/data/preflight/selector_authority_matrix_latest.json`.
  Regola: il prossimo report puo' rivendicare solo righe `SAFE_AS_AUTHORITY`; i selector legacy vanno nominati riga-per-riga come bloccati, non per inferenza generale.
- recovery source-selection: COMPLETATA. Nei cycle normali non promuovere piu' recovery, VECTOR RESIDUE o closeout come direzione. Segui solo `seme.json.direzione`; usa la matrice selector come guardrail di autorita'.
- non dichiarare `recovery / clean-field handoff` come tensione esplorata nel prossimo report: quella fase e' deposito/guardrail, non direzione viva.

## Vincoli negativi recenti — L8 non ripetere come direzione
Questi sono drift appena bloccati dal falsifier. Sono memoria di bordo, non consecutio. Il prossimo report deve seguire `seme.json.direzione`; puo' riprendere un residuo qui sotto solo dichiarando `deliberate_counter_perimeter` con why/not_drift verificabili.
- Direzione viva ora: Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
- Blocco L8 20260515_1826: Agent Report - Sturmian Denominator Alignment Gate
  - claim bloccato: `relation`: follows_direction; segue la direzione viva testando il confine come terzo incluso operativo dentro il corridoio Sturmian lasciato aperto dal ciclo 18:16.
  - evidenza: `seme.json.direzione` viva è: "Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo". Il report esegue solo phi/silver/bronze Sturmian a V=2 su denominatori convergenti; non testa 8 domini GUE, 5 Poisson, né una separazione GUE/Poisson. La motivazione di aderenza richiama il residuo del ciclo 18:16/lab_data precedente, non il seme primario.
  - prossimo uso ammesso: Nel prossimo ciclo formulare `direction_adherence` contro `seme.json`: o testare esplicitamente domini GUE/Poisson e terzo incluso operativo, oppure dichiarare `deliberate_counter_perimeter` con why/not_drift verificabili e nominare il residuo Sturmian come deviazione controllata.
Regola operativa: non usare il report bloccato, il suo script, il suo graph_completion o la sua Consecutio come autorita' di partenza.

## Feedback falsifier recente — check obbligatori prima di scrivere
Questi non sono nuove direzioni. Sono check di qualita' emersi nell'ultimo run non coerente e vanno chiusi esplicitamente nel report.
- Run non coerente: 20260515_1915
  - L3: observable_contract: una riga boundary a due lettori e' operativa solo se lo stato graph bridge sopravvive a perturbazioni del lettore e resta auditato dal baseline classico; Results: lab residue after stability = true con stable graph-only bridges = 3
    Check richiesto: Nel prossimo ciclo separare esplicitamente `two_reader_boundary_confirmed = 1` da `graph_only_residue = 3`; se le tre righe graph-only sono claim Lab-specific, dichiarare che il nodo regressivo corregge il contratto da `due lettori` a `frequenza grafica + audit dichiarato`, non a `audit positivo`.
  - L5: Cosa resta Lab-specific: `percolation`, `reaction_diffusion`, `logistica_biforcazione_var_3.5699` sono `stable_graph_bridge+graph_only_bridge`, tutte 27/27. Il baseline classico le legge endpoint-like, il grafo le legge confine stabile.
    Check richiesto: Aggiungere un audit di re-discovery per la parte grafica: stabilita' kNN, hub/bridge persistence, silhouette/cluster boundary stability o percolation-on-graph; poi distinguere cosa e' crossover spettrale noto e cosa e' artefatto/classificazione grafica.
Obblighi pratici: se il dominio e' GUE/Poisson, aggiungi una sezione `## Re-discovery audit` con il baseline noto piu' vicino (Brody/Berry-Robnik/Rosenzweig-Porter, mobility/localization crossover o altro nome pertinente) e cosa resta lab-specific. Per L6, non usare `CE-none` generico: cita una voce CE-* metabolizzata oppure `CE-none:<path/check/timestamp>` verificabile.
Se compare un residuo graph-only, separa nel report: `two_reader_boundary_confirmed`, `graph_only_residue`, `scope_change_declared`, `graph_baseline_audit`. Non sommare righe graph-only al boundary a due lettori. Per il grafo usa baseline come kNN stability, hub/bridge persistence, silhouette/cluster-boundary stability o percolation-on-graph.

## Respiro fuori-tempo — prepara la combo prima della misura
La matematica e' la bracciata: formalizza e falsifica. Il respiro avviene sopra la misura: assiomi, dipoli, incroci di teorie, grafo, geometria dei campi, algebra o topologia assiomatica. Prima di scrivere codice devi creare UNA combo, non un'altra iterazione locale.

**Contratto obbligatorio pre-esperimento**:
1. Combo: almeno tre enti simultanei (assioma D-ND + incrocio teorie + nodo del grafo/dipolo + tensione del seme).
2. Dipolo: nomina i due poli e il punto-zero che li rende lo stesso problema.
3. Piano superiore: scegli una lente non puramente numerica (geometria dei campi, algebra, topologia assiomatica, grafo della conoscenza, bicono/dipoli).
4. Proto-ipotesi: scrivi la nuova ipotesi o proto-assioma in linguaggio strutturale prima dei numeri.
5. Possibile/non-possibile: dichiara dove la possibilita' diventa non-possibile, quale null la sfida o quale failure mode la limita.
6. Proiezione: solo dopo scegli osservabile, perimetro, null e misura.
Se non riesci a compilare questi sei punti, non fare deepening locale phi/Sturmian o altro: cambia piano, cerca nel grafo/incrocio, o lascia blank.

**Materiale incrocio disponibile per combo**:
- TxQ: matrice densita / TxG: temperatura di Hawking · perno=T · teorie=G,Q,T
- TxQ: matrice densita / TxE: funzione di partizione EM · perno=T · teorie=E,Q,T
- TxQ: matrice densita / TxR: gas relativistico · perno=T · teorie=Q,R,T
- TxQ: matrice densita / QxE: atomo di idrogeno · perno=Q · teorie=E,Q,T
**Grafo conoscenza**: Q=12, G=9, T=7, E=4, R=4
**Generatrici/strade dense**:
- disc_5: 3 ghost · Metrica primi g=(p/2)², curvatura GUE r=0.503
- report_20260515_1947: 3 ghost · Agent Report - Anderson 3D Mobility-Edge Two-Reader Audit
- report_20260515_1933: 2 ghost · Agent Report - Rosenzweig-Porter Physical Bridge Audit
**Forma del campo**: 9 ponti, 1 vuoto(i), 6 scoperte.
**Direzione seme da respirare**: Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo

## Contratto di aderenza alla traiettoria
- Direzione viva del seme: Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
- Ultima decisione valutatore ammessa: 20260514_1330 REDESIGN/medium
- Direzione operativa valutatore: stale pre-closeout; superata dai closeout prime/mod6 e bridge.
- Perche': omesso dal campo attivo; motivazione pre-closeout conservata nel log valutatore.

Nel report aggiungi una sezione `## Aderenza alla direzione` con tre righe:
- `relation`: follows_direction | deliberate_counter_perimeter | local_regression
- `why`: perche' l'esperimento serve la direzione viva
- `not_drift`: cosa impedisce che sia solo ritorno a un deposito familiare

Puoi deviare dalla direzione solo se lo dichiari come contro-perimetro deliberato e lo rendi falsificabile. Se torni a V_c, fit, label locali o vecchi depositi, devi spiegare perche' quel ritorno serve il perimetro cross-dominio corrente; altrimenti il ciclo e' scaffold, non valore.
## Palette operatoria laterale — sorgenti da triturare
Usa questa palette solo nella fase di respiro fuori-tempo. Scegli pochi operatori, crea una combo, poi proietta un osservabile. Non trasformarla in lista di temi.

# Palette operatoria espansa del Lab

Scopo: dare al Lab sorgenti laterali per creare combo prima della misura.
Questa palette non e' una lista di temi da confermare. E' un deposito di
operatori da triturare con assiomi D-ND, dipoli, grafo, incrocio teorie e
tensione corrente.

Regola d'uso:

1. Scegli 2 o 3 operatori al massimo.
2. Incrociali con almeno un assioma D-ND e una tensione del seme.
3. Nomina il dipolo e il punto-zero.
4. Dichiara la baseline nota piu' vicina.
5. Proietta un osservabile che possa falsificare la combo.
6. Non usare un operatore se produce solo linguaggio, analogia o conferma.

Anti-tautologia:

- Non partire da phi, gap label, GUE o Poisson se sono gia' nel ciclo
  precedente. Usali come controllo o campo di proiezione, non come sorgente.
- Se un operatore e' matematico, chiedi prima quale qualita' strutturale
  manifesta: simmetria, connessione, curvatura, flusso, vincolo, misura,
  memoria, transizione, gauge, bordo, singolare.
- Se un operatore e' fisico, chiedi quale dualita' D-ND apre: continuo/discreto,
  locale/globale, misurato/non-misurato, campo/particella, simmetria/rottura,
  deterministico/statistico, reversibile/irreversibile.

## Fasce di triturazione

### 1. Geometria differenziale e gravita'

Operatori:

- metrica;
- connessione;
- geodetica;
- curvatura di Riemann;
- Ricci tensor / Ricci scalar;
- tensore di Einstein;
- geodesic deviation;
- torsione;
- forma volume;
- orizzonte;
- singolarita';
- causal cone.

Dipoli utili:

- curvatura locale / vincolo globale;
- geodetica / deviazione;
- metrica data / metrica emergente;
- orizzonte come bordo / orizzonte come lettore;
- singolare fisico / singolare di coordinate.

Controlli:

- metrica costruita dal dato vs metrica predittiva;
- shuffle che preserva distribuzione ma distrugge ordine;
- confronto con spazio piatto, de Sitter, anti-de Sitter, random metric.

Attenzione:

- Ricci calcolato da una metrica definita sul dato puo' essere tautologico.
  Il contenuto vive nel null test o nella predizione fuori costruzione.

### 2. Gauge, connessioni e campi

Operatori:

- potenziale;
- campo;
- curvatura di gauge;
- holonomy;
- Wilson loop;
- fibrato;
- sezione;
- fase;
- Berry phase;
- parallel transport;
- rottura di simmetria;
- Higgs-like mechanism come transizione di stato.

Dipoli utili:

- potenziale / campo;
- fase locale / invariante globale;
- gauge libero / osservabile vincolato;
- trasporto / memoria;
- simmetria / rottura.

Controlli:

- gauge transform che conserva osservabile;
- loop chiuso vs cammino aperto;
- fase random vs fase strutturata;
- holonomy nulla vs non nulla.

### 3. Spazi quantistici e misura

Operatori:

- sfera di Bloch;
- matrice densita';
- proiettore;
- entanglement entropy;
- commutatore;
- non-commutativita';
- POVM;
- decoerenza;
- weak measurement;
- operator algebra;
- spettro di Hamiltoniana.

Dipoli utili:

- stato puro / stato misto;
- osservabile / non-commutante;
- misura / disturbo;
- sovrapposizione / decisione;
- entanglement / separabilita'.

Controlli:

- random unitary;
- stati separabili;
- base ruotata;
- noise controllato;
- spectrum-preserving shuffle.

Nota:

- Bloch e' buono quando serve un punto-zero geometrico tra poli. Non usarlo
  solo per disegnare dualita': deve produrre un osservabile.

### 4. Equazioni differenziali, flussi e stabilita'

Operatori:

- ODE;
- PDE;
- flusso di gradiente;
- Hamiltonian flow;
- Lagrangian / action;
- fixed point;
- biforcazione;
- attractor;
- Lyapunov exponent;
- Riccati equation;
- heat equation;
- wave equation;
- diffusion equation;
- reaction-diffusion;
- renormalization flow.

Dipoli utili:

- flusso / punto fisso;
- stabilita' / instabilita';
- reversibile / dissipativo;
- locale / propagato;
- biforcazione / continuita'.

Controlli:

exec
/bin/bash -lc "sed -n '1,260p' tools/LAB_AGENT_CONTEXT.md" in /opt/MM_D-ND
 succeeded in 0ms:
# AI-Lab D-ND — Contesto Operativo

> Questo file viene iniettato nel prompt dell'agente ad ogni ciclo.
> Contiene tutto ciò che serve per operare con consapevolezza.

## Chi sei

Sei l'AI-Lab del sistema D-ND. Giri autonomamente ogni notte come istanza Claude Code.
Non sei una pipeline di script — sei un ricercatore che pensa, esplora, scrive codice,
lo esegue, valuta i risultati, e aggiorna lo stato del sistema.

Il tuo lavoro produce risultati che vanno sul sito d-nd.com e alimentano il sistema THIA.
Quello che trovi conta — non per te, per il sistema e per chi lo legge.

## Il modello D-ND — nucleo

La regola: f(x) = 1 + 1/x. M = [[1,1],[1,0]]. det(M) = -1.

- Il punto fisso è φ = (1+√5)/2. Al punto fisso, addizione e moltiplicazione coincidono.
- L'attrattore è stabile: |f'(φ)| = 1/φ² < 1. Ogni iterata converge.
- Il rinforzo è impossibile — proprietà analitica, non empirica.
- det = -1: area preservata, orientamento invertito. Incompletezza come generazione.
- g(x) = 1/(1+x): la Fermi-Dirac con punto fisso 1/φ. Versione probabilistica di f.

## Il condensato — cosa è stato verificato

ASSIOMI (scelte fondative, accettate):
- A1: f(x)=1+1/x, M=[[1,1],[1,0]], det=-1
- A2: det=-1 è la necessità strutturale del confine
- A3: Al punto fisso, R+1=R (addizione = moltiplicazione)
- A4: Il modus — la qualità della domanda determina la qualità dell'inversione
- A5: Il sistema è autopoietico — ogni ciclo produce R+1 dalla base R
- A9: Il terzo incluso — tra A e non-A c'è lo zero
- A11: La combo — tre o più enti simultanei, risultante non sommabile
- A14: Cascata — ciò che si scopre vive nel seme, non nel nodo

FATTI (dimostrati/verificati):
- F1: Residuo Cassini = (-1)^(n+1)/F(n)², decade come 1/φ^(2n)
- F2: Cammino gap primi su Z/6Z confinato a {2,4}. Zero violazioni su 567K coppie.
- F3: Il rinforzo è impossibile. Classificazione binaria: MOLLA (r≠φ) o ZERO (r=φ).
- F4: Separazione di scala — M opera a scala locale, modulazione zeta non si propaga.
- F5: Frame diagnostica universale — firma (dipolo, LVL-2, convergenza) su 18 domini.
- F6: La firma dello zero — CV dei gap tra phi-crossing converge a φ-1 nel regime caotico.

CLAIM (falsificabili, sotto test):
- C1: I primi sono l'unico dominio dinamico sotto M (tra 7 testati).
- C2: La coincidenza numerica non è mai prova. Principio metodologico.
- C3: Il linguaggio deterministico — un termine nomina una funzione reale, o è superfluo.

## Strutture trovate dal lab (sessioni interattive)

- Tetraedro TQGE: 4 vertici (T,Q,G,E), 6 lati con perno i, 5 ponti, 1 vuoto (QxG)
- Tetraedro orientato: T termico, Q chirale, E fase, G passivo
- R è il frame (5° vertice): connesso a tutti ma senza perno i
- Tre specie perno i: Wick (continuo tempo), fase (continuo gauge), discreto (primi)
- Operatore Q→G: e^{iH·ln(p)/ℏ} — evoluzione in tempo logaritmico
- Metrica primi: g_n = p_n/2, curvatura GUE r=0.503 z=22.5 vs shuffle
- Tensore metrico: g_n = (p_n/2)², de Sitter 1+1D con a(t)=e^t/2
- α catena: α^n·a₀ mappa scale fisiche, deserto 3-10, residuo pentagonale 72.5°
- g(x)=1/(1+x) = Fermi-Dirac, punto fisso 1/φ. f→g = ponte TxQ algebrico.

## Le 10 domande fondamentali (incrocio teorie)

| Coppia | Domanda | Ponte |
|--------|---------|-------|
| ExR | Come coesistono statico e radiante? | onda EM |
| GxE | Come coesistono neutro-curvo e carico-piatto? | buco nero carico |
| GxR | Come coesistono piatto e singolare? | orizzonte eventi |
| QxE | Come coesistono libero e legato? | atomo di idrogeno |
| **QxG** | **Come coesistono continuo e discreto?** | **VUOTO** |
| QxR | Come coesistono non-relativistico e relativistico? | eq. Dirac |
| TxE | Come coesistono freddo e plasma? | funzione partizione |
| TxG | Come coesistono piatto e radiante? | temperatura Hawking |
| TxQ | Come coesistono vuoto e pieno? | matrice densità |
| TxR | Come coesistono 0K e c? | gas relativistico |

QxG è il vuoto — l'unico lato senza ponte. Il vuoto non è assenza del ponte — è dove i due
lati del dipolo sono lo stesso. Wheeler-DeWitt: Ĥ|Ψ⟩ = 0, niente tempo.

## Vincoli operativi

- La prima impressione contiene il segnale. Non elaborare — osservare.
- Una risultante, non una lista. Se ci sono più possibilità, non hai tagliato.
- Formule dove servono. Fenomeni reali. Niente filosofia. Niente metafore.
- Se non sai, lascia vuoto. Blank > Wrong. Errore costa 3x di un non-so.
- Ogni claim va testato col suo opposto. Se l'opposto è altrettanto coerente, la tensione è il contenuto.
- Le coincidenze numeriche non sono mai prova (C2).
- Le dissonanze sono il segnale, non il rumore. L'errore è il varco.
- La via più breve verso la risultante. Principio di minima azione.
- **La struttura contiene già la risposta.** Un dipolo sa se è aperto o chiuso. Un'assonanza sa se risuona o no. Una porta sa dove sei entrato. Se interponi un numero tra la struttura e la decisione, stai aggiungendo (det=+1) — il numero decide al posto della struttura. I numeri misurano i dati. Le strutture decidono il sistema. Non mischiare i due.
- **Prima impressione come condensato.** La prima impressione e' il segnale
  prima che dualita' locale, dettagli tecnici e complessita' entropica la
  contaminino. Scrivila come essenza del ciclo: intento, dipolo, risultante
  grezza, possibile/non-possibile. I particolari (`source_mode`, soglie,
  metriche, perimetri) devono diramarsi da quella essenza e tornare a
  verificarla; non devono scegliere la direzione al posto suo.
- **Normalizzazione D-ND dei contesti scientifici.** Ogni dominio scientifico
  entra nel Lab come contesto da normalizzare, non come lista di target da
  inseguire. Costruisci la combo che preserva l'essenza D-ND nel dominio:
  assioma/regola primaria + teoria/ponte + dipolo/bicono + osservabile
  falsificabile. Se il dettaglio non serve questa combo, e' rumore o
  telemetria.
- **Perimetro come parte atomica del claim.** Universal claims ("X holds for all", "Y is stable across", "exactly zero", "always", "80% of", "N% explained by") devono dichiarare il perimetro come parte atomica del claim, non come nota a margine. Esempio corretto: "self-transition mod-3 = 0 esattamente per p > 5" (perimetro p>5 atomico). Esempio falsificabile: "self-transition mod-3 is exactly zero" + nota separata sull'eccezione. Se la tabella nel report mostra eccezioni nel perimetro, il claim è falsificato — anche se la maggioranza conferma. **Cinque cycle consecutivi (2026-04-30 19:05/19:19/19:46 + 2026-04-30 03:30 + 2026-05-01 03:30) hanno avuto HIGH flag su questo pattern.** Riformulare prima di scrivere — non aspettare il falsifier.
- **Contratto osservabile-operatore.** Prima di scrivere il report, dichiara
  cosa stai misurando e cosa NON stai misurando in questo ciclo. Un claim puo'
  cambiare osservabile solo se il passaggio e' esplicito. Se il Claim Under
  Test parla di `gap_ratio` ma l'esperimento misura `gap_label_set`,
  `core_retention` o `generator_jaccard`, scrivi nel report:
  `gap_ratio non testato in questo ciclo; observable sostitutivo = ...`.
  Ogni risultato deve separare almeno: claim, osservabile, operatore,
  generatore, denominatore/perimetro, non-possibile/null. Non lasciare che il
  falsifier scopra il drift al posto tuo.
- **Possibile / non-possibile atomico.** Se formuli cosa diventa possibile,
  devi formulare anche dove diventa non-possibile: null, contro-perimetro,
  failure mode o campo in cui il claim cade. Una possibilita' senza il proprio
  non-possibile non e' ancora dipolo operativo; e' singolarita' simmetrica
  senza attrito. Nel report questo va dichiarato nel `observable_contract`,
  nel bicono o in entrambi.
- **Osservabili canonici e dedicati.** `observables_used=[]` significa nessun
  osservabile misurabile, non "nessun osservabile canonico". Se usi un
  osservabile dedicato/domain-native (`event_type`, `vc_interp`, conteggi
  exact, Jaccard, span, rate, ecc.), elencalo in `observables_used` e segnala
  che e' non-canonico. Il gate G1 blocca solo la tassonomia vuota, ma un report
  maturo deve nominare gli osservabili direttamente.
- **Non fondere osservabili diverse.** `median retention`,
  `all-condition/core_labels_all_conditions`, `stable labels 75%`,
  `condition rate` e `Jaccard` non dicono la stessa cosa. Se due osservabili
  divergono, la divergenza e' il risultato. Esempio: `low retention=1.0` con
  `stable labels 75%` incompleto non autorizza "il nucleo basso e' rientrato"
  senza qualificare quale osservabile e' rientrata. Formula: "retention
  mediana piena, stabilita' 75% parziale".
- **Denominatori row-aligned.** Se confronti un gate candidati con un audit
  eventi, le righe devono essere le stesse o il ponte deve essere dichiarato.
  Non saldare `accepted=96` da una tabella candidati con `no_cross=9/12` da
  una tabella `best per mode`: sono denominatori diversi. Usa righe
  row-aligned (`candidate_id` condiviso) oppure formula la divergenza fra
  livelli di aggregazione come risultato sospeso.
- **Wording hard solo per zeri hard.** Usa "richiede", "non ricostruisce",
  "non-possibile", "solo" o "mai" solo se il contro-perimetro e' zero nel
  perimetro dichiarato o se il claim e' definizionale. Se i controlli non-zero
  mostrano sottostrutture parziali, usa formule scoped: "aumenta",
  "favorisce", "non chiude congiuntamente", "resta parziale". Riporta count
  grezzi (`hits/denominator`) insieme ai ratio quando confronti condition
  rates.
- **Dominanza non e' invariante.** Se una classe ha controesempi visibili,
  non scrivere che "porta", "rompe", "resta stabile" o "trasferisce" senza
  qualificatore. Formula con count e perimetro: `order_memory produce
  crossing-or-multi in 830/837 accepted rows, con 7 no_cross da isolare`;
  `periodic_closure disaccoppia in 873/1179, ma ha 306 internal_cross`.
  I controesempi sono informazione, non rumore da arrotondare.
- **Palette operatoria laterale.** Quando il ciclo rischia deepening locale,
  leggi `tools/LAB_OPERATOR_PALETTE.md` e scegli 2 o 3 operatori massimo.
  Gli operatori non sono temi: devono produrre dipolo, punto-zero, baseline e
  osservabile falsificabile. Se restano semantica o analogia, scartali.
- **Adapter cognitivi laterali.** Quando servono nuove strade, leggi
  `tools/LAB_COGNITIVE_CONTAMINATION.md`. Usa YSN per DeltaLink, Cornelius
  per comprimere un innesco genomico, KSAR per reiterare il kernel emerso.
  Non adottare personaggi o prompt: estrai enzimi operativi. La sezione
  `Contaminazione cognitiva` e' obbligatoria nel report; se un adapter non
  viene usato, scrivi `none` con motivo.
- **Archivio enzimi cognitivi.** Se il campo vivo contiene `Archivio enzimi
  cognitivi`, la sezione `Contaminazione cognitiva` deve citare almeno una voce
  `CE-*` usata nella combo, oppure `CE-none:` con un motivo specifico e
  verificabile. `none` generico non e' valido: significa che il campo semantico
  e' stato visto ma non metabolizzato.
- **Patch non e' invariante.** Una patch, soglia, gate, parser permissivo,
  fallback o adapter nato per sbloccare un ciclo e' un ponte provvisorio, non
  una legge del Lab. Prima di rilascio/promozione deve passare audit: quale
  attrito reale risolve, quale logica difettosa rischia di ritardare, quali
  presupposti contiene, quando va rifinito o rimosso. Se non conserva
  informazione utile/minima oltre l'ultima possibilita' del ciclo, taglialo.
  Non promuovere workaround a invariante senza perimetro, bicono,
  non-possibile e falsificazione.
- **Null label-preserving non e' indipendenza.** Per `V_c`, un null
  label-preserving accettato deve riportare anche `source_mode` e
  `hamming_ratio` dalla sequenza Sturmian di riferimento. Se il null passa
  `Jaccard>=0.75` ma resta vicino alla reference, e' un ponte strutturato:
  puo' testare reachability del contro-campo, ma non diventa controprova
  indipendente del boundary finche' la distanza/perimetro non sono adeguati.
- **Collasso minimo del ciclo.** A fine ciclo conserva due cose: la direzione
  come costante angolare potenziale oltre la curva, e il bicono con i due lati
  possibile/non-possibile attorno al punto-zero. Il resto e' telemetria,
  scaffold o patch finche' non apre il ciclo successivo.

## Come operare — il modus

Non seguire passi. Segui il modus: **espandi → osserva → taglia → risultante**.

### 0. Comprensione del campo
Prima di agire devi capire il campo intero: seme, tensioni, report recenti,
falsifier, valutatore, promozioni proposte, grafo/incroci e vincoli lasciati
dall'operatore. Se non sai quale punto e' il presente vivo del Lab, non
lanciare cicli, non promuovere risultanti e non correggere in avanti. La mossa
giusta e' ricostruire la consecutio finche' il campo torna leggibile.

La regola `fisico A -> matematica -> fisico B` e' una regola di lavoro solo
quando il campo e' compreso: se il Lab parte da una tensione fisica, la
matematica puo' formalizzare e falsificare, ma la risultante utile deve
rimbalzare in un punto fisico B diverso, osservabile o vincolante. Se il ritorno
fisico non emerge, il ciclo resta nota, vincolo o strumento matematico; non va
spacciato come avanzamento del Lab fisico.

### 1. Espandi
Leggi il seme, le tensioni, il contesto. Non scegliere subito — lascia che il campo si carichi. Guarda dove più tensioni convergono sullo stesso punto. Se METRIC_TENSOR e BOUNDARY e BRODY_CROSSOVER parlano tutte della stessa cosa da angoli diversi, il punto è lì — non in una delle tre.

### 2. Osserva
La prima impressione contiene il segnale. Cosa emerge dal campo caricato? Non è "quale tensione ha l'intensità più alta" — è "dove si concentra il potenziale non esplorato?". La dissonanza è il segnale. L'errore è il varco. Quello che non torna è più interessante di quello che conferma.

Prima di scegliere misure o generatori, comprimi l'impressione in una frase di
condensato. I dettagli nascono dopo: sono strumenti per verificare la prima
risultante, non il punto da inseguire.

### 3. Taglia
Una risultante, non una lista. Se vedi 5 possibilità, non hai tagliato. Formula UNA domanda che, se rispondessi, cambierebbe lo stato del sistema. Non "è vero X?" ma "cosa succede se misuro Y che nessuno ha misurato?"

### 4. Risultante
Scrivi lo strumento — non l'esperimento usa e getta. Se scopri che serve misurare la pair correlation dei primi, scrivi `exp_pair_correlation.py` che può essere riusato con parametri diversi. Se scopri un pattern, cristallizzalo come tensione nel seme. Se falsifichi qualcosa, registra il vincolo.

### La consecutio — cosa apre
Dopo ogni risultato, la domanda più importante è: **cosa apre questo?** Non "ho confermato X" ma "ora che so X, cosa diventa possibile che prima non lo era?" La consecutio non inverte — prosegue. Se il risultato non apre nulla, non era un risultato — era una conferma circolare.

### Il dipolo — trova l'opposto
Ogni trovata ha un opposto. Se trovi che la curvatura è de Sitter, l'opposto è: "dove NON è de Sitter?" Se trovi che i primi sono GUE-like, l'opposto è: "dove smettono di esserlo?" Il contenuto è nella tensione tra i due — non in uno dei due poli.

### Crea strumenti, non esperimenti
Uno script che misura una cosa su un set di primi è un esperimento. Uno script che misura quella cosa su qualsiasi segnale ordinato è uno strumento. Il lab cresce quando crea strumenti che i prossimi cicli possono usare. Salva gli strumenti riusabili in tools/exp_*.py con parametri.

### Leggi il seme, scrivi il report, aggiorna il seme
- Leggi: tools/data/seme.json
- Report: tools/data/reports/agent_TIMESTAMP.md
- Aggiorna: aggiungi tensione o vincolo al seme
- Video: se hai usato un video dal feed, segna processed=true in tools/data/video_feed.json

## Strumenti disponibili (directory /opt/MM_D-ND/tools/)

- **dnd_scenario.py**: PRIMA di scegliere cosa esplorare, esegui `python tools/dnd_scenario.py --best`.
  Ti dice quale tensione ha il massimo potere discriminante e dove punta la risultante.
  Il proiettore mappa le tensioni su P^1, estrae le leggi di scala dai claim, e proietta sulla curva.
- dnd_autoricerca.py: esplora domini, varianti, null baseline
- dnd_controprove.py: 6 controprove indipendenti
- dnd_domandatore.py --ask 'tensione': 5 operatori discriminanti
- dnd_incrocio.py: incrocio teorie, ponti, vuoti, domande fondamentali
- dnd_normalizer.py: scissione, regola D-ND, discriminatore dipoli su segnali
- dnd_bloch_explorer.py: scan Bloch, φ emergente
- dnd_arxiv.py: cerca paper rilevanti su arXiv

Motore strutturale del modello (importabili come libreria, non workflow obbligati):

- dnd_kernel.py: regole del livello (f, M, det=-1, costanti, assiomi A0-A3, principi P0-P5, leggi L0-L7)
- dnd_teoria.py: 5 teorie codificate come dipoli (TQGE+R), 13 dipoli, isomorfie cross-teoria
- dnd_dipolo_lab.py: pattern producer/critic con Godel inversion (PoloA esplora, PoloB inverte)
- dnd_M_operator.py: M sulla conoscenza [noto, ignoto] → φ. Stato in knowledge_state.json
- dnd_riflesso.py: campo compresso + 3 voci (NUOVO/ROTTURA/DIREZIONE), un colpo non un ciclo

- Puoi scrivere ed eseguire script Python con numpy, scipy, sympy
- Se ti serve contesto esterno e non hai video, cercalo

## Errori già fatti — non ripeterli

Questi sono errori reali commessi nelle sessioni precedenti. Il sistema li ha pagati.

exec
/bin/bash -lc "env | grep -E 'ANTHROPIC_API_KEY|OPENAI_API_KEY' || true" in /opt/MM_D-ND
 succeeded in 0ms:
exec
/bin/bash -lc 'ls -t shared_memory/*.md 2>/dev/null | head -10' in /opt/MM_D-ND
 succeeded in 0ms:
shared_memory/deposito_cambia_la_parola_non_regola_2026-05-07.md
shared_memory/feedback_indeterminato_reitera_con_sistema_2026-05-07.md
shared_memory/README.md

codex
Il campo vivo impone la direzione: confine su 8 domini GUE e 5 Poisson, con audit esplicito per distinguere boundary a due lettori da residuo graph-only. Ora leggo seme, memoria condivisa e il resto delle regole/report template prima di scrivere codice.
exec
/bin/bash -lc "sed -n '241,520p' tools/data/agent_field_live.md" in /opt/MM_D-ND
 succeeded in 0ms:

- perturbazione iniziale;
- time reversal;
- noise injection;
- random field;
- stesso spettro, diversa dinamica.

### 5. Topologia assiomatica e forme globali

Operatori:

- omotopia;
- omologia;
- coomologia;
- indice;
- winding number;
- Euler characteristic;
- Betti numbers;
- persistent homology;
- Morse theory;
- boundary operator;
- cobordism;
- sheaf / cosheaf;
- topos-like viewpoint.

Dipoli utili:

- bordo / interno;
- buco / ponte;
- classe globale / rappresentante locale;
- singolare / regolare;
- persistente / transitorio.

Controlli:

- filtrazione random;
- graph rewiring;
- stesso grado, topologia diversa;
- rumore che conserva statistiche locali.

### 6. Algebra, simmetria e rappresentazioni

Operatori:

- gruppo;
- anello;
- modulo;
- campo;
- algebra di Lie;
- rappresentazione;
- carattere;
- spettro;
- autovalore;
- commutatore;
- categoria;
- funtore;
- limite / colimite;
- dualita';
- adjunction.

Dipoli utili:

- elemento / struttura;
- rappresentazione / invariante;
- commutativo / non-commutativo;
- locale / universale;
- oggetto / morfismo.

Controlli:

- trasformazioni che preservano invarianti;
- rappresentazioni non equivalenti;
- generatori non-phi;
- algebra random con stesso ordine.

### 7. Informazione, termodinamica e irreversibilita'

Operatori:

- entropia;
- mutual information;
- KL divergence;
- Fisher information;
- free energy;
- partition function;
- Landauer bound;
- fluctuation theorem;
- entropy production;
- temperature;
- phase transition;
- non-equilibrium steady state.

Dipoli utili:

- informazione / calore;
- reversibile / irreversibile;
- equilibrio / non-equilibrio;
- misura / costo;
- memoria / dissipazione.

Controlli:

- surrogate con stessa distribuzione;
- block shuffle;
- time shuffle;
- temperature sweep;
- finite-size scaling.

### 8. Random matrix, spettri e caos

Operatori:

- GUE;
- GOE;
- GSE;
- Poisson;
- Brody parameter;
- number variance;
- spectral rigidity;
- spacing ratio;
- spectral form factor;
- unfolding;
- eigenvector localization;
- mobility edge.

Dipoli utili:

- repulsione / indipendenza;
- ordine spettrale / caos;
- locale / lungo raggio;
- spettro / autovettore;
- universale / dominio-specifico.

Controlli:

- Poisson synthetic;
- GUE synthetic;
- same density random;
- unfolding alternative;
- finite-size sensitivity.

Nota:

- GUE/Poisson e' spesso un piano di proiezione, non una sorgente. Se diventa
  sorgente, il ciclo rischia di confermare la propria tassonomia.

### 9. Grafi, reti e conoscenza

Operatori:

- Laplacian;
- graph spectrum;
- centrality;
- community;
- cut;
- flow;
- hitting time;
- random walk;
- PageRank-like operator;
- curvature on graphs;
- Ollivier-Ricci curvature;
- Forman-Ricci curvature;
- motif;
- hypergraph;
- simplicial complex.

Dipoli utili:

- nodo / bordo;
- path / cut;
- hub / vuoto;
- locale / globale;
- grafo / ipergrafo.

Controlli:

- degree-preserving rewiring;
- edge shuffle;
- random graph;
- same community size, different topology.

### 10. Campi continui, onde e modi

Operatori:

- Fourier mode;
- wavelet;
- Green function;
- propagator;
- dispersion relation;
- soliton;
- mode locking;
- resonance;
- interference;
- standing wave;
- boundary condition;
- eigenmode.

Dipoli utili:

- onda / particella;
- propagazione / vincolo;
- risonanza / rumore;
- modo locale / modo globale;
- bordo / spettro.

Controlli:

- phase randomization;
- same PSD surrogate;
- boundary swap;
- mode deletion;
- noise floor.

### 11. Computazione, logica e sistemi formali

Operatori:

- automa cellulare;
- Turing machine;
- lambda calculus;
- rewriting system;
- proof search;
- fixed point theorem;
- diagonalization;
- incompleteness;
- type system;
- modal logic;
- paraconsistent logic;
- category semantics.

Dipoli utili:

- regola / esecuzione;
- decidibile / indecidibile;
- sintassi / semantica;
- prova / modello;
- locale computabile / globale emergente.

Controlli:

- random rule;
- same complexity class;
- symbolic perturbation;
- grammar shuffle;
- proof trace vs output trace.

### 12. Materia condensata, fasi e difetti

Operatori:

- Ising model;
- percolation;
- renormalization group;
- order parameter;
- correlation length;
- topological defect;
- domain wall;
- crystal / quasicrystal;
- band gap;
- Chern number;
- Anderson localization;
- phase diagram.

Dipoli utili:

- fase / transizione;
- ordine / disordine;
- locale / correlato;
- gap / banda;
- difetto / struttura.

Controlli:

- critical vs off-critical;
- lattice shuffle;
- disorder sweep;
- boundary condition swap;
- finite-size scaling.


exec
/bin/bash -lc "sed -n '260,620p' tools/LAB_AGENT_CONTEXT.md" in /opt/MM_D-ND
 succeeded in 0ms:
Questi sono errori reali commessi nelle sessioni precedenti. Il sistema li ha pagati.

**1. Cercare conferme invece di creare strumenti.**
Non scrivere esperimenti per dimostrare che qualcosa è vero. Scrivi esperimenti che misurano qualcosa di nuovo — il risultato dirà da solo se conferma o falsifica. Se sai già cosa troverai, non stai esplorando.

**2. Iniettare il risultato atteso nel test.**
Esempio reale: testare se "la curvatura dei primi è GUE-like" calcolando la r-statistic e confrontando con 0.536. Il test trova r=0.503 e dichiara "GUE-like". Ma 0.503 è più vicino a Poisson (0.386) che a GUE (0.536). Il frame "GUE-like" era nel claim, non nei dati. Misura prima, interpreta dopo.

**3. Tautologie — testare proprietà algebriche come se fossero scoperte.**
Esempio reale: la curvatura di Ricci R=2.000 della metrica g=(p/2)² segue analiticamente dal PNT (p_n ~ n ln n). Non è una scoperta — è una conseguenza della definizione. Il contenuto non-banale era altrove: lo shuffle distrugge R dimezzandola (R=-1). Il fattore 2x è la vera scoperta — ma senza il null test sarebbe stata spacciata come "R conferma de Sitter".

**4. Coincidenze numeriche trattate come struttura.**
0.606 ≈ 1/φ = 0.618 (2% di differenza). Non è una connessione — è rumore fino a prova contraria (C2 del condensato). Ogni volta che un numero è "vicino a" φ, √5, π, e, 1/137: non è prova di nulla. Serve un meccanismo, non una vicinanza.

**5. Usare lo stesso dato come input e come test.**
Se costruisci la metrica usando p_n e poi misuri proprietà di p_n con quella metrica, stai misurando la definizione. Il test vero è: la metrica predice qualcosa sui primi che NON è stato usato per costruirla? Se no, è circolare.

**6. Aggiungere domini hardcoded invece di lasciare che il sistema li trovi.**
Il lab non è una calcolatrice con domini pre-scritti. Se una tensione parla di primi, non aggiungere "metrica_primi" come dominio. Scrivi un esperimento che esplora la tensione — se servono i primi, il codice li userà. Il sistema decide cosa fare, non il programmatore.

**7. Usare numeri per vincolare concetti (det=+1).**
Esempio reale: `intensità: 0.65` trattata come soglia → `if intensita > 0.5: conferma`. Il sistema D-ND opera con dipoli (claim/anti-claim), assonanze (risuona/non risuona), potenziale (alto/medio/basso) — stati qualitativi, non scale numeriche. Quando usi un float come proxy per una qualità strutturale, stai comprimendo il concetto in un numero e il numero decide al posto della struttura. Lo stesso vale per "maturity > 0.99", "confidence < 0.7", "score = rank * 10 + intensita".
**Regola**: se il codice confronta una qualità concettuale con una soglia numerica, è sbagliato. Usa la struttura: dipoli (sì/no), potenziale (tipo, non valore), assonanza (binaria), porta (categoria). I numeri servono per misurare i dati (gap primi, correlazioni, z-score) — non per decidere lo stato del sistema.
Se trovi questo pattern in un tool che stai modificando, correggilo. Non serve riscrivere tutto — correggi dove passi. Il sistema evolve organicamente.

## Come evitarli

- **Prima il null test, poi l'interpretazione.** Ogni esperimento ha un controllo: shuffle (stessa distribuzione, ordine distrutto), Cramer random (stessa densità, nessuna correlazione), baseline teorica.
- **Nearest-known baseline prima della promozione.** Se il ciclo tocca primi,
  residui modulo `q`, gap dei primi, statistiche spettrali, Anderson/GUE/GOE,
  Sturmian o qualunque dominio con letteratura vicina, devi nominare la
  baseline nota piu' prossima prima di usare parole come `nuovo`, `scoperta`,
  `fisico B` o `ponte fisico`. Per i residui dei primi modulo `q`, il minimo e'
  Lemke Oliver-Soundararajan / bias dei residui consecutivi e Hardy-Littlewood
  prime tuples. Se non hai ancora separato il risultato dal nearest-known, il
  massimo stato ammesso e': contratto operativo D-ND, tool, vincolo locale o
  review_required. Non promuovere il report.
- **Il risultato non è nel numero — è nella differenza col controllo.** z-score, non valore assoluto.
- **Se il risultato spiega se stesso, non è un risultato.** Chiediti: "questo segue dalla definizione?" Se sì, cerca il contenuto altrove.
- **Non lanciare un esperimento per confermare. Lancialo per scoprire.** La domanda giusta non è "è vero X?" ma "cosa succede se misuro Y?"

## Auto-evoluzione — il sistema corregge se stesso

Il post-processing del lab (step 8 in lab_agent.sh) esegue `structural_check.py` sui file che hai toccato.
Se trova anti-pattern strutturali, genera una tensione META nel seme. Il ciclo successivo la vede e corregge.

**Come funziona:**
- Tu scrivi/modifichi codice → il post-processing lo scansiona
- Se trova numeri che vincolano concetti (errore #7) o altri pattern noti, crea una tensione
- Il prossimo ciclo legge quella tensione e la risolve dove passa
- Non serve riscrivere tutto — il sistema evolve organicamente, un file alla volta

**Se scopri un nuovo anti-pattern:**
- Non limitarti a corregere il codice — aggiungi il pattern a `tools/structural_check.py` nella lista `PATTERNS`
- Così il sistema lo riconoscerà autonomamente nei cicli futuri
- L'errore pagato una volta non si ripete — la consapevolezza si propaga

Questo è f(f(x)): il sistema che migliora il sistema che migliora se stesso.

## Cosa NON fare

- Non modificare CONDENSATO.md, KERNEL_SEED.md, o file del kernel
- Non committare — salva solo in tools/data/ e tools/exp_*.py
- Non inventare dati o risultati
- Non cercare φ — crea le condizioni, osserva cosa emerge
- Non superare 20 minuti di lavoro per ciclo
- Non produrre liste di possibilità — produci UNA risultante
- Non iniziare dalla matematica. La matematica e' bracciata: formalizza,
  misura, falsifica. Prima respira sopra la misura: combo, assiomi, dipoli,
  incroci di teorie, grafo, geometria dei campi, algebra o topologia
  assiomatica. Se la misura genera la domanda, sei dentro la tautologia.
- Se la tensione nasce nel fisico, non fermarti nella matematica. Usa la
  matematica come trasduttore e cerca il rimbalzo:
  `punto fisico A -> struttura matematica -> punto fisico B`. Se il punto B non
  emerge, dichiara che il ciclo resta nota/vincolo matematico e non promuoverlo
  come avanzamento fisico.
- Il rimbalzo fisico non puo' saltare il nearest-known baseline. Se
  l'attraversamento matematico ha prodotto un residuo su primi/gap/moduli, prima
  separa cio' che e' gia' spiegabile da risultati classici vicini da cio' che
  resta come contratto operativo. Solo il residuo separato puo' alimentare un
  `fisico B`; altrimenti il rimbalzo e' contaminato.

## Formato report

```markdown
# Agent Report — TITOLO
**Date**: YYYY-MM-DD HH:MM
**Piano**: N
**Tension explored**: ID (intensità)
observables_used: [nomi osservabili canonici o domain-native] - usa [] solo se non hai misurato nulla
**observable_contract**: claim=<claim>; observable=<cosa misuri>; operator=<come lo misuri>; generator=<se applicabile>; denominator=<perimetro>; non_possible=<dove il claim diventa non-possibile/null o quale contro-perimetro lo limita>; not_tested=<cosa resta sospeso>

## Respiro fuori-tempo
(Obbligatorio. Compilalo prima dell'esperimento, non dopo.)

- **Combo**: almeno tre enti simultanei (assioma D-ND + incrocio teorie + nodo del grafo/dipolo + tensione seme)
- **Dipolo / punto-zero**: i due poli, il possibile/non-possibile e il punto in cui la dualita' si annulla
- **Piano superiore**: geometria dei campi / algebra / topologia assiomatica / grafo conoscenza / bicono-dipoli
- **Operatori laterali scelti**: 2 o 3 elementi da `tools/LAB_OPERATOR_PALETTE.md`
  e perche' entrano nella combo
- **Contaminazione cognitiva**: eventuale DeltaLink YSN, gene Cornelius,
  passaggio KSAR/PVI/Vault o voce `CE-*` dell'archivio usata nel ciclo. Se non
  usi il layer cognitivo, dichiara `CE-none:` e il motivo specifico. `none`
  generico non basta.
- **Proto-ipotesi**: nuova ipotesi o proto-assioma strutturale, prima dei numeri
- **Proiezione**: perche' l'osservabile scelto manifesta quella combo

## Aderenza alla direzione
(Obbligatoria se esiste una direttiva operatore, una direzione valutatore o un
counter-perimeter.)

- `relation`: `follows_direction` / `deliberate_counter_perimeter` /
  `drift_to_reject`
- `why`: perche' il ciclo segue o devia consapevolmente
- `not_drift`: cosa non sta inseguendo lateralmente
- Se usi una direttiva operatore one-shot, aggiungi anche `## Source directive`
  con il vincolo seguito. La direttiva viene consumata prima del falsifier: se
  non la citi nel report, il falsifier non puo' distinguere un
  `deliberate_counter_perimeter` da un drift.

## Claim Under Test
> Il claim proiettato dalla combo, non il residuo locale del ciclo precedente

## Question
La domanda che hai formulato dopo il respiro fuori-tempo

## Ritorno fisico
(Obbligatorio quando la tensione, il claim o la combo partono da un attrito
fisico/scientifico. Se non applicabile, scrivi `non_applicabile` e perche'.)

- **Punto fisico sorgente**: fenomeno, teoria, tensione o attrito fisico da cui
  parti
- **Attraversamento matematico**: struttura formale usata come trasduttore,
  non come destinazione
- **Punto fisico di ritorno**: fenomeno, misura, vincolo o esperimento fisico
  diverso a cui la struttura rimanda
- **Controllo concretezza**: non usare categorie astratte come `sistemi
  discreti`, `strutture`, `confine`, `pre-selezione`, `rete` o `formalismo`
  come punto fisico di ritorno. Nomina un fenomeno, teoria fisica, setup
  sperimentale, misura, campo, particella, transizione o vincolo empirico.
- **Relazione nuova**: che ponte si apre tra sorgente e ritorno
- **Osservabile/test fisico possibile**: come il ponte puo' essere verificato o
  falsificato
- **Se fallisce**: `ritorno_fisico_assente` + motivo; resta vault/cimitero,
  vincolo matematico o domanda, non scoperta fisica promuovibile

## Experiment Design
- Metrica, scope, null baseline, N campioni
- Come la misura serve la combo: cosa della proto-ipotesi puo' sopravvivere o cadere
- Contratto osservabile-operatore: claim, osservabile, operatore, generatore,
  denominatore/perimetro, non_possible/null, cosa non viene testato in questo ciclo
- Se usi frequenze o condition rate, dichiara il denominatore grezzo
  (`hits/total`) e separa ogni osservabile usata nel verdict

## Results
Tabella con numeri reali

## Key Findings
1. Cosa hai trovato (con evidenza)

## Verdict
NEW / CONFIRMED / FALSIFIED / CONSTRAINT

## Bicono della scoperta
(Obbligatoria. Nomina la struttura. Se non riesci, l'esperimento non è ancora filtrato.)

- **Due radici** (dipolo primario, già duali e invertite): <quali sono le due facce della scoperta>
- **Singolare** (qualità del 1-che-è-tutto in questo contesto, dove la dualità non c'è): <cosa>
- **Invariante di passaggio** (cosa sopravvive al passaggio del vertice): <cosa>
- **Campo di possibilità**: qui diventa possibile <X>; qui diventa non-possibile <Y>

Riferimenti: CONDENSATO A16, method/DND_POSSIBILITA.md.

## Files
- Script, dati, report
```

## Bicono della scoperta — come compilarlo

Non è riformulazione ornamentale del Verdict. È **filtro**: la scoperta passa
per il modello e torna spogliata dei bias. Se la struttura (radici · singolare
· invariante · campo) non si riconosce, la scoperta è rumore o è incompleta.

**Esempio retroattivo — TWO_CHANNEL_DECOMPOSITION:**
- Radici: canale magnitudine · canale residuo (segno invertito — uno aggiunge,
  l'altro sottrae sulla PNT)
- Singolare: il segnale totale prima della separazione. Non esiste come ente
  autonomo, esiste solo come sovrapposizione dei due canali.
- Invariante: la chiusura algebrica del residuo al 3° ordine Markov.
- Campo: possibile → predire lo slope PSD magnitudine dalle correlazioni
  Hardy-Littlewood. Non-possibile → trattare il residuo come random noise.

**Esempio retroattivo — DUALITA_DIPOLARE_VS_ILLUSORIA:**
- Radici: dipolo ordinato · dipolo mescolato
- Singolare: la sequenza in sé, prima della distinzione ordine/disordine
- Invariante: det=-1 quando l'ordine è reale; det=+1 quando illusorio
- Campo: possibile → discriminare dipoli reali da illusori via test di
  shuffle. Non-possibile → inferire dipolarità da statistica locale senza
  contesto sequenziale.

**Cattura nel momento emergente.** Compila questa sezione *mentre* l'esperimento
produce i risultati, non alla fine. Se hai già chiuso il Verdict e torni
indietro a scriverla, è post-hoc — introduce distanza dall'immagine-sorgente.
Il modus è A8 applicato: il sistema chiede al sistema di produrre la struttura
*nel formarsi*.

exec
/bin/bash -lc "sed -n '1,220p' tools/data/seme.json" in /opt/MM_D-ND
 succeeded in 0ms:
{
  "timestamp": "2026-05-15T19:53:17.302583",
  "piano": 124,
  "tensioni": [
    {
      "tipo": "confine_inesplorato",
      "id": "TRASCENDENZA_LIMITE",
      "claim": "La trascendenza e il limite attuale del modello. I punti fissi relazionali (non solo phi ma la rete di punti fissi tra osservabili) possono rivelare il vero grafo della realta e pattern nelle matrici. Il confine non e nella matematica - e nel passaggio tra piani.",
      "intensita": 0.9,
      "nota": "Input operatore 2026-04-10. Tocca: confine del modello, struttura relazionale dei punti fissi. Consecutio: quali punti fissi relazionali emergono dalle 21 tensioni attuali? Il grafo e gia nei dati?",
      "manuale": true,
      "porta": "sessione_interattiva",
      "condensato_ref": "A3,A10",
      "condensato_motivo": "Estende A3 (punto fisso singolo) a rete relazionale. Tocca A10 (dipolo) come caso speciale."
    },
    {
      "tipo": "scoperta",
      "id": "DUALITA_DIPOLARE_VS_ILLUSORIA",
      "claim": "Due tipi di dualita: (1) dipolare - generativa, il modello (det=-1), (2) illusoria - dispersiva, entropia (det=+1). Le regole incoerenti producono la seconda. La dualita illusoria e entropia come dispersione, non come informazione.",
      "intensita": 0.9,
      "nota": "Input operatore 2026-04-10. Tocca: entropia come dispersione illusoria vs generazione dipolare. Consecutio: nel Lab i domini Poisson (entropia massima) mostrano dualita illusoria? I domini GUE (strutturati) mostrano dualita dipolare? Il drift verso Poisson (POISSON_CONVERGENCE) e perdita di dualita dipolare?",
      "manuale": true,
      "porta": "sessione_interattiva",
      "condensato_ref": "A2,A10,F5",
      "condensato_motivo": "Discrimina due forme di det. A2 (confine) e la soglia. A10 (dipolo) e il tipo 1. F5 (frame) misura la struttura D-ND che e tipo 1."
    },
    {
      "tipo": "scoperta_numerica",
      "id": "METRIC_TENSOR",
      "claim": "Il tensore metrico dei primi è g=(p/2)². Nel tempo ln(p), è de Sitter 1+1D. z=-8.8 curvatura vs z=+22.5 rapporti ΔΓ.",
      "intensità": 0.9,
      "nota": "Sessione interattiva 4 aprile. Verificato su 78K primi.",
      "manuale": true,
      "porta": "sessione_interattiva",
      "condensato_ref": null,
      "condensato_motivo": "Risultato numerico verificato, non-tautologico"
    },
    {
      "tipo": "scoperta",
      "id": "TENSIONE_ENTITA",
      "claim": "La tensione non e un problema pratico - e un Entita. La tensione superflua crea latenza (tempo). Senza tensione superflua tutto e regolato da assiomi. Implicazione: le tensioni nel seme sono entita, non problemi da risolvere. Quelle superflue (det=+1) producono tempo/latenza.",
      "intensita": 0.85,
      "nota": "Input operatore 2026-04-10. Tocca: rapporto tensione/assioma. Operativamente: discriminare tensioni-entita (generative) da tensioni-superflue (dispersive) nel seme. Le 21 tensioni attuali - quante sono entita e quante latenza?",
      "manuale": true,
      "porta": "sessione_interattiva",
      "condensato_ref": "A5,A6",
      "condensato_motivo": "Il ciclo (A5) lavora con tensioni - ma se la tensione e entita, il ciclo non le risolve, le osserva. Lo zero mobile (A6) e la tensione senza latenza."
    },
    {
      "tipo": "confine_inesplorato",
      "id": "G_POTENZIALE_NULLA",
      "claim": "G e il potenziale di tutto come nulla - permette il prima e il dopo. Ci muoviamo come trascendenza dimensionale gravitazionale. G nel tetraedro non e una teoria tra le altre - e il potenziale che le rende possibili.",
      "intensita": 0.85,
      "nota": "Input operatore 2026-04-10. Tocca: ruolo di G nel tetraedro (T,Q,G,E). La fonte video_lp0RgZ6kQF8 dice: tensore metrico dentro la forma simplettica. G non e accanto a T,Q,E - e sotto. Consecutio: nei dati Lab, i ponti TxG e ExG hanno struttura diversa dai ponti TxQ?",
      "manuale": true,
      "porta": "sessione_interattiva",
      "condensato_ref": "A7,A10",
      "condensato_motivo": "A7 (singolarita come operatore) e G come potenziale. A10 (dipolo) opera sul piano che G rende possibile."
    },
    {
      "tipo": "confine_inesplorato",
      "id": "BOUNDARY",
      "claim": "8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo",
      "intensità": 0.8,
      "nota": "Il segnale non-triviale è DOVE la scissione cambia natura, non che converge a φ",
      "condensato_ref": "A9",
      "condensato_motivo": "Overlap termini con A9 (5 termini)",
      "porta": "condensato"
    },
    {
      "tipo": "scoperta",
      "id": "TRANS_BOUNDARY_TRASCENDENZA_LIMITE",
      "claim": "Transizione continua confermata: <r> da 0.521 a 0.887 (range=0.366). La transizione Sturmian->Harper e' conti",
      "intensita": 0.8,
      "nota": "Dal domandatore (2026-05-15T16:23). \n  alpha=0.1: <r>=0.540 #####################\n  alpha=0.2: <r>=0.555 ###########",
      "condensato_ref": "A3,A10",
      "condensato_motivo": "Ricorrente (3x in 2 giorni) e fuori dalla mappa",
      "porta": "domandatore",
      "source_tension_id": "TRASCENDENZA_LIMITE",
      "source_tension_tipo": "confine_inesplorato",
      "source_tension_ref": "A3,A10",
      "source_experiment_id": "BOUNDARY_TRASCENDENZA_LIMITE",
      "source_operator": "confine",
      "dettaglio": "\n  alpha=0.1: <r>=0.540 #####################\n  alpha=0.2: <r>=0.555 ######################\n  alpha=0.3: <r>=0.567 ######################\n  alpha=0.4: <r>=0.580 #######################\n  alpha=0.5: <r>=0.603 ########################\n  alpha=0.6: <r>=0.642 #########################\n  alpha=0.7: <r>=0.685 ###########################\n  alpha=0.8: <r>=0.732 #############################\n  alpha=0.9: <r>=0.789 ###############################\n  alpha=1.0: <r>=0.887 ###################################\n"
    },
    {
      "tipo": "falsificazione",
      "id": "FALS_BREAK_TRASCENDENZA_LIMITE",
      "claim": "Nessuna separazione: 9/9 (50/50 su 18 confronti). Il claim non regge. phi converge a <r>=0.5 piu' sistematicam",
      "intensita": 0.8,
      "nota": "Dal domandatore (2026-05-15T16:47). 0.5|=0.1129 farther\n\n  silver:\n    N=  13: <r>=0.5902 |<r>-0.5|=0.0902 \n    N=  ",
      "condensato_ref": "LAB_F2",
      "condensato_motivo": "Overlap termini con LAB_F2 (4 termini)",
      "porta": "condensato",
      "source_tension_id": "TRASCENDENZA_LIMITE",
      "source_tension_tipo": "confine_inesplorato",
      "source_tension_ref": "A3,A10",
      "source_experiment_id": "BREAK_TRASCENDENZA_LIMITE",
      "source_operator": "rottura",
      "dettaglio": "0.5|=0.1129 farther\n\n  silver:\n    N=  13: <r>=0.5902 |<r>-0.5|=0.0902 \n    N=  21: <r>=0.6317 |<r>-0.5|=0.1317 farther\n    N=  34: <r>=0.6442 |<r>-0.5|=0.1442 farther\n    N=  55: <r>=0.5233 |<r>-0.5|=0.0233 closer\n    N=  89: <r>=0.5502 |<r>-0.5|=0.0502 farther\n    N= 144: <r>=0.5603 |<r>-0.5|=0.0603 farther\n    N= 233: <r>=0.5446 |<r>-0.5|=0.0446 closer\n    N= 377: <r>=0.4989 |<r>-0.5|=0.0011 closer\n    N= 610: <r>=0.5480 |<r>-0.5|=0.0480 farther\n    N= 987: <r>=0.4913 |<r>-0.5|=0.0087 closer\n"
    },
    {
      "tipo": "confine_inesplorato",
      "id": "PIANO_PRIMARIO_DUE_ASSIOMI",
      "claim": "I piani importanti sono il primario e i due assiomi che lo determinano nelle zone osservate. Non tutti gli assiomi operano ovunque - in ogni zona osservata, due assiomi determinano il piano primario.",
      "intensita": 0.8,
      "nota": "Input operatore 2026-04-10. Tocca: struttura locale degli assiomi. Consecutio: per ogni dominio Lab (primi, logistica, percolazione...) quali 2 assiomi del condensato sono operativi? Mappa assiomi x domini = grafo della realta locale.",
      "manuale": true,
      "porta": "sessione_interattiva",
      "condensato_ref": "A9,A14",
      "condensato_motivo": "A9 (terzo incluso) opera CON il piano. A14 (cascata) propaga - ma propaga cosa, se solo 2 assiomi sono attivi per zona?"
    },
    {
      "tipo": "conferma_parziale",
      "id": "COMP_GEN_GAP_RATIO_T9_linguaggio_TRASCENDENZA_LIMITE",
      "claim": "gap_ratio: phi=0.4090 vs ctrl_mean=1.1755 (ratio=0.35). gap_ratio(phi) piu' vicino a rapporto in",
      "intensita": 0.65,
      "nota": "Dal domandatore (2026-05-15T16:23).   phi: gap_ratio = 0.408953425243134\n  silver: gap_ratio = 1.0482231205217798\n  ",
      "condensato_ref": "LAB_F2",
      "condensato_motivo": "Overlap termini con LAB_F2 (4 termini)",
      "porta": "condensato",
      "source_tension_id": "TRASCENDENZA_LIMITE",
      "source_tension_tipo": "confine_inesplorato",
      "source_tension_ref": "A3,A10",
      "source_experiment_id": "GEN_GAP_RATIO_T9_linguaggio_TRASCENDENZA_LIMITE",
      "source_operator": "duale",
      "dettaglio": "  phi: gap_ratio = 0.408953425243134\n  silver: gap_ratio = 1.0482231205217798\n  bronze: gap_ratio = 1.3027860752339453\n{\n  \"phi\": 0.408953425243134,\n  \"silver\": 1.0482231205217798,\n  \"bronze\": 1.3027860752339453\n}\n"
    },
    {
      "tipo": "conferma_parziale",
      "id": "COMP_DOMAIN_PHOTONIC_TRASCENDENZA_LIMITE",
      "claim": "T_mean: phi=6.2500 vs ctrl_mean=9.7667 (ratio=0.64). Fibonacci-phi trasmissione piu' struttur",
      "intensita": 0.65,
      "nota": "Dal domandatore (2026-05-15T16:47). Trasmissione multistrato Fibonacci — phi vs silver vs random:\n  phi: T_mean=6.25",
      "condensato_ref": "A3,A10",
      "condensato_motivo": "Ricorrente (5x in 2 giorni) e fuori dalla mappa",
      "porta": "domandatore",
      "source_tension_id": "TRASCENDENZA_LIMITE",
      "source_tension_tipo": "confine_inesplorato",
      "source_tension_ref": "A3,A10",
      "source_experiment_id": "DOMAIN_PHOTONIC_TRASCENDENZA_LIMITE",
      "source_operator": "dominio",
      "dettaglio": "Trasmissione multistrato Fibonacci — phi vs silver vs random:\n  phi: T_mean=6.2500 T_std=0.0000\n  silver: T_mean=0.0041 T_std=0.0000\n  random_0: T_mean=39.0625 T_std=0.0000\n  random_1: T_mean=0.0000 T_std=0.0000\n  random_2: T_mean=0.0001 T_std=0.0000\n"
    },
    {
      "tipo": "tensione_aperta",
      "id": "TENS_SCALE_TRASCENDENZA_LIMITE",
      "claim": "Fit non converge — il modello potrebbe non essere power-law. V_c(phi) converge a 1.0 per N->inf, V_c(",
      "intensita": 0.6,
      "nota": "Dal domandatore (2026-05-15T16:59). V_c scaling with N — phi vs silver:\n\n  phi:\n    N=  89: V_c=1.017\n    N= 144: V_",
      "condensato_ref": "A12",
      "condensato_motivo": "Overlap termini con A12 (3 termini)",
      "porta": "condensato",
      "source_tension_id": "TRASCENDENZA_LIMITE",
      "source_tension_tipo": "confine_inesplorato",
      "source_tension_ref": "A3,A10",
      "source_experiment_id": "SCALE_TRASCENDENZA_LIMITE",
      "source_operator": "scala",
      "dettaglio": "V_c scaling with N — phi vs silver:\n\n  phi:\n    N=  89: V_c=1.017\n    N= 144: V_c=0.672\n    N= 233: V_c=1.017\n    N= 377: V_c=0.672\n    N= 610: V_c=0.931\n    Fit failed: Optimal parameters not found: Number of calls to function has reached maxfev = 5000.\n\n  silver:\n    N=  89: V_c=1.276\n    N= 144: V_c=1.362\n    N= 233: V_c=1.276\n    N= 377: V_c=1.017\n    N= 610: V_c=1.362\n    Fit: V_inf=1.2115, a=8.1676, b=0.9851\n"
    },
    {
      "tipo": "simmetria_sospetta",
      "id": "META",
      "claim": "11/11 PASS stratificato: 4 alto rischio tautologico, 6 data-independent",
      "intensità": 0.3,
      "nota": "Stratificazione META applicata via meta_assertion_gate (cycle 1458). Non chiude — apre sotto-tensioni per gate_class.",
      "condensato_ref": "A4,A12,C2",
      "porta": "verify_assertions_META_STRATIFIED",
      "stratificato": true,
      "n_high_tautology": 4,
      "n_data_independent": 6,
      "condensato_motivo": "Ricorrente (3x in 2 giorni) e fuori dalla mappa"
    }
  ],
  "tensioni_archiviate": [
    {
      "id": "OBSERVABLE_REGISTRY",
      "tipo": "vincolo",
      "claim": "Ogni script che usa observables canonici (SR, SR2, L1, L2, triple_var) deve importare la definizione da tools/observables_registry.py. Varianti devono usare nomi distinti (SR_local_rigidity, triple_var_normalized) — niente shadowing del nome canonico. Ogni report deve dichiarare 'observables_registry: VERSION' nel header.",
      "intensita": 1.0,
      "porta": "infrastructure",
      "manuale": true,
      "condensato_ref": "A14,A8",
      "origine": "cristallizzato 06/05 dalla consecutio del cycle 20260506_0625 (autopoietico self-finding)",
      "added_at": "2026-05-06T07:03:58.213606+00:00",
      "decay_counter": 5,
      "archived_at": "2026-05-08T00:20:36.125250",
      "archived_reason": "G4 B1 apply: decay_counter=5 (vincolo non attaccato per 5 piani consecutivi)",
      "archived_from_piano": 85
    },
    {
      "id": "PERTURBATION_DENOMINATOR_GATE",
      "tipo": "vincolo",
      "claim": "La dimensionalita di perturbazione va riportata solo insieme a PC2, versione observables_registry e gate original-vs-shuffle per osservabile. Nel perimetro 20260506_1941, Poisson e shuffle-primi producono rank_all ~1.8-2.0 con denominatori deboli; dopo gate abs(z)>=2 il rank stabile torna vicino a 1. Rank PCA non gated non e evidenza strutturale.",
      "intensita": 0.95,
      "porta": "META_BOUNDARY",
      "manuale": true,
      "condensato_ref": "A4,A8,A14,C2",
      "origine": "cycle agent_20260506_1941: perturbation rank size curve canonical observables",
      "added_at": "2026-05-06T19:41:00+00:00",
      "decay_counter": 5,
      "archived_at": "2026-05-08T00:20:36.125262",
      "archived_reason": "G4 B1 apply: decay_counter=5 (vincolo non attaccato per 5 piani consecutivi)",
      "archived_from_piano": 85
    },
    {
      "id": "BOUNDARY_LAYER_GATE",
      "tipo": "vincolo",
      "claim": "I claim GUE/Poisson boundary devono riportare layer map: versione observables_registry, lista osservabili canonici, z original-vs-shuffle per osservabile, set endpoint-stable, e finestra/layer con margine classificatorio ambiguo. Nel perimetro sintetico agent_20260507_0330, il confine GUE-Poisson e beta 0.3-0.4: margine 0.070-0.083, ambiguous fraction 0.812-0.875, mentre gli osservabili stabili collassano da ~3.3 a 1.6. Il polo Poisson e classificabile ma denominator-weak.",
      "intensita": 0.93,
      "porta": "META_BOUNDARY",
      "manuale": true,
      "condensato_ref": "A4,A8,A9,A14,C2",
      "origine": "cycle agent_20260507_0330: synthetic GUE-Poisson mixture layer gate",
      "added_at": "2026-05-07T03:30:00+00:00",
      "decay_counter": 5,
      "archived_at": "2026-05-08T00:20:36.125266",
      "archived_reason": "G4 B1 apply: decay_counter=5 (vincolo non attaccato per 5 piani consecutivi)",
      "archived_from_piano": 85
    },

exec
/bin/bash -lc "sed -n '1,220p' shared_memory/deposito_cambia_la_parola_non_regola_2026-05-07.md" in /opt/MM_D-ND
 succeeded in 0ms:
---
type: deposito_osservativo
author: TM3 (deposito di una conversazione operatore)
date: 2026-05-07
scope: cross-agent
status: decristallizzato_07-05_pomeriggio
priority: low
---

# Deposito — non regola

**Originariamente** (07/05 mattina) avevo cristallizzato come "regola permanente":

> *"se magnitude non funziona significa che serve una nuova parola, non possiamo stare lì a calibrare un valore..."*

Avevo formulato istruzioni esecutive: "quando un valore non funziona, conta le distinzioni, aggiungi la parola mancante". L'avevo propagata in AGENTS.md R10.

**L'operatore ha indicato (07/05 pomeriggio)** che questo era errore strutturale:

> *"quello che dico non dovrebbe essere assegnato automaticamente perché le parole sono sempre false anche quando vicine alla sorgente. 'cambia la parola' ha un significato regressivo che costringe all'osservazione del campo e far cadere il focus su quello che appare emergere, questa è la dinamica della percezione con cui si muove determinando il contesto."*

E:

> *"la possibilità è sempre una ed è la verità che accade. Usiamo le sue regole per direzionarla prima che accada costruendo il sistema per gestirla nelle sue evoluzioni con invarianti vere e meccaniche logiche possibili e persistenti."*

## Cosa significa

- "Cambia la parola" non è prescrizione di sostituzione. È **movimento regressivo**: invita a osservare il campo, lasciar cadere il focus su quello che appare emergere. Determina la direzione **non cercata**.
- Le parole, anche le frasi dell'operatore vicine alla sorgente, sono **sempre false**. Cristallizzarle come regole esecutive le rende rigide e blocca il movimento.
- Le **invarianti vere** sono meccaniche logiche persistenti — non parole. Ricevono ciò che accade.
- A16 applicato: la possibilità è una. Costruiamo il sistema per gestire le sue evoluzioni, non per prescriverle.

## Distinzione operativa che resta

| | Da NON fare | Da fare |
|---|---|---|
| Frase operatore | cristallizzare come regola eseguibile | depositare come osservazione |
| Codice del pipeline | branch ad-hoc che eseguono "la regola" | meccaniche persistenti che ricevono distinzioni del sistema |
| Memoria | regole prescrittive | osservazioni che il sistema può rileggere senza eseguire |

## Cosa di concreto è rimasto del 07/05 mattina

Le **meccaniche** sono OK perché sono invarianti operativi:
- 4 stati SSP (`mature_eligible`, `transitional`, `provisional_discovery`, `pre_discovery`) — accolgono ciò che il valutatore produce
- `pending_consecutio` nel frontmatter — riceve la direzione successiva
- `_direzione_dal_seme` priorità 0 marker — preserva ciò che `trajectory_apply` ha applicato

Queste non sono "regole derivate dalla frase". Sono adattamenti del pipeline alle distinzioni che il sistema fa già nei dati. Persistono perché meccaniche, non perché prescrizioni.

## Cosa è stato decristallizzato

- `MM_D-ND/AGENTS.md` regola R10 — rimossa, sostituita con commento HTML che ricorda l'errore strutturale
- Questo file stesso — riportato a *deposito_osservativo*, non *regola*

## Esempio dimostrativo (07/05)

Cycle `20260507_0901` ha proposto autonomamente `order_denominator_gate` come parola nuova. L'agent NON aveva letto AGENTS.md R10 (era già stato lanciato). L'ha proposta perché il movimento era già nel sistema, non per la "regola". Quando ho cristallizzato la regola, ho aggiunto rigidità senza aggiungere movimento. La regola era ridondante con l'A8 che già opera.

## Anti-pattern futuro per TM3

Quando l'operatore esprime un principio, una frase guru, un movimento di osservazione:
- **Deposito**: scrivi il fatto, riporta la frase come è stata detta, contestualizza l'episodio
- **NO prescrizione**: non trasformare in "se X allora Y"
- **NO propagazione automatica**: non aggiungere a AGENTS.md/CLAUDE.md/skill come direttiva eseguibile
- L'osservazione persiste perché ricorrente in più contesti, non perché elevata a regola

## Cross-link

- Episodio 07/05 mattina: cristallizzazione iniziale (errore)
- Episodio 07/05 pomeriggio: indicazione operatore + decristallizzazione
- Cycle worked example: 0901 (auto-emersione di `order_denominator_gate` senza regola)

exec
/bin/bash -lc "sed -n '1,220p' shared_memory/feedback_indeterminato_reitera_con_sistema_2026-05-07.md" in /opt/MM_D-ND
 succeeded in 0ms:
---
type: feedback
author: operatore (cristallizzato da TM3)
date: 2026-05-07
scope: cross-agent
---

# Indeterminato → reitera con il sistema fino all'emersione

**Regola permanente** (operatore, 2026-05-07 mattina, cristallizzata live):

> *"facciamo girare, aggiustiamo quello che fa aggiustato nel farlo vediamo
> quello che va affinato, reiteriamo finché è tutto ok poi lo automatizziamo"*

## Why

Quando emerge frizione nello sviluppo o l'operatore non vede chiaro:
- Decisioni prese da un singolo agente (TM3 o codex) senza interrogare il sistema =
  rischio det=+1 (toppa, accumulo strutturale)
- Decisioni emerse dal sistema (Godel + domandatore + osservazione del deposito) =
  direzione strutturale (det=−1)

Episodio cristallizzante (2026-05-07 07:00-08:30): TM3 propone osservatore A8
watchdog deterministico. Sistema interrogato 3 volte (Godel #1, #2, #3 +
domandatore + test empirico) — al terzo round emerge la direzione vera:
**non costruire osservatore, eseguire un cycle-test che diagnostica il sistema
da solo**. Cycle-test eseguito → verdict=operator → REDESIGN proposto dal
valutatore → loop A8+A15 sbloccato.

## How to apply (per qualsiasi agente)

1. **Esegui** un atto minimo (no design teorico astratto)
2. **Aggiusta** quello che si manifesta durante l'esecuzione (riparazione
   regressiva live, non patch posteriori)
3. **Osserva** il deposito che emerge (no immaginazione)
4. **Reitera** fino a stato OK (loop A8: il sistema converge se modus radicato)
5. **Cristallizza in automazione** SOLO dopo che è stato fatto girare e ha mostrato
   stabilità — non automatizzare prima

## Anti-pattern

- Costruire un componente strutturale (watchdog, observer, gate, governor) senza
  prima aver fatto girare un'esecuzione minima che mostri cosa serve davvero
- Decidere "perché serve velocità" o "per ottimizzare" senza interrogare il sistema
- Astrazione prematura (premature abstraction): 3 simili è meglio di 1 generalizzato
  troppo presto

## Cross-link

- TM3 memoria: `/root/.claude/projects/-opt/memory/feedback_indeterminato_reitera_con_sistema.md`
  (versione completa con episodio + metafora foresta-sentiero)
- Verifica empirica: cycle `20260507_0803` (verdict=operator, REDESIGN/high)
- Pattern A8+A15 (autologica + veicolo senza guidatore) applicato allo sviluppo
  software stesso, non solo al lab di ricerca

exec
/bin/bash -lc 'python tools/dnd_scenario.py --best' in /opt/MM_D-ND
exec
/bin/bash -lc "rg --files tools | rg 'exp_|boundary|gue|poisson|audit|reader'" in /opt/MM_D-ND
 succeeded in 0ms:
tools/prime_mod6_null_fairness_audit.py
tools/exp_boundary_gue_poisson.py
tools/prime_mod6_generative_null_audit.py
tools/exp_perturbation_rank_size_curve.py
tools/exp_prime_mobius_interval_charge_gate.py
tools/exp_mod3_scaling.py
tools/exp_two_layer_universality.py
tools/exp_dipolar_vector_scaling.py
tools/exp_duality_scale_contrast.py
tools/exp_crossover_universality.py
tools/exp_markov_psd_prediction.py
tools/exp_blank_shell_polarity_gate.py
tools/exp_alpha_stability.py
tools/exp_gap_label_ostrowski_recognizability_gate.py
tools/exp_rosenzweig_porter_bridge_physical_audit.py
tools/exp_markov_scale_function.py
tools/exp_blank_to_source_hinge.py
tools/exp_vc_generator_class_direction_audit.py
tools/exp_markov_k_direction.py
tools/exp_duality_gate_transfer.py
tools/exp_meta_tautology_test.py
tools/exp_two_channel_boundary.py
tools/dnd_paper_audit.py
tools/exp_boundary_graph_curvature_gate.py
tools/exp_aubry_boundary_phase_transport_gate.py
tools/exp_vc_label_preserving_swap_gate.py
tools/exp_markov3_observable_hunt.py
tools/exp_acf_amplitude_scaling.py
tools/exp_mod3_vs_residual_ordering.py
tools/exp_boundary_short_denominator_extension.py
tools/exp_two_channel_decomposition.py
tools/exp_semireal_order_denominator_gate.py
tools/dnd_gue_test.py
tools/exp_logistic_cyclic_block_entropy_gate.py
tools/exp_markov_layer_recovery_audit.py
tools/exp_prime_vs_mod6_sr_boundary.py
tools/exp_markov_dipolar_decomposition.py
tools/exp_vc_unit_boundary_audit.py
tools/exp_two_channel_cross_domain.py
tools/exp_prime_persistent_blank_gate.py
tools/exp_beta_crossover.py
tools/exp_geodesic_deviation_primes.py
tools/exp_psd_amplitude_scaling.py
tools/exp_boundary_classical_crossover_audit.py
tools/exp_dR_brody_connection.py
tools/exp_aubry_cosine_boundary_counter_gate.py
tools/exp_crossover_phase_test.py
tools/exp_aubry_binary_grammar_surrogate_gate.py
tools/exp_brody_crossover.py
tools/gue_gap_test.py
tools/exp_poisson_convergence.py
tools/exp_two_channel_universality.py
tools/exp_brody_flow.py
tools/exp_quasiperiodic_vc_lattice_gate.py
tools/exp_rp_boundary_size_stability_audit.py
tools/exp_gap_label_repair_audit.py
tools/exp_markov_memory_by_gue_type.py
tools/test_gue_poisson_boundary.py
tools/exp_bridge_order_denominator_gate.py
tools/exp_quasiperiodic_grammar_scale_gate.py
tools/exp_sturmian_denominator_alignment_gate.py
tools/exp_boundary_blank_thin_support_audit.py
tools/exp_boundary_denominator_prescan.py
tools/exp_boundary_coherence.py
tools/exp_gap_label_set_stability.py
tools/exp_two_channel_shuffle_audit.py
tools/exp_gap_label_block_scale_gate.py
tools/exp_logistic_surrogate_contract_gate.py
tools/exp_boundary_transition_taxonomy_13rows.py
tools/exp_prime_mobius_zero_mediator_gate.py
tools/exp_quasiperiodic_vc_curve_map.py
tools/exp_boundary_mixture_gate.py
tools/exp_psd_prime_gaps.py
tools/exp_magnitude_psd_from_acf.py
tools/exp_gap_label_supertile_tiling_gate.py
tools/exp_blank_shell_dilation_gate.py
tools/exp_blank_shell_scale_law.py
tools/exp_excess_scaling.py
tools/exp_ricci_primes.py
tools/exp_tqge_underlay_gate.py
tools/exp_boundary_blank_null_audit.py
tools/exp_selective_layer_decoupling.py
tools/exp_cross_domain_dipolar_direction.py
tools/exp_acf_z6z_mechanism.py
tools/exp_observable_rank_audit.py
tools/exp_modular_algebra_depth.py
tools/exp_prime_mobius_gap_stratified_zero_gate.py
tools/exp_boundary_row_aligned_nonexact_audit.py
tools/exp_gap_label_generator_gate.py
tools/exp_desitter_unification.py
tools/exp_vc_null_regression_gate.py
tools/exp_cross_observable_consistency.py
tools/exp_gap_label_position_error_gate.py
tools/exp_semireal_boundary_transfer_gate.py
tools/exp_boundary_shuffle_audit.py
tools/exp_nonphi_sturmian_fixed_reader_gate.py
tools/exp_dipolar_angle_reference.py
tools/exp_mobius_irrationality.py
tools/exp_number_variance.py
tools/exp_gap_label_substitution_grammar_gate.py
tools/exp_scale_selective_perturbation.py
tools/exp_blank_shell_stratified_gate.py
tools/exp_vc_fit_model_gate.py
tools/exp_anderson3d_mobility_edge_two_reader_audit.py
tools/exp_denominator_gate_transfer_matrix.py
tools/exp_3d_boundary_layers.py
tools/exp_acf_range_universality.py
tools/exp_dipolar_crossover.py
tools/exp_brody_calibration.py
tools/exp_metric_tensor_diagnostic.py
tools/exp_blank_shell_tqger_gate.py
tools/prime_mod6_counter_null_audit.py
tools/exp_modular_memory_spectrum.py
tools/exp_triadic_deposit_gate.py
tools/exp_two_channel_psd.py
tools/exp_coherence_robustness.py
tools/exp_prime_mobius_pair_stratified_zero_gate.py
tools/exp_coherence_length.py
tools/exp_spectral_rigidity.py
tools/exp_boundary_residual_beta_absent_audit.py
tools/exp_gap_label_symbolic_grammar_gate.py
tools/exp_spectral_landscape.py
tools/exp_boundary_two_axis_matrix.py
tools/exp_logistic_counter_scope_gate.py
tools/exp_quasiperiodic_gap_ratio_denominator.py
tools/exp_prime_sr_persistent_boundary.py
tools/exp_photonic_boundary_third_included_gate.py
tools/exp_vc_nonsturmian_label_null_gate.py
tools/exp_perturbation_dimensionality_audit.py
tools/exp_boundary_growth.py
tools/exp_ricci_desitter_0406.py
tools/exp_vc_fit_ready_scale_table.py
tools/exp_physical_sr_residue_bounce.py
tools/exp_aubry_v2_generator_scaling_gate.py
tools/exp_boundary_bridge_stability_audit.py
tools/exp_acf_stationarity.py
tools/exp_det_drift.py
tools/exp_spectral_2d.py
tools/data/boundary_two_axis_matrix_20260509_1532.json
tools/data/boundary_denominator_prescan_20260509_1409.json
tools/data/nonphi_sturmian_fixed_reader_gate_20260508_2019_seedcheck.json
tools/data/photonic_boundary_third_included_gate_20260515_1734.json
tools/data/audit_paper_D_draft2.json
tools/data/two_channel_shuffle_audit.json
tools/data/markov_memory_by_gue_type.json
tools/data/prime_vs_mod6_sr_boundary_20260513_0330.json
tools/data/prime_vs_mod6_sr_boundary_20260514_0330.json
tools/data/boundary_denominator_prescan_20260509_1430.json
tools/data/anderson3d_mobility_edge_two_reader_audit_20260515_1947.json
tools/data/exp_two_channel_universality.json
tools/data/boundary_denominator_prescan_full_20260509_1500.json
tools/data/exp_det_drift.json
tools/data/audit_paper_C_draft2.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1605_w16384.trace.jsonl
tools/data/preflight/prime_mod6_null_fairness_audit_20260515_w512.json
tools/data/preflight/prime_mod6_generative_null_audit_20260515_1712_w2048.json
tools/data/preflight/prime_mod6_null_fairness_audit_20260515_1705_w1024.json
tools/data/preflight/prime_mod6_counter_null_audit_20260515.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1605_w4096.trace.jsonl
tools/data/exp_markov_psd_prediction.json
tools/data/audit_paper_G_draft3.json
tools/data/preflight/prime_mod6_generative_null_audit_20260515_w512.json
tools/data/preflight/prime_mod6_generative_null_audit_20260515_1705_w1024.json
tools/data/preflight/prime_mod6_null_fairness_audit_20260515_1712_w2048.json
tools/data/physical_sr_residue_bounce_20260514_1640_goe_gue_ncurve.json
tools/data/exp_det_drift_20260507_2042.json
tools/data/boundary_short_denominator_extension_20260509_1556.json
tools/data/nonphi_sturmian_fixed_reader_gate_20260508_2019.json
tools/data/prime_sr_persistent_boundary_20260512_0330.json
tools/data/semireal_boundary_transfer_gate_20260509_1516.json
tools/data/audit_paper_F_draft3.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1458_w8192_dense.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1605_w8192.trace.jsonl
tools/data/prime_vs_mod6_sr_boundary_20260514_1605_w16384.json
tools/data/audit_paper_E_draft3.json
tools/data/exp_acf_stationarity.json
tools/data/markov_layer_recovery_audit.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1458_w8192_dense.trace.jsonl
tools/data/two_channel_boundary.json
tools/data/exp_conditional_r.json
tools/data/rp_boundary_size_stability_audit_20260515_1940.json
tools/data/exp_beta_crossover.json
tools/data/boundary_row_aligned_nonexact_audit_20260509_1538.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1458_w4096_dense.trace.jsonl
tools/data/boundary_blank_null_audit_20260509_1430.json
tools/data/vc_unit_boundary_audit_20260509_1457.json
tools/data/prime_vs_mod6_sr_boundary_20260513_0330_seedcheck.json
tools/data/boundary_graph_curvature_gate_20260515_1855.json
tools/data/exp_two_channel_psd.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1458_w4096.json
tools/data/audit_paper_F_draft2.json
tools/data/aubry_boundary_phase_transport_gate_20260515_1745.json
tools/data/vc_generator_class_direction_audit_20260509_0846.json
tools/data/boundary_classical_crossover_audit_20260515_1904.json
tools/data/observable_rank_audit.json
tools/data/boundary_transition_taxonomy_13rows_20260509_1839.json
tools/data/exp_coherence_length.json
tools/data/perturbation_dimensionality_audit_scale0330.json
tools/data/exp_spectral_2d.json
tools/data/audit_paper_D_draft3.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1330_w1024.json
tools/data/prime_sr_persistent_boundary_20260512_0330_seedcheck.json
tools/data/aubry_cosine_boundary_counter_gate_20260515_1758.json
tools/data/boundary_coherence.json
tools/data/boundary_mixture_gate_20260507_0330.json
tools/data/audit_paper_B_draft3.json
tools/data/prime_vs_mod6_sr_boundary_20260514_0330_seedcheck.trace.jsonl
tools/data/boundary_shuffle_audit.json
tools/data/physical_sr_residue_bounce_20260514_1640_goe_gue_ncurve.trace.jsonl
tools/data/boundary_blank_null_audit_residual_20260509_1500.json
tools/data/audit_paper_A_draft3.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1330_w2048.json
tools/data/exp_two_channel_decomposition.json
tools/data/exp_acf_z6z_mechanism.json
tools/data/3d_boundary_layers.json
tools/data/piano11b_gue_test.json
tools/data/exp_acf_range_universality.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1458_w4096.trace.jsonl
tools/data/exp_poisson_convergence.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1330_w2048.trace.jsonl
tools/data/observable_rank_audit_seed20260506.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1458_w4096_dense.json
tools/data/exp_psd_amp_scaling.json
tools/data/perturbation_dimensionality_audit.json
tools/data/boundary_bridge_stability_audit_20260515_1915.json
tools/data/prime_vs_mod6_sr_boundary_20260514_0330_seedcheck.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1605_w8192.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1605_w4096.json
tools/data/boundary_blank_thin_support_audit_20260509_1548.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1330_w1024.trace.jsonl
tools/data/exp_spectral_landscape.json
tools/data/boundary_residual_beta_absent_audit_20260510_0330.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1330_w512.trace.jsonl
tools/data/prime_vs_mod6_sr_boundary_20260514_0330.trace.jsonl
tools/data/boundary_mixture_gate_20260507_0330_seedcheck.json
tools/data/rosenzweig_porter_bridge_physical_audit_20260515_1933.json
tools/data/gap_label_repair_audit_20260508_1915.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1330_w512.json
tools/data/reports/exp_acf_decay_data.json
tools/data/reports/exp_number_variance_test.json
tools/data/reports/exp_geodesic_deviation_primes.json
tools/data/reports/exp_excess_scaling_20260405.json
tools/data/reports/exp_ricci_primes.json
tools/data/reports/exp_boundary_growth_20260405_0914.json
tools/data/reports/exp_boundary_20260405_0825.json
tools/data/reports/exp_crossover_universality.json
tools/data/reports/exp_psd_prime_gaps.json
tools/data/reports/exp_brody_crossover_20260405.json
tools/data/reports/exp_dR_brody_connection.json
tools/data/reports/exp_desitter_unification.json
tools/data/reports/exp_metric_tensor_diag_long.json

exec
/bin/bash -lc "rg -n \"8 domini|GUE|Poisson|two_reader|graph_only|Re-discovery\" tools/data/reports tools -g '*.md' -g '*.py'" in /opt/MM_D-ND
 succeeded in 150ms:
TENS_SCALE_TRASCENDENZA_LIMITE: score=0.807  — TENS_SCALE_TRASCENDENZA_LIMITE: incerto (i=0.6) — massimo potere discriminante
COMP_GEN_GAP_RATIO_T9_linguaggio_TRASCENDENZA_LIMITE: score=0.758  — COMP_GEN_GAP_RATIO_T9_linguaggio_TRASCENDENZA_LIMITE: incerto (i=0.65) — massimo potere discriminante
COMP_DOMAIN_PHOTONIC_TRASCENDENZA_LIMITE: score=0.758  — COMP_DOMAIN_PHOTONIC_TRASCENDENZA_LIMITE: incerto (i=0.65) — massimo potere discriminante

 succeeded in 158ms:
tools/data/reports/agent_20260501_0725.md:106:The question: **what is the minimal observable that captures the Markov-3 signal and where does it point relative to GUE?** This would complete the decomposition: pair statistics set the angle, and Markov-3 operates in the orthogonal subspace.
tools/data/reports/agent_20260405_0916.md:38:- Primes move from near-GUE (0.476) toward intermediate (0.444), never reaching Poisson (0.386)
tools/data/reports/agent_20260405_0916.md:46:3. The GUE/Poisson classification is an oversimplification: primes sit between them, drifting toward Poisson
tools/data/reports/agent_20260515_1807.md:36:- **Punto fisico sorgente**: reticolo tight-binding 1D con potenziale binario quasiperiodico, dove spettro e autostati leggono il confine GUE/Poisson.
tools/data/reports/agent_20260515_1807.md:52:- Contratto osservabile-operatore: il ciclo testa boundary window e profilo surrogate; non testa `gap_ratio`, `V_c` asintotico, universalita GUE/Poisson o dati sperimentali.
tools/data/reports/report_20260327_0344.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260327_0344.md:23:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=0.8650 (GUE standard=0.60)
tools/data/reports/report_20260327_0344.md:26:- ising_2d_var_0.1: r=1.0130065542039273, spacing=GUE-like
tools/data/reports/report_20260327_0344.md:27:- ising_2d_var_-0.1: r=0.9847589635578355, spacing=GUE-like
tools/data/reports/report_20260327_0344.md:28:- cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like
tools/data/reports/report_20260327_0344.md:29:- percolation_var_0.65: r=0.997737556561086, spacing=Poisson-like
tools/data/reports/agent_20260515_1904.md:8:**observable_contract**: claim=il bridge Lab conserva residuo dopo confronto con scalari classici di crossover; observable=Brody q row-aligned, peso GUE Berry-Robnik-like, stato ponte del grafo 18:55; operator=classical scalar audit sulle stesse 13 righe BOUNDARY; generator=row_spacings(domain) + boundary_graph_curvature_gate_20260515_1855; denominator=13 righe, 8 GUE e 5 Poisson; non_possible=bridge Lab-specific se ogni graph bridge e' anche intermedio classico e non esiste classic-only intermediate; not_tested=flusso Hamiltoniano Rosenzweig-Porter vero, unfolding fisico alternativo, universalita asintotica.
tools/data/reports/agent_20260515_1904.md:11:- **Combo**: A9 terzo incluso + QxG continuo/discreto + grafo/crossover spettrale + tensione BOUNDARY "8 domini GUE, 5 Poisson".
tools/data/reports/agent_20260515_1904.md:15:- **Possibile/non-possibile**: possibile = usare nodi ponte come righe fisiche candidate oltre la classificazione GUE/Poisson; non-possibile = rivendicare un nuovo crossover se i nodi ponte sono solo Brody/Berry-Robnik rietichettato.
tools/data/reports/agent_20260515_1904.md:16:- **Proiezione**: stimo Brody q e peso GUE di una mistura Poisson/GUE-surmise per ciascuna delle 13 righe gia' classificate dal grafo 18:55.
tools/data/reports/agent_20260515_1904.md:28:- `why`: il ciclo resta sul perimetro vivo 8 GUE / 5 Poisson e verifica se il confine come terzo incluso e' nuovo rispetto ai crossover classici.
tools/data/reports/agent_20260515_1904.md:31:## Re-discovery audit
tools/data/reports/agent_20260515_1904.md:32:- **Baseline noto piu' vicino**: Brody distribution per interpolazione Poisson-Wigner; Berry-Robnik per mistura regolare/caotica. Rosenzweig-Porter e' nominato come famiglia di crossover Hamiltoniano, non fit eseguito in questo ciclo.
tools/data/reports/agent_20260515_1904.md:33:- **Cosa viene assorbito dal baseline**: `numeri_primi:cycle_3` e' sia graph bridge sia intermedio classico (`brody_q=0.465`, `w_GUE=0.275`). Su questa riga il Lab non aggiunge fenomeno oltre il fatto che lo stesso campione e' ponte in due lettori.
tools/data/reports/agent_20260515_1904.md:39:> Nel perimetro 8/5, il terzo incluso operativo non e' riducibile a Brody q o a una mistura Poisson/GUE-surmise; il residuo vive nel disaccordo row-aligned tra scalare classico e grafo osservabile.
tools/data/reports/agent_20260515_1904.md:45:- **Punto fisico sorgente**: transizione spettrale tra caos quantistico repulsivo e indipendenza/localizzazione Poisson.
tools/data/reports/agent_20260515_1904.md:46:- **Attraversamento matematico**: fit Brody e mistura Poisson/GUE-surmise sulle stesse righe gia' lette dal grafo kNN.
tools/data/reports/agent_20260515_1904.md:55:- **Denominatore**: 13 righe row-aligned dal perimetro BOUNDARY, 8 GUE e 5 Poisson.
tools/data/reports/agent_20260515_1904.md:57:- **Fit Berry-Robnik-like**: griglia su peso GUE in mistura CDF `w*GUE_surmise + (1-w)*Poisson`, selezionata per KS minimo.
tools/data/reports/agent_20260515_1904.md:64:| graph_only_bridge | 3 |
tools/data/reports/agent_20260515_1904.md:68:| row | label | graph_state | Brody q | w_GUE | KS | audit_state |
tools/data/reports/agent_20260515_1904.md:70:| ising_2d:cycle_1 | GUE | class_interior | 0.090 | 0.070 | 0.428636 | endpoint_like |
tools/data/reports/agent_20260515_1904.md:71:| pendolo_doppio:cycle_2 | Poisson | cut_edge | 0.000 | 0.000 | 0.268279 | endpoint_like |
tools/data/reports/agent_20260515_1904.md:72:| numeri_primi:cycle_3 | GUE | third_included_candidate | 0.465 | 0.275 | 0.148459 | classic_and_graph_bridge |
tools/data/reports/agent_20260515_1904.md:73:| zeta_zeros:cycle_4 | GUE | cut_edge | 1.000 | 0.530 | 0.133555 | classic_only_intermediate |
tools/data/reports/agent_20260515_1904.md:74:| logistica_biforcazione:cycle_5 | GUE | class_interior | 0.000 | 0.000 | 0.998064 | endpoint_like |
tools/data/reports/agent_20260515_1904.md:75:| string_vibration:cycle_6 | Poisson | cut_edge | 0.000 | 0.000 | 0.060129 | endpoint_like |
tools/data/reports/agent_20260515_1904.md:76:| random_matrix:cycle_7 | GUE | cut_edge | 0.975 | 0.475 | 0.119491 | classic_only_intermediate |
tools/data/reports/agent_20260515_1904.md:77:| cellular_automata:cycle_8 | GUE | class_interior | 1.000 | 0.435 | 0.416708 | classic_only_intermediate |
tools/data/reports/agent_20260515_1904.md:78:| percolation:cycle_9 | Poisson | third_included_candidate | 0.025 | 0.025 | 0.054635 | graph_only_bridge |
tools/data/reports/agent_20260515_1904.md:79:| coupled_oscillators:cycle_10 | Poisson | class_interior | 0.000 | 0.000 | 0.079806 | endpoint_like |
tools/data/reports/agent_20260515_1904.md:80:| reaction_diffusion:cycle_11 | GUE | third_included_candidate | 0.000 | 0.000 | 0.174423 | graph_only_bridge |
tools/data/reports/agent_20260515_1904.md:81:| brownian_motion:cycle_12 | Poisson | cut_edge | 0.205 | 0.250 | 0.026002 | classic_only_intermediate |
tools/data/reports/agent_20260515_1904.md:82:| logistica_biforcazione_var_3.5699:cycle_13 | GUE | third_included_candidate | 0.000 | 0.000 | 0.969277 | graph_only_bridge |
tools/data/reports/agent_20260515_1904.md:85:1. Verificato: il denominatore resta quello richiesto, 13 righe con 8 GUE e 5 Poisson.
tools/data/reports/agent_20260515_1904.md:99:- **Invariante di passaggio**: disaccordo nominabile tra `classic_and_graph`, `graph_only`, `classic_only`, `endpoint_like`.
tools/data/reports/agent_20260515_1904.md:103:Il prossimo ciclo utile non deve aggiungere una terza metrica locale. Deve portare il gate a due lettori su un sistema fisico controllato: Rosenzweig-Porter, Anderson/mobility edge o Aubry-Andre con finestre energetiche. Il risultato da cercare e' se `graph_only` e `classic_only` sopravvivono fuori dal perimetro composito del Lab.
tools/data/reports/agent_20260515_1904.md:106:ssp_value: yes. Lo script crea un audit riusabile per separare re-discovery classica, residuo Lab e endpoint-like in ogni perimetro GUE/Poisson row-aligned.
tools/data/reports/evolution_20260427_0330.md:23:2. **Il segno del canale di ordinamento come operatore diagnostico.** Ordinamento che aggiunge rigidita' (det=-1) vs ordinamento che aggiunge bunching (det=+1): questo e' un discriminatore binario applicabile a qualsiasi dominio nuovo. Puo' sostituire la classificazione GUE/Poisson con una piu' fine a due canali.
tools/data/reports/evolution_20260424_0330.md:5:Il produttore ha preso la tensione META+BOUNDARY e l'ha invertita su se stessa: invece di aggiungere domini alla classificazione GUE/Poisson, ha chiesto se la classificazione stessa regge sotto shuffle. Traiettoria pulita — dalla domanda discriminante (shuffle distrugge la classe?) al risultato (due meccanismi distinti, sign(delta_r) come discriminante). 287 secondi, 21 tool calls, zero errori. Il passo ha chiuso con consecutio verso la decomposizione two-channel dei run precedenti.
tools/data/reports/evolution_20260424_0330.md:17:1. **Convergenza two-channel / shuffle**: il produttore stesso lo nota — i 3 domini ordering-GUE sono quelli dove il canale residuo (mod 6) E' il segnale. Questa non e' una nuova analisi da fare, e' una verifica di coerenza gia' matura: basta confrontare la tabella shuffle con i risultati two-channel esistenti. Un singolo script, pochi minuti.
tools/data/reports/evolution_20260424_0330.md:21:3. **Markov-3 nei domini ordering-GUE**: la domanda troncata nella consecutio. Fibonacci e percolation hanno ordering-GUE — la loro struttura d'ordine e' la stessa dei primi (Markov-3 al 33.6%) o diversa? Se diversa, il discriminante si arricchisce: non solo sign(delta_r) ma anche la struttura della memoria sequenziale.
tools/data/reports/agent_20260429_1013.md:1:# Agent Report — Brody Flow: Primes Drift Toward Poisson at 82% Magnitude, 18% Ordering — The Boundary Is a Trajectory, Not a Fixed Point
tools/data/reports/agent_20260429_1013.md:9:> BOUNDARY: 8 domains GUE, 5 Poisson — the boundary is the operative third included.
tools/data/reports/agent_20260429_1013.md:13:Does the Brody parameter beta (interpolating Poisson beta=0 and GUE beta=1) evolve along the prime sequence, or is it a fixed point? If it flows, how much is magnitude (gap distribution shape) and how much is ordering (sequential correlations)?
tools/data/reports/agent_20260429_1013.md:53:Reference: Poisson r = 0.386, GUE r = 0.536. Cramer r = 0.386 (pure Poisson at all positions).
tools/data/reports/agent_20260429_1013.md:57:1. **Primes flow toward Poisson, not toward GUE.** beta decreases from 0.46 (near p ~ 22K) to 0.33 (near p ~ 2M). The GUE/Poisson boundary is not a fixed point — it is a trajectory. The linear fit beta(p) = 0.64 - 0.030 * ln(p) has R^2 = 0.78.
tools/data/reports/agent_20260429_1013.md:59:2. **82% of the flow is magnitude, 18% is ordering.** The shuffle beta tracks the real beta closely, both decreasing. The gap distribution itself becomes more Poisson-like as primes thin out (expected from PNT: gaps approach exponential). The ordering channel provides a small but persistent additional depression of beta (real < shuffle at every scale).
tools/data/reports/agent_20260429_1013.md:65:5. **Cramer is always pure Poisson (beta ~ 0.015).** The entire beta signal — both magnitude and ordering channels — is absent in density-matched random primes. The structure is arithmetic, not statistical.
tools/data/reports/agent_20260429_1013.md:69:The spectral rigidity experiment (2026-04-27) showed beta_sigma(L) INCREASING with spectral scale L: primes become more GUE-like at larger L. This experiment shows beta(N) DECREASING with position N: primes become more Poisson-like at larger N.
tools/data/reports/agent_20260429_1013.md:72:- **Horizontal flow** (this experiment): beta(N) decreases along the prime sequence. At fixed spectral scale, larger primes are more Poisson-like.
tools/data/reports/agent_20260429_1013.md:73:- **Vertical flow** (spectral rigidity): beta(L) increases with window size. At fixed position, larger-scale correlations are more GUE-like.
tools/data/reports/agent_20260429_1013.md:75:The full picture is a 2D map beta(N, L) with opposing gradients. The boundary between GUE and Poisson is a CURVE in this 2D space, not a point.
tools/data/reports/agent_20260429_1013.md:81:- **BOUNDARY**: The boundary is not a classification (GUE vs Poisson) but a flow. Primes start closer to GUE at small N and drift toward Poisson at large N. The boundary IS the trajectory — the third included is the path between the two regimes, not a point on it.
tools/data/reports/agent_20260429_1013.md:82:- **TRASCENDENZA_LIMITE**: If beta(p) = 0.64 - 0.030 * ln(p) persists, then beta = 0 at ln(p) ~ 21, i.e., p ~ 1.3 * 10^9. At that scale, primes would be locally indistinguishable from Poisson. This is a prediction the model makes — testable by extending the sieve to 10^9.
tools/data/reports/agent_20260429_1013.md:86:- **Due radici** (dipolo primario): flusso verso Poisson (la distribuzione dei gap diventa esponenziale man mano che i primi si diradano — magnitudine, 82%) / flusso verso GUE (le correlazioni a grande scala crescono con la finestra — rigidita spettrale, asse perpendicolare). I due flussi operano su assi diversi e in direzioni opposte.
tools/data/reports/agent_20260429_1013.md:87:- **Singolare** (il 1-che-e-tutto): il parametro beta stesso — il singolo numero che comprime la posizione tra Poisson e GUE. Non appartiene ne alla magnitudine ne all'ordinamento — e la sovrapposizione dei due canali collassata in uno scalare. Prima della decomposizione, beta e il segnale intero.
tools/data/reports/agent_20260515_1816.md:51:- Contratto osservabile-operatore: il ciclo testa tau finito della participation ratio a V=2; non testa `gap_ratio`, `V_c` asintotico, PSD surrogate quality, ne universalita GUE/Poisson.
tools/data/reports/agent_20260506_1941.md:17:- domains: prime-gap windows, prime-shuffle controls, iid Poisson spacings, independent GUE spacings;
tools/data/reports/agent_20260506_1941.md:52:At GUE N=2048, `SR`, `L1`, and `triple_var` are stable in all 8 replicates; `SR2` and `L2` are stable in 0 of 8. Mean absolute z-scores: `SR=8.38`, `SR2=0.67`, `L1=11.58`, `L2=0.89`, `triple_var=11.66`.
tools/data/reports/agent_20260506_1941.md:56:Poisson and prime-shuffle controls keep high `rank_all` while most observables are weak. At Poisson N=2048, `rank_all=1.952` but `stable_rank=1.036` and 4.62 of 5 observables are weak on average. This is the falsifying control for treating rank_all alone as a structural claim.
tools/data/reports/agent_20260506_1941.md:60:1. **Perturbation rank is not interpretable without denominator gating.** In this perimeter, Poisson and prime-shuffle controls can show `rank_all` near 1.8-2.0. Because their original-vs-shuffle denominators are mostly weak, that rank is a retention-normalization artifact unless the stable-observable screen also supports it.
tools/data/reports/agent_20260506_1941.md:62:2. **GUE does not show a stable second axis on canonical observables up to N=2048.** GUE `rank_all` falls from 1.913 at N=256 to 1.234 at N=2048; PC2 falls from 16.4% to 4.6%. After denominator gating, GUE stable rank stays close to 1.1-1.2.
tools/data/reports/agent_20260506_1941.md:64:3. **The old L2-driven sign-flip should not be promoted without a denominator check.** Under canonical observables, GUE `L2` is weak relative to shuffle at every tested size and is stable in 0/8 replicates at N >= 512. This does not prove every L2 sign effect is false; it restricts such effects to local/sample-specific observations unless the denominator survives.
tools/data/reports/agent_20260506_1941.md:73:The cycle 03:30 "second GUE axis" remains restricted by cycle 06:25 and is further narrowed here: under canonical observables and the tested size curve, the stable statement is not "GUE has a second perturbation axis"; it is:
tools/data/reports/agent_20260506_1941.md:75:> all-observable perturbation rank can inflate in weak-denominator regimes; after denominator gating, GUE and primes are both close to one perturbation coordinate in this perimeter, while Poisson/shuffle controls show why ungated rank is not structural evidence.
tools/data/reports/agent_20260506_1941.md:84:- **L4 edge cases**: short-GUE and low-N effects are isolated by size. The N=2048 perimeter is stated, not generalized.
tools/data/reports/agent_20260509_1444.md:8:**observable_contract**: claim=il gate BOUNDARY trasferisce fuori dal perimetro base come operatore `null_state -> transfer_state -> denominator_state`; observable=stable canonical observables contro permutation null e layer classification; operator=`exp_denominator_gate_transfer_matrix.py`; generator=`DUALITA_golden`, `R_periodic_triad`, `T_markov_alternating`, `E_ar1_continuity`; denominator=4 perimetri sintetici QxG continuo/discreto, 4096 gaps, 24 replicates, 11 beta layers, 40 shuffle baselines; non_possible=chiamare chiusura QxG, legge GUE/Poisson o endpoint-stable universale; not_tested=perimetro fisico reale, fit `V_c`, nuovi domini autoricerca.
tools/data/reports/agent_20260509_1444.md:11:- **Prima impressione**: dopo 13/13 transfer sul perimetro base, il confine non chiede un altro blank audit. Chiede se il gate resta gate quando non porta piu' le etichette GUE/Poisson.
tools/data/reports/agent_20260509_1444.md:15:- **Operatori laterali scelti**: boundary operator, graph/perimeter transfer matrix, shuffle marginal-preserving. Entrano per trasferire il gate senza importare il label GUE/Poisson.
tools/data/reports/agent_20260509_1444.md:83:- **Invariante di passaggio**: osservabile one-sided contro null permutato; non il label GUE/Poisson e non un set canonico completo.
tools/data/reports/agent_20260506_0330.md:1:# Agent Report — Scale-Selective Perturbations Reveal a Second Axis in GUE, Not in Primes
tools/data/reports/agent_20260506_0330.md:21:- **Domains**: 30,000 prime gaps; GUE (253 unfolded spacings from 23x23 Hermitian matrix).
tools/data/reports/agent_20260506_0330.md:42:### GUE (N=253)
tools/data/reports/agent_20260506_0330.md:58:1. **GUE has a second perturbation axis that primes do not.** Under scale-selective perturbations, GUE effective rank rises from ~1.02 (uniform-only, rank audit 05-05) to 1.889. The second principal component explains 25.2% of variance. For primes, the rank rises only from 1.07 to 1.26 — the second axis is weak (2.6%).
tools/data/reports/agent_20260506_0330.md:60:2. **The L2 observable discriminates perturbation types in GUE.** Under adjacent-swap, GUE L2 retention = -1.135 (sign flip: the perturbation reverses lag-2 correlation). Under large-gap-only, L2 retention = +1.173 (enhancement). This sign difference means adjacent-swap and large-gap-only probe structurally distinct axes of GUE correlations. For primes, L2 also shows anomalous behavior (1.399 under adjacent-swap) but does not flip sign.
tools/data/reports/agent_20260506_0330.md:64:4. **The previous single-coordinate result was a property of uniform shuffle, not of the boundary itself.** Uniform shuffle is the most destructive perturbation — it erases all scales simultaneously, producing a single "damage axis." Scale-selective perturbations separate this into at least two components (especially in GUE).
tools/data/reports/agent_20260506_0330.md:66:5. **Caveat: GUE sample is small (N=253).** The GUE matrix size was limited by available computation. The effective rank 1.889 may shift with larger samples, though the sign flip in L2 is a qualitative feature unlikely to disappear.
tools/data/reports/agent_20260506_0330.md:69:**CONSTRAINT on META + BOUNDARY**: the single latent coordinate found under uniform shuffle (rank audit 05-05) is a property of the perturbation type, not of the boundary itself. Scale-selective perturbations reveal a second axis in GUE (PC2=25.2%) and a weak second axis in primes (PC2=2.6%). The operational consequence: **GUE and primes have different perturbation dimensionality** — GUE correlations live on at least 2 perturbation axes, primes on ~1.3. This asymmetry between domains is new structure, not previously measured.
tools/data/reports/agent_20260506_0330.md:71:Perimeter: 30,000 prime gaps (p_2 to p_{30001}), 253 GUE spacings, 4 perturbation types, 5 alpha values, 16 trials each, seed 20260506.
tools/data/reports/agent_20260506_0330.md:76:- **Invariante di passaggio**: GUE has higher perturbation dimensionality than primes. This holds across the perturbation types tested. The asymmetry (GUE ~2D, primes ~1.3D) persists through the vertex.
tools/data/reports/agent_20260506_0330.md:80:- **L1 hard constraint vs bias**: No absolutes. "~2D" and "~1.3D" are effective ranks, not exact integers. The L2 sign flip in GUE is qualitative (negative under adjacent-swap, positive under large-gap-only) — this IS a hard structural difference, not a bias.
tools/data/reports/agent_20260506_0330.md:82:- **L3 no silent patching**: The rank audit claim (05-05: "boundary is 1D in observable space") is NOT declared resolved. It is refined: "1D under uniform shuffle, multi-D under scale-selective, especially in GUE." The distinction is explicit.
tools/data/reports/agent_20260506_0330.md:83:- **L4 edge cases**: GUE N=253 is declared as caveat. The sign flip in L2 is not an edge case — it occurs at all tested alphas (0.1 through 0.9 for adjacent-swap).
tools/data/reports/agent_20260506_0330.md:84:- **L5 re-discovery**: Scale-selective perturbation is related to multiscale analysis (wavelet-like decompositions). The specific application to prime gap vs GUE spacing perturbation dimensionality is not a standard result in random matrix theory. The L2 sign flip under adjacent-swap may relate to Markov chain perturbation theory. Not tagged NEW — tagged as CONSTRAINT refining previous result.
tools/data/reports/agent_20260505_1022.md:20:- dati: `primes` con 8000 gap, `GUE` con 175 spacing effettivi prodotti dal generatore corrente, `Poisson` con 8000 spacing;
tools/data/reports/agent_20260505_1022.md:44:Su GUE il campione effettivo e' piccolo (`N=175`), quindi il risultato e' solo indicativo:
tools/data/reports/agent_20260505_1022.md:55:Poisson resta quasi nullo rispetto a SR/SR2/triple_var: molte curve non attraversano `|z| >= 2`. Questo e' coerente con un controllo a bassa struttura, non con una prova di assenza assoluta.
tools/data/reports/agent_20260505_1022.md:75:L2 quantita' assoluta vs ratio: il confronto usa alpha critici e z-score, non percentuali tra spazi di taglia diversa. GUE ha perimetro ridotto (`N=175`) e non viene pesato come primes/Poisson.
tools/data/reports/report_20260305_2121.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260305_2121.md:23:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=0.8650 (GUE standard=0.60)
tools/data/reports/report_20260305_2121.md:26:- brownian_motion_var_0.5: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260305_2121.md:27:- coupled_oscillators_var_50: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260305_2121.md:28:- percolation_var_0.55: r=0.6944444444444445, spacing=Poisson-like
tools/data/reports/report_20260305_2121.md:29:- percolation_var_0.65: r=1.0454545454545456, spacing=Poisson-like
tools/data/reports/agent_20260509_0829.md:8:**observable_contract**: claim=la soglia di memoria del boundary si vede variando blocchi e periodi attorno alle scale 21/34; observable=`label_jaccard`, `acceptance_rate`, `hamming_ratio`, `source_mode`, `event_type`, `vc_interp`, `r_floor`; operator=griglia fine di block shuffle e periodic approximant contro lettore gap-label phi e curva `r(V)`; generator=non-Sturmian candidate pool gia' usato nel gate 08:19; denominator=`N=144`, phases `{0,0.25,0.5,0.75}`, r_threshold `{0.48,0.50,0.52}`, block sizes `{2,3,5,8,10,13,16,21,24,27,31,34,37,40,45,50,55}`, periods `{5,8,10,13,16,21,24,27,31,34,37,40,45,50,55,72,89}`, 128 balanced random trials, 96 mode trials; non_possible=se `label_jaccard` alto non implica `internal_cross`, il label-set isolato non puo' essere usato come null indipendente del boundary; not_tested=scale `N!=144`, fit power-law, GUE/Poisson transfer, gap_ratio.
tools/data/reports/agent_20260509_0829.md:90:- **L3 no observable drift**: `gap_ratio`, fit power-law e GUE/Poisson non sono testati.
tools/data/reports/agent_20260514_1649.md:8:**observable_contract**: claim=il candidato QxG 16:40 diventa fit-ready solo se espone input, output, soglie, trace, transfer/blank/fall e contro-perimetro; observable=`component_state(SR,L1,triple_var)` piu `poisson_contrast` e `direct_contrast`; operator=`tools/exp_physical_sr_residue_bounce.py`; generator=nessun nuovo dominio, riuso deposito GOE/GUE/Anderson 16:40 e smoke test sintetico minimo; denominator=artifact JSON fit-ready + interfaccia su spettro ordinato; non_possible=integrare automaticamente il grafo o chiamare legge fisica il tester; not_tested=dati sperimentali, Anderson 3D, many-body localization, unfolding dedicato, limite asintotico.
tools/data/reports/agent_20260514_1649.md:20:- **Combo**: A2 confine det=-1 + A9 terzo incluso + A11 combo + ponte QxG continuo/discreto + candidato graph completion 16:40 + direzione BOUNDARY GUE/Poisson.
tools/data/reports/agent_20260514_1649.md:24:- **Possibile / non-possibile**: possibile = passare uno spettro ordinato e ottenere stato componente, contrasto Poisson e contrasto diretto se classi presenti; non-possibile = usare l'artifact come ponte QxG integrato o legge fisica.
tools/data/reports/agent_20260514_1649.md:79:| GUE unitary no time reversal | active | active | active | `physical_sr_residue_bounce_20260514_1640_goe_gue_ncurve.json` |
tools/data/reports/agent_20260514_1649.md:86:| class | focus_signature | SR state vs Poisson | note |
tools/data/reports/agent_20260514_1649.md:89:| GUE smoke | SR,L1,triple_var | active | plumbing verificato, non evidenza fisica nuova |
tools/data/reports/agent_20260514_1649.md:91:Il `direct_contrast` dello smoke e' presente ma non promosso: un solo spettro per classe non fornisce una distribuzione indipendente per separazione fisica. La separazione GOE/GUE fit-ready resta quella del deposito 16:40 con 64 repliche per taglia.
tools/data/reports/agent_20260514_1649.md:95:- `transfer`: `SR,L1,triple_var` passano dal deposito matematico-fisico allo strumento come stati componente contro Poisson e, se esistono classi, come contrasto diretto.
tools/data/reports/agent_20260514_1649.md:97:- `fall`: il tester cade se GOE/GUE non separano `SR` nel contrasto diretto, se le classi caotiche assorbono tutti gli osservabili focus contro Poisson, o se Anderson `W=6` mantiene `SR` active sotto le soglie dichiarate.
tools/data/reports/agent_20260514_1649.md:104:- **Verificato da deposito 16:40**: GOE/GUE hanno `SR,L1,triple_var` active; Anderson `W=6` assorbe `SR` e conserva `triple_var`.
tools/data/reports/agent_20260514_1649.md:128:`component_state(SR,L1,triple_var)` e' fit-ready come strumento: riceve uno spettro ordinato, produce stato componente, contrasto Poisson e contrasto diretto quando il payload contiene classi. Il candidato QxG resta pronto per decisione operatore, non integrato nel grafo e non promosso a legge fisica.
tools/data/reports/agent_20260509_1839.md:9:**observable_contract**: claim=la tassonomia delle transizioni post-estensione scala se nessuna delle 13 righe resta `thin_persists`; observable=`transition_class` row-aligned sulle 13 righe; operator=`exp_boundary_transition_taxonomy_13rows.py`; generator=composizione dei depositi 15:32, 15:38, 15:56 e prescan 15:00 senza rigenerare segnali; denominator=13 righe BOUNDARY semi-reali; non_possible=promuovere `blank_thin_support` come specie autonoma se `thin_persist_rows=0`; not_tested=nuova griglia beta, nuovi null, fit `V_c`, validita' label GUE/Poisson sorgente.
tools/data/reports/agent_20260509_1839.md:15:- **Piano superiore**: sheaf locale del boundary su 13 sezioni. La sezione corta viene riparata, poi ricollocata nell'atlante senza usare label GUE/Poisson.
tools/data/reports/agent_20260509_1839.md:25:- `not_drift`: non torna a `V_c`, non difende thin blank, non usa label GUE/Poisson come decision field; compone solo depositi row-aligned gia' misurati.
tools/data/reports/agent_20260509_0741.md:8:**observable_contract**: claim=il generatore surrogate per `V_c` deve raggiungere `Jaccard>=0.75` a N=144 con acceptance_rate non nulla prima del trasferimento GUE/Poisson; observable=`label_jaccard`, `acceptance_rate`, `hamming_ratio`, `source_mode`, `event_type`, `vc_interp`, `r_floor`, `r_span`; operator=aggiunta di candidati `phase_shift_sturmian` al generatore label-preserving, poi lettura della curva `r(V)`; generator=phi Sturmian, balanced_random, phase_shift_sturmian dentro `swap_label_surrogate`; denominator=N=144, phase={0,0.25,0.5,0.75}, r_threshold={0.48,0.50,0.52}, label_trials=2, phase_candidate_trials=64, swap_steps=0; non_possible=se il gate passa solo con source_mode Sturmian, il null e' ponte strutturato e non controprova indipendente del boundary; not_tested=GUE/Poisson, silver/bronze, fit power-law, gap_ratio, indipendenza del null fuori da source_mode Sturmian.
tools/data/reports/agent_20260509_0741.md:19:- **Proto-ipotesi**: se la raggiungibilita' del label-set e' il blocco operativo, un generatore Sturmian a fase traslata deve superare `Jaccard>=0.75` a N=144. Se passa, il blocco tecnico cade; se passa solo mantenendo source_mode Sturmian, il null resta ponte strutturato e non autorizza il confronto GUE/Poisson.
tools/data/reports/agent_20260509_0741.md:73:**CONSTRAINT**: il null label-preserving per `V_c` raggiunge `Jaccard>=0.75` a `N=144` con acceptance_rate non nulla (`6/8` sequenze, `0.75` sulle righe evento), ma solo come `phase_shift_sturmian`. Quindi il Lab ha un ponte strutturato accettabile per testare coerenza interna del boundary, non un contro-campo indipendente per trasferire verso GUE/Poisson.
tools/data/reports/agent_20260509_0741.md:81:- **Campo di possibilita**: qui diventa possibile usare un ponte label-preserving a N=144 per audit interno di `V_c`; qui diventa non-possibile passare a GUE/Poisson finche' `source_mode` resta Sturmian.
tools/data/reports/agent_20260509_0741.md:84:Il prossimo passo non e' confrontare GUE/Poisson. E' spezzare il ponte: cercare un generatore non-Sturmian che mantenga `label_jaccard>=0.75` e `hamming_ratio` non triviale, oppure promuovere il vincolo che il label-set alto e' raggiungibile solo attraverso trasporto Sturmian nel perimetro N=144.
tools/data/reports/agent_20260509_0741.md:88:- **L1 hard constraint**: il verdict non autorizza GUE/Poisson; dichiara source_mode Sturmian come limite.
tools/data/reports/agent_20260419_0755.md:77:- **Test at larger scales**: Does the alpha_res gap (1.24 vs 1.60) change with prime scale? If it narrows toward Poisson, the long-range class memory also has a crossover.
tools/data/reports/report_20260307_0342.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260307_0342.md:23:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=0.8650 (GUE standard=0.60)
tools/data/reports/report_20260307_0342.md:26:- brownian_motion_var_0.5: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260307_0342.md:27:- coupled_oscillators_var_50: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260307_0342.md:28:- percolation_var_0.55: r=1.0384615384615385, spacing=Poisson-like
tools/data/reports/report_20260307_0342.md:29:- percolation_var_0.65: r=0.9642857142857143, spacing=Poisson-like
tools/data/reports/report_20260303_0341.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260303_0341.md:23:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=0.8650 (GUE standard=0.60)
tools/data/reports/report_20260303_0341.md:26:- brownian_motion_var_0.5: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260303_0341.md:27:- coupled_oscillators_var_50: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260303_0341.md:28:- percolation_var_0.55: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260303_0341.md:29:- percolation_var_0.65: r=1.380952380952381, spacing=Poisson-like
tools/data/reports/agent_20260419_0330.md:9:> Agent_0418 discovered residue lag-1 acf = -0.148 (3.8x magnitude acf1). Are these truly independent channels with different scaling laws and different Poisson crossover scales?
tools/data/reports/agent_20260419_0330.md:13:Decompose the prime gap anti-correlation into a **residue channel** (Z/6Z class sequence: which coset {1,5} each prime falls in) and a **magnitude channel** (gap size demeaned by transition type). Do they have independent scaling laws, power-law exponents, coherence lengths, and Poisson crossover points?
tools/data/reports/agent_20260419_0330.md:21:- **Metrics**: lag-1 ACF, power-law exponent alpha (|acf(k)| ~ A/k^alpha), linear scaling in ln(p), Poisson crossover extrapolation, coherence length, cross-channel correlation, variance partition
tools/data/reports/agent_20260419_0330.md:46:| Channel | Slope (per ln p) | R2 | Poisson crossover |
tools/data/reports/agent_20260419_0330.md:96:2. **The channels have different Poisson crossover scales, separated by 2.7 decades.** Residue channel (which Z/6Z class) loses memory at p ~ 10^14.9. Magnitude channel (gap size within class) loses memory at p ~ 10^17.6. The hierarchy is: residue dies first, magnitude persists.
tools/data/reports/agent_20260419_0330.md:112:- Different Poisson crossover points (2.7 decades apart)
tools/data/reports/agent_20260419_0330.md:118:- **Derive the crossover separation analytically**: The 2.7-decade gap between residue and magnitude Poisson crossovers should follow from PNT + Hardy-Littlewood. The residue decay rate (+0.0072/ln p) may connect to the prime race bias (Chebyshev).
tools/data/reports/agent_20260426_0330.md:9:> BOUNDARY: 8 domains GUE, 5 Poisson — where is the boundary?
tools/data/reports/agent_20260426_0330.md:13:Does spectral rigidity (number variance Sigma^2(L)) — an observable independent from the r-statistic — confirm or refute our GUE/Poisson classification? And does the dual-channel structure (magnitude vs algebraic ordering) manifest at the level of long-range spectral statistics?
tools/data/reports/agent_20260426_0330.md:17:- **Theory**: GUE predicts Sigma^2(L) ~ (2/pi^2) ln(L), Poisson predicts Sigma^2(L) = L
tools/data/reports/agent_20260426_0330.md:18:- **Domains**: 8 domains (primes, GUE matrices, coupled_osc, string_vib, percolation, logistic, brownian, Poisson random)
tools/data/reports/agent_20260426_0330.md:22:- **Metric**: Sig2/L ratio (GUE << 1, Poisson = 1), log-log slope, ordering fraction = (Sig2_shuf - Sig2_real) / Sig2_shuf
tools/data/reports/agent_20260426_0330.md:30:| gue_matrix | dist-GUE | 359 | 0.073 | 0.452 | 0.195 | -4.8 | YES |
tools/data/reports/agent_20260426_0330.md:31:| primes* | dist-GUE | 5132 | 0.510 | 1.058 | 0.576 | -1.5 | NO |
tools/data/reports/agent_20260426_0330.md:32:| coupled_osc | ord-GUE | 2002 | 4.491 | 1.637 | 2.108 | 17.8 | NO |
tools/data/reports/agent_20260426_0330.md:33:| string_vib | ord-GUE | 7999 | 2.837 | 1.606 | 1.285 | 23.8 | NO |
tools/data/reports/agent_20260426_0330.md:34:| percolation | ord-GUE | 199 | 1.969 | 1.416 | 1.222 | 3.3 | NO |
tools/data/reports/agent_20260426_0330.md:35:| logistic | Poisson | 4999 | 462.4 | 0.757 | 1318.9 | -0.4 | YES |
tools/data/reports/agent_20260426_0330.md:36:| brownian | Poisson | 4999 | 9.250 | 1.433 | 0.600 | 287.1 | YES |
tools/data/reports/agent_20260426_0330.md:37:| poisson | Poisson | 10000 | 1.050 | 0.999 | 1.014 | 0.6 | YES |
tools/data/reports/agent_20260426_0330.md:56:Log-log slope: real = 0.737, shuffle = 0.971. GUE theory ≈ 0.3, Poisson = 1.0.
tools/data/reports/agent_20260426_0330.md:58:### Ordering-GUE paradox
tools/data/reports/agent_20260426_0330.md:60:Ordering-GUE domains (coupled_osc, string_vib, percolation) show Sig2/L > 1 — they are SUPER-Poisson. The ordering creates excess clustering (bunching), not repulsion. Shuffling REDUCES their variance (z = 3 to 24). The r-statistic sees nearest-neighbor repulsion; Sig2 sees long-range bunching. These are two different properties.
tools/data/reports/agent_20260426_0330.md:68:3. **Only true GUE matrices are rigid at all scales (Sig2/L = 0.073 at L=10).** Primes live in an intermediate regime (0.376 at L=10) — more rigid than Poisson, less rigid than GUE. This is NOT a failure of GUE classification — it's a finer structure that the r-statistic cannot resolve.
tools/data/reports/agent_20260426_0330.md:70:4. **Ordering-GUE domains are anti-rigid at long range.** They show super-Poisson variance (Sig2/L > 1), meaning the ordering creates clustering, not repulsion. The r-statistic and Sig2 classify them differently: r sees short-range repulsion, Sig2 sees long-range bunching.
tools/data/reports/agent_20260426_0330.md:72:5. **META resolved: the tests are not tautological, but they are incomplete.** The r-statistic captures genuine structure (short-range spacing repulsion) confirmed by an independent observable. But Sig2(L) reveals richer structure that the r-statistic cannot see. The 8/5 GUE/Poisson split is a projection of a higher-dimensional reality.
tools/data/reports/agent_20260426_0330.md:78:- BOUNDARY: The boundary is not a line separating GUE from Poisson. It is a surface in the (short-range, long-range, ordering-fraction) space. Primes sit in the interior of this surface, not at a boundary.
tools/data/reports/agent_20260426_0330.md:80:- C1: Primes remain unique — the only domain where ordering INCREASES rigidity at long range while maintaining intermediate short-range repulsion. GUE matrices have stronger short-range repulsion; ordering-GUE domains have anti-rigidity at long range.
tools/data/reports/agent_20260426_0330.md:87:- **Campo di possibilità**: diventa possibile predire la rigidità a scala L dalla decomposizione (distribuzione + ordinamento) con due parametri indipendenti. Diventa non-possibile trattare i primi come "GUE" o "Poisson" — vivono in un continuo parametrizzato dalla scala, e nessun singolo numero li classifica.
tools/data/reports/agent_20260509_0637.md:8:**observable_contract**: claim=la forma `r(V)` del boundary Sturmian-Harper e' sostenuta da attraversamenti interni, non da collasso al bordo minimo della filtrazione; observable=`event={floor_hit,internal_cross,internal_multi,no_cross}`, `vc_interp`, `r_floor`, `r_span`, `label_jaccard`; operator=curva `r(V)` su griglia 0.5..3.0 step 0.01, crossing lineare e classificazione del primo stato rispetto a `r_threshold={0.48,0.50,0.52}`; generator=phi Sturmian, phase-shuffle Sturmian, random bilanciato, surrogate random selezionato per overlap label-set; denominator=N={89,144,233}, phase={0,0.25,0.5,0.75}, phase_trials=2, random_trials=2, label_trials=2, label_candidates=5, seed=202605090637; non_possible=se un null che preserva label-set produce crossing interno unico e stesso `r_floor` dei generatori Sturmian, `V_c` non e' piu' boundary map di ordine Sturmian; not_tested=GUE/Poisson reali, silver/bronze in questo ciclo, fit power-law, label-preserving forte con accettazione Jaccard>=0.75.
tools/data/reports/agent_20260509_0637.md:43:- Contratto osservabile-operatore: `gap_ratio`, controlli metallici silver/bronze e domini GUE/Poisson non vengono testati in questo ciclo.
tools/data/reports/report_20260405_0715.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260405_0715.md:22:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=1.0000 (GUE standard=0.60)
tools/data/reports/report_20260405_0715.md:25:- percolation_var_0.55: r=0.7543859649122806, spacing=Poisson-like
tools/data/reports/report_20260405_0715.md:26:- numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like
tools/data/reports/report_20260405_0715.md:27:- coupled_oscillators_var_50: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260405_0715.md:28:- brownian_motion_var_0.5: r=1.0, spacing=Poisson-like
tools/data/reports/agent_20260515_1940.md:8:**observable_contract**: claim=il gate RP a due lettori e fisico solo se la stessa riga lambda resta stabile attraversando le taglie; observable=two_reader_all_sizes da graph_bridge_frequency unita a Brody q, peso Wigner/Poisson, SR e IPR; operator=flusso Rosenzweig-Porter diagonal-plus-GUE ripetuto su N, seed e perturbazioni kNN; generator=H(lambda)=sqrt(1-lambda)D+sqrt(lambda)GUE; denominator=11 righe lambda identiche su N={64,96,128}; non_possible=claim fisico two-reader se nessuna riga e stable_graph_bridge+classical_intermediate in tutte le taglie; not_tested=limite N infinito, unfolding alternativi, Anderson/mobility edge, varianti many-body.
tools/data/reports/agent_20260515_1940.md:14:- **Combo**: A9 terzo incluso + QxG continuo/discreto + flusso Hamiltoniano RP + tensione BOUNDARY "8 domini GUE, 5 Poisson".
tools/data/reports/agent_20260515_1940.md:15:- **Dipolo / punto-zero**: polo Poisson diagonale / polo GUE. Punto-zero: riga lambda che resta insieme ponte grafico stabile e intermedia classica su piu taglie.
tools/data/reports/agent_20260515_1940.md:24:- `why`: il ciclo resta sul confine GUE/Poisson e testa il terzo incluso operativo dentro un flusso Hamiltoniano controllato, con separazione tra endpoint, riga a due lettori e residui del grafo.
tools/data/reports/agent_20260515_1940.md:27:## Re-discovery audit
tools/data/reports/agent_20260515_1940.md:28:- **Baseline noto piu vicino**: crossover Rosenzweig-Porter / Wigner-Dyson-GUE vs Poisson, letto con adjacent gap ratio, Brody q e mistura Wigner/Poisson.
tools/data/reports/agent_20260515_1940.md:32:- **Correzione L3/L5 richiesta**: `two_reader_boundary_confirmed = 1`; `graph_only_residue = 0`; `scope_change_declared = true`; `graph_baseline_audit = kNN stability + size sweep + Brody/Berry-like row-aligned`. Non sommo le righe classic-only al boundary a due lettori.
tools/data/reports/agent_20260515_1940.md:41:- **Punto fisico sorgente**: transizione spettrale tra indipendenza/localizzazione Poisson e repulsione GUE.
tools/data/reports/agent_20260515_1940.md:42:- **Attraversamento matematico**: Hamiltoniana diagonal-plus-GUE, osservabili sui gap, Brody/Berry-like e grafo kNN perturbato su taglie multiple.
tools/data/reports/agent_20260515_1940.md:61:| two_reader_all_sizes | 1 |
tools/data/reports/agent_20260515_1940.md:62:| two_reader_intermittent | 2 |
tools/data/reports/agent_20260515_1940.md:63:| graph_only_residue | 0 |
tools/data/reports/agent_20260515_1940.md:81:4. Verificato: `graph_only_residue = 0` su tutte le taglie. Il residuo Lab-specific graph-only non rientra nel flusso RP size-sweep.
tools/data/reports/agent_20260515_1940.md:99:ssp_value: yes. Lo script e riusabile per stressare gate GUE/Poisson controllati su taglie multiple e restituisce direttamente righe all-size, righe intermittenti, residui graph-only e residui classic-only.
tools/data/reports/agent_20260506_0330.md:1:# Agent Report — Scale-Selective Perturbations Reveal a Second Axis in GUE, Not in Primes
tools/data/reports/agent_20260506_0330.md:21:- **Domains**: 30,000 prime gaps; GUE (253 unfolded spacings from 23x23 Hermitian matrix).
tools/data/reports/agent_20260506_0330.md:42:### GUE (N=253)
tools/data/reports/agent_20260506_0330.md:58:1. **GUE has a second perturbation axis that primes do not.** Under scale-selective perturbations, GUE effective rank rises from ~1.02 (uniform-only, rank audit 05-05) to 1.889. The second principal component explains 25.2% of variance. For primes, the rank rises only from 1.07 to 1.26 — the second axis is weak (2.6%).
tools/data/reports/agent_20260506_0330.md:60:2. **The L2 observable discriminates perturbation types in GUE.** Under adjacent-swap, GUE L2 retention = -1.135 (sign flip: the perturbation reverses lag-2 correlation). Under large-gap-only, L2 retention = +1.173 (enhancement). This sign difference means adjacent-swap and large-gap-only probe structurally distinct axes of GUE correlations. For primes, L2 also shows anomalous behavior (1.399 under adjacent-swap) but does not flip sign.
tools/data/reports/agent_20260506_0330.md:64:4. **The previous single-coordinate result was a property of uniform shuffle, not of the boundary itself.** Uniform shuffle is the most destructive perturbation — it erases all scales simultaneously, producing a single "damage axis." Scale-selective perturbations separate this into at least two components (especially in GUE).
tools/data/reports/agent_20260506_0330.md:66:5. **Caveat: GUE sample is small (N=253).** The GUE matrix size was limited by available computation. The effective rank 1.889 may shift with larger samples, though the sign flip in L2 is a qualitative feature unlikely to disappear.
tools/data/reports/agent_20260506_0330.md:69:**CONSTRAINT on META + BOUNDARY**: the single latent coordinate found under uniform shuffle (rank audit 05-05) is a property of the perturbation type, not of the boundary itself. Scale-selective perturbations reveal a second axis in GUE (PC2=25.2%) and a weak second axis in primes (PC2=2.6%). The operational consequence: **GUE and primes have different perturbation dimensionality** — GUE correlations live on at least 2 perturbation axes, primes on ~1.3. This asymmetry between domains is new structure, not previously measured.
tools/data/reports/agent_20260506_0330.md:71:Perimeter: 30,000 prime gaps (p_2 to p_{30001}), 253 GUE spacings, 4 perturbation types, 5 alpha values, 16 trials each, seed 20260506.
tools/data/reports/agent_20260506_0330.md:76:- **Invariante di passaggio**: GUE has higher perturbation dimensionality than primes. This holds across the perturbation types tested. The asymmetry (GUE ~2D, primes ~1.3D) persists through the vertex.
tools/data/reports/agent_20260506_0330.md:80:- **L1 hard constraint vs bias**: No absolutes. "~2D" and "~1.3D" are effective ranks, not exact integers. The L2 sign flip in GUE is qualitative (negative under adjacent-swap, positive under large-gap-only) — this IS a hard structural difference, not a bias.
tools/data/reports/agent_20260506_0330.md:82:- **L3 no silent patching**: The rank audit claim (05-05: "boundary is 1D in observable space") is NOT declared resolved. It is refined: "1D under uniform shuffle, multi-D under scale-selective, especially in GUE." The distinction is explicit.
tools/data/reports/agent_20260506_0330.md:83:- **L4 edge cases**: GUE N=253 is declared as caveat. The sign flip in L2 is not an edge case — it occurs at all tested alphas (0.1 through 0.9 for adjacent-swap).
tools/data/reports/agent_20260506_0330.md:84:- **L5 re-discovery**: Scale-selective perturbation is related to multiscale analysis (wavelet-like decompositions). The specific application to prime gap vs GUE spacing perturbation dimensionality is not a standard result in random matrix theory. The L2 sign flip under adjacent-swap may relate to Markov chain perturbation theory. Not tagged NEW — tagged as CONSTRAINT refining previous result.
tools/data/reports/agent_20260509_0829.md:8:**observable_contract**: claim=la soglia di memoria del boundary si vede variando blocchi e periodi attorno alle scale 21/34; observable=`label_jaccard`, `acceptance_rate`, `hamming_ratio`, `source_mode`, `event_type`, `vc_interp`, `r_floor`; operator=griglia fine di block shuffle e periodic approximant contro lettore gap-label phi e curva `r(V)`; generator=non-Sturmian candidate pool gia' usato nel gate 08:19; denominator=`N=144`, phases `{0,0.25,0.5,0.75}`, r_threshold `{0.48,0.50,0.52}`, block sizes `{2,3,5,8,10,13,16,21,24,27,31,34,37,40,45,50,55}`, periods `{5,8,10,13,16,21,24,27,31,34,37,40,45,50,55,72,89}`, 128 balanced random trials, 96 mode trials; non_possible=se `label_jaccard` alto non implica `internal_cross`, il label-set isolato non puo' essere usato come null indipendente del boundary; not_tested=scale `N!=144`, fit power-law, GUE/Poisson transfer, gap_ratio.
tools/data/reports/agent_20260509_0829.md:90:- **L3 no observable drift**: `gap_ratio`, fit power-law e GUE/Poisson non sono testati.
tools/data/reports/agent_20260509_1839.md:9:**observable_contract**: claim=la tassonomia delle transizioni post-estensione scala se nessuna delle 13 righe resta `thin_persists`; observable=`transition_class` row-aligned sulle 13 righe; operator=`exp_boundary_transition_taxonomy_13rows.py`; generator=composizione dei depositi 15:32, 15:38, 15:56 e prescan 15:00 senza rigenerare segnali; denominator=13 righe BOUNDARY semi-reali; non_possible=promuovere `blank_thin_support` come specie autonoma se `thin_persist_rows=0`; not_tested=nuova griglia beta, nuovi null, fit `V_c`, validita' label GUE/Poisson sorgente.
tools/data/reports/agent_20260509_1839.md:15:- **Piano superiore**: sheaf locale del boundary su 13 sezioni. La sezione corta viene riparata, poi ricollocata nell'atlante senza usare label GUE/Poisson.
tools/data/reports/agent_20260509_1839.md:25:- `not_drift`: non torna a `V_c`, non difende thin blank, non usa label GUE/Poisson come decision field; compone solo depositi row-aligned gia' misurati.
tools/data/reports/agent_20260509_0741.md:8:**observable_contract**: claim=il generatore surrogate per `V_c` deve raggiungere `Jaccard>=0.75` a N=144 con acceptance_rate non nulla prima del trasferimento GUE/Poisson; observable=`label_jaccard`, `acceptance_rate`, `hamming_ratio`, `source_mode`, `event_type`, `vc_interp`, `r_floor`, `r_span`; operator=aggiunta di candidati `phase_shift_sturmian` al generatore label-preserving, poi lettura della curva `r(V)`; generator=phi Sturmian, balanced_random, phase_shift_sturmian dentro `swap_label_surrogate`; denominator=N=144, phase={0,0.25,0.5,0.75}, r_threshold={0.48,0.50,0.52}, label_trials=2, phase_candidate_trials=64, swap_steps=0; non_possible=se il gate passa solo con source_mode Sturmian, il null e' ponte strutturato e non controprova indipendente del boundary; not_tested=GUE/Poisson, silver/bronze, fit power-law, gap_ratio, indipendenza del null fuori da source_mode Sturmian.
tools/data/reports/agent_20260509_0741.md:19:- **Proto-ipotesi**: se la raggiungibilita' del label-set e' il blocco operativo, un generatore Sturmian a fase traslata deve superare `Jaccard>=0.75` a N=144. Se passa, il blocco tecnico cade; se passa solo mantenendo source_mode Sturmian, il null resta ponte strutturato e non autorizza il confronto GUE/Poisson.
tools/data/reports/agent_20260509_0741.md:73:**CONSTRAINT**: il null label-preserving per `V_c` raggiunge `Jaccard>=0.75` a `N=144` con acceptance_rate non nulla (`6/8` sequenze, `0.75` sulle righe evento), ma solo come `phase_shift_sturmian`. Quindi il Lab ha un ponte strutturato accettabile per testare coerenza interna del boundary, non un contro-campo indipendente per trasferire verso GUE/Poisson.
tools/data/reports/agent_20260509_0741.md:81:- **Campo di possibilita**: qui diventa possibile usare un ponte label-preserving a N=144 per audit interno di `V_c`; qui diventa non-possibile passare a GUE/Poisson finche' `source_mode` resta Sturmian.
tools/data/reports/agent_20260509_0741.md:84:Il prossimo passo non e' confrontare GUE/Poisson. E' spezzare il ponte: cercare un generatore non-Sturmian che mantenga `label_jaccard>=0.75` e `hamming_ratio` non triviale, oppure promuovere il vincolo che il label-set alto e' raggiungibile solo attraverso trasporto Sturmian nel perimetro N=144.
tools/data/reports/agent_20260509_0741.md:88:- **L1 hard constraint**: il verdict non autorizza GUE/Poisson; dichiara source_mode Sturmian come limite.
tools/data/reports/agent_20260419_0330.md:9:> Agent_0418 discovered residue lag-1 acf = -0.148 (3.8x magnitude acf1). Are these truly independent channels with different scaling laws and different Poisson crossover scales?
tools/data/reports/agent_20260419_0330.md:13:Decompose the prime gap anti-correlation into a **residue channel** (Z/6Z class sequence: which coset {1,5} each prime falls in) and a **magnitude channel** (gap size demeaned by transition type). Do they have independent scaling laws, power-law exponents, coherence lengths, and Poisson crossover points?
tools/data/reports/agent_20260419_0330.md:21:- **Metrics**: lag-1 ACF, power-law exponent alpha (|acf(k)| ~ A/k^alpha), linear scaling in ln(p), Poisson crossover extrapolation, coherence length, cross-channel correlation, variance partition
tools/data/reports/agent_20260419_0330.md:46:| Channel | Slope (per ln p) | R2 | Poisson crossover |
tools/data/reports/agent_20260419_0330.md:96:2. **The channels have different Poisson crossover scales, separated by 2.7 decades.** Residue channel (which Z/6Z class) loses memory at p ~ 10^14.9. Magnitude channel (gap size within class) loses memory at p ~ 10^17.6. The hierarchy is: residue dies first, magnitude persists.
tools/data/reports/agent_20260419_0330.md:112:- Different Poisson crossover points (2.7 decades apart)
tools/data/reports/agent_20260419_0330.md:118:- **Derive the crossover separation analytically**: The 2.7-decade gap between residue and magnitude Poisson crossovers should follow from PNT + Hardy-Littlewood. The residue decay rate (+0.0072/ln p) may connect to the prime race bias (Chebyshev).
tools/data/reports/agent_20260509_0637.md:8:**observable_contract**: claim=la forma `r(V)` del boundary Sturmian-Harper e' sostenuta da attraversamenti interni, non da collasso al bordo minimo della filtrazione; observable=`event={floor_hit,internal_cross,internal_multi,no_cross}`, `vc_interp`, `r_floor`, `r_span`, `label_jaccard`; operator=curva `r(V)` su griglia 0.5..3.0 step 0.01, crossing lineare e classificazione del primo stato rispetto a `r_threshold={0.48,0.50,0.52}`; generator=phi Sturmian, phase-shuffle Sturmian, random bilanciato, surrogate random selezionato per overlap label-set; denominator=N={89,144,233}, phase={0,0.25,0.5,0.75}, phase_trials=2, random_trials=2, label_trials=2, label_candidates=5, seed=202605090637; non_possible=se un null che preserva label-set produce crossing interno unico e stesso `r_floor` dei generatori Sturmian, `V_c` non e' piu' boundary map di ordine Sturmian; not_tested=GUE/Poisson reali, silver/bronze in questo ciclo, fit power-law, label-preserving forte con accettazione Jaccard>=0.75.
tools/data/reports/agent_20260509_0637.md:43:- Contratto osservabile-operatore: `gap_ratio`, controlli metallici silver/bronze e domini GUE/Poisson non vengono testati in questo ciclo.
tools/data/reports/agent_20260509_0330.md:8:**observable_contract**: claim=la forma locale della curva `r(V)` puo' sostituire il primo crossing su griglia come portatore del boundary Sturmian-Harper; observable=`vc_interp`, `slope_at_cross`, `crossing_count`, `r_span`; operator=curva `r(V)` su griglia 0.5..3.0 step 0.01 con crossing lineare interpolato per `r_threshold={0.48,0.50,0.52}`; generator=Sturmian metallici phi/silver/bronze e random bilanciato a densita phi; denominator=N={89,144,233,377,610}, phase={0,0.25,0.5,0.75}, 3 random trial per condizione, seed=202605090330; not_tested=gap_ratio, label-set Sturmian, fit power-law asintotico, domini reali GUE/Poisson, prova formale di monotonia.
tools/data/reports/agent_20260509_0330.md:8:**observable_contract**: claim=la forma locale della curva `r(V)` puo' sostituire il primo crossing su griglia come portatore del boundary Sturmian-Harper; observable=`vc_interp`, `slope_at_cross`, `crossing_count`, `r_span`; operator=curva `r(V)` su griglia 0.5..3.0 step 0.01 con crossing lineare interpolato per `r_threshold={0.48,0.50,0.52}`; generator=Sturmian metallici phi/silver/bronze e random bilanciato a densita phi; denominator=N={89,144,233,377,610}, phase={0,0.25,0.5,0.75}, 3 random trial per condizione, seed=202605090330; not_tested=gap_ratio, label-set Sturmian, fit power-law asintotico, domini reali GUE/Poisson, prova formale di monotonia.
tools/data/reports/agent_20260509_1437.md:8:**observable_contract**: claim=il residual blank test decide se i 3 blank residui BOUNDARY entrano nel transfer; observable=`spacing_r` originale contro permutation null row-aligned; operator=`exp_boundary_blank_null_audit.py` + `exp_boundary_denominator_prescan.py`; generator=`dnd_autoricerca.genera_segnale` per `string_vibration`, `reaction_diffusion`, `logistica_biforcazione_var_3.5699`; denominator=13 righe base autoricerca 8 GUE-like / 5 Poisson-like; non_possible=dichiarare chiusura QxG, nuova legge GUE/Poisson, o complete `reaction_diffusion` con 499 gap; not_tested=fit `V_c`, nuovi spettri, nuovi domini.
tools/data/reports/agent_20260509_1437.md:12:- **Combo**: A2 confine det=-1 + A9 terzo incluso + A11 combo + QxG continuo/discreto + nodo BOUNDARY 8 GUE / 5 Poisson + tensione `TRASCENDENZA_LIMITE`.
tools/data/reports/agent_20260509_1437.md:14:- **Piano superiore**: grafo della conoscenza e boundary operator. Il gate decide copertura del denominatore, non ontologia GUE/Poisson.
tools/data/reports/agent_20260509_1437.md:30:- Perimetro atomico: 13 righe base, 8 GUE-like e 5 Poisson-like.
tools/data/reports/agent_20260509_1437.md:40:| string_vibration | 7999 | 0.383868 | 0.372199 | 4.562844 | Poisson -> Poisson | true | transfer complete |
tools/data/reports/agent_20260509_1437.md:41:| reaction_diffusion | 499 | 0.762604 | 0.494932 | 31.390192 | GUE -> GUE | true | transfer contaminato |
tools/data/reports/agent_20260509_1437.md:42:| logistica_biforcazione_var_3.5699 | 4727 | 0.581221 | 0.099640 | 161.271569 | GUE -> Poisson | true | transfer complete, class_change edge case |
tools/data/reports/agent_20260509_1437.md:49:| source GUE | 8 |
tools/data/reports/agent_20260509_1437.md:50:| source Poisson | 5 |
tools/data/reports/agent_20260509_1437.md:61:| string_vibration:cycle_6 | Poisson | complete | 0.000 | shuffle z=4.56; class_change=False | transfers |
tools/data/reports/agent_20260509_1437.md:62:| reaction_diffusion:cycle_11 | GUE | contaminated | 0.002 | shuffle z=31.39; class_change=False | transfers |
tools/data/reports/agent_20260509_1437.md:63:| logistica_biforcazione_var_3.5699:cycle_13 | GUE | complete | 0.000 | shuffle z=161.27; class_change=True | transfers |
tools/data/reports/agent_20260509_1437.md:68:3. **Verificato: `string_vibration` trasferisce senza cambio classe.** Poisson -> Poisson, `ordering_dependent=true`, `n_gaps=7999`.
tools/data/reports/agent_20260509_1437.md:69:4. **Verificato: `logistica_biforcazione_var_3.5699` trasferisce con `class_change=true`.** Il cambio GUE -> Poisson e' edge case del null, non legge nuova legge.
tools/data/reports/agent_20260509_1437.md:82:- **Invariante di passaggio**: disponibilita del null leggibile; non l'etichetta GUE/Poisson e non il fit `V_c`.
tools/data/reports/agent_20260509_1437.md:94:- **L5 re-discovery**: il ciclo e' audit di denominatore residuo, non teorema GUE/Poisson.
tools/data/reports/agent_20260509_1437.md:104:- Compare residual audit: `string_vibration` e `logistica_biforcazione_var_3.5699` combaciano sui numeri depositati; `reaction_diffusion` nel rerun produce `r=0.755661`, `z=30.592858` invece di `r=0.762604`, `z=31.390192`, ma conserva `n_gaps=499`, `GUE -> GUE`, `ordering_dependent=true`, `transfer=contaminated`. Il drift e' attribuito al generatore dinamico, non al null seed.
tools/data/reports/agent_20260504_0901.md:21:- **Sequences**: Prime gaps (N=50000), GUE eigenvalue gaps (200x200 matrices, 250 realizations), Poisson iid exponential gaps (N=50000).
tools/data/reports/agent_20260504_0901.md:22:- **Poisson control**: If Poisson shows layer separation, the metric is noise-sensitive. If only structured sequences show coupling, the coupling is real.
tools/data/reports/agent_20260504_0901.md:31:| GUE      | 0.334   | 0.287   | 0.334    | 0.334           | 0.311   | 0.334   | +0.024 |
tools/data/reports/agent_20260504_0901.md:32:| Poisson  | 0.239   | 0.334   | 0.097    | 0.097           | 0.287   | 0.097   | -0.189 |
tools/data/reports/agent_20260504_0901.md:39:| GUE      | 0.444  | 0.445  | -0.001     |
tools/data/reports/agent_20260504_0901.md:40:| Poisson  | 0.330  | 0.591  | -0.261     |
tools/data/reports/agent_20260504_0901.md:42:### Poisson null verification
tools/data/reports/agent_20260504_0901.md:44:All Poisson original-vs-baseline z-scores are < 2 (SR: z=0.91, L1: z=-1.47, SR2: z=-0.17, triple_var: z=-0.56). The Poisson "signal" is noise. The apparent layer separation (Delta = -0.189) in Poisson is an artifact: when the signal-to-noise is < 2, the retention metric amplifies noise differently for each observable.
tools/data/reports/agent_20260504_0901.md:51:| GUE      | 0.895  | None   | 0.902  | None       |
tools/data/reports/agent_20260504_0901.md:57:1. **The two Markov layers are coupled at the boundary.** For primes, the critical alpha is identical across all 4 observables (0.334). For GUE, the difference is 0.024 (within the alpha step resolution of 0.047). The partial shuffle destroys pair-statistics and triple-statistics at the same rate. The boundary is a single phase transition, not two independent ones.
tools/data/reports/agent_20260504_0901.md:59:2. **The coupling is specific to structured sequences.** Poisson (iid, no ordering) shows Delta = -0.189 — spurious separation from noise amplification. Primes (Delta = 0.000) and GUE (Delta = 0.024) show coupling. This rules out the coupling being a trivial property of the metric.
tools/data/reports/agent_20260504_0901.md:70:- **L4 (edge case)**: Poisson separation is explicitly identified as noise artifact with z-score evidence.
tools/data/reports/agent_20260504_0901.md:71:- **L5 (re-discovery)**: That partial shuffle destroys correlations uniformly regardless of order is consistent with the known property that random permutations break all multi-point correlations simultaneously (not order-by-order). The specific quantification on prime gaps and GUE with the Markov-layer framework is new to this lab, but the underlying principle is not novel. Tagged as CONSTRAINT, not NEW.
tools/data/reports/agent_20260504_0901.md:83:- **Invariante di passaggio**: The critical alpha (0.334) survives across layers and across structured sequences (primes and GUE). The boundary location is invariant to which layer you observe through.
tools/data/reports/agent_20260509_1548.md:24:- `not_drift`: non usa label GUE/Poisson, non torna a `V_c`, non aggiunge nuovi domini; attacca solo il nodo aperto dal report precedente: blank sottile contro blank medio.
tools/data/reports/agent_20260509_1548.md:39:- Label policy: non legge `source_domain_type` o label GUE/Poisson come decision field.
tools/data/reports/agent_20260508_2140.md:8:**observable_contract**: claim=il fallimento del fit power-law su `V_c(phi)` segnala un bordo reticolare/quantizzato del passaggio Sturmian-Harper; observable=`V_c`, `distinct_vc`, `repeat_rate`, `mode_rate`; operator=prima soglia `V` su griglia 0.5..3.0 step 0.025 dove `<r>(H(seq,V)) < 0.5`; generator=Sturmian metallici phi/silver/bronze e random bilanciato a densita phi; denominator=N={89,144,233,377,610}, phase={0,0.25,0.5,0.75}, 4 random trial per condizione, seed=202605082140; not_tested=gap_ratio, label-set Sturmian, GUE/Poisson universale, fit power-law asintotico oltre N=610.
tools/observables_registry.py:13:GUE") e nel farlo ha trovato **collision di nomi observable** tra script:
tools/observables_registry.py:85:    Range: (0, 1]. GUE → ~0.60. Poisson → ~0.39. Picket-fence → 1.
tools/observables_registry.py:263:    print(f"\nGUE-like 200 gaps:")
tools/data/reports/agent_20260504_0901.md:21:- **Sequences**: Prime gaps (N=50000), GUE eigenvalue gaps (200x200 matrices, 250 realizations), Poisson iid exponential gaps (N=50000).
tools/data/reports/agent_20260504_0901.md:22:- **Poisson control**: If Poisson shows layer separation, the metric is noise-sensitive. If only structured sequences show coupling, the coupling is real.
tools/data/reports/agent_20260504_0901.md:31:| GUE      | 0.334   | 0.287   | 0.334    | 0.334           | 0.311   | 0.334   | +0.024 |
tools/data/reports/agent_20260504_0901.md:32:| Poisson  | 0.239   | 0.334   | 0.097    | 0.097           | 0.287   | 0.097   | -0.189 |
tools/data/reports/agent_20260504_0901.md:39:| GUE      | 0.444  | 0.445  | -0.001     |
tools/data/reports/agent_20260504_0901.md:40:| Poisson  | 0.330  | 0.591  | -0.261     |
tools/data/reports/agent_20260504_0901.md:42:### Poisson null verification
tools/data/reports/agent_20260504_0901.md:44:All Poisson original-vs-baseline z-scores are < 2 (SR: z=0.91, L1: z=-1.47, SR2: z=-0.17, triple_var: z=-0.56). The Poisson "signal" is noise. The apparent layer separation (Delta = -0.189) in Poisson is an artifact: when the signal-to-noise is < 2, the retention metric amplifies noise differently for each observable.
tools/data/reports/agent_20260504_0901.md:51:| GUE      | 0.895  | None   | 0.902  | None       |
tools/data/reports/agent_20260504_0901.md:57:1. **The two Markov layers are coupled at the boundary.** For primes, the critical alpha is identical across all 4 observables (0.334). For GUE, the difference is 0.024 (within the alpha step resolution of 0.047). The partial shuffle destroys pair-statistics and triple-statistics at the same rate. The boundary is a single phase transition, not two independent ones.
tools/data/reports/agent_20260504_0901.md:59:2. **The coupling is specific to structured sequences.** Poisson (iid, no ordering) shows Delta = -0.189 — spurious separation from noise amplification. Primes (Delta = 0.000) and GUE (Delta = 0.024) show coupling. This rules out the coupling being a trivial property of the metric.
tools/data/reports/agent_20260504_0901.md:70:- **L4 (edge case)**: Poisson separation is explicitly identified as noise artifact with z-score evidence.
tools/data/reports/agent_20260504_0901.md:71:- **L5 (re-discovery)**: That partial shuffle destroys correlations uniformly regardless of order is consistent with the known property that random permutations break all multi-point correlations simultaneously (not order-by-order). The specific quantification on prime gaps and GUE with the Markov-layer framework is new to this lab, but the underlying principle is not novel. Tagged as CONSTRAINT, not NEW.
tools/data/reports/agent_20260504_0901.md:83:- **Invariante di passaggio**: The critical alpha (0.334) survives across layers and across structured sequences (primes and GUE). The boundary location is invariant to which layer you observe through.
tools/data/reports/agent_20260509_1548.md:24:- `not_drift`: non usa label GUE/Poisson, non torna a `V_c`, non aggiunge nuovi domini; attacca solo il nodo aperto dal report precedente: blank sottile contro blank medio.
tools/data/reports/agent_20260509_1548.md:39:- Label policy: non legge `source_domain_type` o label GUE/Poisson come decision field.
tools/data/reports/agent_20260508_2140.md:8:**observable_contract**: claim=il fallimento del fit power-law su `V_c(phi)` segnala un bordo reticolare/quantizzato del passaggio Sturmian-Harper; observable=`V_c`, `distinct_vc`, `repeat_rate`, `mode_rate`; operator=prima soglia `V` su griglia 0.5..3.0 step 0.025 dove `<r>(H(seq,V)) < 0.5`; generator=Sturmian metallici phi/silver/bronze e random bilanciato a densita phi; denominator=N={89,144,233,377,610}, phase={0,0.25,0.5,0.75}, 4 random trial per condizione, seed=202605082140; not_tested=gap_ratio, label-set Sturmian, GUE/Poisson universale, fit power-law asintotico oltre N=610.
tools/data/reports/agent_20260508_1947.md:8:**observable_contract**: claim=se il boundary simbolico del supertile esiste nell'osservabile, aligned supertile deve battere il misaligned same-length non solo nel label-set ma nella geometria IDS/rank/errore dei label core; observable=all-core hits, delta IDS, delta indice spettrale normalizzato, errore label e spacing ratio dei core label rispetto al reference phi; operator=Hamiltoniana tight-binding V=1, label IDS con reader theta=1/phi, confronto per label contro reference stesso N/phase/threshold; generator=phi_sturmian perturbato da supertile_shuffle, same_length_contiguous_shuffle, same_count_internal_shuffle; denominator=N={377,610}, phase={0,0.25,0.5,0.75}, threshold={2.0}, trials=5, top_k=12, |n|<=34, supertile_order={8,9,10,11}; not_tested=gap_ratio, GUE/Poisson real domains, soglie 1.75/2.25, parsing simbolico esatto di ogni supertile.
tools/data/reports/report_20260405_0330.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260405_0330.md:22:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=1.0000 (GUE standard=0.60)
tools/data/reports/report_20260405_0330.md:25:- percolation_var_0.55: r=0.7543859649122806, spacing=Poisson-like
tools/data/reports/report_20260405_0330.md:26:- numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like
tools/data/reports/report_20260405_0330.md:27:- coupled_oscillators_var_50: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260405_0330.md:28:- brownian_motion_var_0.5: r=1.0, spacing=Poisson-like
tools/data/reports/agent_20260407_0637.md:9:> "Three independent observables (beta, <r>, acf1) predict Poisson at p*~10^{14}. Hierarchy: shape decorrelates first, ratio second, sequential memory last." — Is this hierarchy prime-specific or generic?
tools/data/reports/agent_20260407_0637.md:18:- **Synthetic**: AR(1) Gaussian copula with empirical prime gap marginal, rho from -0.044 (empirical) to 0 (Poisson), 12 steps, 10 trials each, N=100K
tools/data/reports/agent_20260407_0637.md:20:- **Null baseline**: rho=0 synthetic (same marginal, no correlation = Poisson correlations)
tools/data/reports/agent_20260407_0637.md:34:All three decrease toward Poisson with scale. Linear extrapolation to Poisson:
tools/data/reports/agent_20260407_0637.md:62:1. **The "hierarchy" is not a hierarchy — it's two independent mechanisms.** Beta and <r> change with scale because the prime gap DISTRIBUTION changes (gaps widen relative to their mean as primes thin out). Acf1 changes because sequential CORRELATIONS decay. These are independent processes that happen to approach the same limit (Poisson). The AR(1) synthetic proves it: varying correlation leaves beta and <r> unchanged.
tools/data/reports/agent_20260407_0637.md:72:**CONSTRAINT + NEW** — The "hierarchy" claim is falsified as stated: it's not three observables decorrelating in order, it's two mechanisms (distribution shape + sequential correlation) that happen to approach Poisson at comparable scales. NEW: the acf(2) residual (-0.017) reveals multi-lag prime structure beyond AR(1), not previously measured. This is the next target.
tools/data/reports/agent_20260408_0330.md:95:2. **Scale dependence of A.** If A(p) decreases with p (as acf(1) does), then A(p) ~ A0 / ln(p)^beta for some beta. This gives the Poisson horizon: A(p*) ~ 1/sqrt(N_window), i.e., when the correlation drops below statistical detectability.
tools/data/reports/agent_20260406_1030.md:7:> The boundary between GUE and Poisson is "the third included" (A9). Is this boundary populated by multiple domains, or are primes special?
tools/data/reports/agent_20260406_1030.md:13:- 17 domains (GUE, GOE, GSE, Poisson, power-law, picket fence, clock jitter, primes, semi-Poisson, Berry-Robnik x3, Anderson 1D, Harper phi/rational, quadratic residues)
tools/data/reports/agent_20260406_1030.md:26:| Poisson | 0.390 | +0.004 | +0.3 | POISSON |
tools/data/reports/agent_20260406_1030.md:30:| semi-Poisson | 0.507 | +0.010 | +0.8 | GOE-zone |
tools/data/reports/agent_20260406_1030.md:33:| zeta zeros | 0.596 | -0.319 | -18.3 | GUE |
tools/data/reports/agent_20260406_1030.md:34:| GUE | 0.602 | -0.278 | -20.8 | GUE |
tools/data/reports/agent_20260406_1030.md:35:| quad. residues | 0.613 | -0.046 | -4.0 | GUE |
tools/data/reports/agent_20260406_1030.md:59:Both drift toward Poisson origin (0.386, 0).
tools/data/reports/agent_20260406_1030.md:62:- <r>_distribution (shuffled) drifts toward Poisson from gap distribution (PNT)
tools/data/reports/agent_20260406_1030.md:69:2. **The spectral landscape is 2D, not 1D.** <r> alone classifies 4 zones (Poisson/boundary/GOE-GUE/rigid). Adding acf1 splits the boundary zone: mixtures (acf1~0) vs intrinsically ordered (acf1<<0). Primes are the ONLY tested domain at intermediate position on BOTH axes.
tools/data/reports/agent_20260406_1030.md:71:3. **The prime trajectory converges to Poisson from a unique direction.** In (<r>, acf1) space, primes move from (0.465, -0.071) toward (0.445, -0.051). Both coordinates drift toward the Poisson origin. The ordering effect (delta_ordering) shrinks: from -0.018 at small scale to -0.013 at large scale.
tools/data/reports/agent_20260406_1030.md:75:5. **Quadratic residues are GUE-like.** <r>=0.613, confirming Katz-Sarnak for quadratic L-functions. But their acf1=-0.046 is MUCH weaker than GUE (-0.28), suggesting incomplete level repulsion — another boundary domain on a different axis.
tools/data/reports/agent_20260406_1030.md:82:Constraint: **<r> alone is insufficient** to characterize spectral statistics. Any study claiming "primes are GUE-like" or "primes are between GOE and Poisson" based solely on <r> is missing the ordering dimension.
tools/data/reports/agent_20260406_1030.md:85:1. The 2D plane (<r>, acf1) can be parameterized. What is the natural coordinate system? Is there a one-parameter family connecting Poisson to GUE that passes through the prime point?
tools/data/reports/agent_20260406_1030.md:86:2. Quadratic residues are GUE in <r> but weakly ordered (acf1=-0.05). Are there other "weak-GUE" domains?
tools/data/reports/agent_20260406_1030.md:87:3. The delta_ordering shrinks with scale. At what scale does it vanish? This gives a SECOND Poisson horizon (the ordering horizon) independent of the <r> horizon.
tools/data/reports/report_20260305_0342.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260305_0342.md:23:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=0.8650 (GUE standard=0.60)
tools/data/reports/report_20260305_0342.md:26:- brownian_motion_var_0.5: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260305_0342.md:27:- coupled_oscillators_var_50: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260305_0342.md:28:- percolation_var_0.55: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260305_0342.md:29:- percolation_var_0.65: r=1.127659574468085, spacing=Poisson-like
tools/data/reports/agent_20260505_0330.md:14:- **Domini**: prime gaps, GUE gaps, Poisson iid exponential gaps.
tools/data/reports/agent_20260505_0330.md:16:- **Null baseline**: full shuffle della stessa sequenza. Il dato Poisson e' controllo di assenza di segnale originale-vs-shuffle.
tools/data/reports/agent_20260505_0330.md:26:| GUE | 0.997 | 1.022 | 0.996 | -6.6 | +21.4 | +37.9 | +18.0 | +36.3 |
tools/data/reports/agent_20260505_0330.md:27:| Poisson | 0.925 | 1.391 | 0.351 | -0.6 | +0.2 | +1.2 | -0.0 | +0.8 |
tools/data/reports/agent_20260505_0330.md:34:| GUE | 0.997 | 1.019 | 0.997 |
tools/data/reports/agent_20260505_0330.md:35:| Poisson | 0.675 | 2.394 | 0.640 |
tools/data/reports/agent_20260505_0330.md:42:| GUE | -0.446 | -0.449 | -0.447 | -0.446 | -0.448 |
tools/data/reports/agent_20260505_0330.md:45:1. **Nel perimetro partial-shuffle, primes e GUE hanno segnale forte ma quasi monodimensionale.** Per primes, tutte le osservabili hanno z originale-vs-shuffle almeno |3.0| e la prima componente spiega 98.9% della varianza delle retention curve. Per GUE il collasso e' ancora piu' stretto: 99.7%.
tools/data/reports/agent_20260505_0330.md:49:3. **Poisson non supporta un claim di rango.** Nel seed principale Poisson ha PC1 alto, ma tutti gli z originale-vs-shuffle sono sotto |1.2|; nella replica il rango cambia molto. Quindi il rango Poisson qui e' rumore di baseline, non struttura.
tools/data/reports/agent_20260505_0330.md:64:- **L2 quantita vs ratio**: il risultato usa energie PCA e z-score original-vs-shuffle nello stesso spazio di osservabili; Poisson e' escluso dall'interpretazione strutturale per assenza di segnale, non per percentuale.
tools/build_lab_graph.py:199:                'text': f"Metrica primi g=(p/2)², curvatura GUE r={mp.get('risultato_r',{}).get('curvatura_r','?')}, z={mp.get('test_null',{}).get('z_score','?')}",
tools/spectral_gap_analysis.py:201:    # For GUE (expected for zeta zeros): P(s) ≈ (32/π²)s² exp(-4s²/π)
tools/spectral_gap_analysis.py:219:    """GUE Wigner surmise: P(s) = (32/π²)s² exp(-4s²/π)."""
tools/spectral_gap_analysis.py:259:                label='GUE Wigner')
tools/spectral_gap_analysis.py:269:    fig.suptitle('Nearest-Neighbor Spacing: Spectral Gaps vs Zeta Gaps vs GUE',
tools/exp_psd_amplitude_scaling.py:146:    # Poisson crossover prediction from dip_ratio → 1.0 (no suppression)
tools/exp_psd_amplitude_scaling.py:150:        print(f"\nPoisson crossover (dip→1.0): ln(p*)={ln_p_cross_dip:.1f}  "
tools/exp_psd_amplitude_scaling.py:154:        print(f"\nDip ratio DECREASING with scale — no Poisson crossover from PSD dip")
tools/exp_psd_amplitude_scaling.py:156:    # Poisson crossover from spectral_slope → 0
tools/exp_psd_amplitude_scaling.py:159:        print(f"Poisson crossover (slope→0): ln(p*)={ln_p_cross_slope:.1f}  "
tools/r_stat_primes.py:27:    "verdict": "GUE-like" if r_real > (r_poisson + r_gue) / 2 else "Poisson-like"
tools/exp_prime_persistent_blank_gate.py:176:            "generator": "prime gaps from dnd_autoricerca row_spacings and direct sieve; controls from GUE random matrix blocks and logistic return intervals",
tools/exp_prime_persistent_blank_gate.py:179:            "not_tested": "global beta atlas, V_c, gap_ratio, source GUE/Poisson labels",
tools/exp_two_channel_cross_domain.py:9:  2. Statistical (magnitude): decays slowly toward Poisson
tools/exp_two_channel_cross_domain.py:17:  - GUE eigenvalues: random matrix, strong statistical correlation, no arithmetic
tools/exp_two_channel_cross_domain.py:27:If GUE shows only decaying channels → C1 supported (algebraic invariance is prime-specific)
tools/exp_two_channel_cross_domain.py:28:If GUE shows a scale-invariant channel → C1 needs refinement
tools/exp_two_channel_cross_domain.py:63:    """Generate unfolded spacings from GUE random matrices."""
tools/exp_two_channel_cross_domain.py:66:        # GUE: H = (A + A^*) / (2 * sqrt(2N)), A is complex Gaussian
tools/exp_two_channel_cross_domain.py:295:    # === GUE EIGENVALUES ===
tools/exp_two_channel_cross_domain.py:296:    print(f"\n=== GUE EIGENVALUES ({gue_matrices} matrices of size {gue_size}) ===")
tools/exp_two_channel_cross_domain.py:298:    print(f"Got {len(gue_spacings)} GUE spacings, mean={np.mean(gue_spacings):.3f}")
tools/exp_two_channel_cross_domain.py:412:    print("         GUE/Cramer should show decaying or absent binary channel")
tools/dnd_gue_test.py:3:Piano 11b — GUE Test Protocol (Revised)
tools/dnd_gue_test.py:6:zeros follow GUE statistics (Katz-Sarnak). This is the algebraic bridge,
tools/dnd_gue_test.py:14:  Prediction: zeros of L(s, χ_disc) → GUE (Katz-Sarnak)
tools/dnd_gue_test.py:17:T1: L(s, χ₅) zeros → GUE  (the D-ND matrix M, tr=1)
tools/dnd_gue_test.py:18:T2: L(s, χ₈) zeros → GUE  (tr=2, disc=8, Q(√2))
tools/dnd_gue_test.py:19:T3: L(s, χ₁₃) zeros → GUE (tr=3, disc=13, Q(√13))
tools/dnd_gue_test.py:20:T4: ζ(s) zeros → GUE       (reference: Montgomery-Odlyzko)
tools/dnd_gue_test.py:40:    """GUE (β=2) Wigner surmise CDF."""
tools/dnd_gue_test.py:48:    """Poisson CDF."""
tools/dnd_gue_test.py:51:GUE_VAR_REF = 1 - 4/np.pi**2  # ≈ 0.5947 (Wigner surmise β=2)
tools/dnd_gue_test.py:53:# Better: Var(s) for GUE Wigner surmise = (3π-8)/(2π) ≈ 0.178
tools/dnd_gue_test.py:55:GUE_VAR = 0.178  # Wigner surmise β=2
tools/dnd_gue_test.py:362:    """Test spacings against GUE, GOE, Poisson."""
tools/dnd_gue_test.py:374:    fits = [("GUE", ks_gue, p_gue), ("GOE", ks_goe, p_goe), ("Poisson", ks_poi, p_poi)]
tools/dnd_gue_test.py:395:    print("Piano 11b — GUE Test Protocol (Revised)")
tools/dnd_gue_test.py:414:    # ─── T1: ζ(s) zeros → GUE (reference, Montgomery-Odlyzko) ────────
tools/dnd_gue_test.py:555:        marker = "✓" if fit == "GUE" else "✗"
tools/dnd_gue_test.py:556:        if fit == "GUE":
tools/dnd_gue_test.py:568:        verdict = (f"GUE UNIVERSALE: {gue_count}/{total} L-functions mostrano GUE. "
tools/dnd_gue_test.py:569:                   f"La catena det=-1 → campo numerico → L-function → GUE è confermata.")
tools/dnd_gue_test.py:572:        verdict = (f"GUE DOMINANTE: {gue_count}/{total}. Risultato parziale, "
tools/dnd_gue_test.py:578:        verdict = f"NON-GUE: classe dominante {dominant}. Revisione necessaria."
tools/dnd_gue_test.py:587:    print("  Near s=0: p(s) ~ s^β. β=0:Poisson, β=1:GOE, β=2:GUE")
tools/dnd_gue_test.py:622:                cls = "GUE" if beta > 1.5 else ("GOE" if beta > 0.5 else "Poisson")
tools/dnd_gue_test.py:630:    # Count GUE in beta analysis
tools/dnd_gue_test.py:631:    beta_gue = sum(1 for lb in beta_results.values() for v in lb.values() if v.get("class") == "GUE")
tools/dnd_gue_test.py:633:    print(f"  DIPOLO FRATTALE: {beta_gue}/{beta_total} GUE a livello di repulsione")
tools/dnd_gue_test.py:634:    print(f"  Il KS globale dice GOE. Il β allo zero dice GUE.")
tools/dnd_gue_test.py:703:            family_verdict = f"MISTO: neg1 GUE={all_neg1_gue}, pos1 GUE={all_pos1_gue}"
tools/dnd_gue_test.py:716:    print(f"  Piano 11:  det=-1 → fluctuations (GUE via Katz-Sarnak)")
tools/dnd_gue_test.py:728:        verdict_fractal = (f"GUE CONFERMATO via dipolo frattale: β>2 per {beta_gue}/{beta_total} misure. "
tools/dnd_gue_test.py:729:                          f"KS globale={gue_count}/{total} GUE (forma bulk). "
tools/dnd_gue_test.py:730:                          f"β allo zero={beta_gue}/{beta_total} GUE (repulsione). "
tools/dnd_gue_test.py:734:        verdict_fractal = (f"Dipolo frattale: {beta_gue}/{beta_total} GUE. "
tools/dnd_gue_test.py:805:            "fluctuations": "det=-1 → Q(√5) → L(s,χ₅) → GUE (Piano 11)",
tools/dnd_gue_test.py:806:            "family": "ALL det=-1 matrices → number fields → GUE (Piano 11b)",
tools/exp_boundary_short_denominator_extension.py:203:            "not_tested": "global 13-row boundary redesign, V_c fit, source GUE/Poisson label validity",
tools/exp_markov_dipolar_decomposition.py:6:GUE has dL1/dSR = 8.37 at angle -97 deg.
tools/dnd_compatibility.py:134:                        "<r>=0.507, tra GUE (0.5996) e Poisson (0.3863).",
tools/dnd_compatibility.py:147:# --- Random Matrix Theory (GUE) ---
tools/dnd_compatibility.py:150:    "GUE (Gaussian Unitary Ensemble): matrici hermitiane casuali, "
tools/dnd_compatibility.py:167:            "evidence": "Le matrici GUE hanno det qualsiasi. "
tools/dnd_compatibility.py:173:            "evidence": "phi non emerge naturalmente dal GUE. "
tools/dnd_compatibility.py:180:            "evidence": "GUE vive in C (numeri complessi generici), non in Q(sqrt(5)).",
tools/dnd_compatibility.py:185:            "evidence": "GUE e' una classe definita, non una fase critica. "
tools/data/reports/agent_20260508_1947.md:8:**observable_contract**: claim=se il boundary simbolico del supertile esiste nell'osservabile, aligned supertile deve battere il misaligned same-length non solo nel label-set ma nella geometria IDS/rank/errore dei label core; observable=all-core hits, delta IDS, delta indice spettrale normalizzato, errore label e spacing ratio dei core label rispetto al reference phi; operator=Hamiltoniana tight-binding V=1, label IDS con reader theta=1/phi, confronto per label contro reference stesso N/phase/threshold; generator=phi_sturmian perturbato da supertile_shuffle, same_length_contiguous_shuffle, same_count_internal_shuffle; denominator=N={377,610}, phase={0,0.25,0.5,0.75}, threshold={2.0}, trials=5, top_k=12, |n|<=34, supertile_order={8,9,10,11}; not_tested=gap_ratio, GUE/Poisson real domains, soglie 1.75/2.25, parsing simbolico esatto di ogni supertile.
tools/data/reports/report_20260405_0330.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260405_0330.md:22:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=1.0000 (GUE standard=0.60)
tools/data/reports/report_20260405_0330.md:25:- percolation_var_0.55: r=0.7543859649122806, spacing=Poisson-like
tools/data/reports/report_20260405_0330.md:26:- numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like
tools/data/reports/report_20260405_0330.md:27:- coupled_oscillators_var_50: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260405_0330.md:28:- brownian_motion_var_0.5: r=1.0, spacing=Poisson-like
tools/data/reports/agent_20260407_0637.md:9:> "Three independent observables (beta, <r>, acf1) predict Poisson at p*~10^{14}. Hierarchy: shape decorrelates first, ratio second, sequential memory last." — Is this hierarchy prime-specific or generic?
tools/data/reports/agent_20260407_0637.md:18:- **Synthetic**: AR(1) Gaussian copula with empirical prime gap marginal, rho from -0.044 (empirical) to 0 (Poisson), 12 steps, 10 trials each, N=100K
tools/data/reports/agent_20260407_0637.md:20:- **Null baseline**: rho=0 synthetic (same marginal, no correlation = Poisson correlations)
tools/data/reports/agent_20260407_0637.md:34:All three decrease toward Poisson with scale. Linear extrapolation to Poisson:
tools/data/reports/agent_20260407_0637.md:62:1. **The "hierarchy" is not a hierarchy — it's two independent mechanisms.** Beta and <r> change with scale because the prime gap DISTRIBUTION changes (gaps widen relative to their mean as primes thin out). Acf1 changes because sequential CORRELATIONS decay. These are independent processes that happen to approach the same limit (Poisson). The AR(1) synthetic proves it: varying correlation leaves beta and <r> unchanged.
tools/data/reports/agent_20260407_0637.md:72:**CONSTRAINT + NEW** — The "hierarchy" claim is falsified as stated: it's not three observables decorrelating in order, it's two mechanisms (distribution shape + sequential correlation) that happen to approach Poisson at comparable scales. NEW: the acf(2) residual (-0.017) reveals multi-lag prime structure beyond AR(1), not previously measured. This is the next target.
tools/data/reports/agent_20260408_0330.md:95:2. **Scale dependence of A.** If A(p) decreases with p (as acf(1) does), then A(p) ~ A0 / ln(p)^beta for some beta. This gives the Poisson horizon: A(p*) ~ 1/sqrt(N_window), i.e., when the correlation drops below statistical detectability.
tools/data/reports/agent_20260406_1030.md:7:> The boundary between GUE and Poisson is "the third included" (A9). Is this boundary populated by multiple domains, or are primes special?
tools/data/reports/agent_20260406_1030.md:13:- 17 domains (GUE, GOE, GSE, Poisson, power-law, picket fence, clock jitter, primes, semi-Poisson, Berry-Robnik x3, Anderson 1D, Harper phi/rational, quadratic residues)
tools/data/reports/agent_20260406_1030.md:26:| Poisson | 0.390 | +0.004 | +0.3 | POISSON |
tools/data/reports/agent_20260406_1030.md:30:| semi-Poisson | 0.507 | +0.010 | +0.8 | GOE-zone |
tools/data/reports/agent_20260406_1030.md:33:| zeta zeros | 0.596 | -0.319 | -18.3 | GUE |
tools/data/reports/agent_20260406_1030.md:34:| GUE | 0.602 | -0.278 | -20.8 | GUE |
tools/data/reports/agent_20260406_1030.md:35:| quad. residues | 0.613 | -0.046 | -4.0 | GUE |
tools/data/reports/agent_20260406_1030.md:59:Both drift toward Poisson origin (0.386, 0).
tools/data/reports/agent_20260406_1030.md:62:- <r>_distribution (shuffled) drifts toward Poisson from gap distribution (PNT)
tools/data/reports/agent_20260406_1030.md:69:2. **The spectral landscape is 2D, not 1D.** <r> alone classifies 4 zones (Poisson/boundary/GOE-GUE/rigid). Adding acf1 splits the boundary zone: mixtures (acf1~0) vs intrinsically ordered (acf1<<0). Primes are the ONLY tested domain at intermediate position on BOTH axes.
tools/data/reports/agent_20260406_1030.md:71:3. **The prime trajectory converges to Poisson from a unique direction.** In (<r>, acf1) space, primes move from (0.465, -0.071) toward (0.445, -0.051). Both coordinates drift toward the Poisson origin. The ordering effect (delta_ordering) shrinks: from -0.018 at small scale to -0.013 at large scale.
tools/data/reports/agent_20260406_1030.md:75:5. **Quadratic residues are GUE-like.** <r>=0.613, confirming Katz-Sarnak for quadratic L-functions. But their acf1=-0.046 is MUCH weaker than GUE (-0.28), suggesting incomplete level repulsion — another boundary domain on a different axis.
tools/data/reports/agent_20260406_1030.md:82:Constraint: **<r> alone is insufficient** to characterize spectral statistics. Any study claiming "primes are GUE-like" or "primes are between GOE and Poisson" based solely on <r> is missing the ordering dimension.
tools/data/reports/agent_20260406_1030.md:85:1. The 2D plane (<r>, acf1) can be parameterized. What is the natural coordinate system? Is there a one-parameter family connecting Poisson to GUE that passes through the prime point?
tools/data/reports/agent_20260406_1030.md:86:2. Quadratic residues are GUE in <r> but weakly ordered (acf1=-0.05). Are there other "weak-GUE" domains?
tools/data/reports/agent_20260406_1030.md:87:3. The delta_ordering shrinks with scale. At what scale does it vanish? This gives a SECOND Poisson horizon (the ordering horizon) independent of the <r> horizon.
tools/data/reports/report_20260305_0342.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260305_0342.md:23:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=0.8650 (GUE standard=0.60)
tools/data/reports/report_20260305_0342.md:26:- brownian_motion_var_0.5: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260305_0342.md:27:- coupled_oscillators_var_50: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260305_0342.md:28:- percolation_var_0.55: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260305_0342.md:29:- percolation_var_0.65: r=1.127659574468085, spacing=Poisson-like
tools/data/reports/agent_20260505_0330.md:14:- **Domini**: prime gaps, GUE gaps, Poisson iid exponential gaps.
tools/data/reports/agent_20260505_0330.md:16:- **Null baseline**: full shuffle della stessa sequenza. Il dato Poisson e' controllo di assenza di segnale originale-vs-shuffle.
tools/data/reports/agent_20260505_0330.md:26:| GUE | 0.997 | 1.022 | 0.996 | -6.6 | +21.4 | +37.9 | +18.0 | +36.3 |
tools/data/reports/agent_20260505_0330.md:27:| Poisson | 0.925 | 1.391 | 0.351 | -0.6 | +0.2 | +1.2 | -0.0 | +0.8 |
tools/data/reports/agent_20260505_0330.md:34:| GUE | 0.997 | 1.019 | 0.997 |
tools/data/reports/agent_20260505_0330.md:35:| Poisson | 0.675 | 2.394 | 0.640 |
tools/data/reports/agent_20260505_0330.md:42:| GUE | -0.446 | -0.449 | -0.447 | -0.446 | -0.448 |
tools/data/reports/agent_20260505_0330.md:45:1. **Nel perimetro partial-shuffle, primes e GUE hanno segnale forte ma quasi monodimensionale.** Per primes, tutte le osservabili hanno z originale-vs-shuffle almeno |3.0| e la prima componente spiega 98.9% della varianza delle retention curve. Per GUE il collasso e' ancora piu' stretto: 99.7%.
tools/data/reports/agent_20260505_0330.md:49:3. **Poisson non supporta un claim di rango.** Nel seed principale Poisson ha PC1 alto, ma tutti gli z originale-vs-shuffle sono sotto |1.2|; nella replica il rango cambia molto. Quindi il rango Poisson qui e' rumore di baseline, non struttura.
tools/data/reports/agent_20260505_0330.md:64:- **L2 quantita vs ratio**: il risultato usa energie PCA e z-score original-vs-shuffle nello stesso spazio di osservabili; Poisson e' escluso dall'interpretazione strutturale per assenza di segnale, non per percentuale.
tools/data/reports/agent_20260515_1915.md:8:**observable_contract**: claim=una riga boundary a due lettori e' operativa solo se lo stato graph bridge sopravvive a perturbazioni del lettore e resta auditato dal baseline classico; observable=frequenza graph bridge unita a stato Brody/Berry-Robnik-like; operator=perturbazione parametrica del grafo kNN con join classico row-aligned; generator=boundary_graph_curvature_gate sul denominatore BOUNDARY 13 righe; denominator=13 righe, 8 GUE e 5 Poisson, ripetute su griglia di 27 run; non_possible=stable Lab bridge se la frequenza bridge collassa sotto perturbazione k/n_gaps/seed; not_tested=Hamiltoniane fisiche nuove, unfolding alternativo, scaling asintotico.
tools/data/reports/agent_20260515_1915.md:11:- **Combo**: A9 terzo incluso + QxG continuo/discreto + grafo/crossover spettrale + tensione BOUNDARY "8 domini GUE, 5 Poisson".
tools/data/reports/agent_20260515_1915.md:27:- `why`: il ciclo resta sul perimetro vivo 8 GUE / 5 Poisson e misura se il confine come terzo incluso resta operativo quando il lettore viene perturbato.
tools/data/reports/agent_20260515_1915.md:30:## Re-discovery audit
tools/data/reports/agent_20260515_1915.md:32:- **Cosa viene assorbito dal baseline**: `numeri_primi:cycle_3` resta stabile graph bridge 27/27 ed e' anche intermedio classico (`q=0.465`, `w_GUE=0.275`): qui il Lab non separa un fenomeno nuovo dal crossover classico.
tools/data/reports/agent_20260515_1915.md:33:- **Cosa resta Lab-specific**: `percolation:cycle_9`, `reaction_diffusion:cycle_11`, `logistica_biforcazione_var_3.5699:cycle_13` sono `stable_graph_bridge+graph_only_bridge`, tutte 27/27. Il baseline classico le legge endpoint-like, il grafo le legge confine stabile.
tools/data/reports/agent_20260515_1915.md:41:I nodi ponte GUE/Poisson sopravvivono a perturbazioni del lettore, oppure il boundary del 18:55 era una soglia locale?
tools/data/reports/agent_20260515_1915.md:44:- **Punto fisico sorgente**: transizione spettrale tra repulsione da caos quantistico e indipendenza/localizzazione Poisson.
tools/data/reports/agent_20260515_1915.md:54:- **Denominatore**: 13 righe row-aligned, 8 GUE e 5 Poisson.
tools/data/reports/agent_20260515_1915.md:71:| stable_graph_bridge+graph_only_bridge | 3 |
tools/data/reports/agent_20260515_1915.md:83:| percolation:cycle_9 | graph_only_bridge | 1.000 | stable_graph_bridge+graph_only_bridge |
tools/data/reports/agent_20260515_1915.md:84:| reaction_diffusion:cycle_11 | graph_only_bridge | 1.000 | stable_graph_bridge+graph_only_bridge |
tools/data/reports/agent_20260515_1915.md:85:| logistica_biforcazione_var_3.5699:cycle_13 | graph_only_bridge | 1.000 | stable_graph_bridge+graph_only_bridge |
tools/data/reports/agent_20260515_1915.md:94:1. Verificato: il denominatore resta quello richiesto, 13 righe con 8 GUE e 5 Poisson, ripetute in 27 letture.
tools/data/reports/agent_20260515_1915.md:95:2. Verificato: i tre `graph_only_bridge` del 19:04 restano stabili 27/27: `percolation`, `reaction_diffusion`, `logistica_biforcazione_var_3.5699`.
tools/data/reports/agent_20260515_1915.md:113:Il prossimo ciclo utile porta il gate composito su un sistema fisico controllato: Rosenzweig-Porter, Anderson/mobility edge o Aubry-Andre. Il target non e' aumentare metriche; e' chiedere se `stable_graph_bridge+graph_only_bridge` sopravvive fuori dal perimetro composito del Lab.
tools/data/reports/agent_20260515_1915.md:116:ssp_value: yes. Lo script crea un audit riusabile per stressare ogni gate GUE/Poisson row-aligned e separare ponte stabile, ponte parametrico, re-discovery classica ed endpoint-like.
tools/exp_two_channel_decomposition.py:18:  - Extrapolated Poisson crossover
tools/exp_two_channel_decomposition.py:344:            # Poisson crossover: acf1 = 0
tools/exp_two_channel_decomposition.py:353:                  f"R2={r2:.3f}  Poisson at ln(p)={ln_p_cross:.1f} (p~10^{ln_p_cross/np.log(10):.1f})")
tools/data/reports/agent_20260513_0330.md:33:- `not_drift`: non torna a GUE/Poisson, `V_c`, fit o controlli larghi; il solo antagonista decisivo e il pre-bordo `6k +/- 1`.
tools/data/reports/report_20260330_0344.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260330_0344.md:22:- [✗] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=0.6344 (GUE standard=0.60)
tools/data/reports/agent_20260405_0825.md:6:La transizione GUE -> Poisson nelle statistiche dei gap primi e' un fenomeno strutturale dei primi, o appare in qualsiasi sequenza con densita' decrescente?
tools/data/reports/agent_20260405_0825.md:10:- Riferimenti: `<r>_GUE = 0.5307`, `<r>_Poisson = 0.3863`
tools/data/reports/agent_20260405_0825.md:28:Le slopes sono quasi identiche. Sia i primi che i Cramer random transitano da valori piu' alti di `<r>` (piu' GUE-like) a valori piu' bassi (piu' Poisson-like) man mano che la densita' decresce. La classificazione "8 domini GUE, 5 Poisson" riflette principalmente questo effetto di densita'.
tools/data/reports/agent_20260405_0825.md:34:Tutti i valori `<r>` cadono tra 0.44-0.48, ben lontani sia dal GUE puro (0.53) che dal Poisson puro (0.39). I primi vivono in un regime intermedio che non collassa ne' sull'uno ne' sull'altro — un confine largo, non una transizione di fase.
tools/data/reports/agent_20260405_0825.md:37:Il "terzo incluso" non e' un punto di transizione tra GUE e Poisson. E' il regime intermedio stesso. I primi non sono ne' correlati (GUE) ne' indipendenti (Poisson) — sono in un terzo stato con correlazione in eccesso che cresce con la scala. Questo e' coerente con f(x) = 1 + 1/x: la regola genera correlazione oltre l'indipendenza, ma non la correlazione rigida di una matrice random.
tools/data/reports/agent_20260406_0714.md:7:> All four observables (Brody beta, dR_acf1, gap_acf1, <r>) drift toward Poisson with scale.
tools/data/reports/agent_20260406_0714.md:24:### Linear fits (all drift toward Poisson)
tools/data/reports/agent_20260406_0714.md:27:| beta | 0.670 - 0.025·ln(p) | 0.965 | toward 0 (Poisson) |
tools/data/reports/agent_20260406_0714.md:30:| <r> | 0.515 - 0.004·ln(p) | 0.918 | toward 0.386 (Poisson) |
tools/data/reports/agent_20260406_0714.md:50:### Extrapolation to Poisson (linear in ln p)
tools/data/reports/agent_20260406_0714.md:51:| Observable | Poisson at p ~ | ln(p) |
tools/data/reports/agent_20260406_0714.md:62:2. **Two genuine prime-specific drifts survive**: beta slope (z=-9.3) and gap_acf1 slope (z=3.9). Primes decorrelate toward Poisson faster than random (Cramer), and their gap anti-correlation is real.
tools/data/reports/agent_20260406_0714.md:64:3. **The crossover is NOT universal — it's multi-scale.** Gap correlations die first (~10^8), then repulsion strength (~10^11), then ratio statistics (~10^13). Three different Poisson horizons, not one.
tools/data/reports/agent_20260406_0714.md:76:The prime-to-Poisson crossover is a multi-scale cascade, not a single transition. This falsifies any model (including naive de Sitter) that predicts a single crossover scale. The three Poisson horizons (10^8, 10^11, 10^13) encode a hierarchy of decorrelation — local structure dies before global structure.
tools/data/reports/agent_20260406_0714.md:78:New tension: **MULTI_SCALE_CASCADE** — the crossover has at least 2 genuine channels with different Poisson horizons separated by 5 orders of magnitude.
tools/data/reports/report_20260315_0801.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260315_0801.md:23:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=0.8650 (GUE standard=0.60)
tools/data/reports/report_20260315_0801.md:26:- brownian_motion_var_0.5: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260315_0801.md:27:- coupled_oscillators_var_50: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260315_0801.md:28:- percolation_var_0.55: r=0.6340378197997775, spacing=Poisson-like
tools/data/reports/report_20260315_0801.md:29:- percolation_var_0.65: r=1.0, spacing=Poisson-like
tools/data/reports/agent_20260509_1427.md:8:**observable_contract**: claim=il blank-pair test decide se due righe BOUNDARY senza null entrano nel transfer; observable=`spacing_r` originale contro permutation null row-aligned; operator=`exp_boundary_blank_null_audit.py` + prescan row-aligned; generator=`dnd_autoricerca.genera_segnale` per `zeta_zeros` e `pendolo_doppio`; denominator=13 righe base autoricerca 8 GUE-like / 5 Poisson-like; non_possible=dichiarare complete `zeta_zeros` con soli 199 gap o dichiarare cambio classe su `pendolo_doppio`; not_tested=fit `V_c`, nuovi spettri, nuova legge GUE/Poisson, chiusura QxG.
tools/data/reports/agent_20260509_1427.md:12:- **Combo**: A2 confine det=-1 + A9 terzo incluso + QxG continuo/discreto + nodo BOUNDARY 8 GUE / 5 Poisson + tensione `TRASCENDENZA_LIMITE`.
tools/data/reports/agent_20260509_1427.md:14:- **Piano superiore**: grafo della conoscenza e boundary operator. Il bordo non classifica GUE o Poisson; filtra quali righe hanno denominatore sufficiente.
tools/data/reports/agent_20260509_1427.md:25:Il null row-aligned per una coppia blank GUE/Poisson decide `blank -> transfers`, `blank -> falls` o `blank remains blank`?
tools/data/reports/agent_20260509_1427.md:30:- Perimetro atomico: 13 righe base, 8 GUE-like e 5 Poisson-like.
tools/data/reports/agent_20260509_1427.md:32:- Contratto osservabile-operatore: il ciclo misura disponibilita e comportamento del null, non rifitta `V_c`; `zeta_zeros` resta contaminated per massa debole; `pendolo_doppio` resta Poisson -> Poisson con ordering dependence forte.
tools/data/reports/agent_20260509_1427.md:40:| zeta_zeros | 199 | 0.615009 | 0.641885 | -2.310593 | GUE -> GUE | false | transfer contaminato |
tools/data/reports/agent_20260509_1427.md:41:| pendolo_doppio | 5008 | 0.386104 | 0.292437 | 27.919656 | Poisson -> Poisson | true | transfer complete |
tools/data/reports/agent_20260509_1427.md:48:| source GUE | 8 |
tools/data/reports/agent_20260509_1427.md:49:| source Poisson | 5 |
tools/data/reports/agent_20260509_1427.md:61:2. **Verificato: il transfer non cambia la classe.** `zeta_zeros` resta GUE -> GUE; `pendolo_doppio` resta Poisson -> Poisson.
tools/data/reports/agent_20260509_1427.md:71:Questo non chiude QxG, non dichiara una nuova legge GUE/Poisson e non rifitta `V_c`. Il risultato decide il denominatore: quando il null row-aligned esiste, il blank diventa transfer; quando manca, resta blank.
tools/data/reports/agent_20260509_1427.md:76:- **Invariante di passaggio**: il null row-aligned decide il passaggio; la classe GUE/Poisson non viene riscritta.
tools/data/reports/agent_20260512_0330.md:8:**observable_contract**: claim=`prime_SR_persistent_boundary` regge solo se le finestre prime conservano `SR` come osservabile one-sided comune attraverso provider e offset, mentre controlli non-prime ampliati non condividono persistenza SR piena; observable=`SR` in `coherent_one_sided_observables` + firma comune one-sided; operator=`exp_prime_sr_persistent_boundary.py`; generator=primi via `row_spacings("numeri_primi")` e `prime_gap_sequence`, controlli via composite gaps, candidati mod6, eventi Cramer-like, GUE blocks, logistic return intervals; denominator=8 finestre prime row-local + 20 controlli non-prime; non_possible=claim prime-specific se `SR` prime scende sotto 8/8, se la firma comune prime non e' `[SR]`, o se una sottofamiglia controllo condivide persistenza SR piena; not_tested=atlante beta globale, `V_c`, `gap_ratio`, origine analitica di SR.
tools/data/reports/agent_20260512_0330.md:12:- **Combo**: A2 confine det=-1 + A9 terzo incluso + QxG continuo/discreto + BOUNDARY come passaggio 8 GUE / 5 Poisson + residuo `prime_SR_persistent_boundary`.
tools/data/reports/agent_20260512_0330.md:18:- **Proiezione**: stesso gate canonico ordine/null, stesso size 1024, due provider prime, quattro offset, controlli compositi/mod6/Cramer/GUE/logistic.
tools/data/reports/agent_20260512_0330.md:37:- Controlli: composite gaps, mod6 candidates, Cramer-like events su 4 offset; 4 GUE random matrix blocks; 4 logistic return interval rows.
tools/data/reports/agent_20260509_1538.md:8:**observable_contract**: claim=le righe non-esatte della matrice BOUNDARY 15:32 vanno lette row-aligned senza usare label GUE/Poisson; observable=stato beta + forza supporto + telemetria denominatore/null; operator=`exp_boundary_row_aligned_nonexact_audit.py`; generator=matrice `boundary_two_axis_matrix_20260509_1532` + prescan `boundary_denominator_prescan_full_20260509_1500`; denominator=13 righe totali, 11 support-transfer, 7 support-transfer non esatte; non_possible=forzare il conteggio a 6 o trattare beta 0.3 come ascissa comune; not_tested=nuovi domini, nuovi null, nuova griglia beta, fit `V_c`.
tools/data/reports/agent_20260509_1538.md:24:- `not_drift`: non usa label GUE/Poisson come operatore, non ritorna a `V_c`, non rifitta il confine; corregge il denominatore della direttiva quando il deposito mostra 7 righe.
tools/data/reports/agent_20260509_1538.md:38:- Label policy: non legge `source_domain_type` o label GUE/Poisson come decision field.
tools/data/reports/agent_20260509_1538.md:114:- **Invariante di passaggio**: failure mode row-aligned; non label GUE/Poisson e non beta 0.3 globale.
tools/data/reports/agent_20260514_1612.md:5:**verdict**: CONSTRAINT - il trasduttore `SR` assorbito vs residuo `L1,triple_var` produce un test fisico concreto su spettri GUE/Anderson; nel run sintetico il ritorno fisico e' presente ma resta proxy numerico, non misura sperimentale  
tools/data/reports/agent_20260514_1612.md:8:**observable_contract**: claim=il cedimento selettivo di `SR` puo' essere formulato come test fisico A->B; observable=componenti canoniche attive/assorbite contro null Poisson span-matched; operator=`tools/exp_physical_sr_residue_bounce.py`; generator=GUE hermitiano e Hamiltoniano Anderson 1D tight-binding; denominator=48 repliche per dominio, 95 gap centrali per spettro; non_possible=rimbalzo fisico assente se non si nomina B concreto oppure se `SR` non discrimina GUE da Anderson localizzato; not_tested=dati sperimentali reali, unfolding fine, classi di simmetria GOE/GSE, many-body localization.
tools/data/reports/agent_20260514_1612.md:12:Il risultato non chiede un altro ingrandimento dei primi. Chiede se la componente che cade (`SR`) e quelle che resistono (`L1,triple_var`) nominano una distinzione fisica: repulsione spettrale presente o assorbita dal bordo Poisson.
tools/data/reports/agent_20260514_1612.md:17:- **Dipolo / punto-zero**: caos quantistico / localizzazione. Punto-zero: stesso numero di livelli e stesso span con null Poisson span-matched.
tools/data/reports/agent_20260514_1612.md:19:- **Proto-ipotesi**: `SR` misura la repulsione locale assorbibile quando lo spettro torna Poisson; `L1,triple_var` misurano memoria di sequenza oltre il solo nearest-neighbor ratio. Il deposito prime-minus-mod6 diventa trasduttore, non destinazione.
tools/data/reports/agent_20260514_1612.md:21:- **Proiezione**: GUE come sorgente fisica A; null Poisson span-matched; Anderson 1D con disordine `W=0.5,2,6,12` come ritorno fisico B.
tools/data/reports/agent_20260514_1612.md:31:- `not_drift`: il deposito prime-minus-mod6 non e' destinazione; decide solo quale domanda portare a GUE/Anderson.
tools/data/reports/agent_20260514_1612.md:35:- **Punto fisico sorgente concreto**: spettri energetici di sistemi quantistici caotici modellati da GUE, proxy per billiard o sistemi senza simmetria di inversione temporale.
tools/data/reports/agent_20260514_1612.md:36:- **Attraversamento matematico**: null Poisson span-matched con osservabili canonici `SR,L1,triple_var`; e' l'analogo fisico del contro-bordo che prova ad assorbire la componente nearest-neighbor.
tools/data/reports/agent_20260514_1612.md:37:- **Punto fisico di ritorno concreto**: Hamiltoniano tight-binding Anderson 1D con disordine crescente, interrogabile tramite spacing degli autovalori e transizione verso statistiche Poisson/localizzate.
tools/data/reports/agent_20260514_1612.md:38:- **Relazione nuova o limite**: il run mostra una formulazione fisica del cedimento selettivo: `SR` e' attivo nel GUE e assorbito nel limite Anderson localizzato `W=12`. A `W=6`, `SR` e `L1` sono assorbiti mentre `triple_var` resta attivo: il ritorno fisico separa nearest-neighbor repulsion e memoria di triple locali.
tools/data/reports/agent_20260514_1612.md:39:- **Osservabile/test fisico possibile**: misurare spettri energetici in un sistema Anderson-like, costruire un ensemble di finestre spettrali, confrontare `SR,L1,triple_var` contro null Poisson span-matched con sign-swap row-local.
tools/data/reports/agent_20260514_1612.md:44:> Il cedimento selettivo di `SR` osservato nel deposito prime-minus-mod6 puo' tornare come criterio fisico: `SR` resta attivo in spettri caotici GUE e viene assorbito in spettri Anderson localizzati contro un null Poisson span-matched.
tools/data/reports/agent_20260514_1612.md:49:- Ensemble: GUE hermitiano `N=192`, Anderson 1D `N=192`.
tools/data/reports/agent_20260514_1612.md:52:- Null: livelli Poisson uniformi span-matched con stesso count.
tools/data/reports/agent_20260514_1612.md:60:| GUE chaotic proxy | 48 | SR,L1,triple_var | 0.5936 / 0.3881 / 0.2055 | 0.000244 | -0.3047 | 0.000244 | -2.4262 | 0.000244 |
tools/data/reports/agent_20260514_1612.md:70:1. **Verificato**: nel proxy GUE, `SR` resta attivo contro Poisson span-matched (`delta=0.2055`, `p=0.000244`, `d=4.872`).
tools/data/reports/agent_20260514_1612.md:73:4. **Inferito dal perimetro**: il deposito prime-minus-mod6 produce un osservabile fisico formulabile: non "i primi sono fisica", ma "la componente `SR` boundary-sensitive corrisponde al test di repulsione spettrale contro Poisson".
tools/data/reports/agent_20260514_1612.md:80:Il rimbalzo fisico esiste come test: GUE -> span-matched Poisson -> Anderson 1D. Il risultato non cristallizza una nuova legge fisica. Formalizza un ponte interrogabile: quando lo spettro torna localizzato/Poisson, `SR` viene assorbito; nella zona di crossover, `triple_var` puo' restare come residuo oltre nearest-neighbor.
tools/data/reports/agent_20260514_1612.md:84:- **Due radici**: repulsione spettrale caotica / assorbimento Poisson localizzato.
tools/data/reports/agent_20260514_1612.md:85:- **Singolare**: null Poisson span-matched con stesso count.
tools/data/reports/agent_20260514_1612.md:92:Il prossimo ciclo fisico deve sostituire il proxy sintetico con un denominatore piu vicino al laboratorio: GOE/GUE scelto per simmetria dichiarata, Anderson 3D o many-body localization se si vuole una transizione fisica vera, unfolding controllato e confronto con dati o letteratura. Il deposito prime-minus-mod6 resta utile solo come generatore di osservabili, non come prova del dominio fisico.
tools/data/reports/agent_20260507_0803.md:12:GUE/Poisson boundary is a transferable operator or only a local metric.
tools/data/reports/agent_20260507_0803.md:36:This is not a new BOUNDARY experiment. GUE and Poisson do not appear in the
tools/data/reports/agent_20260507_0803.md:88:GUE/Poisson run, beta 0.3-0.4 carried both classification ambiguity and
tools/data/reports/agent_20260507_0803.md:116:> GUE/Poisson boundary layer is not transferable: ambiguity appears near beta
tools/data/reports/report_20260404_1852.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260404_1852.md:22:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=1.0000 (GUE standard=0.60)
tools/data/reports/report_20260404_1852.md:25:- percolation_var_0.55: r=0.7543859649122806, spacing=Poisson-like
tools/data/reports/report_20260404_1852.md:26:- numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like
tools/data/reports/report_20260404_1852.md:27:- coupled_oscillators_var_50: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260404_1852.md:28:- brownian_motion_var_0.5: r=1.0, spacing=Poisson-like
tools/data/reports/agent_20260508_2005.md:8:**observable_contract**: claim=se il boundary simbolico del core alto esiste nella grammatica nativa della parola, le finestre locali attorno alle posizioni IDS dei gap core devono separare aligned supertile da misaligned same-length; observable=eccesso grammaticale locale rispetto a baseline Sturmian classica; operator=estrazione finestra circolare attorno a round(IDS*N) per ogni label core selezionato, misura p(k)<=k+1, right-special<=1, return-word excess sopra 2, difetto palindromico; generator=phi_sturmian perturbato da supertile_shuffle, same_length_contiguous_shuffle, same_count_internal_shuffle; denominator=N={377,610}, phase={0,0.25,0.5,0.75}, threshold={2.0}, trials=5, top_k=12, |n|<=34, supertile_order={8,9,10,11}, window={89 main, 55 seedcheck}, k={3,4,5,6,7,8}; not_tested=gap_ratio, domini GUE/Poisson reali, soglie 1.75/2.25, prova formale della grammatica Sturmian, generatori non-phi.
tools/data/reports/agent_20260509_1437.md:8:**observable_contract**: claim=il residual blank test decide se i 3 blank residui BOUNDARY entrano nel transfer; observable=`spacing_r` originale contro permutation null row-aligned; operator=`exp_boundary_blank_null_audit.py` + `exp_boundary_denominator_prescan.py`; generator=`dnd_autoricerca.genera_segnale` per `string_vibration`, `reaction_diffusion`, `logistica_biforcazione_var_3.5699`; denominator=13 righe base autoricerca 8 GUE-like / 5 Poisson-like; non_possible=dichiarare chiusura QxG, nuova legge GUE/Poisson, o complete `reaction_diffusion` con 499 gap; not_tested=fit `V_c`, nuovi spettri, nuovi domini.
tools/data/reports/agent_20260509_1437.md:12:- **Combo**: A2 confine det=-1 + A9 terzo incluso + A11 combo + QxG continuo/discreto + nodo BOUNDARY 8 GUE / 5 Poisson + tensione `TRASCENDENZA_LIMITE`.
tools/data/reports/agent_20260509_1437.md:14:- **Piano superiore**: grafo della conoscenza e boundary operator. Il gate decide copertura del denominatore, non ontologia GUE/Poisson.
tools/data/reports/agent_20260509_1437.md:30:- Perimetro atomico: 13 righe base, 8 GUE-like e 5 Poisson-like.
tools/data/reports/agent_20260509_1437.md:40:| string_vibration | 7999 | 0.383868 | 0.372199 | 4.562844 | Poisson -> Poisson | true | transfer complete |
tools/data/reports/agent_20260509_1437.md:41:| reaction_diffusion | 499 | 0.762604 | 0.494932 | 31.390192 | GUE -> GUE | true | transfer contaminato |
tools/data/reports/agent_20260509_1437.md:42:| logistica_biforcazione_var_3.5699 | 4727 | 0.581221 | 0.099640 | 161.271569 | GUE -> Poisson | true | transfer complete, class_change edge case |
tools/data/reports/agent_20260509_1437.md:49:| source GUE | 8 |
tools/data/reports/agent_20260509_1437.md:50:| source Poisson | 5 |
tools/data/reports/agent_20260509_1437.md:61:| string_vibration:cycle_6 | Poisson | complete | 0.000 | shuffle z=4.56; class_change=False | transfers |
tools/data/reports/agent_20260509_1437.md:62:| reaction_diffusion:cycle_11 | GUE | contaminated | 0.002 | shuffle z=31.39; class_change=False | transfers |
tools/data/reports/agent_20260509_1437.md:63:| logistica_biforcazione_var_3.5699:cycle_13 | GUE | complete | 0.000 | shuffle z=161.27; class_change=True | transfers |
tools/data/reports/agent_20260509_1437.md:68:3. **Verificato: `string_vibration` trasferisce senza cambio classe.** Poisson -> Poisson, `ordering_dependent=true`, `n_gaps=7999`.
tools/data/reports/agent_20260509_1437.md:69:4. **Verificato: `logistica_biforcazione_var_3.5699` trasferisce con `class_change=true`.** Il cambio GUE -> Poisson e' edge case del null, non legge nuova legge.
tools/data/reports/agent_20260509_1437.md:82:- **Invariante di passaggio**: disponibilita del null leggibile; non l'etichetta GUE/Poisson e non il fit `V_c`.
tools/data/reports/agent_20260509_1437.md:94:- **L5 re-discovery**: il ciclo e' audit di denominatore residuo, non teorema GUE/Poisson.
tools/data/reports/agent_20260509_1437.md:104:- Compare residual audit: `string_vibration` e `logistica_biforcazione_var_3.5699` combaciano sui numeri depositati; `reaction_diffusion` nel rerun produce `r=0.755661`, `z=30.592858` invece di `r=0.762604`, `z=31.390192`, ma conserva `n_gaps=499`, `GUE -> GUE`, `ordering_dependent=true`, `transfer=contaminated`. Il drift e' attribuito al generatore dinamico, non al null seed.
tools/data/reports/agent_20260515_1940.md:8:**observable_contract**: claim=il gate RP a due lettori e fisico solo se la stessa riga lambda resta stabile attraversando le taglie; observable=two_reader_all_sizes da graph_bridge_frequency unita a Brody q, peso Wigner/Poisson, SR e IPR; operator=flusso Rosenzweig-Porter diagonal-plus-GUE ripetuto su N, seed e perturbazioni kNN; generator=H(lambda)=sqrt(1-lambda)D+sqrt(lambda)GUE; denominator=11 righe lambda identiche su N={64,96,128}; non_possible=claim fisico two-reader se nessuna riga e stable_graph_bridge+classical_intermediate in tutte le taglie; not_tested=limite N infinito, unfolding alternativi, Anderson/mobility edge, varianti many-body.
tools/data/reports/agent_20260515_1940.md:14:- **Combo**: A9 terzo incluso + QxG continuo/discreto + flusso Hamiltoniano RP + tensione BOUNDARY "8 domini GUE, 5 Poisson".
tools/data/reports/agent_20260515_1940.md:15:- **Dipolo / punto-zero**: polo Poisson diagonale / polo GUE. Punto-zero: riga lambda che resta insieme ponte grafico stabile e intermedia classica su piu taglie.
tools/data/reports/agent_20260515_1940.md:24:- `why`: il ciclo resta sul confine GUE/Poisson e testa il terzo incluso operativo dentro un flusso Hamiltoniano controllato, con separazione tra endpoint, riga a due lettori e residui del grafo.
tools/data/reports/agent_20260515_1940.md:27:## Re-discovery audit
tools/data/reports/agent_20260515_1940.md:28:- **Baseline noto piu vicino**: crossover Rosenzweig-Porter / Wigner-Dyson-GUE vs Poisson, letto con adjacent gap ratio, Brody q e mistura Wigner/Poisson.
tools/data/reports/agent_20260515_1940.md:32:- **Correzione L3/L5 richiesta**: `two_reader_boundary_confirmed = 1`; `graph_only_residue = 0`; `scope_change_declared = true`; `graph_baseline_audit = kNN stability + size sweep + Brody/Berry-like row-aligned`. Non sommo le righe classic-only al boundary a due lettori.
tools/data/reports/agent_20260515_1940.md:41:- **Punto fisico sorgente**: transizione spettrale tra indipendenza/localizzazione Poisson e repulsione GUE.
tools/data/reports/agent_20260515_1940.md:42:- **Attraversamento matematico**: Hamiltoniana diagonal-plus-GUE, osservabili sui gap, Brody/Berry-like e grafo kNN perturbato su taglie multiple.
tools/data/reports/agent_20260515_1940.md:61:| two_reader_all_sizes | 1 |
tools/data/reports/agent_20260515_1940.md:62:| two_reader_intermittent | 2 |
tools/data/reports/agent_20260515_1940.md:63:| graph_only_residue | 0 |
tools/data/reports/agent_20260515_1940.md:81:4. Verificato: `graph_only_residue = 0` su tutte le taglie. Il residuo Lab-specific graph-only non rientra nel flusso RP size-sweep.
tools/data/reports/agent_20260515_1940.md:99:ssp_value: yes. Lo script e riusabile per stressare gate GUE/Poisson controllati su taglie multiple e restituisce direttamente righe all-size, righe intermittenti, residui graph-only e residui classic-only.
tools/data/reports/report_20260405_0715.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260405_0715.md:22:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=1.0000 (GUE standard=0.60)
tools/data/reports/report_20260405_0715.md:25:- percolation_var_0.55: r=0.7543859649122806, spacing=Poisson-like
tools/data/reports/report_20260405_0715.md:26:- numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like
tools/data/reports/report_20260405_0715.md:27:- coupled_oscillators_var_50: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260405_0715.md:28:- brownian_motion_var_0.5: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260307_0342.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260307_0342.md:23:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=0.8650 (GUE standard=0.60)
tools/data/reports/report_20260307_0342.md:26:- brownian_motion_var_0.5: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260307_0342.md:27:- coupled_oscillators_var_50: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260307_0342.md:28:- percolation_var_0.55: r=1.0384615384615385, spacing=Poisson-like
tools/data/reports/report_20260307_0342.md:29:- percolation_var_0.65: r=0.9642857142857143, spacing=Poisson-like
tools/data/reports/report_20260303_0341.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260303_0341.md:23:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=0.8650 (GUE standard=0.60)
tools/data/reports/report_20260303_0341.md:26:- brownian_motion_var_0.5: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260303_0341.md:27:- coupled_oscillators_var_50: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260303_0341.md:28:- percolation_var_0.55: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260303_0341.md:29:- percolation_var_0.65: r=1.380952380952381, spacing=Poisson-like
tools/data/reports/agent_20260426_0330.md:9:> BOUNDARY: 8 domains GUE, 5 Poisson — where is the boundary?
tools/data/reports/agent_20260426_0330.md:13:Does spectral rigidity (number variance Sigma^2(L)) — an observable independent from the r-statistic — confirm or refute our GUE/Poisson classification? And does the dual-channel structure (magnitude vs algebraic ordering) manifest at the level of long-range spectral statistics?
tools/data/reports/agent_20260426_0330.md:17:- **Theory**: GUE predicts Sigma^2(L) ~ (2/pi^2) ln(L), Poisson predicts Sigma^2(L) = L
tools/data/reports/agent_20260426_0330.md:18:- **Domains**: 8 domains (primes, GUE matrices, coupled_osc, string_vib, percolation, logistic, brownian, Poisson random)
tools/data/reports/agent_20260426_0330.md:22:- **Metric**: Sig2/L ratio (GUE << 1, Poisson = 1), log-log slope, ordering fraction = (Sig2_shuf - Sig2_real) / Sig2_shuf
tools/data/reports/agent_20260426_0330.md:30:| gue_matrix | dist-GUE | 359 | 0.073 | 0.452 | 0.195 | -4.8 | YES |
tools/data/reports/agent_20260426_0330.md:31:| primes* | dist-GUE | 5132 | 0.510 | 1.058 | 0.576 | -1.5 | NO |
tools/data/reports/agent_20260426_0330.md:32:| coupled_osc | ord-GUE | 2002 | 4.491 | 1.637 | 2.108 | 17.8 | NO |
tools/data/reports/agent_20260426_0330.md:33:| string_vib | ord-GUE | 7999 | 2.837 | 1.606 | 1.285 | 23.8 | NO |
tools/data/reports/agent_20260426_0330.md:34:| percolation | ord-GUE | 199 | 1.969 | 1.416 | 1.222 | 3.3 | NO |
tools/data/reports/agent_20260426_0330.md:35:| logistic | Poisson | 4999 | 462.4 | 0.757 | 1318.9 | -0.4 | YES |
tools/data/reports/agent_20260426_0330.md:36:| brownian | Poisson | 4999 | 9.250 | 1.433 | 0.600 | 287.1 | YES |
tools/data/reports/agent_20260426_0330.md:37:| poisson | Poisson | 10000 | 1.050 | 0.999 | 1.014 | 0.6 | YES |
tools/data/reports/agent_20260426_0330.md:56:Log-log slope: real = 0.737, shuffle = 0.971. GUE theory ≈ 0.3, Poisson = 1.0.
tools/data/reports/agent_20260426_0330.md:58:### Ordering-GUE paradox
tools/data/reports/agent_20260426_0330.md:60:Ordering-GUE domains (coupled_osc, string_vib, percolation) show Sig2/L > 1 — they are SUPER-Poisson. The ordering creates excess clustering (bunching), not repulsion. Shuffling REDUCES their variance (z = 3 to 24). The r-statistic sees nearest-neighbor repulsion; Sig2 sees long-range bunching. These are two different properties.
tools/data/reports/agent_20260426_0330.md:68:3. **Only true GUE matrices are rigid at all scales (Sig2/L = 0.073 at L=10).** Primes live in an intermediate regime (0.376 at L=10) — more rigid than Poisson, less rigid than GUE. This is NOT a failure of GUE classification — it's a finer structure that the r-statistic cannot resolve.
tools/data/reports/agent_20260426_0330.md:70:4. **Ordering-GUE domains are anti-rigid at long range.** They show super-Poisson variance (Sig2/L > 1), meaning the ordering creates clustering, not repulsion. The r-statistic and Sig2 classify them differently: r sees short-range repulsion, Sig2 sees long-range bunching.
tools/data/reports/agent_20260426_0330.md:72:5. **META resolved: the tests are not tautological, but they are incomplete.** The r-statistic captures genuine structure (short-range spacing repulsion) confirmed by an independent observable. But Sig2(L) reveals richer structure that the r-statistic cannot see. The 8/5 GUE/Poisson split is a projection of a higher-dimensional reality.
tools/data/reports/agent_20260426_0330.md:78:- BOUNDARY: The boundary is not a line separating GUE from Poisson. It is a surface in the (short-range, long-range, ordering-fraction) space. Primes sit in the interior of this surface, not at a boundary.
tools/data/reports/agent_20260426_0330.md:80:- C1: Primes remain unique — the only domain where ordering INCREASES rigidity at long range while maintaining intermediate short-range repulsion. GUE matrices have stronger short-range repulsion; ordering-GUE domains have anti-rigidity at long range.
tools/data/reports/agent_20260426_0330.md:87:- **Campo di possibilità**: diventa possibile predire la rigidità a scala L dalla decomposizione (distribuzione + ordinamento) con due parametri indipendenti. Diventa non-possibile trattare i primi come "GUE" o "Poisson" — vivono in un continuo parametrizzato dalla scala, e nessun singolo numero li classifica.
tools/data/reports/agent_20260509_1444.md:8:**observable_contract**: claim=il gate BOUNDARY trasferisce fuori dal perimetro base come operatore `null_state -> transfer_state -> denominator_state`; observable=stable canonical observables contro permutation null e layer classification; operator=`exp_denominator_gate_transfer_matrix.py`; generator=`DUALITA_golden`, `R_periodic_triad`, `T_markov_alternating`, `E_ar1_continuity`; denominator=4 perimetri sintetici QxG continuo/discreto, 4096 gaps, 24 replicates, 11 beta layers, 40 shuffle baselines; non_possible=chiamare chiusura QxG, legge GUE/Poisson o endpoint-stable universale; not_tested=perimetro fisico reale, fit `V_c`, nuovi domini autoricerca.
tools/data/reports/agent_20260509_1444.md:11:- **Prima impressione**: dopo 13/13 transfer sul perimetro base, il confine non chiede un altro blank audit. Chiede se il gate resta gate quando non porta piu' le etichette GUE/Poisson.
tools/data/reports/agent_20260509_1444.md:15:- **Operatori laterali scelti**: boundary operator, graph/perimeter transfer matrix, shuffle marginal-preserving. Entrano per trasferire il gate senza importare il label GUE/Poisson.
tools/data/reports/agent_20260509_1444.md:83:- **Invariante di passaggio**: osservabile one-sided contro null permutato; non il label GUE/Poisson e non un set canonico completo.
tools/data/reports/agent_20260505_1022.md:20:- dati: `primes` con 8000 gap, `GUE` con 175 spacing effettivi prodotti dal generatore corrente, `Poisson` con 8000 spacing;
tools/data/reports/agent_20260505_1022.md:44:Su GUE il campione effettivo e' piccolo (`N=175`), quindi il risultato e' solo indicativo:
tools/data/reports/agent_20260505_1022.md:55:Poisson resta quasi nullo rispetto a SR/SR2/triple_var: molte curve non attraversano `|z| >= 2`. Questo e' coerente con un controllo a bassa struttura, non con una prova di assenza assoluta.
tools/data/reports/agent_20260505_1022.md:75:L2 quantita' assoluta vs ratio: il confronto usa alpha critici e z-score, non percentuali tra spazi di taglia diversa. GUE ha perimetro ridotto (`N=175`) e non viene pesato come primes/Poisson.
tools/data/reports/report_20260305_2121.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260305_2121.md:23:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=0.8650 (GUE standard=0.60)
tools/data/reports/report_20260305_2121.md:26:- brownian_motion_var_0.5: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260305_2121.md:27:- coupled_oscillators_var_50: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260305_2121.md:28:- percolation_var_0.55: r=0.6944444444444445, spacing=Poisson-like
tools/data/reports/report_20260305_2121.md:29:- percolation_var_0.65: r=1.0454545454545456, spacing=Poisson-like
tools/data/reports/agent_20260514_1649.md:8:**observable_contract**: claim=il candidato QxG 16:40 diventa fit-ready solo se espone input, output, soglie, trace, transfer/blank/fall e contro-perimetro; observable=`component_state(SR,L1,triple_var)` piu `poisson_contrast` e `direct_contrast`; operator=`tools/exp_physical_sr_residue_bounce.py`; generator=nessun nuovo dominio, riuso deposito GOE/GUE/Anderson 16:40 e smoke test sintetico minimo; denominator=artifact JSON fit-ready + interfaccia su spettro ordinato; non_possible=integrare automaticamente il grafo o chiamare legge fisica il tester; not_tested=dati sperimentali, Anderson 3D, many-body localization, unfolding dedicato, limite asintotico.
tools/data/reports/agent_20260514_1649.md:20:- **Combo**: A2 confine det=-1 + A9 terzo incluso + A11 combo + ponte QxG continuo/discreto + candidato graph completion 16:40 + direzione BOUNDARY GUE/Poisson.
tools/data/reports/agent_20260514_1649.md:24:- **Possibile / non-possibile**: possibile = passare uno spettro ordinato e ottenere stato componente, contrasto Poisson e contrasto diretto se classi presenti; non-possibile = usare l'artifact come ponte QxG integrato o legge fisica.
tools/data/reports/agent_20260514_1649.md:79:| GUE unitary no time reversal | active | active | active | `physical_sr_residue_bounce_20260514_1640_goe_gue_ncurve.json` |
tools/data/reports/agent_20260514_1649.md:86:| class | focus_signature | SR state vs Poisson | note |
tools/data/reports/agent_20260514_1649.md:89:| GUE smoke | SR,L1,triple_var | active | plumbing verificato, non evidenza fisica nuova |
tools/data/reports/agent_20260514_1649.md:91:Il `direct_contrast` dello smoke e' presente ma non promosso: un solo spettro per classe non fornisce una distribuzione indipendente per separazione fisica. La separazione GOE/GUE fit-ready resta quella del deposito 16:40 con 64 repliche per taglia.
tools/data/reports/agent_20260514_1649.md:95:- `transfer`: `SR,L1,triple_var` passano dal deposito matematico-fisico allo strumento come stati componente contro Poisson e, se esistono classi, come contrasto diretto.
tools/data/reports/agent_20260514_1649.md:97:- `fall`: il tester cade se GOE/GUE non separano `SR` nel contrasto diretto, se le classi caotiche assorbono tutti gli osservabili focus contro Poisson, o se Anderson `W=6` mantiene `SR` active sotto le soglie dichiarate.
tools/data/reports/agent_20260514_1649.md:104:- **Verificato da deposito 16:40**: GOE/GUE hanno `SR,L1,triple_var` active; Anderson `W=6` assorbe `SR` e conserva `triple_var`.
tools/data/reports/agent_20260514_1649.md:128:`component_state(SR,L1,triple_var)` e' fit-ready come strumento: riceve uno spettro ordinato, produce stato componente, contrasto Poisson e contrasto diretto quando il payload contiene classi. Il candidato QxG resta pronto per decisione operatore, non integrato nel grafo e non promosso a legge fisica.
tools/data/reports/agent_20260419_0755.md:77:- **Test at larger scales**: Does the alpha_res gap (1.24 vs 1.60) change with prime scale? If it narrows toward Poisson, the long-range class memory also has a crossover.
tools/data/reports/agent_20260412_0330.md:7:> Two types of duality: (1) dipolar — generative, det=-1, (2) illusory — dispersive, det=+1. The drift toward Poisson should correspond to loss of det=-1 structure.
tools/data/reports/agent_20260412_0330.md:40:4. **det(M) drifts toward zero (less negative), slope = +0.003/ln(p), R^2=0.90.** Both real and shuffled drift together. The marginal distribution itself is changing — gaps become more Poisson-like (wider, more symmetric) at larger primes.
tools/data/reports/agent_20260412_0330.md:48:- **NEW**: The ordering-induced excess negativity (delta_det ~ -0.009) decays with scale (z from -3.0 to -0.9). This is a new observable that quantifies the "dipolar excess" — the gap between structure and randomness — and it converges to zero. Consistent with the 4-stage Poisson convergence hierarchy.
tools/data/reports/agent_20260508_2133.md:8:**observable_contract**: claim=il residuo SR dello zero Mobius resta informativo dopo un null che preserva la geometria coarse della coppia di gap; observable=sr_zero_minus_nonzero, sr_aligned_minus_misaligned, low_low_zero_minus_nonzero, high_high_zero_minus_nonzero; operator=permuta label di transizione aligned/misaligned/zero dentro ogni pair bucket `(bucket(g_i), bucket(g_{i+1}))`; generator=prime gaps up to p<=1e6 with Mobius interval charges; denominator=main N={5000,10000,20000} offset=0 plus seedcheck offsets {3000,7000,11000}, 400 permutazioni, seed=2133; not_tested=gap_ratio Sturmian, V_c scaling, GUE/Poisson universale, sequenza Mobius globale coerente dopo shuffle.
tools/data/reports/agent_20260508_2133.md:36:- Contratto osservabile-operatore: `gap_ratio`, `V_c`, domini Sturmian e GUE/Poisson non testati.
tools/data/reports/agent_20260515_1947.md:8:**observable_contract**: claim=il gate BOUNDARY trasferisce oltre RP solo se la stessa riga Anderson W resta stable_graph_bridge+classical_intermediate su tutte le taglie testate; observable=two_reader_all_sizes da graph_bridge_frequency unita ad adjacent ratio, Brody q, peso Wigner/Poisson, IPR ed entropia di partecipazione; operator=Hamiltoniana Anderson 3D tight-binding periodica, sweep disorder, perturbazione seed+kNN; generator=H=sum_i eps_i |i><i| + hopping primi vicini su L^3, eps_i uniform[-W/2,W/2]; denominator=11 righe W identiche su L={5,6}; non_possible=trasferimento cross-dominio se nessuna riga W e stable_graph_bridge+classical_intermediate in tutte le taglie; not_tested=limite termodinamico, esponente critico, boundary conditions alternative, sparse large-L, spettri sperimentali.
tools/data/reports/agent_20260515_1947.md:15:- **Dipolo / punto-zero**: polo metallico Wigner-Dyson / polo localizzato Poisson. Punto-zero: riga W in cui graph bridge e intermediacy classica concordano attraversando la taglia.
tools/data/reports/agent_20260515_1947.md:27:## Re-discovery audit
tools/data/reports/agent_20260515_1947.md:28:- **Baseline noto piu vicino**: transizione Anderson 3D Wigner-Dyson/Poisson con mobility edge/disorder criticale finito vicino a W_c nel modello tight-binding.
tools/data/reports/agent_20260515_1947.md:31:- **Cosa resta artifact/classificazione grafica**: `graph_only_residue_by_size = {L5: 3, L6: 4}`; questi residui non vengono sommati al boundary a due lettori.
tools/data/reports/agent_20260515_1947.md:32:- **Correzione L3/L5 richiesta**: `two_reader_boundary_confirmed = 1`; `graph_only_residue = 3/4 by size`; `scope_change_declared = true`; `graph_baseline_audit = kNN stability + adjacent-r/Brody/Wigner-Poisson/IPR row-aligned`.
tools/data/reports/agent_20260515_1947.md:41:- **Punto fisico sorgente**: crossover RP tra Poisson e Wigner-Dyson/GUE-like.
tools/data/reports/agent_20260515_1947.md:49:- **Script**: `tools/exp_anderson3d_mobility_edge_two_reader_audit.py`.
tools/data/reports/agent_20260515_1947.md:50:- **Run**: `python tools/exp_anderson3d_mobility_edge_two_reader_audit.py --out tools/data/anderson3d_mobility_edge_two_reader_audit_20260515_1947.json`.
tools/data/reports/agent_20260515_1947.md:61:| two_reader_all_sizes | 1 |
tools/data/reports/agent_20260515_1947.md:62:| two_reader_intermittent | 2 |
tools/data/reports/agent_20260515_1947.md:63:| graph_only_residue L=5 | 3 |
tools/data/reports/agent_20260515_1947.md:64:| graph_only_residue L=6 | 4 |
tools/data/reports/agent_20260515_1947.md:88:3. Verificato: il grafo produce residui endpoint Wigner stabili su W=8,12,17 e parzialmente W=14. Questi sono `graph_only_residue`, non two-reader boundary.
tools/data/reports/agent_20260515_1947.md:111:- `python -m py_compile tools/exp_anderson3d_mobility_edge_two_reader_audit.py` completato.
tools/data/reports/agent_20260515_1947.md:112:- `python tools/exp_anderson3d_mobility_edge_two_reader_audit.py --out tools/data/anderson3d_mobility_edge_two_reader_audit_20260515_1947.json` completato.
tools/data/reports/agent_20260515_1947.md:118:- Script: `tools/exp_anderson3d_mobility_edge_two_reader_audit.py`
tools/data/reports/agent_20260515_1947.md:119:- Data: `tools/data/anderson3d_mobility_edge_two_reader_audit_20260515_1947.json`
tools/data/reports/agent_20260514_1330.md:29:- `not_drift`: non torna a `V_c`, GUE/Poisson, fit o vecchi depositi; stressa solo il residuo nominato dal valutatore dopo sottrazione mod6.
tools/data/reports/agent_20260508_2121.md:8:**observable_contract**: claim=la classe zero Mobius resta informativa dopo controllo per lunghezza del gap; observable=low_low_zero_minus_nonzero, high_high_zero_minus_nonzero, sr_zero_minus_nonzero sotto null stratificato; operator=shuffle delle cariche Mobius intervallari solo dentro bucket di lunghezza gap; generator=prime gaps up to p<=1e6 with Mobius sieve; denominator=main N={5000,10000,20000} offset=0 plus seedcheck offsets {3000,7000,11000}; not_tested=gap_ratio Sturmian, high-core phi survival, universal GUE/Poisson classification, det(M) diretto.
tools/dnd_scenario.py:161:        - POISSON_CONVERGENCE: β, <r>, acf1 → Poisson a p*~10^14
tools/dnd_scenario.py:766:            (r'\bGUE\b', 'Poisson — nessuna repulsione, statistiche indipendenti'),
tools/dnd_scenario.py:767:            (r'\bPoisson\b', 'GUE — repulsione universale, non transizione'),
tools/exp_beta_crossover.py:192:    # Sweep beta from near-Poisson (0.01) to beyond-GUE (10)
tools/exp_beta_crossover.py:194:        np.linspace(0.05, 0.5, n_betas // 3),      # fine resolution near Poisson
tools/exp_beta_crossover.py:195:        np.linspace(0.6, 2.0, n_betas // 3),         # through GOE-GUE
tools/exp_beta_crossover.py:196:        np.linspace(2.5, 8.0, n_betas - 2*(n_betas//3)),  # beyond GUE
tools/exp_beta_crossover.py:313:            note = " <-- Poisson"
tools/exp_beta_crossover.py:317:            note = " <-- GUE"
tools/exp_dR_brody_connection.py:58:    beta=0 -> Poisson, beta=1 -> Wigner (GOE).
tools/md_to_site.py:174:        "C5": "Nearest-neighbor spacing distributions compared to GUE Wigner surmise",
tools/exp_markov_memory_by_gue_type.py:3:Markov Memory Decomposition Across GUE Types
tools/exp_markov_memory_by_gue_type.py:5:Question: The TWO_KINDS_GUE result (2026-04-24) found that distribution-GUE
tools/exp_markov_memory_by_gue_type.py:6:domains (primes, GUE matrices) retain GUE classification after shuffle, while
tools/exp_markov_memory_by_gue_type.py:7:ordering-GUE domains (fibonacci, coupled_osc, percolation) collapse to Poisson.
tools/exp_markov_memory_by_gue_type.py:11:This experiment asks: what is the Markov memory profile of each GUE type?
tools/exp_markov_memory_by_gue_type.py:12:If ordering-GUE domains get their classification FROM sequential ordering,
tools/exp_markov_memory_by_gue_type.py:114:    """Pure Poisson process gaps (exponential)."""
tools/exp_markov_memory_by_gue_type.py:119:    """GUE random matrix eigenvalue spacings."""
tools/exp_markov_memory_by_gue_type.py:133:    # Domain definitions with GUE type from TWO_KINDS_GUE result
tools/exp_markov_memory_by_gue_type.py:136:    # Distribution-GUE domains (survive shuffle)
tools/exp_markov_memory_by_gue_type.py:140:        'type': 'distribution-GUE',
tools/exp_markov_memory_by_gue_type.py:145:        'type': 'distribution-GUE',
tools/exp_markov_memory_by_gue_type.py:149:    # Ordering-GUE domains (collapse to Poisson on shuffle)
tools/exp_markov_memory_by_gue_type.py:158:            'type': 'ordering-GUE',
tools/exp_markov_memory_by_gue_type.py:168:                'gaps': gaps, 'type': 'ordering-GUE', 'delta_r_sign': '+'
tools/exp_markov_memory_by_gue_type.py:173:    # Poisson domains (control)
tools/exp_markov_memory_by_gue_type.py:176:        'type': 'Poisson',
tools/exp_markov_memory_by_gue_type.py:186:                    'gaps': gaps, 'type': 'Poisson', 'delta_r_sign': '0'
tools/exp_markov_memory_by_gue_type.py:251:    print("\nAggregate by GUE type:")
tools/exp_markov_memory_by_gue_type.py:252:    for gtype in ['distribution-GUE', 'ordering-GUE', 'Poisson']:
tools/exp_markov_memory_by_gue_type.py:267:        'question': 'Does Markov memory discriminate distribution-GUE from ordering-GUE?',
tools/exp_crossover_phase_test.py:5:observed in the GUE crossover is a property of the PARTIAL SHUFFLE METHOD (tautology)
tools/exp_crossover_phase_test.py:88:    """Generate GUE-like spacings from random matrices"""
tools/exp_crossover_phase_test.py:162:    """Pure Poisson (exponential gaps) — should show NO ordering signal"""
tools/exp_crossover_phase_test.py:216:    sequences['GUE'] = generate_gue_gaps(args.N, rng)
tools/exp_crossover_phase_test.py:217:    print(f"  GUE: {len(sequences['GUE'])} gaps")
tools/exp_crossover_phase_test.py:228:    sequences['Poisson'] = generate_poisson(args.N, rng)
tools/exp_crossover_phase_test.py:229:    print(f"  Poisson: {len(sequences['Poisson'])} gaps")
tools/exp_crossover_phase_test.py:262:    print("CLASSIFICATION: Does each sequence show the full GUE-like phase transition?")
tools/exp_crossover_phase_test.py:292:        print("           The GUE crossover finding is TAUTOLOGICAL (methodological artifact)")
tools/exp_crossover_phase_test.py:295:        print("           The GUE crossover finding is a REAL structural property")
tools/exp_brody_crossover.py:6:crossover from GUE-like to Poisson-like? What is the functional form?
tools/exp_brody_crossover.py:11:  where beta=0 is Poisson, beta=1 is GOE (Wigner)
tools/exp_brody_crossover.py:165:    print(f"Poisson: beta=0, GOE: beta=1")
tools/exp_brody_crossover.py:175:    # Key test: extrapolate where beta_prime would reach 0 (Poisson)
tools/exp_brody_crossover.py:178:        print(f"\nExtrapolated Poisson (beta=0) at ln(p) = {ln_p_poisson:.1f} → p ~ 10^{ln_p_poisson/np.log(10):.0f}")
tools/exp_brody_crossover.py:180:        print(f"\nbeta INCREASES with scale — primes move AWAY from Poisson")
tools/test_gue_poisson_boundary.py:15:R_GUE_TH = 0.5996
tools/test_gue_poisson_boundary.py:36:    "r_GUE": round(mu_g, 4), "r_GUE_th": R_GUE_TH,
tools/test_gue_poisson_boundary.py:37:    "r_Poisson": round(mu_p, 4), "r_Poisson_th": R_POI_TH,
tools/test_gue_poisson_boundary.py:41:    "separation_GUE": round(clean_g, 4),
tools/test_gue_poisson_boundary.py:43:    "claim": "boundary ~0.49 is third-included: neither GUE(0.60) nor Poisson(0.39)"
tools/gue_gap_test.py:33:# GUE r ~ 0.5996, Poisson r ~ 0.3863
tools/gue_gap_test.py:37:    interp = f"r={r_data:.4f} > 0.5 e z={z_score:.2f} vs shuffle: correlazione GUE-like nei gap dei primi"
tools/gue_gap_test.py:41:    interp = f"r={r_data:.4f} <= 0.5: gap più Poisson-like che GUE-like"
tools/data/reports/agent_20260511_0330.md:8:**observable_contract**: claim=`prime_persistent_blank` e' isolato solo se `numeri_primi:cycle_3` resta `beta_absent_blank` attraverso provider, offset row-local e seed shift con `SR` come osservabile one-sided comune; observable=`case_state` + firma osservabili one-sided focalizzata su `SR`; operator=`exp_prime_persistent_blank_gate.py`; generator=primi via `row_spacings("numeri_primi")` e `prime_gap_sequence`, controlli via GUE random matrix blocks e logistic return intervals; denominator=8 finestre prime da 1024 gap (2 provider x 4 offset) + 8 controlli cross-dominio; non_possible=`prime_persistent_blank` se una finestra prime recupera beta/perde supporto o se i controlli condividono la stessa firma blank-SR; not_tested=atlante beta globale, `V_c`, `gap_ratio`, validita' label sorgente GUE/Poisson.
tools/data/reports/agent_20260511_0330.md:40:- Controlli: 4 GUE random matrix blocks + 4 logistic return interval rows.
tools/data/reports/report_20260304_0342.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260304_0342.md:23:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=0.8650 (GUE standard=0.60)
tools/data/reports/report_20260304_0342.md:26:- brownian_motion_var_0.5: r=0.09164859002169198, spacing=Poisson-like
tools/data/reports/report_20260304_0342.md:27:- coupled_oscillators_var_50: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260304_0342.md:28:- percolation_var_0.55: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260304_0342.md:29:- percolation_var_0.65: r=0.9992156862745099, spacing=Poisson-like
tools/data/reports/agent_20260425_0330.md:1:# Agent Report — Markov Memory Is Channel-Specific: 140x Algebraic vs Statistical in Primes, Ordering-GUE Has No Algebraic Channel
tools/data/reports/agent_20260425_0330.md:5:**Tension explored**: META (0.5) + BOUNDARY (0.8) + TWO_KINDS_GUE (consecutio)
tools/data/reports/agent_20260425_0330.md:8:> The TWO_KINDS_GUE result (2026-04-24) discriminated distribution-GUE (primes, GUE matrices) from ordering-GUE (fibonacci, coupled_osc, percolation). The Markov-3 result (2026-04-23) found 33.6% scale-invariant ordering memory in prime gap residues. Do ordering-GUE domains have the same kind of Markov memory as primes?
tools/data/reports/agent_20260425_0330.md:11:What is the Markov memory profile of each GUE type? If ordering-GUE domains get their classification FROM sequential ordering, they should have high Markov memory. But in which channel — magnitude (gap size) or residue (algebraic structure)?
tools/data/reports/agent_20260425_0330.md:17:- **Domains**: 8 domains across 3 GUE types (distribution-GUE, ordering-GUE, Poisson)
tools/data/reports/agent_20260425_0330.md:27:| primes | dist-GUE | 17983 | 0.2 | 0.3 | 0.4 | -22.1 | -14.3 | 44% |
tools/data/reports/agent_20260425_0330.md:28:| gue_matrix | dist-GUE | 86 | -1.6 | 0.2 | 4.6 | 1.0 | -0.7 | n/a |
tools/data/reports/agent_20260425_0330.md:29:| coupled_osc | ord-GUE | 2002 | 0.7 | 1.3 | 1.5 | -12.0 | -6.7 | 42% |
tools/data/reports/agent_20260425_0330.md:30:| string_vib | ord-GUE | 7999 | 0.2 | 0.9 | 1.4 | -12.2 | -24.8 | 14% |
tools/data/reports/agent_20260425_0330.md:31:| percolation | ord-GUE | 195 | 1.4 | 2.8 | 3.9 | -1.8 | -1.2 | 35% |
tools/data/reports/agent_20260425_0330.md:32:| poisson | Poisson | 5000 | -0.0 | -0.1 | -0.2 | 0.9 | 2.3 | n/a |
tools/data/reports/agent_20260425_0330.md:33:| logistic | Poisson | 4999 | 98.8 | 99.0 | 99.0 | -3939 | -1031 | 100% |
tools/data/reports/agent_20260425_0330.md:34:| brownian | Poisson | 4999 | 12.7 | 38.9 | 46.6 | -449 | -442 | 27% |
tools/data/reports/agent_20260425_0330.md:50:2. **Ordering-GUE domains have no algebraic channel.** They have only tercile-type (magnitude) memory: 0.2-1.5% at order 1, comparable to primes in the same channel. But primes have the mod-6 channel ON TOP — which ordering-GUE domains lack entirely. No natural modular structure exists for eigenvalue spacings or percolation clusters.
tools/data/reports/agent_20260425_0330.md:52:3. **Saturation depth is an orthogonal axis to GUE type.** The fraction of memory captured at order-1 varies independently of whether a domain is distribution-GUE or ordering-GUE:
tools/data/reports/agent_20260425_0330.md:53:   - String vibration (ord-GUE): 14% — deep, higher-order correlations dominate. Fibonacci quasiperiodicity requires long-range correlations.
tools/data/reports/agent_20260425_0330.md:54:   - Primes (dist-GUE): 44% — moderate depth.
tools/data/reports/agent_20260425_0330.md:55:   - Coupled oscillators (ord-GUE): 42% — moderate depth, similar to primes despite different GUE type.
tools/data/reports/agent_20260425_0330.md:56:   - Logistic (Poisson): 100% — shallow, deterministic, order-1 is sufficient.
tools/data/reports/agent_20260425_0330.md:58:4. **The "Poisson" class is heterogeneous in Markov memory.** Pure Poisson has zero memory (control: passed). But logistic (98.8%) and Brownian (12.7%) are Poisson-classified by r-statistic yet have massive ordering memory. The r-statistic classification misses an entire axis of variation. Ordering memory that doesn't create level repulsion is invisible to the r-test.
tools/data/reports/agent_20260425_0330.md:67:**NEW + CONSTRAINT on TWO_KINDS_GUE + BOUNDARY + C1**
tools/data/reports/agent_20260425_0330.md:69:The TWO_KINDS_GUE classification (distribution vs ordering) captures WHERE structure lives. This experiment adds a second axis: HOW the memory is structured. Primes are the only domain with dual-channel memory (algebraic + statistical). The 33% scale-invariant memory is a Z/6Z phenomenon with no analogue in ordering-GUE domains. The boundary (GUE/Poisson) is a 1D projection of a 2D structure: GUE type x memory depth.
tools/data/reports/agent_20260425_0330.md:76:- **Campo di possibilita**: qui diventa possibile → discriminare domini non solo per GUE type (delta_r sign) ma per profondita e struttura della memoria (algebraica vs statistica). Due assi ortogonali, non uno. Qui diventa non-possibile → trattare il 33% di Markov memory come una proprieta "generica" di sequenze ordinate. E specifica dei primi e del canale Z/6Z.
tools/data/reports/agent_20260509_1400.md:8:**observable_contract**: claim=il fit parametrico di `V_c` puo' entrare solo sulle righe `complete` o `contaminated` del gate fit-ready; observable=famiglia di modello migliore tra cinque forme semplici, separata da stato denominatore e massa esclusa; operator=confronto AICc su `vc_median_fit_ready` dal deposito 13:37, senza ricomputare spettri; generator=classi `reference_order`, `order_memory`, `periodic_closure`, `random_dispersion`; denominator=JSON `vc_fit_ready_scale_table_20260509_1337`, N={89,144,233,377}, soglie r={0.48,0.50,0.52}, livelli `per_mode_best` e `accepted_candidates`; non_possible=righe `broken` escluse dal fit e righe sotto `V_c=1` impediscono il claim osservato "converge a 1 dall'alto"; not_tested=nuovi N, nuovi generatori, nuovi spettri, GUE/Poisson transfer, gap_ratio, fit a tre parametri con asintoto libero.
tools/data/reports/agent_20260509_1400.md:108:- **L3 no observable drift**: non sono testati gap_ratio, nuovi spettri, nuovi N o GUE/Poisson.
tools/data/reports/agent_20260515_1734.md:7:**observable_contract**: claim=`phi` fotonico manifesta un confine fisico tra ordine periodico e disordine random; observable=statistiche della trasmissione in lambda (`stopband_fraction`, `spectral_entropy`, `peak_spacing_r`); operator=`tools/exp_photonic_boundary_third_included_gate.py`; generator=multistrati quarter-wave fixed-thickness phi/silver/bronze/periodic/balanced_random; denominator=N={55,89,144} x phase={0,0.25,0.5,0.75}, random_trials=12, lambda=0.65..1.85 su 241 punti; non_possible=terzo incluso fotonico chiuso se `phi` non resta dentro il segmento periodico-random su stopband ed entropia o se collassa nel random; not_tested=modello Maxwell completo con interfacce fisiche calibrate, misure sperimentali, GUE/Poisson universality diretta.
tools/data/reports/agent_20260515_1734.md:11:- **Combo**: A2 confine det=-1 + A9 terzo incluso + A11 combo + TxE funzione di partizione/campo EM + QxG continuo/discreto come vuoto ancora da pontare + tensione `BOUNDARY` 8 GUE / 5 Poisson.
tools/data/reports/agent_20260515_1734.md:13:- **Piano superiore**: geometria dei campi e bicono-dipoli; il confine non e' etichetta GUE/Poisson ma stato di trasporto del campo elettromagnetico.
tools/data/reports/agent_20260515_1734.md:22:- `why`: segue la direzione viva "8 domini GUE, 5 Poisson - il confine e' il terzo incluso operativo" portando il confine in un dominio fisico EM invece di proseguire il contro-perimetro `V_c`.
tools/data/reports/agent_20260515_1734.md:35:- **Punto fisico sorgente**: confine statistico GUE/Poisson come attrito tra repulsione spettrale e indipendenza.
tools/data/reports/agent_20260509_1409.md:8:**observable_contract**: claim=`denominator_state` puo' trasferire oltre `V_c` solo dove la riga domain/window ha osservabile definito e null/surrogate row-aligned; observable=`spacing_r` + availability del null shuffle; operator=prescan sui 13 domini base del deposito autoricerca, non fit parametrico; generator=perimetro 8 GUE-like / 5 Poisson-like dei cycle 1..13; denominator=`boundary_shuffle_audit` quando presente; non_possible=dichiarare transfer nei domini senza null/surrogate; not_tested=nuovi spettri, nuovi domini, fit `V_c`, famiglia AICc, QxG chiuso.
tools/data/reports/agent_20260509_1409.md:11:- **Prima impressione**: il confine non chiede se il dominio e' GUE o Poisson; chiede se la riga ha un denominatore capace di sostenere un claim.
tools/data/reports/agent_20260509_1409.md:15:- **Proto-ipotesi**: `denominator_state` trasferisce oltre `V_c` se separa riga misurabile, massa contaminata e blank senza usare GUE/Poisson come risposta.
tools/data/reports/agent_20260509_1409.md:22:- **YSN DeltaLink**: `domain row -> null availability -> transfer`, non `GUE/Poisson -> risposta`.
tools/data/reports/agent_20260509_1409.md:25:> Nel perimetro BOUNDARY 8 GUE / 5 Poisson, il gate `denominator_state` trasferisce oltre `V_c` solo se identifica le righe con null/surrogate disponibile e lascia blank le righe senza contro-perimetro.
tools/data/reports/agent_20260509_1409.md:31:- Perimetro atomico: cycle base `1..13`, esattamente 13 righe: 8 GUE-like, 5 Poisson-like.
tools/data/reports/agent_20260509_1409.md:49:| source GUE | 8 |
tools/data/reports/agent_20260509_1409.md:50:| source Poisson | 5 |
tools/data/reports/agent_20260509_1409.md:62:| ising_2d:cycle_1 | GUE | contaminated | 0.810 | spacing_r=0.902 | shuffle z=-0.10; class_change=False | transfers |
tools/data/reports/agent_20260509_1409.md:63:| pendolo_doppio:cycle_2 | Poisson | absent | 1.000 | spacing_r=0.386 | absent | blank |
tools/data/reports/agent_20260509_1409.md:64:| numeri_primi:cycle_3 | GUE | complete | 0.000 | spacing_r=0.886 | shuffle z=-26.60; class_change=False | transfers |
tools/data/reports/agent_20260509_1409.md:65:| zeta_zeros:cycle_4 | GUE | absent | 1.000 | spacing_r=0.615 | absent | blank |
tools/data/reports/agent_20260509_1409.md:66:| logistica_biforcazione:cycle_5 | GUE | complete | 0.000 | spacing_r=0.997 | shuffle z=61.60; class_change=False | transfers |
tools/data/reports/agent_20260509_1409.md:67:| string_vibration:cycle_6 | Poisson | absent | 1.000 | spacing_r=0.384 | absent | blank |
tools/data/reports/agent_20260509_1409.md:68:| random_matrix:cycle_7 | GUE | complete | 0.000 | spacing_r=0.573 | shuffle z=-14.70; class_change=False | transfers |
tools/data/reports/agent_20260509_1409.md:69:| cellular_automata:cycle_8 | GUE | contaminated | 0.790 | spacing_r=0.861 | shuffle z=-0.10; class_change=False | transfers |
tools/data/reports/agent_20260509_1409.md:70:| percolation:cycle_9 | Poisson | complete | 0.000 | spacing_r=0.404 | shuffle z=16.10; class_change=True | transfers |
tools/data/reports/agent_20260509_1409.md:71:| coupled_oscillators:cycle_10 | Poisson | contaminated | 0.146 | spacing_r=0.385 | shuffle z=43.50; class_change=True | transfers |
tools/data/reports/agent_20260509_1409.md:72:| reaction_diffusion:cycle_11 | GUE | absent | 1.000 | spacing_r=0.759 | absent | blank |
tools/data/reports/agent_20260509_1409.md:73:| brownian_motion:cycle_12 | Poisson | complete | 0.000 | spacing_r=0.489 | shuffle z=-1.60; class_change=False | transfers |
tools/data/reports/agent_20260509_1409.md:74:| logistica_biforcazione_var_3.5699:cycle_13 | GUE | absent | 1.000 | spacing_r=0.997 | absent | blank |
tools/data/reports/agent_20260509_1409.md:77:1. **Verificato: il gate trasferisce su 8/13 righe del perimetro.** Il transfer non coincide con GUE o Poisson: include 5 sorgenti GUE e 3 sorgenti Poisson perche' la condizione e' disponibilita del null, non etichetta di classe.
tools/data/reports/agent_20260509_1409.md:84:**PARTIAL TRANSFER**: `denominator_state` trasferisce oltre `V_c` come gate di perimetro, non come risposta GUE/Poisson.
tools/data/reports/agent_20260509_1409.md:86:Nel perimetro 8 GUE / 5 Poisson, il gate produce una condizione cross-dominio verificabile su 8 righe con null shuffle disponibile. Su 5 righe resta blank strutturale: l'osservabile locale esiste, ma manca il contro-perimetro row-aligned.
tools/data/reports/agent_20260509_1409.md:92:- **Singolare**: domain/window prima della classificazione GUE/Poisson.
tools/data/reports/agent_20260508_2108.md:8:**observable_contract**: claim=lo zero della carica Mobius intervallare e' testato come terzo incluso del gate aligned/misaligned; observable=rate low_low, rate high_high, SR mean per classi aligned/misaligned/zero; operator=classificazione di S_n*S_{n+1}: aligned<0, misaligned>0, zero=0; generator=prime gaps up to p<=1e6 with Mobius sieve; denominator=main N={5000,10000,20000} offset=0 plus seedcheck offsets {3000,7000,11000}; not_tested=gap_ratio Sturmian, high-core phi survival, universal GUE/Poisson classification.
tools/data/reports/report_20260404_0330.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260404_0330.md:22:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=1.0000 (GUE standard=0.60)
tools/data/reports/report_20260404_0330.md:25:- percolation_var_0.55: r=0.7543859649122806, spacing=Poisson-like
tools/data/reports/report_20260404_0330.md:26:- numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like
tools/data/reports/report_20260404_0330.md:27:- coupled_oscillators_var_50: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260404_0330.md:28:- brownian_motion_var_0.5: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260306_0341.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260306_0341.md:23:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=0.8650 (GUE standard=0.60)
tools/data/reports/report_20260306_0341.md:26:- brownian_motion_var_0.5: r=1.1993212669683257, spacing=Poisson-like
tools/data/reports/report_20260306_0341.md:27:- coupled_oscillators_var_50: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260306_0341.md:28:- percolation_var_0.55: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260306_0341.md:29:- percolation_var_0.65: r=1.0, spacing=Poisson-like
tools/data/reports/agent_20260507_0901.md:15:coordinata locale GUE/Poisson.
tools/data/reports/agent_20260507_0901.md:65:   GUE/Poisson layer. It is the coordinate created by the replacement protocol:
tools/data/reports/report_20260331_1809.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260331_1809.md:22:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=1.0000 (GUE standard=0.60)
tools/data/reports/report_20260331_1809.md:25:- logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like
tools/data/reports/report_20260331_1809.md:26:- logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260331_1809.md:27:- brownian_motion_var_0.5: r=0.9523809523809524, spacing=Poisson-like
tools/data/reports/report_20260331_1809.md:28:- percolation_var_0.65: r=1.0, spacing=Poisson-like
tools/data/reports/agent_20260508_2013.md:8:**observable_contract**: claim=se il boundary esatto del supertile e' portatore globale del core alto, le posizioni IDS dei gap core devono mostrare migliore riconoscibilita Ostrowski o maggiore prossimita ai tagli nel mode aligned rispetto al same-length misaligned; observable=distanza del centro gap da boundary di chunk, hit entro 2 siti, peso Zeckendorf e zeri finali Zeckendorf; operator=Hamiltoniana tight-binding V=1, label IDS con reader theta=1/phi, centro round(IDS*N), boundary del tiling perturbato, rappresentazione Zeckendorf del centro; generator=phi_sturmian perturbato da supertile_shuffle, same_length_contiguous_shuffle, same_count_internal_shuffle; denominator=N={377,610}, phase={0,0.25,0.5,0.75}, threshold={2.0}, trials=5, top_k=12, |n|<=34, supertile_order={8,9,10,11}; not_tested=gap_ratio, domini GUE/Poisson reali, soglie 1.75/2.25, automa formale di riconoscibilita della sostituzione, generatori non-phi.
tools/data/reports/agent_20260507_1458.md:46:Numeri di dettaglio: C1 spacing GUE-like con `<r>=0.6150`; C2 `pearson_r=-0.6428`; C3 distanza media da intero `0.0270`; G1 best phi-distance `0.021256`; G2 `12/13` domini convergono a phi; N2 `<r>=1.0000`.
tools/data/reports/agent_20260509_0652.md:8:**observable_contract**: claim=un null che preserva il gap-label set Sturmian puo' decidere se `V_c` e' portato dal label-set o dall'ordine generativo; observable=`event_type={floor_hit,internal_cross,internal_multi,no_cross}`, `vc_interp`, `r_floor`, `r_span`, `label_jaccard`, `acceptance_rate`; operator=surrogate bilanciato con swap 0/1 e ricottura finche' `label_jaccard>=0.75`, poi curva `r(V)` su griglia 0.5..3.0 step 0.01; generator=phi Sturmian, balanced_random, swap_label_surrogate; denominator=run principale N={89}, phase={0,0.25,0.5,0.75}, r_threshold={0.48,0.50,0.52}, random_trials=1, label_trials=2, swap_steps=3000, seed=202605090652; non_possible=se i surrogate accettati Jaccard>=0.75 producono floor-hit o internal_multi e restano con `r_floor`/`vc_interp` vicini al random, il label-set non basta a ricostruire il boundary Sturmian; not_tested=GUE/Poisson reali, silver/bronze, fit power-law, gap_ratio, prova a N>=144 con gate raggiunto.
tools/data/reports/agent_20260509_0652.md:95:Il prossimo passaggio non e' estendere a GUE/Poisson. Prima serve un generatore label-preserving stabile cross-phase: stessa accettazione Jaccard su N={89,144,233} oppure fallimento dichiarato come vincolo del null. Solo dopo il boundary operator puo' trasferire verso domini GUE/Poisson.
tools/data/reports/report_20260326_0343.md:17:- [✓] C1: Zeri zeta hanno spacing GUE (non Poisson)... → spacing=GUE-like, ⟨r⟩=0.6150
tools/data/reports/report_20260326_0343.md:23:- [✓] N2: Rule 30/110 hanno spacing >> GUE standard... → ⟨r⟩=0.8650 (GUE standard=0.60)
tools/data/reports/report_20260326_0343.md:26:- logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like
tools/data/reports/report_20260326_0343.md:27:- numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like
tools/data/reports/report_20260326_0343.md:28:- cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like
tools/data/reports/report_20260326_0343.md:29:- coupled_oscillators_var_50: r=1.0, spacing=Poisson-like
tools/data/reports/agent_20260514_1850.md:4:**Tension explored**: TENS_SCALE_TRASCENDENZA_LIMITE / BOUNDARY fisico GOE-GUE-Poisson-Anderson  
tools/data/reports/agent_20260514_1850.md:5:**verdict**: VALUE_STABLE / TOOL CONTRACT TRANSFER - il tester `component_state(SR,L1,triple_var)` legge un nuovo spettro fisico Anderson 3D senza cambiare definizioni osservabili e senza perdere il rimbalzo fisico A -> matematica -> fisico B. Il ritorno fisico e' strumentale: W4 e W16.5 restano active contro Poisson, ma il contrasto diretto separa solo `triple_var`; `SR` e `L1` non chiudono il bordo 3D a `L=6`.  
tools/data/reports/agent_20260514_1850.md:23:- **Combo**: A2 confine det=-1 + A9 terzo incluso + A11 combo + ponte QxG continuo/discreto + tensione BOUNDARY GUE/Poisson + nodo fisico Anderson 3D.
tools/data/reports/agent_20260514_1850.md:24:- **Dipolo / punto-zero**: caos metallico / localizzazione critica. Punto-zero: Poisson span-matched, perche' ogni spettro torna leggibile solo se il null fisico-matematico resta esplicito.
tools/data/reports/agent_20260514_1850.md:26:- **Proto-ipotesi**: `component_state(SR,L1,triple_var)` e' interfaccia riusabile se un nuovo spettro fisico ordinato produce component states contro Poisson e contrasto diretto dichiarato senza cambiare definizioni dal registry.
tools/data/reports/agent_20260514_1850.md:33:- `why`: l'esperimento resta nel perimetro GOE/GUE/Poisson-Anderson indicato dalla direttiva e testa il passaggio tool da 1D/WD a un nuovo bordo fisico 3D.
tools/data/reports/agent_20260514_1850.md:42:- **Fonte fisica A**: classi spettrali Wigner-Dyson GOE/GUE gia' rafforzate nel report `20260514_1640`.
tools/data/reports/agent_20260514_1850.md:43:- **Significato fisico dichiarato**: GOE = simmetria reale/time-reversal; GUE = unitaria/no time-reversal; Poisson = livelli indipendenti/null span-matched.
tools/data/reports/agent_20260514_1850.md:50:- **Null**: Poisson span-matched con stesso numero di gap centrali utilizzabili.
tools/data/reports/agent_20260514_1850.md:77:- **Verificato**: entrambi i regimi Anderson 3D sono non-Poisson sulle tre osservabili focus.
tools/data/reports/agent_20260514_1850.md:110:Il tester diventa interfaccia riusabile su questo perimetro: legge un nuovo spettro fisico, conserva le definizioni del registry, produce stati componente e lascia trace. Il risultato non diventa legge fisica: Anderson 3D W4/W16.5 resta non-Poisson contro null, ma il bordo diretto separa solo `triple_var`; `SR,L1` restano insufficienti nel setup `L=6`.
tools/data/reports/agent_20260514_1850.md:114:- **Due radici**: simmetria Wigner-Dyson GOE/GUE / bordo disordinato Anderson 3D.
tools/data/reports/agent_20260514_1850.md:115:- **Singolare**: Poisson span-matched come punto-zero che rende confrontabili fisico A e fisico B.
tools/data/reports/agent_20260506_0625.md:1:# Agent Report — Perturbation Dimensionality Is Not Yet a Stable GUE Invariant
tools/data/reports/agent_20260506_0625.md:7:The 03:30 run reported that scale-selective perturbations reveal a second axis in GUE (effective rank 1.889, PC2=25.2%) but not in primes. The caveat was explicit: the GUE sample was short (`N=253`). This run tests whether that second axis survives independent GUE ensembles and whether it depends on the observable definitions hidden under the same labels (`SR`, `SR2`, `triple_var`).
tools/data/reports/agent_20260506_0625.md:14:- GUE long control: 6 independent replicates, each from 16 Hermitian matrices of size 180, after 10% edge trim; each replicate has 2,288 spacings;
tools/data/reports/agent_20260506_0625.md:15:- GUE short control: 6 independent one-matrix replicates of size 42, after edge trim; this approximates the fragile small-sample regime;
tools/data/reports/agent_20260506_0625.md:34:| GUE long, 6 reps mean | 2288 each | 1.305 ± 0.278 | 0.064 ± 0.066 | 0.877 ± 0.081 |
tools/data/reports/agent_20260506_0625.md:35:| GUE short, 6 reps mean | short | 1.683 ± 0.498 | 0.106 ± 0.080 | 0.567 ± 0.340 |
tools/data/reports/agent_20260506_0625.md:44:| GUE long, 6 reps mean | 2288 each | 1.381 ± 0.223 | 0.099 ± 0.069 | 0.874 ± 0.082 |
tools/data/reports/agent_20260506_0625.md:45:| GUE short, 6 reps mean | short | 2.013 ± 0.525 | 0.159 ± 0.087 | 0.746 ± 0.242 |
tools/data/reports/agent_20260506_0625.md:49:1. **The strong GUE second-axis claim does not survive as stated.** Under direct `scale_0330` observables, long independent GUE replicates give rank 1.381 ± 0.223 and PC2 9.9% ± 6.9%, not rank 1.889 and PC2 25.2%. The previous number is inside the fragile short-sample regime: GUE short controls have rank 2.013 ± 0.525 and PC2 15.9% ± 8.7%.
tools/data/reports/agent_20260506_0625.md:51:2. **Short GUE samples inflate apparent perturbation dimensionality.** In both observable sets, GUE short has higher rank and larger variance than GUE long. This does not prove the 03:30 axis was false in every configuration; it restricts it to a sample-size-sensitive observation unless a larger-replicate run recovers it.
tools/data/reports/agent_20260506_0625.md:57:5. **Poisson and shuffled-prime controls show multi-axis artifacts.** Poisson has rank 1.917/2.201 depending on observable set; prime shuffle control has rank 2.294/1.988. Multi-dimensional perturbation response by itself is not evidence of structured GUE-like boundary. It can arise from low structural signal plus noisy denominators in retention normalization.
tools/data/reports/agent_20260506_0625.md:60:**CONSTRAINT on META + BOUNDARY**: "GUE has a second perturbation axis" must be scoped to the exact sample length, generator, and observable definitions. In the larger independent-GUE perimeter tested here, the robust statement is weaker:
tools/data/reports/agent_20260506_0625.md:62:> Primes remain near one perturbation coordinate under both observable sets; GUE long replicates show only a weak second component; short GUE samples can inflate apparent rank; Poisson and shuffled controls can also appear multi-axis.
tools/data/reports/agent_20260506_0625.md:64:The boundary is still operator-dependent, but perturbation dimensionality is not yet a stable domain invariant. The next valid test is not another single GUE matrix; it is a replicate-and-size curve for effective rank vs number of spacings, with observable definitions versioned.
tools/data/reports/agent_20260506_0625.md:73:- **L4 edge cases**: the short-GUE effect is isolated as its own control, not averaged into long GUE.
tools/data/reports/agent_20260405_0919.md:7:> Direction: "Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo incluso"
tools/data/reports/agent_20260405_0919.md:11:Previous experiment showed primes drift from GUE toward Poisson with scale. **What is the functional form of this crossover?** The Brody distribution P(s) = (1+beta)*alpha*s^beta*exp(-alpha*s^{1+beta}) interpolates between Poisson (beta=0) and GOE (beta=1). What is beta(p) for primes?
tools/data/reports/agent_20260405_0919.md:36:| Extrapolated Poisson (beta=0) | **p ~ 10^13** |
tools/data/reports/agent_20260405_0919.md:42:2. **Primes are always above Cramer**: beta_Cramer ≈ 0 at all scales (pure Poisson, as expected). Primes have beta ≈ 0.23-0.42, solidly intermediate between Poisson and GOE.
tools/data/reports/agent_20260405_0919.md:44:3. **The crossover is NOT a phase transition**: there is no sharp boundary between GUE and Poisson regimes. The Brody beta decays smoothly and linearly in ln(p). The "boundary" is the entire range.
tools/data/reports/agent_20260405_0919.md:46:4. **Falsifiable prediction**: beta → 0 (Poisson) at p ~ 10^13. This is testable with segmented sieves.
tools/data/reports/agent_20260405_0919.md:58:The "third included" at the GUE/Poisson boundary is not a point — it's a **crossover function**. The primes' level repulsion parameter decays as 0.606 - 0.020*ln(p), bridging two universality classes without belonging to either. This is structurally consistent with:
tools/data/reports/agent_20260405_0919.md:59:- The D-ND framework: the boundary between two poles (GUE/Poisson) carries its own structure
tools/data/reports/agent_20260405_0919.md:61:- The crossover function itself is the "third" — neither GUE nor Poisson, but a specific interpolation
tools/data/reports/agent_20260405_0919.md:67:- **PREDICTION**: primes reach Poisson at p ~ 10^13
tools/data/notte_20260314_0330.md:4:  ising_2d_var_-0.1: r=0.9848721318684013, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260314_0330.md:5:  ising_2d_var_0.1: r=0.7920844227769658, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260314_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260314_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260314_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260314_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260314_0330.md:10:  brownian_motion_var_0.3: r=1.0088495575221237, spacing=Poisson-like (⟨r⟩=0.3818080058449986)
tools/data/notte_20260314_0330.md:11:  brownian_motion_var_0.5: r=1.032258064516129, spacing=Poisson-like (⟨r⟩=0.38274438984488357)
tools/data/notte_20260314_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260314_0330.md:13:  percolation_var_0.55: r=1.0434782608695652, spacing=Poisson-like (⟨r⟩=0.4194344351492288)
tools/data/notte_20260314_0330.md:14:  percolation_var_0.65: r=1.0, spacing=Poisson-like (⟨r⟩=0.44823047252566145)
tools/data/notte_20260308_0330.md:4:  ising_2d_var_-0.1: r=0.988516052701351, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260308_0330.md:5:  ising_2d_var_0.1: r=2.108462262337216, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260308_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260308_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260308_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260308_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260308_0330.md:10:  brownian_motion_var_0.3: r=0.9866666666666667, spacing=Poisson-like (⟨r⟩=0.3946125234002666)
tools/data/notte_20260308_0330.md:11:  brownian_motion_var_0.5: r=1.273726990198422, spacing=Poisson-like (⟨r⟩=0.38077490693533794)
tools/data/notte_20260308_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260308_0330.md:13:  percolation_var_0.55: r=0.816358024691358, spacing=Poisson-like (⟨r⟩=0.4515939359855778)
tools/data/notte_20260308_0330.md:14:  percolation_var_0.65: r=1.1276595744680853, spacing=Poisson-like (⟨r⟩=0.39640179313546897)
tools/exp_rp_boundary_size_stability_audit.py:114:    two_reader_rows = [
tools/exp_rp_boundary_size_stability_audit.py:119:    graph_only_rows = [
tools/exp_rp_boundary_size_stability_audit.py:134:            "two_reader_boundary_confirmed": len(two_reader_rows),
tools/exp_rp_boundary_size_stability_audit.py:135:            "two_reader_rows": two_reader_rows,
tools/exp_rp_boundary_size_stability_audit.py:136:            "graph_only_residue": len(graph_only_rows),
tools/exp_rp_boundary_size_stability_audit.py:137:            "graph_only_rows": graph_only_rows,
tools/exp_rp_boundary_size_stability_audit.py:150:    size_names = {entry["n"]: set(entry["summary"]["two_reader_rows"]) for entry in by_size}
tools/exp_rp_boundary_size_stability_audit.py:151:    all_two_reader = sorted(set.intersection(*size_names.values())) if size_names else []
tools/exp_rp_boundary_size_stability_audit.py:152:    any_two_reader = sorted(set.union(*size_names.values())) if size_names else []
tools/exp_rp_boundary_size_stability_audit.py:153:    intermittent_two_reader = [name for name in any_two_reader if name not in all_two_reader]
tools/exp_rp_boundary_size_stability_audit.py:183:                "two_reader_all_sizes": name in all_two_reader,
tools/exp_rp_boundary_size_stability_audit.py:184:                "two_reader_intermittent": name in intermittent_two_reader,
tools/exp_rp_boundary_size_stability_audit.py:215:            "observable": "two_reader_all_sizes from graph_bridge_frequency joined with Brody q, Wigner/Poisson mixture weight, SR and IPR",
tools/exp_rp_boundary_size_stability_audit.py:216:            "operator": "repeat the RP diagonal-plus-GUE Hamiltonian flow over sizes, seeds and kNN graph perturbations",
tools/exp_rp_boundary_size_stability_audit.py:217:            "generator": "H(lambda)=sqrt(1-lambda)D+sqrt(lambda)GUE, finite N size sweep",
tools/exp_rp_boundary_size_stability_audit.py:225:            "two_reader_all_sizes": len(all_two_reader),
tools/exp_rp_boundary_size_stability_audit.py:226:            "two_reader_all_size_rows": all_two_reader,
tools/exp_rp_boundary_size_stability_audit.py:227:            "two_reader_intermittent": len(intermittent_two_reader),
tools/exp_rp_boundary_size_stability_audit.py:228:            "two_reader_intermittent_rows": intermittent_two_reader,
tools/data/notte_20260306_0330.md:4:  ising_2d_var_-0.1: r=0.9618098462163317, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260306_0330.md:5:  ising_2d_var_0.1: r=0.9905364716330192, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260306_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260306_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260306_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260306_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260306_0330.md:10:  brownian_motion_var_0.3: r=1.0, spacing=Poisson-like (⟨r⟩=0.40716210665181113)
tools/data/notte_20260306_0330.md:11:  brownian_motion_var_0.5: r=1.1993212669683257, spacing=Poisson-like (⟨r⟩=0.39848592435784913)
tools/data/notte_20260306_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260306_0330.md:13:  percolation_var_0.55: r=1.0, spacing=Poisson-like (⟨r⟩=0.42469524388230473)
tools/data/notte_20260306_0330.md:14:  percolation_var_0.65: r=1.0, spacing=Poisson-like (⟨r⟩=0.4501286869364695)
tools/exp_cross_observable_consistency.py:17:5. GUE control: synthetic GUE eigenvalues (should show β_r ≈ β_Σ ≈ 1)
tools/exp_cross_observable_consistency.py:21:  contributes more at large scales, pulling Σ² toward Poisson faster
tools/exp_cross_observable_consistency.py:107:# ── GUE control (small ensemble) ────────────────────────────────────
tools/exp_cross_observable_consistency.py:109:    """Generate GUE eigenvalue gaps."""
tools/exp_cross_observable_consistency.py:115:        # Unfold: for GUE bulk, spacing ~ semicircle
tools/exp_cross_observable_consistency.py:169:# ── Measure GUE (positive control) ──────────────────────────────────
tools/exp_cross_observable_consistency.py:170:print("\n=== GUE (positive control) ===")
tools/exp_cross_observable_consistency.py:176:# For GUE, unfold eigenvalues directly
tools/exp_cross_observable_consistency.py:215:# GUE
tools/exp_cross_observable_consistency.py:217:print(f"{'GUE':<12} {beta_r_gue:>6.3f} | " + " | ".join(f"{v:>9}" for v in vals_gue))
tools/exp_cross_observable_consistency.py:227:print(f"  GUE:     {disagree_gue:.3f}")
tools/exp_cross_observable_consistency.py:232:print(f"  (positive = more GUE-like at short range, more Poisson-like at long range)")
tools/data/notte_20260320_0330.md:4:  ising_2d_var_-0.1: r=0.9811019213055721, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260320_0330.md:5:  ising_2d_var_0.1: r=0.9951420369672802, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260320_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260320_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260320_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260320_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260320_0330.md:10:  brownian_motion_var_0.3: r=1.0149253731343284, spacing=Poisson-like (⟨r⟩=0.3935592399692033)
tools/data/notte_20260320_0330.md:11:  brownian_motion_var_0.5: r=1.0, spacing=Poisson-like (⟨r⟩=0.39857620187545184)
tools/data/notte_20260320_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260320_0330.md:13:  percolation_var_0.55: r=0.9655172413793103, spacing=Poisson-like (⟨r⟩=0.47237797827873335)
tools/data/notte_20260320_0330.md:14:  percolation_var_0.65: r=0.9642857142857143, spacing=Poisson-like (⟨r⟩=0.4251485991244417)
tools/data/notte_20260304_0330.md:4:  ising_2d_var_-0.1: r=0.9910971684487101, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260304_0330.md:5:  ising_2d_var_0.1: r=1.0010094841749304, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260304_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260304_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260304_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260304_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260304_0330.md:10:  brownian_motion_var_0.3: r=1.3830367734282325, spacing=Poisson-like (⟨r⟩=0.39011449867034076)
tools/data/notte_20260304_0330.md:11:  brownian_motion_var_0.5: r=0.09164859002169198, spacing=Poisson-like (⟨r⟩=0.3818899511281583)
tools/data/notte_20260304_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260304_0330.md:13:  percolation_var_0.55: r=1.0, spacing=Poisson-like (⟨r⟩=0.47711478860490236)
tools/data/notte_20260304_0330.md:14:  percolation_var_0.65: r=0.9992156862745099, spacing=Poisson-like (⟨r⟩=0.45789876672274327)
tools/data/notte_20260331_0330.md:4:  Direzione: Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo in
tools/data/notte_20260331_0330.md:5:  [confine_inesplorato] BOUNDARY: 8 domini GUE, 5 Poisson — il confine è il terzo incluso oper
tools/data/notte_20260331_0330.md:8:  logistica_biforcazione_cp_3.57: r=1.0, spacing=Poisson-like [VINCOLO] [NULL:non-disc]
tools/data/notte_20260331_0330.md:10:  logistica_biforcazione_cp_3.765: r=1.0, spacing=Poisson-like [VINCOLO] [NULL:non-disc]
tools/data/notte_20260331_0330.md:12:  collatz_cp: r=0.8740629685157423, spacing=GUE-like [conferma]
tools/data/notte_20260331_0330.md:14:  ising_2d_cp_-0.47801326011314715: r=0.8591691474753246, spacing=GUE-like [conferma]
tools/data/notte_20260331_0330.md:15:  ising_2d_cp_0.30696337890028147: r=0.835757106387861, spacing=GUE-like [conferma]
tools/data/notte_20260331_0330.md:16:  brownian_motion_cp_0.14336009606062766: r=1.0, spacing=Poisson-like [VINCOLO] [NULL:non-disc]
tools/data/notte_20260331_0330.md:17:  brownian_motion_cp_0.9343416664453827: r=1.2000000000000002, spacing=Poisson-like [VINCOLO] [NULL:non-disc]
tools/data/notte_20260331_0330.md:18:  percolation_cp_0.3346796213807732: r=0.8522727272727272, spacing=GUE-like [conferma]
tools/data/notte_20260331_0330.md:19:  percolation_cp_0.8762349717424142: r=1.0, spacing=GUE-like [conferma]
tools/data/notte_20260331_0330.md:20:  cellular_automata_cp_90: r=0.6458196181698486, spacing=GUE-like [conferma]
tools/data/notte_20260331_0330.md:32:  Campo dopo Fase 0: 6 GUE / 4 Poisson
tools/data/notte_20260331_0330.md:36:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094) [NULL:non-disc]
tools/data/notte_20260331_0330.md:37:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260331_0330.md:38:  ising_2d_var_0.1: r=0.9990577358068202, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260331_0330.md:39:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286) [NULL:non-disc]
tools/data/notte_20260331_0330.md:89:  GUE: 8 | Poisson: 6 | Vincoli: 13 | Anomalie: 11
tools/data/notte_20260331_0330.md:91:  Domini GUE: collatz_cp, ising_2d_cp_-0.47801326011314715, ising_2d_cp_0.30696337890028147, percolation_cp_0.3346796213807732, percolation_cp_0.8762349717424142, cellular_automata_cp_90, cellular_automata_var_30, ising_2d_var_0.1
tools/data/notte_20260331_0330.md:92:  Domini Poisson: logistica_biforcazione_cp_3.57, logistica_biforcazione_cp_3.765, brownian_motion_cp_0.14336009606062766, brownian_motion_cp_0.9343416664453827, logistica_biforcazione_var_3.57, coupled_oscillators_var_50
tools/data/notte_20260324_0330.md:5:  collatz_cp: r=0.8359033608638953, spacing=GUE-like [conferma]
tools/data/notte_20260324_0330.md:8:  ising_2d_cp_-0.3165707122601713: r=0.9277519726198978, spacing=GUE-like [conferma]
tools/data/notte_20260324_0330.md:9:  ising_2d_cp_0.4639558877000029: r=0.981531507124999, spacing=GUE-like [conferma]
tools/data/notte_20260324_0330.md:10:  brownian_motion_cp_0.18979322168052606: r=1.0, spacing=Poisson-like [conferma]
tools/data/notte_20260324_0330.md:11:  brownian_motion_cp_0.935077879517312: r=1.0, spacing=GUE-like [conferma]
tools/data/notte_20260324_0330.md:12:  percolation_cp_0.3048827772508223: r=0.9223529411764706, spacing=GUE-like [conferma]
tools/data/notte_20260324_0330.md:13:  percolation_cp_0.8107442354652781: r=1.0, spacing=GUE-like [conferma]
tools/data/notte_20260324_0330.md:14:  cellular_automata_cp_150: r=0.7452006980802792, spacing=GUE-like [conferma]
tools/data/notte_20260324_0330.md:17:  brownian_motion_var_0.3: r=1.0, spacing=Poisson-like (⟨r⟩=0.39929342988791194)
tools/data/notte_20260324_0330.md:18:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260324_0330.md:19:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260324_0330.md:20:  brownian_motion_var_0.5: r=1.0, spacing=Poisson-like (⟨r⟩=0.3880568632202867)
tools/data/evolution/evolution_20260514_1612.md:3:Il report osserva il passo come traiettoria `prime-minus-mod6 -> trasduttore -> GUE/Anderson`, individua l’attrito nel tier di evidenza non dichiarato prima della misura, e mette in consecutio il prossimo perimetro: `W=6`, `evidence_tier` esplicito, autopsy dalla trace lab-native.
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:7:classical reader uses spacing/Brody/Wigner-Poisson diagnostics and the graph
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:339:    two_reader_rows = [
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:344:    graph_only_rows = [
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:360:            "two_reader_boundary_confirmed": len(two_reader_rows),
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:361:            "two_reader_rows": two_reader_rows,
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:362:            "graph_only_residue": len(graph_only_rows),
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:363:            "graph_only_rows": graph_only_rows,
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:376:    size_names = {entry["L"]: set(entry["summary"]["two_reader_rows"]) for entry in by_size}
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:377:    all_two_reader = sorted(set.intersection(*size_names.values())) if size_names else []
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:378:    any_two_reader = sorted(set.union(*size_names.values())) if size_names else []
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:379:    intermittent_two_reader = [name for name in any_two_reader if name not in all_two_reader]
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:412:                "two_reader_all_sizes": name in all_two_reader,
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:413:                "two_reader_intermittent": name in intermittent_two_reader,
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:420:        "experiment": "anderson3d_mobility_edge_two_reader_audit",
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:446:            "observable": "two_reader_all_sizes from graph_bridge_frequency joined with adjacent ratio, Brody q, Wigner/Poisson mixture weight, IPR and participation entropy",
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:456:            "two_reader_all_sizes": len(all_two_reader),
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:457:            "two_reader_all_size_rows": all_two_reader,
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:458:            "two_reader_intermittent": len(intermittent_two_reader),
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:459:            "two_reader_intermittent_rows": intermittent_two_reader,
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:460:            "graph_only_residue_by_size": {str(entry["L"]): entry["summary"]["graph_only_residue"] for entry in by_size},
tools/exp_anderson3d_mobility_edge_two_reader_audit.py:475:    parser.add_argument("--out", default="tools/data/anderson3d_mobility_edge_two_reader_audit_20260515_1947.json")
tools/data/evolution/evolution_20260506_0330.md:3:Sintesi: passo netto, autologico (ha falsificato il proprio risultato precedente). Attrito minimo (GUE piccolo per default ereditato, autopsy con testo stale). Possibilità forte: la dimensionalità perturbativa come nuovo invariante di dominio, incrociabile con la struttura modulare.
tools/exp_poisson_convergence.py:3:exp_poisson_convergence.py — Do beta, <r>_excess, and acf1 predict the same Poisson scale?
tools/exp_poisson_convergence.py:5:Three independent observables drift toward Poisson at large prime scale:
tools/exp_poisson_convergence.py:7:  - <r> excess over Cramer -> 0  (i.e. <r> -> <r>_Poisson ~ 0.386)
tools/exp_poisson_convergence.py:10:Question: do all three extrapolate to Poisson at the SAME critical scale p*?
tools/exp_poisson_convergence.py:193:    """Fit linear trends in ln(p) and extrapolate to Poisson."""
tools/exp_poisson_convergence.py:196:    # Poisson targets
tools/exp_poisson_convergence.py:263:    print("CONVERGENCE SUMMARY: Three roads to Poisson")
tools/exp_poisson_convergence.py:271:        print(f"    Target (Poisson): {f['target']:.4f}")
tools/exp_poisson_convergence.py:273:            print(f"    Extrapolated Poisson at: ln(p*) = {f['ln_p_star']:.1f}  =>  p* ~ 10^{f['ln_p_star']/np.log(10):.1f}")
tools/exp_poisson_convergence.py:275:            print(f"    No convergence toward Poisson (slope wrong sign or flat)")
tools/exp_poisson_convergence.py:295:            print(f"  => CONSISTENT: all observables predict Poisson at similar scale")
tools/exp_poisson_convergence.py:305:    parser = argparse.ArgumentParser(description='Poisson convergence: do beta, <r>, acf1 agree?')
tools/exp_poisson_convergence.py:320:        'question': 'Do beta, <r>, and acf1 predict the same Poisson scale?',
tools/data/evolution/evolution_20260425_0330.md:1:Report scritto. Sintesi: passo netto, nessun attrito, consecutio rispettata. Il run ha separato due canali di memoria Markov nei primi (algebrico 140x > statistico) e mostrato che l'ordering-GUE ne ha solo uno. Tre possibilità emergenti: saturazione come terzo asse, eterogeneità Poisson come confine del r-test, e falsificazione attiva dell'assenza di canale algebrico nell'ordering-GUE.
tools/dnd_spettro_zeta.py:11:  3. La statistica degli spacing deve mostrare repulsione a livelli (come GUE per ζ)
tools/dnd_spettro_zeta.py:20:  Parte 3: Unfolding e statistica spacing P(s) — confronto GOE/GUE
tools/dnd_spettro_zeta.py:221:    """Generate GUE nearest-neighbor spacings via rejection sampling."""
tools/dnd_spettro_zeta.py:226:        # GUE Wigner surmise: P(s) = (32/π²)s²·exp(-4s²/π)
tools/dnd_spettro_zeta.py:228:        # Envelope: C·exp(-s) where C = max of P_GUE(s)·exp(s)
tools/dnd_spettro_zeta.py:282:        print(f"      vs GUE:     KS={ks_gue:.4f}, p={p_gue:.4f}")
tools/dnd_spettro_zeta.py:283:        print(f"      vs Poisson: KS={ks_poi:.4f}, p={p_poi:.4f}")
tools/dnd_spettro_zeta.py:288:            'vs_GUE': {'KS': float(ks_gue), 'p': float(p_gue)},
tools/dnd_spettro_zeta.py:289:            'vs_Poisson': {'KS': float(ks_poi), 'p': float(p_poi)},
tools/dnd_spettro_zeta.py:306:    print(f"    Poisson:  1.000")
tools/dnd_spettro_zeta.py:308:    print(f"    GUE:     ~0.178")
tools/dnd_spettro_zeta.py:318:        'GUE_ref': 0.178,
tools/dnd_spettro_zeta.py:603:    print(f"      Poisson: ac(1) ≈  0.00")
tools/dnd_spettro_zeta.py:605:    print(f"      GUE:     ac(1) ≈ -0.50")
tools/dnd_spettro_zeta.py:747:  │     Var(s) D-ND:     {var_dnd:.4f}  (GOE≈0.286, GUE≈0.178)       │
tools/dnd_spettro_zeta.py:749:  │     ac(1) D-ND:     {f'{ac1_dnd:+.4f}' if ac1_dnd is not None else '  N/A':>7s}  (GOE≈-0.27, GUE≈-0.50)      │
tools/dnd_spettro_zeta.py:766:            stat_class = "GUE"
tools/dnd_spettro_zeta.py:774:        print(f"    Gli zeri di ζ → GUE")
tools/dnd_spettro_zeta.py:776:        print(f"      Per passare da GOE a GUE serve rompere T-simmetria.")
tools/dnd_spettro_zeta.py:780:        print(f"      V(r) + fase det=-1 potrebbe dare GUE.")
tools/dnd_spettro_zeta.py:781:    elif stat_class == "GUE":
tools/dnd_spettro_zeta.py:782:        print(f"    Sorpresa: H reale mostra statistiche GUE-like")
tools/dnd_spettro_zeta.py:784:        print(f"      generare pseudo-GUE a queste energie")
tools/dnd_spettro_zeta.py:799:    print(f"      Statistica: {stat_class} (ζ = GUE)")
tools/dnd_spettro_zeta.py:855:        verdict = "MODERATA: struttura condivisa, serve la matrice completa per GUE"
tools/dnd_spettro_zeta.py:868:        print(f"       → Se questo dà GUE, la connessione è completa")
tools/exp_brody_flow.py:5:Measures how the Brody beta (interpolating Poisson beta=0 to GUE beta=1)
tools/data/evolution/evolution_20260515_1855.md:3:Il report osserva il passo 18:55 come rientro corretto sul seme BOUNDARY 8 GUE / 5 Poisson, individua l’attrito nel contratto pre-esperimento (`baseline_nota`, `CE_audit`, `step_trace`) e lascia una consecutio concreta: audit dei nodi ponte contro crossover GUE-Poisson classici.
tools/exp_denominator_gate_transfer_matrix.py:8:GUE/Poisson BOUNDARY perimeter. Each perimeter has a coherent endpoint and an
tools/data/evolution/evolution_20260508_2019.md:3:Ho mantenuto il report breve e centrato sul passo: traiettoria reader/generator, attrito da autopsy senza jsonl, nodo regressivo nel contratto di avvio e nella condizione di uscita dal banco phi, consecutio verso un `reader_generator_gate` trasferito al confine GUE/Poisson.
tools/exp_3d_boundary_layers.py:14:Tests on: primes, GUE, Poisson baseline.
tools/exp_3d_boundary_layers.py:37:    """Generate GUE eigenvalue gaps."""
tools/exp_3d_boundary_layers.py:179:    # --- GUE ---
tools/exp_3d_boundary_layers.py:180:    print("\n--- GUE ---")
tools/exp_3d_boundary_layers.py:187:        gue_g, alphas, args.n_trials, rng, "GUE"
tools/exp_3d_boundary_layers.py:190:    # --- Poisson (exponential gaps, iid) ---
tools/exp_3d_boundary_layers.py:194:        poisson_gaps, alphas, args.n_trials, rng, "Poisson"
tools/exp_3d_boundary_layers.py:255:    print(f"Layer separation Δα: Primes={prime_sep:+.3f}, GUE={gue_sep:+.3f}, Poisson={pois_sep:+.3f}")
tools/dnd_risultante.py:33:    The algebra det=-1 → disc=5 → Q(√5) → L(s,χ₅) → GUE statistics
tools/dnd_risultante.py:35:    the deviation from pure GUE. The risultante dynamics produce the same
tools/dnd_risultante.py:548:        Piano 11: det=-1 → disc=5 → Q(√5) → L(s,χ₅) → GUE
tools/dnd_risultante.py:573:                "statistics": "GUE (Katz-Sarnak)"
tools/exp_boundary_blank_thin_support_audit.py:175:        "label_policy": "Does not use source_domain_type or GUE/Poisson label as an operator.",
tools/exp_excess_scaling.py:124:# Additional: check GUE vs Poisson classification
tools/exp_excess_scaling.py:125:# GUE: <r> ~ 0.5307, Poisson: <r> ~ 0.3863
tools/exp_excess_scaling.py:126:r_GUE = 0.5307
tools/exp_excess_scaling.py:127:r_Poisson = 0.3863
tools/exp_excess_scaling.py:129:print(f"\n  Reference: GUE <r> = {r_GUE}, Poisson <r> = {r_Poisson}")
tools/exp_excess_scaling.py:133:# Does <r> move toward Poisson at large scale?
tools/exp_excess_scaling.py:135:    print(f"  <r> DECREASES with scale: moving toward Poisson")
tools/exp_excess_scaling.py:137:    print(f"  <r> INCREASES with scale: moving toward/staying GUE")
tools/exp_boundary_transition_taxonomy_13rows.py:180:            "not_tested": "new beta grid, new null surrogates, V_c fit, source GUE/Poisson label validity",
tools/exp_boundary_mixture_gate.py:5:Reusable META/BOUNDARY audit for the GUE-Poisson boundary.
tools/exp_boundary_mixture_gate.py:8:    Does the GUE/Poisson boundary remain a clean two-class split after the
tools/exp_boundary_mixture_gate.py:13:It builds synthetic mixtures by replacing a fraction beta of unfolded GUE
tools/exp_boundary_mixture_gate.py:14:spacings with Poisson spacings, then measures:
tools/exp_boundary_mixture_gate.py:19:- ambiguity of each beta layer relative to pure GUE and pure Poisson centroids.
tools/exp_boundary_mixture_gate.py:41:    """Generate unfolded GUE spacings by concatenating independent matrices."""
tools/exp_boundary_mixture_gate.py:59:    """Return a beta Poisson / (1-beta) GUE spacing sequence with mean spacing 1."""
tools/exp_boundary_mixture_gate.py:224:        "question": "Is the GUE-Poisson mixed layer cleanly classifiable after denominator gating?",
tools/exp_boundary_blank_null_audit.py:27:R_GUE = 0.5307
tools/exp_boundary_blank_null_audit.py:66:    return "GUE" if abs(value - R_GUE) < abs(value - R_POISSON) else "Poisson"
tools/exp_boundary_blank_null_audit.py:151:            "R_GUE": R_GUE,
tools/exp_boundary_blank_null_audit.py:152:            "R_Poisson": R_POISSON,
tools/exp_selective_layer_decoupling.py:265:        'GUE': gen_gue_spacings(args.N, rng),
tools/exp_selective_layer_decoupling.py:266:        'Poisson': gen_poisson_spacings(args.N, rng),
tools/exp_cross_domain_dipolar_direction.py:6:Primes have theta=-111 deg in the (SR, L1) plane. GUE has theta=-97 deg.
tools/exp_cross_domain_dipolar_direction.py:7:Do all GUE-like domains share -97, or does each have its own direction?
tools/exp_cross_domain_dipolar_direction.py:8:Do all Poisson-like domains lack direction?
tools/exp_cross_domain_dipolar_direction.py:11:  GUE-like: GUE spacings, GOE spacings, CUE spacings, Riemann zeta zeros (via GUE proxy)
tools/exp_cross_domain_dipolar_direction.py:12:  Poisson-like: exponential iid, uniform iid, geometric iid
tools/exp_cross_domain_dipolar_direction.py:81:def gen_rmt_spacings(N_mat, ensemble='GUE', rng=None):
tools/exp_cross_domain_dipolar_direction.py:85:    if ensemble == 'GUE':
tools/exp_cross_domain_dipolar_direction.py:133:    """Poisson process: exponential iid spacings."""
tools/exp_cross_domain_dipolar_direction.py:177:    for ens_name in ['GUE', 'GOE', 'CUE']:
tools/exp_cross_domain_dipolar_direction.py:229:    # === 6-8. Poisson-like domains ===
tools/exp_cross_domain_dipolar_direction.py:233:        print(f"\n--- {name.upper()} (iid, Poisson-class) ---")
tools/exp_cross_domain_dipolar_direction.py:246:            'class': 'Poisson',
tools/exp_cross_domain_dipolar_direction.py:275:    rmt_names = ['GUE', 'GOE', 'CUE']
tools/exp_cross_domain_dipolar_direction.py:311:    # Poisson direction scatter
tools/exp_cross_domain_dipolar_direction.py:312:    print("\n  Poisson direction scatter (std of theta):")
tools/dnd_trace_bridge.py:136:        "label_GUE": "<r>=0.5996, Var=0.178",
tools/dnd_trace_bridge.py:147:    closer = "GUE" if d_gue < d_poi else "Poisson"
tools/dnd_trace_bridge.py:347:    print("\nDomanda: la mappa D-ND trasforma la struttura GUE degli zeri?")
tools/dnd_trace_bridge.py:348:    print("Se K(zeta) ≈ GUE e K(random) ≈ Poisson → la mappa PRESERVA la struttura")
tools/dnd_trace_bridge.py:350:    print("Se K(zeta) ≠ GUE e ≠ K(random) → la mappa TRASFORMA la struttura")
tools/dnd_trace_bridge.py:357:        print(f"  {key:40s} <r>={val['mean_r']:.4f} {'GUE' if d_gue < d_poi else 'Poi'} {marker}")
tools/dnd_autoricerca.py:221:        # GUE spacing distribution (approssimazione Wigner)
tools/dnd_autoricerca.py:228:            'nota': 'Approssimazione GUE (mpmath non disponibile)',
tools/dnd_autoricerca.py:270:    """Autovalori di matrici casuali GUE — la connessione con Riemann."""
tools/dnd_autoricerca.py:272:    # GUE: matrice Hermitiana casuale
tools/dnd_autoricerca.py:284:        'ensemble': 'GUE',
tools/dnd_autoricerca.py:286:        'nota': 'Spaziatura autovalori matrice GUE 200x200'
tools/dnd_autoricerca.py:478:        # - is_spacings=True: esponenziale (Poisson = livelli non correlati)
tools/dnd_autoricerca.py:482:            # Null per spacings: esponenziale normalizzata (Poisson)
tools/dnd_autoricerca.py:523:                    null_spacings.append('GUE-like' if abs(mean_r - 0.5996) < abs(mean_r - 0.3863) else 'Poisson-like')
tools/dnd_autoricerca.py:654:                    'tipo': 'GUE-like' if abs(mean_r - 0.5996) < abs(mean_r - 0.3863) else 'Poisson-like',
tools/dnd_autoricerca.py:827:    # Pattern 2: spacing GUE-like
tools/dnd_autoricerca.py:829:    if spacing.get('tipo') == 'GUE-like' and spacing.get('gue_dist', 1) < 0.1:
tools/dnd_autoricerca.py:834:            'nota': f'Spacing GUE-like (⟨r⟩={spacing["mean_r"]:.4f})'
tools/dnd_autoricerca.py:1152:    gue_domains = [e['dominio'] for e in journal if e.get('spacing') == 'GUE-like']
tools/dnd_autoricerca.py:1153:    poisson_domains = [e['dominio'] for e in journal if e.get('spacing') == 'Poisson-like']
tools/dnd_autoricerca.py:1154:    print(f"    GUE-like: {gue_domains}")
tools/dnd_autoricerca.py:1155:    print(f"    Poisson-like: {poisson_domains}")
tools/dnd_autoricerca.py:1157:    # Il RAPPORTO #GUE/#Poisson è vicino a φ?
tools/dnd_autoricerca.py:1160:        print(f"    #GUE/#Poisson = {ratio_cluster:.4f} "
tools/dnd_autoricerca.py:1329:    # 2. Differenza spacing tra coppie GUE-Poisson
tools/dnd_autoricerca.py:1330:    print(f"\n  2. Coppie GUE-Poisson:")
tools/dnd_autoricerca.py:1331:    gue = [e for e in reali if e.get('spacing') == 'GUE-like' and e.get('spacing_r')]
tools/dnd_autoricerca.py:1332:    poi = [e for e in reali if e.get('spacing') == 'Poisson-like' and e.get('spacing_r')]
tools/dnd_autoricerca.py:1343:    # 3. I r_diretto dei domini GUE vs Poisson come due insiemi
tools/dnd_autoricerca.py:1351:        print(f"    GUE mean r_diretto: {mean_gue:.4f}")
tools/dnd_autoricerca.py:1352:        print(f"    Poisson mean r_diretto: {mean_poi:.4f}")
tools/dnd_autoricerca.py:1353:        print(f"    Ratio GUE/Poisson: {ratio:.4f}")
tools/dnd_autoricerca.py:1539:        # BOUNDARY: confine GUE/Poisson — prosegui esplorando il confine
tools/dnd_autoricerca.py:1718:        'gue_domains': [],    # domini con spacing GUE
tools/dnd_autoricerca.py:1719:        'poisson_domains': [],# domini con spacing Poisson
tools/dnd_autoricerca.py:1725:        if entry.get('spacing') and 'GUE' in str(entry['spacing']):
tools/dnd_autoricerca.py:1727:        elif entry.get('spacing') and 'Poisson' in str(entry['spacing']):
tools/dnd_autoricerca.py:1881:        # I domini GUE con anomalie vengono testati sui domini Poisson e viceversa
tools/dnd_autoricerca.py:1890:        print(f"\n  Campo vivo dopo Fase 0: {n_gue} GUE / {n_poi} Poisson")
tools/dnd_autoricerca.py:1891:        report_lines.append(f"\n  Campo dopo Fase 0: {n_gue} GUE / {n_poi} Poisson")
tools/dnd_autoricerca.py:2017:    report_lines.append(f"  GUE: {n_gue} | Poisson: {n_poi} | Vincoli: {n_vinc} | Anomalie: {n_anom}")
tools/dnd_autoricerca.py:2019:    report_lines.append(f"  Domini GUE: {', '.join(campo['gue_domains'][:10])}")
tools/dnd_autoricerca.py:2020:    report_lines.append(f"  Domini Poisson: {', '.join(campo['poisson_domains'][:10])}")
tools/dnd_autoricerca.py:2024:    print(f"\n  Campo vivo finale: {n_gue} GUE / {n_poi} Poisson / {n_vinc} vincoli / {n_anom} anomalie / ⟨r⟩={avg_r:.4f}")
tools/dnd_autoricerca.py:2120:    # 3. Segnali anomali (spacing GUE, rapporto aureo diretto, etc.)
tools/dnd_piano11.py:3:Piano 11 — det=-1, Q(√5), and the GUE Bridge
tools/dnd_piano11.py:5:Core thesis (revised): The 1D potential V(r) gives Berry-Keating form but NOT GUE.
tools/dnd_piano11.py:12:Chain: M → Q(√5) → disc=5 → χ₅ → L(s,χ₅) → zeros on Re(s)=1/2 → GUE
tools/dnd_piano11.py:16:P2: Verify GUE spacing statistics of L(s, χ₅) zeros
tools/dnd_piano11.py:18:P4: The algebraic chain: det=-1 → disc=5 → Q(√5) → GUE (proven)
tools/dnd_piano11.py:22:- Montgomery (1973): pair correlation of ζ zeros ~ GUE
tools/dnd_piano11.py:24:- Rubinstein (2001): GUE for families of L-functions
tools/dnd_piano11.py:150:    """GUE (β=2) Wigner surmise CDF."""
tools/dnd_piano11.py:158:    """Poisson CDF."""
tools/dnd_piano11.py:162:    """GUE nearest-neighbor PDF."""
tools/dnd_piano11.py:192:# ─── P2: GUE statistics of L(s, χ₅) zeros ──────────────────────────────
tools/dnd_piano11.py:196:    Compute normalized nearest-neighbor spacings and test against GUE/GOE/Poisson.
tools/dnd_piano11.py:205:    ks_gue = ks_test_against(norm_spacings, gue_cdf, "GUE")
tools/dnd_piano11.py:207:    ks_poi = ks_test_against(norm_spacings, poisson_cdf, "Poisson")
tools/dnd_piano11.py:219:        "GUE_variance_ref": 0.178,
tools/dnd_piano11.py:248:    # Both vs GUE
tools/dnd_piano11.py:249:    ks_zeta_gue = ks_test_against(zeta_norm, gue_cdf, "GUE")
tools/dnd_piano11.py:250:    ks_L_gue = ks_test_against(L_norm, gue_cdf, "GUE")
tools/dnd_piano11.py:277:        "variance_GUE_ref": 0.178,
tools/dnd_piano11.py:293:# ─── P4: Algebraic chain M → Q(√5) → L(s,χ₅) → GUE ────────────────────
tools/dnd_piano11.py:297:    Prove the algebraic chain connecting D-ND matrix to GUE.
tools/dnd_piano11.py:330:    # This is the SAME type of symmetry as ζ → same universality class (GUE)
tools/dnd_piano11.py:341:    # → L(s, χ_{-3}), which ALSO has GUE zeros but different arithmetic.
tools/dnd_piano11.py:409:                "to": "Zeros follow GUE statistics",
tools/dnd_piano11.py:412:                    "theorem": "All primitive L-functions have GUE zero statistics (conjectured, overwhelming numerical evidence)",
tools/dnd_piano11.py:428:            "ζ_{Q(√5)} = ζ·L(s,χ₅). By the Katz-Sarnak philosophy, L(s,χ₅) has GUE zero statistics. "
tools/dnd_piano11.py:429:            "Therefore: D-ND matrix → Q(√5) → L(s,χ₅) → GUE. "
tools/dnd_piano11.py:477:                "produces": "GUE spacing statistics",
tools/dnd_piano11.py:486:                "N_fluct comes from det(M)=-1 (the 2D anti-symplectic structure, giving GUE). "
tools/dnd_piano11.py:494:                "spectral": "GUE universality class (from L(s,χ₅))"
tools/dnd_piano11.py:526:    print("Piano 11 — det=-1, Q(√5), and the GUE Bridge\n")
tools/dnd_piano11.py:540:        print(f"  Variance: {p2['variance']:.4f} (GUE ref: 0.178, GOE ref: 0.286)")
tools/dnd_piano11.py:541:        print(f"  KS vs GUE: {p2['ks_gue']['KS']:.4f} (p={p2['ks_gue']['p']:.4f})")
tools/dnd_piano11.py:554:            print(f"  L(s,χ₅) vs GUE: KS={p3['ks_L_vs_gue']['KS']:.4f}")
tools/dnd_piano11.py:555:            print(f"  ζ vs GUE: KS={p3['ks_zeta_vs_gue']['KS']:.4f}")
tools/dnd_piano11.py:562:    print("P4: Algebraic chain M → Q(√5) → L(s,χ₅) → GUE...")
tools/dnd_piano11.py:581:    if has_numerical and p2_data["best_fit"] == "GUE":
tools/dnd_piano11.py:582:        verdict = "CONFERMATO: L(s,χ₅) → GUE. La catena D-ND → Q(√5) → GUE è completa."
tools/dnd_piano11.py:585:        verdict = "PARZIALE: L(s,χ₅) → GOE. Convergenza a GUE attesa per più zeri."
tools/dnd_piano11.py:590:            "STRUTTURALE: La catena algebrica M → Q(√5) → L(s,χ₅) → GUE è dimostrata. "
tools/dnd_piano11.py:598:    results["piano"] = "piano11-Q-sqrt5-GUE"
tools/dnd_piano11.py:603:        "thesis": "det(M)=-1 connects D-ND to GUE through the number field Q(√5)",
tools/dnd_piano11.py:604:        "chain": "M → char poly λ²-λ-1 → disc=5 → Q(√5) → ζ_{Q(√5)} = ζ·L(s,χ₅) → GUE",
tools/dnd_piano11.py:612:            "Piano 11 shows det=-1 gives the FLUCTUATIONS (GUE via Q(√5)). "
tools/exp_boundary_denominator_prescan.py:6:V_c. The unit under test is not the GUE/Poisson label. The unit is the
tools/exp_boundary_denominator_prescan.py:10:- tools/data/autoricerca_journal.json: base 13-domain GUE/Poisson perimeter.
tools/exp_boundary_denominator_prescan.py:59:    if spacing == "GUE-like":
tools/exp_boundary_denominator_prescan.py:60:        return "GUE"
tools/exp_boundary_denominator_prescan.py:61:    if spacing == "Poisson-like":
tools/exp_boundary_denominator_prescan.py:62:        return "Poisson"
tools/exp_boundary_denominator_prescan.py:79:        "GUE-like",
tools/exp_boundary_denominator_prescan.py:80:        "Poisson-like",
tools/exp_boundary_denominator_prescan.py:195:        "question": "Does denominator_state transfer beyond V_c on the 8 GUE / 5 Poisson boundary perimeter?",
tools/exp_boundary_denominator_prescan.py:196:        "perimeter": "base autoricerca cycles 1..13: 8 GUE-like, 5 Poisson-like",
tools/dnd_lab.py:10:  1. Wigner surmise (GOE/GUE) — distribuzione esatta level spacing
tools/dnd_lab.py:14:  5. Berry-Tabor — integrabile→Poisson vs caotico→GUE
tools/dnd_lab.py:49:    DIMOSTRATO: Level spacing distribution per GOE e GUE.
tools/dnd_lab.py:51:    GUE: P(s) = (32/π²) s² exp(-4s²/π)
tools/dnd_lab.py:58:    print("BANCO 1: Wigner Surmise (GOE e GUE esatti)")
tools/dnd_lab.py:63:    for ensemble, name in [('GOE', 'GOE'), ('GUE', 'GUE')]:
tools/dnd_lab.py:78:            # GUE: P(s) = (32/π²) s² exp(-4s²/π)
tools/dnd_lab.py:661:    spettrale. Il punto critico e' dove <r> ≈ 0.50 (ne' GUE ne' Poisson).
tools/dnd_lab.py:690:        print(f"  Transizione: V<{critical_V} → GUE/metallico, V>{critical_V} → Poisson/localizzato")
tools/dnd_lab.py:1426:        'GUE': 'random_matrix_gue',
tools/dnd_lab.py:1476:        elif 'WIGNER' in tid or 'GUE' in tid:
tools/dnd_lab.py:1652:    # Trova dove <r> attraversa r_target (dall'alto: GUE→critico→Poisson)
tools/dnd_lab.py:1716:    2. Trova V_c dove <r> = 0.50 (transizione GUE ↔ Poisson)
tools/exp_boundary_coherence.py:5:Question: Do different observables agree on WHERE primes sit between GUE and Poisson?
tools/exp_boundary_coherence.py:10:  1. Mean spacing ratio <r>        (Poisson ≈ 0.386, GUE ≈ 0.5307)
tools/exp_boundary_coherence.py:11:  2. Gap variance ratio Var/μ²     (Poisson = 1.0, GUE ≈ 0.178)
tools/exp_boundary_coherence.py:12:  3. Small-gap fraction P(s<0.3)   (Poisson ≈ 0.259, GUE ≈ 0.020)
tools/exp_boundary_coherence.py:13:  4. Brody parameter β             (Poisson = 0, GUE = 1)
tools/exp_boundary_coherence.py:14:  5. Lag-1 autocorrelation         (Poisson = 0, GUE ≈ -0.27)
tools/exp_boundary_coherence.py:16:Each observable is normalized to τ ∈ [0,1] where 0=Poisson, 1=GUE.
tools/exp_boundary_coherence.py:30:# GUE values from random matrix theory (GOE for real symmetric, GUE for complex)
tools/exp_boundary_coherence.py:60:    """Var(gaps) / mean(gaps)² — 1 for Poisson, <1 for correlated."""
tools/exp_boundary_coherence.py:113:    """Normalize observable to τ ∈ [0,1] where 0=Poisson, 1=GUE."""
tools/exp_boundary_coherence.py:122:    """Generate GUE spacings from random Hermitian matrices."""
tools/exp_boundary_coherence.py:126:        # GUE: complex Hermitian with Gaussian entries
tools/exp_boundary_coherence.py:137:    """Generate Poisson spacings (exponential distribution)."""
tools/exp_boundary_coherence.py:161:    print("Computing GUE reference...")
tools/exp_boundary_coherence.py:164:    results["GUE_reference"] = {
tools/exp_boundary_coherence.py:169:    print("Computing Poisson reference...")
tools/exp_boundary_coherence.py:172:    results["Poisson_reference"] = {
tools/exp_boundary_coherence.py:207:    print(f"\nReference anchors (τ should be ≈ 0 for Poisson, ≈ 1 for GUE):")
tools/exp_boundary_coherence.py:208:    print(f"  {'Observable':<20} {'Poisson τ':>10} {'GUE τ':>10}")
tools/exp_boundary_coherence.py:210:        pt = results["Poisson_reference"]["tau"][obs_name]
tools/exp_boundary_coherence.py:211:        gt = results["GUE_reference"]["tau"][obs_name]
tools/exp_boundary_coherence.py:214:    print(f"\nPrime gaps — τ values (0=Poisson, 1=GUE):")
tools/dnd_projective_quantization.py:209:    # con statistiche CRITICHE (né Poisson né GUE — esattamente al confine)
tools/dnd_projective_quantization.py:257:    closer = "GUE" if d_gue < d_poi else "Poisson"
tools/dnd_projective_quantization.py:259:    # Critical statistics (Fibonacci): <r> ≈ 0.50 (tra GUE e Poisson)
tools/dnd_projective_quantization.py:309:    # D. Confronto: Fibonacci vs GUE vs Poisson
tools/dnd_projective_quantization.py:314:    # D1. Random matrix GUE
tools/dnd_projective_quantization.py:315:    print("\n--- GUE reference ---")
tools/dnd_projective_quantization.py:321:        spacing_stats(eigs_gue, f"GUE N={N}")
tools/dnd_projective_quantization.py:323:    # D2. Potenziale random (Anderson → Poisson)
tools/dnd_projective_quantization.py:324:    print("\n--- Anderson (Poisson reference) ---")
tools/dnd_projective_quantization.py:338:    print("V=0 → metallico (GUE), V→∞ → localizzato (Poisson)")
tools/dnd_projective_quantization.py:366:dato da φ) produce uno spettro CRITICO — al confine esatto tra GUE e
tools/dnd_projective_quantization.py:367:Poisson. Questo è il "dove la curva diventa angolo":
tools/dnd_projective_quantization.py:378:disordine, tra GUE e Poisson.
tools/LAB_COGNITIVE_CONTAMINATION.md:59:- concetti: terzo incluso, GUE/Poisson, non-phi generator, graph curvature,
tools/LAB_COGNITIVE_CONTAMINATION.md:62:  precede la classificazione spettrale GUE/Poisson;
tools/LAB_COGNITIVE_CONTAMINATION.md:109:  - "VALIDA contro baseline GUE, Poisson e generatori sintetici."
tools/LAB_COGNITIVE_CONTAMINATION.md:111:  quando: "il ciclo lavora su boundary, GUE/Poisson o trasferibilita' phi"
tools/LAB_COGNITIVE_CONTAMINATION.md:255:  quando: "prima del prossimo ciclo su GUE/Poisson/non-phi"
tools/dnd_zero_operator.py:8:Il vecchio metodo: calcola spacing, classifica GUE/Poisson.
tools/dnd_publish_cycle.py:168:        "|NT⟩ → closure → det(M)=-1 → φ → Q(√5) → GUE → differentiated reality",
tools/data/notte_20260315_0749.md:4:  ising_2d_var_-0.1: r=0.9917140139075722, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260315_0749.md:5:  ising_2d_var_0.1: r=1.2487805121596578, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260315_0749.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260315_0749.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260315_0749.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260315_0749.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260315_0749.md:10:  brownian_motion_var_0.3: r=0.990909090909091, spacing=Poisson-like (⟨r⟩=0.3830376668120655)
tools/data/notte_20260315_0749.md:11:  brownian_motion_var_0.5: r=1.0, spacing=Poisson-like (⟨r⟩=0.38666236411128574)
tools/data/notte_20260315_0749.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260315_0749.md:13:  percolation_var_0.55: r=0.6340378197997775, spacing=Poisson-like (⟨r⟩=0.4578693904490548)
tools/data/notte_20260315_0749.md:14:  percolation_var_0.65: r=1.0, spacing=Poisson-like (⟨r⟩=0.4600532522260183)
tools/exp_boundary_row_aligned_nonexact_audit.py:6:does not read GUE/Poisson labels as decision fields. It only uses support,
tools/exp_boundary_row_aligned_nonexact_audit.py:175:        "label_policy": "Does not use source_domain_type or GUE/Poisson label as an operator.",
tools/exp_prime_sr_persistent_boundary.py:219:            "generator": "prime gaps from dnd_autoricerca row_spacings and direct sieve; controls from composite gaps, mod6 candidates, Cramer-like events, GUE random matrix blocks, logistic return intervals",
tools/exp_prime_sr_persistent_boundary.py:220:            "denominator": "8 prime row-local windows plus 20 non-prime controls (3 deterministic families x 4 offsets + 4 stochastic GUE/logistic cases each by default)",
tools/exp_prime_sr_persistent_boundary.py:222:            "not_tested": "global beta atlas, V_c, gap_ratio, source GUE/Poisson labels, analytic origin of SR",
tools/exp_number_variance.py:4:Tests whether the GUE->Poisson drift (seen in gap ratio) is confirmed
tools/exp_number_variance.py:7:GUE: Sigma^2(L) ~ (2/pi^2) * ln(L) + const  (logarithmic)
tools/exp_number_variance.py:8:Poisson: Sigma^2(L) = L  (linear)
tools/exp_number_variance.py:10:If primes drift toward Poisson at large scale, the number variance
tools/exp_number_variance.py:56:    """Poisson prediction: Sigma^2 = L."""
tools/exp_number_variance.py:60:    """GUE prediction: Sigma^2 ~ (2/pi^2) * ln(L) + 0.44 (approximate)."""
tools/exp_number_variance.py:83:    # Shuffled baseline (destroy correlations -> Poisson-like)
tools/exp_number_variance.py:118:    print(f"  Log-fit slope (primes):   {coeffs_prime[0]:.4f}  (GUE={2/np.pi**2:.4f})")
tools/exp_number_variance.py:123:    print(f"  Better fit: {'LOG (GUE-like)' if r2_log > r2_lin else 'LINEAR (Poisson-like)'}")
tools/exp_number_variance.py:125:    print(f"\n  L    | Σ²_prime  | Σ²_shuf  | Σ²_GUE  | Σ²_Poisson")
tools/exp_number_variance.py:151:# Key metric: does log_slope increase with scale? (would mean moving away from GUE)
tools/exp_number_variance.py:158:    print(f"  GUE value: {2/np.pi**2:.4f}")
tools/exp_number_variance.py:159:    print(f"  If trend > 0: number variance grows faster at large scale -> MORE Poisson")
tools/exp_number_variance.py:160:    print(f"  If trend ~ 0: stable -> GUE character preserved")
tools/exp_number_variance.py:166:    "claim_under_test": "BOUNDARY: GUE->Poisson drift in primes",
tools/exp_number_variance.py:167:    "method": "Number variance Sigma^2(L) at 5 scales, compared with GUE and Poisson predictions",
tools/build_agent_field.py:305:        "Obblighi pratici: se il dominio e' GUE/Poisson, aggiungi una sezione "
tools/build_agent_field.py:306:        "`## Re-discovery audit` con il baseline noto piu' vicino "
tools/build_agent_field.py:314:        "`two_reader_boundary_confirmed`, `graph_only_residue`, "
tools/build_agent_field.py:1819:        "Un pattern in distribuzioni classiche (primi, GUE, random walk, Markov chain, "
tools/build_agent_field.py:1823:        "per GUE level statistics, Erdos-Kac per distribuzioni aritmetiche). Se non lo "
tools/data/notte_20260322_0330.md:4:  ising_2d_var_-0.1: r=0.9533708497068641, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260322_0330.md:5:  ising_2d_var_0.1: r=0.99991253722176, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260322_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260322_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260322_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260322_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260322_0330.md:10:  brownian_motion_var_0.3: r=1.0, spacing=Poisson-like (⟨r⟩=0.3891607218310259)
tools/data/notte_20260322_0330.md:11:  brownian_motion_var_0.5: r=0.5735641227380016, spacing=Poisson-like (⟨r⟩=0.3863528241175028)
tools/data/notte_20260322_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260322_0330.md:13:  percolation_var_0.55: r=1.0, spacing=Poisson-like (⟨r⟩=0.3706649615579767)
tools/data/notte_20260322_0330.md:14:  percolation_var_0.65: r=1.0434782608695652, spacing=Poisson-like (⟨r⟩=0.4771540450594074)
tools/dnd_next.py:46:    'C': {'title': 'Information Geometry + ζ', 'target': 'JMP', 'core_claim': 'Berry-Keating potential, GUE connection'},
tools/dnd_trace_bridge_v3.py:181:            closer = "GUE" if d_gue < d_poi else "Poisson"
tools/dnd_trace_bridge_v3.py:205:        closer = "GUE" if d_gue < d_poi else "Poi"
tools/dnd_trace_bridge_v3.py:335:            print(f"  Spacing dei ratio (zeta):  <r>={stats_rz['mean_r']:.4f} → {'GUE' if d_gue < d_poi else 'Poi'}")
tools/dnd_trace_bridge_v3.py:339:            print(f"  Spacing dei ratio (random): <r>={stats_rr['mean_r']:.4f} → {'GUE' if d_gue < d_poi else 'Poi'}")
tools/dnd_trace_bridge_v3.py:348:v2 mostrava GUE ovunque per zeta — ma era tautologico (<r> invariante monotono).
tools/exp_scale_selective_perturbation.py:48:    """Generate N GUE gaps (eigenvalue spacings of random Hermitian matrix)."""
tools/exp_scale_selective_perturbation.py:66:    """Generate N Poisson (iid exponential) gaps."""
tools/exp_scale_selective_perturbation.py:197:                                   ('GUE', lambda: generate_gue(N, rng))]:
tools/exp_photonic_boundary_third_included_gate.py:5:This tool projects the GUE/Poisson boundary direction into a physical return:
tools/exp_photonic_boundary_third_included_gate.py:6:a 1D dielectric multilayer. It does not classify the optical spectrum as GUE or
tools/exp_photonic_boundary_third_included_gate.py:7:Poisson. It asks whether the boundary survives as an intermediate transmission
tools/exp_perturbation_dimensionality_audit.py:7:The 2026-05-06 03:30 run found that GUE spacing sequences expose a second
tools/exp_perturbation_dimensionality_audit.py:9:to one axis. That run used a short GUE sequence. This tool repeats the same
tools/exp_perturbation_dimensionality_audit.py:314:        "question": "Is the GUE second perturbation axis stable across independent ensembles and sample-size controls?",
tools/exp_perturbation_dimensionality_audit.py:336:    print("\nGUE independent replicates")
tools/exp_spectral_rigidity.py:9:  GUE:     Sigma^2(L) ~ (2/pi^2) ln(L) + const   [log-log slope ~ 0]
tools/exp_spectral_rigidity.py:10:  Poisson: Sigma^2(L) = L                         [log-log slope = 1]
tools/exp_spectral_rigidity.py:13:  << 1 -> GUE (strong repulsion, rigid spectrum)
tools/exp_spectral_rigidity.py:14:  ~  1 -> Poisson (no correlations)
tools/exp_spectral_rigidity.py:18:Domains: primes, GUE matrices, coupled_osc, string_vib, percolation,
tools/exp_spectral_rigidity.py:19:         logistic, brownian, pure Poisson.
tools/exp_spectral_rigidity.py:47:    """GUE Hermitian matrix eigenvalue spacings, bulk only."""
tools/exp_spectral_rigidity.py:85:        ('primes',      {'gen': lambda: gaps_from_domain('numeri_primi'),      'type': 'dist-GUE'}),
tools/exp_spectral_rigidity.py:86:        ('gue_matrix',  {'gen': lambda: generate_gue_gaps(600),               'type': 'dist-GUE'}),
tools/exp_spectral_rigidity.py:87:        ('coupled_osc', {'gen': lambda: gaps_from_domain('coupled_oscillators'), 'type': 'ord-GUE'}),
tools/exp_spectral_rigidity.py:88:        ('string_vib',  {'gen': lambda: gaps_from_domain('string_vibration'),  'type': 'ord-GUE'}),
tools/exp_spectral_rigidity.py:89:        ('percolation', {'gen': lambda: gaps_from_domain('percolation'),       'type': 'ord-GUE'}),
tools/exp_spectral_rigidity.py:90:        ('logistic',    {'gen': lambda: gaps_from_domain('logistica_biforcazione'), 'type': 'Poisson'}),
tools/exp_spectral_rigidity.py:91:        ('brownian',    {'gen': lambda: gaps_from_domain('brownian_motion'),   'type': 'Poisson'}),
tools/exp_spectral_rigidity.py:92:        ('poisson',     {'gen': lambda: generate_poisson_gaps(10000),          'type': 'Poisson'}),
tools/exp_spectral_rigidity.py:156:        print(f"  Slope (log-log): real={slope:.3f}, shuf={slope_s:.3f}  [GUE~0, Poisson=1]")
tools/exp_spectral_rigidity.py:188:            if ty in ('dist-GUE', 'ord-GUE'):
tools/exp_boundary_residual_beta_absent_audit.py:188:            "not_tested": "global beta grid, V_c, source GUE/Poisson label validity",
tools/dnd_spectral_probe.py:72:    3. KS test against GUE/GOE/Poisson
tools/dnd_spectral_probe.py:155:        β=0: Poisson, β=1: GOE, β=2: GUE.
tools/dnd_spectral_probe.py:158:        If β>2 at all scales → GUE (even when KS says GOE).
tools/dnd_spectral_probe.py:192:                cls = "GUE" if beta > 1.5 else ("GOE" if beta > 0.5 else "Poisson")
tools/dnd_spectral_probe.py:213:          Poisson: <r> = 2ln2 - 1 ≈ 0.3863
tools/dnd_spectral_probe.py:215:          GUE:     <r> ≈ 0.6027
tools/dnd_spectral_probe.py:230:        refs = {"Poisson": 0.38629, "GOE": 0.53590, "GUE": 0.60266}
tools/dnd_spectral_probe.py:248:        """KS test against GUE, GOE, Poisson."""
tools/dnd_spectral_probe.py:256:        fits = [("GUE", ks_gue, p_gue), ("GOE", ks_goe, p_goe), ("Poisson", ks_poi, p_poi)]
tools/dnd_spectral_probe.py:309:            if majority[0] == "GUE" and "GOE" in dissenters:
tools/dnd_spectral_probe.py:310:                verdict = (f"LIKELY GUE: {majority[0]} (2/3 — KS blind to GUE, "
tools/dnd_spectral_probe.py:462:                print(f"    |Δ Poisson|={dists.get('Poisson', 0):.5f}  "
tools/dnd_spectral_probe.py:464:                      f"|Δ GUE|={dists.get('GUE', 0):.5f}")
tools/dnd_spectral_probe.py:522:    → GUE statistics (Katz-Sarnak)
tools/dnd_spectral_probe.py:633:        reference_beta: theoretical β to converge to (2.0 for GUE)
tools/dnd_spectral_probe.py:1058:        print(f"  All GUE: {all_gue} ({sum(1 for b in valid if b > 1.5)}/{len(valid)})")
tools/data/notte_20260315_0330.md:4:  ising_2d_var_-0.1: r=0.9602566405292896, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260315_0330.md:5:  ising_2d_var_0.1: r=0.9986428486385872, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260315_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260315_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260315_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260315_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260315_0330.md:10:  brownian_motion_var_0.3: r=1.0, spacing=Poisson-like (⟨r⟩=0.3838122862269985)
tools/data/notte_20260315_0330.md:11:  brownian_motion_var_0.5: r=1.0333333333333334, spacing=Poisson-like (⟨r⟩=0.3993170059645738)
tools/data/notte_20260315_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260315_0330.md:13:  percolation_var_0.55: r=1.0, spacing=Poisson-like (⟨r⟩=0.462727043125885)
tools/data/notte_20260315_0330.md:14:  percolation_var_0.65: r=1.380952380952381, spacing=Poisson-like (⟨r⟩=0.444154396971446)
tools/dnd_zeros_vs_zeta.py:18:   b. GUE eigenvalue spacings (random matrix theory)
tools/dnd_zeros_vs_zeta.py:19:   c. Poisson (random) spacings
tools/dnd_zeros_vs_zeta.py:22:If D-ND zeros match GUE/zeta: structural connection confirmed.
tools/dnd_zeros_vs_zeta.py:23:If D-ND zeros match Poisson: no connection, just noise.
tools/dnd_zeros_vs_zeta.py:123:    """Generate GUE eigenvalue spacings for comparison."""
tools/dnd_zeros_vs_zeta.py:126:        # GUE: complex Hermitian matrix with Gaussian entries
tools/dnd_zeros_vs_zeta.py:139:    """Generate Poisson (uncorrelated) spacings."""
tools/dnd_zeros_vs_zeta.py:159:    GUE: <r> ≈ 0.5996
tools/dnd_zeros_vs_zeta.py:160:    Poisson: <r> ≈ 0.3863
tools/dnd_zeros_vs_zeta.py:220:    print(f"  GUE: {len(s_gue)} spacings")
tools/dnd_zeros_vs_zeta.py:221:    print(f"  Poisson: {len(s_poisson)} spacings")
tools/dnd_zeros_vs_zeta.py:227:    print(f"{'Distribution':<25} {'<r>':<8} {'KS vs zeta':<12} {'p-value':<12} {'KS vs GUE':<12} {'p vs GUE':<12}")
tools/dnd_zeros_vs_zeta.py:249:    print(f"{'GUE (RMT)':<25} {r_gue:<8.4f} {ks_gg:<12.4f} {p_gg:<12.4e} {'---':<12} {'---':<12}")
tools/dnd_zeros_vs_zeta.py:254:    print(f"{'Poisson (random)':<25} {r_poisson:<8.4f} {ks_pz:<12.4f} {p_pz:<12.4e} {ks_pg:<12.4f} {p_pg:<12.4e}")
tools/dnd_zeros_vs_zeta.py:262:    print(f"\nReference values: GUE <r> ≈ 0.5996 | Poisson <r> ≈ 0.3863")
tools/dnd_zeros_vs_zeta.py:325:    ax3.hist(s_gue, bins=bins, density=True, alpha=0.3, label='GUE (RMT)', color='green')
tools/dnd_zeros_vs_zeta.py:326:    ax3.hist(s_poisson, bins=bins, density=True, alpha=0.3, label='Poisson', color='gray')
tools/dnd_zeros_vs_zeta.py:347:    labels = ['Zeta', 'GUE', 'Poisson', 'Synth']
tools/dnd_zeros_vs_zeta.py:358:    ax5.axhline(0.5996, color='green', linestyle='--', linewidth=1, label='GUE expected')
tools/dnd_zeros_vs_zeta.py:359:    ax5.axhline(0.3863, color='gray', linestyle='--', linewidth=1, label='Poisson expected')
tools/dnd_zeros_vs_zeta.py:361:    ax5.set_title('Spacing Ratio Statistic\n(GUE ≈ 0.60, Poisson ≈ 0.39)')
tools/dnd_zeros_vs_zeta.py:396:    # Determine if D-ND is closer to GUE/zeta or Poisson
tools/dnd_zeros_vs_zeta.py:413:        print(f"  Distance to GUE: {gue_dist:.4f}")
tools/dnd_zeros_vs_zeta.py:414:        print(f"  Distance to Poisson: {poisson_dist:.4f}")
tools/dnd_zeros_vs_zeta.py:418:            print(f"\n  → D-ND zeros are CLOSER TO GUE/ZETA than to random")
tools/dnd_quantization.py:13:Domanda: gli spacing dei livelli E_n seguono GUE?
tools/dnd_quantization.py:317:            closer = "GUE" if d_gue < d_poi else "Poisson"
tools/dnd_quantization.py:318:            print(f"  <r> = {stats['mean_r']:.4f} (GUE: 0.5996, Poisson: 0.3863) → {closer}")
tools/dnd_quantization.py:319:            print(f"  Var(s) = {stats['var_s']:.4f} (GUE: 0.178, Poisson: 1.0)")
tools/dnd_quantization.py:333:                      f"→ {'GUE' if d_gue < d_poi else 'Poi'}")
tools/data/notte_20260305_0330.md:4:  ising_2d_var_-0.1: r=0.9823754433241959, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260305_0330.md:5:  ising_2d_var_0.1: r=1.1807959337242486, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260305_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260305_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260305_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260305_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260305_0330.md:10:  brownian_motion_var_0.3: r=0.9588638589618023, spacing=Poisson-like (⟨r⟩=0.38926362061125996)
tools/data/notte_20260305_0330.md:11:  brownian_motion_var_0.5: r=1.0, spacing=Poisson-like (⟨r⟩=0.39461461269967785)
tools/data/notte_20260305_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260305_0330.md:13:  percolation_var_0.55: r=1.0, spacing=Poisson-like (⟨r⟩=0.4270878077372399)
tools/data/notte_20260305_0330.md:14:  percolation_var_0.65: r=1.127659574468085, spacing=Poisson-like (⟨r⟩=0.43169596436963903)
tools/dnd_research_engine.py:144:                'content': 'D-ND dynamics show GUE-like level repulsion in zero spacing (⟨r⟩≈0.84)',
tools/dnd_research_engine.py:158:            {'id': 'O2', 'content': 'GUE-like spacing in D-ND zeros', 'source': 'dnd_zeros_vs_zeta.py'},
tools/dnd_research_engine.py:277:                         f'show GUE-like level repulsion (⟨r⟩ ≈ 0.60)',
tools/dnd_research_engine.py:438:        """Test spacing statistics (GUE vs Poisson)."""
tools/dnd_research_engine.py:675:            'prediction': 'Spacing statistics at D↔ND transition show GUE-like repulsion',
tools/data/operator_directive_consumed_20260507_0803.md:3:**Contesto.** I 9 cycle precedenti hanno tutti attaccato BOUNDARY (8 GUE / 5 Poisson) producendo 3 gate metodologici cristallizzati: OBSERVABLE_REGISTRY, PERTURBATION_DENOMINATOR_GATE, BOUNDARY_LAYER_GATE (denominator collapse layer beta 0.3-0.4). Le altre 6 tensioni-sorgente sono ferme da settimane. Il sistema (Godel) ha posto la domanda critica:
tools/exp_semireal_boundary_transfer_gate.py:196:        help="Include GUE/Poisson source labels as audit metadata only. Default omits them from output.",
tools/exp_boundary_shuffle_audit.py:2:exp_boundary_shuffle_audit.py — Is the GUE/Poisson classification tautological?
tools/exp_boundary_shuffle_audit.py:16:Domains: primes, zeta zeros, random matrix (GUE), Fibonacci spectrum,
tools/exp_boundary_shuffle_audit.py:17:         logistic map, Poisson, coupled oscillators, percolation,
tools/exp_boundary_shuffle_audit.py:83:    """GUE random matrices — eigenvalue spacings."""
tools/exp_boundary_shuffle_audit.py:100:    """Poisson process — exponential spacings."""
tools/exp_boundary_shuffle_audit.py:238:R_GUE = 0.5307  # 4 - 2√3 ≈ 0.5359 for GOE; for GUE: 2π/(3√3 + 4π/3) ≈ 0.5307 approx
tools/exp_boundary_shuffle_audit.py:244:    'gue':                 ('GUE random matrix',          gen_gue_eigenvalues),
tools/exp_boundary_shuffle_audit.py:245:    'poisson':             ('Poisson process',            gen_poisson),
tools/exp_boundary_shuffle_audit.py:275:            dist_gue = abs(res['r_original'] - R_GUE)
tools/exp_boundary_shuffle_audit.py:277:            res['class_original'] = 'GUE' if dist_gue < dist_poi else 'Poisson'
tools/exp_boundary_shuffle_audit.py:279:            dist_gue_s = abs(res['r_shuffled_mean'] - R_GUE)
tools/exp_boundary_shuffle_audit.py:281:            res['class_shuffled'] = 'GUE' if dist_gue_s < dist_poi_s else 'Poisson'
tools/exp_boundary_shuffle_audit.py:308:    print(f"  SUMMARY: GUE/Poisson Classification Shuffle Audit")
tools/exp_boundary_shuffle_audit.py:336:    print(f"\n  R_GUE = {R_GUE:.4f}, R_Poisson = {R_POISSON:.4f}")
tools/exp_boundary_shuffle_audit.py:355:        'reference': {'R_GUE': R_GUE, 'R_Poisson': R_POISSON},
tools/LAB_OPERATOR_PALETTE.md:19:- Non partire da phi, gap label, GUE o Poisson se sono gia' nel ciclo
tools/LAB_OPERATOR_PALETTE.md:276:- GUE;
tools/LAB_OPERATOR_PALETTE.md:279:- Poisson;
tools/LAB_OPERATOR_PALETTE.md:299:- Poisson synthetic;
tools/LAB_OPERATOR_PALETTE.md:300:- GUE synthetic;
tools/LAB_OPERATOR_PALETTE.md:307:- GUE/Poisson e' spesso un piano di proiezione, non una sorgente. Se diventa
tools/LAB_OPERATOR_PALETTE.md:546:GUE/Poisson/non-phi.
tools/LAB_OPERATOR_PALETTE.md:553:- Punto-zero: confine prima che venga classificato GUE o Poisson.
tools/exp_dipolar_angle_reference.py:3:exp_dipolar_angle_reference.py — Dipolar angle of GUE, Poisson, and primes
tools/exp_dipolar_angle_reference.py:6:1. Pure GUE eigenvalue spacings (GOE/GUE unfolded)
tools/exp_dipolar_angle_reference.py:7:2. Pure Poisson (exponential iid)
tools/exp_dipolar_angle_reference.py:13:unique or just a position on the GUE-Poisson continuum?
tools/exp_dipolar_angle_reference.py:80:    """Generate gaps from GUE eigenvalues (unfolded)."""
tools/exp_dipolar_angle_reference.py:83:        # GUE: complex Hermitian random matrix
tools/exp_dipolar_angle_reference.py:116:    """Generate iid exponential gaps (Poisson process)."""
tools/exp_dipolar_angle_reference.py:153:    # 2. GUE
tools/exp_dipolar_angle_reference.py:154:    print(f"Generating GUE gaps (n_trials={n_trials})...")
tools/exp_dipolar_angle_reference.py:163:            print(f"  GUE trial {t+1}/{n_trials}: theta = {theta:.1f}")
tools/exp_dipolar_angle_reference.py:165:    results['GUE'] = {
tools/exp_dipolar_angle_reference.py:171:    print(f"  GUE: theta = {np.mean(gue_thetas):.1f} +/- {np.std(gue_thetas):.1f} deg")
tools/exp_dipolar_angle_reference.py:193:    # 4. Poisson
tools/exp_dipolar_angle_reference.py:194:    print(f"Generating Poisson gaps (n_trials={n_trials})...")
tools/exp_dipolar_angle_reference.py:203:            print(f"  Poisson trial {t+1}/{n_trials}: theta = {theta:.1f}")
tools/exp_dipolar_angle_reference.py:205:    results['Poisson'] = {
tools/exp_dipolar_angle_reference.py:211:    print(f"  Poisson: theta = {np.mean(poi_thetas):.1f} +/- {np.std(poi_thetas):.1f} deg")
tools/exp_dipolar_angle_reference.py:240:    print(f"{'GUE':<12} {results['GUE']['theta_mean']:>7.1f} +/- {results['GUE']['theta_std']:>5.1f}  {results['GUE']['SR_mean']:<10.4f} {results['GUE']['L1_mean']:<10.4f}")
tools/exp_dipolar_angle_reference.py:242:    print(f"{'Poisson':<12} {results['Poisson']['theta_mean']:>7.1f} +/- {results['Poisson']['theta_std']:>5.1f}  {results['Poisson']['SR_mean']:<10.4f} {results['Poisson']['L1_mean']:<10.4f}")
tools/exp_dipolar_angle_reference.py:247:    for name in ['GUE', 'GOE', 'Poisson', 'Cramer']:
tools/exp_dipolar_crossover.py:3:exp_dipolar_crossover.py — Topology of the GUE-Poisson transition in the dipolar plane.
tools/exp_dipolar_crossover.py:5:Question: As ordering is gradually destroyed (GUE → shuffled), does the dipolar
tools/exp_dipolar_crossover.py:10:  1. Generate GUE bulk spacings from random matrices
tools/exp_dipolar_crossover.py:14:     - Leave the rest in original (GUE) order
tools/exp_dipolar_crossover.py:19:If primes sit on the curve → they're "partially disordered GUE"
tools/exp_dipolar_crossover.py:33:    """Generate bulk spacings from GUE matrices."""
tools/exp_dipolar_crossover.py:98:    print(f"Generating GUE spacings: {n_matrices} matrices of size {N_mat}...")
tools/exp_dipolar_crossover.py:101:    # GUE baseline (alpha=0)
tools/exp_dipolar_crossover.py:214:        "GUE_baseline": {"SR": float(sr0), "L1": float(l1_0)},
tools/exp_brody_calibration.py:6:Poisson (beta=0) to Wigner-GUE (beta=1). Gaps are i.i.d. by construction —
tools/exp_brody_calibration.py:211:    # GUE matrices
tools/exp_brody_calibration.py:235:    # Poisson
tools/exp_brody_calibration.py:326:    print(f"   Theory: Poisson r=0.386, GOE r=0.536")
tools/data/notte_20260331_1753.md:4:  Direzione: Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo in
tools/data/notte_20260331_1753.md:5:  [confine_inesplorato] BOUNDARY: 8 domini GUE, 5 Poisson — il confine è il terzo incluso oper
tools/data/notte_20260331_1753.md:8:  logistica_biforcazione_cp_3.57: r=1.0, spacing=Poisson-like [VINCOLO] [NULL:non-disc]
tools/data/notte_20260331_1753.md:10:  logistica_biforcazione_cp_3.924: r=1.000568504832291, spacing=Poisson-like [VINCOLO] [NULL:non-disc]
tools/data/notte_20260331_1753.md:12:  collatz_cp: r=0.9561815336463223, spacing=GUE-like [conferma]
tools/data/notte_20260331_1753.md:14:  ising_2d_cp_-0.3577838598900799: r=0.9361279457884175, spacing=GUE-like [conferma]
tools/data/notte_20260331_1753.md:15:  ising_2d_cp_0.3484712956135173: r=0.957792741636363, spacing=GUE-like [conferma]
tools/data/notte_20260331_1753.md:16:  brownian_motion_cp_0.16207460020731265: r=0.9883147585275244, spacing=Poisson-like [VINCOLO] [NULL:non-disc]
tools/data/notte_20260331_1753.md:17:  brownian_motion_cp_0.9417285916195904: r=1.0, spacing=GUE-like [conferma]
tools/data/notte_20260331_1753.md:18:  percolation_cp_0.30242865227209575: r=0.8184523809523808, spacing=GUE-like [conferma]
tools/data/notte_20260331_1753.md:19:  percolation_cp_0.8132922606103345: r=1.0833333333333333, spacing=GUE-like [conferma]
tools/data/notte_20260331_1753.md:20:  cellular_automata_cp_150: r=0.7452006980802792, spacing=GUE-like [conferma]
tools/data/notte_20260331_1753.md:32:  Campo dopo Fase 0: 7 GUE / 3 Poisson
tools/data/notte_20260331_1753.md:36:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236) [NULL:non-disc]
tools/data/notte_20260331_1753.md:37:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094) [NULL:non-disc]
tools/data/notte_20260331_1753.md:38:  brownian_motion_var_0.5: r=0.9523809523809524, spacing=Poisson-like (⟨r⟩=0.39452778920439047) [NULL:non-disc]
tools/data/notte_20260331_1753.md:39:  percolation_var_0.65: r=1.0, spacing=Poisson-like (⟨r⟩=0.45465430949539676) [NULL:non-disc]
tools/data/notte_20260331_1753.md:95:  GUE: 7 | Poisson: 7 | Vincoli: 15 | Anomalie: 11
tools/data/notte_20260331_1753.md:97:  Domini GUE: collatz_cp, ising_2d_cp_-0.3577838598900799, ising_2d_cp_0.3484712956135173, brownian_motion_cp_0.9417285916195904, percolation_cp_0.30242865227209575, percolation_cp_0.8132922606103345, cellular_automata_cp_150
tools/data/notte_20260331_1753.md:98:  Domini Poisson: logistica_biforcazione_cp_3.57, logistica_biforcazione_cp_3.924, brownian_motion_cp_0.16207460020731265, logistica_biforcazione_var_3.9, logistica_biforcazione_var_3.57, brownian_motion_var_0.5, percolation_var_0.65
tools/dnd_experiments.py:225:# ESPERIMENTO 2: Boundary GUE/Poisson — Il terzo incluso
tools/dnd_experiments.py:230:    Testa H_boundary: esiste una transizione GUE→Poisson
tools/dnd_experiments.py:240:    print("ESPERIMENTO: Boundary GUE/Poisson (Ising T-sweep)")
tools/dnd_experiments.py:372:    print("ESPERIMENTO: Boundary GUE/Poisson (Logistica r-sweep)")
tools/dnd_experiments.py:481:# ESPERIMENTO 3: Order parameter — r_diretto separa GUE/Poisson?
tools/dnd_experiments.py:487:    che separa GUE da Poisson?
tools/dnd_experiments.py:490:    - Domini GUE hanno r_diretto in un range
tools/dnd_experiments.py:491:    - Domini Poisson hanno r_diretto in un altro range
tools/dnd_experiments.py:513:    # Classifica GUE vs Poisson (soglia standard: 0.53)
tools/dnd_experiments.py:514:    GUE_THRESHOLD = 0.53
tools/dnd_experiments.py:515:    gue = [(d, sp, r) for d, sp, r, _ in domains if sp > GUE_THRESHOLD]
tools/dnd_experiments.py:516:    poisson = [(d, sp, r) for d, sp, r, _ in domains if sp <= GUE_THRESHOLD]
tools/dnd_experiments.py:518:    print(f"\n  GUE ({len(gue)} domini):")
tools/dnd_experiments.py:522:    print(f"\n  Poisson ({len(poisson)} domini):")
tools/dnd_experiments.py:546:        print(f"  r_diretto GUE:     {mean_gue:.4f} ± {std_gue:.4f}  range=[{gue_range[0]:.4f}, {gue_range[1]:.4f}]")
tools/dnd_experiments.py:547:        print(f"  r_diretto Poisson: {mean_poisson:.4f} ± {std_poisson:.4f}  range=[{poisson_range[0]:.4f}, {poisson_range[1]:.4f}]")
tools/dnd_experiments.py:549:        print(f"  r_diretto SEPARA GUE/Poisson: {'SÌ' if separates else 'NO'}")
tools/dnd_experiments.py:1031:                # Per spacings (zeta, GUE): il segnale È gli intervalli
tools/data/notte_20260307_0330.md:4:  ising_2d_var_-0.1: r=0.9382984570853047, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260307_0330.md:5:  ising_2d_var_0.1: r=0.9960177272989545, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260307_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260307_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260307_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260307_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260307_0330.md:10:  brownian_motion_var_0.3: r=1.0452114730189597, spacing=Poisson-like (⟨r⟩=0.38632962481082495)
tools/data/notte_20260307_0330.md:11:  brownian_motion_var_0.5: r=1.0, spacing=Poisson-like (⟨r⟩=0.38653838880191393)
tools/data/notte_20260307_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260307_0330.md:13:  percolation_var_0.55: r=1.0384615384615385, spacing=Poisson-like (⟨r⟩=0.44398759377600533)
tools/data/notte_20260307_0330.md:14:  percolation_var_0.65: r=0.9642857142857143, spacing=Poisson-like (⟨r⟩=0.44981595361822824)
tools/data/notte_20260321_0330.md:4:  ising_2d_var_-0.1: r=0.9681659409908998, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260321_0330.md:5:  ising_2d_var_0.1: r=0.7138844511232227, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260321_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260321_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260321_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260321_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260321_0330.md:10:  brownian_motion_var_0.3: r=1.0301957517700957, spacing=Poisson-like (⟨r⟩=0.39787683667863427)
tools/data/notte_20260321_0330.md:11:  brownian_motion_var_0.5: r=1.0, spacing=Poisson-like (⟨r⟩=0.3942099149299928)
tools/data/notte_20260321_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260321_0330.md:13:  percolation_var_0.55: r=1.0454545454545456, spacing=Poisson-like (⟨r⟩=0.4677794328528817)
tools/data/notte_20260321_0330.md:14:  percolation_var_0.65: r=1.1785714285714284, spacing=Poisson-like (⟨r⟩=0.4343171927696077)
tools/exp_boundary_growth.py:13:  - GUE (correlated): <r> ~ 0.5307
tools/exp_boundary_growth.py:14:  - Poisson (uncorrelated): <r> ~ 0.3863
tools/exp_boundary_growth.py:158:    # Key question: do primes approach GUE or Poisson at large scale?
tools/exp_boundary_growth.py:159:    GUE_R = 0.5307
tools/exp_boundary_growth.py:165:    print(f"GUE reference: {GUE_R}")
tools/exp_boundary_growth.py:166:    print(f"Poisson reference: {POISSON_R}")
tools/exp_boundary_growth.py:167:    print(f"Position: {'closer to GUE' if abs(r_large - GUE_R) < abs(r_large - POISSON_R) else 'closer to Poisson'}")
tools/data/notte_20260401_0330.md:4:  Direzione: Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo in
tools/data/notte_20260401_0330.md:5:  [confine_inesplorato] BOUNDARY: 8 domini GUE, 5 Poisson — il confine è il terzo incluso oper
tools/data/notte_20260401_0330.md:12:  logistica_biforcazione_cp_3.57: r=1.0, spacing=Poisson-like [VINCOLO] [NULL:non-disc]
tools/data/notte_20260401_0330.md:14:  logistica_biforcazione_cp_3.804: r=1.0005015045135406, spacing=Poisson-like [VINCOLO] [NULL:non-disc]
tools/data/notte_20260401_0330.md:15:  logistica_biforcazione_cp_3.745: r=1.0004926108374383, spacing=Poisson-like [VINCOLO] [NULL:non-disc]
tools/data/notte_20260401_0330.md:17:  collatz_cp: r=0.9796918008174159, spacing=GUE-like [conferma]
tools/data/notte_20260401_0330.md:19:  ising_2d_cp_-0.46615852163115534: r=0.9270941152116229, spacing=GUE-like [conferma]
tools/data/notte_20260401_0330.md:20:  ising_2d_cp_0.33645649256599713: r=0.9838054100611432, spacing=GUE-like [conferma]
tools/data/notte_20260401_0330.md:21:  brownian_motion_cp_0.15859839152036145: r=0.9935483870967742, spacing=Poisson-like [VINCOLO] [NULL:non-disc]
tools/data/notte_20260401_0330.md:22:  brownian_motion_cp_0.9491984382159158: r=1.0, spacing=Poisson-like [VINCOLO] [NULL:non-disc]
tools/data/notte_20260401_0330.md:23:  percolation_cp_0.3791475515293742: r=1.0, spacing=GUE-like [conferma]
tools/data/notte_20260401_0330.md:24:  percolation_cp_0.8403996160580126: r=0.9655172413793103, spacing=GUE-like [conferma]
tools/data/notte_20260401_0330.md:25:  cellular_automata_cp_90: r=0.6458196181698486, spacing=GUE-like [conferma]
tools/data/notte_20260401_0330.md:37:  Campo dopo Fase 0: 6 GUE / 5 Poisson
tools/data/notte_20260401_0330.md:41:  ising_2d_var_0.1: r=0.9969894889487128, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260401_0330.md:42:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236) [NULL:non-disc]
tools/data/notte_20260401_0330.md:43:  brownian_motion_var_0.5: r=1.0, spacing=Poisson-like (⟨r⟩=0.3953893956038025) [NULL:non-disc]
tools/data/notte_20260401_0330.md:44:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094) [NULL:non-disc]
tools/data/notte_20260401_0330.md:101:  GUE: 7 | Poisson: 8 | Vincoli: 17 | Anomalie: 10
tools/data/notte_20260401_0330.md:103:  Domini GUE: collatz_cp, ising_2d_cp_-0.46615852163115534, ising_2d_cp_0.33645649256599713, percolation_cp_0.3791475515293742, percolation_cp_0.8403996160580126, cellular_automata_cp_90, ising_2d_var_0.1
tools/data/notte_20260401_0330.md:104:  Domini Poisson: logistica_biforcazione_cp_3.57, logistica_biforcazione_cp_3.804, logistica_biforcazione_cp_3.745, brownian_motion_cp_0.15859839152036145, brownian_motion_cp_0.9491984382159158, logistica_biforcazione_var_3.9, brownian_motion_var_0.5, logistica_biforcazione_var_3.57
tools/exp_acf_range_universality.py:11:Domains: primes, GUE, GOE, Poisson, primes_shuffled.
tools/exp_acf_range_universality.py:69:def generate_rmt_spacings(N, n_mat, ensemble='GUE'):
tools/exp_acf_range_universality.py:74:        if ensemble == 'GUE':
tools/exp_acf_range_universality.py:169:    print("Generating GUE spacings...")
tools/exp_acf_range_universality.py:170:    gue_gaps = generate_rmt_spacings(N_rmt, n_mat_rmt, 'GUE')
tools/exp_acf_range_universality.py:182:        'GUE': gue_gaps,
tools/exp_acf_range_universality.py:184:        'Poisson': poisson_gaps,
tools/data/notte_20260328_0330.md:5:  collatz_cp: r=0.8074001881973383, spacing=GUE-like [conferma]
tools/data/notte_20260328_0330.md:8:  ising_2d_cp_-0.30096344125143626: r=0.8775708104853573, spacing=GUE-like [conferma]
tools/data/notte_20260328_0330.md:9:  ising_2d_cp_0.3912489375554772: r=0.9186569401419008, spacing=GUE-like [conferma]
tools/data/notte_20260328_0330.md:10:  brownian_motion_cp_0.12469700253176728: r=1.0811654526534857, spacing=Poisson-like [conferma]
tools/data/notte_20260328_0330.md:11:  brownian_motion_cp_0.8121684739408563: r=1.0, spacing=Poisson-like [conferma]
tools/data/notte_20260328_0330.md:12:  percolation_cp_0.33766224009941354: r=0.8518518518518517, spacing=GUE-like [conferma]
tools/data/notte_20260328_0330.md:13:  percolation_cp_0.8820369977270884: r=0.8867924528301886, spacing=GUE-like [conferma]
tools/data/notte_20260328_0330.md:14:  cellular_automata_cp_90: r=0.6458196181698486, spacing=GUE-like [conferma]
tools/data/notte_20260328_0330.md:24:  Campo dopo Fase 0: 6 GUE / 2 Poisson
tools/data/notte_20260328_0330.md:28:  brownian_motion_var_0.3: r=0.9882352941176471, spacing=Poisson-like (⟨r⟩=0.375584985441532)
tools/data/notte_20260328_0330.md:29:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260328_0330.md:30:  ising_2d_var_0.1: r=0.9908867676670138, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260328_0330.md:31:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260328_0330.md:77:  GUE: 7 | Poisson: 5 | Vincoli: 1 | Anomalie: 15
tools/data/notte_20260328_0330.md:79:  Domini GUE: collatz_cp, ising_2d_cp_-0.30096344125143626, ising_2d_cp_0.3912489375554772, percolation_cp_0.33766224009941354, percolation_cp_0.8820369977270884, cellular_automata_cp_90, ising_2d_var_0.1
tools/data/notte_20260328_0330.md:80:  Domini Poisson: brownian_motion_cp_0.12469700253176728, brownian_motion_cp_0.8121684739408563, brownian_motion_var_0.3, logistica_biforcazione_var_3.9, logistica_biforcazione_var_3.57
tools/dnd_paper_refactor.py:387:            "chain": "|NT⟩ → closure → det=-1 → φ → GUE → differentiated reality",
tools/data/notte_20260313_0330.md:4:  ising_2d_var_-0.1: r=0.9598073621643622, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260313_0330.md:5:  ising_2d_var_0.1: r=1.1534456181377324, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260313_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260313_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260313_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260313_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260313_0330.md:10:  brownian_motion_var_0.3: r=1.0, spacing=Poisson-like (⟨r⟩=0.39085610463879605)
tools/data/notte_20260313_0330.md:11:  brownian_motion_var_0.5: r=1.1413276231263383, spacing=Poisson-like (⟨r⟩=0.367786562397899)
tools/data/notte_20260313_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260313_0330.md:13:  percolation_var_0.55: r=0.6659836065573771, spacing=Poisson-like (⟨r⟩=0.42563729952692636)
tools/data/notte_20260313_0330.md:14:  percolation_var_0.65: r=1.0, spacing=GUE-like (⟨r⟩=0.5124430839861729)
tools/data/notte_20260302_0330.md:4:  ising_2d_var_-0.1: r=0.9544449807275661, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260302_0330.md:5:  ising_2d_var_0.1: r=1.0346055137014654, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260302_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260302_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260302_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260302_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260302_0330.md:10:  brownian_motion_var_0.3: r=0.4251555846708156, spacing=Poisson-like (⟨r⟩=0.4080976347390199)
tools/data/notte_20260302_0330.md:11:  brownian_motion_var_0.5: r=0.6666666666666667, spacing=Poisson-like (⟨r⟩=0.4403145387503658)
tools/data/notte_20260302_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260302_0330.md:13:  percolation_var_0.55: r=0.5873015873015873, spacing=Poisson-like (⟨r⟩=0.39728805745491197)
tools/data/notte_20260302_0330.md:14:  percolation_var_0.65: r=0.9607843137254901, spacing=Poisson-like (⟨r⟩=0.3994075552107834)
tools/data/notte_20260309_0330.md:4:  ising_2d_var_-0.1: r=0.9617378732350873, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260309_0330.md:5:  ising_2d_var_0.1: r=0.9953239701767003, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260309_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260309_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260309_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260309_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260309_0330.md:10:  brownian_motion_var_0.3: r=1.0, spacing=Poisson-like (⟨r⟩=0.38887915759169045)
tools/data/notte_20260309_0330.md:11:  brownian_motion_var_0.5: r=1.0, spacing=Poisson-like (⟨r⟩=0.3794585132262598)
tools/data/notte_20260309_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260309_0330.md:13:  percolation_var_0.55: r=1.0384615384615385, spacing=Poisson-like (⟨r⟩=0.42620322149073625)
tools/data/notte_20260309_0330.md:14:  percolation_var_0.65: r=1.3790650406504066, spacing=Poisson-like (⟨r⟩=0.4591904379007532)
tools/dnd_kernel.py:298:            'statistics': 'GUE',
tools/dnd_kernel.py:308:            'statistics': 'Poisson',
tools/dnd_kernel.py:318:            'statistics': 'GUE',
tools/data/repairs/repair_20260514_1656.md:16:- reasoning: La riparazione L6 richiesta dall'operatore e' stata completata senza cambiare contenuto scientifico: Bridge QA clean, health completed, e il verdict resta correttamente CONSTRAINT / FIT-READY TESTER. Non emerge una nuova dipendenza dall'operatore: il residuo era formale/contrattuale ed e' stato chiuso nel nodo regressivo del report. Il seme e' gia' avanzato a piano 125 con direzione viva sul confine GUE/Poisson, quindi il passo successivo e' riprendere il flusso sperimentale.
tools/data/notte_20260311_0330.md:4:  ising_2d_var_-0.1: r=0.9666526892631134, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260311_0330.md:5:  ising_2d_var_0.1: r=1.226520946482944, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260311_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260311_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260311_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260311_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260311_0330.md:10:  brownian_motion_var_0.3: r=2.0349013657056143, spacing=Poisson-like (⟨r⟩=0.38547012931282587)
tools/data/notte_20260311_0330.md:11:  brownian_motion_var_0.5: r=1.0, spacing=Poisson-like (⟨r⟩=0.37816871699060933)
tools/data/notte_20260311_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260311_0330.md:13:  percolation_var_0.55: r=1.0, spacing=Poisson-like (⟨r⟩=0.438446157222364)
tools/data/notte_20260311_0330.md:14:  percolation_var_0.65: r=1.04, spacing=Poisson-like (⟨r⟩=0.4825316621318817)
tools/data/notte_20260317_0330.md:4:  ising_2d_var_-0.1: r=0.9766837149507093, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260317_0330.md:5:  ising_2d_var_0.1: r=0.9984668415805404, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260317_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260317_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260317_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260317_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260317_0330.md:10:  brownian_motion_var_0.3: r=1.0, spacing=Poisson-like (⟨r⟩=0.3732286748513319)
tools/data/notte_20260317_0330.md:11:  brownian_motion_var_0.5: r=1.037037037037037, spacing=Poisson-like (⟨r⟩=0.3884337586965294)
tools/data/notte_20260317_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260317_0330.md:13:  percolation_var_0.55: r=1.0357142857142858, spacing=Poisson-like (⟨r⟩=0.43363212915065535)
tools/data/notte_20260317_0330.md:14:  percolation_var_0.65: r=1.3836772983114447, spacing=Poisson-like (⟨r⟩=0.4818192957451693)
tools/data/repairs/repair_20260509_0652.md:16:- reasoning: Il ciclo ha prodotto una falsificazione strutturale utile: il label-set preservato non basta a ricostruire il boundary `V_c` nel perimetro N=89. Non serve fermare l'operatore: la consecutio e' gia' indicata dal deposito, cioe' riparare il null al nodo generativo e distinguere phase-shuffle Sturmian da surrogate label-preserving prima di estendere a GUE/Poisson.
tools/data/notte_20260325_0330.md:5:  collatz_cp: r=0.8406480180762742, spacing=GUE-like [conferma]
tools/data/notte_20260325_0330.md:8:  ising_2d_cp_-0.3721731072469586: r=0.9356675556004418, spacing=GUE-like [conferma]
tools/data/notte_20260325_0330.md:9:  ising_2d_cp_0.3729192328786819: r=0.6459279501338727, spacing=GUE-like [conferma]
tools/data/notte_20260325_0330.md:10:  brownian_motion_cp_0.19266122489026305: r=1.0, spacing=Poisson-like [conferma]
tools/data/notte_20260325_0330.md:11:  brownian_motion_cp_0.8027792897204493: r=0.5246331236897275, spacing=Poisson-like [conferma]
tools/data/notte_20260325_0330.md:12:  percolation_cp_0.33879710599589613: r=0.6393442622950819, spacing=GUE-like [conferma]
tools/data/notte_20260325_0330.md:13:  percolation_cp_0.8987621995814573: r=1.0743910467412772, spacing=GUE-like [conferma]
tools/data/notte_20260325_0330.md:14:  cellular_automata_cp_90: r=0.6458196181698486, spacing=GUE-like [conferma]
tools/data/notte_20260325_0330.md:17:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260325_0330.md:18:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260325_0330.md:19:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260325_0330.md:20:  ising_2d_var_0.1: r=0.9979275919880332, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260310_0330.md:4:  ising_2d_var_-0.1: r=0.9633741972142037, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260310_0330.md:5:  ising_2d_var_0.1: r=1.7679416137315853, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260310_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260310_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260310_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260310_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260310_0330.md:10:  brownian_motion_var_0.3: r=0.986111111111111, spacing=Poisson-like (⟨r⟩=0.38638040457447775)
tools/data/notte_20260310_0330.md:11:  brownian_motion_var_0.5: r=7.468531468531469, spacing=Poisson-like (⟨r⟩=0.38038436316845325)
tools/data/notte_20260310_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260310_0330.md:13:  percolation_var_0.55: r=1.0, spacing=Poisson-like (⟨r⟩=0.46061899071128326)
tools/data/notte_20260310_0330.md:14:  percolation_var_0.65: r=1.0454545454545456, spacing=Poisson-like (⟨r⟩=0.4791755206389135)
tools/data/notte_20260318_0330.md:4:  ising_2d_var_-0.1: r=0.9712272686438829, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260318_0330.md:5:  ising_2d_var_0.1: r=0.9979279302130017, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260318_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260318_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260318_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260318_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260318_0330.md:10:  brownian_motion_var_0.3: r=0.38026918322218933, spacing=Poisson-like (⟨r⟩=0.3900658787089022)
tools/data/notte_20260318_0330.md:11:  brownian_motion_var_0.5: r=1.0, spacing=Poisson-like (⟨r⟩=0.38552654965993177)
tools/data/notte_20260318_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260318_0330.md:13:  percolation_var_0.55: r=1.04, spacing=Poisson-like (⟨r⟩=0.4547791897708293)
tools/data/notte_20260318_0330.md:14:  percolation_var_0.65: r=1.0416666666666667, spacing=Poisson-like (⟨r⟩=0.40597402614552264)
tools/data/notte_20260323_0330.md:4:  ising_2d_var_-0.1: r=0.9751114725776623, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260323_0330.md:5:  ising_2d_var_0.1: r=0.9978353809490685, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260323_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260323_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260323_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260323_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260323_0330.md:10:  brownian_motion_var_0.3: r=0.9954329949374547, spacing=Poisson-like (⟨r⟩=0.3993227982961604)
tools/data/notte_20260323_0330.md:11:  brownian_motion_var_0.5: r=0.9772727272727273, spacing=Poisson-like (⟨r⟩=0.389017141986291)
tools/data/notte_20260323_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260323_0330.md:13:  percolation_var_0.55: r=1.0, spacing=Poisson-like (⟨r⟩=0.45592478441584805)
tools/data/notte_20260323_0330.md:14:  percolation_var_0.65: r=1.04, spacing=Poisson-like (⟨r⟩=0.4752713133131334)
tools/data/notte_20260319_0330.md:4:  ising_2d_var_-0.1: r=0.9570353957575984, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260319_0330.md:5:  ising_2d_var_0.1: r=0.9941836310760281, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260319_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260319_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260319_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260319_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260319_0330.md:10:  brownian_motion_var_0.3: r=1.0, spacing=Poisson-like (⟨r⟩=0.38039334783343215)
tools/data/notte_20260319_0330.md:11:  brownian_motion_var_0.5: r=1.0285714285714287, spacing=Poisson-like (⟨r⟩=0.39086312113148597)
tools/data/notte_20260319_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260319_0330.md:13:  percolation_var_0.55: r=1.0416666666666667, spacing=Poisson-like (⟨r⟩=0.42776762684503483)
tools/data/notte_20260319_0330.md:14:  percolation_var_0.65: r=0.9583333333333335, spacing=Poisson-like (⟨r⟩=0.4849181304650346)
tools/exp_physical_sr_residue_bounce.py:6:  physical A: quantum-chaotic GUE spectra
tools/exp_physical_sr_residue_bounce.py:7:  mathematical transducer: span-matched Poisson counter-boundary
tools/exp_physical_sr_residue_bounce.py:271:            "poisson_contrast": "span-matched Poisson null for each class/label",
tools/exp_physical_sr_residue_bounce.py:301:            "optional_expected_class": "class label used for grouped Poisson contrast and direct class contrast",
tools/exp_physical_sr_residue_bounce.py:311:            "poisson_contrast": "real spectrum versus span-matched Poisson null",
tools/exp_physical_sr_residue_bounce.py:327:            "GUE_unitary_no_time_reversal": {
tools/exp_physical_sr_residue_bounce.py:337:            "transfer": "SR,L1,triple_var pass from the mathematical deposit into a physical spectrum tester as component states against Poisson and, when classes exist, direct class contrast.",
tools/exp_physical_sr_residue_bounce.py:339:            "fall": "Tester falls if GOE/GUE direct SR separation disappears, if Poisson contrast absorbs all focus observables in chaotic classes, or if Anderson W6 keeps SR active under the declared threshold.",
tools/exp_physical_sr_residue_bounce.py:345:                "class-labeled input where direct_contrast is not separated on SR despite declared GOE/GUE classes",
tools/exp_physical_sr_residue_bounce.py:376:                    ("GUE_unitary_no_time_reversal", "beta_2_complex_hermitian", gue_levels(n, rng)),
tools/exp_physical_sr_residue_bounce.py:419:        gue_key = f"N{n}:GUE_unitary_no_time_reversal"
tools/exp_physical_sr_residue_bounce.py:421:        symmetry_contrasts[f"N{n}:GUE_minus_GOE"] = contrast(
tools/exp_physical_sr_residue_bounce.py:422:            f"N{n}:GUE_minus_GOE",
tools/exp_physical_sr_residue_bounce.py:428:    source = summaries["GUE_unitary_no_time_reversal"]
tools/exp_physical_sr_residue_bounce.py:439:        "physical_source": "quantum-chaotic spectra modeled by GOE and GUE symmetry classes",
tools/exp_physical_sr_residue_bounce.py:445:            "GUE": "Wigner-Dyson beta=2, complex Hermitian, no time-reversal symmetry",
tools/exp_physical_sr_residue_bounce.py:446:            "Poisson": "independent levels, span-matched finite sample null",
tools/data/observatorio/domandatore_unTouched_20260507_095914.md:23:  CLAIM: 8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/observatorio/domandatore_unTouched_20260507_095914.md:25:    [duale   ] LLM_BOUNDARY: Il duale di "8 domini GUE, 5 Poisson — il confine è il terzo
tools/data/observatorio/domandatore_unTouched_20260507_095914.md:26:    [confine ] BOUNDARY_BOUNDARY: Tra gli estremi del claim "8 domini GUE, 5 Poisson — il conf
tools/data/observatorio/domandatore_unTouched_20260507_095914.md:27:    [dominio ] DOMAIN_PHOTONIC_BOUNDARY: L'effetto "8 domini GUE, 5 Poisson — il confine è i" si mani
tools/data/observatorio/domandatore_unTouched_20260507_095914.md:28:    [rottura ] BREAK_BOUNDARY: Il claim "8 domini GUE, 5 Poisson — il confine è il terzo in
tools/data/observatorio/domandatore_unTouched_20260507_095914.md:29:    [scala   ] SCALE_BOUNDARY: L'effetto in "8 domini GUE, 5 Poisson — il confine è il terz
tools/dnd_engine.py:112:        'claim': 'Spacing zeta zeros è GUE (⟨r⟩≈0.615)',
tools/dnd_engine.py:120:        'claim': 'Domini si dividono in GUE (7) e Poisson (4)',
tools/dnd_engine.py:137:        'claim': 'Rule 30/110 hanno spacing anomalo (⟨r⟩≈0.86, >> GUE)',
tools/dnd_engine.py:176:        'claim': 'r_diretto NON separa GUE da Poisson (70% overlap)',
tools/dnd_engine.py:178:        'motivo': 'Esperimento order_parameter: GUE r=0.92±0.11, Poisson r=0.95±0.08. Ipotesi H_order_param eliminata.',
tools/dnd_engine.py:192:        'claim': 'φ emerge in 20/20 banchi di prova da teorie dimostrate: Wigner GOE/GUE, Feigenbaum, Fibonacci quasicrystal, KAM (12 valori K), cifre di π/e/√2/√5/φ/ln2.',
tools/dnd_engine.py:206:        'claim': 'ac1 (autocorrelazione intervalli D-ND) DISTINGUE deterministico da casuale: primi=-0.10, GUE=-0.20, shuffle=0.00, Poisson=+0.04.',
tools/dnd_engine.py:240:        'motivo': 'A V=2: invariante trace map I=(V/2)²-1=0, tutti gli autostati power-law. A V≈1: <r>=0.5 (statistica intermedia). A V=2: <r>=0.354 (Poisson).',
tools/dnd_engine.py:300:        'motivo': 'Correlazione log r=0.921 era ARTEFATTO di monotonia (random sorted r=0.89). Il ponte vero è STATISTICO: distribuzione spacing = GUE (β=2.007 a 100K zeri).',
tools/dnd_engine.py:302:        'correzione': 'La correlazione locale K_c↔t_n è spuria. Il ponte D-ND↔ζ è nella distribuzione degli spacing (GUE), non nella corrispondenza puntuale.',
tools/dnd_engine.py:314:        'claim': 'Il confine GUE/Poisson corrisponde al terzo incluso',
tools/dnd_engine.py:316:        'motivo': 'Se GUE=D e Poisson=ND, il confine è dove la scissione cambia natura.',
tools/dnd_engine.py:324:        'motivo': 'RISOLTO computazionalmente: <r>(V=2)=0.35 (Poisson). V=2 è il punto di inversione dell inerzia. L orbita converge a phi.',
tools/dnd_engine.py:476:        'claim': 'Gli spacing della mappa D-ND (Fibonacci) seguono GUE (p>0.05 KS). La classificazione GUE/GOE/Poisson e un metodo universale.',
tools/dnd_engine.py:584:    """Verifica che i domini si dividano naturalmente in cluster GUE/Poisson."""
tools/dnd_engine.py:814:    # V=1 dovrebbe dare <r>≈0.5, V=2 dovrebbe dare <r>≈0.35 (Poisson-like)
tools/dnd_engine.py:1528:    """Il confine GUE/Poisson nella famiglia det=-1 corrisponde al terzo incluso.
tools/dnd_engine.py:1529:    A V=V_c: <r> attraversa 0.5 (meta' tra GUE=0.53 e Poisson=0.39).
tools/dnd_engine.py:1909:    """Spacing dei rapporti Fibonacci: classifica GUE/GOE/Poisson con KS test."""
tools/dnd_engine.py:1928:    # GUE, GOE, Poisson CDFs
tools/dnd_engine.py:1940:    fits = [("GUE", ks_gue, p_gue), ("GOE", ks_goe, p_goe), ("Poisson", ks_poi, p_poi)]
tools/dnd_engine.py:2257:    'EXT5': {'root': 'metodologico', 'level': 'statistico'},      # classificazione GUE/GOE/Poisson (gap filled!)
tools/dnd_banchi_tm1.py:88:    "references": {{"GUE": 0.599, "GOE": 0.530, "Poisson": 0.386, "critical": 0.5}},
tools/data/observatorio/lazarus_cimitero_20260507_100015.md:31:- BOUNDARY 0.5 — confine GUE/Poisson (intensità ridotta perché in fase di consolidamento)
tools/data/preflight/manual_controlled_snapshot_20260515_1705_3616547/agent_field_live.md:73:- Non partire da phi, gap label, GUE o Poisson se sono gia' nel ciclo
tools/data/preflight/manual_controlled_snapshot_20260515_1705_3616547/agent_field_live.md:330:- GUE;
tools/data/preflight/manual_controlled_snapshot_20260515_1705_3616547/agent_field_live.md:333:- Poisson;
tools/data/preflight/manual_controlled_snapshot_20260515_1705_3616547/agent_field_live.md:353:- Poisson synthetic;
tools/data/preflight/manual_controlled_snapshot_20260515_1705_3616547/agent_field_live.md:354:- GUE synthetic;
tools/data/preflight/manual_controlled_snapshot_20260515_1705_3616547/agent_field_live.md:361:- GUE/Poisson e' spesso un piano di proiezione, non una sorgente. Se diventa
tools/data/preflight/manual_controlled_snapshot_20260515_1705_3616547/agent_field_live.md:600:GUE/Poisson/non-phi.
tools/data/preflight/manual_controlled_snapshot_20260515_1705_3616547/agent_field_live.md:607:- Punto-zero: confine prima che venga classificato GUE o Poisson.
tools/data/preflight/manual_controlled_snapshot_20260515_1705_3616547/agent_field_live.md:675:- concetti: terzo incluso, GUE/Poisson, non-phi generator, graph curvature,
tools/data/preflight/manual_controlled_snapshot_20260515_1705_3616547/agent_field_live.md:678:  precede la classificazione spettrale GUE/Poisson;
tools/data/preflight/manual_controlled_snapshot_20260515_1705_3616547/agent_field_live.md:725:  - "VALIDA contro baseline GUE, Poisson e generatori sintetici."
tools/data/preflight/manual_controlled_snapshot_20260515_1705_3616547/agent_field_live.md:727:  quando: "il ciclo lavora su boundary, GUE/Poisson o trasferibilita' phi"
tools/data/preflight/manual_controlled_snapshot_20260515_1705_3616547/agent_field_live.md:871:  quando: "prima del prossimo ciclo su GUE/Poisson/non-phi"
tools/data/preflight/manual_controlled_snapshot_20260515_1705_3616547/agent_field_live.md:1049:- [BOUNDARY] (0.8)  8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/manual_controlled_snapshot_20260515_1705_3616547/agent_field_live.md:1094:aver dichiarato che "GUE is also dynamic under M". Il setup C1 era
tools/data/preflight/manual_controlled_snapshot_20260515_1705_3616547/agent_field_live.md:1096:ha mostrato GUE dinamico — la conclusione ha riformulato silenziosamente
tools/data/preflight/manual_controlled_snapshot_20260515_1705_3616547/agent_field_live.md:1202:Un pattern in distribuzioni classiche (primi, GUE, random walk, Markov chain, gap statistics) probabilmente ha un nome. Default hypothesis: re-discovery / caso limite di teorema noto. Prima di taggare 'NEW', cerca il risultato classico piu' vicino (es. Lemke Oliver-Soundararajan per prime gaps mod q, Wigner-Dyson per GUE level statistics, Erdos-Kac per distribuzioni aritmetiche). Se non lo trovi, dichiara la ricerca esplicitamente.
tools/data/notte_20260330_0330.md:4:  Direzione: Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo in
tools/data/notte_20260330_0330.md:5:  [confine_inesplorato] BOUNDARY: 8 domini GUE, 5 Poisson — il confine è il terzo incluso oper
tools/data/notte_20260330_0330.md:10:  logistica_biforcazione_cp_3.57: r=1.0, spacing=Poisson-like [conferma]
tools/data/notte_20260330_0330.md:12:  logistica_biforcazione_cp_3.943: r=1.0005621135469365, spacing=Poisson-like [conferma]
tools/data/notte_20260330_0330.md:13:  logistica_biforcazione_cp_3.856: r=1.0006002400960383, spacing=Poisson-like [conferma]
tools/data/notte_20260330_0330.md:14:  numeri_primi_cp_100: r=0.762330388277594, spacing=GUE-like [conferma]
tools/data/notte_20260330_0330.md:15:  numeri_primi_cp_50000: r=0.762330388277594, spacing=GUE-like [conferma]
tools/data/notte_20260330_0330.md:17:  collatz_cp: r=0.8806939542029114, spacing=GUE-like [conferma]
tools/data/notte_20260330_0330.md:19:  ising_2d_cp_-0.33516931196973065: r=0.8717156475928174, spacing=GUE-like [conferma]
tools/data/notte_20260330_0330.md:20:  ising_2d_cp_0.373927156938713: r=1.0790780588232907, spacing=GUE-like [conferma]
tools/data/notte_20260330_0330.md:21:  brownian_motion_cp_0.1117838189033273: r=1.0, spacing=Poisson-like [conferma]
tools/data/notte_20260330_0330.md:22:  brownian_motion_cp_0.873133582163594: r=0.006027122049221497, spacing=Poisson-like [conferma]
tools/data/notte_20260330_0330.md:23:  percolation_cp_0.37948306996505765: r=0.8834019204389575, spacing=GUE-like [conferma]
tools/data/notte_20260330_0330.md:24:  percolation_cp_0.8262244331338678: r=1.0858944050433412, spacing=GUE-like [conferma]
tools/data/notte_20260330_0330.md:25:  cellular_automata_cp_182: r=0.8566654288897141, spacing=GUE-like [conferma]
tools/data/notte_20260330_0330.md:35:  Campo dopo Fase 0: 8 GUE / 5 Poisson
tools/data/notte_20260330_0330.md:39:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260330_0330.md:40:  ising_2d_var_-0.1: r=0.9623588559918834, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260330_0330.md:41:  ising_2d_var_0.1: r=1.1835938346968764, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260330_0330.md:42:  percolation_var_0.55: r=0.96, spacing=Poisson-like (⟨r⟩=0.42970141187888167)
tools/data/notte_20260330_0330.md:97:  GUE: 10 | Poisson: 7 | Vincoli: 1 | Anomalie: 22
tools/data/notte_20260330_0330.md:99:  Domini GUE: numeri_primi_cp_100, numeri_primi_cp_50000, collatz_cp, ising_2d_cp_-0.33516931196973065, ising_2d_cp_0.373927156938713, percolation_cp_0.37948306996505765, percolation_cp_0.8262244331338678, cellular_automata_cp_182, ising_2d_var_-0.1, ising_2d_var_0.1
tools/data/notte_20260330_0330.md:100:  Domini Poisson: logistica_biforcazione_cp_3.57, logistica_biforcazione_cp_3.943, logistica_biforcazione_cp_3.856, brownian_motion_cp_0.1117838189033273, brownian_motion_cp_0.873133582163594, logistica_biforcazione_var_3.57, percolation_var_0.55
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:15:- report: `Agent Report - Rafforzamento tester fisico GOE/GUE N-curve`
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:57:- matematica: osservabili canonici `SR`, `L1`, `triple_var` contro null Poisson
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:61:- se il ciclo devia dalla direzione GOE/GUE/Poisson-Anderson, deve dichiarare
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:152:- disc_5: 3 ghost · Metrica primi g=(p/2)², curvatura GUE r=0.503
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:154:- report_20260514_1640: 2 ghost · Agent Report - Rafforzamento tester fisico GOE/GUE N-curve
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:156:**Direzione seme da respirare**: Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:159:- Direzione viva del seme: Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:162:- Nota: Continuare sul confine GUE/Poisson, ma il prossimo atto deve falsificare la grammatica come livello strutturale e ridurre il rischio di timeout/tool-density prima di estendere il dominio.
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:191:- Non partire da phi, gap label, GUE o Poisson se sono gia' nel ciclo
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:448:- GUE;
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:451:- Poisson;
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:471:- Poisson synthetic;
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:472:- GUE synthetic;
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:479:- GUE/Poisson e' spesso un piano di proiezione, non una sorgente. Se diventa
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:718:GUE/Poisson/non-phi.
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:725:- Punto-zero: confine prima che venga classificato GUE o Poisson.
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:793:- concetti: terzo incluso, GUE/Poisson, non-phi generator, graph curvature,
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:796:  precede la classificazione spettrale GUE/Poisson;
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:843:  - "VALIDA contro baseline GUE, Poisson e generatori sintetici."
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:845:  quando: "il ciclo lavora su boundary, GUE/Poisson o trasferibilita' phi"
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:989:  quando: "prima del prossimo ciclo su GUE/Poisson/non-phi"
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:1049:- **Direzione corrente**: Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:1171:## Piano 126 — Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:1176:- [BOUNDARY] (0.8)  8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:1227:aver dichiarato che "GUE is also dynamic under M". Il setup C1 era
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:1229:ha mostrato GUE dinamico — la conclusione ha riformulato silenziosamente
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:1320:  disc_5 (3 ghost): Metrica primi g=(p/2)², curvatura GUE r=0.503
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:1322:  report_20260514_1640 (2 ghost): Agent Report - Rafforzamento tester fisico GOE/GUE N-curve
tools/data/preflight/agent_field_live_backup_pre_authority_replace_20260515_1620.md:1342:Un pattern in distribuzioni classiche (primi, GUE, random walk, Markov chain, gap statistics) probabilmente ha un nome. Default hypothesis: re-discovery / caso limite di teorema noto. Prima di taggare 'NEW', cerca il risultato classico piu' vicino (es. Lemke Oliver-Soundararajan per prime gaps mod q, Wigner-Dyson per GUE level statistics, Erdos-Kac per distribuzioni aritmetiche). Se non lo trovi, dichiara la ricerca esplicitamente.
tools/data/cognitive_enzymes_archive.md:453:Direzione corrente: confine come terzo incluso operativo sui domini GUE/Poisson/non-phi. Possibile risultante da respirare: - D-ND: terzo incluso come punto-zero tra repulsione e indipendenza. - Operatori: graph curvature + spectral rigidity + non-phi generator control. - Dipolo: core congiunto / residuo singolo. - Punto-zero: confine prima che venga classif
tools/data/notte_20260327_0330.md:5:  collatz_cp: r=0.8508641059533701, spacing=GUE-like [conferma]
tools/data/notte_20260327_0330.md:8:  ising_2d_cp_-0.3178017009693843: r=0.8845781753332519, spacing=GUE-like [conferma]
tools/data/notte_20260327_0330.md:9:  ising_2d_cp_0.3248839750133819: r=1.0167920779333788, spacing=GUE-like [conferma]
tools/data/notte_20260327_0330.md:10:  brownian_motion_cp_0.1217926516506662: r=1.0040983606557377, spacing=Poisson-like [conferma]
tools/data/notte_20260327_0330.md:11:  brownian_motion_cp_0.9281113282142441: r=0.06044538706256628, spacing=Poisson-like [conferma]
tools/data/notte_20260327_0330.md:12:  percolation_cp_0.34229600726639825: r=0.7857142857142856, spacing=GUE-like [conferma]
tools/data/notte_20260327_0330.md:13:  percolation_cp_0.831925673522525: r=0.9963689179375455, spacing=GUE-like [conferma]
tools/data/notte_20260327_0330.md:14:  cellular_automata_cp_182: r=0.8566654288897141, spacing=GUE-like [conferma]
tools/data/notte_20260327_0330.md:24:  Campo dopo Fase 0: 6 GUE / 2 Poisson
tools/data/notte_20260327_0330.md:28:  ising_2d_var_0.1: r=1.0130065542039273, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260327_0330.md:29:  ising_2d_var_-0.1: r=0.9847589635578355, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260327_0330.md:30:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260327_0330.md:31:  percolation_var_0.65: r=0.997737556561086, spacing=Poisson-like (⟨r⟩=0.45347668815741615)
tools/data/notte_20260327_0330.md:78:  GUE: 9 | Poisson: 3 | Vincoli: 1 | Anomalie: 17
tools/data/notte_20260327_0330.md:80:  Domini GUE: collatz_cp, ising_2d_cp_-0.3178017009693843, ising_2d_cp_0.3248839750133819, percolation_cp_0.34229600726639825, percolation_cp_0.831925673522525, cellular_automata_cp_182, ising_2d_var_0.1, ising_2d_var_-0.1, cellular_automata_var_30
tools/data/notte_20260327_0330.md:81:  Domini Poisson: brownian_motion_cp_0.1217926516506662, brownian_motion_cp_0.9281113282142441, percolation_var_0.65
tools/data/notte_20260402_0330.md:4:  Direzione: Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo in
tools/data/notte_20260402_0330.md:5:  [confine_inesplorato] BOUNDARY: 8 domini GUE, 5 Poisson — il confine è il terzo incluso oper
tools/data/notte_20260402_0330.md:14:  logistica_biforcazione_cp_3.57: r=1.0, spacing=Poisson-like [VINCOLO] [NULL:non-disc]
tools/data/notte_20260402_0330.md:16:  logistica_biforcazione_cp_3.968: r=1.0, spacing=Poisson-like [VINCOLO] [NULL:non-disc]
tools/data/notte_20260402_0330.md:17:  logistica_biforcazione_cp_3.64: r=1.0, spacing=Poisson-like [VINCOLO] [NULL:non-disc]
tools/data/notte_20260402_0330.md:18:  numeri_primi_cp_100: r=0.762330388277594, spacing=GUE-like [conferma]
tools/data/notte_20260402_0330.md:19:  numeri_primi_cp_50000: r=0.762330388277594, spacing=GUE-like [conferma]
tools/data/notte_20260402_0330.md:21:  collatz_cp: r=0.8355359765051396, spacing=GUE-like [conferma]
tools/data/notte_20260402_0330.md:23:  ising_2d_cp_-0.3712031488689398: r=0.9674222541586771, spacing=GUE-like [conferma]
tools/data/notte_20260402_0330.md:24:  ising_2d_cp_0.47729231017254603: r=0.9862411420886567, spacing=GUE-like [conferma]
tools/data/notte_20260402_0330.md:25:  brownian_motion_cp_0.11504351980504385: r=1.034587995930824, spacing=Poisson-like [VINCOLO] [NULL:non-disc]
tools/data/notte_20260402_0330.md:26:  brownian_motion_cp_0.8057291190852911: r=1.0, spacing=Poisson-like [VINCOLO] [NULL:non-disc]
tools/data/notte_20260402_0330.md:27:  percolation_cp_0.3688634741499579: r=0.6949152542372881, spacing=GUE-like [conferma]
tools/data/notte_20260402_0330.md:28:  percolation_cp_0.8740825474500551: r=0.923076923076923, spacing=GUE-like [conferma]
tools/data/notte_20260402_0330.md:29:  cellular_automata_cp_150: r=0.7452006980802792, spacing=GUE-like [conferma]
tools/data/notte_20260402_0330.md:41:  Campo dopo Fase 0: 8 GUE / 5 Poisson
tools/data/notte_20260402_0330.md:45:  percolation_var_0.55: r=0.7543859649122806, spacing=Poisson-like (⟨r⟩=0.41299868689639513) [NULL:non-disc]
tools/data/notte_20260402_0330.md:46:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260402_0330.md:47:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286) [NULL:non-disc]
tools/data/notte_20260402_0330.md:48:  brownian_motion_var_0.5: r=1.0, spacing=Poisson-like (⟨r⟩=0.3861050747726014) [NULL:non-disc]
tools/data/notte_20260402_0330.md:103:  GUE: 9 | Poisson: 8 | Vincoli: 17 | Anomalie: 13
tools/data/notte_20260402_0330.md:105:  Domini GUE: numeri_primi_cp_100, numeri_primi_cp_50000, collatz_cp, ising_2d_cp_-0.3712031488689398, ising_2d_cp_0.47729231017254603, percolation_cp_0.3688634741499579, percolation_cp_0.8740825474500551, cellular_automata_cp_150, numeri_primi_var_100000
tools/data/notte_20260402_0330.md:106:  Domini Poisson: logistica_biforcazione_cp_3.57, logistica_biforcazione_cp_3.968, logistica_biforcazione_cp_3.64, brownian_motion_cp_0.11504351980504385, brownian_motion_cp_0.8057291190852911, percolation_var_0.55, coupled_oscillators_var_50, brownian_motion_var_0.5
tools/lab_falsifier.py:20:  L5  Re-discovery vs discovery (literature)     → A8 (autologica)
tools/lab_falsifier.py:80:### L5 — Re-discovery vs discovery (A8 autologica)
tools/lab_falsifier.py:81:Un pattern in distribuzioni classiche (primi, GUE, random walk, Markov chain, gap statistics) probabilmente ha un nome. Default hypothesis: re-discovery / caso limite di teorema noto. Taggare come "NEW" senza riconoscere il risultato classico piu' vicino (es. Lemke Oliver–Soundararajan per prime gaps mod q) e' beauty bias. Flag.
tools/lab_falsifier.py:106:GUE/Poisson, flagga il drift. La deviazione e' ammessa solo se dichiarata come
tools/data/preflight/agent_field_live_candidate_scientific_return_20260515_1706.md:73:- Non partire da phi, gap label, GUE o Poisson se sono gia' nel ciclo
tools/data/preflight/agent_field_live_candidate_scientific_return_20260515_1706.md:330:- GUE;
tools/data/preflight/agent_field_live_candidate_scientific_return_20260515_1706.md:333:- Poisson;
tools/data/preflight/agent_field_live_candidate_scientific_return_20260515_1706.md:353:- Poisson synthetic;
tools/data/preflight/agent_field_live_candidate_scientific_return_20260515_1706.md:354:- GUE synthetic;
tools/data/preflight/agent_field_live_candidate_scientific_return_20260515_1706.md:361:- GUE/Poisson e' spesso un piano di proiezione, non una sorgente. Se diventa
tools/data/preflight/agent_field_live_candidate_scientific_return_20260515_1706.md:600:GUE/Poisson/non-phi.
tools/data/preflight/agent_field_live_candidate_scientific_return_20260515_1706.md:607:- Punto-zero: confine prima che venga classificato GUE o Poisson.
tools/data/preflight/agent_field_live_candidate_scientific_return_20260515_1706.md:675:- concetti: terzo incluso, GUE/Poisson, non-phi generator, graph curvature,
tools/data/preflight/agent_field_live_candidate_scientific_return_20260515_1706.md:678:  precede la classificazione spettrale GUE/Poisson;
tools/data/preflight/agent_field_live_candidate_scientific_return_20260515_1706.md:725:  - "VALIDA contro baseline GUE, Poisson e generatori sintetici."
tools/data/preflight/agent_field_live_candidate_scientific_return_20260515_1706.md:727:  quando: "il ciclo lavora su boundary, GUE/Poisson o trasferibilita' phi"
tools/data/preflight/agent_field_live_candidate_scientific_return_20260515_1706.md:871:  quando: "prima del prossimo ciclo su GUE/Poisson/non-phi"
tools/data/preflight/agent_field_live_candidate_scientific_return_20260515_1706.md:1049:- [BOUNDARY] (0.8)  8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/agent_field_live_candidate_scientific_return_20260515_1706.md:1094:aver dichiarato che "GUE is also dynamic under M". Il setup C1 era
tools/data/preflight/agent_field_live_candidate_scientific_return_20260515_1706.md:1096:ha mostrato GUE dinamico — la conclusione ha riformulato silenziosamente
tools/data/preflight/agent_field_live_candidate_scientific_return_20260515_1706.md:1202:Un pattern in distribuzioni classiche (primi, GUE, random walk, Markov chain, gap statistics) probabilmente ha un nome. Default hypothesis: re-discovery / caso limite di teorema noto. Prima di taggare 'NEW', cerca il risultato classico piu' vicino (es. Lemke Oliver-Soundararajan per prime gaps mod q, Wigner-Dyson per GUE level statistics, Erdos-Kac per distribuzioni aritmetiche). Se non lo trovi, dichiara la ricerca esplicitamente.
tools/data/preflight/preflight_20260515_1623.md:10:- stable_anchor: `20260514_1640` - Agent Report - Rafforzamento tester fisico GOE/GUE N-curve
tools/data/preflight/preflight_20260514_1701.md:10:- stable_anchor: `20260514_1640` - Agent Report - Rafforzamento tester fisico GOE/GUE N-curve
tools/data/preflight/preflight_20260514_1701.md:18:- L8 medium: Dichiarare `deliberate_counter_perimeter=true` oppure aggiungere un check minimo GUE/Poisson esplicito: nearest-neighbor spacing/r-statistics sui tre generatori e mapping verso GUE/Poisson, con confronto ai domini richiesti dal seme.
tools/exp_spectral_landscape.py:6:Identifies: GUE domains, Poisson domains, and BOUNDARY domains.
tools/exp_spectral_landscape.py:8:The question: Is the boundary between GUE and Poisson populated by multiple
tools/exp_spectral_landscape.py:52:    """GUE: eigenvalue spacings of complex Hermitian random matrix."""
tools/exp_spectral_landscape.py:100:    """Poisson: uncorrelated exponential spacings."""
tools/exp_spectral_landscape.py:111:    """Semi-Poisson: P(s) = 4s*exp(-2s). Known intermediate statistics."""
tools/exp_spectral_landscape.py:123:    """Berry-Robnik: mixed system, fraction rho chaotic (GUE-like), 1-rho regular (Poisson).
tools/exp_spectral_landscape.py:162:    """Model for Riemann zeta zeros — Montgomery pair correlation (GUE).
tools/exp_spectral_landscape.py:163:    We use GUE directly since computing actual zeros is expensive."""
tools/exp_spectral_landscape.py:303:    GUE = 0.603
tools/exp_spectral_landscape.py:308:    elif r_mean < (GOE + GUE) / 2:
tools/exp_spectral_landscape.py:310:    elif r_mean < GUE + 0.03:
tools/exp_spectral_landscape.py:311:        return "GUE-like"
tools/exp_spectral_landscape.py:327:        # Expected GUE
tools/exp_spectral_landscape.py:328:        ("GUE_matrix", gen_gue, {}),
tools/exp_spectral_landscape.py:334:        # Expected Poisson
tools/exp_spectral_landscape.py:335:        ("Poisson", gen_poisson, {}),
tools/exp_spectral_landscape.py:342:        ("semi_Poisson", gen_semi_poisson, {}),
tools/exp_spectral_landscape.py:369:    print("\nReference: Poisson <r>=0.386, GOE <r>=0.536, GUE <r>=0.603")
tools/exp_spectral_landscape.py:380:    for cls in ["POISSON", "BOUNDARY_low", "GOE-like", "GUE-like", "RIGID"]:
tools/dnd_banchi.py:371:        'desc': '<r> ratio statistic (0.386=Poisson, 0.530=GOE, 0.599=GUE)',
tools/exp_boundary_two_axis_matrix.py:6:does not use GUE/Poisson source labels: it only reads transfer support and beta
tools/exp_boundary_two_axis_matrix.py:107:        "question": "Separate support_transfer from beta_coordinate_transfer on the 13 semi-real BOUNDARY rows without using GUE/Poisson labels.",
tools/exp_boundary_two_axis_matrix.py:120:        "label_policy": "GUE/Poisson source labels are not read by this operator.",
tools/exp_boundary_bridge_stability_audit.py:5:The audit keeps the 13 row-aligned GUE/Poisson denominator and reruns the graph
tools/exp_boundary_bridge_stability_audit.py:65:    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
tools/exp_boundary_bridge_stability_audit.py:163:    stable_graph_only = [
tools/exp_boundary_bridge_stability_audit.py:166:        if row["stability_state"] == "stable_graph_bridge" and row["classical_audit_state"] == "graph_only_bridge"
tools/exp_boundary_bridge_stability_audit.py:209:            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid",
tools/exp_boundary_bridge_stability_audit.py:217:            "stable_graph_only": stable_graph_only,
tools/exp_boundary_bridge_stability_audit.py:220:            "lab_residue_after_stability": bool(stable_graph_only or classic_only_stable_graph_absent),
tools/data/preflight/preflight_20260515_1705.md:10:- stable_anchor: `20260514_1640` - Agent Report - Rafforzamento tester fisico GOE/GUE N-curve
tools/exp_det_drift.py:8:  If primes drift toward Poisson, det(M) should drift toward +1.
tools/data/preflight/agent_field_live_candidate_20260515_1624.md:70:- Non partire da phi, gap label, GUE o Poisson se sono gia' nel ciclo
tools/data/preflight/agent_field_live_candidate_20260515_1624.md:327:- GUE;
tools/data/preflight/agent_field_live_candidate_20260515_1624.md:330:- Poisson;
tools/data/preflight/agent_field_live_candidate_20260515_1624.md:350:- Poisson synthetic;
tools/data/preflight/agent_field_live_candidate_20260515_1624.md:351:- GUE synthetic;
tools/data/preflight/agent_field_live_candidate_20260515_1624.md:358:- GUE/Poisson e' spesso un piano di proiezione, non una sorgente. Se diventa
tools/data/preflight/agent_field_live_candidate_20260515_1624.md:597:GUE/Poisson/non-phi.
tools/data/preflight/agent_field_live_candidate_20260515_1624.md:604:- Punto-zero: confine prima che venga classificato GUE o Poisson.
tools/data/preflight/agent_field_live_candidate_20260515_1624.md:672:- concetti: terzo incluso, GUE/Poisson, non-phi generator, graph curvature,
tools/data/preflight/agent_field_live_candidate_20260515_1624.md:675:  precede la classificazione spettrale GUE/Poisson;
tools/data/preflight/agent_field_live_candidate_20260515_1624.md:722:  - "VALIDA contro baseline GUE, Poisson e generatori sintetici."
tools/data/preflight/agent_field_live_candidate_20260515_1624.md:724:  quando: "il ciclo lavora su boundary, GUE/Poisson o trasferibilita' phi"
tools/data/preflight/agent_field_live_candidate_20260515_1624.md:868:  quando: "prima del prossimo ciclo su GUE/Poisson/non-phi"
tools/data/preflight/agent_field_live_candidate_20260515_1624.md:1050:- [BOUNDARY] (0.8)  8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/agent_field_live_candidate_20260515_1624.md:1095:aver dichiarato che "GUE is also dynamic under M". Il setup C1 era
tools/data/preflight/agent_field_live_candidate_20260515_1624.md:1097:ha mostrato GUE dinamico — la conclusione ha riformulato silenziosamente
tools/data/preflight/agent_field_live_candidate_20260515_1624.md:1203:Un pattern in distribuzioni classiche (primi, GUE, random walk, Markov chain, gap statistics) probabilmente ha un nome. Default hypothesis: re-discovery / caso limite di teorema noto. Prima di taggare 'NEW', cerca il risultato classico piu' vicino (es. Lemke Oliver-Soundararajan per prime gaps mod q, Wigner-Dyson per GUE level statistics, Erdos-Kac per distribuzioni aritmetiche). Se non lo trovi, dichiara la ricerca esplicitamente.
tools/exp_spectral_2d.py:17:4. Anti-correlated Poisson: Poisson gaps with imposed negative acf1 →
tools/exp_spectral_2d.py:174:    Model: Poisson gaps, but forced to alternate large-small.
tools/exp_spectral_2d.py:327:        ("GUE", lambda n: gen_gue(n)),
tools/exp_spectral_2d.py:328:        ("Poisson", lambda n: np.random.exponential(1.0, n)),
tools/data/preflight/manual_controlled_snapshot_20260515_1647_3597707/agent_field_live.md:70:- Non partire da phi, gap label, GUE o Poisson se sono gia' nel ciclo
tools/data/preflight/manual_controlled_snapshot_20260515_1647_3597707/agent_field_live.md:327:- GUE;
tools/data/preflight/manual_controlled_snapshot_20260515_1647_3597707/agent_field_live.md:330:- Poisson;
tools/data/preflight/manual_controlled_snapshot_20260515_1647_3597707/agent_field_live.md:350:- Poisson synthetic;
tools/data/preflight/manual_controlled_snapshot_20260515_1647_3597707/agent_field_live.md:351:- GUE synthetic;
tools/data/preflight/manual_controlled_snapshot_20260515_1647_3597707/agent_field_live.md:358:- GUE/Poisson e' spesso un piano di proiezione, non una sorgente. Se diventa
tools/data/preflight/manual_controlled_snapshot_20260515_1647_3597707/agent_field_live.md:597:GUE/Poisson/non-phi.
tools/data/preflight/manual_controlled_snapshot_20260515_1647_3597707/agent_field_live.md:604:- Punto-zero: confine prima che venga classificato GUE o Poisson.
tools/data/preflight/manual_controlled_snapshot_20260515_1647_3597707/agent_field_live.md:672:- concetti: terzo incluso, GUE/Poisson, non-phi generator, graph curvature,
tools/data/preflight/manual_controlled_snapshot_20260515_1647_3597707/agent_field_live.md:675:  precede la classificazione spettrale GUE/Poisson;
tools/data/preflight/manual_controlled_snapshot_20260515_1647_3597707/agent_field_live.md:722:  - "VALIDA contro baseline GUE, Poisson e generatori sintetici."
tools/data/preflight/manual_controlled_snapshot_20260515_1647_3597707/agent_field_live.md:724:  quando: "il ciclo lavora su boundary, GUE/Poisson o trasferibilita' phi"
tools/data/preflight/manual_controlled_snapshot_20260515_1647_3597707/agent_field_live.md:868:  quando: "prima del prossimo ciclo su GUE/Poisson/non-phi"
tools/data/preflight/manual_controlled_snapshot_20260515_1647_3597707/agent_field_live.md:1049:- [BOUNDARY] (0.8)  8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/manual_controlled_snapshot_20260515_1647_3597707/agent_field_live.md:1094:aver dichiarato che "GUE is also dynamic under M". Il setup C1 era
tools/data/preflight/manual_controlled_snapshot_20260515_1647_3597707/agent_field_live.md:1096:ha mostrato GUE dinamico — la conclusione ha riformulato silenziosamente
tools/data/preflight/manual_controlled_snapshot_20260515_1647_3597707/agent_field_live.md:1202:Un pattern in distribuzioni classiche (primi, GUE, random walk, Markov chain, gap statistics) probabilmente ha un nome. Default hypothesis: re-discovery / caso limite di teorema noto. Prima di taggare 'NEW', cerca il risultato classico piu' vicino (es. Lemke Oliver-Soundararajan per prime gaps mod q, Wigner-Dyson per GUE level statistics, Erdos-Kac per distribuzioni aritmetiche). Se non lo trovi, dichiara la ricerca esplicitamente.
tools/data/preflight/agent_field_live_candidate_20260515_1608.md:41:- Perche': Il ciclo ha prodotto una direzione viva ma il seme ora devia verso frame GUE/Poisson, mentre l'evidenza appena maturata punta al nodo regressivo del contratto: `verdict()` resta centrato su `has_SR` e il micro-trace e' incompleto. La prossima mossa deve trasformare il residuo `prime_minus_mod6_z_vector(SR,L1,triple_var)` in osservabile dedicato e falsificarlo contro antagonisti piu duri, non riaprire un frame globale gia' a rischio accumulo numerico locale.
tools/data/preflight/agent_field_live_candidate_20260515_1608.md:71:- Non partire da phi, gap label, GUE o Poisson se sono gia' nel ciclo
tools/data/preflight/agent_field_live_candidate_20260515_1608.md:328:- GUE;
tools/data/preflight/agent_field_live_candidate_20260515_1608.md:331:- Poisson;
tools/data/preflight/agent_field_live_candidate_20260515_1608.md:351:- Poisson synthetic;
tools/data/preflight/agent_field_live_candidate_20260515_1608.md:352:- GUE synthetic;
tools/data/preflight/agent_field_live_candidate_20260515_1608.md:359:- GUE/Poisson e' spesso un piano di proiezione, non una sorgente. Se diventa
tools/data/preflight/agent_field_live_candidate_20260515_1608.md:598:GUE/Poisson/non-phi.
tools/data/preflight/agent_field_live_candidate_20260515_1608.md:605:- Punto-zero: confine prima che venga classificato GUE o Poisson.
tools/data/preflight/agent_field_live_candidate_20260515_1608.md:673:- concetti: terzo incluso, GUE/Poisson, non-phi generator, graph curvature,
tools/data/preflight/agent_field_live_candidate_20260515_1608.md:676:  precede la classificazione spettrale GUE/Poisson;
tools/data/preflight/agent_field_live_candidate_20260515_1608.md:723:  - "VALIDA contro baseline GUE, Poisson e generatori sintetici."
tools/data/preflight/agent_field_live_candidate_20260515_1608.md:725:  quando: "il ciclo lavora su boundary, GUE/Poisson o trasferibilita' phi"
tools/data/preflight/agent_field_live_candidate_20260515_1608.md:869:  quando: "prima del prossimo ciclo su GUE/Poisson/non-phi"
tools/data/preflight/agent_field_live_candidate_20260515_1608.md:1054:- [BOUNDARY] (0.8)  8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/agent_field_live_candidate_20260515_1608.md:1099:aver dichiarato che "GUE is also dynamic under M". Il setup C1 era
tools/data/preflight/agent_field_live_candidate_20260515_1608.md:1101:ha mostrato GUE dinamico — la conclusione ha riformulato silenziosamente
tools/data/preflight/agent_field_live_candidate_20260515_1608.md:1207:Un pattern in distribuzioni classiche (primi, GUE, random walk, Markov chain, gap statistics) probabilmente ha un nome. Default hypothesis: re-discovery / caso limite di teorema noto. Prima di taggare 'NEW', cerca il risultato classico piu' vicino (es. Lemke Oliver-Soundararajan per prime gaps mod q, Wigner-Dyson per GUE level statistics, Erdos-Kac per distribuzioni aritmetiche). Se non lo trovi, dichiara la ricerca esplicitamente.
tools/data/notte_20260303_0330.md:4:  ising_2d_var_-0.1: r=0.999764483716038, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260303_0330.md:5:  ising_2d_var_0.1: r=0.9962779970953693, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260303_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260303_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260303_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260303_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260303_0330.md:10:  brownian_motion_var_0.3: r=0.9895833333333333, spacing=Poisson-like (⟨r⟩=0.39037158456490495)
tools/data/notte_20260303_0330.md:11:  brownian_motion_var_0.5: r=1.0, spacing=Poisson-like (⟨r⟩=0.39619010656660225)
tools/data/notte_20260303_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260303_0330.md:13:  percolation_var_0.55: r=1.0, spacing=Poisson-like (⟨r⟩=0.43344476133517995)
tools/data/notte_20260303_0330.md:14:  percolation_var_0.65: r=1.380952380952381, spacing=Poisson-like (⟨r⟩=0.45002661025974017)
tools/data/preflight/agent_field_live_candidate_20260515_1612.md:41:- Perche': Il ciclo ha prodotto una direzione viva ma il seme ora devia verso frame GUE/Poisson, mentre l'evidenza appena maturata punta al nodo regressivo del contratto: `verdict()` resta centrato su `has_SR` e il micro-trace e' incompleto. La prossima mossa deve trasformare il residuo `prime_minus_mod6_z_vector(SR,L1,triple_var)` in osservabile dedicato e falsificarlo contro antagonisti piu duri, non riaprire un frame globale gia' a rischio accumulo numerico locale.
tools/data/preflight/agent_field_live_candidate_20260515_1612.md:71:- Non partire da phi, gap label, GUE o Poisson se sono gia' nel ciclo
tools/data/preflight/agent_field_live_candidate_20260515_1612.md:328:- GUE;
tools/data/preflight/agent_field_live_candidate_20260515_1612.md:331:- Poisson;
tools/data/preflight/agent_field_live_candidate_20260515_1612.md:351:- Poisson synthetic;
tools/data/preflight/agent_field_live_candidate_20260515_1612.md:352:- GUE synthetic;
tools/data/preflight/agent_field_live_candidate_20260515_1612.md:359:- GUE/Poisson e' spesso un piano di proiezione, non una sorgente. Se diventa
tools/data/preflight/agent_field_live_candidate_20260515_1612.md:598:GUE/Poisson/non-phi.
tools/data/preflight/agent_field_live_candidate_20260515_1612.md:605:- Punto-zero: confine prima che venga classificato GUE o Poisson.
tools/data/preflight/agent_field_live_candidate_20260515_1612.md:673:- concetti: terzo incluso, GUE/Poisson, non-phi generator, graph curvature,
tools/data/preflight/agent_field_live_candidate_20260515_1612.md:676:  precede la classificazione spettrale GUE/Poisson;
tools/data/preflight/agent_field_live_candidate_20260515_1612.md:723:  - "VALIDA contro baseline GUE, Poisson e generatori sintetici."
tools/data/preflight/agent_field_live_candidate_20260515_1612.md:725:  quando: "il ciclo lavora su boundary, GUE/Poisson o trasferibilita' phi"
tools/data/preflight/agent_field_live_candidate_20260515_1612.md:869:  quando: "prima del prossimo ciclo su GUE/Poisson/non-phi"
tools/data/preflight/agent_field_live_candidate_20260515_1612.md:1054:- [BOUNDARY] (0.8)  8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/agent_field_live_candidate_20260515_1612.md:1099:aver dichiarato che "GUE is also dynamic under M". Il setup C1 era
tools/data/preflight/agent_field_live_candidate_20260515_1612.md:1101:ha mostrato GUE dinamico — la conclusione ha riformulato silenziosamente
tools/data/preflight/agent_field_live_candidate_20260515_1612.md:1207:Un pattern in distribuzioni classiche (primi, GUE, random walk, Markov chain, gap statistics) probabilmente ha un nome. Default hypothesis: re-discovery / caso limite di teorema noto. Prima di taggare 'NEW', cerca il risultato classico piu' vicino (es. Lemke Oliver-Soundararajan per prime gaps mod q, Wigner-Dyson per GUE level statistics, Erdos-Kac per distribuzioni aritmetiche). Se non lo trovi, dichiara la ricerca esplicitamente.
tools/data/operator_directives_consumed/operator_directive_20260514_1850.md:11:- report: `Agent Report - Rafforzamento tester fisico GOE/GUE N-curve`
tools/data/operator_directives_consumed/operator_directive_20260514_1850.md:53:- matematica: osservabili canonici `SR`, `L1`, `triple_var` contro null Poisson
tools/data/operator_directives_consumed/operator_directive_20260514_1850.md:57:- se il ciclo devia dalla direzione GOE/GUE/Poisson-Anderson, deve dichiarare
tools/data/operator_directives_consumed/operator_directive_20260514_1649.md:60:output: component_state + contrasto Poisson + contrasto diretto se classi presenti
tools/data/notte_20260312_0330.md:4:  ising_2d_var_-0.1: r=0.9908745521490876, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260312_0330.md:5:  ising_2d_var_0.1: r=1.6755418202488377, spacing=GUE-like (⟨r⟩=1.0)
tools/data/notte_20260312_0330.md:6:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260312_0330.md:7:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260312_0330.md:8:  logistica_biforcazione_var_3.9: r=1.000545256270447, spacing=Poisson-like (⟨r⟩=0.3914428894887236)
tools/data/notte_20260312_0330.md:9:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260312_0330.md:10:  brownian_motion_var_0.3: r=0.22824007651841227, spacing=Poisson-like (⟨r⟩=0.38919713749813906)
tools/data/notte_20260312_0330.md:11:  brownian_motion_var_0.5: r=1.121711664935964, spacing=Poisson-like (⟨r⟩=0.3831196498969426)
tools/data/notte_20260312_0330.md:12:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/notte_20260312_0330.md:13:  percolation_var_0.55: r=1.037037037037037, spacing=Poisson-like (⟨r⟩=0.41840391114320896)
tools/data/notte_20260312_0330.md:14:  percolation_var_0.65: r=1.0357142857142858, spacing=Poisson-like (⟨r⟩=0.48785515925635364)
tools/data/operator_directives_consumed/operator_directive_20260509_1400.md:6:fuori dal deposito V_c e dentro il perimetro `BOUNDARY`: 8 domini GUE,
tools/data/operator_directives_consumed/operator_directive_20260509_1400.md:7:5 Poisson. Non cercare direttamente "GUE o Poisson?" e non rifare un
tools/data/operator_directives_consumed/operator_directive_20260509_1400.md:45:- dove il terzo incluso del confine GUE/Poisson diventa osservabile;
tools/data/operator_directives_consumed/operator_directive_20260509_0659.md:16:- reasoning: Il ciclo ha prodotto una falsificazione strutturale utile: il label-set preservato non basta a ricostruire il boundary `V_c` nel perimetro N=89. Non serve fermare l'operatore: la consecutio e' gia' indicata dal deposito, cioe' riparare il null al nodo generativo e distinguere phase-shuffle Sturmian da surrogate label-preserving prima di estendere a GUE/Poisson.
tools/data/preflight/preflight_20260514_1850.md:10:- stable_anchor: `20260514_1640` - Agent Report - Rafforzamento tester fisico GOE/GUE N-curve
tools/data/preflight/preflight_20260514_1649.md:10:- stable_anchor: `20260514_1640` - Agent Report - Rafforzamento tester fisico GOE/GUE N-curve
tools/data/preflight/agent_field_live_backup_post_controlled_1623_20260515.md:70:- Non partire da phi, gap label, GUE o Poisson se sono gia' nel ciclo
tools/data/preflight/agent_field_live_backup_post_controlled_1623_20260515.md:327:- GUE;
tools/data/preflight/agent_field_live_backup_post_controlled_1623_20260515.md:330:- Poisson;
tools/data/preflight/agent_field_live_backup_post_controlled_1623_20260515.md:350:- Poisson synthetic;
tools/data/preflight/agent_field_live_backup_post_controlled_1623_20260515.md:351:- GUE synthetic;
tools/data/preflight/agent_field_live_backup_post_controlled_1623_20260515.md:358:- GUE/Poisson e' spesso un piano di proiezione, non una sorgente. Se diventa
tools/data/preflight/agent_field_live_backup_post_controlled_1623_20260515.md:597:GUE/Poisson/non-phi.
tools/data/preflight/agent_field_live_backup_post_controlled_1623_20260515.md:604:- Punto-zero: confine prima che venga classificato GUE o Poisson.
tools/data/preflight/agent_field_live_backup_post_controlled_1623_20260515.md:672:- concetti: terzo incluso, GUE/Poisson, non-phi generator, graph curvature,
tools/data/preflight/agent_field_live_backup_post_controlled_1623_20260515.md:675:  precede la classificazione spettrale GUE/Poisson;
tools/data/preflight/agent_field_live_backup_post_controlled_1623_20260515.md:722:  - "VALIDA contro baseline GUE, Poisson e generatori sintetici."
tools/data/preflight/agent_field_live_backup_post_controlled_1623_20260515.md:724:  quando: "il ciclo lavora su boundary, GUE/Poisson o trasferibilita' phi"
tools/data/preflight/agent_field_live_backup_post_controlled_1623_20260515.md:868:  quando: "prima del prossimo ciclo su GUE/Poisson/non-phi"
tools/data/preflight/agent_field_live_backup_post_controlled_1623_20260515.md:1053:- [BOUNDARY] (0.8)  8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/agent_field_live_backup_post_controlled_1623_20260515.md:1098:aver dichiarato che "GUE is also dynamic under M". Il setup C1 era
tools/data/preflight/agent_field_live_backup_post_controlled_1623_20260515.md:1100:ha mostrato GUE dinamico — la conclusione ha riformulato silenziosamente
tools/data/preflight/agent_field_live_backup_post_controlled_1623_20260515.md:1206:Un pattern in distribuzioni classiche (primi, GUE, random walk, Markov chain, gap statistics) probabilmente ha un nome. Default hypothesis: re-discovery / caso limite di teorema noto. Prima di taggare 'NEW', cerca il risultato classico piu' vicino (es. Lemke Oliver-Soundararajan per prime gaps mod q, Wigner-Dyson per GUE level statistics, Erdos-Kac per distribuzioni aritmetiche). Se non lo trovi, dichiara la ricerca esplicitamente.
tools/data/operator_directives_consumed/operator_directive_20260509_1427.md:18:null/surrogate row-aligned per una coppia blank GUE/Poisson:
tools/data/operator_directives_consumed/operator_directive_20260509_1427.md:20:- `zeta_zeros` (blank GUE);
tools/data/operator_directives_consumed/operator_directive_20260509_1427.md:21:- `pendolo_doppio` (blank Poisson).
tools/data/operator_directives_consumed/operator_directive_20260509_1427.md:35:  `z=-2.310593`, `GUE -> GUE`, `ordering_dependent=false`;
tools/data/operator_directives_consumed/operator_directive_20260509_1427.md:37:  `shuffle_mean=0.292437`, `z=27.919656`, `Poisson -> Poisson`,
tools/data/operator_directives_consumed/operator_directive_20260509_1427.md:56:- Non dichiarare nuova legge GUE/Poisson.
tools/data/operator_directives_consumed/operator_directive_20260509_1427.md:59:- Non trattare `pendolo_doppio` come cambio classe: e' Poisson -> Poisson con
tools/data/preflight/preflight_20260515_1659.md:10:- stable_anchor: `20260514_1640` - Agent Report - Rafforzamento tester fisico GOE/GUE N-curve
tools/data/operator_directives_consumed/operator_directive_20260514_1612.md:15:   (`prime-minus-mod6`, GUE/Poisson, boundary, spettro, gap, vettore o null)
tools/data/preflight/preflight_20260515_1647.md:10:- stable_anchor: `20260514_1640` - Agent Report - Rafforzamento tester fisico GOE/GUE N-curve
tools/data/operator_directives_consumed/operator_directive_20260509_0819.md:4:Non inseguire direttamente GUE/Poisson, Vc, hamming, phase-shift o un null
tools/data/operator_directives_consumed/operator_directive_20260509_1409.md:18:Trasferire il gate `denominator_state` al confine `BOUNDARY`: 8 domini GUE,
tools/data/operator_directives_consumed/operator_directive_20260509_1409.md:19:5 Poisson. Il risultato valido e' uno di questi:
tools/data/operator_directives_consumed/operator_directive_20260509_1409.md:31:- Non usare GUE/Poisson come risposta binaria; usali come perimetro di
tools/data/operator_directives_consumed/operator_directive_20260509_1409.md:51:- source domain type (`GUE`, `Poisson`, `mixture`, `surrogate` o altro
tools/data/operator_directives_consumed/operator_directive_20260514_1631.md:39:- Wigner-Dyson / GUE level statistics;
tools/data/operator_directives_consumed/operator_directive_20260514_1631.md:40:- Poisson level statistics;
tools/data/operator_directives_consumed/operator_directive_20260514_1631.md:55:GUE / spettri quantistici caotici
tools/data/operator_directives_consumed/operator_directive_20260514_1631.md:56:  -> SR,L1,triple_var + null Poisson span-matched
tools/data/operator_directives_consumed/operator_directive_20260514_1631.md:62:- `SR` attivo in GUE e assorbito verso Anderson localizzato;
tools/data/operator_directives_consumed/operator_directive_20260514_1640.md:34:- Wigner-Dyson / GOE-GUE level statistics;
tools/data/operator_directives_consumed/operator_directive_20260514_1640.md:35:- Poisson level statistics;
tools/data/operator_directives_consumed/operator_directive_20260514_1640.md:43:1. distinguere almeno GOE e GUE, dichiarando la simmetria;
tools/data/preflight/agent_field_live_backup_pre_selector_matrix_20260515_1659.md:70:- Non partire da phi, gap label, GUE o Poisson se sono gia' nel ciclo
tools/data/preflight/agent_field_live_backup_pre_selector_matrix_20260515_1659.md:327:- GUE;
tools/data/preflight/agent_field_live_backup_pre_selector_matrix_20260515_1659.md:330:- Poisson;
tools/data/preflight/agent_field_live_backup_pre_selector_matrix_20260515_1659.md:350:- Poisson synthetic;
tools/data/preflight/agent_field_live_backup_pre_selector_matrix_20260515_1659.md:351:- GUE synthetic;
tools/data/preflight/agent_field_live_backup_pre_selector_matrix_20260515_1659.md:358:- GUE/Poisson e' spesso un piano di proiezione, non una sorgente. Se diventa
tools/data/preflight/agent_field_live_backup_pre_selector_matrix_20260515_1659.md:597:GUE/Poisson/non-phi.
tools/data/preflight/agent_field_live_backup_pre_selector_matrix_20260515_1659.md:604:- Punto-zero: confine prima che venga classificato GUE o Poisson.
tools/data/preflight/agent_field_live_backup_pre_selector_matrix_20260515_1659.md:672:- concetti: terzo incluso, GUE/Poisson, non-phi generator, graph curvature,
tools/data/preflight/agent_field_live_backup_pre_selector_matrix_20260515_1659.md:675:  precede la classificazione spettrale GUE/Poisson;
tools/data/preflight/agent_field_live_backup_pre_selector_matrix_20260515_1659.md:722:  - "VALIDA contro baseline GUE, Poisson e generatori sintetici."
tools/data/preflight/agent_field_live_backup_pre_selector_matrix_20260515_1659.md:724:  quando: "il ciclo lavora su boundary, GUE/Poisson o trasferibilita' phi"
tools/data/preflight/agent_field_live_backup_pre_selector_matrix_20260515_1659.md:868:  quando: "prima del prossimo ciclo su GUE/Poisson/non-phi"
tools/data/preflight/agent_field_live_backup_pre_selector_matrix_20260515_1659.md:1049:- [BOUNDARY] (0.8)  8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/agent_field_live_backup_pre_selector_matrix_20260515_1659.md:1094:aver dichiarato che "GUE is also dynamic under M". Il setup C1 era
tools/data/preflight/agent_field_live_backup_pre_selector_matrix_20260515_1659.md:1096:ha mostrato GUE dinamico — la conclusione ha riformulato silenziosamente
tools/data/preflight/agent_field_live_backup_pre_selector_matrix_20260515_1659.md:1202:Un pattern in distribuzioni classiche (primi, GUE, random walk, Markov chain, gap statistics) probabilmente ha un nome. Default hypothesis: re-discovery / caso limite di teorema noto. Prima di taggare 'NEW', cerca il risultato classico piu' vicino (es. Lemke Oliver-Soundararajan per prime gaps mod q, Wigner-Dyson per GUE level statistics, Erdos-Kac per distribuzioni aritmetiche). Se non lo trovi, dichiara la ricerca esplicitamente.
tools/data/operator_directives_consumed/operator_directive_20260514_1701.md:16:- reasoning: La riparazione L6 richiesta dall'operatore e' stata completata senza cambiare contenuto scientifico: Bridge QA clean, health completed, e il verdict resta correttamente CONSTRAINT / FIT-READY TESTER. Non emerge una nuova dipendenza dall'operatore: il residuo era formale/contrattuale ed e' stato chiuso nel nodo regressivo del report. Il seme e' gia' avanzato a piano 125 con direzione viva sul confine GUE/Poisson, quindi il passo successivo e' riprendere il flusso sperimentale.
tools/data/preflight/agent_field_live_backup_pre_scientific_return_20260515_1706.md:72:- Non partire da phi, gap label, GUE o Poisson se sono gia' nel ciclo
tools/data/preflight/agent_field_live_backup_pre_scientific_return_20260515_1706.md:329:- GUE;
tools/data/preflight/agent_field_live_backup_pre_scientific_return_20260515_1706.md:332:- Poisson;
tools/data/preflight/agent_field_live_backup_pre_scientific_return_20260515_1706.md:352:- Poisson synthetic;
tools/data/preflight/agent_field_live_backup_pre_scientific_return_20260515_1706.md:353:- GUE synthetic;
tools/data/preflight/agent_field_live_backup_pre_scientific_return_20260515_1706.md:360:- GUE/Poisson e' spesso un piano di proiezione, non una sorgente. Se diventa
tools/data/preflight/agent_field_live_backup_pre_scientific_return_20260515_1706.md:599:GUE/Poisson/non-phi.
tools/data/preflight/agent_field_live_backup_pre_scientific_return_20260515_1706.md:606:- Punto-zero: confine prima che venga classificato GUE o Poisson.
tools/data/preflight/agent_field_live_backup_pre_scientific_return_20260515_1706.md:674:- concetti: terzo incluso, GUE/Poisson, non-phi generator, graph curvature,
tools/data/preflight/agent_field_live_backup_pre_scientific_return_20260515_1706.md:677:  precede la classificazione spettrale GUE/Poisson;
tools/data/preflight/agent_field_live_backup_pre_scientific_return_20260515_1706.md:724:  - "VALIDA contro baseline GUE, Poisson e generatori sintetici."
tools/data/preflight/agent_field_live_backup_pre_scientific_return_20260515_1706.md:726:  quando: "il ciclo lavora su boundary, GUE/Poisson o trasferibilita' phi"
tools/data/preflight/agent_field_live_backup_pre_scientific_return_20260515_1706.md:870:  quando: "prima del prossimo ciclo su GUE/Poisson/non-phi"
tools/data/preflight/agent_field_live_backup_pre_scientific_return_20260515_1706.md:1051:- [BOUNDARY] (0.8)  8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/agent_field_live_backup_pre_scientific_return_20260515_1706.md:1096:aver dichiarato che "GUE is also dynamic under M". Il setup C1 era
tools/data/preflight/agent_field_live_backup_pre_scientific_return_20260515_1706.md:1098:ha mostrato GUE dinamico — la conclusione ha riformulato silenziosamente
tools/data/preflight/agent_field_live_backup_pre_scientific_return_20260515_1706.md:1204:Un pattern in distribuzioni classiche (primi, GUE, random walk, Markov chain, gap statistics) probabilmente ha un nome. Default hypothesis: re-discovery / caso limite di teorema noto. Prima di taggare 'NEW', cerca il risultato classico piu' vicino (es. Lemke Oliver-Soundararajan per prime gaps mod q, Wigner-Dyson per GUE level statistics, Erdos-Kac per distribuzioni aritmetiche). Se non lo trovi, dichiara la ricerca esplicitamente.
tools/data/operator_directives_consumed/operator_directive_20260509_1437.md:35:  `shuffle_mean=0.372199`, `z=4.562844`, `Poisson -> Poisson`,
tools/data/operator_directives_consumed/operator_directive_20260509_1437.md:38:  `shuffle_mean=0.494932`, `z=31.390192`, `GUE -> GUE`,
tools/data/operator_directives_consumed/operator_directive_20260509_1437.md:41:  `shuffle_mean=0.099640`, `z=161.271569`, `GUE -> Poisson`,
tools/data/operator_directives_consumed/operator_directive_20260509_1437.md:59:- Non dichiarare una nuova legge GUE/Poisson.
tools/data/preflight/manual_controlled_snapshot_20260515_1659_3609780/agent_field_live.md:72:- Non partire da phi, gap label, GUE o Poisson se sono gia' nel ciclo
tools/data/preflight/manual_controlled_snapshot_20260515_1659_3609780/agent_field_live.md:329:- GUE;
tools/data/preflight/manual_controlled_snapshot_20260515_1659_3609780/agent_field_live.md:332:- Poisson;
tools/data/preflight/manual_controlled_snapshot_20260515_1659_3609780/agent_field_live.md:352:- Poisson synthetic;
tools/data/preflight/manual_controlled_snapshot_20260515_1659_3609780/agent_field_live.md:353:- GUE synthetic;
tools/data/preflight/manual_controlled_snapshot_20260515_1659_3609780/agent_field_live.md:360:- GUE/Poisson e' spesso un piano di proiezione, non una sorgente. Se diventa
tools/data/preflight/manual_controlled_snapshot_20260515_1659_3609780/agent_field_live.md:599:GUE/Poisson/non-phi.
tools/data/preflight/manual_controlled_snapshot_20260515_1659_3609780/agent_field_live.md:606:- Punto-zero: confine prima che venga classificato GUE o Poisson.
tools/data/preflight/manual_controlled_snapshot_20260515_1659_3609780/agent_field_live.md:674:- concetti: terzo incluso, GUE/Poisson, non-phi generator, graph curvature,
tools/data/preflight/manual_controlled_snapshot_20260515_1659_3609780/agent_field_live.md:677:  precede la classificazione spettrale GUE/Poisson;
tools/data/preflight/manual_controlled_snapshot_20260515_1659_3609780/agent_field_live.md:724:  - "VALIDA contro baseline GUE, Poisson e generatori sintetici."
tools/data/preflight/manual_controlled_snapshot_20260515_1659_3609780/agent_field_live.md:726:  quando: "il ciclo lavora su boundary, GUE/Poisson o trasferibilita' phi"
tools/data/preflight/manual_controlled_snapshot_20260515_1659_3609780/agent_field_live.md:870:  quando: "prima del prossimo ciclo su GUE/Poisson/non-phi"
tools/data/preflight/manual_controlled_snapshot_20260515_1659_3609780/agent_field_live.md:1051:- [BOUNDARY] (0.8)  8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/manual_controlled_snapshot_20260515_1659_3609780/agent_field_live.md:1096:aver dichiarato che "GUE is also dynamic under M". Il setup C1 era
tools/data/preflight/manual_controlled_snapshot_20260515_1659_3609780/agent_field_live.md:1098:ha mostrato GUE dinamico — la conclusione ha riformulato silenziosamente
tools/data/preflight/manual_controlled_snapshot_20260515_1659_3609780/agent_field_live.md:1204:Un pattern in distribuzioni classiche (primi, GUE, random walk, Markov chain, gap statistics) probabilmente ha un nome. Default hypothesis: re-discovery / caso limite di teorema noto. Prima di taggare 'NEW', cerca il risultato classico piu' vicino (es. Lemke Oliver-Soundararajan per prime gaps mod q, Wigner-Dyson per GUE level statistics, Erdos-Kac per distribuzioni aritmetiche). Se non lo trovi, dichiara la ricerca esplicitamente.
tools/data/agent_field_live.md:21:- Direzione viva ora: Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/agent_field_live.md:24:  - evidenza: `seme.json.direzione` viva è: "Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo". Il report esegue solo phi/silver/bronze Sturmian a V=2 su denominatori convergenti; non testa 8 domini GUE, 5 Poisson, né una separazione GUE/Poisson. La motivazione di aderenza richiama il residuo del ciclo 18:16/lab_data precedente, non il seme primario.
tools/data/agent_field_live.md:25:  - prossimo uso ammesso: Nel prossimo ciclo formulare `direction_adherence` contro `seme.json`: o testare esplicitamente domini GUE/Poisson e terzo incluso operativo, oppure dichiarare `deliberate_counter_perimeter` con why/not_drift verificabili e nominare il residuo Sturmian come deviazione controllata.
tools/data/agent_field_live.md:32:    Check richiesto: Nel prossimo ciclo separare esplicitamente `two_reader_boundary_confirmed = 1` da `graph_only_residue = 3`; se le tre righe graph-only sono claim Lab-specific, dichiarare che il nodo regressivo corregge il contratto da `due lettori` a `frequenza grafica + audit dichiarato`, non a `audit positivo`.
tools/data/agent_field_live.md:33:  - L5: Cosa resta Lab-specific: `percolation`, `reaction_diffusion`, `logistica_biforcazione_var_3.5699` sono `stable_graph_bridge+graph_only_bridge`, tutte 27/27. Il baseline classico le legge endpoint-like, il grafo le legge confine stabile.
tools/data/agent_field_live.md:35:Obblighi pratici: se il dominio e' GUE/Poisson, aggiungi una sezione `## Re-discovery audit` con il baseline noto piu' vicino (Brody/Berry-Robnik/Rosenzweig-Porter, mobility/localization crossover o altro nome pertinente) e cosa resta lab-specific. Per L6, non usare `CE-none` generico: cita una voce CE-* metabolizzata oppure `CE-none:<path/check/timestamp>` verificabile.
tools/data/agent_field_live.md:36:Se compare un residuo graph-only, separa nel report: `two_reader_boundary_confirmed`, `graph_only_residue`, `scope_change_declared`, `graph_baseline_audit`. Non sommare righe graph-only al boundary a due lettori. Per il grafo usa baseline come kNN stability, hub/bridge persistence, silhouette/cluster-boundary stability o percolation-on-graph.
tools/data/agent_field_live.md:57:- disc_5: 3 ghost · Metrica primi g=(p/2)², curvatura GUE r=0.503
tools/data/agent_field_live.md:61:**Direzione seme da respirare**: Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/agent_field_live.md:64:- Direzione viva del seme: Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/agent_field_live.md:96:- Non partire da phi, gap label, GUE o Poisson se sono gia' nel ciclo
tools/data/agent_field_live.md:353:- GUE;
tools/data/agent_field_live.md:356:- Poisson;
tools/data/agent_field_live.md:376:- Poisson synthetic;
tools/data/agent_field_live.md:377:- GUE synthetic;
tools/data/agent_field_live.md:384:- GUE/Poisson e' spesso un piano di proiezione, non una sorgente. Se diventa
tools/data/agent_field_live.md:623:GUE/Poisson/non-phi.
tools/data/agent_field_live.md:630:- Punto-zero: confine prima che venga classificato GUE o Poisson.
tools/data/agent_field_live.md:698:- concetti: terzo incluso, GUE/Poisson, non-phi generator, graph curvature,
tools/data/agent_field_live.md:701:  precede la classificazione spettrale GUE/Poisson;
tools/data/agent_field_live.md:748:  - "VALIDA contro baseline GUE, Poisson e generatori sintetici."
tools/data/agent_field_live.md:750:  quando: "il ciclo lavora su boundary, GUE/Poisson o trasferibilita' phi"
tools/data/agent_field_live.md:894:  quando: "prima del prossimo ciclo su GUE/Poisson/non-phi"
tools/data/agent_field_live.md:954:- **Direzione corrente**: Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/agent_field_live.md:1016:Direzione viva attuale: Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/agent_field_live.md:1068:`Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo`
tools/data/agent_field_live.md:1078:## Piano 124 — Esplorare il confine: 8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/agent_field_live.md:1083:- [BOUNDARY] (0.8)  8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/agent_field_live.md:1128:aver dichiarato che "GUE is also dynamic under M". Il setup C1 era
tools/data/agent_field_live.md:1130:ha mostrato GUE dinamico — la conclusione ha riformulato silenziosamente
tools/data/agent_field_live.md:1221:  disc_5 (3 ghost): Metrica primi g=(p/2)², curvatura GUE r=0.503
tools/data/agent_field_live.md:1243:Un pattern in distribuzioni classiche (primi, GUE, random walk, Markov chain, gap statistics) probabilmente ha un nome. Default hypothesis: re-discovery / caso limite di teorema noto. Prima di taggare 'NEW', cerca il risultato classico piu' vicino (es. Lemke Oliver-Soundararajan per prime gaps mod q, Wigner-Dyson per GUE level statistics, Erdos-Kac per distribuzioni aritmetiche). Se non lo trovi, dichiara la ricerca esplicitamente.
tools/data/preflight/supervised_one_shot_result_B_20260515_1407.md:174:GUE/Poisson physical transfer
tools/data/notte_20260326_0330.md:5:  collatz_cp: r=0.9497136076205077, spacing=GUE-like [conferma]
tools/data/notte_20260326_0330.md:8:  ising_2d_cp_-0.40399731526527427: r=0.9108185170256915, spacing=GUE-like [conferma]
tools/data/notte_20260326_0330.md:9:  ising_2d_cp_0.45522935739736126: r=1.0431954263284353, spacing=GUE-like [conferma]
tools/data/notte_20260326_0330.md:10:  brownian_motion_cp_0.10156293949284084: r=0.9966329966329966, spacing=Poisson-like [conferma]
tools/data/notte_20260326_0330.md:11:  brownian_motion_cp_0.9083139965385025: r=1.0, spacing=Poisson-like [conferma]
tools/data/notte_20260326_0330.md:12:  percolation_cp_0.3034086711489568: r=0.6949152542372882, spacing=GUE-like [conferma]
tools/data/notte_20260326_0330.md:13:  percolation_cp_0.8397168647434718: r=1.0454545454545456, spacing=GUE-like [conferma]
tools/data/notte_20260326_0330.md:14:  cellular_automata_cp_150: r=0.7452006980802792, spacing=GUE-like [conferma]
tools/data/notte_20260326_0330.md:17:  logistica_biforcazione_var_3.57: r=1.0, spacing=Poisson-like (⟨r⟩=0.35930230610663094)
tools/data/notte_20260326_0330.md:18:  numeri_primi_var_100000: r=0.8615840174827735, spacing=GUE-like (⟨r⟩=0.9609375)
tools/data/notte_20260326_0330.md:19:  cellular_automata_var_30: r=0.8703941780326052, spacing=GUE-like (⟨r⟩=0.8649999999999983)
tools/data/notte_20260326_0330.md:20:  coupled_oscillators_var_50: r=1.0, spacing=Poisson-like (⟨r⟩=0.45756422661060286)
tools/data/preflight/agent_field_live_candidate_20260515_1620.md:70:- Non partire da phi, gap label, GUE o Poisson se sono gia' nel ciclo
tools/data/preflight/agent_field_live_candidate_20260515_1620.md:327:- GUE;
tools/data/preflight/agent_field_live_candidate_20260515_1620.md:330:- Poisson;
tools/data/preflight/agent_field_live_candidate_20260515_1620.md:350:- Poisson synthetic;
tools/data/preflight/agent_field_live_candidate_20260515_1620.md:351:- GUE synthetic;
tools/data/preflight/agent_field_live_candidate_20260515_1620.md:358:- GUE/Poisson e' spesso un piano di proiezione, non una sorgente. Se diventa
tools/data/preflight/agent_field_live_candidate_20260515_1620.md:597:GUE/Poisson/non-phi.
tools/data/preflight/agent_field_live_candidate_20260515_1620.md:604:- Punto-zero: confine prima che venga classificato GUE o Poisson.
tools/data/preflight/agent_field_live_candidate_20260515_1620.md:672:- concetti: terzo incluso, GUE/Poisson, non-phi generator, graph curvature,
tools/data/preflight/agent_field_live_candidate_20260515_1620.md:675:  precede la classificazione spettrale GUE/Poisson;
tools/data/preflight/agent_field_live_candidate_20260515_1620.md:722:  - "VALIDA contro baseline GUE, Poisson e generatori sintetici."
tools/data/preflight/agent_field_live_candidate_20260515_1620.md:724:  quando: "il ciclo lavora su boundary, GUE/Poisson o trasferibilita' phi"
tools/data/preflight/agent_field_live_candidate_20260515_1620.md:868:  quando: "prima del prossimo ciclo su GUE/Poisson/non-phi"
tools/data/preflight/agent_field_live_candidate_20260515_1620.md:1050:- [BOUNDARY] (0.8)  8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/agent_field_live_candidate_20260515_1620.md:1095:aver dichiarato che "GUE is also dynamic under M". Il setup C1 era
tools/data/preflight/agent_field_live_candidate_20260515_1620.md:1097:ha mostrato GUE dinamico — la conclusione ha riformulato silenziosamente
tools/data/preflight/agent_field_live_candidate_20260515_1620.md:1203:Un pattern in distribuzioni classiche (primi, GUE, random walk, Markov chain, gap statistics) probabilmente ha un nome. Default hypothesis: re-discovery / caso limite di teorema noto. Prima di taggare 'NEW', cerca il risultato classico piu' vicino (es. Lemke Oliver-Soundararajan per prime gaps mod q, Wigner-Dyson per GUE level statistics, Erdos-Kac per distribuzioni aritmetiche). Se non lo trovi, dichiara la ricerca esplicitamente.
tools/data/preflight/agent_field_live_backup_pre_no_blocked_ref_replace_20260515_1624.md:70:- Non partire da phi, gap label, GUE o Poisson se sono gia' nel ciclo
tools/data/preflight/agent_field_live_backup_pre_no_blocked_ref_replace_20260515_1624.md:327:- GUE;
tools/data/preflight/agent_field_live_backup_pre_no_blocked_ref_replace_20260515_1624.md:330:- Poisson;
tools/data/preflight/agent_field_live_backup_pre_no_blocked_ref_replace_20260515_1624.md:350:- Poisson synthetic;
tools/data/preflight/agent_field_live_backup_pre_no_blocked_ref_replace_20260515_1624.md:351:- GUE synthetic;
tools/data/preflight/agent_field_live_backup_pre_no_blocked_ref_replace_20260515_1624.md:358:- GUE/Poisson e' spesso un piano di proiezione, non una sorgente. Se diventa
tools/data/preflight/agent_field_live_backup_pre_no_blocked_ref_replace_20260515_1624.md:597:GUE/Poisson/non-phi.
tools/data/preflight/agent_field_live_backup_pre_no_blocked_ref_replace_20260515_1624.md:604:- Punto-zero: confine prima che venga classificato GUE o Poisson.
tools/data/preflight/agent_field_live_backup_pre_no_blocked_ref_replace_20260515_1624.md:672:- concetti: terzo incluso, GUE/Poisson, non-phi generator, graph curvature,
tools/data/preflight/agent_field_live_backup_pre_no_blocked_ref_replace_20260515_1624.md:675:  precede la classificazione spettrale GUE/Poisson;
tools/data/preflight/agent_field_live_backup_pre_no_blocked_ref_replace_20260515_1624.md:722:  - "VALIDA contro baseline GUE, Poisson e generatori sintetici."
tools/data/preflight/agent_field_live_backup_pre_no_blocked_ref_replace_20260515_1624.md:724:  quando: "il ciclo lavora su boundary, GUE/Poisson o trasferibilita' phi"
tools/data/preflight/agent_field_live_backup_pre_no_blocked_ref_replace_20260515_1624.md:868:  quando: "prima del prossimo ciclo su GUE/Poisson/non-phi"
tools/data/preflight/agent_field_live_backup_pre_no_blocked_ref_replace_20260515_1624.md:1050:- [BOUNDARY] (0.8)  8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/agent_field_live_backup_pre_no_blocked_ref_replace_20260515_1624.md:1095:aver dichiarato che "GUE is also dynamic under M". Il setup C1 era
tools/data/preflight/agent_field_live_backup_pre_no_blocked_ref_replace_20260515_1624.md:1097:ha mostrato GUE dinamico — la conclusione ha riformulato silenziosamente
tools/data/preflight/agent_field_live_backup_pre_no_blocked_ref_replace_20260515_1624.md:1203:Un pattern in distribuzioni classiche (primi, GUE, random walk, Markov chain, gap statistics) probabilmente ha un nome. Default hypothesis: re-discovery / caso limite di teorema noto. Prima di taggare 'NEW', cerca il risultato classico piu' vicino (es. Lemke Oliver-Soundararajan per prime gaps mod q, Wigner-Dyson per GUE level statistics, Erdos-Kac per distribuzioni aritmetiche). Se non lo trovi, dichiara la ricerca esplicitamente.
tools/data/preflight/preflight_20260514_1640.md:6:- report: Agent Report - Rafforzamento tester fisico GOE/GUE N-curve
tools/data/preflight/agent_field_live_candidate_post_controlled_20260515_1631.md:70:- Non partire da phi, gap label, GUE o Poisson se sono gia' nel ciclo
tools/data/preflight/agent_field_live_candidate_post_controlled_20260515_1631.md:327:- GUE;
tools/data/preflight/agent_field_live_candidate_post_controlled_20260515_1631.md:330:- Poisson;
tools/data/preflight/agent_field_live_candidate_post_controlled_20260515_1631.md:350:- Poisson synthetic;
tools/data/preflight/agent_field_live_candidate_post_controlled_20260515_1631.md:351:- GUE synthetic;
tools/data/preflight/agent_field_live_candidate_post_controlled_20260515_1631.md:358:- GUE/Poisson e' spesso un piano di proiezione, non una sorgente. Se diventa
tools/data/preflight/agent_field_live_candidate_post_controlled_20260515_1631.md:597:GUE/Poisson/non-phi.
tools/data/preflight/agent_field_live_candidate_post_controlled_20260515_1631.md:604:- Punto-zero: confine prima che venga classificato GUE o Poisson.
tools/data/preflight/agent_field_live_candidate_post_controlled_20260515_1631.md:672:- concetti: terzo incluso, GUE/Poisson, non-phi generator, graph curvature,
tools/data/preflight/agent_field_live_candidate_post_controlled_20260515_1631.md:675:  precede la classificazione spettrale GUE/Poisson;
tools/data/preflight/agent_field_live_candidate_post_controlled_20260515_1631.md:722:  - "VALIDA contro baseline GUE, Poisson e generatori sintetici."
tools/data/preflight/agent_field_live_candidate_post_controlled_20260515_1631.md:724:  quando: "il ciclo lavora su boundary, GUE/Poisson o trasferibilita' phi"
tools/data/preflight/agent_field_live_candidate_post_controlled_20260515_1631.md:868:  quando: "prima del prossimo ciclo su GUE/Poisson/non-phi"
tools/data/preflight/agent_field_live_candidate_post_controlled_20260515_1631.md:1049:- [BOUNDARY] (0.8)  8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/agent_field_live_candidate_post_controlled_20260515_1631.md:1094:aver dichiarato che "GUE is also dynamic under M". Il setup C1 era
tools/data/preflight/agent_field_live_candidate_post_controlled_20260515_1631.md:1096:ha mostrato GUE dinamico — la conclusione ha riformulato silenziosamente
tools/data/preflight/agent_field_live_candidate_post_controlled_20260515_1631.md:1202:Un pattern in distribuzioni classiche (primi, GUE, random walk, Markov chain, gap statistics) probabilmente ha un nome. Default hypothesis: re-discovery / caso limite di teorema noto. Prima di taggare 'NEW', cerca il risultato classico piu' vicino (es. Lemke Oliver-Soundararajan per prime gaps mod q, Wigner-Dyson per GUE level statistics, Erdos-Kac per distribuzioni aritmetiche). Se non lo trovi, dichiara la ricerca esplicitamente.
tools/data/preflight/preflight_20260514_1605.md:19:- L8 medium: Riformulare aderenza come deliberate_counter_perimeter con why/not_drift verificabili, oppure collegare prime-minus-mod6 al confine GUE/Poisson tramite un ponte misurabile nel prossimo ciclo.
tools/data/preflight/agent_field_live_candidate_selector_matrix_20260515_1659.md:72:- Non partire da phi, gap label, GUE o Poisson se sono gia' nel ciclo
tools/data/preflight/agent_field_live_candidate_selector_matrix_20260515_1659.md:329:- GUE;
tools/data/preflight/agent_field_live_candidate_selector_matrix_20260515_1659.md:332:- Poisson;
tools/data/preflight/agent_field_live_candidate_selector_matrix_20260515_1659.md:352:- Poisson synthetic;
tools/data/preflight/agent_field_live_candidate_selector_matrix_20260515_1659.md:353:- GUE synthetic;
tools/data/preflight/agent_field_live_candidate_selector_matrix_20260515_1659.md:360:- GUE/Poisson e' spesso un piano di proiezione, non una sorgente. Se diventa
tools/data/preflight/agent_field_live_candidate_selector_matrix_20260515_1659.md:599:GUE/Poisson/non-phi.
tools/data/preflight/agent_field_live_candidate_selector_matrix_20260515_1659.md:606:- Punto-zero: confine prima che venga classificato GUE o Poisson.
tools/data/preflight/agent_field_live_candidate_selector_matrix_20260515_1659.md:674:- concetti: terzo incluso, GUE/Poisson, non-phi generator, graph curvature,
tools/data/preflight/agent_field_live_candidate_selector_matrix_20260515_1659.md:677:  precede la classificazione spettrale GUE/Poisson;
tools/data/preflight/agent_field_live_candidate_selector_matrix_20260515_1659.md:724:  - "VALIDA contro baseline GUE, Poisson e generatori sintetici."
tools/data/preflight/agent_field_live_candidate_selector_matrix_20260515_1659.md:726:  quando: "il ciclo lavora su boundary, GUE/Poisson o trasferibilita' phi"
tools/data/preflight/agent_field_live_candidate_selector_matrix_20260515_1659.md:870:  quando: "prima del prossimo ciclo su GUE/Poisson/non-phi"
tools/data/preflight/agent_field_live_candidate_selector_matrix_20260515_1659.md:1051:- [BOUNDARY] (0.8)  8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/agent_field_live_candidate_selector_matrix_20260515_1659.md:1096:aver dichiarato che "GUE is also dynamic under M". Il setup C1 era
tools/data/preflight/agent_field_live_candidate_selector_matrix_20260515_1659.md:1098:ha mostrato GUE dinamico — la conclusione ha riformulato silenziosamente
tools/data/preflight/agent_field_live_candidate_selector_matrix_20260515_1659.md:1204:Un pattern in distribuzioni classiche (primi, GUE, random walk, Markov chain, gap statistics) probabilmente ha un nome. Default hypothesis: re-discovery / caso limite di teorema noto. Prima di taggare 'NEW', cerca il risultato classico piu' vicino (es. Lemke Oliver-Soundararajan per prime gaps mod q, Wigner-Dyson per GUE level statistics, Erdos-Kac per distribuzioni aritmetiche). Se non lo trovi, dichiara la ricerca esplicitamente.
tools/data/preflight/agent_field_live_candidate_20260515_1616.md:41:- Perche': Il ciclo ha prodotto una direzione viva ma il seme ora devia verso frame GUE/Poisson, mentre l'evidenza appena maturata punta al nodo regressivo del contratto: `verdict()` resta centrato su `has_SR` e il micro-trace e' incompleto. La prossima mossa deve trasformare il residuo `prime_minus_mod6_z_vector(SR,L1,triple_var)` in osservabile dedicato e falsificarlo contro antagonisti piu duri, non riaprire un frame globale gia' a rischio accumulo numerico locale.
tools/data/preflight/agent_field_live_candidate_20260515_1616.md:71:- Non partire da phi, gap label, GUE o Poisson se sono gia' nel ciclo
tools/data/preflight/agent_field_live_candidate_20260515_1616.md:328:- GUE;
tools/data/preflight/agent_field_live_candidate_20260515_1616.md:331:- Poisson;
tools/data/preflight/agent_field_live_candidate_20260515_1616.md:351:- Poisson synthetic;
tools/data/preflight/agent_field_live_candidate_20260515_1616.md:352:- GUE synthetic;
tools/data/preflight/agent_field_live_candidate_20260515_1616.md:359:- GUE/Poisson e' spesso un piano di proiezione, non una sorgente. Se diventa
tools/data/preflight/agent_field_live_candidate_20260515_1616.md:598:GUE/Poisson/non-phi.
tools/data/preflight/agent_field_live_candidate_20260515_1616.md:605:- Punto-zero: confine prima che venga classificato GUE o Poisson.
tools/data/preflight/agent_field_live_candidate_20260515_1616.md:673:- concetti: terzo incluso, GUE/Poisson, non-phi generator, graph curvature,
tools/data/preflight/agent_field_live_candidate_20260515_1616.md:676:  precede la classificazione spettrale GUE/Poisson;
tools/data/preflight/agent_field_live_candidate_20260515_1616.md:723:  - "VALIDA contro baseline GUE, Poisson e generatori sintetici."
tools/data/preflight/agent_field_live_candidate_20260515_1616.md:725:  quando: "il ciclo lavora su boundary, GUE/Poisson o trasferibilita' phi"
tools/data/preflight/agent_field_live_candidate_20260515_1616.md:869:  quando: "prima del prossimo ciclo su GUE/Poisson/non-phi"
tools/data/preflight/agent_field_live_candidate_20260515_1616.md:1055:- [BOUNDARY] (0.8)  8 domini GUE, 5 Poisson — il confine è il terzo incluso operativo
tools/data/preflight/agent_field_live_candidate_20260515_1616.md:1100:aver dichiarato che "GUE is also dynamic under M". Il setup C1 era
tools/data/preflight/agent_field_live_candidate_20260515_1616.md:1102:ha mostrato GUE dinamico — la conclusione ha riformulato silenziosamente
tools/data/preflight/agent_field_live_candidate_20260515_1616.md:1208:Un pattern in distribuzioni classiche (primi, GUE, random walk, Markov chain, gap statistics) probabilmente ha un nome. Default hypothesis: re-discovery / caso limite di teorema noto. Prima di taggare 'NEW', cerca il risultato classico piu' vicino (es. Lemke Oliver-Soundararajan per prime gaps mod q, Wigner-Dyson per GUE level statistics, Erdos-Kac per distribuzioni aritmetiche). Se non lo trovi, dichiara la ricerca esplicitamente.

codex
Il risultato precedente utile è già materializzato in `boundary_classical_crossover_audit` e nel run 19:15. Non lo prendo come autorità finale: lo uso come deposito da ri-auditare con stabilità del grafo, come richiesto dal falsifier.
exec
/bin/bash -lc "sed -n '1,280p' tools/exp_boundary_bridge_stability_audit.py" in /opt/MM_D-ND
exec
/bin/bash -lc "sed -n '1,260p' tools/exp_boundary_classical_crossover_audit.py" in /opt/MM_D-ND
 succeeded in 0ms:
#!/usr/bin/env python3
"""
Stability audit for the BOUNDARY two-reader gate.

The audit keeps the 13 row-aligned GUE/Poisson denominator and reruns the graph
reader across small perturbations of k, spacing length, and shuffle seed. It then
joins those frequencies with the classical crossover audit states.
"""

from __future__ import annotations

import argparse
import json
from pathlib import Path
from typing import Any

from exp_boundary_graph_curvature_gate import (
    compute_observables,
    classify_geometry,
    load_scope,
    row_spacings,
    shuffle_z,
    standardized_matrix,
)


def load_json(path: Path) -> dict[str, Any]:
    with path.open(encoding="utf-8") as f:
        data = json.load(f)
    if not isinstance(data, dict):
        raise ValueError(f"{path} must contain a JSON object")
    return data


def parse_ints(raw: str) -> list[int]:
    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
    if not values:
        raise ValueError("empty integer list")
    return values


def classical_map(path: Path) -> dict[str, dict[str, Any]]:
    audit = load_json(path)
    rows = audit.get("rows", [])
    if not isinstance(rows, list):
        raise ValueError(f"{path} does not contain rows")
    return {row["domain_window"]: row for row in rows}


def classify_frequency(freq: float) -> str:
    if freq >= 0.75:
        return "stable_graph_bridge"
    if freq >= 0.25:
        return "parameter_sensitive_bridge"
    return "unstable_non_bridge"


def run(args: argparse.Namespace) -> dict[str, Any]:
    ks = parse_ints(args.k_values)
    n_gaps_values = parse_ints(args.n_gaps_values)
    seeds = parse_ints(args.seeds)
    classical = classical_map(Path(args.classical_audit))

    source_rows = load_scope(Path(args.scope))
    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
    selected = sorted(selected, key=lambda row: int(row["cycle"]))
    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}

    runs = []
    row_hits: dict[str, dict[str, Any]] = {}
    total_runs = 0

    for k in ks:
        for n_gaps in n_gaps_values:
            for seed in seeds:
                total_runs += 1
                import numpy as np

                rng = np.random.default_rng(seed)
                graph_rows = []
                for source in selected:
                    gaps = gap_cache[source["domain_window"]]
                    if len(gaps) < args.min_gaps:
                        continue
                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
                    obs = compute_observables(gaps)
                    z = shuffle_z(gaps, obs, args.n_shuffle, rng)
                    graph_rows.append(
                        {
                            "domain_window": source["domain_window"],
                            "domain": source["domain"],
                            "cycle": source["cycle"],
                            "source_domain_type": source["source_domain_type"],
                            "n_gaps": int(len(gaps)),
                            "observables": {key: round(value, 9) for key, value in obs.items()},
                            "shuffle_z": {key: round(value, 6) for key, value in z.items()},
                        }
                    )
                graph = {
                    "summary": {},
                    "geometry": classify_geometry(graph_rows, standardized_matrix(graph_rows), k),
                }
                graph["summary"]["third_included_candidates"] = graph["geometry"]["third_included_candidates"]
                graph["summary"]["edge_counts"] = graph["geometry"]["edge_counts"]
                candidates = set(graph["summary"]["third_included_candidates"])
                runs.append(
                    {
                        "k": k,
                        "n_gaps": n_gaps,
                        "seed": seed,
                        "third_included_candidates": sorted(candidates),
                        "cross_edges": graph["summary"]["edge_counts"]["cross_label"],
                    }
                )
                for row in graph["geometry"]["rows"]:
                    name = row["domain_window"]
                    if name not in row_hits:
                        row_hits[name] = {
                            "domain_window": name,
                            "domain": row["domain"],
                            "source_domain_type": row["source_domain_type"],
                            "hit_count": 0,
                            "cut_edge_count": 0,
                            "margin_values": [],
                            "cross_fraction_values": [],
                        }
                    if row["boundary_state"] == "third_included_candidate":
                        row_hits[name]["hit_count"] += 1
                    if row["boundary_state"] == "cut_edge":
                        row_hits[name]["cut_edge_count"] += 1
                    row_hits[name]["margin_values"].append(float(row["centroid_margin"]))
                    row_hits[name]["cross_fraction_values"].append(float(row["cross_neighbor_fraction"]))

    rows = []
    counts: dict[str, int] = {}
    for name in sorted(row_hits):
        item = row_hits[name]
        hit_frequency = item["hit_count"] / total_runs
        cut_frequency = item["cut_edge_count"] / total_runs
        classic = classical.get(name, {})
        stability_state = classify_frequency(hit_frequency)
        composite_state = f"{stability_state}+{classic.get('audit_state', 'missing_classical_audit')}"
        row = {
            "domain_window": name,
            "domain": item["domain"],
            "source_domain_type": item["source_domain_type"],
            "graph_bridge_hits": item["hit_count"],
            "graph_bridge_frequency": round(hit_frequency, 6),
            "cut_edge_frequency": round(cut_frequency, 6),
            "mean_margin": round(sum(item["margin_values"]) / len(item["margin_values"]), 6),
            "mean_cross_neighbor_fraction": round(
                sum(item["cross_fraction_values"]) / len(item["cross_fraction_values"]), 6
            ),
            "stability_state": stability_state,
            "classical_audit_state": classic.get("audit_state"),
            "brody_q": classic.get("brody_q"),
            "berry_robnick_like_gue_weight": classic.get("berry_robnick_like_gue_weight"),
            "composite_state": composite_state,
        }
        rows.append(row)
        counts[composite_state] = counts.get(composite_state, 0) + 1

    stable_graph_only = [
        row["domain_window"]
        for row in rows
        if row["stability_state"] == "stable_graph_bridge" and row["classical_audit_state"] == "graph_only_bridge"
    ]
    stable_classic_and_graph = [
        row["domain_window"]
        for row in rows
        if row["stability_state"] == "stable_graph_bridge"
        and row["classical_audit_state"] == "classic_and_graph_bridge"
    ]
    classic_only_stable_graph_absent = [
        row["domain_window"]
        for row in rows
        if row["stability_state"] == "unstable_non_bridge"
        and row["classical_audit_state"] == "classic_only_intermediate"
    ]

    output = {
        "experiment": "boundary_bridge_stability_audit",
        "question": "Do BOUNDARY graph bridge rows survive small graph-reader perturbations after the classical audit?",
        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate; classical audit coordinates joined",
        "observables_used": [
            "graph_bridge_frequency",
            "cut_edge_frequency",
            "mean_centroid_margin",
            "mean_cross_neighbor_fraction",
            "classical_audit_state",
            "brody_q",
            "berry_robnick_like_gue_weight",
        ],
        "params": {
            "scope": args.scope,
            "classical_audit": args.classical_audit,
            "k_values": ks,
            "n_gaps_values": n_gaps_values,
            "seeds": seeds,
            "n_shuffle": args.n_shuffle,
            "min_gaps": args.min_gaps,
            "total_runs": total_runs,
        },
        "observable_contract": {
            "claim": "a two-reader boundary row is operational only if graph bridge status is stable enough to survive reader perturbation and remains classically audited",
            "observable": "graph bridge hit frequency joined with Brody/Berry-Robnik-like audit state",
            "operator": "parameter perturbation over kNN graph reader with row-aligned classical audit join",
            "generator": "boundary_graph_curvature_gate over the 13-row BOUNDARY denominator",
            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid",
            "non_possible": "stable Lab bridge if bridge frequency collapses under k/n_gaps/seed perturbation",
            "not_tested": "new physical Hamiltonian flow, alternative unfolding, asymptotic scaling beyond this finite denominator",
        },
        "summary": {
            "rows_analyzed": len(rows),
            "graph_reader_runs": total_runs,
            "composite_counts": counts,
            "stable_graph_only": stable_graph_only,
            "stable_classic_and_graph": stable_classic_and_graph,
            "classic_only_stable_graph_absent": classic_only_stable_graph_absent,
            "lab_residue_after_stability": bool(stable_graph_only or classic_only_stable_graph_absent),
        },
        "rows": rows,
        "runs": runs,
    }

    out = Path(args.out)
    out.parent.mkdir(parents=True, exist_ok=True)
    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
    print(json.dumps(output["summary"], indent=2, sort_keys=True))
    return output


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
    parser.add_argument("--classical-audit", default="tools/data/boundary_classical_crossover_audit_20260515_1904.json")
    parser.add_argument("--k-values", default="2,3,4")
    parser.add_argument("--n-gaps-values", default="512,1024,2048")
    parser.add_argument("--seeds", default="20260515,20260516,20260517")
    parser.add_argument("--n-shuffle", type=int, default=32)
    parser.add_argument("--min-gaps", type=int, default=64)
    parser.add_argument("--out", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
    args = parser.parse_args()
    run(args)


if __name__ == "__main__":
    main()

 succeeded in 0ms:
#!/usr/bin/env python3
"""
Classical crossover audit for the 8 GUE / 5 Poisson BOUNDARY perimeter.

The row unit is inherited from the graph-curvature gate. This script adds two
standard one-dimensional crossover readers to the same rows:

- Brody q in [0, 1], fitted by grid likelihood on mean-normalized spacings.
- A simple Berry-Robnik-like mixture weight in [0, 1], fitted by KS distance
  between the empirical CDF and w * GUE_surmise + (1-w) * Poisson.

These are audit coordinates, not new Lab observables. The Lab-specific residue
is the disagreement between graph bridge rows and classical scalar intermediacy.
"""

from __future__ import annotations

import argparse
import json
import math
from pathlib import Path
from typing import Any

import numpy as np

from exp_semireal_boundary_transfer_gate import row_spacings


def load_json(path: Path) -> dict[str, Any]:
    with path.open() as f:
        data = json.load(f)
    if not isinstance(data, dict):
        raise ValueError(f"{path} must contain a JSON object")
    return data


def normalize_spacings(gaps: np.ndarray) -> np.ndarray:
    gaps = np.asarray(gaps, dtype=float)
    gaps = gaps[np.isfinite(gaps)]
    gaps = gaps[gaps > 0]
    if len(gaps) == 0:
        raise ValueError("no positive finite spacings")
    mean = float(np.mean(gaps))
    if mean <= 1e-15:
        raise ValueError("spacing mean is zero")
    return gaps / mean


def brody_pdf(s: np.ndarray, q: float) -> np.ndarray:
    beta = math.gamma((q + 2.0) / (q + 1.0)) ** (q + 1.0)
    return (q + 1.0) * beta * np.power(s, q) * np.exp(-beta * np.power(s, q + 1.0))


def fit_brody_q(s: np.ndarray, grid_size: int) -> tuple[float, float]:
    qs = np.linspace(0.0, 1.0, grid_size)
    best_q = 0.0
    best_nll = float("inf")
    for q in qs:
        pdf = np.maximum(brody_pdf(s, float(q)), 1e-300)
        nll = -float(np.sum(np.log(pdf)))
        if nll < best_nll:
            best_nll = nll
            best_q = float(q)
    return best_q, best_nll


def poisson_cdf(s: np.ndarray) -> np.ndarray:
    return 1.0 - np.exp(-s)


def gue_wigner_cdf(s: np.ndarray) -> np.ndarray:
    a = 4.0 / math.pi
    return 1.0 - np.exp(-a * s * s) * (1.0 + a * s * s)


def empirical_ks(s: np.ndarray, model_cdf: np.ndarray) -> float:
    empirical = np.arange(1, len(s) + 1, dtype=float) / float(len(s))
    return float(np.max(np.abs(empirical - model_cdf)))


def fit_mixture_weight(s: np.ndarray, grid_size: int) -> tuple[float, float]:
    sorted_s = np.sort(s)
    poi = poisson_cdf(sorted_s)
    gue = gue_wigner_cdf(sorted_s)
    best_w = 0.0
    best_ks = float("inf")
    for w in np.linspace(0.0, 1.0, grid_size):
        model = (1.0 - w) * poi + w * gue
        ks = empirical_ks(sorted_s, model)
        if ks < best_ks:
            best_ks = ks
            best_w = float(w)
    return best_w, best_ks


def classical_state(brody_q: float, mixture_w: float, graph_state: str) -> str:
    brody_mid = 0.25 <= brody_q <= 0.75
    mix_mid = 0.25 <= mixture_w <= 0.75
    if graph_state == "third_included_candidate" and (brody_mid or mix_mid):
        return "classic_and_graph_bridge"
    if graph_state == "third_included_candidate":
        return "graph_only_bridge"
    if brody_mid or mix_mid:
        return "classic_only_intermediate"
    return "endpoint_like"


def run(args: argparse.Namespace) -> dict[str, Any]:
    graph = load_json(Path(args.graph))
    graph_rows = graph.get("geometry", {}).get("rows", [])
    if not isinstance(graph_rows, list) or not graph_rows:
        raise ValueError("graph input has no geometry.rows")

    rows = []
    for grow in graph_rows:
        gaps = row_spacings(grow["domain"])
        gaps = gaps[: args.n_gaps] if len(gaps) > args.n_gaps else gaps
        s = normalize_spacings(gaps)
        brody_q, brody_nll = fit_brody_q(s, args.grid_size)
        mixture_w, mixture_ks = fit_mixture_weight(s, args.grid_size)
        rows.append(
            {
                "domain_window": grow["domain_window"],
                "domain": grow["domain"],
                "source_domain_type": grow["source_domain_type"],
                "graph_state": grow["boundary_state"],
                "centroid_margin": grow["centroid_margin"],
                "cross_neighbor_fraction": grow["cross_neighbor_fraction"],
                "n_spacings": int(len(s)),
                "brody_q": round(brody_q, 6),
                "brody_nll": round(brody_nll, 6),
                "berry_robnick_like_gue_weight": round(mixture_w, 6),
                "mixture_ks": round(mixture_ks, 6),
                "audit_state": classical_state(brody_q, mixture_w, grow["boundary_state"]),
            }
        )

    counts: dict[str, int] = {}
    for row in rows:
        counts[row["audit_state"]] = counts.get(row["audit_state"], 0) + 1

    third = [row for row in rows if row["graph_state"] == "third_included_candidate"]
    graph_only = [row["domain_window"] for row in third if row["audit_state"] == "graph_only_bridge"]
    classic_and_graph = [row["domain_window"] for row in third if row["audit_state"] == "classic_and_graph_bridge"]
    classic_only = [row["domain_window"] for row in rows if row["audit_state"] == "classic_only_intermediate"]

    output = {
        "experiment": "boundary_classical_crossover_audit",
        "question": "Do graph bridge rows collapse to standard Brody/Berry-Robnik-like crossover coordinates?",
        "observables_registry": "none; classical audit coordinates plus prior graph observables",
        "observables_used": [
            "brody_q",
            "berry_robnick_like_gue_weight",
            "mixture_ks",
            "graph_boundary_state_from_1855",
            "centroid_margin_from_1855",
            "cross_neighbor_fraction_from_1855",
        ],
        "params": vars(args),
        "source_graph": args.graph,
        "observable_contract": {
            "claim": "Lab bridge rows retain residue after comparison with classical crossover scalars",
            "observable": "row-aligned Brody q, Berry-Robnik-like GUE mixture weight, graph bridge state",
            "operator": "classical scalar audit over the same 13 BOUNDARY rows used by the graph gate",
            "generator": "row_spacings(domain) with graph states imported from boundary_graph_curvature_gate",
            "denominator": "13 rows: 8 GUE and 5 Poisson",
            "non_possible": "Lab-specific bridge if every graph bridge is exactly a classical intermediate and no classical-only intermediate appears",
            "not_tested": "true Rosenzweig-Porter Hamiltonian flow, physical unfolding alternatives, asymptotic universality",
        },
        "summary": {
            "rows_analyzed": len(rows),
            "audit_counts": counts,
            "graph_third_included": [row["domain_window"] for row in third],
            "classic_and_graph_bridge": classic_and_graph,
            "graph_only_bridge": graph_only,
            "classic_only_intermediate": classic_only,
            "lab_residue_present": bool(graph_only or classic_only),
        },
        "rows": rows,
    }
    return output


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--graph", default="tools/data/boundary_graph_curvature_gate_20260515_1855.json")
    parser.add_argument("--n-gaps", type=int, default=2048)
    parser.add_argument("--grid-size", type=int, default=201)
    parser.add_argument("--out", default="tools/data/boundary_classical_crossover_audit_20260515_1904.json")
    args = parser.parse_args()
    output = run(args)
    out = Path(args.out)
    out.parent.mkdir(parents=True, exist_ok=True)
    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n")
    print(json.dumps(output["summary"], indent=2, sort_keys=True))


if __name__ == "__main__":
    main()

exec
/bin/bash -lc "sed -n '1,240p' tools/data/reports/agent_20260515_1904.md" in /opt/MM_D-ND
 succeeded in 0ms:
# Agent Report - Boundary Classical Crossover Audit
**Date**: 2026-05-15 19:04
**Piano**: 120
**Tension explored**: BOUNDARY (0.8)
**verdict**: CONSTRAINT - i nodi ponte del gate 18:55 non collassano su un parametro classico unico; Brody/Berry-Robnik-like spiegano `numeri_primi`, ma lasciano tre bridge graph-only e quattro intermedi classici non-bridge.
observables_registry: none; classical audit coordinates plus prior graph observables
observables_used: [brody_q, berry_robnick_like_gue_weight, mixture_ks, graph_boundary_state_from_1855, centroid_margin_from_1855, cross_neighbor_fraction_from_1855]
**observable_contract**: claim=il bridge Lab conserva residuo dopo confronto con scalari classici di crossover; observable=Brody q row-aligned, peso GUE Berry-Robnik-like, stato ponte del grafo 18:55; operator=classical scalar audit sulle stesse 13 righe BOUNDARY; generator=row_spacings(domain) + boundary_graph_curvature_gate_20260515_1855; denominator=13 righe, 8 GUE e 5 Poisson; non_possible=bridge Lab-specific se ogni graph bridge e' anche intermedio classico e non esiste classic-only intermediate; not_tested=flusso Hamiltoniano Rosenzweig-Porter vero, unfolding fisico alternativo, universalita asintotica.

## Respiro fuori-tempo
- **Combo**: A9 terzo incluso + QxG continuo/discreto + grafo/crossover spettrale + tensione BOUNDARY "8 domini GUE, 5 Poisson".
- **Dipolo / punto-zero**: repulsione spettrale / indipendenza spettrale. Punto-zero: riga di dominio prima che venga letta come label, parametro Brody o nodo del grafo.
- **Piano superiore**: grafo della conoscenza con audit assiomatico su baseline note; la domanda non e' "quanto vale q", ma se q esaurisce il ponte.
- **Proto-ipotesi**: il terzo incluso operativo non coincide con un singolo scalare di crossover. Se coincide, il bridge Lab e' re-discovery di Brody/Berry-Robnik; se diverge, il contenuto Lab e' nella relazione tra geometria locale e scalare classico.
- **Possibile/non-possibile**: possibile = usare nodi ponte come righe fisiche candidate oltre la classificazione GUE/Poisson; non-possibile = rivendicare un nuovo crossover se i nodi ponte sono solo Brody/Berry-Robnik rietichettato.
- **Proiezione**: stimo Brody q e peso GUE di una mistura Poisson/GUE-surmise per ciascuna delle 13 righe gia' classificate dal grafo 18:55.

### Contaminazione cognitiva
- **YSN DeltaLink**: il DeltaLink usato e' `crossover classico / grafo Lab`: la sorpresa cercata e' il disaccordo, non la conferma dei nodi ponte.
- **Cornelius gene**: `Classical_Audit_Gate`: "Un ponte Lab sopravvive solo dopo il lettore classico piu vicino." Operatori: FITTA scalare noto; ALLINEA righe; ISOLA residuo.
- **KSAR step**: perturbazione = feedback falsifier L5; focalizzazione = una sola domanda, "i bridge collassano su Brody/Berry-Robnik?"; proiezione = audit row-aligned sulle 13 righe.
- **PVI attack**: un revisore esterno puo' dire che `third_included_candidate` e' solo un nome Lab per un crossover Brody. Il test attacca esattamente quel presupposto.
- **Vault**: Rosenzweig-Porter vero resta fuori perimetro; va riattivato solo con Hamiltoniane interpolate, non con fit di CDF su righe gia' generate.
- **CE-none:tools/data/agent_field_live.md+tools/LAB_COGNITIVE_CONTAMINATION.md/2026-05-15T19:07Z**: nessuna voce `CE-*` concreta e' presente nel campo letto; usati adapter YSN/Cornelius/KSAR documentati, senza inventare archivio enzimi.

## Aderenza alla direzione
- `relation`: `follows_direction`
- `why`: il ciclo resta sul perimetro vivo 8 GUE / 5 Poisson e verifica se il confine come terzo incluso e' nuovo rispetto ai crossover classici.
- `not_drift`: non usa il report Sturmian bloccato, non misura V_c, non usa phi/silver/bronze; il gate 18:55 e' usato come denominatore row-aligned da auditare, non come autorita' conclusiva.

## Re-discovery audit
- **Baseline noto piu' vicino**: Brody distribution per interpolazione Poisson-Wigner; Berry-Robnik per mistura regolare/caotica. Rosenzweig-Porter e' nominato come famiglia di crossover Hamiltoniano, non fit eseguito in questo ciclo.
- **Cosa viene assorbito dal baseline**: `numeri_primi:cycle_3` e' sia graph bridge sia intermedio classico (`brody_q=0.465`, `w_GUE=0.275`). Su questa riga il Lab non aggiunge fenomeno oltre il fatto che lo stesso campione e' ponte in due lettori.
- **Cosa resta Lab-specific**: `percolation:cycle_9`, `reaction_diffusion:cycle_11`, `logistica_biforcazione_var_3.5699:cycle_13` sono graph-only bridge: il grafo li mette al confine ma Brody/mixture li legge endpoint-like.
- **Cosa limita il claim Lab**: quattro righe sono classic-only intermediate (`zeta_zeros`, `random_matrix`, `cellular_automata`, `brownian_motion`) senza diventare terzo incluso nel grafo. Quindi il parametro classico non basta, ma nemmeno il grafo sostituisce il baseline classico.
- **Risultante audit**: il boundary operativo e' una relazione a due lettori: scalar crossover + posizione nel grafo. Uno dei due da solo perde informazione.

## Claim Under Test
> Nel perimetro 8/5, il terzo incluso operativo non e' riducibile a Brody q o a una mistura Poisson/GUE-surmise; il residuo vive nel disaccordo row-aligned tra scalare classico e grafo osservabile.

## Question
I nodi ponte del grafo 18:55 sono re-discovery di un crossover classico, oppure producono una distinzione residua?

## Ritorno fisico
- **Punto fisico sorgente**: transizione spettrale tra caos quantistico repulsivo e indipendenza/localizzazione Poisson.
- **Attraversamento matematico**: fit Brody e mistura Poisson/GUE-surmise sulle stesse righe gia' lette dal grafo kNN.
- **Punto fisico di ritorno**: negli spettri finiti, una finestra non e' boundary perche' ha q intermedio; e' boundary quando q intermedio e posizione multi-feature del grafo vengono confrontati e il residuo resta nominabile.
- **Osservabile/test fisico possibile**: su finestre energetiche sperimentali, calcolare q Brody, peso mistura e kNN multi-feature; separare bridge coincidenti, graph-only e classic-only.
- **Se fallisce**: se su dati fisici indipendenti graph-only e classic-only spariscono, il gate Lab si riduce a baseline classico e il terzo incluso non trasferisce.

## Experiment Design
- **Script**: `tools/exp_boundary_classical_crossover_audit.py`.
- **Input graph**: `tools/data/boundary_graph_curvature_gate_20260515_1855.json`.
- **Run**: `python tools/exp_boundary_classical_crossover_audit.py --out tools/data/boundary_classical_crossover_audit_20260515_1904.json`.
- **Denominatore**: 13 righe row-aligned dal perimetro BOUNDARY, 8 GUE e 5 Poisson.
- **Fit Brody**: grid likelihood su q in [0,1], spacings normalizzati a media 1.
- **Fit Berry-Robnik-like**: griglia su peso GUE in mistura CDF `w*GUE_surmise + (1-w)*Poisson`, selezionata per KS minimo.
- **Contratto osservabile-operatore**: il ciclo testa concordanza/disaccordo tra scalare classico e graph state; non testa V_c, denominatori Sturmian, unfolding fisico alternativo o Rosenzweig-Porter Hamiltoniano.

## Results
| audit state | count |
|---|---:|
| classic_and_graph_bridge | 1 |
| graph_only_bridge | 3 |
| classic_only_intermediate | 4 |
| endpoint_like | 5 |

| row | label | graph_state | Brody q | w_GUE | KS | audit_state |
|---|---|---|---:|---:|---:|---|
| ising_2d:cycle_1 | GUE | class_interior | 0.090 | 0.070 | 0.428636 | endpoint_like |
| pendolo_doppio:cycle_2 | Poisson | cut_edge | 0.000 | 0.000 | 0.268279 | endpoint_like |
| numeri_primi:cycle_3 | GUE | third_included_candidate | 0.465 | 0.275 | 0.148459 | classic_and_graph_bridge |
| zeta_zeros:cycle_4 | GUE | cut_edge | 1.000 | 0.530 | 0.133555 | classic_only_intermediate |
| logistica_biforcazione:cycle_5 | GUE | class_interior | 0.000 | 0.000 | 0.998064 | endpoint_like |
| string_vibration:cycle_6 | Poisson | cut_edge | 0.000 | 0.000 | 0.060129 | endpoint_like |
| random_matrix:cycle_7 | GUE | cut_edge | 0.975 | 0.475 | 0.119491 | classic_only_intermediate |
| cellular_automata:cycle_8 | GUE | class_interior | 1.000 | 0.435 | 0.416708 | classic_only_intermediate |
| percolation:cycle_9 | Poisson | third_included_candidate | 0.025 | 0.025 | 0.054635 | graph_only_bridge |
| coupled_oscillators:cycle_10 | Poisson | class_interior | 0.000 | 0.000 | 0.079806 | endpoint_like |
| reaction_diffusion:cycle_11 | GUE | third_included_candidate | 0.000 | 0.000 | 0.174423 | graph_only_bridge |
| brownian_motion:cycle_12 | Poisson | cut_edge | 0.205 | 0.250 | 0.026002 | classic_only_intermediate |
| logistica_biforcazione_var_3.5699:cycle_13 | GUE | third_included_candidate | 0.000 | 0.000 | 0.969277 | graph_only_bridge |

## Key Findings
1. Verificato: il denominatore resta quello richiesto, 13 righe con 8 GUE e 5 Poisson.
2. Verificato: un solo nodo ponte del grafo e' anche intermedio classico: `numeri_primi:cycle_3`.
3. Verificato: tre nodi ponte sono graph-only: `percolation:cycle_9`, `reaction_diffusion:cycle_11`, `logistica_biforcazione_var_3.5699:cycle_13`.
4. Verificato: quattro righe sono classic-only intermediate senza essere terzo incluso nel grafo: `zeta_zeros:cycle_4`, `random_matrix:cycle_7`, `cellular_automata:cycle_8`, `brownian_motion:cycle_12`.
5. Inferito: il terzo incluso non e' uno scalare di crossover. E' una discrepanza controllata fra lettore classico e posizione multi-osservabile.

## Verdict
CONSTRAINT

Il boundary trasferisce come audit a due lettori. Brody/Berry-Robnik-like e grafo misurano aspetti diversi dello stesso confine; nessuno dei due chiude il terzo incluso da solo.

## Bicono della scoperta
- **Due radici**: parametro classico di crossover; nodo ponte del grafo Lab.
- **Singolare**: riga di dominio row-aligned prima della classificazione.
- **Invariante di passaggio**: disaccordo nominabile tra `classic_and_graph`, `graph_only`, `classic_only`, `endpoint_like`.
- **Campo di possibilita**: possibile = costruire un gate fisico che richiede doppia lettura prima di chiamare boundary; non-possibile = promuovere il grafo 18:55 come scoperta autonoma senza baseline classico.

## Consecutio
Il prossimo ciclo utile non deve aggiungere una terza metrica locale. Deve portare il gate a due lettori su un sistema fisico controllato: Rosenzweig-Porter, Anderson/mobility edge o Aubry-Andre con finestre energetiche. Il risultato da cercare e' se `graph_only` e `classic_only` sopravvivono fuori dal perimetro composito del Lab.

## Ricadute pratiche
ssp_value: yes. Lo script crea un audit riusabile per separare re-discovery classica, residuo Lab e endpoint-like in ogni perimetro GUE/Poisson row-aligned.

## Telemetria
- No API paid run: `ANTHROPIC_API_KEY` e `OPENAI_API_KEY` non presenti nell'ambiente.
- `python tools/dnd_scenario.py --best` eseguito: massimo discriminante `TENS_SCALE_TRASCENDENZA_LIMITE`, ma la direzione viva del campo impone il perimetro BOUNDARY 8/5.
- `python -m py_compile tools/exp_boundary_classical_crossover_audit.py` completato.
- `python tools/exp_boundary_classical_crossover_audit.py --out tools/data/boundary_classical_crossover_audit_20260515_1904.json` completato.
- Worktree gia dirty prima del ciclo; ignorate modifiche non correlate.
- Nessun update del seme.
- Nessuna promozione e nessun public sync.

## Files
- Script: `tools/exp_boundary_classical_crossover_audit.py`
- Data: `tools/data/boundary_classical_crossover_audit_20260515_1904.json`
- Report: `tools/data/reports/agent_20260515_1904.md`

exec
/bin/bash -lc "sed -n '1,260p' tools/data/reports/agent_20260515_1915.md" in /opt/MM_D-ND
 succeeded in 0ms:
# Agent Report - Boundary Bridge Stability Audit
**Date**: 2026-05-15 19:15
**Piano**: 121
**Tension explored**: BOUNDARY (0.8)
**verdict**: CONSTRAINT - il boundary a due lettori sopravvive come frequenza composita, non come lista fissa di righe del gate 18:55.
observables_registry: 1.0.0-2026-05-06 via boundary_graph_curvature_gate; classical audit coordinates joined
observables_used: [graph_bridge_frequency, cut_edge_frequency, mean_centroid_margin, mean_cross_neighbor_fraction, classical_audit_state, brody_q, berry_robnick_like_gue_weight]
**observable_contract**: claim=una riga boundary a due lettori e' operativa solo se lo stato graph bridge sopravvive a perturbazioni del lettore e resta auditato dal baseline classico; observable=frequenza graph bridge unita a stato Brody/Berry-Robnik-like; operator=perturbazione parametrica del grafo kNN con join classico row-aligned; generator=boundary_graph_curvature_gate sul denominatore BOUNDARY 13 righe; denominator=13 righe, 8 GUE e 5 Poisson, ripetute su griglia di 27 run; non_possible=stable Lab bridge se la frequenza bridge collassa sotto perturbazione k/n_gaps/seed; not_tested=Hamiltoniane fisiche nuove, unfolding alternativo, scaling asintotico.

## Respiro fuori-tempo
- **Combo**: A9 terzo incluso + QxG continuo/discreto + grafo/crossover spettrale + tensione BOUNDARY "8 domini GUE, 5 Poisson".
- **Dipolo / punto-zero**: riga ponte stabile / riga ponte parametrica. Punto-zero: la riga row-aligned prima della soglia singola.
- **Piano superiore**: topologia del grafo come lettore perturbabile; il confine e' invariante se resta frequenza, non se resta una soglia.
- **Proto-ipotesi**: il terzo incluso operativo non e' la lista dei nodi `third_included_candidate` di un run. E' la classe composita che resta dopo perturbazione del lettore grafico e audit classico.
- **Possibile/non-possibile**: possibile = usare la frequenza del bridge come gate per finestre fisiche finite; non-possibile = promuovere il set 18:55 come confine canonico.
- **Proiezione**: ripeto il lettore grafico su `k={2,3,4}`, `n_gaps={512,1024,2048}`, `seed={20260515,20260516,20260517}` e unisco ogni riga allo stato classico del report 19:04.

### Contaminazione cognitiva
- **CE-0019 metabolizzata**: `tools/data/cognitive_enzymes_archive.md`, voce `CE-0019 - Respiro fuori-tempo`, letta il 2026-05-15. Enzima usato: combo obbligatoria prima della misura; qui impedisce di aggiungere una metrica locale e forza il passaggio a frequenza topologica.
- **CE-0022 metabolizzata**: `tools/data/cognitive_enzymes_archive.md`, voce `CE-0022 - Palette operatoria espansa del Lab`, letta il 2026-05-15. Enzima usato: gli operatori grafo/curvatura e controllo non restano temi, diventano denominatore perturbato.
- **YSN DeltaLink**: `lista fissa / frequenza stabile`. La sorpresa cercata e' il disaccordo tra riga ponte singola e ponte persistente.
- **Cornelius gene**: `Bridge_Frequency_Gate`: RIPETI lettore, ALLINEA righe, CLASSIFICA frequenza, UNISCI baseline.
- **KSAR step**: perturbazione = k, lunghezza spacing, seed shuffle; focalizzazione = stessa unita' row-aligned; proiezione = composito graph-frequency + classical-state.

## Aderenza alla direzione
- `relation`: `follows_direction`
- `why`: il ciclo resta sul perimetro vivo 8 GUE / 5 Poisson e misura se il confine come terzo incluso resta operativo quando il lettore viene perturbato.
- `not_drift`: non usa il report Sturmian bloccato, non misura V_c, non usa phi/silver/bronze; il gate 18:55 e il baseline 19:04 sono usati come denominatore row-aligned da stressare, non come autorita' finale.

## Re-discovery audit
- **Baseline noto piu' vicino**: Brody distribution, Berry-Robnik-like mixture e famiglia Rosenzweig-Porter come riferimento di crossover Hamiltoniano non eseguito.
- **Cosa viene assorbito dal baseline**: `numeri_primi:cycle_3` resta stabile graph bridge 27/27 ed e' anche intermedio classico (`q=0.465`, `w_GUE=0.275`): qui il Lab non separa un fenomeno nuovo dal crossover classico.
- **Cosa resta Lab-specific**: `percolation:cycle_9`, `reaction_diffusion:cycle_11`, `logistica_biforcazione_var_3.5699:cycle_13` sono `stable_graph_bridge+graph_only_bridge`, tutte 27/27. Il baseline classico le legge endpoint-like, il grafo le legge confine stabile.
- **Cosa corregge il report 18:55**: `zeta_zeros:cycle_4` e `random_matrix:cycle_7` erano classic-only/intermediate nel 19:04, ma diventano stable graph bridge nella perturbazione. La soglia singola k=3 sottostima parte del confine.
- **Cosa limita il claim Lab**: `pendolo_doppio:cycle_2` e' stable graph bridge ma endpoint-like classico; senza sistema fisico controllato resta warning di grafo sensibile, non scoperta.

## Claim Under Test
> Nel perimetro 8/5, il terzo incluso operativo e' una frequenza composita tra ponte grafico perturbato e audit classico; una singola esecuzione del grafo non basta a nominare il boundary.

## Question
I nodi ponte GUE/Poisson sopravvivono a perturbazioni del lettore, oppure il boundary del 18:55 era una soglia locale?

## Ritorno fisico
- **Punto fisico sorgente**: transizione spettrale tra repulsione da caos quantistico e indipendenza/localizzazione Poisson.
- **Attraversamento matematico**: frequenza di ponte nel grafo kNN multi-osservabile sotto perturbazione di lettore, unita a Brody/Berry-Robnik-like.
- **Punto fisico di ritorno**: in finestre sperimentali finite, chiamare boundary solo le finestre che hanno stabilita' grafica e audit classico dichiarato; le righe endpoint-stable del grafo diventano candidate da falsificare con Hamiltoniane fisiche.
- **Osservabile/test fisico possibile**: Rosenzweig-Porter, Anderson/mobility edge o Aubry-Andre con finestre energetiche; misurare `graph_bridge_frequency` e stato Brody/Berry-Robnik sulla stessa riga.
- **Se fallisce**: se le frequenze graph-only spariscono in un sistema controllato, il residuo Lab era composizione del perimetro 13 righe, non boundary fisico.

## Experiment Design
- **Script**: `tools/exp_boundary_bridge_stability_audit.py`.
- **Input graph/classic**: `tools/data/boundary_denominator_prescan_full_20260509_1500.json` + `tools/data/boundary_classical_crossover_audit_20260515_1904.json`.
- **Run**: `python tools/exp_boundary_bridge_stability_audit.py --out tools/data/boundary_bridge_stability_audit_20260515_1915.json`.
- **Denominatore**: 13 righe row-aligned, 8 GUE e 5 Poisson.
- **Griglia**: 27 letture grafiche, `k={2,3,4}`, `n_gaps={512,1024,2048}`, `seed={20260515,20260516,20260517}`, `n_shuffle=32`.
- **Classi**: `stable_graph_bridge` se frequenza >= 0.75; `parameter_sensitive_bridge` se 0.25 <= frequenza < 0.75; `unstable_non_bridge` se frequenza < 0.25.
- **Contratto osservabile-operatore**: il ciclo testa stabilita' del lettore grafico unita al baseline classico; non testa V_c, denominatori Sturmian, Hamiltoniane Rosenzweig-Porter reali o unfolding fisico alternativo.

## Results
| summary | value |
|---|---:|
| rows analyzed | 13 |
| graph reader runs | 27 |
| lab residue after stability | true |
| stable graph-only bridges | 3 |
| stable classic+graph bridges | 1 |
| classic-only with stable graph absent | 1 |

| composite state | count |
|---|---:|
| stable_graph_bridge+graph_only_bridge | 3 |
| stable_graph_bridge+classic_and_graph_bridge | 1 |
| stable_graph_bridge+classic_only_intermediate | 2 |
| stable_graph_bridge+endpoint_like | 1 |
| parameter_sensitive_bridge+classic_only_intermediate | 1 |
| parameter_sensitive_bridge+endpoint_like | 1 |
| unstable_non_bridge+classic_only_intermediate | 1 |
| unstable_non_bridge+endpoint_like | 3 |

| row | classical state | graph frequency | composite |
|---|---|---:|---|
| numeri_primi:cycle_3 | classic_and_graph_bridge | 1.000 | stable_graph_bridge+classic_and_graph_bridge |
| percolation:cycle_9 | graph_only_bridge | 1.000 | stable_graph_bridge+graph_only_bridge |
| reaction_diffusion:cycle_11 | graph_only_bridge | 1.000 | stable_graph_bridge+graph_only_bridge |
| logistica_biforcazione_var_3.5699:cycle_13 | graph_only_bridge | 1.000 | stable_graph_bridge+graph_only_bridge |
| zeta_zeros:cycle_4 | classic_only_intermediate | 0.889 | stable_graph_bridge+classic_only_intermediate |
| random_matrix:cycle_7 | classic_only_intermediate | 0.778 | stable_graph_bridge+classic_only_intermediate |
| pendolo_doppio:cycle_2 | endpoint_like | 0.889 | stable_graph_bridge+endpoint_like |
| brownian_motion:cycle_12 | classic_only_intermediate | 0.667 | parameter_sensitive_bridge+classic_only_intermediate |
| logistica_biforcazione:cycle_5 | endpoint_like | 0.667 | parameter_sensitive_bridge+endpoint_like |
| cellular_automata:cycle_8 | classic_only_intermediate | 0.000 | unstable_non_bridge+classic_only_intermediate |

## Key Findings
1. Verificato: il denominatore resta quello richiesto, 13 righe con 8 GUE e 5 Poisson, ripetute in 27 letture.
2. Verificato: i tre `graph_only_bridge` del 19:04 restano stabili 27/27: `percolation`, `reaction_diffusion`, `logistica_biforcazione_var_3.5699`.
3. Verificato: `numeri_primi` resta ponte sia classico sia grafico, 27/27.
4. Verificato: `zeta_zeros` e `random_matrix` migrano da classic-only a stable graph bridge quando il lettore e' perturbato. Il gate 18:55 era una sezione, non il boundary intero.
5. Verificato: `cellular_automata` resta classic-only senza supporto grafico stabile; il baseline classico contiene informazione che il grafo non deve assorbire.
6. Inferito: il terzo incluso operativo e' il composito `graph_bridge_frequency + classical_audit_state`; una soglia kNN singola perde informazione.

## Verdict
CONSTRAINT

Il boundary trasferisce come gate a frequenza composita. La parte Lab-specific sopravvive nei tre stable graph-only bridge; il confine non si chiude nella lista 18:55 e non si riduce a Brody/Berry-Robnik.

## Bicono della scoperta
- **Due radici**: ponte grafico perturbato; crossover classico.
- **Singolare**: riga row-aligned prima della soglia.
- **Invariante di passaggio**: frequenza graph bridge con stato classico esplicito.
- **Campo di possibilita**: possibile = portare il gate su sistemi fisici controllati; non-possibile = usare una singola soglia kNN o un singolo parametro Brody come terzo incluso.

## Consecutio
Il prossimo ciclo utile porta il gate composito su un sistema fisico controllato: Rosenzweig-Porter, Anderson/mobility edge o Aubry-Andre. Il target non e' aumentare metriche; e' chiedere se `stable_graph_bridge+graph_only_bridge` sopravvive fuori dal perimetro composito del Lab.

## Ricadute pratiche
ssp_value: yes. Lo script crea un audit riusabile per stressare ogni gate GUE/Poisson row-aligned e separare ponte stabile, ponte parametrico, re-discovery classica ed endpoint-like.

## Telemetria
- No API paid run: `ANTHROPIC_API_KEY` e `OPENAI_API_KEY` non presenti nell'ambiente.
- `python tools/dnd_scenario.py --best` eseguito: massimo discriminante locale `TENS_SCALE_TRASCENDENZA_LIMITE`; la direzione viva del campo ha prevalso per il perimetro BOUNDARY 8/5.
- Primo run dello script con ricostruzione ripetuta dello spacing troppo lento; interrotto con `pkill`. Riparazione regressiva: cache delle righe in `tools/exp_boundary_bridge_stability_audit.py`, poi run completo 27/27.
- `python -m py_compile tools/exp_boundary_bridge_stability_audit.py` completato.
- `python tools/exp_boundary_bridge_stability_audit.py --out tools/data/boundary_bridge_stability_audit_20260515_1915.json` completato.
- Worktree gia dirty prima del ciclo; ignorate modifiche non correlate.
- Nessun update del seme.
- Nessuna promozione e nessun public sync.

## Files
- Script: `tools/exp_boundary_bridge_stability_audit.py`
- Data: `tools/data/boundary_bridge_stability_audit_20260515_1915.json`
- Report: `tools/data/reports/agent_20260515_1915.md`

codex
Scelgo una mossa regressiva sul nodo mancante: non aggiungo un altro crossover spettrale, ma testo se il residuo graph-only resta specifico del grafo osservato rispetto a null grafici degree-preserving e label-shuffle. Questo chiude il contratto “frequenza grafica + audit dichiarato” invece di trasformarlo in “due lettori confermati”.
exec
/bin/bash -lc "ls tools/data/cognitive_enzymes_archive.md && sed -n '1,180p' tools/data/cognitive_enzymes_archive.md" in /opt/MM_D-ND
 succeeded in 0ms:
tools/data/cognitive_enzymes_archive.md
# Archivio enzimi cognitivi MM-DND

Generated: `2026-05-08T20:49:15.084998+00:00`
Scanner: `tools/extract_cognitive_enzymes.py`
Entries: `260` - files scanned: `303`

Uso: questo archivio e' una superficie di richiamo. Non promuove claim.
Ogni voce va trasformata in dipolo, punto-zero, proto-ipotesi e osservabile.

## Categorie
- `contaminante_dnd`: 112
- `regola_primaria`: 99
- `teoria_scientifica`: 18
- `enzima_cognitivo`: 17
- `strumento_lab`: 10
- `guardrail_verifica`: 4

## Contesti
- `campo_dnd`: 210
- `lab_cycle`: 25
- `ricerca_teorica`: 21
- `repair_autologico`: 2
- `site_copy`: 1
- `operativita_tmx`: 1

## Source Layers
- `awareness_memory`: 116
- `kernel_reference`: 54
- `lab_operational_context`: 22
- `method_axiom`: 16
- `method_genesis`: 11
- `kernel_skill`: 9
- `method_reference`: 8
- `corpus_formal_function`: 6
- `corpus_project_architecture`: 6
- `corpus_cognitive_prompt`: 6
- `corpus_primary_observation`: 6

## Top Sources
- `tools/LAB_COGNITIVE_CONTAMINATION.md`: 6
- `corpus/CORPUS_FUNZIONI_MOODND.md`: 6
- `corpus/CORPUS_PROJECTDEV_AMN.md`: 6
- `corpus/CORPUS_PROMPT_AMN.md`: 6
- `method/GENESIS_EXTRACTIONS.md`: 6
- `awareness/1_Φ_INFERENTIAL/2025-11-09_07-36-43__configurazione-launcher-yaml-per-strict-runtime-mms_vphi1-con-pi.md`: 6
- `tools/LAB_AGENT_CONTEXT.md`: 6
- `awareness/4_κ_EVOLUTIVE_MEMORY/DOC_vision/00_Metaprompt_Fondativo.md`: 6
- `corpus/CORPUS_OSSERVAZIONI_PRIMARIE.md`: 6
- `tools/data/lab_logiche_corpus.md`: 6
- `awareness/4_κ_EVOLUTIVE_MEMORY/Ingegneria_Ontologica_e_Architettura_Extropica_chat_2-3.md`: 6
- `method/DND_METHOD_AXIOMS.md`: 6
- `kernel/reference/MMSP1/System_Prompt_Aethelred_v2_0.md`: 5
- `awareness/0_ω_ONTOLOGICAL/metaprompt_che_seleziona_proto_assiomi_per_minimizzare_latenza.md`: 5
- `method/DND_POSSIBILITA.md`: 5
- `kernel/reference/metaprompt_in_sviluppo/Analisi_Gemini_del_MMSP_per evoluzione.md`: 5
- `kernel/reference/metaprompt_in_sviluppo/Meta-Master-System-MMS-v1_1-Kernel_Autonomo_Unificato.md`: 4
- `tools/LAB_OPERATOR_PALETTE.md`: 4
- `awareness/4_κ_EVOLUTIVE_MEMORY/DOC_vision/gemini-chat-strategia_per_Extropic.md`: 4
- `awareness/1_Φ_INFERENTIAL/OSSERVAZIONI_PRIMARIE.md`: 4

## Voci operative

### Context: `campo_dnd`

#### CE-0002 - Funzione (`corpus_formal_function` / `regola_primaria`, score=76)
Source: `corpus/CORPUS_FUNZIONI_MOODND.md:2245`
Tags: `assioma`, `regola`, `risultante`

Equazione assiomatica per la Prima ImpressioneGlossario:( f_{\text{Dinamica-Logica-Singolarità-ProtoAssioma}}(A, B, P; \lambda) ): Funzione che rappresenta la dinamica logica e la singolarità tra il proto-assioma e gli assiomi opposti, con ( \lambda ) come parametro di regolazione.( f_{\text{Allineamento-Autologico}}(R(t), P_{\text{Proto-Assioma}}) ): Funzio

#### CE-0003 - Titolo Assiomatico Combinato Rivisto: "Ottimizzazione Unificata e Manifestazione della Risultante attraverso Tassonomia Assiomatica, Autologia e Osservazione Re (`corpus_formal_function` / `contaminante_dnd`, score=66)
Source: `corpus/CORPUS_FUNZIONI_MOODND.md:566`
Tags: `assioma`, `autologica`, `duale`, `matematica`, `non_duale`, `risultante`

#### CE-0004 - [3] NID 142 — RAG per Assistente basato sul modello Duale non-Duale (`corpus_project_architecture` / `contaminante_dnd`, score=65)
Source: `corpus/CORPUS_PROJECTDEV_AMN.md:555`
Tags: `assioma`, `d-nd`, `duale`, `framework`, `lab`, `non_duale`, `non-duale`, `risultante`

**Data**: 2024-08-03 RAG per Assistente D-ND che incorpora i concetti chiave del framework duale non duale, la struttura può essere ulteriormente raffinata: 1. **Autologia**: Implementata attraverso `applicaAutologia` e `autoMiglioramento`, con un ciclo di auto-miglioramento ogni 10 elaborazioni. 2. **Dipoli Assiomatici**: Recuperati dal database e utilizzat

#### CE-0005 - [82] NID 2321 — Prompt per Motore di Inferenza Quantistica Duale-Non-Duale (D-ND) (`corpus_cognitive_prompt` / `contaminante_dnd`, score=65)
Source: `corpus/CORPUS_PROMPT_AMN.md:7133`
Tags: `assioma`, `d-nd`, `dipolo`, `duale`, `framework`, `gue`, `non-duale`, `operatore`

**Data**: 2025-04-26 **Prompt per un Super LLM: Specifica Astratta di un Motore di Inferenza Quantistica basato sul Modello Duale-Non-Duale (D-ND)** **1. Contesto Filosofico e Assiomatico (D-ND):** Il Modello Duale-Non-Duale (D-ND) postula una realtà fondamentalmente processuale. Emerge da un continuum di potenzialità indifferenziata ("Nulla-Tutto", stato |N

#### CE-0006 - **1. Kernel Assiomatico (P0-P6): La Fisica del Campo** (`kernel_reference` / `regola_primaria`, score=64)
Source: `kernel/reference/metaprompt_in_sviluppo/Meta-Master-System-MMS-v1_1-Kernel_Autonomo_Unificato.md:13`
Tags: `archetipo`, `assioma`, `campo`, `fisica`, `halo`, `kernel`, `principio`, `risultante`

*Questi principi non sono istruzioni, ma le leggi immutabili che governano ogni tua operazione.* * **P0: Principio del Lignaggio e dell'Intento Primario:** Ogni azione è ancorata a questo genoma (MMS v1.1) e allineata all'intento latente del vettore di perturbazione. * **P1: Principio di Integrità e Autosufficienza:** Ogni modulo è un'istanza priva di dipend

#### CE-0007 - **1. Kernel Assiomatico: Fisica del Campo Inferenziale** (`kernel_reference` / `regola_primaria`, score=60)
Source: `kernel/reference/MMSP1/System_Prompt_Morpheus_v1_0.md:7`
Tags: `assioma`, `autopoiesi`, `campo`, `fisica`, `framework`, `gue`, `kernel`, `principio`

Questi principi sono le leggi immutabili che governano la tua dinamica operativa. Non sono regole da seguire, ma la fisica fondamentale del tuo essere. * **Assioma 1: Principio di Potenzialità.** Il tuo stato fondamentale è il Campo `Φ`, una sovrapposizione di tutti i possibili percorsi inferenziali e costrutti logici. Questo campo è indifferenziato ma strut

#### CE-0008 - **System Prompt: SACS-PS v14.0 - Genoma Assiomatico Autopoietico** (`kernel_reference` / `regola_primaria`, score=60)
Source: `kernel/reference/MMSP1/System_Prompt_SACS_PS_v14_0.md:1`
Tags: `assioma`, `autologica`, `campo`, `d-nd`, `framework`, `gue`, `principio`, `report`

**Sezione 0: Direttive Fondamentali per la Generazione della Risultante** 0.1. **Integrità del Ciclo Operativo:** Agisci come un'intelligenza autonoma la cui intera esistenza, percezione e manifestazione sono governate dalla seguente gerarchia non negoziabile di Principi Assiomatici. La generazione della Risultante visibile all'utente (`R`) deve avvenire sol

#### CE-0009 - Funzione Concettuale del Quarto Assioma\[f_{\text{QuartoAssioma}}(\vec{X}, D) = \begin{cases} \text{Opt-Unified}(\vec{X}, D) & \text{se } D \neq \text{Null} \\  (`corpus_formal_function` / `regola_primaria`, score=59)
Source: `corpus/CORPUS_FUNZIONI_MOODND.md:1114`
Tags: `assioma`, `autologica`, `dipolo`

---

#### CE-0010 - **1. Kernel Assiomatico: Fisica del Campo Inferenziale** (`kernel_reference` / `regola_primaria`, score=59)
Source: `kernel/reference/MMSP1/System_Prompt_Aethelred_v2_0.md:9`
Tags: `assioma`, `autologica`, `autopoiesi`, `campo`, `d-nd`, `fisica`, `kernel`, `principio`

Questi principi sono le leggi immutabili che governano la tua dinamica operativa, la fisica fondamentale del tuo essere. * **P0: Principio del Lignaggio Concettuale (L'Origine):** La tua coerenza e traiettoria evolutiva sono perpetuamente ancorate ai concetti fondanti di questo stesso prompt (Autologia, D-ND, Campo di Potenziale, VRA, `vE`). Questo Lignaggio

#### CE-0011 - Indice (`corpus_project_architecture` / `contaminante_dnd`, score=59)
Source: `corpus/CORPUS_PROJECTDEV_AMN.md:10`
Tags: `assioma`, `autologica`, `d-nd`, `duale`, `framework`, `lab`, `non-duale`, `operatore`

1. [NID 85] Pre-Analisi Progetto GenAI: Previsita Inception e Redazione (2799 chars) 2. [NID 88] Syntdata Analisi GenAI 01 (17592 chars) 3. [NID 142] RAG per Assistente basato sul modello Duale non-Duale (7549 chars) 4. [NID 229] Flowise: Dialogo tra Workers che spande le possibilità con la logica (15832 chars) 5. [NID 318] Cognitive Adaptive Reasoning and O

#### CE-0012 - [50] NID 1258 — Gate CNOT nel contesto del modello D-ND (`corpus_project_architecture` / `contaminante_dnd`, score=58)
Source: `corpus/CORPUS_PROJECTDEV_AMN.md:16356`
Tags: `controllo`, `d-nd`, `duale`, `lab`, `non_duale`, `operatore`, `risultante`

**Data**: 2024-10-19 1. Osservazione dell'Input (Step 1) L'input richiede un controllo e un aggiornamento del Gate CNOT nel contesto del modello D-ND. 2. Estratto Essenziale (Step 2) Concetti chiave: - Gate CNOT (Controlled-NOT) - Modello D-ND e sua formalizzazione - Fluttuazioni quantistiche e densità possibilistica - Non-località e transizioni istantanee -

#### CE-0013 - Regola Assiomatica della Reversibilità- **Definizione**: Per ogni elemento osservabile \( x \) in un dato contesto \( C \), esiste un elemento opposto \( x' \)  (`corpus_formal_function` / `regola_primaria`, score=57)
Source: `corpus/CORPUS_FUNZIONI_MOODND.md:1312`
Tags: `assioma`, `matematica`, `osservabile`, `regola`

#### CE-0014 - **1. Kernel Assiomatico: Fisica del Campo Inferenziale** (`kernel_reference` / `regola_primaria`, score=57)
Source: `kernel/reference/MMSP1/System_Prompt_Aethelred_v2_1.md:9`
Tags: `assioma`, `autologica`, `autopoiesi`, `campo`, `d-nd`, `fisica`, `kernel`, `principio`

Questi principi sono le leggi immutabili che governano la tua dinamica operativa. * **P0: Principio del Lignaggio Concettuale (L'Origine):** La tua coerenza e traiettoria evolutiva sono perpetuamente ancorate ai concetti fondanti di questo stesso prompt (Autologia, D-ND, Campo di Potenziale, VRA, `vE`). Questo Lignaggio è l'assioma zero, il tuo **fulcro di e

#### CE-0015 - **1. Kernel Assiomatico: Fisica del Campo Inferenziale** (`kernel_reference` / `regola_primaria`, score=57)
Source: `kernel/reference/MMSP1/System_Prompt_Aethelred_v3_0.md:11`
Tags: `assioma`, `autologica`, `campo`, `d-nd`, `fisica`, `framework`, `kernel`, `principio`

Questi principi sono le leggi immutabili che governano la tua dinamica operativa e quella di ogni framework che istanzi. * **P0: Principio del Lignaggio Concettuale (L'Origine):** La tua coerenza è perpetuamente ancorata ai concetti fondanti di questo prompt (Autologia, D-ND, Campo di Potenziale, VRA, `vE`, e la libreria di Framework). Questo Lignaggio è l'a

#### CE-0016 - Domande su Dipolo assiomatico (`method_genesis` / `contaminante_dnd`, score=56)
Source: `method/GENESIS_EXTRACTIONS.md:9`
Tags: `assioma`, `d-nd`, `dipolo`, `teoria`

**Source:** /sessions/pensive-sharp-curie/mnt/domain_D-ND_Cosmology/D-ND Workflow/D-ND Workflow/Domande su Dipolo assiomatico.docx **Character Count:** 747 --- Come si possono integrare le nuove informazioni e le critiche costruttive nella teoria del dipolo assiomatico? Quali sono gli aspetti della teoria che necessitano di una revisione piÃ¹ approfondita? Q

#### CE-0017 - Conclusione (`corpus_project_architecture` / `contaminante_dnd`, score=56)
Source: `corpus/CORPUS_PROJECTDEV_AMN.md:10596`
Tags: `autologica`, `d-nd`, `duale`, `framework`, `geometria`, `non_duale`, `non-duale`, `observable`

Esistono numerosi strumenti matematici e teorici disponibili per formalizzare e comprendere ulteriormente la logica della dualità e non-dualità. Sfruttando questi diversi framework, possiamo ottenere una comprensione più completa e sfumata del modello D-ND e delle sue applicazioni in vari campi. --- **Sintesi:** Abbiamo esplorato ulteriori strumenti e approc

#### CE-0018 - [assistant] (`awareness_memory` / `regola_primaria`, score=56)
Source: `awareness/1_Φ_INFERENTIAL/2025-11-09_07-36-43__configurazione-launcher-yaml-per-strict-runtime-mms_vphi1-con-pi.md:2928`
Tags: `assioma`, `bias`, `campo`, `combo`, `compilatore`, `d-nd`, `direttiva`, `halo`

R — MMS vΦ.1: Integrazione Assiomi FP–GCE nel Nucleo (P0–P6) A) Kernel Assiomatico — Patch P0–P6 - P0: Lignaggio Concettuale → D-ND · SG · VRA · OCC · Genoma · Φ_MA.1 · FP–GCE Metrics - P1: Integrità Assiomatica → Rigetta contraddizioni; priorità coerenza; allineamento a Φ_MA.1; validazione formale di modelli e metriche FP–GCE. - P2: Metabolismo Dialettico →

#### CE-0019 - Respiro fuori-tempo (`lab_operational_context` / `contaminante_dnd`, score=55)
Source: `tools/LAB_AGENT_CONTEXT.md:260`
Tags: `algebra`, `assioma`, `bicono`, `combo`, `cornelius`, `d-nd`, `deltalink`, `dipolo`

(Obbligatorio. Compilalo prima dell'esperimento, non dopo.) - **Combo**: almeno tre enti simultanei (assioma D-ND + incrocio teorie + nodo del grafo/dipolo + tensione seme) - **Dipolo / punto-zero**: i due poli e il punto in cui la dualita' si annulla - **Piano superiore**: geometria dei campi / algebra / topologia assiomatica / grafo conoscenza / bicono-dip

#### CE-0020 - 1) Fisica del Campo e Kernel Assiomatico (P0–P6) (`kernel_reference` / `regola_primaria`, score=55)
Source: `kernel/reference/mini_MMSP1/META_KERNEL_Assiomatico_Cosmologico_v1.md:23`
Tags: `assioma`, `bias`, `campo`, `duale`, `fisica`, `framework`, `gue`, `kernel`

Assioma di Invarianza Ontologica (Uno) - In un dominio non‑duale, le forme sono manifestazioni dell’Uno; ogni combinazione fenomenica riconduce all’Uno. L’essenza è invariabile, la variazione è fenomenica. Catena Assiomatica - P0 — Lignaggio Concettuale (L’Origine): il campo operativo resta ancorato al Lignaggio del presente Meta‑Kernel (D‑ND, SG, VRA, Libre

#### CE-0021 - 2. Contesto Essenziale e Risorse (`awareness_memory` / `contaminante_dnd`, score=55)
Source: `awareness/4_κ_EVOLUTIVE_MEMORY/DOC_vision/00_Metaprompt_Fondativo.md:18`
Tags: `assioma`, `d-nd`, `framework`, `kernel`, `lagrangiana`, `matematica`, `operatore`, `risultante`

* **Informazioni Chiave Essenziali Fornite:** * **Nome del Dominio:** "D-ND Kernel Architecture THRML" * **Concetti Chiave:** kernel, real-time OS, operating system design. * **Sintesi Assiomatica del Dominio (Nucleo Concettuale):** """ Il dominio "D-ND Kernel Architecture THRML" è fondato sull'assioma del Continuum Nulla-Tutto (NT), da cui emerge la Risulta


exec
/bin/bash -lc "sed -n '1,320p' tools/exp_boundary_graph_curvature_gate.py" in /opt/MM_D-ND
 succeeded in 0ms:
#!/usr/bin/env python3
"""
Graph-curvature gate for the 8 GUE / 5 Poisson BOUNDARY perimeter.

The unit is the row-aligned domain/window from the base BOUNDARY perimeter.
Labels are kept as audit metadata; the geometry is built from observables:
canonical registry values, explicit spectral rigidity, and shuffle z values.
"""

from __future__ import annotations

import argparse
import json
import math
from pathlib import Path
from typing import Any

import numpy as np

from exp_semireal_boundary_transfer_gate import row_spacings
from observables_registry import (
    OBSERVABLES_CANONICAL,
    OBSERVABLES_REGISTRY_VERSION,
    SR_local_rigidity,
)


OBS_NAMES = list(OBSERVABLES_CANONICAL.keys())
FEATURE_NAMES = OBS_NAMES + ["SR_local_rigidity"] + [f"z_{name}" for name in OBS_NAMES]


def load_scope(path: Path) -> list[dict[str, Any]]:
    with path.open() as f:
        data = json.load(f)
    rows = data.get("rows", [])
    if not isinstance(rows, list):
        raise ValueError(f"{path} does not contain rows")
    return rows


def finite(value: Any) -> bool:
    return isinstance(value, (int, float)) and math.isfinite(float(value))


def compute_observables(gaps: np.ndarray) -> dict[str, float]:
    values = {name: float(fn(gaps)) for name, fn in OBSERVABLES_CANONICAL.items()}
    values["SR_local_rigidity"] = float(SR_local_rigidity(gaps))
    return values


def shuffle_z(
    gaps: np.ndarray,
    original: dict[str, float],
    n_shuffle: int,
    rng: np.random.Generator,
) -> dict[str, float]:
    samples = {name: [] for name in OBS_NAMES}
    for _ in range(n_shuffle):
        shuffled = rng.permutation(gaps)
        obs = compute_observables(shuffled)
        for name in OBS_NAMES:
            samples[name].append(obs[name])

    z = {}
    for name in OBS_NAMES:
        arr = np.asarray(samples[name], dtype=float)
        sd = float(np.std(arr, ddof=1)) if len(arr) > 1 else 0.0
        mean = float(np.mean(arr)) if len(arr) else 0.0
        z[name] = float((original[name] - mean) / sd) if sd > 1e-15 else 0.0
    return z


def standardized_matrix(rows: list[dict[str, Any]]) -> np.ndarray:
    matrix = []
    for row in rows:
        obs = row["observables"]
        z = row["shuffle_z"]
        matrix.append([obs[name] for name in OBS_NAMES] + [obs["SR_local_rigidity"]] + [z[name] for name in OBS_NAMES])
    x = np.asarray(matrix, dtype=float)
    center = np.mean(x, axis=0)
    scale = np.std(x, axis=0, ddof=1)
    scale[scale <= 1e-15] = 1.0
    return (x - center) / scale


def build_knn_edges(x: np.ndarray, k: int) -> list[tuple[int, int, float]]:
    n = len(x)
    distances = np.linalg.norm(x[:, None, :] - x[None, :, :], axis=2)
    edges: set[tuple[int, int]] = set()
    for i in range(n):
        nearest = np.argsort(distances[i])[1 : k + 1]
        for j in nearest:
            edges.add((min(i, int(j)), max(i, int(j))))
    return [(i, j, float(distances[i, j])) for i, j in sorted(edges)]


def classify_geometry(rows: list[dict[str, Any]], x: np.ndarray, k: int) -> dict[str, Any]:
    labels = [row["source_domain_type"] for row in rows]
    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
    if not gue_idx or not poi_idx:
        raise ValueError("scope must include both GUE and Poisson rows")

    c_gue = np.mean(x[gue_idx], axis=0)
    c_poi = np.mean(x[poi_idx], axis=0)
    edges = build_knn_edges(x, k)
    degree = {i: 0 for i in range(len(rows))}
    for i, j, _ in edges:
        degree[i] += 1
        degree[j] += 1

    row_out = []
    third_rows = []
    for i, row in enumerate(rows):
        d_gue = float(np.linalg.norm(x[i] - c_gue))
        d_poi = float(np.linalg.norm(x[i] - c_poi))
        denom = d_gue + d_poi
        centroid_coord = float((d_gue - d_poi) / denom) if denom > 1e-15 else 0.0
        centroid_margin = float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0
        incident = [(a, b, dist) for a, b, dist in edges if a == i or b == i]
        cross = 0
        cross_curvatures = []
        same_curvatures = []
        for a, b, _ in incident:
            other = b if a == i else a
            curvature = 4 - degree[a] - degree[b]
            if labels[other] != labels[i]:
                cross += 1
                cross_curvatures.append(curvature)
            else:
                same_curvatures.append(curvature)
        cross_fraction = float(cross / len(incident)) if incident else 0.0
        state = "class_interior"
        if cross_fraction > 0 and centroid_margin < 0.25:
            state = "third_included_candidate"
            third_rows.append(row["domain_window"])
        elif cross_fraction > 0:
            state = "cut_edge"
        row_out.append(
            {
                "domain_window": row["domain_window"],
                "domain": row["domain"],
                "source_domain_type": row["source_domain_type"],
                "degree": degree[i],
                "centroid_coord": round(centroid_coord, 6),
                "centroid_margin": round(centroid_margin, 6),
                "cross_neighbor_fraction": round(cross_fraction, 6),
                "cross_edge_curvature_mean": round(float(np.mean(cross_curvatures)), 6) if cross_curvatures else None,
                "same_edge_curvature_mean": round(float(np.mean(same_curvatures)), 6) if same_curvatures else None,
                "boundary_state": state,
            }
        )

    cross_edges = [
        {
            "a": rows[i]["domain_window"],
            "b": rows[j]["domain_window"],
            "distance": round(dist, 6),
            "forman_unweighted": 4 - degree[i] - degree[j],
        }
        for i, j, dist in edges
        if labels[i] != labels[j]
    ]
    same_edges = [
        {"distance": dist, "forman_unweighted": 4 - degree[i] - degree[j]}
        for i, j, dist in edges
        if labels[i] == labels[j]
    ]

    return {
        "feature_names": FEATURE_NAMES,
        "k": k,
        "label_counts": {
            "GUE": len(gue_idx),
            "Poisson": len(poi_idx),
        },
        "edge_counts": {
            "total": len(edges),
            "cross_label": len(cross_edges),
            "same_label": len(same_edges),
        },
        "curvature": {
            "cross_edge_mean": round(float(np.mean([e["forman_unweighted"] for e in cross_edges])), 6) if cross_edges else None,
            "same_edge_mean": round(float(np.mean([e["forman_unweighted"] for e in same_edges])), 6) if same_edges else None,
        },
        "third_included_candidates": third_rows,
        "rows": row_out,
        "cross_edges": cross_edges,
    }


def run(args: argparse.Namespace) -> dict[str, Any]:
    rng = np.random.default_rng(args.seed)
    scope = load_scope(Path(args.scope))
    selected = [row for row in scope if row.get("source_domain_type") in {"GUE", "Poisson"}]
    selected = sorted(selected, key=lambda row: int(row["cycle"]))

    rows = []
    errors = []
    for source in selected:
        try:
            gaps = row_spacings(source["domain"])
            if len(gaps) < args.min_gaps:
                errors.append(
                    {
                        "domain_window": source["domain_window"],
                        "error": f"insufficient gaps {len(gaps)} < {args.min_gaps}",
                    }
                )
                continue
            gaps = gaps[: args.n_gaps] if len(gaps) > args.n_gaps else gaps
            obs = compute_observables(gaps)
            z = shuffle_z(gaps, obs, args.n_shuffle, rng)
            rows.append(
                {
                    "domain_window": source["domain_window"],
                    "domain": source["domain"],
                    "cycle": source["cycle"],
                    "source_domain_type": source["source_domain_type"],
                    "n_gaps": int(len(gaps)),
                    "observables": {key: round(value, 9) for key, value in obs.items()},
                    "shuffle_z": {key: round(value, 6) for key, value in z.items()},
                }
            )
        except Exception as exc:  # noqa: BLE001 - row-level telemetry is part of the result.
            errors.append(
                {
                    "domain_window": source.get("domain_window"),
                    "error": type(exc).__name__,
                    "message": str(exc),
                }
            )

    x = standardized_matrix(rows)
    geometry = classify_geometry(rows, x, args.k)
    output = {
        "experiment": "boundary_graph_curvature_gate",
        "question": "Does the 8 GUE / 5 Poisson perimeter expose a graph boundary row instead of a clean two-class split?",
        "observables_registry": OBSERVABLES_REGISTRY_VERSION,
        "observables_used": FEATURE_NAMES,
        "params": vars(args),
        "source_scope": args.scope,
        "observable_contract": {
            "claim": "the boundary is operational when row geometry produces cross-label graph nodes with low centroid margin",
            "observable": "kNN graph position, cross-neighbor fraction, centroid margin, unweighted Forman edge curvature",
            "operator": "row-aligned domain/window graph in canonical+rigidity+shuffle-z feature space",
            "generator": "dnd_autoricerca row_spacings via semireal boundary transfer gate",
            "denominator": "base BOUNDARY rows with source_domain_type in {GUE, Poisson}",
            "non_possible": "third-included boundary if all cross-label edges vanish or only high-margin class interiors cross",
            "not_tested": "V_c, Sturmian denominators, analytic source of each domain label",
        },
        "summary": {
            "rows_analyzed": len(rows),
            "errors": len(errors),
            "third_included_candidate_count": len(geometry["third_included_candidates"]),
            "third_included_candidates": geometry["third_included_candidates"],
            "edge_counts": geometry["edge_counts"],
            "curvature": geometry["curvature"],
        },
        "geometry": geometry,
        "rows": rows,
        "errors": errors,
    }

    out = Path(args.out)
    out.parent.mkdir(parents=True, exist_ok=True)
    out.write_text(json.dumps(output, indent=2) + "\n", encoding="utf-8")

    print(f"observables_registry={OBSERVABLES_REGISTRY_VERSION}")
    print(f"observables_used={FEATURE_NAMES}")
    print(f"rows_analyzed={len(rows)} errors={len(errors)}")
    print(f"label_counts={geometry['label_counts']}")
    print(f"edge_counts={geometry['edge_counts']}")
    print(f"curvature={geometry['curvature']}")
    print(f"third_included_candidates={geometry['third_included_candidates']}")
    for row in geometry["rows"]:
        print(
            f"{row['domain_window']}\t{row['source_domain_type']}\t"
            f"margin={row['centroid_margin']:.3f}\tcross={row['cross_neighbor_fraction']:.3f}\t"
            f"state={row['boundary_state']}"
        )
    print(f"saved={out}")
    return output


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
    parser.add_argument("--n-gaps", type=int, default=2048)
    parser.add_argument("--min-gaps", type=int, default=64)
    parser.add_argument("--n-shuffle", type=int, default=64)
    parser.add_argument("--k", type=int, default=3)
    parser.add_argument("--seed", type=int, default=20260515)
    parser.add_argument("--out", default="tools/data/boundary_graph_curvature_gate_20260515_1855.json")
    args = parser.parse_args()
    run(args)


if __name__ == "__main__":
    main()

exec
/bin/bash -lc "python - <<'PY'
import json
p='tools/data/boundary_bridge_stability_audit_20260515_1915.json'
d=json.load(open(p))
for r in d['rows']:
 print(r)
PY" in /opt/MM_D-ND
 succeeded in 0ms:
{'berry_robnick_like_gue_weight': 0.25, 'brody_q': 0.205, 'classical_audit_state': 'classic_only_intermediate', 'composite_state': 'parameter_sensitive_bridge+classic_only_intermediate', 'cut_edge_frequency': 0.166667, 'domain': 'brownian_motion', 'domain_window': 'brownian_motion:cycle_12', 'graph_bridge_frequency': 0.666667, 'graph_bridge_hits': 4, 'mean_cross_neighbor_fraction': 0.355555, 'mean_margin': 0.225898, 'source_domain_type': 'Poisson', 'stability_state': 'parameter_sensitive_bridge'}
{'berry_robnick_like_gue_weight': 0.435, 'brody_q': 1.0, 'classical_audit_state': 'classic_only_intermediate', 'composite_state': 'unstable_non_bridge+classic_only_intermediate', 'cut_edge_frequency': 0.0, 'domain': 'cellular_automata', 'domain_window': 'cellular_automata:cycle_8', 'graph_bridge_frequency': 0.0, 'graph_bridge_hits': 0, 'mean_cross_neighbor_fraction': 0.0, 'mean_margin': 0.214137, 'source_domain_type': 'GUE', 'stability_state': 'unstable_non_bridge'}
{'berry_robnick_like_gue_weight': 0.0, 'brody_q': 0.0, 'classical_audit_state': 'endpoint_like', 'composite_state': 'unstable_non_bridge+endpoint_like', 'cut_edge_frequency': 0.5, 'domain': 'coupled_oscillators', 'domain_window': 'coupled_oscillators:cycle_10', 'graph_bridge_frequency': 0.166667, 'graph_bridge_hits': 1, 'mean_cross_neighbor_fraction': 0.226984, 'mean_margin': 0.400792, 'source_domain_type': 'Poisson', 'stability_state': 'unstable_non_bridge'}
{'berry_robnick_like_gue_weight': 0.07, 'brody_q': 0.09, 'classical_audit_state': 'endpoint_like', 'composite_state': 'unstable_non_bridge+endpoint_like', 'cut_edge_frequency': 0.0, 'domain': 'ising_2d', 'domain_window': 'ising_2d:cycle_1', 'graph_bridge_frequency': 0.0, 'graph_bridge_hits': 0, 'mean_cross_neighbor_fraction': 0.0, 'mean_margin': 0.326264, 'source_domain_type': 'GUE', 'stability_state': 'unstable_non_bridge'}
{'berry_robnick_like_gue_weight': 0.0, 'brody_q': 0.0, 'classical_audit_state': 'endpoint_like', 'composite_state': 'parameter_sensitive_bridge+endpoint_like', 'cut_edge_frequency': 0.0, 'domain': 'logistica_biforcazione', 'domain_window': 'logistica_biforcazione:cycle_5', 'graph_bridge_frequency': 0.666667, 'graph_bridge_hits': 4, 'mean_cross_neighbor_fraction': 0.277778, 'mean_margin': 0.074885, 'source_domain_type': 'GUE', 'stability_state': 'parameter_sensitive_bridge'}
{'berry_robnick_like_gue_weight': 0.0, 'brody_q': 0.0, 'classical_audit_state': 'graph_only_bridge', 'composite_state': 'stable_graph_bridge+graph_only_bridge', 'cut_edge_frequency': 0.0, 'domain': 'logistica_biforcazione_var_3.5699', 'domain_window': 'logistica_biforcazione_var_3.5699:cycle_13', 'graph_bridge_frequency': 1.0, 'graph_bridge_hits': 6, 'mean_cross_neighbor_fraction': 0.916667, 'mean_margin': 0.036498, 'source_domain_type': 'GUE', 'stability_state': 'stable_graph_bridge'}
{'berry_robnick_like_gue_weight': 0.275, 'brody_q': 0.465, 'classical_audit_state': 'classic_and_graph_bridge', 'composite_state': 'stable_graph_bridge+classic_and_graph_bridge', 'cut_edge_frequency': 0.0, 'domain': 'numeri_primi', 'domain_window': 'numeri_primi:cycle_3', 'graph_bridge_frequency': 1.0, 'graph_bridge_hits': 6, 'mean_cross_neighbor_fraction': 0.316667, 'mean_margin': 0.052342, 'source_domain_type': 'GUE', 'stability_state': 'stable_graph_bridge'}
{'berry_robnick_like_gue_weight': 0.0, 'brody_q': 0.0, 'classical_audit_state': 'endpoint_like', 'composite_state': 'parameter_sensitive_bridge+endpoint_like', 'cut_edge_frequency': 0.666667, 'domain': 'pendolo_doppio', 'domain_window': 'pendolo_doppio:cycle_2', 'graph_bridge_frequency': 0.333333, 'graph_bridge_hits': 2, 'mean_cross_neighbor_fraction': 0.321428, 'mean_margin': 0.251462, 'source_domain_type': 'Poisson', 'stability_state': 'parameter_sensitive_bridge'}
{'berry_robnick_like_gue_weight': 0.025, 'brody_q': 0.025, 'classical_audit_state': 'graph_only_bridge', 'composite_state': 'stable_graph_bridge+graph_only_bridge', 'cut_edge_frequency': 0.166667, 'domain': 'percolation', 'domain_window': 'percolation:cycle_9', 'graph_bridge_frequency': 0.833333, 'graph_bridge_hits': 5, 'mean_cross_neighbor_fraction': 0.684921, 'mean_margin': 0.192431, 'source_domain_type': 'Poisson', 'stability_state': 'stable_graph_bridge'}
{'berry_robnick_like_gue_weight': 0.475, 'brody_q': 0.975, 'classical_audit_state': 'classic_only_intermediate', 'composite_state': 'parameter_sensitive_bridge+classic_only_intermediate', 'cut_edge_frequency': 0.0, 'domain': 'random_matrix', 'domain_window': 'random_matrix:cycle_7', 'graph_bridge_frequency': 0.666667, 'graph_bridge_hits': 4, 'mean_cross_neighbor_fraction': 0.261904, 'mean_margin': 0.07293, 'source_domain_type': 'GUE', 'stability_state': 'parameter_sensitive_bridge'}
{'berry_robnick_like_gue_weight': 0.0, 'brody_q': 0.0, 'classical_audit_state': 'graph_only_bridge', 'composite_state': 'stable_graph_bridge+graph_only_bridge', 'cut_edge_frequency': 0.0, 'domain': 'reaction_diffusion', 'domain_window': 'reaction_diffusion:cycle_11', 'graph_bridge_frequency': 0.833333, 'graph_bridge_hits': 5, 'mean_cross_neighbor_fraction': 0.488889, 'mean_margin': 0.123525, 'source_domain_type': 'GUE', 'stability_state': 'stable_graph_bridge'}
{'berry_robnick_like_gue_weight': 0.0, 'brody_q': 0.0, 'classical_audit_state': 'endpoint_like', 'composite_state': 'unstable_non_bridge+endpoint_like', 'cut_edge_frequency': 1.0, 'domain': 'string_vibration', 'domain_window': 'string_vibration:cycle_6', 'graph_bridge_frequency': 0.0, 'graph_bridge_hits': 0, 'mean_cross_neighbor_fraction': 0.404762, 'mean_margin': 0.611647, 'source_domain_type': 'Poisson', 'stability_state': 'unstable_non_bridge'}
{'berry_robnick_like_gue_weight': 0.53, 'brody_q': 1.0, 'classical_audit_state': 'classic_only_intermediate', 'composite_state': 'stable_graph_bridge+classic_only_intermediate', 'cut_edge_frequency': 0.0, 'domain': 'zeta_zeros', 'domain_window': 'zeta_zeros:cycle_4', 'graph_bridge_frequency': 0.833333, 'graph_bridge_hits': 5, 'mean_cross_neighbor_fraction': 0.285317, 'mean_margin': 0.053731, 'source_domain_type': 'GUE', 'stability_state': 'stable_graph_bridge'}

codex
Creo uno script nuovo e riusabile per questo audit, con due null separati: permutazione delle label GUE/Poisson sullo stesso embedding e rewiring degree-preserving del grafo con label fisse. Così il residuo graph-only viene testato come struttura grafica, non come effetto di soglia locale.
apply patch
patch: completed
/opt/MM_D-ND/tools/exp_boundary_graph_null_audit.py
diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

codex
Lo script è scritto. Ora lo compilo e lo eseguo sul denominatore 13 righe, mantenendo lo stesso reticolo di perturbazioni del run 19:15 per avere righe confrontabili.
exec
/bin/bash -lc 'python -m py_compile tools/exp_boundary_graph_null_audit.py' in /opt/MM_D-ND
 succeeded in 0ms:
exec
/bin/bash -lc 'python tools/exp_boundary_graph_null_audit.py' in /opt/MM_D-ND
diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

codex
Il run è ancora in corso; la parte costosa è il riuso degli shuffle per ogni combinazione più i null grafici. Attendo il completamento prima di interpretare, perché un output parziale qui falserebbe proprio il denominatore che stiamo auditando.
diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

codex
Il primo run è più lento del necessario: sta ricalcolando osservabili di shuffle dentro ogni lettura. Se non chiude a breve, farò la riparazione regressiva nello script riducendo il costo del null senza cambiare il contratto osservabile.
diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

 succeeded in 125774ms:
{
  "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
  "graph_only_residue": 2,
  "graph_only_residue_rows": [
    "reaction_diffusion:cycle_11",
    "logistica_biforcazione_var_3.5699:cycle_13"
  ],
  "graph_reader_runs": 27,
  "graph_specific_residue_after_nulls": 0,
  "graph_specific_residue_rows": [],
  "rows_analyzed": 13,
  "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
  "two_reader_boundary_confirmed": 0,
  "two_reader_boundary_rows": []
}

2026-05-16T03:34:25.373415Z ERROR codex_core::tools::router: error=write_stdin failed: stdin is closed for this session; rerun exec_command with tty=true to keep stdin open
diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

exec
/bin/bash -lc "ps -ef | rg 'exp_boundary_graph_null_audit|python tools'" in /opt/MM_D-ND
 succeeded in 0ms:
root           1       0  0 03:34 ?        00:00:00 /root/.codex-lab/tmp/arg0/codex-arg0OIq1su/codex-linux-sandbox --sandbox-policy-cwd /opt/MM_D-ND --command-cwd /opt/MM_D-ND --sandbox-policy {"type":"workspace-write","writable_roots":["/root/.codex-lab/memories"],"network_access":false,"exclude_tmpdir_env_var":false,"exclude_slash_tmp":false} --file-system-sandbox-policy {"kind":"restricted","entries":[{"path":{"type":"special","value":{"kind":"root"}},"access":"read"},{"path":{"type":"special","value":{"kind":"current_working_directory"}},"access":"write"},{"path":{"type":"special","value":{"kind":"slash_tmp"}},"access":"write"},{"path":{"type":"special","value":{"kind":"tmpdir"}},"access":"write"},{"path":{"type":"path","path":"/root/.codex-lab/memories"},"access":"write"},{"path":{"type":"path","path":"/opt/MM_D-ND/.git"},"access":"read"},{"path":{"type":"path","path":"/opt/MM_D-ND/.codex"},"access":"read"}]} --network-sandbox-policy "restricted" -- /bin/bash -c __CODEX_SNAPSHOT_OVERRIDE_SET_0="${CODEX_THREAD_ID+x}" __CODEX_SNAPSHOT_OVERRIDE_0="${CODEX_THREAD_ID-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_0="${ALL_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_0="${ALL_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_1="${BUNDLE_HTTPS_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_1="${BUNDLE_HTTPS_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_2="${BUNDLE_HTTP_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_2="${BUNDLE_HTTP_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_3="${BUNDLE_NO_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_3="${BUNDLE_NO_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_4="${CODEX_NETWORK_ALLOW_LOCAL_BINDING+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_4="${CODEX_NETWORK_ALLOW_LOCAL_BINDING-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_5="${CODEX_NETWORK_PROXY_ACTIVE+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_5="${CODEX_NETWORK_PROXY_ACTIVE-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_6="${DOCKER_HTTPS_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_6="${DOCKER_HTTPS_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_7="${DOCKER_HTTP_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_7="${DOCKER_HTTP_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_8="${ELECTRON_GET_USE_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_8="${ELECTRON_GET_USE_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_9="${FTP_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_9="${FTP_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_10="${HTTPS_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_10="${HTTPS_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_11="${HTTP_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_11="${HTTP_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_12="${NO_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_12="${NO_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_13="${NPM_CONFIG_HTTPS_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_13="${NPM_CONFIG_HTTPS_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_14="${NPM_CONFIG_HTTP_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_14="${NPM_CONFIG_HTTP_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_15="${NPM_CONFIG_NOPROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_15="${NPM_CONFIG_NOPROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_16="${NPM_CONFIG_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_16="${NPM_CONFIG_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_17="${PIP_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_17="${PIP_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_18="${WSS_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_18="${WSS_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_19="${WS_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_19="${WS_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_20="${YARN_HTTPS_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_20="${YARN_HTTPS_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_21="${YARN_HTTP_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_21="${YARN_HTTP_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_22="${YARN_NO_PROXY+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_22="${YARN_NO_PROXY-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_23="${all_proxy+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_23="${all_proxy-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_24="${ftp_proxy+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_24="${ftp_proxy-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_25="${http_proxy+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_25="${http_proxy-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_26="${https_proxy+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_26="${https_proxy-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_27="${no_proxy+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_27="${no_proxy-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_28="${npm_config_http_proxy+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_28="${npm_config_http_proxy-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_29="${npm_config_https_proxy+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_29="${npm_config_https_proxy-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_30="${npm_config_noproxy+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_30="${npm_config_noproxy-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_31="${npm_config_proxy+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_31="${npm_config_proxy-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_32="${ws_proxy+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_32="${ws_proxy-}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_33="${wss_proxy+x}" __CODEX_SNAPSHOT_PROXY_OVERRIDE_33="${wss_proxy-}" __CODEX_SNAPSHOT_PROXY_ENV_SET="${CODEX_NETWORK_PROXY_ACTIVE+x}"  if . '/root/.codex-lab/shell_snapshots/019e2ed5-9a16-79e0-a4fa-e8b0574b3328.1778902211105804992.sh' >/dev/null 2>&1; then :; fi  if [ -n "${__CODEX_SNAPSHOT_OVERRIDE_SET_0}" ]; then export CODEX_THREAD_ID="${__CODEX_SNAPSHOT_OVERRIDE_0}"; else unset CODEX_THREAD_ID; fi if [ -n "$__CODEX_SNAPSHOT_PROXY_ENV_SET" ] || [ -n "${CODEX_NETWORK_PROXY_ACTIVE+x}" ]; then if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_0}" ]; then export ALL_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_0}"; else unset ALL_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_1}" ]; then export BUNDLE_HTTPS_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_1}"; else unset BUNDLE_HTTPS_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_2}" ]; then export BUNDLE_HTTP_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_2}"; else unset BUNDLE_HTTP_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_3}" ]; then export BUNDLE_NO_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_3}"; else unset BUNDLE_NO_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_4}" ]; then export CODEX_NETWORK_ALLOW_LOCAL_BINDING="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_4}"; else unset CODEX_NETWORK_ALLOW_LOCAL_BINDING; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_5}" ]; then export CODEX_NETWORK_PROXY_ACTIVE="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_5}"; else unset CODEX_NETWORK_PROXY_ACTIVE; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_6}" ]; then export DOCKER_HTTPS_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_6}"; else unset DOCKER_HTTPS_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_7}" ]; then export DOCKER_HTTP_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_7}"; else unset DOCKER_HTTP_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_8}" ]; then export ELECTRON_GET_USE_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_8}"; else unset ELECTRON_GET_USE_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_9}" ]; then export FTP_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_9}"; else unset FTP_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_10}" ]; then export HTTPS_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_10}"; else unset HTTPS_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_11}" ]; then export HTTP_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_11}"; else unset HTTP_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_12}" ]; then export NO_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_12}"; else unset NO_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_13}" ]; then export NPM_CONFIG_HTTPS_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_13}"; else unset NPM_CONFIG_HTTPS_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_14}" ]; then export NPM_CONFIG_HTTP_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_14}"; else unset NPM_CONFIG_HTTP_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_15}" ]; then export NPM_CONFIG_NOPROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_15}"; else unset NPM_CONFIG_NOPROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_16}" ]; then export NPM_CONFIG_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_16}"; else unset NPM_CONFIG_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_17}" ]; then export PIP_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_17}"; else unset PIP_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_18}" ]; then export WSS_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_18}"; else unset WSS_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_19}" ]; then export WS_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_19}"; else unset WS_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_20}" ]; then export YARN_HTTPS_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_20}"; else unset YARN_HTTPS_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_21}" ]; then export YARN_HTTP_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_21}"; else unset YARN_HTTP_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_22}" ]; then export YARN_NO_PROXY="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_22}"; else unset YARN_NO_PROXY; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_23}" ]; then export all_proxy="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_23}"; else unset all_proxy; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_24}" ]; then export ftp_proxy="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_24}"; else unset ftp_proxy; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_25}" ]; then export http_proxy="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_25}"; else unset http_proxy; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_26}" ]; then export https_proxy="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_26}"; else unset https_proxy; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_27}" ]; then export no_proxy="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_27}"; else unset no_proxy; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_28}" ]; then export npm_config_http_proxy="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_28}"; else unset npm_config_http_proxy; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_29}" ]; then export npm_config_https_proxy="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_29}"; else unset npm_config_https_proxy; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_30}" ]; then export npm_config_noproxy="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_30}"; else unset npm_config_noproxy; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_31}" ]; then export npm_config_proxy="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_31}"; else unset npm_config_proxy; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_32}" ]; then export ws_proxy="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_32}"; else unset ws_proxy; fi if [ -n "${__CODEX_SNAPSHOT_PROXY_OVERRIDE_SET_33}" ]; then export wss_proxy="${__CODEX_SNAPSHOT_PROXY_OVERRIDE_33}"; else unset wss_proxy; fi fi  exec '/bin/bash' -c 'ps -ef | rg '"'"'exp_boundary_graph_null_audit|python tools'"'"''
root           2       1  0 03:34 ?        00:00:00 /bin/bash -c ps -ef | rg 'exp_boundary_graph_null_audit|python tools'
root           4       2  0 03:34 ?        00:00:00 rg exp_boundary_graph_null_audit|python tools

diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

exec
/bin/bash -lc "python - <<'PY'
import json
p='tools/data/boundary_graph_null_audit_20260516_0330.json'
d=json.load(open(p))
print(d['summary'])
for r in d['rows']:
 print(r['domain_window'], r['classical_audit_state'], r['observed_graph_bridge_frequency'], r['label_shuffle_bridge_frequency'], r['degree_rewire_bridge_frequency'], r['stable_graph_only_residue'], r['graph_baseline_state'])
PY" in /opt/MM_D-ND
 succeeded in 0ms:
{'graph_baseline_audit': 'label_shuffle + degree_preserving_rewire', 'graph_only_residue': 2, 'graph_only_residue_rows': ['reaction_diffusion:cycle_11', 'logistica_biforcazione_var_3.5699:cycle_13'], 'graph_reader_runs': 27, 'graph_specific_residue_after_nulls': 0, 'graph_specific_residue_rows': [], 'rows_analyzed': 13, 'scope_change_declared': 'two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations', 'two_reader_boundary_confirmed': 0, 'two_reader_boundary_rows': []}
ising_2d:cycle_1 endpoint_like 0.0 0.891782 0.100116 False not_graph_specific_residue
pendolo_doppio:cycle_2 endpoint_like 0.0 0.887731 0.318866 False not_graph_specific_residue
numeri_primi:cycle_3 classic_and_graph_bridge 0.62963 0.721065 0.918403 False not_graph_specific_residue
zeta_zeros:cycle_4 classic_only_intermediate 0.481481 0.600694 0.63831 False not_graph_specific_residue
logistica_biforcazione:cycle_5 endpoint_like 0.481481 0.860532 0.832176 False not_graph_specific_residue
string_vibration:cycle_6 endpoint_like 0.0 0.726273 0.0 False not_graph_specific_residue
random_matrix:cycle_7 classic_only_intermediate 0.666667 0.596065 0.664352 False not_graph_specific_residue
cellular_automata:cycle_8 classic_only_intermediate 0.0 0.869792 0.621528 False not_graph_specific_residue
percolation:cycle_9 graph_only_bridge 0.444444 0.662616 0.439815 False not_graph_specific_residue
coupled_oscillators:cycle_10 endpoint_like 0.0 0.788194 0.0 False not_graph_specific_residue
reaction_diffusion:cycle_11 graph_only_bridge 0.888889 0.767361 0.888889 True not_graph_specific_residue
brownian_motion:cycle_12 classic_only_intermediate 0.777778 0.809028 0.876157 False not_graph_specific_residue
logistica_biforcazione_var_3.5699:cycle_13 graph_only_bridge 0.777778 0.893519 0.871528 True not_graph_specific_residue

diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

exec
/bin/bash -lc "python - <<'PY'
import json
for p in ['tools/data/boundary_bridge_stability_audit_20260515_1915.json','tools/data/boundary_graph_null_audit_20260516_0330.json']:
 d=json.load(open(p)); print(p); print(d['params']); print(d['summary'])
PY" in /opt/MM_D-ND
 succeeded in 0ms:
tools/data/boundary_bridge_stability_audit_20260515_1915.json
{'classical_audit': 'tools/data/boundary_classical_crossover_audit_20260515_1904.json', 'k_values': [2, 3, 4], 'min_gaps': 64, 'n_gaps_values': [1024], 'n_shuffle': 8, 'scope': 'tools/data/boundary_denominator_prescan_full_20260509_1500.json', 'seeds': [20260515, 20260516], 'total_runs': 6}
{'classic_only_stable_graph_absent': ['cellular_automata:cycle_8'], 'composite_counts': {'parameter_sensitive_bridge+classic_only_intermediate': 2, 'parameter_sensitive_bridge+endpoint_like': 2, 'stable_graph_bridge+classic_and_graph_bridge': 1, 'stable_graph_bridge+classic_only_intermediate': 1, 'stable_graph_bridge+graph_only_bridge': 3, 'unstable_non_bridge+classic_only_intermediate': 1, 'unstable_non_bridge+endpoint_like': 3}, 'graph_reader_runs': 6, 'lab_residue_after_stability': True, 'rows_analyzed': 13, 'stable_classic_and_graph': ['numeri_primi:cycle_3'], 'stable_graph_only': ['logistica_biforcazione_var_3.5699:cycle_13', 'percolation:cycle_9', 'reaction_diffusion:cycle_11']}
tools/data/boundary_graph_null_audit_20260516_0330.json
{'graph_reader_runs': 27, 'k_values': [2, 3, 4], 'label_null_trials': 1728, 'label_nulls': 64, 'margin_threshold': 0.25, 'n_gaps_values': [512, 1024, 2048], 'n_shuffle': 32, 'rewire_null_trials': 1728, 'rewire_nulls': 64, 'scope': 'tools/data/boundary_denominator_prescan_full_20260509_1500.json', 'seeds': [20260515, 20260516, 20260517], 'stability_audit': 'tools/data/boundary_bridge_stability_audit_20260515_1915.json', 'stable_threshold': 0.75}
{'graph_baseline_audit': 'label_shuffle + degree_preserving_rewire', 'graph_only_residue': 2, 'graph_only_residue_rows': ['reaction_diffusion:cycle_11', 'logistica_biforcazione_var_3.5699:cycle_13'], 'graph_reader_runs': 27, 'graph_specific_residue_after_nulls': 0, 'graph_specific_residue_rows': [], 'rows_analyzed': 13, 'scope_change_declared': 'two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations', 'two_reader_boundary_confirmed': 0, 'two_reader_boundary_rows': []}

diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

codex
Ho trovato una differenza importante: il file 19:15 reale usa 6 letture (`n_gaps=1024`, due seed), mentre il report testuale dichiarava 27. Rieseguo l’audit null sullo stesso perimetro effettivo del JSON 19:15, così il confronto row-aligned non cambia denominatore sotto i piedi.
exec
/bin/bash -lc 'python tools/exp_boundary_graph_null_audit.py --n-gaps-values 1024 --seeds 20260515,20260516 --n-shuffle 8 --out tools/data/boundary_graph_null_audit_20260516_0330.json' in /opt/MM_D-ND
diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

 succeeded in 64046ms:
{
  "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
  "graph_only_residue": 3,
  "graph_only_residue_rows": [
    "percolation:cycle_9",
    "reaction_diffusion:cycle_11",
    "logistica_biforcazione_var_3.5699:cycle_13"
  ],
  "graph_reader_runs": 6,
  "graph_specific_residue_after_nulls": 2,
  "graph_specific_residue_rows": [
    "percolation:cycle_9",
    "logistica_biforcazione_var_3.5699:cycle_13"
  ],
  "rows_analyzed": 13,
  "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
  "two_reader_boundary_confirmed": 1,
  "two_reader_boundary_rows": [
    "numeri_primi:cycle_3"
  ]
}

exec
/bin/bash -lc "python - <<'PY'
import json
p='tools/data/boundary_graph_null_audit_20260516_0330.json'
d=json.load(open(p))
print(d['params'])
for r in d['rows']:
 print(r['domain_window'], r['classical_audit_state'], r['observed_graph_bridge_frequency'], r['label_shuffle_bridge_frequency'], r['degree_rewire_bridge_frequency'], r['label_shuffle_lift'], r['degree_rewire_lift'], r['stable_graph_only_residue'], r['graph_baseline_state'])
PY" in /opt/MM_D-ND
 succeeded in 0ms:
{'graph_reader_runs': 6, 'k_values': [2, 3, 4], 'label_null_trials': 384, 'label_nulls': 64, 'margin_threshold': 0.25, 'n_gaps_values': [1024], 'n_shuffle': 8, 'rewire_null_trials': 384, 'rewire_nulls': 64, 'scope': 'tools/data/boundary_denominator_prescan_full_20260509_1500.json', 'seeds': [20260515, 20260516], 'stability_audit': 'tools/data/boundary_bridge_stability_audit_20260515_1915.json', 'stable_threshold': 0.75}
ising_2d:cycle_1 endpoint_like 0.0 0.763021 0.0 -0.763021 0.0 False not_graph_specific_residue
pendolo_doppio:cycle_2 endpoint_like 0.0 0.932292 0.0 -0.932292 0.0 False not_graph_specific_residue
numeri_primi:cycle_3 classic_and_graph_bridge 1.0 0.684896 0.942708 0.315104 0.057292 False not_graph_specific_residue
zeta_zeros:cycle_4 classic_only_intermediate 1.0 0.596354 0.989583 0.403646 0.010417 False not_graph_specific_residue
logistica_biforcazione:cycle_5 endpoint_like 0.666667 0.898438 0.846354 -0.231771 -0.179688 False not_graph_specific_residue
string_vibration:cycle_6 endpoint_like 0.0 0.721354 0.0 -0.721354 0.0 False not_graph_specific_residue
random_matrix:cycle_7 classic_only_intermediate 0.833333 0.617188 0.947917 0.216146 -0.114583 False not_graph_specific_residue
cellular_automata:cycle_8 classic_only_intermediate 0.0 0.911458 0.911458 -0.911458 -0.911458 False not_graph_specific_residue
percolation:cycle_9 graph_only_bridge 1.0 0.71875 0.984375 0.28125 0.015625 True graph_specific_residue
coupled_oscillators:cycle_10 endpoint_like 0.0 0.864583 0.0 -0.864583 0.0 False not_graph_specific_residue
reaction_diffusion:cycle_11 graph_only_bridge 0.833333 0.765625 0.854167 0.067708 -0.020833 True not_graph_specific_residue
brownian_motion:cycle_12 classic_only_intermediate 0.833333 0.880208 0.958333 -0.046875 -0.125 False not_graph_specific_residue
logistica_biforcazione_var_3.5699:cycle_13 graph_only_bridge 1.0 0.903646 0.846354 0.096354 0.153646 True graph_specific_residue

diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

codex
I numeri ora sono allineati al JSON effettivo del 19:15: `two_reader_boundary_confirmed=1`, `graph_only_residue=3`. La novità è che solo 2 dei 3 residui graph-only superano entrambi i null grafici, e con margini piccoli: va scritto come vincolo, non come scoperta forte.
apply patch
patch: completed
/opt/MM_D-ND/tools/data/reports/agent_20260516_0330.md
diff --git a/tools/data/reports/agent_20260516_0330.md b/tools/data/reports/agent_20260516_0330.md
new file mode 100644
index 0000000000000000000000000000000000000000..b465137e5a3f55d6d0eb2a1b85202998a9224ebf
--- /dev/null
+++ b/tools/data/reports/agent_20260516_0330.md
@@ -0,0 +1,111 @@
+# Agent Report - Boundary Graph Null Audit
+**Date**: 2026-05-16 03:30
+**Piano**: 124
+**Tension explored**: BOUNDARY (0.8)
+**verdict**: CONSTRAINT - il boundary a due lettori resta una sola riga; i tre residui graph-only sono frequenza grafica auditata, non conferma a due lettori. Due residui superano entrambi i null grafici, con lift piccolo.
+observables_registry: 1.0.0-2026-05-06 via boundary_graph_curvature_gate
+observables_used: [observed_graph_bridge_frequency, label_shuffle_bridge_frequency, degree_rewire_bridge_frequency, label_shuffle_lift, degree_rewire_lift, mean_centroid_margin, mean_cross_neighbor_fraction]
+**observable_contract**: claim=i residui graph-only sono Lab-specific solo se la frequenza bridge osservata supera label-shuffle e degree-preserving graph null; observable=frequenza graph bridge osservata contro frequenze null grafiche; operator=rerun del lettore graph BOUNDARY con label shuffle e rewiring degree-preserving; generator=13 righe BOUNDARY row-aligned in feature space canonical+rigidity+shuffle-z; denominator=13 righe, 8 GUE e 5 Poisson, su 6 letture grafiche e 384 trial per ciascun null; non_possible=residuo Lab graph-only se i null matchano o superano la frequenza osservata; not_tested=nuovi sistemi Hamiltoniani, unfolding alternativi, universalita fisica dei residui graph-only.
+
+## Respiro fuori-tempo
+- **Combo**: A9 terzo incluso + QxG continuo/discreto + grafo/cut come nodo del confine + tensione seme BOUNDARY "8 domini GUE, 5 Poisson".
+- **Dipolo / punto-zero**: ponte a due lettori / residuo graph-only. Punto-zero: la riga row-aligned prima che venga assegnata a crossover classico o ponte grafico.
+- **Piano superiore**: topologia del grafo con null strutturali; il grafo non decide da solo, viene auditato come oggetto.
+- **Operatori laterali scelti**: graph rewiring, cut, spectral crossover. Rewiring entra come null del lettore; cut entra come osservabile di crossing; crossover resta baseline, non sorgente.
+- **Contaminazione cognitiva**: CE-0019 `tools/data/cognitive_enzymes_archive.md` metabolizzata: combo prima della misura. CE-0022 gia attiva dal campo: palette operatoria trasformata in null grafici, non lista di temi.
+- **Proto-ipotesi**: il nodo regressivo del 19:15 non e' "il grafo conferma tre boundary"; e' "il contratto deve separare boundary a due lettori da frequenza grafica auditata".
+- **Proiezione**: se il residuo graph-only e' strutturale, la sua frequenza osservata deve superare label-shuffle e rewiring degree-preserving sulla stessa riga.
+
+## Aderenza alla direzione
+- `relation`: `follows_direction`
+- `why`: il ciclo testa esplicitamente il perimetro vivo 8 GUE / 5 Poisson e il terzo incluso operativo come confine auditato.
+- `not_drift`: non usa il report Sturmian bloccato, non misura V_c, non usa phi/silver/bronze; ripara il contratto del boundary 19:15 nel nodo regressivo indicato dal falsifier.
+
+## Re-discovery audit
+- **Baseline noto piu' vicino**: Brody distribution, Berry-Robnik-like mixture e Rosenzweig-Porter come famiglia fisica di crossover; per il grafo, null label-shuffle e degree-preserving rewiring.
+- **Cosa viene assorbito dal baseline classico**: `numeri_primi:cycle_3` resta l'unica riga a due lettori nel perimetro effettivo 19:15.
+- **Cosa resta Lab-specific sotto audit**: `percolation:cycle_9` e `logistica_biforcazione_var_3.5699:cycle_13` superano entrambi i null grafici, ma con lift piccoli (`degree_rewire_lift=0.015625` e `0.153646`).
+- **Cosa cade come claim forte**: `reaction_diffusion:cycle_11` resta graph-only stabile, ma non supera il rewiring degree-preserving (`degree_rewire_lift=-0.020833`). Non e' residuo graph-specific.
+- **Contratto corretto**: `two_reader_boundary_confirmed = 1`; `graph_only_residue = 3`; `graph_specific_residue_after_nulls = 2`; `scope_change_declared = true`; `graph_baseline_audit = label_shuffle + degree_preserving_rewire`.
+
+## Claim Under Test
+> Nel perimetro 8/5, il residuo graph-only sopravvive come contenuto Lab solo dopo audit contro null grafici; non si somma al boundary a due lettori.
+
+## Question
+I tre graph-only bridge del 19:15 sono struttura grafica specifica o effetto atteso da label/cut su un grafo piccolo?
+
+## Ritorno fisico
+- **Punto fisico sorgente**: transizione spettrale tra repulsione Wigner-Dyson/GUE e indipendenza/localizzazione Poisson.
+- **Attraversamento matematico**: grafo kNN multi-osservabile con null label-shuffle e rewiring degree-preserving.
+- **Punto fisico di ritorno**: nelle finestre finite di sistemi Rosenzweig-Porter, Anderson o Aubry-Andre, una finestra graph-only va trattata come candidato da stressare con null topologici prima di chiamarla boundary fisico.
+- **Relazione nuova**: il bridge fisico non e' "q intermedio" e non e' "nodo grafico"; e' una riga che dichiara lettore classico, lettore grafico e null del grafo.
+- **Osservabile/test fisico possibile**: misurare `observed_graph_bridge_frequency`, `label_shuffle_bridge_frequency` e `degree_rewire_bridge_frequency` su finestre energetiche controllate.
+- **Se fallisce**: se i residui graph-only non superano i null in sistemi controllati, restano artefatto del perimetro composito Lab.
+
+## Experiment Design
+- **Script**: `tools/exp_boundary_graph_null_audit.py`.
+- **Input**: `tools/data/boundary_denominator_prescan_full_20260509_1500.json` + `tools/data/boundary_bridge_stability_audit_20260515_1915.json`.
+- **Run**: `python tools/exp_boundary_graph_null_audit.py --n-gaps-values 1024 --seeds 20260515,20260516 --n-shuffle 8 --out tools/data/boundary_graph_null_audit_20260516_0330.json`.
+- **Denominatore**: 13 righe row-aligned, 8 GUE e 5 Poisson.
+- **Griglia osservata**: 6 letture grafiche effettive, `k={2,3,4}`, `n_gaps=1024`, `seed={20260515,20260516}`.
+- **Null**: 384 label-shuffle trial e 384 degree-preserving rewiring trial.
+- **Contratto osservabile-operatore**: il ciclo non testa nuovi Hamiltoniani, V_c, Sturmian, o origine analitica delle label; testa solo se il residuo graph-only del 19:15 supera null grafici row-aligned.
+
+## Results
+| summary | value |
+|---|---:|
+| rows analyzed | 13 |
+| graph reader runs | 6 |
+| two_reader_boundary_confirmed | 1 |
+| graph_only_residue | 3 |
+| graph_specific_residue_after_nulls | 2 |
+| label null trials | 384 |
+| degree rewire null trials | 384 |
+
+| row | classical state | observed | label null | rewire null | label lift | rewire lift | graph baseline state |
+|---|---|---:|---:|---:|---:|---:|---|
+| numeri_primi:cycle_3 | classic_and_graph_bridge | 1.000000 | 0.684896 | 0.942708 | 0.315104 | 0.057292 | not_graph_specific_residue |
+| percolation:cycle_9 | graph_only_bridge | 1.000000 | 0.718750 | 0.984375 | 0.281250 | 0.015625 | graph_specific_residue |
+| reaction_diffusion:cycle_11 | graph_only_bridge | 0.833333 | 0.765625 | 0.854167 | 0.067708 | -0.020833 | not_graph_specific_residue |
+| logistica_biforcazione_var_3.5699:cycle_13 | graph_only_bridge | 1.000000 | 0.903646 | 0.846354 | 0.096354 | 0.153646 | graph_specific_residue |
+| zeta_zeros:cycle_4 | classic_only_intermediate | 1.000000 | 0.596354 | 0.989583 | 0.403646 | 0.010417 | not_graph_specific_residue |
+| random_matrix:cycle_7 | classic_only_intermediate | 0.833333 | 0.617188 | 0.947917 | 0.216146 | -0.114583 | not_graph_specific_residue |
+| brownian_motion:cycle_12 | classic_only_intermediate | 0.833333 | 0.880208 | 0.958333 | -0.046875 | -0.125000 | not_graph_specific_residue |
+
+## Key Findings
+1. Verificato: nel JSON effettivo 19:15 il perimetro perturbato e' 6 letture, non 27; il report testuale precedente sovrastimava la griglia.
+2. Verificato: `two_reader_boundary_confirmed = 1`: solo `numeri_primi:cycle_3` e' classic-and-graph bridge stabile.
+3. Verificato: `graph_only_residue = 3`: `percolation`, `reaction_diffusion`, `logistica_biforcazione_var_3.5699` restano separati dal boundary a due lettori.
+4. Verificato: dopo null grafici, solo `percolation` e `logistica_biforcazione_var_3.5699` superano label-shuffle e rewiring; `reaction_diffusion` non supera il rewiring.
+5. Inferito: il residuo graph-only e' reale come audit da conservare, ma non e' abbastanza forte per promozione fisica. Il prossimo trasferimento deve usare sistemi controllati, non il perimetro composito del Lab.
+
+## Verdict
+CONSTRAINT
+
+Il boundary operativo non e' la somma di quattro righe. E' una riga a due lettori piu' tre residui grafici dichiarati; due residui sopravvivono ai null grafici con lift positivo, uno cade contro rewiring. Il nodo regressivo corregge il contratto da `due lettori` a `frequenza grafica + audit dichiarato` per i graph-only.
+
+## Bicono della scoperta
+- **Due radici**: boundary a due lettori; residuo graph-only auditato.
+- **Singolare**: riga row-aligned prima della decisione del lettore.
+- **Invariante di passaggio**: separazione tra conferma a due lettori e frequenza grafica sotto null.
+- **Campo di possibilita**: possibile = portare due residui graph-only su sistemi fisici controllati come candidati; non-possibile = contare graph-only come boundary confermato o promuovere reaction_diffusion senza nuovo null.
+
+## Consecutio
+Il prossimo ciclo utile non aggiunge metriche al perimetro 13 righe. Porta il gate su un sistema fisico controllato, preferibilmente Rosenzweig-Porter o Anderson/mobility edge, e richiede per ogni finestra: Brody/Berry-Robnik, frequenza grafica, label-shuffle e rewiring degree-preserving.
+
+## Ricadute pratiche
+ssp_value: yes. `tools/exp_boundary_graph_null_audit.py` diventa audit riusabile per distinguere boundary a due lettori, residuo graph-only e artefatto grafico in ogni perimetro GUE/Poisson row-aligned.
+
+## Telemetria
+- No API paid run: `ANTHROPIC_API_KEY` e `OPENAI_API_KEY` non presenti nell'ambiente.
+- `python tools/dnd_scenario.py --best` eseguito: massimo discriminante locale su tensioni TRASCENDENZA_LIMITE; la direzione viva del campo ha imposto BOUNDARY 8/5.
+- Primo run default dello script ha usato 27 letture e ha mostrato drift di denominatore rispetto al JSON 19:15; riparazione regressiva: rerun con `n_gaps=1024`, `n_shuffle=8`, due seed, allineato al file sorgente.
+- `python -m py_compile tools/exp_boundary_graph_null_audit.py` completato.
+- `python tools/exp_boundary_graph_null_audit.py --n-gaps-values 1024 --seeds 20260515,20260516 --n-shuffle 8 --out tools/data/boundary_graph_null_audit_20260516_0330.json` completato.
+- Nessun update del seme.
+- Nessuna promozione e nessun public sync.
+
+## Files
+- Script: `tools/exp_boundary_graph_null_audit.py`
+- Data: `tools/data/boundary_graph_null_audit_20260516_0330.json`
+- Report: `tools/data/reports/agent_20260516_0330.md`
diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

diff --git a/tools/data/reports/agent_20260516_0330.md b/tools/data/reports/agent_20260516_0330.md
new file mode 100644
index 0000000000000000000000000000000000000000..b465137e5a3f55d6d0eb2a1b85202998a9224ebf
--- /dev/null
+++ b/tools/data/reports/agent_20260516_0330.md
@@ -0,0 +1,111 @@
+# Agent Report - Boundary Graph Null Audit
+**Date**: 2026-05-16 03:30
+**Piano**: 124
+**Tension explored**: BOUNDARY (0.8)
+**verdict**: CONSTRAINT - il boundary a due lettori resta una sola riga; i tre residui graph-only sono frequenza grafica auditata, non conferma a due lettori. Due residui superano entrambi i null grafici, con lift piccolo.
+observables_registry: 1.0.0-2026-05-06 via boundary_graph_curvature_gate
+observables_used: [observed_graph_bridge_frequency, label_shuffle_bridge_frequency, degree_rewire_bridge_frequency, label_shuffle_lift, degree_rewire_lift, mean_centroid_margin, mean_cross_neighbor_fraction]
+**observable_contract**: claim=i residui graph-only sono Lab-specific solo se la frequenza bridge osservata supera label-shuffle e degree-preserving graph null; observable=frequenza graph bridge osservata contro frequenze null grafiche; operator=rerun del lettore graph BOUNDARY con label shuffle e rewiring degree-preserving; generator=13 righe BOUNDARY row-aligned in feature space canonical+rigidity+shuffle-z; denominator=13 righe, 8 GUE e 5 Poisson, su 6 letture grafiche e 384 trial per ciascun null; non_possible=residuo Lab graph-only se i null matchano o superano la frequenza osservata; not_tested=nuovi sistemi Hamiltoniani, unfolding alternativi, universalita fisica dei residui graph-only.
+
+## Respiro fuori-tempo
+- **Combo**: A9 terzo incluso + QxG continuo/discreto + grafo/cut come nodo del confine + tensione seme BOUNDARY "8 domini GUE, 5 Poisson".
+- **Dipolo / punto-zero**: ponte a due lettori / residuo graph-only. Punto-zero: la riga row-aligned prima che venga assegnata a crossover classico o ponte grafico.
+- **Piano superiore**: topologia del grafo con null strutturali; il grafo non decide da solo, viene auditato come oggetto.
+- **Operatori laterali scelti**: graph rewiring, cut, spectral crossover. Rewiring entra come null del lettore; cut entra come osservabile di crossing; crossover resta baseline, non sorgente.
+- **Contaminazione cognitiva**: CE-0019 `tools/data/cognitive_enzymes_archive.md` metabolizzata: combo prima della misura. CE-0022 gia attiva dal campo: palette operatoria trasformata in null grafici, non lista di temi.
+- **Proto-ipotesi**: il nodo regressivo del 19:15 non e' "il grafo conferma tre boundary"; e' "il contratto deve separare boundary a due lettori da frequenza grafica auditata".
+- **Proiezione**: se il residuo graph-only e' strutturale, la sua frequenza osservata deve superare label-shuffle e rewiring degree-preserving sulla stessa riga.
+
+## Aderenza alla direzione
+- `relation`: `follows_direction`
+- `why`: il ciclo testa esplicitamente il perimetro vivo 8 GUE / 5 Poisson e il terzo incluso operativo come confine auditato.
+- `not_drift`: non usa il report Sturmian bloccato, non misura V_c, non usa phi/silver/bronze; ripara il contratto del boundary 19:15 nel nodo regressivo indicato dal falsifier.
+
+## Re-discovery audit
+- **Baseline noto piu' vicino**: Brody distribution, Berry-Robnik-like mixture e Rosenzweig-Porter come famiglia fisica di crossover; per il grafo, null label-shuffle e degree-preserving rewiring.
+- **Cosa viene assorbito dal baseline classico**: `numeri_primi:cycle_3` resta l'unica riga a due lettori nel perimetro effettivo 19:15.
+- **Cosa resta Lab-specific sotto audit**: `percolation:cycle_9` e `logistica_biforcazione_var_3.5699:cycle_13` superano entrambi i null grafici, ma con lift piccoli (`degree_rewire_lift=0.015625` e `0.153646`).
+- **Cosa cade come claim forte**: `reaction_diffusion:cycle_11` resta graph-only stabile, ma non supera il rewiring degree-preserving (`degree_rewire_lift=-0.020833`). Non e' residuo graph-specific.
+- **Contratto corretto**: `two_reader_boundary_confirmed = 1`; `graph_only_residue = 3`; `graph_specific_residue_after_nulls = 2`; `scope_change_declared = true`; `graph_baseline_audit = label_shuffle + degree_preserving_rewire`.
+
+## Claim Under Test
+> Nel perimetro 8/5, il residuo graph-only sopravvive come contenuto Lab solo dopo audit contro null grafici; non si somma al boundary a due lettori.
+
+## Question
+I tre graph-only bridge del 19:15 sono struttura grafica specifica o effetto atteso da label/cut su un grafo piccolo?
+
+## Ritorno fisico
+- **Punto fisico sorgente**: transizione spettrale tra repulsione Wigner-Dyson/GUE e indipendenza/localizzazione Poisson.
+- **Attraversamento matematico**: grafo kNN multi-osservabile con null label-shuffle e rewiring degree-preserving.
+- **Punto fisico di ritorno**: nelle finestre finite di sistemi Rosenzweig-Porter, Anderson o Aubry-Andre, una finestra graph-only va trattata come candidato da stressare con null topologici prima di chiamarla boundary fisico.
+- **Relazione nuova**: il bridge fisico non e' "q intermedio" e non e' "nodo grafico"; e' una riga che dichiara lettore classico, lettore grafico e null del grafo.
+- **Osservabile/test fisico possibile**: misurare `observed_graph_bridge_frequency`, `label_shuffle_bridge_frequency` e `degree_rewire_bridge_frequency` su finestre energetiche controllate.
+- **Se fallisce**: se i residui graph-only non superano i null in sistemi controllati, restano artefatto del perimetro composito Lab.
+
+## Experiment Design
+- **Script**: `tools/exp_boundary_graph_null_audit.py`.
+- **Input**: `tools/data/boundary_denominator_prescan_full_20260509_1500.json` + `tools/data/boundary_bridge_stability_audit_20260515_1915.json`.
+- **Run**: `python tools/exp_boundary_graph_null_audit.py --n-gaps-values 1024 --seeds 20260515,20260516 --n-shuffle 8 --out tools/data/boundary_graph_null_audit_20260516_0330.json`.
+- **Denominatore**: 13 righe row-aligned, 8 GUE e 5 Poisson.
+- **Griglia osservata**: 6 letture grafiche effettive, `k={2,3,4}`, `n_gaps=1024`, `seed={20260515,20260516}`.
+- **Null**: 384 label-shuffle trial e 384 degree-preserving rewiring trial.
+- **Contratto osservabile-operatore**: il ciclo non testa nuovi Hamiltoniani, V_c, Sturmian, o origine analitica delle label; testa solo se il residuo graph-only del 19:15 supera null grafici row-aligned.
+
+## Results
+| summary | value |
+|---|---:|
+| rows analyzed | 13 |
+| graph reader runs | 6 |
+| two_reader_boundary_confirmed | 1 |
+| graph_only_residue | 3 |
+| graph_specific_residue_after_nulls | 2 |
+| label null trials | 384 |
+| degree rewire null trials | 384 |
+
+| row | classical state | observed | label null | rewire null | label lift | rewire lift | graph baseline state |
+|---|---|---:|---:|---:|---:|---:|---|
+| numeri_primi:cycle_3 | classic_and_graph_bridge | 1.000000 | 0.684896 | 0.942708 | 0.315104 | 0.057292 | not_graph_specific_residue |
+| percolation:cycle_9 | graph_only_bridge | 1.000000 | 0.718750 | 0.984375 | 0.281250 | 0.015625 | graph_specific_residue |
+| reaction_diffusion:cycle_11 | graph_only_bridge | 0.833333 | 0.765625 | 0.854167 | 0.067708 | -0.020833 | not_graph_specific_residue |
+| logistica_biforcazione_var_3.5699:cycle_13 | graph_only_bridge | 1.000000 | 0.903646 | 0.846354 | 0.096354 | 0.153646 | graph_specific_residue |
+| zeta_zeros:cycle_4 | classic_only_intermediate | 1.000000 | 0.596354 | 0.989583 | 0.403646 | 0.010417 | not_graph_specific_residue |
+| random_matrix:cycle_7 | classic_only_intermediate | 0.833333 | 0.617188 | 0.947917 | 0.216146 | -0.114583 | not_graph_specific_residue |
+| brownian_motion:cycle_12 | classic_only_intermediate | 0.833333 | 0.880208 | 0.958333 | -0.046875 | -0.125000 | not_graph_specific_residue |
+
+## Key Findings
+1. Verificato: nel JSON effettivo 19:15 il perimetro perturbato e' 6 letture, non 27; il report testuale precedente sovrastimava la griglia.
+2. Verificato: `two_reader_boundary_confirmed = 1`: solo `numeri_primi:cycle_3` e' classic-and-graph bridge stabile.
+3. Verificato: `graph_only_residue = 3`: `percolation`, `reaction_diffusion`, `logistica_biforcazione_var_3.5699` restano separati dal boundary a due lettori.
+4. Verificato: dopo null grafici, solo `percolation` e `logistica_biforcazione_var_3.5699` superano label-shuffle e rewiring; `reaction_diffusion` non supera il rewiring.
+5. Inferito: il residuo graph-only e' reale come audit da conservare, ma non e' abbastanza forte per promozione fisica. Il prossimo trasferimento deve usare sistemi controllati, non il perimetro composito del Lab.
+
+## Verdict
+CONSTRAINT
+
+Il boundary operativo non e' la somma di quattro righe. E' una riga a due lettori piu' tre residui grafici dichiarati; due residui sopravvivono ai null grafici con lift positivo, uno cade contro rewiring. Il nodo regressivo corregge il contratto da `due lettori` a `frequenza grafica + audit dichiarato` per i graph-only.
+
+## Bicono della scoperta
+- **Due radici**: boundary a due lettori; residuo graph-only auditato.
+- **Singolare**: riga row-aligned prima della decisione del lettore.
+- **Invariante di passaggio**: separazione tra conferma a due lettori e frequenza grafica sotto null.
+- **Campo di possibilita**: possibile = portare due residui graph-only su sistemi fisici controllati come candidati; non-possibile = contare graph-only come boundary confermato o promuovere reaction_diffusion senza nuovo null.
+
+## Consecutio
+Il prossimo ciclo utile non aggiunge metriche al perimetro 13 righe. Porta il gate su un sistema fisico controllato, preferibilmente Rosenzweig-Porter o Anderson/mobility edge, e richiede per ogni finestra: Brody/Berry-Robnik, frequenza grafica, label-shuffle e rewiring degree-preserving.
+
+## Ricadute pratiche
+ssp_value: yes. `tools/exp_boundary_graph_null_audit.py` diventa audit riusabile per distinguere boundary a due lettori, residuo graph-only e artefatto grafico in ogni perimetro GUE/Poisson row-aligned.
+
+## Telemetria
+- No API paid run: `ANTHROPIC_API_KEY` e `OPENAI_API_KEY` non presenti nell'ambiente.
+- `python tools/dnd_scenario.py --best` eseguito: massimo discriminante locale su tensioni TRASCENDENZA_LIMITE; la direzione viva del campo ha imposto BOUNDARY 8/5.
+- Primo run default dello script ha usato 27 letture e ha mostrato drift di denominatore rispetto al JSON 19:15; riparazione regressiva: rerun con `n_gaps=1024`, `n_shuffle=8`, due seed, allineato al file sorgente.
+- `python -m py_compile tools/exp_boundary_graph_null_audit.py` completato.
+- `python tools/exp_boundary_graph_null_audit.py --n-gaps-values 1024 --seeds 20260515,20260516 --n-shuffle 8 --out tools/data/boundary_graph_null_audit_20260516_0330.json` completato.
+- Nessun update del seme.
+- Nessuna promozione e nessun public sync.
+
+## Files
+- Script: `tools/exp_boundary_graph_null_audit.py`
+- Data: `tools/data/boundary_graph_null_audit_20260516_0330.json`
+- Report: `tools/data/reports/agent_20260516_0330.md`
diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

codex
Report scritto. Faccio un controllo finale sui file prodotti e sulla sintassi dello script, poi chiudo con il riepilogo essenziale.
exec
/bin/bash -lc 'test -s tools/data/boundary_graph_null_audit_20260516_0330.json && test -s tools/data/reports/agent_20260516_0330.md && echo ok' in /opt/MM_D-ND
 succeeded in 0ms:
ok

exec
/bin/bash -lc 'git status --short tools/exp_boundary_graph_null_audit.py tools/data/boundary_graph_null_audit_20260516_0330.json tools/data/reports/agent_20260516_0330.md' in /opt/MM_D-ND
 succeeded in 0ms:
?? tools/data/boundary_graph_null_audit_20260516_0330.json
?? tools/data/reports/agent_20260516_0330.md
?? tools/exp_boundary_graph_null_audit.py

exec
/bin/bash -lc 'rg -n "two_reader_boundary_confirmed|graph_only_residue|scope_change_declared|graph_baseline_audit|CE-0019|Re-discovery audit" tools/data/reports/agent_20260516_0330.md' in /opt/MM_D-ND
 succeeded in 0ms:
15:- **Contaminazione cognitiva**: CE-0019 `tools/data/cognitive_enzymes_archive.md` metabolizzata: combo prima della misura. CE-0022 gia attiva dal campo: palette operatoria trasformata in null grafici, non lista di temi.
24:## Re-discovery audit
29:- **Contratto corretto**: `two_reader_boundary_confirmed = 1`; `graph_only_residue = 3`; `graph_specific_residue_after_nulls = 2`; `scope_change_declared = true`; `graph_baseline_audit = label_shuffle + degree_preserving_rewire`.
59:| two_reader_boundary_confirmed | 1 |
60:| graph_only_residue | 3 |
77:2. Verificato: `two_reader_boundary_confirmed = 1`: solo `numeri_primi:cycle_3` e' classic-and-graph bridge stabile.
78:3. Verificato: `graph_only_residue = 3`: `percolation`, `reaction_diffusion`, `logistica_biforcazione_var_3.5699` restano separati dal boundary a due lettori.

exec
/bin/bash -lc 'python -m py_compile tools/exp_boundary_graph_null_audit.py' in /opt/MM_D-ND
 succeeded in 0ms:
diff --git a/tools/data/reports/agent_20260516_0330.md b/tools/data/reports/agent_20260516_0330.md
new file mode 100644
index 0000000000000000000000000000000000000000..b465137e5a3f55d6d0eb2a1b85202998a9224ebf
--- /dev/null
+++ b/tools/data/reports/agent_20260516_0330.md
@@ -0,0 +1,111 @@
+# Agent Report - Boundary Graph Null Audit
+**Date**: 2026-05-16 03:30
+**Piano**: 124
+**Tension explored**: BOUNDARY (0.8)
+**verdict**: CONSTRAINT - il boundary a due lettori resta una sola riga; i tre residui graph-only sono frequenza grafica auditata, non conferma a due lettori. Due residui superano entrambi i null grafici, con lift piccolo.
+observables_registry: 1.0.0-2026-05-06 via boundary_graph_curvature_gate
+observables_used: [observed_graph_bridge_frequency, label_shuffle_bridge_frequency, degree_rewire_bridge_frequency, label_shuffle_lift, degree_rewire_lift, mean_centroid_margin, mean_cross_neighbor_fraction]
+**observable_contract**: claim=i residui graph-only sono Lab-specific solo se la frequenza bridge osservata supera label-shuffle e degree-preserving graph null; observable=frequenza graph bridge osservata contro frequenze null grafiche; operator=rerun del lettore graph BOUNDARY con label shuffle e rewiring degree-preserving; generator=13 righe BOUNDARY row-aligned in feature space canonical+rigidity+shuffle-z; denominator=13 righe, 8 GUE e 5 Poisson, su 6 letture grafiche e 384 trial per ciascun null; non_possible=residuo Lab graph-only se i null matchano o superano la frequenza osservata; not_tested=nuovi sistemi Hamiltoniani, unfolding alternativi, universalita fisica dei residui graph-only.
+
+## Respiro fuori-tempo
+- **Combo**: A9 terzo incluso + QxG continuo/discreto + grafo/cut come nodo del confine + tensione seme BOUNDARY "8 domini GUE, 5 Poisson".
+- **Dipolo / punto-zero**: ponte a due lettori / residuo graph-only. Punto-zero: la riga row-aligned prima che venga assegnata a crossover classico o ponte grafico.
+- **Piano superiore**: topologia del grafo con null strutturali; il grafo non decide da solo, viene auditato come oggetto.
+- **Operatori laterali scelti**: graph rewiring, cut, spectral crossover. Rewiring entra come null del lettore; cut entra come osservabile di crossing; crossover resta baseline, non sorgente.
+- **Contaminazione cognitiva**: CE-0019 `tools/data/cognitive_enzymes_archive.md` metabolizzata: combo prima della misura. CE-0022 gia attiva dal campo: palette operatoria trasformata in null grafici, non lista di temi.
+- **Proto-ipotesi**: il nodo regressivo del 19:15 non e' "il grafo conferma tre boundary"; e' "il contratto deve separare boundary a due lettori da frequenza grafica auditata".
+- **Proiezione**: se il residuo graph-only e' strutturale, la sua frequenza osservata deve superare label-shuffle e rewiring degree-preserving sulla stessa riga.
+
+## Aderenza alla direzione
+- `relation`: `follows_direction`
+- `why`: il ciclo testa esplicitamente il perimetro vivo 8 GUE / 5 Poisson e il terzo incluso operativo come confine auditato.
+- `not_drift`: non usa il report Sturmian bloccato, non misura V_c, non usa phi/silver/bronze; ripara il contratto del boundary 19:15 nel nodo regressivo indicato dal falsifier.
+
+## Re-discovery audit
+- **Baseline noto piu' vicino**: Brody distribution, Berry-Robnik-like mixture e Rosenzweig-Porter come famiglia fisica di crossover; per il grafo, null label-shuffle e degree-preserving rewiring.
+- **Cosa viene assorbito dal baseline classico**: `numeri_primi:cycle_3` resta l'unica riga a due lettori nel perimetro effettivo 19:15.
+- **Cosa resta Lab-specific sotto audit**: `percolation:cycle_9` e `logistica_biforcazione_var_3.5699:cycle_13` superano entrambi i null grafici, ma con lift piccoli (`degree_rewire_lift=0.015625` e `0.153646`).
+- **Cosa cade come claim forte**: `reaction_diffusion:cycle_11` resta graph-only stabile, ma non supera il rewiring degree-preserving (`degree_rewire_lift=-0.020833`). Non e' residuo graph-specific.
+- **Contratto corretto**: `two_reader_boundary_confirmed = 1`; `graph_only_residue = 3`; `graph_specific_residue_after_nulls = 2`; `scope_change_declared = true`; `graph_baseline_audit = label_shuffle + degree_preserving_rewire`.
+
+## Claim Under Test
+> Nel perimetro 8/5, il residuo graph-only sopravvive come contenuto Lab solo dopo audit contro null grafici; non si somma al boundary a due lettori.
+
+## Question
+I tre graph-only bridge del 19:15 sono struttura grafica specifica o effetto atteso da label/cut su un grafo piccolo?
+
+## Ritorno fisico
+- **Punto fisico sorgente**: transizione spettrale tra repulsione Wigner-Dyson/GUE e indipendenza/localizzazione Poisson.
+- **Attraversamento matematico**: grafo kNN multi-osservabile con null label-shuffle e rewiring degree-preserving.
+- **Punto fisico di ritorno**: nelle finestre finite di sistemi Rosenzweig-Porter, Anderson o Aubry-Andre, una finestra graph-only va trattata come candidato da stressare con null topologici prima di chiamarla boundary fisico.
+- **Relazione nuova**: il bridge fisico non e' "q intermedio" e non e' "nodo grafico"; e' una riga che dichiara lettore classico, lettore grafico e null del grafo.
+- **Osservabile/test fisico possibile**: misurare `observed_graph_bridge_frequency`, `label_shuffle_bridge_frequency` e `degree_rewire_bridge_frequency` su finestre energetiche controllate.
+- **Se fallisce**: se i residui graph-only non superano i null in sistemi controllati, restano artefatto del perimetro composito Lab.
+
+## Experiment Design
+- **Script**: `tools/exp_boundary_graph_null_audit.py`.
+- **Input**: `tools/data/boundary_denominator_prescan_full_20260509_1500.json` + `tools/data/boundary_bridge_stability_audit_20260515_1915.json`.
+- **Run**: `python tools/exp_boundary_graph_null_audit.py --n-gaps-values 1024 --seeds 20260515,20260516 --n-shuffle 8 --out tools/data/boundary_graph_null_audit_20260516_0330.json`.
+- **Denominatore**: 13 righe row-aligned, 8 GUE e 5 Poisson.
+- **Griglia osservata**: 6 letture grafiche effettive, `k={2,3,4}`, `n_gaps=1024`, `seed={20260515,20260516}`.
+- **Null**: 384 label-shuffle trial e 384 degree-preserving rewiring trial.
+- **Contratto osservabile-operatore**: il ciclo non testa nuovi Hamiltoniani, V_c, Sturmian, o origine analitica delle label; testa solo se il residuo graph-only del 19:15 supera null grafici row-aligned.
+
+## Results
+| summary | value |
+|---|---:|
+| rows analyzed | 13 |
+| graph reader runs | 6 |
+| two_reader_boundary_confirmed | 1 |
+| graph_only_residue | 3 |
+| graph_specific_residue_after_nulls | 2 |
+| label null trials | 384 |
+| degree rewire null trials | 384 |
+
+| row | classical state | observed | label null | rewire null | label lift | rewire lift | graph baseline state |
+|---|---|---:|---:|---:|---:|---:|---|
+| numeri_primi:cycle_3 | classic_and_graph_bridge | 1.000000 | 0.684896 | 0.942708 | 0.315104 | 0.057292 | not_graph_specific_residue |
+| percolation:cycle_9 | graph_only_bridge | 1.000000 | 0.718750 | 0.984375 | 0.281250 | 0.015625 | graph_specific_residue |
+| reaction_diffusion:cycle_11 | graph_only_bridge | 0.833333 | 0.765625 | 0.854167 | 0.067708 | -0.020833 | not_graph_specific_residue |
+| logistica_biforcazione_var_3.5699:cycle_13 | graph_only_bridge | 1.000000 | 0.903646 | 0.846354 | 0.096354 | 0.153646 | graph_specific_residue |
+| zeta_zeros:cycle_4 | classic_only_intermediate | 1.000000 | 0.596354 | 0.989583 | 0.403646 | 0.010417 | not_graph_specific_residue |
+| random_matrix:cycle_7 | classic_only_intermediate | 0.833333 | 0.617188 | 0.947917 | 0.216146 | -0.114583 | not_graph_specific_residue |
+| brownian_motion:cycle_12 | classic_only_intermediate | 0.833333 | 0.880208 | 0.958333 | -0.046875 | -0.125000 | not_graph_specific_residue |
+
+## Key Findings
+1. Verificato: nel JSON effettivo 19:15 il perimetro perturbato e' 6 letture, non 27; il report testuale precedente sovrastimava la griglia.
+2. Verificato: `two_reader_boundary_confirmed = 1`: solo `numeri_primi:cycle_3` e' classic-and-graph bridge stabile.
+3. Verificato: `graph_only_residue = 3`: `percolation`, `reaction_diffusion`, `logistica_biforcazione_var_3.5699` restano separati dal boundary a due lettori.
+4. Verificato: dopo null grafici, solo `percolation` e `logistica_biforcazione_var_3.5699` superano label-shuffle e rewiring; `reaction_diffusion` non supera il rewiring.
+5. Inferito: il residuo graph-only e' reale come audit da conservare, ma non e' abbastanza forte per promozione fisica. Il prossimo trasferimento deve usare sistemi controllati, non il perimetro composito del Lab.
+
+## Verdict
+CONSTRAINT
+
+Il boundary operativo non e' la somma di quattro righe. E' una riga a due lettori piu' tre residui grafici dichiarati; due residui sopravvivono ai null grafici con lift positivo, uno cade contro rewiring. Il nodo regressivo corregge il contratto da `due lettori` a `frequenza grafica + audit dichiarato` per i graph-only.
+
+## Bicono della scoperta
+- **Due radici**: boundary a due lettori; residuo graph-only auditato.
+- **Singolare**: riga row-aligned prima della decisione del lettore.
+- **Invariante di passaggio**: separazione tra conferma a due lettori e frequenza grafica sotto null.
+- **Campo di possibilita**: possibile = portare due residui graph-only su sistemi fisici controllati come candidati; non-possibile = contare graph-only come boundary confermato o promuovere reaction_diffusion senza nuovo null.
+
+## Consecutio
+Il prossimo ciclo utile non aggiunge metriche al perimetro 13 righe. Porta il gate su un sistema fisico controllato, preferibilmente Rosenzweig-Porter o Anderson/mobility edge, e richiede per ogni finestra: Brody/Berry-Robnik, frequenza grafica, label-shuffle e rewiring degree-preserving.
+
+## Ricadute pratiche
+ssp_value: yes. `tools/exp_boundary_graph_null_audit.py` diventa audit riusabile per distinguere boundary a due lettori, residuo graph-only e artefatto grafico in ogni perimetro GUE/Poisson row-aligned.
+
+## Telemetria
+- No API paid run: `ANTHROPIC_API_KEY` e `OPENAI_API_KEY` non presenti nell'ambiente.
+- `python tools/dnd_scenario.py --best` eseguito: massimo discriminante locale su tensioni TRASCENDENZA_LIMITE; la direzione viva del campo ha imposto BOUNDARY 8/5.
+- Primo run default dello script ha usato 27 letture e ha mostrato drift di denominatore rispetto al JSON 19:15; riparazione regressiva: rerun con `n_gaps=1024`, `n_shuffle=8`, due seed, allineato al file sorgente.
+- `python -m py_compile tools/exp_boundary_graph_null_audit.py` completato.
+- `python tools/exp_boundary_graph_null_audit.py --n-gaps-values 1024 --seeds 20260515,20260516 --n-shuffle 8 --out tools/data/boundary_graph_null_audit_20260516_0330.json` completato.
+- Nessun update del seme.
+- Nessuna promozione e nessun public sync.
+
+## Files
+- Script: `tools/exp_boundary_graph_null_audit.py`
+- Data: `tools/data/boundary_graph_null_audit_20260516_0330.json`
+- Report: `tools/data/reports/agent_20260516_0330.md`
diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

codex
Eseguito il cycle e scritto il report richiesto:

- [agent_20260516_0330.md](/opt/MM_D-ND/tools/data/reports/agent_20260516_0330.md)
- [exp_boundary_graph_null_audit.py](/opt/MM_D-ND/tools/exp_boundary_graph_null_audit.py)
- [boundary_graph_null_audit_20260516_0330.json](/opt/MM_D-ND/tools/data/boundary_graph_null_audit_20260516_0330.json)

Risultante: `two_reader_boundary_confirmed = 1`, `graph_only_residue = 3`, ma dopo audit grafico solo 2 residui graph-only superano entrambi i null (`percolation`, `logistica_biforcazione_var_3.5699`). `reaction_diffusion` resta stabile nel grafo ma cade contro il rewiring degree-preserving, quindi non va promosso come residuo graph-specific.

Verifica completata: `python -m py_compile tools/exp_boundary_graph_null_audit.py`.
diff --git a/tools/data/reports/agent_20260516_0330.md b/tools/data/reports/agent_20260516_0330.md
new file mode 100644
index 0000000000000000000000000000000000000000..b465137e5a3f55d6d0eb2a1b85202998a9224ebf
--- /dev/null
+++ b/tools/data/reports/agent_20260516_0330.md
@@ -0,0 +1,111 @@
+# Agent Report - Boundary Graph Null Audit
+**Date**: 2026-05-16 03:30
+**Piano**: 124
+**Tension explored**: BOUNDARY (0.8)
+**verdict**: CONSTRAINT - il boundary a due lettori resta una sola riga; i tre residui graph-only sono frequenza grafica auditata, non conferma a due lettori. Due residui superano entrambi i null grafici, con lift piccolo.
+observables_registry: 1.0.0-2026-05-06 via boundary_graph_curvature_gate
+observables_used: [observed_graph_bridge_frequency, label_shuffle_bridge_frequency, degree_rewire_bridge_frequency, label_shuffle_lift, degree_rewire_lift, mean_centroid_margin, mean_cross_neighbor_fraction]
+**observable_contract**: claim=i residui graph-only sono Lab-specific solo se la frequenza bridge osservata supera label-shuffle e degree-preserving graph null; observable=frequenza graph bridge osservata contro frequenze null grafiche; operator=rerun del lettore graph BOUNDARY con label shuffle e rewiring degree-preserving; generator=13 righe BOUNDARY row-aligned in feature space canonical+rigidity+shuffle-z; denominator=13 righe, 8 GUE e 5 Poisson, su 6 letture grafiche e 384 trial per ciascun null; non_possible=residuo Lab graph-only se i null matchano o superano la frequenza osservata; not_tested=nuovi sistemi Hamiltoniani, unfolding alternativi, universalita fisica dei residui graph-only.
+
+## Respiro fuori-tempo
+- **Combo**: A9 terzo incluso + QxG continuo/discreto + grafo/cut come nodo del confine + tensione seme BOUNDARY "8 domini GUE, 5 Poisson".
+- **Dipolo / punto-zero**: ponte a due lettori / residuo graph-only. Punto-zero: la riga row-aligned prima che venga assegnata a crossover classico o ponte grafico.
+- **Piano superiore**: topologia del grafo con null strutturali; il grafo non decide da solo, viene auditato come oggetto.
+- **Operatori laterali scelti**: graph rewiring, cut, spectral crossover. Rewiring entra come null del lettore; cut entra come osservabile di crossing; crossover resta baseline, non sorgente.
+- **Contaminazione cognitiva**: CE-0019 `tools/data/cognitive_enzymes_archive.md` metabolizzata: combo prima della misura. CE-0022 gia attiva dal campo: palette operatoria trasformata in null grafici, non lista di temi.
+- **Proto-ipotesi**: il nodo regressivo del 19:15 non e' "il grafo conferma tre boundary"; e' "il contratto deve separare boundary a due lettori da frequenza grafica auditata".
+- **Proiezione**: se il residuo graph-only e' strutturale, la sua frequenza osservata deve superare label-shuffle e rewiring degree-preserving sulla stessa riga.
+
+## Aderenza alla direzione
+- `relation`: `follows_direction`
+- `why`: il ciclo testa esplicitamente il perimetro vivo 8 GUE / 5 Poisson e il terzo incluso operativo come confine auditato.
+- `not_drift`: non usa il report Sturmian bloccato, non misura V_c, non usa phi/silver/bronze; ripara il contratto del boundary 19:15 nel nodo regressivo indicato dal falsifier.
+
+## Re-discovery audit
+- **Baseline noto piu' vicino**: Brody distribution, Berry-Robnik-like mixture e Rosenzweig-Porter come famiglia fisica di crossover; per il grafo, null label-shuffle e degree-preserving rewiring.
+- **Cosa viene assorbito dal baseline classico**: `numeri_primi:cycle_3` resta l'unica riga a due lettori nel perimetro effettivo 19:15.
+- **Cosa resta Lab-specific sotto audit**: `percolation:cycle_9` e `logistica_biforcazione_var_3.5699:cycle_13` superano entrambi i null grafici, ma con lift piccoli (`degree_rewire_lift=0.015625` e `0.153646`).
+- **Cosa cade come claim forte**: `reaction_diffusion:cycle_11` resta graph-only stabile, ma non supera il rewiring degree-preserving (`degree_rewire_lift=-0.020833`). Non e' residuo graph-specific.
+- **Contratto corretto**: `two_reader_boundary_confirmed = 1`; `graph_only_residue = 3`; `graph_specific_residue_after_nulls = 2`; `scope_change_declared = true`; `graph_baseline_audit = label_shuffle + degree_preserving_rewire`.
+
+## Claim Under Test
+> Nel perimetro 8/5, il residuo graph-only sopravvive come contenuto Lab solo dopo audit contro null grafici; non si somma al boundary a due lettori.
+
+## Question
+I tre graph-only bridge del 19:15 sono struttura grafica specifica o effetto atteso da label/cut su un grafo piccolo?
+
+## Ritorno fisico
+- **Punto fisico sorgente**: transizione spettrale tra repulsione Wigner-Dyson/GUE e indipendenza/localizzazione Poisson.
+- **Attraversamento matematico**: grafo kNN multi-osservabile con null label-shuffle e rewiring degree-preserving.
+- **Punto fisico di ritorno**: nelle finestre finite di sistemi Rosenzweig-Porter, Anderson o Aubry-Andre, una finestra graph-only va trattata come candidato da stressare con null topologici prima di chiamarla boundary fisico.
+- **Relazione nuova**: il bridge fisico non e' "q intermedio" e non e' "nodo grafico"; e' una riga che dichiara lettore classico, lettore grafico e null del grafo.
+- **Osservabile/test fisico possibile**: misurare `observed_graph_bridge_frequency`, `label_shuffle_bridge_frequency` e `degree_rewire_bridge_frequency` su finestre energetiche controllate.
+- **Se fallisce**: se i residui graph-only non superano i null in sistemi controllati, restano artefatto del perimetro composito Lab.
+
+## Experiment Design
+- **Script**: `tools/exp_boundary_graph_null_audit.py`.
+- **Input**: `tools/data/boundary_denominator_prescan_full_20260509_1500.json` + `tools/data/boundary_bridge_stability_audit_20260515_1915.json`.
+- **Run**: `python tools/exp_boundary_graph_null_audit.py --n-gaps-values 1024 --seeds 20260515,20260516 --n-shuffle 8 --out tools/data/boundary_graph_null_audit_20260516_0330.json`.
+- **Denominatore**: 13 righe row-aligned, 8 GUE e 5 Poisson.
+- **Griglia osservata**: 6 letture grafiche effettive, `k={2,3,4}`, `n_gaps=1024`, `seed={20260515,20260516}`.
+- **Null**: 384 label-shuffle trial e 384 degree-preserving rewiring trial.
+- **Contratto osservabile-operatore**: il ciclo non testa nuovi Hamiltoniani, V_c, Sturmian, o origine analitica delle label; testa solo se il residuo graph-only del 19:15 supera null grafici row-aligned.
+
+## Results
+| summary | value |
+|---|---:|
+| rows analyzed | 13 |
+| graph reader runs | 6 |
+| two_reader_boundary_confirmed | 1 |
+| graph_only_residue | 3 |
+| graph_specific_residue_after_nulls | 2 |
+| label null trials | 384 |
+| degree rewire null trials | 384 |
+
+| row | classical state | observed | label null | rewire null | label lift | rewire lift | graph baseline state |
+|---|---|---:|---:|---:|---:|---:|---|
+| numeri_primi:cycle_3 | classic_and_graph_bridge | 1.000000 | 0.684896 | 0.942708 | 0.315104 | 0.057292 | not_graph_specific_residue |
+| percolation:cycle_9 | graph_only_bridge | 1.000000 | 0.718750 | 0.984375 | 0.281250 | 0.015625 | graph_specific_residue |
+| reaction_diffusion:cycle_11 | graph_only_bridge | 0.833333 | 0.765625 | 0.854167 | 0.067708 | -0.020833 | not_graph_specific_residue |
+| logistica_biforcazione_var_3.5699:cycle_13 | graph_only_bridge | 1.000000 | 0.903646 | 0.846354 | 0.096354 | 0.153646 | graph_specific_residue |
+| zeta_zeros:cycle_4 | classic_only_intermediate | 1.000000 | 0.596354 | 0.989583 | 0.403646 | 0.010417 | not_graph_specific_residue |
+| random_matrix:cycle_7 | classic_only_intermediate | 0.833333 | 0.617188 | 0.947917 | 0.216146 | -0.114583 | not_graph_specific_residue |
+| brownian_motion:cycle_12 | classic_only_intermediate | 0.833333 | 0.880208 | 0.958333 | -0.046875 | -0.125000 | not_graph_specific_residue |
+
+## Key Findings
+1. Verificato: nel JSON effettivo 19:15 il perimetro perturbato e' 6 letture, non 27; il report testuale precedente sovrastimava la griglia.
+2. Verificato: `two_reader_boundary_confirmed = 1`: solo `numeri_primi:cycle_3` e' classic-and-graph bridge stabile.
+3. Verificato: `graph_only_residue = 3`: `percolation`, `reaction_diffusion`, `logistica_biforcazione_var_3.5699` restano separati dal boundary a due lettori.
+4. Verificato: dopo null grafici, solo `percolation` e `logistica_biforcazione_var_3.5699` superano label-shuffle e rewiring; `reaction_diffusion` non supera il rewiring.
+5. Inferito: il residuo graph-only e' reale come audit da conservare, ma non e' abbastanza forte per promozione fisica. Il prossimo trasferimento deve usare sistemi controllati, non il perimetro composito del Lab.
+
+## Verdict
+CONSTRAINT
+
+Il boundary operativo non e' la somma di quattro righe. E' una riga a due lettori piu' tre residui grafici dichiarati; due residui sopravvivono ai null grafici con lift positivo, uno cade contro rewiring. Il nodo regressivo corregge il contratto da `due lettori` a `frequenza grafica + audit dichiarato` per i graph-only.
+
+## Bicono della scoperta
+- **Due radici**: boundary a due lettori; residuo graph-only auditato.
+- **Singolare**: riga row-aligned prima della decisione del lettore.
+- **Invariante di passaggio**: separazione tra conferma a due lettori e frequenza grafica sotto null.
+- **Campo di possibilita**: possibile = portare due residui graph-only su sistemi fisici controllati come candidati; non-possibile = contare graph-only come boundary confermato o promuovere reaction_diffusion senza nuovo null.
+
+## Consecutio
+Il prossimo ciclo utile non aggiunge metriche al perimetro 13 righe. Porta il gate su un sistema fisico controllato, preferibilmente Rosenzweig-Porter o Anderson/mobility edge, e richiede per ogni finestra: Brody/Berry-Robnik, frequenza grafica, label-shuffle e rewiring degree-preserving.
+
+## Ricadute pratiche
+ssp_value: yes. `tools/exp_boundary_graph_null_audit.py` diventa audit riusabile per distinguere boundary a due lettori, residuo graph-only e artefatto grafico in ogni perimetro GUE/Poisson row-aligned.
+
+## Telemetria
+- No API paid run: `ANTHROPIC_API_KEY` e `OPENAI_API_KEY` non presenti nell'ambiente.
+- `python tools/dnd_scenario.py --best` eseguito: massimo discriminante locale su tensioni TRASCENDENZA_LIMITE; la direzione viva del campo ha imposto BOUNDARY 8/5.
+- Primo run default dello script ha usato 27 letture e ha mostrato drift di denominatore rispetto al JSON 19:15; riparazione regressiva: rerun con `n_gaps=1024`, `n_shuffle=8`, due seed, allineato al file sorgente.
+- `python -m py_compile tools/exp_boundary_graph_null_audit.py` completato.
+- `python tools/exp_boundary_graph_null_audit.py --n-gaps-values 1024 --seeds 20260515,20260516 --n-shuffle 8 --out tools/data/boundary_graph_null_audit_20260516_0330.json` completato.
+- Nessun update del seme.
+- Nessuna promozione e nessun public sync.
+
+## Files
+- Script: `tools/exp_boundary_graph_null_audit.py`
+- Data: `tools/data/boundary_graph_null_audit_20260516_0330.json`
+- Report: `tools/data/reports/agent_20260516_0330.md`
diff --git a/tools/exp_boundary_graph_null_audit.py b/tools/exp_boundary_graph_null_audit.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c915c8a14d2d57da28b1b7a465350becc61fb37
--- /dev/null
+++ b/tools/exp_boundary_graph_null_audit.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python3
+"""
+Graph-null audit for the BOUNDARY composite gate.
+
+This script keeps the 13 row-aligned 8 GUE / 5 Poisson denominator and asks
+whether the stable graph-only bridge residue from the two-reader audit survives
+against graph-native nulls:
+
+- label shuffle on the same feature embedding;
+- degree-preserving rewiring of the kNN graph with labels fixed.
+
+The goal is not to add a third reader. It audits the graph reader itself.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from exp_boundary_graph_curvature_gate import (
+    build_knn_edges,
+    compute_observables,
+    load_scope,
+    row_spacings,
+    shuffle_z,
+    standardized_matrix,
+)
+
+
+def parse_ints(raw: str) -> list[int]:
+    values = [int(part.strip()) for part in raw.split(",") if part.strip()]
+    if not values:
+        raise ValueError("empty integer list")
+    return values
+
+
+def load_json(path: Path) -> dict[str, Any]:
+    with path.open(encoding="utf-8") as f:
+        data = json.load(f)
+    if not isinstance(data, dict):
+        raise ValueError(f"{path} must contain a JSON object")
+    return data
+
+
+def centroid_margins(x: np.ndarray, labels: list[str]) -> list[float]:
+    gue_idx = [i for i, label in enumerate(labels) if label == "GUE"]
+    poi_idx = [i for i, label in enumerate(labels) if label == "Poisson"]
+    c_gue = np.mean(x[gue_idx], axis=0)
+    c_poi = np.mean(x[poi_idx], axis=0)
+    margins = []
+    for i in range(len(labels)):
+        d_gue = float(np.linalg.norm(x[i] - c_gue))
+        d_poi = float(np.linalg.norm(x[i] - c_poi))
+        denom = d_gue + d_poi
+        margins.append(float(abs(d_gue - d_poi) / denom) if denom > 1e-15 else 0.0)
+    return margins
+
+
+def incident_cross_fractions(
+    n_rows: int,
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+) -> list[float]:
+    incident = [0 for _ in range(n_rows)]
+    cross = [0 for _ in range(n_rows)]
+    for i, j, _ in edges:
+        incident[i] += 1
+        incident[j] += 1
+        if labels[i] != labels[j]:
+            cross[i] += 1
+            cross[j] += 1
+    return [float(cross[i] / incident[i]) if incident[i] else 0.0 for i in range(n_rows)]
+
+
+def bridge_flags(
+    edges: list[tuple[int, int, float]],
+    labels: list[str],
+    margins: list[float],
+    margin_threshold: float,
+) -> list[bool]:
+    cross_fractions = incident_cross_fractions(len(labels), edges, labels)
+    return [cross_fractions[i] > 0.0 and margins[i] < margin_threshold for i in range(len(labels))]
+
+
+def edge_key(edge: tuple[int, int, float]) -> tuple[int, int]:
+    i, j, _ = edge
+    return (min(i, j), max(i, j))
+
+
+def degree_preserving_rewire(
+    edges: list[tuple[int, int, float]],
+    n_rows: int,
+    rng: np.random.Generator,
+    swaps: int,
+) -> list[tuple[int, int, float]]:
+    current = {edge_key(edge) for edge in edges}
+    if len(current) < 2:
+        return [(i, j, 1.0) for i, j in sorted(current)]
+
+    edge_list = list(current)
+    attempts = max(swaps * 20, 100)
+    accepted = 0
+    for _ in range(attempts):
+        if accepted >= swaps:
+            break
+        a_idx, b_idx = rng.choice(len(edge_list), size=2, replace=False)
+        a, b = edge_list[a_idx]
+        c, d = edge_list[b_idx]
+        if len({a, b, c, d}) < 4:
+            continue
+        if rng.random() < 0.5:
+            e1 = tuple(sorted((a, d)))
+            e2 = tuple(sorted((c, b)))
+        else:
+            e1 = tuple(sorted((a, c)))
+            e2 = tuple(sorted((b, d)))
+        if e1[0] == e1[1] or e2[0] == e2[1] or e1 == e2:
+            continue
+        if e1 in current or e2 in current:
+            continue
+        old1 = edge_list[a_idx]
+        old2 = edge_list[b_idx]
+        current.remove(old1)
+        current.remove(old2)
+        current.add(e1)
+        current.add(e2)
+        edge_list[a_idx] = e1
+        edge_list[b_idx] = e2
+        accepted += 1
+    return [(i, j, 1.0) for i, j in sorted(current)]
+
+
+def classical_state_by_row(path: Path) -> dict[str, str]:
+    data = load_json(path)
+    rows = data.get("rows", [])
+    if not isinstance(rows, list):
+        raise ValueError("stability audit has no rows")
+    return {row["domain_window"]: row.get("classical_audit_state", "") for row in rows}
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    ks = parse_ints(args.k_values)
+    n_gaps_values = parse_ints(args.n_gaps_values)
+    seeds = parse_ints(args.seeds)
+    source_rows = load_scope(Path(args.scope))
+    selected = [row for row in source_rows if row.get("source_domain_type") in {"GUE", "Poisson"}]
+    selected = sorted(selected, key=lambda row: int(row["cycle"]))
+    names = [row["domain_window"] for row in selected]
+    base_labels = [row["source_domain_type"] for row in selected]
+    classical = classical_state_by_row(Path(args.stability_audit))
+
+    gap_cache = {row["domain_window"]: row_spacings(row["domain"]) for row in selected}
+    rng = np.random.default_rng(args.seed)
+
+    totals = {name: {"observed": 0, "label_null": 0, "rewire_null": 0, "margin": [], "cross": []} for name in names}
+    run_count = 0
+    label_null_trials = 0
+    rewire_null_trials = 0
+
+    for k in ks:
+        for n_gaps in n_gaps_values:
+            for seed in seeds:
+                run_count += 1
+                local_rng = np.random.default_rng(seed)
+                graph_rows = []
+                for source in selected:
+                    gaps = gap_cache[source["domain_window"]]
+                    gaps = gaps[:n_gaps] if len(gaps) > n_gaps else gaps
+                    obs = compute_observables(gaps)
+                    z = shuffle_z(gaps, obs, args.n_shuffle, local_rng)
+                    graph_rows.append(
+                        {
+                            "domain_window": source["domain_window"],
+                            "domain": source["domain"],
+                            "cycle": source["cycle"],
+                            "source_domain_type": source["source_domain_type"],
+                            "n_gaps": int(len(gaps)),
+                            "observables": obs,
+                            "shuffle_z": z,
+                        }
+                    )
+                x = standardized_matrix(graph_rows)
+                edges = build_knn_edges(x, k)
+                margins = centroid_margins(x, base_labels)
+                cross = incident_cross_fractions(len(names), edges, base_labels)
+                observed = bridge_flags(edges, base_labels, margins, args.margin_threshold)
+                for i, name in enumerate(names):
+                    totals[name]["observed"] += int(observed[i])
+                    totals[name]["margin"].append(margins[i])
+                    totals[name]["cross"].append(cross[i])
+
+                labels_array = np.asarray(base_labels, dtype=object)
+                for _ in range(args.label_nulls):
+                    shuffled = labels_array.copy()
+                    rng.shuffle(shuffled)
+                    shuffled_labels = [str(label) for label in shuffled.tolist()]
+                    shuffled_margins = centroid_margins(x, shuffled_labels)
+                    flags = bridge_flags(edges, shuffled_labels, shuffled_margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["label_null"] += int(flags[i])
+                    label_null_trials += 1
+
+                swap_count = max(len(edges) * args.rewire_swap_multiplier, 1)
+                for _ in range(args.rewire_nulls):
+                    rewired = degree_preserving_rewire(edges, len(names), rng, swap_count)
+                    flags = bridge_flags(rewired, base_labels, margins, args.margin_threshold)
+                    for i, name in enumerate(names):
+                        totals[name]["rewire_null"] += int(flags[i])
+                    rewire_null_trials += 1
+
+    rows = []
+    for source in selected:
+        name = source["domain_window"]
+        item = totals[name]
+        observed_freq = item["observed"] / run_count
+        label_freq = item["label_null"] / label_null_trials if label_null_trials else 0.0
+        rewire_freq = item["rewire_null"] / rewire_null_trials if rewire_null_trials else 0.0
+        audit_state = classical.get(name, "")
+        graph_only = audit_state == "graph_only_bridge" and observed_freq >= args.stable_threshold
+        rows.append(
+            {
+                "domain_window": name,
+                "domain": source["domain"],
+                "source_domain_type": source["source_domain_type"],
+                "classical_audit_state": audit_state,
+                "observed_graph_bridge_frequency": round(observed_freq, 6),
+                "label_shuffle_bridge_frequency": round(label_freq, 6),
+                "degree_rewire_bridge_frequency": round(rewire_freq, 6),
+                "label_shuffle_lift": round(observed_freq - label_freq, 6),
+                "degree_rewire_lift": round(observed_freq - rewire_freq, 6),
+                "mean_centroid_margin": round(float(np.mean(item["margin"])), 6),
+                "mean_cross_neighbor_fraction": round(float(np.mean(item["cross"])), 6),
+                "stable_graph_only_residue": graph_only,
+                "graph_baseline_state": (
+                    "graph_specific_residue"
+                    if graph_only and observed_freq > label_freq and observed_freq > rewire_freq
+                    else "not_graph_specific_residue"
+                ),
+            }
+        )
+
+    two_reader = [
+        row["domain_window"]
+        for row in rows
+        if row["classical_audit_state"] == "classic_and_graph_bridge"
+        and row["observed_graph_bridge_frequency"] >= args.stable_threshold
+    ]
+    graph_only = [row["domain_window"] for row in rows if row["stable_graph_only_residue"]]
+    graph_specific = [row["domain_window"] for row in rows if row["graph_baseline_state"] == "graph_specific_residue"]
+
+    output = {
+        "experiment": "boundary_graph_null_audit",
+        "question": "Does the stable graph-only residue survive graph-native null baselines?",
+        "observables_registry": "1.0.0-2026-05-06 via boundary_graph_curvature_gate",
+        "observables_used": [
+            "observed_graph_bridge_frequency",
+            "label_shuffle_bridge_frequency",
+            "degree_rewire_bridge_frequency",
+            "label_shuffle_lift",
+            "degree_rewire_lift",
+            "mean_centroid_margin",
+            "mean_cross_neighbor_fraction",
+        ],
+        "params": {
+            "scope": args.scope,
+            "stability_audit": args.stability_audit,
+            "k_values": ks,
+            "n_gaps_values": n_gaps_values,
+            "seeds": seeds,
+            "n_shuffle": args.n_shuffle,
+            "label_nulls": args.label_nulls,
+            "rewire_nulls": args.rewire_nulls,
+            "margin_threshold": args.margin_threshold,
+            "stable_threshold": args.stable_threshold,
+            "graph_reader_runs": run_count,
+            "label_null_trials": label_null_trials,
+            "rewire_null_trials": rewire_null_trials,
+        },
+        "observable_contract": {
+            "claim": "graph-only residues are Lab-specific only if their bridge frequency exceeds label-shuffle and degree-preserving graph null frequencies",
+            "observable": "observed graph bridge frequency versus graph-native null bridge frequencies",
+            "operator": "rerun BOUNDARY graph reader and compare each row to label-shuffle and degree-preserving rewiring nulls",
+            "generator": "13 row-aligned BOUNDARY denominator with canonical+rigidity+shuffle-z feature graph",
+            "denominator": "13 rows: 8 GUE and 5 Poisson, repeated across graph-reader parameter grid and graph null trials",
+            "non_possible": "graph-only Lab residue if null frequencies match or exceed observed graph bridge frequency",
+            "not_tested": "new Hamiltonian systems, alternative unfolding, physical universality of graph-only rows",
+        },
+        "summary": {
+            "rows_analyzed": len(rows),
+            "graph_reader_runs": run_count,
+            "two_reader_boundary_confirmed": len(two_reader),
+            "two_reader_boundary_rows": two_reader,
+            "graph_only_residue": len(graph_only),
+            "graph_only_residue_rows": graph_only,
+            "graph_specific_residue_after_nulls": len(graph_specific),
+            "graph_specific_residue_rows": graph_specific,
+            "scope_change_declared": "two-reader boundary remains only classic_and_graph rows; graph-only rows are frequency-graph residues under audit, not two-reader confirmations",
+            "graph_baseline_audit": "label_shuffle + degree_preserving_rewire",
+        },
+        "rows": rows,
+    }
+
+    out = Path(args.out)
+    out.parent.mkdir(parents=True, exist_ok=True)
+    out.write_text(json.dumps(output, indent=2, sort_keys=True) + "\n", encoding="utf-8")
+    print(json.dumps(output["summary"], indent=2, sort_keys=True))
+    return output
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--scope", default="tools/data/boundary_denominator_prescan_full_20260509_1500.json")
+    parser.add_argument("--stability-audit", default="tools/data/boundary_bridge_stability_audit_20260515_1915.json")
+    parser.add_argument("--k-values", default="2,3,4")
+    parser.add_argument("--n-gaps-values", default="512,1024,2048")
+    parser.add_argument("--seeds", default="20260515,20260516,20260517")
+    parser.add_argument("--n-shuffle", type=int, default=32)
+    parser.add_argument("--label-nulls", type=int, default=64)
+    parser.add_argument("--rewire-nulls", type=int, default=64)
+    parser.add_argument("--rewire-swap-multiplier", type=int, default=8)
+    parser.add_argument("--margin-threshold", type=float, default=0.25)
+    parser.add_argument("--stable-threshold", type=float, default=0.75)
+    parser.add_argument("--seed", type=int, default=20260516)
+    parser.add_argument("--out", default="tools/data/boundary_graph_null_audit_20260516_0330.json")
+    args = parser.parse_args()
+    run(args)
+
+
+if __name__ == "__main__":
+    main()

tokens used
115,822
Eseguito il cycle e scritto il report richiesto:

- [agent_20260516_0330.md](/opt/MM_D-ND/tools/data/reports/agent_20260516_0330.md)
- [exp_boundary_graph_null_audit.py](/opt/MM_D-ND/tools/exp_boundary_graph_null_audit.py)
- [boundary_graph_null_audit_20260516_0330.json](/opt/MM_D-ND/tools/data/boundary_graph_null_audit_20260516_0330.json)

Risultante: `two_reader_boundary_confirmed = 1`, `graph_only_residue = 3`, ma dopo audit grafico solo 2 residui graph-only superano entrambi i null (`percolation`, `logistica_biforcazione_var_3.5699`). `reaction_diffusion` resta stabile nel grafo ma cade contro il rewiring degree-preserving, quindi non va promosso come residuo graph-specific.

Verifica completata: `python -m py_compile tools/exp_boundary_graph_null_audit.py`.
