| Title: | Interactive Node-Graph Computation with Live Previews |
|---|---|
| Description: | Build and run computation graphs interactively. Nodes are typed R functions with declared ports and parameters; a flow is a plain JSON document; execution is pull-based, content-addressed and incremental, so editing a parameter recomputes only what depends on it. Every node renders its own result in the canvas with a registered renderer, making each step of a pipeline visible rather than implied. Domain knowledge lives entirely in registrable collections, never in the core, so new domains are added without modifying the package. |
| Authors: | Pedro Mambelli Fernandes [aut, cre] (ORCID: <https://orcid.org/0009-0001-1695-279X>), João Paulo Assis Bonifácio [aut] (ORCID: <https://orcid.org/0009-0009-2335-8391>) |
| Maintainer: | Pedro Mambelli Fernandes <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.5.16 |
| Built: | 2026-10-10 22:51:14 UTC |
| Source: | https://github.com/uaipedro/trama |
A lista é essa direção de propósito. Enumerar as ops semânticas (e tratar o
resto como cosmético) faria o custo de esquecer uma op nova ser exatamente
o bug que R/session.R diz ter erradicado: edição aceita, revisão avança,
preview não atualiza, nenhum erro. Enumerando as cosméticas, o custo de
esquecer vira "recomputou à toa" — barulhento e inofensivo.
.tr_presentation_ops.tr_presentation_ops
Inserido pelo motor NA ARESTA (vira marca visual, não nó) e entra na chave
de cache. É o que dispensa nós identidade como o height_as_field do
insumo, mantendo o motor cego a semântica de domínio.
tr_adapter(from, to, fn)tr_adapter(from, to, fn)
from_type -> to_type, ou NULL se não houver (tipos iguais não precisam de adaptador).Busca o adaptador from_type -> to_type, ou NULL se não houver (tipos iguais não precisam de adaptador).
tr_adapter_for(from_type, to_type, registry = .tr_default_registry)tr_adapter_for(from_type, to_type, registry = .tr_default_registry)
... são params (nomeados). from liga a saída de um ou
mais nós existentes às portas de entrada obrigatórias ainda livres deste,
na ordem declarada — o caso comum de "encadeia" sem precisar nomear porta.Acrescenta um nó. ... são params (nomeados). from liga a saída de um ou
mais nós existentes às portas de entrada obrigatórias ainda livres deste,
na ordem declarada — o caso comum de "encadeia" sem precisar nomear porta.
tr_add( flow, id, type, ..., from = NULL, label = NULL, seed = NULL, position = NULL )tr_add( flow, id, type, ..., from = NULL, label = NULL, seed = NULL, position = NULL )
Os assets do front vêm de inst/www via htmltools::htmlDependency(), e
cada coleção carregada publica os próprios em tr-<id>/. A ordem de carga
é determinística e importa: runtime.js cria window.tr (os registros),
as coleções registram renderers e widgets, e só então o editor monta.
tr_app( project = tr_project("."), flow = "main", executor = tr_executor_sequential(), port = getOption("trama.port", tr_port_default()), controle = getOption("trama.controle", TRUE), ... )tr_app( project = tr_project("."), flow = "main", executor = tr_executor_sequential(), port = getOption("trama.port", tr_port_default()), controle = getOption("trama.controle", TRUE), ... )
port |
Porta em que o editor sobe. O padrão é fixo (e não sorteado pelo
Shiny) para que a URL do editor seja sempre a mesma; se estiver ocupada,
|
controle |
Sobe o canal de controle para agentes ( |
Casa se QUALQUER coleção que produziu o artefato for a pedida. Nó comum tem
uma só, derivada do node_type. A região de fluxo tem o conjunto dos membros,
gravado no handle, porque o node_type dela é trama/stream_region e lia
como coleção "trama": tr_bust(store, "minha_colecao") não a alcançava, todo
nó comum recomputava, e o histórico de dez mil pontos continuava vindo do
código de antes do upgrade — calado, pra sempre.
tr_bust(store, collection = NULL)tr_bust(store, collection = NULL)
Catálogo do registro em forma serializável: tipos, categorias, nós e adaptadores — o que o front usa pra desenhar a paleta e validar conexões sem round-trip.
tr_catalog(registry = .tr_default_registry)tr_catalog(registry = .tr_default_registry)
tr_catalog() como JSON.tr_catalog() como JSON.
tr_catalog_json(registry = .tr_default_registry)tr_catalog_json(registry = .tr_default_registry)
A cor deve vir do PAPEL do bloco no fluxo, não da coleção: assim um ajuste de modelo e um ARIMA têm a mesma cor em qualquer coleção, e o tipo do que o bloco produz fica nas portas. Os papéis seguem a ordem em que o analista trabalha, e são genéricos, nada de domínio:
tr_category(id, label, color = "#6366f1", role = NULL)tr_category(id, label, color = "#6366f1", role = NULL)
id |
Identificador da categoria, único no registro. |
label |
Rótulo na paleta. |
color |
Cor fixa, usada só quando |
role |
Papel dos blocos da categoria no fluxo; um dos listados acima. |
origem: traz dados para o fluxo (ler arquivo, dados de exemplo).
preparacao: limpa, transforma ou reorganiza.
inspecao: descreve o que chegou (resumo, gráfico do dado bruto).
ajuste: constrói o objeto de análise (modelo, decomposição, PCA).
leitura: lê um ajuste que já existe (quadro da ANOVA, coeficientes,
médias, previsões, cargas). São os mostradores do modelo, não um passo a
mais da sequência; por isso a cor é uma versão clara da do ajuste.
avaliacao: julga um resultado (teste, pressupostos, métricas).
saida: tira algo do fluxo (gravar arquivo).
O papel da categoria é o padrão dos blocos dela; um bloco que faz outra
coisa declara o próprio em tr_node(). A cor de cada papel é do editor e
muda com o tema. color fica para quem não declara papel: vale nos dois
temas, e a tinta do título é escura.
Fala com o editor aberto (tr_app()) pelo servidor de R/control.R. Toda
saída é JSON em stdout, para agente ler, inclusive help e erro de
argumento; recusa sai com ok: false e, pelo script trama-agente,
status 1.
tr_cli(args = commandArgs(TRUE))tr_cli(args = commandArgs(TRUE))
args |
Vetor de argumentos, como |
trama-agente state # fluxo na tela: nós, status, arestas trama-agente catalog [tipo] [--busca T] # blocos disponíveis / um bloco / os que casam trama-agente add <tipo> [--id X] [--from no[:porta]]... [--label L] [param=valor]... trama-agente link <no[:porta]> <no[:porta]> trama-agente set <no> param=valor... trama-agente rm <no> trama-agente op '<json>' # op crua (ver schema do documento) trama-agente apply <arquivo.json> # lista de ops como um passo de undo trama-agente result <no> [--wait 30] # status, resumo, preview, PNG trama-agente undo trama-agente explain <tipo> # sem editor: o bloco inteiro trama-agente validate <arquivo.json> # sem editor: lê, migra e valida
catalog, explain e validate funcionam sem editor aberto, sobre as
coleções instaladas (ou --colecoes a,b). catalog só faz isso quando
não acha editor ou com --offline.
Toda edição (add, link, set, rm, op, apply, undo) aceita
--wait N: espera até N segundos o fluxo parar de rodar e devolve, em
efeito, o status dos nós tocados e de tudo abaixo deles. Nó blocked
traz causa, os ancestrais que falharam.
valor é lido como JSON quando dá (n=3, x=true, v=["a","b"]) e como
texto quando não (coluna=peso). --projeto DIR escolhe qual editor
quando há mais de um.
A resposta do editor (lista), invisível; imprime o JSON.
Uma coleção é o que um pacote de domínio exporta. js aponta pra um módulo
ES em inst/ que registra renderers e widgets no front — é o que permite
visualização nova sem tocar no núcleo.
tr_collection( id, version = "0.0.0", label = id, types = list(), nodes = list(), adapters = list(), categories = list(), js = NULL, css = NULL, transitions = NULL, migrations = list(), datasets = list() )tr_collection( id, version = "0.0.0", label = id, types = list(), nodes = list(), adapters = list(), categories = list(), js = NULL, css = NULL, transitions = NULL, migrations = list(), datasets = list() )
js |
Caminho, relativo ao |
css |
Caminho, relativo ao |
transitions |
|
migrations |
Renomes que a coleção declara pra documentos antigos abrirem já migrados, sem nó órfão. Lista com até três entradas, todas opcionais:
Aplicadas por |
datasets |
Bases públicas que a coleção oferece no catálogo de bases,
cada uma um |
label é o nome que a coleção mostra na aba da paleta; sem ele, a aba
escreve o id, que serve mas não explica.
Hoje: igualdade, ou existe adaptador registrado. O insumo espalhava
identical(a$type, b$type) por três lugares (validação de aresta,
isValidConnection no front, inserir-sobre-aresta), o que tornava qualquer
evolução de tipagem uma refatoração em três frentes. Sendo função, crescer
depois (subtipos, list<T>) custa mudar aqui.
tr_compatible(from_type, to_type, registry = .tr_default_registry)tr_compatible(from_type, to_type, registry = .tr_default_registry)
O catálogo guarda só METADADOS: o dado mora no pacote R que o publica e só
é baixado quando alguém escolhe a base. Por isso n e variaveis são
declarados à mão, e não lidos: com o pacote ainda não instalado não há de
onde lê-los, e é justamente antes de instalar que o usuário quer saber se a
base serve.
tr_dataset( pacote, nome, titulo, node, params = list(), descricao = NULL, fonte = NULL, temas = character(), n = NULL, variaveis = NULL, licenca = NULL, url = NULL )tr_dataset( pacote, nome, titulo, node, params = list(), descricao = NULL, fonte = NULL, temas = character(), n = NULL, variaveis = NULL, licenca = NULL, url = NULL )
pacote |
Pacote R que publica a base. |
nome |
Nome do objeto no pacote. |
titulo |
Título curto, para a lista. |
node |
Id do bloco que carrega a base. |
params |
Params do bloco (lista nomeada). |
descricao |
Uma ou duas frases: o que é e para que serve. |
fonte |
Livro, artigo ou instituição de origem. |
temas |
Palavras para filtrar (ex.: |
n, variaveis
|
Linhas e colunas (número), declarados. |
licenca |
Licença do dado, como o pacote a declara. |
url |
Página da base ou do pacote. |
O núcleo não sabe carregar dado nenhum. Quem sabe é o bloco node, com os
params dados: escolher a base no modal insere esse bloco no canvas, e a
prévia e o CSV rodam a função dele. Assim uma base de datasets, de
agridat ou de uma API futura passa pelo mesmo caminho, e o fluxo salvo
guarda a referência, não o dado.
Só para bloco sem entrada: é isso que o modal promete (a base vira a ORIGEM de um fluxo). Um bloco com várias saídas entrega a primeira.
tr_dataset_load(id, registry = .tr_default_registry)tr_dataset_load(id, registry = .tr_default_registry)
A mesma base declarada por duas coleções aparece uma vez (a primeira).
instalado é lido agora, e não no registro: o usuário instala pacote com o
editor aberto, e o catálogo reaberto tem que mostrar isso.
tr_datasets(registry = .tr_default_registry)tr_datasets(registry = .tr_default_registry)
trama.css do núcleo (a ordem vem de tr_ui()).Devolve SEMPRE uma lista de dependências, possivelmente vazia. Uma
htmlDependency serve UM diretório, então js e css em pastas diferentes
precisam de duas — e com nomes distintos, porque o htmltools deduplica
dependência por nome e ficaria só com uma delas, sem aviso. No mesmo
diretório (o caso comum, inst/trama/) vira uma só, com os dois.
tr_dependency_collection(registry, id)tr_dependency_collection(registry, id)
Asset cujo diretório não existe no pacote é pulado, cada um por conta
própria — o mesmo silêncio de antes pro js: coleção de globalenv (sem
pacote) ou instalada sem o inst/ não derruba o app.
runtime.js, editor.js).Dependência Shiny com os assets do núcleo (CSS do core e do xyflow, runtime.js, editor.js).
tr_dependency_core(ver = as.character(utils::packageVersion("trama")))tr_dependency_core(ver = as.character(utils::packageVersion("trama")))
Dependência Shiny que injeta o importmap e a captura de erro de boot, antes de qualquer módulo.
tr_dependency_importmap(base)tr_dependency_importmap(base)
Semântica de valor — toda op devolve um documento novo, nunca muta. É o que torna undo/redo um log de ops e o que permite comparar duas revisões.
tr_doc()tr_doc()
Duas camadas no mesmo arquivo, deliberadamente:
semântica (nodes, edges, seed) entra na chave de cache;
apresentação (ui) nunca entra.
Se posição e parâmetro morassem no mesmo lugar, todo caminho de
invalidação precisaria saber ignorar posição — o insumo pagava esse preço
com uma função dedicada a comparar "só o que afeta o cálculo".
tr_submit() — ela é que carrega a proteção de revisão. Chamar isto
direto a partir do servidor reintroduziria a corrida do insumo.Conveniência (console, testes, replay). A superfície do transporte é
tr_submit() — ela é que carrega a proteção de revisão. Chamar isto
direto a partir do servidor reintroduziria a corrida do insumo.
tr_doc_apply(doc, op, registry = .tr_default_registry)tr_doc_apply(doc, op, registry = .tr_default_registry)
Documento como JSON canônico — a forma que trafega pro front e vai pro disco.
tr_doc_json(doc)tr_doc_json(doc)
Um só ponto de entrada, dois mecanismos, sempre nesta ordem:
tr_doc_migrate(doc, registry = .tr_default_registry)tr_doc_migrate(doc, registry = .tr_default_registry)
Migrações de coleção (tr_collection(migrations = )): renomes de
tipo de nó (inclusive entre coleções, em cadeia), de param (com
conversão de valor) e de porta. Dependem só do NOME antigo, não da
versão.
Migrações por versão do nó (tr_node(migracoes = )): funções por
versão de destino, aplicadas da type_version gravada até a da spec.
Nó cujo TIPO foi renomeado no passo 1 não passa pelo passo 2: a
type_version gravada é a do nó antigo (outro tipo, outra numeração), e
aplicá-la ao destino rodaria funções erradas. O renome declarado é quem
leva os params ao formato atual do destino; o nó sai com a type_version
da spec de destino. Nó não renomeado passa pelos dois: o renome de param
da coleção vem antes, e as funções por versão tratam param ausente como
já migrado.
Idempotente. O resultado leva attr(, "migrated") = TRUE quando algo
mudou (o editor regrava no autosave) e attr(, "migracoes") com o
registro das subidas de versão (o editor e tr_project_flow() avisam).
Lê um documento a partir de texto JSON, validando o formato e reconstituindo vetores achatados pelo parse.
tr_doc_parse(txt)tr_doc_parse(txt)
.json.Lê um documento de um arquivo .json.
tr_doc_read(path)tr_doc_read(path)
ids e só as arestas internas.Aresta que sai do grupo é descartada, e não pendurada: no destino ela
apontaria pra um nó que não existe. ids = NULL devolve o documento todo.
tr_doc_subset(doc, ids = NULL)tr_doc_subset(doc, ids = NULL)
Nós sem aresta de saída — os alvos default de execução.
tr_doc_terminals(doc)tr_doc_terminals(doc)
Existe por causa de documento escrito à mão ou por LLM — que é uma modalidade de autoria que o formato JSON convida. Por isso checa as mesmas invariantes que as ops garantem (ciclo, aridade de porta, input obrigatório desconectado): um documento escrito à mão viola qualquer uma delas trivialmente, e sem checar aqui quem descobre é o executor — o "erro longe da causa" que a checagem na edição existe pra evitar.
tr_doc_validate(doc, registry = .tr_default_registry)tr_doc_validate(doc, registry = .tr_default_registry)
Tipo desconhecido NÃO é erro fatal: vira nó órfão, visível na tela, pra que o usuário veja o que falta em vez de receber um documento que não abre.
path como JSON.Grava num temporário da MESMA pasta e renomeia, como .tr_atomic no store:
o autosave roda a cada op, e um crash ou disco cheio no meio do
writeLines truncaria o fluxo do usuário — o único artefato que não se
recompõe. Falhar deixa o arquivo anterior intacto.
tr_doc_write(doc, path)tr_doc_write(doc, path)
Todo erro do núcleo é classificado (rlang::abort(class = "tr_error_*")) e
viaja com mensagem acionável — é o que permite ao front escolher o que
mostrar e a um modelo corrigir o próprio documento sem ler código. Esta
tabela é a referência; um teste garante que toda classe usada no código
está aqui, então ela não envelhece em silêncio.
tr_errors()tr_errors()
data.frame com class e when.
mirai).Cancelar = MATAR o daemon. Cancelamento cooperativo em R (e no C++ de um
terra da vida) não existe; matar é o único contrato honesto. Isso só é
aceitável porque o worker é sem estado: o que ele produziu de útil já está
no store sob a chave certa, e o que não terminou não deixa rastro (escrita
atômica, store.R). Projete o nó para o worker morrer a qualquer momento.
tr_executor_pool(n = 2L, registry = .tr_default_registry, setup = NULL, ...)tr_executor_pool(n = 2L, registry = .tr_default_registry, setup = NULL, ...)
O executor é plugável de propósito. O sequencial é o default em teste e headless — rápido, determinístico, sem processo extra e sem ramificação de código entre "com UI" e "de script". O pool existe pra que o processo interativo nunca bloqueie.
tr_executor_sequential()tr_executor_sequential()
Contrato: $submit(unit, registry, store, ctx_extra = NULL) -> token $collect(token) -> NULL enquanto não terminou, senão list(ok=, handles=|error=) $capacity() -> quantas unidades em voo ao mesmo tempo $cancel(tokens) -> descarta trabalho em andamento $shutdown()
ctx_extra são as CONFIGURAÇÕES DO RUN que o worker precisa e que não estão
na unidade — hoje a cadência de publicação do parcial e a do checkpoint da
região (worker.R, stream-driver.R). Atravessa como dado, o que o contrato
deste arquivo já exige de tudo: é lista de escalares, sem função nem
ambiente, e por isso serializa para o daemon como a unidade serializa.
Não confundir com o control.json de stream-control.R: lá vão COMANDOS
VIVOS (o clique de pausa, o arrasto do controle de velocidade enquanto a
região roda), que mudam durante o run; aqui vão AJUSTES DO RUN, fixos quando
ele começa. Juntá-los num arquivo só faria um comando tempo poder mexer no
checkpoint em silêncio, e poria política de durabilidade no mesmo arquivo de
um gesto de interface.
Executor sequencial: roda no próprio processo, na hora.
cancel() é honesto sobre não fazer nada: cancelamento cooperativo em R
arbitrário não existe, e sem processo separado não há o que matar. O
contrato existe nos dois executores mesmo assim, pra que o protocolo não
precise mudar depois — mudar protocolo com o front pronto custa dobrado.
Diferente de tr_flow_code(), que gera a DSL usada para reconstruir o
documento no editor, esta função gera chamadas diretas às funções R dos nós.
O script resultante não cria registro, documento, plano ou store do Trama;
ele pode ser aberto e executado como qualquer outro script R, desde que os
pacotes das coleções estejam instalados.
tr_export_code( doc, registry = .tr_default_registry, format = c("r", "quarto"), title = "Análise", settings = NULL )tr_export_code( doc, registry = .tr_default_registry, format = c("r", "quarto"), title = "Análise", settings = NULL )
doc |
Documento ou |
registry |
Registro que resolve os nós e adaptadores do documento. |
format |
|
title |
Título usado no documento Quarto. |
settings |
Configurações do projeto, para resolver params de tema como o executor resolve. |
Cada nó vira uma variável com o nome do card (correlograma_acf, e não o
id), e um nó de várias saídas é UMA variável: quem a consome lê
ajuste$out. Os params passados são os efetivos, os mesmos que o executor
passa, omitidos só quando coincidem com o default literal da própria
função: o script calcula o que o canvas calculou, sem repetir o óbvio.
No formato Quarto, cada card é um chunk; os frames viram seções, na ordem
de slide, e as notas viram texto no lugar em que estão. As saídas que
nenhum fio consome são as que o fluxo produziu para ser LIDAS, e o
documento as mostra (pelo report do tipo, quando há; senão, o print).
Fluxos ponto a ponto ainda dependem do executor do Trama e, por isso, são recusados em vez de gerar um script que calcularia outra coisa.
Uma string única pronta para ser gravada em um arquivo .R ou .qmd.
O documento sempre pôde ser construído no console aplicando listas de ops, que é ótimo para máquina e ruim para gente. O manifesto mira "referência de como se constrói fluxo lógico em R": isso exige que o fluxo se ESCREVA em R idiomático, com pipe, e que o mesmo texto produza o mesmo JSON que a interface. Esta camada não tem semântica própria — cada verbo vira uma op e passa pela mesma validação — para que não existam dois caminhos que possam divergir.
tr_flow(registry = .tr_default_registry, doc = tr_doc())tr_flow(registry = .tr_default_registry, doc = tr_doc())
Referência a porta: "no:porta". Os dois pontos são proibidos em id de nó
(.tr_check_node_id), então a separação é inequívoca; "." não serviria,
porque id pode conter ponto.
Ordem topológica; params só quando diferem do default; from = quando a
ligação é a canônica (primeira saída -> primeira entrada obrigatória livre),
senão tr_link() explícito. Posições NÃO entram: são apresentação.
tr_flow_code(doc, registry = .tr_default_registry, registry_expr = "reg")tr_flow_code(doc, registry = .tr_default_registry, registry_expr = "reg")
Param cujo NOME é formal de tr_add() (from, label, seed, position,
e também type, id, flow) não pode sair dentro do tr_add(): o R casa
o nome com o formal, não com o param — é a armadilha que
.tr_check_param_shadow() denuncia do lado de quem escreve. Emitido
ingenuamente, data/rename (param from) saía como
tr_add("ren", "data/rename", from = "mpg", to = "consumo", from = "ler"):
argumento duplicado, código que não roda. Esses params saem num tr_set()
encadeado — o caminho que o próprio guard manda usar — e, quando o param
sombreado é from, as arestas do nó saem por tr_link() explícito, porque
o formal from continua vedado para aquele tr_add().
tr_doc por trás de um tr_flow — a saída pronta pra tr_run(), tr_doc_write() etc.O tr_doc por trás de um tr_flow — a saída pronta pra tr_run(), tr_doc_write() etc.
tr_flow_doc(flow)tr_flow_doc(flow)
O insumo mantinha essa propriedade (com teste de equivalência nível 1 vs. grafo) e ela é o que impede a ferramenta de virar gaiola — qualquer nó é inspecionável no console, sem documento e sem cache.
tr_fn(id, registry = .tr_default_registry)tr_fn(id, registry = .tr_default_registry)
O nó tem que estar na galeria como captura. png é o PNG em base64, cru
ou como data URL (data:image/png;base64,...). Substitui o arquivo anterior
do nó, se o nome mudou, e atualiza o manifesto.
tr_galeria_captura( root, store, registry, doc, settings = NULL, handles = list(), node, key, png )tr_galeria_captura( root, store, registry, doc, settings = NULL, handles = list(), node, key, png )
root |
Raiz do projeto; a pasta da galeria é |
store |
Store do projeto (só a exportação o usa; aqui fica pela forma comum). |
registry |
Registro que resolve os tipos. |
doc |
Documento. |
settings |
Settings do projeto; |
handles |
Lista nó -> handle, como |
node |
Id do nó. |
key |
Chave do resultado que a captura representa. |
png |
PNG em base64 (cru ou data URL). |
O item gravado, com pronto = TRUE.
Ordem: topológica pelas arestas; empate pela posição x do card, depois pelo
id (para não depender da ordem de inserção). Cada item traz node,
rotulo, arquivo, key, tipo ("imagem" ou "captura") e nome
(base do arquivo, sem extensão). Um nó marcado TRUE sem resultado válido
vira captura com key = NULL.
tr_galeria_itens(root, store, registry, doc, settings = NULL, handles = list())tr_galeria_itens(root, store, registry, doc, settings = NULL, handles = list())
root |
Raiz do projeto; a pasta da galeria é |
store |
Store do projeto (só a exportação o usa; aqui fica pela forma comum). |
registry |
Registro que resolve os tipos. |
doc |
Documento. |
settings |
Settings do projeto; |
handles |
Lista nó -> handle, como |
Lista de itens (listas nomeadas).
Exporta os itens-imagem cuja assinatura (key + formato + dpi) mudou ou
que não têm arquivo; remove os arquivos do manifesto que saíram da galeria
ou trocaram de nome; mantém os itens-captura que já têm PNG. Erro de
exportação de um item fica em erro e não derruba os outros.
tr_galeria_sincronizar( root, store, registry, doc, settings = NULL, handles = list() )tr_galeria_sincronizar( root, store, registry, doc, settings = NULL, handles = list() )
root |
Raiz do projeto; a pasta da galeria é |
store |
Store do projeto (só a exportação o usa; aqui fica pela forma comum). |
registry |
Registro que resolve os tipos. |
doc |
Documento. |
settings |
Settings do projeto; |
handles |
Lista nó -> handle, como |
Itens (como em tr_galeria_itens()) com versao (assinatura) e
pronto (o arquivo existe).
tr_node pelo id qualificado (colecao/nome); erro alto se não registrado.Busca um tr_node pelo id qualificado (colecao/nome); erro alto se não registrado.
tr_get_node(id, registry = .tr_default_registry)tr_get_node(id, registry = .tr_default_registry)
tr_type pelo id qualificado; erro alto se não registrado.Busca um tr_type pelo id qualificado; erro alto se não registrado.
tr_get_type(id, registry = .tr_default_registry)tr_get_type(id, registry = .tr_default_registry)
$error.Um handle representa erro (não valor) quando carrega $error.
tr_handle_failed(handle)tr_handle_failed(handle)
O card trama/test é do núcleo e desenhado igual em toda coleção: régua do
p-valor com estrelas quando há p-valor, três pontinhos de nível quando o
teste só tem tabela de valores críticos. A explicação mora aqui, escrita UMA
vez, pelo mesmo motivo do componente: quinze páginas com quinze cópias
divergiriam na primeira mudança do renderer (inst/www/runtime.js,
VereditoTeste; regras em inst/www/teste.js).
tr_help_test_card()tr_help_test_card()
Os cortes vão por extenso ("1 em mil"), e não "0,001": a coleção series
confere que todo decimal escrito na ajuda de um teste existe como literal no
código do bloco, e o corte do CARD não é do bloco.
Uma coleção que emite um tipo de teste registra o id dele apontando para o
renderer do núcleo (registerRenderer("x/test", getRenderer("trama/test")))
e cola esta seção na ajuda de cada bloco de teste.
string markdown, começando por um título de terceiro nível.
Exatamente um dos dois. name é conferido contra os ícones que o sprite
realmente tem — sem isso, tr_icon("filter") (que não existe; o nome é
list-filter) viraria um buraco silencioso no canto do card, descoberto
meses depois. Mesma régua de tr_node() sem description: erro alto, na
declaração.
tr_icon(name = NULL, svg = NULL)tr_icon(name = NULL, svg = NULL)
svg é o MIOLO de um <svg>, não um documento: o elemento e o viewBox
de 24×24 são do front, a coleção entrega a geometria. Não é barreira de
confiança — uma coleção é um pacote R instalado e já roda JS próprio — é só
o que faz o ícone de terceiro herdar cor e tamanho como os outros.
O texto é um template do trama? Nunca falha: JSON inválido é só "não".
tr_is_template(txt)tr_is_template(txt)
from a to. Porta omitida = primeira saída / primeira entrada.Liga from a to. Porta omitida = primeira saída / primeira entrada.
tr_link(flow, from, to, index = NULL)tr_link(flow, from, to, index = NULL)
Expõe os verbos do trama-agente como tools do Model Context Protocol,
por stdio (JSON-RPC 2.0, uma mensagem por linha). Serve qualquer cliente
MCP local: Claude Code, Claude Desktop, Codex, Cursor. Registro de uma
linha, por exemplo:
tr_mcp(projeto = ".", entrada = file("stdin"), saida = stdout())tr_mcp(projeto = ".", entrada = file("stdin"), saida = stdout())
projeto |
Pasta do projeto trama. Sem |
entrada, saida
|
Conexões do protocolo (o padrão é stdio). |
claude mcp add trama -- Rscript -e "trama::tr_mcp()"
Cada tool é um comando do CLI (R/cli.R) montado a partir dos argumentos
da tool: não há segundo caminho de edição. As tools que mexem no fluxo
precisam do editor aberto; se não houver um para o projeto, o servidor
sobe tr_app() num processo R em segundo plano, abre o navegador e
espera a aba conectar. O editor que o servidor subiu é encerrado junto com
ele; o que a pessoa abriu fica.
stdout é do protocolo. Tudo o que um bloco ou pacote imprimir durante uma tool é desviado para stderr, que o cliente trata como log.
Invisível, quando a entrada fecha.
Sem efeito colateral: devolve uma especificação. Quem registra é
tr_collection() + tr_use(). No insumo, wm_register_node() escrevia no
registro E criava o wrapper de nível 1 em globalenv() no ato — o que
impedia o projeto de virar pacote e impedia dois registros isolados
coexistirem (todo teste compartilhava o mesmo estado global).
tr_node( id, fn, version = 1L, label = NULL, description, help = NULL, category = NULL, inputs = list(), outputs = list(), params = list(), pure = TRUE, fingerprint = NULL, volatile = FALSE, stochastic = FALSE, icon = NULL, init = NULL, step = NULL, role = NULL, pressupostos = list(), referencias = list(), migracoes = list(), galeria = FALSE )tr_node( id, fn, version = 1L, label = NULL, description, help = NULL, category = NULL, inputs = list(), outputs = list(), params = list(), pure = TRUE, fingerprint = NULL, volatile = FALSE, stochastic = FALSE, icon = NULL, init = NULL, step = NULL, role = NULL, pressupostos = list(), referencias = list(), migracoes = list(), galeria = FALSE )
fn é uma função R comum, usável direto no console — o nível 1 da API. Os
argumentos permitidos são os nomes de inputs, os de params, e os dois
reservados .seed e .ctx (com ponto, pra nunca colidirem com um param
que por acaso se chame seed).
description é obrigatória e help é opcional: a primeira é a linha que
aparece na paleta e no tooltip do card, a segunda é a página que abre no
painel lateral. Obrigar a primeira é o que impede uma coleção de nascer
muda — e o catálogo é lido por máquina também, então a ausência custa
duas vezes.
pure = FALSE exige fingerprint(params): o que do mundo externo entra na
chave de cache (mtime+tamanho de arquivo, ETag de URL). Sem isso, um nó que
lê um CSV serviria dado velho em silêncio quando o arquivo muda, porque
nada no hash teria mudado. volatile = TRUE nunca cacheia entre execuções.
init/step declaram o nó COM MEMÓRIA: init(<params>) devolve o estado
inicial, e step(state, <inputs/params>) devolve list(state = , out = ) —
sempre essa forma, sem atalho, porque duas formas de retorno obrigariam o
driver, o preview e os testes a tratar as duas.
role é o papel do bloco no fluxo (ver tr_category()). Sem ele, vale o
da categoria; declare só quando o bloco faz outra coisa que os vizinhos de
paleta, como um "Prever" (leitura) dentro de "Modelar" (ajuste).
pressupostos e referencias são listas de tr_pressuposto() e
tr_ref(): campos genéricos (o núcleo não sabe o que é normalidade), que o
catálogo publica para a ajuda do editor e o site mostrarem igual.
migracoes leva flows salvos com params antigos à versão atual: uma função
function(params) params por versão de DESTINO, nomeada pelo número —
list(3 = function(p) { p$confianca <- p$nivel; p$nivel <- NULL; p }).
Ver tr_doc_migrate().
galeria = TRUE diz que o bloco é de visualização: o gráfico dele entra
sozinho na galeria do projeto (com galeria.imagens ligado em
trama.json). Os demais blocos, mesmo com preview de imagem, só entram
marcados pelo usuário.
Percorre o código da função do bloco e das funções internas do mesmo
pacote que ela chama, e devolve as chamadas a funções de OUTROS pacotes
(stats::aov(...), lme4::lmer(...)), com o trecho de código e o arquivo
e a linha em que aparecem. É o que a ajuda e o site mostram em
"Implementação": a chamada que faz a conta, e não o invólucro do Trama.
tr_node_raiox(node, ignorar = .tr_raiox_ignorar)tr_node_raiox(node, ignorar = .tr_raiox_ignorar)
node |
Nó, como devolvido por |
ignorar |
Pacotes cujas chamadas não entram no raio-x. |
Pacotes de infraestrutura (base, rlang, tibble, cli...) ficam de fora por padrão: dizem como a saída é montada, não como a conta é feita.
Lista com funcao ("pacote::nome"), arquivo, linha e
chamadas: uma lista de list(pacote, funcao, codigo, arquivo, linha, dentro), sem repetir o mesmo pacote::funcao no mesmo lugar.
batch é semântico se qualquer op dentro dele for.Uma op é SEMÂNTICA se não for puramente de apresentação — só ops semânticas disparam re-execução. Um batch é semântico se qualquer op dentro dele for.
tr_op_semantic(op)tr_op_semantic(op)
kind decide o widget no front — e o front resolve o widget por um
registro (registerWidget), então uma coleção pode trazer kind novo sem
o núcleo saber o que ele significa. Os kinds embutidos abaixo existem
porque são universais, não porque o núcleo os privilegia.
tr_param(kind, default, label = NULL, ...)tr_param(kind, default, label = NULL, ...)
default é obrigatório: sem ele o documento teria que carregar valor de
todo param de todo nó pra ser interpretável, e um nó recém-criado não teria
estado válido.
Param booleano.
tr_param_bool(default = FALSE, label = NULL)tr_param_bool(default = FALSE, label = NULL)
role diz que tipo de coluna serve; multi = FALSE é uma coluna só (select);
from nomeia a entrada (padrão: a primeira do nó). Continua kind = "cols":
o valor é texto ("a" ou "a, b"), igual aos cols já existentes — quem já
declara tr_param("cols", ...) segue valendo, só sem as anotações que
deixam o front oferecer as colunas do schema da entrada.
tr_param_col( default = "", label = NULL, role = "qualquer", multi = FALSE, from = NULL, example = NULL, suggest = TRUE )tr_param_col( default = "", label = NULL, role = "qualquer", multi = FALSE, from = NULL, example = NULL, suggest = TRUE )
default |
Valor inicial (texto). |
label |
Rótulo do campo. |
role |
|
multi |
|
from |
Nome da entrada cujas colunas servem; |
example |
Exemplo mostrado no campo. |
suggest |
|
role inválido aborta com tr_error_bad_param e não com o erro cru de
match.arg: é erro de declaração da coleção, da mesma família dos outros
que tr_node() levanta.
choices.Param de escolha única entre choices.
tr_param_enum(default, choices, label = NULL)tr_param_enum(default, choices, label = NULL)
botao troca o campo por um botão com esse texto, que soma 1 ao valor a
cada clique: é o gesto "faça de novo" (baixar de novo, sortear de novo)
expresso como param, então entra no hash, no undo e no documento como
qualquer outro, e o bloco decide o que fazer quando o número sobe.
tr_param_int( default, min = NA, max = NA, label = NULL, botao = NULL, vazio = NULL, example = NULL )tr_param_int( default, min = NA, max = NA, label = NULL, botao = NULL, vazio = NULL, example = NULL )
vazio |
Valor que o campo vazio representa, ou |
example |
Texto em cinza no campo vazio. |
vazio é o valor que o campo VAZIO representa (tipicamente o "automático",
como 0 defasagens): com ele igual ao valor, o campo aparece vazio, com
example em cinza dizendo o que o vazio quer dizer, e apagar o número volta
a ele em vez de acusar "obrigatório".
Param numérico (ponto flutuante).
tr_param_num( default, min = NA, max = NA, step = NULL, label = NULL, unit = NA_character_ )tr_param_num( default, min = NA, max = NA, step = NULL, label = NULL, unit = NA_character_ )
Param de texto livre.
tr_param_text(default = "", label = NULL)tr_param_text(default = "", label = NULL)
O valor é "padrão" ou o nome de um tema do projeto; o fn recebe a
DEFINIÇÃO resolvida (ver .tr_theme_resolve), nunca o nome — é a definição
que entra na chave de cache, então editar um tema invalida só quem o usa.
tr_param_theme(default = "padrão", label = "Tema")tr_param_theme(default = "padrão", label = "Tema")
Pull-based, herdado do insumo: nó fora do caminho do alvo não executa. O que muda é que aqui o plano é um VALOR inspecionável — dá pra perguntar "o que vai rodar e o que vem do cache" sem executar nada, comparar dois planos pra ver o que uma edição afetou, e decidir cache hit no coordenador.
tr_plan( doc, targets = NULL, registry = .tr_default_registry, store = NULL, settings = NULL )tr_plan( doc, targets = NULL, registry = .tr_default_registry, store = NULL, settings = NULL )
A unidade não carrega o tr_node vivo. Carregar carregaria junto o
fn e o ambiente dele: numa coleção que mora em globalenv() (console,
testes — o "nível 1 da API" que E1 preserva de propósito), despachar isso
pra um daemon arrastaria o ambiente inteiro por unidade. O worker recebe
node_type e resolve no próprio registro. Efeito colateral bom: o plano
vira serializável, coerente com "o plano é um valor".
settings (de project$settings) resolve os params de tema ANTES do hash.
Quem tem projeto em mãos TEM que passá-lo: plano sem ele calcula as chaves
com os temas embutidos, e o GC apagaria o cache vivo de quem usa tema do
projeto. NULL é a sessão sem projeto.
Unidades que não podem rodar: falharam, dependem de quem falhou, ou têm porta obrigatória solta.
tr_plan_blocked(plan)tr_plan_blocked(plan)
keep do tr_store_gc().Todas as chaves de artefato alcançáveis — o conjunto keep do tr_store_gc().
tr_plan_keys(plan)tr_plan_keys(plan)
O que de fato precisa rodar.
tr_plan_pending(plan)tr_plan_pending(plan)
multiple = TRUE (porta variádica) existe desde o início porque afeta o
schema da aresta (index): acrescentar depois seria mudança de formato de
documento, não de API. "Combinar N entradas" aparece em todo domínio.
tr_port(type, required = TRUE, multiple = FALSE, stream = FALSE)tr_port(type, required = TRUE, multiple = FALSE, stream = FALSE)
stream = TRUE marca porta de fluxo: o dado chega ponto a ponto. Ao
contrário de multiple, não guarda nada por aresta — logo não toca no
schema do documento.
Fora das faixas que costumam dar conflito — abaixo dos portos efêmeros do Linux (32768–60999, sorteados pelo kernel) e longe dos padrões de ferramenta de dev (3000, 3838, 5173, 8000, 8080, 8787).
tr_port_default()tr_port_default()
port.O teste é abrir um socket servidor e fechar: é o mesmo recurso que o Shiny vai pedir, então não há falso negativo por protocolo. Sobra a corrida entre o fechamento aqui e o bind do Shiny — improvável em uso interativo, e o preço de não manter o socket aberto enquanto o app sobe.
tr_port_free(port, tentativas = 20L)tr_port_free(port, tentativas = 20L)
verificar aponta para BLOCOS (ids "colecao/nome"), não para funções R:
o que a ajuda oferece é "arraste este bloco para conferir", e só um id de
nó vira isso no editor. A existência do bloco não é checada aqui porque a
coleção que o verifica pode ser carregada depois (ou nem estar instalada).
tr_pressuposto(texto, verificar = NULL, se_falhar = NULL)tr_pressuposto(texto, verificar = NULL, se_falhar = NULL)
texto |
o pressuposto (texto i18n, ver |
verificar |
|
se_falhar |
o que fazer se ele não valer (texto i18n, opcional). |
renderer é o id do componente JS que sabe desenhar isso. data viaja no
payload (dado já reduzido — downsample, bins, head); files são caminhos
relativos no store, pra quando o dado não cabe (imagem raster).
Regra: arquivo só quando o dado não cabe.
tr_preview(renderer, data = NULL, files = NULL)tr_preview(renderer, data = NULL, files = NULL)
.ctx$progress()/.ctx$partial()/.ctx$partial_node() pra uma chave, ou NULL se a unidade não publicou nada (ou já terminou — o scheduler limpa ao concluir).Lê o progresso publicado por .ctx$progress()/.ctx$partial()/.ctx$partial_node() pra uma chave, ou NULL se a unidade não publicou nada (ou já terminou — o scheduler limpa ao concluir).
tr_progress(store, key)tr_progress(store, key)
Existe porque três coisas no insumo eram caminhos fixos no código — o diretório de blocos, o de blueprints e o que o "usos de bloco" varria. Sem um conceito de projeto, "abri e deu tipo de nó desconhecido" não tem diagnóstico: o manifesto é o que permite dizer QUAL coleção falta.
tr_project(root = ".", collections = character(), create = TRUE)tr_project(root = ".", collections = character(), create = TRUE)
meu-projeto/ trama.json coleções, executor flows/*.json documentos .trama/store/ artefatos (gitignore)
É o que permite trocar de projeto com o editor em pé. tr_project() monta um
registry novo, carregando cada coleção do manifesto — e é isso que não se pode
fazer numa página que já está aberta: o JS e o CSS de cada coleção entram como
htmlDependency quando a UI é montada (tr_ui()), uma vez só. Registry novo
com coleção nova no R e nenhum renderer no navegador seria um card verde sem
desenho nenhum.
tr_project_at(root, registry)tr_project_at(root, registry)
Daí a recusa ser a regra, e não um remendo: se o manifesto pede coleção que a página não tem, isto aborta NOMEANDO-A, antes de criar pasta ou tocar em qualquer estado. É o diagnóstico para o qual o manifesto existe.
A comparação é contra o PACOTE ($package), não contra o id da coleção: o
manifesto guarda trama.data e o registry indexa por data, e os dois só
coincidem por acidente (ver tr_use()).
name do projeto; documento vazio se o arquivo ainda não existe.É AQUI que as migrações das coleções entram (tr_doc_migrate()): este é o
ponto único em que um documento de disco encontra o registro que vai
interpretá-lo — o editor abre por aqui tanto na subida quanto ao trocar de
projeto, e importar grava e reabre por aqui. tr_doc_read() não tem
registro, e tr_doc_validate() é só diagnóstico (não devolve documento).
tr_project_flow(project, name = "main")tr_project_flow(project, name = "main")
tr_store_gc
recebia keep de um documento só; com dois flows no mesmo projeto, coletar
pelo primeiro apagaria o cache do segundo. Falha de leitura de um flow não
impede o GC dos outros, mas aborta a coleta — melhor não coletar do que
apagar o que um flow ilegível alcançava. Os settings do projeto entram no
plano pelo mesmo motivo: sem eles a chave de quem usa tema do projeto seria
outra, e o cache vivo pareceria órfão.GC do projeto: preserva tudo que QUALQUER flow do projeto alcança. tr_store_gc
recebia keep de um documento só; com dois flows no mesmo projeto, coletar
pelo primeiro apagaria o cache do segundo. Falha de leitura de um flow não
impede o GC dos outros, mas aborta a coleta — melhor não coletar do que
apagar o que um flow ilegível alcançava. Os settings do projeto entram no
plano pelo mesmo motivo: sem eles a chave de quem usa tema do projeto seria
outra, e o cache vivo pareceria órfão.
tr_project_gc(project, max_age_days = 7)tr_project_gc(project, max_age_days = 7)
tr_project() LÊ o trama.json e nunca o escreve — os manifestos do
repositório foram escritos à mão. Fazer create = TRUE passar a gravá-lo
seria pior que a lacuna: tr_project() é chamado sobre projeto que JÁ existe
o tempo todo, e ele reescreveria o manifesto alheio com as coleções que por
acaso lhe passaram, em silêncio. Criar é outro verbo.
tr_project_new(root, collections = character())tr_project_new(root, collections = character())
collections são nomes de PACOTE, como no manifesto. Quem chama pela
interface passa as coleções que a página tem — é a única escolha que produz
projeto abrível ali mesmo, sem recarregar.
name do projeto em flows/<name>.json.Grava o flow name do projeto em flows/<name>.json.
tr_project_save(project, doc, name = "main")tr_project_save(project, doc, name = "main")
galeria em trama.json).Mesmo molde de tr_project_set_sugestoes(): valida antes de abrir o
arquivo e mexe só na chave galeria. Campo ausente em galeria vale o
padrão (pasta gallery, imagens entram sozinhas, PNG a 300 dpi).
tr_project_set_galeria(root, galeria)tr_project_set_galeria(root, galeria)
root |
Pasta do projeto (precisa ter |
galeria |
Lista com |
Os settings do projeto já com a galeria nova, invisível.
Verbo separado de tr_project_set_themes() porque a escolha é separada: o
painel salva as duas coisas no mesmo gesto, mas quem chama do console quer
desligar a marca sem ter que reescrever a lista de temas inteira para isso.
tr_project_set_marca(root, mostrar)tr_project_set_marca(root, mostrar)
root |
Pasta do projeto (precisa ter |
mostrar |
|
Valida ANTES de abrir o arquivo, pelo mesmo motivo dos temas: valor que
.tr_settings() recusa tranca o projeto fora do editor. Mexe só na chave
marca; o resto do manifesto volta como estava.
Os settings do projeto já com a marca nova, invisível.
Espelha tr_project_set_marca(): chave própria do manifesto, validada antes
de abrir o arquivo, e o resto do trama.json volta como estava. Desligada,
o editor não pré-preenche nada ao conectar; o que já foi sugerido fica.
tr_project_set_sugestoes(root, ligar)tr_project_set_sugestoes(root, ligar)
root |
Pasta do projeto (precisa ter |
ligar |
|
Os settings do projeto já com a flag nova, invisível.
temas.Valida ANTES de abrir o arquivo: manifesto com tema quebrado não abre o
projeto (tr_project_at() recusa), então gravar e validar depois deixaria o
usuário trancado fora do próprio projeto.
tr_project_set_themes(root, temas, padrao)tr_project_set_themes(root, temas, padrao)
root |
Pasta do projeto (precisa ter |
temas |
Lista nomeada de temas (campos como em |
padrao |
Nome do tema padrão (obrigatório): uma string não vazia que
esteja em |
paleta vai com I() pelo mesmo motivo de collections em
.tr_cfg_rewrite(): uma cor só não pode sair como string onde oito saem
como array.
padrao é obrigatório aqui, ao contrário da leitura: escolher o padrão
calado só serve pra manifesto antigo ou editado à mão. Quem GRAVA (o painel)
sabe qual escolheu, e mensagem sem o campo é bug do front — gravar "escuro"
ou o primeiro tema no lugar esconderia o bug.
Os settings normalizados, invisível.
Para cada nó do fluxo, de onde veio o valor que o editor mostra: a chave,
quando foi produzido, com que versão de R e de cada pacote de coleção. A
chave de cache NÃO inclui versão de dependência (ver hash.R), então um
resultado pode ter sido calculado por outra versão da coleção — esta tabela
é como se descobre, e é o que um relatório reprodutível precisa citar.
tr_provenance(project, flow = "main")tr_provenance(project, flow = "main")
project |
Projeto ( |
flow |
Nome do fluxo em |
Nós sem resultado em cache saem com status = "pendente"; resultados
gravados antes de o handle guardar o ambiente saem com r = NA.
data.frame com node, type, port, status, key, created,
r e uma coluna pkg_<pacote> por pacote registrado no handle.
O papel diz POR QUE a fonte está ali: a teoria do método, o livro-texto
que o ensina, o pacote que o implementa. A de implementação é outra
espécie — o autor sai de utils::citation(pacote) e a versão é lida do
pacote instalado na hora do catálogo —, por isso ela exige pacote e
funcao e dispensa autores, ano e título.
tr_ref( autores = NULL, ano = NULL, titulo = NULL, fonte = NULL, doi = NULL, url = NULL, papel = "teoria", pacote = NULL, funcao = NULL, nota = NULL )tr_ref( autores = NULL, ano = NULL, titulo = NULL, fonte = NULL, doi = NULL, url = NULL, papel = "teoria", pacote = NULL, funcao = NULL, nota = NULL )
doi vai sem prefixo de URL para haver UMA forma dele: com e sem
https://doi.org/ convivendo, o front teria que adivinhar qual montar.
É um objeto, não estado global. O insumo escrevia em globalenv(), o que
(a) impede virar pacote e (b) faz todo teste compartilhar o mesmo estado —
um registro por teste é o que permite testar coleções em isolamento.
O registro default do processo existe só pra conveniência interativa.
tr_registry()tr_registry()
Reconstrói um registro a partir dos NOMES DE PACOTE — como o worker recupera o catálogo sem receber funções pela fronteira.
tr_registry_for(packages)tr_registry_for(packages)
doc — a base do undo.Reexecutar o log é mais simples e mais confiável que inverter cada op
(disconnect inverso precisaria lembrar o index; remove_node inverso
precisaria lembrar params, seed e posição). O documento é pequeno e as ops
são baratas — reconstruir é o caminho honesto.
tr_replay(ops, registry = .tr_default_registry, doc = tr_doc())tr_replay(ops, registry = .tr_default_registry, doc = tr_doc())
doc tem que ser o documento em que o log COMEÇOU. O default vazio só serve
pra um log que cobre a história inteira; o undo do editor não é esse caso —
ver .tr_undo_doc().
O gesto explícito que faltou depois da Fase 5. Uma falha grava handle de
ERRO sob toda porta de saída da unidade (R/scheduler.R, o ramo de falha de
collect(), via tr_store_put_error); tr_plan() marca a unidade
failed, e o scheduler a serve direto de skipped/from_cache —
tr_run() sozinho, no MESMO store, nunca redespacha, mesmo com um
ckpt.rds bom esperando no disco (R/stream-driver.R: falha no step,
falha no colapso e worker morto guardam o checkpoint DE PROPÓSITO). O único
jeito público de tirar o handle de erro era tr_bust() — que apaga
<store>/stream/ inteiro (R/hash.R), porque bustar significa "o código
mudou sem a chave mudar", e aí retomar serviria um histórico metade velho,
metade novo. Usado contra um erro comum, tr_bust() jogaria fora os pontos
bons do checkpoint pela mesma chamada que limpa o erro do último ponto —
exatamente o custo que a retenção do checkpoint existe pra evitar.
tr_retry(doc, node, registry = .tr_default_registry, store, settings = NULL)tr_retry(doc, node, registry = .tr_default_registry, store, settings = NULL)
tr_retry() faz só a metade que falta: tira o(s) handle(s) de erro da
unidade e NUNCA toca <store>/stream/<chave da unidade>/. Não é
tr_bust() com um filtro a mais — é o oposto dele: um preserva o
checkpoint de propósito, o outro apaga de propósito, e os dois precisam
continuar existindo porque servem casos diferentes ("o ponto 10.000 errou,
quero retomar dali" vs. "mudei o código, o checkpoint velho não presta
mais").
Recusa apagar um handle BOM (tr_handle_failed() falso): um artefato
válido não é candidato a retry — apagá-lo seria um tr_bust() disfarçado,
silencioso, sem o comentário que explica a perda do checkpoint. A recusa é
TRANSACIONAL: se qualquer porta de saída da unidade guarda um artefato bom,
a chamada aborta ANTES de apagar qualquer handle de erro das outras portas
— uma região de dois colapsos não pode sair com um retry parcial.
Nó sem handle nenhum (nunca rodou, ou já foi limpo) é NO-OP silencioso: a
mesma forma que tr_stream_command() escolheu pra chave sem diretório
(R/stream-control.R) — clique perdido não é erro.
Membro INTERIOR de uma região não tem unidade nem chave de saída própria
(ver o comentário de tr_value() acima, e plan$region_of): pedir retry
dele significa "retry a região", resolvida pelo mesmo mapa que tr_value()
já usa pra leitura — o efeito observável é o de pedir retry por qualquer um
dos colapsos dela.
settings existe pelo mesmo motivo que em tr_value(): sem ele o plano
calcularia a chave com o tema embutido, e talvez não achasse a MESMA
unidade (nem as MESMAS chaves de saída) que o run de verdade usou.
Não roda nada — só planeja, pra achar a unidade e as chaves, e mexe no
store. É COMANDO, não op de documento: não toca doc, rev nem o log de
undo (Decisão 10), do mesmo jeito que pause/step/stop de
R/stream-control.R.
invisible(TRUE) se limpou pelo menos um handle de erro;
invisible(FALSE) se não havia nada a limpar (nó sem handle).
Vale em nó COMUM também, e não só em região: ali limpa o erro cacheado e o
nó roda de novo — só não há de onde retomar, porque nó comum não tem
checkpoint. É por isso que a recusa não fala de checkpoint: o tr_bust() que
ela indica serve para os dois casos, e o que ele apaga a mais (o diretório
stream/) só existe num deles.
SEM entrada no barramento do Shiny, de propósito: transport.R só decodifica
comando VIVO cuja chave o front já tem em mão (pause, step, stop). Retentar
precisa de doc + registry + settings para resolver um id de nó numa
unidade, que é conhecimento de plano dentro do barramento — o que aquele
arquivo recusa por desenho. Consequência a registrar em vez de descobrir: um
botão de retentar no front precisa de mensagem nova ou de um caminho por
chave, e isso é trabalho da fase da interface.
Pull-based herdado do insumo — nó fora do caminho do alvo não roda. O "acender os cards em sequência" continua saindo de graça da ordem real de execução: o coordenador não calcula fila nenhuma, só emite o que acontece.
tr_run( doc, targets = NULL, registry = .tr_default_registry, store, executor = tr_executor_sequential(), on_event = function(ev) invisible(NULL), run_id = NULL, settings = NULL, ctx_extra = NULL )tr_run( doc, targets = NULL, registry = .tr_default_registry, store, executor = tr_executor_sequential(), on_event = function(ev) invisible(NULL), run_id = NULL, settings = NULL, ctx_extra = NULL )
on_event(ev) recebe, por unidade: cached, failed, invalid,
blocked, running, done, cancelled (unidade superada por um run novo,
ou região PARADA por comando); mais progress e partial de quem publica
pelo .ctx; e um run_finished no fim. É o contrato que
o transporte (E5) traduz pra mensagem de front — o motor não sabe o que é
Shiny.
ctx_extra é a PORTA DOS AJUSTES DO RUN: uma lista de escalares que desce
inteira até o .ctx do worker (.tr_make_ctx()), pelo scheduler e pelo
executor, e vale igual nos dois executores. Hoje é lida por:
publish_every — segundos entre publicações de parcial (default 0.1)
checkpoint_every — passos entre checkpoints da região (default 100; 0
desliga a serialização periódica do acumulador)
Não é settings, e a distinção é de desenho: settings é conteúdo do projeto
e ENTRA no hash (o tema muda o gráfico), enquanto ctx_extra é ajuste de
sessão e não toca documento nem chave (Decisão 9) — se tocasse, baixar a
cadência de checkpoint recomputaria o fluxo inteiro que ela existe pra
proteger. Também não é tr_stream_command(): lá vão comandos VIVOS, que
mudam durante o run; aqui vão ajustes fixados quando o run começa.
Existe como argumento explícito, e não como opção global, porque duas sessões
sobre o mesmo store são o caso normal (é como os testes encenam o segundo
processo) e um options() faria a cadência de uma vazar na outra.
O motor já emite duração real e chave por unidade; até aqui isso morria no front. Gravar é o que dá base ao "Monitorar" do manifesto sem inventar superfície nova — e é telemetria honesta para medir o que o cache poupa. Handles ficam de fora: são pesados e já estão no store.
tr_run_logger(root, run_id)tr_run_logger(root, run_id)
tr_run() porque o coordenador
interativo (E5) monta o plano a cada edição pra decidir o que cancelar, e
só então manda executar.Executa um plano já montado — separado de tr_run() porque o coordenador
interativo (E5) monta o plano a cada edição pra decidir o que cancelar, e
só então manda executar.
tr_run_plan( plan, registry = .tr_default_registry, store, executor = tr_executor_sequential(), on_event = function(ev) invisible(NULL), run_id = NULL, ctx_extra = NULL )tr_run_plan( plan, registry = .tr_default_registry, store, executor = tr_executor_sequential(), on_event = function(ev) invisible(NULL), run_id = NULL, ctx_extra = NULL )
Lê todos os runs de um projeto como data.frame (um evento por linha).
tr_runs(project)tr_runs(project)
tr_run_plan() era um while com Sys.sleep, e tr_server o chamava
dentro de um observeEvent — o processo do Shiny ficava travado até o run
acabar, MESMO com o pool mirai. Três promessas do design dependiam de o
coordenador ficar livre e não existiam: cancelar unidade superada, mostrar
progresso enquanto roda, mostrar parcial. Aqui o estado é explícito e
step() faz um passo (despacha o que está pronto, coleta o que terminou).
Headless: while (!s$step()) .... Shiny: later::later(step).
tr_scheduler( plan, registry = .tr_default_registry, store, executor = tr_executor_sequential(), on_event = function(ev) invisible(NULL), run_id = NULL, inherit = list(), ctx_extra = NULL )tr_scheduler( plan, registry = .tr_default_registry, store, executor = tr_executor_sequential(), on_event = function(ev) invisible(NULL), run_id = NULL, inherit = list(), ctx_extra = NULL )
DOIS conjuntos, e a distinção importa:
done — terminou com valor bom (executou agora, ou veio do cache).
skipped — não vai produzir valor (falhou, inválido, bloqueado).
A versão anterior inferia "terminou" de "não está mais em pending" — e
uma unidade EM VOO também não está em pending. Com capacidade > 1 o
consumidor era despachado antes do produtor gravar, falhava com "chave
ausente", e — pior — esse erro virava handle no store: como a chave é
determinística, o erro falso ficava cacheado com a mesma autoridade de um
resultado bom, para sempre.
O Shiny é barramento, não lógica. Nenhuma regra de domínio, de documento ou de execução mora aqui — decodifica, chama o motor, codifica de volta.
tr_server( project, flow = "main", executor = tr_executor_sequential(), autosave = TRUE, ctx_extra = NULL )tr_server( project, flow = "main", executor = tr_executor_sequential(), autosave = TRUE, ctx_extra = NULL )
Pra cima: input$tr_op com {seq, base_rev, op}.
Pra baixo: sendCustomMessage("tr_event", ...).
O front emite OPS, nunca o documento inteiro. No insumo o front empurrava o grafo todo com debounce e o servidor diffava; havia ainda um segundo timer pedindo execução em paralelo, sem garantia de ordem. Quando a ordem não valia, a engine rodava contra o documento antigo, via que nada mudou, servia do cache e o preview não atualizava — sem erro, em silêncio.
set_param para cada ... nomeado, um por um.Aplica set_param para cada ... nomeado, um por um.
tr_set(flow, id, ...)tr_set(flow, id, ...)
É o que permite o worker ser outro processo sem serializar objeto vivo: o worker computa, grava o artefato e o preview sob a chave, e devolve só o handle. O coordenador nunca carrega um valor pesado.
tr_store(root, project_root = NULL)tr_store(root, project_root = NULL)
Layout:
objects/store do TIPO
handles/
progress/ e stream/ são de trabalho em voo, não de artefato: nascem quando
a unidade começa e saem quando ela conclui. stream/ é por chave de UNIDADE
(não de saída), criado pelo driver quando o laço começa.
Escrita atômica (temp + rename) não é refinamento: o contrato do executor é que o worker pode morrer a qualquer momento (cancelar = matar o daemon, porque cancelamento cooperativo em R não existe). Um arquivo parcial sob uma chave válida seria indistinguível de um resultado bom, para sempre — o cache serviria lixo silenciosamente.
keep são as chaves alcançáveis do documento
atual; o resto sai por idade.Coleta de lixo: o store cresce sem limite por construção (cada edição de
param cria uma chave nova). keep são as chaves alcançáveis do documento
atual; o resto sai por idade.
tr_store_gc(store, keep = character(), max_age_days = 7)tr_store_gc(store, keep = character(), max_age_days = 7)
key, restaurado pelo tr_type; erro alto se a chave estiver ausente ou guardar um erro.Lê o valor sob key, restaurado pelo tr_type; erro alto se a chave estiver ausente ou guardar um erro.
tr_store_get(store, key, type_spec)tr_store_get(store, key, type_spec)
Sem isto, um único JSON truncado (worker morto, disco cheio) abortava
tr_store_gc() e tr_bust() — deixando o store PERMANENTEMENTE
incoletável, num sistema que cresce sem limite por construção. Disco cheio
esperando acontecer, sem caminho de recuperação dentro da API.
tr_store_handle(store, key)tr_store_handle(store, key)
cached = TRUE e a unidade
ser pulada — e o consumidor a jusante explodiria com erro nu de conexão.Uma chave só "existe" se handle E objeto existem. Handle órfão (remoção
externa, GC interrompido) faria o plano marcar cached = TRUE e a unidade
ser pulada — e o consumidor a jusante explodiria com erro nu de conexão.
tr_store_has(store, key)tr_store_has(store, key)
Resolve um caminho relativo contra a raiz do projeto. Absoluto passa direto; sem raiz conhecida (store solto em teste), cai no cwd — que é o que acontecia antes, só que agora explícito.
tr_store_path(store, path)tr_store_path(store, path)
tr_store_has), então um worker morto no meio
deixa no máximo um objeto órfão — nunca um handle apontando pra nada.collections é o conjunto de coleções que produziram o artefato. Existe
porque tr_bust(store, coleção) deriva a coleção de node_type, e isso não
funciona quando a unidade não é um nó: a região de fluxo grava sob
node_type = "trama/stream_region" — que lê como coleção "trama" — e mistura
coleções de verdade. Ausente (todo nó comum), tr_bust() cai no node_type,
que é onde a informação sempre esteve.
tr_store_put( store, key, value, type_spec, node_type = NULL, duration = NA_real_, collections = NULL, env = NULL, tema = NULL )tr_store_put( store, key, value, type_spec, node_type = NULL, duration = NA_real_, collections = NULL, env = NULL, tema = NULL )
Sem isto, um nó que falha não deixa registro, e o jusante tenta executar a cada edição — reexecutando o mundo por causa de uma ponta quebrada. Com o handle de erro, o plano marca o jusante como bloqueado e não roda nada.
tr_store_put_error( store, key, message, class = NULL, traceback = NULL, node_type = NULL, collections = NULL )tr_store_put_error( store, key, message, class = NULL, traceback = NULL, node_type = NULL, collections = NULL )
key (a chave da UNIDADE, que é
a que vem nos eventos do run).cmd é um de:
"play" — volta a andar sozinho
"pause" — para entre passos e espera
"step" — um passo e volta a pausado
"tempo" — muda o atraso por passo, sem mexer em pausado/rodando
"stop" — encerra o run desta região deixando checkpoint
tr_stream_command(store, key, cmd, tempo = NULL)tr_stream_command(store, key, cmd, tempo = NULL)
Chave sem diretório de região é NO-OP silencioso: o clique do usuário e o
evento done se cruzam, e comando de região que já terminou é normal, não
erro. Erro ali viraria banner vermelho por um botão que chegou tarde — e
criar o diretório "por cortesia" encheria o store de diretórios de chaves que
nunca rodaram. O diretório é criado pelo DRIVER quando a região começa: é ele
quem sabe que há laço pra comandar.
{seq, base_rev, op}.O front emite OPS, nunca o documento inteiro. No insumo o front empurrava o grafo todo com debounce e o servidor diffava pra descobrir se algo relevante tinha mudado — e havia um segundo timer pedindo execução em paralelo, sem garantia de ordem de chegada. Quando a ordem não valia, a engine rodava contra o documento ANTIGO, via que nada mudou, servia do cache, e o preview não atualizava: sem erro, silenciosamente.
tr_submit(doc, envelope, registry = .tr_default_registry)tr_submit(doc, envelope, registry = .tr_default_registry)
Aqui essa corrida vira erro explícito: se base_rev não bate com a revisão
atual, a op é recusada e o cliente ressincroniza com o documento inteiro.
Estado nunca fica corrompido em silêncio.
seq existe por uma armadilha do Shiny: input$x ignora valores idênticos
consecutivos. Duas ops iguais em sequência (clicar "remover" no mesmo nó
duas vezes, mover pro mesmo lugar) desapareceriam sem um campo que muda
sempre.
Só a estrutura viaja: a posição é normalizada pro canto (0,0) e colecoes
lista os pacotes que quem cola precisa ter.
tr_template(doc, nome, descricao = "", registry = .tr_default_registry)tr_template(doc, nome, descricao = "", registry = .tr_default_registry)
biblioteca é pessoal (vale em qualquer projeto); projeto viaja junto
com a pasta do projeto. Templates de coleção moram em inst/templates do
pacote e não têm pasta gravável: só são listados.
tr_template_dir(escopo = c("biblioteca", "projeto"), root = ".")tr_template_dir(escopo = c("biblioteca", "projeto"), root = ".")
Template como JSON (envelope + documento canônico).
tr_template_json(tpl)tr_template_json(tpl)
Uma lista de registros (escopo, nome, descricao, arquivo,
colecoes). Arquivo que não é template — dado solto, JSON quebrado — é
ignorado: uma pasta suja não pode esconder os templates bons.
tr_template_list(root = ".", registry = .tr_default_registry)tr_template_list(root = ".", registry = .tr_default_registry)
batch que insere o template com o canto em origin, ids novos.Ids novos a cada chamada: colar o mesmo template duas vezes não pode colidir. Uma op só (e não várias) pra inserção ser um passo de undo. Frames vêm antes dos nós (ficam por baixo); tamanho, vista, modo e arestas vêm depois, porque exigem o nó já criado.
tr_template_op(tpl, origin = c(0, 0))tr_template_op(tpl, origin = c(0, 0))
Lê um template a partir de texto JSON.
tr_template_parse(txt)tr_template_parse(txt)
.json.Lê um template de um arquivo .json.
tr_template_read(path)tr_template_read(path)
dir; nome de arquivo derivado do nome do template.Grava o template em dir; nome de arquivo derivado do nome do template.
tr_template_save(tpl, dir, overwrite = FALSE)tr_template_save(tpl, dir, overwrite = FALSE)
trama/test desenha.Precisa de uma fonte de decisão: p_valor ou criticos com o nível "5%".
Sem nenhuma das duas o teste viraria um "não rejeita H0" confiante, e isso
é erro aqui, não card.
tr_test( teste, h0, estatistica, rotulo_estat = "estatística", p_valor = NA_real_, gl = NA_character_, criticos = NULL, sentido = "menor", conclusao_sim, conclusao_nao, efeito = NULL, nota = "", fonte = "", extra = NULL, classe = NULL )tr_test( teste, h0, estatistica, rotulo_estat = "estatística", p_valor = NA_real_, gl = NA_character_, criticos = NULL, sentido = "menor", conclusao_sim, conclusao_nao, efeito = NULL, nota = "", fonte = "", extra = NULL, classe = NULL )
teste, h0
|
nome do teste e a hipótese nula, em texto. |
estatistica |
o valor da estatística; |
p_valor |
o p-valor, ou |
gl |
graus de liberdade: texto ("2; 27"), usado como veio, ou número(s),
formatados aqui — inteiro sem casas, fracionário (Welch, Satterthwaite)
com uma casa e vírgula decimal, vários separados por "; " — ou |
criticos |
vetor nomeado de valores críticos ("10%", "5%", "1%"). |
sentido |
"menor" ou "maior": a cauda em que a estatística rejeita
contra os |
conclusao_sim, conclusao_nao
|
a conclusão em uma linha, conforme rejeite ou não H0 a 5%. |
efeito |
|
nota, fonte
|
texto livre; |
classe |
classe S3 a mais, na frente de |
objeto tr_test.
Sempre as mesmas colunas, com NA onde não se aplica (teste de tabela não tem
p-valor; quase nenhum tem efeito): o pesquisador empilha testes de coleções
diferentes num quadro só, e esquema estável é o que deixa o empilhamento
ser um relatório. Só extra vira coluna quando existe — é, por definição,
o que cada teste tem de próprio —, e com prefixo extra_ e o nome como veio:
as chaves podem ser nomes de contraste do usuário ("A - B", até "nota"), e
sem prefixo colidiriam com as fixas; sem check.names = FALSE, "A - B"
viraria "A...B".
tr_test_table(x)tr_test_table(x)
x |
um |
data.frame de uma linha.
trama/test.O núcleo não registra tipos: quem registra é uma coleção (a data), com o
id no namespace dela. Por isso o id é argumento.
tr_test_type(id)tr_test_type(id)
id |
id qualificado do tipo. |
um tr_type.
Hoje quase tudo é escrito só em português, mas pressupostos e referências
vão para o editor E para o site — e o site terá versão em inglês. Aceitar
list(pt = , en = ) desde já faz a tradução ser acréscimo de conteúdo, não
mudança de API em todas as coleções. A escolha cai para pt (a língua em
que o conteúdo nasce) e, na falta dela, para o primeiro idioma declarado:
um texto só em inglês ainda é melhor que nenhum.
tr_text(x, lang = getOption("trama.lang", "pt"))tr_text(x, lang = getOption("trama.lang", "pt"))
x |
string única, ou lista nomeada de strings únicas. |
lang |
código do idioma pedido. |
string única.
Para o console e para coleções: fn de nó recebe a definição já resolvida
pelo plano, mas quem chama a função direto passa um nome ("claro"). Sem
projeto, valem os temas embutidos.
tr_theme(tema = "padrão", settings = NULL)tr_theme(tema = "padrão", settings = NULL)
tema |
Nome de um tema, |
settings |
Settings do projeto; |
Aceitar também a lista já resolvida é o que deixa a coleção ter UM caminho
só: ela chama tr_theme() sem saber de onde veio o valor. A lista é
revalidada porque no console qualquer um monta uma à mão, e fudno calado
seria o mesmo silêncio que o manifesto recusa. Nome desconhecido não é erro
aqui, pelo mesmo motivo do plano (documento não quebra por cosmético): volta
o padrão marcado ausente, e quem chama decide se isso é erro.
Lista com nome, os campos do tema e, se o nome não existe,
ausente = TRUE.
Existe para a mensagem de erro de quem chama no console: "tema 'escurro' não existe" só ajuda se disser quais existem, e a coleção não pode copiar a lista dos embutidos — o projeto troca essa lista.
tr_theme_names(settings = NULL)tr_theme_names(settings = NULL)
settings |
Settings do projeto; |
Vetor de nomes.
O arquivo é o transicoes.json que tools/sugestor/minerar.R grava: um
array [{from, to, n}]. Ausente devolve NULL em vez de erro, porque uma
coleção sem exemplos não tem o que declarar, e isso não deve impedi-la de
carregar.
tr_transitions_read(path)tr_transitions_read(path)
path |
Caminho do JSON; costuma vir de |
data.frame(from, to, n) ou NULL.
O tipo carrega a própria persistência (store/restore/ext). É aqui
que "SpatRaster é ponteiro C++ e não sobrevive a saveRDS" se resolve — não
no store, que permanece cego ao que guarda.
tr_type( id, version = 1L, label = NULL, color = "#64748b", store = NULL, restore = NULL, ext = "rds", preview = NULL, summary = NULL, report = NULL, tema = FALSE )tr_type( id, version = 1L, label = NULL, color = "#64748b", store = NULL, restore = NULL, ext = "rds", preview = NULL, summary = NULL, report = NULL, tema = FALSE )
tema |
O preview é um gráfico que segue o tema padrão do projeto. |
O insumo despachava serialização por classe R (inherits(x, "SpatRaster")),
e isso não escala nem dentro de um domínio só: Heightmap e Field são
ambos SpatRaster, e foi exatamente essa colisão que obrigou a existência
de um nó identidade (height_as_field) só pra reetiquetar um no outro.
Tipo é nominal; classe R é detalhe de implementação de um tipo.
preview(x, ctx) devolve um artefato — tr_preview() — cujo renderer é
um id de string resolvido no front. Dispatch por id, nunca por inherits():
é o que permite uma coleção trazer visualização nova sem tocar no núcleo.
report(x) é opcional e diz como o valor aparece num relatório exportado
(tr_export_code()): uma função EXPORTADA de pacote, que o documento chama
sobre a saída (tr_series_plot(x) para uma série). Sem ele, o relatório
imprime o objeto. Função e não preview porque o relatório roda sem o Trama.
Sem store/restore, cai em RDS — suficiente pra tipo que é objeto R
comum (data.frame, lista, escalar).
tema = TRUE diz que o preview desenha um GRÁFICO com o tema "padrão".
Aí o tema padrão do projeto entra na chave das saídas desse tipo (trocar o
padrão redesenha o card) e vale como "padrão" enquanto o preview roda —
sem isto o card de uma série ficava no tema embutido, escuro, num projeto
todo claro.
<div> onde o React monta.Monta a UI Shiny do editor: importmap, dependências e o <div> onde o React monta.
tr_ui(project)tr_ui(project)
Aceita o objeto tr_collection direto, ou o nome de um pacote que exporte
trama_collection(). Colisão de id é erro alto e cedo — mas agora é
improvável por construção, já que id é qualificado por coleção.
tr_use( collection, registry = .tr_default_registry, requires_of = .tr_collection_requires )tr_use( collection, registry = .tr_default_registry, requires_of = .tr_collection_requires )
Pelo nome de pacote, as coleções de que ela depende (campo
Config/trama/requires do DESCRIPTION, ver .tr_collection_requires())
entram antes, recursivamente, pulando as que o registro já tem. É isso que
tira a ordem do trama.json do caminho: a validação de portas abaixo
recusa tipo desconhecido, e sem isso um projeto que lista só
trama.series quebrava por não ter pedido trama.data antes.
requires_of existe para teste: injeta o grafo sem pacotes de verdade.
O caminho inverso do nível 1 (tr_fn(id)), e a outra metade do que impede
a ferramenta de virar gaiola: dá pra inspecionar no REPL o que qualquer
caixa da tela produziu, sem passar pela UI.
tr_value( doc, node, registry = .tr_default_registry, store, port = NULL, executor = tr_executor_sequential(), settings = NULL, ctx_extra = NULL )tr_value( doc, node, registry = .tr_default_registry, store, port = NULL, executor = tr_executor_sequential(), settings = NULL, ctx_extra = NULL )
Reaproveita o plano que tr_run() já devolve, em vez de montar outro. Um
segundo plano sorteia um nonce novo pra nó volatile, e a chave
consultada não seria a que acabou de ser gravada.
settings é o project$settings: tema entra no hash, e sem ele o console
resolveria o tema pelos embutidos e calcularia chave diferente da sessão —
recomputando o que já está no cache, ou pior, mostrando outro gráfico.
ctx_extra está aqui porque pedir o valor de um nó no console RODA o grafo, e
uma região de dez mil pontos é justamente o que alguém pede assim: sem o
argumento, o único jeito de baixar a cadência de checkpoint num run de console
seria não usar tr_value().
tr_when(tr_param_num(0.05), metodo = c("holm", "bonferroni")) só exibe o
campo quando metodo vale um dos valores dados. Várias condições valem
juntas (E). Os valores podem ser texto, lógico ou número.
tr_when(p, ...)tr_when(p, ...)
p |
Um param de |
... |
Condições nomeadas: |
É puramente de interface: param escondido guarda o valor e continua indo
para fn. Que cada nome citado seja outro param do nó é conferido em
tr_node() (tr_error_bad_when), porque o param sozinho não conhece os
irmãos.
p com when preenchido.
tr_when() por padrão de texto: o param aparece quando o valor
do outro casa com a expressão regular (sem diferenciar maiúsculas). Serve
para o que uma lista de valores não expressa, como "o caminho é um link":
tr_when(p, path = tr_when_padrao("^https?://")).Condição de tr_when() por padrão de texto: o param aparece quando o valor
do outro casa com a expressão regular (sem diferenciar maiúsculas). Serve
para o que uma lista de valores não expressa, como "o caminho é um link":
tr_when(p, path = tr_when_padrao("^https?://")).
tr_when_padrao(padrao)tr_when_padrao(padrao)
padrao |
Expressão regular, na sintaxe comum ao R e ao JavaScript. |