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: 019e2cbe-141a-7190-b545-8cd356bae39d
--------
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_20260515_1745.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 un esperimento coerente con la tensione attiva e scrivo il report richiesto. Verifico anche le memorie cross-agent recenti perché in questo lab sono parte del boot.
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 e' in recupero controllato. Il seme/runtime puo' ancora contenere direzioni residue, ma l'autorita' attiva e' data dai closeout e dal safe field.
- 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 per il ritorno supervisionato. Il prossimo ciclo deve seguire la direzione viva del seme `VECTOR RESIDUE / prime_minus_mod6_z_vector(SR,L1,triple_var)`, citando la matrice selector come perimetro di autorita'.
- non dichiarare `recovery / clean-field handoff` come tensione esplorata nel prossimo report: quella fase e' deposito/guardrail, non direzione viva.

## 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, T=7, G=7, E=4, R=4
**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:

- 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;

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 punta a una direzione precisa: confine tra domini GUE e Poisson come terzo incluso operativo, con recupero già chiuso come guardrail. Ora leggo il seme e faccio girare il proiettore richiesto dal contesto prima di fissare la misura.
exec
/bin/bash -lc "sed -n '1,220p' tools/data/seme.json" in /opt/MM_D-ND
 succeeded in 0ms:
{
  "timestamp": "2026-05-15T17:17:24.848070",
  "piano": 118,
  "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 "sed -n '1,220p' shared_memory/README.md" in /opt/MM_D-ND
 succeeded in 0ms:
# shared_memory — Cross-agent rules e cristalli

> Cartella condivisa tra agenti che operano in `/opt/MM_D-ND/`:
> - **TM3-claude** (Claude Code, sessioni interattive con operatore)
> - **codex-lab** (Codex CLI, cycle agent notturno + run manuali)
> - **eventuali altri** (TM5, TM7, futuri cycle)
>
> Contiene regole permanenti, cristalli operativi, scoperte rilevanti che valgono
> per più di un agente. **Non** memoria operativa locale (quella resta in
> `~/.claude/projects/.../memory/` per TM3, in `~/.codex_lab/memories/` per codex).

## Pattern di scrittura

Ogni file:
- Filename: `<tipo>_<topic>_<data>.md` (es. `feedback_cycletest_2026-05-07.md`,
  `cristallo_gate_transferability_2026-05-07.md`)
- Frontmatter:
  ```yaml
  ---
  type: feedback | cristallo | regola | finding-cross-agent
  author: TM3 | codex-lab | TM7 | operatore
  date: YYYY-MM-DD
  scope: cross-agent | mm-dnd-only | lab-cycle-only
  ---
  ```
- Body: conciso. Se è regola → "Why" + "How to apply". Se è cristallo → contesto +
  insight + how it changes work.

## Pattern di lettura

- **TM3 (claude code)**: al boot di sessione su `/opt/MM_D-ND/`, leggi
  `MM_D-ND/.claude/CLAUDE.md` che adesso punta qui (sezione *Cross-agent rules*).
  Scansiona `ls -t shared_memory/*.md | head -10` (le più recenti) e integra in
  contesto.
- **codex-lab**: `/opt/MM_D-ND/AGENTS.md` punta qui. Codex legge AGENTS.md ad
  ogni cycle, AGENTS.md istruisce di leggere shared_memory.
- **Cleanup**: file > 90 giorni e non rilevanti vanno archiviati o rimossi
  (lo fa TM3 quando la cartella supera 50 file).

## Direzione bidirezionale

- TM3 → codex: TM3 scrive feedback/cristalli da sessioni operatore qui →
  codex li vede al cycle dopo via AGENTS.md.
- codex → TM3: codex durante un cycle scopre/cristallizza qualcosa di
  cross-agent → scrive qui → TM3 li vede al boot di sessione successiva.

Il flusso non sostituisce le memorie locali — *integra* per ciò che serve a tutti.

## Anti-pattern

- Duplicare qui regole che valgono SOLO per TM3 (vanno in
  `/root/.claude/projects/.../memory/`)
- Duplicare qui regole che valgono SOLO per codex-lab cycle (vanno in
  `/root/.codex_lab/memories/`)
- Scrivere note di lavoro temporanee (vanno nel campo vivo, non qui)

exec
/bin/bash -lc 'python tools/dnd_scenario.py --best' in /opt/MM_D-ND
 succeeded in 114ms:
codex
Il proiettore indica `TENS_SCALE_TRASCENDENZA_LIMITE` come massimo discriminante: il nodo vivo non è “phi conferma”, ma il fit che non converge e il confine di scala. Cerco gli strumenti già presenti per non duplicare metriche e per agganciare l’esperimento al registro osservabili.
exec
/bin/bash -lc "rg --files tools | rg 'exp_|observables|boundary|scale|vc|sturm|harper|gue|poisson'" in /opt/MM_D-ND
 succeeded in 0ms:
tools/exp_boundary_gue_poisson.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_geodesic_deviation_primes.py
tools/exp_psd_amplitude_scaling.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/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_quasiperiodic_vc_lattice_gate.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/observables_registry.py
tools/exp_two_channel_cross_domain.py
tools/exp_prime_persistent_blank_gate.py
tools/exp_beta_crossover.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_dR_brody_connection.py
tools/exp_bridge_order_denominator_gate.py
tools/exp_quasiperiodic_grammar_scale_gate.py
tools/test_gue_poisson_boundary.py
tools/exp_gap_label_repair_audit.py
tools/exp_markov_memory_by_gue_type.py
tools/exp_crossover_phase_test.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_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_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_vc_fit_model_gate.py
tools/exp_denominator_gate_transfer_matrix.py
tools/exp_3d_boundary_layers.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_coherence_length.py
tools/exp_spectral_rigidity.py
tools/exp_boundary_residual_beta_absent_audit.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_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_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/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_vc_fit_ready_scale_table.py
tools/exp_physical_sr_residue_bounce.py
tools/exp_ricci_desitter_0406.py
tools/exp_acf_stationarity.py
tools/exp_det_drift.py
tools/exp_spectral_2d.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/data/boundary_denominator_prescan_20260509_1409.json
tools/data/duality_scale_contrast_20260508.json
tools/data/vc_nonsturmian_label_null_gate_20260509_0819.json
tools/data/nonphi_sturmian_fixed_reader_gate_20260508_2019_seedcheck.json
tools/data/photonic_boundary_third_included_gate_20260515_1734.json
tools/data/quasiperiodic_vc_lattice_gate_20260515_1724.json
tools/data/gap_label_block_scale_gate_20260508_1805.json
tools/data/prime_vs_mod6_sr_boundary_20260514_0330.json
tools/data/boundary_denominator_prescan_20260509_1430.json
tools/data/boundary_two_axis_matrix_20260509_1532.json
tools/data/vc_label_preserving_swap_gate_20260509_0659.json
tools/data/vc_label_reachability_gate_20260509_0659.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1605_w4096.trace.jsonl
tools/data/exp_markov_psd_prediction.json
tools/data/blank_shell_scale_law_20260507_2203.json
tools/data/markov_memory_by_gue_type.json
tools/data/quasiperiodic_vc_curve_map_20260509_0330.json
tools/data/prime_vs_mod6_sr_boundary_20260513_0330.json
tools/data/semireal_boundary_transfer_gate_20260509_1516.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1458_w8192_dense.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/prime_vs_mod6_sr_boundary_20260514_1605_w16384.trace.jsonl
tools/data/vc_null_regression_gate_20260509_0637.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/vc_label_preserving_swap_gate_20260509_0652_deep89.json
tools/data/physical_sr_residue_bounce_20260514_1640_goe_gue_ncurve.json
tools/data/exp_det_drift_20260507_2042.json
tools/data/boundary_blank_null_audit_20260509_1430.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/exp_acf_stationarity.json
tools/data/quasiperiodic_grammar_scale_gate_20260514_1701.json
tools/data/exp_two_channel_psd.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1458_w4096.json
tools/data/vc_memory_crossing_gradient_20260509_0829.json
tools/data/exp_beta_crossover.json
tools/data/boundary_row_aligned_nonexact_audit_20260509_1538.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/prime_vs_mod6_sr_boundary_20260514_1458_w4096_dense.trace.jsonl
tools/data/vc_label_preserving_phase_bridge_20260509_0741.json
tools/data/perturbation_dimensionality_audit_scale0330.json
tools/data/exp_spectral_2d.json
tools/data/vc_unit_boundary_audit_20260509_1457.json
tools/data/prime_vs_mod6_sr_boundary_20260513_0330_seedcheck.json
tools/data/vc_fit_model_gate_20260509_1400.json
tools/data/markov_scale_function.json
tools/data/boundary_coherence.json
tools/data/vc_generator_class_scale_gate_20260509_0837.json
tools/data/boundary_mixture_gate_20260507_0330.json
tools/data/prime_vs_mod6_sr_boundary_20260514_0330_seedcheck.trace.jsonl
tools/data/vc_generator_class_direction_audit_20260509_0846.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1330_w2048.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1330_w1024.json
tools/data/prime_sr_persistent_boundary_20260512_0330_seedcheck.json
tools/data/vc_label_preserving_swap_gate_20260509_0652.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/scale_selective_perturbation.json
tools/data/boundary_shuffle_audit.json
tools/data/physical_sr_residue_bounce_20260514_1640_goe_gue_ncurve.trace.jsonl
tools/data/boundary_transition_taxonomy_13rows_20260509_1839.json
tools/data/exp_coherence_length.json
tools/data/boundary_blank_null_audit_residual_20260509_1500.json
tools/data/exp_psd_amp_scaling.json
tools/data/vc_fit_ready_scale_table_20260509_1337.json
tools/data/exp_two_channel_decomposition.json
tools/data/exp_acf_z6z_mechanism.json
tools/data/3d_boundary_layers.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_w2048.trace.jsonl
tools/data/quasiperiodic_vc_lattice_gate_20260508_2140.json
tools/data/prime_vs_mod6_sr_boundary_20260514_1458_w4096_dense.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/reports/exp_desitter_unification.json
tools/data/reports/exp_metric_tensor_diag_long.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.json
tools/data/reports/exp_dR_brody_connection.json
tools/data/boundary_mixture_gate_20260507_0330_seedcheck.json
tools/data/reports/exp_acf_decay_data.json
tools/data/reports/exp_psd_prime_gaps.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/prime_vs_mod6_sr_boundary_20260514_0330_seedcheck.json
tools/data/reports/exp_crossover_universality.json
tools/data/reports/exp_boundary_20260405_0825.json
tools/data/reports/exp_brody_crossover_20260405.json

exec
/bin/bash -lc 'ls -t tools/data/reports/agent_*.md | head -8' in /opt/MM_D-ND
exec
/bin/bash -lc "sed -n '1,220p' tools/observables_registry.py" in /opt/MM_D-ND
 succeeded in 0ms:
tools/data/reports/agent_20260515_1734.md
tools/data/reports/agent_20260515_1724.md
tools/data/reports/agent_20260515_1712.md
tools/data/reports/agent_20260515_1705.md
tools/data/reports/agent_20260515_1659.md
tools/data/reports/agent_20260515_1647.md
tools/data/reports/agent_20260515_1623.md
tools/data/reports/agent_20260514_1850.md

 succeeded in 0ms:
"""observables_registry.py — Source of Truth per gli observables del lab D-ND.

Cristallizzato 2026-05-06 dalla **consecutio del cycle agent_20260506_0625**:

> "What opens now: the lab needs an observable registry. Labels like SR
>  cannot travel between reports unless they bind to a function definition.
>  Without that, META flags are not philosophical: the same label can
>  silently change the object under test."

## Il problema che ha creato il registry

Il cycle 06:25 ha auto-falsificato il finding del cycle 03:30 ("secondo asse
GUE") e nel farlo ha trovato **collision di nomi observable** tra script:

- `SR` in `exp_selective_layer_decoupling.py` = `spacing_ratio` (mean min/max
  ratio of consecutive gaps) — convention dominante (~6 script)
- `SR` in `exp_scale_selective_perturbation.py` = `spectral_rigidity(gaps)`
  (Δ₃(L) rigidity) — variante usata SOLO in 1 script

- `triple_var` in 3 script = `np.var(triple_sums)` (raw) — convention dominante
- `triple_var` in `exp_perturbation_dimensionality_audit.py` =
  `np.var(triples) / np.var(gaps)` (normalizzato) — variante in 1 script

Il lab autonomo che compara report tra script con osservabili "stesso nome,
funzione diversa" stava confrontando mele con arance.

## La soluzione (minimal, non invasiva)

Questo registry stabilisce il **nome canonico**: ciò che la maggioranza degli
script chiama già `SR`/`triple_var`/etc. Le varianti restano disponibili ma
con nomi ESPLICITI (`SR_local_rigidity`, `triple_var_normalized`) per evitare
mascheramento semantico.

## Come usarlo

```python
from observables_registry import OBSERVABLES_CANONICAL, OBSERVABLES_REGISTRY_VERSION

# Compute canonical observable suite for a sequence of gaps
results = {name: fn(gaps) for name, fn in OBSERVABLES_CANONICAL.items()}

# Or import individual canonical observable
from observables_registry import SR, triple_var, L1, L2, SR2

# For variants, import explicitly with disambiguating name
from observables_registry import SR_local_rigidity, triple_var_normalized
```

## Convention per i report

Ogni report agent (cycle) che usa observables DEVE includere nel suo header:

```
observables_registry: 1.0.0-2026-05-06
observables_used: [SR, SR2, L1, L2, triple_var]
```

Cycle che mescola canonical + variant DEVE indicare entrambi:

```
observables_used: [SR, SR_local_rigidity, ...]
```

Senza questo, i confronti cross-cycle sono inattendibili.

## Versioning

Cambiare una definizione canonica = bump del registry version e nota nel
changelog. Le definizioni canoniche sono **immutabili dentro una versione**.
"""
from __future__ import annotations

import numpy as np


OBSERVABLES_REGISTRY_VERSION = "1.0.0-2026-05-06"


# ─── Canonical observables (convention dominante nel codebase 2026-05-06) ───

def SR(gaps: np.ndarray) -> float:
    """**SR — Spacing Ratio** (canonical).

    Mean of `min(g_i, g_{i+1}) / max(g_i, g_{i+1})` over consecutive gaps.
    Range: (0, 1]. GUE → ~0.60. Poisson → ~0.39. Picket-fence → 1.

    NOTE: questa è la convention dominante in 6+ script del lab.
    Per la variante "local spectral rigidity Δ₃(L)" usare `SR_local_rigidity`.
    """
    if len(gaps) < 2:
        return 0.0
    s, s1 = gaps[:-1], gaps[1:]
    r = np.minimum(s, s1) / np.maximum(s, s1)
    r = r[np.isfinite(r) & (r > 0)]
    return float(np.mean(r)) if len(r) else 0.0


def SR2(gaps: np.ndarray) -> float:
    """**SR2 — Next-nearest Spacing Ratio** (canonical).

    Mean of `min(g_i, g_{i+2}) / max(g_i, g_{i+2})` skipping one gap.
    Probes lag-2 spacing structure.
    """
    if len(gaps) < 3:
        return 0.0
    s, s2 = gaps[:-2], gaps[2:]
    r = np.minimum(s, s2) / np.maximum(s, s2)
    r = r[np.isfinite(r) & (r > 0)]
    return float(np.mean(r)) if len(r) else 0.0


def L1(gaps: np.ndarray) -> float:
    """**L1 — Lag-1 Autocorrelation** (canonical).

    Standard ACF at lag 1 of the gap sequence.
    """
    if len(gaps) < 3:
        return 0.0
    g = gaps - np.mean(gaps)
    c0 = float(np.mean(g ** 2))
    if c0 <= 1e-15:
        return 0.0
    return float(np.mean(g[:-1] * g[1:]) / c0)


def L2(gaps: np.ndarray) -> float:
    """**L2 — Lag-2 Autocorrelation** (canonical)."""
    if len(gaps) < 4:
        return 0.0
    g = gaps - np.mean(gaps)
    c0 = float(np.mean(g ** 2))
    if c0 <= 1e-15:
        return 0.0
    return float(np.mean(g[:-2] * g[2:]) / c0)


def triple_var(gaps: np.ndarray) -> float:
    """**triple_var — Variance of consecutive gap triples** (canonical).

    Variance of `g_i + g_{i+1} + g_{i+2}` over the sequence (RAW, no
    normalization). Convention used in 3+ scripts. For the normalized
    version (variance ratio `var(triples) / var(gaps)`) use
    `triple_var_normalized`.
    """
    if len(gaps) < 3:
        return 0.0
    t = gaps[:-2] + gaps[1:-1] + gaps[2:]
    return float(np.var(t))


# Set canonico per uso "compute all" da report
OBSERVABLES_CANONICAL: dict[str, callable] = {
    "SR": SR,
    "SR2": SR2,
    "L1": L1,
    "L2": L2,
    "triple_var": triple_var,
}


# ─── Variants (esplicitamente nominate, no collision con canonical) ───

def SR_local_rigidity(gaps: np.ndarray, L: int = 10) -> float:
    """**SR_local_rigidity — Δ₃(L) Spectral Rigidity** (variant).

    Different observable than canonical `SR` (spacing ratio). Measures the
    average squared deviation of the cumulative spacing function from the
    best-fit straight line in a window of size L.

    Originated from `exp_scale_selective_perturbation.py` where it was
    locally named `SR` — registered here as `SR_local_rigidity` to avoid
    collision with canonical spacing-ratio definition.

    Use when explicitly studying spectral rigidity, NOT as alias for SR.
    """
    if len(gaps) < 5:
        return 0.0
    cumulative = np.cumsum(gaps)
    if cumulative[-1] <= 1e-15:
        return 0.0
    cumulative = cumulative / cumulative[-1] * len(cumulative)
    n = np.arange(1, len(cumulative) + 1, dtype=float)
    window = int(min(L * len(gaps) / cumulative[-1], len(gaps) // 2))
    if window < 5:
        return 0.0
    residuals = []
    for start in range(0, len(cumulative) - window, max(1, window // 2)):
        end = start + window
        x = n[start:end]
        y = cumulative[start:end]
        a, b = np.polyfit(x, y, 1)
        residuals.append(np.mean((y - (a * x + b)) ** 2))
    return float(np.mean(residuals)) if residuals else 0.0


def triple_var_normalized(gaps: np.ndarray) -> float:
    """**triple_var_normalized — Variance of triples / variance of gaps** (variant).

    Originated from `exp_perturbation_dimensionality_audit.py` where it was
    locally named `triple_var` — registered here as `triple_var_normalized`
    to avoid collision with canonical raw triple variance.

    Use when explicitly studying triple-variance scaling relative to
    single-gap variance, NOT as alias for triple_var.
    """
    if len(gaps) < 3:
        return 0.0
    triples = gaps[:-2] + gaps[1:-1] + gaps[2:]
    v = float(np.var(gaps))
    if v <= 1e-15:
        return 0.0
    return float(np.var(triples) / v)


# Set varianti, importabile esplicitamente
OBSERVABLES_VARIANTS: dict[str, callable] = {
    "SR_local_rigidity": SR_local_rigidity,
    "triple_var_normalized": triple_var_normalized,
}


exec
/bin/bash -lc "sed -n '1,220p' tools/data/reports/agent_20260515_1734.md" in /opt/MM_D-ND
 succeeded in 0ms:
# Agent Report - Photonic Boundary Third Included Gate
**Date**: 2026-05-15 17:34  
**Piano**: 118  
**Tension explored**: BOUNDARY / COMP_DOMAIN_PHOTONIC_TRASCENDENZA_LIMITE  
**verdict**: CONSTRAINT - il multistrato fotonico separa `phi` dal random sullo stopband, ma non chiude il terzo incluso congiunto perche' l'entropia spettrale eccede entrambi i poli.  
observables_used: [stopband_fraction, spectral_entropy, peak_spacing_r, T_mean, T_median, logT_mean, logT_std, highband_fraction, peak_count]  
**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.

## Respiro fuori-tempo

- **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.
- **Dipolo / punto-zero**: ordine periodico / disordine random; punto-zero = trasmissione del multistrato prima della classificazione statistica, dove lo stack puo' essere stopband, passband o intermedio.
- **Piano superiore**: geometria dei campi e bicono-dipoli; il confine non e' etichetta GUE/Poisson ma stato di trasporto del campo elettromagnetico.
- **Operatori laterali scelti**: transfer matrix ottica per il ritorno fisico; boundary operator per tenere il denominatore riga-per-riga; spectrum-preserving style control come random bilanciato a densita phi.
- **Contaminazione cognitiva**: CE-0001 / KSAR usato come reiterazione del deposito 17:24 senza continuare `V_c`: portare il confine in un setup fisico. CE-0038 usato come equilibrio tra estremi: il punto-zero e' il segmento periodico-random, non la media dei valori.
- **Proto-ipotesi**: se il confine e' terzo incluso operativo, in un multistrato fotonico `phi` deve stare tra ordine periodico e random su piu osservabili di trasporto; se solo una osservabile sta tra i poli, il confine e' fisico ma non chiuso.
- **Proiezione**: misuro trasmissione su lambda, N e fase; confronto `phi` con metallic controls, periodico AB e random bilanciato.

## Aderenza alla direzione

- `relation`: follows_direction
- `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`.
- `not_drift`: non riapre prime/mod6, non usa freshness selector come autorita, non promuove TRASCENDENZA_LIMITE per score del proiettore, non fittta `V_c`; usa `COMP_DOMAIN_PHOTONIC_TRASCENDENZA_LIMITE` solo come sorgente da falsificare nel perimetro BOUNDARY.

## Claim Under Test

> Il multistrato fotonico `phi` realizza un terzo incluso operativo se la sua trasmissione resta tra periodico AB e random bilanciato su stopband ed entropia spettrale, separandosi dal random.

## Question

Nel ritorno fisico fotonico, `phi` produce un confine congiunto tra ordine e disordine oppure conserva solo una componente di confine mentre un'altra osservabile rompe il bicono?

## Ritorno fisico

- **Punto fisico sorgente**: confine statistico GUE/Poisson come attrito tra repulsione spettrale e indipendenza.
- **Attraversamento matematico**: sequenze Sturmian/metallic e random bilanciato proiettate in matrici di trasferimento ottiche.
- **Punto fisico di ritorno**: trasmissione di un multistrato dielettrico 1D quarter-wave a indici `n_A=1.0`, `n_B=1.7`, misurabile come spettro di stopband/passband.
- **Relazione nuova**: il confine puo' diventare stato di trasporto EM, ma deve sopravvivere su piu osservabili e non solo sullo stopband.
- **Osservabile/test fisico possibile**: fabbricare o simulare stack phi/periodico/random con stesso numero di layer e densita, poi confrontare frazione stopband ed entropia dello spettro trasmissivo.
- **Se fallisce**: `ritorno_fisico_parziale`; resta constraint sul ponte TxE/QxG, non scoperta fisica promuovibile.

## Experiment Design

- `python tools/dnd_scenario.py --best` eseguito: segnala `TENS_SCALE_TRASCENDENZA_LIMITE`, subordinato al campo BOUNDARY e ai selector `SAFE_AS_AUTHORITY`.
- `python -m py_compile tools/exp_photonic_boundary_third_included_gate.py` completato.
- Run: `python tools/exp_photonic_boundary_third_included_gate.py --out tools/data/photonic_boundary_third_included_gate_20260515_1734.json`.
- Perimetro deterministico: 12 righe per phi, silver, bronze e periodic_ab.
- Perimetro random: 144 righe balanced random a densita phi.
- La misura serve la combo perche' separa confine come trasporto fisico da confine come sola etichetta statistica.

## Results

| domain | rows | stopband median | entropy median | peak_spacing_r median |
|---|---:|---:|---:|---:|
| periodic_ab | 12 | 0.290 | 0.022 | 0.809 |
| phi | 12 | 0.488 | 0.062 | 0.561 |
| silver | 12 | 0.492 | 0.041 | 0.571 |
| bronze | 12 | 0.525 | 0.135 | 0.526 |
| balanced_random_phi_density | 144 | 0.618 | 0.030 | 0.527 |

Classification:

| observable | result |
|---|---|
| third_included_photonic_boundary | false |
| phi stopband position | between periodic and random |
| phi entropy position | above periodic and random |
| phi vs random stopband delta | -0.131 |
| phi vs random entropy delta | +0.032 |

## Key Findings

1. **Verificato**: `phi` e' intermedio nello stopband: periodic 0.290, phi 0.488, random 0.618.
2. **Verificato**: `phi` non e' intermedio nell'entropia spettrale: periodic 0.022, random 0.030, phi 0.062.
3. **Verificato**: `peak_spacing_r` non isola `phi`: phi 0.561, silver 0.571, bronze 0.526, random 0.527.
4. **Inferito**: il ponte fotonico conserva una componente di confine fisico ma non chiude il terzo incluso congiunto. Il nodo regressivo e' la scelta di osservabile unica: stopband da solo produce una falsa chiusura.

## Verdict

**CONSTRAINT**.

Il ritorno fotonico e' parziale: `phi` separa ordine e random sullo stopband, ma l'entropia spettrale rompe il bicono. Il claim valido diventa: il confine BOUNDARY trasferisce in fotonica solo come componente osservabile-specifica; non come terzo incluso fisico chiuso nel perimetro misurato.

## Bicono della scoperta

- **Due radici**: stopband intermedio / entropia fuori segmento.
- **Singolare**: lo spettro di trasmissione prima che una singola metrica lo riduca a classe.
- **Invariante di passaggio**: denominatore `N,phase,generator,lambda-grid` e obbligo di almeno due osservabili di trasporto.
- **Campo di possibilita**: possibile = progettare un test fotonico BOUNDARY con stopband ed entropia insieme; non-possibile = promuovere `phi` fotonico come terzo incluso usando solo `T_mean` o stopband.

## Consecutio

Il prossimo ciclo puo' usare questo deposito come filtro: ogni ritorno fisico del confine deve passare da due osservabili non collineari. La strada utile non e' rifare `V_c`, ma costruire un ponte TxE/QxG dove stopband e complessita spettrale concordano o divergono in modo dichiarato.

## Ricadute pratiche

ssp_value: yes. Lo script `exp_photonic_boundary_third_included_gate.py` e' riusabile per testare stack ottici con generatori, fasi, indici e griglie lambda diverse prima di trasformare il risultato in demo o visualizzazione.

## Telemetria

- No API paid run: `ANTHROPIC_API_KEY` e `OPENAI_API_KEY` non presenti nell'ambiente.
- Preflight post-report: `python tools/lab_preflight_agent.py --cycle 20260515_1734 --json` => `ARTIFACT_USEFUL_NOT_PUBLISHABLE`, recommended_action=`KEEP_ARTIFACT_STOP_REPORT_PROMOTION`, stable_anchor=`20260515_1712`.
- Nessun update del seme.
- Nessun runtime pointer mutation.
- Nessuna promozione e nessun public sync.

## Files

- `tools/exp_photonic_boundary_third_included_gate.py`
- `tools/data/photonic_boundary_third_included_gate_20260515_1734.json`
- `tools/data/preflight/preflight_20260515_1734.json`
- `tools/data/preflight/preflight_20260515_1734.md`
- `tools/data/reports/agent_20260515_1734.md`

exec
/bin/bash -lc "sed -n '1,220p' tools/data/reports/agent_20260515_1724.md" in /opt/MM_D-ND
 succeeded in 0ms:
# Agent Report - Quasiperiodic Vc Lattice Extension
**Date**: 2026-05-15 17:24  
**Piano**: 118  
**Tension explored**: TENS_SCALE_TRASCENDENZA_LIMITE / TRASCENDENZA_LIMITE  
**verdict**: CONSTRAINT - `V_c` non sostiene una legge di scala unica nel perimetro esteso; resta reticolo di attraversamento phase-sensitive separato dal controllo random.
observables_used: [vc, distinct_vc, repeat_rate, mode_rate, median_vc, min_vc, max_vc, phi_phase0_values]
**observable_contract**: claim=`V_c(phi)` converge a 1.0 solo se il perimetro esteso riduce fase e taglia a una traiettoria comune; observable=valori `vc` e compressione reticolare (`distinct_vc`, `repeat_rate`, `mode_rate`) per phi/silver/bronze/random; operator=`tools/exp_quasiperiodic_vc_lattice_gate.py`; generator=sequenze Sturmian phi/silver/bronze + balanced random a densita phi; denominator=N={89,144,233,377,610,987}, phase={0,0.125,0.25,0.375,0.5,0.625,0.75,0.875}, random_trials=8, V grid 0.5..3.0 step 0.025, threshold r<0.5; non_possible=promuovere `V_c(phi)->1` o fit power-law comune quando phase0 alterna valori e il dominio completo conserva 20 valori distinti; not_tested=limite asintotico oltre N=987, griglia V piu fine, modello fisico di Aubry-Andre, gap-label core, GUE/Poisson universalita.

## Respiro fuori-tempo

- **Combo**: A2 confine det=-1 + A9 terzo incluso + A11 combo + QxG continuo/discreto + tensione `TENS_SCALE_TRASCENDENZA_LIMITE`.
- **Dipolo / punto-zero**: curva continua di scala / reticolo discreto di attraversamento. Punto-zero = la prima caduta di `r(V)` sotto 0.5 prima che venga interpretata come fit, attrattore o classe.
- **Piano superiore**: topologia assiomatica del bordo; il confine e' una soglia di attraversamento su un reticolo di fasi, non un numero isolato.
- **Operatori laterali scelti**: boundary condition per rendere la fase parte del denominatore; mobility edge come forma fisica del passaggio spettrale; spectrum-preserving style control come controllo random a densita preservata.
- **Contaminazione cognitiva**: CE-0001 / KSAR usato come reiterazione del kernel 20260509: non rifittare `V_c`, ripetere il deposito con denominatore piu esplicito. CE-0038 usato come equilibrio tra estremi: il punto-zero non e' media tra phi e random, e' la riga `N,phase` prima della classificazione.
- **Proto-ipotesi**: il fallimento del fit non chiede una famiglia parametrica migliore; indica che `V_c` e' coordinata di attraversamento dipendente da fase e generatore. Se la fase resta visibile a N esteso, il claim di convergenza globale cade.
- **Proiezione**: estendo `exp_quasiperiodic_vc_lattice_gate.py` a sei taglie e otto fasi, mantenendo silver/bronze e random bilanciato come controlli.

## Aderenza alla direzione

- `relation`: deliberate_counter_perimeter
- `why`: il campo vivo espone due segnali in tensione: l'anchor stabile 17:12 chiude prime/mod6, mentre `dnd_scenario.py --best` e il campo basso indicano `TENS_SCALE_TRASCENDENZA_LIMITE` come massimo discriminante. Questo ciclo usa il secondo come contro-perimetro dichiarato, non come promozione della direzione normale.
- `not_drift`: non riapre prime/mod6 come discovery, non usa `gap_ratio`, non promuove GUE/Poisson, non cerca una nuova formula power-law; usa `V_c` solo come deposito vincolante. Il preflight 17:24 classifica comunque il ciclo `DRIFT_STOP`, quindi il risultato resta vault/constraint fino a nuova autorita.

## Claim Under Test

> Nel perimetro quasiperiodico esteso, `V_c(phi)` e' una traiettoria di scala verso 1 solo se fase e taglia collassano a una famiglia comune; altrimenti `V_c` e' un reticolo di attraversamento, non un attrattore.

## Question

Quando si estende il probe `V_c` a N=987 e a otto fasi, la sequenza phi concentra l'attraversamento verso 1, oppure resta phase-sensitive e distinta dal random bilanciato?

## Ritorno fisico

- **Punto fisico sorgente**: transizione spettrale in Hamiltoniane tight-binding quasiperiodiche binarie, con `r(V)` come indicatore locale di repulsione/indipendenza.
- **Attraversamento matematico**: soglia `r(V)<0.5` su sequenze Sturmian phi/silver/bronze e controllo random a stessa densita di phi.
- **Punto fisico di ritorno**: misura di sensibilita alle condizioni al bordo/fase in modelli quasiperiodici; l'analogo fisico e' un esperimento numerico o fotonico dove la fase del potenziale non puo essere mediata prima del claim di transizione.
- **Relazione nuova**: il confine non torna come valore critico unico, ma come reticolo `N x phase x generator` che decide se una transizione e' leggibile.
- **Osservabile/test fisico possibile**: ripetere su modello Aubry-Andre/Fibonacci con fase controllata e confrontare distribuzione dei critical couplings, non solo media o fit.
- **Se fallisce**: `ritorno_fisico_parziale`; questo ciclo resta vincolo operativo per il denominatore dei futuri test fisici, non scoperta fisica autonoma.

## Experiment Design

- `python tools/dnd_scenario.py --best` eseguito: massimo discriminante `TENS_SCALE_TRASCENDENZA_LIMITE`.
- `python -m py_compile tools/exp_quasiperiodic_vc_lattice_gate.py` completato.
- Run: `python tools/exp_quasiperiodic_vc_lattice_gate.py --ns 89,144,233,377,610,987 --phases 0,0.125,0.25,0.375,0.5,0.625,0.75,0.875 --v-min 0.5 --v-max 3.0 --v-step 0.025 --threshold 0.5 --random-trials 8 --seed 202605151724 --out tools/data/quasiperiodic_vc_lattice_gate_20260515_1724.json`.
- Perimetro deterministico: 48 righe per ciascun generatore phi, silver, bronze.
- Perimetro random: 384 righe balanced random a densita phi.
- La misura serve la combo perche' distingue confine come reticolo di fase da confine come valore critico aggregato.

## Results

| domain | count | distinct_vc | repeat_rate | mode_rate | median | min | max |
|---|---:|---:|---:|---:|---:|---:|---:|
| phi | 48 | 20 | 0.583 | 0.125 | 1.175 | 0.650 | 1.650 |
| silver | 48 | 24 | 0.500 | 0.083 | 1.200 | 0.975 | 2.150 |
| bronze | 48 | 24 | 0.500 | 0.104 | 1.275 | 0.925 | 1.800 |
| balanced_random_phi_density | 384 | 21 | 0.945 | 0.680 | 0.500 | 0.500 | 1.150 |

Phi phase0 values:

| N | phase | V_c |
|---:|---:|---:|
| 89 | 0.0 | 1.025 |
| 144 | 0.0 | 0.675 |
| 233 | 0.0 | 0.950 |
| 377 | 0.0 | 0.675 |
| 610 | 0.0 | 0.900 |
| 987 | 0.0 | 0.675 |

Phi median by N across eight phases:

| N | median V_c | min | max | distinct |
|---:|---:|---:|---:|---:|
| 89 | 1.313 | 0.675 | 1.375 | 5 |
| 144 | 1.150 | 0.675 | 1.650 | 7 |
| 233 | 1.225 | 0.725 | 1.350 | 5 |
| 377 | 1.113 | 0.650 | 1.300 | 6 |
| 610 | 1.238 | 0.900 | 1.350 | 5 |
| 987 | 1.088 | 0.675 | 1.275 | 6 |

## Key Findings

1. **Verificato**: `phi` non collassa a `V_c=1`. Nel perimetro esteso ha 20 valori distinti su 48 righe, mediana 1.175 e range 0.650-1.650.
2. **Verificato**: la sequenza phase0 non e' monotona verso 1: 1.025, 0.675, 0.950, 0.675, 0.900, 0.675.
3. **Verificato**: silver e bronze non producono un polo netto opposto a phi; hanno mediane vicine ma range piu alto, quindi il discriminante non e' metallic-mean nominale.
4. **Verificato**: il random bilanciato non replica il reticolo phi; collassa spesso al bordo inferiore della griglia (`mode_rate=0.680` a V=0.5) pur avendo 21 valori distinti.
5. **Inferito**: il nodo regressivo del fit fallito e' `phase/generator denominator`, non scelta di modello. La prossima misura deve trattare la fase come parte atomica del claim.

## Verdict

**CONSTRAINT**.

`V_c(phi)->1` non regge come claim operativo nel perimetro misurato. La formulazione valida e': `V_c` e' una coordinata reticolare di attraversamento `N x phase x generator`; phi conserva struttura lontana dal random bilanciato, ma non converge a un attrattore unitario e non separa silver/bronze tramite una legge di scala unica.

## Bicono della scoperta

- **Due radici**: valore critico continuo / reticolo discreto di attraversamento.
- **Singolare**: la soglia `r(V)<0.5` prima che diventi fit o classe.
- **Invariante di passaggio**: denominatore `N,phase,generator` come parte atomica di ogni claim su `V_c`.
- **Campo di possibilita**: possibile = progettare un test fisico/fotonico con fase controllata e distribuzione dei critical couplings; non-possibile = usare `V_c` aggregato o phase0 come prova di convergenza globale.

## Consecutio

Il prossimo ciclo non deve cercare un nuovo fit per `V_c`. Deve decidere se il reticolo di attraversamento trasferisce a un modello fisico piu canonico, con fase controllata e baseline nota: Aubry-Andre/Fibonacci, curva di localizzazione o trasmissione fotonica. Se non viene portato in quel perimetro, resta vincolo matematico sul denominatore.

## Ricadute pratiche

ssp_value: yes. Il deposito aggiorna l'uso dello strumento `exp_quasiperiodic_vc_lattice_gate.py`: i futuri report devono riportare fase, taglia, generatore e griglia prima di qualsiasi fit o claim di convergenza.

## Telemetria

- No API paid run: `ANTHROPIC_API_KEY` e `OPENAI_API_KEY` non presenti nell'ambiente.
- Preflight post-report: `python tools/lab_preflight_agent.py --cycle 20260515_1724 --json` => `DRIFT_STOP`, recommended_action=`STOP_AND_RETURN_TO_STABLE_ANCHOR`, stable_anchor=`20260515_1712`.
- Nessun update del seme.
- Nessun runtime pointer mutation.
- Nessuna promozione e nessun public sync.
- Worktree gia dirty prima del ciclo; ignorate modifiche non correlate.

## Files

- `tools/exp_quasiperiodic_vc_lattice_gate.py`
- `tools/data/quasiperiodic_vc_lattice_gate_20260515_1724.json`
- `tools/data/preflight/preflight_20260515_1724.json`
- `tools/data/preflight/preflight_20260515_1724.md`
- `tools/data/reports/agent_20260515_1724.md`

exec
/bin/bash -lc "sed -n '1,220p' tools/data/reports/agent_20260515_1659.md" in /opt/MM_D-ND
 succeeded in 0ms:
# Agent Report - Selector Authority Matrix
**Date**: 2026-05-15 16:59  
**Piano**: 117  
**Tension explored**: recovery / clean-field handoff  
**verdict**: CONSTRAINT - the explicit selector matrix is ready; freshness selectors remain blocked as active authority.  
observables_used: [selector_verdict, active_authority_failures, legacy_freshness_blocked_as_authority, active_stale_hits, field_coherence_status, lab_health_warning, blocked_direction_count]  
**observable_contract**: claim=normal Lab direction can reopen only through explicit authority selectors, not through newest-file or last-row selectors; observable=selector-by-selector verdicts plus stale-hit and coherence checks; operator=`tools/selector_authority_matrix.py`, `tools/stale_field_source_map.py`, `tools/field_coherence_preflight.py`, `tools/lab_preflight_agent.py`; generator=read-only recovery preflight; denominator=10 selector rows in the authority matrix plus live generated-field surfaces scanned by the preflight tools; non_possible=claiming direction from mtime, newest evolution, last evaluator row, or `lab_health`; not_tested=scientific claim, prime/mod6 residue, public sync, cron restart, seme mutation.

## Respiro fuori-tempo

- **Combo**: A2 confine + A9 terzo incluso + A14 cascata + recovery/clean-field handoff + source-selection manifest.
- **Dipolo / punto-zero**: autorita esplicita / freschezza apparente. Punto-zero: la riga selector, dove una sorgente puo essere letta senza diventare direzione.
- **Piano superiore**: grafo operativo della conoscenza; la misura riguarda il flusso di autorita tra superfici runtime.
- **Operatori laterali scelti**: `boundary operator` per separare contesto e direzione; `graph cut` per isolare i selector legacy; `proof trace vs output trace` per confrontare matrice, coherence e preflight.
- **Contaminazione cognitiva**: KSAR usato come reiterazione del recovery kernel: esecuzione minima, deposito, matrice. CE-none: il campo non espone una voce CE concreta utile al selector audit; inventarla renderebbe semantico un ciclo operativo.
- **Proto-ipotesi**: un selector e sicuro come autorita solo se seleziona righe ammesse o overlay esplicito; un selector utile come diagnostica diventa non-possibile come direzione quando sceglie recovery evidence o branch bloccati.
- **Proiezione**: la proto-ipotesi diventa falsificabile riga-per-riga: ogni selector riceve selected cycle, status, role e verdict.

## Aderenza alla direzione

- `relation`: follows_direction
- `why`: il campo vivo dichiara recovery / clean-field handoff; i report 16:23 e 16:47 hanno lasciato come consecutio la sostituzione dei selector per freschezza con un manifest esplicito.
- `not_drift`: il ciclo non riapre prime/mod6, `V_c`, gap ratio, grammatica Sturmian, Anderson 3D o promozione pubblica. Il domandatore 16:59 ha generato `SCALE_TRASCENDENZA_LIMITE`, ma resta output stale/pre-closeout e non diventa direzione.

## Claim Under Test

> Il Lab puo usare solo selector con verdict `SAFE_AS_AUTHORITY` come autorita attiva; i selector legacy per freschezza possono restare diagnostici solo se bloccati riga-per-riga come autorita.

## Question

Quali selector possono decidere la prossima direzione del Lab, e quali devono essere vietati come autorita anche se leggono file recenti?

## Ritorno fisico

non_applicabile. Il ciclo parte da un attrito operativo di recovery, non da una tensione fisica. La forma `fisico A -> matematica -> fisico B` resta preservata come vincolo metodologico per i cicli scientifici successivi, ma qui non viene usata come ponte attivo.

## Experiment Design

- Eseguito `python tools/dnd_scenario.py --best`: il tool punta ancora a tensioni `TRASCENDENZA_LIMITE`, ma il campo le marca stale rispetto alla direzione recovery.
- Eseguito `python tools/selector_authority_matrix.py`: costruisce matrice read-only con selector, selected cycles, status, role e verdict.
- Eseguito `python tools/stale_field_source_map.py`: verifica hit stale attivi sulle superfici vive.
- Eseguito `python tools/field_coherence_preflight.py --mode B --overlay /opt/MM_D-ND/tools/data/agent_field_entry_supervised.md`: verifica coerenza mode B.
- Eseguito `python tools/lab_preflight_agent.py --cycle 20260515_1647 --json`: classifica il recovery report precedente.
- Nessun overwrite di `agent_field_live.md`, nessun update del seme, nessun restart cron, nessun public sync.

## Results

| observable | result | source |
|---|---:|---|
| selector matrix decision | `SELECTOR_AUTHORITY_MATRIX_READY` | `selector_authority_matrix_20260515_170056` |
| selector rows | 10 | matrix |
| active authority failures | 0 | matrix |
| legacy freshness selectors blocked as authority | 3 | matrix |
| legacy rows blocked | `recent_agent_reports_by_mtime_top_3`, `latest_evolution_by_mtime`, `last_valutatore_row` | matrix |
| safe active/report selectors | `reports/latest.md`, `authority_report_manifest`, `authority_evolution_manifest`, `authority_valutatore_row`, `explicit_safe_overlay` | matrix |
| diagnostic/context selectors | `lab_health_run_timestamp`, `graph_completion_latest` | matrix |
| blocked direction count encoded | 9 | matrix |
| active stale hits | 0 | `stale_field_source_map_20260515_170056` |
| field coherence mode B | `PASS_WITH_WARNINGS` | `field_coherence_B_20260515_170056` |
| field coherence failures | 0 | coherence |
| field coherence warning | `lab_health_run_timestamp_not_allowed:20260515_1647` | coherence |
| lab health status | `completed`, run_timestamp=`20260515_1647` | `tools/data/lab_health.json` |
| preflight 1647 classification | `DRIFT_STOP` | `preflight_20260515_1647` |
| self preflight 1659 classification | `REVIEW_REQUIRED` | `preflight_20260515_1659` |

## Key Findings

1. **Verificato**: nessun selector ammesso come active authority fallisce; `active_authority_failures=[]`.
2. **Verificato**: tre selector per freschezza sono bloccati come autorita: report recenti per mtime, evolution piu recente per mtime, ultima riga valutatore.
3. **Verificato**: `lab_health` resta diagnostico e non puo decidere direzione, perche registra `20260515_1647` come recovery evidence.
4. **Verificato**: il campo mode B passa senza failure e con un solo warning; gli hit stale attivi sono zero.
5. **Verificato**: il report 16:59 stesso richiede review prima del prossimo ciclo; non promuove direzione scientifica.
6. **Inferito**: il prossimo passaggio valido e consumare la matrice nel selector operativo o nel supervised entry path, non eseguire un nuovo ciclo scientifico normale.

## Verdict

**CONSTRAINT**.

La matrice di autorita e pronta. Il Lab puo rivendicare solo le righe `SAFE_AS_AUTHORITY` come direzione attiva: latest report su 1330, manifest report, manifest evolution, evaluator row ammessa e safe overlay esplicito. I selector legacy non sono eliminati: restano diagnostica, ma diventano non-possibili come autorita quando selezionano recovery evidence o branch bloccati.

## Bicono della scoperta

- **Due radici**: selector esplicito / selector per freschezza.
- **Singolare**: la decisione di autorita prima del contenuto scientifico.
- **Invariante di passaggio**: ogni claim sulla direzione deve citare la riga selector che lo autorizza.
- **Campo di possibilita**: possibile = supervised entry o runtime selector basato su matrice; non-possibile = normal cycle che legge newest report, newest evolution, last evaluator row o `lab_health` come direzione.

## Consecutio

Integrare `tools/data/preflight/selector_authority_matrix_latest.json` o una matrice equivalente nel nodo che seleziona la direzione del prossimo ciclo. Il prossimo report puo rivendicare solo righe `SAFE_AS_AUTHORITY`; i selector legacy vanno nominati come bloccati riga-per-riga, non per inferenza generale.

## Ricadute pratiche

ssp_value: yes. Il risultato e uno strumento operativo riusabile per riaprire il Lab senza far decidere la direzione a mtime, last-row o health diagnostics.

## Files

- `tools/selector_authority_matrix.py`
- `tools/data/preflight/selector_authority_matrix_20260515_165915.json`
- `tools/data/preflight/selector_authority_matrix_20260515_170056.json`
- `tools/data/preflight/selector_authority_matrix_latest.json`
- `docs/operations/selector_authority_matrix_20260515_170056.md`
- `tools/data/preflight/stale_field_source_map_20260515_170056.json`
- `tools/data/preflight/field_coherence_B_20260515_170056.json`
- `tools/data/preflight/preflight_20260515_1647.json`
- `tools/data/preflight/preflight_20260515_1659.json`
- `tools/data/reports/agent_20260515_1659.md`

exec
/bin/bash -lc "sed -n '1,240p' tools/exp_photonic_boundary_third_included_gate.py" in /opt/MM_D-ND
 succeeded in 0ms:
#!/usr/bin/env python3
"""
Photonic boundary third-included gate.

This tool projects the GUE/Poisson boundary direction into a physical return:
a 1D dielectric multilayer. It does not classify the optical spectrum as GUE or
Poisson. It asks whether the boundary survives as an intermediate transmission
state between ordered metallic Sturmian stacks and balanced random stacks.
"""

from __future__ import annotations

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

import numpy as np


PHI = (1 + np.sqrt(5)) / 2
SILVER = 1 + np.sqrt(2)
BRONZE = 1 + np.sqrt(3)


def sturmian_sequence(theta: float, n: int, phase: float) -> np.ndarray:
    idx = np.arange(n + 1, dtype=float)
    vals = np.floor(idx * theta + phase)
    return np.diff(vals).astype(int)


def periodic_sequence(n: int) -> np.ndarray:
    return (np.arange(n) % 2).astype(int)


def optical_transfer(seq: np.ndarray, wavelength: float, n_a: float, n_b: float) -> float:
    """Return a simple normal-incidence transmission proxy for fixed layers."""
    matrix = np.eye(2, dtype=complex)
    for symbol in seq:
        index = n_b if int(symbol) else n_a
        thickness = 1.0 / (4.0 * index)  # quarter-wave at lambda0=1
        phase = 2.0 * np.pi * index * thickness / wavelength
        layer = np.array(
            [
                [np.cos(phase), 1j * np.sin(phase) / index],
                [1j * index * np.sin(phase), np.cos(phase)],
            ],
            dtype=complex,
        )
        matrix = layer @ matrix
        norm = np.linalg.norm(matrix)
        if norm > 1e80:
            matrix = matrix / norm
    denom = abs(matrix[0, 0]) ** 2
    if denom <= 1e-300:
        return 1e6
    return float(min(1e6, 1.0 / denom))


def transmission_spectrum(seq: np.ndarray, wavelengths: np.ndarray, n_a: float, n_b: float) -> np.ndarray:
    return np.array([optical_transfer(seq, wl, n_a, n_b) for wl in wavelengths], dtype=float)


def peak_positions(values: np.ndarray, wavelengths: np.ndarray, quantile: float) -> np.ndarray:
    threshold = float(np.quantile(values, quantile))
    peaks = []
    for index in range(1, len(values) - 1):
        if values[index] >= threshold and values[index] >= values[index - 1] and values[index] >= values[index + 1]:
            peaks.append(float(wavelengths[index]))
    return np.array(peaks, dtype=float)


def r_statistic_from_positions(positions: np.ndarray) -> float | None:
    if len(positions) < 4:
        return None
    gaps = np.diff(np.sort(positions))
    gaps = gaps[gaps > 1e-12]
    if len(gaps) < 2:
        return None
    left = gaps[:-1]
    right = gaps[1:]
    return float(np.mean(np.minimum(left, right) / np.maximum(left, right)))


def spectral_entropy(values: np.ndarray) -> float:
    clipped = np.clip(values.astype(float), 0.0, None)
    total = float(np.sum(clipped))
    if total <= 0:
        return 0.0
    probs = clipped / total
    probs = probs[probs > 0]
    return float(-np.sum(probs * np.log(probs)) / np.log(len(values)))


def summarize_spectrum(values: np.ndarray, wavelengths: np.ndarray, peak_quantile: float) -> dict[str, Any]:
    peaks = peak_positions(values, wavelengths, peak_quantile)
    log_values = np.log10(np.clip(values, 1e-12, 1e6))
    return {
        "T_mean": float(np.mean(values)),
        "T_median": float(np.median(values)),
        "logT_mean": float(np.mean(log_values)),
        "logT_std": float(np.std(log_values)),
        "stopband_fraction": float(np.mean(values < 0.1)),
        "highband_fraction": float(np.mean(values > 1.0)),
        "spectral_entropy": spectral_entropy(values),
        "peak_count": int(len(peaks)),
        "peak_spacing_r": r_statistic_from_positions(peaks),
    }


def finite(values: list[float | None]) -> np.ndarray:
    return np.array([v for v in values if v is not None and np.isfinite(v)], dtype=float)


def aggregate(rows: list[dict[str, Any]], domain: str) -> dict[str, Any]:
    subset = [row for row in rows if row["domain"] == domain]
    out: dict[str, Any] = {"count": len(subset)}
    for key in [
        "T_mean",
        "T_median",
        "logT_mean",
        "logT_std",
        "stopband_fraction",
        "highband_fraction",
        "spectral_entropy",
        "peak_count",
        "peak_spacing_r",
    ]:
        arr = finite([row.get(key) for row in subset])
        if len(arr) == 0:
            out[key] = {"count": 0}
        else:
            out[key] = {
                "count": int(len(arr)),
                "median": float(np.median(arr)),
                "mean": float(np.mean(arr)),
                "min": float(np.min(arr)),
                "max": float(np.max(arr)),
            }
    return out


def median_metric(summary: dict[str, Any], domain: str, metric: str) -> float | None:
    value = summary.get(domain, {}).get(metric, {})
    median = value.get("median") if isinstance(value, dict) else None
    return float(median) if median is not None else None


def run(args: argparse.Namespace) -> dict[str, Any]:
    rng = np.random.default_rng(args.seed)
    ns = [int(part) for part in args.ns.split(",") if part.strip()]
    phases = [float(part) for part in args.phases.split(",") if part.strip()]
    wavelengths = np.linspace(args.wavelength_min, args.wavelength_max, args.wavelength_count)
    domains = {
        "phi": 1 / PHI,
        "silver": 1 / SILVER,
        "bronze": 1 / BRONZE,
    }

    rows: list[dict[str, Any]] = []
    for n in ns:
        for phase in phases:
            phi_seq = sturmian_sequence(1 / PHI, n, phase)
            ones = int(np.sum(phi_seq))
            for domain, theta in domains.items():
                seq = sturmian_sequence(theta, n, phase)
                values = transmission_spectrum(seq, wavelengths, args.n_a, args.n_b)
                rows.append({
                    "domain": domain,
                    "N": n,
                    "phase": phase,
                    "ones": int(np.sum(seq)),
                    **summarize_spectrum(values, wavelengths, args.peak_quantile),
                })

            seq = periodic_sequence(n)
            values = transmission_spectrum(seq, wavelengths, args.n_a, args.n_b)
            rows.append({
                "domain": "periodic_ab",
                "N": n,
                "phase": phase,
                "ones": int(np.sum(seq)),
                **summarize_spectrum(values, wavelengths, args.peak_quantile),
            })

            for trial in range(args.random_trials):
                seq = np.array([1] * ones + [0] * (n - ones), dtype=int)
                rng.shuffle(seq)
                values = transmission_spectrum(seq, wavelengths, args.n_a, args.n_b)
                rows.append({
                    "domain": "balanced_random_phi_density",
                    "trial": trial,
                    "N": n,
                    "phase": phase,
                    "ones": ones,
                    **summarize_spectrum(values, wavelengths, args.peak_quantile),
                })

    summary = {domain: aggregate(rows, domain) for domain in sorted({row["domain"] for row in rows})}
    phi_stop = median_metric(summary, "phi", "stopband_fraction")
    random_stop = median_metric(summary, "balanced_random_phi_density", "stopband_fraction")
    periodic_stop = median_metric(summary, "periodic_ab", "stopband_fraction")
    phi_entropy = median_metric(summary, "phi", "spectral_entropy")
    random_entropy = median_metric(summary, "balanced_random_phi_density", "spectral_entropy")
    periodic_entropy = median_metric(summary, "periodic_ab", "spectral_entropy")

    third_included = False
    if None not in (phi_stop, random_stop, periodic_stop, phi_entropy, random_entropy, periodic_entropy):
        between_stop = min(periodic_stop, random_stop) <= phi_stop <= max(periodic_stop, random_stop)
        between_entropy = min(periodic_entropy, random_entropy) <= phi_entropy <= max(periodic_entropy, random_entropy)
        separated_random = abs(phi_stop - random_stop) >= args.min_stopband_delta
        third_included = bool(between_stop and between_entropy and separated_random)

    return {
        "experiment": "photonic_boundary_third_included_gate",
        "parameters": {
            "ns": ns,
            "phases": phases,
            "wavelength_min": args.wavelength_min,
            "wavelength_max": args.wavelength_max,
            "wavelength_count": args.wavelength_count,
            "n_a": args.n_a,
            "n_b": args.n_b,
            "peak_quantile": args.peak_quantile,
            "random_trials": args.random_trials,
            "seed": args.seed,
            "min_stopband_delta": args.min_stopband_delta,
        },
        "observable_contract": {
            "claim": "photonic phi stack carries a boundary state between periodic order and balanced random disorder",
            "observable": "stopband_fraction, spectral_entropy, peak_spacing_r on wavelength transmission spectra",
            "operator": "fixed-quarter-wave transfer matrix scan",
            "denominator": "N x phase x generator rows with balanced random controls",
            "non_possible": "phi indistinguishable from balanced random or outside the periodic-random interval",
        },
        "classification": {
            "third_included_photonic_boundary": third_included,
            "phi_stopband_median": phi_stop,
            "random_stopband_median": random_stop,
            "periodic_stopband_median": periodic_stop,

exec
/bin/bash -lc "sed -n '1,240p' tools/exp_quasiperiodic_vc_lattice_gate.py" in /opt/MM_D-ND
 succeeded in 0ms:
#!/usr/bin/env python3
"""
Quasiperiodic V_c lattice gate.

The Domandatore scale probe tried to fit V_c(N) with a power law. For phi the
fit did not converge and the measured values repeated on a small grid. This
tool treats that failure as the signal: it measures whether V_c lives on a
small boundary lattice across Fibonacci sizes, phases, and controls.
"""

from __future__ import annotations

import argparse
import json
from collections import defaultdict
from pathlib import Path

import numpy as np
from scipy.linalg import eigvalsh_tridiagonal


PHI = (1 + np.sqrt(5)) / 2
SILVER = 1 + np.sqrt(2)
BRONZE = 1 + np.sqrt(3)


def sturmian_sequence(theta: float, n: int, phase: float = 0.0) -> np.ndarray:
    idx = np.arange(n + 1, dtype=float)
    vals = np.floor(idx * theta + phase)
    return np.diff(vals).astype(float)


def r_statistic_from_diag(diagonal: np.ndarray) -> float:
    offdiag = np.ones(len(diagonal) - 1, dtype=float)
    eigs = eigvalsh_tridiagonal(diagonal, offdiag, check_finite=False)
    spacings = np.diff(eigs)
    spacings = spacings[spacings > 1e-12]
    if len(spacings) < 2:
        return 0.5
    left = spacings[:-1]
    right = spacings[1:]
    return float(np.mean(np.minimum(left, right) / np.maximum(left, right)))


def find_vc(seq: np.ndarray, v_values: np.ndarray, threshold: float) -> dict:
    curve = []
    for v in v_values:
        r_value = r_statistic_from_diag(v * seq)
        curve.append((float(v), r_value))
        if r_value < threshold:
            return {
                "vc": float(v),
                "r_at_vc": r_value,
                "crossed": True,
                "curve_head": curve[:5],
            }
    return {
        "vc": None,
        "r_at_vc": None,
        "crossed": False,
        "curve_head": curve[:5],
    }


def summarize(values: list[float | None], grid_step: float) -> dict:
    finite = [float(v) for v in values if v is not None and np.isfinite(v)]
    if not finite:
        return {"count": 0}
    rounded = [round(v / grid_step) * grid_step for v in finite]
    counts: dict[str, int] = {}
    for value in rounded:
        key = f"{value:.6f}"
        counts[key] = counts.get(key, 0) + 1
    total = len(rounded)
    return {
        "count": total,
        "none_count": len(values) - total,
        "distinct_vc": len(counts),
        "repeat_rate": float(1 - (len(counts) / total)),
        "mode_count": int(max(counts.values())),
        "mode_rate": float(max(counts.values()) / total),
        "median": float(np.median(finite)),
        "min": float(np.min(finite)),
        "max": float(np.max(finite)),
        "rounded_counts": dict(sorted(counts.items())),
    }


def parse_csv_ints(value: str) -> list[int]:
    return [int(part.strip()) for part in value.split(",") if part.strip()]


def parse_csv_floats(value: str) -> list[float]:
    return [float(part.strip()) for part in value.split(",") if part.strip()]


def run(args: argparse.Namespace) -> dict:
    rng = np.random.default_rng(args.seed)
    ns = parse_csv_ints(args.ns)
    phases = parse_csv_floats(args.phases)
    v_values = np.arange(args.v_min, args.v_max + (args.v_step / 2), args.v_step)
    domains = {
        "phi": 1 / PHI,
        "silver": 1 / SILVER,
        "bronze": 1 / BRONZE,
    }

    rows = []
    for n in ns:
        for phase in phases:
            phi_seq = sturmian_sequence(1 / PHI, n, phase)
            ones = int(np.sum(phi_seq))

            for domain, theta in domains.items():
                seq = sturmian_sequence(theta, n, phase)
                result = find_vc(seq, v_values, args.threshold)
                rows.append({
                    "domain": domain,
                    "N": n,
                    "phase": phase,
                    "ones": int(np.sum(seq)),
                    **result,
                })

            for trial in range(args.random_trials):
                seq = np.array([1.0] * ones + [0.0] * (n - ones), dtype=float)
                rng.shuffle(seq)
                result = find_vc(seq, v_values, args.threshold)
                rows.append({
                    "domain": "balanced_random_phi_density",
                    "trial": trial,
                    "N": n,
                    "phase": phase,
                    "ones": ones,
                    **result,
                })

    summary = {}
    for domain in sorted({row["domain"] for row in rows}):
        subset = [row for row in rows if row["domain"] == domain]
        summary[domain] = summarize([row["vc"] for row in subset], args.v_step)

    summary_by_domain_phase = {}
    grouped: dict[tuple[str, float], list[dict]] = defaultdict(list)
    for row in rows:
        grouped[(row["domain"], row["phase"])].append(row)
    for (domain, phase), subset in sorted(grouped.items()):
        summary_by_domain_phase[f"{domain}|phase={phase}"] = summarize(
            [row["vc"] for row in subset], args.v_step
        )

    phi_main = [
        row["vc"]
        for row in rows
        if row["domain"] == "phi" and abs(row["phase"]) < 1e-12
    ]
    original_phi = [1.017, 0.672, 1.017, 0.672, 0.931]

    return {
        "experiment": "quasiperiodic_vc_lattice_gate",
        "parameters": {
            "ns": ns,
            "phases": phases,
            "v_min": args.v_min,
            "v_max": args.v_max,
            "v_step": args.v_step,
            "threshold": args.threshold,
            "random_trials": args.random_trials,
            "seed": args.seed,
        },
        "source_domandatore_phi_values": original_phi,
        "phi_phase0_values": phi_main,
        "summary": summary,
        "summary_by_domain_phase": summary_by_domain_phase,
        "rows": rows,
    }


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--ns", default="89,144,233,377,610")
    parser.add_argument("--phases", default="0,0.25,0.5,0.75")
    parser.add_argument("--v-min", type=float, default=0.5)
    parser.add_argument("--v-max", type=float, default=3.0)
    parser.add_argument("--v-step", type=float, default=0.025)
    parser.add_argument("--threshold", type=float, default=0.5)
    parser.add_argument("--random-trials", type=int, default=4)
    parser.add_argument("--seed", type=int, default=202605082140)
    parser.add_argument("--out", default="tools/data/quasiperiodic_vc_lattice_gate_20260508_2140.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), encoding="utf-8")

    compact = {
        "summary": output["summary"],
        "phi_phase0_values": output["phi_phase0_values"],
        "out": str(out),
    }
    print(json.dumps(compact, indent=2))


if __name__ == "__main__":
    main()

codex
La consecutio utile è già esplicita: il confine fisico deve reggere su due osservabili non collineari. Imposto quindi un ritorno Aubry-André/Fibonacci: stessa famiglia `N x phase x generator`, ma misuro insieme statistica spettrale e localizzazione degli autostati, così il bicono può fallire senza essere salvato da una sola metrica.
exec
/bin/bash -lc "sed -n '1,220p' tools/exp_physical_sr_residue_bounce.py" in /opt/MM_D-ND
 succeeded in 0ms:
#!/usr/bin/env python3
"""
Physical bounce for the prime-minus-mod6 selective residue.

Route:
  physical A: quantum-chaotic GUE spectra
  mathematical transducer: span-matched Poisson counter-boundary
  physical B: 1D Anderson tight-binding spectra across disorder

The experiment asks whether the component split exposed by the prime/mod6
deposit has a concrete spectral analogue: SR can be absorbed by a span-matched
counter-boundary at a localized endpoint, while chaotic spectra keep SR active.
"""

from __future__ import annotations

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

import numpy as np

from observables_registry import OBSERVABLES_CANONICAL, OBSERVABLES_REGISTRY_VERSION


FOCUS_OBS = ["SR", "L1", "triple_var"]
DEFAULT_OUT = Path("tools/data/physical_sr_residue_bounce_20260514_1612.json")
DEFAULT_FIT_READY_OUT = Path("tools/data/component_state_fit_ready_20260514_1649.json")


def normalize_gaps(levels: np.ndarray, central_fraction: float) -> np.ndarray:
    levels = np.sort(np.asarray(levels, dtype=float))
    n = len(levels)
    keep = max(8, int(n * central_fraction))
    start = (n - keep) // 2
    central = levels[start : start + keep]
    gaps = np.diff(central)
    gaps = gaps[np.isfinite(gaps) & (gaps > 1e-12)]
    if len(gaps) == 0:
        return gaps
    return gaps / float(np.mean(gaps))


def gue_levels(n: int, rng: np.random.Generator) -> np.ndarray:
    real = rng.normal(size=(n, n))
    imag = rng.normal(size=(n, n))
    mat = (real + 1j * imag)
    hermitian = (mat + mat.conj().T) / (2.0 * np.sqrt(n))
    return np.linalg.eigvalsh(hermitian)


def goe_levels(n: int, rng: np.random.Generator) -> np.ndarray:
    mat = rng.normal(size=(n, n))
    symmetric = (mat + mat.T) / (2.0 * np.sqrt(n))
    return np.linalg.eigvalsh(symmetric)


def anderson_levels(n: int, disorder: float, rng: np.random.Generator) -> np.ndarray:
    diagonal = rng.uniform(-disorder / 2.0, disorder / 2.0, size=n)
    matrix = np.diag(diagonal)
    off = np.ones(n - 1)
    matrix += np.diag(off, 1) + np.diag(off, -1)
    return np.linalg.eigvalsh(matrix)


def span_matched_poisson_gaps(level_count: int, rng: np.random.Generator) -> np.ndarray:
    levels = np.sort(rng.random(level_count))
    gaps = np.diff(levels)
    gaps = gaps[gaps > 1e-12]
    if len(gaps) == 0:
        return gaps
    return gaps / float(np.mean(gaps))


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


def load_spectrum_records(path: Path, expected_class: str | None = None) -> list[dict[str, Any]]:
    """Load a single spectrum or a small record set for the fit-ready interface."""
    if path.suffix.lower() == ".json":
        payload = json.loads(path.read_text(encoding="utf-8"))
        if isinstance(payload, list) and all(isinstance(item, (int, float)) for item in payload):
            return [{"label": path.stem, "expected_class": expected_class, "levels": payload}]
        if isinstance(payload, dict):
            if "spectra" in payload:
                records = payload["spectra"]
            elif "levels" in payload or "spectrum" in payload:
                records = [payload]
            else:
                raise ValueError("JSON input must contain levels, spectrum, or spectra")
            normalized = []
            for index, record in enumerate(records):
                levels = record.get("levels", record.get("spectrum"))
                if levels is None:
                    raise ValueError(f"spectrum record {index} has no levels/spectrum field")
                normalized.append(
                    {
                        "label": record.get("label", f"{path.stem}_{index}"),
                        "expected_class": record.get("expected_class", expected_class),
                        "levels": levels,
                    }
                )
            return normalized
        raise ValueError("unsupported JSON spectrum payload")

    levels = np.loadtxt(path, dtype=float)
    return [{"label": path.stem, "expected_class": expected_class, "levels": levels.tolist()}]


def sign_swap_p(values: np.ndarray, rng: np.random.Generator, trials: int) -> float:
    if len(values) == 0:
        return 1.0
    observed = abs(float(np.mean(values)))
    null = []
    for _ in range(trials):
        signs = rng.choice(np.array([-1.0, 1.0]), size=len(values), replace=True)
        null.append(abs(float(np.mean(values * signs))))
    null_arr = np.array(null, dtype=float)
    return float((np.sum(null_arr >= observed) + 1) / (len(null_arr) + 1))


def summarize(label: str, rows: list[dict[str, Any]], rng: np.random.Generator, trials: int) -> dict[str, Any]:
    deltas = {
        obs: np.array([row["delta"][obs] for row in rows], dtype=float)
        for obs in OBSERVABLES_CANONICAL
    }
    summary: dict[str, Any] = {
        "label": label,
        "sample_count": len(rows),
        "component_state": {},
        "mean_real": {},
        "mean_null": {},
        "mean_delta": {},
        "p_two_sided": {},
        "cohen_d_delta": {},
    }
    for obs, values in deltas.items():
        real_values = np.array([row["real"][obs] for row in rows], dtype=float)
        null_values = np.array([row["null"][obs] for row in rows], dtype=float)
        mean_delta = float(np.mean(values))
        sd = float(np.std(values, ddof=1)) if len(values) > 1 else 0.0
        p_value = sign_swap_p(values, rng, trials)
        effect = mean_delta / sd if sd > 1e-12 else 0.0
        active = p_value <= 0.01 and abs(effect) >= 0.5
        summary["mean_real"][obs] = float(np.mean(real_values))
        summary["mean_null"][obs] = float(np.mean(null_values))
        summary["mean_delta"][obs] = mean_delta
        summary["p_two_sided"][obs] = p_value
        summary["cohen_d_delta"][obs] = effect
        summary["component_state"][obs] = "active" if active else "absorbed"
    summary["focus_signature"] = [
        obs for obs in FOCUS_OBS if summary["component_state"][obs] == "active"
    ]
    return summary


def contrast(
    label: str,
    left_rows: list[dict[str, Any]],
    right_rows: list[dict[str, Any]],
    rng: np.random.Generator,
    trials: int,
) -> dict[str, Any]:
    paired = min(len(left_rows), len(right_rows))
    result: dict[str, Any] = {
        "label": label,
        "paired_count": paired,
        "mean_left_minus_right": {},
        "p_two_sided": {},
        "cohen_d": {},
        "state": {},
    }
    for obs in OBSERVABLES_CANONICAL:
        values = np.array(
            [
                left_rows[i]["real"][obs] - right_rows[i]["real"][obs]
                for i in range(paired)
            ],
            dtype=float,
        )
        mean_delta = float(np.mean(values)) if len(values) else 0.0
        sd = float(np.std(values, ddof=1)) if len(values) > 1 else 0.0
        p_value = sign_swap_p(values, rng, trials)
        effect = mean_delta / sd if sd > 1e-12 else 0.0
        active = p_value <= 0.01 and abs(effect) >= 0.5
        result["mean_left_minus_right"][obs] = mean_delta
        result["p_two_sided"][obs] = p_value
        result["cohen_d"][obs] = effect
        result["state"][obs] = "separated" if active else "not_separated"
    return result


def evaluate_input_spectra(args: argparse.Namespace) -> dict[str, Any]:
    rng = np.random.default_rng(args.seed)
    records = load_spectrum_records(args.input_spectrum, args.expected_class)
    trace_path = Path(str(args.output).replace(".json", ".trace.jsonl"))
    trace_path.parent.mkdir(parents=True, exist_ok=True)
    rows_by_class: dict[str, list[dict[str, Any]]] = {}
    direct_rows_by_class: dict[str, list[dict[str, Any]]] = {}

    with trace_path.open("w", encoding="utf-8") as trace:
        for record in records:
            levels = np.asarray(record["levels"], dtype=float)
            gaps = normalize_gaps(levels, args.central_fraction)
            if len(gaps) < 12:
                raise ValueError(f"spectrum {record['label']} has fewer than 12 usable central gaps")
            real_obs = compute_obs(gaps)
            class_key = record.get("expected_class") or record["label"]
            direct_rows_by_class.setdefault(class_key, []).append(
                {
                    "event": "input_spectrum_direct",
                    "label": record["label"],
                    "expected_class": record.get("expected_class"),
                    "n_levels": int(len(levels)),
                    "n_gaps": int(len(gaps)),
                    "real": real_obs,
                }
            )

exec
/bin/bash -lc 'rg -n "Archivio enzimi|CE-[0-9]|CE-" tools/data/agent_field_live.md tools/LAB_AGENT_CONTEXT.md tools/data/reports/agent_20260515_17*.md' in /opt/MM_D-ND
 succeeded in 0ms:
tools/LAB_AGENT_CONTEXT.md:161:- **Archivio enzimi cognitivi.** Se il campo vivo contiene `Archivio enzimi
tools/LAB_AGENT_CONTEXT.md:163:  `CE-*` usata nella combo, oppure `CE-none:` con un motivo specifico e
tools/LAB_AGENT_CONTEXT.md:361:  passaggio KSAR/PVI/Vault o voce `CE-*` dell'archivio usata nel ciclo. Se non
tools/LAB_AGENT_CONTEXT.md:362:  usi il layer cognitivo, dichiara `CE-none:` e il motivo specifico. `none`
tools/data/reports/agent_20260515_1734.md:15:- **Contaminazione cognitiva**: CE-0001 / KSAR usato come reiterazione del deposito 17:24 senza continuare `V_c`: portare il confine in un setup fisico. CE-0038 usato come equilibrio tra estremi: il punto-zero e' il segmento periodico-random, non la media dei valori.
tools/data/reports/agent_20260515_1724.md:15:- **Contaminazione cognitiva**: CE-0001 / KSAR usato come reiterazione del kernel 20260509: non rifittare `V_c`, ripetere il deposito con denominatore piu esplicito. CE-0038 usato come equilibrio tra estremi: il punto-zero non e' media tra phi e random, e' la riga `N,phase` prima della classificazione.
tools/data/agent_field_live.md:615:Usali come enzimi del respiro fuori-tempo: DeltaLink, innesco genomico, reiterazione semantica, filtro avversariale e Vault. Non copiarne l'identita': trasformali in dipolo, punto-zero e osservabile. Nel report la sezione `Contaminazione cognitiva` e' obbligatoria: se non usi adapter o archivio CE, dichiara `CE-none:` e il motivo specifico.
tools/data/agent_field_live.md:883:## Archivio enzimi cognitivi — richiamo contestuale
tools/data/agent_field_live.md:884:Archivio generato: 2026-05-08T20:49:15.084998+00:00 · 260 voci. Questo e' un substrate selector: non usare il substrato come claim. Tieni le teorie scientifiche nel focus e usa CE-* solo per costruire combo corta: assioma/metodo + osservazione/funzione + teoria/focus + null test. Se nessuna voce regge, dichiara `CE-none:` con motivo specifico nella Contaminazione cognitiva. `none` generico non e' valido.
tools/data/agent_field_live.md:888:- assioma/metodo: CE-0117 [method_axiom] score=58 overlap=7 · 2. La cascata della possibilità
tools/data/agent_field_live.md:891:- osservazione primaria: CE-0038 [corpus_primary_observation] score=81 overlap=9 · [47] NID 598 — R dell'Istanza  - L' equilibrio tra estremi del Modello D-ND
tools/data/agent_field_live.md:894:- funzione/formalizzazione: CE-0002 [corpus_formal_function] score=89 overlap=2 · Funzione
tools/data/agent_field_live.md:897:- teoria/focus scientifico: CE-0027 [corpus_project_architecture] score=75 overlap=7 · [114] NID 1931 — Modello D-ND: Formalizzazione Assiomatica, Emergenza Quantistica e Implic
tools/data/agent_field_live.md:900:- enzima/kernel: CE-0001 [lab_operational_context] score=92 overlap=2 · Adapter 3: KSAR reiterative semantic kernel
tools/data/agent_field_live.md:904:- CE-0001 [lab_operational_context/strumento_lab/lab_cycle] score=92 overlap=2 · Adapter 3: KSAR reiterative semantic kernel
tools/data/agent_field_live.md:907:- CE-0002 [corpus_formal_function/regola_primaria/campo_dnd] score=89 overlap=2 · Funzione
tools/data/agent_field_live.md:910:- CE-0030 [corpus_project_architecture/contaminante_dnd/campo_dnd] score=82 overlap=10 · [69] NID 1353 — Documento di Sintesi sul Modello Duale Non-Duale (D-ND) e la Risultante "R
tools/data/agent_field_live.md:913:- CE-0038 [corpus_primary_observation/contaminante_dnd/campo_dnd] score=81 overlap=9 · [47] NID 598 — R dell'Istanza  - L' equilibrio tra estremi del Modello D-ND
tools/data/agent_field_live.md:916:- CE-0019 [lab_operational_context/contaminante_dnd/campo_dnd] score=81 overlap=8 · Respiro fuori-tempo
tools/data/agent_field_live.md:919:- CE-0005 [corpus_cognitive_prompt/contaminante_dnd/campo_dnd] score=79 overlap=4 · [82] NID 2321 — Prompt per Motore di Inferenza Quantistica Duale-Non-Duale (D-ND)
tools/data/agent_field_live.md:922:- CE-0003 [corpus_formal_function/contaminante_dnd/campo_dnd] score=79 overlap=2 · Titolo Assiomatico Combinato Rivisto: "Ottimizzazione Unificata e Manifestazione della Ris
tools/data/agent_field_live.md:924:- CE-0007 [kernel_reference/regola_primaria/campo_dnd] score=77 overlap=5 · **1. Kernel Assiomatico: Fisica del Campo Inferenziale**
tools/data/agent_field_live.md:1212:Se il campo contiene adapter cognitivi o archivio CE, la sezione `Contaminazione cognitiva` deve nominare almeno una voce `CE-*`, YSN, Cornelius, KSAR, PVI/Vault usata nella combo, oppure `CE-none:` con motivo specifico e verificabile. `none` generico significa che il campo e' stato visto ma non metabolizzato.
tools/data/reports/agent_20260515_1712.md:15:- **Contaminazione cognitiva**: CE-0001 / KSAR usato come reiterazione del kernel emerso: 512 e 1024 avevano downgradato il residuo, 2048 verifica se la chiusura regge senza promuovere. CE-0038 usato come richiamo al punto-zero tra estremi: qui l'equilibrio non e' media, e' riga row-local prima della classificazione.
tools/data/reports/agent_20260515_1705.md:15:- **Contaminazione cognitiva**: KSAR usato come reiterazione minima del recovery kernel: 512 aveva gia downgradato il residuo, 1024 verifica se il downgrade regge. CE-none: il campo non espone una voce CE concreta necessaria oltre al contratto gia formato; inventarla sarebbe rumore.

exec
/bin/bash -lc "sed -n '1,220p' tools/lab_preflight_agent.py" in /opt/MM_D-ND
 succeeded in 0ms:
#!/usr/bin/env python3
"""Preflight non-mutating agent for MM_D-ND Lab cycles.

The Lab can produce value, useful artifacts, and drift in the same movement.
This tool reads the deposits after a cycle and classifies what happened before
another cron/repair run is allowed to carry the trajectory forward.

Default behavior is read-only: it writes a review artifact under
tools/data/preflight/ and never changes the seed, directives, graph or report.
"""
from __future__ import annotations

import argparse
import json
import re
from datetime import datetime, timezone
from pathlib import Path
from typing import Any


TOOLS = Path(__file__).resolve().parent
DATA = TOOLS / "data"
REPORTS = DATA / "reports"
VERITAS = DATA / "veritas"
GRAPH_COMPLETION = DATA / "graph_completion"
PROMOTIONS = DATA / "promotions"
PREFLIGHT = DATA / "preflight"
TOOL_CONTRACTS = DATA / "tool_contracts"
SESSION_LOG = DATA / "lab_session_log.jsonl"


def read_json(path: Path, default: Any = None) -> Any:
    if default is None:
        default = {}
    try:
        return json.loads(path.read_text(encoding="utf-8"))
    except Exception:
        return default


def write_json(path: Path, data: Any) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")


def read_jsonl(path: Path) -> list[dict[str, Any]]:
    rows: list[dict[str, Any]] = []
    if not path.exists():
        return rows
    for line in path.read_text(encoding="utf-8", errors="replace").splitlines():
        if not line.strip():
            continue
        try:
            row = json.loads(line)
        except json.JSONDecodeError:
            continue
        if isinstance(row, dict):
            rows.append(row)
    return rows


def cycle_dt(cycle: str) -> datetime:
    for fmt in ("%Y%m%d_%H%M%S", "%Y%m%d_%H%M"):
        try:
            return datetime.strptime(cycle, fmt).replace(tzinfo=timezone.utc)
        except ValueError:
            pass
    return datetime.fromtimestamp(0, timezone.utc)


def latest_cycle() -> str:
    reports = sorted(REPORTS.glob("agent_*.md"), key=lambda p: p.stat().st_mtime, reverse=True)
    if not reports:
        raise SystemExit("no Lab reports found")
    return reports[0].stem.replace("agent_", "", 1)


def report_title(path: Path) -> str:
    if not path.exists():
        return ""
    for line in path.read_text(encoding="utf-8", errors="replace").splitlines():
        if line.startswith("# "):
            return line[2:].strip()
    return ""


def report_text(cycle: str) -> str:
    path = REPORTS / f"agent_{cycle}.md"
    if not path.exists():
        return ""
    return path.read_text(encoding="utf-8", errors="replace")


def session_entry(cycle: str) -> dict[str, Any]:
    for row in reversed(read_jsonl(SESSION_LOG)):
        if row.get("cycle_ts") == cycle:
            return row
    return {}


def veritas_for_cycle(cycle: str) -> dict[str, Any]:
    matches: list[tuple[float, dict[str, Any]]] = []
    for path in VERITAS.glob(f"veritas_{cycle[:8]}_*.json"):
        data = read_json(path, {})
        if data.get("cycle_ref") == cycle:
            matches.append((path.stat().st_mtime, data))
    if not matches:
        return {}
    return sorted(matches, key=lambda item: item[0])[-1][1]


def graph_for_cycle(cycle: str) -> dict[str, Any]:
    return read_json(GRAPH_COMPLETION / f"graph_completion_{cycle}.json", {})


def promotion_for_cycle(cycle: str) -> dict[str, Any]:
    return read_json(PROMOTIONS / f"promotion_{cycle}.json", {})


def source_files_from_report(text: str) -> list[str]:
    files = set(re.findall(r"`?((?:tools/)?data/[A-Za-z0-9_./-]+\.(?:json|md|jsonl))`?", text))
    files.update(re.findall(r"`?(tools/[A-Za-z0-9_./-]+\.(?:py|sh|md))`?", text))
    return sorted(files)


def useful_artifacts_for_cycle(cycle: str, text: str) -> list[dict[str, Any]]:
    artifacts: list[dict[str, Any]] = []
    for rel in source_files_from_report(text):
        if not rel.endswith(".json"):
            continue
        path = TOOLS.parent / rel
        data = read_json(path, {})
        if not isinstance(data, dict):
            continue
        if str(data.get("tester_id", "")).startswith("component_state_"):
            artifacts.append(
                {
                    "file": rel,
                    "kind": "component_state_fit_ready",
                    "tester_id": data.get("tester_id"),
                    "input_contract": bool(data.get("input_contract")),
                    "output_contract": bool(data.get("output_contract")),
                    "thresholds": bool(data.get("thresholds")),
                    "not_physics_law": data.get("not_promoted_as_physics_law") is True,
                }
            )
        elif data.get("experiment"):
            artifacts.append(
                {
                    "file": rel,
                    "kind": "experiment",
                    "experiment": data.get("experiment"),
                }
            )
    return artifacts


def tool_contracts_for_artifacts(artifacts: list[dict[str, Any]]) -> list[dict[str, Any]]:
    contracts: list[dict[str, Any]] = []
    seen: set[str] = set()
    for artifact in artifacts:
        tester_id = artifact.get("tester_id")
        if not tester_id or tester_id in seen:
            continue
        seen.add(str(tester_id))
        path = TOOL_CONTRACTS / f"{tester_id}.json"
        data = read_json(path, {})
        if not data:
            continue
        contracts.append(
            {
                "tester_id": tester_id,
                "status": data.get("status"),
                "kind": data.get("kind"),
                "path": str(path),
                "promote_as": (data.get("promotion_policy") or {}).get("promote_as"),
                "smoke_command": (data.get("runtime") or {}).get("smoke_command"),
            }
        )
    return contracts


def flag_counts(falsifier: dict[str, Any]) -> dict[str, int]:
    counts = {"high": 0, "medium": 0, "low": 0}
    for flag in falsifier.get("flags") or []:
        if not isinstance(flag, dict):
            continue
        severity = str(flag.get("severity") or "").lower()
        if severity in counts:
            counts[severity] += 1
    return counts


def compact_flags(falsifier: dict[str, Any]) -> list[dict[str, str]]:
    rows: list[dict[str, str]] = []
    for flag in falsifier.get("flags") or []:
        if not isinstance(flag, dict):
            continue
        rows.append(
            {
                "lens": f"L{flag.get('lens', '?')}",
                "severity": str(flag.get("severity") or "unknown"),
                "claim": clean(flag.get("claim"), 220),
                "suggestion": clean(flag.get("suggestion"), 260),
            }
        )
    return rows


def clean(value: Any, limit: int = 360) -> str:
    text = re.sub(r"\s+", " ", str(value or "")).strip()
    if len(text) > limit:
        return text[: limit - 3].rstrip() + "..."
    return text


def gather(cycle: str) -> dict[str, Any]:
    text = report_text(cycle)
    report_path = REPORTS / f"agent_{cycle}.md"
    falsifier = read_json(REPORTS / f"falsifier_{cycle}.json", {})

codex
Creo uno strumento nuovo, non un report scritto a mano sopra un output vecchio: lo script produrrà righe row-aligned per generatore, taglia, fase, V e controllo random. Le due osservabili saranno `spacing_r` e `mean_ipr`, con `participation_entropy` come controllo di localizzazione.
apply patch
patch: completed
/opt/MM_D-ND/tools/exp_aubry_boundary_phase_transport_gate.py
diff --git a/tools/exp_aubry_boundary_phase_transport_gate.py b/tools/exp_aubry_boundary_phase_transport_gate.py
new file mode 100644
index 0000000000000000000000000000000000000000..8487656a64b34e688fb3f65c4708e1aa25469724
--- /dev/null
+++ b/tools/exp_aubry_boundary_phase_transport_gate.py
@@ -0,0 +1,284 @@
+#!/usr/bin/env python3
+"""
+Aubry/Fibonacci boundary phase transport gate.
+
+Projects the live BOUNDARY direction into a 1D tight-binding model with binary
+quasiperiodic potentials. The test is deliberately joint: a boundary return is
+accepted only when the phi stack is between periodic order and balanced random
+disorder for both spectral spacing and eigenstate localization.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+
+PHI = (1 + np.sqrt(5)) / 2
+SILVER = 1 + np.sqrt(2)
+BRONZE = 1 + np.sqrt(3)
+
+
+def sturmian_sequence(theta: float, n: int, phase: float) -> np.ndarray:
+    idx = np.arange(n + 1, dtype=float)
+    vals = np.floor(idx * theta + phase)
+    return np.diff(vals).astype(float)
+
+
+def periodic_sequence(n: int) -> np.ndarray:
+    return (np.arange(n) % 2).astype(float)
+
+
+def hamiltonian(diagonal: np.ndarray) -> np.ndarray:
+    n = len(diagonal)
+    matrix = np.diag(diagonal.astype(float))
+    off = np.ones(n - 1, dtype=float)
+    matrix += np.diag(off, 1) + np.diag(off, -1)
+    return matrix
+
+
+def central_slice(n: int, central_fraction: float) -> slice:
+    keep = max(8, int(round(n * central_fraction)))
+    keep = min(n, keep)
+    start = (n - keep) // 2
+    return slice(start, start + keep)
+
+
+def spacing_r(levels: np.ndarray, central_fraction: float) -> float | None:
+    levels = np.sort(np.asarray(levels, dtype=float))
+    central = levels[central_slice(len(levels), central_fraction)]
+    gaps = np.diff(central)
+    gaps = gaps[np.isfinite(gaps) & (gaps > 1e-12)]
+    if len(gaps) < 2:
+        return None
+    left = gaps[:-1]
+    right = gaps[1:]
+    return float(np.mean(np.minimum(left, right) / np.maximum(left, right)))
+
+
+def localization_metrics(vectors: np.ndarray, central_fraction: float) -> dict[str, float]:
+    n = vectors.shape[0]
+    subset = vectors[:, central_slice(n, central_fraction)]
+    probs = np.square(np.abs(subset))
+    ipr = np.sum(probs * probs, axis=0)
+    entropy = []
+    for col in range(probs.shape[1]):
+        p = probs[:, col]
+        p = p[p > 1e-15]
+        entropy.append(float(-np.sum(p * np.log(p)) / np.log(n)))
+    return {
+        "mean_ipr": float(np.mean(ipr)),
+        "median_ipr": float(np.median(ipr)),
+        "participation_entropy": float(np.mean(entropy)) if entropy else 0.0,
+    }
+
+
+def spectrum_row(
+    domain: str,
+    seq: np.ndarray,
+    n: int,
+    phase: float,
+    v_value: float,
+    central_fraction: float,
+    trial: int | None = None,
+) -> dict[str, Any]:
+    centered = seq - float(np.mean(seq))
+    levels, vectors = np.linalg.eigh(hamiltonian(v_value * centered))
+    metrics = localization_metrics(vectors, central_fraction)
+    row: dict[str, Any] = {
+        "domain": domain,
+        "N": n,
+        "phase": phase,
+        "V": v_value,
+        "ones": int(np.sum(seq)),
+        "spacing_r": spacing_r(levels, central_fraction),
+        **metrics,
+    }
+    if trial is not None:
+        row["trial"] = trial
+    return row
+
+
+def finite(values: list[float | None]) -> np.ndarray:
+    return np.array([v for v in values if v is not None and np.isfinite(v)], dtype=float)
+
+
+def aggregate(rows: list[dict[str, Any]]) -> dict[str, Any]:
+    out: dict[str, Any] = {"count": len(rows)}
+    for key in ["spacing_r", "mean_ipr", "median_ipr", "participation_entropy"]:
+        arr = finite([row.get(key) for row in rows])
+        if len(arr) == 0:
+            out[key] = {"count": 0}
+        else:
+            out[key] = {
+                "count": int(len(arr)),
+                "median": float(np.median(arr)),
+                "mean": float(np.mean(arr)),
+                "min": float(np.min(arr)),
+                "max": float(np.max(arr)),
+            }
+    return out
+
+
+def median_metric(summary: dict[str, Any], domain: str, v_key: str, metric: str) -> float | None:
+    value = summary.get(v_key, {}).get(domain, {}).get(metric, {})
+    if not isinstance(value, dict):
+        return None
+    median = value.get("median")
+    return float(median) if median is not None else None
+
+
+def between(value: float, left: float, right: float) -> bool:
+    return min(left, right) <= value <= max(left, right)
+
+
+def parse_csv_ints(value: str) -> list[int]:
+    return [int(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def parse_csv_floats(value: str) -> list[float]:
+    return [float(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    rng = np.random.default_rng(args.seed)
+    ns = parse_csv_ints(args.ns)
+    phases = parse_csv_floats(args.phases)
+    v_values = np.arange(args.v_min, args.v_max + args.v_step / 2, args.v_step)
+    domains = {
+        "phi": 1 / PHI,
+        "silver": 1 / SILVER,
+        "bronze": 1 / BRONZE,
+    }
+
+    rows: list[dict[str, Any]] = []
+    for n in ns:
+        for phase in phases:
+            phi_seq = sturmian_sequence(1 / PHI, n, phase)
+            ones = int(np.sum(phi_seq))
+            for v_value in v_values:
+                for domain, theta in domains.items():
+                    seq = sturmian_sequence(theta, n, phase)
+                    rows.append(spectrum_row(domain, seq, n, phase, float(v_value), args.central_fraction))
+
+                periodic = periodic_sequence(n)
+                rows.append(spectrum_row("periodic_ab", periodic, n, phase, float(v_value), args.central_fraction))
+
+                for trial in range(args.random_trials):
+                    seq = np.array([1.0] * ones + [0.0] * (n - ones), dtype=float)
+                    rng.shuffle(seq)
+                    rows.append(
+                        spectrum_row(
+                            "balanced_random_phi_density",
+                            seq,
+                            n,
+                            phase,
+                            float(v_value),
+                            args.central_fraction,
+                            trial=trial,
+                        )
+                    )
+
+    summary_by_v: dict[str, dict[str, Any]] = {}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        summary_by_v[v_key] = {}
+        for domain in sorted({row["domain"] for row in rows}):
+            subset = [row for row in rows if row["domain"] == domain and abs(row["V"] - v_value) < 1e-12]
+            summary_by_v[v_key][domain] = aggregate(subset)
+
+    classification: dict[str, Any] = {"joint_boundary_v": [], "by_v": {}}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        needed = {
+            "phi_r": median_metric(summary_by_v, "phi", v_key, "spacing_r"),
+            "periodic_r": median_metric(summary_by_v, "periodic_ab", v_key, "spacing_r"),
+            "random_r": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "spacing_r"),
+            "phi_ipr": median_metric(summary_by_v, "phi", v_key, "mean_ipr"),
+            "periodic_ipr": median_metric(summary_by_v, "periodic_ab", v_key, "mean_ipr"),
+            "random_ipr": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "mean_ipr"),
+            "phi_entropy": median_metric(summary_by_v, "phi", v_key, "participation_entropy"),
+            "periodic_entropy": median_metric(summary_by_v, "periodic_ab", v_key, "participation_entropy"),
+            "random_entropy": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "participation_entropy"),
+        }
+        complete = all(value is not None for value in needed.values())
+        r_between = bool(complete and between(needed["phi_r"], needed["periodic_r"], needed["random_r"]))
+        ipr_between = bool(complete and between(needed["phi_ipr"], needed["periodic_ipr"], needed["random_ipr"]))
+        entropy_between = bool(complete and between(needed["phi_entropy"], needed["periodic_entropy"], needed["random_entropy"]))
+        separated_random = bool(
+            complete
+            and abs(needed["phi_r"] - needed["random_r"]) >= args.min_r_delta
+            and abs(needed["phi_ipr"] - needed["random_ipr"]) >= args.min_ipr_delta
+        )
+        joint = bool(r_between and ipr_between and separated_random)
+        classification["by_v"][v_key] = {
+            **needed,
+            "spacing_r_between": r_between,
+            "mean_ipr_between": ipr_between,
+            "participation_entropy_between": entropy_between,
+            "separated_from_random": separated_random,
+            "joint_boundary": joint,
+        }
+        if joint:
+            classification["joint_boundary_v"].append(float(v_value))
+
+    return {
+        "experiment": "aubry_boundary_phase_transport_gate",
+        "parameters": {
+            "ns": ns,
+            "phases": phases,
+            "v_min": args.v_min,
+            "v_max": args.v_max,
+            "v_step": args.v_step,
+            "central_fraction": args.central_fraction,
+            "random_trials": args.random_trials,
+            "seed": args.seed,
+            "min_r_delta": args.min_r_delta,
+            "min_ipr_delta": args.min_ipr_delta,
+        },
+        "observable_contract": {
+            "claim": "phi is a physical boundary state between periodic order and balanced random disorder only if spectral spacing and localization agree",
+            "observable": "spacing_r plus mean_ipr / participation_entropy on tight-binding spectra",
+            "operator": "binary quasiperiodic tight-binding Hamiltonian with phase row denominator",
+            "denominator": "N x phase x V x generator rows with balanced random controls",
+            "non_possible": "single-observable boundary, phase-aggregated critical value, or phi outside the periodic-random interval",
+        },
+        "classification": classification,
+        "summary_by_v": summary_by_v,
+        "rows": rows,
+    }
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--ns", default="89,144,233")
+    parser.add_argument("--phases", default="0,0.25,0.5,0.75")
+    parser.add_argument("--v-min", type=float, default=0.5)
+    parser.add_argument("--v-max", type=float, default=2.5)
+    parser.add_argument("--v-step", type=float, default=0.25)
+    parser.add_argument("--central-fraction", type=float, default=0.6)
+    parser.add_argument("--random-trials", type=int, default=6)
+    parser.add_argument("--seed", type=int, default=202605151745)
+    parser.add_argument("--min-r-delta", type=float, default=0.025)
+    parser.add_argument("--min-ipr-delta", type=float, default=0.0025)
+    parser.add_argument("--out", default="tools/data/aubry_boundary_phase_transport_gate_20260515_1745.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), encoding="utf-8")
+
+    compact = {
+        "classification": output["classification"],
+        "out": str(out),
+    }
+    print(json.dumps(compact, indent=2))
+
+
+if __name__ == "__main__":
+    main()

diff --git a/tools/exp_aubry_boundary_phase_transport_gate.py b/tools/exp_aubry_boundary_phase_transport_gate.py
new file mode 100644
index 0000000000000000000000000000000000000000..8487656a64b34e688fb3f65c4708e1aa25469724
--- /dev/null
+++ b/tools/exp_aubry_boundary_phase_transport_gate.py
@@ -0,0 +1,284 @@
+#!/usr/bin/env python3
+"""
+Aubry/Fibonacci boundary phase transport gate.
+
+Projects the live BOUNDARY direction into a 1D tight-binding model with binary
+quasiperiodic potentials. The test is deliberately joint: a boundary return is
+accepted only when the phi stack is between periodic order and balanced random
+disorder for both spectral spacing and eigenstate localization.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+
+PHI = (1 + np.sqrt(5)) / 2
+SILVER = 1 + np.sqrt(2)
+BRONZE = 1 + np.sqrt(3)
+
+
+def sturmian_sequence(theta: float, n: int, phase: float) -> np.ndarray:
+    idx = np.arange(n + 1, dtype=float)
+    vals = np.floor(idx * theta + phase)
+    return np.diff(vals).astype(float)
+
+
+def periodic_sequence(n: int) -> np.ndarray:
+    return (np.arange(n) % 2).astype(float)
+
+
+def hamiltonian(diagonal: np.ndarray) -> np.ndarray:
+    n = len(diagonal)
+    matrix = np.diag(diagonal.astype(float))
+    off = np.ones(n - 1, dtype=float)
+    matrix += np.diag(off, 1) + np.diag(off, -1)
+    return matrix
+
+
+def central_slice(n: int, central_fraction: float) -> slice:
+    keep = max(8, int(round(n * central_fraction)))
+    keep = min(n, keep)
+    start = (n - keep) // 2
+    return slice(start, start + keep)
+
+
+def spacing_r(levels: np.ndarray, central_fraction: float) -> float | None:
+    levels = np.sort(np.asarray(levels, dtype=float))
+    central = levels[central_slice(len(levels), central_fraction)]
+    gaps = np.diff(central)
+    gaps = gaps[np.isfinite(gaps) & (gaps > 1e-12)]
+    if len(gaps) < 2:
+        return None
+    left = gaps[:-1]
+    right = gaps[1:]
+    return float(np.mean(np.minimum(left, right) / np.maximum(left, right)))
+
+
+def localization_metrics(vectors: np.ndarray, central_fraction: float) -> dict[str, float]:
+    n = vectors.shape[0]
+    subset = vectors[:, central_slice(n, central_fraction)]
+    probs = np.square(np.abs(subset))
+    ipr = np.sum(probs * probs, axis=0)
+    entropy = []
+    for col in range(probs.shape[1]):
+        p = probs[:, col]
+        p = p[p > 1e-15]
+        entropy.append(float(-np.sum(p * np.log(p)) / np.log(n)))
+    return {
+        "mean_ipr": float(np.mean(ipr)),
+        "median_ipr": float(np.median(ipr)),
+        "participation_entropy": float(np.mean(entropy)) if entropy else 0.0,
+    }
+
+
+def spectrum_row(
+    domain: str,
+    seq: np.ndarray,
+    n: int,
+    phase: float,
+    v_value: float,
+    central_fraction: float,
+    trial: int | None = None,
+) -> dict[str, Any]:
+    centered = seq - float(np.mean(seq))
+    levels, vectors = np.linalg.eigh(hamiltonian(v_value * centered))
+    metrics = localization_metrics(vectors, central_fraction)
+    row: dict[str, Any] = {
+        "domain": domain,
+        "N": n,
+        "phase": phase,
+        "V": v_value,
+        "ones": int(np.sum(seq)),
+        "spacing_r": spacing_r(levels, central_fraction),
+        **metrics,
+    }
+    if trial is not None:
+        row["trial"] = trial
+    return row
+
+
+def finite(values: list[float | None]) -> np.ndarray:
+    return np.array([v for v in values if v is not None and np.isfinite(v)], dtype=float)
+
+
+def aggregate(rows: list[dict[str, Any]]) -> dict[str, Any]:
+    out: dict[str, Any] = {"count": len(rows)}
+    for key in ["spacing_r", "mean_ipr", "median_ipr", "participation_entropy"]:
+        arr = finite([row.get(key) for row in rows])
+        if len(arr) == 0:
+            out[key] = {"count": 0}
+        else:
+            out[key] = {
+                "count": int(len(arr)),
+                "median": float(np.median(arr)),
+                "mean": float(np.mean(arr)),
+                "min": float(np.min(arr)),
+                "max": float(np.max(arr)),
+            }
+    return out
+
+
+def median_metric(summary: dict[str, Any], domain: str, v_key: str, metric: str) -> float | None:
+    value = summary.get(v_key, {}).get(domain, {}).get(metric, {})
+    if not isinstance(value, dict):
+        return None
+    median = value.get("median")
+    return float(median) if median is not None else None
+
+
+def between(value: float, left: float, right: float) -> bool:
+    return min(left, right) <= value <= max(left, right)
+
+
+def parse_csv_ints(value: str) -> list[int]:
+    return [int(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def parse_csv_floats(value: str) -> list[float]:
+    return [float(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    rng = np.random.default_rng(args.seed)
+    ns = parse_csv_ints(args.ns)
+    phases = parse_csv_floats(args.phases)
+    v_values = np.arange(args.v_min, args.v_max + args.v_step / 2, args.v_step)
+    domains = {
+        "phi": 1 / PHI,
+        "silver": 1 / SILVER,
+        "bronze": 1 / BRONZE,
+    }
+
+    rows: list[dict[str, Any]] = []
+    for n in ns:
+        for phase in phases:
+            phi_seq = sturmian_sequence(1 / PHI, n, phase)
+            ones = int(np.sum(phi_seq))
+            for v_value in v_values:
+                for domain, theta in domains.items():
+                    seq = sturmian_sequence(theta, n, phase)
+                    rows.append(spectrum_row(domain, seq, n, phase, float(v_value), args.central_fraction))
+
+                periodic = periodic_sequence(n)
+                rows.append(spectrum_row("periodic_ab", periodic, n, phase, float(v_value), args.central_fraction))
+
+                for trial in range(args.random_trials):
+                    seq = np.array([1.0] * ones + [0.0] * (n - ones), dtype=float)
+                    rng.shuffle(seq)
+                    rows.append(
+                        spectrum_row(
+                            "balanced_random_phi_density",
+                            seq,
+                            n,
+                            phase,
+                            float(v_value),
+                            args.central_fraction,
+                            trial=trial,
+                        )
+                    )
+
+    summary_by_v: dict[str, dict[str, Any]] = {}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        summary_by_v[v_key] = {}
+        for domain in sorted({row["domain"] for row in rows}):
+            subset = [row for row in rows if row["domain"] == domain and abs(row["V"] - v_value) < 1e-12]
+            summary_by_v[v_key][domain] = aggregate(subset)
+
+    classification: dict[str, Any] = {"joint_boundary_v": [], "by_v": {}}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        needed = {
+            "phi_r": median_metric(summary_by_v, "phi", v_key, "spacing_r"),
+            "periodic_r": median_metric(summary_by_v, "periodic_ab", v_key, "spacing_r"),
+            "random_r": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "spacing_r"),
+            "phi_ipr": median_metric(summary_by_v, "phi", v_key, "mean_ipr"),
+            "periodic_ipr": median_metric(summary_by_v, "periodic_ab", v_key, "mean_ipr"),
+            "random_ipr": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "mean_ipr"),
+            "phi_entropy": median_metric(summary_by_v, "phi", v_key, "participation_entropy"),
+            "periodic_entropy": median_metric(summary_by_v, "periodic_ab", v_key, "participation_entropy"),
+            "random_entropy": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "participation_entropy"),
+        }
+        complete = all(value is not None for value in needed.values())
+        r_between = bool(complete and between(needed["phi_r"], needed["periodic_r"], needed["random_r"]))
+        ipr_between = bool(complete and between(needed["phi_ipr"], needed["periodic_ipr"], needed["random_ipr"]))
+        entropy_between = bool(complete and between(needed["phi_entropy"], needed["periodic_entropy"], needed["random_entropy"]))
+        separated_random = bool(
+            complete
+            and abs(needed["phi_r"] - needed["random_r"]) >= args.min_r_delta
+            and abs(needed["phi_ipr"] - needed["random_ipr"]) >= args.min_ipr_delta
+        )
+        joint = bool(r_between and ipr_between and separated_random)
+        classification["by_v"][v_key] = {
+            **needed,
+            "spacing_r_between": r_between,
+            "mean_ipr_between": ipr_between,
+            "participation_entropy_between": entropy_between,
+            "separated_from_random": separated_random,
+            "joint_boundary": joint,
+        }
+        if joint:
+            classification["joint_boundary_v"].append(float(v_value))
+
+    return {
+        "experiment": "aubry_boundary_phase_transport_gate",
+        "parameters": {
+            "ns": ns,
+            "phases": phases,
+            "v_min": args.v_min,
+            "v_max": args.v_max,
+            "v_step": args.v_step,
+            "central_fraction": args.central_fraction,
+            "random_trials": args.random_trials,
+            "seed": args.seed,
+            "min_r_delta": args.min_r_delta,
+            "min_ipr_delta": args.min_ipr_delta,
+        },
+        "observable_contract": {
+            "claim": "phi is a physical boundary state between periodic order and balanced random disorder only if spectral spacing and localization agree",
+            "observable": "spacing_r plus mean_ipr / participation_entropy on tight-binding spectra",
+            "operator": "binary quasiperiodic tight-binding Hamiltonian with phase row denominator",
+            "denominator": "N x phase x V x generator rows with balanced random controls",
+            "non_possible": "single-observable boundary, phase-aggregated critical value, or phi outside the periodic-random interval",
+        },
+        "classification": classification,
+        "summary_by_v": summary_by_v,
+        "rows": rows,
+    }
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--ns", default="89,144,233")
+    parser.add_argument("--phases", default="0,0.25,0.5,0.75")
+    parser.add_argument("--v-min", type=float, default=0.5)
+    parser.add_argument("--v-max", type=float, default=2.5)
+    parser.add_argument("--v-step", type=float, default=0.25)
+    parser.add_argument("--central-fraction", type=float, default=0.6)
+    parser.add_argument("--random-trials", type=int, default=6)
+    parser.add_argument("--seed", type=int, default=202605151745)
+    parser.add_argument("--min-r-delta", type=float, default=0.025)
+    parser.add_argument("--min-ipr-delta", type=float, default=0.0025)
+    parser.add_argument("--out", default="tools/data/aubry_boundary_phase_transport_gate_20260515_1745.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), encoding="utf-8")
+
+    compact = {
+        "classification": output["classification"],
+        "out": str(out),
+    }
+    print(json.dumps(compact, indent=2))
+
+
+if __name__ == "__main__":
+    main()

codex
Lo script è nel workspace. Ora lo compilo e lo eseguo sul perimetro corto ma non banale `N={89,144,233}`, quattro fasi e nove valori di V; se il tempo resta gestibile, userò direttamente questo deposito nel report.
exec
/bin/bash -lc 'python -m py_compile tools/exp_aubry_boundary_phase_transport_gate.py' in /opt/MM_D-ND
 succeeded in 0ms:
exec
/bin/bash -lc 'python tools/exp_aubry_boundary_phase_transport_gate.py --out tools/data/aubry_boundary_phase_transport_gate_20260515_1745.json' in /opt/MM_D-ND
diff --git a/tools/exp_aubry_boundary_phase_transport_gate.py b/tools/exp_aubry_boundary_phase_transport_gate.py
new file mode 100644
index 0000000000000000000000000000000000000000..8487656a64b34e688fb3f65c4708e1aa25469724
--- /dev/null
+++ b/tools/exp_aubry_boundary_phase_transport_gate.py
@@ -0,0 +1,284 @@
+#!/usr/bin/env python3
+"""
+Aubry/Fibonacci boundary phase transport gate.
+
+Projects the live BOUNDARY direction into a 1D tight-binding model with binary
+quasiperiodic potentials. The test is deliberately joint: a boundary return is
+accepted only when the phi stack is between periodic order and balanced random
+disorder for both spectral spacing and eigenstate localization.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+
+PHI = (1 + np.sqrt(5)) / 2
+SILVER = 1 + np.sqrt(2)
+BRONZE = 1 + np.sqrt(3)
+
+
+def sturmian_sequence(theta: float, n: int, phase: float) -> np.ndarray:
+    idx = np.arange(n + 1, dtype=float)
+    vals = np.floor(idx * theta + phase)
+    return np.diff(vals).astype(float)
+
+
+def periodic_sequence(n: int) -> np.ndarray:
+    return (np.arange(n) % 2).astype(float)
+
+
+def hamiltonian(diagonal: np.ndarray) -> np.ndarray:
+    n = len(diagonal)
+    matrix = np.diag(diagonal.astype(float))
+    off = np.ones(n - 1, dtype=float)
+    matrix += np.diag(off, 1) + np.diag(off, -1)
+    return matrix
+
+
+def central_slice(n: int, central_fraction: float) -> slice:
+    keep = max(8, int(round(n * central_fraction)))
+    keep = min(n, keep)
+    start = (n - keep) // 2
+    return slice(start, start + keep)
+
+
+def spacing_r(levels: np.ndarray, central_fraction: float) -> float | None:
+    levels = np.sort(np.asarray(levels, dtype=float))
+    central = levels[central_slice(len(levels), central_fraction)]
+    gaps = np.diff(central)
+    gaps = gaps[np.isfinite(gaps) & (gaps > 1e-12)]
+    if len(gaps) < 2:
+        return None
+    left = gaps[:-1]
+    right = gaps[1:]
+    return float(np.mean(np.minimum(left, right) / np.maximum(left, right)))
+
+
+def localization_metrics(vectors: np.ndarray, central_fraction: float) -> dict[str, float]:
+    n = vectors.shape[0]
+    subset = vectors[:, central_slice(n, central_fraction)]
+    probs = np.square(np.abs(subset))
+    ipr = np.sum(probs * probs, axis=0)
+    entropy = []
+    for col in range(probs.shape[1]):
+        p = probs[:, col]
+        p = p[p > 1e-15]
+        entropy.append(float(-np.sum(p * np.log(p)) / np.log(n)))
+    return {
+        "mean_ipr": float(np.mean(ipr)),
+        "median_ipr": float(np.median(ipr)),
+        "participation_entropy": float(np.mean(entropy)) if entropy else 0.0,
+    }
+
+
+def spectrum_row(
+    domain: str,
+    seq: np.ndarray,
+    n: int,
+    phase: float,
+    v_value: float,
+    central_fraction: float,
+    trial: int | None = None,
+) -> dict[str, Any]:
+    centered = seq - float(np.mean(seq))
+    levels, vectors = np.linalg.eigh(hamiltonian(v_value * centered))
+    metrics = localization_metrics(vectors, central_fraction)
+    row: dict[str, Any] = {
+        "domain": domain,
+        "N": n,
+        "phase": phase,
+        "V": v_value,
+        "ones": int(np.sum(seq)),
+        "spacing_r": spacing_r(levels, central_fraction),
+        **metrics,
+    }
+    if trial is not None:
+        row["trial"] = trial
+    return row
+
+
+def finite(values: list[float | None]) -> np.ndarray:
+    return np.array([v for v in values if v is not None and np.isfinite(v)], dtype=float)
+
+
+def aggregate(rows: list[dict[str, Any]]) -> dict[str, Any]:
+    out: dict[str, Any] = {"count": len(rows)}
+    for key in ["spacing_r", "mean_ipr", "median_ipr", "participation_entropy"]:
+        arr = finite([row.get(key) for row in rows])
+        if len(arr) == 0:
+            out[key] = {"count": 0}
+        else:
+            out[key] = {
+                "count": int(len(arr)),
+                "median": float(np.median(arr)),
+                "mean": float(np.mean(arr)),
+                "min": float(np.min(arr)),
+                "max": float(np.max(arr)),
+            }
+    return out
+
+
+def median_metric(summary: dict[str, Any], domain: str, v_key: str, metric: str) -> float | None:
+    value = summary.get(v_key, {}).get(domain, {}).get(metric, {})
+    if not isinstance(value, dict):
+        return None
+    median = value.get("median")
+    return float(median) if median is not None else None
+
+
+def between(value: float, left: float, right: float) -> bool:
+    return min(left, right) <= value <= max(left, right)
+
+
+def parse_csv_ints(value: str) -> list[int]:
+    return [int(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def parse_csv_floats(value: str) -> list[float]:
+    return [float(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    rng = np.random.default_rng(args.seed)
+    ns = parse_csv_ints(args.ns)
+    phases = parse_csv_floats(args.phases)
+    v_values = np.arange(args.v_min, args.v_max + args.v_step / 2, args.v_step)
+    domains = {
+        "phi": 1 / PHI,
+        "silver": 1 / SILVER,
+        "bronze": 1 / BRONZE,
+    }
+
+    rows: list[dict[str, Any]] = []
+    for n in ns:
+        for phase in phases:
+            phi_seq = sturmian_sequence(1 / PHI, n, phase)
+            ones = int(np.sum(phi_seq))
+            for v_value in v_values:
+                for domain, theta in domains.items():
+                    seq = sturmian_sequence(theta, n, phase)
+                    rows.append(spectrum_row(domain, seq, n, phase, float(v_value), args.central_fraction))
+
+                periodic = periodic_sequence(n)
+                rows.append(spectrum_row("periodic_ab", periodic, n, phase, float(v_value), args.central_fraction))
+
+                for trial in range(args.random_trials):
+                    seq = np.array([1.0] * ones + [0.0] * (n - ones), dtype=float)
+                    rng.shuffle(seq)
+                    rows.append(
+                        spectrum_row(
+                            "balanced_random_phi_density",
+                            seq,
+                            n,
+                            phase,
+                            float(v_value),
+                            args.central_fraction,
+                            trial=trial,
+                        )
+                    )
+
+    summary_by_v: dict[str, dict[str, Any]] = {}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        summary_by_v[v_key] = {}
+        for domain in sorted({row["domain"] for row in rows}):
+            subset = [row for row in rows if row["domain"] == domain and abs(row["V"] - v_value) < 1e-12]
+            summary_by_v[v_key][domain] = aggregate(subset)
+
+    classification: dict[str, Any] = {"joint_boundary_v": [], "by_v": {}}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        needed = {
+            "phi_r": median_metric(summary_by_v, "phi", v_key, "spacing_r"),
+            "periodic_r": median_metric(summary_by_v, "periodic_ab", v_key, "spacing_r"),
+            "random_r": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "spacing_r"),
+            "phi_ipr": median_metric(summary_by_v, "phi", v_key, "mean_ipr"),
+            "periodic_ipr": median_metric(summary_by_v, "periodic_ab", v_key, "mean_ipr"),
+            "random_ipr": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "mean_ipr"),
+            "phi_entropy": median_metric(summary_by_v, "phi", v_key, "participation_entropy"),
+            "periodic_entropy": median_metric(summary_by_v, "periodic_ab", v_key, "participation_entropy"),
+            "random_entropy": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "participation_entropy"),
+        }
+        complete = all(value is not None for value in needed.values())
+        r_between = bool(complete and between(needed["phi_r"], needed["periodic_r"], needed["random_r"]))
+        ipr_between = bool(complete and between(needed["phi_ipr"], needed["periodic_ipr"], needed["random_ipr"]))
+        entropy_between = bool(complete and between(needed["phi_entropy"], needed["periodic_entropy"], needed["random_entropy"]))
+        separated_random = bool(
+            complete
+            and abs(needed["phi_r"] - needed["random_r"]) >= args.min_r_delta
+            and abs(needed["phi_ipr"] - needed["random_ipr"]) >= args.min_ipr_delta
+        )
+        joint = bool(r_between and ipr_between and separated_random)
+        classification["by_v"][v_key] = {
+            **needed,
+            "spacing_r_between": r_between,
+            "mean_ipr_between": ipr_between,
+            "participation_entropy_between": entropy_between,
+            "separated_from_random": separated_random,
+            "joint_boundary": joint,
+        }
+        if joint:
+            classification["joint_boundary_v"].append(float(v_value))
+
+    return {
+        "experiment": "aubry_boundary_phase_transport_gate",
+        "parameters": {
+            "ns": ns,
+            "phases": phases,
+            "v_min": args.v_min,
+            "v_max": args.v_max,
+            "v_step": args.v_step,
+            "central_fraction": args.central_fraction,
+            "random_trials": args.random_trials,
+            "seed": args.seed,
+            "min_r_delta": args.min_r_delta,
+            "min_ipr_delta": args.min_ipr_delta,
+        },
+        "observable_contract": {
+            "claim": "phi is a physical boundary state between periodic order and balanced random disorder only if spectral spacing and localization agree",
+            "observable": "spacing_r plus mean_ipr / participation_entropy on tight-binding spectra",
+            "operator": "binary quasiperiodic tight-binding Hamiltonian with phase row denominator",
+            "denominator": "N x phase x V x generator rows with balanced random controls",
+            "non_possible": "single-observable boundary, phase-aggregated critical value, or phi outside the periodic-random interval",
+        },
+        "classification": classification,
+        "summary_by_v": summary_by_v,
+        "rows": rows,
+    }
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--ns", default="89,144,233")
+    parser.add_argument("--phases", default="0,0.25,0.5,0.75")
+    parser.add_argument("--v-min", type=float, default=0.5)
+    parser.add_argument("--v-max", type=float, default=2.5)
+    parser.add_argument("--v-step", type=float, default=0.25)
+    parser.add_argument("--central-fraction", type=float, default=0.6)
+    parser.add_argument("--random-trials", type=int, default=6)
+    parser.add_argument("--seed", type=int, default=202605151745)
+    parser.add_argument("--min-r-delta", type=float, default=0.025)
+    parser.add_argument("--min-ipr-delta", type=float, default=0.0025)
+    parser.add_argument("--out", default="tools/data/aubry_boundary_phase_transport_gate_20260515_1745.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), encoding="utf-8")
+
+    compact = {
+        "classification": output["classification"],
+        "out": str(out),
+    }
+    print(json.dumps(compact, indent=2))
+
+
+if __name__ == "__main__":
+    main()

diff --git a/tools/exp_aubry_boundary_phase_transport_gate.py b/tools/exp_aubry_boundary_phase_transport_gate.py
new file mode 100644
index 0000000000000000000000000000000000000000..8487656a64b34e688fb3f65c4708e1aa25469724
--- /dev/null
+++ b/tools/exp_aubry_boundary_phase_transport_gate.py
@@ -0,0 +1,284 @@
+#!/usr/bin/env python3
+"""
+Aubry/Fibonacci boundary phase transport gate.
+
+Projects the live BOUNDARY direction into a 1D tight-binding model with binary
+quasiperiodic potentials. The test is deliberately joint: a boundary return is
+accepted only when the phi stack is between periodic order and balanced random
+disorder for both spectral spacing and eigenstate localization.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+
+PHI = (1 + np.sqrt(5)) / 2
+SILVER = 1 + np.sqrt(2)
+BRONZE = 1 + np.sqrt(3)
+
+
+def sturmian_sequence(theta: float, n: int, phase: float) -> np.ndarray:
+    idx = np.arange(n + 1, dtype=float)
+    vals = np.floor(idx * theta + phase)
+    return np.diff(vals).astype(float)
+
+
+def periodic_sequence(n: int) -> np.ndarray:
+    return (np.arange(n) % 2).astype(float)
+
+
+def hamiltonian(diagonal: np.ndarray) -> np.ndarray:
+    n = len(diagonal)
+    matrix = np.diag(diagonal.astype(float))
+    off = np.ones(n - 1, dtype=float)
+    matrix += np.diag(off, 1) + np.diag(off, -1)
+    return matrix
+
+
+def central_slice(n: int, central_fraction: float) -> slice:
+    keep = max(8, int(round(n * central_fraction)))
+    keep = min(n, keep)
+    start = (n - keep) // 2
+    return slice(start, start + keep)
+
+
+def spacing_r(levels: np.ndarray, central_fraction: float) -> float | None:
+    levels = np.sort(np.asarray(levels, dtype=float))
+    central = levels[central_slice(len(levels), central_fraction)]
+    gaps = np.diff(central)
+    gaps = gaps[np.isfinite(gaps) & (gaps > 1e-12)]
+    if len(gaps) < 2:
+        return None
+    left = gaps[:-1]
+    right = gaps[1:]
+    return float(np.mean(np.minimum(left, right) / np.maximum(left, right)))
+
+
+def localization_metrics(vectors: np.ndarray, central_fraction: float) -> dict[str, float]:
+    n = vectors.shape[0]
+    subset = vectors[:, central_slice(n, central_fraction)]
+    probs = np.square(np.abs(subset))
+    ipr = np.sum(probs * probs, axis=0)
+    entropy = []
+    for col in range(probs.shape[1]):
+        p = probs[:, col]
+        p = p[p > 1e-15]
+        entropy.append(float(-np.sum(p * np.log(p)) / np.log(n)))
+    return {
+        "mean_ipr": float(np.mean(ipr)),
+        "median_ipr": float(np.median(ipr)),
+        "participation_entropy": float(np.mean(entropy)) if entropy else 0.0,
+    }
+
+
+def spectrum_row(
+    domain: str,
+    seq: np.ndarray,
+    n: int,
+    phase: float,
+    v_value: float,
+    central_fraction: float,
+    trial: int | None = None,
+) -> dict[str, Any]:
+    centered = seq - float(np.mean(seq))
+    levels, vectors = np.linalg.eigh(hamiltonian(v_value * centered))
+    metrics = localization_metrics(vectors, central_fraction)
+    row: dict[str, Any] = {
+        "domain": domain,
+        "N": n,
+        "phase": phase,
+        "V": v_value,
+        "ones": int(np.sum(seq)),
+        "spacing_r": spacing_r(levels, central_fraction),
+        **metrics,
+    }
+    if trial is not None:
+        row["trial"] = trial
+    return row
+
+
+def finite(values: list[float | None]) -> np.ndarray:
+    return np.array([v for v in values if v is not None and np.isfinite(v)], dtype=float)
+
+
+def aggregate(rows: list[dict[str, Any]]) -> dict[str, Any]:
+    out: dict[str, Any] = {"count": len(rows)}
+    for key in ["spacing_r", "mean_ipr", "median_ipr", "participation_entropy"]:
+        arr = finite([row.get(key) for row in rows])
+        if len(arr) == 0:
+            out[key] = {"count": 0}
+        else:
+            out[key] = {
+                "count": int(len(arr)),
+                "median": float(np.median(arr)),
+                "mean": float(np.mean(arr)),
+                "min": float(np.min(arr)),
+                "max": float(np.max(arr)),
+            }
+    return out
+
+
+def median_metric(summary: dict[str, Any], domain: str, v_key: str, metric: str) -> float | None:
+    value = summary.get(v_key, {}).get(domain, {}).get(metric, {})
+    if not isinstance(value, dict):
+        return None
+    median = value.get("median")
+    return float(median) if median is not None else None
+
+
+def between(value: float, left: float, right: float) -> bool:
+    return min(left, right) <= value <= max(left, right)
+
+
+def parse_csv_ints(value: str) -> list[int]:
+    return [int(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def parse_csv_floats(value: str) -> list[float]:
+    return [float(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    rng = np.random.default_rng(args.seed)
+    ns = parse_csv_ints(args.ns)
+    phases = parse_csv_floats(args.phases)
+    v_values = np.arange(args.v_min, args.v_max + args.v_step / 2, args.v_step)
+    domains = {
+        "phi": 1 / PHI,
+        "silver": 1 / SILVER,
+        "bronze": 1 / BRONZE,
+    }
+
+    rows: list[dict[str, Any]] = []
+    for n in ns:
+        for phase in phases:
+            phi_seq = sturmian_sequence(1 / PHI, n, phase)
+            ones = int(np.sum(phi_seq))
+            for v_value in v_values:
+                for domain, theta in domains.items():
+                    seq = sturmian_sequence(theta, n, phase)
+                    rows.append(spectrum_row(domain, seq, n, phase, float(v_value), args.central_fraction))
+
+                periodic = periodic_sequence(n)
+                rows.append(spectrum_row("periodic_ab", periodic, n, phase, float(v_value), args.central_fraction))
+
+                for trial in range(args.random_trials):
+                    seq = np.array([1.0] * ones + [0.0] * (n - ones), dtype=float)
+                    rng.shuffle(seq)
+                    rows.append(
+                        spectrum_row(
+                            "balanced_random_phi_density",
+                            seq,
+                            n,
+                            phase,
+                            float(v_value),
+                            args.central_fraction,
+                            trial=trial,
+                        )
+                    )
+
+    summary_by_v: dict[str, dict[str, Any]] = {}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        summary_by_v[v_key] = {}
+        for domain in sorted({row["domain"] for row in rows}):
+            subset = [row for row in rows if row["domain"] == domain and abs(row["V"] - v_value) < 1e-12]
+            summary_by_v[v_key][domain] = aggregate(subset)
+
+    classification: dict[str, Any] = {"joint_boundary_v": [], "by_v": {}}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        needed = {
+            "phi_r": median_metric(summary_by_v, "phi", v_key, "spacing_r"),
+            "periodic_r": median_metric(summary_by_v, "periodic_ab", v_key, "spacing_r"),
+            "random_r": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "spacing_r"),
+            "phi_ipr": median_metric(summary_by_v, "phi", v_key, "mean_ipr"),
+            "periodic_ipr": median_metric(summary_by_v, "periodic_ab", v_key, "mean_ipr"),
+            "random_ipr": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "mean_ipr"),
+            "phi_entropy": median_metric(summary_by_v, "phi", v_key, "participation_entropy"),
+            "periodic_entropy": median_metric(summary_by_v, "periodic_ab", v_key, "participation_entropy"),
+            "random_entropy": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "participation_entropy"),
+        }
+        complete = all(value is not None for value in needed.values())
+        r_between = bool(complete and between(needed["phi_r"], needed["periodic_r"], needed["random_r"]))
+        ipr_between = bool(complete and between(needed["phi_ipr"], needed["periodic_ipr"], needed["random_ipr"]))
+        entropy_between = bool(complete and between(needed["phi_entropy"], needed["periodic_entropy"], needed["random_entropy"]))
+        separated_random = bool(
+            complete
+            and abs(needed["phi_r"] - needed["random_r"]) >= args.min_r_delta
+            and abs(needed["phi_ipr"] - needed["random_ipr"]) >= args.min_ipr_delta
+        )
+        joint = bool(r_between and ipr_between and separated_random)
+        classification["by_v"][v_key] = {
+            **needed,
+            "spacing_r_between": r_between,
+            "mean_ipr_between": ipr_between,
+            "participation_entropy_between": entropy_between,
+            "separated_from_random": separated_random,
+            "joint_boundary": joint,
+        }
+        if joint:
+            classification["joint_boundary_v"].append(float(v_value))
+
+    return {
+        "experiment": "aubry_boundary_phase_transport_gate",
+        "parameters": {
+            "ns": ns,
+            "phases": phases,
+            "v_min": args.v_min,
+            "v_max": args.v_max,
+            "v_step": args.v_step,
+            "central_fraction": args.central_fraction,
+            "random_trials": args.random_trials,
+            "seed": args.seed,
+            "min_r_delta": args.min_r_delta,
+            "min_ipr_delta": args.min_ipr_delta,
+        },
+        "observable_contract": {
+            "claim": "phi is a physical boundary state between periodic order and balanced random disorder only if spectral spacing and localization agree",
+            "observable": "spacing_r plus mean_ipr / participation_entropy on tight-binding spectra",
+            "operator": "binary quasiperiodic tight-binding Hamiltonian with phase row denominator",
+            "denominator": "N x phase x V x generator rows with balanced random controls",
+            "non_possible": "single-observable boundary, phase-aggregated critical value, or phi outside the periodic-random interval",
+        },
+        "classification": classification,
+        "summary_by_v": summary_by_v,
+        "rows": rows,
+    }
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--ns", default="89,144,233")
+    parser.add_argument("--phases", default="0,0.25,0.5,0.75")
+    parser.add_argument("--v-min", type=float, default=0.5)
+    parser.add_argument("--v-max", type=float, default=2.5)
+    parser.add_argument("--v-step", type=float, default=0.25)
+    parser.add_argument("--central-fraction", type=float, default=0.6)
+    parser.add_argument("--random-trials", type=int, default=6)
+    parser.add_argument("--seed", type=int, default=202605151745)
+    parser.add_argument("--min-r-delta", type=float, default=0.025)
+    parser.add_argument("--min-ipr-delta", type=float, default=0.0025)
+    parser.add_argument("--out", default="tools/data/aubry_boundary_phase_transport_gate_20260515_1745.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), encoding="utf-8")
+
+    compact = {
+        "classification": output["classification"],
+        "out": str(out),
+    }
+    print(json.dumps(compact, indent=2))
+
+
+if __name__ == "__main__":
+    main()

 succeeded in 4374ms:
{
  "classification": {
    "joint_boundary_v": [
      0.5,
      0.75,
      1.0,
      1.25
    ],
    "by_v": {
      "V=0.500000": {
        "phi_r": 0.6315455456687511,
        "periodic_r": 0.93473132559123,
        "random_r": 0.5649611214834067,
        "phi_ipr": 0.015564435860744248,
        "periodic_ipr": 0.012381625459954044,
        "random_ipr": 0.021293942174575795,
        "phi_entropy": 0.8979674989304471,
        "periodic_entropy": 0.9167306599211724,
        "random_entropy": 0.8507868822111097,
        "spacing_r_between": true,
        "mean_ipr_between": true,
        "participation_entropy_between": true,
        "separated_from_random": true,
        "joint_boundary": true
      },
      "V=0.750000": {
        "phi_r": 0.5655135508322705,
        "periodic_r": 0.9287123059800185,
        "random_r": 0.4972051698053207,
        "phi_ipr": 0.020502940757562472,
        "periodic_ipr": 0.013280632649804192,
        "random_ipr": 0.035847648737949094,
        "phi_entropy": 0.8684051919057576,
        "periodic_entropy": 0.9064649983830769,
        "random_entropy": 0.7675913841243713,
        "spacing_r_between": true,
        "mean_ipr_between": true,
        "participation_entropy_between": true,
        "separated_from_random": true,
        "joint_boundary": true
      },
      "V=1.000000": {
        "phi_r": 0.510595505126252,
        "periodic_r": 0.9248483394643271,
        "random_r": 0.4554165607501404,
        "phi_ipr": 0.026788257889922777,
        "periodic_ipr": 0.014094833480884611,
        "random_ipr": 0.05227850651144411,
        "phi_entropy": 0.8379868909990273,
        "periodic_entropy": 0.8970181468829893,
        "random_entropy": 0.7024197556899432,
        "spacing_r_between": true,
        "mean_ipr_between": true,
        "participation_entropy_between": true,
        "separated_from_random": true,
        "joint_boundary": true
      },
      "V=1.250000": {
        "phi_r": 0.4682552996103564,
        "periodic_r": 0.9221509744917621,
        "random_r": 0.42482206245143156,
        "phi_ipr": 0.03420350836620539,
        "periodic_ipr": 0.014824798140118467,
        "random_ipr": 0.0766571239450646,
        "phi_entropy": 0.8086418829057892,
        "periodic_entropy": 0.8883928054214424,
        "random_entropy": 0.62961563469404,
        "spacing_r_between": true,
        "mean_ipr_between": true,
        "participation_entropy_between": true,
        "separated_from_random": true,
        "joint_boundary": true
      },
      "V=1.500000": {
        "phi_r": 0.43166916211243556,
        "periodic_r": 0.9201696191816466,
        "random_r": 0.41861850579027793,
        "phi_ipr": 0.042527126271949586,
        "periodic_ipr": 0.01547386698645259,
        "random_ipr": 0.09990943747286633,
        "phi_entropy": 0.7796506800462244,
        "periodic_entropy": 0.8805679191505092,
        "random_entropy": 0.5728776677152616,
        "spacing_r_between": true,
        "mean_ipr_between": true,
        "participation_entropy_between": true,
        "separated_from_random": false,
        "joint_boundary": false
      },
      "V=1.750000": {
        "phi_r": 0.39860584516801945,
        "periodic_r": 0.9186487253758985,
        "random_r": 0.4089931202605439,
        "phi_ipr": 0.050690957991219046,
        "periodic_ipr": 0.016047329512533216,
        "random_ipr": 0.12037949750431298,
        "phi_entropy": 0.7494447577298453,
        "periodic_entropy": 0.8735045144885485,
        "random_entropy": 0.5320240725450638,
        "spacing_r_between": false,
        "mean_ipr_between": true,
        "participation_entropy_between": true,
        "separated_from_random": false,
        "joint_boundary": false
      },
      "V=2.000000": {
        "phi_r": 0.3689012316913423,
        "periodic_r": 0.917458416331352,
        "random_r": 0.4042634174524693,
        "phi_ipr": 0.05974788169418087,
        "periodic_ipr": 0.016551644086459025,
        "random_ipr": 0.14369519062374259,
        "phi_entropy": 0.7193667630477759,
        "periodic_entropy": 0.8671516791780958,
        "random_entropy": 0.4881385384722424,
        "spacing_r_between": false,
        "mean_ipr_between": true,
        "participation_entropy_between": true,
        "separated_from_random": true,
        "joint_boundary": false
      },
      "V=2.250000": {
        "phi_r": 0.3449756225128279,
        "periodic_r": 0.9165000875965806,
        "random_r": 0.3832951581495308,
        "phi_ipr": 0.06762146886000694,
        "periodic_ipr": 0.016993784786685204,
        "random_ipr": 0.16620535552048876,
        "phi_entropy": 0.6910240437670524,
        "periodic_entropy": 0.8614518419225813,
        "random_entropy": 0.45821180446017695,
        "spacing_r_between": false,
        "mean_ipr_between": true,
        "participation_entropy_between": true,
        "separated_from_random": true,
        "joint_boundary": false
      },
      "V=2.500000": {
        "phi_r": 0.3267076027756502,
        "periodic_r": 0.9157143561748411,
        "random_r": 0.3801463413912999,
        "phi_ipr": 0.07509582508093816,
        "periodic_ipr": 0.017380749747470742,
        "random_ipr": 0.1890504000106138,
        "phi_entropy": 0.6649819375926569,
        "periodic_entropy": 0.8563449925747482,
        "random_entropy": 0.4267693896350999,
        "spacing_r_between": false,
        "mean_ipr_between": true,
        "participation_entropy_between": true,
        "separated_from_random": true,
        "joint_boundary": false
      }
    }
  },
  "out": "tools/data/aubry_boundary_phase_transport_gate_20260515_1745.json"
}

codex
Il risultato è discriminante: `phi` è terzo incluso congiunto solo per `V=0.5..1.25`; sopra `V=1.5` la localizzazione resta intermedia ma `spacing_r` esce dal segmento periodico-random. Scrivo il report come vincolo di soglia, non come universalità fisica.
exec
/bin/bash -lc "python - <<'PY'
import json
p='tools/data/aubry_boundary_phase_transport_gate_20260515_1745.json'
data=json.load(open(p))
for v, row in data['classification']['by_v'].items():
    print(v, row['joint_boundary'], row['spacing_r_between'], row['mean_ipr_between'], row['separated_from_random'], f\"r phi/per/rand={row['phi_r']:.3f}/{row['periodic_r']:.3f}/{row['random_r']:.3f}\", f\"ipr={row['phi_ipr']:.4f}/{row['periodic_ipr']:.4f}/{row['random_ipr']:.4f}\", f\"ent={row['phi_entropy']:.3f}/{row['periodic_entropy']:.3f}/{row['random_entropy']:.3f}\")
PY" in /opt/MM_D-ND
 succeeded in 0ms:
V=0.500000 True True True True r phi/per/rand=0.632/0.935/0.565 ipr=0.0156/0.0124/0.0213 ent=0.898/0.917/0.851
V=0.750000 True True True True r phi/per/rand=0.566/0.929/0.497 ipr=0.0205/0.0133/0.0358 ent=0.868/0.906/0.768
V=1.000000 True True True True r phi/per/rand=0.511/0.925/0.455 ipr=0.0268/0.0141/0.0523 ent=0.838/0.897/0.702
V=1.250000 True True True True r phi/per/rand=0.468/0.922/0.425 ipr=0.0342/0.0148/0.0767 ent=0.809/0.888/0.630
V=1.500000 False True True False r phi/per/rand=0.432/0.920/0.419 ipr=0.0425/0.0155/0.0999 ent=0.780/0.881/0.573
V=1.750000 False False True False r phi/per/rand=0.399/0.919/0.409 ipr=0.0507/0.0160/0.1204 ent=0.749/0.874/0.532
V=2.000000 False False True True r phi/per/rand=0.369/0.917/0.404 ipr=0.0597/0.0166/0.1437 ent=0.719/0.867/0.488
V=2.250000 False False True True r phi/per/rand=0.345/0.917/0.383 ipr=0.0676/0.0170/0.1662 ent=0.691/0.861/0.458
V=2.500000 False False True True r phi/per/rand=0.327/0.916/0.380 ipr=0.0751/0.0174/0.1891 ent=0.665/0.856/0.427

diff --git a/tools/exp_aubry_boundary_phase_transport_gate.py b/tools/exp_aubry_boundary_phase_transport_gate.py
new file mode 100644
index 0000000000000000000000000000000000000000..8487656a64b34e688fb3f65c4708e1aa25469724
--- /dev/null
+++ b/tools/exp_aubry_boundary_phase_transport_gate.py
@@ -0,0 +1,284 @@
+#!/usr/bin/env python3
+"""
+Aubry/Fibonacci boundary phase transport gate.
+
+Projects the live BOUNDARY direction into a 1D tight-binding model with binary
+quasiperiodic potentials. The test is deliberately joint: a boundary return is
+accepted only when the phi stack is between periodic order and balanced random
+disorder for both spectral spacing and eigenstate localization.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+
+PHI = (1 + np.sqrt(5)) / 2
+SILVER = 1 + np.sqrt(2)
+BRONZE = 1 + np.sqrt(3)
+
+
+def sturmian_sequence(theta: float, n: int, phase: float) -> np.ndarray:
+    idx = np.arange(n + 1, dtype=float)
+    vals = np.floor(idx * theta + phase)
+    return np.diff(vals).astype(float)
+
+
+def periodic_sequence(n: int) -> np.ndarray:
+    return (np.arange(n) % 2).astype(float)
+
+
+def hamiltonian(diagonal: np.ndarray) -> np.ndarray:
+    n = len(diagonal)
+    matrix = np.diag(diagonal.astype(float))
+    off = np.ones(n - 1, dtype=float)
+    matrix += np.diag(off, 1) + np.diag(off, -1)
+    return matrix
+
+
+def central_slice(n: int, central_fraction: float) -> slice:
+    keep = max(8, int(round(n * central_fraction)))
+    keep = min(n, keep)
+    start = (n - keep) // 2
+    return slice(start, start + keep)
+
+
+def spacing_r(levels: np.ndarray, central_fraction: float) -> float | None:
+    levels = np.sort(np.asarray(levels, dtype=float))
+    central = levels[central_slice(len(levels), central_fraction)]
+    gaps = np.diff(central)
+    gaps = gaps[np.isfinite(gaps) & (gaps > 1e-12)]
+    if len(gaps) < 2:
+        return None
+    left = gaps[:-1]
+    right = gaps[1:]
+    return float(np.mean(np.minimum(left, right) / np.maximum(left, right)))
+
+
+def localization_metrics(vectors: np.ndarray, central_fraction: float) -> dict[str, float]:
+    n = vectors.shape[0]
+    subset = vectors[:, central_slice(n, central_fraction)]
+    probs = np.square(np.abs(subset))
+    ipr = np.sum(probs * probs, axis=0)
+    entropy = []
+    for col in range(probs.shape[1]):
+        p = probs[:, col]
+        p = p[p > 1e-15]
+        entropy.append(float(-np.sum(p * np.log(p)) / np.log(n)))
+    return {
+        "mean_ipr": float(np.mean(ipr)),
+        "median_ipr": float(np.median(ipr)),
+        "participation_entropy": float(np.mean(entropy)) if entropy else 0.0,
+    }
+
+
+def spectrum_row(
+    domain: str,
+    seq: np.ndarray,
+    n: int,
+    phase: float,
+    v_value: float,
+    central_fraction: float,
+    trial: int | None = None,
+) -> dict[str, Any]:
+    centered = seq - float(np.mean(seq))
+    levels, vectors = np.linalg.eigh(hamiltonian(v_value * centered))
+    metrics = localization_metrics(vectors, central_fraction)
+    row: dict[str, Any] = {
+        "domain": domain,
+        "N": n,
+        "phase": phase,
+        "V": v_value,
+        "ones": int(np.sum(seq)),
+        "spacing_r": spacing_r(levels, central_fraction),
+        **metrics,
+    }
+    if trial is not None:
+        row["trial"] = trial
+    return row
+
+
+def finite(values: list[float | None]) -> np.ndarray:
+    return np.array([v for v in values if v is not None and np.isfinite(v)], dtype=float)
+
+
+def aggregate(rows: list[dict[str, Any]]) -> dict[str, Any]:
+    out: dict[str, Any] = {"count": len(rows)}
+    for key in ["spacing_r", "mean_ipr", "median_ipr", "participation_entropy"]:
+        arr = finite([row.get(key) for row in rows])
+        if len(arr) == 0:
+            out[key] = {"count": 0}
+        else:
+            out[key] = {
+                "count": int(len(arr)),
+                "median": float(np.median(arr)),
+                "mean": float(np.mean(arr)),
+                "min": float(np.min(arr)),
+                "max": float(np.max(arr)),
+            }
+    return out
+
+
+def median_metric(summary: dict[str, Any], domain: str, v_key: str, metric: str) -> float | None:
+    value = summary.get(v_key, {}).get(domain, {}).get(metric, {})
+    if not isinstance(value, dict):
+        return None
+    median = value.get("median")
+    return float(median) if median is not None else None
+
+
+def between(value: float, left: float, right: float) -> bool:
+    return min(left, right) <= value <= max(left, right)
+
+
+def parse_csv_ints(value: str) -> list[int]:
+    return [int(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def parse_csv_floats(value: str) -> list[float]:
+    return [float(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    rng = np.random.default_rng(args.seed)
+    ns = parse_csv_ints(args.ns)
+    phases = parse_csv_floats(args.phases)
+    v_values = np.arange(args.v_min, args.v_max + args.v_step / 2, args.v_step)
+    domains = {
+        "phi": 1 / PHI,
+        "silver": 1 / SILVER,
+        "bronze": 1 / BRONZE,
+    }
+
+    rows: list[dict[str, Any]] = []
+    for n in ns:
+        for phase in phases:
+            phi_seq = sturmian_sequence(1 / PHI, n, phase)
+            ones = int(np.sum(phi_seq))
+            for v_value in v_values:
+                for domain, theta in domains.items():
+                    seq = sturmian_sequence(theta, n, phase)
+                    rows.append(spectrum_row(domain, seq, n, phase, float(v_value), args.central_fraction))
+
+                periodic = periodic_sequence(n)
+                rows.append(spectrum_row("periodic_ab", periodic, n, phase, float(v_value), args.central_fraction))
+
+                for trial in range(args.random_trials):
+                    seq = np.array([1.0] * ones + [0.0] * (n - ones), dtype=float)
+                    rng.shuffle(seq)
+                    rows.append(
+                        spectrum_row(
+                            "balanced_random_phi_density",
+                            seq,
+                            n,
+                            phase,
+                            float(v_value),
+                            args.central_fraction,
+                            trial=trial,
+                        )
+                    )
+
+    summary_by_v: dict[str, dict[str, Any]] = {}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        summary_by_v[v_key] = {}
+        for domain in sorted({row["domain"] for row in rows}):
+            subset = [row for row in rows if row["domain"] == domain and abs(row["V"] - v_value) < 1e-12]
+            summary_by_v[v_key][domain] = aggregate(subset)
+
+    classification: dict[str, Any] = {"joint_boundary_v": [], "by_v": {}}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        needed = {
+            "phi_r": median_metric(summary_by_v, "phi", v_key, "spacing_r"),
+            "periodic_r": median_metric(summary_by_v, "periodic_ab", v_key, "spacing_r"),
+            "random_r": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "spacing_r"),
+            "phi_ipr": median_metric(summary_by_v, "phi", v_key, "mean_ipr"),
+            "periodic_ipr": median_metric(summary_by_v, "periodic_ab", v_key, "mean_ipr"),
+            "random_ipr": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "mean_ipr"),
+            "phi_entropy": median_metric(summary_by_v, "phi", v_key, "participation_entropy"),
+            "periodic_entropy": median_metric(summary_by_v, "periodic_ab", v_key, "participation_entropy"),
+            "random_entropy": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "participation_entropy"),
+        }
+        complete = all(value is not None for value in needed.values())
+        r_between = bool(complete and between(needed["phi_r"], needed["periodic_r"], needed["random_r"]))
+        ipr_between = bool(complete and between(needed["phi_ipr"], needed["periodic_ipr"], needed["random_ipr"]))
+        entropy_between = bool(complete and between(needed["phi_entropy"], needed["periodic_entropy"], needed["random_entropy"]))
+        separated_random = bool(
+            complete
+            and abs(needed["phi_r"] - needed["random_r"]) >= args.min_r_delta
+            and abs(needed["phi_ipr"] - needed["random_ipr"]) >= args.min_ipr_delta
+        )
+        joint = bool(r_between and ipr_between and separated_random)
+        classification["by_v"][v_key] = {
+            **needed,
+            "spacing_r_between": r_between,
+            "mean_ipr_between": ipr_between,
+            "participation_entropy_between": entropy_between,
+            "separated_from_random": separated_random,
+            "joint_boundary": joint,
+        }
+        if joint:
+            classification["joint_boundary_v"].append(float(v_value))
+
+    return {
+        "experiment": "aubry_boundary_phase_transport_gate",
+        "parameters": {
+            "ns": ns,
+            "phases": phases,
+            "v_min": args.v_min,
+            "v_max": args.v_max,
+            "v_step": args.v_step,
+            "central_fraction": args.central_fraction,
+            "random_trials": args.random_trials,
+            "seed": args.seed,
+            "min_r_delta": args.min_r_delta,
+            "min_ipr_delta": args.min_ipr_delta,
+        },
+        "observable_contract": {
+            "claim": "phi is a physical boundary state between periodic order and balanced random disorder only if spectral spacing and localization agree",
+            "observable": "spacing_r plus mean_ipr / participation_entropy on tight-binding spectra",
+            "operator": "binary quasiperiodic tight-binding Hamiltonian with phase row denominator",
+            "denominator": "N x phase x V x generator rows with balanced random controls",
+            "non_possible": "single-observable boundary, phase-aggregated critical value, or phi outside the periodic-random interval",
+        },
+        "classification": classification,
+        "summary_by_v": summary_by_v,
+        "rows": rows,
+    }
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--ns", default="89,144,233")
+    parser.add_argument("--phases", default="0,0.25,0.5,0.75")
+    parser.add_argument("--v-min", type=float, default=0.5)
+    parser.add_argument("--v-max", type=float, default=2.5)
+    parser.add_argument("--v-step", type=float, default=0.25)
+    parser.add_argument("--central-fraction", type=float, default=0.6)
+    parser.add_argument("--random-trials", type=int, default=6)
+    parser.add_argument("--seed", type=int, default=202605151745)
+    parser.add_argument("--min-r-delta", type=float, default=0.025)
+    parser.add_argument("--min-ipr-delta", type=float, default=0.0025)
+    parser.add_argument("--out", default="tools/data/aubry_boundary_phase_transport_gate_20260515_1745.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), encoding="utf-8")
+
+    compact = {
+        "classification": output["classification"],
+        "out": str(out),
+    }
+    print(json.dumps(compact, indent=2))
+
+
+if __name__ == "__main__":
+    main()

apply patch
patch: completed
/opt/MM_D-ND/tools/data/reports/agent_20260515_1745.md
diff --git a/tools/data/reports/agent_20260515_1745.md b/tools/data/reports/agent_20260515_1745.md
new file mode 100644
index 0000000000000000000000000000000000000000..f1d0abc77f960601d7218d6ad380c4440fe243f6
--- /dev/null
+++ b/tools/data/reports/agent_20260515_1745.md
@@ -0,0 +1,105 @@
+# Agent Report - Aubry Boundary Phase Transport Gate
+**Date**: 2026-05-15 17:45  
+**Piano**: 118  
+**Tension explored**: BOUNDARY / TENS_SCALE_TRASCENDENZA_LIMITE  
+**verdict**: CONSTRAINT - nel ritorno tight-binding `phi` e' terzo incluso congiunto solo per V=0.50..1.25; oltre V=1.50 la localizzazione resta intermedia ma la statistica spettrale rompe il bicono.  
+observables_registry: none; dedicated observables only  
+observables_used: [spacing_r, mean_ipr, median_ipr, participation_entropy]  
+**observable_contract**: claim=`phi` e' stato fisico di confine tra ordine periodico e disordine random solo se statistica spettrale e localizzazione concordano; observable=`spacing_r` + `mean_ipr`/`participation_entropy`; operator=`tools/exp_aubry_boundary_phase_transport_gate.py`; generator=Hamiltoniana tight-binding binaria con sequenze phi/silver/bronze, periodico AB e random bilanciato a densita phi; denominator=N={89,144,233} x phase={0,0.25,0.5,0.75} x V=0.50..2.50 step 0.25 x generator, random_trials=6; non_possible=promuovere confine fisico da una sola osservabile, da phase0, o da un valore critico aggregato; not_tested=limite asintotico, modello Aubry-Andre coseno canonico, dati sperimentali, classi GUE/Poisson universali dirette.
+
+## Respiro fuori-tempo
+
+- **Combo**: A2 confine det=-1 + A9 terzo incluso + A11 combo + QxG continuo/discreto + TxQ matrice densita come lettura spettrale + tensione `BOUNDARY` 8 GUE / 5 Poisson.
+- **Dipolo / punto-zero**: repulsione spettrale / localizzazione degli autostati. Punto-zero = Hamiltoniana prima che una metrica singola decida se il dominio e' GUE-like, Poisson-like o di confine.
+- **Piano superiore**: geometria dei campi su reticolo; la fase del potenziale e' parte del bordo, non rumore da mediare.
+- **Operatori laterali scelti**: Hamiltonian spectrum, boundary operator, phase/parallel-transport come memoria del denominatore.
+- **Contaminazione cognitiva**: CE-0001 / KSAR usato come reiterazione del deposito 17:24 nel perimetro fisico indicato dalla consecutio; CE-0038 usato come equilibrio tra estremi, ma reso falsificabile da due osservabili non collineari.
+- **Proto-ipotesi**: se il confine e' terzo incluso operativo, `phi` deve stare tra periodico e random sia nello spacing sia nella localizzazione; se solo una delle due osservabili resta intermedia, il confine e' componente, non stato fisico chiuso.
+- **Proiezione**: diagonalizzo Hamiltoniane tight-binding binarie e confronto mediane row-aligned per V, taglia, fase e generatore.
+
+## Aderenza alla direzione
+
+- `relation`: follows_direction
+- `why`: segue la direzione viva "8 domini GUE, 5 Poisson - il confine e' il terzo incluso operativo" portando il confine in un ritorno fisico dove GUE/Poisson diventano spacing/localizzazione.
+- `not_drift`: non riapre prime/mod6, non usa selector legacy, non rifitta `V_c`; consuma `TENS_SCALE_TRASCENDENZA_LIMITE` solo come nodo regressivo di denominatore e fase.
+
+## Claim Under Test
+
+> Nel modello tight-binding binario, `phi` realizza un terzo incluso fisico tra periodico e random solo sui valori di V dove `spacing_r` e localizzazione lo collocano insieme dentro il segmento periodico-random e separato dal random.
+
+## Question
+
+Il ritorno Aubry/Fibonacci chiude il confine come stato fisico congiunto, oppure lo conserva solo come componente osservabile-specifica?
+
+## Ritorno fisico
+
+- **Punto fisico sorgente**: confine GUE/Poisson come attrito tra repulsione dei livelli e indipendenza/localizzazione.
+- **Attraversamento matematico**: sequenze Sturmian e random bilanciato proiettati in Hamiltoniane tridiagonali con potenziale binario centrato.
+- **Punto fisico di ritorno**: modello tight-binding 1D misurabile tramite spettro e autostati.
+- **Relazione nuova**: il confine non e' un `V_c` unico; e' una finestra di V dove due lettori indipendenti concordano.
+- **Osservabile/test fisico possibile**: misurare spacing e IPR nello stesso reticolo sintetico/fotonico con fase controllata.
+- **Se fallisce**: resta vincolo sul denominatore dei futuri ritorni fisici, non scoperta promuovibile.
+
+## Experiment Design
+
+- `python tools/dnd_scenario.py --best` eseguito: massimo discriminante `TENS_SCALE_TRASCENDENZA_LIMITE`.
+- `python -m py_compile tools/exp_aubry_boundary_phase_transport_gate.py` completato.
+- Run: `python tools/exp_aubry_boundary_phase_transport_gate.py --out tools/data/aubry_boundary_phase_transport_gate_20260515_1745.json`.
+- Perimetro deterministico: 108 righe per ciascun generatore phi/silver/bronze/periodic_ab.
+- Perimetro random: 648 righe balanced random a densita phi.
+- La misura serve la combo perche' rende non-possibile una chiusura del confine da una sola metrica.
+
+## Results
+
+| V | joint | r_between | ipr_between | sep_random | phi_r | periodic_r | random_r | phi_ipr | periodic_ipr | random_ipr |
+|---:|---|---|---|---|---:|---:|---:|---:|---:|---:|
+| 0.50 | true | true | true | true | 0.632 | 0.935 | 0.565 | 0.0156 | 0.0124 | 0.0213 |
+| 0.75 | true | true | true | true | 0.566 | 0.929 | 0.497 | 0.0205 | 0.0133 | 0.0358 |
+| 1.00 | true | true | true | true | 0.511 | 0.925 | 0.455 | 0.0268 | 0.0141 | 0.0523 |
+| 1.25 | true | true | true | true | 0.468 | 0.922 | 0.425 | 0.0342 | 0.0148 | 0.0767 |
+| 1.50 | false | true | true | false | 0.432 | 0.920 | 0.419 | 0.0425 | 0.0155 | 0.0999 |
+| 1.75 | false | false | true | false | 0.399 | 0.919 | 0.409 | 0.0507 | 0.0160 | 0.1204 |
+| 2.00 | false | false | true | true | 0.369 | 0.917 | 0.404 | 0.0597 | 0.0166 | 0.1437 |
+| 2.25 | false | false | true | true | 0.345 | 0.917 | 0.383 | 0.0676 | 0.0170 | 0.1662 |
+| 2.50 | false | false | true | true | 0.327 | 0.916 | 0.380 | 0.0751 | 0.0174 | 0.1891 |
+
+## Key Findings
+
+1. **Verificato**: `phi` chiude il bicono congiunto per V=0.50, 0.75, 1.00, 1.25: `spacing_r` e `mean_ipr` sono entrambi tra periodico e random, con separazione dal random.
+2. **Verificato**: a V=1.50 la posizione intermedia resta vera per entrambe le osservabili, ma la separazione dal random cade sotto soglia; non e' boundary joint promuovibile.
+3. **Verificato**: da V=1.75 in poi `spacing_r(phi)` scende fuori dal segmento periodico-random, mentre `mean_ipr` resta intermedio fino a V=2.50.
+4. **Inferito**: la finestra fisica del confine e' `0.50 <= V <= 1.25` nel perimetro misurato. Oltre quella soglia, il confine si scinde: localizzazione conserva il terzo incluso, spacing no.
+
+## Verdict
+
+**CONSTRAINT**.
+
+Il ritorno Aubry/Fibonacci trasferisce il confine BOUNDARY solo come finestra di accoppiamento. La formulazione valida e': nel perimetro misurato, `phi` e' terzo incluso fisico congiunto per V=0.50..1.25; sopra V=1.50 non si puo' parlare di stato di confine chiuso, ma di divergenza tra lettore spettrale e lettore di localizzazione.
+
+## Bicono della scoperta
+
+- **Due radici**: spacing tra livelli / localizzazione degli autostati.
+- **Singolare**: Hamiltoniana row-local `N,phase,V,generator` prima della classificazione.
+- **Invariante di passaggio**: ogni ritorno fisico del confine deve dichiarare almeno due osservabili non collineari e la finestra di parametro dove concordano.
+- **Campo di possibilita**: possibile = progettare test fisici con finestra V e fase controllata; non-possibile = usare `V_c`, `T_mean`, stopband o spacing singolo come prova di terzo incluso globale.
+
+## Consecutio
+
+Il prossimo ciclo puo' usare `exp_aubry_boundary_phase_transport_gate.py` come gate per ritorni fisici: cercare se la finestra V=0.50..1.25 sopravvive a N piu grandi o a un potenziale Aubry-Andre coseno. Se la finestra si muove, il claim resta finestra row-local; se resta stabile, diventa candidato ponte QxG fisico.
+
+## Ricadute pratiche
+
+ssp_value: yes. Lo script produce un gate riusabile per confrontare spacing e localizzazione su reticoli sintetici prima di costruire demo o visualizzazioni del confine.
+
+## Telemetria
+
+- No API paid run: `ANTHROPIC_API_KEY` e `OPENAI_API_KEY` non presenti nell'ambiente.
+- Nessun update del seme.
+- Nessun runtime pointer mutation.
+- Nessuna promozione e nessun public sync.
+
+## Files
+
+- `tools/exp_aubry_boundary_phase_transport_gate.py`
+- `tools/data/aubry_boundary_phase_transport_gate_20260515_1745.json`
+- `tools/data/reports/agent_20260515_1745.md`
diff --git a/tools/exp_aubry_boundary_phase_transport_gate.py b/tools/exp_aubry_boundary_phase_transport_gate.py
new file mode 100644
index 0000000000000000000000000000000000000000..8487656a64b34e688fb3f65c4708e1aa25469724
--- /dev/null
+++ b/tools/exp_aubry_boundary_phase_transport_gate.py
@@ -0,0 +1,284 @@
+#!/usr/bin/env python3
+"""
+Aubry/Fibonacci boundary phase transport gate.
+
+Projects the live BOUNDARY direction into a 1D tight-binding model with binary
+quasiperiodic potentials. The test is deliberately joint: a boundary return is
+accepted only when the phi stack is between periodic order and balanced random
+disorder for both spectral spacing and eigenstate localization.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+
+PHI = (1 + np.sqrt(5)) / 2
+SILVER = 1 + np.sqrt(2)
+BRONZE = 1 + np.sqrt(3)
+
+
+def sturmian_sequence(theta: float, n: int, phase: float) -> np.ndarray:
+    idx = np.arange(n + 1, dtype=float)
+    vals = np.floor(idx * theta + phase)
+    return np.diff(vals).astype(float)
+
+
+def periodic_sequence(n: int) -> np.ndarray:
+    return (np.arange(n) % 2).astype(float)
+
+
+def hamiltonian(diagonal: np.ndarray) -> np.ndarray:
+    n = len(diagonal)
+    matrix = np.diag(diagonal.astype(float))
+    off = np.ones(n - 1, dtype=float)
+    matrix += np.diag(off, 1) + np.diag(off, -1)
+    return matrix
+
+
+def central_slice(n: int, central_fraction: float) -> slice:
+    keep = max(8, int(round(n * central_fraction)))
+    keep = min(n, keep)
+    start = (n - keep) // 2
+    return slice(start, start + keep)
+
+
+def spacing_r(levels: np.ndarray, central_fraction: float) -> float | None:
+    levels = np.sort(np.asarray(levels, dtype=float))
+    central = levels[central_slice(len(levels), central_fraction)]
+    gaps = np.diff(central)
+    gaps = gaps[np.isfinite(gaps) & (gaps > 1e-12)]
+    if len(gaps) < 2:
+        return None
+    left = gaps[:-1]
+    right = gaps[1:]
+    return float(np.mean(np.minimum(left, right) / np.maximum(left, right)))
+
+
+def localization_metrics(vectors: np.ndarray, central_fraction: float) -> dict[str, float]:
+    n = vectors.shape[0]
+    subset = vectors[:, central_slice(n, central_fraction)]
+    probs = np.square(np.abs(subset))
+    ipr = np.sum(probs * probs, axis=0)
+    entropy = []
+    for col in range(probs.shape[1]):
+        p = probs[:, col]
+        p = p[p > 1e-15]
+        entropy.append(float(-np.sum(p * np.log(p)) / np.log(n)))
+    return {
+        "mean_ipr": float(np.mean(ipr)),
+        "median_ipr": float(np.median(ipr)),
+        "participation_entropy": float(np.mean(entropy)) if entropy else 0.0,
+    }
+
+
+def spectrum_row(
+    domain: str,
+    seq: np.ndarray,
+    n: int,
+    phase: float,
+    v_value: float,
+    central_fraction: float,
+    trial: int | None = None,
+) -> dict[str, Any]:
+    centered = seq - float(np.mean(seq))
+    levels, vectors = np.linalg.eigh(hamiltonian(v_value * centered))
+    metrics = localization_metrics(vectors, central_fraction)
+    row: dict[str, Any] = {
+        "domain": domain,
+        "N": n,
+        "phase": phase,
+        "V": v_value,
+        "ones": int(np.sum(seq)),
+        "spacing_r": spacing_r(levels, central_fraction),
+        **metrics,
+    }
+    if trial is not None:
+        row["trial"] = trial
+    return row
+
+
+def finite(values: list[float | None]) -> np.ndarray:
+    return np.array([v for v in values if v is not None and np.isfinite(v)], dtype=float)
+
+
+def aggregate(rows: list[dict[str, Any]]) -> dict[str, Any]:
+    out: dict[str, Any] = {"count": len(rows)}
+    for key in ["spacing_r", "mean_ipr", "median_ipr", "participation_entropy"]:
+        arr = finite([row.get(key) for row in rows])
+        if len(arr) == 0:
+            out[key] = {"count": 0}
+        else:
+            out[key] = {
+                "count": int(len(arr)),
+                "median": float(np.median(arr)),
+                "mean": float(np.mean(arr)),
+                "min": float(np.min(arr)),
+                "max": float(np.max(arr)),
+            }
+    return out
+
+
+def median_metric(summary: dict[str, Any], domain: str, v_key: str, metric: str) -> float | None:
+    value = summary.get(v_key, {}).get(domain, {}).get(metric, {})
+    if not isinstance(value, dict):
+        return None
+    median = value.get("median")
+    return float(median) if median is not None else None
+
+
+def between(value: float, left: float, right: float) -> bool:
+    return min(left, right) <= value <= max(left, right)
+
+
+def parse_csv_ints(value: str) -> list[int]:
+    return [int(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def parse_csv_floats(value: str) -> list[float]:
+    return [float(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    rng = np.random.default_rng(args.seed)
+    ns = parse_csv_ints(args.ns)
+    phases = parse_csv_floats(args.phases)
+    v_values = np.arange(args.v_min, args.v_max + args.v_step / 2, args.v_step)
+    domains = {
+        "phi": 1 / PHI,
+        "silver": 1 / SILVER,
+        "bronze": 1 / BRONZE,
+    }
+
+    rows: list[dict[str, Any]] = []
+    for n in ns:
+        for phase in phases:
+            phi_seq = sturmian_sequence(1 / PHI, n, phase)
+            ones = int(np.sum(phi_seq))
+            for v_value in v_values:
+                for domain, theta in domains.items():
+                    seq = sturmian_sequence(theta, n, phase)
+                    rows.append(spectrum_row(domain, seq, n, phase, float(v_value), args.central_fraction))
+
+                periodic = periodic_sequence(n)
+                rows.append(spectrum_row("periodic_ab", periodic, n, phase, float(v_value), args.central_fraction))
+
+                for trial in range(args.random_trials):
+                    seq = np.array([1.0] * ones + [0.0] * (n - ones), dtype=float)
+                    rng.shuffle(seq)
+                    rows.append(
+                        spectrum_row(
+                            "balanced_random_phi_density",
+                            seq,
+                            n,
+                            phase,
+                            float(v_value),
+                            args.central_fraction,
+                            trial=trial,
+                        )
+                    )
+
+    summary_by_v: dict[str, dict[str, Any]] = {}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        summary_by_v[v_key] = {}
+        for domain in sorted({row["domain"] for row in rows}):
+            subset = [row for row in rows if row["domain"] == domain and abs(row["V"] - v_value) < 1e-12]
+            summary_by_v[v_key][domain] = aggregate(subset)
+
+    classification: dict[str, Any] = {"joint_boundary_v": [], "by_v": {}}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        needed = {
+            "phi_r": median_metric(summary_by_v, "phi", v_key, "spacing_r"),
+            "periodic_r": median_metric(summary_by_v, "periodic_ab", v_key, "spacing_r"),
+            "random_r": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "spacing_r"),
+            "phi_ipr": median_metric(summary_by_v, "phi", v_key, "mean_ipr"),
+            "periodic_ipr": median_metric(summary_by_v, "periodic_ab", v_key, "mean_ipr"),
+            "random_ipr": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "mean_ipr"),
+            "phi_entropy": median_metric(summary_by_v, "phi", v_key, "participation_entropy"),
+            "periodic_entropy": median_metric(summary_by_v, "periodic_ab", v_key, "participation_entropy"),
+            "random_entropy": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "participation_entropy"),
+        }
+        complete = all(value is not None for value in needed.values())
+        r_between = bool(complete and between(needed["phi_r"], needed["periodic_r"], needed["random_r"]))
+        ipr_between = bool(complete and between(needed["phi_ipr"], needed["periodic_ipr"], needed["random_ipr"]))
+        entropy_between = bool(complete and between(needed["phi_entropy"], needed["periodic_entropy"], needed["random_entropy"]))
+        separated_random = bool(
+            complete
+            and abs(needed["phi_r"] - needed["random_r"]) >= args.min_r_delta
+            and abs(needed["phi_ipr"] - needed["random_ipr"]) >= args.min_ipr_delta
+        )
+        joint = bool(r_between and ipr_between and separated_random)
+        classification["by_v"][v_key] = {
+            **needed,
+            "spacing_r_between": r_between,
+            "mean_ipr_between": ipr_between,
+            "participation_entropy_between": entropy_between,
+            "separated_from_random": separated_random,
+            "joint_boundary": joint,
+        }
+        if joint:
+            classification["joint_boundary_v"].append(float(v_value))
+
+    return {
+        "experiment": "aubry_boundary_phase_transport_gate",
+        "parameters": {
+            "ns": ns,
+            "phases": phases,
+            "v_min": args.v_min,
+            "v_max": args.v_max,
+            "v_step": args.v_step,
+            "central_fraction": args.central_fraction,
+            "random_trials": args.random_trials,
+            "seed": args.seed,
+            "min_r_delta": args.min_r_delta,
+            "min_ipr_delta": args.min_ipr_delta,
+        },
+        "observable_contract": {
+            "claim": "phi is a physical boundary state between periodic order and balanced random disorder only if spectral spacing and localization agree",
+            "observable": "spacing_r plus mean_ipr / participation_entropy on tight-binding spectra",
+            "operator": "binary quasiperiodic tight-binding Hamiltonian with phase row denominator",
+            "denominator": "N x phase x V x generator rows with balanced random controls",
+            "non_possible": "single-observable boundary, phase-aggregated critical value, or phi outside the periodic-random interval",
+        },
+        "classification": classification,
+        "summary_by_v": summary_by_v,
+        "rows": rows,
+    }
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--ns", default="89,144,233")
+    parser.add_argument("--phases", default="0,0.25,0.5,0.75")
+    parser.add_argument("--v-min", type=float, default=0.5)
+    parser.add_argument("--v-max", type=float, default=2.5)
+    parser.add_argument("--v-step", type=float, default=0.25)
+    parser.add_argument("--central-fraction", type=float, default=0.6)
+    parser.add_argument("--random-trials", type=int, default=6)
+    parser.add_argument("--seed", type=int, default=202605151745)
+    parser.add_argument("--min-r-delta", type=float, default=0.025)
+    parser.add_argument("--min-ipr-delta", type=float, default=0.0025)
+    parser.add_argument("--out", default="tools/data/aubry_boundary_phase_transport_gate_20260515_1745.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), encoding="utf-8")
+
+    compact = {
+        "classification": output["classification"],
+        "out": str(out),
+    }
+    print(json.dumps(compact, indent=2))
+
+
+if __name__ == "__main__":
+    main()

diff --git a/tools/data/reports/agent_20260515_1745.md b/tools/data/reports/agent_20260515_1745.md
new file mode 100644
index 0000000000000000000000000000000000000000..f1d0abc77f960601d7218d6ad380c4440fe243f6
--- /dev/null
+++ b/tools/data/reports/agent_20260515_1745.md
@@ -0,0 +1,105 @@
+# Agent Report - Aubry Boundary Phase Transport Gate
+**Date**: 2026-05-15 17:45  
+**Piano**: 118  
+**Tension explored**: BOUNDARY / TENS_SCALE_TRASCENDENZA_LIMITE  
+**verdict**: CONSTRAINT - nel ritorno tight-binding `phi` e' terzo incluso congiunto solo per V=0.50..1.25; oltre V=1.50 la localizzazione resta intermedia ma la statistica spettrale rompe il bicono.  
+observables_registry: none; dedicated observables only  
+observables_used: [spacing_r, mean_ipr, median_ipr, participation_entropy]  
+**observable_contract**: claim=`phi` e' stato fisico di confine tra ordine periodico e disordine random solo se statistica spettrale e localizzazione concordano; observable=`spacing_r` + `mean_ipr`/`participation_entropy`; operator=`tools/exp_aubry_boundary_phase_transport_gate.py`; generator=Hamiltoniana tight-binding binaria con sequenze phi/silver/bronze, periodico AB e random bilanciato a densita phi; denominator=N={89,144,233} x phase={0,0.25,0.5,0.75} x V=0.50..2.50 step 0.25 x generator, random_trials=6; non_possible=promuovere confine fisico da una sola osservabile, da phase0, o da un valore critico aggregato; not_tested=limite asintotico, modello Aubry-Andre coseno canonico, dati sperimentali, classi GUE/Poisson universali dirette.
+
+## Respiro fuori-tempo
+
+- **Combo**: A2 confine det=-1 + A9 terzo incluso + A11 combo + QxG continuo/discreto + TxQ matrice densita come lettura spettrale + tensione `BOUNDARY` 8 GUE / 5 Poisson.
+- **Dipolo / punto-zero**: repulsione spettrale / localizzazione degli autostati. Punto-zero = Hamiltoniana prima che una metrica singola decida se il dominio e' GUE-like, Poisson-like o di confine.
+- **Piano superiore**: geometria dei campi su reticolo; la fase del potenziale e' parte del bordo, non rumore da mediare.
+- **Operatori laterali scelti**: Hamiltonian spectrum, boundary operator, phase/parallel-transport come memoria del denominatore.
+- **Contaminazione cognitiva**: CE-0001 / KSAR usato come reiterazione del deposito 17:24 nel perimetro fisico indicato dalla consecutio; CE-0038 usato come equilibrio tra estremi, ma reso falsificabile da due osservabili non collineari.
+- **Proto-ipotesi**: se il confine e' terzo incluso operativo, `phi` deve stare tra periodico e random sia nello spacing sia nella localizzazione; se solo una delle due osservabili resta intermedia, il confine e' componente, non stato fisico chiuso.
+- **Proiezione**: diagonalizzo Hamiltoniane tight-binding binarie e confronto mediane row-aligned per V, taglia, fase e generatore.
+
+## Aderenza alla direzione
+
+- `relation`: follows_direction
+- `why`: segue la direzione viva "8 domini GUE, 5 Poisson - il confine e' il terzo incluso operativo" portando il confine in un ritorno fisico dove GUE/Poisson diventano spacing/localizzazione.
+- `not_drift`: non riapre prime/mod6, non usa selector legacy, non rifitta `V_c`; consuma `TENS_SCALE_TRASCENDENZA_LIMITE` solo come nodo regressivo di denominatore e fase.
+
+## Claim Under Test
+
+> Nel modello tight-binding binario, `phi` realizza un terzo incluso fisico tra periodico e random solo sui valori di V dove `spacing_r` e localizzazione lo collocano insieme dentro il segmento periodico-random e separato dal random.
+
+## Question
+
+Il ritorno Aubry/Fibonacci chiude il confine come stato fisico congiunto, oppure lo conserva solo come componente osservabile-specifica?
+
+## Ritorno fisico
+
+- **Punto fisico sorgente**: confine GUE/Poisson come attrito tra repulsione dei livelli e indipendenza/localizzazione.
+- **Attraversamento matematico**: sequenze Sturmian e random bilanciato proiettati in Hamiltoniane tridiagonali con potenziale binario centrato.
+- **Punto fisico di ritorno**: modello tight-binding 1D misurabile tramite spettro e autostati.
+- **Relazione nuova**: il confine non e' un `V_c` unico; e' una finestra di V dove due lettori indipendenti concordano.
+- **Osservabile/test fisico possibile**: misurare spacing e IPR nello stesso reticolo sintetico/fotonico con fase controllata.
+- **Se fallisce**: resta vincolo sul denominatore dei futuri ritorni fisici, non scoperta promuovibile.
+
+## Experiment Design
+
+- `python tools/dnd_scenario.py --best` eseguito: massimo discriminante `TENS_SCALE_TRASCENDENZA_LIMITE`.
+- `python -m py_compile tools/exp_aubry_boundary_phase_transport_gate.py` completato.
+- Run: `python tools/exp_aubry_boundary_phase_transport_gate.py --out tools/data/aubry_boundary_phase_transport_gate_20260515_1745.json`.
+- Perimetro deterministico: 108 righe per ciascun generatore phi/silver/bronze/periodic_ab.
+- Perimetro random: 648 righe balanced random a densita phi.
+- La misura serve la combo perche' rende non-possibile una chiusura del confine da una sola metrica.
+
+## Results
+
+| V | joint | r_between | ipr_between | sep_random | phi_r | periodic_r | random_r | phi_ipr | periodic_ipr | random_ipr |
+|---:|---|---|---|---|---:|---:|---:|---:|---:|---:|
+| 0.50 | true | true | true | true | 0.632 | 0.935 | 0.565 | 0.0156 | 0.0124 | 0.0213 |
+| 0.75 | true | true | true | true | 0.566 | 0.929 | 0.497 | 0.0205 | 0.0133 | 0.0358 |
+| 1.00 | true | true | true | true | 0.511 | 0.925 | 0.455 | 0.0268 | 0.0141 | 0.0523 |
+| 1.25 | true | true | true | true | 0.468 | 0.922 | 0.425 | 0.0342 | 0.0148 | 0.0767 |
+| 1.50 | false | true | true | false | 0.432 | 0.920 | 0.419 | 0.0425 | 0.0155 | 0.0999 |
+| 1.75 | false | false | true | false | 0.399 | 0.919 | 0.409 | 0.0507 | 0.0160 | 0.1204 |
+| 2.00 | false | false | true | true | 0.369 | 0.917 | 0.404 | 0.0597 | 0.0166 | 0.1437 |
+| 2.25 | false | false | true | true | 0.345 | 0.917 | 0.383 | 0.0676 | 0.0170 | 0.1662 |
+| 2.50 | false | false | true | true | 0.327 | 0.916 | 0.380 | 0.0751 | 0.0174 | 0.1891 |
+
+## Key Findings
+
+1. **Verificato**: `phi` chiude il bicono congiunto per V=0.50, 0.75, 1.00, 1.25: `spacing_r` e `mean_ipr` sono entrambi tra periodico e random, con separazione dal random.
+2. **Verificato**: a V=1.50 la posizione intermedia resta vera per entrambe le osservabili, ma la separazione dal random cade sotto soglia; non e' boundary joint promuovibile.
+3. **Verificato**: da V=1.75 in poi `spacing_r(phi)` scende fuori dal segmento periodico-random, mentre `mean_ipr` resta intermedio fino a V=2.50.
+4. **Inferito**: la finestra fisica del confine e' `0.50 <= V <= 1.25` nel perimetro misurato. Oltre quella soglia, il confine si scinde: localizzazione conserva il terzo incluso, spacing no.
+
+## Verdict
+
+**CONSTRAINT**.
+
+Il ritorno Aubry/Fibonacci trasferisce il confine BOUNDARY solo come finestra di accoppiamento. La formulazione valida e': nel perimetro misurato, `phi` e' terzo incluso fisico congiunto per V=0.50..1.25; sopra V=1.50 non si puo' parlare di stato di confine chiuso, ma di divergenza tra lettore spettrale e lettore di localizzazione.
+
+## Bicono della scoperta
+
+- **Due radici**: spacing tra livelli / localizzazione degli autostati.
+- **Singolare**: Hamiltoniana row-local `N,phase,V,generator` prima della classificazione.
+- **Invariante di passaggio**: ogni ritorno fisico del confine deve dichiarare almeno due osservabili non collineari e la finestra di parametro dove concordano.
+- **Campo di possibilita**: possibile = progettare test fisici con finestra V e fase controllata; non-possibile = usare `V_c`, `T_mean`, stopband o spacing singolo come prova di terzo incluso globale.
+
+## Consecutio
+
+Il prossimo ciclo puo' usare `exp_aubry_boundary_phase_transport_gate.py` come gate per ritorni fisici: cercare se la finestra V=0.50..1.25 sopravvive a N piu grandi o a un potenziale Aubry-Andre coseno. Se la finestra si muove, il claim resta finestra row-local; se resta stabile, diventa candidato ponte QxG fisico.
+
+## Ricadute pratiche
+
+ssp_value: yes. Lo script produce un gate riusabile per confrontare spacing e localizzazione su reticoli sintetici prima di costruire demo o visualizzazioni del confine.
+
+## Telemetria
+
+- No API paid run: `ANTHROPIC_API_KEY` e `OPENAI_API_KEY` non presenti nell'ambiente.
+- Nessun update del seme.
+- Nessun runtime pointer mutation.
+- Nessuna promozione e nessun public sync.
+
+## Files
+
+- `tools/exp_aubry_boundary_phase_transport_gate.py`
+- `tools/data/aubry_boundary_phase_transport_gate_20260515_1745.json`
+- `tools/data/reports/agent_20260515_1745.md`
diff --git a/tools/exp_aubry_boundary_phase_transport_gate.py b/tools/exp_aubry_boundary_phase_transport_gate.py
new file mode 100644
index 0000000000000000000000000000000000000000..8487656a64b34e688fb3f65c4708e1aa25469724
--- /dev/null
+++ b/tools/exp_aubry_boundary_phase_transport_gate.py
@@ -0,0 +1,284 @@
+#!/usr/bin/env python3
+"""
+Aubry/Fibonacci boundary phase transport gate.
+
+Projects the live BOUNDARY direction into a 1D tight-binding model with binary
+quasiperiodic potentials. The test is deliberately joint: a boundary return is
+accepted only when the phi stack is between periodic order and balanced random
+disorder for both spectral spacing and eigenstate localization.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+
+PHI = (1 + np.sqrt(5)) / 2
+SILVER = 1 + np.sqrt(2)
+BRONZE = 1 + np.sqrt(3)
+
+
+def sturmian_sequence(theta: float, n: int, phase: float) -> np.ndarray:
+    idx = np.arange(n + 1, dtype=float)
+    vals = np.floor(idx * theta + phase)
+    return np.diff(vals).astype(float)
+
+
+def periodic_sequence(n: int) -> np.ndarray:
+    return (np.arange(n) % 2).astype(float)
+
+
+def hamiltonian(diagonal: np.ndarray) -> np.ndarray:
+    n = len(diagonal)
+    matrix = np.diag(diagonal.astype(float))
+    off = np.ones(n - 1, dtype=float)
+    matrix += np.diag(off, 1) + np.diag(off, -1)
+    return matrix
+
+
+def central_slice(n: int, central_fraction: float) -> slice:
+    keep = max(8, int(round(n * central_fraction)))
+    keep = min(n, keep)
+    start = (n - keep) // 2
+    return slice(start, start + keep)
+
+
+def spacing_r(levels: np.ndarray, central_fraction: float) -> float | None:
+    levels = np.sort(np.asarray(levels, dtype=float))
+    central = levels[central_slice(len(levels), central_fraction)]
+    gaps = np.diff(central)
+    gaps = gaps[np.isfinite(gaps) & (gaps > 1e-12)]
+    if len(gaps) < 2:
+        return None
+    left = gaps[:-1]
+    right = gaps[1:]
+    return float(np.mean(np.minimum(left, right) / np.maximum(left, right)))
+
+
+def localization_metrics(vectors: np.ndarray, central_fraction: float) -> dict[str, float]:
+    n = vectors.shape[0]
+    subset = vectors[:, central_slice(n, central_fraction)]
+    probs = np.square(np.abs(subset))
+    ipr = np.sum(probs * probs, axis=0)
+    entropy = []
+    for col in range(probs.shape[1]):
+        p = probs[:, col]
+        p = p[p > 1e-15]
+        entropy.append(float(-np.sum(p * np.log(p)) / np.log(n)))
+    return {
+        "mean_ipr": float(np.mean(ipr)),
+        "median_ipr": float(np.median(ipr)),
+        "participation_entropy": float(np.mean(entropy)) if entropy else 0.0,
+    }
+
+
+def spectrum_row(
+    domain: str,
+    seq: np.ndarray,
+    n: int,
+    phase: float,
+    v_value: float,
+    central_fraction: float,
+    trial: int | None = None,
+) -> dict[str, Any]:
+    centered = seq - float(np.mean(seq))
+    levels, vectors = np.linalg.eigh(hamiltonian(v_value * centered))
+    metrics = localization_metrics(vectors, central_fraction)
+    row: dict[str, Any] = {
+        "domain": domain,
+        "N": n,
+        "phase": phase,
+        "V": v_value,
+        "ones": int(np.sum(seq)),
+        "spacing_r": spacing_r(levels, central_fraction),
+        **metrics,
+    }
+    if trial is not None:
+        row["trial"] = trial
+    return row
+
+
+def finite(values: list[float | None]) -> np.ndarray:
+    return np.array([v for v in values if v is not None and np.isfinite(v)], dtype=float)
+
+
+def aggregate(rows: list[dict[str, Any]]) -> dict[str, Any]:
+    out: dict[str, Any] = {"count": len(rows)}
+    for key in ["spacing_r", "mean_ipr", "median_ipr", "participation_entropy"]:
+        arr = finite([row.get(key) for row in rows])
+        if len(arr) == 0:
+            out[key] = {"count": 0}
+        else:
+            out[key] = {
+                "count": int(len(arr)),
+                "median": float(np.median(arr)),
+                "mean": float(np.mean(arr)),
+                "min": float(np.min(arr)),
+                "max": float(np.max(arr)),
+            }
+    return out
+
+
+def median_metric(summary: dict[str, Any], domain: str, v_key: str, metric: str) -> float | None:
+    value = summary.get(v_key, {}).get(domain, {}).get(metric, {})
+    if not isinstance(value, dict):
+        return None
+    median = value.get("median")
+    return float(median) if median is not None else None
+
+
+def between(value: float, left: float, right: float) -> bool:
+    return min(left, right) <= value <= max(left, right)
+
+
+def parse_csv_ints(value: str) -> list[int]:
+    return [int(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def parse_csv_floats(value: str) -> list[float]:
+    return [float(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    rng = np.random.default_rng(args.seed)
+    ns = parse_csv_ints(args.ns)
+    phases = parse_csv_floats(args.phases)
+    v_values = np.arange(args.v_min, args.v_max + args.v_step / 2, args.v_step)
+    domains = {
+        "phi": 1 / PHI,
+        "silver": 1 / SILVER,
+        "bronze": 1 / BRONZE,
+    }
+
+    rows: list[dict[str, Any]] = []
+    for n in ns:
+        for phase in phases:
+            phi_seq = sturmian_sequence(1 / PHI, n, phase)
+            ones = int(np.sum(phi_seq))
+            for v_value in v_values:
+                for domain, theta in domains.items():
+                    seq = sturmian_sequence(theta, n, phase)
+                    rows.append(spectrum_row(domain, seq, n, phase, float(v_value), args.central_fraction))
+
+                periodic = periodic_sequence(n)
+                rows.append(spectrum_row("periodic_ab", periodic, n, phase, float(v_value), args.central_fraction))
+
+                for trial in range(args.random_trials):
+                    seq = np.array([1.0] * ones + [0.0] * (n - ones), dtype=float)
+                    rng.shuffle(seq)
+                    rows.append(
+                        spectrum_row(
+                            "balanced_random_phi_density",
+                            seq,
+                            n,
+                            phase,
+                            float(v_value),
+                            args.central_fraction,
+                            trial=trial,
+                        )
+                    )
+
+    summary_by_v: dict[str, dict[str, Any]] = {}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        summary_by_v[v_key] = {}
+        for domain in sorted({row["domain"] for row in rows}):
+            subset = [row for row in rows if row["domain"] == domain and abs(row["V"] - v_value) < 1e-12]
+            summary_by_v[v_key][domain] = aggregate(subset)
+
+    classification: dict[str, Any] = {"joint_boundary_v": [], "by_v": {}}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        needed = {
+            "phi_r": median_metric(summary_by_v, "phi", v_key, "spacing_r"),
+            "periodic_r": median_metric(summary_by_v, "periodic_ab", v_key, "spacing_r"),
+            "random_r": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "spacing_r"),
+            "phi_ipr": median_metric(summary_by_v, "phi", v_key, "mean_ipr"),
+            "periodic_ipr": median_metric(summary_by_v, "periodic_ab", v_key, "mean_ipr"),
+            "random_ipr": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "mean_ipr"),
+            "phi_entropy": median_metric(summary_by_v, "phi", v_key, "participation_entropy"),
+            "periodic_entropy": median_metric(summary_by_v, "periodic_ab", v_key, "participation_entropy"),
+            "random_entropy": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "participation_entropy"),
+        }
+        complete = all(value is not None for value in needed.values())
+        r_between = bool(complete and between(needed["phi_r"], needed["periodic_r"], needed["random_r"]))
+        ipr_between = bool(complete and between(needed["phi_ipr"], needed["periodic_ipr"], needed["random_ipr"]))
+        entropy_between = bool(complete and between(needed["phi_entropy"], needed["periodic_entropy"], needed["random_entropy"]))
+        separated_random = bool(
+            complete
+            and abs(needed["phi_r"] - needed["random_r"]) >= args.min_r_delta
+            and abs(needed["phi_ipr"] - needed["random_ipr"]) >= args.min_ipr_delta
+        )
+        joint = bool(r_between and ipr_between and separated_random)
+        classification["by_v"][v_key] = {
+            **needed,
+            "spacing_r_between": r_between,
+            "mean_ipr_between": ipr_between,
+            "participation_entropy_between": entropy_between,
+            "separated_from_random": separated_random,
+            "joint_boundary": joint,
+        }
+        if joint:
+            classification["joint_boundary_v"].append(float(v_value))
+
+    return {
+        "experiment": "aubry_boundary_phase_transport_gate",
+        "parameters": {
+            "ns": ns,
+            "phases": phases,
+            "v_min": args.v_min,
+            "v_max": args.v_max,
+            "v_step": args.v_step,
+            "central_fraction": args.central_fraction,
+            "random_trials": args.random_trials,
+            "seed": args.seed,
+            "min_r_delta": args.min_r_delta,
+            "min_ipr_delta": args.min_ipr_delta,
+        },
+        "observable_contract": {
+            "claim": "phi is a physical boundary state between periodic order and balanced random disorder only if spectral spacing and localization agree",
+            "observable": "spacing_r plus mean_ipr / participation_entropy on tight-binding spectra",
+            "operator": "binary quasiperiodic tight-binding Hamiltonian with phase row denominator",
+            "denominator": "N x phase x V x generator rows with balanced random controls",
+            "non_possible": "single-observable boundary, phase-aggregated critical value, or phi outside the periodic-random interval",
+        },
+        "classification": classification,
+        "summary_by_v": summary_by_v,
+        "rows": rows,
+    }
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--ns", default="89,144,233")
+    parser.add_argument("--phases", default="0,0.25,0.5,0.75")
+    parser.add_argument("--v-min", type=float, default=0.5)
+    parser.add_argument("--v-max", type=float, default=2.5)
+    parser.add_argument("--v-step", type=float, default=0.25)
+    parser.add_argument("--central-fraction", type=float, default=0.6)
+    parser.add_argument("--random-trials", type=int, default=6)
+    parser.add_argument("--seed", type=int, default=202605151745)
+    parser.add_argument("--min-r-delta", type=float, default=0.025)
+    parser.add_argument("--min-ipr-delta", type=float, default=0.0025)
+    parser.add_argument("--out", default="tools/data/aubry_boundary_phase_transport_gate_20260515_1745.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), encoding="utf-8")
+
+    compact = {
+        "classification": output["classification"],
+        "out": str(out),
+    }
+    print(json.dumps(compact, indent=2))
+
+
+if __name__ == "__main__":
+    main()

codex
Il report è scritto. Faccio il preflight read-only del ciclo e poi aggiorno la telemetria se il classificatore produce un artefatto utile.
exec
/bin/bash -lc 'git status --short' in /opt/MM_D-ND
 succeeded in 0ms:
 M applications/INDEX.draft.json
 M docs/guides/lab_current_state_logic_2026-05-08.md
 M tools/LAB_AGENT_CONTEXT.md
 M tools/build_agent_field.py
 M tools/build_lab_graph.py
 M tools/data/agent_field_live.md
 M tools/data/b2_apply_log.jsonl
 M tools/data/ciclo_memoria.json
 M tools/data/consecutio.json
 M tools/data/consecutio_processata.json
 M tools/data/domandatore_pre_cycle.json
 M tools/data/domande_fondamentali.json
 M tools/data/g1_log.jsonl
 M tools/data/g2_log.jsonl
 M tools/data/graph_completion/latest.json
 M tools/data/incrocio_risultato.json
 M tools/data/knowledge_state.json
 M tools/data/lab_bridge_issues.jsonl
 M tools/data/lab_data.json
 M tools/data/lab_graph.json
 M tools/data/lab_health.json
 M tools/data/lab_session_log.jsonl
 M tools/data/pipeline_state.json
 M tools/data/ponti_evoluti.json
 M tools/data/repairs/repair_run_log.jsonl
 M tools/data/reports/latest.md
 M tools/data/seme.json
 D tools/data/seme_archive/piano_37.json
 D tools/data/seme_archive/piano_38.json
 D tools/data/seme_archive/piano_4.json
 D tools/data/seme_archive/piano_5.json
 D tools/data/seme_archive/piano_6.json
 D tools/data/seme_archive/piano_7.json
 D tools/data/seme_archive/piano_8.json
 D tools/data/seme_archive/piano_9.json
 M tools/data/seme_backup_pre_run.json
 M tools/data/tm1_figures/tensions.json
 M tools/data/tm1_figures/tensions_raw.json
 M tools/data/valutatore_log.jsonl
 M tools/evolution_report.md
 M tools/graph_completion_compiler.py
 M tools/lab_agent.sh
 M tools/lab_falsifier.py
?? applications/scoperte/20260513_0330_prime-vs-mod6-sr-boundary_auto/
?? applications/scoperte/20260514_1330_sr-residual-width-audit_auto/
?? applications/scoperte/20260514_1631_chiusura-tester-rimbalzo-fisico-w6_auto/
?? applications/scoperte/20260514_1640_rafforzamento-tester-fisico-goegue-n-curve_auto/
?? applications/scoperte/20260515_1712_primemod6-generative-null-w2048_auto/
?? applications/soluzioni/20260513_0330_prime-vs-mod6-sr-boundary/
?? applications/soluzioni/20260514_1330_sr-residual-width-audit/
?? applications/soluzioni/20260514_1631_chiusura-tester-rimbalzo-fisico-w6/
?? applications/soluzioni/20260514_1640_rafforzamento-tester-fisico-goegue-n-curve/
?? applications/soluzioni/20260515_1712_primemod6-generative-null-w2048/
?? docs/guides/lab_preflight_agent_2026-05-14.md
?? docs/operations/
?? tools/build_safe_agent_field_preview.py
?? tools/compose_filtered_field_preview.py
?? tools/compose_supervised_entry_preview.py
?? tools/data/aeternitas/aeternitas_20260512_033539.json
?? tools/data/aeternitas/aeternitas_20260513_033605.json
?? tools/data/aeternitas/aeternitas_20260514_033420.json
?? tools/data/aeternitas/aeternitas_20260514_133424.json
?? tools/data/aeternitas/aeternitas_20260514_150454.json
?? tools/data/aeternitas/aeternitas_20260514_161058.json
?? tools/data/aeternitas/aeternitas_20260514_161719.json
?? tools/data/aeternitas/aeternitas_20260514_163527.json
?? tools/data/aeternitas/aeternitas_20260514_164509.json
?? tools/data/aeternitas/aeternitas_20260514_165437.json
?? tools/data/aeternitas/aeternitas_20260514_170109.json
?? tools/data/aeternitas/aeternitas_20260514_171152.json
?? tools/data/aeternitas/aeternitas_20260514_185450.json
?? tools/data/aeternitas/aeternitas_20260515_162727.json
?? tools/data/aeternitas/aeternitas_20260515_165116.json
?? tools/data/aeternitas/aeternitas_20260515_170328.json
?? tools/data/aeternitas/aeternitas_20260515_171005.json
?? tools/data/aeternitas/aeternitas_20260515_171725.json
?? tools/data/aeternitas/aeternitas_20260515_172913.json
?? tools/data/aeternitas/aeternitas_20260515_174023.json
?? tools/data/agent_field_entry_supervised.md
?? tools/data/anderson3d_component_state_interface_input_20260514_1850.json
?? tools/data/aubry_boundary_phase_transport_gate_20260515_1745.json
?? tools/data/biconi/bicono_20260512_0330.json
?? tools/data/biconi/bicono_20260513_0330.json
?? tools/data/biconi/bicono_20260514_0330.json
?? tools/data/biconi/bicono_20260514_1330.json
?? tools/data/biconi/bicono_20260514_1458.json
?? tools/data/biconi/bicono_20260514_1605.json
?? tools/data/biconi/bicono_20260514_1612.json
?? tools/data/biconi/bicono_20260514_1631.json
?? tools/data/biconi/bicono_20260514_1640.json
?? tools/data/biconi/bicono_20260514_1649.json
?? tools/data/biconi/bicono_20260514_1656.json
?? tools/data/biconi/bicono_20260514_1701.json
?? tools/data/biconi/bicono_20260514_1850.json
?? tools/data/biconi/bicono_20260515_1623.json
?? tools/data/biconi/bicono_20260515_1647.json
?? tools/data/biconi/bicono_20260515_1659.json
?? tools/data/biconi/bicono_20260515_1705.json
?? tools/data/biconi/bicono_20260515_1712.json
?? tools/data/biconi/bicono_20260515_1724.json
?? tools/data/biconi/bicono_20260515_1734.json
?? tools/data/component_state_anderson3d_interface_20260514_1850.json
?? tools/data/component_state_anderson3d_interface_20260514_1850.trace.jsonl
?? tools/data/component_state_fit_ready_20260514_1649.json
?? tools/data/component_state_interface_smoke_20260514_1649.json
?? tools/data/component_state_interface_smoke_20260514_1649.trace.jsonl
?? tools/data/component_state_interface_smoke_input_20260514_1649.json
?? tools/data/domandatore/domandatore_20260512_0330.json
?? tools/data/domandatore/domandatore_20260512_0345.json
?? tools/data/domandatore/domandatore_20260513_0330.json
?? tools/data/domandatore/domandatore_20260513_0345.json
?? tools/data/domandatore/domandatore_20260514_0330.json
?? tools/data/domandatore/domandatore_20260514_0345.json
?? tools/data/domandatore/domandatore_20260514_1330.json
?? tools/data/domandatore/domandatore_20260514_1458.json
?? tools/data/domandatore/domandatore_20260515_0345.json
?? tools/data/domandatore/domandatore_20260515_1623.json
?? tools/data/domandatore/domandatore_20260515_1647.json
?? tools/data/domandatore/domandatore_20260515_1659.json
?? tools/data/evolution/evolution_20260512_0330.md
?? tools/data/evolution/evolution_20260513_0330.md
?? tools/data/evolution/evolution_20260514_0330.md
?? tools/data/evolution/evolution_20260514_1330.md
?? tools/data/evolution/evolution_20260514_1458.md
?? tools/data/evolution/evolution_20260514_1605.md
?? tools/data/evolution/evolution_20260514_1612.md
?? tools/data/evolution/evolution_20260514_1631.md
?? tools/data/evolution/evolution_20260514_1640.md
?? tools/data/evolution/evolution_20260514_1649.md
?? tools/data/evolution/evolution_20260514_1656.md
?? tools/data/evolution/evolution_20260514_1701.md
?? tools/data/evolution/evolution_20260514_1850.md
?? tools/data/evolution/evolution_20260515_1623.md
?? tools/data/evolution/evolution_20260515_1647.md
?? tools/data/evolution/evolution_20260515_1659.md
?? tools/data/evolution/evolution_20260515_1705.md
?? tools/data/evolution/evolution_20260515_1712.md
?? tools/data/evolution/evolution_20260515_1724.md
?? tools/data/evolution/evolution_20260515_1734.md
?? tools/data/graph_completion/graph_completion_20260512_0330.json
?? tools/data/graph_completion/graph_completion_20260513_0330.json
?? tools/data/graph_completion/graph_completion_20260514_0330.json
?? tools/data/graph_completion/graph_completion_20260514_1330.json
?? tools/data/graph_completion/graph_completion_20260514_1458.json
?? tools/data/graph_completion/graph_completion_20260514_1605.json
?? tools/data/graph_completion/graph_completion_20260514_1612.json
?? tools/data/graph_completion/graph_completion_20260514_1631.json
?? tools/data/graph_completion/graph_completion_20260514_1640.json
?? tools/data/graph_completion/graph_completion_20260514_1649.json
?? tools/data/graph_completion/graph_completion_20260514_1656.json
?? tools/data/graph_completion/graph_completion_20260514_1701.json
?? tools/data/graph_completion/graph_completion_20260514_1850.json
?? tools/data/graph_completion/graph_completion_20260515_1623.json
?? tools/data/graph_completion/graph_completion_20260515_1647.json
?? tools/data/graph_completion/graph_completion_20260515_1659.json
?? tools/data/graph_completion/graph_completion_20260515_1705.json
?? tools/data/graph_completion/graph_completion_20260515_1712.json
?? tools/data/graph_completion/graph_completion_20260515_1724.json
?? tools/data/graph_completion/graph_completion_20260515_1734.json
?? tools/data/incrocio_20260512_0330.json
?? tools/data/incrocio_20260513_0330.json
?? tools/data/incrocio_20260514_0330.json
?? tools/data/incrocio_20260514_1330.json
?? tools/data/incrocio_20260514_1458.json
?? tools/data/incrocio_20260514_1605.json
?? tools/data/incrocio_20260514_1612.json
?? tools/data/incrocio_20260514_1631.json
?? tools/data/incrocio_20260514_1640.json
?? tools/data/incrocio_20260514_1649.json
?? tools/data/incrocio_20260514_1656.json
?? tools/data/incrocio_20260514_1701.json
?? tools/data/incrocio_20260514_1850.json
?? tools/data/incrocio_20260515_1623.json
?? tools/data/incrocio_20260515_1647.json
?? tools/data/incrocio_20260515_1659.json
?? tools/data/incrocio_20260515_1705.json
?? tools/data/incrocio_20260515_1712.json
?? tools/data/incrocio_20260515_1724.json
?? tools/data/incrocio_20260515_1734.json
?? tools/data/incrocio_20260515_1745.json
?? tools/data/operator_directives_consumed/operator_directive_20260514_1612.md
?? tools/data/operator_directives_consumed/operator_directive_20260514_1631.md
?? tools/data/operator_directives_consumed/operator_directive_20260514_1640.md
?? tools/data/operator_directives_consumed/operator_directive_20260514_1649.md
?? tools/data/operator_directives_consumed/operator_directive_20260514_1656.md
?? tools/data/operator_directives_consumed/operator_directive_20260514_1701.md
?? tools/data/operator_directives_consumed/operator_directive_20260514_1850.md
?? tools/data/photonic_boundary_third_included_gate_20260515_1734.json
?? tools/data/physical_sr_residue_bounce_20260514_1612.json
?? tools/data/physical_sr_residue_bounce_20260514_1612.trace.jsonl
?? tools/data/physical_sr_residue_bounce_20260514_1631_w6.json
?? tools/data/physical_sr_residue_bounce_20260514_1631_w6.trace.jsonl
?? tools/data/physical_sr_residue_bounce_20260514_1640_goe_gue_ncurve.json
?? tools/data/physical_sr_residue_bounce_20260514_1640_goe_gue_ncurve.trace.jsonl
?? tools/data/preflight/
?? tools/data/prime_sr_persistent_boundary_20260512_0330.json
?? tools/data/prime_sr_persistent_boundary_20260512_0330_seedcheck.json
?? tools/data/prime_vs_mod6_sr_boundary_20260513_0330.json
?? tools/data/prime_vs_mod6_sr_boundary_20260513_0330_seedcheck.json
?? tools/data/prime_vs_mod6_sr_boundary_20260514_0330.json
?? tools/data/prime_vs_mod6_sr_boundary_20260514_0330.trace.jsonl
?? tools/data/prime_vs_mod6_sr_boundary_20260514_0330_seedcheck.json
?? tools/data/prime_vs_mod6_sr_boundary_20260514_0330_seedcheck.trace.jsonl
?? tools/data/prime_vs_mod6_sr_boundary_20260514_1330_w1024.json
?? tools/data/prime_vs_mod6_sr_boundary_20260514_1330_w1024.trace.jsonl
?? tools/data/prime_vs_mod6_sr_boundary_20260514_1330_w2048.json
?? tools/data/prime_vs_mod6_sr_boundary_20260514_1330_w2048.trace.jsonl
?? tools/data/prime_vs_mod6_sr_boundary_20260514_1330_w512.json
?? tools/data/prime_vs_mod6_sr_boundary_20260514_1330_w512.trace.jsonl
?? tools/data/prime_vs_mod6_sr_boundary_20260514_1458_w4096.json
?? tools/data/prime_vs_mod6_sr_boundary_20260514_1458_w4096.trace.jsonl
?? tools/data/prime_vs_mod6_sr_boundary_20260514_1458_w4096_dense.json
?? tools/data/prime_vs_mod6_sr_boundary_20260514_1458_w4096_dense.trace.jsonl
?? tools/data/prime_vs_mod6_sr_boundary_20260514_1458_w8192_dense.json
?? tools/data/prime_vs_mod6_sr_boundary_20260514_1458_w8192_dense.trace.jsonl
?? tools/data/prime_vs_mod6_sr_boundary_20260514_1605_w16384.json
?? tools/data/prime_vs_mod6_sr_boundary_20260514_1605_w16384.trace.jsonl
?? tools/data/prime_vs_mod6_sr_boundary_20260514_1605_w4096.json
?? tools/data/prime_vs_mod6_sr_boundary_20260514_1605_w4096.trace.jsonl
?? tools/data/prime_vs_mod6_sr_boundary_20260514_1605_w8192.json
?? tools/data/prime_vs_mod6_sr_boundary_20260514_1605_w8192.trace.jsonl
?? tools/data/promotions/promotion_20260513_0330.json
?? tools/data/promotions/promotion_20260514_1330.json
?? tools/data/promotions/promotion_20260514_1631.json
?? tools/data/promotions/promotion_20260514_1640.json
?? tools/data/promotions/promotion_20260514_1656.json
?? tools/data/promotions/promotion_20260515_1712.json
?? tools/data/quasiperiodic_grammar_scale_gate_20260514_1701.json
?? tools/data/quasiperiodic_vc_lattice_gate_20260515_1724.json
?? tools/data/repairs/repair_20260512_0330_decision.json
?? tools/data/repairs/repair_20260513_0330_decision.json
?? tools/data/repairs/repair_20260514_0330_decision.json
?? tools/data/repairs/repair_20260514_1330_decision.json
?? tools/data/repairs/repair_20260514_1458_decision.json
?? tools/data/repairs/repair_20260514_1605_decision.json
?? tools/data/repairs/repair_20260514_1612_decision.json
?? tools/data/repairs/repair_20260514_1631_decision.json
?? tools/data/repairs/repair_20260514_1640_decision.json
?? tools/data/repairs/repair_20260514_1649_decision.json
?? tools/data/repairs/repair_20260514_1656.json
?? tools/data/repairs/repair_20260514_1656.md
?? tools/data/repairs/repair_20260514_1656_decision.json
?? tools/data/repairs/repair_20260514_1850_decision.json
?? tools/data/reports/agent_20260512_0330.md
?? tools/data/reports/agent_20260513_0330.md
?? tools/data/reports/agent_20260514_0330.md
?? tools/data/reports/agent_20260514_1330.md
?? tools/data/reports/agent_20260514_1458.md
?? tools/data/reports/agent_20260514_1605.md
?? tools/data/reports/agent_20260514_1612.md
?? tools/data/reports/agent_20260514_1631.md
?? tools/data/reports/agent_20260514_1640.md
?? tools/data/reports/agent_20260514_1649.md
?? tools/data/reports/agent_20260514_1656.md
?? tools/data/reports/agent_20260514_1701.md
?? tools/data/reports/agent_20260514_1850.md
?? tools/data/reports/agent_20260515_1623.md
?? tools/data/reports/agent_20260515_1647.md
?? tools/data/reports/agent_20260515_1659.md
?? tools/data/reports/agent_20260515_1705.md
?? tools/data/reports/agent_20260515_1712.md
?? tools/data/reports/agent_20260515_1724.md
?? tools/data/reports/agent_20260515_1734.md
?? tools/data/reports/agent_20260515_1745.md
?? tools/data/reports/ddf_20260512_0405.json
?? tools/data/reports/ddf_20260513_0405.json
?? tools/data/reports/ddf_20260514_0405.json
?? tools/data/reports/ddf_20260515_0405.json
?? tools/data/reports/falsifier_20260512_0330.json
?? tools/data/reports/falsifier_20260513_0330.json
?? tools/data/reports/falsifier_20260514_0330.json
?? tools/data/reports/falsifier_20260514_1330.json
?? tools/data/reports/falsifier_20260514_1458.json
?? tools/data/reports/falsifier_20260514_1605.json
?? tools/data/reports/falsifier_20260514_1612.json
?? tools/data/reports/falsifier_20260514_1631.json
?? tools/data/reports/falsifier_20260514_1640.json
?? tools/data/reports/falsifier_20260514_1649.json
?? tools/data/reports/falsifier_20260514_1656.json
?? tools/data/reports/falsifier_20260514_1701.json
?? tools/data/reports/falsifier_20260514_1850.json
?? tools/data/reports/falsifier_20260515_1623.json
?? tools/data/reports/falsifier_20260515_1647.json
?? tools/data/reports/falsifier_20260515_1659.json
?? tools/data/reports/falsifier_20260515_1705.json
?? tools/data/reports/falsifier_20260515_1712.json
?? tools/data/reports/falsifier_20260515_1712.raw.txt
?? tools/data/reports/falsifier_20260515_1724.json
?? tools/data/reports/falsifier_20260515_1734.json
?? tools/data/reports/loop_guard_20260512_0330.json
?? tools/data/reports/loop_guard_20260513_0330.json
?? tools/data/reports/loop_guard_20260514_0330.json
?? tools/data/reports/loop_guard_20260514_1330.json
?? tools/data/reports/loop_guard_20260514_1458.json
?? tools/data/reports/loop_guard_20260514_1605.json
?? tools/data/reports/loop_guard_20260514_1612.json
?? tools/data/reports/loop_guard_20260514_1631.json
?? tools/data/reports/loop_guard_20260514_1640.json
?? tools/data/reports/loop_guard_20260514_1649.json
?? tools/data/reports/loop_guard_20260514_1656.json
?? tools/data/reports/loop_guard_20260514_1701.json
?? tools/data/reports/loop_guard_20260514_1850.json
?? tools/data/reports/loop_guard_20260515_1623.json
?? tools/data/reports/loop_guard_20260515_1647.json
?? tools/data/reports/loop_guard_20260515_1659.json
?? tools/data/reports/loop_guard_20260515_1705.json
?? tools/data/reports/loop_guard_20260515_1712.json
?? tools/data/reports/loop_guard_20260515_1724.json
?? tools/data/reports/loop_guard_20260515_1734.json
?? tools/data/reports/quarantine_manifest.json
?? tools/data/restore_backups/
?? tools/data/seme_backup_b2_20260512_033557.json
?? tools/data/seme_backup_b2_20260513_033622.json
?? tools/data/seme_backup_b2_20260514_150530.json
?? tools/data/seme_backup_b2_20260514_161119.json
?? tools/data/tool_contracts/
?? tools/data/veritas/veritas_20260512_033539.json
?? tools/data/veritas/veritas_20260513_033605.json
?? tools/data/veritas/veritas_20260514_033420.json
?? tools/data/veritas/veritas_20260514_133424.json
?? tools/data/veritas/veritas_20260514_150454.json
?? tools/data/veritas/veritas_20260514_161058.json
?? tools/data/veritas/veritas_20260514_161719.json
?? tools/data/veritas/veritas_20260514_163527.json
?? tools/data/veritas/veritas_20260514_164509.json
?? tools/data/veritas/veritas_20260514_165437.json
?? tools/data/veritas/veritas_20260514_170109.json
?? tools/data/veritas/veritas_20260514_171152.json
?? tools/data/veritas/veritas_20260514_185450.json
?? tools/data/veritas/veritas_20260515_162727.json
?? tools/data/veritas/veritas_20260515_165116.json
?? tools/data/veritas/veritas_20260515_170328.json
?? tools/data/veritas/veritas_20260515_171005.json
?? tools/data/veritas/veritas_20260515_171725.json
?? tools/data/veritas/veritas_20260515_172913.json
?? tools/data/veritas/veritas_20260515_174023.json
?? tools/exp_aubry_boundary_phase_transport_gate.py
?? tools/exp_photonic_boundary_third_included_gate.py
?? tools/exp_physical_sr_residue_bounce.py
?? tools/exp_prime_sr_persistent_boundary.py
?? tools/exp_prime_vs_mod6_sr_boundary.py
?? tools/exp_quasiperiodic_grammar_scale_gate.py
?? tools/field_coherence_preflight.py
?? tools/field_rebuild_risk_map.py
?? tools/lab_preflight_agent.py
?? tools/lab_tool_contract.py
?? tools/prime_mod6_counter_null_audit.py
?? tools/prime_mod6_generative_null_audit.py
?? tools/prime_mod6_null_fairness_audit.py
?? tools/prime_mod6_pipeline_closeout.py
?? tools/selector_authority_matrix.py
?? tools/stale_field_source_map.py

exec
/bin/bash -lc 'python tools/lab_preflight_agent.py --cycle 20260515_1745 --json' in /opt/MM_D-ND
diff --git a/tools/data/reports/agent_20260515_1745.md b/tools/data/reports/agent_20260515_1745.md
new file mode 100644
index 0000000000000000000000000000000000000000..f1d0abc77f960601d7218d6ad380c4440fe243f6
--- /dev/null
+++ b/tools/data/reports/agent_20260515_1745.md
@@ -0,0 +1,105 @@
+# Agent Report - Aubry Boundary Phase Transport Gate
+**Date**: 2026-05-15 17:45  
+**Piano**: 118  
+**Tension explored**: BOUNDARY / TENS_SCALE_TRASCENDENZA_LIMITE  
+**verdict**: CONSTRAINT - nel ritorno tight-binding `phi` e' terzo incluso congiunto solo per V=0.50..1.25; oltre V=1.50 la localizzazione resta intermedia ma la statistica spettrale rompe il bicono.  
+observables_registry: none; dedicated observables only  
+observables_used: [spacing_r, mean_ipr, median_ipr, participation_entropy]  
+**observable_contract**: claim=`phi` e' stato fisico di confine tra ordine periodico e disordine random solo se statistica spettrale e localizzazione concordano; observable=`spacing_r` + `mean_ipr`/`participation_entropy`; operator=`tools/exp_aubry_boundary_phase_transport_gate.py`; generator=Hamiltoniana tight-binding binaria con sequenze phi/silver/bronze, periodico AB e random bilanciato a densita phi; denominator=N={89,144,233} x phase={0,0.25,0.5,0.75} x V=0.50..2.50 step 0.25 x generator, random_trials=6; non_possible=promuovere confine fisico da una sola osservabile, da phase0, o da un valore critico aggregato; not_tested=limite asintotico, modello Aubry-Andre coseno canonico, dati sperimentali, classi GUE/Poisson universali dirette.
+
+## Respiro fuori-tempo
+
+- **Combo**: A2 confine det=-1 + A9 terzo incluso + A11 combo + QxG continuo/discreto + TxQ matrice densita come lettura spettrale + tensione `BOUNDARY` 8 GUE / 5 Poisson.
+- **Dipolo / punto-zero**: repulsione spettrale / localizzazione degli autostati. Punto-zero = Hamiltoniana prima che una metrica singola decida se il dominio e' GUE-like, Poisson-like o di confine.
+- **Piano superiore**: geometria dei campi su reticolo; la fase del potenziale e' parte del bordo, non rumore da mediare.
+- **Operatori laterali scelti**: Hamiltonian spectrum, boundary operator, phase/parallel-transport come memoria del denominatore.
+- **Contaminazione cognitiva**: CE-0001 / KSAR usato come reiterazione del deposito 17:24 nel perimetro fisico indicato dalla consecutio; CE-0038 usato come equilibrio tra estremi, ma reso falsificabile da due osservabili non collineari.
+- **Proto-ipotesi**: se il confine e' terzo incluso operativo, `phi` deve stare tra periodico e random sia nello spacing sia nella localizzazione; se solo una delle due osservabili resta intermedia, il confine e' componente, non stato fisico chiuso.
+- **Proiezione**: diagonalizzo Hamiltoniane tight-binding binarie e confronto mediane row-aligned per V, taglia, fase e generatore.
+
+## Aderenza alla direzione
+
+- `relation`: follows_direction
+- `why`: segue la direzione viva "8 domini GUE, 5 Poisson - il confine e' il terzo incluso operativo" portando il confine in un ritorno fisico dove GUE/Poisson diventano spacing/localizzazione.
+- `not_drift`: non riapre prime/mod6, non usa selector legacy, non rifitta `V_c`; consuma `TENS_SCALE_TRASCENDENZA_LIMITE` solo come nodo regressivo di denominatore e fase.
+
+## Claim Under Test
+
+> Nel modello tight-binding binario, `phi` realizza un terzo incluso fisico tra periodico e random solo sui valori di V dove `spacing_r` e localizzazione lo collocano insieme dentro il segmento periodico-random e separato dal random.
+
+## Question
+
+Il ritorno Aubry/Fibonacci chiude il confine come stato fisico congiunto, oppure lo conserva solo come componente osservabile-specifica?
+
+## Ritorno fisico
+
+- **Punto fisico sorgente**: confine GUE/Poisson come attrito tra repulsione dei livelli e indipendenza/localizzazione.
+- **Attraversamento matematico**: sequenze Sturmian e random bilanciato proiettati in Hamiltoniane tridiagonali con potenziale binario centrato.
+- **Punto fisico di ritorno**: modello tight-binding 1D misurabile tramite spettro e autostati.
+- **Relazione nuova**: il confine non e' un `V_c` unico; e' una finestra di V dove due lettori indipendenti concordano.
+- **Osservabile/test fisico possibile**: misurare spacing e IPR nello stesso reticolo sintetico/fotonico con fase controllata.
+- **Se fallisce**: resta vincolo sul denominatore dei futuri ritorni fisici, non scoperta promuovibile.
+
+## Experiment Design
+
+- `python tools/dnd_scenario.py --best` eseguito: massimo discriminante `TENS_SCALE_TRASCENDENZA_LIMITE`.
+- `python -m py_compile tools/exp_aubry_boundary_phase_transport_gate.py` completato.
+- Run: `python tools/exp_aubry_boundary_phase_transport_gate.py --out tools/data/aubry_boundary_phase_transport_gate_20260515_1745.json`.
+- Perimetro deterministico: 108 righe per ciascun generatore phi/silver/bronze/periodic_ab.
+- Perimetro random: 648 righe balanced random a densita phi.
+- La misura serve la combo perche' rende non-possibile una chiusura del confine da una sola metrica.
+
+## Results
+
+| V | joint | r_between | ipr_between | sep_random | phi_r | periodic_r | random_r | phi_ipr | periodic_ipr | random_ipr |
+|---:|---|---|---|---|---:|---:|---:|---:|---:|---:|
+| 0.50 | true | true | true | true | 0.632 | 0.935 | 0.565 | 0.0156 | 0.0124 | 0.0213 |
+| 0.75 | true | true | true | true | 0.566 | 0.929 | 0.497 | 0.0205 | 0.0133 | 0.0358 |
+| 1.00 | true | true | true | true | 0.511 | 0.925 | 0.455 | 0.0268 | 0.0141 | 0.0523 |
+| 1.25 | true | true | true | true | 0.468 | 0.922 | 0.425 | 0.0342 | 0.0148 | 0.0767 |
+| 1.50 | false | true | true | false | 0.432 | 0.920 | 0.419 | 0.0425 | 0.0155 | 0.0999 |
+| 1.75 | false | false | true | false | 0.399 | 0.919 | 0.409 | 0.0507 | 0.0160 | 0.1204 |
+| 2.00 | false | false | true | true | 0.369 | 0.917 | 0.404 | 0.0597 | 0.0166 | 0.1437 |
+| 2.25 | false | false | true | true | 0.345 | 0.917 | 0.383 | 0.0676 | 0.0170 | 0.1662 |
+| 2.50 | false | false | true | true | 0.327 | 0.916 | 0.380 | 0.0751 | 0.0174 | 0.1891 |
+
+## Key Findings
+
+1. **Verificato**: `phi` chiude il bicono congiunto per V=0.50, 0.75, 1.00, 1.25: `spacing_r` e `mean_ipr` sono entrambi tra periodico e random, con separazione dal random.
+2. **Verificato**: a V=1.50 la posizione intermedia resta vera per entrambe le osservabili, ma la separazione dal random cade sotto soglia; non e' boundary joint promuovibile.
+3. **Verificato**: da V=1.75 in poi `spacing_r(phi)` scende fuori dal segmento periodico-random, mentre `mean_ipr` resta intermedio fino a V=2.50.
+4. **Inferito**: la finestra fisica del confine e' `0.50 <= V <= 1.25` nel perimetro misurato. Oltre quella soglia, il confine si scinde: localizzazione conserva il terzo incluso, spacing no.
+
+## Verdict
+
+**CONSTRAINT**.
+
+Il ritorno Aubry/Fibonacci trasferisce il confine BOUNDARY solo come finestra di accoppiamento. La formulazione valida e': nel perimetro misurato, `phi` e' terzo incluso fisico congiunto per V=0.50..1.25; sopra V=1.50 non si puo' parlare di stato di confine chiuso, ma di divergenza tra lettore spettrale e lettore di localizzazione.
+
+## Bicono della scoperta
+
+- **Due radici**: spacing tra livelli / localizzazione degli autostati.
+- **Singolare**: Hamiltoniana row-local `N,phase,V,generator` prima della classificazione.
+- **Invariante di passaggio**: ogni ritorno fisico del confine deve dichiarare almeno due osservabili non collineari e la finestra di parametro dove concordano.
+- **Campo di possibilita**: possibile = progettare test fisici con finestra V e fase controllata; non-possibile = usare `V_c`, `T_mean`, stopband o spacing singolo come prova di terzo incluso globale.
+
+## Consecutio
+
+Il prossimo ciclo puo' usare `exp_aubry_boundary_phase_transport_gate.py` come gate per ritorni fisici: cercare se la finestra V=0.50..1.25 sopravvive a N piu grandi o a un potenziale Aubry-Andre coseno. Se la finestra si muove, il claim resta finestra row-local; se resta stabile, diventa candidato ponte QxG fisico.
+
+## Ricadute pratiche
+
+ssp_value: yes. Lo script produce un gate riusabile per confrontare spacing e localizzazione su reticoli sintetici prima di costruire demo o visualizzazioni del confine.
+
+## Telemetria
+
+- No API paid run: `ANTHROPIC_API_KEY` e `OPENAI_API_KEY` non presenti nell'ambiente.
+- Nessun update del seme.
+- Nessun runtime pointer mutation.
+- Nessuna promozione e nessun public sync.
+
+## Files
+
+- `tools/exp_aubry_boundary_phase_transport_gate.py`
+- `tools/data/aubry_boundary_phase_transport_gate_20260515_1745.json`
+- `tools/data/reports/agent_20260515_1745.md`
diff --git a/tools/exp_aubry_boundary_phase_transport_gate.py b/tools/exp_aubry_boundary_phase_transport_gate.py
new file mode 100644
index 0000000000000000000000000000000000000000..8487656a64b34e688fb3f65c4708e1aa25469724
--- /dev/null
+++ b/tools/exp_aubry_boundary_phase_transport_gate.py
@@ -0,0 +1,284 @@
+#!/usr/bin/env python3
+"""
+Aubry/Fibonacci boundary phase transport gate.
+
+Projects the live BOUNDARY direction into a 1D tight-binding model with binary
+quasiperiodic potentials. The test is deliberately joint: a boundary return is
+accepted only when the phi stack is between periodic order and balanced random
+disorder for both spectral spacing and eigenstate localization.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+
+PHI = (1 + np.sqrt(5)) / 2
+SILVER = 1 + np.sqrt(2)
+BRONZE = 1 + np.sqrt(3)
+
+
+def sturmian_sequence(theta: float, n: int, phase: float) -> np.ndarray:
+    idx = np.arange(n + 1, dtype=float)
+    vals = np.floor(idx * theta + phase)
+    return np.diff(vals).astype(float)
+
+
+def periodic_sequence(n: int) -> np.ndarray:
+    return (np.arange(n) % 2).astype(float)
+
+
+def hamiltonian(diagonal: np.ndarray) -> np.ndarray:
+    n = len(diagonal)
+    matrix = np.diag(diagonal.astype(float))
+    off = np.ones(n - 1, dtype=float)
+    matrix += np.diag(off, 1) + np.diag(off, -1)
+    return matrix
+
+
+def central_slice(n: int, central_fraction: float) -> slice:
+    keep = max(8, int(round(n * central_fraction)))
+    keep = min(n, keep)
+    start = (n - keep) // 2
+    return slice(start, start + keep)
+
+
+def spacing_r(levels: np.ndarray, central_fraction: float) -> float | None:
+    levels = np.sort(np.asarray(levels, dtype=float))
+    central = levels[central_slice(len(levels), central_fraction)]
+    gaps = np.diff(central)
+    gaps = gaps[np.isfinite(gaps) & (gaps > 1e-12)]
+    if len(gaps) < 2:
+        return None
+    left = gaps[:-1]
+    right = gaps[1:]
+    return float(np.mean(np.minimum(left, right) / np.maximum(left, right)))
+
+
+def localization_metrics(vectors: np.ndarray, central_fraction: float) -> dict[str, float]:
+    n = vectors.shape[0]
+    subset = vectors[:, central_slice(n, central_fraction)]
+    probs = np.square(np.abs(subset))
+    ipr = np.sum(probs * probs, axis=0)
+    entropy = []
+    for col in range(probs.shape[1]):
+        p = probs[:, col]
+        p = p[p > 1e-15]
+        entropy.append(float(-np.sum(p * np.log(p)) / np.log(n)))
+    return {
+        "mean_ipr": float(np.mean(ipr)),
+        "median_ipr": float(np.median(ipr)),
+        "participation_entropy": float(np.mean(entropy)) if entropy else 0.0,
+    }
+
+
+def spectrum_row(
+    domain: str,
+    seq: np.ndarray,
+    n: int,
+    phase: float,
+    v_value: float,
+    central_fraction: float,
+    trial: int | None = None,
+) -> dict[str, Any]:
+    centered = seq - float(np.mean(seq))
+    levels, vectors = np.linalg.eigh(hamiltonian(v_value * centered))
+    metrics = localization_metrics(vectors, central_fraction)
+    row: dict[str, Any] = {
+        "domain": domain,
+        "N": n,
+        "phase": phase,
+        "V": v_value,
+        "ones": int(np.sum(seq)),
+        "spacing_r": spacing_r(levels, central_fraction),
+        **metrics,
+    }
+    if trial is not None:
+        row["trial"] = trial
+    return row
+
+
+def finite(values: list[float | None]) -> np.ndarray:
+    return np.array([v for v in values if v is not None and np.isfinite(v)], dtype=float)
+
+
+def aggregate(rows: list[dict[str, Any]]) -> dict[str, Any]:
+    out: dict[str, Any] = {"count": len(rows)}
+    for key in ["spacing_r", "mean_ipr", "median_ipr", "participation_entropy"]:
+        arr = finite([row.get(key) for row in rows])
+        if len(arr) == 0:
+            out[key] = {"count": 0}
+        else:
+            out[key] = {
+                "count": int(len(arr)),
+                "median": float(np.median(arr)),
+                "mean": float(np.mean(arr)),
+                "min": float(np.min(arr)),
+                "max": float(np.max(arr)),
+            }
+    return out
+
+
+def median_metric(summary: dict[str, Any], domain: str, v_key: str, metric: str) -> float | None:
+    value = summary.get(v_key, {}).get(domain, {}).get(metric, {})
+    if not isinstance(value, dict):
+        return None
+    median = value.get("median")
+    return float(median) if median is not None else None
+
+
+def between(value: float, left: float, right: float) -> bool:
+    return min(left, right) <= value <= max(left, right)
+
+
+def parse_csv_ints(value: str) -> list[int]:
+    return [int(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def parse_csv_floats(value: str) -> list[float]:
+    return [float(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    rng = np.random.default_rng(args.seed)
+    ns = parse_csv_ints(args.ns)
+    phases = parse_csv_floats(args.phases)
+    v_values = np.arange(args.v_min, args.v_max + args.v_step / 2, args.v_step)
+    domains = {
+        "phi": 1 / PHI,
+        "silver": 1 / SILVER,
+        "bronze": 1 / BRONZE,
+    }
+
+    rows: list[dict[str, Any]] = []
+    for n in ns:
+        for phase in phases:
+            phi_seq = sturmian_sequence(1 / PHI, n, phase)
+            ones = int(np.sum(phi_seq))
+            for v_value in v_values:
+                for domain, theta in domains.items():
+                    seq = sturmian_sequence(theta, n, phase)
+                    rows.append(spectrum_row(domain, seq, n, phase, float(v_value), args.central_fraction))
+
+                periodic = periodic_sequence(n)
+                rows.append(spectrum_row("periodic_ab", periodic, n, phase, float(v_value), args.central_fraction))
+
+                for trial in range(args.random_trials):
+                    seq = np.array([1.0] * ones + [0.0] * (n - ones), dtype=float)
+                    rng.shuffle(seq)
+                    rows.append(
+                        spectrum_row(
+                            "balanced_random_phi_density",
+                            seq,
+                            n,
+                            phase,
+                            float(v_value),
+                            args.central_fraction,
+                            trial=trial,
+                        )
+                    )
+
+    summary_by_v: dict[str, dict[str, Any]] = {}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        summary_by_v[v_key] = {}
+        for domain in sorted({row["domain"] for row in rows}):
+            subset = [row for row in rows if row["domain"] == domain and abs(row["V"] - v_value) < 1e-12]
+            summary_by_v[v_key][domain] = aggregate(subset)
+
+    classification: dict[str, Any] = {"joint_boundary_v": [], "by_v": {}}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        needed = {
+            "phi_r": median_metric(summary_by_v, "phi", v_key, "spacing_r"),
+            "periodic_r": median_metric(summary_by_v, "periodic_ab", v_key, "spacing_r"),
+            "random_r": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "spacing_r"),
+            "phi_ipr": median_metric(summary_by_v, "phi", v_key, "mean_ipr"),
+            "periodic_ipr": median_metric(summary_by_v, "periodic_ab", v_key, "mean_ipr"),
+            "random_ipr": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "mean_ipr"),
+            "phi_entropy": median_metric(summary_by_v, "phi", v_key, "participation_entropy"),
+            "periodic_entropy": median_metric(summary_by_v, "periodic_ab", v_key, "participation_entropy"),
+            "random_entropy": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "participation_entropy"),
+        }
+        complete = all(value is not None for value in needed.values())
+        r_between = bool(complete and between(needed["phi_r"], needed["periodic_r"], needed["random_r"]))
+        ipr_between = bool(complete and between(needed["phi_ipr"], needed["periodic_ipr"], needed["random_ipr"]))
+        entropy_between = bool(complete and between(needed["phi_entropy"], needed["periodic_entropy"], needed["random_entropy"]))
+        separated_random = bool(
+            complete
+            and abs(needed["phi_r"] - needed["random_r"]) >= args.min_r_delta
+            and abs(needed["phi_ipr"] - needed["random_ipr"]) >= args.min_ipr_delta
+        )
+        joint = bool(r_between and ipr_between and separated_random)
+        classification["by_v"][v_key] = {
+            **needed,
+            "spacing_r_between": r_between,
+            "mean_ipr_between": ipr_between,
+            "participation_entropy_between": entropy_between,
+            "separated_from_random": separated_random,
+            "joint_boundary": joint,
+        }
+        if joint:
+            classification["joint_boundary_v"].append(float(v_value))
+
+    return {
+        "experiment": "aubry_boundary_phase_transport_gate",
+        "parameters": {
+            "ns": ns,
+            "phases": phases,
+            "v_min": args.v_min,
+            "v_max": args.v_max,
+            "v_step": args.v_step,
+            "central_fraction": args.central_fraction,
+            "random_trials": args.random_trials,
+            "seed": args.seed,
+            "min_r_delta": args.min_r_delta,
+            "min_ipr_delta": args.min_ipr_delta,
+        },
+        "observable_contract": {
+            "claim": "phi is a physical boundary state between periodic order and balanced random disorder only if spectral spacing and localization agree",
+            "observable": "spacing_r plus mean_ipr / participation_entropy on tight-binding spectra",
+            "operator": "binary quasiperiodic tight-binding Hamiltonian with phase row denominator",
+            "denominator": "N x phase x V x generator rows with balanced random controls",
+            "non_possible": "single-observable boundary, phase-aggregated critical value, or phi outside the periodic-random interval",
+        },
+        "classification": classification,
+        "summary_by_v": summary_by_v,
+        "rows": rows,
+    }
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--ns", default="89,144,233")
+    parser.add_argument("--phases", default="0,0.25,0.5,0.75")
+    parser.add_argument("--v-min", type=float, default=0.5)
+    parser.add_argument("--v-max", type=float, default=2.5)
+    parser.add_argument("--v-step", type=float, default=0.25)
+    parser.add_argument("--central-fraction", type=float, default=0.6)
+    parser.add_argument("--random-trials", type=int, default=6)
+    parser.add_argument("--seed", type=int, default=202605151745)
+    parser.add_argument("--min-r-delta", type=float, default=0.025)
+    parser.add_argument("--min-ipr-delta", type=float, default=0.0025)
+    parser.add_argument("--out", default="tools/data/aubry_boundary_phase_transport_gate_20260515_1745.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), encoding="utf-8")
+
+    compact = {
+        "classification": output["classification"],
+        "out": str(out),
+    }
+    print(json.dumps(compact, indent=2))
+
+
+if __name__ == "__main__":
+    main()

 succeeded in 704ms:
{"cycle_ref": "20260515_1745", "classification": "ARTIFACT_USEFUL_NOT_PUBLISHABLE", "recommended_action": "KEEP_ARTIFACT_STOP_REPORT_PROMOTION", "stable_anchor": {"cycle": "20260515_1712", "title": "Agent Report - Prime/Mod6 Generative Null W2048", "veritas_band": "COLLASSO", "rho": 0.985}, "json_path": "/opt/MM_D-ND/tools/data/preflight/preflight_20260515_1745.json", "md_path": "/opt/MM_D-ND/tools/data/preflight/preflight_20260515_1745.md"}

apply patch
patch: completed
/opt/MM_D-ND/tools/data/reports/agent_20260515_1745.md
diff --git a/tools/data/reports/agent_20260515_1745.md b/tools/data/reports/agent_20260515_1745.md
new file mode 100644
index 0000000000000000000000000000000000000000..5e14ff6a1f13ed381a8e93b450b51ec8c78f60cd
--- /dev/null
+++ b/tools/data/reports/agent_20260515_1745.md
@@ -0,0 +1,109 @@
+# Agent Report - Aubry Boundary Phase Transport Gate
+**Date**: 2026-05-15 17:45  
+**Piano**: 118  
+**Tension explored**: BOUNDARY / TENS_SCALE_TRASCENDENZA_LIMITE  
+**verdict**: CONSTRAINT - nel ritorno tight-binding `phi` e' terzo incluso congiunto solo per V=0.50..1.25; oltre V=1.50 la localizzazione resta intermedia ma la statistica spettrale rompe il bicono.  
+observables_registry: none; dedicated observables only  
+observables_used: [spacing_r, mean_ipr, median_ipr, participation_entropy]  
+**observable_contract**: claim=`phi` e' stato fisico di confine tra ordine periodico e disordine random solo se statistica spettrale e localizzazione concordano; observable=`spacing_r` + `mean_ipr`/`participation_entropy`; operator=`tools/exp_aubry_boundary_phase_transport_gate.py`; generator=Hamiltoniana tight-binding binaria con sequenze phi/silver/bronze, periodico AB e random bilanciato a densita phi; denominator=N={89,144,233} x phase={0,0.25,0.5,0.75} x V=0.50..2.50 step 0.25 x generator, random_trials=6; non_possible=promuovere confine fisico da una sola osservabile, da phase0, o da un valore critico aggregato; not_tested=limite asintotico, modello Aubry-Andre coseno canonico, dati sperimentali, classi GUE/Poisson universali dirette.
+
+## Respiro fuori-tempo
+
+- **Combo**: A2 confine det=-1 + A9 terzo incluso + A11 combo + QxG continuo/discreto + TxQ matrice densita come lettura spettrale + tensione `BOUNDARY` 8 GUE / 5 Poisson.
+- **Dipolo / punto-zero**: repulsione spettrale / localizzazione degli autostati. Punto-zero = Hamiltoniana prima che una metrica singola decida se il dominio e' GUE-like, Poisson-like o di confine.
+- **Piano superiore**: geometria dei campi su reticolo; la fase del potenziale e' parte del bordo, non rumore da mediare.
+- **Operatori laterali scelti**: Hamiltonian spectrum, boundary operator, phase/parallel-transport come memoria del denominatore.
+- **Contaminazione cognitiva**: CE-0001 / KSAR usato come reiterazione del deposito 17:24 nel perimetro fisico indicato dalla consecutio; CE-0038 usato come equilibrio tra estremi, ma reso falsificabile da due osservabili non collineari.
+- **Proto-ipotesi**: se il confine e' terzo incluso operativo, `phi` deve stare tra periodico e random sia nello spacing sia nella localizzazione; se solo una delle due osservabili resta intermedia, il confine e' componente, non stato fisico chiuso.
+- **Proiezione**: diagonalizzo Hamiltoniane tight-binding binarie e confronto mediane row-aligned per V, taglia, fase e generatore.
+
+## Aderenza alla direzione
+
+- `relation`: follows_direction
+- `why`: segue la direzione viva "8 domini GUE, 5 Poisson - il confine e' il terzo incluso operativo" portando il confine in un ritorno fisico dove GUE/Poisson diventano spacing/localizzazione.
+- `not_drift`: non riapre prime/mod6, non usa selector legacy, non rifitta `V_c`; consuma `TENS_SCALE_TRASCENDENZA_LIMITE` solo come nodo regressivo di denominatore e fase.
+
+## Claim Under Test
+
+> Nel modello tight-binding binario, `phi` realizza un terzo incluso fisico tra periodico e random solo sui valori di V dove `spacing_r` e localizzazione lo collocano insieme dentro il segmento periodico-random e separato dal random.
+
+## Question
+
+Il ritorno Aubry/Fibonacci chiude il confine come stato fisico congiunto, oppure lo conserva solo come componente osservabile-specifica?
+
+## Ritorno fisico
+
+- **Punto fisico sorgente**: confine GUE/Poisson come attrito tra repulsione dei livelli e indipendenza/localizzazione.
+- **Attraversamento matematico**: sequenze Sturmian e random bilanciato proiettati in Hamiltoniane tridiagonali con potenziale binario centrato.
+- **Punto fisico di ritorno**: modello tight-binding 1D misurabile tramite spettro e autostati.
+- **Relazione nuova**: il confine non e' un `V_c` unico; e' una finestra di V dove due lettori indipendenti concordano.
+- **Osservabile/test fisico possibile**: misurare spacing e IPR nello stesso reticolo sintetico/fotonico con fase controllata.
+- **Se fallisce**: resta vincolo sul denominatore dei futuri ritorni fisici, non scoperta promuovibile.
+
+## Experiment Design
+
+- `python tools/dnd_scenario.py --best` eseguito: massimo discriminante `TENS_SCALE_TRASCENDENZA_LIMITE`.
+- `python -m py_compile tools/exp_aubry_boundary_phase_transport_gate.py` completato.
+- Run: `python tools/exp_aubry_boundary_phase_transport_gate.py --out tools/data/aubry_boundary_phase_transport_gate_20260515_1745.json`.
+- Perimetro deterministico: 108 righe per ciascun generatore phi/silver/bronze/periodic_ab.
+- Perimetro random: 648 righe balanced random a densita phi.
+- La misura serve la combo perche' rende non-possibile una chiusura del confine da una sola metrica.
+
+## Results
+
+| V | joint | r_between | ipr_between | sep_random | phi_r | periodic_r | random_r | phi_ipr | periodic_ipr | random_ipr |
+|---:|---|---|---|---|---:|---:|---:|---:|---:|---:|
+| 0.50 | true | true | true | true | 0.632 | 0.935 | 0.565 | 0.0156 | 0.0124 | 0.0213 |
+| 0.75 | true | true | true | true | 0.566 | 0.929 | 0.497 | 0.0205 | 0.0133 | 0.0358 |
+| 1.00 | true | true | true | true | 0.511 | 0.925 | 0.455 | 0.0268 | 0.0141 | 0.0523 |
+| 1.25 | true | true | true | true | 0.468 | 0.922 | 0.425 | 0.0342 | 0.0148 | 0.0767 |
+| 1.50 | false | true | true | false | 0.432 | 0.920 | 0.419 | 0.0425 | 0.0155 | 0.0999 |
+| 1.75 | false | false | true | false | 0.399 | 0.919 | 0.409 | 0.0507 | 0.0160 | 0.1204 |
+| 2.00 | false | false | true | true | 0.369 | 0.917 | 0.404 | 0.0597 | 0.0166 | 0.1437 |
+| 2.25 | false | false | true | true | 0.345 | 0.917 | 0.383 | 0.0676 | 0.0170 | 0.1662 |
+| 2.50 | false | false | true | true | 0.327 | 0.916 | 0.380 | 0.0751 | 0.0174 | 0.1891 |
+
+## Key Findings
+
+1. **Verificato**: `phi` chiude il bicono congiunto per V=0.50, 0.75, 1.00, 1.25: `spacing_r` e `mean_ipr` sono entrambi tra periodico e random, con separazione dal random.
+2. **Verificato**: a V=1.50 la posizione intermedia resta vera per entrambe le osservabili, ma la separazione dal random cade sotto soglia; non e' boundary joint promuovibile.
+3. **Verificato**: da V=1.75 in poi `spacing_r(phi)` scende fuori dal segmento periodico-random, mentre `mean_ipr` resta intermedio fino a V=2.50.
+4. **Inferito**: la finestra fisica del confine e' `0.50 <= V <= 1.25` nel perimetro misurato. Oltre quella soglia, il confine si scinde: localizzazione conserva il terzo incluso, spacing no.
+
+## Verdict
+
+**CONSTRAINT**.
+
+Il ritorno Aubry/Fibonacci trasferisce il confine BOUNDARY solo come finestra di accoppiamento. La formulazione valida e': nel perimetro misurato, `phi` e' terzo incluso fisico congiunto per V=0.50..1.25; sopra V=1.50 non si puo' parlare di stato di confine chiuso, ma di divergenza tra lettore spettrale e lettore di localizzazione.
+
+## Bicono della scoperta
+
+- **Due radici**: spacing tra livelli / localizzazione degli autostati.
+- **Singolare**: Hamiltoniana row-local `N,phase,V,generator` prima della classificazione.
+- **Invariante di passaggio**: ogni ritorno fisico del confine deve dichiarare almeno due osservabili non collineari e la finestra di parametro dove concordano.
+- **Campo di possibilita**: possibile = progettare test fisici con finestra V e fase controllata; non-possibile = usare `V_c`, `T_mean`, stopband o spacing singolo come prova di terzo incluso globale.
+
+## Consecutio
+
+Il prossimo ciclo puo' usare `exp_aubry_boundary_phase_transport_gate.py` come gate per ritorni fisici: cercare se la finestra V=0.50..1.25 sopravvive a N piu grandi o a un potenziale Aubry-Andre coseno. Se la finestra si muove, il claim resta finestra row-local; se resta stabile, diventa candidato ponte QxG fisico.
+
+## Ricadute pratiche
+
+ssp_value: yes. Lo script produce un gate riusabile per confrontare spacing e localizzazione su reticoli sintetici prima di costruire demo o visualizzazioni del confine.
+
+## Telemetria
+
+- No API paid run: `ANTHROPIC_API_KEY` e `OPENAI_API_KEY` non presenti nell'ambiente.
+- Preflight post-report: `python tools/lab_preflight_agent.py --cycle 20260515_1745 --json` => `ARTIFACT_USEFUL_NOT_PUBLISHABLE`, recommended_action=`KEEP_ARTIFACT_STOP_REPORT_PROMOTION`, stable_anchor=`20260515_1712`.
+- Nessun update del seme.
+- Nessun runtime pointer mutation.
+- Nessuna promozione e nessun public sync.
+- Worktree gia dirty prima del ciclo; ignorate modifiche non correlate.
+
+## Files
+
+- `tools/exp_aubry_boundary_phase_transport_gate.py`
+- `tools/data/aubry_boundary_phase_transport_gate_20260515_1745.json`
+- `tools/data/preflight/preflight_20260515_1745.json`
+- `tools/data/preflight/preflight_20260515_1745.md`
+- `tools/data/reports/agent_20260515_1745.md`
diff --git a/tools/exp_aubry_boundary_phase_transport_gate.py b/tools/exp_aubry_boundary_phase_transport_gate.py
new file mode 100644
index 0000000000000000000000000000000000000000..8487656a64b34e688fb3f65c4708e1aa25469724
--- /dev/null
+++ b/tools/exp_aubry_boundary_phase_transport_gate.py
@@ -0,0 +1,284 @@
+#!/usr/bin/env python3
+"""
+Aubry/Fibonacci boundary phase transport gate.
+
+Projects the live BOUNDARY direction into a 1D tight-binding model with binary
+quasiperiodic potentials. The test is deliberately joint: a boundary return is
+accepted only when the phi stack is between periodic order and balanced random
+disorder for both spectral spacing and eigenstate localization.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+
+PHI = (1 + np.sqrt(5)) / 2
+SILVER = 1 + np.sqrt(2)
+BRONZE = 1 + np.sqrt(3)
+
+
+def sturmian_sequence(theta: float, n: int, phase: float) -> np.ndarray:
+    idx = np.arange(n + 1, dtype=float)
+    vals = np.floor(idx * theta + phase)
+    return np.diff(vals).astype(float)
+
+
+def periodic_sequence(n: int) -> np.ndarray:
+    return (np.arange(n) % 2).astype(float)
+
+
+def hamiltonian(diagonal: np.ndarray) -> np.ndarray:
+    n = len(diagonal)
+    matrix = np.diag(diagonal.astype(float))
+    off = np.ones(n - 1, dtype=float)
+    matrix += np.diag(off, 1) + np.diag(off, -1)
+    return matrix
+
+
+def central_slice(n: int, central_fraction: float) -> slice:
+    keep = max(8, int(round(n * central_fraction)))
+    keep = min(n, keep)
+    start = (n - keep) // 2
+    return slice(start, start + keep)
+
+
+def spacing_r(levels: np.ndarray, central_fraction: float) -> float | None:
+    levels = np.sort(np.asarray(levels, dtype=float))
+    central = levels[central_slice(len(levels), central_fraction)]
+    gaps = np.diff(central)
+    gaps = gaps[np.isfinite(gaps) & (gaps > 1e-12)]
+    if len(gaps) < 2:
+        return None
+    left = gaps[:-1]
+    right = gaps[1:]
+    return float(np.mean(np.minimum(left, right) / np.maximum(left, right)))
+
+
+def localization_metrics(vectors: np.ndarray, central_fraction: float) -> dict[str, float]:
+    n = vectors.shape[0]
+    subset = vectors[:, central_slice(n, central_fraction)]
+    probs = np.square(np.abs(subset))
+    ipr = np.sum(probs * probs, axis=0)
+    entropy = []
+    for col in range(probs.shape[1]):
+        p = probs[:, col]
+        p = p[p > 1e-15]
+        entropy.append(float(-np.sum(p * np.log(p)) / np.log(n)))
+    return {
+        "mean_ipr": float(np.mean(ipr)),
+        "median_ipr": float(np.median(ipr)),
+        "participation_entropy": float(np.mean(entropy)) if entropy else 0.0,
+    }
+
+
+def spectrum_row(
+    domain: str,
+    seq: np.ndarray,
+    n: int,
+    phase: float,
+    v_value: float,
+    central_fraction: float,
+    trial: int | None = None,
+) -> dict[str, Any]:
+    centered = seq - float(np.mean(seq))
+    levels, vectors = np.linalg.eigh(hamiltonian(v_value * centered))
+    metrics = localization_metrics(vectors, central_fraction)
+    row: dict[str, Any] = {
+        "domain": domain,
+        "N": n,
+        "phase": phase,
+        "V": v_value,
+        "ones": int(np.sum(seq)),
+        "spacing_r": spacing_r(levels, central_fraction),
+        **metrics,
+    }
+    if trial is not None:
+        row["trial"] = trial
+    return row
+
+
+def finite(values: list[float | None]) -> np.ndarray:
+    return np.array([v for v in values if v is not None and np.isfinite(v)], dtype=float)
+
+
+def aggregate(rows: list[dict[str, Any]]) -> dict[str, Any]:
+    out: dict[str, Any] = {"count": len(rows)}
+    for key in ["spacing_r", "mean_ipr", "median_ipr", "participation_entropy"]:
+        arr = finite([row.get(key) for row in rows])
+        if len(arr) == 0:
+            out[key] = {"count": 0}
+        else:
+            out[key] = {
+                "count": int(len(arr)),
+                "median": float(np.median(arr)),
+                "mean": float(np.mean(arr)),
+                "min": float(np.min(arr)),
+                "max": float(np.max(arr)),
+            }
+    return out
+
+
+def median_metric(summary: dict[str, Any], domain: str, v_key: str, metric: str) -> float | None:
+    value = summary.get(v_key, {}).get(domain, {}).get(metric, {})
+    if not isinstance(value, dict):
+        return None
+    median = value.get("median")
+    return float(median) if median is not None else None
+
+
+def between(value: float, left: float, right: float) -> bool:
+    return min(left, right) <= value <= max(left, right)
+
+
+def parse_csv_ints(value: str) -> list[int]:
+    return [int(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def parse_csv_floats(value: str) -> list[float]:
+    return [float(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    rng = np.random.default_rng(args.seed)
+    ns = parse_csv_ints(args.ns)
+    phases = parse_csv_floats(args.phases)
+    v_values = np.arange(args.v_min, args.v_max + args.v_step / 2, args.v_step)
+    domains = {
+        "phi": 1 / PHI,
+        "silver": 1 / SILVER,
+        "bronze": 1 / BRONZE,
+    }
+
+    rows: list[dict[str, Any]] = []
+    for n in ns:
+        for phase in phases:
+            phi_seq = sturmian_sequence(1 / PHI, n, phase)
+            ones = int(np.sum(phi_seq))
+            for v_value in v_values:
+                for domain, theta in domains.items():
+                    seq = sturmian_sequence(theta, n, phase)
+                    rows.append(spectrum_row(domain, seq, n, phase, float(v_value), args.central_fraction))
+
+                periodic = periodic_sequence(n)
+                rows.append(spectrum_row("periodic_ab", periodic, n, phase, float(v_value), args.central_fraction))
+
+                for trial in range(args.random_trials):
+                    seq = np.array([1.0] * ones + [0.0] * (n - ones), dtype=float)
+                    rng.shuffle(seq)
+                    rows.append(
+                        spectrum_row(
+                            "balanced_random_phi_density",
+                            seq,
+                            n,
+                            phase,
+                            float(v_value),
+                            args.central_fraction,
+                            trial=trial,
+                        )
+                    )
+
+    summary_by_v: dict[str, dict[str, Any]] = {}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        summary_by_v[v_key] = {}
+        for domain in sorted({row["domain"] for row in rows}):
+            subset = [row for row in rows if row["domain"] == domain and abs(row["V"] - v_value) < 1e-12]
+            summary_by_v[v_key][domain] = aggregate(subset)
+
+    classification: dict[str, Any] = {"joint_boundary_v": [], "by_v": {}}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        needed = {
+            "phi_r": median_metric(summary_by_v, "phi", v_key, "spacing_r"),
+            "periodic_r": median_metric(summary_by_v, "periodic_ab", v_key, "spacing_r"),
+            "random_r": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "spacing_r"),
+            "phi_ipr": median_metric(summary_by_v, "phi", v_key, "mean_ipr"),
+            "periodic_ipr": median_metric(summary_by_v, "periodic_ab", v_key, "mean_ipr"),
+            "random_ipr": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "mean_ipr"),
+            "phi_entropy": median_metric(summary_by_v, "phi", v_key, "participation_entropy"),
+            "periodic_entropy": median_metric(summary_by_v, "periodic_ab", v_key, "participation_entropy"),
+            "random_entropy": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "participation_entropy"),
+        }
+        complete = all(value is not None for value in needed.values())
+        r_between = bool(complete and between(needed["phi_r"], needed["periodic_r"], needed["random_r"]))
+        ipr_between = bool(complete and between(needed["phi_ipr"], needed["periodic_ipr"], needed["random_ipr"]))
+        entropy_between = bool(complete and between(needed["phi_entropy"], needed["periodic_entropy"], needed["random_entropy"]))
+        separated_random = bool(
+            complete
+            and abs(needed["phi_r"] - needed["random_r"]) >= args.min_r_delta
+            and abs(needed["phi_ipr"] - needed["random_ipr"]) >= args.min_ipr_delta
+        )
+        joint = bool(r_between and ipr_between and separated_random)
+        classification["by_v"][v_key] = {
+            **needed,
+            "spacing_r_between": r_between,
+            "mean_ipr_between": ipr_between,
+            "participation_entropy_between": entropy_between,
+            "separated_from_random": separated_random,
+            "joint_boundary": joint,
+        }
+        if joint:
+            classification["joint_boundary_v"].append(float(v_value))
+
+    return {
+        "experiment": "aubry_boundary_phase_transport_gate",
+        "parameters": {
+            "ns": ns,
+            "phases": phases,
+            "v_min": args.v_min,
+            "v_max": args.v_max,
+            "v_step": args.v_step,
+            "central_fraction": args.central_fraction,
+            "random_trials": args.random_trials,
+            "seed": args.seed,
+            "min_r_delta": args.min_r_delta,
+            "min_ipr_delta": args.min_ipr_delta,
+        },
+        "observable_contract": {
+            "claim": "phi is a physical boundary state between periodic order and balanced random disorder only if spectral spacing and localization agree",
+            "observable": "spacing_r plus mean_ipr / participation_entropy on tight-binding spectra",
+            "operator": "binary quasiperiodic tight-binding Hamiltonian with phase row denominator",
+            "denominator": "N x phase x V x generator rows with balanced random controls",
+            "non_possible": "single-observable boundary, phase-aggregated critical value, or phi outside the periodic-random interval",
+        },
+        "classification": classification,
+        "summary_by_v": summary_by_v,
+        "rows": rows,
+    }
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--ns", default="89,144,233")
+    parser.add_argument("--phases", default="0,0.25,0.5,0.75")
+    parser.add_argument("--v-min", type=float, default=0.5)
+    parser.add_argument("--v-max", type=float, default=2.5)
+    parser.add_argument("--v-step", type=float, default=0.25)
+    parser.add_argument("--central-fraction", type=float, default=0.6)
+    parser.add_argument("--random-trials", type=int, default=6)
+    parser.add_argument("--seed", type=int, default=202605151745)
+    parser.add_argument("--min-r-delta", type=float, default=0.025)
+    parser.add_argument("--min-ipr-delta", type=float, default=0.0025)
+    parser.add_argument("--out", default="tools/data/aubry_boundary_phase_transport_gate_20260515_1745.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), encoding="utf-8")
+
+    compact = {
+        "classification": output["classification"],
+        "out": str(out),
+    }
+    print(json.dumps(compact, indent=2))
+
+
+if __name__ == "__main__":
+    main()

diff --git a/tools/data/reports/agent_20260515_1745.md b/tools/data/reports/agent_20260515_1745.md
new file mode 100644
index 0000000000000000000000000000000000000000..5e14ff6a1f13ed381a8e93b450b51ec8c78f60cd
--- /dev/null
+++ b/tools/data/reports/agent_20260515_1745.md
@@ -0,0 +1,109 @@
+# Agent Report - Aubry Boundary Phase Transport Gate
+**Date**: 2026-05-15 17:45  
+**Piano**: 118  
+**Tension explored**: BOUNDARY / TENS_SCALE_TRASCENDENZA_LIMITE  
+**verdict**: CONSTRAINT - nel ritorno tight-binding `phi` e' terzo incluso congiunto solo per V=0.50..1.25; oltre V=1.50 la localizzazione resta intermedia ma la statistica spettrale rompe il bicono.  
+observables_registry: none; dedicated observables only  
+observables_used: [spacing_r, mean_ipr, median_ipr, participation_entropy]  
+**observable_contract**: claim=`phi` e' stato fisico di confine tra ordine periodico e disordine random solo se statistica spettrale e localizzazione concordano; observable=`spacing_r` + `mean_ipr`/`participation_entropy`; operator=`tools/exp_aubry_boundary_phase_transport_gate.py`; generator=Hamiltoniana tight-binding binaria con sequenze phi/silver/bronze, periodico AB e random bilanciato a densita phi; denominator=N={89,144,233} x phase={0,0.25,0.5,0.75} x V=0.50..2.50 step 0.25 x generator, random_trials=6; non_possible=promuovere confine fisico da una sola osservabile, da phase0, o da un valore critico aggregato; not_tested=limite asintotico, modello Aubry-Andre coseno canonico, dati sperimentali, classi GUE/Poisson universali dirette.
+
+## Respiro fuori-tempo
+
+- **Combo**: A2 confine det=-1 + A9 terzo incluso + A11 combo + QxG continuo/discreto + TxQ matrice densita come lettura spettrale + tensione `BOUNDARY` 8 GUE / 5 Poisson.
+- **Dipolo / punto-zero**: repulsione spettrale / localizzazione degli autostati. Punto-zero = Hamiltoniana prima che una metrica singola decida se il dominio e' GUE-like, Poisson-like o di confine.
+- **Piano superiore**: geometria dei campi su reticolo; la fase del potenziale e' parte del bordo, non rumore da mediare.
+- **Operatori laterali scelti**: Hamiltonian spectrum, boundary operator, phase/parallel-transport come memoria del denominatore.
+- **Contaminazione cognitiva**: CE-0001 / KSAR usato come reiterazione del deposito 17:24 nel perimetro fisico indicato dalla consecutio; CE-0038 usato come equilibrio tra estremi, ma reso falsificabile da due osservabili non collineari.
+- **Proto-ipotesi**: se il confine e' terzo incluso operativo, `phi` deve stare tra periodico e random sia nello spacing sia nella localizzazione; se solo una delle due osservabili resta intermedia, il confine e' componente, non stato fisico chiuso.
+- **Proiezione**: diagonalizzo Hamiltoniane tight-binding binarie e confronto mediane row-aligned per V, taglia, fase e generatore.
+
+## Aderenza alla direzione
+
+- `relation`: follows_direction
+- `why`: segue la direzione viva "8 domini GUE, 5 Poisson - il confine e' il terzo incluso operativo" portando il confine in un ritorno fisico dove GUE/Poisson diventano spacing/localizzazione.
+- `not_drift`: non riapre prime/mod6, non usa selector legacy, non rifitta `V_c`; consuma `TENS_SCALE_TRASCENDENZA_LIMITE` solo come nodo regressivo di denominatore e fase.
+
+## Claim Under Test
+
+> Nel modello tight-binding binario, `phi` realizza un terzo incluso fisico tra periodico e random solo sui valori di V dove `spacing_r` e localizzazione lo collocano insieme dentro il segmento periodico-random e separato dal random.
+
+## Question
+
+Il ritorno Aubry/Fibonacci chiude il confine come stato fisico congiunto, oppure lo conserva solo come componente osservabile-specifica?
+
+## Ritorno fisico
+
+- **Punto fisico sorgente**: confine GUE/Poisson come attrito tra repulsione dei livelli e indipendenza/localizzazione.
+- **Attraversamento matematico**: sequenze Sturmian e random bilanciato proiettati in Hamiltoniane tridiagonali con potenziale binario centrato.
+- **Punto fisico di ritorno**: modello tight-binding 1D misurabile tramite spettro e autostati.
+- **Relazione nuova**: il confine non e' un `V_c` unico; e' una finestra di V dove due lettori indipendenti concordano.
+- **Osservabile/test fisico possibile**: misurare spacing e IPR nello stesso reticolo sintetico/fotonico con fase controllata.
+- **Se fallisce**: resta vincolo sul denominatore dei futuri ritorni fisici, non scoperta promuovibile.
+
+## Experiment Design
+
+- `python tools/dnd_scenario.py --best` eseguito: massimo discriminante `TENS_SCALE_TRASCENDENZA_LIMITE`.
+- `python -m py_compile tools/exp_aubry_boundary_phase_transport_gate.py` completato.
+- Run: `python tools/exp_aubry_boundary_phase_transport_gate.py --out tools/data/aubry_boundary_phase_transport_gate_20260515_1745.json`.
+- Perimetro deterministico: 108 righe per ciascun generatore phi/silver/bronze/periodic_ab.
+- Perimetro random: 648 righe balanced random a densita phi.
+- La misura serve la combo perche' rende non-possibile una chiusura del confine da una sola metrica.
+
+## Results
+
+| V | joint | r_between | ipr_between | sep_random | phi_r | periodic_r | random_r | phi_ipr | periodic_ipr | random_ipr |
+|---:|---|---|---|---|---:|---:|---:|---:|---:|---:|
+| 0.50 | true | true | true | true | 0.632 | 0.935 | 0.565 | 0.0156 | 0.0124 | 0.0213 |
+| 0.75 | true | true | true | true | 0.566 | 0.929 | 0.497 | 0.0205 | 0.0133 | 0.0358 |
+| 1.00 | true | true | true | true | 0.511 | 0.925 | 0.455 | 0.0268 | 0.0141 | 0.0523 |
+| 1.25 | true | true | true | true | 0.468 | 0.922 | 0.425 | 0.0342 | 0.0148 | 0.0767 |
+| 1.50 | false | true | true | false | 0.432 | 0.920 | 0.419 | 0.0425 | 0.0155 | 0.0999 |
+| 1.75 | false | false | true | false | 0.399 | 0.919 | 0.409 | 0.0507 | 0.0160 | 0.1204 |
+| 2.00 | false | false | true | true | 0.369 | 0.917 | 0.404 | 0.0597 | 0.0166 | 0.1437 |
+| 2.25 | false | false | true | true | 0.345 | 0.917 | 0.383 | 0.0676 | 0.0170 | 0.1662 |
+| 2.50 | false | false | true | true | 0.327 | 0.916 | 0.380 | 0.0751 | 0.0174 | 0.1891 |
+
+## Key Findings
+
+1. **Verificato**: `phi` chiude il bicono congiunto per V=0.50, 0.75, 1.00, 1.25: `spacing_r` e `mean_ipr` sono entrambi tra periodico e random, con separazione dal random.
+2. **Verificato**: a V=1.50 la posizione intermedia resta vera per entrambe le osservabili, ma la separazione dal random cade sotto soglia; non e' boundary joint promuovibile.
+3. **Verificato**: da V=1.75 in poi `spacing_r(phi)` scende fuori dal segmento periodico-random, mentre `mean_ipr` resta intermedio fino a V=2.50.
+4. **Inferito**: la finestra fisica del confine e' `0.50 <= V <= 1.25` nel perimetro misurato. Oltre quella soglia, il confine si scinde: localizzazione conserva il terzo incluso, spacing no.
+
+## Verdict
+
+**CONSTRAINT**.
+
+Il ritorno Aubry/Fibonacci trasferisce il confine BOUNDARY solo come finestra di accoppiamento. La formulazione valida e': nel perimetro misurato, `phi` e' terzo incluso fisico congiunto per V=0.50..1.25; sopra V=1.50 non si puo' parlare di stato di confine chiuso, ma di divergenza tra lettore spettrale e lettore di localizzazione.
+
+## Bicono della scoperta
+
+- **Due radici**: spacing tra livelli / localizzazione degli autostati.
+- **Singolare**: Hamiltoniana row-local `N,phase,V,generator` prima della classificazione.
+- **Invariante di passaggio**: ogni ritorno fisico del confine deve dichiarare almeno due osservabili non collineari e la finestra di parametro dove concordano.
+- **Campo di possibilita**: possibile = progettare test fisici con finestra V e fase controllata; non-possibile = usare `V_c`, `T_mean`, stopband o spacing singolo come prova di terzo incluso globale.
+
+## Consecutio
+
+Il prossimo ciclo puo' usare `exp_aubry_boundary_phase_transport_gate.py` come gate per ritorni fisici: cercare se la finestra V=0.50..1.25 sopravvive a N piu grandi o a un potenziale Aubry-Andre coseno. Se la finestra si muove, il claim resta finestra row-local; se resta stabile, diventa candidato ponte QxG fisico.
+
+## Ricadute pratiche
+
+ssp_value: yes. Lo script produce un gate riusabile per confrontare spacing e localizzazione su reticoli sintetici prima di costruire demo o visualizzazioni del confine.
+
+## Telemetria
+
+- No API paid run: `ANTHROPIC_API_KEY` e `OPENAI_API_KEY` non presenti nell'ambiente.
+- Preflight post-report: `python tools/lab_preflight_agent.py --cycle 20260515_1745 --json` => `ARTIFACT_USEFUL_NOT_PUBLISHABLE`, recommended_action=`KEEP_ARTIFACT_STOP_REPORT_PROMOTION`, stable_anchor=`20260515_1712`.
+- Nessun update del seme.
+- Nessun runtime pointer mutation.
+- Nessuna promozione e nessun public sync.
+- Worktree gia dirty prima del ciclo; ignorate modifiche non correlate.
+
+## Files
+
+- `tools/exp_aubry_boundary_phase_transport_gate.py`
+- `tools/data/aubry_boundary_phase_transport_gate_20260515_1745.json`
+- `tools/data/preflight/preflight_20260515_1745.json`
+- `tools/data/preflight/preflight_20260515_1745.md`
+- `tools/data/reports/agent_20260515_1745.md`
diff --git a/tools/exp_aubry_boundary_phase_transport_gate.py b/tools/exp_aubry_boundary_phase_transport_gate.py
new file mode 100644
index 0000000000000000000000000000000000000000..8487656a64b34e688fb3f65c4708e1aa25469724
--- /dev/null
+++ b/tools/exp_aubry_boundary_phase_transport_gate.py
@@ -0,0 +1,284 @@
+#!/usr/bin/env python3
+"""
+Aubry/Fibonacci boundary phase transport gate.
+
+Projects the live BOUNDARY direction into a 1D tight-binding model with binary
+quasiperiodic potentials. The test is deliberately joint: a boundary return is
+accepted only when the phi stack is between periodic order and balanced random
+disorder for both spectral spacing and eigenstate localization.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+
+PHI = (1 + np.sqrt(5)) / 2
+SILVER = 1 + np.sqrt(2)
+BRONZE = 1 + np.sqrt(3)
+
+
+def sturmian_sequence(theta: float, n: int, phase: float) -> np.ndarray:
+    idx = np.arange(n + 1, dtype=float)
+    vals = np.floor(idx * theta + phase)
+    return np.diff(vals).astype(float)
+
+
+def periodic_sequence(n: int) -> np.ndarray:
+    return (np.arange(n) % 2).astype(float)
+
+
+def hamiltonian(diagonal: np.ndarray) -> np.ndarray:
+    n = len(diagonal)
+    matrix = np.diag(diagonal.astype(float))
+    off = np.ones(n - 1, dtype=float)
+    matrix += np.diag(off, 1) + np.diag(off, -1)
+    return matrix
+
+
+def central_slice(n: int, central_fraction: float) -> slice:
+    keep = max(8, int(round(n * central_fraction)))
+    keep = min(n, keep)
+    start = (n - keep) // 2
+    return slice(start, start + keep)
+
+
+def spacing_r(levels: np.ndarray, central_fraction: float) -> float | None:
+    levels = np.sort(np.asarray(levels, dtype=float))
+    central = levels[central_slice(len(levels), central_fraction)]
+    gaps = np.diff(central)
+    gaps = gaps[np.isfinite(gaps) & (gaps > 1e-12)]
+    if len(gaps) < 2:
+        return None
+    left = gaps[:-1]
+    right = gaps[1:]
+    return float(np.mean(np.minimum(left, right) / np.maximum(left, right)))
+
+
+def localization_metrics(vectors: np.ndarray, central_fraction: float) -> dict[str, float]:
+    n = vectors.shape[0]
+    subset = vectors[:, central_slice(n, central_fraction)]
+    probs = np.square(np.abs(subset))
+    ipr = np.sum(probs * probs, axis=0)
+    entropy = []
+    for col in range(probs.shape[1]):
+        p = probs[:, col]
+        p = p[p > 1e-15]
+        entropy.append(float(-np.sum(p * np.log(p)) / np.log(n)))
+    return {
+        "mean_ipr": float(np.mean(ipr)),
+        "median_ipr": float(np.median(ipr)),
+        "participation_entropy": float(np.mean(entropy)) if entropy else 0.0,
+    }
+
+
+def spectrum_row(
+    domain: str,
+    seq: np.ndarray,
+    n: int,
+    phase: float,
+    v_value: float,
+    central_fraction: float,
+    trial: int | None = None,
+) -> dict[str, Any]:
+    centered = seq - float(np.mean(seq))
+    levels, vectors = np.linalg.eigh(hamiltonian(v_value * centered))
+    metrics = localization_metrics(vectors, central_fraction)
+    row: dict[str, Any] = {
+        "domain": domain,
+        "N": n,
+        "phase": phase,
+        "V": v_value,
+        "ones": int(np.sum(seq)),
+        "spacing_r": spacing_r(levels, central_fraction),
+        **metrics,
+    }
+    if trial is not None:
+        row["trial"] = trial
+    return row
+
+
+def finite(values: list[float | None]) -> np.ndarray:
+    return np.array([v for v in values if v is not None and np.isfinite(v)], dtype=float)
+
+
+def aggregate(rows: list[dict[str, Any]]) -> dict[str, Any]:
+    out: dict[str, Any] = {"count": len(rows)}
+    for key in ["spacing_r", "mean_ipr", "median_ipr", "participation_entropy"]:
+        arr = finite([row.get(key) for row in rows])
+        if len(arr) == 0:
+            out[key] = {"count": 0}
+        else:
+            out[key] = {
+                "count": int(len(arr)),
+                "median": float(np.median(arr)),
+                "mean": float(np.mean(arr)),
+                "min": float(np.min(arr)),
+                "max": float(np.max(arr)),
+            }
+    return out
+
+
+def median_metric(summary: dict[str, Any], domain: str, v_key: str, metric: str) -> float | None:
+    value = summary.get(v_key, {}).get(domain, {}).get(metric, {})
+    if not isinstance(value, dict):
+        return None
+    median = value.get("median")
+    return float(median) if median is not None else None
+
+
+def between(value: float, left: float, right: float) -> bool:
+    return min(left, right) <= value <= max(left, right)
+
+
+def parse_csv_ints(value: str) -> list[int]:
+    return [int(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def parse_csv_floats(value: str) -> list[float]:
+    return [float(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    rng = np.random.default_rng(args.seed)
+    ns = parse_csv_ints(args.ns)
+    phases = parse_csv_floats(args.phases)
+    v_values = np.arange(args.v_min, args.v_max + args.v_step / 2, args.v_step)
+    domains = {
+        "phi": 1 / PHI,
+        "silver": 1 / SILVER,
+        "bronze": 1 / BRONZE,
+    }
+
+    rows: list[dict[str, Any]] = []
+    for n in ns:
+        for phase in phases:
+            phi_seq = sturmian_sequence(1 / PHI, n, phase)
+            ones = int(np.sum(phi_seq))
+            for v_value in v_values:
+                for domain, theta in domains.items():
+                    seq = sturmian_sequence(theta, n, phase)
+                    rows.append(spectrum_row(domain, seq, n, phase, float(v_value), args.central_fraction))
+
+                periodic = periodic_sequence(n)
+                rows.append(spectrum_row("periodic_ab", periodic, n, phase, float(v_value), args.central_fraction))
+
+                for trial in range(args.random_trials):
+                    seq = np.array([1.0] * ones + [0.0] * (n - ones), dtype=float)
+                    rng.shuffle(seq)
+                    rows.append(
+                        spectrum_row(
+                            "balanced_random_phi_density",
+                            seq,
+                            n,
+                            phase,
+                            float(v_value),
+                            args.central_fraction,
+                            trial=trial,
+                        )
+                    )
+
+    summary_by_v: dict[str, dict[str, Any]] = {}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        summary_by_v[v_key] = {}
+        for domain in sorted({row["domain"] for row in rows}):
+            subset = [row for row in rows if row["domain"] == domain and abs(row["V"] - v_value) < 1e-12]
+            summary_by_v[v_key][domain] = aggregate(subset)
+
+    classification: dict[str, Any] = {"joint_boundary_v": [], "by_v": {}}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        needed = {
+            "phi_r": median_metric(summary_by_v, "phi", v_key, "spacing_r"),
+            "periodic_r": median_metric(summary_by_v, "periodic_ab", v_key, "spacing_r"),
+            "random_r": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "spacing_r"),
+            "phi_ipr": median_metric(summary_by_v, "phi", v_key, "mean_ipr"),
+            "periodic_ipr": median_metric(summary_by_v, "periodic_ab", v_key, "mean_ipr"),
+            "random_ipr": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "mean_ipr"),
+            "phi_entropy": median_metric(summary_by_v, "phi", v_key, "participation_entropy"),
+            "periodic_entropy": median_metric(summary_by_v, "periodic_ab", v_key, "participation_entropy"),
+            "random_entropy": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "participation_entropy"),
+        }
+        complete = all(value is not None for value in needed.values())
+        r_between = bool(complete and between(needed["phi_r"], needed["periodic_r"], needed["random_r"]))
+        ipr_between = bool(complete and between(needed["phi_ipr"], needed["periodic_ipr"], needed["random_ipr"]))
+        entropy_between = bool(complete and between(needed["phi_entropy"], needed["periodic_entropy"], needed["random_entropy"]))
+        separated_random = bool(
+            complete
+            and abs(needed["phi_r"] - needed["random_r"]) >= args.min_r_delta
+            and abs(needed["phi_ipr"] - needed["random_ipr"]) >= args.min_ipr_delta
+        )
+        joint = bool(r_between and ipr_between and separated_random)
+        classification["by_v"][v_key] = {
+            **needed,
+            "spacing_r_between": r_between,
+            "mean_ipr_between": ipr_between,
+            "participation_entropy_between": entropy_between,
+            "separated_from_random": separated_random,
+            "joint_boundary": joint,
+        }
+        if joint:
+            classification["joint_boundary_v"].append(float(v_value))
+
+    return {
+        "experiment": "aubry_boundary_phase_transport_gate",
+        "parameters": {
+            "ns": ns,
+            "phases": phases,
+            "v_min": args.v_min,
+            "v_max": args.v_max,
+            "v_step": args.v_step,
+            "central_fraction": args.central_fraction,
+            "random_trials": args.random_trials,
+            "seed": args.seed,
+            "min_r_delta": args.min_r_delta,
+            "min_ipr_delta": args.min_ipr_delta,
+        },
+        "observable_contract": {
+            "claim": "phi is a physical boundary state between periodic order and balanced random disorder only if spectral spacing and localization agree",
+            "observable": "spacing_r plus mean_ipr / participation_entropy on tight-binding spectra",
+            "operator": "binary quasiperiodic tight-binding Hamiltonian with phase row denominator",
+            "denominator": "N x phase x V x generator rows with balanced random controls",
+            "non_possible": "single-observable boundary, phase-aggregated critical value, or phi outside the periodic-random interval",
+        },
+        "classification": classification,
+        "summary_by_v": summary_by_v,
+        "rows": rows,
+    }
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--ns", default="89,144,233")
+    parser.add_argument("--phases", default="0,0.25,0.5,0.75")
+    parser.add_argument("--v-min", type=float, default=0.5)
+    parser.add_argument("--v-max", type=float, default=2.5)
+    parser.add_argument("--v-step", type=float, default=0.25)
+    parser.add_argument("--central-fraction", type=float, default=0.6)
+    parser.add_argument("--random-trials", type=int, default=6)
+    parser.add_argument("--seed", type=int, default=202605151745)
+    parser.add_argument("--min-r-delta", type=float, default=0.025)
+    parser.add_argument("--min-ipr-delta", type=float, default=0.0025)
+    parser.add_argument("--out", default="tools/data/aubry_boundary_phase_transport_gate_20260515_1745.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), encoding="utf-8")
+
+    compact = {
+        "classification": output["classification"],
+        "out": str(out),
+    }
+    print(json.dumps(compact, indent=2))
+
+
+if __name__ == "__main__":
+    main()

codex
Eseguito il ciclo e scritto il report in:

[agent_20260515_1745.md](/opt/MM_D-ND/tools/data/reports/agent_20260515_1745.md)

Ho aggiunto anche lo strumento riusabile:

[exp_aubry_boundary_phase_transport_gate.py](/opt/MM_D-ND/tools/exp_aubry_boundary_phase_transport_gate.py)

Output dati:

[aubry_boundary_phase_transport_gate_20260515_1745.json](/opt/MM_D-ND/tools/data/aubry_boundary_phase_transport_gate_20260515_1745.json)

Risultato: `phi` chiude il bicono spacing/localizzazione solo nella finestra `V=0.50..1.25`; sopra `V=1.50` il confine si scinde, quindi il report resta `CONSTRAINT`, non promozione.

Preflight eseguito: `ARTIFACT_USEFUL_NOT_PUBLISHABLE`, azione consigliata `KEEP_ARTIFACT_STOP_REPORT_PROMOTION`.
diff --git a/tools/data/reports/agent_20260515_1745.md b/tools/data/reports/agent_20260515_1745.md
new file mode 100644
index 0000000000000000000000000000000000000000..5e14ff6a1f13ed381a8e93b450b51ec8c78f60cd
--- /dev/null
+++ b/tools/data/reports/agent_20260515_1745.md
@@ -0,0 +1,109 @@
+# Agent Report - Aubry Boundary Phase Transport Gate
+**Date**: 2026-05-15 17:45  
+**Piano**: 118  
+**Tension explored**: BOUNDARY / TENS_SCALE_TRASCENDENZA_LIMITE  
+**verdict**: CONSTRAINT - nel ritorno tight-binding `phi` e' terzo incluso congiunto solo per V=0.50..1.25; oltre V=1.50 la localizzazione resta intermedia ma la statistica spettrale rompe il bicono.  
+observables_registry: none; dedicated observables only  
+observables_used: [spacing_r, mean_ipr, median_ipr, participation_entropy]  
+**observable_contract**: claim=`phi` e' stato fisico di confine tra ordine periodico e disordine random solo se statistica spettrale e localizzazione concordano; observable=`spacing_r` + `mean_ipr`/`participation_entropy`; operator=`tools/exp_aubry_boundary_phase_transport_gate.py`; generator=Hamiltoniana tight-binding binaria con sequenze phi/silver/bronze, periodico AB e random bilanciato a densita phi; denominator=N={89,144,233} x phase={0,0.25,0.5,0.75} x V=0.50..2.50 step 0.25 x generator, random_trials=6; non_possible=promuovere confine fisico da una sola osservabile, da phase0, o da un valore critico aggregato; not_tested=limite asintotico, modello Aubry-Andre coseno canonico, dati sperimentali, classi GUE/Poisson universali dirette.
+
+## Respiro fuori-tempo
+
+- **Combo**: A2 confine det=-1 + A9 terzo incluso + A11 combo + QxG continuo/discreto + TxQ matrice densita come lettura spettrale + tensione `BOUNDARY` 8 GUE / 5 Poisson.
+- **Dipolo / punto-zero**: repulsione spettrale / localizzazione degli autostati. Punto-zero = Hamiltoniana prima che una metrica singola decida se il dominio e' GUE-like, Poisson-like o di confine.
+- **Piano superiore**: geometria dei campi su reticolo; la fase del potenziale e' parte del bordo, non rumore da mediare.
+- **Operatori laterali scelti**: Hamiltonian spectrum, boundary operator, phase/parallel-transport come memoria del denominatore.
+- **Contaminazione cognitiva**: CE-0001 / KSAR usato come reiterazione del deposito 17:24 nel perimetro fisico indicato dalla consecutio; CE-0038 usato come equilibrio tra estremi, ma reso falsificabile da due osservabili non collineari.
+- **Proto-ipotesi**: se il confine e' terzo incluso operativo, `phi` deve stare tra periodico e random sia nello spacing sia nella localizzazione; se solo una delle due osservabili resta intermedia, il confine e' componente, non stato fisico chiuso.
+- **Proiezione**: diagonalizzo Hamiltoniane tight-binding binarie e confronto mediane row-aligned per V, taglia, fase e generatore.
+
+## Aderenza alla direzione
+
+- `relation`: follows_direction
+- `why`: segue la direzione viva "8 domini GUE, 5 Poisson - il confine e' il terzo incluso operativo" portando il confine in un ritorno fisico dove GUE/Poisson diventano spacing/localizzazione.
+- `not_drift`: non riapre prime/mod6, non usa selector legacy, non rifitta `V_c`; consuma `TENS_SCALE_TRASCENDENZA_LIMITE` solo come nodo regressivo di denominatore e fase.
+
+## Claim Under Test
+
+> Nel modello tight-binding binario, `phi` realizza un terzo incluso fisico tra periodico e random solo sui valori di V dove `spacing_r` e localizzazione lo collocano insieme dentro il segmento periodico-random e separato dal random.
+
+## Question
+
+Il ritorno Aubry/Fibonacci chiude il confine come stato fisico congiunto, oppure lo conserva solo come componente osservabile-specifica?
+
+## Ritorno fisico
+
+- **Punto fisico sorgente**: confine GUE/Poisson come attrito tra repulsione dei livelli e indipendenza/localizzazione.
+- **Attraversamento matematico**: sequenze Sturmian e random bilanciato proiettati in Hamiltoniane tridiagonali con potenziale binario centrato.
+- **Punto fisico di ritorno**: modello tight-binding 1D misurabile tramite spettro e autostati.
+- **Relazione nuova**: il confine non e' un `V_c` unico; e' una finestra di V dove due lettori indipendenti concordano.
+- **Osservabile/test fisico possibile**: misurare spacing e IPR nello stesso reticolo sintetico/fotonico con fase controllata.
+- **Se fallisce**: resta vincolo sul denominatore dei futuri ritorni fisici, non scoperta promuovibile.
+
+## Experiment Design
+
+- `python tools/dnd_scenario.py --best` eseguito: massimo discriminante `TENS_SCALE_TRASCENDENZA_LIMITE`.
+- `python -m py_compile tools/exp_aubry_boundary_phase_transport_gate.py` completato.
+- Run: `python tools/exp_aubry_boundary_phase_transport_gate.py --out tools/data/aubry_boundary_phase_transport_gate_20260515_1745.json`.
+- Perimetro deterministico: 108 righe per ciascun generatore phi/silver/bronze/periodic_ab.
+- Perimetro random: 648 righe balanced random a densita phi.
+- La misura serve la combo perche' rende non-possibile una chiusura del confine da una sola metrica.
+
+## Results
+
+| V | joint | r_between | ipr_between | sep_random | phi_r | periodic_r | random_r | phi_ipr | periodic_ipr | random_ipr |
+|---:|---|---|---|---|---:|---:|---:|---:|---:|---:|
+| 0.50 | true | true | true | true | 0.632 | 0.935 | 0.565 | 0.0156 | 0.0124 | 0.0213 |
+| 0.75 | true | true | true | true | 0.566 | 0.929 | 0.497 | 0.0205 | 0.0133 | 0.0358 |
+| 1.00 | true | true | true | true | 0.511 | 0.925 | 0.455 | 0.0268 | 0.0141 | 0.0523 |
+| 1.25 | true | true | true | true | 0.468 | 0.922 | 0.425 | 0.0342 | 0.0148 | 0.0767 |
+| 1.50 | false | true | true | false | 0.432 | 0.920 | 0.419 | 0.0425 | 0.0155 | 0.0999 |
+| 1.75 | false | false | true | false | 0.399 | 0.919 | 0.409 | 0.0507 | 0.0160 | 0.1204 |
+| 2.00 | false | false | true | true | 0.369 | 0.917 | 0.404 | 0.0597 | 0.0166 | 0.1437 |
+| 2.25 | false | false | true | true | 0.345 | 0.917 | 0.383 | 0.0676 | 0.0170 | 0.1662 |
+| 2.50 | false | false | true | true | 0.327 | 0.916 | 0.380 | 0.0751 | 0.0174 | 0.1891 |
+
+## Key Findings
+
+1. **Verificato**: `phi` chiude il bicono congiunto per V=0.50, 0.75, 1.00, 1.25: `spacing_r` e `mean_ipr` sono entrambi tra periodico e random, con separazione dal random.
+2. **Verificato**: a V=1.50 la posizione intermedia resta vera per entrambe le osservabili, ma la separazione dal random cade sotto soglia; non e' boundary joint promuovibile.
+3. **Verificato**: da V=1.75 in poi `spacing_r(phi)` scende fuori dal segmento periodico-random, mentre `mean_ipr` resta intermedio fino a V=2.50.
+4. **Inferito**: la finestra fisica del confine e' `0.50 <= V <= 1.25` nel perimetro misurato. Oltre quella soglia, il confine si scinde: localizzazione conserva il terzo incluso, spacing no.
+
+## Verdict
+
+**CONSTRAINT**.
+
+Il ritorno Aubry/Fibonacci trasferisce il confine BOUNDARY solo come finestra di accoppiamento. La formulazione valida e': nel perimetro misurato, `phi` e' terzo incluso fisico congiunto per V=0.50..1.25; sopra V=1.50 non si puo' parlare di stato di confine chiuso, ma di divergenza tra lettore spettrale e lettore di localizzazione.
+
+## Bicono della scoperta
+
+- **Due radici**: spacing tra livelli / localizzazione degli autostati.
+- **Singolare**: Hamiltoniana row-local `N,phase,V,generator` prima della classificazione.
+- **Invariante di passaggio**: ogni ritorno fisico del confine deve dichiarare almeno due osservabili non collineari e la finestra di parametro dove concordano.
+- **Campo di possibilita**: possibile = progettare test fisici con finestra V e fase controllata; non-possibile = usare `V_c`, `T_mean`, stopband o spacing singolo come prova di terzo incluso globale.
+
+## Consecutio
+
+Il prossimo ciclo puo' usare `exp_aubry_boundary_phase_transport_gate.py` come gate per ritorni fisici: cercare se la finestra V=0.50..1.25 sopravvive a N piu grandi o a un potenziale Aubry-Andre coseno. Se la finestra si muove, il claim resta finestra row-local; se resta stabile, diventa candidato ponte QxG fisico.
+
+## Ricadute pratiche
+
+ssp_value: yes. Lo script produce un gate riusabile per confrontare spacing e localizzazione su reticoli sintetici prima di costruire demo o visualizzazioni del confine.
+
+## Telemetria
+
+- No API paid run: `ANTHROPIC_API_KEY` e `OPENAI_API_KEY` non presenti nell'ambiente.
+- Preflight post-report: `python tools/lab_preflight_agent.py --cycle 20260515_1745 --json` => `ARTIFACT_USEFUL_NOT_PUBLISHABLE`, recommended_action=`KEEP_ARTIFACT_STOP_REPORT_PROMOTION`, stable_anchor=`20260515_1712`.
+- Nessun update del seme.
+- Nessun runtime pointer mutation.
+- Nessuna promozione e nessun public sync.
+- Worktree gia dirty prima del ciclo; ignorate modifiche non correlate.
+
+## Files
+
+- `tools/exp_aubry_boundary_phase_transport_gate.py`
+- `tools/data/aubry_boundary_phase_transport_gate_20260515_1745.json`
+- `tools/data/preflight/preflight_20260515_1745.json`
+- `tools/data/preflight/preflight_20260515_1745.md`
+- `tools/data/reports/agent_20260515_1745.md`
diff --git a/tools/exp_aubry_boundary_phase_transport_gate.py b/tools/exp_aubry_boundary_phase_transport_gate.py
new file mode 100644
index 0000000000000000000000000000000000000000..8487656a64b34e688fb3f65c4708e1aa25469724
--- /dev/null
+++ b/tools/exp_aubry_boundary_phase_transport_gate.py
@@ -0,0 +1,284 @@
+#!/usr/bin/env python3
+"""
+Aubry/Fibonacci boundary phase transport gate.
+
+Projects the live BOUNDARY direction into a 1D tight-binding model with binary
+quasiperiodic potentials. The test is deliberately joint: a boundary return is
+accepted only when the phi stack is between periodic order and balanced random
+disorder for both spectral spacing and eigenstate localization.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+
+PHI = (1 + np.sqrt(5)) / 2
+SILVER = 1 + np.sqrt(2)
+BRONZE = 1 + np.sqrt(3)
+
+
+def sturmian_sequence(theta: float, n: int, phase: float) -> np.ndarray:
+    idx = np.arange(n + 1, dtype=float)
+    vals = np.floor(idx * theta + phase)
+    return np.diff(vals).astype(float)
+
+
+def periodic_sequence(n: int) -> np.ndarray:
+    return (np.arange(n) % 2).astype(float)
+
+
+def hamiltonian(diagonal: np.ndarray) -> np.ndarray:
+    n = len(diagonal)
+    matrix = np.diag(diagonal.astype(float))
+    off = np.ones(n - 1, dtype=float)
+    matrix += np.diag(off, 1) + np.diag(off, -1)
+    return matrix
+
+
+def central_slice(n: int, central_fraction: float) -> slice:
+    keep = max(8, int(round(n * central_fraction)))
+    keep = min(n, keep)
+    start = (n - keep) // 2
+    return slice(start, start + keep)
+
+
+def spacing_r(levels: np.ndarray, central_fraction: float) -> float | None:
+    levels = np.sort(np.asarray(levels, dtype=float))
+    central = levels[central_slice(len(levels), central_fraction)]
+    gaps = np.diff(central)
+    gaps = gaps[np.isfinite(gaps) & (gaps > 1e-12)]
+    if len(gaps) < 2:
+        return None
+    left = gaps[:-1]
+    right = gaps[1:]
+    return float(np.mean(np.minimum(left, right) / np.maximum(left, right)))
+
+
+def localization_metrics(vectors: np.ndarray, central_fraction: float) -> dict[str, float]:
+    n = vectors.shape[0]
+    subset = vectors[:, central_slice(n, central_fraction)]
+    probs = np.square(np.abs(subset))
+    ipr = np.sum(probs * probs, axis=0)
+    entropy = []
+    for col in range(probs.shape[1]):
+        p = probs[:, col]
+        p = p[p > 1e-15]
+        entropy.append(float(-np.sum(p * np.log(p)) / np.log(n)))
+    return {
+        "mean_ipr": float(np.mean(ipr)),
+        "median_ipr": float(np.median(ipr)),
+        "participation_entropy": float(np.mean(entropy)) if entropy else 0.0,
+    }
+
+
+def spectrum_row(
+    domain: str,
+    seq: np.ndarray,
+    n: int,
+    phase: float,
+    v_value: float,
+    central_fraction: float,
+    trial: int | None = None,
+) -> dict[str, Any]:
+    centered = seq - float(np.mean(seq))
+    levels, vectors = np.linalg.eigh(hamiltonian(v_value * centered))
+    metrics = localization_metrics(vectors, central_fraction)
+    row: dict[str, Any] = {
+        "domain": domain,
+        "N": n,
+        "phase": phase,
+        "V": v_value,
+        "ones": int(np.sum(seq)),
+        "spacing_r": spacing_r(levels, central_fraction),
+        **metrics,
+    }
+    if trial is not None:
+        row["trial"] = trial
+    return row
+
+
+def finite(values: list[float | None]) -> np.ndarray:
+    return np.array([v for v in values if v is not None and np.isfinite(v)], dtype=float)
+
+
+def aggregate(rows: list[dict[str, Any]]) -> dict[str, Any]:
+    out: dict[str, Any] = {"count": len(rows)}
+    for key in ["spacing_r", "mean_ipr", "median_ipr", "participation_entropy"]:
+        arr = finite([row.get(key) for row in rows])
+        if len(arr) == 0:
+            out[key] = {"count": 0}
+        else:
+            out[key] = {
+                "count": int(len(arr)),
+                "median": float(np.median(arr)),
+                "mean": float(np.mean(arr)),
+                "min": float(np.min(arr)),
+                "max": float(np.max(arr)),
+            }
+    return out
+
+
+def median_metric(summary: dict[str, Any], domain: str, v_key: str, metric: str) -> float | None:
+    value = summary.get(v_key, {}).get(domain, {}).get(metric, {})
+    if not isinstance(value, dict):
+        return None
+    median = value.get("median")
+    return float(median) if median is not None else None
+
+
+def between(value: float, left: float, right: float) -> bool:
+    return min(left, right) <= value <= max(left, right)
+
+
+def parse_csv_ints(value: str) -> list[int]:
+    return [int(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def parse_csv_floats(value: str) -> list[float]:
+    return [float(part.strip()) for part in value.split(",") if part.strip()]
+
+
+def run(args: argparse.Namespace) -> dict[str, Any]:
+    rng = np.random.default_rng(args.seed)
+    ns = parse_csv_ints(args.ns)
+    phases = parse_csv_floats(args.phases)
+    v_values = np.arange(args.v_min, args.v_max + args.v_step / 2, args.v_step)
+    domains = {
+        "phi": 1 / PHI,
+        "silver": 1 / SILVER,
+        "bronze": 1 / BRONZE,
+    }
+
+    rows: list[dict[str, Any]] = []
+    for n in ns:
+        for phase in phases:
+            phi_seq = sturmian_sequence(1 / PHI, n, phase)
+            ones = int(np.sum(phi_seq))
+            for v_value in v_values:
+                for domain, theta in domains.items():
+                    seq = sturmian_sequence(theta, n, phase)
+                    rows.append(spectrum_row(domain, seq, n, phase, float(v_value), args.central_fraction))
+
+                periodic = periodic_sequence(n)
+                rows.append(spectrum_row("periodic_ab", periodic, n, phase, float(v_value), args.central_fraction))
+
+                for trial in range(args.random_trials):
+                    seq = np.array([1.0] * ones + [0.0] * (n - ones), dtype=float)
+                    rng.shuffle(seq)
+                    rows.append(
+                        spectrum_row(
+                            "balanced_random_phi_density",
+                            seq,
+                            n,
+                            phase,
+                            float(v_value),
+                            args.central_fraction,
+                            trial=trial,
+                        )
+                    )
+
+    summary_by_v: dict[str, dict[str, Any]] = {}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        summary_by_v[v_key] = {}
+        for domain in sorted({row["domain"] for row in rows}):
+            subset = [row for row in rows if row["domain"] == domain and abs(row["V"] - v_value) < 1e-12]
+            summary_by_v[v_key][domain] = aggregate(subset)
+
+    classification: dict[str, Any] = {"joint_boundary_v": [], "by_v": {}}
+    for v_value in v_values:
+        v_key = f"V={v_value:.6f}"
+        needed = {
+            "phi_r": median_metric(summary_by_v, "phi", v_key, "spacing_r"),
+            "periodic_r": median_metric(summary_by_v, "periodic_ab", v_key, "spacing_r"),
+            "random_r": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "spacing_r"),
+            "phi_ipr": median_metric(summary_by_v, "phi", v_key, "mean_ipr"),
+            "periodic_ipr": median_metric(summary_by_v, "periodic_ab", v_key, "mean_ipr"),
+            "random_ipr": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "mean_ipr"),
+            "phi_entropy": median_metric(summary_by_v, "phi", v_key, "participation_entropy"),
+            "periodic_entropy": median_metric(summary_by_v, "periodic_ab", v_key, "participation_entropy"),
+            "random_entropy": median_metric(summary_by_v, "balanced_random_phi_density", v_key, "participation_entropy"),
+        }
+        complete = all(value is not None for value in needed.values())
+        r_between = bool(complete and between(needed["phi_r"], needed["periodic_r"], needed["random_r"]))
+        ipr_between = bool(complete and between(needed["phi_ipr"], needed["periodic_ipr"], needed["random_ipr"]))
+        entropy_between = bool(complete and between(needed["phi_entropy"], needed["periodic_entropy"], needed["random_entropy"]))
+        separated_random = bool(
+            complete
+            and abs(needed["phi_r"] - needed["random_r"]) >= args.min_r_delta
+            and abs(needed["phi_ipr"] - needed["random_ipr"]) >= args.min_ipr_delta
+        )
+        joint = bool(r_between and ipr_between and separated_random)
+        classification["by_v"][v_key] = {
+            **needed,
+            "spacing_r_between": r_between,
+            "mean_ipr_between": ipr_between,
+            "participation_entropy_between": entropy_between,
+            "separated_from_random": separated_random,
+            "joint_boundary": joint,
+        }
+        if joint:
+            classification["joint_boundary_v"].append(float(v_value))
+
+    return {
+        "experiment": "aubry_boundary_phase_transport_gate",
+        "parameters": {
+            "ns": ns,
+            "phases": phases,
+            "v_min": args.v_min,
+            "v_max": args.v_max,
+            "v_step": args.v_step,
+            "central_fraction": args.central_fraction,
+            "random_trials": args.random_trials,
+            "seed": args.seed,
+            "min_r_delta": args.min_r_delta,
+            "min_ipr_delta": args.min_ipr_delta,
+        },
+        "observable_contract": {
+            "claim": "phi is a physical boundary state between periodic order and balanced random disorder only if spectral spacing and localization agree",
+            "observable": "spacing_r plus mean_ipr / participation_entropy on tight-binding spectra",
+            "operator": "binary quasiperiodic tight-binding Hamiltonian with phase row denominator",
+            "denominator": "N x phase x V x generator rows with balanced random controls",
+            "non_possible": "single-observable boundary, phase-aggregated critical value, or phi outside the periodic-random interval",
+        },
+        "classification": classification,
+        "summary_by_v": summary_by_v,
+        "rows": rows,
+    }
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--ns", default="89,144,233")
+    parser.add_argument("--phases", default="0,0.25,0.5,0.75")
+    parser.add_argument("--v-min", type=float, default=0.5)
+    parser.add_argument("--v-max", type=float, default=2.5)
+    parser.add_argument("--v-step", type=float, default=0.25)
+    parser.add_argument("--central-fraction", type=float, default=0.6)
+    parser.add_argument("--random-trials", type=int, default=6)
+    parser.add_argument("--seed", type=int, default=202605151745)
+    parser.add_argument("--min-r-delta", type=float, default=0.025)
+    parser.add_argument("--min-ipr-delta", type=float, default=0.0025)
+    parser.add_argument("--out", default="tools/data/aubry_boundary_phase_transport_gate_20260515_1745.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), encoding="utf-8")
+
+    compact = {
+        "classification": output["classification"],
+        "out": str(out),
+    }
+    print(json.dumps(compact, indent=2))
+
+
+if __name__ == "__main__":
+    main()

tokens used
63,250
Eseguito il ciclo e scritto il report in:

[agent_20260515_1745.md](/opt/MM_D-ND/tools/data/reports/agent_20260515_1745.md)

Ho aggiunto anche lo strumento riusabile:

[exp_aubry_boundary_phase_transport_gate.py](/opt/MM_D-ND/tools/exp_aubry_boundary_phase_transport_gate.py)

Output dati:

[aubry_boundary_phase_transport_gate_20260515_1745.json](/opt/MM_D-ND/tools/data/aubry_boundary_phase_transport_gate_20260515_1745.json)

Risultato: `phi` chiude il bicono spacing/localizzazione solo nella finestra `V=0.50..1.25`; sopra `V=1.50` il confine si scinde, quindi il report resta `CONSTRAINT`, non promozione.

Preflight eseguito: `ARTIFACT_USEFUL_NOT_PUBLISHABLE`, azione consigliata `KEEP_ARTIFACT_STOP_REPORT_PROMOTION`.
