Skip to content

Il land riallineava dopo il verdetto, e le bozze restavano aperte per sempre - #86

Merged
zorahrel merged 11 commits into
mainfrom
topics/land-non-riletto-e-bozze-mai-chiuse
Sep 17, 2026
Merged

zorahrel merged 11 commits into
mainfrom
topics/land-non-riletto-e-bozze-mai-chiuse

Conversation

@zorahrel

Copy link
Copy Markdown
Contributor

Change approvata: openspec/changes/coda-che-riparte-da-sola (T4.1, T4.2).

Cosa cambia

  • Un land che riallinea lo dice, e azzera checks_commit. La consegna misura C, i comandi e la CI danno verde su C, la card sta in review in media 1,93 ore, main avanza (32 land in 7 giorni), e al land si fonde merge(C, main) — che nessuno ha misurato. Osservato sul primo land passato dalle righe CI: il land ha creato 596e828dd e poi 20a271a11, mentre checks_commit restava 71e96ec13f. Non e' nato col PATCH, ma prima la card diceva «i comandi sono usciti zero» e adesso dice «la CI della PR e' verde», che e' un'affermazione piu' forte su un commit che non atterra. 22 land su 32 negli ultimi 7 giorni avevano la riga di riallineamento, e solo 4 producevano l'avviso: 18 passavano muti.
  • La bozza e il ramo remoto si chiudono, ma solo dove chiuderli e' giusto. Nel repo le uniche chiamate a gh erano quattro, tutte per aprire e leggere: zero pr close, zero push --delete. Oggi 41 rami topics/* vivono su origin, 39 gia' dentro main, nessuno cancellato.

La cosa che ha richiesto tre giri

Il primo tentativo chiudeva la PR guardando main locale, e niente nel server pusha main: la spazzata precedeva di secondi il push che marca la PR come fusa, quindi le circa 32 card che atterrano ogni settimana avrebbero letto «Closed» invece di «Merged». Il secondo metteva un flag closePr ma cancellava il ramo comunque — e GitHub chiude una pull request da se' quando le si cancella il ramo di testa: misurato su questo repo, le PR #27 e #28 risultano closed e head_ref_deleted allo stesso secondo. Adesso finche' origin/main non porta il commit non si tocca ne' la PR ne' il ramo, e il test gira su un origin bare vero, non su uno stub.

La pulizia e' anche uscita dalla coda seriale dei land: una card con 11 rami faceva 11 volte (gh pr list + push --delete) con cap 60 s ciascuna, e con gh sloggato teneva ferma la coda di tutti gli altri fino a 22 minuti. Provato con la mutazione: rimettendo l'await il test della coda torna rosso.

Residuo dichiarato

Quando resolveLanding pubblica il ramo vivo invece di quello consegnato — il caso «il ramo consegnato non esiste piu'» su una card non-fan-out — quel ramo non e' scritto in nessun posto che la spazzata legge, quindi resta su origin con la bozza aperta. Non e' una regressione (main non chiude niente), ma la rete di sicurezza ha un buco nominabile.

… che non atterra

`tryMerge` fonde C2 = merge(C, main) mentre i check - i quattro comandi locali
e le due righe CI - hanno misurato C, la consegna, in media 1,93 h prima. La CI
di C2 non la legge nessuno: il push, la bozza e il giro di attesa sono avvenuti
una volta sola, su C. `checks_commit` continuava a nominare C come se fosse cio'
che e' atterrato.

Misurato il 17/09 sugli ultimi 7 giorni: 32 land, 22 con la riga di
riallineamento, e solo 4 di quei 22 hanno anche avvisato che il land era diverso
dalla consegna. Diciotto sono passati muti - e da quando le righe CI sono vive
l'affermazione e' piu' forte di prima, perche' la card non dice piu' «i comandi
sono usciti zero» ma «la CI della PR e' verde».

La nota del riallineamento adesso nomina il commit che i check hanno misurato, e
`clearChecksCommit` toglie il puntatore lasciando in piedi stato, ora ed
evidenza comando per comando: il verdetto su C resta vero e resta leggibile,
sparisce solo la pretesa che parli del commit fuso.

