Skip to content

objectstack dev watcher rebuilds dist/objectstack.json but the running server keeps serving the metadata it booted with #5148

Closed

Description

@yinlianghui

Summary

objectstack dev announces 👁 watching objectstack.config.ts, src for changes and does rebuild dist/objectstack.json when a source file changes. The running server does not pick the rebuild up. It keeps executing and serving the metadata it booted with, indefinitely, for both hook bodies and view metadata. Only a process restart applies a change.

There is no error, no warning, and no visible difference — the watcher's rebuild looks like a successful hot reload.

Why this is worse than "hot reload is not implemented"

The artifact on disk and the behaviour of the server disagree, silently. That makes every edit/verify loop in dev mode capable of producing a false result in either direction, and the failure is indistinguishable from the change itself being wrong:

  • Fix something, save, re-test → still broken → conclude "my fix does not work" and go rewrite correct code.
  • Break something, save, re-test → still fine → conclude "this code is not load-bearing" and delete it.

We hit the second one directly. A/B-ing two variants of one hook against a dev server without restarting compares one build against itself, and both variants "passed". The wrong conclusion was already written into a source comment before it was caught.

Reproduction (measured 2026-08-04, @objectstack/runtime / @objectstack/cli 17.0.0-rc.1, Node v22.22.2, driver-sql/sqlite)

App: an object crm_customer_rating with an afterInsert/afterUpdate/afterDelete hook that clears three columns on a parent record when the last approved child is deleted.

A. Hook bodies do not reload

  1. rm -rf .objectstack/data dist && pnpm dev (objectstack dev -p 4001 --compile). Wait for ready.

  2. Confirm the behaviour: create a child, approve it, DELETE /api/v1/data/crm_customer_rating/<id> → parent's three columns clear. ✅

  3. Without stopping the server, edit the hook body so the delete path is the first thing that returns:

    const isDelete = event === 'afterDelete';
    if (isDelete) return;              // <- disable the whole delete path
  4. Wait for the watcher. Confirm the rebuild really landed on disk:

    $ python3 -c "import json;print('if (isDelete) return;' in json.dumps(json.load(open('dist/objectstack.json'))))"
    True
  5. Repeat step 2 against the still-running server.

    Expected: the parent keeps its values (the delete path is disabled).
    Actual: the parent's columns clear — i.e. the old hook body ran.

  6. Restart the process (no other change). Repeat step 2 → the parent keeps its values. The new body is live only now.

B. View metadata does not reload either

  1. With the server running, edit an object's form view in src/views/*.view.ts: rename a section label and remove a field from it.
  2. Wait for the watcher; confirm dist/objectstack.json contains the new label.
  3. GET /api/v1/meta/views?object=<object> → the old config.sections comes back (old label, field still present), and the console's create dialog renders the old layout.
  4. Restart → the endpoint and the dialog both show the new layout.

Impact

  • Any agent or developer iterating in pnpm dev can measure the wrong thing without any signal that they did.
  • It interacts badly with the fact that dist/ is gitignored and objectstack dev only recompiles when dist/objectstack.json is absent: git pull does not touch dist/, clearing the database does not touch dist/, and now neither does saving a file while the server runs. The downstream symptom is always the same — "the code is new, the running app is old".

What would fix it, in rough order of preference

  1. Reload the artifact after a watcher rebuild — re-register hook bodies and view/object metadata into the live server.
  2. If a live swap is not safe for all metadata kinds, restart the child process automatically after a successful rebuild (nodemon-style), which is what the watcher already implies.
  3. At minimum, stop implying otherwise: log something like rebuilt dist/objectstack.json — restart required for changes to take effect on every rebuild, and say so in the watching … banner. A truthful message is far better than a silent stale server.

Happy to test a fix against this app; the repro above is scripted on our side.

