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 8a65474
Browse filesBrowse the repository at this point in the historyBrowse files
fix(lint): the list-view sort and search, form-predicate path, bulk-dispatch, component-type and preset-comparand findings print one verdict line, and `os explain <rule-id>` carries their reasoning
6
+
7
+
Clause-②: no
8
+
9
+
-**Shorter verdicts.** Each finding of these 12 rule ids now prints a `message` of one verdict sentence. Every finding the rules' own test suites fire is at most 193 characters, and the runtime publish gate's suites at most 137; before, the longest of each ran from 287 to 927 characters. The ids:
10
+
- list-view `sort` (`objects[].listViews`, `views[]` lists, list overlays and ViewItem records): `sort-field-unknown`, `sort-field-unsortable`, `sort-field-unprovisioned`;
11
+
-`searchableFields` (the object's own set, list views, and a react page's `<ListView searchableFields>`): `searchable-field-unknown`, `searchable-field-unsearchable`, `searchable-field-unprovisioned`;
12
+
- metadata-form `visibleWhen` predicates (`views[]` forms bound to a schema): `predicate-path-unresolved`, `predicate-path-unrooted`, `predicate-rhs-path-shaped`;
A verdict no longer repeats what the finding's `where` already names: the view that wires an action (`action-dispatch-contract-mismatch`). The unprovisioned-anchor ids print the same one-clause cause the other converted anchor rules print (`'owner_id' is an injected column with no storage on external object 'x'`), and the two virtual-entry ids (`sort-field-unsortable`, `searchable-field-unsearchable`) state the storage fact in one shared wording. `searchable-field-unsearchable` quotes at most three names of an object's declared set, then `(and N more)`. A retired component type (`user:profile`, `element:filter`, `element:form`, `ai:chat_window`) is quoted to the head of its prescription in `RETIRED_PAGE_COMPONENT_TYPES` (`` `element:filter` was removed in @objectstack/spec 17 (ADR-0049) ``), and the verdict says the parse refuses the node by name; the whole prescription is the parse door's refusal of the same name, which `os validate` and `os build` print. `filter-preset-comparand` opens with the first sentence of the refusal the schema door shares (`"last_30_days" is a dashboard date-range PRESET name, not a filter value`), then names the operator and the preset's `{date-macro}` window. The `fix` (the CLI's `fix:` line, the runtime issue's `hint`), every rule id, severity and `path`, and what each rule accepts or refuses are unchanged. A tool that matched the old message text should match on `rule` and `path` instead.
18
+
- **`os explain <rule-id>` takes these 12 ids**, for example `os explain sort-field-unknown`. It prints the reasoning the verdicts no longer carry: why an unknown sort field breaks a view's first fetch and every load after it, and which list-view surfaces the sort and search rules walk and skip; what a `formula` field's lack of storage does to an ORDER BY and to a search; what an unprovisioned anchor is and what sorting or searching one measured; how a stale `searchableFields` entry narrows a search or falls through to the auto-default set, and how a list view's narrowing reaches the runtime as the `$searchFields` override; how a metadata-form predicate path is resolved against the edited schema, why a dead predicate fails open, and why the right side of `==` / `!=` is a literal; how the two bulk wirings call an action and why nothing refuses a mismatch at run time; which component types the vocabulary closes and where a retired type's prescription lives; and where a date-range preset name is understood and what each layer does with a bare one. Paragraphs shared across ids are one text, printed under every id they explain. The `rule:` line under each of these findings now ends with `` — `os explain <rule-id>` for … ``. The no-argument listing and its `--json` `rules` array list the 12 ids, and so does the unknown-id error's `Rules with an explanation:` line. `RULE_EXPLANATIONS` in `@objectstack/lint` gains the 12 entries.
19
+
-**Where the new text prints.** On the CLI, all 12 ids: `os validate`, `os build` (and `os compile`, which `os dev` runs on every compile), `os lint`, `os verify`, and the scaffold check `os init` and `os generate` run print the new `message` on the text face, and `os validate --json` and `os build --json` carry it in their `errors` and author-time `issues`. At the runtime publish gate (Studio, REST `/meta`, MCP), the 422 issue's `message` and the refusal log line under `OS_ALLOW_UNLINTED_METADATA_WRITES` change for: the three `searchable-field-*` ids on a `view`, `object` or `flow` write; the three `sort-field-*` ids on a `view` or `flow` write; the three `predicate-*` ids on a `view` write; and `filter-preset-comparand` on a `dashboard`, `view`, `object`, `page`, `flow` or `report` write. Each issue's `hint` is unchanged.
20
+
-**Never at the runtime gate:**`component-type-unknown`, which runs on the CLI doors only, and `action-dispatch-contract-mismatch`, whose rule runs at that door only for a `flow` write, whose snapshot carries no actions to judge, so both speak only on the CLI doors above.
fix(cli): `os migrate plan` / `apply` plan each object in the database it lives in, the `telemetry` sibling included (#22579)
6
+
7
+
A development `os serve` boot on a file-backed SQLite database, and any boot with `OS_TELEMETRY_DB=<path>`, keeps lifecycle-classed system data (audit, telemetry and event objects such as `sys_audit_log`, `sys_activity`, `sys_metadata_audit`, `sys_job_run`, `sys_notification`) in a sibling `telemetry` database. The one-shot boot `os migrate` runs never opened that sibling, so every one of those objects resolved to the primary database: after a development boot, `os migrate plan` listed each as a table to create, and `os migrate apply` created each in the primary — empty tables beside the ones the served boot uses.
8
+
9
+
The migrate boot now provisions the sibling exactly when the serving boot would, through the same helper and under the same rule: `--dev` or `NODE_ENV=development` on a file-backed SQLite primary, or `OS_TELEMETRY_DB=<path>`, and never with `OS_TELEMETRY_DB=0`. `plan` and `apply` diff and apply every object against the database it lives in, and print the sibling under the database line (`Telemetry database: …`); in `--json` it is the new `telemetryDatabase` field, present only when a sibling is planned. `apply`'s confirmation names both databases. A `plan` against a sibling that does not exist yet lists its tables to create and creates no file. `os migrate unmapped-columns` reads a lifecycle-classed object from the sibling, and names it as the database.
10
+
11
+
A deployment with no sibling — production without `OS_TELEMETRY_DB`, or `OS_TELEMETRY_DB=0` — is unchanged. Run the migration with the `NODE_ENV` the deployment is served with: without `NODE_ENV=development` the plan describes a production `os serve`. Tables an earlier `os migrate apply` created in the primary for these objects are not removed.
12
+
13
+
This supersedes the "Known limit" in this release's `os migrate plan` / `apply` composition entry: the migration boot now provisions the `telemetry` database.
14
+
15
+
`os serve` loads the project's `.env*` files through the same function `os migrate` does; which files it reads, and in which mode, is unchanged.
An import row answers a sandboxed hook's refusal in the hook's own words, not the sandbox debug wrapper
6
+
7
+
Clause-②: no
8
+
9
+
When a hook body refused a row during `POST /api/v1/data/:object/import` (or the async `/import/jobs` job), the row's `error` read `hook 'NAME' threw: Error: SENTENCE`, while `POST /api/v1/data/:object` and `/createMany` answered the same refusal as `SENTENCE`. The import runner now reads the row's sentence the way those routes do: the hook's sentence, unchanged, with any `code` the body declared still on the row. A hook body that crashes (for example with a `TypeError`) is not a refusal, and its row reads as it did before.
0 commit comments