You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit c25d320
Browse filesBrowse the repository at this point in the historyBrowse files
`AutomationEngine.resume(runId)` called with no signal object is now held to the suspended `screen` node's declared field contract exactly like a signal-carrying resume: a run paused on a screen with an unconditional `required` field is refused with `INVALID_SCREEN_INPUT` and stays paused, instead of proceeding with that variable unbound. The bare `if (!signal) return null` early return in `refuseInvalidScreenInput` is gone — an absent signal is an empty submission, the same shape the HTTP resume route has always assembled for an empty body — and the engine's own continuations (subflow output mapping, `map` item handoff) remain exempt only through the existing engine-built-signal mechanism. Pauses that declare no input contract are unaffected: `wait` and `approval` nodes, message-only and object-form screens, and screens whose fields are all optional or hidden resume without a signal exactly as before.
B --> C["v3.4.0 — migration continues (minimum 2 minor releases)"]
80
-
C --> D["v3.5.0 — feature removed (earliest possible removal, MINOR)"]
78
+
A["Deprecation notice — feature marked deprecated (warning emitted)"] --> B["Migration period — deprecated feature keeps working, no behavior change"]
79
+
B --> C["Removal — retired at the boundary the ADR-0087 registry records (MINOR release, during the launch window)"]
81
80
```
82
81
83
82
<Callouttype="warn">
84
-
**Minimum guarantee:**Deprecated features survive for at least **2 MINOR releases** before they can be removed. During the launch window the removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive.
83
+
**No minimum survival window.**ObjectStack does not guarantee a deprecated feature survives for a fixed number of releases — [ADR-0049](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0049-no-unenforced-security-properties.md)'s dispositions for a flagged property are enforce, `experimental`, or remove, and none of them carries a dwell time. A deprecated feature is retired at the boundary the ADR-0087 registry records for it; during the launch window that removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive.
85
84
</Callout>
86
85
87
86
---
@@ -109,7 +108,7 @@ Read the **Breaking?** column, not the version number: during the launch window
109
108
110
109
### Breaking Change Process
111
110
112
-
1.**RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `breaking-change` label.
111
+
1.**RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `protocol:breaking` label.
113
112
2.**Deprecation** — The old behavior is deprecated in a MINOR release (see timeline above).
114
113
3.**Migration Guide** — A detailed migration guide is published before the removal lands, in the release notes for the version that carries it.
115
114
4.**Release** — During the launch window the breaking change ships in the next **MINOR** version, carrying a changeset entry marked `**BREAKING**`. `scripts/check-changeset-no-major.mjs` fails any pull request that declares a `major` bump, because under lockstep one `major` would promote all 69 published packages.
@@ -204,7 +203,7 @@ MAJOR releases do still happen — 17.0.0 was cut precisely because its breaking
204
203
205
204
### When it stops applying
206
205
207
-
The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes; the condition that closes the window is tracked separately and is deliberately not stated here. Until it closes, **this section is the operative rule wherever any part of this page disagrees.**
206
+
The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes: the window closes at GA, and strict SemVer resumes from that point. Until it closes, **this section is the operative rule wherever any part of this page disagrees.**
208
207
209
208
---
210
209
@@ -213,6 +212,6 @@ The tables and deprecation timeline above now state this rule directly, so the p
213
212
If you encounter an unintended breaking change:
214
213
215
214
1.**Check the CHANGELOG** — Verify the change was not documented as intentional.
216
-
2.**Open an issue** — File a GitHub issue with the `compatibility` label.
215
+
2.**Open an issue** — File a GitHub issue describing the unintended change.
217
216
3.**Include a reproduction** — Provide a minimal code sample showing the breakage.
218
217
4.**Reference the version** — Specify the exact versions where behavior changed.
|**filter**|`{ field: string; operator: Enum<'equals' \| 'not_equals' \| 'contains' \| 'not_contains' \| 'icontains' \| …>; value?: string \| number \| boolean \| null \| (string \| number)[] }[]`| optional | Filter rules narrowing the aggregate — the ViewFilterRule array form `[{ field, operator, value }, ...]`, the one filter orthography every `filter` input in this map shares. The MongoDB-style record form is refused — see migration `element-number-filter-rule-array`|
179
179
|**format**|`Enum<'number' \| 'currency' \| 'percent'>`| optional | Number display format |
180
180
|**prefix**|`string`| optional | Prefix text (e.g. "$") |
181
181
|**suffix**|`string`| optional | Suffix text (e.g. "%") |
|**value**|`string \| number \| boolean \| null \| (string \| number)[]`| optional | Filter value. The accepted SHAPE depends on the operator: `in` / `not_in` take an array (any length, including []), `between` takes exactly [min, max], every other operator takes a scalar. The unary operators (is_empty / is_not_empty / is_null / is_not_null) take their direction from the operator name and ignore this key. |
0 commit comments