Skip to content

board: topics/clumsy-wren (card 9c85dbe5) - #79

Draft
zorahrel wants to merge 26 commits into
mainfrom
topics/clumsy-wren
Draft

zorahrel wants to merge 26 commits into
mainfrom
topics/clumsy-wren

Conversation

@zorahrel

Copy link
Copy Markdown
Contributor

Draft opened by the Topics board for commit 127782c of card 9c85dbe5, to read the pull request CI (e2e jobs, unit tests). Not a request to merge: a person marks it ready when the card lands.

La change openspec direct-endpoints, poi il nucleo che descrive.

- shared/direct-endpoints.ts: la forma di un endpoint e le regole che dicono
  se e' usabile, pure, cosi' il form in Impostazioni rifiuta prima del giro.
- server/services/direct-endpoint-store.ts: <STATE_DIR>/direct-endpoints.json
  scritto tmp+rename. Il bearer NON sta li' dentro: providers.json viene
  riscritto con le sole chiavi openai/claude e lo cancellerebbe al primo
  salvataggio, quindi ogni token ha il suo file 0600.
- server/lib/private-endpoint-url.ts: la guardia, che non e' isSafePublicUrl
  al contrario. Ammette loopback, RFC1918, 100.64/10 e fc00::/7; rifiuta
  169.254/16, fe80::/10, 0/8 e multicast; DNS risolto a ogni richiesta e
  redirect ricontrollato a ogni hop.
- server/providers/openai-wire.ts: consumeSSE e il body estratti da openai.ts,
  che resta un'istanza fra le altre e tiene verdi i suoi 8 test.
- server/providers/openai-compatible.ts: il provider parametrico, name
  direct-<slug>, finestra di contesto letta dalla config e da meta.n_ctx.

73 test verdi, typecheck server pulito.
server/routes/direct-endpoints.ts, modulo suo perche' providers.ts e' gia'
oltre la riga che il ratchet congela.

Due regole che stanno nelle route e non nello store. Il token non torna MAI
indietro: il client riceve hasToken, non il segreto che ha appena mandato,
altrimenti la pagina delle impostazioni si ridisegna col bearer dentro e
finisce in ogni screenshot di quella pagina. E un endpoint viene SONDATO
prima di essere salvato, cosi' un indirizzo sbagliato e' una frase nel form
invece di un provider che fallisce al primo messaggio.

ProviderSnapshotEntry.modelContextWindows: le finestre le dichiara il
provider, come fastMode e languages. Senza, un 200k locale si presenta col
badge di una finestra da 1M, cioe' un numero inventato davanti a una persona.
L'etichetta di un endpoint e' quella che ha scritto chi lo ha aggiunto:
labelFor saprebbe solo rigirare direct-<slug>.

10 test di route verdi, typecheck server pulito.
contextWindowFor prende una finestra dichiarata e la fa vincere sulla
tabella. La tabella e' un elenco scritto a mano di modelli ospitati: di un
llama locale non ha mai sentito parlare, ripiega sul default, e un modello a
200k finisce col badge di una finestra da 1M. Non e' un badge impreciso, e'
un numero che nessuno ha misurato messo davanti a una persona.

Letta nei due posti che il badge lo mostrano davvero, ProviderModelPicker e
le righe del menu, da ProviderSnapshotEntry.modelContextWindows.
TaskExecutionOption ha lo stesso campo e li' resta sempre vuoto: i runtime di
coding servono modelli che la tabella conosce, e il menu e' uno solo.

Un numero dichiarato assurdo (0, negativo, NaN, Infinity) viene ignorato
invece che stampato.

E il confine di MP-TASK-01 ora e' un test invece di una proprieta' che vale
per caso: un endpoint configurato non dichiara coding-tasks, quindi non
compare fra i runtime dei task, e nemmeno i suoi modelli, neanche quando uno
si chiama claude-sonnet-4. Il nome non e' un permesso.

8 test nuovi verdi, typecheck completo pulito.
DirectEndpointsPanel in un file suo: AIProvidersSection e' a 644 righe e
sale verso la soglia che il ratchet congela.

Il form PROVA prima di salvare, e anche il salvataggio rifa' la stessa
sonda. Una porta sbagliata cosi' e' una frase sotto il campo, non un
provider che si rompe al primo messaggio di una chat, quando la persona ha
gia' scritto qualcosa e si aspetta una risposta.

Il token e' a senso unico: esce dal campo, non torna mai indietro. Un
endpoint riaperto mostra un campo vuoto con scritto accanto che il token
c'e' gia', perche' il server risponde hasToken e non il segreto.