NON e' un merge-queue: ri-spingere C2 e rileggerne la CI costa 15-40 minuti per
land ed e' una decisione del proprietario.
…dentro main

`awaitCiEvidence` apre una bozza di PR per ogni consegna che entra in review, e
in tutto il repo le chiamate a `gh` erano quattro, tutte li': `pr list`,
`pr create`, `api .../runs|jobs`, `pr view`. Zero `pr close`, zero
`push --delete`. Il gemello si misurava gia' senza le bozze: 41 rami `topics/*`
su origin, 39 dei quali gia' dentro `main`, nessuno cancellato. Con 121 ingressi
in review in 7 giorni le bozze si accumulano allo stesso ritmo.

`closeCiDraft` chiude la bozza con la STESSA lookup che la apre (`openPullRequest`,
estratta da `draftPullRequest`) e poi cancella il ramo su origin - in
quest'ordine, perche' la PR chiusa tiene i commit raggiungibili via
`refs/pull/N/head`. Un ramo gia' assente e' un esito, non un errore.

Tre porte, quelle in cui il ramo e' finito per sempre: il land CONFERMATO da una
rilettura di main (con `proof === null` il ramo resta, perche' e' il caso in cui
la card chiede a una persona di andare a guardare), l'approvazione marcata
`superseded` - il gesto esplicito che dice «questo ramo non atterrera'» - e
l'archiviazione. NON l'approvazione semplice, che lascia lavoro vero sul ramo,
e NON il rifiuto, che rimanda l'agente sullo stesso ramo a riconsegnare.

La guardia che conta: una card girata in-place registra come ramo di consegna il
ramo del checkout, cioe' `main`. Il ramo di integrazione e quello su cui il
checkout e' fermo sono rifiutati, qualunque cosa dica la card.

Pulizia, non un cancello: un `gh` scollegato finisce nel log e la card riceve la
ricevuta solo di cio' che e' davvero successo, il land non fallisce mai.
… la bozza sbagliata

Due difetti nello stesso giro, misurati sul DB vivo.

IL RAMO CHE ATTERRA NON PERDE LA SUA BOZZA. `confirmLandedOnMain` rilegge il main
LOCALE e niente in questo server pusha main: quando la spazzata gira, `origin/main`
non ha ancora la fusione e una persona la spinge dopo. GitHub marca allora quella
pull request MERGED da sola - la #78, 17/09, un minuto dopo il land. Un `gh pr close`
che corre contro quel push ci stampa sopra «Closed», sulle circa 32 card che
atterrano in una settimana. Da qui `closePr` sull'ingresso di `closeCiDraft`, senza
default perche' la risposta cambia per porta: falso solo per il ramo che la fusione
ha davvero portato su main, vero per ogni ramo che non atterrera' mai.

UNA CARD HA PIU' DI UN RAMO. Ogni consegna che passa dal cancello dei check pusha il
ramo del SUO worktree e apre la SUA bozza (`awaitCiEvidence` prende
`port.branch(cwd)`), mentre la card ricorda solo l'ultimo in `delivery_branch`. Sul
DB vivo: 151 card con due o piu' rami di tentativo distinti, 104 delle quali hanno
anche un `delivery_branch`, e 83 rami appartengono a un tentativo `delivered` che non
e' il ramo di consegna della card. Spazzarne uno solo lasciava indietro proprio quegli
83 - la perdita per cui T4.2 esiste. Ora la spazzata prende l'unione di `res.branch`,
`delivery_branch` e le righe dei tentativi, deduplicata.

IL RAMO SI CANCELLA ANCHE SENZA `gh`. `push --delete` e' git contro origin: un progetto
il cui `origin` non e' GitHub usciva prima da `closeCiDraft` perche' `port.repo` non
sapeva nominare un repo, e perdeva anche la meta' che con `gh` non c'entra. Ora il
fallimento di `repo` salta solo la meta' della pull request.

L'ORDINE chiudi-poi-cancella era dichiarato portante e nessun test lo misurava
(invertito: 45 pass / 0 fail). La ragione vera: GitHub chiude da se' una pull request
quando le si cancella il ramo di testa, quindi cancellare per primo fa arrivare la
chiusura a una PR gia' chiusa, `openPullRequest` filtra `--state open` e non trova
niente, e la `reason` - l'unica riga che su GitHub dice perche' quel ramo e' finito -
non viene mai scritta. Il test modella quel comportamento; invertendo le due meta' va
rosso.

