Преглед на файлове

docs(openspec): 立项 define-customer-tiering 客户接入三档分流

新 capability customer-tiering, 约束新客户接入决策:

- R1 三档矩阵 A 公共托管(mjava-pro) / B 独立子模块(mjava-{客户}) / C 独立 git 仓
- R2 决策流程: 从 A 起评, 按硬指标升档; A→B 触发(需写 Java 代码 / 需私有 schema / 需独立部署) / B→C 触发(客户机房 / 源码进客户 git / 基座大幅分叉); 升档单向不可逆
- R3 升档迁移路径: A→B 双轨期 1-2 周; B→C fork 基座 + 移出 mjava-ai
- R4 A 档(mjava-pro)入驻清单 8 必填字段 + DB/缓存/日志隔离边界
- R5 mjava-com 暴露白名单制 + 危险动作默认禁开 + 60 QPS 默认限流 + 强制审计
- R6 C 档独立仓与 mjava-ai 解耦边界 (R4 grep 互不联动 + fork 不依赖 jar)
- R7 README/CLAUDE.md 同步更新约束

仅产出规范, 不动 Java 源; 与 add-mjava-pro/add-mjava-com 互补不阻塞
malk преди 1 седмица
родител
ревизия
9444e46cf5

+ 100 - 0
openspec/changes/define-customer-tiering/design.md

