Skip to content

Patterns - #638

Open
brennercruvinel wants to merge 48 commits into
mainfrom
patterns
Open

brennercruvinel wants to merge 48 commits into
mainfrom
patterns

Conversation

@brennercruvinel

Copy link
Copy Markdown
Member

Closes #. Part of #.

Change

Validation

Tested commit: <sha>

  • ()
  • ()

Final review

Remaining limits

ask, retrieve and search-text with the default embedder no longer start a
python interpreter per query: the potion table is read in process with the
same recipe and the same model_hash as python/urna/embed/potiontab.py, and
the scores match the script to the sixth decimal. embed_and_search reads
the model, dim and model_hash through engine accessors instead of
inspect_json, which re-verified every section of the file (1.3 s on a
1.2 GB corpus) on each query. The engine exposes embedding_model,
full_dim, chunker_version and span.

Signed-off-by: Brenner Cruvinel <[email protected]>
The explorer opens the corpus once, on the loader thread, and every query
reuses it: a question on a 1.2 GB corpus goes from 5.1 s to 1-2 ms.
Typing offers the corpus's labels (the first line of each chunk, with a
trailing alias in parentheses counted as a name); tab asks for the one
pointed at, and a label spelled exactly goes first, marked as a name hit
rather than given a score it does not have.

A hit whose source is an inlined AVIF blob shows its image, decoded by
rav1d: pixels where the terminal speaks kitty, sixel or iterm2, quadrant
characters elsewhere. A corpus built with the mtgdataset/1 chunker gets a
card layout of its stored text. `urna tui FILE --ask QUERY` opens on the
ask tab with the question asked, and `--features window` adds ctrl+v, a
native window with the image at full resolution.

Signed-off-by: Brenner Cruvinel <[email protected]>
demo/pasta is a standalone crate, its own workspace, that opens a card
corpus built with the mtgdataset/1 chunker and answers card search in
process: type-ahead over English and Portuguese names (nucleo-matcher,
ranked by EDHREC popularity), hybrid text search with the potion table,
deck filters (format, color identity, cmc, type) and the card's AVIF bytes
sliced off the mmap. The deck fields come from tools/build_fields.py,
which joins the dataset's Parquet with Scryfall's oracle-cards bulk by
oracle_id into a zstd json file bound to the corpus content_hash.

bin/pasta is a line-driven shell over the library, bin/eval measures the
type-ahead on every playable card, and bin/pasta-tui (feature tui) is a
terminal view with the card image. examples/openprobe times the open path.

Signed-off-by: Brenner Cruvinel <[email protected]>
…xels

pasta-tui drew through chafa, whose full symbol set (legacy computing,
braille, wide katakana) misaligns in fonts that lack those glyphs. Where
the terminal speaks no graphics protocol it now draws the same 2x2
quadrant characters the urna ask tab uses, from block elements every
monospace font carries, and the chafa system library is no longer a
dependency. eval prints its usage instead of panicking on missing
arguments.

Signed-off-by: Brenner Cruvinel <[email protected]>
The graphics probe wrote its queries to the terminal and read the reply
from stdin on a thread of its own. When a reply never came, that read
stayed pending and took the first keys typed: right after opening, the
ask tab ignored typing and ctrl+q. The protocol now comes from the
variables terminals set (kitty and ghostty draw with kitty's protocol,
iTerm2 and WezTerm with iTerm2's, everything else with characters;
URNA_GRAPHICS overrides), and the cell size from the window size the
kernel reports. Nothing reads stdin but the event loop. The pasta demo's
terminal view does the same.

Signed-off-by: Brenner Cruvinel <[email protected]>
The app lived under demo/, next to the few-line python examples. It is a
rust crate on top of urna-engine with its own dependencies, so it now
sits where rust/ingest sits: under rust/, excluded from the urna
workspace, with its own lockfile and build, formatted with the repo's
rustfmt and clean under clippy -D warnings.

Signed-off-by: Brenner Cruvinel <[email protected]>
…e ask tab

