Skip to content

Commit 496e706

Browse files
chore: version packages (rc)
1 parent 8bd437f commit 496e706

153 files changed

Lines changed: 20975 additions & 84 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/pre.json

Lines changed: 183 additions & 1 deletion
Large diffs are not rendered by default.

examples/app-crm/CHANGELOG.md

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,265 @@
11
# @objectstack/example-crm
22

3+
## 4.0.92-rc.2
4+
5+
### Patch Changes
6+
7+
- e533b0b: feat(spec)!: retire `datasource.capabilities` — eleven flags nothing read, one of them a safety claim (#4583)
8+
9+
`DatasourceCapabilities` declared eleven booleans — `transactions`, seven `query*`
10+
flags, `joins`, `fullTextSearch`, `readOnly`, `dynamicSchema` — all strict-guarded,
11+
all read by nothing. Pushdown is decided by the runtime driver's own `supports.*`
12+
object, a different mechanism entirely, so a datasource declaring
13+
`queryAggregations: false` never once changed which engine path ran. The block is
14+
removed rather than bridged: there was nothing on the other side to connect it to.
15+
16+
**`readOnly` is why this is not tidy-up.** It reads as a safety property and was
17+
authored as one — the shipped CRM example labelled a datasource "CRM Analytics Read
18+
Replica" on the strength of it, while the datasource accepted writes exactly like the
19+
primary. The key had already been MOVED twice toward somewhere it might be enforced,
20+
out of `config` in #4410 and into `capabilities` in #4465, and was inert at every
21+
address. This removes it instead of moving it a third time.
22+
23+
**Removing it does not hand you a working replacement, and the rejection says so.**
24+
The one enforced datasource-wide write gate is `external.allowWrites: false`, and it
25+
applies only to a FEDERATED datasource — `assertWriteAllowed` returns early for a
26+
`managed` (or unset-`schemaMode`) datasource, so that key would be equally inert for a
27+
local database. **A managed datasource has no read-only gate at all**; that gap is
28+
#4584, deliberately not invented here. Until it is answered, enforce read-only where
29+
it is real: grant the connection SELECT-only at the database.
30+
31+
FROM → TO:
32+
33+
```ts
34+
// before — parsed cleanly, changed nothing
35+
defineDatasource({
36+
name: 'analytics', driver: 'sqlite', config: { filename: ':memory:' },
37+
capabilities: { readOnly: true, queryAggregations: true },
38+
})
39+
40+
// after — delete the block; for a FEDERATED datasource the enforced gate is:
41+
defineDatasource({
42+
name: 'warehouse', driver: 'postgres', config: { … },
43+
schemaMode: 'external',
44+
external: { allowWrites: false },
45+
})
46+
```
47+
48+
`os migrate meta --from 16` rewrites it automatically (ADR-0087 conversion
49+
`datasource-capabilities-removed`). Both `DatasourceSchema` and
50+
`DriverDefinitionSchema` are `.strict()`, so a leftover key is a loud rejection
51+
carrying the prescription — never a silent strip.
52+
53+
Also fixed: `READ_ONLY_BELONGS_ON_DATASOURCE`, the prescription every SQL driver
54+
shares for a `readOnly` written inside `config`, was still sending authors _to_ the
55+
removed key. It now names the enforced gate and states plainly where that gate does
56+
not apply — a prescription that lands on an inert key manufactures exactly the belief
57+
it was meant to correct.
58+
59+
The `datasource` liveness ledger drops from 20 dead properties to 9 (remaining:
60+
`healthCheck` ×3, `retryPolicy` ×4, `external` ×2 — batches B/C/D of #4583).
61+
62+
- 5293114: fix(automation): a decision's three declared ways to route a branch are now one working model (#4414)
63+
64+
A `decision` node advertised three mechanisms for splitting a path and only one
65+
of them did anything. The other two were the ADR-0049 `declared ≠ enforced`
66+
shape, and the pair of them shipped a guard that does not guard in
67+
`examples/app-crm`.
68+
69+
| mechanism | before | now |
70+
| :--------------------------------------------------- | :----------------------------------------------------------------------------------------------------- | :---------------------------------------------------- |
71+
| `edge.condition` | ✅ the only one that worked | unchanged |
72+
| `edge.isDefault` | **zero readers** anywhere but the schema declaration | BPMN default flow, enforced in `traverseNext` |
73+
| `decision.config.conditions[].label``branchLabel` | matched **0** out-edge labels across every example app, then fell back to the full edge set in silence | routes; an unclaimable label is logged, not swallowed |
74+
75+
## What was broken, end to end
76+
77+
`crm_convert_lead_wizard` means "already converted → abort screen; otherwise →
78+
the wizard". It ran **both**: an already-converted lead got
79+
"This lead has already been converted" and then walked straight into the
80+
conversion wizard behind it. Four independent silences stacked up:
81+
82+
1. the decision's first condition was authored `{lead_record.status} ==
83+
'converted'` — braces in a slot declared bare CEL, so it was string-compared
84+
and never true;
85+
2. the second (`'true'`) therefore won, yielding `branchLabel: 'No — proceed'`;
86+
3. no out-edge carried that label (they were `'Yes'` / `'No'`), so traversal
87+
discarded the branch and considered every out-edge;
88+
4. `e3b` was unconditional, so it ran regardless — and the natural fix, marking
89+
it `isDefault: true`, was a dead key.
90+
91+
## The model
92+
93+
`branchLabel` narrows the edge set → `condition` gates each edge → `isDefault`
94+
catches whatever is left. Concretely:
95+
96+
- **`isDefault` is enforced.** A default edge is traversed only when no
97+
conditional sibling of the same source node matched, and it is no longer part
98+
of the unconditional parallel fan-out — that distinction is the whole point of
99+
the marker. Passed over because a real branch won, its target records the same
100+
`skipped` step a closed gate does (#4354).
101+
- **An unclaimable branch label warns.** Traversal still falls back to the full
102+
edge set (a run mid-flight must not die on a metadata error) but says so,
103+
naming the computed branch and the out-edge labels that exist.
104+
- **A decision that declares no `conditions` reports no branch.** It used to
105+
report `'default'` unconditionally — a label no out-edge in the repo ever
106+
carried — which is why every decision node fell back to the full edge set.
107+
The `'default'` sentinel survives for the case it actually describes (declared
108+
conditions, none matched) and is now claimed by the `isDefault` edge as well
109+
as by an edge literally labelled `'default'`.
110+
- **`conditions[].expression` is evaluated as the bare CEL it is declared to
111+
be.** The raw string went to the legacy `{var}` template path, where
112+
`lead.status == 'converted'` cannot resolve and the branch is decided by
113+
string comparison. Unlike `edge.condition` this slot carries no
114+
`ExpressionInput` envelope — the decision descriptor is deliberately
115+
schemaless — so the executor supplies the dialect. A brace-in-CEL predicate
116+
now fails loudly (ADR-0032 §1c) instead of deciding `false`.
117+
118+
## Caught at authoring time too
119+
120+
Four new `os build` / `os validate` warnings, because a wrong route is silent at
121+
run time by nature (Prime Directive #12):
122+
123+
`flow-branch-label-unmatched` (the shipped shape),
124+
`flow-decision-unconditional-branch` (a guarded decision with an unconditional
125+
sibling — the actual hole), `flow-default-edge-with-condition` and
126+
`flow-multiple-default-edges`.
127+
128+
Both of the first two fire on the pre-fix `convert-lead.flow.ts` and are silent
129+
after it.
130+
131+
## Effect on flows that already exist
132+
133+
Enforcing `isDefault` changes how a **stored** flow behaves, and the flows it
134+
changes are mostly Studio's own. `objectui`'s flow edge inspector has always
135+
written `isDefault: true` when you bind an out-edge to a decision's default/else
136+
branch — into a key with zero readers, so that edge ran unconditionally, in
137+
parallel with whichever branch actually matched. Those flows now take exactly
138+
one branch. That is the fix, but it is a behaviour change on existing data
139+
rather than only on newly authored metadata, so it is worth knowing before
140+
upgrading: a flow that quietly ran two paths will now run one.
141+
142+
Nothing changes for an edge that never carried the marker — `isDefault` defaults
143+
to `false`, and an ordinary unconditional out-edge still fans out in parallel
144+
exactly as before.
145+
146+
## The example app
147+
148+
`crm_convert_lead_wizard`'s guard is now a plain exclusive gateway: the
149+
redundant `config.conditions` is gone and `e3b` carries `isDefault: true`. One
150+
mechanism per decision, and exactly one branch runs.
151+
152+
Verified: 11 new engine/executor tests (including the reported repro in both
153+
directions), 12 new linter tests; `@objectstack/service-automation` 577 tests
154+
and `@objectstack/cli` 652 tests green, all three example apps build with no new
155+
findings.
156+
157+
- Updated dependencies [430dcc2]
158+
- Updated dependencies [e6ac4bd]
159+
- Updated dependencies [80334c7]
160+
- Updated dependencies [ce5242c]
161+
- Updated dependencies [a7163ea]
162+
- Updated dependencies [e6e9379]
163+
- Updated dependencies [98877c9]
164+
- Updated dependencies [98877c9]
165+
- Updated dependencies [e6b1b69]
166+
- Updated dependencies [7e7a605]
167+
- Updated dependencies [ad047d2]
168+
- Updated dependencies [2826d1e]
169+
- Updated dependencies [5a84d41]
170+
- Updated dependencies [20b1a9e]
171+
- Updated dependencies [203a449]
172+
- Updated dependencies [4820f55]
173+
- Updated dependencies [462d9c4]
174+
- Updated dependencies [7d21581]
175+
- Updated dependencies [f2445c9]
176+
- Updated dependencies [23338c3]
177+
- Updated dependencies [5b843fb]
178+
- Updated dependencies [b4487aa]
179+
- Updated dependencies [65ca83a]
180+
- Updated dependencies [67bf2e2]
181+
- Updated dependencies [c6d1cb4]
182+
- Updated dependencies [36030ff]
183+
- Updated dependencies [6117f7b]
184+
- Updated dependencies [e533b0b]
185+
- Updated dependencies [cdf4d9a]
186+
- Updated dependencies [aee1806]
187+
- Updated dependencies [c13350b]
188+
- Updated dependencies [c13350b]
189+
- Updated dependencies [63b33e6]
190+
- Updated dependencies [9ca2d85]
191+
- Updated dependencies [c13350b]
192+
- Updated dependencies [891d345]
193+
- Updated dependencies [a52e2ef]
194+
- Updated dependencies [5293114]
195+
- Updated dependencies [ff17642]
196+
- Updated dependencies [20bc357]
197+
- Updated dependencies [5966c2a]
198+
- Updated dependencies [2382580]
199+
- Updated dependencies [d9fa683]
200+
- Updated dependencies [3c7bcc0]
201+
- Updated dependencies [4b6cac7]
202+
- Updated dependencies [7631964]
203+
- Updated dependencies [ac471a0]
204+
- Updated dependencies [60ae58e]
205+
- Updated dependencies [ce92674]
206+
- Updated dependencies [9f601e8]
207+
- Updated dependencies [51c5227]
208+
- Updated dependencies [a4a85c8]
209+
- Updated dependencies [07a4e26]
210+
- Updated dependencies [ec975f1]
211+
- Updated dependencies [eb4204b]
212+
- Updated dependencies [4f13be2]
213+
- Updated dependencies [d52d4fe]
214+
- Updated dependencies [742cebb]
215+
- Updated dependencies [ce92674]
216+
- Updated dependencies [cf2c9b7]
217+
- Updated dependencies [0f9faa2]
218+
- Updated dependencies [7cf42fe]
219+
- Updated dependencies [5966c2a]
220+
- Updated dependencies [8aacf94]
221+
- Updated dependencies [f78dd83]
222+
- Updated dependencies [a2cd18a]
223+
- Updated dependencies [4638aaa]
224+
- Updated dependencies [0222d3c]
225+
- Updated dependencies [071d0dc]
226+
- Updated dependencies [0a936ea]
227+
- Updated dependencies [155507e]
228+
- Updated dependencies [7bba90b]
229+
- Updated dependencies [7e05d8e]
230+
- Updated dependencies [061406d]
231+
- Updated dependencies [c1f344b]
232+
- Updated dependencies [9c93465]
233+
- Updated dependencies [ebb209c]
234+
- Updated dependencies [63b33e6]
235+
- Updated dependencies [2a44c1d]
236+
- Updated dependencies [695cfbd]
237+
- Updated dependencies [071d0dc]
238+
- Updated dependencies [0848bea]
239+
- Updated dependencies [b8b3c64]
240+
- Updated dependencies [0c0fbd9]
241+
- Updated dependencies [f3141d8]
242+
- Updated dependencies [5a84d41]
243+
- Updated dependencies [fd3013a]
244+
- Updated dependencies [21676eb]
245+
- Updated dependencies [e336549]
246+
- Updated dependencies [e5e7ee0]
247+
- Updated dependencies [800bdb0]
248+
- Updated dependencies [04f1182]
249+
- Updated dependencies [5647006]
250+
- Updated dependencies [38f7e4f]
251+
- Updated dependencies [c57f3cf]
252+
- Updated dependencies [97faca3]
253+
- Updated dependencies [ad5fe25]
254+
- Updated dependencies [ea90179]
255+
- Updated dependencies [ce92674]
256+
- Updated dependencies [5ef0b5b]
257+
- Updated dependencies [48fbacb]
258+
- Updated dependencies [355e951]
259+
- Updated dependencies [dadb43f]
260+
- @objectstack/runtime@17.0.0-rc.2
261+
- @objectstack/spec@17.0.0-rc.2
262+
3263
## 4.0.92-rc.1
4264

5265
### Patch Changes

examples/app-crm/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@objectstack/example-crm",
3-
"version": "4.0.92-rc.1",
3+
"version": "4.0.92-rc.2",
44
"description": "Minimal CRM example — a smoke-test workspace that exercises the metadata loading pipeline (objects → views → app → dashboard → hook → flow → seed). For a full-featured enterprise CRM see https://github.com/objectstack-ai/hotcrm.",
55
"license": "Apache-2.0",
66
"private": true,

0 commit comments

Comments
 (0)