Skip to content

Commit 3b90fd6

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/row-crud-cel-predicates-uzb7pz
2 parents be1f07c + 3fe9df1 commit 3b90fd6

159 files changed

Lines changed: 2659 additions & 475 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.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
Security (#2991): the AI `ToolExecutionContext` contract no longer documents system-level execution as the missing-actor default. A missing `toolExecutionContext` / `actor` now means an unauthenticated (RLS-on, sees-nothing) principal — executors MUST fail closed to anonymous, never fall open to system. System execution becomes an explicit, greppable opt-in via the new `ToolExecutionContext.isSystem?: boolean` field (same convention as `IDataEngine` / `IKnowledgeService`), reserved for trusted server-side invocations and ignored when an `actor` is present. Migration for internal callers that relied on the old omission default (cron, migrations, server jobs): pass `toolExecutionContext: { isSystem: true }` explicitly.

.changeset/cors-allow-if-match.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@objectstack/plugin-hono-server': patch
3+
'@objectstack/hono': patch
4+
---
5+
6+
CORS default `allowHeaders` now includes `If-Match`. The REST record update
7+
accepts the OCC token as an `If-Match` header (objectui's record-level inline
8+
edit sends it on every save), but the preflight allow-list omitted it — so on
9+
any split-origin deployment (console dev server against a backend on another
10+
origin) the browser failed the preflight and every inline-edit save died with
11+
"Failed to fetch". Found live while dogfooding objectui#2572; same
12+
split-origin failure class as the #2548 Bearer fixes. Explicit user-supplied
13+
`allowHeaders` still win unchanged.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
'@objectstack/rest': patch
3+
---
4+
5+
fix(import): make async import-job cancellation actually stop the worker (#2824)
6+
7+
Cancelling a running async import used to have no effect on a synchronous
8+
storage driver (better-sqlite3 / wasm fallback): every `await` in the row
9+
loop resolved as a microtask, so a 50k-row import monopolized the Node event
10+
loop for minutes — the cancel route's HTTP handler (and every progress poll)
11+
could never run, so the in-memory flag `shouldCancel` polls was never set.
12+
The job then finished `succeeded` with all rows written despite the user's
13+
cancel.
14+
15+
Three-part fix:
16+
17+
- **`runImport` yields one macrotask at every progress boundary** (every
18+
`progressEvery` rows), so pending I/O — the cancel request, progress
19+
polls, any other traffic — gets serviced during a large import. This is
20+
the root-cause fix; it also unblocks progress polling for the wizard.
21+
- **The worker's `shouldCancel` now also reads the durable job row** as a
22+
fallback: a cancel accepted by another process (or after a restart
23+
dropped the in-memory flag) still stops the worker.
24+
- **A late cancel wins the terminal state**: the worker's final patch no
25+
longer overwrites the cancel route's durable `cancelled` with
26+
`succeeded`, and a job cancelled while still `pending` doesn't start at
27+
all. Counts stay truthful — they reflect what was actually written.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
'@objectstack/rest': patch
3+
---
4+
5+
fix(rest): split multi-value fields on import so `multiple: true` columns resolve per-token (#3063)
6+
7+
The bulk-import coercion (`import-coerce.ts`) resolved a reference cell as a
8+
single value regardless of the field's `multiple` flag: a `multiple: true`
9+
lookup/user cell like `张焊工;李质检` was passed whole to name resolution and
10+
always failed with `no <object> matches "张焊工;李质检"`, so every multi-value
11+
association had to be back-filled by hand in the record UI after import.
12+
13+
Coercion now mirrors objectql's `isMultiValueField` predicate. A field whose
14+
stored value is an array — an inherently-multi type (multiselect/checkboxes/tags)
15+
or a multi-capable type flagged `multiple: true` (per the spec: select, lookup,
16+
file, image; `radio` shares select's branch and `user` shares lookup's) — has
17+
its cell split on the export separator (`, ` / `;` / `` / newline) and each
18+
token coerced individually:
19+
20+
- **lookup / user (`multiple: true`)** — resolve each name token to an id, store
21+
the id array; an unmatched/ambiguous token reports the **specific token**
22+
(`no sys_user matches "查无此人"`) instead of the whole string.
23+
- **select / radio (`multiple: true`)** — match each token against the options,
24+
store the option-value array.
25+
- **file / image (`multiple: true`)** — split into an id/url array.
26+
27+
Single-value fields and the non-multi-capable reference types (master_detail /
28+
reference / tree) are unchanged — a stray `multiple: true` on them stays a
29+
single resolved value, matching the engine.

.changeset/owner-id-anchor-and-bulk-write-scoping.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,6 @@ row-level scoping keys off to decide who may update/delete a record.
5555
now correctly affects zero rows instead of all of them.
5656

5757
Proven end-to-end on the real showcase app
58-
(`packages/dogfood/test/owner-anchor-and-bulk-writes.dogfood.test.ts`) and pinned
58+
(`packages/qa/dogfood/test/owner-anchor-and-bulk-writes.dogfood.test.ts`) and pinned
5959
in the ADR-0096 authz-conformance ledger (`ownership-anchor-guard`,
6060
`bulk-write-owner-scoping`).

.changeset/pinyin-search-companion.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
'@objectstack/cli': minor
66
---
77

8-
Generic pinyin search recall (#2486, ADR-0097): a locale-gated
8+
Generic pinyin search recall (#2486, ADR-0098): a locale-gated
99
`OS_SEARCH_PINYIN_ENABLED` switch (auto-on when the stack configures any
1010
`zh-*` locale) provisions a hidden `__search` companion column for each
1111
object's display/name field at compile time, the new

.changeset/turn-atomic-publish.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@objectstack/metadata-protocol': minor
3+
'@objectstack/objectql': minor
4+
---
5+
6+
Package-draft publishing is now turn-atomic (ADR-0067 Decision-2, #3066). `publishPackageDrafts` runs every draft promotion AND the `sys_metadata_commit` record inside ONE engine transaction — a mid-batch failure rolls back the whole batch (`publishedCount: 0`; the causal item carries its real error, the rest report `batch_aborted`). Side effects (registry refresh, table DDL, seed apply, materializers, ADR-0094 projections, events) run after the metadata commits and are surfaced-not-swallowed on failure. `@objectstack/objectql`'s `engine.transaction()` now JOINS an already-open ambient transaction instead of opening a nested driver transaction (deadlock on single-connection pools; escaped the outer rollback). BREAKING (behavioral): API consumers that relied on partial batch publishes ("2 of 3 landed") now get all-or-nothing; engines without `transaction()` (memory driver, minimal stubs) keep the previous sequential behavior.

.github/workflows/ci.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,18 +107,21 @@ jobs:
107107
# spec sits at the root of the dependency graph, so spec-touching PRs
108108
# still run (close to) everything — but the many PRs that don't touch
109109
# spec skip the bulk of the 75-package matrix.
110+
# --concurrency=4: turbo's default (10) oversubscribes the 4-vCPU
111+
# hosted runner; matching the core count bounds peak memory and the
112+
# job is CPU-bound anyway.
110113
- name: Run affected tests (PR)
111114
if: github.event_name == 'pull_request'
112115
env:
113116
TURBO_SCM_BASE: ${{ github.event.pull_request.base.sha }}
114-
run: pnpm turbo run test --affected
117+
run: pnpm turbo run test --affected --concurrency=4
115118

116119
# Push to main: full run, but exclude spec's plain test task — the
117120
# coverage step below executes the exact same 250-file spec suite once,
118121
# with coverage. Previously the spec suite ran twice per push.
119122
- name: Run all tests (push)
120123
if: github.event_name == 'push'
121-
run: pnpm turbo run test --filter=!@objectstack/spec
124+
run: pnpm turbo run test --filter=!@objectstack/spec --concurrency=4
122125

123126
- name: Generate coverage report
124127
if: github.event_name == 'push'

.github/workflows/lint.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,15 @@ jobs:
123123
# bad signal to copy from. tsup transpiles them without a full typecheck,
124124
# so build alone will not catch type drift — typecheck them explicitly.
125125
# They import from built workspace packages, so the packages must be built
126-
# first for cross-package type resolution to succeed.
126+
# first for cross-package type resolution to succeed. The examples'
127+
# dependency closure is requested EXPLICITLY (`./examples/*^...` = deps
128+
# of the examples, not the examples themselves): the bare `./packages/*`
129+
# glob only matches direct children, and the connector packages the
130+
# showcase imports were previously built only by accident — through
131+
# dogfood's dependency chain, which broke when dogfood moved to
132+
# packages/qa/ (#3037).
127133
- name: Build workspace packages
128-
run: pnpm exec turbo run build --filter='./packages/*'
134+
run: pnpm exec turbo run build --filter='./packages/*' --filter='./examples/*^...'
129135

130136
- name: Type check example apps
131137
run: pnpm --filter './examples/*' run typecheck

.github/workflows/spec-liveness-check.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ name: Spec Liveness Check
99
#
1010
# ADR-0054 (prove-it-runs): bound high-risk 'live' properties must carry a `proof`
1111
# pointing to a dogfood test that declares the matching `@proof:` tag. The gate also
12-
# triggers on packages/dogfood/** so deleting/renaming a proof re-runs this check and
12+
# triggers on packages/qa/dogfood/** so deleting/renaming a proof re-runs this check and
1313
# the dangling reference is caught (the proof files live outside packages/spec/).
1414

1515
on:
1616
pull_request:
1717
types: [opened, synchronize, reopened]
1818
paths:
1919
- 'packages/spec/**'
20-
- 'packages/dogfood/**'
20+
- 'packages/qa/dogfood/**'
2121

2222
permissions:
2323
contents: read

0 commit comments

Comments
 (0)