Skip to content

feat(lint,showcase,automation): record-after-write follow-ups — validation lint, showcase flow, create-leg previous fix (#3427)#3467

Merged
os-zhuang merged 3 commits into
mainfrom
claude/record-change-lifecycle-binding-kobc1m
Jul 24, 2026
Merged

feat(lint,showcase,automation): record-after-write follow-ups — validation lint, showcase flow, create-leg previous fix (#3427)#3467
os-zhuang merged 3 commits into
mainfrom
claude/record-change-lifecycle-binding-kobc1m

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Summary

Follow-ups to the record-after-write feature (#3427, merged in #3446). Three things, one of them a real bug the dogfood caught:

1. Authoring-time lint for never-firing record triggers (@objectstack/lint)

New flow-trigger-unknown-event rule in validateFlowTriggerReadiness: a start node whose triggerType is record-lifecycle-shaped (record-before|after-<op>) but names an op the record-change trigger can't map — a typo like record-after-updated — binds to the trigger yet maps to no hook and never fires (only a runtime warn). The rule surfaces that at os validate time. Warning severity, consistent with the file's existing rules. (Bare record-<noun> shapes like record-change are out of this rule's scope.)

2. Showcase example (examples/app-showcase)

UrgentTaskAlertFlow — one record-after-write flow that fires when a task is created Urgent or escalated to Urgent, using the previous == null create/update discrimination. Demonstrates the feature as a real, dogfood-verified example.

3. Fix: previous unbound on the create leg (@objectstack/service-automation) 🐛

Dogfooding #2 in a live app surfaced a real bug: the engine bound previous into the flow condition scope only when truthy, so on a record insert previous was an unknown CEL variable. The previous == null discrimination that #3446 documented (flows docs + the Studio designer) actually threw condition failed to evaluate as CEL: Unknown variable: previous and dropped the run. previous is now always bound — null when there is no prior row — so previous == null is the create leg and previous.<field> the update leg, as advertised. Update-triggered flows are unchanged.

Verification (dogfood)

Booted the showcase app on a fresh backend and drove it over REST:

  • record-after-write flow binds to afterInsert + afterUpdate; among the 20 active bound flows.
  • Create an Urgent task → flow fires (create leg, previous == null → true), zero Unknown variable errors, a task.urgent notification is delivered.
  • Create a non-urgent task → flow correctly does not fire.
  • Escalate low → urgent → flow fires (update leg).

The dogfood also caught a second, showcase-local bug (the notify node had no recipient → every run failed "at least one recipient is required"); fixed by notifying the assignee with {$User.Id} as a guaranteed fallback (live-verified a notification is delivered).

Automated: service-automation (347) + trigger-record-change (42, incl. a new create/update discrimination integration test) + lint (271) suites all green.

Changesets

  • @objectstack/service-automation: patch (the previous binding fix).
  • (lint + showcase changes need no changeset — the lint rule ships in an unreleased line and the showcase is not published.)

Related


Generated by Claude Code

claude added 2 commits July 24, 2026 15:38
…ord-after-write showcase flow (#3427)

Follow-ups to the record-after-write feature (#3446).

Lint (item 3): new `flow-trigger-unknown-event` rule in validateFlowTriggerReadiness
flags a start node whose `triggerType` is record-lifecycle-shaped
(`record-before|after-<op>`) but names an op the record-change trigger cannot map
— a typo like `record-after-updated`. The engine still routes any `record-` token
to the record trigger, which then binds to NO hook and never fires (only a runtime
warn), so this surfaces the never-fire defect at authoring/`os validate` time.
Warning severity, consistent with the file's existing rules. Bare `record-<noun>`
shapes (e.g. `record-change`) are out of scope for this rule.

Showcase (item 2): add `UrgentTaskAlertFlow`, a single `record-after-write` flow
that fires when a task is created Urgent OR escalated to Urgent — using the
`previous == null` create/update discrimination the write trigger enables. Plus a
durable integration test booting a real kernel that verifies the create leg
(`previous == null` → fires on afterInsert), the non-urgent create (no fire), and
the escalation leg (fires on afterUpdate).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018939yJ413zG3irLzcTtqaa
…ix showcase notify recipient (#3427)

Dogfooding the record-after-write showcase flow in a live app surfaced two bugs:

1. The engine bound `previous` into the flow condition scope only when truthy, so
   on a record insert `previous` was an unknown CEL variable — the documented
   `previous == null` create-discrimination threw "Unknown variable: previous" and
   failed the whole start condition, dropping the run. `previous` is now always
   bound (null when there is no prior row), making the create/update discrimination
   the record-after-write docs + Studio designer advertise actually work. Verified
   end-to-end (integration test + a live showcase boot: the flow fires on
   create-urgent and on escalation, and correctly skips a non-urgent create).

2. The showcase `UrgentTaskAlertFlow` notify node had no recipient, so every run
   failed "at least one recipient is required". Now notifies the assignee, falling
   back to the triggering user (`{$User.Id}`) so an unassigned urgent task still
   pings someone. Live-verified: a `task.urgent` notification is delivered.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018939yJ413zG3irLzcTtqaa
@vercel

vercel Bot commented Jul 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Canceled Canceled Jul 24, 2026 4:39pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/m labels Jul 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/lint, packages/services, packages/triggers.

7 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/automation/webhooks.mdx (via packages/services)
  • content/docs/kernel/runtime-services/audit-service.mdx (via packages/services)
  • content/docs/kernel/runtime-services/index.mdx (via packages/services)
  • content/docs/kernel/runtime-services/settings-service.mdx (via packages/services)
  • content/docs/permissions/authorization.mdx (via @objectstack/lint)
  • content/docs/plugins/packages.mdx (via packages/services)
  • content/docs/protocol/kernel/i18n-standard.mdx (via packages/services)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang marked this pull request as ready for review July 24, 2026 16:07
@os-zhuang
os-zhuang merged commit 9bf4588 into main Jul 24, 2026
15 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants