|
| 1 | +--- |
| 2 | +'@objectstack/spec': major |
| 3 | +--- |
| 4 | + |
| 5 | +Close nine authorable automation shapes against unknown keys (#4001 batch 11, ADR-0078) |
| 6 | + |
| 7 | +zod's default is `.strip`: a key a schema does not declare is silently discarded |
| 8 | +and the parse still succeeds. On an authoring surface that is the worst failure |
| 9 | +mode — the author (increasingly, an AI) gets a success envelope and ships |
| 10 | +metadata that quietly ignores what they wrote. This batch closes the nine |
| 11 | +remaining shapes in `automation/`'s main body. |
| 12 | + |
| 13 | +**BREAKING.** Each of these now raises a named, fixable error instead of dropping |
| 14 | +the key. The rejection carries the surface, the offending key, and — where the |
| 15 | +word is recognisable — the canonical spelling. |
| 16 | + |
| 17 | +**`flow.zod.ts` — the six NESTED blocks.** The four outer shapes (flow / node / |
| 18 | +edge / variable) were closed earlier; their inner blocks were not, so the gate |
| 19 | +rejected `nodee:` at node level while `connectorConfig: { connectorID }` — one |
| 20 | +capital letter — parsed clean and dispatched the action against an undefined |
| 21 | +connector id. Now strict: `FlowNode.connectorConfig`, `.position`, |
| 22 | +`.inputSchema` (each parameter declaration), `.waitEventConfig`, |
| 23 | +`.boundaryConfig`, and `Flow.errorHandling`. |
| 24 | + |
| 25 | +Renames the rejections offer, each one a real spelling of the same knob on a |
| 26 | +neighbouring surface in this repo: |
| 27 | + |
| 28 | +| you wrote | write instead | where the other word comes from | |
| 29 | +|---|---|---| |
| 30 | +| `connectorConfig.params` / `parameters` / `arguments` / `payload` | `input` | script-node `config.inputs`, integration products | |
| 31 | +| `waitEventConfig.event` / `signal` / `duration` / `delay` | `eventType` / `signalName` / `timerDuration` | — | |
| 32 | +| `boundaryConfig.attachedToRef` / `cancelActivity` | `attachedToNodeId` / `interrupting` | BPMN 2.0's own attribute names | |
| 33 | +| `errorHandling.backoffMs` | `retryDelayMs` | `shared/retry-policy.zod.ts` (#4661) | |
| 34 | +| `errorHandling.initialDelayMs` / `maxDelayMs` | `retryDelayMs` / `maxRetryDelayMs` | connector `RetryConfig` | |
| 35 | +| `errorHandling.retries` / `attempts` / `onError` | `maxRetries` / `strategy` | — | |
| 36 | + |
| 37 | +Two are prescriptions rather than renames, because a rename would be wrong: |
| 38 | +`inputSchema`'s `optional` is the opposite polarity of `required` (write |
| 39 | +`required: false`), and `errorHandling.maxAttempts` counts the first attempt |
| 40 | +while `maxRetries` counts the ones after it (write `maxRetries: maxAttempts - 1`). |
| 41 | + |
| 42 | +**Deliberately still open**, both now pinned in code and in tests so a later |
| 43 | +sweep stops rather than "finishing" the file: the flow node `config` slot |
| 44 | +(ADR-0018 — the plugin node-type namespace, owned by each executor's |
| 45 | +`configSchema`) and `FlowVersionHistorySchema` (emitted on publish, never |
| 46 | +authored; the flow *inside* a history record is still gated by `FlowSchema`). |
| 47 | + |
| 48 | +**`time-relative-trigger.zod.ts`.** `config.timeRelative` sits under the open |
| 49 | +node `config` slot, so this schema is the only key gate it has — and it is |
| 50 | +`safeParse`d at BIND time, not only at authoring. `{ …valid, offsetDay: 7 }` |
| 51 | +used to bind a sweep that ran daily with the author's narrowing discarded, and |
| 52 | +reported itself configured; it now refuses to bind and says why. `field` → |
| 53 | +`dateField`, `filters` → `filter`, `objectName` → `object`, `limit` → |
| 54 | +`maxRecords`; `schedule` and `runAs` get pointed at the layer that owns them. |
| 55 | + |
| 56 | +**`flow-function.zod.ts`.** `{ handler, effect }` in `defineStack({ functions })`. |
| 57 | +This binds at authoring only — the boot path reads entries with |
| 58 | +`normalizeFlowFunctionEntry`, not a parse — which is exactly why it matters: |
| 59 | +that reader takes two keys and ignores the rest by construction, so a misspelled |
| 60 | +`effect` was dropped at the schema and then not looked for. The function still |
| 61 | +registered, still ran, and its writes were still counted as none, keeping |
| 62 | +#4354's broken-sweep query silent on the one run that needed it. |
| 63 | + |
| 64 | +**`webhook.zod.ts`.** `object_name` → `object` and `active` → `isActive` (the |
| 65 | +`sys_webhook` column names, for anyone re-authoring from a row), `events` → |
| 66 | +`triggers`, `endpoint` → `url`; the five props #3494 removed now reject with |
| 67 | +their reason instead of vanishing. Strictness also rides `.extend()` onto the |
| 68 | +connector `WebhookConfigSchema`. |
| 69 | + |
| 70 | +**`webhook` also gains the ADR-0010 protection envelope** (`protection`, plus |
| 71 | +the loader-set `_lock` / `_lockReason` / `_lockSource` / `_provenance` / |
| 72 | +`_packageId` / `_packageVersion` / `_lockDocsUrl`). This is not a separate |
| 73 | +feature: both metadata load paths call `applyProtection` on every type, so a |
| 74 | +package-loaded webhook already carried those keys when `plugin-webhooks` |
| 75 | +re-parsed it at boot. Closing the shape without declaring them would have turned |
| 76 | +every package-shipped webhook into a skipped subscription after a redeploy. |
0 commit comments