/** * Artifact Registry — artefatos como objetos rastreáveis, não só validados * na hora e descartados. Complementa contracts/artifacts.ts (que valida * schema/conteúdo de UM artefato) com um índice persistido: quem criou, * de que run, com que hash, dependendo de quais outros artefatos, e em que * versão (replan/retry pode reproduzir o mesmo nome mais de uma vez). * * Responde: "quem criou / quem consumiu / qual decisão gerou / qual * avaliação validou" — sem isso, um artefato vive só como Map efêmero * dentro de ExecuteCtx e desaparece ao fim do run. */ export interface ArtifactProducer { runId: string; nodeId: string; agent?: string; skill?: string; } export interface ArtifactRecord { /** `${runId}:${nodeId}` — único por (run, nó); versionado quando reproduzido (replan/retry). */ id: string; kind: string; name: string; version: number; producer: ArtifactProducer; createdAt: string; hash: string; size: number; valid: boolean; score: number; /** Ids (`runId:nodeId`) dos artefatos dos quais este depende. */ dependencies: string[]; /** * Caminho RELATIVO a `.izanagi/state/` do conteúdo persistido, quando o * content store está ligado. Ausente = só metadado (comportamento anterior, * e o que acontece quando o conteúdo excede o teto). */ contentRef?: string; /** Tamanho original antes de qualquer truncamento no content store. */ originalSize?: number; /** true quando o conteúdo gravado foi cortado para caber no teto. */ truncated?: boolean; /** * Checksum COMPLETO do conteúdo (sha256 hex). * * `hash` continua sendo sha1 truncado em 12 hex (48 bits) porque é o que os * registros gravados carregam e o que a detecção de duplicação usa. 48 bits * são suficientes para "é o mesmo artefato de novo?" num run; não são para * "este arquivo é exatamente o que eu gravei", que é a pergunta de um * checksum. Ausente em registro escrito por versão anterior. */ checksum?: string; /** * Metadado livre de quem produziu o artefato. * * Existe porque todo campo do registro é previsto por este arquivo, e quem * produz um artefato não tinha onde anexar contexto que o registro não * previsse. Tem teto (`MAX_METADATA_BYTES`) e é recusado inteiro quando * estoura: metadado é para contexto, e um campo livre sem teto vira o * segundo content store, sem nenhuma das garantias do primeiro. */ metadata?: Record; /** * Chave de REUSO: identifica os insumos que produziram este artefato. * * Dois nós com a mesma chave receberam exatamente a mesma pergunta, no mesmo * estado de projeto, com os mesmos insumos a montante. Ver `reuseKey()` para * o que entra e, principalmente, para a política de invalidação — sem ela * isto vira um cache que devolve resposta velha com cara de nova, que é pior * que não ter cache nenhum. */ reuseKey?: string; } /** * Teto de conteúdo gravado por artefato. Um artefato maior é truncado com * marca explícita: o registro declara `truncated: true` e `originalSize`, de * modo que ninguém leia um conteúdo cortado achando que é o inteiro. */ export declare const DEFAULT_MAX_CONTENT_BYTES: number; /** * Teto do metadado livre por artefato. Pequeno de propósito: o índice inteiro * é lido e reescrito a cada registro, então metadado grande custa em TODO * registro seguinte, não só no seu. */ export declare const MAX_METADATA_BYTES: number; export declare class ArtifactRegistry { private readonly file; private readonly contentDir; private readonly stateDir; private readonly maxContentBytes; private readonly persistContent; private records; /** * `persistContent` (default true) grava o CONTEÚDO do artefato em disco, não * só o metadado. Sem isso, o conteúdo vive apenas no Map efêmero do * ExecuteCtx e morre com o processo: `izanagi explain` não consegue mostrar * o que foi produzido e não existe reuso de artefato entre runs. */ constructor(opts: { baseDir: string; persistContent?: boolean; maxContentBytes?: number; }); private load; save(): void; /** * Registra um artefato produzido. Se já existe um registro com o mesmo * `runId`+`nodeId` (retry/replan reprocessando o mesmo nó), incrementa a * versão em vez de duplicar o id. */ register(input: { kind: string; name: string; producer: ArtifactProducer; hash: string; size: number; valid: boolean; score: number; dependencies?: string[]; /** Conteúdo produzido. Quando presente e o content store está ligado, é gravado em disco. */ content?: unknown; /** Metadado livre do produtor. Recusado inteiro acima de `MAX_METADATA_BYTES`. */ metadata?: Record; /** Chave de reuso (`reuseKey`). Ausente: o artefato não é reutilizável. */ reuseKey?: string; }): ArtifactRecord; /** * Grava o conteúdo dentro de `.izanagi/state/artifacts//`. O nome do * arquivo é derivado de nodeId+version e SANEADO: um nodeId vindo de uma * decomposição externa não pode escrever fora dessa pasta. */ private writeContent; /** * Lê o conteúdo persistido de um artefato. `null` quando o registro não tem * contentRef (content store desligado, conteúdo grande demais, ou registro * gravado por uma versão anterior do framework). */ readContent(id: string, version?: number): string | null; /** Remove o conteúdo persistido de um run inteiro (o metadado permanece). */ purgeContent(runId: string): number; get(id: string): ArtifactRecord | undefined; /** Todas as versões de um artefato (histórico de replan/retry). */ history(id: string): ArtifactRecord[]; /** Artefatos produzidos por um run, na ordem em que foram registrados. */ forRun(runId: string): ArtifactRecord[]; /** * Artefato de um run ANTERIOR que respondeu exatamente à mesma pergunta. * * Três condições, e cada uma é uma parte da política de invalidação: * * - `reuseKey` idêntica: mesmos insumos, mesmo estado de projeto declarado, * mesmo contrato (ver `reuseKey()`); * - o artefato foi VÁLIDO. Reaproveitar o que não passou na validação * economizaria a chamada e importaria o defeito; * - dentro do prazo. Um artefato correto há seis meses descreve um projeto * que provavelmente não existe mais, e a chave não tem como perceber isso * sozinha: o prazo é o que impede o cache de envelhecer em silêncio. * * Devolve o registro MAIS RECENTE que satisfaz as três, com o conteúdo já * lido — sem conteúdo em disco não há reuso, só metadado. */ findReusable(key: string, opts: { maxAgeMs: number; now?: number; }): { record: ArtifactRecord; content: string; } | null; /** Quem consome (depende de) um artefato — rastreabilidade a jusante. */ consumers(id: string): ArtifactRecord[]; /** * Linhagem COMPLETA de um artefato: tudo que entrou nele e tudo que saiu * dele, atravessando o grafo até o fim. * * `dependencies` e `consumers` respondem um salto: "de quem este depende" e * "quem depende deste". Um salto responde "de onde veio isto?" apenas quando * a cadeia tem tamanho um, e num grafo de sete nós ela nunca tem. As arestas * já estavam gravadas desde sempre; o que faltava era percorrê-las. * * Travessia em largura com marca de visitado: um ciclo (que o * `ExecutionGraphBuilder` recusa no plano, mas que um registro escrito à mão * ou uma decomposição externa pode produzir) termina em vez de girar. * * A ordem é por distância: os primeiros da lista são os vizinhos diretos. */ lineage(id: string): { ancestors: ArtifactRecord[]; descendants: ArtifactRecord[]; }; private walk; /** * Compara duas versões de um artefato pelo CONTEÚDO, não só pelo score. * * `detectRegression` responde "piorou?" com dois números. Esta responde "o * que mudou?", que é a pergunta de quem vai decidir o que fazer com a * regressão. Sem conteúdo persistido nas duas pontas, `changed` fica * `undefined`: "não deu para comparar" nunca vira "não mudou". * * O diff é por LINHA e conta, não reconstrói o texto: um diff completo dentro * do índice seria o content store de novo, com outro nome. */ compare(id: string, versionA: number, versionB: number): { a?: ArtifactRecord; b?: ArtifactRecord; scoreDelta?: number; sizeDelta?: number; /** Conteúdo idêntico byte a byte, por checksum. `undefined` sem checksum nos dois. */ identical?: boolean; /** Linhas acrescentadas e removidas. `undefined` sem conteúdo nos dois. */ changed?: { added: number; removed: number; }; }; /** * Regression Protection — compara a última versão registrada de um artefato * com a anterior (replan/retry após healing). Regressão = versão nova * inválida onde a anterior era válida, ou queda crítica de score (>= 0.3) * numa versão anterior que já era válida. Sem histórico anterior, nunca há * regressão (primeira versão não tem baseline pra comparar). */ detectRegression(id: string): { regressed: boolean; previousScore?: number; currentScore?: number; }; } /** * Prazo padrão de reuso de artefato entre runs. * * Sete dias. O número é uma escolha, e o motivo dela é que a chave de reuso * NÃO consegue enxergar tudo que importa: ela cobre o contrato, os insumos a * montante e o levantamento do projeto quando existe, e não cobre o que mudou * no mundo fora disso (uma dependência atualizada, um requisito que virou * outro). O prazo é o único mecanismo que expira o que a chave não vê. */ export declare const DEFAULT_REUSE_MAX_AGE_MS: number; /** * Chave de reuso de um artefato: o que precisa ser idêntico para a resposta * anterior ainda ser a resposta. * * Entra tudo que muda a PERGUNTA: * * kind : o tipo de artefato pedido * objective : o objetivo do contrato daquela tarefa * constraints : as restrições, que mudam o que é aceitável * acceptance : os critérios pelos quais a saída será cobrada * agent/role : quem responde, porque a resposta depende de quem responde * upstream : os checksums dos artefatos consumidos, EM ORDEM * project : impressão do projeto (checksum do survey), quando houve * * O que NÃO entra: o runId, o horário, o modelo escolhido. Os dois primeiros * fariam toda chave ser única e o reuso nunca aconteceria; o modelo fica de * fora porque a pergunta é a mesma, e trocar de modelo não invalida uma * resposta que passou pela mesma verificação. * * O que a chave NÃO consegue ver está coberto pelo prazo * (`DEFAULT_REUSE_MAX_AGE_MS`), e um run sem survey não declara estado de * projeto nenhum: por isso o campo entra como `sem-survey`, que é uma chave * DIFERENTE de qualquer run que tenha levantado o projeto. Reaproveitar entre * os dois seria assumir que o projeto não importava. */ export declare function reuseKey(input: { kind: string; objective: string; constraints: string[]; acceptance: string[]; agent?: string; role?: string; upstreamChecksums: string[]; projectFingerprint?: string; }): string; //# sourceMappingURL=registry.d.ts.map