LA RICEVUTA DICE SOLO CIO' CHE E' SUCCESSO: togliendo il suo cancello (40 pass / 0
fail prima) la card con `gh` sloggato riceve «Pulizia su GitHub: .», e adesso c'e' il
test che lo vede.

La guardia su `main` resta, con la giustificazione vera al posto di quella inventata:
`select count(*) from tasks where delivery_branch='main'` da' 0, perche' una card
girata in-place non registra nessun ramo di consegna. E' un pavimento su un argomento
che la funzione non controlla - arriva da tre posti diversi - su una mossa che il Mac
non puo' annullare, non il rimedio a una riga che qualcuno ha visto.
…le il ramo

`closePr: false` esiste perche' la bozza del ramo ATTERRATO la deve marcare
MERGED GitHub, quando una persona spinge main. Ma `closeCiDraft` cancellava il
ramo comunque, fuori dall'`if`, e cancellare il ramo di testa e' gia' un modo di
chiudere la pull request — chiusa come Closed, mai come merged. Su questo repo
#27 e #28, mai fuse, leggono `closed` e `head_ref_deleted` allo STESSO secondo
dentro una raffica di dieci cancellazioni; #78, che e' atterrata davvero, ha
`merged` 33 secondi PRIMA che il ramo sparisse. La porta del land puo' produrre
solo il primo ordine: `confirmLandedOnMain` rilegge il main LOCALE e qui nessuno
spinge main, quindi il `push --delete` precede sempre la spinta umana. Il flag
non salvava la bozza: spostava chi la chiudeva, e per strada perdeva il commento
con la ragione.

Adesso «non chiudo la pull request» vuol dire anche «non cancello il ramo finche'
origin/main non lo porta»: due `ls-remote` e un `merge-base --is-ancestor`, e una
lettura che non conclude tiene il ramo — cancellare su origin e' l'unica mossa
qui che il Mac non puo' disfare. Il ramo non resta orfano: la porta dell'archivio
rispazza la stessa card con `closePr: true`, e a quel punto la bozza e' gia'
MERGED e `--state open` non trova niente da chiudere.

La prova non e' su uno stub: un origin bare vero, il ramo spinto, la fusione sul
main locale, e il ramo che sopravvive; poi la spinta di main, e il ramo che se ne
va. `gh` non entra mai.

Seconda cosa, la coda. La spazzata era `await` dentro `landTask`, che gira in
`landings.enqueue`, un land alla volta per progetto. La card peggiore del DB vivo
porta 11 rami, ognuno `gh pr list` + `git push --delete` con cap 60 s: con `gh`
sloggato o sotto rate limit un land teneva ferma la coda di tutti gli altri per
~22 minuti invece di ~3, e 124 card archiviate hanno rami di tentativo che non
hanno mai consegnato, cioe' chiamate che non comprano niente. Niente a valle
legge l'esito della spazzata, e le altre due porte la chiamavano gia' cosi'.
…, sette file

`check:bloat` diceva NEW offender su `server/routes/tasks.landing.test.ts`, 1.076
righe contro una soglia di 800, e cadeva con lui il meta-test che pretende che la
baseline del repo sia verde oggi. La soglia non si e' alzata: il file conteneva
sette domande diverse, e ognuna adesso ha il suo file con la sua intestazione.

  tasks.landing.test.ts             quale GESTO atterra, e l'interruttore della board
  tasks.landing-failures.test.ts    ogni modo in cui tryMerge dice di no
  tasks.landing-success.test.ts     cosa chiude il land riuscito, e chi ferma
  tasks.landing-realign.test.ts     il riallineamento si dichiara (e i check misurati)
  tasks.landing-verdict.test.ts     landed/unlanded/unverifiable/ask
  tasks.landing-superseded.test.ts  chiusa apposta senza landare
  tasks.delivery-sweep.test.ts      la meta' REMOTA della potatura: bozza e ramo su origin

