父 issue
背景
#713 的 A/B/C 已完成:daemon recovery pipeline、daemon async result contract、Feishu setup/autoconfig facade 都已收口。重新评估当前代码后,下一处仍值得优先治理的是 Feishu projector 的 Operation 构造边界。
当前 internal/adapter/feishu/projector.go 和相邻 projector 文件中仍有多处手写 Operation{...},重复组合这些跨切面规则:
GatewayID / SurfaceSessionID / ChatID 基础字段。
OperationSendCard 与 OperationUpdateCard 的 message id 判定。
ReplyToMessageID reply lane 只应作用于新发送卡片/文本/图片。
CardUpdateMulti、cardEnvelopeV2、rawCardDocument(...) / header subtitle。
temporary session header 注入。
theme fallback 与 card body/elements 同步。
这类重复不是视觉问题,而是 correctness / maintainability 问题:新增 payload 时容易漏 reply lane、temporary session header、patchable update_multi、card envelope 或 message id update 语义。
范围
本 issue 只覆盖 Feishu projector 把 eventcontract.Event 投影为 Operation 的 card operation construction 边界:
普通卡片 payload 的 send/update 判定。
projector card operation 的基础字段填充。
reply lane、temporary session header、card envelope、theme/body/elements 同步。
与这些规则直接相关的结构测试和现有 projector 回归。
不覆盖 gateway transport apply、callback payload 解析、card 视觉改版、业务 read model 生成或 final markdown normalize 语义变更。
相关文档
docs/general/feishu-card-api-constraints.md
docs/general/feishu-card-content-context-guidelines.md
docs/general/feishu-card-ui-state-machine.md
docs/general/issue-orchestration-workflow.md
涉及文件
预计涉及:
internal/adapter/feishu/projector.go
internal/adapter/feishu/projector_exec_command_progress.go
internal/adapter/feishu/projector_thread_history.go
internal/adapter/feishu/card_renderer.go
internal/adapter/feishu/card_header_context.go
internal/adapter/feishu/*projector*_test.go
默认不修改:
internal/adapter/feishu/gateway_runtime*.go
internal/core/orchestrator/**
internal/core/control/** 的 read model contract
建议范围
先补结构测试,锁定普通 card operation path 应通过统一 builder,而不是在多个 case 中重复设置 envelope/raw document/reply/update 组合。
增加 projector 内部 card operation spec/builder,覆盖 GatewayID、SurfaceSessionID、ChatID、title/body/theme/elements、patchable message id、reply lane、temporary header。
逐步迁移 projectEventBase 中普通卡片 payload,以及 projectExecCommandProgress / projectThreadHistory 的同类路径。
对 final reply split、exec progress split 里的 transport-size probe 保持谨慎:只在不改变测量语义时复用 helper,否则保留手写临时 op 并写明理由。
跑目标 projector 测试、全量测试、文件长度检查和 Feishu card guardrail 复核。
目标
建立 projector 内部统一的 card Operation builder / factory,让常见卡片投影路径通过一个入口设置基础字段、send/update 判定、reply lane、temporary session header、card envelope 与 raw card document。
非目标
不改变现有 Feishu 卡片视觉、文案或用户语义。
不重写整个 projector 架构。
不迁移到 CardKit streaming 或改变 message.patch 策略。
不扩大到 gateway transport、callback parsing、menu/action payload schema。
不改变 final reply markdown split、exec progress 分卡预算算法或 path/target picker read model。
完成标准
projectEventBase 中常见卡片 payload 不再各自手写完整 Operation{... cardEnvelope/card ...} 组合,而是复用统一 builder。
projectExecCommandProgress、projectThreadHistory 这类相邻 projector path 至少覆盖同一类 send/update + reply lane builder,避免继续形成第二套实现。
需要保留手写 Operation 的路径必须有明确理由,例如 text/image/reaction/delete 或 transport-size probe 只构造临时 payload。
现有卡片渲染输出行为保持等价,目标 projector 测试和全量测试通过。
Feishu card API constraints、card content context、Feishu card UI state-machine guardrail 已检查;若行为确实未变,文档无需更新,但需要在收尾中记录检查结论。
依赖
信息索引
当前重点代码:
internal/adapter/feishu/projector.go
internal/adapter/feishu/projector_exec_command_progress.go
internal/adapter/feishu/projector_thread_history.go
internal/adapter/feishu/projector_exec_command_progress_split.go
internal/adapter/feishu/projector_final_split.go
internal/adapter/feishu/card_renderer.go
internal/adapter/feishu/card_header_context.go
相关文档:
docs/general/feishu-card-api-constraints.md
docs/general/feishu-card-content-context-guidelines.md
docs/general/feishu-card-ui-state-machine.md
实现参考
建议先做一个内部 builder,优先覆盖普通 card operation,不碰非 card operation:
输入:event、chat id、title/body/theme/elements、可选 message id、patchable/update_multi、temporary session label、subtitle/title tag。
输出:完整 Operation,默认 OperationSendCard;有 message id 时转 OperationUpdateCard 并清空 reply lane;新发送时按 event delivery lane 应用 reply。
builder 内部统一设置 GatewayID、SurfaceSessionID、ChatID、cardEnvelopeV2、rawCardDocument(...)。
对 final reply / exec progress split 里的 transport-size probe,可以先保持手写临时 op,或只接入一个不带 event 的 render helper;不要为了统一而改变预算测量对象。
对 text/image/reaction/delete operation 不强行纳入 card builder。
建议先用结构测试锁定:普通 projector path 不应再在 projectEventBase 内重复设置 cardEnvelope: cardEnvelopeV2 与 card: rawCardDocument(...) 多次;允许 list 要明确。
检查参考
目标检查:
go test ./internal/adapter/feishu -run 'Projector|ExecCommandProgress|ThreadHistory|Final|PathPicker|TargetPicker|Request|Selection|Plan|Notice' -count=1
go test ./internal/adapter/feishu -count=1
go test ./...
bash scripts/check/go-file-length.sh && git diff --check
Guardrail 检查:
对照 docs/general/feishu-card-api-constraints.md:确认没有增加卡片体积、更新频率或交互模式。
对照 docs/general/feishu-card-content-context-guidelines.md:确认没有把动态文本新塞回 raw markdown。
对照 docs/general/feishu-card-ui-state-machine.md:确认 callback payload、inline replace、old-card/freshness 语义未变化;若有变化必须同步文档。
收尾参考
把实现结果、验证命令、guardrail 结论回卷到 调研核心大包与高耦合结构拆分方案 #713 。
中型结构重构默认需要独立 verifier pass。
通过 issuectl close-plan 后再 finish --close。
执行决策
是否拆分:不再拆。本 issue 是 调研核心大包与高耦合结构拆分方案 #713 的 D 子执行单元,范围单一,验证面集中在 Feishu projector/card projection。
当前执行单元:Projector cross-cutting operation builder 收口。
verifier:需要。该 issue 改动 Feishu projector 结构,虽然目标是行为等价,但默认做独立 verifier。
执行快照
当前阶段:plan
当前执行点:issue body 已补齐执行闭包字段,等待 prepare/lint 后进入实现。
已完成:从 调研核心大包与高耦合结构拆分方案 #713 复核 A/B/C 后确认 D 仍是下一优先级;识别 Operation 构造重复点。
下一步:切换状态到 status:implementable-now,运行 issue workflow prepare/lint;通过后按 TDD/结构测试开始实现。
当前阻塞:无产品决策阻塞。
最后一致状态:master/origin/master 在 328f93fa,工作区干净。
未完成尾项:prepare、lint、实现、验证、verifier、回卷 调研核心大包与高耦合结构拆分方案 #713 、commit/push、close。
恢复步骤:读取本 issue 与 调研核心大包与高耦合结构拆分方案 #713 ;重新确认 internal/adapter/feishu/projector*.go 的 Operation 构造现状;先补结构测试再实现 builder。
父 issue
背景
#713 的 A/B/C 已完成:daemon recovery pipeline、daemon async result contract、Feishu setup/autoconfig facade 都已收口。重新评估当前代码后,下一处仍值得优先治理的是 Feishu projector 的 Operation 构造边界。
当前
internal/adapter/feishu/projector.go和相邻 projector 文件中仍有多处手写Operation{...},重复组合这些跨切面规则:GatewayID/SurfaceSessionID/ChatID基础字段。OperationSendCard与OperationUpdateCard的 message id 判定。ReplyToMessageIDreply lane 只应作用于新发送卡片/文本/图片。CardUpdateMulti、cardEnvelopeV2、rawCardDocument(...)/ header subtitle。这类重复不是视觉问题,而是 correctness / maintainability 问题:新增 payload 时容易漏 reply lane、temporary session header、patchable update_multi、card envelope 或 message id update 语义。
范围
本 issue 只覆盖 Feishu projector 把
eventcontract.Event投影为Operation的 card operation construction 边界:不覆盖 gateway transport apply、callback payload 解析、card 视觉改版、业务 read model 生成或 final markdown normalize 语义变更。
相关文档
docs/general/feishu-card-api-constraints.mddocs/general/feishu-card-content-context-guidelines.mddocs/general/feishu-card-ui-state-machine.mddocs/general/issue-orchestration-workflow.md涉及文件
预计涉及:
internal/adapter/feishu/projector.gointernal/adapter/feishu/projector_exec_command_progress.gointernal/adapter/feishu/projector_thread_history.gointernal/adapter/feishu/card_renderer.gointernal/adapter/feishu/card_header_context.gointernal/adapter/feishu/*projector*_test.go默认不修改:
internal/adapter/feishu/gateway_runtime*.gointernal/core/orchestrator/**internal/core/control/**的 read model contract建议范围
GatewayID、SurfaceSessionID、ChatID、title/body/theme/elements、patchable message id、reply lane、temporary header。projectEventBase中普通卡片 payload,以及projectExecCommandProgress/projectThreadHistory的同类路径。目标
建立 projector 内部统一的 card Operation builder / factory,让常见卡片投影路径通过一个入口设置基础字段、send/update 判定、reply lane、temporary session header、card envelope 与 raw card document。
非目标
完成标准
projectEventBase中常见卡片 payload 不再各自手写完整Operation{... cardEnvelope/card ...}组合,而是复用统一 builder。projectExecCommandProgress、projectThreadHistory这类相邻 projector path 至少覆盖同一类 send/update + reply lane builder,避免继续形成第二套实现。Operation的路径必须有明确理由,例如 text/image/reaction/delete 或 transport-size probe 只构造临时 payload。依赖
信息索引
当前重点代码:
internal/adapter/feishu/projector.gointernal/adapter/feishu/projector_exec_command_progress.gointernal/adapter/feishu/projector_thread_history.gointernal/adapter/feishu/projector_exec_command_progress_split.gointernal/adapter/feishu/projector_final_split.gointernal/adapter/feishu/card_renderer.gointernal/adapter/feishu/card_header_context.go相关文档:
docs/general/feishu-card-api-constraints.mddocs/general/feishu-card-content-context-guidelines.mddocs/general/feishu-card-ui-state-machine.md实现参考
建议先做一个内部 builder,优先覆盖普通 card operation,不碰非 card operation:
Operation,默认OperationSendCard;有 message id 时转OperationUpdateCard并清空 reply lane;新发送时按 event delivery lane 应用 reply。GatewayID、SurfaceSessionID、ChatID、cardEnvelopeV2、rawCardDocument(...)。建议先用结构测试锁定:普通 projector path 不应再在
projectEventBase内重复设置cardEnvelope: cardEnvelopeV2与card: rawCardDocument(...)多次;允许 list 要明确。检查参考
目标检查:
go test ./internal/adapter/feishu -run 'Projector|ExecCommandProgress|ThreadHistory|Final|PathPicker|TargetPicker|Request|Selection|Plan|Notice' -count=1go test ./internal/adapter/feishu -count=1go test ./...bash scripts/check/go-file-length.sh && git diff --checkGuardrail 检查:
docs/general/feishu-card-api-constraints.md:确认没有增加卡片体积、更新频率或交互模式。docs/general/feishu-card-content-context-guidelines.md:确认没有把动态文本新塞回 raw markdown。docs/general/feishu-card-ui-state-machine.md:确认 callback payload、inline replace、old-card/freshness 语义未变化;若有变化必须同步文档。收尾参考
issuectl close-plan后再finish --close。执行决策
执行快照
status:implementable-now,运行 issue workflow prepare/lint;通过后按 TDD/结构测试开始实现。328f93fa,工作区干净。internal/adapter/feishu/projector*.go的 Operation 构造现状;先补结构测试再实现 builder。