Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/30279-time-relative-entry-surface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@objectstack/spec': patch
---

fix(spec): the `flow-record-read-and-decision-type-mismatch-refused` upgrade-guide entry names the object a time-relative flow's conditions are typed against: the swept `config.timeRelative.object`, when the start node names no `objectName`, and the start node's `objectName` when that is the swept object (#30279)

Clause-②: no
Metadata-protocol: none

Text only: the entry's `surface` and the matching sentence of its `reason`, which `os migrate meta` and the upgrade guide show, said the refusal covers a time-relative flow "sweeping the start node's objectName". The author-time gate also types such a flow against `config.timeRelative.object` when its start names no `objectName`, and refuses the same conditions there; the entry now says so. Its id, kind, replacement and acceptance criteria are unchanged, and no verdict moves.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ export const entry: SemanticMigration = {
+ 'config.condition, edges[].condition or a decision branch, where FIELD is a text or boolean field of '
+ 'the trigger object ordered or arithmetic\'d against a number (record.name > 5, title > 5 in a '
+ 'decision branch, record.active > 0); and the same conditions, bare name included, of a time-relative '
+ 'flow (start config.timeRelative) sweeping the start node\'s objectName',
+ 'flow (start config.timeRelative) sweeping config.timeRelative.object, typed against that object when '
+ 'the start node names no objectName and against the start node\'s objectName when that is the swept '
+ 'object',
replacement:
'Compare like with like. size(record.name) > 5 for the text\'s length; a number field '
+ '(record.score > 5) for a value; double(record.name) > 5 only when every value is numeric text, '
Expand All @@ -23,8 +25,12 @@ export const entry: SemanticMigration = {
+ 'run fails at that condition while the record-change trigger isolates the write that fired it. The '
+ 'publish gate refused the bare name in a start or edge condition already, but passed the record.FIELD '
+ 'read and every decision branch with no verdict at all. It now refuses them with the same run-fails '
+ 'wording. A time-relative sweep launches each run with the swept row as its record, the same total '
+ 'row of the same object, so its conditions get the same refusal where 18.3.0 warned. A bare name a '
+ 'wording. A time-relative sweep launches each run with a row of the object it sweeps '
+ '(config.timeRelative.object) as its record, the same total row of one object, so its conditions are '
+ 'typed against that object when the start node names no objectName, and against the start node\'s '
+ 'objectName when that is the swept object, and get the same refusal: where 18.3.0 warned on a sweep of '
+ 'the start node\'s objectName, and where a start that names no objectName got no verdict at all. '
+ 'A bare name a '
+ 'declared flow variable or node id holds is the variable, not the field (the run seeds those before '
+ 'it flattens the record), so it is not typed as the field. Any other flow keeps a warning, now worded for the failed run instead '
+ 'of a null. There is no mechanical rewrite: whether the author meant the length, a number stored as '
Expand Down
14 changes: 14 additions & 0 deletions packages/spec/src/migrations/migrations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -802,3 +802,17 @@ describe('per-release section (ADR-0087 D4, package-version resolution)', () =>
expect(Object.keys(release.added[0])).not.toContain('since');
});
});

describe('flow-record-read-and-decision-type-mismatch-refused names the object a time-relative flow is typed against', () => {
// The author-time gate types a time-relative flow's conditions against
// config.timeRelative.object when the start node names no objectName
// (packages/lint validate-expressions.ts, the flow walk), so the entry's
// surface must name that key, not only the start node's objectName.
const entry = () =>
MIGRATIONS_BY_MAJOR[18]!.semantic.find((s) => s.id === 'flow-record-read-and-decision-type-mismatch-refused');

it('the surface names config.timeRelative.object as the swept object', () => {
expect(entry()).toBeDefined();
expect(entry()!.surface).toMatch(/sweeping config\.timeRelative\.object/);
});
});
Loading