跳到主要内容
译文日期 2026-10-16;原文此后已有更改。

事件

Brabo 中并存着三族名称,而且很容易混淆,因为它们都使用 主题.动词 的形式:

族所在位置持久性
领域事件session_events 中的一行永久且不可变
BroadcastPhoenix 通道 → 浏览器短暂,不持久化
SpanOpenTelemetry 的 traceTempo 的保留期(本地为 24 小时)

只有第一种是契约。另外两种是传输和观测。

领域事件​

session_events 中的一行,只追加,每个会话有一个连续的 seq(RN-002)。列:

列含义
iduuid
session_id所属会话
seq会话内的顺序,从 1 开始,没有空洞
type下面的标识符
actor由谁引起:{kind, id}——用户、agent 或系统
payloadjsonb,每种类型有自己的格式
created_at—

type 在代码中是自由的 string——没有 TypeScript 的 union,也没有数据库 enum 列出合法的类型。下面的列表是从发出事件的位置提取的,因此可能过时而不会弄坏任何东西。参见本页末尾的说明。

会话​

类型时机
session.created会话以 created 状态诞生
session.activated转为 active
session.draining承载它的节点正在排空
session.closed正常结束
session.closed_abnormally带原因的结束——node_shutdown 是最常见的
session.reopened一个 closed/closed_abnormally 的会话回到了 active(ADR 0183)

终止状态的会话不再接收对话(RN-581)。 当会话处于 closed 或 closed_abnormally 时,写入对话事件会被以 409 和 reason: "sessao_encerrada" 拒绝。对话是指只作为对话存在的类型(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),或者任何 actor 是对话 agent 的事件——所以创意 agent 的 tool.call/agent.response 会被拒绝,而来自心理学家的同类事件——它运行在已关闭的会话之上——仍然可以写入。对待处理动作的人工决策(action.approved/action.denied)也仍然可以写入:动作队列比会话活得久。因对话停滞超过上限而关闭的会话带有 termination_reason: "conversation_idle_timeout",并以 closed 结束,与 heartbeat_timeout 相同。

已关闭的会话可以重新打开(RN-649,ADR 0183)。 POST /projects/:projectId/sessions/:sessionId/reopen(developer,ADR 0184)把一个 closed/closed_abnormally 会话带回 active,并写入 session.reopened,内容为 {from, to, closedAt, terminationReason}——这是它处于关闭状态那段时间的唯一记录,因为该行的 closed_at 和 termination_reason 会被清空。不会编辑任何之前的事件,会话的 kind 也不变。已有 execution.activated 的会话会被以 409 sessao_com_execucao 拒绝。

聊天与 agent​

