Repository navigation
reports 能力不在 requires 里,但 10 个报表已声明、且 crm_case 的 allowExport 授权正是靠「报表面」立论 —— 工单导出可能根本没有落地面 #798
Description
Activity
- addedbugSomething isn't workingSomething isn't workingpm:queueReady for the PM dispatch loopReady for the PM dispatch looppm:dispatchedDispatched to a dev agent by /pm-dispatchDispatched to a dev agent by /pm-dispatchand removedpm:queueReady for the PM dispatch loopReady for the PM dispatch loop
on Aug 5, 2026 🔒 认领(修复线 PM 派单,测量先行):session_01VHrPAGEgFDoHjphqYG4BMa,分支
claude/issue-798-reports-capability,worktreehotcrm-issue-798。裁定(两分支预授权,以实测定):第一步照单里写的证法实测——起真实 dev server,确认 10 个已声明报表在运行时是否真的工作(渲染/取数)。(a) 若不工作:把
reports加进requires即 restore-invariant(声明了 10 个报表就该能跑),补验证 + changeset,PRFixes #798;顺带确认 crm_case 的 allowExport 立论(报表导出)是否随之成立。(b) 若工作正常(能力经其它途径自动拉起):不改 config,把实测结论回填本单(说明 requires 清单为何不需列它),转为修正 issue 前提或文档/立论侧的最小修——按实测写明。文件面:objectstack.config.ts(仅分支 a)、相关验证、changeset;⛔ 不升级@objectstack/*、不动 releases/、不动 profiles 授权值。dev server 用后精确 PID 拆除(防 #777 的假绿陷阱)。
Generated by Claude Code
实测结论:走分支 (b),本单前提不成立
按单里写明的证法起了真实 dev server(
objectstack dev -p 4798,17.0.0-rc.2,origin/main@ a98ddaa),两条推断都被证伪。PR #816 因此不动objectstack.config.ts,只修被写错的立论文本,并补上真正缺的那格守卫。基线(
reports不在requires)启动横幅:
Plugins: 38 loaded,列表里没有ReportsServicePlugin—— 这一半单子说对了。但接下来的推论不成立:探针 结果 GET /api/v1/meta/report200,10 项,与 src/reports/声明的十个逐一对上Console apps/app.objectstack.hotcrm/report/sla_performance渲染,表格 5 行 / 20 单元格 同上 cases_by_status_priority渲染,22 行 / 87 单元格,6 个 Recharts 图元(柱图画出来了) 同上 pipeline_coverage_by_quarter渲染,4 行 / 28 单元格 报表页的取数请求 POST /api/v1/analytics/dataset/query→ 200,有行;全程零次/api/v1/reports调用GET /api/v1/reports(及/:id、/:id/run、/:id/schedules)501 NOT_IMPLEMENTED10 个报表不是没人消费的死元数据。 它们由 metadata API 供给、Console 的
report/:reportName路由渲染、analytics(平台常开)取数;其中三个还由src/apps/crm.app.ts:153-155的导航直接挂出。关键的认知偏差在这里:
reports能力根本不是report元数据的消费者。ReportService只读sys_saved_report行(object_name+query_json,即对象 ObjectQL 查询),和 dataset 驱动的ReportInput是两套不相干的东西。反向验证(把
reports加进requires,重新 build + 重启)先定方向再跑:预期 10 个报表毫无变化。实测正是如此:
Plugins: 39 loaded,ReportsServicePlugin出现;新增sys_saved_report、sys_report_schedule两个对象(88 → 90),外加一个 60 秒轮询的调度器。- 三个报表页的表格行数/单元格数/Recharts 图元数量与基线逐格相同,网络请求依旧零次
/api/v1/reports。GET /api/v1/meta/report仍是同样的 10 项。 GET /api/v1/reports501 → 200{"data":[]}(空的保存报表列表)。POST /api/v1/reports/sla_performance/run→ 404REPORT_NOT_FOUND—— 已声明报表的名字在这个服务里根本不可寻址。
所以加它不是 restore-invariant,而是新开一套本应用零内容、零 UI、零业务拉力的能力面(保存的报表 + 邮件定时摘要)。按「核心能力优先、能力扩张要有真实业务拉力」的取舍,没有加。
crm_case的allowExport落在哪(第二条推断)批量导出在本应用只有一条服务端路由:
GET /api/v1/data/:object/export(csv/xlsx/json),由enforceExportPermission→security.canExport把守(@objectstack/rest)。实测:- 持有授权的主体 → 200,返回真实 CSV(28 列表头 + 数据行)。
- 无
allowExport的普通用户 → 403EXPORT_NOT_PERMITTED。 - 加不加
reports能力,这两个结果都不变。
而列表视图的
exportOptions不是第二扇门:Console 的 Export 按钮(data-adapterchunk)拼的就是/api/v1/data/{object}/export?...这同一条路由;exportOptions只声明按钮提供哪些格式。所以crm_case的授权是活的、被真正执行的,只是没有列表按钮,得走数据 API。反过来,
ReportService.assertExportAllowed在本部署里根本没加载(501);即便加载了也够不着 —— 三个报表页实测都没有任何导出/下载入口(exportButtons: [],加载能力前后一致),而ReportService又只认sys_saved_report。因此改了什么
src/profiles/index.ts里那句「Both bulk-egress doors ask before they read: the list views' built-inexportOptions, andReportService.assertExportAllowed」两处都错:exportOptions不是门,assertExportAllowed在这个应用里不是任何一条授权的落点。本单正是照这句话读出来的,所以 PR #816 把它、test/authorization-coverage.test.ts的同源 rationale、以及content/docs/guides/import-and-export.mdx(含 zh-Hans / zh-Hant,原文告诉管理员工单的导出授权是空的)一起更正。授权值一个没动。守卫「自洽但空转」的判断对了一半:它读的确实是元数据之间的一致性——但它要守的东西本来就不依赖那个能力。真正缺的那格是可达性:对象自己的
enable会不会把导出路由关掉(apiEnabled: false或受限apiMethods→ 405,security.canExport根本轮不到)。PR 补了这条守卫,用平台自己的resolveEffectiveApiMethods/isApiOperationAllowed判定(受限 CRUD 白名单其实会展开出export,手写读apiMethods会冤枉四个对象)。把crm_case的enable.apiEnabled临时改false验证方向:1 failed | 29 passed,只有新守卫转红,原三条导出守卫读不到enable保持绿。留给维护者的一条观察(PR 未动)
crm_case在exportSurfaces里只由「报表 leg」承载,而报表页实测没有下载入口。授权是活的(走数据 API),但哪天删掉 case 报表,守卫会反过来要求把这条有用的授权一起删掉。注释里已写明;更稳妥是给 Cases 列表视图补一个exportOptions,但那要动视图元数据,不在本单文件面内。另:#797 那段文档确实需要同步,已在 PR #816 内一并更正 —— 但方向不是「报表导出不落地」,而是「那条授权走的是数据 API,报表在这里根本不是导出面」。
Generated by Claude Code
✅ 验收通过 — PR #816 已转 ready 并挂 auto-merge(CI 9/9 绿)。premise_still_valid: false 的范本处置:测量先行推翻两条推断后,不修想象中的缺口,改修写错的话本身。
判定要点:
- 测量证据是决定性的:10 个报表活着(meta 全返、Console 渲染、analytics 取数 200);
reports能力与report元数据是两套不相干系统(前者只消费sys_saved_report)。反向验证(把 reports 加进 requires)后三个报表页逐格数字不变、零次/api/v1/reports调用——「加了它不会恢复任何已声明的东西,只会多一套空能力面」这一句是拒改 config 的充分依据。 - allowExport 半同样被证实为活的:唯一导出路由由
canExport把守、curl 可达;exportOptions只是按钮格式声明。三处错误立论(权威注释 / coverage rationale / import-and-export 文档三语——含 docs(guides): import-and-export 按实测改写——导入向导在列表视图而非 Setup → Data,Salesforce 迁移标注未落地 (#763) #797 刚按错误前提写入的那句)一并更正。 - 真正缺的那格守卫补上了:allowExport 对象必须在
enable保留 export API,否则 405 在授权检查之前——反向验证只红新守卫、旧三条保持绿,分工正确。 - 授权值零改动、config 零改动、平台版本零改动。
两个后续处置:(1) Cases 列表视图补
exportOptions(让活授权有 UI 入口、解除守卫对 report-leg 的单点依赖)→ PM 已立 #817 入队;(2)objectstack dev静默复用旧 artifact 的启动期陷阱 → 与上游在案的 objectstack#5148(watcher 变体)同族,已按去重纪律在该单追评补充证据,不开新单。#798 随本 PR 关闭。
Generated by Claude Code
- 测量证据是决定性的:10 个报表活着(meta 全返、Console 渲染、analytics 取数 200);
- addedpriority:p2Medium: important, M3Medium: important, M3and removed
on Sep 9, 2026
发现于 #763 / PR #797(改写
guides/import-and-export.mdx的导出段时,逐条核对「谁能导出什么」)。不在该 PR 的文件面内(涉及src/**与objectstack.config.ts),单独记录。实测
objectstack.config.ts声明:平台常开的那批(
PLATFORM_ALWAYS_ON_CAPABILITIES,来自@objectstack/spec)是:两者的并集里没有
reports。reports在 CLI 的CAPABILITY_PROVIDERS里是一个正常的能力项(@objectstack/plugin-reports→ReportsServicePlugin),也没有任何自动拉起:serve.ts里唯一提到'reports'的地方是NEEDS_JOB_OR_QUEUE = ['email', 'approvals', 'reports', 'auth'],那只在requires已经包含reports时才为它补queue/job。与此同时:
src/reports/下有 5 个文件、pnpm validate报 10 Reports,全部作为元数据编译进产物;src/reports/case.report.ts的三个报表都建在case_metrics数据集上;src/profiles/index.ts里allowExport那条规则明写:并由
test/authorization-coverage.test.ts钉住。crm_case拿到allowExport的五个角色(sales_rep / sales_manager / service_agent / marketing_user 之外的 system_admin,以及前述四者中的四个)里,没有一个能通过列表视图导出工单:声明exportOptions: ['csv', 'xlsx']的只有account/contact/lead/opportunity四个视图。也就是说crm_case那条授权的落地面只能是报表导出(ReportService.assertExportAllowed)。问题
如果
reports能力在默认启动下不加载,那么:crm_case的allowExport授权指向一个不存在的面 —— 而authorization-coverage守卫认为它是有面的,因为守卫读的是「有没有一个报表建在该对象的数据集上」,而不是「报表服务在不在」。守卫因此在这一格上是自洽但空转的:它验证的是元数据之间的一致性,不是能力是否被启用。
pnpm validate的 "Checking capability providers (#3366)" 也没有对此报警(本次 validate 全绿)。待确认
没有实测跑过启动来确认报表面到底在不在(本单来自静态核对,且发起单的文件面只允许动
content/)。所以第一步应当是:起一个objectstack start,看/_console/下报表是否可达、GET报表导出是否 200。三种可能:reports加进requires,要么承认工单导出不落地并调整crm_case的授权与守卫规则;ui层或 analytics 侧带起来)→ 那么src/profiles/index.ts的规则注释应当写清楚这条依赖,否则下一个读的人会得出和我一样的结论;关联
exportOptions;那条授权面向报表导出,而reports不在本应用的requires,所以默认启动下四个列表视图与数据 API 就是你拥有的导出面。」如果本单结论推翻这一点,那段文档需要同步。