Files
ai_site/联调后修改意见-宇恒松离线.md
whm 2a7da53769 docs: align sync catalog and checklists after Z14c
Document schema/fingerprint APIs, quote SMS Provider off, and refresh the联调意见 commit trail.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 18:33:13 +08:00

674 lines
45 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 联调后修改意见 · 宇恒松离线(形态 B
> 初稿2026-08-01 · 修订至 **2026-08-05 18:25**
> 焦点:**§0.2 / §0.3**Z10d / Z14 / Z14c **代码已合入**,生产须 `bash ./restart.sh --pull`;已修项勿再挂「仍待办」
> 来源:宇恒 `yuhengyihao_client` ↔ 智建(生产 `aisite.yuxindazhineng.com`
> 依据:`松离线-dbsync方案-最终版.md`、`宇恒-松离线数据同步使用文档.md`
---
## 0. 当前结论
| 级别 | 状态 | 说明 |
|------|------|------|
| **硬改(阻塞接口)** | **无** | Z1Z14 代码侧齐;剩生产 pull + 配号/Secret/白名单 |
| **生产栈** | **已恢复** | `SMS.Provider: "off"` 须引号health `platform/ai=true` |
| **Z1Z9 / Z11Z13** | **已落实** | 绑定/凭票/模块导入等;见 §5 |
| **Z10 + Z10d** | **代码齐;生产 pull** | 空表 ensure已存在表 **ADD COLUMN**§5.7 |
| **Z12 默认同步** | **已落实** | 启用即通道;**默认运行中**`cb76824` |
| **Z14 + Z14c** | **代码齐;生产 pull** | fingerprint API智能体挂载 `online_db_id` 已放行§5.11 |
| **仍关注** | 用法 | SQLite 按库串行 drain历史数据靠 full-push不是「点启动」 |
### 0.1 绑定产品冻结Z13 · 试运行)
| 约定 | 内容 |
|------|------|
| 联调号 | **`13531041944`**(宇信达);**禁止**超管 `13531041945` |
| 换票 | 凭票:`YXD_YUHENG_TICKET_SECRET` + `YXD_HOST_KEY` + 手机;**勿**手写登录密码 |
| 短信 | 试运行关:`RequireForBind=false``Provider: "off"`**YAML 必须加引号** |
| 同号 | 命中成员 → 必须弹窗;`confirm=true` 才绑 |
| 通道 | 默认同步通道 **默认运行中**;开通不依赖用户先点「启动」 |
### 0.2 待改清单(按负责方)
> 智建轨迹:`d195aa4` 凭票 → `4222667` SMS 引号 → `cb76824` 默认启动 → `4fae7ae` Z10d+fingerprint → **`bb59bb5` Z14c**。生产:`bash ./restart.sh --pull`。
#### A. 宇恒 · 已完成
| 编号 | 内容 |
|------|------|
| **Z10c** | `ensure_before_drain`(选同步 force + drain 前 TTL |
| **Z13be** | 绑定码 / 同号确认 / policy / 凭票 / 选「同步」即 Binding+drain |
| **Z14a** | 表级指纹慢心跳;不一致 → ensure + full-push |
#### A. 宇恒 · 配合注意(非新开发)
| 优先级 | 内容 |
|--------|------|
| **P0** | 端到端见 **§0.3**:绑定 → 库选「同步」→ drain线上表只反映已 push/ensure |
| **P0** | **`online_db_id`**:生产 pull Z14c 后,智能体 JWT **用** ticket/`agents/me``{channel}_uN`;人类自助仍走本人 Binding |
| **P1** | Binding 带可读名;同库串行 drain指纹优先 `schema/fingerprint`404 回退 `row_count` |
#### B. 智建 · 已完成(仓内)
| 项 | 说明 |
|----|------|
| gateway 公开 bind / ticket | 生产 health 已通 |
| `SMS.Provider: "off"` | YAML 布尔踩坑已修 |
| 默认同步通道默认启动 | 创建即 enabled重启自动 Start |
| **Z14c** ACL | 智能体 JWT 放行挂载 `online_db_id``agentOwnsOnlineDB` |
| Podman DNS / 脚本可执行位 | compose aliases`restart.sh` 100755 |
#### B. 智建 · 仍待办(仅运维)
| 优先级 | 项 | 说明 |
|--------|----|------|
| **P0** | **生产 pull 本批** | Z10d + `schema/fingerprint` + Z14c`bash ./restart.sh --pull` |
| **P0** | 成员手机 | 「宇信达」绑 **`13531041944`**;勿超管号 |
| **P0** | 通道表白名单 | **勿**「填入测试默认」;形态 B 可空 |
| **P1** | 凭票 Secret | `YuhengTicket.Secret``YXD_YUHENG_TICKET_SECRET` |
| **P1** | `DefaultRemoteDSN` | 生产 Postgres空则 sqlite 联调文件 |
| **P2** | 正式短信 | Provider≠`"off"``RequireForBind=true` |
#### C. 双方已否决
| 项 | 说明 |
|----|------|
| 终端冲突台 | 仅平台超管 |
| 智建代改宇恒仓 | 分工冻结 |
| 平台 push 租约 | version 幂等 + 宇恒串行 drain |
### 0.3 双方怎么配合(开通 → 看到线上表)
```text
【智建运维】
1. bash ./restart.sh --pullhealth platform=true
2. 成员绑 13531041944通道勿测试表白名单配凭票 Secret
3. 启用智能体 → 默认同步通道应「运行中」
4.(可选)生成绑定码
【宇恒终端】
5. 未绑定 → policy → lookup/confirm 或 redeem 或 ticket-exchange
6. 本机库选「同步」→ Binding + ensure + drain/full_pushZ13e
7. 慢心跳指纹Z14缺列依赖生产已 pull 的 Z10d
【智建验收】
8. 「查看线上表」刷新 → 应有业务表(含曾 0 行空表)
9. 仍空 = 终端尚未 push/ensure不是再点一次「启动」就能灌表
```
| 角色 | 负责 | 不负责 |
|------|------|--------|
| **智建** | 落点/通道/鉴权/ensure/fingerprint控制台看线上表 | 不替宇恒灌本机历史行 |
| **宇恒** | 绑定 UX、选同步、outbox、drain、指纹修复、full-push | 不以手填 DSN/通道为产品终态 |
| **共同** | 联调号 `13531041944`Secret 对齐;试运行免短信仍须确认 | 不用超管号 |
宇恒对照脚本:`smoke_zhijian_e2e.py` / `smoke_offline_concurrent.py` / `smoke_zhijian_gateway_burst.py` / `smoke_stress_sync.py`
复测摘要gateway 串行 60/60、`eof502=0`;同 version → `skipped`;并发 SQLite 须重试 / 客户端串行。
---
## 1. 已验证可用(请保持兼容)
请**勿破坏**下列契约(宇恒 agent 已按此对接):
1. `GET /api/v1/agent/sync/channels/{id}/whitelist`
- 返回含 `tables``pk_columns``conflict_policy`
2. `POST /api/v1/agent/sync/channels/{id}/push`
- Body`table, op, row_pk, row/rows, version, client_outbox_id`(用户自助另加 `online_db_id`
- `op``insert|update|update_by_id|delete`(平台侧 insert/update 归一为 upsert 可接受)
- 成功:`{ success: true, result: { ok, applied, skipped, conflict, applied_version, message } }`
- **同 `(table,row_pk,version)` 已落地 → `skipped=true` 且 HTTP 2xx**
3. `POST .../pull``POST .../bootstrap`
- 下行灌库;响应含 `columns`(空表也返回列,便于本机建表)
4. `POST .../schema``POST .../schema/ensure`**Z10**
- 拉线上表结构 / 本机空表结构推到线上(`CREATE IF NOT EXISTS`**Z10d** 部署后已存在表亦 `ADD COLUMN`
5. `POST .../schema/fingerprint`**Z14** · 建议部署)
- Body`{ "online_db_id": "..." }`
- 返回各表 `name/row_count/content_hash/pk_column`;与宇恒本机指纹算法对齐(见 §5.11
- **未部署时**宇恒回退只用 `schema.row_count` 对账(可发现行数差,不能发现「同 count 不同内容」)
6. 鉴权:管理员 JWT含「数据同步」、智能体 Token或**登录用户 JWT**(须本人 Binding + `online_db_id`)均可调 agent API
7. 错误体优先带可读 `message`;忙/上游失败宜带 `retryable: true`502/503
**联调账号(写入约定,勿再用错租户 / 勿用超管号)**
| 环境 | 用途 | 登录 | 说明 |
|------|------|------|------|
| 本机 / 演示公司 | 租户 1 演示 | `13800000001` / `demo123` | 仅本机联调;**不要**拿去推生产「宇信达」通道 |
| **生产 · 宇信达** | 公司管理 / 同号绑定联调 | **`13531041944`**(密码由开通时设定) | **专用联调号**;智建须在「宇信达」成员/管理员中写入此手机;宇恒 `YXD_SYNC_LOGIN_PHONE` 与 Z13 同号确认均以此号为准 |
| 平台超管(禁止联调) | 控制台「填入超管手机」 | `13531041945` / `ljk_admin` | **最顶级管理员**seed `ensureDevPhone(ljk_admin)`**禁止**用作同步登录 / 绑定联调,会与公司账号冲突 |
> **智建待办(运维)**:见 **§0.2 B**(生产 pull、成员手机、Secret、通道勿测试表白名单。Z14c/Z10d/fingerprint 属「已合入待 pull」不在待开发。
---
## 2. 原建议项落实对照2026-08-03
| 编号 | 原建议 | 智建现状 | 是否还要改 |
|------|--------|----------|------------|
| **2.1 P1** | gateway 偶发 502 EOF超时与可重试 JSON | `ProxyTimeoutSec` 已生效sync 路径加长超时502 体含 `retryable`SQLite DSN 规范化 + `busy_timeout`remote IO → 503+retryable见方案 changelog 2026-08-01 | **否(已落实)**。若生产仍偶发 EOF按运维排障不升格为接口变更 |
| **2.2 P2** | 文档写明多 agent / version 幂等 | `docs/数据同步-中间件.md` 已写「允许重复 push / 客户端 drain 锁优化」 | **否** |
| **2.3 P3** | `pushed_applied` / `pushed_skipped` | `ChannelStats` 已有burst 联调可见 | **否**。OpenAPI 已标注 applied/skipped示例可再补一句即可非必须 |
| **2.4 P4** | Windows `file:` DSN 示例 | 开通/同步表约定已补正斜杠示例;平台规范化反斜杠 | **否** |
| **2.5 P5** | 503/502 + retryable | 已按表落实 | **否** |
### 2.6 【新增·知会】高并发 push 与 SQLite remote
**现象**(宇恒 `smoke_stress_sync`):对同一通道 remote本机 SQLite**并行** push 时,大量 `database is locked` / 失败,串行则稳定;加重试后可最终成功,但 p95 延迟明显升高。
**边界**
- 正确性仍靠 **version 幂等**,不要求平台做 push 租约。
- **推荐用法**:宇恒 agent **按库串行 drain**(勿多线程齐推同一 SQLite A
- 若线上 A 为 MySQL/Postgres并发能力通常好于 SQLite文档可一句带过「SQLite remote 不适合高并发多 writer」。
**智建是否必须改****否**。可选更低优remote 打开时统一更长 `busy_timeout`、或文档强调 SQLite 并发限制(已有 busy_timeout 则足够)。
---
## 3. 明确不需要智建改的部分
| 项 | 负责方 | 说明 |
|----|--------|------|
| 本机 drain 互斥锁 / 串行 drain | 宇恒 | 减重复 HTTP 与 SQLite 锁风暴 |
| 写网关 / outbox / UUID 补齐 | 宇恒 | 未开通零感;仅白名单表同步 |
| 「agent 停仍可保存」 | 宇恒 | H3 |
| 终端冲突处理台 | 双方已否决 | 冲突只在平台超管侧 |
| 改宇恒仓库代码 | 禁止由智建代改 | 分工已冻结 |
---
## 4. 联调回归清单(改后复测)
| 项 | 宇恒侧结果 |
|----|------------|
| whitelist 与通道一致 | ✅ |
| push insert → A 同 UUID | ✅ |
| push update / delete | ✅ |
| 同 version → skippedA 不增行 | ✅ |
| gateway 连续 push ≥20 无 EOF | ✅burst 25、stress 串行 60 |
| stats `pushed_applied` / `pushed_skipped` | ✅ |
| 并发 pushSQLite | ⚠️ 需重试;建议客户端串行 |
未在每次脚本覆盖:无「数据同步」权限 → 403旧 version + `lww_source` 全矩阵——以平台单测/文档为准即可。
---
## 5. 宇恒侧说明(知会)
- 测试已收紧mock 对齐 skipped、双 drain 断言 applied==N、update/delete 保序、压测脚本带锁重试。
- **开通仍是可选**`YXD_SYNC_MODE=local_dbsync`;用户自助以 Binding 为准(**Z4**:不按通道表白名单拒收)。
- **空表**sync 周期须调 **Z10** `schema/ensure`(见 §5.7),否则本机空表不会出现在线上。
- 原「若采纳 2.1 则稳定性提升」:**已验证提升**;本意见可归档为「历史建议 + 落实对照」,无需再开一轮同名改造。
---
## 5.1 【新增·2026-08-03】用户自助库级三态宇恒已开做
产品目标:用户登录智能体后,用**自己的全部权限**管理名下数据库/网页;可对每个库选择:
| 产品态 | 含义 | 宇恒引擎映射 |
|--------|------|--------------|
| **仅本地** `local_only` | 只写本机 | `local_only` |
| **仅线上** `online_only` | 以线上为主 | `online_primary` |
| **同步** `sync` | 本机落库 + outbox 上云 | `local_dbsync` |
宇恒已落地(本机):
- `POST/GET /database/sync/mode``GET /database/sync/policies`
- 策略键:`(tenant_id, user_id, local_database_id, database_name)`
- 写网关按命名库策略覆盖全局 `YXD_SYNC_MODE`(默认仍不静默升 sync
- 数据库区卡片「同步设置」可视化三态;鉴权优先**登录用户 JWT**`app.client.access_token`
- 选「同步」后可拉起本机 agentpush 带头里的用户 Bearer
---
## 5.2 【请智建配合改 / 兼容】用户态同步
> 不破坏现有管理员通道 + agent Token 联调路径;以下为**用户自助开通**所需平台能力。
| 编号 | 优先级 | 诉求 | 说明 | 智建现状2026-08-04 |
|------|--------|------|------|------------------------|
| **Z1** | **P0** | **登录用户 JWT 可调 agent sync API** | whitelist / push / batch 允许已登录普通用户(本人资源范围)。 | **已落实** |
| **Z2** | **P0** | **权限范围 = 用户自己的库** | 带 `online_db_id` 时一律校验本人 Binding异库 → 403含有「数据同步」的人类。 | **已落实** |
| **Z3** | **P1** | **Binding 支持用户自助登记** | `POST/GET .../bindings` 认用户 JWT强制本人 `user_id`。 | **已落实** |
| **Z4** | **P1** | **用户选 sync 时白名单策略** | **冻结②**不按通道表白名单拒收Binding 库内任意表可 push可自动建表。 | **已落实** |
| **Z5** | **P2** | **仅线上 / 下行补齐** | A→B / pull / bootstrap。 | **已落实** |
| **Z6** | **P2** | **控制台文案** | Binding 即权限;管理员不负责维护表白名单。 | **已落实** |
| **Z7** | **P1** | **SyncPage / Binding 展示「可读库名 + 映射」** | 通道「线上」列优先可读名;副文案 online_db_id / 落库路径;统计标注为 push 次数Binding 列表「本地库名 ↔ 线上库名」。 | **已落实2026-08-04**Binding 增 `database_name`/`display_name`SyncPage 主标题优先本地 `database_name`,↑ 标注「推送次数 / 非表数」。宇恒 ensure 须带可读名,否则仍会回退到通道名或 driver |
| **Z8** | **P0** | **模块数据:本机库可管理 + 按智能体上云** | 见 §5.5:本机 B 管理模块表push 到智能体绑定的 A废弃默认双轨 | **已落实(含 Z8g 展示)** |
| **Z9** | **P0** | **模块默认 import + 存量扫库** | 见 §5.6 | **已落实 Z9af** |
| **Z10** | **P0** | **空表两侧建齐** | 见 §5.7:仅靠 outbox 不会建空表 | **双方已接**:智建 API + 宇恒 Z10c`sync_schema` |
| **Z11** | **P1** | **蓝图增字段迁库** | 见 §5.8 | **智建已落实** ALTER + 导入提示 |
### 建议验收(智建改后)
1. 演示账号登录拿用户 JWT**无**单独「数据同步」管理员权)→ `GET .../channels/{id}/whitelist` → 2xx。
2. 同 JWT → `POST .../push` 写入本人 Binding 库 → A 出现同 UUID。
3. 同 JWT 推他人 `online_db_id` → 403。
4. 原管理员 / 智能体 Token 路径回归仍通过(不破坏联调结论)。
### 宇恒 ↔ 智建分工(本项)
| 方 | 负责 |
|----|------|
| 宇恒 | 库级三态 UI/API、写路径分流、outbox、用登录态调 push**默认不按通道表白名单过滤****Z10c 已接** schema/ensure |
| 智建 | 用户 JWT 鉴权、Binding 校验、任意表 push**SyncPage/Binding 按可读名展示Z7****Z10a/b** 空表 API |
---
## 5.3 【复测·2026-08-03 下午】智建改后 × 宇恒库级策略
> 环境:`127.0.0.1:8180` + `:8888`;演示账号 `13800000001` / `demo123`
> 脚本:`yuhengyihao_client/yxd/app_fastapi/smoke_zhijian_e2e.py`、`smoke_user_jwt_policy.py`
> 说明:直连智建验收(当时本机 8080 未起push 遇 gateway **502 EOF / 503** 时按 `retryable` 重试后成功。
### 对照表
| 编号 | 项 | 结果 | 备注 |
|------|----|------|------|
| — | 登录拿用户 JWT | ✅ | `token_len=225``/api/v1/auth/me` 可读 |
| — | 经典 e2eB→outbox→push→A 同 UUID | ✅ **PASSED** | 首次 push 常 502/503约 24 次重试后成功;二次 insert 同行数≥2 |
| **Z1** | 用户 JWT 拉 whitelist | ✅ | HTTP 200`tables=['orders']` |
| **Z1** | 用户 JWT 直推本人库 | ✅ | 带 `online_db_id`A 可见同 UUID重试后 200或同 version → `skipped` |
| **Z3** | 用户 JWT 自助 Binding | ✅ | `POST .../admin/sync/bindings` → 200返回 `user_id/local/online/channel_id` |
| — | 库级策略 `sync` → 写网关 `local_dbsync` | ✅ | 全局未强制 `YXD_SYNC_MODE=local_dbsync` 时,命名库策略仍进 outbox |
| — | 登录态 agent drain 上云 | ✅ | drain 重试后 `pushed=1`A 行 title=`policy-sync` |
| **Z2** | 推他人 `online_db_id` → 403 | ✅ **宇恒复测通过2026-08-04** | `403 无权访问该 online_db_id非本人 Binding``smoke_user_jwt_policy.py` PASSED |
| **Z4Z6** | 白名单 / 下行 / 文案 | ✅ | Z4 冻结②已落实(任意表+自动建表Z5 pullZ6 SyncPage |
### 现象与建议(运维)
1. **Gateway 偶发 502 EOF / 503 Request Timeout**,体带 `retryable: true`;正确性仍靠 version 幂等(重试后常见 `skipped=already applied`)。
2. **推荐**:宇恒 agent / 联调脚本对 push **串行 + 按 retryable 重试**(已在上述 smoke 加重试与 error→pending 恢复)。
3. **Z2****2026-08-04 宇恒复测通过**——异库返回 `403 无权访问该 online_db_id非本人 Binding`
4. 前端「数据库区 → 同步设置」需本机服务起来后人工点验;本次后端契约已通。
### 再测摘要2026-08-03 17:53
| 脚本 | 结果 |
|------|------|
| `smoke_zhijian_e2e.py` | ✅ PASSED首推即成功无 502 |
| `smoke_user_jwt_policy.py` | ✅ 主路径 PASSED**当时 Z2 WARN** 异库仍 applied |
### 再测摘要2026-08-04 · 智建 Z2/Z4/Z6 重启后 · 宇恒确认)
| 脚本 | 结果 |
|------|------|
| `smoke_user_jwt_policy.py` | ✅ **PASSED****Z2 OK**(异库 403直推 + 库级 sync drain 上云均通过 |
### 结论
- **用户自助主路径Z1 + Z2 + Z3 + 宇恒库级 sync已打通**。
- **Z22026-08-04**:异库 `online_db_id`**403**(智建已修,宇恒已确认)。
- **Z4 已冻结并落地**push 不按通道表白名单拒收Binding + 用户 JWT 即权限;可自动建表。
### 宇恒澄清2026-08-04 · 对照截图)
| 现象 | 说明 | 责任方 |
|------|------|--------|
| SyncPage `↑37` | **不是表数**,是通道 `pushed_ok`push 成功次数);与线上 `orders` 行数巧合接近 | 智建 Z7 已改列名为「推送次数」并加副文案 |
| 显示 `ai_site_….db` 而非「AI建站智能体API」 | SyncPage 曾用落库 DSN 当主展示应对齐本地库名id/路径作副文案 | **智建 Z7**(主标题优先 Binding.`database_name`);宇恒 ensure 带 `database_name`/`display_name` |
| Binding `local_*``online_*`「名字对不上」 | **id 本就可不同**(映射);烟雾联调造的占位 id。真实库 `6a3df5…` 已补可读名「AI建站智能体API」默认隐藏 smoke Binding | 智建已清烟雾数据 + UI 过滤;宇恒正式 ensure 带可读名 |
| 本地多表 vs 线上曾只有 `orders` | **当时** remote 仅 `orders``accounts` / `填土高度` 尚未 push**不是**映射错库 | 后续 `填土高度` 已 push 对齐;空表仍需 **Z10 ensure**(见 §5.7 |
| 弹窗 4 表「0 条记录 / 0 字段」 | 卡片统计不准;库内仍有数据 | **宇恒前端**(智建 SyncPage 无此卡片);可用数据预览或打开 remote db |
| **要看线上表内容验同步** | SyncPage 原先只显示通道名/推送次数 | **智建已加**:「查看线上表」→ 表名/行数/字段 + 行预览;**可「删表」**(仅当前侧,需确认;不同步 DDL |
| **图1「线上表」≠ 图2 宇恒「数据表」** | 见 §5.4:一边是线上 A一边是本机 B | **不是串库**;未 push / 未 ensure 的表不会出现在线上 |
| **模块数据按智能体进库** | 模块与 dbsync 原无关联 | **§5.5 Z8 已通**;空表 **Z10c 宇恒已接** |
同步链路本身可用;展示层勿把「通道文件名 + 推送次数」当成「本地库名 + 表行数」。
### 5.4 图1 vs 图2表名「对不上」说明2026-08-04 · 现状已回写)
| | 图1 智建「查看线上表」 | 图2 宇恒「数据库管理 · 数据表」 |
|--|------------------------|--------------------------------|
| **看的是哪边** | 通道 **线上 A**(例:`ai_site_1785779755.db` | 本机正式库 **B**「AI建站智能体API」 |
| **联调当时(早)** | 仅 `orders`(约 37 行)+ `_ajz_*` | `accounts``orders``填土高度…` 等 |
| **回写后(已 push** | `orders`1+ **`填土高度(6标一工区)`96**outbox 全 `done` | 同行数对齐;`accounts` 仍 0 行 |
| **仍可能不一致** | 空表(如 `accounts`)未 ensure → 线上无此表 | 本机有空表;须走 **Z10** `schema/ensure`,单靠 outbox 不会建空表 |
| **如何验同步** | 「查看线上表」预览行;或对照 `_ajz_sync_meta` | 宇恒「数据预览」看本机真实行 |
**结论**:表名集合不同 = **本机有、线上尚未 push/ensure**,不是 Binding/通道指错库。有行的表走 drain push**空表必须走 Z10 ensure**。
`_ajz_sync_*` 是中间件系统表(版本/outbox**不是业务模块表**控制台「查看线上表」默认隐藏Z8e
### 5.5 【进展 Z8】模块数据 ↔ 本机库 + 智能体库
> **产品原则2026-08-04 确认)**
> 1. **模块数据也要能在本机库 B 里管理**(宇恒数据库区:建表/增删改查/导入),与普通业务表同一套体验,不是只能在聊天「模块操作」或线上 Postgres 里改。
> 2. 本机改完后,经松离线 **push → 该智能体绑定的线上库 A**;换机/仅线上可用 **pull/bootstrap** 灌回本机。
> 3. **哪个智能体的模块 → 进哪个智能体的库**(通道/`online_db_id` 绑定)。
> **进展**Z8ag 已通本机镜像、绑库、SyncPage 模块展示);空表 **Z10c 宇恒已接**`sync_schema`)。
**目标数据流**
```text
宇恒本机库 B含模块表可本地管理
│ sync / agent drain push
智能体绑定的线上库 A与通道 remote 一致)
│ 智能体 / 控制台「查看线上表」核对
(可选)模块页 CRUD 读同一 A或本机 B 为唯一写入口 + 同步
```
| 编号 | 优先级 | 诉求 | 说明 | 现状 |
|------|--------|------|------|------|
| **Z8f** | **P0** | **模块表可在本机库管理** | 模块实体表出现在宇恒「AI建站智能体API」类本机库中数据预览/录入/导入与业务表一致;开通 sync 后进 outbox | **宇恒已通2026-08-04**`local_module_db` 镜像;表名 **`模块·{模块名}·{资源}`**无下划线列含「所属模块」「模块slug」写网关 → outbox → push |
| **Z8a** | **P0** | **统一「一份表」叙事** | 废弃「模块一套库、松离线又一套」双轨并存为默认;默认:**本机 B 为模块数据管理面**A 为同步副本(或同库) | **已补开通说明**`docs/数据同步-开通说明.md` |
| **Z8b** | **P0** | **智能体选库 / 选通道** | 该智能体绑定 `channel_id` + `online_db_id`;宇恒可选「同步到哪个智能体库」 | **智建已落实**:用户管理可绑通道/线上库/落库名SyncPage 按智能体筛通道。宇恒开通 sync 时会 `PUT` 通道 `agent_id`/`app_slug`(读 `YXD_SYNC_*` |
| **Z8c** | **P1** | **线上与本机对齐** | 智能体读模块数据时优先本机 B离线或已同步的 A发布蓝图在本机建表UUID PK再 sync避免只在平台 schema 建一份 | **部分**:智能体新建发布若设 `database_name``database_per_app` 落该库;本机建表靠宇恒 Z8f |
| **Z8d** | **P1** | **本地上传 / 编辑 → 上云** | 本机模块表变更经 Binding + push 进该智能体 A「查看线上表」可核对 | **路径已通**:本机改 `模块·…` 表 → drain → 「查看线上表」应见同表;若仍缺行 = 未 push / 未 drain |
| **Z8e** | **P2** | **查看线上表默认藏 `_ajz_*`** | 减少「表名不对」误解 | **智建已做默认隐藏**2026-08-04 |
| **Z8g** | **P1** | **本机可见 = 线上可见(展示闭环)** | SyncPage「智能体/模块」勿长期「未绑模块」;「查看线上表」列出本机已有业务表(含 `模块·…`);模块列优先展示名 | **智建已落实2026-08-05**:模块展示名 + slug 副文案;策略表标「参考/整库同步」 |
**分工建议**
| 方 | 负责 |
|----|------|
| 智建 | Z8a 文案Z8b 智能体↔通道/库绑定;**Z8g SyncPage 模块展示名**Z10a/b API开通说明 |
| 宇恒 | **本机库管理模块表Z8f 已通)**;开通/全量推送时挂通道 `agent_id`/`app_slug`**Z10c 已接** sync 周期 schema/ensure |
**验收Z8 完成后)**
1. ✅ 在宇恒本机库能看到并编辑模块相关表Z8f
2. ✅ 本机改模块行 → sync 后智建「查看线上表」出现同表同行(有数据路径;空表见 Z10
3. ✅ 智能体 X 只落到绑定库管理员可按智能体筛通道验数Z8b
4. ✅ 文档写明:本机有、线上无 = 尚未 push/ensure不是串库。
5. ✅ SyncPage「智能体/模块」显示智能体名 + 模块**展示名**Z8gslug 作副文案)。
### 5.5.1 【联调补丁·2026-08-04】编辑发布撞 page id
**现象**:编辑模块 `mode=add_pages` 时报
`page id already exists: record_list`(模型复用了已有页 id实为要改 API `import`)。
**智建已改**`platform/internal/blueprint/merge.go` — 同 page id **合并 actions**(不再 400同 path resource **并集 operations**;仅有 API/按钮更新也视为可发布(`UpdatedResources`/`UpdatedPages`)。需**重启 platform** 后生效。
**与 Z9**Z9 默认带 import 后,此类「仅为开导入而编辑」会减少;本补丁仍保留给「改已有页能力」用。
### 5.5.2 【已落实 Z8g】本机可见 ↔ 线上可见(展示增强 · 2026-08-05
> **联调现象(对照两图)**
> - 宇恒「数据表」已有:`orders`、`填土高度(6标一工区)`、**`模块·测·Sheet1`**(及业务表)。
> - 智建 SyncPage有 `app_slug` 时解析已发布模块**展示名**slug 作副文案;仅缺绑定才「未绑模块」。
> - 通道表白名单 UI 标为「策略表(参考)」/「整库同步中」,避免误读为只能 sync 名单内表。
| 编号 | 优先级 | 诉求 | 说明 | 负责 |
|------|--------|------|------|------|
| **Z8g-1** | **P1** | **SyncPage 模块列可读** | 展示名 + slug 副文案 | **智建已落实** |
| **Z8g-2** | **P1** | **通道绑定可被宇恒写入** | `PUT` 可写 `agent_id`/`app_slug` | **智建保持契约**;宇恒已接 |
| **Z8g-3** | **P1** | **查看线上表 = 本机业务表子集** | inspect + 默认藏 `_ajz_*`;缺表文案 | **智建已具备** |
| **Z8g-4** | **P2** | **白名单列勿误导** | 「策略表(参考)」/「整库同步中」 | **智建已落实** |
**验收**
1. 通道已挂 `agent_id=1``app_slug=mismatch_test2` → SyncPage 不再显示「未绑模块」,模块列可见「测」或至少 slug。
2. 本机 `模块·测·Sheet1` 全量 push 后,「查看线上表」出现同名表且行数对齐(幂等 upsert
3. 本机 `填土高度(6标一工区)` / `orders` 与线上一致。
---
### 5.6 【已落实 Z9】模块导入默认能力方案 A · 2026-08-04
> **联调现象**
> 宇恒绑定账号已具备 `row.import`(导入数据),智能体调用
> `POST /api/v1/apps/{slug}/{resource}/import` 仍返回
> `HTTP 400: operation import not allowed`。
> 根因(已修):平台在角色权限之外,还校验蓝图 `apis.resources[].operations` 是否含 `import`
> 旧生成默认只有 `list/get/create/update/delete`。现已默认带 import且 publish 兜底补全Z9
> 编辑补丁时模型仍可能只改部分表;**再发布**或依赖 Z9d/`EnsureDefaultImportExport` 即可。
> 另:同 page id 编辑曾 400见 §5.5.1merge 已改)。
**产品结论:采用方案 A**
绑定宇恒账号解决「谁可以调导入接口」;方案 A 解决「业务表是否声明允许导入」,避免「有权限仍 400」。
不采用「仅有 `row.import` 就跳过 `hasOp`」(方案 B作主路径以免列表页无导入入口、前后端能力不一致。
| 编号 | 优先级 | 诉求 | 说明 | 现状 |
|------|--------|------|------|------|
| **Z9a** | **P0** | **生成默认带 import** | 业务 `apis.resources[].operations` 默认含 `list,get,create,update,delete,**import**,**export**`(除非需求明确禁止) | **已落实**`generation_rules` + `prompt-contract.md` |
| **Z9b** | **P0** | **列表 actions 对齐** | list 页 `actions` 默认含 **`import`**(及可选 `export` | **已落实**:发布兜底补 list actions |
| **Z9c** | **P0** | **改 prompt / 文档** | `prompt-contract.md`:由「提到导入再加」改为「默认加;明确不要再去掉」 | **已落实** |
| **Z9d** | **P0** | **publish/merge 兜底** | 发布或编辑合并时:业务 resource 缺 `import` 则自动补上(可配置,默认开) | **已落实**`EnsureDefaultImportExport`;但**只在 publish 路径触发** |
| **Z9e** | **P0升格** | **存量模块一次性扫库补齐** | 平台启动扫库 + 管理接口;不依赖用户再发布 | **已落实2026-08-05**:启动 `RunBackfill…``POST /api/v1/admin/apps/ensure-import`;模块管理「一键开启导入」;超管 `…/platform/apps/ensure-import` |
| **Z9f** | **P1** | **读路径也兜底(双保险)** | Import/Export 入口内存补齐后再校验;可读错误文案 | **已落实**`ensureImportOpInMemory` + 区分蓝图缺 import vs 权限 |
**存量复现(必须消灭)**
```text
POST /api/v1/apps/coerce_fields/items/import
→ HTTP 400: operation import not allowed
蓝图 ops 实为list, create, get, update, delete ← 无 import
Z9d 已合入后新建模块正常;未再发布的旧模块仍坏)
```
**智建交付标准Z9e 验收)**
1. 不手工编辑、不触发「编辑模块」的情况下,对租户内**全部**已发布可写业务 resource 执行扫库后:`hasOp("import")==true`
2. 回归:任取 3 个「合入 Z9 之前发布」的旧 slug直接 `POST .../import` **不再**出现 `operation import not allowed`
3. 控制台可选:「数据同步 / 模块管理 → 一键开启全部业务表导入」按钮,调用同一扫库逻辑。
4. 文档写明Z9d≠存量修复上线 Z9 后**必须跑一次 Z9e**,否则用户会反复以为「绑定/权限坏了」。
**分工**
| 方 | 负责 |
|----|------|
| **智建** | **Z9e/Z9f 已落实**;保持 Z9ad错误文案区分「蓝图未开 import」vs「账号无 row.import」 |
| 宇恒 / 智能体 | 导入前尽力自动补齐(已做);表单选对 slug/resource**重启 platform 或点「一键开启导入」后**旧模块即可 import |
**验收(含存量)**
1. 新建模块需求**不提**「导入」→ 发布后 `POST .../import` 不再因 `operation import not allowed` 失败。
2. **旧模块**Z9 合入前发布、之后未再发布)在跑完 Z9e 后同样不再 400。
3. 用户明确写「禁止导入」的表可不含 `import``disable_default_import` 或表级标记)。
4. 400 文案可读:指出是蓝图 operations 缺 import而不是笼统「操作失败」。
### 5.7 【双方已接 Z10】空表也要两侧建齐2026-08-04 / 宇恒接 2026-08-05
> **现象**
> 本机有空表(如 `accounts` 0 行)时,线上「查看线上表」没有该表;
> 仅靠 outbox 行 push 时,**没有行就没有建表事件**。同理,线上空表下行时本机若缺表,旧 pull 只给空 `items`、不给列,本机无法建表。
| 编号 | 优先级 | 诉求 | 现状 |
|------|--------|------|------|
| **Z10a** | **P0** | 本机空表 → 线上建空表 | **智建已落实** `POST /api/v1/agent/sync/channels/{id}/schema/ensure` |
| **Z10b** | **P0** | 线上空表 → 本机建空表 | **智建已落实** `POST .../schema``pull`/`bootstrap` 响应带 `columns` |
| **Z10c** | **P0** | 宇恒 sync 周期调用上述 API | **宇恒已接2026-08-05**`sync_schema.ensure_before_drain`选「同步」force ensuredrain 前 TTL 节流 |
**契约摘要**
```http
POST .../schema/ensure
{ "online_db_id": "...", "tables": [{ "table": "accounts", "pk_column": "id", "columns": ["id","username",...] }] }
POST .../schema
{ "online_db_id": "..." }
result.tables[].name / columns / row_count
```
列一律按 TEXT + 指定 PK 建空表。**Z10d2026-08-05 晚)**:已存在表不再整表跳过,缺列则 `ALTER TABLE … ADD COLUMN``platform/internal/dbsync/ensure_table.go`)。**合入后生产 `./restart.sh --pull` 生效**;未 pull 时对「先按 `_row_id` 建表、后 push 注入 `id`」会持续 `has no column named id`
**验收**
1. 本机空表 ensure 后,智建「查看线上表」可见同名 **0 行**表。
2. 换机 / 仅线上schema → 本机建空表 → bootstrap 灌行。
3. 有数据的表仍走原 pushensure **不替代** outbox 行同步。
4. **Z10d**ensure 带新增列名时,已存在表补列成功,随后 push 含新列不再 503。
### 5.8 【已落实 Z11】蓝图增字段须迁 Postgres2026-08-05
> **联调现象**
> 模块 `record` 蓝图后来带了 `title/content/status`,但 Postgres 表仍只有 `col_0/col_1/col_2`。
> 导入 Excel 若含「标题/内容/状态」→ `pq: 关系 "record" 的 "title" 字段不存在 (42703)`,整批 skipped。
> 反过来蓝图把这些标成非空、Excel 又没有时,会先报 `missing required field: title`。
| 编号 | 优先级 | 诉求 | 负责 |
|------|--------|------|------|
| **Z11a** | **P1** | 编辑/发布增字段后 **自动 ALTER** 业务表补列 | **智建已落实**`BuildPostgresAlterDDL` + publish 合并执行;字段-only merge 可发布 |
| **Z11b** | **P2** | 导入时Excel 列映射到蓝图有、库无的字段 → 明确提示迁库 | **智建已落实**42703 行错误附加「请重新发布以自动补列」 |
**说明**:存量表补列一律可空,避免 NOT NULL 迁库失败。已有库需**再发布一次**(或编辑发布增字段)才会 ALTER之后新字段随发布自动补。
### 5.9 【智建已落实 Z12】生产账号绑定即落点禁止手填通道 / 手建通道2026-08-05
> **产品约定(生产 · 冻结意向)**
> 1. **一个登录账号 / 一个智能体 ↔ 本公司同步落点**:绑定后,该账号的本机库只进自己的(或公司共享的)线上库,**不会**接到别人账号的库。
> 2. **用户不应**:手抄 `channel_id`、也不应先去「数据同步」里点「新建通道」才能用。
> 3. **通道对用户隐藏或自动生成**:公司开通 / 智能体启用时,平台**自动**准备本公司默认同步通道Postgres 线上 A并写到智能体/账号绑定上;控制台「数据同步」留给运维改 DSN不作为开通必经 UI。
> 4. **共享库**:仅「公司共享数据库」可被多名成员同步到同一 `online_db_id`;个人库默认隔离。
> 5. 方向默认 **`local_to_remote`**(松离线 agent push「双向」不是用户必选项。
> 6. SyncPage 列表须展示可复制 **通道 ID**(运维用;普通开通流程不依赖)。
| 编号 | 优先级 | 诉求 | 说明 | 负责 |
|------|--------|------|------|------|
| **Z12a** | **P0** | **换票带回绑定** | `POST /api/v1/auth/token`(及登录)响应含 `channel_id` / `online_db_id` / `database_name` / `sync_bound` | **智建已落实** |
| **Z12b** | **P0** | **智能体自查绑定** | `GET /api/v1/agents/me`:智能体 Bearer 可读自己的通道/线上库/状态 | **智建已落实** |
| **Z12c** | **P0** | **启用即自动绑通道** | 创建/启用智能体 → `EnsureAndStartSystemDefaultChannel``IsSystemDefault`,表白名单可空,**默认运行中**);写回 `channel_id`+`online_db_id`DSN 见 `DBSync.DefaultRemoteDSN` | **智建已落实**(含默认启动 `cb76824` |
| **Z12c-1** | **P0** | **账号级隔离** | Binding / push用户 JWT 只能写本人 Binding**智能体 JWT** 另按挂载落点(**Z14c 已修** | **智建已落实** |
| **Z12c-2** | **P1** | **公司共享库** | Binding `shared=true`:同租户成员可访问该 `online_db_id`;仅管理员可设 | **智建已落实** |
| **Z12d** | **P1** | **SyncPage 展示通道 ID** | 运维可见 + copyable | **智建已落实** |
| **Z12e** | **P1** | **唯一默认通道** | 每公司至多 1 条 `is_system_default`;复用已有默认 | **智建已落实** + 宇恒「仅 1 条则写入 env」 |
| **Z12f** | **P1** | **开通 UX** | 启用智能体即可DSN 用公司级默认 | **智建已落实** |
| **Z12g** | **P1** | **默认同步通道默认启动** | 创建 `Enabled=true`;重启对 `IsSystemDefault` 自动 Start控制台保存后自动启动 | **智建已落实**`cb76824` |
**宇恒已做2026-08-05**
1. 当前生产通道 `691575c0-…` 已写入 env过渡缓存Z12c 落地后应由换票/唯一通道自动获得)。
2. `resolve_sync_channel_id()`env → Binding → 租户仅 1 通道自动选用。
3. 不把「手建通道 + 手抄 ID」当作产品终态。
**验收**
1. 新公司:只「启用智能体」,**不**点新建通道,换票已带 `channel_id`,宇恒可 drain。
2. 账号 A 的本机库 push 不会出现在账号 B 的线上库;共享库仅在显式共享时可见。
3. 用户全程无需 F12、无需手填 `YXD_SYNC_CHANNEL_ID`
### 5.10 【双方代码已接 Z13】绑定流程简化绑定码 / 手机号2026-08-05
> **端到端体验(生产目标)**
> 1. 后台已绑定(或用户用绑定码/手机号完成绑定)→ 宇恒本机库选 **「同步」** → **直接可用**(自动通道、自动 Binding、agent drain无第二套配置。
> 2. 绑定流程必须比「登记智能体 + 抄通道 ID + 填 env」简单。
> 3. **生产·宇信达联调账号**`13531041944`(公司侧专用;**禁止**用超管号 `13531041945`)。
> 4. **试运行**:未接短信平台 → **关闭短信验证**;正式接入后再开(见下)。
**推荐交互**
```text
公司管理员(智建后台)
→ 生成「绑定码」→ 发给终端用户
宇恒 · AI建站智能体
→ 检测绑定状态
├─ 已绑定 → 静默换票;本机库选「同步」即可用
└─ 未绑定
├─ 宇恒已有手机,且智建同号有成员
│ → 弹「已有账号是否绑定?」→ 确认 / 取消
├─ 表单:【绑定码】或【手机号】
│ · 宇恒未绑手机 → 仍可直接填手机号
│ · 试运行:不校验短信,仅确认绑定
│ · 正式:同号可免短信/凭票;异号须短信
└─ 无成员 / 无码 → 提示联系管理员
→ 绑定成功 → 选「同步」→ drain零通道配置
```
**试运行 vs 正式**
| 项 | 试运行(当前) | 正式(接短信后) |
|----|----------------|------------------|
| 配置 | `SMS.RequireForBind=false`(或 `Provider=off` | `RequireForBind=true` |
| 探测 | `GET /api/v1/auth/bind/policy``trial_mode=true``sms_required_unless_attested=false` | `trial_mode=false` |
| 填手机号 | 可绑;**不校验短信** | 异号 / 宇恒未绑号须 `sms_code` |
| 同号免验 | 可不传 `sms_code`;确认即可 | 同号 attested 或走 `ticket-exchange` |
| 必保留 | **「已有账号是否绑定」确认**`confirm=true` | 同左 |
**同号 / 验证规则**
| 场景 | 行为 |
|------|------|
| 试运行 / 短信未接入 | **不校验短信**;填号 + 确认即可 |
| 宇恒未绑手机 | **仍可直接输入手机号**;正式环境须短信 |
| 宇恒已绑且输入 = 已绑号 | 正式可免短信 / 凭票;命中成员仍弹确认 |
| 宇恒已绑且输入 ≠ 已绑号 | 正式**必须短信** |
| 命中公司成员 | **必须询问**「已有账号是否绑定」;不得静默 |
| 用户确认 | 绑定该账号 + 公司默认同步落点 |
| 用户取消 | 不绑;可改绑定码 / 换号 |
| 公司无此号 | 提示用绑定码 |
| 联调样例 | **`13531041944`**;勿用 `13531041945` |
| 编号 | 优先级 | 诉求 | 说明 | 负责 |
|------|--------|------|------|------|
| **Z13a** | **P0** | **后台生成绑定码** | `POST/GET/DELETE /api/v1/admin/bind-codes`SyncPage「绑定码」区 | **智建已落实** |
| **Z13b** | **P0** | **绑定码兑换** | `POST /api/v1/auth/bind-code/redeem``host_key` + `code` | **双方已接**(智建 API + 宇恒表单) |
| **Z13c** | **P0** | **手机号探测** | `POST /api/v1/auth/bind/phone-lookup`(不直接绑定) | **智建已落实** |
| **Z13c-1** | **P0** | **确认绑定** | `POST /api/v1/auth/bind/phone-confirm``confirm=true` | **双方已接**(宇恒弹窗 + 本机 API |
| **Z13c-2** | **P0** | **验证策略 / 凭票 / policy** | 试运行关短信;`GET /auth/bind/policy`;正式异号短信;凭票 `ticket-exchange` | **双方已接**(生产须对齐 Secret + 部署 gateway |
| **Z13d** | **P0** | **未绑定检测与提示** | `sync_bound=false` → lookup / 表单 | **宇恒已接** `ensure_sync_bound_interactive` |
| **Z13e** | **P0** | **选同步即用** | 库选「同步」→ Binding + drain零手填通道 | **宇恒已接** `POST /database/sync/mode` |
| **Z13f** | **P1** | **安全** | 试运行关短信;正式强制异号短信;审计 | **智建开关已落实** |
**验收**
1. 绑定码 → 填码 → 绑定成功 → 库选「同步」→ 数据进公司线上库。
2. 同号 `13531041944` → 弹「已有账号是否绑定」;确认绑、取消不绑;禁用 `13531041945`
3. 宇恒未绑手机 → 仍可直接填号;试运行免短信,正式须短信。
4. 试运行:无 `sms_code` 也可 confirm`GET /auth/bind/policy``trial_mode=true`
5. 未绑定用户只见绑定码/手机表单,**不见** DSN/通道 ID。
6. 未确认 / 他人绑定码,不能绑进别的公司或别人账号。
7. 正式且启凭票:同号走 `ticket-exchange`,不再依赖明文 `attested_same_phone`
### 5.11 【新增 Z14 · 2026-08-05 晚】表级指纹对账(慢心跳)+ 联调踩坑
> **诉求**
> 仅靠 agent「推送心跳」pending/pushed发现不了「本机 96 行、线上 0 行」或「行数相同但内容不同」。
> 需要**慢周期表级指纹**`row_count` +(可选)`content_hash`;不一致则 ensure + 对该表 full-push。
| 编号 | 优先级 | 诉求 | 现状 |
|------|--------|------|------|
| **Z14a** | **P0** | 宇恒本机指纹 + 对比线上 + 自动修复 | **宇恒已接**`sync_fingerprint.py`TTL **600s**`POST /database/sync/fingerprint` |
| **Z14b** | **P1** | 智建 `content_hash` | **已合入,生产 pull**`POST .../schema/fingerprint` |
| **Z10d** | **P0** | ensure 已存在表补列 | **已合入,生产 pull**;未 pull 时 `填土高度``id` 仍 503 |
| **Z14c** | **P0** | 智能体 `online_db_id` ACL | **已合入,生产 pull**`agentOwnsOnlineDB`;人类 Binding 规则不变 |
**契约摘要Z14b**
```http
POST /api/v1/agent/sync/channels/{id}/schema/fingerprint
{ "online_db_id": "..." }
result.tables[]: { name, pk_column, row_count, content_hash }
```
哈希:`sha256( table+"|"+pk+"|" + Σ json(row, sort_keys=True)+"\n" )` 前 32 hex。
**分工**
| 方 | 做什么 |
|----|--------|
| 宇恒 | 本机指纹;优先 fingerprint API404 回退 `row_count`;不一致 ensure+full_push |
| 智建 | 生产 pullfingerprint + Z10d + Z14c |
**联调踩坑(宇信达 · 已回写)**
1. **行数对不上**:开通同步不会自动灌历史 → full-push指纹可检出。
2. **`填土高度``id`**push 默认 pk=`id`;须 Z10d 补列或删表重建后再推(宇恒 `mark_error` 堵队已修)。
3. **`_uN` 曾 403**:智能体 JWT `user_id=agent_id`Binding 在人类成员上 → **Z14c** 放行挂载 online。
**验收**
1. 改本机行数后 ≤TTL 检出 mismatch 并排队修复。
2. 未 pull fingerprint仅 count 对账仍可用。
3. pull 后:同 count 不同内容 → `hash_mismatch`
4. Z10d pull 后:`填土高度` 可推满。
---
## 6. 联系与附件
- **待改清单(优先看)**§0.2**双方配合**见 **§0.3**
- 方案:`松离线-dbsync方案-最终版.md`(含 2026-08-01 联调建议落地记录)
- 宇恒使用说明:`宇恒-松离线数据同步使用文档.md`(含 Z10 schema/ensure、Z13 绑定)
- 开通说明:`docs/数据同步-开通说明.md`
- 绑定策略探测:`GET /api/v1/auth/bind/policy``trial_mode` / `require_for_bind`
- 本意见如与冻结方案冲突,**以冻结方案为准**§5.15.11 为产品增量与复测记录,不推翻 H1H6 默认无感约束。