类型时机
chat.message会话对话流中的消息,来自用户或 agent
chat.message_queued消息在 agent 回合进行中到达,进入了它的队列而不是被拒绝(RN-673,ADR 0191)。Actor:该 agent。Payload:mensagemId(chat.message 的 id)、texto、idioma 和 posicao。文本放在这里是因为 engine 重启后队列要重新读取它。入队的消息保持待处理,直到某个 chat.message_delivered 或 chat.message_cancelled 指名它;每个 agent 每个会话最多 10 条
chat.message_delivered读取队列的回合开始了:待处理的消息被一起、按顺序在一个回合中读取。Actor:该 agent。Payload:mensagemIds
chat.message_cancelled发送待处理消息的人取消了它——没有 agent 读过它。Actor:该用户。Payload:mensagemId、agente。由 engine 写入;在已关闭的会话中被拒绝(RN-581)
chat.structured_question创意 agent 通过表单一次请求多个回答——工具 ask_structured_questions(RN-162)。每个问题带有 id、label、type、options 和 allowOther——后者是 select 的自由文本出口,模型未声明时为 true(RN-171)
chat.structured_question_answered用户回答了表单;回答也会作为 chat.message 返回给 agent 阅读
agent.activated某个 agent 在会话中接手了工作
agent.responseagent 完整、合并后的回应。modelName 说明是哪个模型生成的,三个生产方都会写(五个对话 agent、每个执行/gate agent 的 ToolLoop,以及没有活动 agent 时 api 的聊天)——回合在解析 binding 之前就失败时为 null,在该规则存在之前写入的事件中不存在(RN-175)。来自对话 agent 时,content 只是回合的收尾文本,passos(没有时不存在)按顺序列出之前各轮的文本,对话流把它们折叠在"回合步骤"之下(RN-698,AT-395)
agent.erroragent 的失败,带有 origem(infra/modelo/codigo/politica)以及它在对话流中说出的 mensagem(RN-059)。当 engine 在回合中途重启、孤立的 working 状态在启动时或 agent 启动时被关闭时,也会以 reason: turno_interrompido_por_reinicio(origem infra)发出,随后是 agent.status: idle(RN-586)。它覆盖整个回合,也覆盖循环中途某一个工具的失败,payload 中带有 tool 和 retentativa(RN-163)
tool_router.decidedJev 选择工具的那一步(RN-625,ADR 0179):menuAntes/menuDepois、escolha、confianca、segunda、anterior、aplicado(菜单比目录小)、Jev 失败时的 motivoDaQueda + origemDaQueda(infra/modelo/codigo)、latenciaMs、custoMicros(真实的 usage.cost)和 foraDoCardapio(模型调用了但未被提供的工具)。由 engine 的门面为每个 agent 写入;没有咨询路由器时不存在(provider 不是 OpenRouter、工具少于两个、workspace 开关关闭)。收益的度量(AT-239)从这个事件和 token_usage 中读取,从不手工记录
tool.result工具执行的结果,由 hook Engine.Harness.Hooks.EventLog 写入,对七个对话 agent(Infra Lead 自 RN-617 起)则由它们的服务器写入,带 tool、ok 和 resultado(或 erro),截断为 2.000 个字符,截断时带 resultadoTotal(RN-589)。Infra Lead 为它内联分派的每个工具产生同样的 payload,Dev Lead 在挂起的调用被解决时也会产生——但在它等待审批期间绝不会(RN-593)
handoff.offered一个 agent 把工作提供给另一个
handoff.accepted接收方接受了。通常由人接受(actor user)。当接受隐含在"我准备好了"点击中时(RN-658),actor 仍是这个人,payload——以及随后的 agent.activated 的 payload——还带有 implicito: { via: "readiness.confirmed", readinessEventId }。自 RN-660(ADR 0186)起,PO 向架构师的提议可以由 system actor handoff-auto-accept 接受,此时 payload 还带有 automatico: true、emNomeDe(打开会话的人——仓库以此人的名义创建)和 criterio(regras、cobertas、repositorio)
handoff.auto_accept_failed系统决定接受 PO 向架构师的提议(RN-660),但接受失败了。Payload:handoffId、toAgent、origem(infra)和 error。Actor system handoff-auto-accept。提议未被标记为 accepted 时保留按钮
handoff.superseded一个 offered 状态的 handoff 不再是现行的,也不能再被接受(RN-635,ADR 0182)。Payload:handoffId、toAgent、motivo(agente_ativado——目标通过任何途径被激活;nova_oferta——项目中对同一目标的更新提议取代了它)和 substitutaId(取代它的提议,agente_ativado 时为 null)。由 system actor handoff-lifecycle 写入旧提议所在的会话,该会话可能已关闭——这是记录而不是对话,所以 RN-581 允许它写入。该行的 status 变为 superseded;事件即历史
context.compacted上下文管理器把某个 agent 历史中较早的回合做了摘要,以适应窗口。自 RN-580 起,payload 除了 tokensBefore/tokensAfter 之外,还带有替代这些回合的 summary、拥有该历史的 agent 以及 messagesSummarized。在此之前写入的事件只带两个计数——摘要已经丢失,重新水合时会说明这一点,而不是编造一个

对话 agent 重新读取什么(RN-580)。 当七个对话 agent 之一在已有对话的会话上启动时,Engine.Agents.Reidratacao 从这个日志的尾部(最近 200 个事件)重建它的历史:chat.message、agent.response、chat.structured_question(作为 agent 自己的回合,带标签和选项)以及它自己的 tool.call/tool.result(作为文本注记——事件不带调用 id,所以绝不会被重放为 role: tool)。chat.structured_question_answered 被故意跳过:api 随后会把同样的回答写成 chat.message,agent 实时读到的就是那个。在队列中被取消的 chat.message,或对该 agent 仍待处理的 chat.message,也会被跳过(RN-673):前者从未被说出,后者会通过读取队列的回合到达。自 RN-589 起,七者都会把工具返回的文本写入 tool.result(resultado,截断为 2.000 个字符,截断时 resultadoTotal 给出真实长度);在此之前写入的会话调用没有 tool.result,注记会说明日志中没有结果。当对话长于尾部时,历史以一条系统消息开头,说明有多少更早的事件被省略,然后是最后写入的压缩摘要和开场消息。

提议的动作​

类型时机
proposed_action.created动作诞生——在任何执行之前。payload.status 说明它如何诞生(pending、auto_approved 或 denied),这正是区分人工决策与策略的依据(RN-049)。payload.reason 说明是策略的哪条规则做了决定——即 decide() 返回的字符串,三种结果都有;在该规则存在之前写入的事件中不存在,这意味着"未记录",绝不意味着"没有原因"。同名的 outbox 行不带它(RN-567)。在 terminal 动作中——唯一一种决策会参考路径范围的类型——payload.scopeRoot 说明范围是与哪个根比较的,形式为 { executionMode, ancora, segmento }:container → ancora: raiz_gerenciada 和 workspace_dir_name;mounted → ancora: base_de_projetos 和 BRABO_PROJECTS_BASE 之下的路径段(文件夹在基础目录之外或没有基础目录时为 ancora: indisponivel、segmento: null);runner → ancora: nome_da_pasta 和 workspace_dir_name。绝不是绝对路径,那会把用户的 $HOME 暴露给每个成员。在旧事件和其他动作类型中不存在;也不在 outbox 中(RN-609)
proposed_action.approved由用户决定——actor 是点击的人。自动批准不经过这里:它出现在 created 中,带 status: auto_approved 和 agent actor
proposed_action.denied被拒绝——终止状态。actor 是拒绝的人,payload.reason 是原因
proposed_action.executed执行成功
action.failed已执行但失败
permission.granted授权已写入策略

Backlog​

类型时机
backlog.epic_created—
backlog.story_created—
backlog.story_transitioned故事的状态变更。actor 说明是谁推进的:auto 模式下是 agent/po,manual 模式下是 user(RN-048)
backlog.story_completedPO 用 complete_story 补全了一个已有的 draft 故事——关联规则,填写 RF/DoD/DoR——而不是重新创建它(RN-720)
backlog.story_promotion_proposedPO 在 manual 模式的项目中完成了一个完整的故事:它保持 draft,等待用户决定,在此之前它的任何任务都不可领取(RN-048)
backlog.story_promotion_returned用户拒绝了推进,并带着原因把故事退回给 PO——原因成为其会话中的置顶消息,就像退回给 dev 的 gate 一样(RN-048)
backlog.story_updated一个没有执行中任务的 draft 故事的标题和/或描述被修正,由用户(Backlog 标签页)或 PO(update_story)完成。带有 previousTitle、title 和 descriptionChanged(RN-727)
backlog.story_archived一个没有执行中任务的 draft 故事被归档,由用户或 PO(archive_story)完成:它及其任务离开 backlog、覆盖率、计划和领取;该行保留。带有 reason 和 taskIds(RN-727,ADR 0212)
backlog.story_demoted某个模块从 module_map 中消失;故事回到 draft(RN-012)
backlog.story_overlap_warned故事已创建,但它引用的所有规则都已被另一个故事覆盖——是警告,不是阻止:由用户判断是否重叠(RN-081)
backlog.semantic_duplicate_warned写入了一个故事或业务规则,其标题通过 embedding 与项目中同类型的已有项相比达到或超过阈值——是警告,绝不拒绝;payload 指出相似项和余弦值。Actor system/duplicata-semantica(RN-681)
backlog.semantic_duplicate_check_skipped语义重复检查未能运行——没有 embedding provider、daemon 宕机、10 秒上限——reason 说明是哪一种;该项照样被写入(RN-681)
backlog.story_modules_assigned故事 ↔ 模块的关联
backlog.task_created—
backlog.task_claimed某个 dev agent 领取了任务
backlog.task_status_changed—
backlog.task_merge_conflict任务 PR 已批准的合并因合并冲突被拒:taskId、pullRequestId、conflictingFiles、actionId。自 RN-715 起,任务回到 in_progress,所属 dev agent 被 outbox 中的 task.merge_conflict 行唤醒(RN-705)
backlog.task_blocked修正尝试次数耗尽,或登记了一个阻碍
backlog.task_unblocked—
backlog.epic_without_storyPO 结束了一个回合,创建了一个史诗却没有为它创建任何故事。史诗不产生任务——故事才产生——所以这是一个在没有可见错误的情况下卡住执行的状态。按 RN-059 模式的显式结果:带 origem、epicIds/epicTitles 和消息的持久事件,外加 broadcast agent.error。每次发生只报告一次,绝不循环(RN-165)
backlog.handoff_not_offeredPO 结束了一个回合,所有业务规则都已覆盖,但未能向架构师提供 handoff:某个故事没有任务(origem: modelo,带 storyIds/storyTitles),或者读取 backlog 或创建提议失败(origem: infra)。当每个故事都有任务时,PO 的服务器自己会提供 handoff(RN-700)

执行与 gate​

类型时机
execution.plan_proposed自 ADR 0086 起已弃用(RN-284)——旧会话的日志中仍可能有这个类型;新会话使用 propose_execution_plan 类型的 proposed_action(参见 docs/reference/permissions.md),因为 Dev Lead 的计划变成了真正的决策,在"审批"中被批准或拒绝,而不再是一个简单事件
execution.activated执行阶段开始了。只能写入 criativa 会话——在 consultiva 会话中写入会返回 409(RN-097)。说明一个会话是否正在执行的,仍然是这个事件,而不是 sessions.kind 列
execution.parallelization_suggested系统提议并行化——只有当项目有一个登记为 running 的容器时才在激活时发出(否则 dev agent 会被阻塞,dev.blocked_by_container,更多 agent 只会让被阻塞的数量翻倍;AT-104)
execution.plan_applied人批准了 Dev Lead 的 propose_execution_plan(或它被自动批准),而这次批准激活了执行:计划中的每个任务获得了自己的模块,ActivateExecutionUseCase 运行了(RN-677,RN-678)。Payload:actionId、sessaoDeExecucao、modulos、tarefasAtribuidas,以及——当计划带有 tarefas 时——tarefasPorModulo 和 modulosSemTarefa(没有 agent 的模块,RN-709)。写入 Dev Lead 的会话,actor system/action-executor
execution.plan_failed已批准的计划没有激活执行——计划与现行的 module_map 不再吻合,或激活被拒绝(例如没有仓库时的 409)。Payload:相同的字段外加 motivo,即 api 自己的说法。人工决策被记录下来;当计划本身被拒时,任务上什么都没有写入
execution.parallelization_accepted已接受——子 agent 继承基础 agent 的上限
dev.starteddev agent 开始了周期(激活、并行化——不包括重新水合,后者从不重新触发)
dev.working领取了任务并搭建了 worktree
dev.idle模块的队列为空——现在没有可领取的任务
dev.blocked_by_containeragent 没有领取任务,因为项目没有登记为 running 的容器(RN-502)。它像上面两个一样停在 :idle——idle 是唤醒仍能救回的唯一状态——而这个事件是区分"没有容器"、"队列为空"(dev.idle)和"领取失败"(dev.error)的唯一依据。api 从不被调用:这个守卫在 claim_task/1 之前运行,所以不会有任务被领取却没有活着的主人。它会自行解决:outbox 中的 container.running 会唤醒项目中每个 :idle 的 agent
dev.awaiting_approvalgit 动作等待审批(Fase 12e):gate 不会打开——没有 PR 就没有可评判的东西——worktree 一直被占用,直到 task.pr_settled(RN-050)
dev.awaiting_gatePR 已打开,等待 gate(Fase 12b)——task_id/worktree 仍被占用
dev.blocked任务带着诊断被退回(迭代上限、预算超支、report_blocked、worktree/上下文失败)。自 RN-743 起带有 trabalhoPreservado({branch, commit} 或 null):未提交的工作以 <agente>[bot] 身份提交到任务分支,agent 的下一个任务从这个分支出发
dev.idle_tripped断路器跳闸(RN-047)——连续 N 个被阻塞的任务会让 agent 停下
dev.credit_exhausted模型的 provider 因额度不足而拒绝(RN-726)——agent 暂停在 idle_tripped,不计入断路器,也不领取下一个任务;"重新启用"会恢复
dev.rearmed用户重新启用了一个被卡住的 agent(Fase 12b)——actor 是点击的用户,而不是 agent
pr.gate_changedPR 进入新的 gate(awaiting_qa → awaiting_secops → awaiting_user)
infra.gate_changedInfra 的 gate
gate.rescue_parkedGateRescuer 搁置了一个停滞超过 2 小时的 QA/SecOps 周期,而不是重启它(RN-722,ADR 0207);对话流显示一个"恢复 gate"按钮(RN-724)。Payload:taskId、gate、idadeSegundos、motivo。Actor system/gate-rescuer
gate.interrupted在 engine 启动时,一个最近的 in_progress gate 周期在集群的任何节点上都没有活着的进程:是重启杀死了它,救援者立即重启它,而不是等待 15 分钟的阈值(RN-823)。Payload:taskId、gate、ultimaAtividadeEm、retomadoEm。Actor system/gate-rescuer;对话流把它显示为提示。不投影到图谱
infra.artifact_blockedInfra 的产物被拒绝

委派(Fase 8b,ADR 0038)​

类型时机
delegation.completed子专业完成了;payload.parecerArtifactId 引用它内部的 artifact.qa_verdict——绝不是到达 gate 的那个
delegation.failed子专业没有完成;payload.failureOrigin 是来源(infra|modelo|codigo|politica,绝不靠排除法——ADR 0020)
delegation.dispensedlead 决定不委派;payload.justification 解释原因——免除从不沉默

这三个都由 QaLeadServer 发出,每个子专业一个,与对 record_gate_verdict 的调用分开——上面的 pr.gate_changed 事件仍然是唯一描述 gate 本身的事件;这些描述的是 gate 背后的领域。

工具循环​

类型时机
toolloop.limit_reached循环达到了迭代上限。payload 带有 iteration 和 max_iterations——上限按 agent 类型设定(RN-085)
toolloop.budget_exceeded该循环的 token 预算在工作完成前耗尽;payload 带有 tokens_spent_micros 和 token_budget_micros

由 Engine.Harness.ToolLoop 发出,而且——自 RN-166 起——也由 PoServer 发出,它有自己的循环,在此之前耗尽上限时是沉默的。类型和 payload 故意相同:这是同一个事实,读日志的人不应该仅仅因为对话 agent 不使用 ToolLoop 就去学第二个名字。

产物​

类型schema
artifact.product_brieftitle、summary、rules
artifact.business_ruletitle、description、origin
artifact.decision_recordcontext、options、choice、consequences——一个"精简 ADR",六个对话 agent 中任何一个都可以发出;复用通用模式而不是专用模式,并与 open_adr_pr(只有架构师,真正提交的文档)共存(RN-505)
artifact.module_map架构师的模块地图
artifact.module_routing架构师为每个模块给出的候选镜像,现行 module_map 中每个模块一项——架构师提名,Infra 选定(RN-487,ADR 0131)
artifact.module_contracts架构师给出的模块间契约:每个模块暴露什么(modulo、expoe: [{tipo, assinatura, descricao}]),带 version——每次都是完整列表,现行的取代之前的;模块消费什么是 module_map 的 dependsOn(RN-684,ADR 0200)
artifact.project_imageimage、rationale、network、resources、version(谁决定的由 actor 表示:arquiteto 或 infra-lead)——项目的容器镜像,当架构师决定它或 Infra Lead 选定一个候选时由 api 发出(DecidirImagemDoProjetoUseCase)(RN-491);有版本,没有表。engine 只读取它是否存在,以便在没有它时本地拒绝 container_start_via_runner(RN-610)
artifact.insight—
artifact.prototipo_navegavelpersonas、jornadas、prototipo(telas、anotacoes)、resumo——UX Designer 的原型(RN-286,ADR 0087)
artifact.rfc_staff—(在 Engine.Agents.StaffTools 中校验,而不是由 ArtifactSchemas 校验——与 artifact.insight 情况相同):problema、opcoes(descricao/tradeoffs 的列表)、recomendacao、poc(escopo,固定为 descartavel: true)。Staff 的 RFC(ADR 0088),在同一次工具调用中通过 handoff 返回给架构师
artifact.plano_de_testestoryId、taskId、planoDeTeste、criteriosExecutaveis、estrategiaDeAutomacao——qa-estratégia 的交付物(ADR 0090),在 dev 交付之后撰写,自 ADR 0192 起由 qa-verificada 周期消费(RN-674)
artifact.threat_modelstoryId、threatModel、requisitosDeSeguranca——SecOps 针对故事设计的"第二时刻"(RN-360,ADR 0090)

schema 是封闭的:缺少字段会拒绝发出(Engine.Harness.ArtifactSchemas)。

架构与就绪​

