Skip to content

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

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/cli17.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:

    constisDelete=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 oldconfig.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.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions