跳到主要内容

ADR 0061 — 会话的类型是创建时的数据,而执行仍然是事件

  • 状态: 已接受
  • 日期: 2026-08-09
  • 先前背景: ADR 0045(dev agent 的状态从事件派生——同一类决定)、ADR 0046(新默认值 + 定向回填,这里复用的迁移形式)

背景​

每个会话诞生时都一样。要到达 Criativo——引导构思、也是产出链条入口的 agent——必须先打开会话,之后才在顶栏发现一个按钮。用户报告的正是这一点:"o processo de ter que selecionar o botão acima para iniciar o criativo não ficou claro o suficiente; faça com que exista diferença entre o tipo de sessão apenas consultiva e uma para iniciar de fato o criativo"。同时还有来自同一导航的两个请求:能够在不丢失用于指向会话的话题标签的情况下给会话命名,以及有一条回到仪表盘的路——SessionPage 既没有导入 Link 也没有导入 useNavigate,进入会话就是进了死胡同。

第一个请求带有架构风险,而风险不在新字段:而在于产品已经知道如何区分一个会执行的会话。它通过派生知道——findActiveExecutionSession 查找带有事件 execution.activated 的 active 会话,正是这一点让重新激活落到 dev agent 已经在其中的那个会话上,而不是每次点击都打开一个孤儿会话(第一次 dogfooding 的发现 #11)。在表中写入一个类型,就为一个相似的问题制造了第二个来源。关于同一件事的两个来源最终会分歧,而这里的分歧不会是装饰性的:它决定 dev agent 在哪里写入。

有一个出路是什么都不写,继续从日志派生一切。它不管用,原因在于时间:意图在任何事件之前就存在——在打开会话的那次点击中——而那正是产品需要提问的时刻。一个还没有人激活的创意会话没有任何事件可供派生,而用户的请求描述的恰恰就是它。

决策​

两个来源都存在,它们回答不同的问题。

sessions.kind(consultiva | criativa)分类的是创建的意图。它在 POST /projects/:projectId/sessions 的正文中选择,这个路由到目前为止不接收任何正文,并且它是不可变的——没有任何路由能改变它。事件 execution.activated 继续分类执行状态,而 findActiveExecutionSession 没有开始看 kind:一个从未激活执行的创意会话不是当前的执行会话。

阻止两者相互覆盖的只有一条规则,而它才是真正的决定:在 consultiva 会话中的 execution.activated 是错误(409),而不是静默转换。让事件提升类型,恰恰就是两个来源争夺同一行。这把锁位于漏斗——AppendSessionEventUseCase——而不是 ActivateExecutionUseCase 中:写入事件的两条路径(用户的路由和 engine 的 /internal/*)都经过它,锁在用例中会让另一条路径敞开。它在 incrementSeq 之前运行,以免被拒绝的尝试消耗 seq。

类型在所有创建点都是必需的,包括内部的:仓库方法和用例都要求这个字段。可选参数会让五个调用者默默继承默认值,那是换了个名字的原始缺陷。四条内部路径(开通、接管、激活执行、种子)声明 criativa,因为这些会话接下来收到的就是 execution.activated。

列的默认值是 consultiva——能力最小的类型。它不是为了方便而存在的:它的存在是为了让来自不经过路由的路径的行,不获得执行的权利。迁移的回填走相反的方向,而且是定向的:在它运行的那一刻,每个会话都早于这一区分,其中一些正是 dev agent 此刻在其中工作的会话——如果醒来时是 consultiva,它们会在没有任何人作出决定的情况下拒绝一个进行中项目的重新激活。这是 ADR 0046 的形式,用同样的论证得出同样的结论。

名称是标签,不是事实。 sessions.name 是可选的,可以通过 PATCH /projects/:projectId/sessions/:sessionId 更改(角色 developer,与打开会话相同)。重命名不变成事件:日志是会话所经历的东西,N 次重命名会把真正重要的东西推出 200 条的尾部。标签是组合的——名称和话题标签——因为话题标签是粘到 URL 中的东西,而人选的名称不是唯一的;没有名称时,它退化为只有话题标签。空白算作缺失,正文中的 null 是撤销的途径。

后果​

改善的东西。 选择发生在它被作出的地方,两种解释都摆在眼前,咨询会话不再提供它做不到的东西——“Iniciar ideação”按钮只存在于创意会话中。会话获得了名称和出口。而设计系统的 Disclosure 获得了第一个真实的调用点:“Log de eventos”的折叠,原来是一个带 −/+ 文本的 button,没有 aria-controls,也没有具名的区域。

变差的、但被接受的东西。 “这个会话会执行吗?”这个问题现在有两个地方要查找,而没有任何注释能替代这份记忆:谁把 findActiveExecutionSession 改成去看 kind,就会让产品开始把 dev agent 派往没人激活过的会话。在这种情况下会失败的测试是明确的,并且就是为了因为这个原因而失败而写的。

类型是不可变的,这会让人不舒服。 打开了咨询会话又改变主意的人,会打开另一个会话。这是有意的:一个 changeKind 会把意图变成状态,并一次性带回本 ADR 存在要避免制造的问题。如果这种不适被证明是真实的,出路不是解锁这个字段——而是一个显式的“晋升”动作,把上下文复制到一个新会话中,并留下发生过晋升的痕迹。

名称有上限,没有唯一性。 名称限制为 80 个字符,因为标签与话题标签共享一条固定宽度的栏。没有 unique:两个会话可以同名,区分它们的是话题标签——要求唯一性就是要求用户管理一个他没有要求的命名空间。

范围之外,已声明。 创意会话不会自行激活 Criativo:它仍然是一次点击,只是现在只在有意义的地方。自行激活会是打开界面时产生的效果,而产品不这样做。FASE 24 将在 kind 之上构建的 Chat 和 Criativo 标签页,也不在这里。