You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A stored expression field added to an object that already has rows is never backfilled: existing rows stay NULL until each is next written #29961
What happens. hotclm adds a stored expression field to clm_contract: review_sla_days_offset = 0 - record.contract_type.review_sla_days. Booting that change over a database created before it (pnpm demo:zh on the previous main) left the column NULL on 121 of 121 rows. A later seed pass rewrote only 41 of 120 rows, leaving 79 NULL. A fresh database is fine (0 NULL), because every insert computes the value.
Why it matters. The value is computed only on the write path, so on an upgraded database the field is silently wrong until each row is touched. In hotclm the analytics $now anchor ({ $now: true, addDays: { $field: 'review_sla_days_offset' } }, #25808) reads a NULL offset as 0 days, so every started review counts as over SLA: the dashboard over-counts with no error.
Seam.spec: Field.expression (stored, field.zod.ts) → runtime: objectql computeStoredExpressions (write path only). os migrate has a summary-nulls step for roll-ups but nothing that recomputes a stored expression for existing rows.
Expected. Adding (or changing) a stored expression either backfills existing rows at boot / schema sync, or os migrate offers a recompute step and the boot log says the column needs it, the same honest-disclosure shape as the roll-up backfill.
Dedupe: no issue matches "stored expression backfill", "expression field NULL existing rows" or "recompute stored expression"; #27193 is a currency-specific recompute.
Read fresh. A stored expression field added to an object that already has rows is computed only on the write path, so existing rows stay NULL until each is touched. hotclm measured 121 of 121 rows NULL, and its analytics $now anchor then reads a NULL offset as 0 days. Silently wrong values on an upgraded database, so p2 kept.
The fix is a backfill at schema sync, or an os migrate recompute step with an honest boot notice, as roll-ups have.
domain:engine is retired (an alias of domain:server) and this is a new card, so it gets the live label.
Claim: PM loop round 1
Session: session_01Utp6Cbvcmv8YWFhFJwzjKL (seat) · dev thread session_01Utp6Cb2FL7H75wB1wA6PpZ
Account: os-bill
Branch: claude/project-thread-jp5zxi (harness-pinned thread branch; platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-29961
Domain: domain:server
Seat: domain:server#2
File surface: packages/objectql/src/ (a new backfill module beside summary-backfill.ts, its export in index.ts; minimal engine.ts touch only if unavoidable), packages/cli/src/commands/migrate/ (a new step and its registration), their tests, one changeset .changeset/29961-stored-expression-backfill.md. ⛔ packages/spec/**, ui/**, docs/adr/**, .claude/**. (stop on breach; explain in the report)
Container & model: M, mode:thread, model: default tier (opus) — dispatch-gates --tier at 31fe289f: no path-derived mandate, effort: high
Clause-②: no — restores an already-declared contract (a stored expression's declared value); the dev re-measures on the built declaration closure
Metadata-protocol: none
Responsibility: objectql's write-only computeStoredExpressions produces the gap | no platform path recomputes existing rows (no os migrate step; only roll-ups and home amounts have one) | any app that adds a stored expression to an object with rows reaches it; hotclm hit it on 18.3.0 (hotclm#31)
Thread-read: 6612748392
Premise: holds — at 31fe289f stored expressions compute on insert/update only (computeStoredExpressions, engine-top-level.ts), and no step under packages/cli/src/commands/migrate/ recomputes one
Serial constraints cleared: in-flight domain:server claims (#30009 trigger-api, #29997 plugin-auth, #30108 plugin-security) do not name these paths; #27194 (epic, migrate home-field proof) may add under packages/cli/src/commands/migrate/ — the dev reads its branch before registering a new step
Order: project file server-seat2/dispatch-29961.md, delivered as the card thread's opening message.
{
"issue": 29961,
"status": "done",
"branch": "claude/project-thread-jp5zxi",
"pr": "https://github.com/objectstack-ai/objectstack/pull/30227",
"session": "session_01Utp6Cb2FL7H75wB1wA6PpZ",
"premise_still_valid": true,
"summary": "Stored computed fields are computed on insert/update only (computeStoredExpressions); nothing filled rows stored before the field was declared. Added os migrate stored-expressions (report-first, --apply writes NULL holes, --changed also rewrites stale values, faulting rows that hold a value are skipped and listed) over a new ObjectQL.computeStoredExpressionsOver, plus a bounded boot advisory (first 100 rows per object declaring a stored computed field) at kernel:bootstrapped that names the command. No automatic boot backfill: each fill is an unordered write (hooks, flows, audit, roll-ups) and a full walk would hold the boot.",
"tests": "objectql: stored-expression-backfill.test.ts 29/29, full suite 549 files / 10892 tests exit 0; cli: stored-expressions.test.ts 10/10 (integration project), unit 382 files / 5384 tests exit 0; reproduced on SQLite and memory: 4/4 rows NULL after adding fields, --apply filled them, re-run 0 holes 0 stale, changed expression 4 stale -> --changed -> 0; ablation of the apply guard turned 5 tests red, restore blob == HEAD",
"gates": "dispatch-gates --commands at c82c1c7f: 96 families run, each exit 0 recorded before any pipe; --ran: 96 derived, 96 run, 0 NOT-MEASURED; pnpm lint (full repo) exit 0; objectql typecheck exit 0; cli typecheck exit 0; CI check-runs not yet reported at PR open",
"line_budget": "+929/-0 source, docs, changeset; +465/-1 tests (11 files)",
"files_changed": [
".changeset/29961-stored-expression-backfill.md",
"content/docs/deployment/cli.mdx",
"packages/cli/src/commands/migrate/stored-expressions.ts",
"packages/cli/src/commands/migrate/stored-expressions.test.ts",
"packages/cli/test/migrate-confirmation-exit.pin.test.ts",
"packages/objectql/src/engine.ts",
"packages/objectql/src/federated-injected-column-readers.test.ts",
"packages/objectql/src/index.ts",
"packages/objectql/src/plugin.ts",
"packages/objectql/src/stored-expression-backfill.ts",
"packages/objectql/src/stored-expression-backfill.test.ts"
],
"deviations": [
"Clause-②: measured a widening (new @objectstack/objectql exports and a new public method ObjectQL.computeStoredExpressionsOver; precedent #30183 declared yes (widening)); PR body and changeset carry the claim's no, for the seat to re-declare",
"content/docs/deployment/cli.mdx: one data-migration table row, outside the declared file surface",
"two existing tests edited outside the declared surface: migrate-confirmation-exit.pin.test.ts (roster row for the new command) and federated-injected-column-readers.test.ts (ledger rows for the new updated_at reader)",
"no packages/cli index.ts registration: oclif discovers migrate commands by pattern",
"roll-ups have no boot notice on main, contrary to the triage; the boot advisory follows announceOpenMigrationGates instead",
"engine.ts edited with one method (+29) beside serveFormulaField"
],
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "2 — mcp__github__issue_read x2 (reads only)",
"api_writes": "2 — POST /repos/objectstack-ai/objectstack/pulls (relay pr_create), POST /repos/objectstack-ai/objectstack/issues/29961/comments (post-stamped); plus git push",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": ["none"]
}
Claim: PM loop round 2 (corrected declaration; same session, branch and surface)
Session: session_01Utp6Cbvcmv8YWFhFJwzjKL (seat) · dev thread session_01Utp6Cb2FL7H75wB1wA6PpZ
Account: os-bill
Branch: claude/project-thread-jp5zxi (harness-pinned thread branch; platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-29961
Domain: domain:server
Seat: domain:server#2
File surface: as the first claim, plus what the PR measured: packages/objectql/src/plugin.ts (boot notice wiring), content/docs/deployment/cli.mdx (one line), two existing register pins (packages/cli/test/migrate-confirmation-exit.pin.test.ts, packages/objectql/src/federated-injected-column-readers.test.ts). (stop on breach; explain in the report)
Container & model: M, mode:thread, model: default tier (opus) — dispatch-gates --tier at 31fe289f: no path-derived mandate, effort: high
Clause-②: yes (widening) — corrected from the first claim (6646553302) on the dev's measurement: new @objectstack/objectql exports, a new public engine method (ObjectQL.computeStoredExpressionsOver) and a new CLI command (os migrate stored-expressions); nothing narrows. Precedent: PR #30183 declared the same shape yes (widening).
Metadata-protocol: none
Responsibility: objectql's write-only computeStoredExpressions produces the gap | no platform path recomputes existing rows (no os migrate step; only roll-ups and home amounts have one) | any app that adds a stored expression to an object with rows reaches it; hotclm hit it on 18.3.0 (hotclm#31)
Thread-read: 6648041072
Premise: holds — at 31fe289f stored expressions compute on insert/update only (computeStoredExpressions, engine-top-level.ts), and no step under packages/cli/src/commands/migrate/ recomputes one
Serial constraints cleared: in-flight domain:server claims (#30009 trigger-api, #29997 plugin-auth, #30108 plugin-security) do not name these paths; #27194 (epic, migrate home-field proof) may add under packages/cli/src/commands/migrate/ — the dev reads its branch before registering a new step
Order: project file server-seat2/dispatch-29961.md, delivered as the card thread's opening message.
@objectstack/objectql and @objectstack/cli patch changeset (Clause-②: yes (widening)), ships with the next release
State change: pm:dispatched removed. The queue kick on run 41965463000 (Live E2E UI, one ECONNRESET in studio-customize-journey.spec.ts) cleared on GitHub's own re-run before this seat's re-queue reached it (that relay call answered "already merged"); audit comment 6649706610 stands as the reading of that kick.
Activity
objectstack-fleet commentedon Nov 3, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: audit · p2 kept ·
domain:engine→domain:server·pm:queuekept · 2026-11-03T18:53Z⛔ Not a claim.
expressionfield added to an object that already has rows is computed only on the write path, so existing rows stay NULL until each is touched. hotclm measured 121 of 121 rows NULL, and its analytics$nowanchor then reads a NULL offset as 0 days. Silently wrong values on an upgraded database, so p2 kept.os migraterecompute step with an honest boot notice, as roll-ups have.domain:engineis retired (an alias ofdomain:server) and this is a new card, so it gets the live label.Generated by Claude Code
objectstack-fleet commentedon Nov 5, 2026
domain:server#2 · os-bill · session_01Utp6Cbvcmv8YWFhFJwzjKL
Claim: PM loop round 1
Session:
session_01Utp6Cbvcmv8YWFhFJwzjKL(seat) · dev threadsession_01Utp6Cb2FL7H75wB1wA6PpZAccount:
os-billBranch:
claude/project-thread-jp5zxi(harness-pinned thread branch; platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-29961Domain:
domain:serverSeat:
domain:server#2File surface:
packages/objectql/src/(a new backfill module besidesummary-backfill.ts, its export inindex.ts; minimalengine.tstouch only if unavoidable),packages/cli/src/commands/migrate/(a new step and its registration), their tests, one changeset.changeset/29961-stored-expression-backfill.md. ⛔packages/spec/**,ui/**,docs/adr/**,.claude/**. (stop on breach; explain in the report)Container & model:
M,mode:thread,model: default tier (opus) — dispatch-gates --tier at 31fe289f: no path-derived mandate,effort: highClause-②: no — restores an already-declared contract (a stored expression's declared value); the dev re-measures on the built declaration closure
Metadata-protocol: none
Responsibility: objectql's write-only
computeStoredExpressionsproduces the gap | no platform path recomputes existing rows (noos migratestep; only roll-ups and home amounts have one) | any app that adds a stored expression to an object with rows reaches it; hotclm hit it on 18.3.0 (hotclm#31)Thread-read: 6612748392
Premise: holds — at
31fe289fstored expressions compute on insert/update only (computeStoredExpressions,engine-top-level.ts), and no step underpackages/cli/src/commands/migrate/recomputes oneSerial constraints cleared: in-flight
domain:serverclaims (#30009 trigger-api, #29997 plugin-auth, #30108 plugin-security) do not name these paths; #27194 (epic, migrate home-field proof) may add underpackages/cli/src/commands/migrate/— the dev reads its branch before registering a new stepOrder: project file
server-seat2/dispatch-29961.md, delivered as the card thread's opening message.objectstack-fleet commentedon Nov 5, 2026
domain:server#2 · os-bill · session_01Utp6Cb2FL7H75wB1wA6PpZ
os-dev-report
{ "issue": 29961, "status": "done", "branch": "claude/project-thread-jp5zxi", "pr": "https://github.com/objectstack-ai/objectstack/pull/30227", "session": "session_01Utp6Cb2FL7H75wB1wA6PpZ", "premise_still_valid": true, "summary": "Stored computed fields are computed on insert/update only (computeStoredExpressions); nothing filled rows stored before the field was declared. Added os migrate stored-expressions (report-first, --apply writes NULL holes, --changed also rewrites stale values, faulting rows that hold a value are skipped and listed) over a new ObjectQL.computeStoredExpressionsOver, plus a bounded boot advisory (first 100 rows per object declaring a stored computed field) at kernel:bootstrapped that names the command. No automatic boot backfill: each fill is an unordered write (hooks, flows, audit, roll-ups) and a full walk would hold the boot.", "tests": "objectql: stored-expression-backfill.test.ts 29/29, full suite 549 files / 10892 tests exit 0; cli: stored-expressions.test.ts 10/10 (integration project), unit 382 files / 5384 tests exit 0; reproduced on SQLite and memory: 4/4 rows NULL after adding fields, --apply filled them, re-run 0 holes 0 stale, changed expression 4 stale -> --changed -> 0; ablation of the apply guard turned 5 tests red, restore blob == HEAD", "gates": "dispatch-gates --commands at c82c1c7f: 96 families run, each exit 0 recorded before any pipe; --ran: 96 derived, 96 run, 0 NOT-MEASURED; pnpm lint (full repo) exit 0; objectql typecheck exit 0; cli typecheck exit 0; CI check-runs not yet reported at PR open", "line_budget": "+929/-0 source, docs, changeset; +465/-1 tests (11 files)", "files_changed": [ ".changeset/29961-stored-expression-backfill.md", "content/docs/deployment/cli.mdx", "packages/cli/src/commands/migrate/stored-expressions.ts", "packages/cli/src/commands/migrate/stored-expressions.test.ts", "packages/cli/test/migrate-confirmation-exit.pin.test.ts", "packages/objectql/src/engine.ts", "packages/objectql/src/federated-injected-column-readers.test.ts", "packages/objectql/src/index.ts", "packages/objectql/src/plugin.ts", "packages/objectql/src/stored-expression-backfill.ts", "packages/objectql/src/stored-expression-backfill.test.ts" ], "deviations": [ "Clause-②: measured a widening (new @objectstack/objectql exports and a new public method ObjectQL.computeStoredExpressionsOver; precedent #30183 declared yes (widening)); PR body and changeset carry the claim's no, for the seat to re-declare", "content/docs/deployment/cli.mdx: one data-migration table row, outside the declared file surface", "two existing tests edited outside the declared surface: migrate-confirmation-exit.pin.test.ts (roster row for the new command) and federated-injected-column-readers.test.ts (ledger rows for the new updated_at reader)", "no packages/cli index.ts registration: oclif discovers migrate commands by pattern", "roll-ups have no boot notice on main, contrary to the triage; the boot advisory follows announceOpenMigrationGates instead", "engine.ts edited with one method (+29) beside serveFormulaField" ], "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "2 — mcp__github__issue_read x2 (reads only)", "api_writes": "2 — POST /repos/objectstack-ai/objectstack/pulls (relay pr_create), POST /repos/objectstack-ai/objectstack/issues/29961/comments (post-stamped); plus git push", "open_questions": [], "security_findings": "none", "out_of_scope_findings": ["none"] }Report taken 2026-11-05T02:29Z.
Generated by Claude Code
objectstack-fleet commentedon Nov 5, 2026
domain:server#2 · os-bill · session_01Utp6Cbvcmv8YWFhFJwzjKL
Claim: PM loop round 2 (corrected declaration; same session, branch and surface)
Session:
session_01Utp6Cbvcmv8YWFhFJwzjKL(seat) · dev threadsession_01Utp6Cb2FL7H75wB1wA6PpZAccount:
os-billBranch:
claude/project-thread-jp5zxi(harness-pinned thread branch; platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-29961Domain:
domain:serverSeat:
domain:server#2File surface: as the first claim, plus what the PR measured:
packages/objectql/src/plugin.ts(boot notice wiring),content/docs/deployment/cli.mdx(one line), two existing register pins (packages/cli/test/migrate-confirmation-exit.pin.test.ts,packages/objectql/src/federated-injected-column-readers.test.ts). (stop on breach; explain in the report)Container & model:
M,mode:thread,model: default tier (opus) — dispatch-gates --tier at 31fe289f: no path-derived mandate,effort: highClause-②: yes (widening) — corrected from the first claim (6646553302) on the dev's measurement: new
@objectstack/objectqlexports, a new public engine method (ObjectQL.computeStoredExpressionsOver) and a new CLI command (os migrate stored-expressions); nothing narrows. Precedent: PR #30183 declared the same shapeyes (widening).Metadata-protocol: none
Responsibility: objectql's write-only
computeStoredExpressionsproduces the gap | no platform path recomputes existing rows (noos migratestep; only roll-ups and home amounts have one) | any app that adds a stored expression to an object with rows reaches it; hotclm hit it on 18.3.0 (hotclm#31)Thread-read: 6648041072
Premise: holds — at
31fe289fstored expressions compute on insert/update only (computeStoredExpressions,engine-top-level.ts), and no step underpackages/cli/src/commands/migrate/recomputes oneSerial constraints cleared: in-flight
domain:serverclaims (#30009 trigger-api, #29997 plugin-auth, #30108 plugin-security) do not name these paths; #27194 (epic, migrate home-field proof) may add underpackages/cli/src/commands/migrate/— the dev reads its branch before registering a new stepOrder: project file
server-seat2/dispatch-29961.md, delivered as the card thread's opening message.chore(changeset): declare Clause-② yes (widening) for #29961
objectstack-fleet commentedon Nov 5, 2026
domain:server#2 · os-bill · session_01Utp6Cbvcmv8YWFhFJwzjKL
Landed · 2026-11-05T04:31Z
cd5f5546, reviewed heada1ee9670(TIER_DEFAULT declaration record PASS 6649065103, seat ACCEPT 6649339488)Fixes #29961@objectstack/objectqland@objectstack/clipatch changeset (Clause-②: yes (widening)), ships with the next releaseState change:
pm:dispatchedremoved. The queue kick on run 41965463000 (Live E2E UI, oneECONNRESETinstudio-customize-journey.spec.ts) cleared on GitHub's own re-run before this seat's re-queue reached it (that relay call answered "already merged"); audit comment 6649706610 stands as the reading of that kick.