Skip to main content
Translation from 2026-10-16; the original has changed since.

Events

Three name families coexist in Brabo, and they're easy to confuse because all of them use subject.verb:

familywhere it livesdurability
Domain eventrow in session_eventspermanent and immutable
BroadcastPhoenix channel → browserephemeral, not persisted
SpanOpenTelemetry traceTempo retention (24 h locally)

Only the first is a contract. The other two are transport and observation.

Domain event​

A row in session_events, append-only, with a seq that's dense per session (RN-002). Columns:

columnwhat
iduuid
session_idthe owning session
seqorder within the session, starting at 1, no gaps
typethe identifier below
actorwho caused it: {kind, id} — user, agent or system
payloadjsonb, format specific to each type
created_at—

type is a free-form string in the code — there's no TypeScript union or database enum listing the valid types. The list below was extracted from the emission points, which is why it can go stale without breaking anything. See the note at the end of this page.

Session​

typewhen
session.createdsession is born in created
session.activatedmoved to active
session.drainingthe node hosting it is draining
session.closednormal termination
session.closed_abnormallytermination with a cause — node_shutdown is the most common
session.reopeneda closed/closed_abnormally session went back to active (ADR 0183)

A terminal session no longer takes conversation (RN-581). Once a session is closed or closed_abnormally, appending a conversation event is refused with 409 and reason: "sessao_encerrada". Conversation is either a type that only exists as conversation (chat.message, chat.message_cancelled, chat.structured_question, chat.structured_question_answered, agent.status, agent.activated, handoff.offered, handoff.accepted, readiness.confirmed, necessity.validated, architecture.readiness_confirmed) or any event whose actor is a conversational agent — so tool.call/agent.response from the Creative agent are refused while the same types from the Psychologist, which runs on the closed session, still come in. Human decisions on a pending action (action.approved/action.denied) also still come in: the action queue outlives the session. A session closed because the conversation sat idle past its ceiling carries termination_reason: "conversation_idle_timeout" and ends closed, like heartbeat_timeout.

A closed session can be reopened (RN-649, ADR 0183). POST /projects/:projectId/sessions/:sessionId/reopen (developer, ADR 0184) moves a closed/closed_abnormally session back to active and appends session.reopened with {from, to, closedAt, terminationReason} — the only record of the interval that was closed, since closed_at and termination_reason are cleared on the row. No earlier event is edited, and the session's kind does not change. A session that already has execution.activated is refused with 409 sessao_com_execucao.

Chat and agents​

typewhen
chat.messagemessage in the session thread, from the user or the agent
chat.message_queuedthe message arrived while the agent was mid-turn and joined its QUEUE instead of being refused (RN-673, ADR 0191). Actor: the agent. Payload: mensagemId (the chat.message id), texto, idioma and posicao. The text travels here because it is what the queue re-reads after an engine restart. A queued message is PENDING until a chat.message_delivered or chat.message_cancelled names it; at most 10 per agent and session
chat.message_deliveredthe turn that reads the queue started: the pending messages are read TOGETHER, in order, in one turn. Actor: the agent. Payload: mensagemIds
chat.message_cancelledthe person who SENT a pending message cancelled it — no agent ever read it. Actor: that user. Payload: mensagemId, agente. Recorded by the engine; refused in a closed session (RN-581)
chat.structured_questionthe Creative agent asked for SEVERAL answers at once, via a form — ask_structured_questions tool (RN-162). Each question carries id, label, type, options and allowOther — the latter is the select's free-text output, and defaults to true when the model declares nothing (RN-171)
chat.structured_question_answeredthe user answered the form; the answers also come back as chat.message for the agent to read
agent.activatedan agent took on work in the session
agent.responsethe agent's complete, consolidated response. modelName says WHICH model generated it, across the three producers (the five conversational agents, the ToolLoop of every execution/gate agent, and the api chat with no active agent) — null when the turn failed before resolving the binding, and absent in events recorded before the rule existed (RN-175). From a conversational agent, content is only the turn's CLOSING text and passos (absent when there is none) lists, in order, the text of the earlier rounds, which the thread folds under "Turn steps" (RN-698, AT-395)
agent.erroragent failure, with origem (infra/modelo/codigo/politica) and the mensagem it states in the thread (RN-059). Also emitted with reason: turno_interrompido_por_reinicio (origem infra) when the engine restarted mid-turn and the orphaned working status is closed on boot or agent start, followed by agent.status: idle (RN-586). Covers the whole turn as well as the failure of a SINGLE tool mid-loop, with tool and retentativa in the payload (RN-163)
tool_router.decidedthe step where the Jev chose the tool (RN-625, ADR 0179): menuAntes/menuDepois, escolha, confianca, segunda, anterior, aplicado (the menu came out smaller than the catalog), motivoDaQueda + origemDaQueda (infra/modelo/codigo) when the Jev fell, latenciaMs, custoMicros (the REAL usage.cost) and foraDoCardapio (tools the model called that were not offered). Written by the engine facade for every agent; absent when the router was not consulted (provider other than OpenRouter, fewer than two tools, workspace switch off). The measure of the gain (AT-239) is read from this event and from token_usage, never annotated
tool.resultresult of a tool execution, recorded by the Engine.Harness.Hooks.EventLog hook and, for the seven conversational agents (the Infra Lead since RN-617), by their servers with tool, ok and resultado (or erro), cut at 2,000 characters with resultadoTotal when it cuts (RN-589). The same payload comes from the Infra Lead for every tool it dispatches inline, and from the Dev Lead when a suspended call is settled — never while it waits for approval (RN-593)
handoff.offeredone agent offered the work to another
handoff.acceptedthe recipient accepted. Usually by a person (actor user). When the acceptance was implicit in the "I'm ready" click (RN-658), the actor is still that person and the payload — and that of the agent.activated that follows — also carries implicito: { via: "readiness.confirmed", readinessEventId }. Since RN-660 (ADR 0186) the PO's offer to the Architect can be accepted by the system actor handoff-auto-accept, and then the payload also carries automatico: true, emNomeDe (who opened the session — the repository is provisioned on their behalf) and criterio (regras, cobertas, repositorio)
handoff.auto_accept_failedthe system decided to accept the PO's offer to the Architect (RN-660) and the accept failed. Payload: handoffId, toAgent, origem (infra) and error. Actor system handoff-auto-accept. The offer keeps its button when it was not marked accepted
handoff.supersededan offered handoff stopped being the current one and can no longer be accepted (RN-635, ADR 0182). Payload: handoffId, toAgent, motivo (agente_ativado — the target was activated by any path; nova_oferta — a newer offer to the same target in the project replaced it) and substitutaId (the offer that replaced it, null for agente_ativado). Recorded by the system actor handoff-lifecycle in the session of the OLD offer, which may be closed — it is bookkeeping, not conversation, so RN-581 lets it in. The row's status becomes superseded; the event is the history
context.compactedthe context manager summarized the oldest turns of an agent's history to fit its window. Since RN-580 the payload carries, besides tokensBefore/tokensAfter, the summary that replaced those turns, the agent whose history it was and messagesSummarized. Events recorded before that carry only the two counts — the summary is gone, and rehydration says so instead of inventing one

What a conversational agent reads back (RN-580). When one of the seven conversational agents comes up over a session that already has a conversation, Engine.Agents.Reidratacao rebuilds its history from the tail of this log (the last 200 events): chat.message, agent.response, chat.structured_question (as the agent's own turn, with labels and options), and its OWN tool.call/tool.result (as a text note — the events carry no call id, so they are never replayed as role: tool). chat.structured_question_answered is deliberately skipped: the api records the same answers as a chat.message right after it, and that is what the agent read live. A chat.message that was cancelled in the queue, or that is still pending for this agent, is skipped too (RN-673): the first was never said, the second arrives through the turn that reads the queue. Since RN-589 all seven record tool.result with the text the tool returned (resultado, cut at 2,000 characters, with resultadoTotal giving the real length when it cuts); a call from a session recorded before that has no tool.result and the note says the log has no outcome. When the conversation is longer than the tail, the history opens with a system message stating how many earlier events were left out, the latest recorded compaction summary, and the opening messages.

Proposed actions​

typewhen
proposed_action.createdthe action was born — before any execution. payload.status says how it was born (pending, auto_approved or denied), and that's what distinguishes a human decision from policy (RN-049). payload.reason says WHICH policy rule decided — the string decide() returns, on all three outcomes; absent on events recorded before the rule existed, which means "not recorded", never "no reason". The outbox row of the same name does NOT carry it (RN-567). On terminal actions — the only type whose decision consults the path scope — payload.scopeRoot says WHICH root the scope compared against, as { executionMode, ancora, segmento }: container → ancora: raiz_gerenciada and the workspace_dir_name; mounted → ancora: base_de_projetos and the segment under BRABO_PROJECTS_BASE (ancora: indisponivel, segmento: null when the folder is outside the base or there is none); runner → ancora: nome_da_pasta and the workspace_dir_name. NEVER the absolute path, which would expose the user's $HOME to every member. Absent on older events and on other action types; not in the outbox either (RN-609)
proposed_action.approveddecided by the user — actor is whoever clicked. Auto-approval does NOT go through here: it shows up in created with status: auto_approved and an agent actor
proposed_action.denieddenied — terminal state. actor is whoever refused it, and payload.reason is the reason
proposed_action.executedexecuted successfully
action.failedexecuted and failed
permission.grantedgrant recorded in the policy

Backlog​

typewhen
backlog.epic_created—
backlog.story_created—
backlog.story_transitionedstory state change. actor says who promoted it: agent/po in auto mode, user in manual mode (RN-048)
backlog.story_completedthe PO completed an existing draft story with complete_story — rules linked, RF/DoD/DoR filled — instead of re-creating it (RN-720)
backlog.story_promotion_proposedthe PO finished a COMPLETE story in a project running manual mode: it stays draft awaiting the user's decision, and none of its tasks is claimable until then (RN-048)
backlog.story_promotion_returnedthe user REFUSED to promote it and returned the story to the PO with a reason — which becomes a pinned message in its session, the same as a gate being returned to a dev (RN-048)
backlog.story_updatedthe title and/or description of a draft story with no task in execution was corrected, by the user (Backlog tab) or the PO (update_story). Carries previousTitle, title and descriptionChanged (RN-727)
backlog.story_archiveda draft story with no task in execution was ARCHIVED, by the user or the PO (archive_story): it and its tasks leave the backlog, coverage, plan and claim; the row stays. Carries reason and taskIds (RN-727, ADR 0212)
backlog.story_demoteda module disappeared from module_map; the story went back to draft (RN-012)
backlog.story_overlap_warnedthe story was created, but ALL the rules it cites were already covered by another one — a warning, not a block: the user is the one who judges whether it's overlap (RN-081)
backlog.semantic_duplicate_warneda story or business rule was written whose TITLE is, by embedding, at or above the threshold of an existing one of the same kind in the project — a warning, never a refusal; the payload names the similar item and the cosine. Actor system/duplicata-semantica (RN-681)
backlog.semantic_duplicate_check_skippedthe semantic duplicate check could NOT run — no embedding provider, daemon down, the 10 s ceiling — and the reason says which; the item was written anyway (RN-681)
backlog.story_modules_assignedstory ↔ module link
backlog.task_created—
backlog.task_claimeda dev agent claimed the task
backlog.task_status_changed—
backlog.task_merge_conflictthe approved merge of the task's PR was refused by a merge conflict: taskId, pullRequestId, conflictingFiles, actionId. Since RN-715 the task goes back to in_progress and the owner dev agent is woken through the task.merge_conflict outbox row (RN-705)
backlog.task_blockedfix-attempt cap exhausted, or an impediment was recorded
backlog.task_unblocked—
backlog.epic_without_storythe PO ended a turn having created an epic and NO story for it. An epic doesn't generate a task — a story does —, so this is the state that stalls execution with no visible error. An explicit outcome in the RN-059 pattern: a durable event with origem, epicIds/epicTitles and the message, plus the agent.error broadcast. Reported ONCE per occurrence, never in a loop (RN-165)
backlog.handoff_not_offeredthe PO ended a turn with every business rule covered but did not get the handoff to the Architect offered: some story has no task (origem: modelo, with storyIds/storyTitles), or reading the backlog or creating the offer failed (origem: infra). With every story tasked, the PO server offers the handoff itself instead (RN-700)

Execution and gates​

typewhen
execution.plan_proposedDISCONTINUED since ADR 0086 (RN-284) — older sessions may still have this type in the log; new sessions use a proposed_action of type propose_execution_plan (see docs/reference/permissions.md), because the Dev Lead's plan became a real decision, approved or refused in Approvals, no longer a plain event
execution.activatedthe execution phase began. Only enters on a criativa session — on a consultiva one the append responds 409 (RN-097). It's still this event, not the sessions.kind column, that says whether a session IS executing
execution.parallelization_suggestedthe system proposed parallelizing — emitted at activation only when the project has a registered running container (otherwise the dev agents are blocked, dev.blocked_by_container, and more agents would only multiply the blocked ones; AT-104)
execution.plan_appliedthe human APPROVED the Dev Lead's propose_execution_plan (or it was auto-approved) and that approval ACTIVATED execution: each task in the plan got its module and ActivateExecutionUseCase ran (RN-677, RN-678). Payload: actionId, sessaoDeExecucao, modulos, tarefasAtribuidas, and — when the plan carries tarefas — tarefasPorModulo and modulosSemTarefa (modules left without an agent, RN-709). Recorded on the Dev Lead's session, actor system/action-executor
execution.plan_failedthe approved plan did NOT activate execution — the plan no longer matches the current module_map, or the activation refused (e.g. 409 without a repository). Payload: the same fields plus motivo, the api's own sentence. The human's decision stays recorded; nothing was written to the tasks when the plan itself was refused
execution.parallelization_acceptedaccepted — the subagent inherits the base agent's cap
dev.startedthe dev agent began the cycle (activation, parallelization — NOT rehydration, which never re-fires)
dev.workingclaimed a task and set up the worktree
dev.idlethe module's queue is empty — no task claimable right now
dev.blocked_by_containerthe agent did NOT claim, because the project has no container REGISTERED running (RN-502). It stops in :idle like the two above — idle is the only state a wake still rescues — and this event is the only thing that tells "no container" apart from "empty queue" (dev.idle) and "the claim failed" (dev.error). The api never gets called: the guard runs BEFORE claim_task/1, so no task is ever left claimed without a live owner. It resolves on its own: container.running on the outbox wakes every :idle agent of the project
dev.awaiting_approvalthe git actions became pending approval (Phase 12e): the gate does NOT open — with no PR there's nothing to judge — and the worktree stays held until task.pr_settled (RN-050)
dev.awaiting_gatePR open, waiting on the gate (Phase 12b) — task_id/worktree remain held
dev.blockedthe task was returned with a diagnosis (iteration limit, budget exceeded, report_blocked, worktree/context failure). Since RN-743 it carries trabalhoPreservado ({branch, commit} or null): the uncommitted work was committed on the task branch as <agent>[bot], and the agent's next task starts from that branch
dev.idle_trippedcircuit breaker tripped (RN-047) — N consecutive blocked tasks stop the agent
dev.credit_exhaustedthe model provider refused for lack of credit (RN-726) — the agent pauses in idle_tripped without counting the breaker nor claiming the next task; Rearm resumes
dev.rearmedthe user re-armed a stuck agent (Phase 12b) — actor is the USER who clicked, not the agent
pr.gate_changedthe PR moved to a new gate (awaiting_qa → awaiting_secops → awaiting_user)
infra.gate_changedInfra's gate
gate.rescue_parkedthe GateRescuer PARKED a QA/SecOps cycle stalled for more than 2 h instead of restarting it (RN-722, ADR 0207); the thread shows a "Resume gate" button (RN-724). Payload: taskId, gate, idadeSegundos, motivo. Actor system/gate-rescuer
gate.interruptedon engine BOOT, a recent in_progress gate cycle had no live process on any node of the cluster: the restart killed it, and the rescuer restarts it at once instead of waiting for the 15-minute threshold (RN-823). Payload: taskId, gate, ultimaAtividadeEm, retomadoEm. Actor system/gate-rescuer; the thread shows it as a notice. Not projected to the graph
infra.artifact_blockedInfra's artifact was rejected

Delegation (Phase 8b, ADR 0038)​

typewhen
delegation.completedthe subspecialty finished; payload.parecerArtifactId references its INTERNAL artifact.qa_verdict — never the one that reaches the gate
delegation.failedthe subspecialty didn't finish; payload.failureOrigin is the ORIGIN (infra|modelo|codigo|politica, never by elimination — ADR 0020)
delegation.dispensedthe lead decided NOT to delegate; payload.justification explains why — dispensing is never silent

All three are emitted by QaLeadServer, one per subspecialty, SEPARATE from the call to record_gate_verdict — the pr.gate_changed event above remains the only event that describes the gate itself; these describe the area behind it.

Tool loop​

typewhen
toolloop.limit_reachedthe loop hit its iteration cap. payload carries iteration and max_iterations — the cap is per agent TYPE (RN-085)
toolloop.budget_exceededthat loop's token budget ran out before the work did; payload carries tokens_spent_micros and token_budget_micros

Emitted by Engine.Harness.ToolLoop and — since RN-166 — also by PoServer, which has its OWN loop and, until then, exhausted its cap silently. Same type and same payload on purpose: it's the same fact, and whoever reads the log shouldn't have to learn a second name just because the conversational agent doesn't use ToolLoop.

Artifacts​

typeschema
artifact.product_brieftitle, summary, rules
artifact.business_ruletitle, description, origin
artifact.decision_recordcontext, options, choice, consequences — a "summarized ADR" any of the six conversational agents can emit; reuses the generic pattern instead of the dedicated one, and coexists with open_adr_pr (Architect-only, a real committed document) (RN-505)
artifact.module_mapthe Architect's module map
artifact.module_routingthe Architect's candidate image per module, one item per module of the current module_map — the Architect CANDIDATES, Infra ELECTS (RN-487, ADR 0131)
artifact.module_contractsthe Architect's contract between modules: per module, what it EXPOSES (modulo, expoe: [{tipo, assinatura, descricao}]), with version — the whole list each time, the current one replaces the previous; what a module consumes is the module_map's dependsOn (RN-684, ADR 0200)
artifact.project_imageimage, rationale, network, resources, version (who decided is the actor: arquiteto or infra-lead) — the project's container image, emitted by the api (DecidirImagemDoProjetoUseCase) when the Architect decides it or the Infra Lead elects a candidate (RN-491); versioned, no table. The engine only READS whether one exists, to refuse container_start_via_runner locally without it (RN-610)
artifact.insight—
artifact.prototipo_navegavelpersonas, jornadas, prototipo (telas, anotacoes), resumo — the UX Designer's prototype (RN-286, ADR 0087)
artifact.rfc_staff— (validated in Engine.Agents.StaffTools, not by ArtifactSchemas — same case as artifact.insight): problema, opcoes (list of descricao/tradeoffs), recomendacao, poc (escopo, descartavel: true fixed). The Staff's RFC (ADR 0088), returned to the Architect via handoff in the same tool call
artifact.plano_de_testestoryId, taskId, planoDeTeste, criteriosExecutaveis, estrategiaDeAutomacao — the QA-strategy deliverable (ADR 0090), written AFTER the dev's delivery and consumed by the qa-verificada cycle since ADR 0192 (RN-674)
artifact.threat_modelstoryId, threatModel, requisitosDeSeguranca — SecOps' "second moment" over a story's DESIGN (RN-360, ADR 0090)

The schemas are closed: a missing field rejects the emission (Engine.Harness.ArtifactSchemas).

Architecture and readiness​

typewhen
architecture.readiness_confirmed—
readiness.confirmedthe user clicked "I'm ready — the need is validated" with the Creative. Since ADR 0185 the payload carries necessidadeValidada: true and aceiteImplicitoDoPo: true, the mark the implicit PO acceptance reads (RN-658); older events carry {}
necessity.validatednecessidade-validada gate (Creative → PO): a person — never a model inference — declares that the business need the Creative agent consolidated is right. Since ADR 0185 it is recorded by the SAME click as readiness.confirmed, once the engine accepts the turn, with productBriefId: null (the brief comes from that turn), via: "readiness.confirmed" and readinessEventId (RN-657). Events from the separate click of ADR 0095 (RN-406) carry productBriefId pointing at the validated artifact.product_brief

Git and bootstrap​

typewhen
project.git_connectedgit credential linked to the project
project.repository_provisionedrepository created by Brabo
project.repository_adopteda repository that already existed became the project's (RN-046)
bootstrap.repository_adoptedthe same fact in the dedicated session, with the defaultBranch observed from the provider
bootstrap.step_startedone of the six Gitflow steps began
bootstrap.step_completedcompleted
bootstrap.step_skippedit was already done — that's a success, not an error (RN-029)
bootstrap.step_degradedcompleted without one of the provider's capabilities (e.g. branch protection on Local)
bootstrap.step_failedfailed; the bootstrap is resumable from this point
bootstrap.plan_approvedthe user approved the adoption plan — only from here does bootstrap run on an adopted repo (RN-045)
bootstrap.adopted_as_isthe user dismissed the bootstrap; no step ran, and the plan is kept as evidence of what wasn't applied

Cost​

typewhen
budget.threshold_crossed70%, 90% or 100% — once per threshold (RN-018)

RAG telemetry (RN-479/480/481)​

These two are narration, never the source of the measurement, and they only exist when there is a session — that is, on the agent path. A search coming from the RAG tab is a project search and has no session at all, and session_events.session_id is NOT NULL, so measuring from the event log would silently drop exactly the searches a human ran while looking at the scores. The source of the measurement is the rag_searches/rag_feedback pair of tables (read by pnpm --filter api medir:rag); the event only puts the same fact on the session timeline.

typewhen
rag.searchan agent ran a hybrid search inside a session — payload carries the searchId of the telemetry row, the query, topK, how many hits passed the threshold, whether the search was degraded, and the chunk ids (RN-479, RN-481)
rag.feedbackan agent judged one retrieved chunk as util/irrelevante — payload carries searchId, chunkId, the verdict and the rank that chunk held in that search (RN-480)

Local runner (ADR 0103/0104)​

typewhen
project.workspace_verifiedbrabo-runner connected and confirmed the path for a project with execution_mode: runner — the runner is the source of truth for the path, overwriting whatever was typed at creation (RN-423)

Psychologist​

typewhen
psychologist.analysis_completed—
psychologist.analysis_failedwith a classified cause: infra, modelo, código or política
psychologist.analysis_skippedthe session had no ANALYZABLE event and the analysis didn't run — payload.analisaveis and payload.eventCount show the difference (RN-079)
psychologist.hypothesis_proposeda hypothesis with valid evidenceEventIds (RN-021)
psychologist.hypothesis_accepted—
psychologist.hypothesis_dismissed—
psychologist.hypothesis_accepted_for_anamneseaccepted and forwarded — the link that closes the loop with the Anamnesis

Anamnesis and instructions​

typewhen
anamnese.run_completed—
anamnese.run_failed—
anamnese.run_skippedthe round ended WITHOUT a profile, on purpose — payload.motivo says why (RN-063)
anamnese.analysisthe text the Anamnesis model wrote during the round (payload.content); never drawn in the session thread (RN-695)
anamnese.profile_updatedthe proficiency_profile changed
anamnese.agent_deviationan AGENT deviation seen by the Anamnesis (loop, cancelled by the user, repaired by the user) — a product signal, never a user trait; payload is only agente, tipo and evidenceEventIds, no personal data, not projected into the graph (RN-717, ADR 0205)
instruction.rolled_backrollback of an instruction version — creates a new version, doesn't delete (RN-027)

Broadcast​

Phoenix channel messages to the browser. They are not persisted: whoever reloads the page doesn't get them back — they get the event log back instead.

broadcastwhat
agent.deltachunk of text from the model's streaming
agent.statusvisible agent state change
agent.doneturn ended
agent.errorturn failed
event.appendeda new event entered the log — the panel's refresh trigger; carries only type and actorId

A domain event almost always produces an event.appended; the reverse doesn't hold. Since RN-579 the engine emits it for every write the api confirmed through the EngineApiClient facade (appends, proposed actions, handoffs, the PO's backlog), and never for a refused one. Writes the api makes on its own — a human deciding an action, a session transition — reach the same broadcast since AT-157: the api asks the engine for it after the write commits (POST /internal/sessions/:id/event-appended), and does not ask for writes that came from the engine, which already announces them. The browser uses it only as a trigger: it invalidates what the type affects (events and budget always; actions, handoffs or backlog by prefix), with a minimum window per target, and while the channel is alive the session screen's polls fall back to 15s (30s for the budget).

Who can listen (RN-108)​

Joining the session:<id> channel — and therefore receiving any of these broadcasts — requires a single-use opaque ticket issued by POST /projects/:projectId/sessions/:sessionId/socket-ticket (30s TTL). Before RN-108, the Phoenix socket's connect/3 checked nothing beyond the session_id existing: anyone who discovered the UUID could join and listen to everything. The ticket isn't persisted/read in the event log — it lives in session_socket_tickets, verified and consumed by the engine itself — so it doesn't show up in the domain-event inventory below. See RN-108.

Span​

OpenTelemetry span names. A session is a root trace spanning both the api and the engine (ADR 0026).

Domain names​

Named by hand, one per operation that matters. These are the ones you look up in Tempo:

spanwhere
session.createapi — the root
agent.turnengine — one pass of the ToolLoop
llm.turnengine — the call to the model
tool.callengine — a tool execution
gate.scannerengine — a SecOps scanner, with the scanner name in an attribute
outbox.session_lifecycleengine — Oban job, attached to the session's trace
outbox.psychologistengine — same, the Psychologist's analysis

Code-derived names​

The api's @Traced decorator (ADR 0035) names the span as Class.method — for example TransitionSessionUseCase.execute, DrizzleOutboxRepository.append. They can't be enumerated here: they're born and die with the code, and the list would go stale the first time something gets renamed.

The distinction matters when querying: a domain name is stable and can go into a saved query or a dashboard; a code-derived name can't.

The attributes these spans carry — brabo.layer, code.namespace, code.function — are for searching in Tempo and are not a contract, unlike trace_id. They can be renamed without breaking anything outside Tempo.

How to navigate from a session to a tool.call is documented in the runbook; the whole model is documented in observability.


How the event reaches the engine​

Not through an external queue. The api writes the event and the intent to publish it in the same transaction (transactional outbox); Oban consumes straight from Postgres. That's what prevents "the event was written but the engine never knew."


Complete inventory​

The sections above say when each event happens. This is the mechanical scan of the emission points, redone on every pnpm docs:generate — it's what makes a new identifier show up here even if nobody wrote about it.

⚠️ Block generated by pnpm docs:generate. Do not edit by hand — the next build overwrites it.

Extracted from the emission points: 115 identifiers, of which 3 are not described above.

  • action.failed (apps/api/src/application/use-cases/actions/execute-git-action.use-case.ts)
  • agent.activated (apps/api/src/application/use-cases/agents/activate-agent.use-case.ts)
  • agent.delta (apps/engine/lib/engine/agents/arquiteto_server.ex)
  • agent.done (apps/engine/lib/engine/agents/turno_assincrono.ex)
  • agent.error (apps/api/src/application/use-cases/agents/aceite-implicito-do-po.use-case.ts)
  • agent.plan (apps/engine/lib/engine/agents/diario.ex)
  • agent.response (apps/api/src/application/use-cases/llm/send-chat-message.use-case.ts)
  • agent.status (apps/engine/lib/engine/agents/diario.ex)
  • agent.turn (apps/engine/lib/engine/harness/tool_loop.ex)
  • agent.turn_resumed (apps/engine/lib/engine/agents/diario.ex)
  • anamnese.agent_deviation (apps/engine/lib/engine/anamnese/tools/report_agent_deviation.ex)
  • anamnese.analysis (apps/engine/lib/engine/workers/anamnese_worker.ex)
  • anamnese.profile_updated (apps/api/src/application/use-cases/anamnese/record-proficiency.use-case.ts)
  • anamnese.run_completed (apps/api/src/application/use-cases/anamnese/record-proficiency.use-case.ts)
  • anamnese.run_failed (apps/engine/lib/engine/session_events/event.ex)
  • anamnese.run_skipped (apps/engine/lib/engine/session_events/event.ex)
  • architecture.readiness_confirmed (apps/api/src/application/use-cases/agents/offer-infra-handoff.use-case.ts)
  • artifact.business_rule (apps/engine/lib/engine/agents/arquiteto_server.ex)
  • artifact.decision_record (apps/api/src/application/use-cases/backlog/perguntas-em-aberto.use-case.ts)
  • artifact.insight (apps/engine/lib/engine/harness/tools/emit_insight.ex)
  • artifact.module_map (apps/api/src/application/use-cases/architecture/create-module-map.use-case.ts)
  • artifact.open_question — ⚠️ not described above (apps/engine/lib/engine/harness/decisoes_do_projeto.ex)
  • artifact.plano_de_teste (apps/engine/lib/engine/gates/qa_lead_server.ex)
  • artifact.product_brief (apps/engine/lib/engine/agents/arquiteto_server.ex)
  • artifact.project_image (apps/engine/lib/engine/harness/ambiente_do_agente.ex)
  • artifact.prototipo_navegavel (apps/engine/lib/engine/agents/ux_designer_tools.ex)
  • artifact.qa_verdict (apps/engine/lib/engine/gates/qa_lead_server.ex)
  • artifact.rfc_staff (apps/engine/lib/engine/agents/staff_tools.ex)
  • artifact.secops_verdict — ⚠️ not described above (apps/engine/lib/engine/gates/secops_agent_server.ex)
  • artifact.threat_model (apps/engine/lib/engine/agents/dev_lead_tools.ex)
  • backlog.epic_created (apps/api/src/application/use-cases/backlog/create-epic.use-case.ts)
  • backlog.epic_without_story (apps/engine/lib/engine/agents/po_server.ex)
  • backlog.handoff_not_offered (apps/engine/lib/engine/agents/po_server.ex)
  • backlog.semantic_duplicate_check_skipped (apps/api/src/application/use-cases/backlog/verificar-duplicata-semantica.use-case.ts)
  • backlog.semantic_duplicate_warned (apps/api/src/application/use-cases/backlog/verificar-duplicata-semantica.use-case.ts)
  • backlog.story_archived (apps/api/src/application/use-cases/backlog/corrigir-historia.use-case.ts)
  • backlog.story_completed (apps/api/src/application/use-cases/backlog/complete-story.use-case.ts)
  • backlog.story_created (apps/api/src/application/use-cases/backlog/create-story.use-case.ts)
  • backlog.story_demoted (apps/api/src/application/use-cases/architecture/create-module-map.use-case.ts)
  • backlog.story_modules_assigned (apps/api/src/application/use-cases/architecture/assign-story-modules.use-case.ts)
  • backlog.story_overlap_warned (apps/api/src/application/use-cases/backlog/create-story.use-case.ts)
  • backlog.story_promotion_proposed (apps/api/src/application/use-cases/backlog/complete-story.use-case.ts)
  • backlog.story_promotion_returned (apps/api/src/application/use-cases/backlog/return-story.use-case.ts)
  • backlog.story_transitioned (apps/api/src/application/use-cases/backlog/transition-story.use-case.ts)
  • backlog.story_updated (apps/api/src/application/use-cases/backlog/corrigir-historia.use-case.ts)
  • backlog.task_blocked (apps/api/src/application/use-cases/execution/mark-task-blocked.use-case.ts)
  • backlog.task_claimed (apps/api/src/application/use-cases/execution/claim-next-task.use-case.ts)
  • backlog.task_created (apps/api/src/application/use-cases/backlog/create-task.use-case.ts)
  • backlog.task_merge_conflict (apps/api/src/application/use-cases/actions/execute-git-action.use-case.ts)
  • backlog.task_status_changed (apps/api/src/application/use-cases/actions/execute-git-action.use-case.ts)
  • backlog.task_unblocked (apps/api/src/application/use-cases/execution/unblock-task.use-case.ts)
  • bootstrap.adopted_as_is (apps/api/src/application/use-cases/git/decide-bootstrap-plan.use-case.ts)
  • bootstrap.plan_approved (apps/api/src/application/use-cases/git/decide-bootstrap-plan.use-case.ts)
  • bootstrap.repository_adopted (apps/api/src/application/use-cases/git/adopt-repository.use-case.ts)
  • bootstrap.step_acknowledged — ⚠️ not described above (apps/api/src/application/use-cases/git/acknowledge-protection-failure.use-case.ts)
  • bootstrap.step_completed (apps/api/src/application/use-cases/git/bootstrap-runner.ts)
  • bootstrap.step_degraded (apps/api/src/application/use-cases/git/bootstrap-runner.ts)
  • bootstrap.step_failed (apps/api/src/application/use-cases/git/bootstrap-runner.ts)
  • bootstrap.step_skipped (apps/api/src/application/use-cases/git/bootstrap-runner.ts)
  • bootstrap.step_started (apps/api/src/application/use-cases/git/bootstrap-runner.ts)
  • budget.threshold_crossed (apps/api/src/application/use-cases/llm/record-llm-usage.use-case.ts)
  • chat.message (apps/api/src/application/use-cases/agents/send-agent-message.use-case.ts)
  • chat.message_cancelled (apps/engine/lib/engine/agents/fila_de_mensagens.ex)
  • chat.message_delivered (apps/engine/lib/engine/agents/fila_de_mensagens.ex)
  • chat.message_queued (apps/engine/lib/engine/agents/fila_de_mensagens.ex)
  • chat.structured_question (apps/engine/lib/engine/agents/reidratacao.ex)
  • chat.structured_question_answered (apps/api/src/application/use-cases/agents/answer-structured-question.use-case.ts)
  • delegation.completed (apps/api/src/application/use-cases/execution/record-delegation.use-case.ts)
  • delegation.dispensed (apps/api/src/application/use-cases/execution/record-delegation.use-case.ts)
  • delegation.failed (apps/api/src/application/use-cases/execution/record-delegation.use-case.ts)
  • event.appended (apps/engine/lib/engine/sessions/live_broadcast.ex)
  • execution.activated (apps/api/src/application/use-cases/execution/activate-execution.use-case.ts)
  • execution.parallelization_accepted (apps/api/src/application/use-cases/execution/accept-parallelization.use-case.ts)
  • execution.parallelization_suggested (apps/api/src/application/use-cases/execution/activate-execution.use-case.ts)
  • gate.interrupted (apps/engine/lib/engine/gates/gate_rescuer.ex)
  • gate.rescue_parked (apps/engine/lib/engine/gates/gate_rescuer.ex)
  • gate.scanner (apps/engine/lib/engine/gates/scanner.ex)
  • handoff.accepted (apps/api/src/application/use-cases/agents/accept-handoff.use-case.ts)
  • handoff.auto_accept_failed (apps/api/src/application/use-cases/agents/aceitar-handoff-automaticamente.use-case.ts)
  • handoff.offered (apps/api/src/application/use-cases/agents/create-handoff.use-case.ts)
  • handoff.superseded (apps/api/src/application/use-cases/agents/ciclo-de-vida-do-handoff.service.ts)
  • infra.artifact_blocked (apps/api/src/application/use-cases/execution/mark-infra-artifact-blocked.use-case.ts)
  • infra.correction_delivered (apps/engine/lib/engine/infra/infra_lead_server.ex)
  • infra.correction_queued (apps/engine/lib/engine/infra/infra_lead_server.ex)
  • infra.gate_changed (apps/api/src/application/use-cases/execution/record-infra-gate-verdict.use-case.ts)
  • instruction.rolled_back (apps/api/src/application/use-cases/instructions/rollback-instruction.use-case.ts)
  • llm.turn (apps/engine/lib/engine/sessions/engine_api_client.ex)
  • necessity.validated (apps/api/src/application/use-cases/agents/confirm-readiness.use-case.ts)
  • permission.granted (apps/api/src/application/use-cases/actions/approve-always-action.use-case.ts)
  • pr.gate_changed (apps/api/src/application/use-cases/execution/open-gate.use-case.ts)
  • project.git_connected (apps/api/src/application/use-cases/git/handle-git-oauth-callback.use-case.ts)
  • project.repository_adopted (apps/api/src/application/use-cases/git/adopt-repository.use-case.ts)
  • project.repository_provisioned (apps/api/src/application/use-cases/git/provision-repository.use-case.ts)
  • project.workspace_verified (apps/api/src/application/use-cases/iam/confirm-project-workspace.use-case.ts)
  • proposed_action.approved (apps/api/src/application/use-cases/actions/approve-action.use-case.ts)
  • proposed_action.created (apps/api/src/application/use-cases/actions/propose-action.use-case.ts)
  • proposed_action.denied (apps/api/src/application/use-cases/actions/deny-action.use-case.ts)
  • proposed_action.executed (apps/api/src/application/use-cases/actions/execute-git-action.use-case.ts)
  • psychologist.analysis_completed (apps/api/src/application/use-cases/execution/propose-hypotheses.use-case.ts)
  • psychologist.analysis_failed (apps/engine/lib/engine/workers/psychologist_worker.ex)
  • psychologist.analysis_skipped (apps/engine/lib/engine/workers/psychologist_worker.ex)
  • psychologist.hypothesis_accepted (apps/api/src/application/use-cases/execution/accept-hypothesis.use-case.ts)
  • psychologist.hypothesis_accepted_for_anamnese (apps/api/src/application/use-cases/execution/accept-hypothesis.use-case.ts)
  • psychologist.hypothesis_dismissed (apps/api/src/application/use-cases/execution/dismiss-hypothesis.use-case.ts)
  • psychologist.hypothesis_proposed (apps/api/src/application/use-cases/execution/propose-hypotheses.use-case.ts)
  • rag.feedback (apps/api/src/application/use-cases/rag/record-rag-feedback.use-case.ts)
  • rag.search (apps/api/src/application/use-cases/rag/hybrid-search.use-case.ts)
  • readiness.confirmed (apps/api/src/application/use-cases/agents/confirm-readiness.use-case.ts)
  • session.activated (apps/api/src/db/seed.ts)
  • session.closed (apps/engine/lib/engine/outbox/drain.ex)
  • session.closed_abnormally (apps/engine/lib/engine/outbox/drain.ex)
  • session.created (apps/api/src/application/use-cases/sessions/create-session.use-case.ts)
  • session.draining (apps/engine/lib/engine/shutdown.ex)
  • tool.call (apps/engine/lib/engine/agents/arquiteto_server.ex)
  • tool.result (apps/engine/lib/engine/agents/arquiteto_server.ex)

TODO(human): type is a free-form string in application/ports/session-event-repository.port.ts — nothing prevents a typo from silently creating a new type, and nothing guarantees this page covers all of them. A TypeScript union exported from packages/shared would solve both problems and would let this list be generated instead of maintained by hand. Until that exists, the list above is a dated extraction of the emission points.