Skip to content

Commit b678d8c

Browse files
xuyushun441-sysos-zhuangclaude
authored
chore(changeset): backfill changesets for post-9.3 changes (#1830)
Adds changesets for the package-touching PRs merged after the 9.3.0 release that landed without one: objectql (metadata hydration opt-in, seed id fallback, ADR-0048 namespace gate + package-scoped resolution), service-ai (visualize_data tool + steering, query-only open framework, cross-language current-object), driver-sql ($select zero-rows fix), and spec (ADR-0047 list-page form + filter-mode widget). Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 1e17869 commit b678d8c

10 files changed

Lines changed: 192 additions & 0 deletions
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
"@objectstack/driver-sql": patch
3+
---
4+
5+
fix(driver-sql): an unknown `$select` column must not zero the result set
6+
7+
`find()` swallowed any "no such column" error into an empty array. A projected
8+
`$select` naming a column the table lacks (e.g. a generic list view
9+
auto-requesting `status`/`due_date`/`image` on an object without them) then made
10+
the WHOLE query return zero rows — reading to the UI as "no records exist" while
11+
the data was actually there: a silent data-loss footgun.
12+
13+
When the failure comes from the projection, retry once with `SELECT *` so the
14+
real rows still come back (the phantom field is simply absent from each row).
15+
Non-projection errors (unknown table, etc.) still surface as before. This driver
16+
backstop holds even when the engine's unknown-field filter cannot fire because
17+
the object's schema is not populated in the registry (notably the cloud
18+
multi-tenant runtime).
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
"@objectstack/objectql": minor
3+
---
4+
5+
feat(objectql): ADR-0048 Phase 1+2 — namespace install gate + package-scoped resolution
6+
7+
Phase 1 — install-time namespace gate. `SchemaRegistry.installPackage` refuses a
8+
package whose `manifest.namespace` is already owned by a DIFFERENT installed
9+
package (new `NamespaceConflictError`), making explicit and early the constraint
10+
the table layer already enforces implicitly. Same-package reinstall and
11+
shareable platform namespaces (`base`/`system`/`sys`) are exempt;
12+
`OS_METADATA_COLLISION=warn` downgrades to a warning.
13+
14+
Phase 2 — prefer-local resolution, pivoted to ADR-0048 option A (package id as
15+
the routing key). `getItem(type, name, currentPackageId?)` prefers
16+
`${currentPackageId}:${name}` before any cross-package fallback (ADR-0005 overlay
17+
precedence and backward compatibility unchanged); `getApp(name,
18+
currentPackageId?)` resolves prefer-local by package id. Because package ids are
19+
globally unique, package-scoped resolution always disambiguates two distinct
20+
packages — so the old per-item CROSS-package throw (and the now-dead
21+
`MetadataCollisionError`, `findOtherPackageOwner`, `SYS_METADATA_OWNER`, …) is
22+
retired; two different-namespace packages legitimately coexist on the same bare
23+
name. `collisionPolicy` now governs only the Phase 1 namespace gate.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
"@objectstack/objectql": minor
3+
---
4+
5+
feat(objectql): opt-in `sys_metadata` hydration for isolated project kernels
6+
7+
Boot Phase-2 hydration (`restoreMetadataFromDb``loadMetaFromDb`, which
8+
registers objects WITH their fields into the `SchemaRegistry`) was gated on
9+
`environmentId === undefined`, assuming every project kernel sources its
10+
metadata from a remote artifact / control-plane proxy. That is untrue for an
11+
isolated, proxy-free project kernel that persists its OWN `sys_metadata`
12+
locally (the cloud single-env tenant runtime): objects created at runtime there
13+
never re-entered the registry after a restart, so `registry.getObject(name)`
14+
returned nothing and every registry consumer silently degraded (notably the
15+
`engine.find` unknown-`$select` guard, which then let an unknown projected
16+
column zero the result set).
17+
18+
Adds an explicit `hydrateMetadataFromDb` plugin option (default `false`, so the
19+
control-plane/proxy path is untouched). When set, hydration runs even with
20+
`environmentId` defined — safe because each engine now owns its registry
21+
instance and `loadMetaFromDb` already tolerates a missing table.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
"@objectstack/objectql": patch
3+
---
4+
5+
fix(objectql): seed reference resolution falls back to matching by `id`
6+
7+
`SeedLoaderService.resolveFromDatabase` only matched a reference value against
8+
the target's natural-key field. A seed that wires a lookup to a REAL existing
9+
record by its internal id — e.g. a people field (approver/applicant → user)
10+
pointed at the current user — dangled to `null` when that id is not a
11+
UUID/ObjectId (so the caller's `looksLikeInternalId` guard did not
12+
short-circuit) and is not the target's natural key.
13+
14+
Adds an id fallback: when the natural-key lookup finds nothing, try resolving
15+
the value as the target's `id`. Safe — an id either exists or it doesn't, so
16+
there is no risk of a false natural-key match, and it is tenant-scoped like the
17+
primary lookup.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
"@objectstack/service-ai": patch
3+
"@objectstack/spec": patch
4+
---
5+
6+
fix(service-ai): resolve the current object for AI chat across languages
7+
8+
The console assistant reported "can't find the X object" when asked to analyse
9+
the object on the current page — most visibly for non-English prompts. Three
10+
compounding gaps fixed:
11+
12+
- `SchemaRetriever.tokenise()` dropped all CJK text, so a Chinese request
13+
yielded zero terms; it now emits CJK single-char + bigram terms.
14+
- Nothing fed the current object's schema to the agent, so "this object" could
15+
not be resolved without a lucky keyword hit. `AgentRuntime.buildContextSchema
16+
Messages()` now injects the current object's schema into the system prompt and
17+
both chat routes call it.
18+
- `ToolExecutionContext` (and the `ai-service` spec contract) gains
19+
`currentObjectName`/`currentViewName`; routes thread them through and
20+
`query_data` falls back to the current object when keyword retrieval is empty
21+
(so the open edition, which lacks `describe_object`/`list_objects`, still
22+
resolves the page's object).
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
"@objectstack/service-ai": minor
3+
---
4+
5+
feat(service-ai): open framework AI is query-only, declines app-building
6+
7+
The unified `data_chat` persona (ADR-0040) advertised that it can BUILD or
8+
CHANGE the application, but that capability is supplied entirely by the cloud AI
9+
Studio plugin's `metadata_authoring`/`solution_design` skills. On the open
10+
single-env framework those skills are not registered, so the authoring tools
11+
never resolve — yet the LLM, still reading the "you can build" persona,
12+
role-played designing a whole system (emitting design docs it had no tools to
13+
execute).
14+
15+
Fix: in `buildSystemMessages`, when no authoring (build-register) skill is
16+
active, append a deployment-capability note constraining the assistant to
17+
data/query and instructing it to decline build requests instead of pretending.
18+
Keyed off actual skill presence, so cloud/EE (AI Studio loaded) keeps the full
19+
build UX with zero extra wiring.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
"@objectstack/service-ai": minor
3+
---
4+
5+
feat(service-ai): steer the agent to `visualize_data` for chart requests
6+
7+
Small models sometimes answered a "draw a bar chart" request with a markdown
8+
TABLE instead of calling `visualize_data` — a tool-selection problem where the
9+
chart preference was buried as a low-priority guideline competing with
10+
"format with markdown tables".
11+
12+
- `data-explorer-skill.ts` — adds a prominent "Choosing the right tool" section
13+
above the guidelines: chart intent (incl. CN terms 图表/柱状图/折线图/饼图/画图)
14+
MUST call `visualize_data`, never substitute a table; reconciles the
15+
table-formatting guideline and fixes duplicate guideline numbering.
16+
- `visualize-data.tool.ts` — strengthens the tool description to be imperative
17+
("the ONLY tool that draws a chart… if you already fetched the numbers, still
18+
call `visualize_data` to render them").
19+
20+
Prompt-only tuning — no behavior/contract change.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
"@objectstack/service-ai": minor
3+
---
4+
5+
feat(service-ai): `visualize_data` tool — return charts from AI data queries
6+
7+
Adds a `visualize_data` AI tool so the data-query assistant can answer with a
8+
CHART instead of plain text/markdown. The tool runs an aggregation through the
9+
existing analytics service (auto-inferred cube), maps the result into the SDUI
10+
`<chart>` contract, and emits it to the client as a `data-chart` custom stream
11+
part (the same `onProgress` channel `data-build-progress` already uses). It also
12+
returns a compact textual summary so the model narrates the answer alongside the
13+
rendered chart.
14+
15+
- `tools/visualize-data.tool.ts` — tool definition, handler, and register fn
16+
(function+field → analytics measure key; single dimension → x-axis; measures →
17+
series; `chartType` bar/line/pie/…).
18+
- `plugin.ts` — registers the tool when an analytics service is present and
19+
persists it as tool metadata in lockstep (Studio visibility).
20+
- `skills/data-explorer-skill.ts` — exposes `visualize_data` plus chart trigger
21+
phrases and guidance to prefer it for "chart/plot/trend/breakdown" requests.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
"@objectstack/spec": minor
3+
---
4+
5+
feat(spec): page form filter-mode widget + ADR-0047 §3.4a (omit-is-none)
6+
7+
The Interface section's `interfaceConfig` composite now lists its sub-fields
8+
explicitly so `userFilters` can use the dedicated `filter-mode` selector widget
9+
(None / Tabs / Dropdown, objectui). An unknown widget name degrades gracefully
10+
to the prior composite rendering, so this is independently mergeable.
11+
12+
ADR-0047 §3.4a records the design decision: "no filter bar" is the ABSENCE of
13+
`userFilters`, not a literal `element: 'none'` — presence and style are
14+
orthogonal axes, keeping declarative metadata and overlay diffs clean. The
15+
`userFilters` element `'toggle'` is deprecated (kept in the enum for back-compat;
16+
authoring offers None/Tabs/Dropdown only, Airtable parity).
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
"@objectstack/spec": minor
3+
---
4+
5+
feat(spec): ADR-0047 — list pages hide region/data-context, interface section prominent
6+
7+
Reorganizes the page form (`page.form.ts`) so interface/list pages get a lean,
8+
relevant panel instead of the generic page-form dump:
9+
10+
- Data Context + Layout sections gain `visibleOn` `data.type != 'list'` (region
11+
designer / page object don't apply to a list surface).
12+
- Interface section becomes primary content (`collapsed: false`, named for i18n).
13+
- `interfaceConfig` sub-fields reordered (common first, rare last); `source`
14+
gets the `ref:object` picker; `sourceView`/`userActions`/etc. gain helpText.
15+
- `type` field helpText notes `'list'` = interface page.

0 commit comments

Comments
 (0)