Skip to content

feat(automation): xExpression marker on the loop collection config field (#3304)#3313

Merged
os-zhuang merged 1 commit into
mainfrom
claude/step-logs-run-observability-plxc2p
Jul 19, 2026
Merged

feat(automation): xExpression marker on the loop collection config field (#3304)#3313
os-zhuang merged 1 commit into
mainfrom
claude/step-logs-run-observability-plxc2p

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

First increment of #3304 — the descriptor-side counterpart to objectui #2670 Phase 3 (merged as objectui #2699).

What

Introduce an xExpression: 'expression' | 'template' marker on flow-node config string properties, riding the exact same Zod .meta() → JSON-Schema channel as the existing xRef / xEnumDeprecated markers. It tells the flow designer whether a string is bare CEL or an interpolate() single-brace {var} template, so the designer renders the right editor (mono + {var} picker, and whether the CEL brace-trap applies) instead of guessing from the field name.

Apply it to the one field where it closes a live divergence today: the loop node's collection (a {tasks} template).

Why this scope

There are two separate channels that produce a node's published configSchema:

  1. Zod → z.toJSONSchema — only approval (getApprovalNodeConfigJsonSchema). .meta() flows through here verbatim.
  2. Hand-written JSON literals in service-automation/builtin/*.tsloop / parallel / try_catch / http / connector_action / notify. These ignore their Zod schemas.

A further 11 node types publish no configSchema at all (decision, assignment, CRUD×4, screen, script, wait, subflow, map).

Among every node that already ships a configSchema, loop.collection is the only template/expression field — and it's exactly the divergence #3304 leads with: online the designer rendered it as plain text while the offline hardcoded form rendered a mono expression editor, and the CEL brace-trap false-flagged {tasks} as a malformed condition. (Marking other config-schema string fields such as http.url would create a mono-vs-text divergence, since objectui renders those as plain text — so they are deliberately left unmarked.)

Changes

  • packages/spec/src/automation/control-flow.zod.tsLoopConfigSchema.collection gains .meta({ xExpression: 'template' }) (canonical source; emitted via z.toJSONSchema).
  • packages/services/service-automation/src/builtin/loop-node.ts — the shipped descriptor's configSchema literal (the JSON objectui actually reads) gains xExpression: 'template' on collection. loop's literal doesn't derive from Zod, so both are annotated so they agree.

Follow-up (tracked in #3304, not this PR)

The same marker on map.collection (template), decision.expression / start.condition (expression), script body (expression + multiline) — each lives on a node type that publishes no configSchema yet, so each needs a new config schema authored (using objectui's hardcoded flow-node-config as the spec of record) plus a ratchet-manifest update. Kept out of this PR to land the concrete fix cleanly.

Verification

  • New tests: LoopConfigSchema emits xExpression: 'template' (with description preserved) through z.toJSONSchema (control-flow.test.ts); the registered loop descriptor's configSchema.collection carries the marker (loop-node.test.ts). Both green.
  • tsc --noEmit clean for @objectstack/spec and @objectstack/service-automation; pnpm gen:schema succeeds with no ratchet-manifest drift (annotating an existing schema doesn't change schema keys).
  • Additive + backward-compatible: an unknown xExpression value is ignored by the designer, runtime behavior unchanged.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VuvxWgoadqryqBcjs7TpVi


Generated by Claude Code

…eld (#3304)

The flow designer generates a node's config form from its published
`configSchema` (ADR-0018). Introduce an `xExpression: 'expression' | 'template'`
marker on string properties — riding the same Zod `.meta()` → JSON-Schema
channel as `xRef` / `xEnumDeprecated` — that declares whether a string is bare
CEL or an `interpolate()` single-brace `{var}` template.

Apply it to the `loop` node's `collection` (a `{tasks}` template):
  • LoopConfigSchema.collection — the canonical Zod source (control-flow.zod.ts),
    emitted via z.toJSONSchema.
  • the shipped descriptor's configSchema literal (service-automation
    loop-node.ts) — the JSON objectui actually reads. loop's configSchema is a
    hand-written literal that doesn't derive from the Zod schema, so both are
    annotated so they agree.

Closes the live divergence: without the marker the designer rendered
`collection` as plain text online while the offline hardcoded form rendered it
as a mono expression editor, and the CEL brace-trap false-flagged `{tasks}`.
objectui #2670 Phase 3 (#2699) already consumes the marker.

Additive + backward-compatible: an unknown value is ignored, runtime unchanged.
Follow-up (tracked in #3304): the same marker on map/decision/script and the
node types that publish no configSchema yet.

Tests: LoopConfigSchema emits xExpression through z.toJSONSchema (spec); the
registered loop descriptor's configSchema.collection carries it (service-automation).

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

vercel Bot commented Jul 19, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Building Building Preview, Comment Jul 19, 2026 5:36pm

Request Review

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): packages/services, @objectstack/spec.

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

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via packages/services, @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/cli.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/audit-service.mdx (via packages/services)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/services, packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/settings-service.mdx (via packages/services)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via packages/services, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via packages/services, @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

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 added the allow-major label Jul 19, 2026 — with Claude
@os-zhuang
os-zhuang marked this pull request as ready for review July 19, 2026 18:07
@os-zhuang
os-zhuang merged commit 158aa14 into main Jul 19, 2026
18 of 20 checks passed
@os-zhuang
os-zhuang deleted the claude/step-logs-run-observability-plxc2p branch July 19, 2026 18:07
os-zhuang added a commit that referenced this pull request Jul 20, 2026
…s (designer parity, #3304) (#3327)

assignment, create/update/delete/get_record, and screen now publish configSchemas mirroring objectui's hardcoded field groups — free-form maps as JSON-Schema open objects (additionalProperties: true) rendered by the designer's keyValue editor (objectui #2708), object references as xRef, screen's visibleWhen as an xExpression CEL column. decision/wait/script/subflow stay deliberately schemaless (documented + tested — a partial schema would drop editors). Verified: parity tests, tsc, 335-test suite green. Descriptor metadata only; no runtime behavior change.

Closes the #3304 implementation series: loop (#3313), map (#3321), objectui adapter (#2708), this PR.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

allow-major documentation Improvements or additions to documentation size/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants