Skip to content

Commit 4450fba

Browse files
committed
fix(examples): the showcase authors wait through the declared block (#4045)
Corrects a factual claim in the previous commit. It said "Nothing in-repo authors it" of the loose `config` back door — that is wrong. The showcase's own `wait_revision` node authored exactly that shape: config: { eventType: 'signal', signalName: 'budget_revision' } so the back door was not hypothetical, and the example that demonstrates `wait` was itself on the spelling this PR retires. It moves to `waitEventConfig`. The conversion already handled this shape correctly — the executor's behaviour is identical either way, and a new test pins the exact combination the showcase hit: the DECLARED key names sitting in the UNDECLARED location, which is what the candidate ordering has to get right (`signalName` before `signal`). It also asserts the converted flow still parses, since the lift creates the required block. Found while checking whether a stuck `objectstack verify` CI step could be caused by this change rather than by the runner — it could not, but the audit that ruled it out is what surfaced the false claim. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QoA8AV99Ss1RRkLLAYmDzq
1 parent 083c4cc commit 4450fba

4 files changed

Lines changed: 31 additions & 3 deletions

File tree

.changeset/wait-loose-config-graduation.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ The executor nevertheless also read six loose `config` keys behind `wec.X ?? loo
1414
two of them (`duration`, `signal`) spellings the spec never declared anywhere. That is
1515
the `notify.source` shape #4050 retired: a second de-facto contract announced only by a
1616
code comment, so an author who wrote it got a flow that worked forever and was never
17-
steered to the declared spelling (PD #12).
17+
steered to the declared spelling (PD #12). Not hypothetical: the showcase's own
18+
`wait_revision` node authored it (`config: { eventType: 'signal', signalName: … }`) and
19+
moves to the declared block here.
1820

1921
- New ADR-0087 D2 conversion `flow-node-wait-event-config-lift` lifts
2022
`config.{eventType,timerDuration,duration,timeoutMs,signalName,signal}` onto the

examples/app-showcase/src/automation/flows/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,10 @@ export const BudgetApprovalFlow = defineFlow({
206206
id: 'wait_revision',
207207
type: 'wait',
208208
label: 'Awaiting Revision',
209-
config: { eventType: 'signal', signalName: 'budget_revision' },
209+
// `waitEventConfig`, not a loose `config` — the latter is the undeclared
210+
// back door retired in #4045. The conversion layer still rewrites it at
211+
// load, but the showcase should demonstrate the declared spelling.
212+
waitEventConfig: { eventType: 'signal', signalName: 'budget_revision' },
210213
},
211214
{
212215
id: 'needs_exec',

packages/spec/src/conversions/conversions.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,24 @@ describe('conversion layer (ADR-0087 D2)', () => {
291291
expect(waitNodeOf(stack).config).toEqual({ duration: 'PT2H' });
292292
});
293293

294+
/**
295+
* The exact shape the showcase's `wait_revision` node carried before this
296+
* change — the declared spelling sitting in the undeclared LOCATION, which is
297+
* the combination the ledger's candidate order has to get right.
298+
*/
299+
it('lifts the showcase shape: declared key names in a loose `config`', () => {
300+
const { stack, notices } = collectConversionNotices(
301+
waitFlow({ config: { eventType: 'signal', signalName: 'budget_revision' } }),
302+
);
303+
expect(waitNodeOf(stack).waitEventConfig).toEqual({
304+
eventType: 'signal',
305+
signalName: 'budget_revision',
306+
});
307+
expect(waitNodeOf(stack).config).toEqual({});
308+
expect(notices).toHaveLength(2);
309+
expect(() => FlowSchema.parse((stack.flows as any[])[0])).not.toThrow();
310+
});
311+
294312
it('leaves a wait node with nothing to lift completely untouched', () => {
295313
const before = waitFlow({ waitEventConfig: { eventType: 'manual' }, config: { note: 'keep me' } });
296314
const { stack, notices } = collectConversionNotices(structuredClone(before));

packages/spec/src/conversions/registry.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1117,7 +1117,12 @@ const WAIT_EVENT_CONFIG_LIFTS: ReadonlyArray<readonly [target: string, candidate
11171117
* The executor nevertheless carried `wec.X ?? loose.X` for six `config` keys —
11181118
* a second, undeclared de-facto contract of exactly the `notify.source` shape
11191119
* (PD #12), announced only by the comment "for hand-authored flows that put the
1120-
* same keys under config". Nothing in-repo authors it.
1120+
* same keys under config".
1121+
*
1122+
* The showcase's `wait_revision` node authored exactly that shape
1123+
* (`config: { eventType: 'signal', signalName: 'budget_revision' }`) until this
1124+
* change moved it to the declared block — so the back door was not hypothetical,
1125+
* and the example that demonstrates `wait` was itself on the retiring spelling.
11211126
*
11221127
* Precedence mirrors those `??` chains, so the rewrite is behaviour-preserving:
11231128
* a value already on `waitEventConfig` WINS and its loose counterpart is left

0 commit comments

Comments
 (0)