Skip to content

Commit 21ecd56

Browse files
committed
fix(service-automation): get_record form descriptor's limit declares minimum 1
The contract refuses a get_record limit below 1 at every flow save door; the designer form descriptor offered 0 and negatives. Declare minimum: 1 and pin the descriptor's floor against the contract's refusal boundary. Claude-Session: https://claude.ai/code/session_016g7nYP9wdNx1GDa5XDr7Uy Co-authored-by: Claude <noreply@anthropic.com>
1 parent f0fdd45 commit 21ecd56

3 files changed

Lines changed: 32 additions & 1 deletion

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/service-automation': patch
3+
---
4+
5+
fix: the `get_record` designer form no longer offers a `limit` below 1
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The `get_record` node's `limit` is a positive integer, and every flow save door refuses `0`, a negative or a fraction. The executor's form descriptor still declared `limit` as a bare integer, so the designer form offered values the save always refused. The descriptor's `limit` now declares `minimum: 1`, the same floor as the contract. Nothing a flow can store changes; only the form stops offering what the save refuses.

‎packages/services/service-automation/src/builtin/builtin-node-form-zod-ledger.test.ts‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,24 @@ describe('CRUD / screen / map form ↔ Zod reconciliation (#4045)', () => {
136136
).toEqual([]);
137137
});
138138

139+
it('get_record.limit: the form\'s minimum is the contract\'s refusal boundary', () => {
140+
// The contract refuses a `limit` below 1 at every save door; a form whose
141+
// floor sits lower offers values the save always refuses. Pin both sides
142+
// at the boundary the descriptor declares, so neither moves alone.
143+
const limit = formSchema('get_record').properties?.limit as
144+
| { type?: string; minimum?: number }
145+
| undefined;
146+
expect(limit?.type).toBe('integer');
147+
expect(limit?.minimum).toBe(1);
148+
149+
const parse = (value: number) =>
150+
(GetRecordConfigSchema as unknown as { safeParse(v: unknown): { success: boolean } })
151+
.safeParse({ objectName: 'account', limit: value }).success;
152+
expect(parse(limit!.minimum!), 'the contract accepts the form\'s minimum').toBe(true);
153+
expect(parse(limit!.minimum! - 1), 'the contract refuses one below the form\'s minimum').toBe(false);
154+
expect(parse(0), 'the contract refuses 0').toBe(false);
155+
});
156+
139157
describe('assignment: pinned as un-reconcilable, with the reason on record', () => {
140158
// `assignment` deliberately has NO entry in builtin-node-config.zod.ts: with
141159
// no `assignments` wrapper, the TOP-LEVEL config keys ARE the author's

‎packages/services/service-automation/src/builtin/crud-nodes.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,10 @@ export function registerCrudNodes(engine: AutomationEngine, ctx: PluginContext):
477477
type: 'array', items: { type: 'string' }, title: 'Fields',
478478
description: 'Field projection — only these fields are read (default: all).',
479479
},
480-
limit: { type: 'integer', title: 'Limit' },
480+
// `minimum: 1` mirrors the contract: `GetRecordConfigSchema.limit`
481+
// is a positive integer, refused below 1 at every flow save
482+
// door, so the form never offers a value the save refuses.
483+
limit: { type: 'integer', minimum: 1, title: 'Limit' },
481484
outputVariable: { type: 'string', title: 'Output variable' },
482485
},
483486
required: ['objectName'],

0 commit comments

Comments
 (0)