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
38 changes: 38 additions & 0 deletions .changeset/spec-error-callback-refusal-ids.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
"@objectstack/spec": patch
---

Strip internal issue-tracker ids from refusal prose built inside functions

The fourth customer-facing refusal population in `packages/spec`: prose a
FUNCTION returns rather than prose written at a recognised position — a zod
`error: (issue) => …` map, a hoisted `const X = (key) => '…'` message builder
referenced from `message:` or `retiredKey(X(…))`, a `$ZodErrorMap` const the
`error` callback dispatches to, a `(v): StrictObjectOptions => ({ history })`
options factory. It reaches exactly the same reader at exactly the same moment
as the three populations already stripped: the author whose metadata was just
rejected. That reader has no tracker, so `#NNNN` was a citation-shaped token
resolving to nothing in the one sentence that most needs to be actionable.

47 literals carrying 55 tracker ids across 13 sources.

| how the prose reaches the author | literals | ids | files |
|---|---:|---:|---:|
| built INSIDE a function in a recognised position | 28 | 32 | 8 |
| hoisted into a const an `error:` callback dispatches to | 19 | 23 | 8 |

**Kept, deliberately:** ADR ids, protocol and package versions, error codes
(`400 INVALID_FIELD` traces the runtime twin far better than the id beside it)
and the `os migrate meta --from <N>` commands — the anchors a customer can
actually resolve. Where an id was the whole parenthetical, the parenthetical
went with it; where it was load-bearing for an internal reader, it moved to an
adjacent `//` comment.

`check-doc-authoring` Rule 3 could not see this population at all: its climb
returned `undefined` at `ArrowFunction` / `ReturnStatement`, so the gate printed
`0 violations` over four populated buckets while a fifth sat outside every one
of them. The rule now crosses a function boundary — but only when the FUNCTION
ITSELF sits in a recognised customer-facing position, never unconditionally
through arbitrary function bodies, which would report values as prose. The new
population is its own `functionBuilt` bucket so it carries its own blindness
floor, since an unrecognised spelling produces no flag silently.
60 changes: 30 additions & 30 deletions content/docs/references/data/driver-nosql.mdx

Large diffs are not rendered by default.

60 changes: 30 additions & 30 deletions content/docs/references/data/driver-sql.mdx

Large diffs are not rendered by default.

120 changes: 60 additions & 60 deletions content/docs/references/data/driver.mdx

Large diffs are not rendered by default.

