Skip to content

Commit 086d6f1

Browse files
committed
fix(spec)!: IAutomationService.canonicalizeStoredFlow declares the todos the stored pass reads; the changeset is minor, Clause-② yes (widening)
The contract-tier review on the family PR found the consumer reading a member the published contract never promised. Declared as the optional sibling of notices and conflicts; StoredFlowCanonicalization restates it. Claude-Session: https://claude.ai/code/session_01BRQ9Mp1yH7R1az6MaUM18H Co-authored-by: Claude <noreply@anthropic.com>
1 parent 331fa99 commit 086d6f1

3 files changed

Lines changed: 25 additions & 9 deletions

File tree

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
---
2-
'@objectstack/spec': patch
3-
'@objectstack/service-automation': patch
4-
'@objectstack/metadata-protocol': patch
2+
'@objectstack/spec': minor
3+
'@objectstack/service-automation': minor
4+
'@objectstack/metadata-protocol': minor
55
---
66

77
fix: `os migrate meta --stored` lists every stored row that protocol 18 refuses and no conversion rewrites. Before, the preview counted each such row as already on protocol.
88

9-
Clause-②: no
9+
Clause-②: yes (widening: `IAutomationService.canonicalizeStoredFlow(...).todos?` in `@objectstack/spec`, `AutomationEngine.canonicalizeStoredFlow(...).todos` in `@objectstack/service-automation` and `StoredFlowCanonicalization.todos?` in `@objectstack/metadata-protocol`, each a new output member; nothing narrows, and no door accepts or refuses anything it did not before)
1010

1111
Three step-18 retirements refuse a stored shape that has no lossless rewrite. Upgrading a v17 database left those rows refused at load or at boot, and the stored preview read them as `canonical`. Each shape now has exactly one fate: **refused and listed**. The preview and `--apply` print one `TODO` line per site under its row. The line names the step-18 entry that refuses the shape, the site, and the choice the owner has to make. Nothing is rewritten, and the run's verdict does not change, because no run of the pass could clear these rows.
1212

@@ -16,4 +16,10 @@ Three step-18 retirements refuse a stored shape that has no lossless rewrite. Up
1616

1717
**#23271, a view whose `data` binds the retired view-level API source: refuse and list, by view (`view-data-api-provider-retired`).** There is no mechanical move. The replacement is a page component's `dataSource`, so a page must exist to carry it. Deleting the view's `data` would make the view query its object instead. Views in every stored body spelling are covered, and so are an object's own `listViews`. The line shows the request URL and method and withholds every other request key, so a credential stored in a view's headers never reaches the report. A view on the `object`, `value` or `schema` provider is not listed.
1818

19-
How it is wired: `applyConversions` reports these shapes through its existing `onTodo` sink, after the conversions run. Each TODO's `conversionId` is the id of the D3 entry that refuses the shape. `AutomationEngine.canonicalizeStoredFlow` now returns `todos` beside `notices` and `conflicts`, and the stored pass lists a flow row's TODOs the way it lists any other row's. The authoring funnel and the read seams pass no sink, so nothing changes for them.
19+
How it is wired: `applyConversions` reports these shapes through its existing `onTodo` sink, after the conversions run. Each TODO's `conversionId` is the id of the D3 entry that refuses the shape, so the published `ConversionTodoNotice.conversionId` now names either a conversion or a D3 entry. The published flow-canonicalization result gains one member, a widening in three packages:
20+
21+
- `@objectstack/spec`: the contract `IAutomationService.canonicalizeStoredFlow(...)` declares an optional `todos?: ConversionTodoNotice[]` beside `notices` and `conflicts`. An implementation that reports none still conforms.
22+
- `@objectstack/service-automation`: `AutomationEngine.canonicalizeStoredFlow(...)` returns `todos`. `registerFlow` ignores them, because its own refusal names the same sites.
23+
- `@objectstack/metadata-protocol`: `StoredFlowCanonicalization` gains the matching optional `todos?`, and the stored pass lists a flow row's TODOs the way it lists any other row's.
24+
25+
The authoring funnel and the read seams pass no sink, so nothing changes for them.

‎packages/metadata-protocol/src/stored-migration.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,9 @@ export interface StoredFlowCanonicalization {
6363
* — above all a value slot carrying a `{…}` template token, which
6464
* `registerFlow` refuses although the parse accepts the flow. Listed under
6565
* the row exactly as a non-flow row's TODOs are ({@link StoredMigrationTodo}).
66-
* Optional, so a caller-supplied canonicalizer that reports none still fits;
67-
* absent reads as none.
66+
* It restates the contract's own optional member,
67+
* `IAutomationService.canonicalizeStoredFlow(...).todos?` (`@objectstack/spec`):
68+
* a canonicalizer that reports none still conforms, and absent reads as none.
6869
*/
6970
todos?: Array<{
7071
conversionId: string;

‎packages/spec/src/contracts/automation-service.ts‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import type { FlowParsed } from '../automation/flow.zod';
1717
import type { ExecutionLog, ExecutionStatus, FlowRunSummary } from '../automation/execution.zod';
1818
import type { ActionDescriptor } from '../automation/node-executor.zod';
1919
import type { ConnectorDescriptor } from '../integration/connector-descriptor';
20-
import type { ConversionNotice, ConversionConflictNotice } from '../conversions/types';
20+
import type { ConversionNotice, ConversionConflictNotice, ConversionTodoNotice } from '../conversions/types';
2121
import type { ExecutionContext } from '../kernel/execution-context.zod';
2222

2323
/**
@@ -801,7 +801,8 @@ export interface IAutomationService {
801801
* `storable` (persistence shape — conversions plus the schema's
802802
* `condition` envelopes, deliberately WITHOUT schema defaults, so a
803803
* written-back row is not frozen on today's default values), plus the
804-
* conversions applied and any rewrite the guard refused.
804+
* conversions applied, any rewrite the guard refused, and (`todos`) the
805+
* sites left as stored for want of a lossless rewrite.
805806
* @throws when the definition cannot be canonicalized at all (a strict-schema
806807
* violation, a malformed control-flow region) — such a flow cannot be
807808
* registered either, so a caller reports it rather than persisting a guess.
@@ -811,6 +812,14 @@ export interface IAutomationService {
811812
storable: unknown;
812813
notices: ConversionNotice[];
813814
conflicts: ConversionConflictNotice[];
815+
/**
816+
* The sites the conversion chain left as stored because no lossless
817+
* rewrite exists — a value slot carrying a `{…}` token among them, which
818+
* {@link registerFlow} refuses although the parse accepts it — so
819+
* `os migrate meta --stored` lists a flow the boot refuses (#23269);
820+
* absent reads as none.
821+
*/
822+
todos?: ConversionTodoNotice[];
814823
};
815824

816825
/**

0 commit comments

Comments
 (0)