Repository navigation
Patterns - #638
Open
brennercruvinel wants to merge 48 commits into
Open
Patterns#638brennercruvinel wants to merge 48 commits into
brennercruvinel wants to merge 48 commits into
Conversation
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.
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
…ease bumps every crate from one place
…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
… far apart the word index puts them
…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
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.
Closes #. Part of #.
Change
Validation
Tested commit:
<sha>Final review
Remaining limits