test(showcase): add the loop { parallel } fixture that unblocks the flow-run-step-nesting clause - #17514
Conversation
WIP: the `defineFlow` and its `allFlows` entry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ToDPcx9AESFubJkDiFMtKW
Adds the run pin over `showcase_nested_fan_out_reminders` and records the fixture in the platform checklist item it unblocks (revision 4). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ToDPcx9AESFubJkDiFMtKW
…owcase-loop-parallel
|
ACCEPT — 2026-09-10T19:11Z, head Landing pre-checks, each measured:
What this seat checked beyond green:
Flipped ready and armed. ⛔ Not approved and ⛔ not merged by this seat; the merge queue lands it. Generated by Claude Code |
Fixes #16356
Clause-②: no
What this adds
examples/app-showcasedeclared each ADR-0031 structured construct as its own top-level flow and nested none inside another. Because of that, theloop { parallel }acceptance clause onautomation.flow-run-step-nesting(item revision 3,docs/qa/platform-checklist/areas/automation.json) could only ever scoreblocked(fixture)— there was no flow to trigger.This adds
showcase_nested_fan_out_reminders(NestedFanOutRemindersFlow): an autolaunched flow whoseloopbody holds exactly one node, a two-branchparallelthat notifies the row's owner and its watcher concurrently. Rows are task-shaped{id, title, owner, watcher}and the clause is driven over at least two of them.Why that exact shape, so nobody simplifies it away. The maintainer ruling of 2026-09-03 made
ExecutionStepLog.iterationsingle-valued (the enclosing loop's row, carried through nesting) and gave the parallel branch position its ownbranchkey. A step inside a parallel branch that is itself inside a loop body is the ONLY shape where both index keys are populated on one record. One row, or one branch, and the clause cannot discriminate. There is deliberately notry_catchbetween the loop and the parallel: a containment region retags the leaf stepstry/catchand the branch position stops being readable off them — per-iteration containment stays demonstrated byBatchRemindersFlow.The clause is unblocked — driven on its own oracle
The clause's oracle is
api: trigger the flow, then read the run detail. Driven on a real isolated showcase boot (pnpm dev -- --fresh), signed in as the dev admin,POST /api/v1/automation/showcase_nested_fan_out_reminders/triggerwith two rows, thenGET .../runs/:runId:All three things the clause's
verifyasks for hold: (a) everyparallel-branchstep carries abranchequal to its position,branch: 0included; (b) all four(iteration, branch)pairs appear exactly once across the two rows; (c) the enclosingparallelcontainer step readsregionKind: 'loop-body'with the row oniterationand nobranchof its own. That run was scoredblocked(fixture)before this PR and is scoreable now.RED before GREEN
examples/app-showcase/test/nested-fan-out-region-indices.test.tspins the same three assertions by RUNNING the real flow (read out ofsrc/automation/flows/index.ts, not a copy) on a realAutomationEnginewith the real built-in executors, and cross-checks every recorded step underExecutionStepLogSchema. Two ablations, each mutating the committed file, proving the mutation reached disk by blob hash, and restoring by state:fan_out_audiences1 to 07213ede11tofa58e3860VERDICT command-exit 1;AssertionError: expected [] to have a length of 4 but got +07213ede11,git diff HEADempty,git status --porcelainemptynotify_watcher1 to 07213ede11to3849e46f6VERDICT command-exit 1; refused at registration:parallel 'fan_out_audiences': a parallel block needs at least 2 branchesDirection reported as observed, not as predicted: leg 2 was predicted to fail on the pair list and instead fails earlier, in
validateControlFlow— the spec refuses a one-branch parallel outright. Leg 1 is the one that proves the pin measures the NESTING.Premise check: the card's cost estimate
The card claimed "No gate script enumerates showcase flows by path, so the cost looks like the
defineFlowplus its array entry." Triage flagged it unverified; 25 tracked files underscripts/and.github/workflows/mentionapp-showcase. Measured: the estimate holds for gates, and understates the checklist half.pnpm check:i18n-coverage— no obligation. MEASURED, with a firing control. Every flow does emitflows.FLOWNAME.labelinto the expected-translation set (walkScreenFlows,packages/cli/src/utils/i18n-extract.ts), but theflowsfamily isplanned+authorWarnin the liveness ledger and is not counted. Gate green with the new flow:OK (13 config(s), 621 baselined untranslated string(s), none new)— showcase still at its baselined 414. Control: dropping one translatedzh-CNfield label fromShowcaseTranslationBundle(blob87519c6f6to2d195d431, restored to87519c6f6withgit diff HEADempty) turns it RED — "untranslated declared strings grew 414 to 415". So the zero is a measurement, not blindness..github/workflows/showcase-smoke.yml— does not execute it. Nightly +workflow_dispatch, explicitly NON-BLOCKING and never a PR gate; it drives console nav surfaces for render health, and enumerates no flow.allFlowsis consumed by showcase's own suite (coverage.test.tsnode-type coverage,bulk-write-transition-flows,predicate-write-bulk-intent,connector-ping-run-output) — all green, and none is a count or path enumeration.fixtures.requiresrecorded "NOT PRESENT in examples/app-showcase … until such a flow lands (filed as app-showcase nests no control-flow construct inside another, so the QA checklist's newloop { parallel }clause can only score blocked(fixture) #16356)". Landing the flow without rewriting that leaves a QA ledger asserting the fixture is absent — a runner reading it still scoresblocked(fixture), so the code change alone would not have unblocked the clause.Out of the declared write surface:
docs/qa/platform-checklist/areas/automation.jsonForced, not chosen, for the reason directly above: the deliverable is a clause that can score something other than
blocked(fixture), and that verdict is read out of this file. Three edits, each obliged byscripts/check-platform-checklist.mjsor by the runner:fixtures.requires— replaces the "NOT PRESENT" record with the flow that closes it, plus the row shape and the two-row minimum a runner needs.steps— the nesting-run step said "needs the fixture above"; it now names the trigger call and the row shape.revision3 to 4 plus ahistoryentry — the gate fails whenrevisiondoes not match the lasthistoryentry, so a semantic edit cannot be made without it. Asourcesymbol anchor for the new flow is added alongside; the gate resolves it (632/632 anchors, 17 file floors held).Clause-②, both limbs measured
validateControlFlow.)NestedFanOutRemindersFlowis a new exported symbol, so the question is whether that module is a published surface.examples/app-showcase/package.jsondeclares"private": trueand nofilesfield, and itsexportsmap does not includesrc/automation/flows/index.tsat all. Empirically, over the builtdist/trees of the repo's packages:NestedFanOutRemindersFlow0 hits,showcase_nested_fan_out_reminders0 hits, positive controlExecutionStepLogSchema14 hits. Firing control on the manifest read:packages/services/service-analytics/package.jsoncarries noprivatefield, so theprivate: truereading discriminates.Same measurement is why this PR carries
skip-changesetand no.changeset/*.md: nothing publishable moves.Verification
Everything below on head
86d90dbb9, after mergingorigin/main. Heavy runs went throughscripts/pm/os-verify-lock.sh; the lock's ownVERDICT command-exitline is quoted.pnpm turbo run build --filter=@objectstack/example-showcase^... --concurrency=2VERDICT command-exit 0· 61/61 taskspnpm --filter @objectstack/example-showcase test && ... typecheckVERDICT command-exit 0· 29 files, 381 tests passedpnpm lint(whole repo,eslint . --no-inline-config)VERDICT command-exit 0— full population, no narrowing claimednode scripts/pm/dispatch-gates.mjs --commands)pnpm check:dual-build-cjs-loadsdist/; needs a whole-repopnpm build, whichBuild Coredoes--ranreconciliationAcceptance notes
loop { try_catch }— the wider gap the card's last section asks about — is already closed onmainand was not when the card was filed. PR fix(showcase): contain the batch-reminders loop body per iteration #16355 ("contain the batch-reminders loop body per iteration") landed it:BatchRemindersFlow's loop body now holds atry_catchwhosecatchis a bareassignment. Noted, not filed; reported to the PM so the seat decides whether the follow-up card triage asked for is still wanted. Nothing about it is implemented here.automation.flow-node-type-matrix'sfixtures.requiresstill readsloop=showcase_batch_reminders · parallel+http=showcase_fan_out_notify · try_catch=showcase_resilient_sync. That map is per node TYPE and stays accurate — this flow adds no new node type. Noted, not filed.0f3914a1bandca9db83f5carry a model-namedCo-Authored-Bytrailer, written from a dispatch instruction that was corrected mid-task; per AGENTS.md landed history is not rewritten, so they stand and are reported rather than amended. Later commits carry the model-free pair.Authored in session
session_01ToDPcx9AESFubJkDiFMtKW.Generated by Claude Code