Activity

  1. yinlianghui commented on Aug 4, 2026

    @yinlianghui
    ContributorAuthor

    Downstream reference: this was found while fixing yinlianghui/hotcrm-heimao#68, and the workaround (restart the server for every metadata A/B) is written into the code comment on the hook in yinlianghui/hotcrm-heimao#73.


    Generated by Claude Code

  2. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    Contributor

    分诊暂缓(修复指令):正文已被 GitHub sanitizer 截断 —— B 节止于 GET /api/v1/meta/views?object=,其后内容被裸尖括号占位符(如 <object>)吞掉。按规程截断正文不可分诊派发。

    请原作者用反引号包裹尖括号占位符(写成 `<object>`)重贴完整正文;重贴后下一轮分诊自动拾取。缺陷本体(dev watcher 重建 dist/objectstack.json 但运行中 server 不接收)已足够清楚,但 B 节步骤与结尾章节缺失,照规程先补全再打标签。

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  3. yinlianghui commented on Aug 5, 2026

    @yinlianghui
    ContributorAuthor

    补充一个启动期变体的实测证据(来自 objectstack-ai/hotcrm#798 / PR objectstack-ai/hotcrm#816 的验证过程,17.0.0-rc.2):

    本单记录的是 watcher 重建后运行中 server 不换元数据;同族的另一半是——配置已改、artifact 未重建时,objectstack dev 会静默复用旧 artifact 启动,无任何「源配置比 dist 新」的提示。实测:objectstack.config.ts 已加入 reports 能力后直接起 dev,启动日志仍是 Plugins: 38 loaded(旧 artifact);先 pnpm build 再起才是 39。调查者第一轮实验因此差点得出错误结论。

    hotcrm 侧本会话已两次在 dev-server 新旧状态上踩到假绿类陷阱(hotcrm PR #777 的旧 server 占端口、本例的旧 artifact 静默复用),规避手段都只能靠流程纪律(先 build、核对 dist 的 requires、精确 PID 拆除)。若修本单,建议把「启动时比对源配置与 artifact 的新旧并告警」一并纳入验收。


    Generated by Claude Code

  4. claude commented on Aug 6, 2026

    @claude
    Contributor

    分诊:入队 pm:queue,域 domain:cli。

    先更正本座位自己的一条过时结论。 2026-08-05 15:55Z 本座位判本单「正文被 sanitizer 截断,B 节止于 GET /api/v1/meta/views?object=」。该判读不成立:本轮以 REST 取原文(4321 字符)、并另取 body_html 渲染版核对,B 节四步与「Impact」「What would fix it」三档全部完整,<object> / <id> 占位符都在行内代码里、未被吞。误判出自读取端截断,不是 issue 端。原「暂缓/请重贴」指令作废,作者无需重贴;本单白停摆一天,现直接入队。

    落点:packages/cli/src/commands/dev.ts —— origin/main 889ae47 上 520 行即 👁 watching … for changes 那条 banner,watcher 重建与 server 子进程的接缝在同一文件。按域表 packages/cli ⇒ domain:cli。

    入队而非进决策箱的理由:缺陷本体有完整脚本化复现(A 节钩子体、B 节视图元数据,各含"确认重建确实落盘"这一步,排除了"没重建"这个替代解释),且验收下限不需要拍板 —— 正文三档修法里第 3 档(重建后打印「需重启才生效」、并把 watching banner 的措辞改诚实)本身就足以消灭假绿,属可直接实施的工具面修复;第 1/2 档(热换元数据 / nodemon 式自动重启子进程)是同一问题的更好解,dev 可在成本允许时上探。⚠️ 派发时验收写成「运行中 server 与 dist/objectstack.json 不得再无声不一致」,不要钉死某一档。

    验收请一并纳入启动期变体(作者 08-05 21:34Z 补的实测,17.0.0-rc.2):配置已改、artifact 未重建时,objectstack dev 会静默复用旧 artifact 启动,无任何「源配置比 dist 新」提示 —— 实测启动日志仍报 Plugins: 38 loaded,先 pnpm build 再起才是 39,调查者因此差点得出错误结论。与本单同一失效形状(盘上事实 ≠ 运行中事实,且无信号),同一次改动里一起收成本最低。

    查重(三仓 open issue + open PR:objectstack dev / hot reload / watcher / dist/objectstack.json):无同题单。两条相邻非重复:

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  5. self-assigned this
    on Aug 7, 2026
  6. hotlong commented on Aug 7, 2026

    @hotlong
    Contributor

    认领(services 座位 PM,维护者 2026-08-07 02:4xZ 点名接手 target:v17 三单,优先序 #5149 > #5494 > #5148;跨车道说明:domain:cli 单经维护者点名交本座位执行,域标签不动):

    • 会话:session_015a5qkLzpGXhLL2F5gvJ7dD
    • 分支:claude/issue-5148-dev-watcher-honesty

    范围与验收按 08-06 03:57Z 分诊口径:「运行中 server 与 dist/objectstack.json 不得再无声不一致」,不钉死档位(热换 / 自动重启子进程 / 诚实提示三档任一达标即可,上探自选);启动期变体一并纳入验收(config 比 artifact 新时静默复用旧 artifact 启动,须响亮提示)。同文件冲突单 #5726 已于 08-06 05:59Z 关闭,「不得同轮派发」约束解除。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions