Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .changeset/driver-sql-unknown-select-column-no-zero-rows.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
"@objectstack/driver-sql": patch
---

fix(driver-sql): an unknown `$select` column must not zero the result set

`find()` swallowed any "no such column" error into an empty array. A projected
`$select` naming a column the table lacks (e.g. a generic list view
auto-requesting `status`/`due_date`/`image` on an object without them) then made
the WHOLE query return zero rows — reading to the UI as "no records exist" while
the data was actually there: a silent data-loss footgun.

When the failure comes from the projection, retry once with `SELECT *` so the
real rows still come back (the phantom field is simply absent from each row).
Non-projection errors (unknown table, etc.) still surface as before. This driver
backstop holds even when the engine's unknown-field filter cannot fire because
the object's schema is not populated in the registry (notably the cloud
multi-tenant runtime).
23 changes: 23 additions & 0 deletions .changeset/objectql-adr-0048-namespace-gate-prefer-local.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
"@objectstack/objectql": minor
---

feat(objectql): ADR-0048 Phase 1+2 — namespace install gate + package-scoped resolution

Phase 1 — install-time namespace gate. `SchemaRegistry.installPackage` refuses a
package whose `manifest.namespace` is already owned by a DIFFERENT installed
package (new `NamespaceConflictError`), making explicit and early the constraint
the table layer already enforces implicitly. Same-package reinstall and
shareable platform namespaces (`base`/`system`/`sys`) are exempt;
`OS_METADATA_COLLISION=warn` downgrades to a warning.

Phase 2 — prefer-local resolution, pivoted to ADR-0048 option A (package id as
the routing key). `getItem(type, name, currentPackageId?)` prefers
`${currentPackageId}:${name}` before any cross-package fallback (ADR-0005 overlay
precedence and backward compatibility unchanged); `getApp(name,
currentPackageId?)` resolves prefer-local by package id. Because package ids are
globally unique, package-scoped resolution always disambiguates two distinct
packages — so the old per-item CROSS-package throw (and the now-dead
`MetadataCollisionError`, `findOtherPackageOwner`, `SYS_METADATA_OWNER`, …) is
retired; two different-namespace packages legitimately coexist on the same bare
name. `collisionPolicy` now governs only the Phase 1 namespace gate.
21 changes: 21 additions & 0 deletions .changeset/objectql-hydrate-metadata-from-db-opt-in.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
"@objectstack/objectql": minor
---

feat(objectql): opt-in `sys_metadata` hydration for isolated project kernels

Boot Phase-2 hydration (`restoreMetadataFromDb` → `loadMetaFromDb`, which
registers objects WITH their fields into the `SchemaRegistry`) was gated on
`environmentId === undefined`, assuming every project kernel sources its
metadata from a remote artifact / control-plane proxy. That is untrue for an
isolated, proxy-free project kernel that persists its OWN `sys_metadata`
locally (the cloud single-env tenant runtime): objects created at runtime there
never re-entered the registry after a restart, so `registry.getObject(name)`
returned nothing and every registry consumer silently degraded (notably the
`engine.find` unknown-`$select` guard, which then let an unknown projected
column zero the result set).

Adds an explicit `hydrateMetadataFromDb` plugin option (default `false`, so the
control-plane/proxy path is untouched). When set, hydration runs even with
`environmentId` defined — safe because each engine now owns its registry
instance and `loadMetaFromDb` already tolerates a missing table.
17 changes: 17 additions & 0 deletions .changeset/objectql-seed-reference-id-fallback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
"@objectstack/objectql": patch
---

fix(objectql): seed reference resolution falls back to matching by `id`

`SeedLoaderService.resolveFromDatabase` only matched a reference value against
the target's natural-key field. A seed that wires a lookup to a REAL existing
record by its internal id — e.g. a people field (approver/applicant → user)
pointed at the current user — dangled to `null` when that id is not a
UUID/ObjectId (so the caller's `looksLikeInternalId` guard did not
short-circuit) and is not the target's natural key.

Adds an id fallback: when the natural-key lookup finds nothing, try resolving
the value as the target's `id`. Safe — an id either exists or it doesn't, so
there is no risk of a false natural-key match, and it is tenant-scoped like the
primary lookup.
22 changes: 22 additions & 0 deletions .changeset/service-ai-current-object-cross-language.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
"@objectstack/service-ai": patch
"@objectstack/spec": patch
---

fix(service-ai): resolve the current object for AI chat across languages

The console assistant reported "can't find the X object" when asked to analyse
the object on the current page — most visibly for non-English prompts. Three
compounding gaps fixed:

- `SchemaRetriever.tokenise()` dropped all CJK text, so a Chinese request
yielded zero terms; it now emits CJK single-char + bigram terms.
- Nothing fed the current object's schema to the agent, so "this object" could
not be resolved without a lucky keyword hit. `AgentRuntime.buildContextSchema
Messages()` now injects the current object's schema into the system prompt and
both chat routes call it.
- `ToolExecutionContext` (and the `ai-service` spec contract) gains
`currentObjectName`/`currentViewName`; routes thread them through and
`query_data` falls back to the current object when keyword retrieval is empty
(so the open edition, which lacks `describe_object`/`list_objects`, still
resolves the page's object).
19 changes: 19 additions & 0 deletions .changeset/service-ai-open-framework-query-only.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
"@objectstack/service-ai": minor
---

feat(service-ai): open framework AI is query-only, declines app-building

The unified `data_chat` persona (ADR-0040) advertised that it can BUILD or
CHANGE the application, but that capability is supplied entirely by the cloud AI
Studio plugin's `metadata_authoring`/`solution_design` skills. On the open
single-env framework those skills are not registered, so the authoring tools
never resolve — yet the LLM, still reading the "you can build" persona,
role-played designing a whole system (emitting design docs it had no tools to
execute).

Fix: in `buildSystemMessages`, when no authoring (build-register) skill is
active, append a deployment-capability note constraining the assistant to
data/query and instructing it to decline build requests instead of pretending.
Keyed off actual skill presence, so cloud/EE (AI Studio loaded) keeps the full
build UX with zero extra wiring.
20 changes: 20 additions & 0 deletions .changeset/service-ai-steer-visualize-data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
"@objectstack/service-ai": minor
---

feat(service-ai): steer the agent to `visualize_data` for chart requests

Small models sometimes answered a "draw a bar chart" request with a markdown
TABLE instead of calling `visualize_data` — a tool-selection problem where the
chart preference was buried as a low-priority guideline competing with
"format with markdown tables".

- `data-explorer-skill.ts` — adds a prominent "Choosing the right tool" section
above the guidelines: chart intent (incl. CN terms 图表/柱状图/折线图/饼图/画图)
MUST call `visualize_data`, never substitute a table; reconciles the
table-formatting guideline and fixes duplicate guideline numbering.
- `visualize-data.tool.ts` — strengthens the tool description to be imperative
("the ONLY tool that draws a chart… if you already fetched the numbers, still
call `visualize_data` to render them").

Prompt-only tuning — no behavior/contract change.
21 changes: 21 additions & 0 deletions .changeset/service-ai-visualize-data-tool.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
"@objectstack/service-ai": minor
---

feat(service-ai): `visualize_data` tool — return charts from AI data queries

Adds a `visualize_data` AI tool so the data-query assistant can answer with a
CHART instead of plain text/markdown. The tool runs an aggregation through the
existing analytics service (auto-inferred cube), maps the result into the SDUI
`<chart>` contract, and emits it to the client as a `data-chart` custom stream
part (the same `onProgress` channel `data-build-progress` already uses). It also
returns a compact textual summary so the model narrates the answer alongside the
rendered chart.

- `tools/visualize-data.tool.ts` — tool definition, handler, and register fn
(function+field → analytics measure key; single dimension → x-axis; measures →
series; `chartType` bar/line/pie/…).
- `plugin.ts` — registers the tool when an analytics service is present and
persists it as tool metadata in lockstep (Studio visibility).
- `skills/data-explorer-skill.ts` — exposes `visualize_data` plus chart trigger
phrases and guidance to prefer it for "chart/plot/trend/breakdown" requests.
16 changes: 16 additions & 0 deletions .changeset/spec-adr-0047-filter-mode-widget.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
"@objectstack/spec": minor
---

feat(spec): page form filter-mode widget + ADR-0047 §3.4a (omit-is-none)

The Interface section's `interfaceConfig` composite now lists its sub-fields
explicitly so `userFilters` can use the dedicated `filter-mode` selector widget
(None / Tabs / Dropdown, objectui). An unknown widget name degrades gracefully
to the prior composite rendering, so this is independently mergeable.

ADR-0047 §3.4a records the design decision: "no filter bar" is the ABSENCE of
`userFilters`, not a literal `element: 'none'` — presence and style are
orthogonal axes, keeping declarative metadata and overlay diffs clean. The
`userFilters` element `'toggle'` is deprecated (kept in the enum for back-compat;
authoring offers None/Tabs/Dropdown only, Airtable parity).
15 changes: 15 additions & 0 deletions .changeset/spec-adr-0047-list-page-form.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
"@objectstack/spec": minor
---

feat(spec): ADR-0047 — list pages hide region/data-context, interface section prominent

Reorganizes the page form (`page.form.ts`) so interface/list pages get a lean,
relevant panel instead of the generic page-form dump:

- Data Context + Layout sections gain `visibleOn` `data.type != 'list'` (region
designer / page object don't apply to a list surface).
- Interface section becomes primary content (`collapsed: false`, named for i18n).
- `interfaceConfig` sub-fields reordered (common first, rare last); `source`
gets the `ref:object` picker; `sourceView`/`userActions`/etc. gain helpText.
- `type` field helpText notes `'list'` = interface page.