A corpus with a fields file beside it (`<stem>.fields.json.zst`, or the
path in URNA_FIELDS, bound to the corpus by its content_hash) gets what
pasta-tui had: deck filters after `|` (format=, id=, cmc<=, type=, all)
that narrow the suggestions and the hits, the most played cards under the
filters when nothing is typed, English and Portuguese names in the
type-ahead with the playable card first and one line per name, and the
card's cmc, stats, popularity and legal formats in its view. A corpus
without the file behaves as before. Provisional until the file carries
the fields itself (#208). Part of #625, within #624.
`urna tui FILE --snapshot QUERY` asks the query and prints one 120x40
frame of the ask tab as text, with the image in quadrants. `urna tui
FILE --eval N` types N playable cards' names key by key and searches
each, reporting where the card lands and how long each keystroke took,
the five lines pasta's eval printed. Both need the card fields beside
the corpus and exit 2 without them. Part of #625, within #624.
The card-search demo was a second terminal app with copies of blocks.rs
and potion.rs. Its filters, type-ahead, card view, snapshot and eval
live in the ask tab now; the fields builder moves to rust/clitui/data.
`urna tui --eval 1000` on the card corpus gives the same counts pasta's
eval gave before the move. Part of #625, within #624.
rav1d 1.1.0 (BSD-2-Clause, the Rust port of dav1d) copied into rust/av1
with its license and an UPSTREAM record (version, crate hash, what was
kept): the Rust sources and the include tree only, no assembly and no
build script, so the decoder is the same on every platform urna builds
for. The two crates it pulled are gone: `paste` is expanded by hand in
wedge.rs and `to_method` is a ten-line trait in src/to.rs. The crate's
own lints relax the workspace deny lints that would mean rewriting 54
thousand lines, and keep the unsafe ones. bitdepth_8 by default, what
urna's AVIF corpora use. Part of #606, within #624.
An ISOBMFF reader for what an AVIF still image carries (ftyp, meta with
pitm, iloc, iinf, iref, iprp/ipco/ipma, idat) that hands the primary and
alpha items to the embedded decoder and converts the planes to RGBA in
fixed point, with every box and extent length checked against the bytes
given before it is used, so a truncated or hostile file is a typed error.
Two parity tests over a card corpus named by URNA_CARDS_CORPUS: the AV1
planes sample for sample against the crates.io rav1d, and the RGBA
within two steps of avif-decode, whose conversion is the yuv crate's.
Part of #606, within #624.
The engine gains a label index: labels folded (case and the accents of
Latin-1 and Latin Extended-A dropped) and sorted with front coding, one
list of the labels and one of every word after the first, so a prefix of
a label, of its alias or of one of its words is a binary search and a
walk over one range. The subsequence scan over every label, scored by
word starts, adjacency and how early the letters sit, runs only when the
exact matches do not fill the list. The ask tab keeps its ranking:
playable first, tier, popularity, score, shorter. On the card corpus
`urna tui --eval 1000` gives the same counts as before and a keystroke
goes from 3.23 ms to 0.03 ms at the median under the same load.
Part of #605, within #624.
…es, loader and registry

A crate for the models urna ships, each written once over a `Backend`
trait that declares every operation of the runtime and implements only
what a shipped model uses: today the potion's gather, mean pool and l2
normalize, on a native CPU backend with NEON and AVX2 lanes measured
against a scalar reference. The safetensors reader moves here from the
cli with typed errors and bounds on every claim, the registry maps a
manifest's embedding_model to its graph and tokenizer, and a per-row
int8 quantizer stands ready for a table that measures a gain from it.
Part of #205, within #624.
…nizer.json

BERT's normalizer (controls dropped, CJK spaced, accents stripped through
NFD, lowercase), BERT's pre-tokenizer (whitespace split, punctuation
isolated) and the greedy WordPiece model, with the added tokens matched
on the raw text first, all written here. The Unicode facts come from
tables build.rs generates out of the vendored character database
(Unicode 16.0.0, hash in ucd/UPSTREAM). Parity against the reference
library is a test with that library as a dev-dependency only: a fixed
set that walks every rule, and every card name of a corpus named by
URNA_CARDS_CORPUS. Part of #205, within #624.
…uild

`cmd/potion.rs` tokenizes with urna-infer's WordPiece and reads the table
through its safetensors loader and native backend, keeping the recipe
and the model_hash of the reference embedder byte for byte: against the
python script the worst component difference is 3e-8 and the hash is
identical. `cargo tree -i tokenizers` finds nothing in the workspace; the
reference library lives only in rust/infer/parity, a workspace of its
own like rust/ingest, so neither the build nor the cargo-deny gates see
it. The release binary drops from 11.3 MB to 7.8 MB. Part of #205,
within #624.
`urna doctor` checks what `ask` needs: the potion payload, the table and
one real embed in process through urna-infer, no interpreter. Python is
reported as the corpus-building toolchain: a warning by default, the
typed failures 2 and 3 (and the embedder script run under it) only with
`--build`. `urna setup` keeps the payload and builds the python env
only with `--python` or for a chosen model; `--no-python` stays, hidden.
The in-process path prints no interpreter note. deny.toml bans reqwest,
hyper, ureq and hf-hub, so the offline promise is enforced on every
lock. cli_e2e asks and retrieves with no python on PATH. Part of #205,
within #624.
… writer