Una lista che fallisce e' una lista vuota: il pannello e' additivo, e un
banner d'errore qui si siederebbe sopra i provider che funzionano.

Typecheck completo verde, check:ui-language verde.
…ort morto in meno

I commenti che avevo scritto in italiano su types.ts e snapshot-manager.ts
riscritti in inglese: il ratchet non chiede di tradurre il vecchio, chiede di
non aggiungerne, ed e' giusto cosi'.

ipv, mapped e slugify in PROJECT_WORDS, ognuna con la riga che dice perche':
ipv e' come lo scrivono gli RFC e net.isIPv4, mapped e' l'aggettivo dell'RFC
per un IPv4 dentro un IPv6, slugify e' il nome dell'operazione.

configureDirectEndpointRoot via: lo store risolve da se' la state dir e i
test passano una root, quindi quel setter non lo chiamava nessuno.

check:deadcode verde, check:identifier-language verde, e i miei file sono
puliti anche per check:comment-language. Quel cancello pero' e' gia' rosso
sul commit da cui parto, su file che non ho toccato.
Aggiungere, provare, salvare e togliere un endpoint con ogni richiesta
intercettata: nessun endpoint vero, nessun token, nessuna generazione.

Il test che conta davvero e' l'ultimo. Un endpoint configurato compare nel
picker della chat e NON fra i runtime dei task: e' una regola che nella UI
non si vede: niente riga barrata, niente spiegazione, semplicemente non c'e'.
Per questo serve un test che si rompe quando qualcuno la toglie.

E il token: esce una volta e nella pagina non rientra. Il test lo cerca in
page.content(), non nel campo, perche' il punto e' che non sia da NESSUNA
parte nel DOM.

typecheck:e2e verde.
Con lo snapshot vuoto l'app apre la procedura di installazione: niente
barra laterale, e l'helper che apre le Impostazioni aspettava per sempre
un elemento che quella schermata non ha. Ogni test parte da un provider
predefinito pronto.

La meta' sul picker dei task guardava un test id che non esiste, dentro
un if che lo rendeva un no-op: un test verde che non misura niente. Il
confine MP-TASK-01 e' una funzione pura ed e' asserito dove puo' davvero
rompersi, in shared/direct-endpoints-boundaries.test.ts. Qui resta la
meta' visibile: l'endpoint e' offerto per una chat.
MP-DIRECT-04 e MP-DIRECT-05 erano dichiarati da due test ma non
esistevano nelle spec: il cancello di copertura ha ragione, il test li
prova gia'. Il primo dice il confine col picker dei task, il secondo che
il nome del provider non porta due punti (un modello di task e' salvato
come provider:model e si spezzerebbe nel posto sbagliato) e che una
finestra dichiarata assurda si ignora.
Le mie 16 righe avevano spinto server/providers/types.ts a 809, sopra le
800 che il cancello congela. Il tipo di configurazione di un endpoint
vive in un file suo: lo leggono due moduli, non gli serve stare nel
catalogo condiviso per farsi trovare. types.ts torna a 798.

La baseline si riallinea anche su file cresciuti su main (server.ts,
task-dispatcher, dispatch-capacity e il suo test, che arrivano dal merge
b390da4 e non da questo ramo). Mie sono le righe di api.ts, dei due
i18n e di shared/types.ts.
Uno a nove: fatti tutti tranne la prova live, che ha bisogno di una
macchina che non e' questa.
Catturavo globalThis.fetch al caricamento del modulo. Nello shard 0 quel momento
cade quando un altro file ha gia' messo il suo stub: l'afterEach
rimetteva quello, e da li' in poi lo vedeva chiunque. Tre test di
projectLayoutSync.durability, che usano fetch keepalive, cadevano per
questo e passavano da soli.

Ora la cattura e' in un beforeEach: ogni test rimette quello che ha
trovato lui. I tre file insieme: 28 pass, 0 fail.
L'ultimo test apriva l'app e cercava provider-model-picker sulla schermata
vuota, dove non c'e'. Ora semina un topic, lo apre e poi guarda il picker,
come fa provider-picker.spec.ts, con il reset del pane-store che tiene il
locator fuori dallo strict mode.
La pagina idrata lo snapshot dei provider anche dal WebSocket, non solo dalla HTTP che lo
stub intercettava: la riga dell'endpoint compariva o no a seconda di chi arrivava per ultimo,
e il test passava o falliva secondo l'ordine. Un test che misura l'ordine di due sorgenti non
misura la regola.

La regola resta provata dove puo' davvero rompersi: shared/direct-endpoints-boundaries.test.ts
la verifica come funzione pura, ed e' li' che MP-DIRECT-04 e' coperto. Restano i tre test che
lo stub HTTP governa per intero: provare, salvare, elencare, togliere, l'errore di un endpoint
muto, e il token che esce una volta e non torna mai nella pagina.
# Conflicts:
#	scripts/bloat-baseline.json
L'assembler chiedeva contextWindowFor(topic?.model) e per un modello fuori
tabella cadeva sul default di 1M: lo stesso circa-1M su un'altra superficie.
Ora la porta server della tabella consulta le finestre che i provider
dichiarano, cosi' la riga 342 non cambia (assemble.ts e' congelato dal ratchet)
e MP-DIRECT-03 vale davvero per picker E assembler.

Il registro sta in un modulo suo perche' il produttore e' lo snapshot manager
e il consumatore e' la porta che l'assembler importa: legarli dritti comprava
un ciclo di import.

E il test del redirect ora asserisce init.redirect === 'manual'. Con
"follow" al posto di "manual" i 24 test restavano verdi (il doFetch finto
rende il 302 comunque) mentre in produzione spariva l'unica riga che impedisce
al runtime di seguire da solo un 302 verso 169.254.169.254. Verificato: con la
mutazione ora cade.
Il lazy l'ho provato per primo, perche' e' la causa solita e il messaggio dello
script la nomina. Non era quella: il pannello sta sotto GlobalSettings, che
App.tsx carica gia' con lazy(), quindi nell'entry eager il componente non c'e'
mai entrato. Con un lazy() anche sul pannello il numero passa da 444.903 a
444.898 — cinque byte, rumore — quindi l'ho tolto invece di lasciare un
Suspense che non compra niente e aggiunge un modo di fallire.

Restano le stringhe i18n, che sono eager per costruzione. Baseline alzata di
363 byte con la misura scritta accanto, cosi' chi legge dopo sa che il lazy e'
gia' stato provato e quanto ha reso.
Il test che mancava. Non page.route: a chiamare l'endpoint e' il SERVER, quindi
uno stub nel browser non intercetterebbe nulla. Il test alza un vero server
OpenAI-compatibile su 127.0.0.1 (porta effimera), lo registra con la stessa API
che usa il form, punta il topic su direct-e2e-local e manda un messaggio.

Prova i tre pezzi che il brief chiede e che finora esistevano solo sulla classe:
lo streaming (i token arrivano nel thread), l'abort (il turno si ferma a meta' e
il finto vede il client chiudere) e l'usage (che viaggia sull'ultimo frame
grazie a stream_options.include_usage).

Le asserzioni deboli le ho tolte: la porta viene da E2E_BASE e non da un 3333
scritto a mano, le etichette del bottone di stop sono quelle vere dei due
dizionari, e lo snapshot si legge in poll perche' il refresh e' in background.
Il cancello degli identificatori aveva ragione, era un'abbreviazione.
Ogni spec e2e deve chiamare hermetic(test) a top-level: la mia no, e il
test che lo controlla era l'unico rosso della CI unit.
…serviva

Il ramo era 94 commit dietro main e i due ratchet (bundle, bloat) andavano
risolti sul lato di main: il 17/09 main ha riallineato bundle-baseline.json
su una build vera (444.684 gz misurati in CI contro i 435.937 del 14/09), e
con quel numero i 363 byte di i18n che questa feature costa stanno sotto il
tetto senza alzare niente. Il rialzo del ramo era corretto quando e' stato
scritto e adesso e' rumore: via.

E la spec e2e non era mai passata. startFakeEndpoint tornava l'origine nuda
(http://127.0.0.1:PORT) mentre il finto serve solo /v1/*: la sonda chiedeva
/models, prendeva 404, la POST tornava 502 e il test moriva sulla prima
riga. Il secondo test ingoiava la stessa POST senza guardarla, quindi lo
stesso difetto gli usciva 30 secondi dopo come token che non arrivano.
Il blocco diceva letteralmente "Filled at delivery" e la consegna e' avvenuta
senza riempirlo. Ci scrivo i numeri veri: i 139 test dei moduli, le quattro
mutazioni con il test che ciascuna uccide, i 18 rail della CI, e il bundle
misurato sulla baseline nuova di main invece che sul rialzo di questo ramo.

E soprattutto ci scrivo cosa NON e' stato misurato: il passo 9, la sonda
contro il llama-server vero sulla LAN, resta aperto. Tutto quanto sopra parla
con un finto su loopback.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant