背景
有 5 个模板因为 framework#1867(hook 沙箱在嵌套跨对象写入时崩溃 memory access out of bounds)把 header/rollup 字段降级成了「手工维护的存储字段」。各 CHARTER / hooks 里都明确记着这个原因,例如 packages/compliance/src/hooks/index.ts:
compliance_control.last_status / last_assessed_at are STORED fields … a nested engine write from a hook is unsupported in the standalone runtime (the QuickJS hook sandbox crashes on nested writes; ctx.services.data is undefined) …
受影响字段:
| 模板 |
字段 |
现状 |
expense |
expense_report.total_amount |
存储字段,client/seed 维护 |
procurement |
procurement_order.received_amount |
存储字段,client/seed 维护 |
project |
pm_project.progress_percent / actual_cost |
存储字段 |
content |
content_publication.total_* |
flow/存储,非 live |
compliance |
compliance_control.last_status / last_assessed_at |
存储字段 |
平台侧状态(framework)
底层限制已在 framework 侧解决:
- 崩溃已修复:沙箱早已改为 deferred-promise + pump 的宿主调用模型,嵌套写入不再二次展开 asyncify 栈 → 不再崩溃。
- framework#3232(评审中)补齐了回归覆盖 + 修掉了钩子超时被硬钳到 250ms 的
declared ≠ enforced 缺口(现可按 spec 声明更大的 timeoutMs,给较深的 rollup 链留预算)。
- 已用 真实 SQLite(better-sqlite3,落盘) 验证:
expense_line 的 afterInsert/afterUpdate 钩子嵌套写 expense_report.total_amount,insert/update 都正确回填、无崩溃(见 framework#3232 的 nested-write-real-sqlite.integration.test.ts)。
建议的转换路径(待 framework 修复发布 + 本仓 bump @objectstack 后再做)
按聚合类型选对工具:
- 可声明的求和/计数/极值 rollup(且需 delete 安全)→ 用原生
summary 字段,而不是钩子。引擎在删除时会自己捕获子记录 FK 的 pre-image 并重算父级;afterDelete 钩子只拿到 { id, options }、拿不到被删行的 FK,天然不适合做删除回滚。适用:expense_report.total_amount、procurement_order.received_amount、content_publication.total_*。
- 条件式 / 非聚合的跨对象写入 → 用嵌套写钩子(现已安全)。适用:
compliance_control.last_status/last_assessed_at(取最近一次 assessment 的结果)、pm_project 的派生进度。较深的链在钩子 body 里声明更大的 timeoutMs(≤30_000ms)。
验收(遵循本仓 AGENTS.md「build 通过 ≠ 能跑」)
每个模板转换后必须 boot all env 实测:Server is ready + seeded on empty DB + 0 条 ERROR,并确认子记录变更后父级字段实时更新。同时更新对应 CHARTER 的「Rollup vs. lifecycle hooks」段落与 AGENTS.md 的 Runtime gotchas(移除已过时的 #1867 崩溃说明)。
门槛
依赖 framework#1867 的修复正式发布,且本仓把 @objectstack/* 从 ^11.2.0 bump 到含修复的版本。在此之前保持现状(现状对所 pin 的已发布运行时是正确的)——不要提前转换,否则会 advertise 一个已发布运行时尚未提供的能力。
关联:framework#1867、framework#3232、framework#3233。
背景
有 5 个模板因为 framework#1867(hook 沙箱在嵌套跨对象写入时崩溃
memory access out of bounds)把 header/rollup 字段降级成了「手工维护的存储字段」。各 CHARTER / hooks 里都明确记着这个原因,例如packages/compliance/src/hooks/index.ts:受影响字段:
expenseexpense_report.total_amountprocurementprocurement_order.received_amountprojectpm_project.progress_percent/actual_costcontentcontent_publication.total_*compliancecompliance_control.last_status/last_assessed_at平台侧状态(framework)
底层限制已在 framework 侧解决:
declared ≠ enforced缺口(现可按 spec 声明更大的timeoutMs,给较深的 rollup 链留预算)。expense_line的afterInsert/afterUpdate钩子嵌套写expense_report.total_amount,insert/update 都正确回填、无崩溃(见 framework#3232 的nested-write-real-sqlite.integration.test.ts)。建议的转换路径(待 framework 修复发布 + 本仓 bump
@objectstack后再做)按聚合类型选对工具:
summary字段,而不是钩子。引擎在删除时会自己捕获子记录 FK 的 pre-image 并重算父级;afterDelete钩子只拿到{ id, options }、拿不到被删行的 FK,天然不适合做删除回滚。适用:expense_report.total_amount、procurement_order.received_amount、content_publication.total_*。compliance_control.last_status/last_assessed_at(取最近一次 assessment 的结果)、pm_project的派生进度。较深的链在钩子 body 里声明更大的timeoutMs(≤30_000ms)。验收(遵循本仓 AGENTS.md「build 通过 ≠ 能跑」)
每个模板转换后必须 boot
allenv 实测:Server is ready+seeded on empty DB+ 0 条ERROR,并确认子记录变更后父级字段实时更新。同时更新对应 CHARTER 的「Rollup vs. lifecycle hooks」段落与AGENTS.md的 Runtime gotchas(移除已过时的 #1867 崩溃说明)。门槛
依赖 framework#1867 的修复正式发布,且本仓把
@objectstack/*从^11.2.0bump 到含修复的版本。在此之前保持现状(现状对所 pin 的已发布运行时是正确的)——不要提前转换,否则会 advertise 一个已发布运行时尚未提供的能力。关联:framework#1867、framework#3232、framework#3233。