The per-chunk columns a corpus carries beyond its text live in the file
now: named typed columns parallel to chunk order (int, real, bool, text,
uuid, enum with an optional numeric table, enum list), each with a
validity bitmap, bitpacked through intpack, decoded with typed errors
and bounded allocations. The manifest gains `schema` (what each column
means, its roles, its search prefix and how it is drawn), `dataset`
(source, version, license, hash of the bulk) and `media` (codec,
quality, lossy), all additive and omitted when unset. `add_sections`
rewrites an existing file with sections added, copying every stored
payload as is, so content_hash and every citation stay the same; a
roundtrip test proves it. Part of #208, within #624.
…efore scoring

Behind the additive `scalars_present` capability the runtime decodes the
columns and their schema. Predicates (equality, range, membership,
contains, not, and) resolve to a bitset of kept chunks; the exact scan
skips the rest, BM25 drops their postings, HNSW widens its beam and
keeps only candidates inside, and a small kept set is scanned exactly,
so the top-k is never short while kept chunks remain. The search syntax
people already know, `t:creature c:r pow>=3 o:"draw a card"`, parses
over the schema's prefixes, never over names in code. Explain carries
how many chunks the filter kept. Part of #208, within #624.
…de it is gone

`urna scalars FILE --rows ROWS --schema SCHEMA --out OUT` writes the
per-chunk columns, the schema, the dataset and the media derivation into
a corpus, additively; without --rows it declares the schema again over
the columns the file has. The ask tab takes the schema's search syntax:
predicates narrow the suggestions and the hits before scoring, and a
line of predicates alone browses the kept chunks by the rank column. The
record view is laid out from the schema (labels, symbols, chips, numbers,
the enum map as one line, the rules text from the chunk), with no field
name in code; a corpus without a schema stays plain text. The cards
schema, dataset and media declarations live in demo/corpora/cards. The
provisional fields reader, its filter, the builder script and the JSON
beside the corpus are removed. Part of #208 and #607, within #624.
…ch against brute force

Every prefix truncation, a wrong row count, hostile column and name
counts, a code outside its dictionary, offsets out of order, bad flags,
trailing bytes and a future version are typed errors; two thousand
random byte flips never panic; the fuzz target gains the decoder as an
arm. The manifest's schema, dataset and media serialize only when set
and round-trip exactly. On a built corpus with an hnsw index and two
columns, the exact kept scan equals brute force over the kept rows, the
ann and hybrid paths under the filter agree and never come back short,
the explain line carries the kept count, and the search syntax resolves
over the manifest schema. Part of #208, within #624.
One opaque handle per corpus and per answer: open, ask in the schema's
search syntax (predicates resolved to the bitset before scoring), read
each hit's chunk, exact cosine, stored text and citation, take a chunk's
stored image bytes for the platform to decode, read the schema as JSON,
close. Every string is NUL-terminated UTF-8, nulls are refused, no panic
crosses the boundary. The potion embedder moves from the cli into
urna-infer so both callers share it; the cli keeps its lookup ladder. A
C program compiled against include/urna.h runs the round trip in the
test, under leaks on macOS. Part of #603, within #624.
…e ask line

Each facet-role field is counted over the chunks the typed predicates
keep, and the top values with their counts sit under the status line,
keyed by the field's search prefix, so the next term is one keystroke
away and never guessed. CC0-1.0 joins the license allowlist with its
reason: a public-domain dedication is not a copyleft, which is what the
rule is about. Part of #603 and #608, within #624.
…ec, scalar_rows and schema in urna.build

The JSON-to-columns path moves from the explorer's verb into the format
crate, so the `urna scalars` verb and the python build share one reader
of rows and field specs. `urna.build` takes `scalar_rows` (one dict per
chunk), `schema` (the field specs), `dataset` and `media`, and the forge
spec declares them as `[scalars] fields` and `[dataset]` (sha256 "auto"
hashes the source bulk at build time); the media derivation is derived
from `[media]`. A python test builds a corpus with every column kind,
reads the schema, dataset and media back from the manifest and proves
the content_hash is the one of a build without columns; the spec parser
refuses a field without a name or a kind. Part of #208, within #624.
…uild

A text input with a cursor, a spinner, the key-help footer, a scrollbar,
an OSC 8 link and the file picker in tui/widgets, and the effect engine
in tui/fx: a tree of atoms (sweep, fade, coalesce, evolve, dissolve,
breathe, glint) composed in parallel, in sequence, delayed, repeated or
ping-ponged, run over the drawn frame by elapsed time with a steady
per-cell noise. The presets keep their names and intent, so no screen
changes. ratatui-explorer, ratatui-cheese, tachyonfx, hyperrat and
tui-scrollbar leave Cargo.toml. Part of #603, within #624.
…e crates leave the build

The pixels of the selected hit go out as kitty's graphics protocol
(raw RGBA, zlib, chunked, placed and scaled into the cells), iTerm2's
inline image (a minimal PNG written here, CRC-32 included) or sixel (a
median-cut palette of 256 colors, six-row bands with run lengths), each
written from its specification in tui/graphics and sent after the frame
at the image's anchor cell; terminals with none keep the quadrant
characters. The blob is decoded by urna-avif. Ctrl+v hands the stored
AVIF to the system viewer (open, xdg-open, start): the SDL2 window, its
`view` verb and the `window` feature are gone. `image`, `avif-decode`,
`ratatui-image` and `sdl2` leave the explorer, and the decoders from
crates.io the parity tests compare against move to rust/avif/parity, a
workspace of its own, so neither they nor `paste` and `to_method` enter
the urna build or its gates. Part of #606, within #624.
…sories, bans and sources

With the tokenizer, the matcher, the image decoder and the terminal
image crates replaced by our own, and the reference decoders and
tokenizer moved to parity workspaces of their own, the only finding left
was a yanked zip 9.0.1; 9.0.4 is the fix. The allowlist gained CC0-1.0
only, with its reason. Closes #608, within #624.
…arning unless --build, the env is opt-in

`urna doctor` without a usable python exits 0 with the python rows as
warnings and `urna doctor --build` turns them into exit 2; `urna setup`
builds the python env only with `--python`, so the uv test asks for it.
Part of #205, within #624.
The schema, the provenance and the enrichment tool of the card corpus
belong to mtg-urna-benchmark, where the rows and the bulks they are built
from live. The demo keeps the empty directory; urna scalars takes any
schema path.

Part of #626
… columns of chunk_scalars, not a file beside the corpus
…mages, six letters like their siblings; the record view breaks text fields at their lines
… the 639-line guard skips the fork and build output, locally and in CI
…tead of ranging, and a negated word or `-o:` is refused instead of losing its sign; `c:rg` on a dictionary of single letters holds every letter
…ows carry code 0 against an empty dictionary, and the check knows which rows are absent
…ink or a hard link too, not only the same path
… potion corpus in process, any other model through its query embedder, no python on the potion path
…s and fails the potion parity instead of skipping it; the headless eval exits 2 when no chunk carries both a label and a rank
…minal its protocol and cell size; a terminal with none gets block glyphs chosen per cell in Oklab from the set its font carries
…ase gate resolves a relative corpus before it changes directory, search-text names the embedder when the model_hash check is skipped, and the type-ahead keeps one hit per label by sorting instead of hashing
…ders again, without ratatui-image: the terminal is asked once (kitty query, XTVERSION, cell size, DA1) and read with poll and a deadline, no thread left on stdin; inside tmux only the sixel tmux draws itself; the cells an image leaves are drawn again
…ed (unix) and in the tests, and gatecheck fetches the potion table, checked against its lfs pointer, before the tests that need it
…itted demo/starter/docs.jsonl instead of reading a local build the checkout does not have
…e prefix is an empty value instead of text, an empty contains is an error outside the parser too, the model_hash notice prints only when the check was really skipped, a late terminal reply is not typed, the image a new corpus or a resize leaves is deleted, and a flat cell draws blank with the sRGB table
…tion in process, presetqry.py for any other model
…, paridade no gate e imagem pelos nossos encoders kitty, iTerm2 e sixel (#628)

Parte de #606, dentro de #603 e da épica #624. Toca também #608: sem a
`ratatui-image`, o `cargo deny check` segue verde sem mudança no
`deny.toml`.

## Resumo

Auditoria do PR #627 e da épica #624: os achados da revisão de código
corrigidos com teste, a pendência da #205 (`search-text` ainda abria
Python) fechada, a paridade do tokenizer e do decoder AV1 dentro do gate
de release, e a imagem da aba `ask` desenhada pelos nossos encoders
Kitty, iTerm2 e Sixel, com o terminal perguntado por uma consulta nossa,
como a #606 pede, e um codec de glifos próprio onde o terminal não
desenha pixels.