类型时机
architecture.readiness_confirmed—
readiness.confirmed用户在创意 agent 处点击了"我准备好了——需求已验证"。自 ADR 0185 起,payload 带有 necessidadeValidada: true 和 aceiteImplicitoDoPo: true,这是 PO 隐式接受所读取的标记(RN-658);更早的事件带 {}
necessity.validatedgate necessidade-validada(创意 → PO):由一个人——绝不是模型的推断——声明创意 agent 整合的业务需求是正确的。自 ADR 0185 起,它由 readiness.confirmed 的同一次点击写入,在 engine 接受回合时立即写入,带 productBriefId: null(brief 来自这个回合)、via: "readiness.confirmed" 和 readinessEventId(RN-657)。ADR 0095 那种单独点击产生的事件(RN-406)带有指向被验证的 artifact.product_brief 的 productBriefId

Git 与 bootstrap​

类型时机
project.git_connectedgit 凭据关联到项目
project.repository_provisioned仓库由 Brabo 创建
project.repository_adopted一个已存在的仓库归入项目(RN-046)
bootstrap.repository_adopted专用会话中的同一事实,带有在 provider 中观察到的 defaultBranch
bootstrap.step_startedGitflow 的六个步骤之一开始了
bootstrap.step_completed已完成
bootstrap.step_skipped早已完成——这是成功,不是错误(RN-029)
bootstrap.step_degraded完成了,但缺少 provider 的某项能力(例如 Local 上的分支保护)
bootstrap.step_failed失败;bootstrap 可以从这一点恢复
bootstrap.plan_approved用户批准了接管计划——只有从这里开始,bootstrap 才会在被接管的仓库上运行(RN-045)
bootstrap.adopted_as_is用户免除了 bootstrap;没有任何步骤运行,计划作为未应用内容的证据保留

成本​

类型时机
budget.threshold_crossed70%、90% 或 100%——每个阈值一次(RN-018)

RAG 遥测(RN-479/480/481)​

这两个是叙述,绝不是度量的来源,而且只在有会话时存在——也就是 agent 的路径上。来自 RAG 标签页的搜索是项目搜索,根本没有会话,而 session_events.session_id 是 NOT NULL,所以用 event log 度量会悄无声息地丢掉恰恰是人看着分数做的那些搜索。度量的来源是 rag_searches/rag_feedback 这对表(由 pnpm --filter api medir:rag 读取);事件只是把同一事实放到会话的时间线上。

类型时机
rag.search某个 agent 在会话中做了一次混合搜索——payload 带有遥测行的 searchId、查询、topK、有多少结果超过阈值、搜索是否 degraded 以及 chunk 的 id(RN-479,RN-481)
rag.feedback某个 agent 把检索到的 chunk 判为 util/irrelevante——payload 带有 searchId、chunkId、判定以及该 chunk 在那次搜索中所处的位置(RN-480)

本地 runner(ADR 0103/0104)​

类型时机
project.workspace_verifiedbrabo-runner 已连接,并确认了一个 execution_mode: runner 项目的路径——runner 是路径的事实来源,覆盖创建时输入的内容(RN-423)

心理学家​

类型时机
psychologist.analysis_completed—
psychologist.analysis_failed带分类后的原因:infra、modelo、código 或 política
psychologist.analysis_skipped会话没有可分析的事件,分析没有运行——payload.analisaveis 和 payload.eventCount 显示差别(RN-079)
psychologist.hypothesis_proposed一个带有有效 evidenceEventIds 的假设(RN-021)
psychologist.hypothesis_accepted—
psychologist.hypothesis_dismissed—
psychologist.hypothesis_accepted_for_anamnese已接受并转交——闭合与 Anamnese 之间循环的环节

Anamnese 与指令​

类型时机
anamnese.run_completed—
anamnese.run_failed—
anamnese.run_skipped这一轮故意以没有画像结束——payload.motivo 说明原因(RN-063)
anamnese.analysisAnamnese 的模型在这一轮中写下的文本(payload.content);从不在会话对话流中绘制(RN-695)
anamnese.profile_updatedproficiency_profile 发生了变化
anamnese.agent_deviationAnamnese 观察到的 agent 偏差(循环、被用户取消、被用户修复)——是产品的信号,绝不是用户的特征;payload 只有 agente、tipo 和 evidenceEventIds,不含个人数据,不投影到图谱(RN-717,ADR 0205)
instruction.rolled_back某个指令版本的回滚——创建新版本,不删除(RN-027)

