Skip to content

Upgrade to @objectstack 9.7.0 + fix time-relative flows (#1874) & metric rollups (#1870) - #62

Merged
os-zhuang merged 4 commits into
mainfrom
chore/upgrade-9.7.0
Jun 16, 2026
Merged

os-zhuang merged 4 commits into
mainfrom
chore/upgrade-9.7.0

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Upgrades the template suite to @objectstack 9.7.0 and fixes the runtime-correctness issues the new build-time validators surfaced.

#1874 — time-relative flows were on the wrong trigger

Nine flows used a record_change trigger with daysFromNow/daysAgo date criteria. A record-change trigger only fires when the row is mutated, so an untouched record sails past its threshold and the flow (almost) never fires on the right day. Each is now a daily schedule that QUERIES the population:

renewal_alert, renewal_draft, obligation_overdue (contracts); auto_close_resolved (helpdesk); evidence_auto_expire, evidence_expiring (compliance); expense_approval_overdue (expense); po_overdue (procurement); document_expiring_soon (hr).

Idempotency is declarative — zero new fields (charter-friendly): lifecycle flows transition status (bulk update self-guards); overdue flows scope the query to the open status; discrete T-N alerts hit one calendar day via the daily schedule; renewal_draft guards on an existing-draft title check. Date equality (==) is replaced with range (<=/<) where it fits; $in/== kept for discrete-day tiers, with a date/timestamp-matching caveat noted inline for engine verification.

Note: daysBetween is not in the 9.7 CEL stdlib, so "days remaining" can't be computed in-flow — discrete tiers stay as exact-day $in sets on the reliable daily schedule.

#1870 — publication_rollup aggregations never ran

publication_rollup used type:'script' nodes with an aggregations config, but the automation engine has no aggregate node — script nodes only run a named callable, so the sums never executed and total_* were silently never updated. A hook can't fix it (cross-object rollup write is unsupported in the standalone QuickJS runtime — the repo's own note). Replaced with native Field.summary roll-ups (recomputed server-side on child change) on content_publication and content_piece; removed the broken flow. Also fixed ISCHANGED(status) (no such CEL function) → status != previous.status in the lifecycle flow.

Upgrade mechanics

  • All @objectstack/* deps 9.5.1 → 9.7.0.
  • minimumReleaseAgeExclude entries unpinned from @9.0.0 (the just-published 9.7.0 was age-gated by the supply-chain policy).

Verification

  • All 9 template packages build clean on 9.7.0 (objectstack build), including the new ADR-0032 expression validation.
  • The new 9.7.0 validators (bare-ref, unknown-function, script-callable) pass across the suite.

Follow-ups (noted, not blocking)

  • The discrete-tier $in/== date matching (end_date == daysFromNow(N) on a date field vs a timestamp) should be confirmed against the engine's date coercion; widen to a one-day range if too strict.
  • content/CHARTER.md's "no cron in spec v6" line is stale (trigger-schedule ships in 9.x; project templates already use cron) — worth reconciling.

🤖 Generated with Claude Code

os-zhuang and others added 4 commits June 16, 2026 13:51
…1874); upgrade @objectstack 9.7.0

#1874 — nine flows used a `record_change` trigger with `daysFromNow`/`daysAgo`
date criteria. A record-change trigger only fires on row mutation, so an
untouched record sails past its threshold and the flow (almost) never fires on
the right day. Each is now a daily `schedule` that QUERIES the population:

- contracts: renewal_alert, renewal_draft, obligation_overdue
- helpdesk: auto_close_resolved
- compliance: evidence_auto_expire, evidence_expiring
- expense: expense_approval_overdue
- procurement: po_overdue
- hr: document_expiring_soon

Idempotency is declarative (charter-friendly, zero new fields): lifecycle flows
transition status (bulk update self-guards); overdue flows scope the query to the
open status; discrete T-N alerts hit one calendar day via the daily schedule;
renewal_draft guards on the existing-draft title. Date equality replaced with
range (`<=`/`<`) where it fits; `$in`/`==` kept for discrete-day tiers (with a
date/timestamp caveat noted inline).

Also: bump all @objectstack deps 9.5.1 -> 9.7.0, and unpin the
`minimumReleaseAgeExclude` entries (just-published 9.7.0 was age-gated).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…_rollup flow (#1870)

`publication_rollup` used `type:'script'` nodes with an `aggregations` config to
sum child metrics — but the automation engine has no aggregate node, so script
nodes only run a named callable. The aggregations never ran: publication/piece
`total_*` were silently never updated (they only looked right in seed data). The
9.7.0 build validator now flags this ("script node declares neither actionType
nor function").

A hook can't fix it either — a cross-object rollup write from a hook is
unsupported in the standalone QuickJS runtime (the repo's own note in
content/src/hooks/index.ts). The correct mechanism is the engine's native
`Field.summary` roll-up, recomputed server-side on child insert/update/delete:

- content_publication.total_{views,clicks,signups,revenue} = sum over content_metric
- content_publication.last_metric_at = max(content_metric.period_end)
- content_piece.total_* = sum over content_publication.total_*

Removed the broken flow + its registration; refreshed the now-stale notes in
hooks/index.ts and record_metric_snapshot.action.ts.

Also fixes a second pre-existing bug the 9.7.0 validator caught: the lifecycle
flow used `ISCHANGED(status)` (no such CEL function) — replaced with the
`status != previous.status` change check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ity (#1874)

Field.date stores a full timestamp (verified: '2026-07-15T17:24:56.533Z'), so
`end_date == daysFromNow(N)` compares two independently-computed timestamps and
never matches — the discrete-tier alerts (renewal_alert, evidence_expiring,
document_expiring_soon) would have silently never fired. Replace exact equality /
$in with abutting one-day windows [daysFromNow(N), daysFromNow(N+1)) via $gte/$lt
(+ $or for multi-tier): the windows tile the timeline so each record falls in
exactly one — fires once per tier, still idempotent, no guard field. Range
date-filtering against a cel timestamp is engine-verified.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@os-zhuang
os-zhuang merged commit a85dbb5 into main Jun 16, 2026
3 checks passed
os-zhuang added a commit that referenced this pull request Jun 16, 2026
* chore(deps): upgrade @objectstack to 9.8.0

Bumps all @objectstack/* deps 9.7.0 → 9.8.0 (CEL stdlib catalog alignment +
date-equality/phantom-aggregation flow lints). Validated: all 9 template stacks
`objectstack build` clean on 9.8.0 with zero new lint warnings (the #62
discrete-tier range conversions stay clean under the new filter lint).

Also adds @objectstack/setup, /studio, /trigger-api, /trigger-record-change,
/trigger-schedule to `minimumReleaseAgeExclude` — they were missing from the
first-party bypass list and tripped the release-age gate on local resolution.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(content,todo): drop pruned `colorFormat` knob (9.8.0 spec)

9.8.0 pruned dead per-type display knobs from FieldInput (ADR-0049 enforce-or-
remove): `color`'s `colorFormat`/`allowAlpha`/`presetColors` had no runtime
reader and renderers ignored them. `Field.color({ colorFormat: 'hex' })` no
longer type-checks. Remove it — Field.color stores a hex string regardless.

(Caught by the Verify typecheck job, which `objectstack build` doesn't run.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant