Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.