Broadcast​

从 Phoenix 通道发往浏览器的消息。它们不被持久化:重新加载页面的人收不到它们——收到的是 event log。

broadcast含义
agent.delta模型流式输出的文本片段
agent.statusagent 可见的状态变化
agent.done回合结束了
agent.error回合失败了
event.appended有新事件进入日志——面板刷新的触发器;只带 type 和 actorId

一个领域事件几乎总会产生一个 event.appended;反过来则不成立。自 RN-579 起,engine 会为 api 通过 EngineApiClient 门面确认的每一次写入(记录、提议的动作、handoff、PO 的 backlog)发出它,而对被拒绝的写入从不发出。api 自己完成的写入——人对动作做决定、会话状态转换——自 AT-157 起也会到达同一个 broadcast:api 在写入提交之后向 engine 请求它(POST /internal/sessions/:id/event-appended),而对来自 engine 的写入不请求,因为 engine 已经宣布过了。浏览器只把它当作触发器:使 type 影响的东西失效(事件和预算总是;动作、handoff 或 backlog 按前缀),每个目标有一个最小窗口,通道活着时,会话页面的轮询降到 15 秒(预算为 30 秒)。

谁可以收听(RN-108)​

进入 session:<id> 通道——从而接收这些 broadcast 中的任何一个——需要一个由 POST /projects/:projectId/sessions/:sessionId/socket-ticket 签发的一次性不透明 ticket(TTL 30 秒)。在 RN-108 之前,Phoenix socket 的 connect/3 除了 session_id 存在之外什么都不检查:发现了 UUID 的人就可以进入并收听一切。ticket 既不持久化也不在 event log 中读取——它存放在 session_socket_tickets 中,由 engine 自己验证和消费——因此不会出现在下面的领域事件清单中。参见 RN-108。

Span​

OpenTelemetry 的 span 名称。一个会话是贯穿 api 和 engine 的根 trace(ADR 0026)。

领域名称​

手工命名,每个重要操作一个。它们是在 Tempo 中会去搜索的名字:

span位置
session.createapi——根
agent.turnengine——ToolLoop 的一轮
llm.turnengine——对模型的调用
tool.callengine——一个工具的执行
gate.scannerengine——SecOps 的一个扫描器,扫描器名称在一个属性中
outbox.session_lifecycleengine——Oban 的 job,挂在会话的 trace 上
outbox.psychologistengine——同上,心理学家的分析

从代码派生的名称​

api 的 @Traced 装饰器(ADR 0035)把 span 命名为 类.方法——例如 TransitionSessionUseCase.execute、DrizzleOutboxRepository.append。它们无法列在这里:它们随代码生灭,列表会在第一次改名时过时。

这个区别在查询时很重要:领域名称是稳定的,可以放进保存的查询或仪表盘;从代码派生的名称则不行。

这些 span 带的属性——brabo.layer、code.namespace、code.function——用于在 Tempo 中搜索,与 trace_id 不同,它们不是契约。它们可以被重命名,而不会弄坏 Tempo 之外的任何东西。

如何从一个会话导航到一个 tool.call,记录在 runbook 中;完整的模型记录在可观测性中。


事件如何到达 engine​

不是通过外部队列。api 在同一个事务中写入事件以及发布它的意图(transactional outbox);Oban 直接从 Postgres 消费。正是这一点防止了"事件已写入,但 engine 从不知道"。


完整清单​

上面各节说明每个事件何时发生。这一节是对发出位置的机械扫描,每次 pnpm docs:generate 时重做——正是它让新的标识符即使没人写过也会出现在这里。

⚠️ 此区块由 pnpm docs:generate 生成。请勿手动编辑——下次构建会覆盖它。

从发出点提取:115 个标识符,其中 3 个未在上文描述。

  • 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 — ⚠️ 上文未描述 (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 — ⚠️ 上文未描述 (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 — ⚠️ 上文未描述 (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(humano): type 在 application/ports/session-event-repository.port.ts 中是自由的 string——没有任何东西能阻止一个拼写错误悄无声息地创建新类型,也没有任何东西保证本页覆盖了全部类型。从 packages/shared 导出一个 TypeScript union 可以同时解决这两个问题,并让这个列表可以被生成而不是手工维护。在它出现之前,上面的列表是对发出位置的一次带日期的提取。