- 严格分层:
Domain / Application / Infrastructure / Host,API仅做宿主与组合,不承载核心业务编排。 - 统一投影链路:CQRS 与 AGUI 走同一套 Projection Pipeline,统一入口、一对多分发,避免双轨实现。
- 投影编排 Actor 化:Projection 的会话、订阅、关联关系等运行态必须由 Actor 或分布式状态承载;禁止在中间层通过进程内注册表/字典持有事实状态。
- 明确读写分离:
Command -> Event,Query -> ReadModel;异步完成通过事件通知与推送,不在会话内临时拼装流程。 - 严格依赖反转:上层依赖抽象,禁止跨层反向依赖和对具体实现的直接耦合。
- 命名语义优先:项目名、命名空间、目录一致;缩写全大写(如
LLM/CQRS/AGUI);集合语义使用复数。 - API 字段单一语义:一个字段只能表达一个含义,禁止同字段承载“名称查找 + inline 内容”等双重语义。
- 核心语义强类型:凡是影响业务语义、控制流、稳定读取且仓库内生产方/消费方可控的数据,必须建模为
proto field / typed option / typed sub-message,禁止塞入通用 bag。 Metadata命名受限:禁止内部无语义、泛化的Metadata命名;只有真正的元模型、明确的开放扩展边界、外部协议/第三方 SDK/基础设施标准术语才允许保留Metadata,其余内部场景必须按职责命名,如Headers / Annotations / Items。- 不保留无效层:空转发、重复抽象、无业务价值代码直接删除。
- 变更必须可验证:架构调整需同步文档,且
build/test通过。
- 适用范围仅限生产环境(线上集群、线上 namespace、线上服务实例)。本地容器、本地
kind/minikube、开发者自有docker compose等本地运行环境不受本条约束,可自由启停、重建、进入调试。 - 调试生产环境时,禁止 LLM/agent 对生产 pod 及其所在集群执行任何变更类操作:
kubectl exec / delete / rollout restart / scale / patch / edit / apply / cordon / drain / port-forward / cp,以及等价的helm、云控制台操作、CI 重启入口。 - 生产只读观测仍然允许:
kubectl logs / get / describe等只读命令与明确只读的日志 skill(如/aevatar-prod-logs)可继续使用;一旦某个动作会改变生产 pod 的状态、生命周期、配置或网络可达性,即属于越界。 - 越界时唯一允许的动作是上报:立即停止操作,整理现象、时间窗、涉及的 namespace/service/pod、已获取的只读证据与建议处置动作,通知 dev,由 dev 告知运维执行;禁止 agent 代为执行,禁止“先重启一次试试”。
- 阻塞必须显式承认:若某个结论只能在生产 pod 侧变更后才能验证,必须在输出中写明“待运维执行 X 后确认”,不得用推测冒充验证,也不得为绕过阻塞改走其他变更路径。
- 禁止绕道与自我豁免:不得通过远程 exec、sandbox、云控制台代理或其他间接集群管理手段,执行上述被禁止的生产 pod 操作;任务紧急或会话内口头“这次可以重启”均不构成豁免。
- 本规则不限制标准源码交付:允许按本仓库既定流程 commit/push 到约定分支,并由既有 CI/CD 自动完成构建与部署;任务明确要求推送时应正常执行,无需把源码 push 视为生产 pod 操作或等待运维代为执行。该许可不允许 agent 在流水线之外直接操作生产 pod,也不允许绕过流水线门禁。
- 禁止用
Build / Bind / Invoke / Observe作为 Studio workflow 的全局产品生命周期模型。仓库中若仍出现BuildReady / BindReady / binding / invoke / observe,只能按所在资源边界解释为局部状态或动作,不得反推出一条线性 workflow 生命周期。 memberId、workflowId、publishedServiceId是隔离身份,不是同一资源在不同阶段的别名:memberId表示 Studio team member authority;workflowId表示 workspace workflow draft / definition document;publishedServiceId表示 callable service runtime identity。- 禁止把
workflowId传给 member API,禁止把memberId传给 workflow draft API,禁止把二者冒充publishedServiceId。任何身份转换都必须来自明确后端 contract/read model,不能靠字符串规则、前缀、相等关系或路由位置猜测。 - 正常业务语义下不得假设
memberId === workflowId。若历史 repair/migration/materialization 代码中出现从 workflow draft 派生 member 的逻辑,只能作为命名明确的后台修复路径理解,不能进入新前端、路由、API helper、普通业务逻辑或测试 fixture。 - 凡是需要在
workflowId / memberId / publishedServiceId之间做身份判别的值,都不得命名为其中任何一个确定身份;必须先命名为routeIdentityCandidate/bindingIdentityCandidate等候选身份,并在确定来源后一次性解析成具体 ID。解析后的确定身份才能进入对应 API。 - 测试 fixture 必须使用不同 ID 形态暴露错传:例如
memberId = "m-alpha"、workflowId = "wf-alpha"、publishedServiceId = "svc-alpha"。禁止用同一个字符串或同一前缀规律同时代表多个身份。
- 单一主干,插件扩展:系统只保留一条权威业务主链路;新增能力以插件/模块方式挂载,避免平行“第二系统”。
- 内核最小化:核心层只承载稳定业务不变量与通用机制;波动能力下沉到扩展层,减少核心侵蚀。
- 扩展对称性:内建能力与扩展能力遵循同一抽象模型与生命周期协议,不为扩展单独再造体系。
- 抽象优先:依赖行为契约与语义接口,而非具体类型与实现细节;组合面向能力,非面向实现。
- 边界清晰:协议适配、业务编排、状态管理分别归属不同层;每层只做本层职责,禁止跨层偷渡语义。
- 事实源唯一:跨请求/跨节点的一致性事实必须有唯一权威来源(Actor 持久态或分布式状态),不依赖进程内偶然状态。
- 数据语义分层:传输元数据用于追踪与上下文;业务语义以领域事件与读模型为准,不混用语义层次。
- 强类型内核,窄扩展点:内核稳定语义默认强类型;只有在确有插件/第三方/跨边界透传需求时才保留 bag。边界 bag 可以保留
Metadata,但内部 bag 不得退回到泛化Metadata。 - 渐进演进:开发期可用本地/内存实现提升反馈速度,但生产语义必须能无缝迁移到分布式与持久化实现。
- 删除优于兼容:重构以清晰正确为第一目标;无业务价值或重复层应直接删除,不为历史包袱保留空壳。
- 治理前置:架构规则必须可自动化验证(门禁、测试、文档一致性),避免依赖口头约定。
- 先判定是否属于核心语义:凡是影响业务语义、控制流、稳定查询或稳定决策的数据,直接建模为强类型字段;不要因为“未来可能还会扩展”就先放 bag。
- bag 只用于明确的开放扩展边界:生产方和消费方不完全同源、允许第三方追加字段、且缺失字段不应破坏主流程时,才允许保留 bag。
- bag 必须服从边界职责:command 头用
Headers,业务完成注解用Annotations,链内临时状态用Items;真正的边界扩展袋可以保留Metadata,或使用更精确但不失真的边界名称。 Metadata看的是对象语义边界,不是“是否跨层”:如果它是request/response/event/command自身的正式扩展信息,可以叫Metadata;如果它只是 middleware / hook / pipeline 执行过程里的进程内临时共享上下文,即使会跨多个处理层,也应叫Items。- 外部新增信息不适合现有 bag 时,新增一个按职责命名的字段或子消息;不要把它硬塞进不匹配的现有 bag,也不要把内部明确语义重新降级成通用
Metadata。 - 不要为了消灭单词而窄化语义:如果一个边界扩展袋天然就是开放式 metadata,就保留
Metadata,不要硬改成会缩窄含义的名字。 - protobuf 字段演进是默认路径:仓库内可控的稳定语义优先通过新增
proto field / typed sub-message / typed option演进,而不是先用字符串 key 兜底。 - 外部名称尊重边界:第三方 SDK、外部协议或基础设施标准术语如果原生使用
Metadata,允许在 adapter/boundary 保留该命名;但进入仓库内部主模型后,必须映射回 typed 字段或按职责命名的内部结构,禁止把外部Metadata语义原样扩散到内核。
- 统一包络不等于统一语义:
Envelope只是 Actor System 的统一消息包络,可承载command/reply/internal signal/domain event/query;是否可持久化、可投影、可对外观察必须由消息契约显式定义,禁止因“都走 Envelope”而混淆语义。 - 已提交领域事件必须可观察:write-side 一旦完成 committed domain event,必须把该事实送入统一 observation/projection 主链;禁止只落 event store / actor state 而不进入可观察流,再由上层用 query fallback 猜测完成态。
- 业务消息语义与查询语义必须分离:
actor1 -> event envelope A -> actor2 -> event envelope B -> actor1这类链路是业务消息协议,由参与 actor 自行协商;actor3 -> query -> actor2 readmodel是查询语义,只能读取 actor2 已物化事实,二者的契约、一致性要求、完成判定都不得混用。 - 禁止 generic actor query/reply:内部模块不得为“读取另一个 actor 当前状态”定义通用
Query*Requested -> *Responded协议,也不得保留通用request-reply client作为兜底读取手段;查询默认只能落到 read model,跨 actor 交互默认只能是 command/event 驱动的业务协议。 - 禁止用 stream request-reply 冒充 RPC:
stream用于事件分发与观察,不用于在 actor turn 内实现同步 query/reply;凡是“先发消息、再等另一条 reply 消息回来”的链路,都必须改成正式 read model 查询,或改成 continuation 化的事件协议。 - 命令骨架必须内聚:标准命令生命周期应收敛为
Normalize -> Resolve Target -> Build Context -> Build Envelope -> Dispatch -> Receipt -> Observe;业务模块只负责目标解析、载荷映射和结果映射,禁止各能力入口各自拼装一套流程。 - 传输载体必须可替换:直接远程调用、
IActorDispatchPort、stream/broker 都只是消息传输机制;上层依赖投递契约,不依赖具体载体,确保链路可从直投切换为异步传输而不污染应用语义。 - 投递语义必须 runtime-neutral:
publish/send统一表示“进入目标 actor inbox 等待处理”,不得因目标是self或底层 runtime 差异而退化为 inline dispatch;需要立即执行时必须走独立dispatch契约,禁止在基类、业务层或中间适配层绕过标准 publisher 直接操作stream/provider/grain等底层传输对象。 - Runtime 与 Dispatch 必须分责:
Runtime负责 actor 的 lifecycle / topology / lookup,Dispatch Port负责消息投递;禁止把创建、查询、投递、观察等职责揉进一个全能接口。 - ACK 语义必须诚实:同步返回只能承诺已经真实达到的阶段,默认应是
accepted for dispatch + stable command id;committed、read-model observed等更强保证必须通过独立契约或异步观察获取,禁止在弱语义 ACK 中暗示强保证。 - 追踪标识与目标身份必须分离:
commandId/correlationId用于追踪一次请求,actorId用于标识处理实体;禁止把追踪 ID 与目标身份混成同一语义,也不得假设二者天然一一对应。 - 命名必须跟随职责语义:接口、类型、目录命名应描述职责与边界,而不是绑定暂时实现路径;一旦底层实现可替换,命名不得泄露
runtime/stream/protocol偶然细节。
- 运行时形态不是业务事实:不得把本地实例类型、代理类型、对象可见结构当成业务绑定依据;业务事实必须来自 actor-owned contract 或 read model。
- 身份与事实必须分离:稳定 ID 只负责寻址与复用键,不承载可变业务事实;可变绑定必须显式建模、显式读取。
- 读写边界不能混合:写侧端口只负责 lifecycle / command;读取必须走窄 query contract 或 projection,禁止在 Application / Infrastructure 直接读取 write-model 内部状态。
- 禁止用侧读冒充 query:当系统缺少正式 query/reply 语义时,禁止通过直读其他 actor 的 event store、持久态快照或任意“事实重建器”在中间层拼装查询结果;这类跨 actor 读取必须回到 actor-owned contract、projection,或显式的事件化 continuation。
- 禁止 query-time replay:
QueryPort / QueryService / ApplicationService / Infrastructure read adapter不得在请求路径中直接读取IEventStore、重放 committed events、临时重建 state mirror/document 后立刻返回;事实回放只能属于正式 projection/materialization 流程,不属于 query 执行流程本身。 - read model 物化必须脱离 query 调用栈:如果查询需要“先刷新 read model”,刷新动作也必须通过正式 projection 会话、后台 materializer、写侧预挂接 projection,或显式的 read-model 更新管线完成;禁止在 query 方法里同步补跑一遍 ES/materialization 逻辑。
- projection 只消费 committed 事实:projection/read model 必须基于 committed domain event 或其同源 durable feed 构建;禁止订阅入站 command、self continuation 或 actor 运行时偶然结构去推测业务完成态。
- 默认路径必须先定义资源语义:任何“缺失即创建”的默认策略,都必须同时定义稳定归属、复用规则和清理责任;禁止生成不可达、不可复用、不可回收的隐式资源。
- 本地可用不等于分布式正确:凡是依赖本地 runtime 偶然细节才能成立的实现,都视为未完成设计,必须收敛到 runtime-neutral 协议。
- 抽象一旦能被滥用,就等于设计未完成:若某个通用接口允许绕过读写分离、绕过 actor 边界或绕过权威事实源,应继续收窄,而不是靠约定克制。
- 单一权威拥有者:每个稳定业务事实都必须有唯一
actor作为权威拥有者;committed event store + actor state是唯一真相,read model只是查询副本,禁止反向定义业务事实。 - 查询始终走 readmodel:对外查询默认只能读取 readmodel;不得把 actor 内部状态、state mirror payload 或 event replay 暴露成查询主路径。
EventEnvelope是唯一投影传输壳:业务消息与投影消息都只使用EventEnvelope;二者区别只能由强类型payload契约表达,禁止再引入第二层投影包络。- 业务协议一致性与查询一致性分层处理:actor 间业务消息链路只对“消息已接收、事件已提交、协议已推进到某一步”负责;query/readmodel 链路只对“某个
StateVersion已物化可见”负责,禁止把 readmodel 可见性当成 actor 间业务协商完成,也禁止把业务 ACK 冒充成 query 新鲜度保证。 - 一权威状态可物化多个 readmodel:默认模型是
one authoritative actor state -> many actor-scoped current-state readmodels;不同 readmodel 只表达同一 actor 当前态的不同查询形态,不得各自重新计算第二套业务状态机。 - 不是每个 actor 都必须产出 readmodel:只有存在稳定消费场景时,才为该 actor 增加 readmodel;没有消费场景的 actor,不必新增 readmodel,也不必额外发布 projection payload。
- 新增 readmodel 必须先声明消费场景:同一 actor 可以对应多个 readmodel,但每个 readmodel 都必须有明确的消费方、查询入口、返回 DTO 或 UI/搜索/图查询场景;没有稳定消费场景的 readmodel 不得新增。
- readmodel 根契约必须收紧:
readmodel在仓库内默认就表示actor-scoped current-state replica;不符合这条约束的对象不得继续挂在通用 readmodel/projection 主链下,必须改名并降级为artifact/export/log,或由新的aggregate actor拥有。 - 聚合必须 actor 化:跨 actor 聚合、汇总、关联、编排结果若具有稳定业务语义,必须建模为新的
aggregate actor;禁止把这类语义长期放在通用history/aggregate view或 query-time 拼装层中。 - projection 负责读模型物化:Projection Pipeline 的默认职责是消费 actor 发布的
EventEnvelope<CommittedStateEventPublished>,再基于其中的state_event + state_root物化到多个 document/index/search/graph store;禁止把 projection 作为第二套通用业务计算框架。 - 正常路径禁止 replay:正常 query path 和正常 projection path 都不得依赖
event replay / rebuild / backfill;replay只属于后台修复、迁移、灾难恢复,不属于线上读路径。 - actor 负责语义,projection 负责物化:凡是可以由 actor 直接确定的当前态查询语义,应尽量前移到 actor 内,并在 committed 后通过
EventEnvelope<CommittedStateEventPublished>暴露state_root;projection 只负责校验、覆盖写入、索引和分发,不负责在读侧重新推导当前态。 - actor 不直接拥有存储实现:actor 可以发布
EventEnvelope<CommittedStateEventPublished>;其中state_root是当前态 readmodel 的统一 committed 输入,但 actor 不得直接承担document store / graph store / query provider / index schema的物化职责;这些仍属于 projection/runtime/provider 边界。 - 版本必须跟权威源对齐:每个 actor-scoped readmodel 的版本必须来自同一个权威 actor 的 committed version 或其等价水位;禁止使用本地
projection counter、本地StateVersion++或其他派生计数冒充权威版本。 - 覆盖复制优先:默认 readmodel 写入语义是“基于权威源版本的单调覆盖”;旧读模型不得覆盖新读模型,重复写入必须幂等,冲突版本必须显式报错。
- 不默认保留通用历史视图:
timeline / audit / report / analytics不是默认 readmodel 形态;如确有业务价值,要么降级为 artifact/export,要么由专门 actor 拥有,不得继续伪装成主查询模型。 - 查询诚实性优先于伪强一致:readmodel 可以最终一致,但必须诚实暴露自己的权威源版本或刷新戳;禁止在
accepted ACK、弱读结果或局部物化结果上暗示“已经强一致”。 - 不得 query-time priming:查询前若需要先“确保投影存在/刷新 read model”,该动作必须在显式 activation、lease、binder 或后台物化流程中完成;禁止在 query 方法内同步补投影。
- 状态镜像契约必须面向查询语义:若某个当前态 readmodel 采用
state mirror payload作为输入,该契约必须是面向读侧的稳定强类型契约,而不是 actor 内部 state 的原样 dump;内部运行态、临时控制字段、仅执行期可见信息不得默认扩散到 projection 主链。
- 默认优先
run/session/task-scoped actor:凡是一次执行、一次会话、一次临时编排即可完成职责的能力,默认建模为短生命周期 actor;静态GAgent、workflow、scripting 只要协议一致,都可作为这类 actor 的实现来源。 - 长期 actor 只保留给事实拥有者:只有
definition/catalog/manager/index/checkpoint这类需要长期持有权威状态、串行推进事实的对象,才允许设计为长期 actor。 - 单线程 actor 不承担热点共享服务:禁止把 actor 当成高并发无状态公共服务容器;单线程 actor 用于维护状态边界和顺序语义,不用于承接无限扩张的共享吞吐。
- 升级默认前滚,不热替换存量 run:脚本、workflow、静态实现升级时,默认语义是“旧 run 留在旧 actor/旧定义,新的创建请求走新实现”;除非明确设计了状态迁移契约,否则禁止对正在运行的 actor 做原地实现替换。
actorId对调用方是不透明地址:允许更换其背后实现,但调用方不得解析前缀、类型名或实现来源,也不得把actorId字面模式当成业务判断条件。
- 单线程事实源:Actor/模块运行态只能在事件处理主线程修改;禁止在模块内使用
lock/Monitor/ConcurrentDictionary作为并发补丁来维护事实状态。 - 回调只发信号:
Task.Run、Timer、线程池回调不得直接读写运行态,也不得直接推进业务分支;只能发布“内部触发事件”(如 timeout/retry fired)。 - 业务推进内聚:工作流推进(成功/失败/分支/重试)必须在 Actor 事件处理流程内完成,保证顺序性与可重放性。
- AI 对话主链必须流式化:仓库内新增或修改 AI 对话执行路径时,禁止使用
ChatAsync作为正式主链;必须使用ChatStreamAsync作为唯一权威执行入口,使文本、reasoning、tool call、tool result、完成态都沿同一条流式链路对外发布。ChatAsync仅可用于明确的非交互式离线场景,且不得用于 CLI / AGUI / Scope Service / NyxID Chat / Workflow Chat 等任何面向用户的实时会话入口。 - self continuation 必须事件化:Actor 需要“下一拍继续”时,必须通过标准 self-message 进入自身 inbox,再由 Actor 事件处理流程消费;禁止新增绕过消息抽象的临时 helper,或依赖特定 runtime 的 self-dispatch 偶然行为来推进业务。
- 延迟与超时事件化:所有
delay/timeout/retry backoff统一采用“异步等待 -> 发布内部事件 -> Actor 内消费并对账”的模式,禁止回调线程直接改状态。 - 跨 actor 等待必须 continuation 化:Actor 向其他 actor 请求事实或动作时,必须采用“发送请求事件 -> 结束当前 turn -> 由 reply event 或 timeout event 唤醒自身继续处理”的模型;禁止在当前 turn 内同步等待 reply,也禁止用本地快照读取、event store 侧读或伪 RPC 绕过这一约束。
- query 与 command 的 actor 边界必须分清:actor 若只是想读取另一侧已提交事实,就不应给对方 actor 发 query 消息,而应读取该事实对应的 read model;只有确实需要对方参与一次新的业务交互时,才允许发送事件/命令,并由 reply/timeout continuation 继续推进。
- 显式对账:内部触发事件必须携带最小充分相关键(如
run_id + step_id),由 Actor 内做活跃态校验,拒绝陈旧事件。 - 无锁优先:若设计需要加锁才能正确,优先判定为“破坏 Actor 边界”,应先重构为事件化串行模型,再实现功能。
- 禁止在中间层维护
entity/actor/workflow-run/session等 ID 到上下文或事实状态的进程内映射(Dictionary<>、ConcurrentDictionary<>、HashSet<>、Queue<>)。 - 允许 Actor 内部运行态集合保留在内存或 Actor
State(例如module_runtime);前提是该状态不作为跨节点事实源,并且按生命周期及时清理。 - 需要跨 Actor/跨节点一致性的显式状态时:优先写入 Actor 持久态;无法放入 Actor 时,使用抽象化分布式状态服务,不允许在中间层落进程内缓存作为事实源。
- 明确例外:
InMemory持久化/基础设施实现仅用于开发与测试,可保留,但不得外溢到中间层业务语义。 - 方法内局部临时集合可用,但不得提升为服务级/单例级事实状态字段。
- 投影端口规范:禁止通过
actorId -> context反查方式管理生命周期,改为显式lease/session句柄传递。
- 所有序列化与反序列化操作统一使用
Protobuf,尤其是State、领域事件、命令、回调载荷、快照、缓存载荷、跨 Actor/跨节点内部传输对象。 - 禁止在
Actor State、WorkflowRun State、模块持久态、投影检查点或其他事实存储中使用JSON/XML/自定义字符串格式作为内部序列化方案。 - 若外部协议或第三方接口必须使用
JSON,仅允许在Host/Adapter边界做临时协议转换;进入应用层、领域层、运行时层后必须恢复为Protobuf对象,且内部落盘、持久化、发布、重放仍统一使用Protobuf。 - 新增状态对象、事件对象、持久化载荷时,先定义
.proto契约并生成类型,再接入实现;禁止先写临时序列化结构、后补Protobuf。
src/:生产代码,按能力与分层组织(Aevatar.Foundation.*、Aevatar.AI.*、Aevatar.CQRS.Projection.Core.Abstractions/Runtime/Stores.Abstractions、src/workflow/Aevatar.Workflow.*、Aevatar.Host.*)。test/:与src/对应的测试项目(单元、集成、API)。docs/:架构与设计文档。tools/ci/:CI 门禁脚本;tools/docs/:文档 lint 与索引工具。apps/aevatar-console-web/:前端控制台工作目录,必须保留。
dotnet restore aevatar.slnx --nologo:还原依赖。dotnet build aevatar.slnx --nologo:编译全部项目。dotnet test aevatar.slnx --nologo:运行全量测试。- 端口约束:仓库内禁止出现
5000端口,这会与系统冲突;所有 Web API 服务同时禁止使用5000与5050端口。新增或修改 Host、启动脚本、launchSettings、README、CLI 示例、测试样例、SDK 默认值或任何代码/脚本文案时,不得写入5000,并必须保持代码、脚本与文档一致。若需要提供默认示例,统一使用其他端口(当前文档默认使用5100)。 bash tools/ci/architecture_guards.sh:本地执行 CI 架构门禁(与 CI 同步)。bash tools/ci/workflow_binding_boundary_guard.sh:单独执行 workflow binding 边界门禁。bash tools/ci/query_projection_priming_guard.sh:校验 query/read 路径不触发 projection priming 或生命周期操作。bash tools/ci/projection_state_version_guard.sh:校验 current-state readmodel 路径不发明本地 state version。bash tools/ci/projection_state_mirror_current_state_guard.sh:校验新的*CurrentState*Projector不回读同类 readmodel。bash tools/ci/projection_route_mapping_guard.sh:单独执行“事件类型 -> reducer 路由映射正确性”静态门禁。bash tools/ci/solution_split_guards.sh:执行分片构建门禁(Foundation/AI/CQRS/Workflow/Hosting)。bash tools/ci/test_solution_ownership_guard.sh:校验测试项目只由aevatar.slnx或慢测守卫拥有。bash tools/ci/slow_test_guards.sh:执行独立慢测门禁。bash tools/docs/lint.sh:执行文档 lint(也由架构门禁调用)。dotnet test test/Aevatar.Workflow.Host.Api.Tests/Aevatar.Workflow.Host.Api.Tests.csproj --collect:"XPlat Code Coverage":单项目覆盖率。dotnet run --project src/workflow/Aevatar.Workflow.Host.Api:启动 Workflow API(/api/chat、/api/ws/chat)。
- 遵循
.editorconfig:UTF-8、LF、4 空格缩进、去除行尾空白。 - 保持
项目名 = 命名空间 = 目录语义,推荐模式:Aevatar.<Layer>.<Feature>。 - 先抽象后实现;优先接口注入;避免跨层直接调用。
- 公开 API 与领域对象命名要表达业务意图,避免含糊词。
- 把不需要的直接删除, 无需考虑兼容性
- 前端工作边界是
apps/aevatar-console-web/。普通前端任务不得顺带修改src/、test/、tools/ci/或外部同级仓库;确需跨边界修改时,必须先明确扩展任务范围。 - 处理
apps/aevatar-console-web/下的文件前,必须完整阅读apps/aevatar-console-web/AGENTS.md。该文件负责具体前端开发、运行、设计与验证流程。 - 添加、修改、选择或运行前端测试前,还必须完整阅读
apps/aevatar-console-web/docs/testing-policy.md。 - 根文件中的跨栈身份语义、仓库级架构、测试门禁、Git、文档和外部依赖规则继续适用;子目录规则只能细化或收紧,不能放松这些约束。
- 测试栈:xUnit、FluentAssertions、
coverlet.collector。 - 测试文件命名:
*Tests.cs,单文件聚焦一个行为域。 - 行为变更必须补测试;重构不得降低关键路径覆盖率。
- 自动生成代码(脚手架生成)不纳入代码覆盖率考核,不将覆盖率作为其合并门禁。
- 轮询等待门禁(
tools/ci/test_stability_guards.sh)为强制项:测试中禁止随意引入Task.Delay(...)/WaitUntilAsync(...)。 - 若确属跨进程/跨节点最终一致性探测且无法改为确定性同步(如
TaskCompletionSource/Channel),必须将测试文件路径显式加入tools/ci/test_polling_allowlist.txt,并在变更说明里写明原因。 - 涉及测试新增/修改时,提交前必须执行:
bash tools/ci/test_stability_guards.sh。 - 涉及 workflow actor binding、definition identity、resume/signal 路径的变更时,提交前必须执行:
bash tools/ci/workflow_binding_boundary_guard.sh。 - 涉及 query/read port、projection priming、projection lifecycle 与 query 边界的变更时,提交前必须执行:
bash tools/ci/query_projection_priming_guard.sh。 - 涉及 current-state readmodel、state version 或 state mirror projector 的变更时,提交前必须执行:
bash tools/ci/projection_state_version_guard.sh与bash tools/ci/projection_state_mirror_current_state_guard.sh。 - CI 守卫(full-scan):禁止
GetAwaiter().GetResult();禁止TypeUrl.Contains(...)字符串路由;禁止Aevatar.Workflow.Core依赖Aevatar.AI.Core;禁止中间层actor/entity/run/sessionID 映射 Dic 事实态字段(仅扫描 Projection/Application/Orchestration 中间层);禁止投影端口回退到actorId反查上下文模型;要求新增非抽象Reducer类必须被测试引用;要求事件类型到 reducer 的路由采用TypeUrl派生 + 精确键路由(由tools/ci/projection_route_mapping_guard.sh专项校验,含EventTypeUrl分组与TryGetValue命中)。
GIT分支命名使用固定格式,不允许任何变体:<type>/YYYY-MM-DD_<purpose>。<type>必须从固定集合中选择:feat、fix、refactor、docs、test、chore。- 日期必须使用前置定长格式
YYYY-MM-DD;禁止使用2026-3-12、20260312、03-12-2026、后置日期或日期时间混合格式。 <purpose>只允许使用小写字母、数字和连字符-,应简短表达单一目标;禁止空泛名称如test、tmp、misc。- 标准示例:
feat/2026-03-12_gagent-protocol-first-plan。 - 提交信息使用祈使句并聚焦单一目的(如:
Refactor projection pipeline)。 - PR 必须包含:问题与方案、影响路径、验证命令与结果、相关文档更新。
- 若涉及架构调整,需同时更新
docs/架构文档与示意图。
- mermaid 默认指令(所有图统一加在代码块首行):
%%{init: {"maxTextSize": 100000, "flowchart": {"useMaxWidth": false, "nodeSpacing": 10, "rankSpacing": 50}, "themeVariables": {"fontSize": "10px"}}}%% - mermaid 的标签用引号包起来, 如 A2[“RoleGAgent”], 不要 A2[RoleGAgent].
sequenceDiagram默认使用紧凑布局(优先收紧actorMargin/messageMargin/diagramMarginX/diagramMarginY与文案长度),避免图整体过大。- 禁止通过固定大宽度样式撑大时序图(如
min-width: 2200px、width: max-content强制放大);优先按容器宽度渲染。 - 需要查看完整细节时,使用外层容器横向滚动(
overflow-x: auto),不要放大图本体。 - 文件名中带时间/日期的文档,时间戳必须放在文件名开头,禁止后置时间戳。
- 时间戳必须使用固定字长格式:仅日期用
YYYY-MM-DD-(10 位日期 + 1 位连接符),日期时间用YYYY-MM-DD-HH-mm-ss-(19 位日期时间 + 1 位连接符)。 - 禁止在同类文档中混用
2026-3-9、20260309、03-09-2026、尾部-2026-03-09等变长或后置格式;统一使用前置定长格式,例如2026-03-09-workflow-architecture.md。 - 默认将打分/审计文档生成到
docs/audit-scorecard/目录。 - 工作文档不需要添加到解决方案(
aevatar.slnx)。
本仓库集成以下同级目录下的外部仓库。是否需要检查外部源码必须按任务性质判定,不能仅因任务或文件中出现相关关键词就强制检查或 clone 仓库。
| 关键词 / 涉及范围 | 外部仓库路径 | 说明 |
|---|---|---|
NyxID、NyxId、nyxid 相关服务/工具/GAgent |
../NyxID |
NyxID 身份服务仓库,包含 NyxID 协议、服务端实现与 SDK |
chrono-storage、ChronoStorage、存储 API/客户端 |
../chrono-storage |
ChronoStorage 存储服务仓库,包含 API 定义、存储引擎与客户端 SDK |
chrono-ornn、Ornn、编排/运行时引擎 |
../Ornn |
Ornn 编排引擎仓库,包含运行时核心与调度逻辑 |
- 源码级集成与问题排查:新增或修改本仓库中面向外部依赖的 adapter、协议映射、SDK 调用代码、认证流程,或者调查已发布行为与契约不一致时,必须检查对应外部仓库的相关源码、proto 和公开 SDK,禁止仅凭本仓库内的抽象猜测外部行为。
- 已发布能力的正常使用:仅通过官方 CLI 配置 service、使用已安装且版本化的 skill、按照正式 API/CLI 文档调用稳定能力,或通过已发布产品界面完成登录、凭据绑定、代理请求时,以对应的已发布契约为准,不强制要求本地存在外部源码仓库。
- 契约歧义升级:当官方文档不足、CLI 行为不明确、版本化契约无法解释实际结果,或出现疑似外部 bug 时,才从已发布契约升级到外部源码核验。
- 先按上述适用边界判断任务属于“已发布能力的正常使用”还是“源码级集成与问题排查”;关键词命中本身不构成源码检查理由。
- 属于已发布能力正常使用时,直接依据对应版本的官方 CLI、versioned skill、正式 API 文档或产品 UI 契约继续工作,无需检查同级源码目录。
- 需要源码核验时,再执行
ls ../NyxID、ls ../chrono-storage、ls ../Ornn(按需)确认对应目录是否存在。若目录存在,阅读与任务直接相关的接口定义、proto、SDK 公开 API 和实现;若任务针对特定发布版本,应优先核对对应 tag 或 commit,不能用默认分支的最新行为替代已发布契约。 - 仅在确实需要源码核验时处理缺失仓库:若
../NyxID不存在,直接执行cd .. && git clone https://github.com/ChronoAIProject/NyxID.git;若../Ornn不存在,直接执行cd .. && git clone https://github.com/ChronoAIProject/Ornn.git。在上一级目录创建对应的同级仓库后继续核验相关源码,无需先询问用户;若缺少其他外部仓库且未明确其官方 clone 地址,再向用户说明并询问是否 clone 或提供替代方案。不得让普通 CLI、service 配置、versioned skill 或产品 UI 使用任务因缺少源码目录而中断。
- 本仓库的功能实现禁止依赖外部仓库的新增 / 修改。NyxID、chrono-storage、chrono-ornn 等都是独立产品,aevatar 的需求不是它们的功能路线图。
- NyxID 不是 aevatar 的 LLM provider 后端、不是 aevatar 的存储后端、不是 aevatar 的任何专用基础设施——它"恰好能"服务这些用途,aevatar 必须只使用其当前已发布的公开能力实现自己的功能。
- 出方案时禁止包含"在 NyxID/chrono-* 加端点"、"在 NyxID/chrono-* 改 schema"、"等外部仓库支持新协议"等步骤。如果当前外部能力不足,方案必须改成"在本仓库内绕开"或"不做这个功能",不能成为对外部团队的 feature request。
- 唯一允许给外部仓库提 issue 的情况:在外部仓库中观察到明确的 bug(行为与其文档/代码注释/已发布契约不一致,或导致明显错误状态)。提之前先确认本仓库这边没有用错。
- review/方案讨论时如果对方建议改外部仓库,先反问"现有 surface 能不能做到?",倾向于在本仓库内部解决;只有把所有本仓库内的可行路径都排除后,才能讨论外部 issue。