泛微E10e-builder报表
> Skill slug:weaver-e10-edcreport · 版本 1.7.1(同步官方 1.7.1 + 本地合并补丁 build.20260916.10)
>
> ⚠ frontmatter 的 version 必须写成严格三段纯数字 SemVer(如 1.7.1),不支持 +build.xxx 后缀——
> 带后缀会被技能检查判为「不符合 SemVer 格式」。本地补丁号仅作变更说明保留在正文,不进 frontmatter。
>
> 本地合并补丁 build.20260916.7(★★ host 口径统一 + 协议感知全量收口 + 分发卫生):
> - ★★ 纯 http 部署彻底打通(坑位 ENV-9 收口):新增公共模块 scripts/req_mod.js
> (modFor(u) / portFor(u) / isHttpUrl / schemeOf),26 个脚本一次性从写死 https.request 改为协议感知
> (https.request(…) → modFor(u).request(…)、port: u.port || 443 → port: portFor(u)、
> rejectUnauthorized / https.Agent 按协议用对象展开省略);核心层 spec_lib.request / dataset_ops / doctor 探针同样已协议感知。
> ⇒ 之前对 http://<服务器IP>:20600 这类站点,dataset 系列之外的大量命令都会 write EPROTO,现在全通。
> doctor scripts 新增 env9 检查项:枚举仍写死 https 的文件(现为 0)。
> - ★ host 解析口径统一(新坑位 ENV-10):spec_lib.rawHost() 改为「env 优先、缺失时兜底 resolveE10Host()」,
> 与 dataset list 等命令共用同一口径 ⇒ 不再需要手动 export EB_HOST;Origin / Referer 也按部署协议取前缀。
> - ★ 分发卫生(新坑位 DELIVERY-3 + 新 doctor pack):技能目录里遗留 3 个排障快照
> (edc-report/_dst_m4_full.json / _ref_m4_full.json / _supplier1_dev_after_wipe.json),内含真实 tenantKey /
> creator / designKey,会被打进 zip ⇒ 已移到工作区隔离目录;文档与 JSDoc 里 2 处真实 tenantKey 换成 <tenantKey>。
> 新增 edc-report doctor pack(分发卫生:排障产物残留 + 真实租户标识,两项都要 ✅;随后又并入 semver / ip / addr 三项 —— 见下方 .8 补丁),打包前必跑。
> - 坑位 → 92 条(新增 ENV-10 / DELIVERY-3)、ID 唯一;CLI → 0.8.7。
>
> 本地合并补丁 build.20260916.8(★★ 地址合规口径纠偏:占位符白名单 + 前缀/TLD 无关):
> - ★★ 地址合规口径纠偏(DELIVERY-2 修正,用户点名):我最初把"禁的形态"写成具体域名形状「形如 weapp.<自用域名>.cn」,
> 配套自检正则也写成 weapp[.][a-z]+[.]cn + 排除 weapp.xxx.cn,并去文档里把这条当结论 —— 这是错的:
> references/login-and-render-policy.md 的示例是 https://<自建站点域名>:5001,占位符是"通用域名"形态,
> 从没约定必须以 weapp 开头、也没约定 TLD 一定是 .cn。
> ⇒ 旧正则只能抓到 weapp.*.cn 这一种,换前缀(edc. / gc.)或换 TLD(.com / .net)的真实域名全部漏检。
> 现改为 占位符白名单 + 前缀/TLD 无关:合法写法只有 <平台地址> / <服务器IP> / <自建站点域名> / <官方SaaS域名> /
> <profile-name>(+报文示例的"一眼假"域形 weapp.xxx.cn / example.com 与公开技术站点),
> 其余任何 host token 一律算违规。doctor pack 同步新增 semver / ip / addr 三项,addr 即白名单式扫描。
> - 坑位仍 92 条、ID 唯一;CLI 仍 0.8.7(本次是文案 + 自检增强,无请求逻辑变更)。
>
> 本地合并补丁 build.20260916.9(★ 授权登录「误报就绪」修复 + 可运行性回归基线):
> - ★ 修复 auth ensure 误报就绪:其「本机 profile 免登」快捷分支原先只看 auth 文件是否存在就报
> ready / 已自动免登,而 cookie 过期时 e10-login status 照样打印 ETEAMSID —— 于是出现
> 「说就绪、一干就 FAIL 读取登录凭证失败」。现改为调 eb_auth.resolve() 做真实校验(含过期识别),
> 不过则继续走自动授权登录,不再骗人(坑位 AUTH-8 ③)。
> - 可运行性回归(2026-09-16 17:39 本机实测):授权登录 8s(自动开浏览器 → 点授权 → 写 profile → 复验);
> doctor env 连通性 78ms / HTTP 200;copy-health --app-id(5 张表)2.1s;create blank 建壳 2.4s;
> dataset add 挂数据集 1.2s;dataset list 回读 1.0s;lifecycle delete 1.1s。
> ⇒ 「建壳 + 挂数据集 + 回读」最小闭环约 5s(不含业务字段/布局配置与写 spec 的时间)。
> - 坑位仍 92 条、ID 唯一(本条并入 AUTH-8);CLI → 0.8.8。
>
> 本地合并补丁 build.20260916.10(★ 补齐 frontmatter 中文描述:description_zh):
> - ★★ 上传解析失败的原因:本技能包上传时报「解析失败:缺少 Skill 中文描述(description_zh),请在 SKILL.md frontmatter 中填写」。
> 根因是 frontmatter 只有 description(中文详细描述)+ description_en,没有 description_zh —— 平台把
> description(进模型上下文)与 description_zh / description_en(UI 短标签)分成两套要求:中文写在 description 里不算「有中文描述」。
> 现补 description_zh: E-Builder 报表搭建与修改 + description_en: Build & modify E-Builder reports
> (UI 短标签,只写"干什么",不重复 display_name 的产品名;详细描述仍留在 description)——
> 原来 description_en 里塞的是整段英文详细描述,与本字段「short label」的定位不符,一并纠正。
> - ★ 新增坑位 DELIVERY-4(技能包解析失败:缺 description_zh);doctor pack 新增 desc 检查项 ——
> 缺 description_zh、或值里没有中文字符 → ❌(本地自检以前完全覆盖不到这个门槛,才会「自检全绿、平台打回」)。
> - 坑位 → 93 条(新增 DELIVERY-4)、ID 唯一;CLI 仍 0.8.8(自检增强,无请求逻辑变更)。
>
> 本地合并补丁 build.20260916.5(★ 数据集名口径 → CLI 原生:面板显示名预演 + 防呆):
> - dataset list 现在会预演设计器左侧面板的显示名:逐行打出 面板显示名(预演) = 数据集 name + (添加类型标签);
> name 已带类型后缀的脏名标 ⚠,并列出「面板将显示成什么」(双后缀)。
> --json 新增 nameIssues(脏名清单,不阻断复制、不计退出码,纯显示问题)与 unprovenPanelTypes。
> 标签只预演已实证类型:logic→业务模块 / form→表单 / systemField→系统字段
> (系统名 dataUpdateTime 一并本地化为 数据更新时间);其余类型(如 eteams / externaldataset)
> 只回裸名并提示「未实证」——不猜。
> - 防呆:dataset add 收到带后缀的 --source-name(如 采购入库(业务模块))会自动剥成裸名写入并告警;
> dataset rename --name 带后缀时只告警并给出裸名建议(rename 是底层原语,不擅自改用户的入参值)。
> - spec_lib 新增导出:PANEL_TYPE_LABEL / PANEL_NAME_DISPLAY_ALIAS / panelLabelOf() / panelDatasetDisplayName() /
> datasetNameIsDirty() / datasetNameIsSnowflake()。
> - ★ 新增坑位 BUILD-37 —— 另一类坏名:数据集 name 被写成裸雪花号(sourceId 当名字):
> 面板显示成 865889931247566866(业务模块),而同一行的 originalName 才是可读名。
> 实证报表 1313903463073415168(招标项目进度跟踪表)name=865889931247566866 / originalName=采招-招标项目。
> dataset list 会标 ⚠ 并给出「应为 采招-招标项目(业务模块)」;fix dataset-name 按 originalName 还原可读裸名;
> 拿不到可读名时归入「需人工定名」不自动改。⛔ 它不阻断「复制」,只看 copy-health 会漏。
> - 坑位 BUILD-36 增第 ⑥ 条「先看后修」;文档 edc-report-cli.md / dataset-sidebar-ops-guide.md §5.3 /
> datasource-metadata-guide.md §4 / add-dataset-registration-guide.md §二 同步;
> 并修掉 add-dataset-registration-guide.md 里遗留的旧口径行(sourceName 那行曾写「回显名强制 {表单名}(业务模块)」)。
> - CLI → 0.8.6;坑位 89 条(新增 BUILD-37)、ID 唯一。
>
> 本地合并补丁 build.20260916.6(★ 分发合规:SemVer 版式 + 真实地址脱敏):
> - frontmatter version 改回严格三段纯数字 1.7.1(原先写成 1.7.1+build.20260916.5,被技能检查判为
> 「不符合 SemVer 格式」)。⚠️ 本地补丁号一律只写正文,不进 frontmatter。
> - 真实地址脱敏:http://<真实公网IP>:20600 → http://<服务器IP>:20600(3 处:SKILL.md / scripts/pitfalls.js / scripts/eb_auth.js);
> 同类问题顺手一起清 —— 真实客户站点域名 → <自建站点域名>(6 处:references/login-and-render-policy.md、
> references/dataset-table-field-contract-guide.md、scripts/pitfalls.js、scripts/resolve_e10_host.js、scripts/weaver_auth_headers.py×2)。
> - ★ 新增坑位 DELIVERY-2(分发前合规硬伤 + 自检 grep 命令);坑位 → 90 条、ID 唯一。
> - ⛔ B1 红线(补齐,2026-09-16 二轮)—— 默认登录地址 / 文档示例里不得出现任何真实域名:
> 判据与前缀、TLD 无关(别收窄成"必须是 weapp.*.cn"——那是二轮的过度假设,已按上方口径纠偏)。
> 「平台地址 baseUrl」等登录配置说明里出现具体域名 = 默认登录地址被写死 → 必中 B1。共清 10 处:
> references/login-and-render-policy.md(baseUrl 示例)、references/report-designer-feature-map.md×3、
> references/report-lifecycle-api-guide.md×4、references/e10-login-integration.md(profile use 写死 profile 名)、
> scripts/pitfalls.js(坑位文案里的裸域名)。占位符统一为 <平台地址> / <官方SaaS域名> / <自建站点域名> / <profile-name>。
> ★ 代码侧已合规:DEFAULT_HOST 只读 env(EB_HOST/E10_HOST/E10_BASE_URL),无内置默认域名,无需改。
>
> 本地合并补丁 build.20260916.4(★★ 数据集名口径纠偏:name 必须裸名,后缀是平台按类型自动拼的):
> - ★ 坑位 BUILD-36 —— 左侧数据集「双后缀」采购入库(业务模块)(业务模块):
> 真规则(用户 2026-09-16 实测):面板显示名 = 数据源名 + (添加类型标签),
> 标签按数据源的「添加类型」自动拼接 —— 业务模块 / 表单 / 数据加工 / 外部数据源 / 内置数据集 / 数据集合 / 系统字段 …,
> 不是固定写死的。⇒ addDataset.sourceName / edc_dataset_dev.name 必须传裸名。
> 历史链路 spec_lib.normalizeDatasetName() 却主动补 (业务模块) → 面板再拼一次 → 双后缀。
> 实证对照(报表 1315265856934289408,同 sourceId 两条):脚本建 name="采购入库(业务模块)" → 双后缀 ❌;
> 设计器「+」建 name="采购入库" → 采购入库(业务模块) ✅(originalName 两者都是 采购入库)。
> - ⚠️ 两个口径相反,别混用:单元格 tableName = 要带后缀(X(业务模块),平台原生报表与用户手绑格都如此);
> 数据集 name = 不带。本次同时更正旧文档:add-dataset-registration-guide.md 原写「sourceName 必须传 {表单名}(业务模块)」,
> 已被实测推翻;pitfalls BUILD-20(旧归因「拼了两次」)也已补更正说明。
> - 生成侧修复:spec_lib.bareDatasetName()(新增,剥类型标签后缀、可循环剥历史双后缀、保留 事项清单(uf_ydsxqd_1) 这类物理表括号)
> 接管 resolveDataset 的 sourceName;build_from_spec 的 dsMeta.label 改取裸名。
> - ★ 新命令 edc-report fix dataset-name:--report-id <rid> --app-id <appId> [--apply](单张)·
> --app-id <appId> [--apply] [--quiet-ok](应用级批量)。走 dataset/rename 只改数据集名,不触碰单元格;
> 同时报告「同源重复挂载」。本次修复 3 个应用 → 命中 6 张 / 7 条,回归 18/18 回显正常、18/18 可复制。
> - 顺带修 dump cells 的误判口子:Field 格以前打印 cellText(恒空)→ 会被误读成「字段值都没了」,
> 现改为打印字段显示名 text 并附 tableName(Const/Formula 仍打印 cellText)。
>
> 本地合并补丁 build.20260916.3(澄清「左侧数据集显示两条」= 正常;坑位 ID 去重):
> - ★ 坑位 BUILD-35 —— 「左侧数据集出现两条」属正常,不是重复挂载:
> 左侧面板的数据源就是 GET /api/bs/edc/dataset/list?hasField=true&reportId=(report-designer-feature-map.md §六),
> 不是 listWithPageReport(那是"源级"记录,只决定源分组名)。实测跨 3 个应用 18/18 张报表都是两条:
> 主业务数据集(logic/form)+ 系统字段数据集 dataUpdateTime(type=systemField、0 个单元格引用)。
> 后者由平台建表时自动附带(build_from_spec 里根本没有它 ⇒ 不是 AI 加的、也与 heal/fix field-binding 无关),
> 用途是提供「数据更新时间」单元格类型,并属禁止删除豁免项。
> 判据:真重复挂载 = 两条记录 sourceId 相同;本场景两条 sourceId 不同 ⇒ 正常共存,勿动。
> - 坑位文件 ID 去重:发现 ENV-5 / PERF-7 各出现两次(历史编辑叠加),已分别改为 ENV-9 / PERF-8
> ——「渲染网格不含公式结果」这条现在是 PERF-8。pitfalls 共 87 条、ID 唯一。
>
> 本地合并补丁 build.20260916.2(批次回补 6 张表的字段回显;定位「重建保存恒定 500」;整表救援):
> - 批量回补:fix field-binding 支持只给 --app-id → 扫应用全量报表、逐张回补并汇总(取代外层 shell 循环);
> 新增 --limit N(只回补前 N 格,诊断用)。本次用它把昨天 dataset heal 抹掉的 6 张表 / 63 格 text 一次补齐。
> - ★ 坑位 BUILD-33(重建保存恒定 500 的真凶):cell transSelectOptions 若是空字符串或 {},
> saveReportDevInfo 会整表 500(与格内容无关:单格 / 零改动 / 极简骨架都 500);
> 值必须是非空数组或整个省略([] → 200、省略 → 200、""/{} → 500)。
> 已修 rebuild_from_fields.buildCellType:只在非空数组时写入。同函数对其它表 200、只对这一张 500 时查这条。
> - ★ 坑位 BUILD-32(血案,最重的一条):排查 500 时禁止在业务报表本体上做 save 实验 ——
> saveReportDevInfo 是整体替换,没有"试保存",200 即落库。本次因此在
> 建设工程合同资金总览 1315264774516604929 上把整表覆盖成 1 格。
> 规矩:保存类实验一律在一次性空壳表或 lifecycle copy 副本上做;实验前先 version save 或 dump 留底。
> - ★ 新命令 edc-report rebuild from-blueprint --blueprint <file> [--apply](坑位 BUILD-34):
> 服务端覆盖是破坏性的(getReportDevInfo 的 useTag 各值返回同一份数据,没有草稿副本;无版本则 rollbackReport 也无效),
> 救援只能靠蓝图重建:cells[](x/y/cellType/cellText,Field 另给 name/text)+ dataset(id/nameIndex/tableName)
> + styleSource(借同批同模板健康表的 style 与模板键)。默认 dry-run 逐格核对位置/父格链/fieldKey,
> --apply 后自动回读校验;收尾必跑 render values(数据取没取到)+ copy-health --probe(能不能复制)。
> ⚠ 该通道的已知损失:样式靠同批表推断、公式格数字格式需人工确认(如"收款比例"是否百分比)。
> - 「只对某一张表 500」的排查顺序(省时间):① --limit 1 缩到单格 —— 仍 500 就别再逐格二分,
> 转查 cellType 键值合法性(BUILD-33 的 transSelectOptions 就是这么找到的);② 用 fix field-binding --app-id 批量确认面。
>
> 本地合并补丁 build.20260916.1(★★★ 更正「换数据集是零损失」;修登录态静默失效):
> - 更正(重要):dataset replace / dataset heal 不是零损失。服务端重建数据集绑定时,
> 除了重映射 dataSetId/fieldKey,还会抹掉 Field 格的 text(字段显示名,设计器拿它当内容)与
> allNeedFields,并把 tableName 从 采招-招标项目(业务模块) 降级成裸雪花号 865889931247566866,
> Const 格的 openType 也被清空 —— 结果设计器里数据行的字段格全空白(用户报「字段值都不展示」),
> 而 verify contract / dump cells / verify_field_bind 全绿(这些键不在结构骨架里)。
> → 归因教训:换绑定的验证必须逐属性 diff,只比「格数/类型/公式/父格」会漏掉整类损伤(同 BUILD-27 的错法)。
> → 新命令:edc-report fix field-binding --report-id <id> --app-id <id> [--apply](默认 dry-run)。
> tableName 规范值推导(已由健康报表反证):${数据集原名}(${form→"表单" / logic→"业务模块"})。
> 走「构建态完整结构 + designKey:''」通道 → Formula 格存活(勿用 fix table-name,那条会丢公式格)。
> 预防:跑完 dataset replace / dataset heal 后必须补跑一次 fix field-binding 才算收工。
> - 渲染口径(更正):渲染网格不含公式结果 —— 把公式临时改成常量 999 再渲染,该格依然为空。
> 即渲染只能判断「字段取不取得到数」,不能判断公式算得对不对(别把「没算」误判成「算错」)。
> 取值用 edc-report render values <rid> [appId] [pageRowCount] [--max-cols=N]。
> ⚠ SpreadJS 的 sheets[x].data.dataTable 不是数组,是「行下标为键的对象」;按数组读会全空。
> - 登录态静默失效(已修):cookie 过期时 e10-login status 照样打印 ETEAMSID,只在末尾附
> 「⚠️ cookie 已过期,请重新登录」;旧 eb_auth 只抓 ETEAMSID → auth status/ensure 误报「已就绪」;
> 而 spec_lib.get/post 对 401 不抛错,调用方 (r.json&&r.json.data)||[] 把 401 吞成空列表 →
> 得出「应用 0 张报表 / 报表 0 个数据集」这种假业务结论(本会话实际踩到)。
> 现:eb_auth 识别过期警告直接判不可用;spec_lib 命中 401/token为空或失效 抛 EB_AUTH_EXPIRED。
> → 心法:任何「0 条 / 空列表」结论,先用一个已知非空的目标复核。
> - 日期字段取整:投标截止日期 这类字段 fieldType=datetime(日期时间带时分秒),
> H3-TODAY() 会得到形如 3.604 的小数。要整数天就用 ROUND 包一层
> (引擎函数表里数学类只有 SUM/AVERAGE/COUNT/MAX/MIN/ROUND/ABS,没有 INT/ROUNDDOWN):
> IFERROR(ROUND(H3-TODAY(),0),"");⚠ ROUND 可能比"整天数"进一位,严格截断需人工确认。
> - 坑位新增 BUILD-31 / PERF-8 / AUTH-8(当轮共 83 条;PERF-8 原误编为 PERF-7,已在 build.20260916.3 去重)。
>
> 本地合并补丁 build.20260915.17(★★ 批量运维三件套固化进 CLI:应用级扫描 / 修复 / 清理):
> - 动机:上一轮的「批量体检 / 批量修复 / 批量清理」是在外层用 shell 循环拼出来的
> (每张报表起一个进程):慢、易漏、每次都要重写一遍分页与去重逻辑。
> 现全部固化为 CLI 子命令 —— 同类批量动作第二次出现一律走命令,不再写循环或临时脚本。
> - 新增公共清单库 scripts/app_reports.js(listAppReports,分页去重),三条命令共用。
> ⚠ 口径:report/list?groupid=-1 只表示「不做分组过滤」,回包包含 groupId=0 的孤儿分组表
> —— 拿到的就是应用全量报表,不要再按分组逐个补拉。
> - 三条新命令:
> 1. edc-report copy-health --app-id <appId> [--probe] [--clean] [--json] [--quiet-ok]
> —— 应用级复制合规扫描:输出「可复制 / 不可复制」矩阵;BAD 行直接给出 dataset heal 修复命令。
> 默认只做数据集静态体检(快、无副作用);--probe 才逐张真打 copySheet(配合 --clean 不留副本)。
> exit 3 = 存在不可复制报表。
> 2. edc-report dataset heal --app-id <appId> [--purge] [--apply] [--json]
> —— 应用级批量修复:逐张体检,只对存在阻断项的报表落库,最后出汇总;取代外层 shell 循环。
> 3. edc-report cleanup shells --app-id <appId> --match "<子串>" | --regex "<正则>" [--empty-only] [--apply]
> —— 按名称模式清理诊断 / 对照临时表(这类表通常非空壳,旧 --prefix 那套只服务空壳)。
> ★ 该模式默认 dry-run,先打印命中清单,加 --apply 才落库;删除后自动回读校验无残留。
> - 实测(应用 1315029143993655301,故障注入 → 扫描 → 修复 → 复检 全闭环):
> · 注入 [LOGIC] 大写数据集后,copy-health --app-id 精准标出 1 张 BAD
> (同时报 DS_TYPE_UPPERCASE 与 DS_DUPLICATE);其余 6 张 OK。
> · dataset heal --app-id --purge --apply 只动这一张(换型 1/1、删脏 0/0、残留 LOGIC 0),
> 副本清理后复制恢复 200。
> · cleanup --match --apply 删除测试表后 residual: [](回读校验通过)。
> - 顺带修正:cli.js 帮助里 validate base 的 --group-id 0 旧口径 → -1(与 BUILD-27 一致)。
> - ⚠ 冷知识(本轮实测):replaceDataset 服务端会自行把 sourceType 归一化成小写;
> 能把 type 写成大写 LOGIC 的只有 addDataset 这一条链路。修复走 replace、故障注入走 add。
>
> 本地合并补丁 build.20260915.16(★★★ 复制 500 真因更正 = 数据集 type 大小写;新增 diagnose/heal/sync 固化命令):
> - 症状:报表「复制」→ body {"code":500,"msg":"报表内部错误"}(HTTP 状态码仍是 200,只看状态码会误判成功)。
> - 真因(受控实验,唯一变量):数据集 edc_dataset_dev.type 的大小写。
> · 设计器「+」/页面手动建表 → 小写 logic → 复制 200;
> · 脚本 addDataset 传 sourceType=LOGIC → 落库成 大写 LOGIC → 复制 500。
> 证据:同一应用 1315029143993655301 + 同一表单 sourceId=1315029981538508905 两张空壳,
> 唯一差别 --source-type logic vs --source-type LOGIC → 200 vs 500(可复现,见 memory/_repair_*)。
> - ★ 更正 build.20260915.15:上版把根因写成「挂了 type=LOGIC 数据集」,方向对(就是这一列)但判据错 ——
> 并非「LOGIC 类型不可复制」,而是大小写必须与设计器一致(小写 logic)。
> 证伪:带数据集+字段的手动建表复制正常(用户实测);小写 logic 数据集完全可复制。
> - 漏洞修复(根因层,源头堵死):spec_lib.resolveDataset() 默认 sourceType 由 'LOGIC' → 'logic'
> (并做 logic 大小写归一 + 落库回读告警 dataset_type_uppercase);card_ops.js(×2)、
> restore_report_from_version.js 同步改小写 → 之后 build spec / card build / dataset add
> 新建的数据集一律与设计器同形,新报表天然可复制。
> - 存量报表修复:edc-report dataset heal --report-id <rid> --app-id <appId> [--purge] [--apply]
> —— 走 replaceDataset 原地换型(同 sourceId + fieldMap 同名字段自映射),
> 实测服务端会把单元格 dataSetId / fieldKey 自动重映射到新数据集,格数 / 公式 / 布局零损失
> (案例:项目信息卡片 33 格、招标项目进度跟踪表 23 格含 IFERROR(H3-TODAY(),""),前后结构完全一致);
> --purge 顺带删除 sourceId 为空的脏数据集。
> - 诊断 / 复现 / 批量对齐:
> · edc-report copy-health --report-id <rid> --app-id <appId> [--clean] ← 阻断项清单 + 复制探针(exit 3=复制失败)
> · edc-report lifecycle copy --report-id <rid> --app-id <appId> [--clean] ← 原始口径复现(HTTP + body.code)
> · edc-report group sync --app-id <appId> [--report-id <rid>]... [--apply] ← 批量把 groupId 对齐到手动建表口径 -1
> · edc-report dataset add ...(sourceType 默认小写)· dataset list --raw(字段级差异排障)
> - ⚠ 勿把「跨应用」当脏数据:共享表应用里数据集 appId 指向表单归属应用属正常现象,
> 只有 sourceId 为空才是真脏(copy-health 对此只出 INFO)。
> - 坑位 BUILD-29(并更正 BUILD-27 / BUILD-28);文档 report-internal-error-guide.md ·
> add-dataset-registration-guide.md §二 · report-list-page-ops-guide.md §1.1.1。
>
> 本地合并补丁 build.20260915.15(复制 500 定位过程;结论已被 .16 更正,保留作过程记录):
> - 症状:报表卡片菜单点「复制」→ 500 报表内部错误(HTTP 状态码仍是 200,body {"code":500,"msg":"报表内部错误"})。
> - 当轮结论(已更正):报表挂了 type=LOGIC 数据集 → copySheet 重建数据集绑定时服务端内部报错。
> 干净证据:同一张空表 addDataset 前 copySheet 200、挂上后立刻 500。
> ★ 该实验的变量其实是「注册时传的 sourceType 大小写」(大写 LOGIC → 500),
> 而非「LOGIC 这一类数据源不可复制」,详见 .16。
> - 已逐条排除:groupId=0 孤儿分组(改成 -1 后复制仍 500)· AI/脚本建表 ·
> Field 格(把 8 格全改成 Const、数据集保留仍 500)· 字段数量 · 卡片式结构 · 接口入参变体。
> - 规避(仍有效):需要「同结构再来一张」时优先 build spec 重建,或先 dataset heal 把存量修好。
> - 坑位 BUILD-28;文档 report-internal-error-guide.md 速查表 · report-list-page-ops-guide.md §1.1.1。
>
> 本地合并补丁 build.20260915.14(★★★ 建表 groupId 必须 -1:0 是孤儿分组 → 菜单选不到):
> - 症状:「菜单选取」整个应用只列出手动建的报表,AI 建的全都看不到(列表页看着却一模一样)。
> - 根因:报表行的 groupId —— UI 建表写 -1(未分组默认桶),而脚本把 baseInfo 的 groupId 写死成 "0"。
> 自建分组 id 是服务端雪花号,不可能是 0 → group_id=0 = 孤儿分组:按「默认分组 / 未分组」枚举时看不到它。
> 实测对照(应用 1315029143993655301 建筑工程项目管理,group/list 为空):手动建(-1)能选到、AI 建(0)选不到。
> - 修复:① 建表默认值全部改为 -1(create_report.js / build_from_spec.js / card_ops.js /
> validate base DEFAULTS,可用 EB_EDC_NEW_REPORT_GROUP_ID 覆盖);② 所有保存路径不再写死,改为
> spec_lib.groupIdOfBaseInfo(bi)(原样回传 baseInfo.groupId);③ 新增 edc-report group audit 只读体检 +
> group set --group-id -1 定点迁移。
> - ★ 归因更正:本补丁上一版曾把「复制 500」也算在孤儿分组头上 —— 已证伪(见 build.20260915.15)。
> 分组只影响「列表 / 菜单能否枚举到」,不会导致复制失败。
> - 坑位 BUILD-27;文档 report-list-page-ops-guide.md §1.1.1 · report-internal-error-guide.md 速查表。
>
> 本地合并补丁 build.20260915.13(★★ 公式格父格规则统一为「归一到区域根格」):
> - 规则收敛:公式格父格 = 把被引用字段的父格 沿左父格链归一到区域根格 后的那一格;
> 只引用 1 个字段时也要归一。原实现里「引用字段父格全部相同 → 取该直接父格」的分支会让
> 同一区域的公式因「引用个数不同」得到不同建议(IFERROR(H3-TODAY(),"") 只引用 H3 →
> 被误判成应挂 G3;而 H3-I3 双引用 → 正确取根格 A3),自相矛盾,已修复。
> 案例:采购招标管理 1313903463073415168 J3,改公式后 check 从「1 处不一致」回到 0 处不一致(A3)。
> - scripts/formula_parent_ops.js:require.main 守卫 + 导出 analyze/parseRefs/chainOf/rootOf,可单测。
> - scripts/restore_cell.js:覆盖已存在的 Formula 格时不再误报「Formula 未 +1」(只有新增公式格才要求 +1;
> 覆盖时总数不变属正常,旧逻辑会 exit 2 假失败)。
> - 自测:[1] 单引用链→A3 [2] 引用根格→A3 [3] 双引用链→A3 [4] 错挂 D3→MISMATCH [5] 跨区域→告警 全通过。
>
> 本地合并补丁 build.20260915.12(★★ 「某格为什么没有公式」的诊断口径 + 固化命令):
> - 最常见真相不是「公式没识别」,而是那一整格在线上根本不存在 —— 历史重建落库被服务端丢掉
> (案例:采购招标管理 1313903463073415168「招标项目进度跟踪表」的 J3 剩余天数,
> 2026-09-14 那轮 fix 落库时字段数 23→22,J2 表头还在、J3 整格没了 → 设计器里看着就是「没公式」)。
> - 新增只读诊断命令 edc-report dump cells --report-id <id> --app-id <id> [--json]:
> 按 A1 记法原样打印全部网格(left / expandType / fieldKey / 公式文本 / 合并跨度 / sheetConfig),
> 并自动做 「表头有列、数据行缺格」缺口检测 —— 一处命中即刻定位丢格位置。
> - 补写走 restore cell(构建态:cell 级 formula 键 + designKey:'' 同通道;实测 916ms 落库,回读校验通过)。
> - 公式文本直接取现场公式,别猜(本次 J3 事故前的原文是 IFERROR(H3-TODAY(),""),
> 靠 memory/report_*_BEFORE_*.json 快照才拿到,重建式写入前务必先翻快照)。
>
> 本地合并补丁 build.20260915.11(★★ 只有 reportId 时反查应用 · 回收站还原 · 固化命令):
> - 场景:用户只甩一条 https://<host>/sp/edc/report/designer/<reportId>,问「这在哪个应用下 / 在回收站吗 / 帮还原」。
> 设计器 URL 不含 appId,而回收站接口按应用维度查 → 过去无命令可走,只能手写临时脚本。
> - 新增 edc-report locate --report-id <rid> [--app-id <id>|--app-name "名"] [--json]:
> ① GET /api/bs/edc/report/baseInfo?id=<rid>&useTag=backend(无需 appId 头)回出 data.appId = 正常态,直接收工;
> ② data 为空(响应恰好 54 字节)= 已删除信号 → 并发扫各应用回收站(先 --app-name 收窄);
> ③ 交叉校验 report/list 的 data.reportList[] 是否含该 id。exit 3 = 既非正常态也不在已扫应用回收站(可能已彻底删除 delete_type=3 或无权限)。
> - 新增 edc-report lifecycle recycle-list --app-id <id> / lifecycle recycle-restore --app-id <id> --report-id <rid> [--apply]
> (默认 dry-run;还原后自动三重校验:回收站已移除 + baseInfo 恢复 + live list 命中)。
> - ⚠️ 回收站记录的 deleteType 不可靠(实测回收站中的报表仍为 0)——禁止只看它下结论,必须靠「回收站列表命中 + live list 未命中」双判。
> - ⚠️ getReportDevInfo 用 urlencoded 提交会 415(只收 multipart);只想看一眼请走只读 verify contract。
> - pitfalls.js 新增 BUILD-25 / BUILD-26 / ENV-8(pitfalls 回收站 可检索)。
>
> 本地合并补丁 build.20260915.10(★★ 超链接占位父格链 · A0.11):
> - A0.11 占位坐标只能取「链接格自身 + 左/上父格链」;引用子格/旁系格属逆引用,
> 静默失效(不报错,占位符原样保留)——血案:A3 引 {M3} 而 M3 是 A3 的子格,穿透全坏。
> - 唯一验证方法:渲染后读 cellType.hreflink,看占位是否被替换为真实值;
> 只看 fields[].pcHreflink 或设计器发现不了。
> - 标准形态(参考 _ref_m4_full.json):链接格 M4 的左父格 = L4(承 id 格),再引 {L4}。
> - 修法:fix table-name --set-parent "x,y=承载id格x,承载id格y"(最小改动,禁止为顺向而重排列)。
> - ⚠️ $datasetId.fieldName$ 在本环境超链接中实测不生效(原样保留)。
> - pitfalls.js 新增 BUILD-21(pitfalls 超链接 / pitfalls 逆引用 可检索)。
>
> 本地合并补丁 build.20260915.9(★★ 设计器回显正确性 · A0.10):
> - A0.10 Field 格「格内文字」= 字段真名:设计器回显串 = {tableName}.{cell.text}({字段名}),
> 而 cell.text 被写成「标题映射名」→ 用户误判「字段未匹配到正确字段」。标题只写 Const 标题行。
> - tableName 单后缀:数据集 datasetName 已带 (业务模块) 时不得再拼(错值 (业务模块)(业务模块))。
> - 工具增强:fix table-name --prefer-canonical(以数据集规范名压过参照单元格错值;默认关闭)。
> - pitfalls.js 新增 BUILD-19 / BUILD-20(pitfalls 设计器 / pitfalls prefer-canonical 可检索)。
>
> 本地合并补丁 build.20260915.8(★★ 禁止整表重建 · A0.8 / 跨源先探 · A0.9):
> - A0.8 禁止「整表重建」:改已有报表只能是「定点补丁」——用脚本手写 schema 重新生成 workbook 落库
> 一律杜绝(与线上脱钩,必丢 tag/selections/colHeaderData/tag.formatterType,曾致公式格消失、格式被抹、居中)。
> 唯一入口 edc-report fix table-name|parent|echo|freeze|query-*(live 派生 + dry-run 自检 + 落库后回读差集);
> 通用 --set-json "x,y:key:JSON值" 可改任意单元键(含列宽 1,12:width:0、汇总、转换、格式)。
> - A0.9 跨源先探:跨源关联/新增跨数据集区域前,先读子表样本数据 + join suggest 定关联键,禁止靠「重建→渲染」试错;
> 查询条件一趟写完(item.json 一次带全 selectOptions/filterParam)。
> - 文档:targeted-fix-guide.md 新增 §〇 术语与硬禁止;policy scope/policy build 各加硬禁止行;
> pitfalls 新增 BUILD-17 / BUILD-18。
>
> 本地合并补丁 build.20260915.7(隐藏列真口径 = 列宽 0):
> - 隐藏列:平台口径是 列宽 = 0(columns[].size = 0,Excel 语义);
> columns[].visible=false 与 cell 顶层 colVisible=0 都会被服务端丢弃 / 归一化成 "1"
> → 留下一条 1px 宽的可见细条(血案:供应商绩效看板 M 列「数据ID」)。
> ⚠️ 与行相反:行的 rows[i]={size:0} 会被归一成 null(行要直接删)。
> - scripts/pitfalls.js 新增 BUILD-16(检索 edc-report pitfalls 隐藏列);
> references/cross-dataset-filter-guide.md §2.3.1 补隐藏列口径;scripts/safe_report_save.js 注释纠错。
>
> 本地合并补丁 build.20260915.6(跨数据集关联「整列为空」定论 + 子表汇总收敛):
> - 血案:供应商绩效看板报表(1315297716934582273,3 数据集)跨源列(评级/风险)整列为空,
> 所有 Equals/Contain、所有 left 指向都试过。两个真因:
> ① leftFilterField.currentField / subordination[1] 误用了本列展示字段(应 = 子表关联键字段);
> ② 关联键口径必须 主表.数据ID ↔ 子表 Ebuilder 关联字段(原始值是目标记录 id,非显示名),
> 且入口格左父格要指向承载该 id 的格(无则加隐藏列;隐藏 = 列宽 0,见 build.20260915.7)。
> - 子表 1:N 收敛成一行(看板必备):汇总格三件套 displayType:"Count" +
> expandType:"Vertical" + groupType:"none";服务端会把 displayType=Count 规范化回 Group,
> 配 NoExpand 会失效(只改 countType 完全无效)。汇总后 Select 值是裸 optionId,
> 需补 transType:"select" + transSelectOptions:[{selectValue:id,selectName:名称}]。
> - 文档:references/cross-dataset-filter-guide.md 新增 §2.3.1 / §2.3.2;
> pitfalls 新增 BUILD-12 / BUILD-13 / BUILD-14 / BUILD-15(检索 edc-report pitfalls 关联键 / 汇总)。
>
> 本地合并补丁 build.20260915.5(登录「自动分诊」:不再让人解释「为什么没免登 / 还要装什么」):
> - 用户诉求:装上 e10-login 后,访问环境即自动登录 —— 有授权自动免登;没授权只提示要 ETEAMSID,
> 而不是把「为什么没免登、要装什么」的解释成本丢给用户。
> - 新增 edc-report auth ensure [--host <url>] [--json](登录统一入口),四态自动分诊:
> ① CLI 缺失 → 自动装到托管隔离工作区(不污染全局),装不上才给一条安装命令;
> ② env(EB_ETEAMSID/EB_HOST)或本机 profile(~/.e10-cli/profiles/<profile>/auth)命中 → ready,免登;
> ③ 无授权 → 自动尝试一次浏览器授权登录(e10-login oidc,硬超时 EB_AUTH_LOGIN_TIMEOUT_MS 默认 25s;
> 失败写 30 分钟冷却,避免反复弹浏览器)→ 成功即写 profile → 永久免登;
> ④ 仍不行 → 只给「一条动作」(one_step)+ AskUserQuestion 字段,不再输出长篇原理。
> - 新增 edc-report auth save --host <url>:把 EB_ETEAMSID 从环境变量(非 argv,禁 --eteamsid 传参)
> 经 e10-login 的 finishLoginByEteamsId 校验后加密写入本机 profile → 一次写入、跨会话免登。
> (auth 文件为 AES-GCM + OS keychain 密钥,外部脚本拼不出来,必须复用 CLI 逻辑。)
> - 新增 edc-report auth status 跨平台实现:原 auth status 走 runShell('e10-login',['status']),
> Windows 下同样跑不了 .cmd(AUTH-5)→ 改为 auth_status.js 统一走 eb_auth.js。
> - 文案统一:eb_auth.js 的 LOGIN_HELP、auth_ready.js 的 askUser/how_to 改为
> 「一条动作 + 取值方法」口径;删掉「本工具不会自动登录」「禁止 oidc」等已过时表述。
> - pitfalls 新增 AUTH-7(登录体验:别把解释成本丢给用户);检索 edc-report pitfalls 免登。
>
> 本地合并补丁 build.20260915.4(登录链路修复:Windows 下 CLI 探测不到 · 免登为何失效):
> - 症状:edc-report auth ready 恒 reason: no_auth_file,cliVersion: unknown;用户反馈
> 「以前自动免登,现在每次要手动给 eteamsid」。
> - 根因一(代码 bug):eb_auth.js: fromCli() 与 auth_ready.js 都用
> spawnSync('e10-login', …, {shell:false}) —— Windows 的 CreateProcess 不做 PATHEXT/.cmd 解析,
> 对 .cmd 直接 ENOENT → CLI 永远探测不到(哪怕本机装了)。
> 已修:eb_auth.js 新增 resolveCli(),定位包内 bin/e10-login.js 用 process.execPath 直跑;
> auth_ready.js 复用同一函数。候选顺序:EB_E10_LOGIN → 托管隔离工作区
> ~/.workbuddy/binaries/node/workspace/node_modules → %APPDATA%/npm/node_modules。
> 输出新增 cliFound / cliPath,失败 JSON 也带 cli。
> - 根因二(环境认知):免登 = ~/.e10-cli/profiles/<profile>/auth 存在且未过期。
> 本机整个 .e10-cli 都不存在 → 从未持久化;且本环境 SSO 不发放授权码(oidc 会卡死,已禁兜底)
> → 自动登录无解,只能人工写一次。且「只在单条命令里 export」不跨会话 = 每会话重取的直接原因。
> - LOGIN_HELP 修正:删掉写死的旧机路径 D:/Workspace/WorkBuddy/.workbuddy … source ./e10env.sh
> (本机不存在,照做必失败),改为「跨会话免登必须落持久位置」+ npm i -g e10-login /
> e10-login set / profile use 指引 + 本机自查命令。
> - pitfalls 新增 AUTH-5(.cmd 不可 spawn)、AUTH-6(免登/持久化);检索 edc-report pitfalls 免登。
>
> 本地合并补丁 build.20260915.3(★★ 父格链最高优先规则:筛选只在入口格 · 后续同源字段以入口格为父格):
> - 规则来源:用户明确口径 —— 「父格筛选只配在该区域的『入口格』上;同区域后续字段只要是同一个数据集,一律以这个入口字段为父格」。
> - 修正旧表述:原 datasource-selection-and-parent-filter-guide.md §2.1 与 cross-dataset-filter-guide.md §2.1.1
> 写的是「同区域其余字段 → 父格用默认」,会让后续格脱离入口格的关联链 ——
> 实测报表 1315015331503456271:F3/G3 左父格误指异源 A3 → 跨源却无筛选 → 数据串源,
> 设计器表现正是用户问的「G3 为啥没有配置父格筛选」(改 leftY 0→4 指向入口格 E3 后正常)。
> 现改为:后续同源字段左父格必须显式指向「本区域入口格」坐标;subordination/leftFilterField 必须为空。
> - 优先级提升:列为父格配置第一优先规则 → SKILL 「会话硬规则 A0.7」 + 执行优先级表 0.2;
> datasource-selection-and-parent-filter-guide.md §2.0 铁律 / §2.1 规则 / §2.3 反例 / §2.6 自检判据(可机械执行);
> cross-dataset-filter-guide.md §2.1.1;pitfalls BUILD-11。
> - 边界(勿混):只适用多数据集报表的跨源区域;单数据集仍按 §〇 用默认 -1,-1。
> - 落库纪律:edc-report fix parent --report-id <id> --set-parent "x,y=leftX,leftY" --apply(可重复)
> → 必须回读校验 leftX/leftY 是否生效(服务端可能静默忽略;CLI 未生效即 exit 2)。坐标口径:x=行、y=列(均 0 基)。
>
> 本地合并补丁 build.20260915.2(左父格定点修正 + tag.formatterType 血案):
> - 新增 edc-report fix parent:--set-parent "x,y=leftX,leftY"(可重复)定点改左父格;
> --set-json "x,y:key:JSON"(可重复)定点改任意 cellType 键。复用 fix table-name 同一套
> 正规重建路径(含 tag / 保真自检 / 落库后回读校验),落库后逐项回读,未生效即 exit 2。
> 规则依据 references/datasource-selection-and-parent-filter-guide.md §2.1(强制)/ §2.3(反例)。
> - ★★ 血案:put() 硬编码 tag.formatterType: ['common'] → 静默抹掉单元格格式。
> 实测报表 1315015331503456271:落库后 B3「身份证」["text"]、G3「金额」["value"]
> 被清成 ["common"],服务端无报错,脚本保真自检也报「0 处差异」。
> - 关键认知:单元格格式存在 tag.formatterType,不是 cellType.formatterType;
> 后者设计器保存值恒为 []。
> - 已修:put() 改为 formatterType: (f.formatterType?.length ? f.formatterType : ['common'])。
> - 已补自检:保真自检新增 tag.formatterType 逐格比对 —— CMP_KEYS 里没有这个键,原属盲区。
> - ★ 教训:CMP_KEYS 是白名单,不在表里的键会被重建静默改写而自检仍报绿。
> 动生产报表前,务必另做一次全键独立比对(本次正是靠它才抓到 formatterType 被改坏)。
>
> 本地合并补丁 build.20260915.1(http:// 部署不可读 · 协议感知):
> - 症状:对 http://<服务器IP>:20600 这类纯 http 部署,doctor env 报
> 连通性探针失败:EPROTO ... ssl3_get_record:wrong version number;getReportDevInfo 直接抛错 → 读不到数据。
> - 根因:请求层写死 require('https') + https.request,服务器讲明文 HTTP,Node 按 TLS 握手 →
> OpenSSL 读到非 TLS 字节流即报错。不是凭证问题,也不是网络不通。
> - 已修:safe_report_save.js: request() 改为协议感知
> (const isHttps = new URL(url).protocol !== 'http:';port: u.port || (isHttps ? 443 : 80))。
> - ⚠️ 未修:其余 scripts 仍多为写死 https —— 接 http 部署时需逐个改。检索:edc-report pitfalls EPROTO(ENV-5)。
>
> 本地合并补丁 build.20260914.6(手写 workbook 漏 tag → 显示位置被改成居中):
> - 根因:用手写临时脚本构造 workbook 落库时漏了 tag → 服务端 tag=null 静默回落
> → showLocation 从 1(居左) 变成 2(居中),用户感受「报表显示不对」。
> 而 fields[]/verify bind/listWithPageReport 全部正常 → 极难排查。
> - 铁律:禁止用手写临时 workbook 落库生产报表。手写产物易漏 tag/selections/colHeaderData
> 等非必填键,服务端静默回落默认值(不报错、无 diff)。必须走 CLI 正规构建路径。
> - 新增 --show-location 1|2:显式修正显示位置(居左=1 / 居中=2)。
> - dry-run 打印 显示位置 showLocation: live=X → 重建=Y (居左/居中);落库后回读校验并计入失败判定。
> - 详见 layout-showlocation-freeze-guide.md §1.5。
>
> 本地合并补丁 build.20260914.5(回落路径漏后缀 · 第二起丢格血案):
> - tableName 回落路径必须自行补形态后缀。原实现回落到 row.originalName(裸显示名,如 采招-招标项目)
> 就直接采用 → 与「参照单元格路径」「人工手绑」的 xxx(业务模块) 不一致 → 设计器仍不回显数据集名。
> 新增 withFormSuffix(name, row):LOGIC + ebuilder/form-service → 显示名(业务模块);FORM → (表单);其它无后缀;
> 已带后缀的不重复拼接。验证:报表 1313903463073415168 修复后 10/10 Field = 采招-招标项目(业务模块)。
> - ⚠️ 第二起 Formula 格丢失案(报表 1313903463073415168):fix table-name --apply 修好 tableName 的同时,
> C 列第 9 行的 Formula 格(J3,IFERROR(H3-TODAY(),""))被服务端丢弃(fields 23→22)。
> 已穷举 6 种组合全部失败(服务端一律 code 200 但不落库):
> ① --restore-from 补回;② leftX/leftY 改为紧邻 (2,8);③ left=2,8 + expandType=Vertical;
> ④ left=2,0 + expandType=Vertical;⑤ 原样提交(仅补 tableName);⑥ 从 pre-loss workbook 全量重放。
> → 定论:Formula 格无法经 saveReportDevInfo 写入。 重建式写入会必然丢 Formula 格。
> 处置:优先让用户在设计器手工补该格;或走 CDP 抓真实 workbook(§四点五)。不要反复盲试。
> - 执行纪律(血案后):对含 Formula 格的报表,fix table-name --apply 前必须先向用户说明
> 「本操作会丢失含公式格」,取得明确授权;或改用只读 verify contract 出报告后由用户手工修。
>
> 本地合并补丁 build.20260914.4(重建 workbook 三个致命坑 · 血案后加固):
> - typeName 必须与 cellType 一一对应:Field→FieldCellType / Formula→FormulaCellType / Const→ConstCellType。
> 原实现把 Formula 写成 ConstCellType → 服务端保存时静默丢格。
> - rows[] 必须覆盖真实工作表行数:rowsLen = max(maxX+1, sheetProps.rowCount, 200)。
> 原实现只按 live fields[] 的 maxX → rows.length 偏小 → 越界格被服务端丢弃。
> - 同一报表可挂多个数据集:tableName 改为按每格自身 dataSetId 分别解析(assemble(f, tnMap))。
> - apply 前硬门禁:格数相等 / typeName 匹配 / rows 覆盖 / tnMap 全覆盖 / 保存后 diff 仅限白名单键。
> 详见 dataset-table-field-contract-guide.md §五 硬门禁。
> - ⚠️ 已知未解(2026-09-14 已定论):Formula 格即使上述全对,重建保存后仍会被服务端丢弃
> (报表 1313903497535856640 与 1313903463073415168 两起血案,共穷举 6 种组合全败)。
> 重建式写入必然丢 Formula 格 → 含公式格的报表改用 CDP 抓真实 workbook(§四点五)或让用户手工补。
>
> 本地合并补丁 build.20260914.3(回显双向修复):
> - fix table-name 升级为「tableName + fieldKey 双向修复」:assemble() 新增 fieldKey 合规校验与重建
> (契约 fieldKey = dataSetId.nameIndex.name 三段一致)。缺段绝不臆造(宁留空不拼假值)。
> dry-run 分别打印「tableName 缺失 n/N」「fieldKey 不合规 n/N」及将修复的单元格坐标;
> 落库后快照 diff 期望仅 tableName/fieldKey/(新增) 三类键变化。
> - 症状→字段归属:①「数据集名称不回显」= tableName;②「字段回显应为『数据集名.字段名』」= fieldKey。
> 详见 dataset-table-field-contract-guide.md §五。
>
> 本地合并补丁 build.20260914.2(安全审计 HIGH 项优化):
> - 登录政策改为「引导用户本机自助登录」:首选本机 e10-login login / 升级后 oidc,或本机 export EB_ETEAMSID=... EB_HOST=...;禁止引导用户把凭证明文粘贴进对话,禁止把凭证作为命令行参数(--eteamsid 会进 shell 历史与 ps 进程列表);见 login-and-render-policy.md、api-auth-headers.md。
> - CLI 新增凭证参数守卫:edc-report/cli.js 的 assertNoCredentialArgs() 在 runNodeScript/runPython 统一拦截 --eteamsid / --eb-eteamsid / --cookie / --token / --secret / --password / --api-key 等(含 --key=value 与连字符/下划线变体),命中即报错并提示改用环境变量。
> - 清理硬编码凭证示例:auth_ready.js / eb_auth.js / weaver_auth_headers.py 中的真实格式示例值统一替换为占位符。
> - 全树 spawnSync 显式 shell:false(9 处)+ 安全注释,消除 shell 注入面。
>
> 本地合并补丁 build.20260914.1(2026-09-14 会话问题优化整合):
> - fieldToCell() 透传 conditionRule:修复「条件格式写了不生效 / 保存后原有规则被清空」——重建 workbook(sheets 为空时必走)原本会丢 conditionRule;见 condition-format-guide.md §六 硬坑一。
> - verify query --fix 升级为 4 件:新增 ④ 补 filterParam(保留已有对象、只填缺失键);并把 Q8 的 filterParam 缺失/required/showType 缺失由「纯 WARN」改为同时置 fixNeeded(否则只剩 WARN 时 --fix 会跳过)。
> - 新增单点修改纪律:仅改用户点名项,禁止顺手改其他内容;生产报表改动前先抓全量快照 diff 校验;见 user-request-scope-only-guide.md + condition-format-guide.md §六 硬坑二。
>
> ⚠️ CLI 入口提示:真正入口是 edc-report/edc-report.js(内部 require('./cli') 后调 run())。cli.js 只是逻辑模块、没有 require.main 引导,直接 node cli.js 会静默 exit 0(无任何输出)——冒烟测试/调用务必用 edc-report.js。
>
> 官方 1.7.0 能力:verify query 查询条件合规体检(Q1~Q10 判据总表,--fix 幂等修)+ doctor 守卫在位扫描;+ 查询条件「默认值三件套」必须写(漏写会让设计器弹窗默认值区炸开、后续整段行被顶出可视区;新增 fix query-default 补齐;query save 自动补缺失项);+ 禁止静默注入查询条件样式字段 advancedSearchType(新增 fix query-style:清「展开条件」回「默认样式」+ 删误插入重复行);+ 冻结单元格 API 落库 fix freeze(cell.tag.fixed 是唯一持久化标记);+ 关联浏览富对象改走 GET browserBeanSearch(旧 POST getBrowserBean 恒 500);+ 重复坐标拦截;+ 空白行/表头合并指南;+ tableName 免浏览器重建;+ doctor 环境体检 / pitfalls 踩坑知识库 / verify limit 规模预检 / render 渲染守卫
搭表完成即停(最高强制)
> 模式 A:saveReportDevInfo 成功并已输出设计器地址后 → 本轮任务结束。
> 禁止继续:verify / 预聚合 / HTML / 需求分析 / 配置检测 / 再 save / 通读知识库 / 扫描无关文件。
> 细则:references/report-delivery-contract.md · CLI:edc-report policy build
冷启动(最高 · 防 502)
> 开场只读:references/cold-start-scan-guide.md(一页)+ 本任务点名的 ≤2 篇 guide。
> 禁止通读本 SKILL.md 全文、全部 references/、report-knowledge-base.md、weaver-e10-login、.workbuddy/。
> 本包不含 e10-cli* — 禁止查找。CLI:edc-report policy build · auth ensure
| 允许(默认) | 禁止(开场) |
|---|---|
edc-report auth ensure(一次即结论;0 文件扫描)/ auth ready |
Glob/Grep 全 skill、扫 profile、通读登录 SKILL |
edc-report … 命令 |
查找 e10-cli* / actionflows |
点名单篇 *-guide.md |
knowledge-base / help / 分析模板 / data-model / config-schema / ai-workflow / datasource-metadata 全文 |
概述
e-Builder 报表模块能力包:依赖 weaver-e10-login(登录)与 edc-report(搭表执行 CLI)。
判断走本 Skill 索引 → 细则走 references/ → 执行优先 edc-report。
| 能力 | 说明 |
|---|---|
| 搭表落库 | 定源 → CLI/脚本落库 → 交设计器地址即停 |
| 点名改表 | 仅改用户点名项(user-request-scope-only-guide.md) |
| 需求分析 | 仅用户要分析/指导时;纯搭表跳过 |
| 已移除 | 测试用例、原型图 — 勿再查找 |
触发条件(摘要)
用户提到:生成/创建/搭建报表、报表设计、edc-report、父格、查询条件、数据集、设计器、权限、快照、定点修改、高级搜索、超链接/事件、模版创建/快速构建、分组/移动/转移等。
登录相关:只跑 auth ensure(自动就绪 CLI + 自动免登,一次即结论),勿为登录扫仓库。
执行优先级(强制)
| 优先级 | 入口 |
|---|---|
| 0 | edc-report build spec --spec <spec.json> — 声明式一次落库(模式 A 首选,见 spec-build-schema.md) |
| 0.1 | 禁止整表重建(A0.8 · 强制):改已有报表只允许「定点补丁」;唯一落库入口 edc-report fix table-name\|parent\|echo\|freeze\|query-*(从 live fields 派生 + dry-run 自检 + 落库后回读差集) |
| 0.2 | 多数据集父格链(A0.7 · 强制):父格筛选只配入口格;同区域后续同源字段左父格显式指向入口格 |
| 0.3 | 回显正确性(A0.10 · 强制):Field 格 text = 字段真名(标题只进 Const 标题行);tableName 单后缀 |
| 0.4 | 超链接占位父格链(A0.11 · 强制):占位坐标只能取自身 + 左/上父格链;逆引用静默失效;必须渲染读 cellType.hreflink 验证占位已替换 |
| 0.5 | edc-report policy scope — 仅改用户点名项 |
| 1 | edc-report auth ensure(自动分诊:缺 CLI 自动装 / 无授权只给「一条动作」) |
| 2 | edc-report …(find / query / verify / create / dataset / …) |
| 3 | 裸 node/python scripts/(仅 CLI 不可用时,且须连接复用) |
映射表:references/edc-report-cli.md。
会话硬规则(摘要 · 细则在 guide)
A0 仅改用户点名项
必加载(改已有表时):user-request-scope-only-guide.md。
未点名:不删、不重建、不顺手改。破坏性命令须用户明确要求。
A0.1 / A0.2 性能与扫描
必加载:cold-start-scan-guide.md(开场)。展开:session-build-performance-guide.md(进行中按需)。
找表已给 app → 只 find form / getList/{appId};禁首轮 /ds/group;禁 SpreadJS 黑盒;交链即停。
A0.3 会话终止 = 未完成 → 强制回滚空壳
会话中止 / 取消 / 超时 / 未交设计器链接 → 视为任务失败。
禁止故意建 __build_* / __probe_*;正常路径用目标名一次落库(build spec)。
仅 save 失败留下的未完成表须 deleteReport;历史残留壳用户点名再 cleanup shells。
正式交付成功的报表保留。细则:failure-cleanup-and-nonblocking-guide.md §〇。
A0.4 字段找不到 → 让用户指定字段(规则 A · 强制 · 2026-09-11)
列字段只允许精确匹配(fieldMap/fieldName → 列标题 === fieldsKey.text → field === text/name)。
禁止模糊自动绑定;禁止回退系统字段(name/id/create_time…)。无命中 → build spec exit 4 且不建表,
把 candidates/allFields 原样交用户挑字段;列文案与字段真实显示名不一致 → 需用户确认(--field-confirmed)。
事故:列「姓名」被静默绑到系统字段 name(真实显示名「标题」)。细则:field-bind-exact-match-guide.md §2.0。
A0.4b 表没指明 / 非精确匹配 → 让用户指定表(规则 B · 强制 · 2026-09-11)
需求未指出具体表,或应用内检索非完全精准匹配(模糊包含 / 多条近名 / 未命中)→ build spec / find form exit 3 且不建表,
输出 candidates[] 请用户自行选取指定。禁止按关键词猜表、用「最像的」顶上、先建壳试探。
确认后 --form-confirmed --source-id <id> 复跑。
统一入口:edc-report policy ask(两条规则一页);spec-build-schema.md §1.1/§2.1。
A0.5 同源只挂一次 + 回显名带后缀(强制 · 2026-09-11)
本报表已有同 sourceId 数据集 → 复用,禁止二次 addDataset(否则左侧出现重复数据源分组)。
sourceName 强制 {表单名}(业务模块)(共享表 (表单));裸表单名会使面板缺后缀。
体检:edc-report verify bind <reportId> [appId]。细则:add-dataset-registration-guide.md §2.1。
A0.6 字段接口返回格式(以 fieldsAndOriginFields 为准 · 2026-09-11)
fieldsBySource → data 是裸数组、dataSetId="0";fieldsAndOriginFields → data 是 {fieldsKey, originFieldsKey}、dataSetId 为真实值;
两者字段元素同形(30/30 仅 dataSetId 不同)。统一用 spec_lib.normalizeFieldList(res,{datasetId}) 取「字段元素数组」,务必注入本报表真实 datasetId。
细则:dataset-table-field-contract-guide.md §三点五。
A0.7 连续同源区域父格链(★★ 最高优先 · 强制 · 2026-09-15)
> 铁律:父格筛选只配在该区域的「入口格」上;同区域后续字段只要是同一个数据集,一律以这个入口字段为父格。
多数据集报表里,连续同源区域(同行连续列 / 同列连续行、字段全部同源):
| 角色 | 左父格 leftX/leftY |
subordination / leftFilterField |
|---|---|---|
| 入口格(区域第一个字段) | 指向异源关联键格 | 必须配(全区域唯一一格) |
| 后续格(同区域其余同源字段) | 显式指向「本区域入口格」坐标 | 必须为空 |
禁止:① 后续字段指异源格 —— 跨源却无筛选 → 数据串源;② 同区域重复配筛选;③ 多数据集报表里靠 -1,-1「默认」蒙扩展链。
边界:单数据集报表的 Field 左父格仍用默认 -1,-1(§〇),勿套用本节。
落库:edc-report fix parent --report-id <id> --set-parent "x,y=leftX,leftY" --apply(可重复)→ 必须回读校验是否生效(服务端可能静默忽略)。
公式格父格(用户规则 · 2026-09-15):公式单元格的父格,通常与它引用的、参与计算的字段父格统一为同一格。
判定:被引用字段父格相同 → 取该父格;被引用字段父格成链(同区域)→ 取区域根格(该行/区域最前面的那一格)。
父格不一致 → 行/列扩展时公式格与数据错位、整块不随行复制。
自查 / 对齐:edc-report formula parent check --report-id <id> --app-id <id>(只读,exit 3=不一致)·formula parent align [--apply]。
细则:formula-cell-api-guide.md 规则 7 / §七;pitfalls LAYOUT-5。
细则:datasource-selection-and-parent-filter-guide.md §2.0 铁律 / §2.1 规则 / §2.3 反例 / §2.6 自检判据;pitfalls BUILD-11。
A0.8 禁止「整表重建」(★★ 最高优先 · 强制 · 2026-09-15)
> 铁律:改「已有报表」只允许「定点补丁」;任何形式的「整表重建」一律杜绝。
平台事实:saveReportDevInfo 是整份 workbook 覆盖式替换,且 getReportDevInfo.workbookProps.sheets 恒为 {}
(服务端不回传 dataTable)。所以传输层必然整份回传,但语义层只能是定点 patch。
| 概念 | 判定 |
|---|---|
❌ 整表重建 —— 用脚本手写的 schema / 列定义 / 单元格构造重新生成 workbook 落库(自写 dash.js / create_xxx.js 式) |
禁止:与线上状态脱钩,必丢 tag/selections/colHeaderData/rowHeaderData/tag.formatterType/派生键(曾致公式格消失、文本·数值格式被抹、显示位置被改成居中) |
✅ 定点补丁 —— getReportDevInfo.fields 原样派生 workbook → 只改点名键 → dry-run 自检 → --apply → 回读逐格差集 |
唯一允许 |
✅ 声明式搭建 —— build spec(仅全新报表,无线上原状需保护) |
允许,但不适用于改已有报表 |
唯一落库入口(全部默认 dry-run,dry-run 显示「保真自检 不一致键 0 处」才可 --apply):
edc-report fix table-name --report-id <id> [--set-json "x,y:key:JSON值"]... [--set-parent "x,y=lx,ly"]... [--apply]
edc-report fix dataset-name --report-id <id> --app-id <appId> [--apply] # 数据集名剥类型后缀(BUILD-36)
edc-report fix echo | freeze | query-style | query-default
edc-report query save --item <item.json>
通用 --set-json "x,y:key:JSON值" 覆盖任意单元键:藏列 1,12:width:0、汇总 2,8:displayType:Count、
转换 2,10:transType:select、格式 2,1:formatterType:["text"]。
禁止:为改一格而重跑整表 spec / 重新定义全部单元格 / 从参考报表整表 clone;未跑 dry-run 直接落库;只回读目标格而不做逐格 Object.keys 差集。
细则:targeted-fix-guide.md §〇(术语与硬禁止);pitfalls BUILD-17。
A0.9 跨源关联 / 查询条件 的「先探后写」(强制 · 2026-09-15)
> 血案:供应商绩效看板 1315297716934582273 的跨源列排查,因没有先读子表样本数据,
> 靠「重建 → 渲染」试错跑了 ~9 轮(每轮整表重建),是本会话最大的时间浪费。
- 跨源关联前必先「探」:① 读子表样本数据确认关联键字段与取值口径(详见
cross-dataset-filter-guide.md§2.3.1);
② 用edc-report join suggest --app-id <appId> --parent-source <主表> --child-source <子表>取建议关联键。
禁止在未读样本的情况下靠反复落库+渲染猜关联方式。 - 查询条件一趟写完:
query save --item <item.json>的 item 一次带全selectOptions/filterParam(日期快捷项)
—— 详见query-condition-api-guide.md§3.2。禁止「先加条件 → 再跑一趟补选项」的两趟写法。
A0.10 Field 格「格内文字」= 字段真名 · tableName 单后缀(★★ 强制 · 2026-09-15)
> 血案:供应商绩效看板 1315297716934582273 建完后用户在设计器里发现 I3/J3/L3 三格
> 「回显的不是绑定字段名」,误以为字段没匹配上。
设计器 Field 格的回显串 = {tableName}.{格内文字(单元格 text)}({字段名}) —— 「格内文字」直接取单元格 text,
设计器不会自动改用字段显示名。所以 text 写错,整串回显就是错的。
| 键 | 硬规则 |
|---|---|
cell.text(Field 格) |
必须 = 该字段自身的显示名(取 fieldsBySource 的 f.text)。自定义中文标题只写在 Const 标题行,禁止写进 Field 格 |
cell.tableName |
单后缀:显示名 + (业务模块) / 显示名 + (表单);数据集 datasetName 已带后缀时不得再拼(重复 = xxx(业务模块)(业务模块)) |
- 历史实现里这条只是 WARN(
build_from_spec.js的「显示名一致性门禁」,exitCode 2)——升级为强制:
自检逐格比对textvsfieldKey末段对应字段的text,不一致即中招。 - 已建错的修法(定点补丁,勿重建):
fix table-name --set-json "x,y:text:\"字段真名\"" --apply。 tableName错值修复:fix table-name --prefer-canonical(以数据集规范名为准,压过参照单元格错值;默认关闭)。
权威对照 =verify contract的dataset = …行。⚠️ 规范名可能只回传裸 id(如1313903497535856640),此时回落参照值。- 细则:
pitfalls BUILD-19 / BUILD-20;targeted-fix-guide.md。
A0.11 超链接占位坐标必须落在「自身 + 父格链」内(★★ 强制 · 2026-09-15)
> 血案:供应商绩效看板 1315297716934582273 的 A3(供应商名称)写 pcHreflink="…/{M3}",
> 而 M3 是 A3 的子格(M3 的左父格 = A3)。配置层回读一切正常、设计器也看不出问题,
> 穿透实际是坏的 —— 渲染后 cellType.hreflink 里 {M3} 原样保留、从未被替换。
规则:链接占位({坐标} / 参数名=坐标)的取值范围 = 链接格自身 + 其左父格/上父格链(父格的父格也支持)。
引用子格 / 旁系格属逆引用,静默失效(不报错、不提示,占位符原样输出)。
| 做法 | 判定 |
|---|---|
| ❌ 链接格引用子格/旁系格 | 逆引用 → 静默失效(占位符原样保留) |
| ✅ 让「承载目标值的格」成为链接格的父格(左父格即可) | 合法 → 运行时替换为真实值 |
⚠️ $datasetId.fieldName$(同行数据集字段) |
本环境超链接中实测不生效(原样保留),勿用 |
标准形态(参考实现 _ref_m4_full.json):链接格 M4(Const「点击查看」)的 leftX/leftY = L4,
再写 pcHreflink="…/{L4}" —— 引的正是自己的左父格。
→ 想「点击某字段列穿透」,就让承载 id 的隐藏列位于该字段列左侧(左侧列才能做它的左父格)。
唯一验证方法(必做):渲染后读单元格 cellType.hreflink(BlankHerflinkCellType),
检查占位符是否已替换为真实值。只看配置层 fields[].pcHreflink 或设计器无法发现问题。
# 渲染验证(参考脚本 render_dump.js / linkprobe.js:取 view/analysis → getJsonStr 解 gzip → 读 cell.style.cellType.hreflink)
# 期望:/sp/ebdfpage/card/0/<sourceId>/<真实记录id> 而非 …/{M3}
修法(最小改动,禁止重排列):fix table-name --set-parent "x,y=承载id格x,承载id格y" ——
让承载 id 的格成为链接格的父格,引用随即合法化。不要为了「顺向」而重排列(违反 A0.8 + 用户「不顺手改其他」偏好);
如确需重排须先向用户说明「结构性变更,定点补丁无法表达」。
- 细则:
pitfalls BUILD-21;event-hreflink-guide.md§三.0;report-self-check-guide.md§2.2。
其它硬规则 → 点名再读(禁止开场全载)
| 主题 | Guide | CLI |
|---|---|---|
| 禁止整表重建(★ A0.8) | targeted-fix-guide.md §〇 |
fix table-name --set-json "x,y:key:JSON值" [--apply] |
| 回显正确性(★ A0.10) | pitfalls BUILD-19/20 |
fix table-name --set-json "x,y:text:\"字段真名\"" / --prefer-canonical |
| 超链接占位父格链(★ A0.11) | event-hreflink-guide.md §三.0 |
渲染读 cellType.hreflink 验占位是否被替换;fix table-name --set-parent "x,y=idX,idY" |
| 跨源关联先探(★ A0.9) | cross-dataset-filter-guide.md §2.3.1 |
join suggest + 读子表样本 |
| 定点修改 | targeted-fix-guide.md |
verify self(可裁剪) |
| 查询条件 | query-condition-api-guide.md |
query save\|type\|list\|check-echo\|fix-echo;条件级 queryType 写JSON 数组短码字符串 '["advance"]'(高级搜索)/ '["filter"]'(筛选)—— 长码 "advancedSearch" 会导致设计器回显不勾选;updateQueryType 会把入参批量覆盖所有条件(传长码=一键摧毁回显,真正根因);保存后必须 check-echo 自检,异常用 fix-echo 修复;人员/部门 browserType 富对象只能从 GET /api/bcw/relevanceBrowser/browserBeanSearch?searchValue=<中文名> 取(旧 POST getBrowserBean 恒 500),CLI 已自动升级简写;🔴 禁止静默注入 advancedSearchType(写 'openCondition' = 把报表页查询区从「默认样式」改成「展开条件」,属擅改展示样式;入参没有该键就不写)→ 存量清理 fix query-style;🔴 默认值三件套 defaultValueType/defaultValue/type1/type2 必写(漏写→弹窗「默认值类型」=请选择、默认值区把所有控件变体炸开、日期类型/显示位置/条件必填被顶出可视区)→ fix query-default --set ... 补齐(query save 已自动补缺失项);⚠️ saveQueryItem 必须带 id 才是原地更新,不带 id 会新增出重复条件;★ 改完条件一律跑 verify query <rid> <appId> 自检(Q1~Q10);query type 不传 --show-where = 未点名 → 只读回读(updateQueryType 是批量覆盖接口) |
| 事件超链接 | event-hreflink-guide.md |
validate href [--fix];ASCII ?;参数名=坐标 |
| 父格链(★ A0.7) / 少挂源 | datasource-selection-and-parent-filter-guide.md(§2.0/§2.1/§2.6)+ cross-dataset-filter-guide.md(§2.1.1) |
fix parent / join suggest |
| 找表 | app-datasource-lookup-guide.md(§2.0 未给表名须确认) |
find form / --list / --suggest |
| API 建表 | api-report-build-fast-path.md |
create … |
| 声明式一次建表(首选) | spec-build-schema.md |
build spec --spec |
| 交付 | report-delivery-contract.md |
policy build |
| 失败删壳 / 清临时表 | failure-cleanup-and-nonblocking-guide.md |
cleanup shells(--report-id / --prefix / --match "<子串>" 默认 dry-run,--apply 才删) |
| 报表不可复制(复制 500) | report-internal-error-guide.md · report-list-page-ops-guide.md §1.1.1 · 坑位 BUILD-29 |
copy-health --report-id <rid> --app-id <appId> [--clean](诊断)· copy-health --app-id <appId> [--probe](应用级扫描)· dataset heal --report-id\|--app-id ... [--purge] --apply(修复) |
| 报表菜单选不到(分组) | report-list-page-ops-guide.md · 坑位 BUILD-27 |
group audit --app-id <appId> · group set … --group-id -1 --apply · group sync --app-id <appId> --apply(批量对齐) |
| 字段值不展示 / 字段格空白 | dataset-sidebar-ops-guide.md §4.x · 坑位 BUILD-31 |
fix field-binding --report-id <rid> --app-id <appId> [--apply](单张)· fix field-binding --app-id <appId> [--apply](应用级批量,取代 shell 循环)· --limit N 诊断用(还原 text 字段名 + tableName 回显名;换过数据集后必跑) |
| 设计区被覆盖 / 整表只剩几格(无版本可退) | 坑位 BUILD-32 / BUILD-34 | rebuild from-blueprint --blueprint <file> [--apply](蓝图重建;dry-run 先逐格核对)→ 收尾 render values + copy-health --probe |
| 重建保存恒定 500(单格/零改动都 500) | 坑位 BUILD-33 | 查 cell transSelectOptions 空串 / {}(必须非空数组或省略)→ 已由 rebuild_from_fields 归一;先 --limit 1 排除格内容因素 |
| 登录态静默失效(全接口 401 被当空数据) | 坑位 AUTH-8 | auth ensure(识别 cookie 过期)· e10-login oidc --base-url "<平台地址>" 续期 |
| 看渲染网格取值(公式格恒空) | 坑位 PERF-8 | render values <rid> [appId] [pageRowCount] [--max-cols=N](只判字段取数,不判公式) |
左侧数据集显示两条(主数据集 + dataUpdateTime) |
坑位 BUILD-35 | 正常,≠ 重复挂载(判据:两条 sourceId 是否相同);dataUpdateTime 禁止删除 → dataset list --report-id <rid> --app-id <appId> |
左侧数据集名「双后缀」采购入库(业务模块)(业务模块) |
坑位 BUILD-36 · add-dataset-registration-guide.md §一/§二 |
先看:dataset list --report-id <rid> --app-id <appId> 会预演左侧面板显示名并把脏名标 ⚠;后修:fix dataset-name --report-id <rid> --app-id <appId> [--apply](单张)· fix dataset-name --app-id <appId> [--apply](应用级批量)。★ 记忆点:数据集 name 裸名 / 单元格 tableName 带后缀,后缀由平台按添加类型自动拼 |
数据集 name 是裸雪花号(面板显示 8658…(业务模块)) |
坑位 BUILD-37 · dataset-sidebar-ops-guide.md §5.3 |
先看:dataset list 标 ⚠ 并给出「应为 XX」;后修:fix dataset-name --report-id <rid> --app-id <appId> [--apply](按 originalName 还原可读裸名)。⛔ 不阻断「复制」,copy-health 查不出 |
纯 http 部署报 EPROTO / wrong version number,或命令莫名报缺 host |
坑位 ENV-9 / ENV-10 | 均已收口:请求层全量协议感知(公共模块 scripts/req_mod.js,26 脚本 + 核心层)、host 自动兜底解析(不再需要 export EB_HOST)。自检 edc-report doctor scripts(env9 项须为 ✅)· edc-report doctor env(连通性探针会标明 http/https) |
| 技能对外分发前(版式 / 脱敏 / 卫生 / frontmatter 必备字段) | 坑位 DELIVERY-2 / DELIVERY-3 / DELIVERY-4 | ① frontmatter version 只写三段纯数字(禁 +build.xxx 后缀,本地补丁号只进正文);② frontmatter 必须有 description_zh(中文短标签)+ description_en —— 缺了平台直接报「解析失败:缺少 Skill 中文描述(description_zh)」;③ 例子里的真实地址换占位符(<服务器IP> / <自建站点域名> / <平台地址> / <官方SaaS域名>);④ 登录地址绝不写死域名(含 e10-login profile use <profile-name>);⑤ 排障产物禁止落技能目录、真实 tenantKey/用户ID 换占位符。自检:edc-report doctor pack(desc / semver / ip / addr / 卫生 六项须 ✅;其中 addr = 白名单式域名扫描,不假设前缀或 TLD)+ 兜底 grep(带协议 host https?://[a-z0-9][a-z0-9.-]+ / 公网 IPv4 / ^version:.*+ / ^description_zh:),除占位符与公开站点白名单外须 0 命中 |
| 局部保存 | safe-partial-save-guide.md |
safe_report_save |
| 设计区空白 | designer-blank-prevention-guide.md |
validate blank |
| 网格线 | report-designer-feature-map.md §4.6.1;report-internal-error-guide.md |
validate gridline |
| 条件格式 | condition-format-guide.md |
validate condition |
| 转换面板 | cell-transform-guide.md |
validate transform |
| 单元格格式 | cell-config-echo-guide.md |
validate format |
| 默认表头样式 | default-header-style-guide.md(未指定/无效果图才套用) |
validate style;build spec 自动分层 |
| 字段绑定 | field-bind-exact-match-guide.md + add-dataset-registration-guide.md |
verify bind |
| 表名/字段映射契约 | dataset-table-field-contract-guide.md(tableName + 三件套,禁删;§四点六 = 免浏览器反向重建修 tableName) |
build spec 自动门禁;verify contract / fix table-name |
| 冻结单元格 / 锁定列 | layout-showlocation-freeze-guide.md §2.6 API 落库(cell.tag.fixed 是唯一持久化标记;锚点格不必是空格;禁写 cellText=fixed-cell-tag;fixed 必须唯一;重复坐标拦截) |
fix freeze --report-id <id> --anchor <x,y> [--apply] |
| 环境体检(工具链坑) | 凭证/host+port/变量名/CLI 版本/连通性;require 解析、漏 port、python 依赖、语法;shim 悬空、入口行尾;三树一致性;渲染守卫 | doctor [env\|scripts\|install\|trees\|perf\|all] [--fix] [--json] |
| 踩坑知识库(动手前先扫) | 历次会话全部坑:症状/根因/对策/对应 CLI,按领域检索 | pitfalls [关键词] [--area auth\|perf\|env\|build\|layout\|field\|query\|excel] [--json] |
| 渲染守卫 / 规模预检 | 默认拒绝全量渲染(必然 555 且白烧 15~80s);pageRowCount 封顶 574;超限倍数可算 | render;verify limit <reportId> |
| 公式 | formula-cell-api-guide.md(规则 7 / §七:公式格父格须与「引用参与计算字段」父格统一为同一格;成链时取区域根格) |
formula parent check(只读 exit 3)·formula parent align [--apply] ·fix parent --set-parent "x,y=L,T" |
| 数据集筛选 | dataset-query-filter-guide.md |
— |
| 跨源筛选 | cross-dataset-filter-guide.md |
py left-filter |
| 新建三模式 | report-create-modes-guide.md |
create models\|blank\|template\|quick |
| 列表页 | report-list-page-ops-guide.md |
group / lifecycle / app search |
| 数据集侧栏 | dataset-sidebar-ops-guide.md |
dataset … |
| 权限/生命周期 | report-permission-guide.md / report-lifecycle-api-guide.md |
scope / lifecycle |
| 快照/版本 | designer-blank-prevention-guide.md 等 |
snapshot / version |
| 基础默认值 | report-baseinfo-defaults-guide.md |
validate base / verify meta |
| 居左/冻结 | layout-showlocation-freeze-guide.md |
fix freeze(冻结);居左 = showLocation:1 |
| 空白行 / 表头合并(Excel 对齐) | hidden-row-and-header-merge-guide.md |
verify all + 渲染回读(size:0 不隐藏行;合并只认 spans) |
| 落库自检 | report-self-check-guide.md |
verify all(默认不在交链后跑) |
| 鉴权头 | api-auth-headers.md |
— |
| 雪花 ID | snowflake-id-precision-guide.md |
— |
| 预聚合 | preaggregate-auto-disable-guide.md |
preagg check\|disable(表含不支持字段 → 必关 dataPreAgg);只读旧版 py preagg |
| 补写/恢复单格(含丢失的 Formula 格) | condition-format-guide.md §六 · cli help |
restore cell --cell-file <单格定义.json>(构建态 cell.formula + designKey:'' 才存活;默认 dry-run) |
| 交叉表直构(月份横向扩展 × 多指标 + 合并表头 + 合计行左父格) | crosstab-raw-workbook-guide.md |
重建统一走 scripts/rebuild_from_fields.js;禁会话内临时手写重建脚本(PERF-6/BUILD-23) |
| 卡片式报表(每条记录一张卡片 · 纵向堆叠同页不分页) | card-report-guide.md |
card example / card build --spec card.json [--apply](通用化,字段精确预检 exit 4) |
| 跨应用找表(表不在指定应用里) | app-datasource-lookup-guide.md §2.0 |
find form-global --name "表名" --app-name "应用名"(限次精确探测;无应用线索 → exit 5) |
| 卡片「复制」报 500 报表内部错误 | report-internal-error-guide.md 速查表 · report-list-page-ops-guide.md §1.1.1 |
dataset list --report-id <rid> --app-id <appId>(只读,含 type=LOGIC → exit 3,即「不可复制」)→ 改用 build spec 重建 |
| 菜单选取列不出报表(怀疑分组) | report-list-page-ops-guide.md §1.1.1 |
group audit --app-id <appId>(只读,exit 3 = 有孤儿分组)· group set --app-id <appId> --report-id <rid> --group-id -1 [--apply] |
| 只有 reportId → 反查应用 / 回收站还原 | report-lifecycle-api-guide.md §三 |
locate --report-id <rid> [--app-name "名"](设计器 URL 无 appId)· lifecycle recycle-list --app-id <id> · lifecycle recycle-restore --app-id <id> --report-id <rid> [--apply] |
| 「某格没有公式」诊断(先看是否整格丢失) | pitfalls FIELD-7 |
dump cells --report-id <id> --app-id <id>(只读:网格原样打印 + 「表头有列、数据行缺格」缺口检测)→ 补写 restore cell |
| 交付纪律 | report-delivery-contract.md |
纯文本设计器地址;交链即停(禁渲染验证/对照报表,提示 pitfalls --area delivery) |
| 日期区间 catalog | date-range-query-reference.md |
— |
查询摘要:multipart 单对象;条件级 queryType='["advance"]'(数组短码)+ save 后 updateQueryType(同数组形态)+ check-echo 自检;下拉 selectKey=ID / selectValue=名称。
事件摘要:?zclb=D7&zcmc=E7&cx={searchfield001};禁全角 ?;Field 写 pcHreflink。
父格摘要:新建单数据集左父格默认 -1,-1;超链接 Const 须指向数据格;(0,0)≠默认。多数据集连续同源区(★ A0.7):父格筛选只配入口格;区域内后续同源字段左父格显式指向入口格、筛选留空。
工作流(极简)
模式 A · 从零搭表
首选(默认):edc-report build spec --spec <spec.json> —— 建表前字段预检(未命中 exit 4 不建表)→ 目标名一次创建 → 本表取字段(复校)→ 落库(禁空壳/rename)。模糊表名须确认(exit 3)。
结构见 spec-build-schema.md(样式/货币/条件格式/公式/标题冻结/fieldMap 全部声明式描述)。实测 1.0s 落库 / 2.8s 含 CLI 启动。
备用(手工分步):
edc-report auth ready- 读
api-report-build-fast-path.md+app-datasource-lookup-guide.md find form→ 建壳/落库(CLI 或脚本 +safe_report_save)- 输出设计器 URL → 停(
report-delivery-contract.md)
定点改
auth ready+user-request-scope-only-guide.md+targeted-fix-guide.md- 只加载与改点相关的 一篇 guide
- 只改点名项 → 交链或说明结果 → 停
仅需求分析
- 加载
report-requirement-analysis-template.md(勿默认搭表) - HTML 仅用户点名时再
build_guide_html.py
禁止冷启动加载(保留文件 · 勿开场读)
| 文件 | 何时才读 |
|---|---|
report-knowledge-base.md |
引擎冲突 / 源码对齐 |
report-building-guide.md |
界面操作指导 |
report-help-guide.md |
帮助问答 |
report-requirement-analysis-template.md |
用户要分析 |
report-data-model.md / report-config-schema.md / ai-report-workflow.md |
整包配置生成 |
datasource-metadata-guide.md |
DataSetAction 全手册 |
build_guide_html.py |
用户要 HTML |
weaver-e10-login |
auth 失败需说明时 |
assets/template-*.json |
布局脚手架(≠ 模版创建) |
源码核对路径(按需):D:\edc_source\weaver-common-edc — 非开场扫描目标。
包结构
| 路径 | 用途 |
|---|---|
references/ |
专题指南;点名加载 |
scripts/ |
可执行源;CLI 开发态直用 |
edc-report/ |
npm CLI(扁平结构:edc-report.js + cli.js);脚本直接解析同仓 scripts/ |
weaver-e10-login |
外部依赖:登录 Skill;运行时用全局 e10-login CLI |
assets/ |
示例 JSON;非默认扫描 |
安装 CLI:cd edc-report && npm i -g .(或 npm link;需先安装 weaver-e10-login 提供的 e10-login CLI,建议固定版本并校验来源)。
综合注意事项(索引)
- 登录:Cookie.raw + eteamsid + User-Agent 三项;见
api-auth-headers.md - CLI 优先:有命令禁止再堆
.workbuddydebug - 网格线:workbook 禁
gridline键;pagegridLine=0;validate gridline - 交链即停:见 delivery-contract;全局修复 ≤1 轮
- 禁止空壳:先字段预检再落库;禁
__build_*/跨表复用 datasetId;模糊表须确认;同源只挂一次;仅失败删未完成表
5.1 字段精确绑定:无精确命中 → exit 4 让用户指定字段(禁模糊兜底/系统字段回退) - 同款样式:只验一次再复用(
cell-config-echo-guide.md) - 完整 CLI 映射与检查清单:
edc-report-cli.md
细则与历史硬规则全文已下沉到各 references/*-guide.md;本 SKILL 为入口索引,禁止当作百科通读。