Skip to main content

Artifacts

An artifact is the structured and validated output of an agent. Concretely: an artifact.<type> event in the log, with the payload validated against a closed schema before being written.

There is no artifact table. Validation happens in one place only — Engine.Harness.ArtifactSchemas — and a missing required field fails the emission, instead of writing a half-formed artifact.

Who can emit what​

This is the most important distinction on this page, and it's easy to miss:

originmeaning
toolthe model decides to emit, calling emit_artifact
serverthe code emits when the outcome happens; the model doesn't choose

Only three types are emittable by tool: note, business_rule and decision_record. Everything else is emitted by the server.

The reason is the same in every case: when the artifact is the record of an outcome, letting the model choose whether to emit it means letting it omit it. A DevAgent that gives up shouldn't be able to decide not to record that it gave up.

The session type filters no artifact. RN-097 gave the session a kind (consultiva | criativa) and put a guard on event append, but it reaches one type — execution.activated — and no artifact is it. A consultative session records artifact.business_rule all the same: what it doesn't do is enter execution. Whoever arrives at the type rule tends to assume the opposite, and the wrong assumption here would erase the record of an entire conversation.

The session STATE filters by actor. Since RN-581 a closed/closed_abnormally session refuses conversation with a 409, and "conversation" includes any event whose actor is a conversational agent — so an artifact.* from the Creative agent, the PO, the Architect, the Dev Lead, the UX Designer, the Staff or the Infra Lead no longer lands in a closed session, while the same artifact types emitted by the Psychologist, which runs on the closed session, still do. The type never decides; the actor does.

The schemas​

note — tool​

fieldrequired
title✅
body✅

business_rule — tool​

fieldrequired
title✅
description✅
origin✅ — non-empty list

origin references the conversation events that originated the rule. An empty list fails: it is the traceability of the rule back to where it was stated, and without it the rule becomes an assertion with no provenance.

:::caution The model needs to KNOW the field names The description of the emit_artifact tool is generated from these schemas (ArtifactSchemas.required/1) and names each required field, in English, with a filled-in example.

This isn't fussiness: the previous description simply said "emits a typed artifact" and listed the types. In a real execution the model — conversing in Portuguese — guessed titulo/descricao/comportamento, and the four business rules from that conversation were silently rejected. origin as free text also fails: it has to be a list, and the description says so in plain terms (RN-061). :::

decision_record — tool​

fieldrequired
context✅
options✅ — list
choice✅
consequences✅

The "summarized ADR" any of the six conversational agents can emit — the Creative, PO, Architect, Dev Lead, UX Designer and Staff all carry emit_artifact with this type (RN-505). No cross-field validation on purpose: choice doesn't have to match one of the options verbatim — free text is more robust, and the generic schema only checks key presence, the same rule as note/business_rule.

It reuses the generic pattern instead of the dedicated one (artifact.project_image/artifact.c4_diagram, versioned, own TS schema, own HTTP route): a decision is append-only by nature, not a "current" value that gets replaced. It is deliberately distinct from open_adr_pr (Architect-only, commits a real file under docs/adr/*.md and opens a real PR with mandatory human approval) — the two coexist for different scales of decision: decision_record is frictionless, searchable in the session log, for any relevant decision; open_adr_pr is for when the decision is big enough to become a real repository document.

open_question — own tool​

fieldrequired
questionId✅ — uuid, generated by the tool
pergunta✅ — up to 500 characters
afeta✅ — {regras, historias, tarefas}, lists of ids
sugestaooptional — up to 300 characters

A doubt an agent leaves for the user (ADR 0214, RN-862). It is not emittable by emit_artifact: only the Creative, PO, Architect and Dev Lead emit it, through their own declare_open_question tool, which also records autor and sessionId. The api closes it with open_question.answered.

product_brief — server​

fieldrequired
title✅
summary✅
rules✅

Validatable, but not emittable by tool. The Creative server emits it only after you confirm readiness — never through a model tool call.

Since ADR 0185 the click that asks for it — "I'm ready — the need is validated" — also records necessity.validated with productBriefId: null: the brief is the next product_brief after that readiness.confirmed, and the handoff that carries it to the PO is accepted on behalf of whoever clicked (RN-657, RN-658).

task_blocked — server​

fieldrequired
taskId✅
agentId✅
reason✅
diagnosis✅

Emitted when the DevAgent gives up on the task: a suite that doesn't close, an iteration ceiling, a blown budget, or a stall without signaling. It is the record of the outcome.

qa_verdict and secops_verdict — server​

fieldrequired
veredito✅ — approved, changes_requested or needs_user_decision (RN-862), nothing else
resumo✅
itens✅
taskId or prActionId✅ — exactly one
coverageMatrixoptional, QA only

Three extra validations live here:

The verdict is closed to the same two values as the api's gate state machine. A value outside that would make the use case blow up further down the line — rejecting the artifact at emission is the right place to fail.

The subject is exclusive. A verdict is about one dev task (taskId) or about one infra PR (prActionId) — never both, never neither. Same artifact type, different consumers.

coverageMatrix is optional on purpose. A QA verdict without a coverage matrix is still a valid verdict; losing it entirely because of one missing field would be worse than recording it incomplete.

SecOps is deterministic — it has no LLM. The QA verdict is born from the emit_qa_verdict tool, which is enforced separately. In neither case does the model choose to emit.

infra_delegation_files — server​

fieldrequired
files✅ — non-empty list
summary✅

Result of ONE delegate from the Infra area (Phase 8c, ADR 0038) — the lead itself (Dockerfiles/compose) or the Workflows subagent (CI pipeline). The InfraLeadServer emits this after each delegate finishes, just to have a parecer_artifact_id to reference in the delegations table — never seen from outside the area. What the api sees is the consolidated PR, via open_infra_pr (the same mechanism as always, untouched by Phase 8c).

An empty files fails for the same reason as nada_a_validar/1 in InfraGateRunner (ADR 0021): a delegate with no files at all didn't finish anything, and "empty" should never pass as "done".

prototipo_navegavel — tool (propose_prototype, not emit_artifact)​

fieldrequired
personas✅ — non-empty list
jornadas✅ — non-empty list
prototipo✅ — telas non-empty
resumo✅

The prototype the UX Designer produces (ADR 0087), from the Creative's necessidade-de-negocio. It is not note/business_rule — the UX Designer emits via propose_prototype, a DEDICATED tool (Engine.Agents.UxDesignerTools), and this module only validates the SHAPE, the same mechanism as product_brief: validatable here without being on the list of types emittable via emit_artifact.

Empty personas/jornadas, or a prototipo with no screen at all, fail by the same rule as business_rule.origin (the business_rule table above): an artifact with no content isn't worth recording. A successful propose_prototype ends the turn (the same reasoning as propose_execution_plan/propose_infra_pr) — no proposed_action, no suspension: proposing a prototype has no external effect.

plano_de_teste — server​

fieldrequired
storyId✅
taskId✅ — since ADR 0192
planoDeTeste✅
criteriosExecutaveis✅ — non-empty list
estrategiaDeAutomacao✅

The deliverable of QA-strategy (ADR 0090; docs/fluxo.yml, role qa-estrategia, the qa-lead's second moment). Since ADR 0192 (RN-674) it is the test plan for ONE task's DELIVERY, written AFTER the dev agent delivered, over the dev's worktree and the files the delivery touched: it is the first step of the qa-verificada cycle (QaLeadServer.run_area/3), consumed as INPUT by the Automation subspecialty, and found again by taskId on the correction round. Until ADR 0192 it was emitted PRE-DEV and fed the implementavel gate. It is born from emit_plano_de_teste, but the model doesn't emit the artifact directly — Engine.Gates.QaEstrategiaAgent extracts the result from the tool call and calls ArtifactEmitter.emit/5, the same pattern as qa_verdict/task_blocked.

An empty criteriosExecutaveis fails for the same reason as infra_delegation_files: a plan with no criteria at all isn't a plan.

threat_model — server​

fieldrequired
storyId✅
threatModel✅
requisitosDeSeguranca✅

SecOps's "second moment" (RN-360, ADR 0090) — a STRIDE-lite checklist over the DESIGN of a story, before any code exists, the same second-moment pattern as qa-estrategia. Engine.Gates.SecOpsAgentServer.run_design/2 emits after Engine.Gates.AppSecAgent.run/3 (no Terminal, no Diff/Scanner/ DevAgentState) finishes the loop with emit_threat_model — the model doesn't choose to emit, the server emits when the loop ends.

riscos is left out of the required fields: an empty list is a valid answer (not every story carries residual risk) and the tool already guarantees the KEY exists — there's no "forgotten" to distinguish from "none".

The artifact is always recorded, but the HANDOFFS that carry it are not: since RN-636 AppSec offers it only to a target (arquiteto, dev-lead, infra) that has no pending offer and is not active in the project. The threat model of a second story stays in the log without an offer of its own — the first offer, still pending, is not replaced (ADR 0182).

Artifacts that don't go through here​

Some artifact.* event types exist in the log without being in this registry, because they are emitted by the api, not by the engine:

eventorigin
artifact.module_mapthe Architect, via a use case in the api — each module may carry resources (cpus, memoryMb, pidsLimit), and the Infra starts the container with their SUM (ADR 0199, RN-683)
artifact.insightanalysis, via a use case in the api
artifact.project_imagethe Architect, via a use case in the api (ADR 0065, RN-105)
artifact.module_routingthe Architect, via a use case in the api — one candidate image per module of the current module_map; the Architect CANDIDATES, Infra ELECTS in a later step (ADR 0131, RN-487)
artifact.module_contractsthe Architect, via a use case in the api — per module of the current module_map, what it EXPOSES; read by dev agents through listar_contratos_de_modulos instead of another module's worktree (ADR 0200, RN-684)

They have their own validations in the api's domain. The asymmetry is historical, and is noted as technical debt: ideally every artifact.* would go through a single registry.

When validation fails​

errorcause
{:unknown_type, tipo}type outside the twelve
{:missing_keys, [...]}required fields missing, all named
:origem_invalidabusiness_rule.origin empty or not a list
{:sujeito_invalido, chaves}verdict with both subjects, or with neither
{:veredito_invalido, valor}verdict outside approved / changes_requested / needs_user_decision
:invalid_payloadpayload is not a map

The failure goes back to the agent as the tool result. A model that emits a malformed artifact receives the error and can correct it — within the ToolLoop's iteration ceiling.