45 titoli prima, 45 dopo: `bun test` sui sette file da 45 pass / 0 fail, e il
confronto dei titoli ordinati fra la versione di HEAD e i sette file esce vuoto.
Il piu' lungo adesso e' 269 righe.

Tre nomi italiani (`cardConsegnata`, `mergiato`, `riallineato`) hanno seguito i
test nei file nuovi: invece di traslocare il debito, pagati in inglese e tolti
dalla baseline di `check:identifier-language`, che chiedeva esattamente questo.
# Conflicts:
#	server/routes/tasks.ts
#	server/services/tasks.ts
…vi: 180 righe riscritte in inglese

Spezzare `tasks.landing.test.ts` in sette file era corretto (45 titoli prima, 45
dopo), ma la baseline di `check:comment-language` era agganciata al VECCHIO
percorso - 154 righe alla riga 1268 di `scripts/comment-language-baseline.json` -
e i file nuovi nascono a zero. Risultato: 180 righe di commento italiano contate
come NUOVE, `check:comment-language` a 1 sul ramo e 0 su main, e la CI rossa sul
passo «Static guard rails» (PR #86, run 35228750537).

La baseline non si travasa: il cancello scende soltanto, e alzarla e' proprio la
mossa che questa change vieta. Quindi i commenti sono riscritti in inglese,
tenendo la misura e la trappola che ognuno inchioda: i 108 replay del verdetto
dedotto (20 falsi allarmi), i $5,64 e $8,24 del turno rifatto, le 1,93 h medie in
review con 18 land su 32 che non dicevano di aver riallineato, le tre card del
18/08 chiuse apposta e contate come debito, `git status` che tace solo su
metadati rotti e non su `index.lock`.

  tasks.landing-verdict.test.ts      56 righe
  tasks.landing-failures.test.ts     41
  tasks.landing-success.test.ts      37
  tasks.landing-superseded.test.ts   22
  tasks.landing-realign.test.ts      20
  tasks.delivery-sweep.test.ts        4

Tre blocchi tolti invece che tradotti, perche' ripetevano parola per parola
l'intestazione del file che li conteneva: la storia dell'11/08 in
`landing-success` (ridotta al meccanismo, «promuoveva a done solo passando da
review», che l'intestazione non diceva), la ricaduta del 13/08 in
`landing-verdict` (ridotta al falsificatore: rimetti il verdetto dedotto e il
test e' rosso) e il paragrafo delle 1,93 h in `landing-realign`, che era gia'
scritto due volte nello stesso file. Due citazioni italiane restano perche' sono
il DATO - la riga di storico «user -> In corso» e la nota «Land NON riuscito» -
e portano l'unica scappatoia prevista dal cancello, `allow-italian:`.

Nomi dei test e asserzioni non toccati.
@zorahrel
zorahrel merged commit 936fe96 into main Sep 17, 2026
9 checks passed
zorahrel added a commit that referenced this pull request Sep 17, 2026
Conflitto reale fra le due ultime tornate: #86 ha aggiunto tredici test per
closeCiDraft dentro ci-evidence.test.ts, che questo ramo aveva spezzato in tre.
Risolto tenendo i test di main nel file rimasto, unendo i blocchi di import (lo
split ne aveva tolti otto che quei test usano) e completando le due porte finte
con i quattro metodi che la pulizia delle bozze ha aggiunto a GithubPort.

Tolto il test "no commit beyond main is green with a note": la risoluzione lo
aveva riportato indietro, ma questo ramo lo ha sostituito di proposito, perche'
ownCommits === 0 non e' piu' un verde. Il sostituto sta in
ci-evidence-verdict.test.ts, stesso input e quattro asserzioni in piu'.

Due voci di baseline registrate invece che spezzate: ci-evidence.ts a 948 (680
su main, 765 qui) e routes/tasks.ts a 4.952 (+249). Nessuno dei due rami le
sforava da solo. La ragione e' scritta dentro il file, e il taglio naturale --
il ciclo di vita della pull request contro la lettura della prova -- va fatto a
freddo, non su codice appena uscito da quattro giri di verifica.

52 + 17 + 21 test verdi sui tre file, typecheck 0 su baseline 0, rail verdi.
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