Nove commits. A base é a `patterns` (#627); ao mergear o #627 este PR
passa a apontar para a `main`.

## Itens

### 1. Sintaxe Scryfall: termos que eram lidos errado

`rust/engine/src/scalars/query.rs`, `rust/engine/src/scalars/filter.rs`

- `kw:haste` era quebrado em letras (`h`, `a`, `s`...); agora um termo
de lista fica inteiro.
- `r!=common` dava erro de operador; `!=` em `enum` compara, só `>`,
`>=`, `<`, `<=` são intervalo.
- `-o:flying` e `-palavra` perdiam o sinal de negação em silêncio; agora
são recusados com erro claro. `-1/-1` continua texto.
- `c:rg` num dicionário de letras únicas (cores) exige cada letra; uma
letra que o dicionário não tem não casa nada.
- Testes novos para cada caso.

### 2. Coluna `enum` sem valor em nenhuma linha

`rust/format/src/sections/scalars/{mod.rs,decode.rs,from_json.rs}`

- Uma coluna `enum` vazia (dicionário com zero entradas) falhava no
encode, porque a conferência de códigos não sabia quais linhas estavam
ausentes.
- `check_codes` recebe a validade: linha presente exige código dentro do
dicionário, linha ausente exige código 0. O decoder passa a mesma
validade.
- Teste de ida e volta com `[{"loyalty":null},{}]`.

### 3. Rótulo repetido no type-ahead

`rust/engine/src/labels/mod.rs`

- `exact()` só conferia duplicata nos últimos 64 hits; um rótulo com
duas palavras que começam com a consulta podia aparecer duas vezes
quando o índice de palavras as separava.
- Agora um registro por rótulo, ficando a palavra de menor posição.
Teste que falha no código anterior.

### 4. `urna scalars --out` apontando para o próprio corpus

`rust/clitui/src/cmd/scalars.rs`

- A guarda só comparava o caminho textual; um symlink ou hard link para
o mesmo arquivo passava e truncava o corpus que o mmap ainda lia.
- `same_file()` compara `dev`+`ino` no unix, caminho canônico nos
demais.

### 5. #205: `search-text` sem Python

`rust/clitui/src/cmd/{embed_gate.rs,search_text.rs}`,
`rust/clitui/src/cli.rs`, `rust/clitui/tests/embedder_resolution.rs`

- `search-text` sempre chamava `searchtxt.py`, mesmo em corpus potion.
- Agora passa pelo mesmo `embed_and_route` de `ask` e `retrieve`: potion
em processo, qualquer outro modelo pelo embedder de consulta. Mantém
`--skip-model-hash-check` para corpus legado.
- Conferido com `URNA_PYTHON` apontando para um caminho inexistente: a
busca responde.

### 6. Gate de release

`rust/clitui/src/cmd/potion.rs`, `tool/tasks/fullcheck.sh`,
`rust/images/parity/Cargo.lock`, `rust/clitui/src/tui/app/headless.rs`

- Passo novo `parity`: roda os workspaces `rust/neural/parity` e
`rust/images/parity` contra o corpus de cartas apontado por
`URNA_CARDS_CORPUS`; sem a variável o passo é reportado como pulado, não
como passado.
- O teste de paridade do potion falha, em vez de pular em silêncio,
quando `URNA_REQUIRE_PARITY=1` (o fullcheck seta).
- O `Cargo.lock` de `rust/images/parity` estava preso à versão 0.1.0 das
crates; atualizado.
- `urna tui --eval` sai com código 2 quando nenhum chunk carrega rótulo
e rank, em vez de dividir por zero; `--snapshot`/`--eval` exigem coluna
de rótulo no schema.

### 7. Imagem da aba `ask` (#606)


`rust/clitui/src/tui/graphics/{mod.rs,probe.rs,glyphs.rs,kitty.rs,iterm.rs,sixel.rs,png.rs,base64.rs}`,
`rust/clitui/src/tui/app/{blocks.rs,media.rs,mod.rs,draw.rs,headless.rs}`,
`rust/clitui/Cargo.toml`, `Cargo.lock`

- A detecção anterior só lia variáveis de ambiente; dentro de tmux, em
terminais não listados e em qualquer terminal Sixel caía em quadrantes.
- Agora o terminal é perguntado uma vez, numa escrita só: a consulta de
imagem do Kitty, XTVERSION (nome), `CSI 16 t` (célula) e DA1 por último
(Sixel; toda resposta termina nele). A leitura é com `poll` e prazo de 1
s, na própria thread: terminal que não responde custa o prazo e não
deixa leitor no stdin.
- Os encoders Kitty, iTerm2, Sixel, PNG e base64 são os nossos; a
`ratatui-image` (45 crates) e a `image` saem do lock. `libc`, já na
árvore, entra direto para o `poll`.
- Dentro do tmux, só o Sixel que o próprio tmux desenha; Kitty ou iTerm2
por passthrough cairiam no cursor do terminal de fora, não no pane.
- As células que a imagem deixa são redesenhadas (`AlwaysUpdate`) e as
que ela cobre ficam de fora da diff (`Skip`); toast, painel de
instalação ou seletor por cima escondem a imagem naquele frame; resize
reenvia.
- Sem protocolo, entra o codec de glifos (Oklab, 8x24 subpixels), com
`URNA_GLYPHS` para sextantes e octantes.
- A primeira tela limpa com `CSI 2J` direto, não com `Terminal::clear`,
que consulta a posição do cursor e trava em terminal que não responde.

### 8. Correções da revisão do PR

`rust/engine/src/scalars/{query.rs,filter.rs}`,
`rust/engine/src/labels/mod.rs`, `tool/tasks/fullcheck.sh`,
`rust/clitui/src/cmd/{embed_gate.rs,payload.rs}`,
`rust/clitui/src/tui/app/media.rs`

- `c:""` casava todas as linhas; valor vazio agora é erro, e o filtro
também se protege.
- `fullcheck.sh` resolve `URNA_CORPUS` e `URNA_CARDS_CORPUS` relativos
antes do `cd`; antes, a paridade abria o caminho a partir do diretório
de cada crate.
- Painel de imagem com largura ou altura 0 estourava em `fit`.
- `search-text --skip-model-hash-check` escreve no stderr qual embedder
rodou.
- `exact()` deduplica ordenando por `(id, posição)` em vez de HashMap
por tecla.

## O que ficou de fora

- Em terminal sem protocolo de imagem (Terminal.app, Zed, Alacritty, VS
Code no padrão) a carta sai em glifos; pixel dentro do painel só existe
por protocolo. Uma janela nativa por cima do terminal foi prototipada e
descartada; não há nada dela neste PR.
- UI e responsividade da aba `ask`: problemas levantados (barras entre
hits, nomes cortados, ficha sem hierarquia, facetas cortadas), nenhum
corrigido aqui.
- `cleanhost` não rodou localmente por ambiente (VM de 24 GB e montagem
do container); fica para o CI.
- Alterações de working tree que não são deste trabalho ficaram de fora:
a remoção de `data/gitignore/*`, o `.gitignore`, `rustfmt.toml` e
`nixosship.yml`.

## Validação

- `cargo fmt --all --check`, `cargo clippy` (workspace e `-p urna
--no-default-features`): limpos.
- `cargo test --workspace`: 639 passaram, 0 falharam.
- `cargo deny check`: advisories, bans, licenses e sources ok.
- `tool/tasks/fullcheck.sh`: exit 0, com `URNA_CARDS_CORPUS` relativo
apontando para o mtg-urna-benchmark e o gate de regressão no corpus
fixado; `cleanhost` pulado (`URNA_CLEANHOST=0`, VM de 24 GB), fica com o
CI.
- Paridade AV1 e tokenizer no corpus de cartas: idênticos.
- CI: o `doctor_through_a_pipe_has_no_escapes_and_names_setup` falhava
também na `patterns`, porque o checkout traz a tabela do potion como
ponteiro do lfs; o gatecheck agora roda `tool/tasks/getpotion.sh`
(Hugging Face na revisão fixada, conferido contra o oid do ponteiro)
antes dos testes.
- Binário real num pty respondendo como kitty, foot, iTerm2,
Terminal.app e um terminal mudo: escolhe kitty, sixel, iterm2, blocos e
blocos, cada um emitindo os bytes do seu protocolo; no mudo, Ctrl+C
encerra em 0,5 s.

## Primeiro ponto visível

`[POCS]` `urna tui <corpus>`, aba `ask`, buscar uma carta: a linha de
status mostra `image N ms (kitty|iterm2|sixel|blocks)`, que é o que o
terminal aceitou, e a carta aparece no painel por esse caminho.

This branch has not been deployed

No deployments
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