Context
ADR-0044 (approval send-back-for-revision) is implemented engine-side, and the Studio designer authoring is now done in objectui — authors can draw a revise branch, mark an edge type: 'back', see un-declared cycles flagged, and one-click an "add revision loop" (objectstack-ai/objectui#1954, #1955, #1958).
The remaining gap is the AI Build path. When the flow-authoring agent generates an approval flow with a send-back, it must emit the full revise-loop shape — otherwise the result is rejected at registration. This is the AI-authoring follow-up flagged in #1770 ("designer support … AI flow authoring all need the same reduced-graph rule").
Problem — the failure chain
The canonical revise loop is:
approval ──approve──▶ …
──reject───▶ …
──revise───▶ wait (signal)
└──resubmit──[type:'back']──▶ approval (round N+1)
registerFlow validates graph-minus-declared-back-edges must be a DAG. So if the agent generates the revise → wait → resubmit loop but forgets to mark the closing edge type: 'back', registerFlow hard-rejects it as an un-declared cycle. The generated flow won't save — the user gets an error instead of a working loop.
The shape knowledge exists in schemas the agent may consume (FlowEdgeSchema.type describes back; ApprovalNodeConfigSchema.maxRevisions is documented), but schema-visibility ≠ reliable generation. We need a deterministic safety net, not just prompt adherence.
Proposed work (prioritized by reliability)
- Deterministic guardrail in
packages/cli/src/utils/lint-flow-patterns.ts (its stated purpose: "guide the author — very often an AI generating templates — toward the robust pattern"). Add a revise-loop rule that detects an approval node sitting on a cycle whose closing edge is not type: 'back', and either:
- auto-repair: mark the closing edge
type: 'back' (turn a hard registerFlow rejection into a working flow), or
- structured feedback: surface the precise fix so the agent self-corrects on the next turn.
Auto-repair is preferred — it doesn't depend on the model "remembering."
- Agent prompt + few-shot — add the revise-loop shape to the flow-authoring guidance and point at the canonical
examples/app-showcase/src/flows/index.ts → showcase_budget_approval. Also nudge maxRevisions when a revise edge is emitted.
- Spec completeness —
packages/spec/src/studio/flow-builder.zod.ts edge visual registry currently covers normal/default/fault but not back; add a back edge style so every flow-builder-protocol consumer (not just objectui's hand-rolled canvas) renders the return arc.
- Eval — a small eval set: given prompts that imply send-back/rework, assert the generated flow (a) registers cleanly and (b) contains a
revise out-edge + a type:'back' closing edge. Guards against regressions in the agent's adherence.
Acceptance criteria
References
Context
ADR-0044 (approval send-back-for-revision) is implemented engine-side, and the Studio designer authoring is now done in objectui — authors can draw a
revisebranch, mark an edgetype: 'back', see un-declared cycles flagged, and one-click an "add revision loop" (objectstack-ai/objectui#1954, #1955, #1958).The remaining gap is the AI Build path. When the flow-authoring agent generates an approval flow with a send-back, it must emit the full revise-loop shape — otherwise the result is rejected at registration. This is the AI-authoring follow-up flagged in #1770 ("designer support … AI flow authoring all need the same reduced-graph rule").
Problem — the failure chain
The canonical revise loop is:
registerFlowvalidates graph-minus-declared-back-edges must be a DAG. So if the agent generates therevise → wait → resubmitloop but forgets to mark the closing edgetype: 'back',registerFlowhard-rejects it as an un-declared cycle. The generated flow won't save — the user gets an error instead of a working loop.The shape knowledge exists in schemas the agent may consume (
FlowEdgeSchema.typedescribesback;ApprovalNodeConfigSchema.maxRevisionsis documented), but schema-visibility ≠ reliable generation. We need a deterministic safety net, not just prompt adherence.Proposed work (prioritized by reliability)
packages/cli/src/utils/lint-flow-patterns.ts(its stated purpose: "guide the author — very often an AI generating templates — toward the robust pattern"). Add a revise-loop rule that detects an approval node sitting on a cycle whose closing edge is nottype: 'back', and either:type: 'back'(turn a hardregisterFlowrejection into a working flow), orAuto-repair is preferred — it doesn't depend on the model "remembering."
examples/app-showcase/src/flows/index.ts→showcase_budget_approval. Also nudgemaxRevisionswhen areviseedge is emitted.packages/spec/src/studio/flow-builder.zod.tsedge visual registry currently covers normal/default/fault but notback; add abackedge style so every flow-builder-protocol consumer (not just objectui's hand-rolled canvas) renders the return arc.reviseout-edge + atype:'back'closing edge. Guards against regressions in the agent's adherence.Acceptance criteria
lint-flow-patternscovers the revise-loop anti-pattern (unit-tested, like the existing [P2] Provide a declarative time-relative trigger (avoid fragile date-equality on record-change) #1874 rule).backedge style.revise+type:'back'shape.References
packages/cli/src/utils/lint-flow-patterns.ts— flow anti-pattern linter (guardrail home)packages/spec/src/studio/flow-builder.zod.ts— flow-builder protocol (edge styles)packages/spec/src/automation/flow.zod.ts—FlowEdgeSchema.type('back');registerFlowDAG-modulo-back-edges rulepackages/spec/src/automation/approval.zod.ts—APPROVAL_BRANCH_LABELS(revise),maxRevisionsexamples/app-showcase/src/flows/index.ts—showcase_budget_approval(canonical shape)