npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fidera/collector

v0.8.0

Published

O coletor forge-native: lê `git` e a forja **na borda do adotante** e empurra os primitivos crus para o Fidera. Zero dependência de runtime — roda via `npx` no CI do adotante.

Downloads

1,292

Readme

@fidera/collector

O coletor forge-native: lê git e a forja na borda do adotante e empurra os primitivos crus para o Fidera. Zero dependência de runtime — roda via npx no CI do adotante.

Passo 0 — o kit do repositório (#454)

Antes do workflow, ou depois: nenhum passo do kit é pré-requisito da coleta. O que ele faz é pôr no seu repositório, de uma vez e como proposta, as convenções que a derivação precisa para produzir dado bom — as mesmas que, sem o kit, você aprenderia depois de derivar N vezes.

npx --yes @fidera/[email protected] init --dry-run   # mostra o plano, escreve nada
npx --yes @fidera/[email protected] init             # escreve o que não existe; nunca sobrescreve
npx --yes @fidera/[email protected] init --forge gitlab
npx --yes @fidera/[email protected] init --with-check  # + o check opcional de PR sem chave (GitHub)

O que entra, e por quê:

  • Template de PR e de issue (.github/ ou .gitlab/) com a convenção da chave do rastreador e o bloco de veredito de review — ver a seção abaixo.
  • .fidera/politica-proposta.md: a taxonomia por pasta rastreada (DOMINIO=pasta, com GERAL=. como resto) e o catálogo skill=F<n> das skills instaladas. É proposta: quem aplica é um ADMIN, em /painel/politica, colando os blocos — nada é aplicado em silêncio (a política é história versionada). O workflow de CI fica de fora de propósito (#526): o campo casa pelo name: publicado, não pelo arquivo, e é a tela que lista, depois da primeira coleta, os nomes que a forja publicou nas suas entregas, separando as rotinas.
  • As skills instrumentadas em .claude/skills — as do superpowers que terminam com a seção ## Fidera, dizendo o ato de fase que produzem (F1 entrega o bloco ao humano; F3 pede o trailer Fidera-Skill/Fidera-Tag no primeiro commit da construção; F4 garante o bloco de veredito). Skill que já existe no seu repositório é pulada inteira.
  • --with-check (GitHub): um workflow que barra PR sem chave do rastreador. É portão seu, no seu repositório, escolhido por você — o Fidera não depende dele e não o liga sozinho. No GitLab, a validação da chave é do pipeline (o adotante medido já a tinha).

O que o kit não faz, de propósito: não instala credencial nenhuma, e a F3 não pede credencial nenhuma — as skills de construção escrevem o trailer no commit (seção "A F3 no trailer do commit", abaixo) e a coleta diária o lê. O token de escopo EMIT do tenant (FIDERA_INGEST_TOKEN no ambiente; no cofre da pipeline para agente em CI) só existe para o canal de exceção, o fidera-collector emit, para código fora do que o coletor lê. O kit também não manda instalar o App fidera-claude na organização: nenhuma skill instalada pede marcador de agente (a F3 vai no trailer, a F1 é marcador humano e a F4 é o bloco de veredito no PR), e pedir a um admin da org um ato que nada usa seria ruído. A instrução volta quando o servidor passar a postar como fidera[bot] (#545). Se algum passo pedir um .pem na sua máquina, está errado.

A chave do rastreador — e o prefixo de domínio

A delivery é o nome da unidade de intenção, o elo entre a entrega e a issue aprovada. O coletor a lê de dois lugares, e um basta:

  • Issue na forja: a branch chama <n>-slug (123-cobranca-recorrente) e o PR fecha com Closes #123 — em inglês, é keyword da máquina.
  • Rastreador externo (Jira e afins): a chave no início do título do PR/MR — [PROJ-123] Cobrança recorrente. Vale também para entrega sem commit de merge (squash, fast-forward): o coletor lê o assunto do próprio commit (#440).

Sem uma das duas, a entrega vira o SHA do commit no painel (fallback-sha, contado na fila de atenção). E o domínio vem do primeiro commit da branch: DOMINIO/tipo(#123): …, com um domínio da taxonomia da política — é o degrau 1 da cascata (#431), convenção declarada, que vence a inferência por caminho. Só o primeiro commit: dois prefixos diferentes na mesma entrega empatam e anulam.

Quickstart

O escopo @fidera no npm é da organização fidera (reivindicado em 19/08/2026 pelo governador — antes disso o 404 deixava qualquer terceiro publicá-lo e ser executado no runner do adotante, com o GITHUB_TOKEN dele em mãos; a razão da versão exata abaixo é a mesma).

permissions:
  contents: read # o checkout, e o `git log`/`git diff` locais
  actions: read # `/actions/runs` — o eixo de CI
  pull-requests: read # `/commits/{sha}/pulls` e os comentários do portão
  issues: read # `/issues/comments` — a via harvest do nível C (#186: sem isto o endpoint
  # devolve 200 FILTRADO, só comentários de PR — os marcadores `fidera:<ato>` em issues
  # somem em silêncio, sem 403) — e `/issues/events`, a fila antes do trabalho (#539)

steps:
  - uses: actions/checkout@v4
    with:
      fetch-depth: 0 # clone completo: a PRIMEIRA corrida traz o histórico inteiro (ver "A primeira carga")
  - run: npx --yes @fidera/[email protected] --mode tail --fidera-url https://api.fidera.tec.br --tenant SEU-SLUG
    env:
      GITHUB_TOKEN: ${{ github.token }}
      FIDERA_INGEST_TOKEN: ${{ secrets.FIDERA_INGEST_TOKEN }}

⚠️ O bloco permissions: não é opcional. Com o default restrito de GITHUB_TOKEN — o padrão de organizações novas — o token recebe contents: read e nada mais, e aí /actions/runs, /commits/{sha}/pulls e /issues/{n}/comments devolvem 403. Esse 403 não traz x-ratelimit-remaining: 0, então não é limite de taxa: é permissão. O coletor passou a dizer isso na mensagem de erro, mas evitar o erro é melhor do que diagnosticá-lo.

⚠️ A versão é exata, e não é preciosismo. Um range (@1, @latest) resolve no runner do adotante, num processo que ninguém está olhando, com o GITHUB_TOKEN do repositório em mãos. Versão exata é o que faz o adotante decidir quando o código que ele executa muda. A guarda fitness/quickstart-exact-version.test.ts recusa range e exige que este número seja o do package.json.

--yes é da mesma família: sem ele, npx num pacote ainda não baixado abre um prompt, e num runner sem tty isso é um step pendurado até o timeout — com um diagnóstico que não aponta para cá.

⚠️ O host é api.fidera.tec.br, e só ele. Até a 0.4.0 este bloco apontava api.fidera.app — um host que nunca existiu, sob um domínio que não é do Fidera (#436). O FIDERA_INGEST_TOKEN vai no Authorization de toda corrida: confira o host antes de colar. A mesma guarda recusa qualquer --fidera-url concreto neste documento que não seja o host para onde o próprio Fidera coleta (.github/workflows/coleta.yml).

Governança ao nascer

O tenant já nasce com uma política aplicada (versão 1, o preset da governança default), então a primeira coleta já pode ser derivada, no grão do repositório inteiro, com um único domínio GERAL. Nada deriva sozinho: quem deriva é um ADMIN, no botão "Derivar" do painel. E a política não viaja com o backfill: o backfill traz o que a forja produziu, e a governança é ato do ADMIN. Revise a política em /painel/politica antes do primeiro "Derivar" (#428). Derivar com o preset dá número errado e obriga a derivar de novo. O painel mostra a política como não revisada até um ADMIN aplicar a dele. Os três campos que mais mudam o sinal:

  • o workflow de CI (ciWorkflow) — o nome publicado do workflow (o name: do YAML), não o nome do arquivo. O preset traz ci.yml, que quase nunca casa, e aí toda entrega sai sem portão observado (#429). Com CI fora do Actions, veja a fonte do portão (#444) abaixo. O coletor carrega, em cada run, o gatilho que a forja declara (event), e o painel só oferece como candidato a portão o que reage à entrega (push, pull_request): workflow agendado, disparado à mão ou encadeado roda no commit que estava na ponta — a coleta do próprio Fidera é um deles — e aparece como rotina, sem botão (#540).
  • os marcadores de retrabalho — se estiverem incompletos, o sinal de aprovação sai otimista.
  • a taxonomia — o GERAL=. do preset casa qualquer arquivo. A cobertura parece perfeita porque só existe um domínio (medido em 03/09: 228 de 228 com o preset, 34 sem domínio depois da taxonomia real).

A taxonomia é o modulos.properties do repositório, verbatim: DOMINIO=pasta1,pasta2,..., uma linha por domínio, prefixo mais longo vence. Extensão do Fidera ao formato: o valor . é o caminho raiz — casa todo arquivo e, sendo o prefixo mais curto possível, perde de qualquer pasta declarada. É o que o preset usa (GERAL=.), e serve de fallback se o adotante quiser manter um domínio "resto" ao lado da taxonomia real.

O que é uma entrega

Cada commit da linha de primeiro pai da branch principal é uma unidade de entrega — o que entrou na main como unidade. Com commit de merge, parents é o [base, head] do próprio merge; com squash, fast-forward ou push direto, é [pai, o próprio commit], e o range da entrega é esse commit (#430). O coletor não exige commit de merge: um adotante de produção que integra por fast-forward estava 90% invisível antes disso, com a coleta reportando sucesso.

Dois efeitos declarados: sem Merge pull request #N from … no assunto, a delivery cai para o SHA curto (origem fallback-sha, contada no relatório; o assunto do commit é a #431); e commits de automação na main (bumps de release) entram como entrega — quem os nomeia é a política do tenant, por markers.botEmailDomain (o domínio do bot ou, quando ele divide domínio com pessoas, o endereço completo), não o coletor. Entrega só de automação aparece na trilha como sistema e não ocupa a janela de trabalho nem avança o nível (#518); sem a declaração, o bump conta como trabalho de uma pessoa. A raiz do repositório não é entrega (não tem de onde partir), e um clone raso cuja fronteira esconde os pais de tudo o que ele tem é recusa, não coleta vazia.

Modos

| --mode | o que faz | fetch-depth | |---|---|---| | tail (padrão) | re-observa a cauda de merges. É a corrida diária. Sem merge assentado no servidor, vira all sozinha (a primeira carga, abaixo). | 0 (200 basta para a cauda, mas não para a primeira carga) | | all | backfill: todos os merges, mais a varredura de drift na forja. | 0 | | drift | só a observação de drift deste run, lida do workspace. Zero git, zero forja. | 200 |

A cauda é re-observada de propósito: re-run de CI e edição do comentário do portão acontecem depois de o merge assentar, e o servidor faz upsert por (tenant, mergeSha, collectorVersion).

O teto da cauda. A janela é o maior entre --tail-k e os merges desde o último assentado, até 50. Acima disso (dias sem coleta, por exemplo), ela pega os 50 mais antigos desde o último assentado e o stderr diz quantas corridas faltam. O marcador avança sem pular merge, e a corrida seguinte continua de onde esta parou; --mode all cobre o resto de uma vez. Até alcançar os mais novos, a re-observação deles espera.

--mode drift devolve merges: [] por desenho — uma corrida de PR não pode reportar como fato o que ainda não foi integrado.

A primeira carga (#420)

A porta do legado. O coletor consulta o estado da ingestão antes de coletar; sem merge assentado para a versão dele, a corrida tail vira all por conta própria: índice de runs, histórico inteiro do clone, varredura de drift e a colheita de atos desde o início. Antes de gastar a forja, o stderr diz a contagem — "carga do histórico: 470 merges no clone, 470 entram nesta corrida (desde 2026-03-12…)" — e a retomada por marcador (do mais antigo ao mais novo) cobre interrupção por teto de API: a corrida seguinte continua de onde parou.

  • Teto, opcional: --history-since AAAA-MM-DD (só merges a partir da data) e --history-cap N (os N mais recentes); combinam pela interseção. Valem para all explícito também. Sem eles, tudo.
  • Clone raso na primeira carga é aviso, não erro — a carga sai parcial e o stderr diz para usar fetch-depth: 0. Repositório sem runs de CI também é aviso na primeira carga (no all explícito continua recusa).
  • Tempo: o tessena carregou ~470 merges em menos de uma hora. Dê timeout-minutes: 60 ao job — a diária, depois, cabe em minutos.
  • Depois de um bump de versão do coletor, a primeira corrida da versão nova é de novo "primeira carga" e recarrega o histórico: a leitura de merges mudou, e é assim que a leitura nova chega ao passado.

Contrato de entrada

| flag | obrigatória | padrão | |---|---|---| | --fidera-url | sim | — | | --tenant | sim — o slug do tenant, o mesmo do host <slug>.… (nunca um id interno) | — | | --mode | não | tail | | --main | não | main | | --tail-k | não | 5 | | --lookback-days | não | 90 | | --repo-root | não | . | | --observacao | não | build/maturity/drift-observacao.json | | --dry-run | não | false | | --history-since | não — teto por data da carga do histórico (AAAA-MM-DD) | sem teto | | --history-cap | não — teto por contagem da carga do histórico (os N mais recentes) | sem teto |

| variável | para quê | |---|---| | FIDERA_INGEST_TOKEN | obrigatória — autoriza o POST. Tenant-scoped, escopo de ingestão. | | GITHUB_TOKEN | leitura da forja. github.token basta. | | GITHUB_REPOSITORY | obrigatória — o runner já a publica. |

Tudo isso é validado na entrada, antes de qualquer I/O: um --tenant esquecido falha em milissegundos, e não depois de gastar o orçamento de requisições da forja.

CI fora do Actions: declare a fonte do portão (#444)

Se o seu CI é Jenkins, CircleCI, Buildkite ou outro que publica na forja, declare na política (painel → Política → fonte do portão):

| gateSource | o que o coletor consulta | e aí o ciWorkflow nomeia | |---|---|---| | actions (padrão) | GET /actions/runs — os runs do workflow | o workflow | | check-runs | GET /commits/{sha}/check-runs | o check (ex.: Jenkins) | | status | GET /commits/{sha}/status | o contexto (ex.: continuous-integration/jenkins/branch) |

Três coisas antes de declarar:

  • As fontes são exclusivas. Medido no jenkinsci/jenkins: o mesmo Jenkins publica 1 commit status agregado e 12 check-runs. Ler as duas contaria o mesmo portão duas vezes — escolha a que o seu CI publica. Em dúvida, gh api repos/OWNER/REPO/commits/<sha>/status e .../check-runs mostram o que existe.
  • Declarar custa. A fonte nova é uma requisição por entrega e não tem índice, ao contrário dos runs (que o backfill lê em lote). Quem não declara não paga nada: a coleta segue idêntica.
  • Nada muda no workflow. A declaração vive só na política; a próxima coleta já obedece, porque o coletor a lê no GET /v1/observacoes:estado antes de coletar.

A consulta é no HEAD da entrega, não em cada commit do range: status e check-run de CI externo são publicados no commit que o CI construiu.

A F5 vem de graça, do CI que você já roda (#452)

O ciclo do workflow declarado em ciWorkflow — o mesmo que o Fidera já lê para decidir o terminal — passa a nascer também como VerificacaoConcluida: { workflow, resultado, ref, confianca }, com resultado PASSOU (conclusão success) ou FALHOU. É fato, nunca nota: não há rubrica, não há score.

Nada a instalar e nada a marcar — se a sua entrega atravessa CI, a F5 já tem evidência. Medido neste repo em 22/09: 59 de 60 merges recentes têm o check do workflow declarado.

No painel, a F5 aparece na árvore de processo como entrega com os atos por domínio, sem nível: não há driver de nível para F2–F5 (#452). A fase vem do próprio ato (#588), então não há linha a declarar no catálogo, e uma linha entrega= lá não decide nada: a rota de aplicar política a descarta e devolve um aviso (a tela ainda não o mostra: #590).

Não existe emit verificacao-concluida, e a razão é a mesma que mantém o terminal fora do emissor: prontidão por build é atestada por quem executa o build. Um emissor que a aceitasse deixaria o adotante pintar a própria aprovação. Adotante cujo CI não é Actions fica sem F5 até o commit status entrar (#444) — como já acontece com o portão.

A F3 no trailer do commit — sem credencial na máquina de ninguém (#594)

O ato de construção (TarefaIniciada) nasce de duas linhas no corpo de um commit da entrega, escritas pelo agente quando a skill de construção começa:

Fidera-Skill: subagent-driven-development
Fidera-Tag: feature

A coleta diária já traz o corpo de cada commit, e a derivação lê o par: um ato por entrega e por skill, do commit mais antigo que traz o par completo, no domínio da cascata, com a origem do Co-Authored-By do mesmo commit. @<n> no valor declara a versão; sem ele, vale a do método em vigor, e sem método declarado para a skill, vale 1. Fidera-Tag é kebab-case minúsculo, como feature ou bugfix: uma tag fora disso (Feature, nova feature) não vira ato, só a perda tag-invalida no relatório. Nada a instalar e nenhum token: é o critério de que ato humano que o Fidera lê é ato que o humano já faria (#595).

A entrega construída por agente sem o trailer não vira evento: entra no relatório da derivação como agentDeliveriesWithoutSkill, a falta contada.

Para a mesma tarefa, um canal de F3 só: o trailer, ou o marcador fidera:tarefa-iniciada / o emit. A ref do trailer é <entrega>@<sha12> e não converge com as do marcador e do emit, que convergem entre si; combinar o trailer com um deles conta o ato duas vezes, e o log é append-only: a duplicata fica para sempre. Quem usa o trailer não precisa do marcador para a fila, porque o início da fila é a primeira atribuição da issue OU o marcador, o mais cedo dos dois (seção "A fila antes do trabalho", abaixo). O emit tarefa-iniciada continua sendo o canal de exceção, para quem tem o código fora do que o coletor lê.

Nível C — a via padrão: o marcador na issue (#147)

Antes do emit, prefira o marcador: um comentário estruturado na issue/PR, que a coleta diária colhe sozinha — sem token na máquina de quem trabalha, sem rede no instante do ato, com autor e instante ATESTADOS pela forja, e recuperável (bloco faltando se edita; a próxima coleta pega). É o mesmo vocabulário do emit, em forma de bloco:

fidera:proposta-editada
skill: brainstorming@1
domain: CRM
delivery: 986-ciclo-vida
doc-path: docs/specs/2026-08-26-design.md
diff-pct: 32
ref: abc1234

Regras que importam:

  • Em COMENTÁRIO, nunca no corpo da issue — é o comentário que a forja atesta.
  • Sem actor/origin no bloco: a proveniência deriva do AUTOR do comentário + dos agentActors da política. Proposta comentada por um autor classificado como agente é perda contada, nunca evento — agente não atesta a própria aprovação.
  • ref ausente ⇒ o id do comentário assume (determinístico, contado). delivery ausente ⇒ issue-<n>/pull-<n>.
  • ref presente é escopada pela entrega (#547): o ato grava <delivery>@<ref>, a menos que a ref já comece por <delivery>@. Duas entregas que partem do mesmo commit podem dar o mesmo sha; sem a entrega na ref, a segunda seria duplicate da primeira e sumiria.
  • Usar marcador E emit para o mesmo ato é seguro: a identidade converge (duplicate). O trailer da F3 não converge com nenhum dos dois — a seção dele, acima, explica por quê.
  • Issues antigas contam: --mode all varre a história desde o início (ou desde --history-since), ignorando a marca d'água da coleta anterior — o backfill de atos, de graça (#552: até a 0.6.0 a marca encurtava o all).

Nível C — emitindo atos da esteira (#144)

O mesmo binário emite os atos de esteira que a forja não vê — é o que dá evidência às fases F1/F2 no painel. A F3 é outro caso: o git log que a coleta já lê carrega o commit, e a tag da construção (a variedade que as recomendações citam) nasce, por padrão, do trailer Fidera-Tag dele (a seção do trailer, acima). O emit tarefa-iniciada é só o canal de exceção, para código fora do que o coletor lê. O emit exige um token de escopo EMIT (permanente e estreito: só os três atos abaixo entram; qualquer veredito — terminal, drift, avaliação, concessão — volta invalid, porque o emissor testemunha atos, nunca vereditos).

A --ref é a identidade do ato, e o emit a escopa pela entrega (#547): grava <delivery>@<ref> (aqui, 986-ciclo-vida@abc1234def56), a menos que ela já comece por <delivery>@. Assim o sha do commit basta, mesmo quando duas entregas partem do mesmo commit da main. Use git rev-parse --short=12 HEAD: o --short sem número cresce com o repositório, e o mesmo commit viraria duas refs. A regra vale para os três atos abaixo; o review-concluida grava a --ref como veio, porque converge com a review colhida da forja.

# o humano aprovou a spec editando — o diff é calculado dos dois arquivos (LCS de linhas)
fidera-collector emit proposta-editada   --skill brainstorming --domain CRM --delivery 986-ciclo-vida   --doc-path docs/specs/2026-08-25-design.md --draft rascunho.md --approved aprovado.md   --ref abc1234def56 --actor alexandre   --fidera-url https://api.fidera.tec.br

# aprovou sem editar
fidera-collector emit proposta-aprovada --skill brainstorming --domain CRM   --delivery 986-ciclo-vida --doc-path docs/specs/2026-08-25-design.md   --ref abc1234def56 --actor alexandre --fidera-url ...

# despacho com tag — é a evidência de variedade que as recomendações citam (não é sinal nem cap, #449)
# canal de exceção: só para código fora do que o coletor lê (ver a seção do trailer)
fidera-collector emit tarefa-iniciada --skill subagent-driven-development --domain CRM   --delivery 986-ciclo-vida --tag migration --ref abc1234def56 --actor agente   --fidera-url ...

O veredito de review (#300)

A F4 — revisão adversarial — é fase de agente por definição (norte §3), e desde a #300 o veredito dela entra por dois canais, sem marcador no meio:

Pela forja, de graça. Se o seu portão já posta o bloco ```json com escopo, veredito e findings no comentário do PR, a coleta diária já o transforma em ReviewConcluida — a mesma leitura que decide o terminal, agora também virando fato de F4. Zero mudança de processo: é o nível A fazendo o trabalho.

Pelo emit, quando a review roda fora da forja. O bloco vai verbatim, num arquivo:

fidera-collector emit review-concluida --skill requesting-code-review --domain CRM   --delivery 986-ciclo-vida --ref abc1234 --actor claude   --block review-block.json --fidera-url https://api.fidera.tec.br

Regras que importam:

  • findings é obrigatório, e não pode ser lista inventada: veredito só com contagens é o formato agregado, que decide terminal e não vira evento de fase. A recusa é na entrada.
  • Não existe marcador fidera:review-concluida. Um bloco chave: valor não carrega array de objetos, e deixá-lo emitir findings: [] faria só review limpa ser marcável — viés que o produto recusa. Veredito entra pela forja ou pelo emit.
  • A aprovação do HUMANO continua sendo só do humano: proposta-aprovada postada por autor classificado como agente segue sendo perda contada. O que a #300 abriu foi o veredito das fases que são de agente, não a assinatura do humano.

O veredito da INTENÇÃO (#452) — a F2, no mesmo bloco

escopo aceita três valores, e é ele que diz de que fase o veredito é:

| escopo | fase | o que foi julgado | skill derivada | |---|---|---|---| | INTENCAO | F2 | a proposta: spec, plano, desenho — antes de construir | intent-review | | BRANCH | F4 | a entrega inteira | branch-review | | TASK | F4 | uma tarefa do plano | task-review |

O bloco é o mesmo, pelos mesmos dois canais, e a máquina de findings e de destino (FindingDisposto, #457) serve às duas fases sem cópia.

O veredito de intenção não move portão, e isso é desenho: as contagens do bloco BRANCH decidem o terminal da entrega, e um achado sobre a spec reprovando a entrega mudaria a semântica do portão por efeito colateral. Um REJEITADO de intenção é fato de F2, e nada mais.

Para a fase aparecer no painel, mapeie intent-review=F2 no catálogo de fases da política — até lá a skill é órfã, e o card de declarar é o degrau 1 pedindo exatamente isso.

O destino de cada finding (#457)

Quando a review entrou pelo emit review-concluida, a linha de saída traz o eventId dela (accepted: <eventId>). Quem recebe a review registra o que fez de cada finding citando esse id:

fidera-collector emit finding-disposto --skill receiving-code-review --domain CRM   --delivery 986-ciclo-vida --ref def5678 --actor claude   --review <eventId> --finding 3 --disposition DEFERIDO --reason "fora do escopo da entrega"   --fidera-url https://api.fidera.tec.br
  • --disposition: CORRIGIDO · CONTESTADO · DEFERIDO. --reason é opcional, e o motivo nunca é inventado.
  • É o insumo da regra ruido-de-review@1: a classe de finding (severidade × categoria) que é mais deferida do que corrigida ganha card. CONTESTADO nunca conta como ruído.
  • Review que entrou pela forja ainda não tem esta via: o eventId dela só nasce na derivação, depois do merge. O destino pela forja é trabalho em andamento na #457. A doutrina do invocador. O emit tem timeout curto (5s), UMA tentativa e exit honesto — quem garante o "nunca trava" é a forma de chamar: ... emit ... || true, ou em background. Re-emitir o mesmo ato é seguro (duplicate sai 0 — o eventId é determinístico pelo ato).

Nunca portão. É proibido, por contrato de uso, condicionar merge ou passo de esteira ao exit do emit: ele é telemetria do processo, e um emissor que trava a esteira é o arrasto que este produto existe para não criar. O único portão do sistema é o que o próprio adotante opera no painel.

A versão da skill (#451)

--skill-version é opcional, e o normal é não passar: sem ele, o emit lê o version: do frontmatter de .claude/skills/<skill>/SKILL.md no --repo-root (default: o diretório atual) no instante do ato, e o stream sai <skill>@<versão>|<DOMINIO>. Quem edita a skill bumpa o version: — é a única coisa a lembrar, e ela mora no arquivo que se está editando.

  • A convenção é inteiro positivo (version: 3). 1.2.0, v2 ou 0 não viram número por adivinhação: o ato vai para @1 e o emit diz no stderr qual é o problema. Sem arquivo ou sem o campo, também @1, cada caso com a sua linha.
  • A precedência, do mais perto do fato ao mais longe: --skill-version (ou skill: nome@n no marcador) › o frontmatter lido pelo emit › o método da última coleta anterior ao ato (para o marcador sem versão, na derivação) › 1.
  • Bumpar não zera nível. A versão separa linhagens para medir a eficácia das recomendações; o nível e a histerese vivem no domínio. E o ato já gravado nunca muda de stream: o passado fica na versão em que entrou.
  • O fidera-collector init copia as skills como estão — inclusive o version:.

A fila antes do trabalho (#539)

Em toda corrida que varre a forja (tail e all), o coletor lê os eventos de issue do repositório (/issues/events, do mais novo ao mais antigo, até a marca d'água que o servidor derivou da coleta anterior) e manda, por issue tocada, a intenção registrada (createdAt), o fechamento (closedAt/closedReason, crus) e a primeira atribuição atestada pela forja (firstAssignedAt). PR não entra. É a lista issues do envelope — opcional: coletor anterior a 0.6.0 não a manda e continua válido.

O que o Fidera faz com isso: a fila — intenção registrada → trabalho iniciado — ao lado das leituras de tempo por fase, antes da F1, como espera e não como fase. É tempo de priorização, não de execução: fila longa se resolve decidindo, não acelerando. Não entra em nível nenhum.

  • O início é a primeira atribuição OU o marcador fidera:tarefa-iniciada na issue — o mais cedo dos dois; quem não atribui nem marca sai "sem início observado", com o denominador dito, nunca como fila zero. O board (Projects v2) não serve: a REST lista a mudança de status, mas não diz qual status.
  • Permissão: nenhuma nova — /issues/events cai sob o issues: read do bloco acima.
  • Custo: no tessena, ~3 páginas por coleta diária; a história desde julho custou 217. --history-since limita a varredura inicial, como limita a de merges.
  • Atribuição vista uma vez não se perde: a coleta diária vê só a janela, e o servidor guarda o instante mais cedo que já viu para cada issue.
  • O que ficou antes da janela da primeira corrida entra por --mode all (desde o início, ou desde --history-since): a marca d'água vale no tail, nunca encurta o backfill (#552).

⚠️ Ordem de deploy (spec #539 §7): o envelope recusa chave desconhecida no topo, então um coletor 0.6.0 contra um servidor anterior à #539 tem o envelope inteiro recusado (400). O servidor sobe e é promovido antes de o coletor ser publicado — e é por isso que a versão acima só muda depois da promoção.

O método detectado (#414)

Em toda corrida — tail, all, drift e --dry-run — o coletor lê .claude/skills/<dir>/SKILL.md no --repo-root e manda a lista no envelope, como method: nome (o name: do frontmatter; o diretório é a reserva), versão se houver, caminho. É o que o marcador carimba em skill: e o que o catálogo da Política mapeia — a Política passa a sugerir o catálogo a partir daqui; quem aplica é o humano.

skills: [] é dado ("nenhuma skill no repositório"). Campo ausente é coletor anterior a 0.4.0. Nome fora de [a-z0-9-] não viaja e sai como perda no stderr. Plugins instalados no usuário não estão no checkout e ficam de fora.

A skill que mudou sem versão nova (#467)

Para cada skill, o coletor pergunta ao git a história do SKILL.md. Se o conteúdo mudou depois do commit que fixou o version: em vigor, a skill viaja com changedSinceVersion: o instante da mudança mais recente. Sem version: também conta, porque o @1 implícito se fixa na criação do arquivo. O painel mostra um item de convenção na fila, "N de M skills mudaram sem versão nova", com os nomes. O item some quando alguém faz o bump: a próxima coleta chega sem o campo.

  • A resposta é retroativa. Uma edição de meses atrás aparece já na primeira coleta, porque a resposta vem da história e não de comparar duas coletas.
  • Não conta: mudança só de fim de linha (CRLF × LF), espaço no fim da linha ou linha em branco no fim do arquivo.
  • Precisa da história inteira. Fora de um repositório git, ou com o git falhando, a skill viaja sem o campo e a perda sai no stderr. Renomear a pasta da skill reinicia a história dela.
  • Ordem de deploy: o servidor recusa campo que não conhece na skill e, com ele, o método inteiro. O coletor que manda o campo só é publicado depois do servidor que o aceita.

Canais

  • stdout — com --dry-run, o envelope collector-v1, um documento JSON parseável. É contrato.
  • stderr — o resumo do run real (lotes e contagem por status, emitido mesmo no sucesso), além de avisos, perdas contadas e recusas do servidor. stderr não-vazio não indica falha; o sinal de falha é o código de saída.

Nenhum canal ecoa o FIDERA_INGEST_TOKEN: o log de um run é público em repositório aberto.

Lotes

O envelope sai em lotes que respeitam os dois tetos do portão: até 1000 itens por lista (o maxItems do ingest-v1; acima dele, 400) e até 10.000.000 bytes no corpo (o orçamento do portão; acima dele, 413), contados em UTF-8 e com o envelope de topo dentro da conta. A ordem de cada lista é preservada de lote em lote — os merges vão do mais antigo ao mais novo —, de modo que uma interrupção deixa assentado um prefixo contíguo do histórico.

Até a 0.7.0 o lote era só por contagem: a primeira carga de um repositório com histórico longo (865 merges, 16,9 MB no caso medido) levava 413 e não gravava nada.

Uma observação que sozinha passa do orçamento não cabe em lote nenhum: ela fica fora do push, nomeada no stderr, e o resto entra. O run sai 1 — o portão a recusaria de qualquer jeito, e ela não volta sozinha numa corrida seguinte.

O que "sucesso" significa aqui

O código de saída é 0 só quando o POST foi aceito e o relatório por item não trouxe invalid. Um 200 cujo corpo diz que todas as observações foram recusadas — tenant divergente do token é o caso real — não é sucesso, e sai 1.

Quando nada pôde ser lido, o resumo sai como não verificado, nunca como zero: "não li" não pode ser confundido com "zero inválidos". A completude é medida por item — um relatório com menos itens do que o lote enviado não passa por completo, senão o run afirmaria {"invalid":0} sobre observações de que nada sabe.

Numa carga multi-lote a leitura pode ser parcial: um lote responde relatório legível, outro não. O que foi apurado é piso confirmado, não estimativa — uma recusa contada num lote sai 1 mesmo que o relatório de outro lote nunca tenha chegado, e o resumo sai como ao menos {…} (relatório incompleto). O que falta é o total, nunca o piso. O contrário — absolver o run porque um lote veio ilegível — era o furo da issue #47.

Segue daí uma assimetria que vale conhecer: com uma carga toda inválida, o run sai 1 se a recusa cair num lote lido, e 0 se o único lote lido não a contiver. O piso é honesto sobre o que mediu; ele não adivinha o que não leu.

Não ler o relatório, sozinho, não é motivo de falha: o POST foi aceito, a retomada cobre o resto e o run sai 0. O sinal de falha é a recusa, não a ignorância.

Se o próprio POST falha, o push é interrompido — e o que já assentou não morre com o erro. Os dois modos de morte do transporte estão cobertos: o status de erro (5xx, limite) e a rede caindo (ECONNRESET, DNS, timeout do runner), que é o caso mais comum. O run sai 1 e relata os lotes concluídos, o piso apurado e as recusas que já conhecia — e quando nada foi apurado diz não verificado, nunca ao menos zero, pela mesma razão de sempre: zero é uma afirmação.

(push interrompido) distingue esse caso do (relatório incompleto), que é o push que terminou sem conseguir ler tudo. Re-rodar é seguro: a ingestão é idempotente e o GET :estado retoma do último assentado.

Perdas

Toda perda é contada e sai por stderr — linha de merge não parseada, range sem commits, commit não parseado, skill com nome fora do alfabeto do stream, SKILL.md ilegível (#414), história do SKILL.md ilegível no git (#467). Um corpus com buraco não pode ser indistinguível de um corpus completo.