@@ -0,0 +1,100 @@
+# Design
+
+## 关键决策
+
+### 1. 为什么是三档而不是两档或四档
+
+**两档(公共/独立)的问题**:把「中等复杂度独立部署」一刀切要么进公共池(污染共享 jar),要么直接独立仓(运维成本高、与基座脱节)。中等客户无处安放。
+
+**四档(极轻/轻/中/重)的问题**:极轻和轻的边界在实操里区分意义小,都是「不写 Controller,只配凭据」。增加判定成本不增加决策价值。
+
+**三档(A/B/C)的依据**:以「是否需要新写 Java 代码」(A→B 分界)和「是否在我们机房部署」(B→C 分界)作两个硬切点。两个切点正交、可观测、不依赖主观判断。
+
+### 2. 各档物理落点约定
+
+| 档 | 物理位置 | artifactId / git 仓 | 部署形态 |
+|---|---|---|---|
+| A | `mjava-pro/` 共享 jar | `mjava-pro`(仓内) | 一个 jar 跑 N 客户,端口 9010 |
+| B | `mjava-ai/mjava-{客户}/` | `mjava-{客户}`(仓内,独立子模块) | 一客户一 jar,端口由客户分配 |
+| C | 独立 git 仓(不在 mjava-ai) | `mjava-{客户}`(独立仓 root artifact) | 客户自管部署 |
+
+**B 档命名**:直接用客户拼音/英文短名,如 `mjava-akds` / `mjava-sikuwh`。禁带客户中文全名或冗长缩写。
+
+**C 档参照**:现有 `cur/mjava-guangming/`(4 模块仓) + akds(独立仓)。命名沿用历史习惯,本 change 不追溯改名。
+
+### 3. A→B→C 升级路径的硬指标
+
+判定矩阵以「**触发即升档**」为准则,触发条件必须可观测(不靠主观判断「这个客户复杂」):
+
+| 升档 | 触发条件(任一即触发) |
+|---|---|
+| A → B | (1) 客户需要写自定义 Controller / Service / Entity;(2) 客户需要私有数据库 schema;(3) 客户需要独立部署节点(隔离故障域) |
+| B → C | (1) 客户提供自己的部署环境(私网/客户机房/客户 K8s);(2) 客户要求源代码进客户 git;(3) 客户要求 mjava 基座做大幅分叉(≥3 个文件改基座) |
+
+**反向降档**:不支持。一旦升档,物理资产已经独立,回退成本远高于继续维护。
+
+### 4. mjava-pro 入驻 SOP(A 档具体步骤)
+
+新客户走 A 档时操作步骤:
+
+1. **校验触发条件**:确认客户不命中 A→B 任一硬指标
+2. **宜搭应用表加一行**:tenantId / vendor / appKey / appSecret / extraJson / enabled
+3. **凭据热验证**:调 `mjava-pro` 的 `/api/pro/_diag/tenant/{tenantId}` 探活(接口归 add-mjava-pro 实现)
+4. **上线通告**:邮件抄送(运维 + 客户对接人),记录 tenantId 进 README 客户清单段
+
+**必填字段(spec R3 正式定义)**:tenantId(业务唯一)/ vendor(dingtalk|aliwork|...)/ appKey / appSecret / corpId(钉钉系才需)/ enabled。其余按 vendor 走 extraJson。
+
+**隔离边界**:
+- DB:A 档客户共享 mjava-pro 的 DB schema(用 tenantId 字段隔离行级数据)
+- 缓存:UtilToken key 命名空间扩为 `{tenant}:{vendor}:{appKey}`(add-mjava-pro 已约定)
+- 日志:traceId 自动带 tenantId 维度(MDC 追加)
+
+### 5. mjava-com 对外暴露白名单规则
+
+`mjava-com` 暴露的是「**mjava 基座的 Client/Service 能力**」,但不是所有方法都自动开放,按以下规则:
+
+- **白名单制**:在 `application.yml` 显式列出可暴露的 `{vendor}.{action}`(如 `dingtalk.user.get`、`aliwork.form.save`),未列入即 404
+- **危险动作禁开**:删除类(delete)、批量改类(batch update)、租户管理类(tenant.create)默认禁开,需主仓维护者 ACK 才能加
+- **限流默认值**:每调用方 60 QPS(本地 RateLimiter,进程级),可在 `application.yml` 按调用方覆盖
+- **审计强制**:每次调用打全字段审计日志(调用方 / vendor / action / 入参摘要 / 出参摘要 / 耗时 / 成功标志),不可关闭
+
+**审批流程**:新增白名单条目走 PR + 主仓维护者 review,commit message 标 `feat(com): 开放 {vendor}.{action} 暴露`。
+
+### 6. C 档客户独立仓与 mjava-ai 的关系
+
+C 档客户仓**不依赖** mjava-ai 发布的 jar,而是 fork 基座源码到自己仓内(如 `mjava-guangming/mjava/`)。原因:
+
+- 客户私网 / 客户机房环境通常无法访问我们的 maven 私服
+- 客户要求源码进客户 git,jar 依赖模型不可行
+- 客户的基座分叉常需要快速改(如临时加字段、兼容客户老接口),等上游回流太慢
+
+**版本号约定**:C 档客户仓内 `mjava` 基座 pom 的 `version` 应**保持与上游同步的语义版本**(如 mjava-ai 的 mjava 是 0.0.3,则客户仓的 mjava 也是 0.0.3,分叉改动通过 git 历史追溯,**不**改版本号)。这样未来客户问「我用的是哪版上游」时,git diff 直接可比。
+
+**回流通道(可选)**:客户仓如有通用价值的改动(修 bug、加产品方接口),鼓励上游 cherry-pick 回 mjava-ai。具体流程不强制,按工程师习惯。
+
+**R4 跨仓 grep 范围**:C 档客户仓内的代码**不**纳入 mjava-ai 的 R4 grep(已在 standardize-client-service-layering 的 design 里约定「基础建设期默认仅本仓」),与本 change 一致。
+
+### 7. README / CLAUDE.md 改写策略
+
+README 「子项目速览」表加「档位」列,但**不在 README 直接展开判定矩阵**(避免重复)。改为:
+
+```markdown
+| 子模块 | 端口 | Context | 档位 | 状态 |
+|--------|------|---------|------|------|
+| `mjava-mcli` | 9001 | /api/mcli | B 模板 | 新客户模板 |
+| `mjava-pro` | 9010 | /api/pro | A 容器 | 多租户单部署骨架 |
+| `mjava-com` | 9020 | /api/com | — | BaaS 网关骨架 |
+```
+
+「新客户接入」段改为指向 `openspec/specs/customer-tiering/spec.md`(archive 后位置),三档分流由 spec 权威。
+
+CLAUDE.md 加一行:「**新客户接入**:先查 customer-tiering 决策树选档 → 按档执行 SOP」。
+
+## 拒绝的方案
+
+- **只写文档不入 spec** — 拒绝。客户接入决策是高频判断点,必须走 capability spec 才能被 R4 类规则交叉引用
+- **强制所有客户必先入 A 档观察 1 个月再决定升 B/C** — 拒绝。增加客户接入摩擦,且部分客户初次接入就明显是 C 档(如客户机房),强观察期是无效流程
+- **B 档不在 mjava-ai 仓内,单独建 mjava-customers 仓** — 拒绝。B 档客户少时拆仓成本高于聚合;mjava-ai 仓内子模块隔离已足够,pom 聚合也能并行编译
+- **A→B 触发条件包含「客户数据量」(如 > 1M 行)** — 拒绝。数据量是结果不是原因;真正的原因是「需要私有 schema」,已在 A→B (2) 覆盖
+- **C 档客户仓强制每月同步上游 mjava** — 拒绝。客户仓独立维护权属客户工程师,强同步反而干扰客户工程师节奏
+- **将「客户复杂度」作为判定因子** — 拒绝。「复杂」不可观测,会变成主观打分;改用「需要写 Java 代码」「需要客户机房」两个硬切点

+ 59 - 0
openspec/changes/define-customer-tiering/proposal.md

@@ -0,0 +1,59 @@
+> 状态(2026-06-10 立项):**提案阶段,未实施**
+> 优先级:中。建立客户接入的分档判定,约束未来子模块/独立仓的取舍。
+
+## Why
+
+当前 mjava-ai 处于基础建设期,仓内只有 `mjava` 基座 + `mjava-mcli` 模板 + `mjava-pro`(多客户单部署骨架) + `mjava-com`(BaaS 网关骨架),无实际客户落点。已有先例:
+
+- `mjava-pro` 立项时的设想:**轻客户**入驻一行配置即可接入(add-mjava-pro proposal)
+- `mjava-com` 立项时的设想:**外部系统**通过 REST 调 mjava 能力(add-mjava-com proposal)
+- 历史已存在两个**复杂客户独立仓**:`cur/mjava-guangming/`(4 模块独立 git 仓)、akds(独立仓)
+
+但「**新客户来了,到底走哪条路径**」没有显式约定。具体痛点:
+
+1. 客户接入第一步要回答的问题(入 mjava-pro / 新建子模块 / 拉独立仓)没有判定标准 → 容易凭感觉选错,后续返工
+2. 三档之间的升级路径(轻→中→复杂)没有约定 → 客户业务长大后没人推动迁移
+3. `mjava-pro` 租户入驻清单不清晰、`mjava-com` 对外暴露白名单缺规则 → 两个公共项目能用但「怎么用」无据可依
+4. 客户独立仓与 mjava-ai 的基座 jar 演进解耦关系无文档 → 上游 R4 跨仓 grep 范围反复被问
+
+R3 已经写了「子项目优先调 mjava」的调用优先级,但**没回答「子项目本身归属哪一档」**。本 change 补这一层。
+
+## What Changes
+
+新增 capability `customer-tiering`,约束客户接入决策:
+
+1. **三档分档矩阵(A/B/C)**:每档的硬指标(触发条件)+ 物理落点 + 部署边界 + 命名约定
+   - **A 档**:入驻 `mjava-pro`,单部署多租户,宜搭应用表加一行配置
+   - **B 档**:mjava-ai 仓内新建 `mjava-{客户}` 子模块(复制 `mjava-mcli` 模板),独立 jar 独立部署
+   - **C 档**:客户独立 git 仓(不在 mjava-ai 仓内),自维护基座 jar 副本
+2. **决策流程**:新客户接入时按硬指标走判定树,输出选档
+3. **升级路径**:A→B→C 的触发条件、迁移步骤、回退禁忌
+4. **`mjava-pro` 入驻清单细化**:宜搭应用表必填字段、隔离边界(DB/缓存/日志)、入驻 SOP(与 add-mjava-pro 的 `tenant-registry` capability 互补,不重复定义)
+5. **`mjava-com` 对外暴露规则细化**:哪些 mjava Service 可暴露、白名单审批流程、限流默认值(与 add-mjava-com 的 `baas-gateway` capability 互补)
+6. **客户独立仓与 mjava-ai 解耦边界**:C 档客户仓如何 fork 基座、版本号约定、上游回流通道(仅约定,不强制)
+
+## Capabilities
+
+### New Capabilities
+- `customer-tiering`:客户接入分档判定 + 升级路径 + 各档物理落点约定
+
+### Modified Capabilities
+<!-- 不修改已归档 capability;引用 add-mjava-pro/add-mjava-com 进行中的 tenant-registry/baas-gateway,由本 change 补判定层 -->
+
+## Impact
+
+- **新增文档**:`openspec/changes/define-customer-tiering/specs/customer-tiering/spec.md`
+- **README.md**:「子项目速览」表追加一列「档位」(A/B/C),README 「新客户接入」段落改写为按档位分流
+- **CLAUDE.md**:在「快速操作」追加「新客户接入决策树」短链接
+- **共享后端规范**:`后端/CLAUDE.md` 加客户分档段(与本仓 spec 互锚)
+- **零代码改动**:本 change 仅产出规范,不动 Java 源
+- **依赖**:与 `add-mjava-pro` / `add-mjava-com` 互补;本 change 不阻塞,可独立 archive
+
+## Non-Goals
+
+- ❌ 不写 mjava-pro 的具体租户注册代码(属 add-mjava-pro 范围)
+- ❌ 不写 mjava-com 的具体 action 注册代码(属 add-mjava-com 范围)
+- ❌ 不约定客户业务建模(每个客户业务自行设计)
+- ❌ 不约定客户前端架构(仅后端 Java 子模块/独立仓的判定)
+- ❌ 不强制 C 档客户仓必须回流上游(仅约定通道,是否回流由各客户工程师决定)
+- ❌ 不约定客户数据库选型(A 档共享 mjava-pro 的 DB,B/C 各自决定)

+ 280 - 0
openspec/changes/define-customer-tiering/specs/customer-tiering/spec.md

@@ -0,0 +1,280 @@
+## ADDED Requirements
+
+### Requirement: R1 三档分档矩阵
+
+mjava-ai 接入新客户时按以下三档分流,三档**互斥**(一个客户同时只属一档):
+
+| 档 | 名称 | 物理落点 | 部署形态 | 命名 |
+|---|---|---|---|---|
+| **A** | 公共托管(多租户) | `mjava-pro/`(mjava-ai 仓内共享 jar) | 一个 jar 跑 N 客户,端口 9010 | 仅在宜搭应用表加一行 `tenantId` |
+| **B** | 独立子模块 | `mjava-ai/mjava-{客户}/`(仓内独立子模块,复制 `mjava-mcli`) | 一客户一 jar,独立端口 | `mjava-{客户拼音/英文短名}`(如 `mjava-akds`) |
+| **C** | 独立 git 仓 | `cur/mjava-{客户}/`(不在 mjava-ai 仓内) | 客户自管部署 | `mjava-{客户}`(仓 root artifactId) |
+
+历史先例:`cur/mjava-guangming/` 与 akds 为 C 档;当前无落地 A/B 档客户实例。
+
+#### Scenario: 新客户接入第一步
+
+- **WHEN** 有新客户接入需求
+- **THEN** 必须先按 R2 决策流程选档
+- **AND** 在客户接入会议纪要 / OpenSpec change proposal / commit message 中显式记录所选档位
+- **AND** 不允许跳过分档判定直接动手建子模块或拉新仓
+
+#### Scenario: 单客户跨档归属
+
+- **WHEN** 一个客户的不同业务模块似乎需要不同档位(如某客户审批流走 A 档共享、某独立报表服务走 B 档)
+- **THEN** 整个客户按**最高档**归属(取 B),不允许跨档混编
+- **AND** 若坚持拆分,必须视为「客户名下两个客户实体」分别走 R2
+
+---
+
+### Requirement: R2 决策流程(按硬指标升档)
+
+判定按「**触发即升档**」原则,逐档自上而下匹配,匹配到最高档位停止:
+
+1. **从 A 起评**:默认所有新客户先评估 A 档
+2. **A→B 触发条件**(任一即触发升档):
+   - (a) 客户需要写自定义 Java Controller / Service / Entity
+   - (b) 客户需要私有数据库 schema(不能共享 mjava-pro 的 DB)
+   - (c) 客户需要独立部署节点以隔离故障域
+3. **B→C 触发条件**(任一即触发升档):
+   - (a) 客户提供自己的部署环境(私网 / 客户机房 / 客户 K8s)
+   - (b) 客户要求源代码进客户 git 仓
+   - (c) 客户要求 mjava 基座做大幅分叉(≥3 个文件改基座)
+
+升档**单向**:A→B→C 不可逆。原因:升档后物理资产已经独立(独立 jar / 独立 git 仓),回退需要数据迁移 + 部署变更 + 历史 git 不可回收,成本远高于继续维护。
+
+#### Scenario: 客户初次接入按硬指标走判定
+
+- **WHEN** 评估新客户「思库文化」时,确认其无需写自定义 Java 代码、共享数据库即可、可部署在我们机房
+- **THEN** A→B 三个触发条件均不命中
+- **AND** 选 A 档:宜搭应用表加一行 `tenantId=sikuwh`,无新增 Java 代码
+
+#### Scenario: 客户初次接入命中 B 档
+
+- **WHEN** 评估「阿科德斯」时,发现需要写自定义工时审批 Controller(命中 A→B(a))
+- **THEN** 升 B 档:在 mjava-ai 仓内 `cp -r mjava-mcli mjava-akds`,独立 artifactId / 端口
+- **AND** 部署仍在我们机房(B→C 不触发)
+
+#### Scenario: 客户初次接入命中 C 档
+
+- **WHEN** 评估「光明集团」时,客户要求源代码进客户 git 仓(命中 B→C(b))
+- **THEN** 升 C 档:拉独立 git 仓 `cur/mjava-guangming/`,fork mjava 基座到客户仓内
+- **AND** 客户仓与 mjava-ai 解耦演进
+
+#### Scenario: 误选低档需升档
+
+- **WHEN** 已落地 A 档的客户业务长大,开始要求私有 schema(命中 A→B(b))
+- **THEN** 走 R3 升档迁移路径,**不**继续凑合在 A 档
+- **AND** 升档迁移期间双轨运行(A 档读写转只读,B 档新实例并行启动),数据迁移完成后下线 A 档租户配置
+
+#### Scenario: 试图降档
+
+- **WHEN** 已升 B 档的客户业务收缩,看似可退回 A 档
+- **THEN** 不降档;继续以 B 档运行
+- **AND** 若长期闲置(≥6 个月无业务变更),评估是否归档下线整个子模块(属另一类决策,不属降档)
+
+---
+
+### Requirement: R3 升档迁移路径
+
+升档触发后按以下步骤执行迁移:
+
+**A → B 迁移步骤**:
+
+1. **新建 B 档子模块**:`cp -r mjava-mcli mjava-{客户}`,改 artifactId / port / context-path
+2. **配置迁移**:把宜搭应用表该 tenantId 的凭据复制到 `application-{profile}.yml`
+3. **数据迁移**:把 mjava-pro DB 内该 tenantId 的行级数据导出 → 导入 B 档独立 schema(脚本一次性,归档备份)
+4. **双轨期**(1~2 周):A 档租户配置 `enabled=false`(拒新请求),B 档接管;监控异常
+5. **下线 A 档配置**:宜搭应用表删除该 tenantId 行
+6. **commit message**:`refactor: 客户 {名} 升档 A → B(trigger: {条件})`
+
+**B → C 迁移步骤**:
+
+1. **新建独立 git 仓**:`cur/mjava-{客户}/`,初始化为多模块 pom 结构
+2. **fork 基座**:把 mjava-ai 当前 `mjava/` 目录全量复制进客户仓 `mjava/`,版本号保持上游一致
+3. **迁移 B 档子模块**:把 `mjava-ai/mjava-{客户}/` 全量复制进客户仓
+4. **删除 mjava-ai 仓内的 B 档子模块**:根 `pom.xml` 移除 `<module>`,删除目录
+5. **客户仓 README**:写明分叉自 mjava-ai 哪个 commit
+6. **commit message**:mjava-ai 侧 `refactor: 客户 {名} 升档 B → C(迁出独立仓 {仓库 URL})`
+
+#### Scenario: A → B 双轨期出错回退
+
+- **WHEN** B 档启动后发现严重 bug,3 天内无法修复
+- **THEN** 临时把 A 档租户配置 `enabled=true` 恢复 A 档接管,B 档进入修复期
+- **AND** 不视为「降档」(B 档子模块继续保留,等待修复后再切)
+
+#### Scenario: B → C 客户仓 fork 基座的版本号
+
+- **WHEN** 把 mjava-ai 的 `mjava/` fork 到客户仓
+- **THEN** `mjava/pom.xml` 的 `<version>` **保持与 mjava-ai 当前一致**(如 0.0.3)
+- **AND** 客户仓内对基座的后续分叉改动**不**改版本号,通过 git 历史追溯
+- **AND** 客户仓 README 写明 fork 来源 commit hash
+
+---
+
+### Requirement: R4 A 档(mjava-pro)入驻清单
+
+客户走 A 档时,宜搭应用表(`mjava-pro.yml` 配置的 formUuid)必填字段:
+
+| 字段 | 含义 | 必填 | 备注 |
+|---|---|---|---|
+| `tenantId` | 业务唯一标识 | ✅ | 全局唯一,禁用中文 / 特殊字符 |
+| `tenantName` | 中文展示名 | ✅ | 仅用于日志 / 监控可读性 |
+| `vendor` | 第三方平台 | ✅ | `dingtalk` / `aliwork` / `feishu` 等枚举 |
+| `appKey` | 产品方 appKey | ✅ | — |
+| `appSecret` | 产品方 appSecret | ✅ | 应在宜搭加密字段存储 |
+| `corpId` | 钉钉系 corp ID | 钉钉系必填 | — |
+| `extraJson` | vendor 特定额外参数 | 可选 | JSON 字符串,由 vendor 适配器解析 |
+| `enabled` | 启用标志 | ✅ | `false` 时拒绝该 tenant 请求 |
+
+**隔离边界**:
+
+- **DB**:A 档客户共享 mjava-pro 的 DB schema;所有业务表必须含 `tenant_id` 列;查询条件强制带 `tenant_id`(由 JPA Interceptor 注入,不可绕过)
+- **缓存**:`UtilToken` key 命名空间扩为 `{tenantId}:{vendor}:{appKey}`(由 add-mjava-pro 的 `tenant-registry` capability 实现)
+- **日志**:MDC 自动注入 `tenantId`,traceId 日志格式追加 `[T:{tenantId}]`
+
+**入驻 SOP**:
+
+1. 校验:确认 R2 决策中 A 档判定通过(不命中任一 A→B 条件)
+2. 宜搭应用表新增一行
+3. 凭据热验证:调 mjava-pro `/api/pro/_diag/tenant/{tenantId}` 探活
+4. 邮件通告运维 + 客户对接人
+
+#### Scenario: A 档入驻缺必填字段
+
+- **WHEN** 宜搭应用表新增行时漏填 `appKey` 或 `corpId`(钉钉系)
+- **THEN** mjava-pro 启动该 tenant 路由时必须显式拒绝,返回明确错误(如 `400 TENANT_CONFIG_INCOMPLETE: appKey is required`)
+- **AND** 不允许「字段缺失但允许跑」的兼容兜底
+
+#### Scenario: A 档客户业务表漏 tenant_id
+
+- **WHEN** A 档客户的 Java Repository 查询 SQL 缺 `tenant_id` 条件
+- **THEN** JPA Interceptor 应强制注入 `WHERE tenant_id = :currentTenant`
+- **AND** 若 Repository 故意绕过(如原生 SQL 字符串),code review 必须打回
+
+---
+
+### Requirement: R5 mjava-com 对外暴露白名单规则
+
+mjava-com 作为对外 BaaS 网关,**不**自动暴露所有 mjava Client/Service 方法。暴露遵循:
+
+**白名单制**:
+
+- 在 `mjava-com/application.yml` 显式声明 `gateway.expose: [{vendor}.{action}, ...]`
+- 未列入即返 404(不返 405,避免泄露内部能力清单)
+
+**危险动作默认禁开**:
+
+- 凡 `action` 名含 `delete` / `remove` / `drop` / `truncate` 的方法
+- 凡 `action` 名含 `batch` + 写动作(`update` / `create` / `delete`)的方法
+- 凡涉及租户管理(`tenant.create` / `tenant.delete` / `tenant.update`)的方法
+
+上述方法即使配置在 `gateway.expose` 也必须由主仓维护者在 PR 中显式 ACK 才能合并。
+
+**限流默认值**:
+
+- 每调用方默认 60 QPS(本地 `RateLimiter`,进程级,不跨实例)
+- 在 `gateway.rateLimit.{callerId}: {qps}` 按调用方覆盖
+
+**审计强制**:
+
+- 每次调用打全字段审计日志:`{callerId, vendor, action, paramSummary, resultSummary, latencyMs, success}`
+- 审计日志不可关闭,不可降级到 DEBUG 级别
+
+**暴露审批流程**:
+
+- 新增白名单条目 → PR + 主仓维护者 review
+- commit message 标 `feat(com): 开放 {vendor}.{action} 暴露`
+- 危险动作额外要求 PR 描述写明业务场景 + 调用方授权范围
+
+#### Scenario: 配置中含危险动作
+
+- **WHEN** PR 把 `dingtalk.user.delete` 加入 `gateway.expose`
+- **THEN** code review 必须打回,要求拆开作为「危险动作专项 PR」
+- **AND** 专项 PR 必须含业务场景说明 + 主仓维护者显式 ACK
+
+#### Scenario: 调用方未配限流
+
+- **WHEN** 新调用方 `callerId=xyz` 首次调用 mjava-com
+- **THEN** 默认走 60 QPS 限流
+- **AND** 若实际业务需要更高 QPS,走 PR 改 `gateway.rateLimit.xyz`
+
+---
+
+### Requirement: R6 C 档独立仓与 mjava-ai 解耦边界
+
+C 档客户仓**不**通过 maven 依赖引入 mjava-ai 发布的 jar,而是 fork 基座源码到客户仓内(如 `cur/mjava-guangming/mjava/`)。
+
+**fork 基座规则**:
+
+- 客户仓 `mjava/pom.xml` 的 `version` 保持与上游同步(fork 时刻的上游版本号),后续分叉改动**不**改版本号
+- 客户仓 README 写明 fork 来源 commit hash + 上游仓库 URL
+- 客户仓 mjava 包结构保持 `com.malk.*`(不改 package),便于 git diff 跨仓比较
+
+**演进解耦**:
+
+- 上游 mjava-ai 的基座演进**不自动**同步到 C 档客户仓
+- C 档客户仓的基座分叉**不自动**回流到上游
+- 双向同步由各仓工程师按需手动 cherry-pick
+
+**R4(接口变更确认)跨仓 grep 范围**:
+
+- mjava-ai 仓内 R4 grep 范围 = mjava-ai 本仓(基础建设期)
+- C 档客户仓内 R4 grep 范围 = 客户仓本仓
+- **两侧不互相联动**:mjava-ai 改基座接口签名时,不需要扫 C 档客户仓;C 档客户仓改自己基座副本时,不需要扫 mjava-ai
+
+**回流通道(可选,不强制)**:
+
+- C 档客户仓的工程师若认为某改动有上游回流价值(修 bug / 加产品方接口),鼓励提交 PR 回 mjava-ai
+- 回流走标准 OpenSpec change 流程(不绕开)
+
+#### Scenario: C 档客户仓 fork 时刻
+
+- **WHEN** 升档 B → C 时把 mjava-ai 的 `mjava/` 复制进客户仓
+- **THEN** 客户仓 `mjava/pom.xml` 保留上游版本号(如 0.0.3)
+- **AND** 客户仓 README 记录 fork 来源 commit hash
+- **AND** 不在客户仓建「上游同步」cron / CI
+
+#### Scenario: 上游 mjava-ai 改 DDClient 接口签名
+
+- **WHEN** mjava-ai 内 `DDClient.foo(String)` 改签名为 `DDClient.foo(String, Boolean)`
+- **THEN** R4 grep 仅扫 mjava-ai 本仓
+- **AND** **不**扫 `cur/mjava-guangming/` 或 akds 等 C 档客户仓
+- **AND** C 档客户仓的同名接口(若已 fork)由客户仓工程师按需自主升级,不阻塞上游
+
+#### Scenario: C 档客户仓回流改动
+
+- **WHEN** `cur/mjava-guangming/mjava/` 内修了一个上游也存在的 bug
+- **THEN** 鼓励但不强制提 PR 回 mjava-ai
+- **AND** 回流走 OpenSpec change 流程,不绕开
+
+---
+
+### Requirement: R7 README / CLAUDE.md 同步更新
+
+本 change archive 后,mjava-ai 仓内文档必须同步:
+
+**README.md**:
+
+- 「子项目速览」表追加「档位」列:`mjava-mcli` 标 `B 模板`、`mjava-pro` 标 `A 容器`、`mjava-com` 标 `—`
+- 「新客户接入」段改写为「先按 customer-tiering 决策树选档 → 按档执行 SOP」,详细判定不再展开(避免与 spec 重复)
+
+**CLAUDE.md**:
+
+- 「快速操作」加一行:「**新客户接入**:先查 `openspec/specs/customer-tiering/spec.md` R2 决策树选档 → 按档执行 SOP」
+
+**共享后端规范**(`/Users/malk/Desktop/Tech/claude/后端/CLAUDE.md`):
+
+- 在「子项目接入流程」段补 customer-tiering 锚点(与本 spec R2/R3 互锚)
+
+#### Scenario: README 子项目速览表更新
+
+- **WHEN** 本 change archive
+- **THEN** README.md 「子项目速览」表必须含「档位」列
+- **AND** 各模块标记与本 spec R1 一致
+
+#### Scenario: 后续新增 B 档子模块
+
+- **WHEN** 任何 B 档客户子模块(如 `mjava-akds`)合入 mjava-ai
+- **THEN** README.md 「子项目速览」表必须追加该子模块行,「档位」列标 `B`
+- **AND** 不允许新子模块合入但表内不登记

+ 34 - 0
openspec/changes/define-customer-tiering/tasks.md

@@ -0,0 +1,34 @@
+# Tasks
+
+> 本 change 仅产出规范文档与索引更新,不动 Java 源码。
+
+## 1. 规范产出(已完成立项)
+
+- [x] 1.1 写 `proposal.md`:定档 + 涉及 capability + Impact + Non-Goals
+- [x] 1.2 写 `design.md`:三档划分依据、升档触发条件、入驻 SOP、暴露白名单规则、C 档解耦边界、拒绝方案
+- [x] 1.3 写 `specs/customer-tiering/spec.md`:R1~R7 七条 Requirement + 各自 Scenario
+
+## 2. 文档同步(archive 前完成)
+
+- [ ] 2.1 README.md:「子项目速览」表追加「档位」列
+- [ ] 2.2 README.md:「新客户接入」段改写为分档分流,详细判定指向 spec
+- [ ] 2.3 CLAUDE.md:「快速操作」加「新客户接入决策树」短链接
+- [ ] 2.4 共享后端规范 `/Users/malk/Desktop/Tech/claude/后端/CLAUDE.md`:补 customer-tiering 锚点(与本 spec R2/R3 互锚)
+
+## 3. 验证(archive 前完成)
+
+- [ ] 3.1 R2 决策流程拿历史真实客户校验:思库(应得 A)、阿科德斯(应得 B)、光明集团(应得 C)→ 与历史决策一致
+- [ ] 3.2 R4 入驻清单与 add-mjava-pro 的 `tenant-registry` capability 字段命名对齐(无冲突)
+- [ ] 3.3 R5 暴露规则与 add-mjava-com 的 `baas-gateway` capability 字段命名对齐(无冲突)
+- [ ] 3.4 R6 跨仓 grep 范围与 standardize-client-service-layering 的 R4 design 段约定一致
+
+## 4. Archive
+
+- [ ] 4.1 `/opsx:archive define-customer-tiering` → capability spec 进 `openspec/specs/customer-tiering/spec.md`
+- [ ] 4.2 archive 后更新 CLAUDE.md 的「现有 change 状态」段
+
+## 关联(不阻塞本 change)
+
+- add-mjava-pro:本 spec R4 是 `tenant-registry` 的入驻面,add-mjava-pro 是实现面;两者互补
+- add-mjava-com:本 spec R5 是 `baas-gateway` 的暴露规则面,add-mjava-com 是实现面;两者互补
+- standardize-client-service-layering:本 spec R6 与其 R4 design 段共享「基础建设期跨仓 grep 仅本仓」约定