24 changes: 12 additions & 12 deletions content/docs/references/ui/component.mdx

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions content/docs/references/ui/dashboard.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ const result = DashboardSchema.parse(data);
| **colorVariant** | `Enum<'default' \| 'blue' \| 'teal' \| 'orange' \| 'purple' \| 'success' \| 'warning' \| 'danger'>` | optional | Widget color variant for theming |
| **requiresObject** | `string` | optional | Hide the widget unless the named object is registered |
| **requiresService** | `string` | optional | Hide the widget unless the named kernel service is registered |
| **actionUrl** | `never` | optional | [REMOVED] `dashboard.widgets[].actionUrl` was removed in @objectstack/spec 17.0.0 (#5010, ADR-0049 enforce-or-remove) — a dashboard widget has NO action button, and never had one. No renderer draws per-widget chrome for it: every action the dashboard dispatches comes from `header.actions[]`. The three keys `actionUrl` / `actionType` / `actionIcon` went together; delete all three. Put the affordance on the dashboard header instead — `header: { actions: [{ label, actionUrl, actionType, icon }] }` — which IS dispatched (`DashboardHeaderAction`, same vocabulary, and `icon` is the header spelling of `actionIcon`). For a per-ROW affordance, the widget to reach for is a `table`/`pivot` bound to a dataset: its rows are clickable and drill through the semantic layer. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
| **actionType** | `never` | optional | [REMOVED] `dashboard.widgets[].actionType` was removed in @objectstack/spec 17.0.0 (#5010, ADR-0049 enforce-or-remove) — a dashboard widget has NO action button, and never had one. No renderer draws per-widget chrome for it: every action the dashboard dispatches comes from `header.actions[]`. The three keys `actionUrl` / `actionType` / `actionIcon` went together; delete all three. Put the affordance on the dashboard header instead — `header: { actions: [{ label, actionUrl, actionType, icon }] }` — which IS dispatched (`DashboardHeaderAction`, same vocabulary, and `icon` is the header spelling of `actionIcon`). For a per-ROW affordance, the widget to reach for is a `table`/`pivot` bound to a dataset: its rows are clickable and drill through the semantic layer. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
| **actionIcon** | `never` | optional | [REMOVED] `dashboard.widgets[].actionIcon` was removed in @objectstack/spec 17.0.0 (#5010, ADR-0049 enforce-or-remove) — a dashboard widget has NO action button, and never had one. No renderer draws per-widget chrome for it: every action the dashboard dispatches comes from `header.actions[]`. The three keys `actionUrl` / `actionType` / `actionIcon` went together; delete all three. Put the affordance on the dashboard header instead — `header: { actions: [{ label, actionUrl, actionType, icon }] }` — which IS dispatched (`DashboardHeaderAction`, same vocabulary, and `icon` is the header spelling of `actionIcon`). For a per-ROW affordance, the widget to reach for is a `table`/`pivot` bound to a dataset: its rows are clickable and drill through the semantic layer. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
| **actionUrl** | `never` | optional | [REMOVED] `dashboard.widgets[].actionUrl` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — a dashboard widget has NO action button, and never had one. No renderer draws per-widget chrome for it: every action the dashboard dispatches comes from `header.actions[]`. The three keys `actionUrl` / `actionType` / `actionIcon` went together; delete all three. Put the affordance on the dashboard header instead — `header: { actions: [{ label, actionUrl, actionType, icon }] }` — which IS dispatched (`DashboardHeaderAction`, same vocabulary, and `icon` is the header spelling of `actionIcon`). For a per-ROW affordance, the widget to reach for is a `table`/`pivot` bound to a dataset: its rows are clickable and drill through the semantic layer. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
| **actionType** | `never` | optional | [REMOVED] `dashboard.widgets[].actionType` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — a dashboard widget has NO action button, and never had one. No renderer draws per-widget chrome for it: every action the dashboard dispatches comes from `header.actions[]`. The three keys `actionUrl` / `actionType` / `actionIcon` went together; delete all three. Put the affordance on the dashboard header instead — `header: { actions: [{ label, actionUrl, actionType, icon }] }` — which IS dispatched (`DashboardHeaderAction`, same vocabulary, and `icon` is the header spelling of `actionIcon`). For a per-ROW affordance, the widget to reach for is a `table`/`pivot` bound to a dataset: its rows are clickable and drill through the semantic layer. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
| **actionIcon** | `never` | optional | [REMOVED] `dashboard.widgets[].actionIcon` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — a dashboard widget has NO action button, and never had one. No renderer draws per-widget chrome for it: every action the dashboard dispatches comes from `header.actions[]`. The three keys `actionUrl` / `actionType` / `actionIcon` went together; delete all three. Put the affordance on the dashboard header instead — `header: { actions: [{ label, actionUrl, actionType, icon }] }` — which IS dispatched (`DashboardHeaderAction`, same vocabulary, and `icon` is the header spelling of `actionIcon`). For a per-ROW affordance, the widget to reach for is a `table`/`pivot` bound to a dataset: its rows are clickable and drill through the semantic layer. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
| **filter** | `any` | optional | Presentation-scope filter (runtimeFilter) |
| **compareTo** | `{ kind: Enum<'previousPeriod' \| 'previousYear'>; dimension?: string }` | optional | Period-over-period comparison window (`{ kind, dimension? }`) |
| **dataset** | `string` | ✅ | Dataset name to bind (ADR-0021) |
Expand Down Expand Up @@ -175,9 +175,9 @@ Dashboard header action
| **colorVariant** | `Enum<'default' \| 'blue' \| 'teal' \| 'orange' \| 'purple' \| 'success' \| 'warning' \| 'danger'>` | optional | Widget color variant for theming |
| **requiresObject** | `string` | optional | Hide the widget unless the named object is registered |
| **requiresService** | `string` | optional | Hide the widget unless the named kernel service is registered |
| **actionUrl** | `never` | optional | [REMOVED] `dashboard.widgets[].actionUrl` was removed in @objectstack/spec 17.0.0 (#5010, ADR-0049 enforce-or-remove) — a dashboard widget has NO action button, and never had one. No renderer draws per-widget chrome for it: every action the dashboard dispatches comes from `header.actions[]`. The three keys `actionUrl` / `actionType` / `actionIcon` went together; delete all three. Put the affordance on the dashboard header instead — `header: { actions: [{ label, actionUrl, actionType, icon }] }` — which IS dispatched (`DashboardHeaderAction`, same vocabulary, and `icon` is the header spelling of `actionIcon`). For a per-ROW affordance, the widget to reach for is a `table`/`pivot` bound to a dataset: its rows are clickable and drill through the semantic layer. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
| **actionType** | `never` | optional | [REMOVED] `dashboard.widgets[].actionType` was removed in @objectstack/spec 17.0.0 (#5010, ADR-0049 enforce-or-remove) — a dashboard widget has NO action button, and never had one. No renderer draws per-widget chrome for it: every action the dashboard dispatches comes from `header.actions[]`. The three keys `actionUrl` / `actionType` / `actionIcon` went together; delete all three. Put the affordance on the dashboard header instead — `header: { actions: [{ label, actionUrl, actionType, icon }] }` — which IS dispatched (`DashboardHeaderAction`, same vocabulary, and `icon` is the header spelling of `actionIcon`). For a per-ROW affordance, the widget to reach for is a `table`/`pivot` bound to a dataset: its rows are clickable and drill through the semantic layer. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
| **actionIcon** | `never` | optional | [REMOVED] `dashboard.widgets[].actionIcon` was removed in @objectstack/spec 17.0.0 (#5010, ADR-0049 enforce-or-remove) — a dashboard widget has NO action button, and never had one. No renderer draws per-widget chrome for it: every action the dashboard dispatches comes from `header.actions[]`. The three keys `actionUrl` / `actionType` / `actionIcon` went together; delete all three. Put the affordance on the dashboard header instead — `header: { actions: [{ label, actionUrl, actionType, icon }] }` — which IS dispatched (`DashboardHeaderAction`, same vocabulary, and `icon` is the header spelling of `actionIcon`). For a per-ROW affordance, the widget to reach for is a `table`/`pivot` bound to a dataset: its rows are clickable and drill through the semantic layer. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
| **actionUrl** | `never` | optional | [REMOVED] `dashboard.widgets[].actionUrl` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — a dashboard widget has NO action button, and never had one. No renderer draws per-widget chrome for it: every action the dashboard dispatches comes from `header.actions[]`. The three keys `actionUrl` / `actionType` / `actionIcon` went together; delete all three. Put the affordance on the dashboard header instead — `header: { actions: [{ label, actionUrl, actionType, icon }] }` — which IS dispatched (`DashboardHeaderAction`, same vocabulary, and `icon` is the header spelling of `actionIcon`). For a per-ROW affordance, the widget to reach for is a `table`/`pivot` bound to a dataset: its rows are clickable and drill through the semantic layer. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
| **actionType** | `never` | optional | [REMOVED] `dashboard.widgets[].actionType` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — a dashboard widget has NO action button, and never had one. No renderer draws per-widget chrome for it: every action the dashboard dispatches comes from `header.actions[]`. The three keys `actionUrl` / `actionType` / `actionIcon` went together; delete all three. Put the affordance on the dashboard header instead — `header: { actions: [{ label, actionUrl, actionType, icon }] }` — which IS dispatched (`DashboardHeaderAction`, same vocabulary, and `icon` is the header spelling of `actionIcon`). For a per-ROW affordance, the widget to reach for is a `table`/`pivot` bound to a dataset: its rows are clickable and drill through the semantic layer. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
| **actionIcon** | `never` | optional | [REMOVED] `dashboard.widgets[].actionIcon` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — a dashboard widget has NO action button, and never had one. No renderer draws per-widget chrome for it: every action the dashboard dispatches comes from `header.actions[]`. The three keys `actionUrl` / `actionType` / `actionIcon` went together; delete all three. Put the affordance on the dashboard header instead — `header: { actions: [{ label, actionUrl, actionType, icon }] }` — which IS dispatched (`DashboardHeaderAction`, same vocabulary, and `icon` is the header spelling of `actionIcon`). For a per-ROW affordance, the widget to reach for is a `table`/`pivot` bound to a dataset: its rows are clickable and drill through the semantic layer. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
| **filter** | `any` | optional | Presentation-scope filter (runtimeFilter) |
| **compareTo** | `{ kind: Enum<'previousPeriod' \| 'previousYear'>; dimension?: string }` | optional | Period-over-period comparison window (`{ kind, dimension? }`) |
| **dataset** | `string` | ✅ | Dataset name to bind (ADR-0021) |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,27 @@ describe('compileDataset', () => {
// carries a prescription instead of naming the supported list. Re-pointed
// rather than deleted: the dataset measure is one of the retirement's two
// authoring surfaces, and this is where that surface is exercised.
//
// ⚠️ Pinned on the CUSTOMER-RESOLVABLE anchors the prescription carries —
// the package version and the ADR — never on a tracker id. The id used to be
// in this message and is not any more: `check:doc-authoring` Rule 3 bans an
// internal `#NNNN` from prose printed at a refused author, and a consumer-side
// regex spelling one is a pin on the thing that must not be there.
it('rejects a retired aggregate at the schema, with the retirement prescription', () => {
expect(() => DatasetSchema.parse({
const parse = () => DatasetSchema.parse({
name: 'agg',
label: 'Agg',
object: 'opportunity',
dimensions: [],
measures: [{ name: 'tags', aggregate: 'array_agg', field: 'tag' }],
})).toThrowError(/`array_agg`.*was removed.*#6188/s);
});
expect(parse).toThrowError(/`array_agg`.*was removed.*ADR-0049 enforce-or-remove/s);
// It is a PRESCRIPTION, not a bare refusal: it must still say what to do.
expect(parse).toThrowError(/Delete the aggregation/s);
// ...and it must carry no tracker id, in either the bare or repo-qualified
// spelling. Negative pin, so a re-introduced id reds HERE too and not only
// at the gate.
expect(parse).not.toThrowError(/#\d{3,5}(?![0-9A-Za-z])/s);
});

it('still compiles the aggregate the ruling kept', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/spec/src/data/driver.zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export const DriverOptionsSchema = lazySchema(() => z.object({
* record is the semantic migration `driver-capabilities-inert-bits-removed`.
*/
const capRemoved = (key: string, mechanism: string) =>
`\`DriverCapabilities.${key}\` was removed in @objectstack/spec 17.0.0 (#4634, ADR-0049 ` +
`\`DriverCapabilities.${key}\` was removed in @objectstack/spec 17.0.0 (ADR-0049 ` +
`enforce-or-remove) — no code in any repository ever read it, so its value never changed ` +
`which code path ran. ${mechanism} Delete the key.`;

Expand Down
Loading
Loading