Skip to content

Commit eccc154

Browse files
committed
perf(service-automation): the registration listing runs only for a caller that reads todos
The save door, a package duplication and the sync's held-flow comparison read parsed or storable only, so they pay nothing for the stored preview's listing. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BRQ9Mp1yH7R1az6MaUM18H
1 parent 377dc37 commit eccc154

2 files changed

Lines changed: 48 additions & 15 deletions

File tree

‎packages/services/service-automation/src/canonicalize-stored-flow.test.ts‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,20 @@ describe('canonicalizeStoredFlow — every site registerFlow refuses after the p
350350
expect(result.todos).toHaveLength(1);
351351
});
352352

353+
it('runs the checks only for a caller that reads `todos`, and once however often it is read', () => {
354+
const engine = engineWithStamp();
355+
const checks = vi.spyOn(engine as any, 'registrationCheckRefusals');
356+
const result = engine.canonicalizeStoredFlow('fd_stored', flowOf([notify('Hi {record.name}')]));
357+
// What the save door, a duplication and the sync's held-flow comparison read.
358+
void result.parsed;
359+
void result.storable;
360+
void result.conflicts;
361+
expect(checks).not.toHaveBeenCalled();
362+
expect(result.todos).toHaveLength(1);
363+
expect(result.todos).toBe(result.todos);
364+
expect(checks).toHaveBeenCalledTimes(1);
365+
});
366+
353367
it('every site of a flow refused by several checks is listed, in the order registerFlow judges them', () => {
354368
const flow = flowOf([
355369
{ id: 'w', type: STAMP, label: 'W', config: { count: 1, countz: 2 } },

‎packages/services/service-automation/src/engine.ts‎

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4465,9 +4465,10 @@ export class AutomationEngine implements IAutomationService {
44654465
* stored pass reports that row `failed` with the parse's words.
44664466
*
44674467
* None of it changes `parsed` or `storable`, and nothing here throws for
4468-
* it, so a caller that reads no `todos` (the save door, a package
4469-
* duplication) behaves as before. `registerFlow` reads none either: it
4470-
* runs the same checks itself and throws the first refusal.
4468+
* it. The checks run on the first read of `todos`, so a caller that reads
4469+
* none (the save door, a package duplication, the sync's comparison of a
4470+
* held flow) does no more work than before. `registerFlow` reads none
4471+
* either: it runs the same checks itself and throws the first refusal.
44714472
*/
44724473
canonicalizeStoredFlow(name: string, definition: unknown): {
44734474
parsed: FlowParsed;
@@ -4476,19 +4477,37 @@ export class AutomationEngine implements IAutomationService {
44764477
conflicts: ConversionConflictNotice[];
44774478
todos: ConversionTodoNotice[];
44784479
} {
4479-
const canonical = this.canonicalizeFlow(name, definition);
4480-
// One listing per site: a site a conversion TODO names (the value-slot
4481-
// refusal the spec lists under its D3 entry) keeps that TODO, which
4482-
// names the retirement; the registration check's line for it is the
4483-
// same refusal and is not repeated.
4484-
const listed = new Set(canonical.todos.map((todo) => todo.path));
4485-
for (const refusal of this.registrationCheckRefusals(name, canonical.parsed)) {
4486-
for (const site of refusal.sites) {
4487-
if (listed.has(site.path)) continue;
4488-
canonical.todos.push(flowRegistrationTodo(name, site));
4480+
const { parsed, storable, notices, conflicts, todos: chainTodos } = this.canonicalizeFlow(name, definition);
4481+
// The registration checks run when `todos` is first READ, once: the
4482+
// stored pass reads it, and the doors that only want `parsed` or
4483+
// `storable` (the save door, a package duplication, the sync's
4484+
// "already held?" comparison) pay nothing for a listing they discard.
4485+
const listRegistrationRefusals = (): ConversionTodoNotice[] => {
4486+
const todos = [...chainTodos];
4487+
// One listing per site: a site a conversion TODO names (the value-slot
4488+
// refusal the spec lists under its D3 entry) keeps that TODO, which
4489+
// names the retirement; the registration check's line for it is the
4490+
// same refusal and is not repeated.
4491+
const listed = new Set(chainTodos.map((todo) => todo.path));
4492+
for (const refusal of this.registrationCheckRefusals(name, parsed)) {
4493+
for (const site of refusal.sites) {
4494+
if (listed.has(site.path)) continue;
4495+
todos.push(flowRegistrationTodo(name, site));
4496+
}
44894497
}
4490-
}
4491-
return canonical;
4498+
return todos;
4499+
};
4500+
let listedTodos: ConversionTodoNotice[] | undefined;
4501+
return {
4502+
parsed,
4503+
storable,
4504+
notices,
4505+
conflicts,
4506+
get todos(): ConversionTodoNotice[] {
4507+
listedTodos ??= listRegistrationRefusals();
4508+
return listedTodos;
4509+
},
4510+
};
44924511
}
44934512

44944513
/**

0 commit comments

Comments
 (0)