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 dbc77fa
Browse filesBrowse the repository at this point in the historyBrowse files
feat(spec): a comment's author is told which @mentioned users will not be notified, and a mention added by editing is notified
10
+
11
+
Clause-②: yes
12
+
Metadata-protocol: added
13
+
14
+
The `@mention` recipient gate withholds a notification from a mentioned user who cannot read the comment or its record. Until now it only logged that, so the author believed the person had been told. The comment still saves with every mention; now the requester's own answer says who will not be notified.
15
+
16
+
-**Spec.**`CreateDataResponseSchema` and `UpdateDataResponseSchema` declare an optional, response-only `withheldMentions`: the ids of the users this write @mentioned whose notification the mention gate withheld. Ids only, never the reason. Only a `sys_comment` write fills it, only on the requester's own answer to that request, and the key is omitted when nobody was withheld. It is not a field of `sys_comment` and is never stored, since a stored member would tell every reader who cannot read the thread. `WriteObservabilityOptions` declares the in-process listener that carries it, `onMentionsWithheld(userIds)`, beside `onFieldsDropped` and `onValidationAdvisory`. Like them it never crosses the wire.
17
+
-**plugin-audit.** The mention writer reports the gate's own verdict to that listener, which it reads off its hook's `ctx.input.options` (the write's own options bag). Readability is not derived a second time, and who the gate admits is unchanged. The writer now also runs on `sys_comment``afterUpdate`, so a mention added by editing a comment is judged and notified like one written with it. An edit judges only the mentions it added; a mention the comment already carried is not judged or notified again. The editor, who may be a parent-record editor rather than the author, is the actor of an edit's notifications: they are named in the title and never notified themselves. `MentionGateInput` takes an optional `onWithheld(userId)` callback.
18
+
-**metadata-protocol.**`createData` and `updateData` pass the listener and answer the ids as `withheldMentions`. A nested write that a hook or a flow makes inside this one carries its own options, so its verdicts never reach this answer.
19
+
-**client.**`CreateDataResult` and `UpdateDataResult` declare `withheldMentions?: string[]`, mirroring the two schemas.
20
+
-**objectql.**`engine.update` accepts the `onMentionsWithheld` option. The engine never calls it; it carries it to the `after*` hooks with the rest of the caller's bag.
21
+
22
+
Nothing to do for existing clients: a client that does not read `withheldMentions` sees the same answer as before.
feat(spec): a report chart can split its bars by a second dataset dimension with `chart.seriesBy`
7
+
8
+
Clause-②: yes (widening)
9
+
Metadata-protocol: added
10
+
11
+
`ReportChartSchema` declares one optional key, `seriesBy`: the name of a second dimension of the report's bound dataset. It splits the chart's one `yAxis` measure into a series per value of that dimension, so a report can draw a bar per owner split by stage. It is the report's spelling of a dashboard widget's two-entry `dimensions` selection. With `seriesBy` set, a `chart.series[]` entry's `name` names a value of that dimension, because each derived series is one value.
12
+
13
+
-**Lint.**`os validate`, `os build` and the report publish door resolve `chart.seriesBy` against the dataset's declared dimensions exactly as they resolve `chart.xAxis`: an undeclared name is a `chart-dimension-unknown` error with the nearest declared dimension suggested. When `seriesBy` is set, `chart.series[].name` is no longer judged against the chart's measure, since it names a dimension value.
14
+
-**Liveness.** The key is graded `planned` with an author warning: this is the contract half, and the report chart still draws one series until the renderer reads `seriesBy` in a later change.
15
+
16
+
Nothing to do for existing reports. A chart without `seriesBy` parses and draws exactly as before.
feat(spec): a kanban board can choose which lanes it draws, and in what order, `kanban.lanes`
7
+
8
+
Clause-②: yes (widening)
9
+
Metadata-protocol: added
10
+
11
+
A board grouped on a select field drew one lane per option, always, so a lane the view's own filter keeps empty (a task board that filters out completed tasks still drawing its Completed lane) could not be left off, and the lanes could not be put in any order but the options' own. `KanbanConfigSchema` (the list view's `kanban` block) gains one optional key:
12
+
13
+
-**`lanes: string[]`.** The lanes the board draws, in this order, as stored option values of the `groupByField` field (the option `value`, not its label). For example `lanes: ['not_started', 'in_progress', 'waiting']` on a task board grouped by `status`.
14
+
-**A choice of lanes, not a record filter.** A record whose value is not listed lands in the board's `Uncategorized` lane, as a record whose value matches no lane already does. To keep records off the board, filter the view.
15
+
-**Absent means one lane per option.** The key has no spec-side default, so a board stored before it existed parses to exactly the block it stored and draws the lanes it always did.
16
+
-**Refused at parse:** an empty list (omit the key instead), and an entry that is not shaped like a stored option value (a display label such as `'In Progress'`).
17
+
-**Refused against the object** (`@objectstack/lint`, so `os validate`, `os build` and the runtime publish gate): a lane that is not an option of the group field (`list-view-kanban-lane-unknown`, which names the nearest option), and a value listed twice (`list-view-kanban-lane-repeated`, at the second entry). A misspelled lane would otherwise draw a lane no card can land in while every card with the option it meant falls into `Uncategorized`. Judged against the group field's inline `options`; a field whose options come from a shared `picklist`, and a group field with no options (a boolean, a lookup), are not judged. `os explain list-view-kanban-lane-unknown` (and `-repeated`) carries the reasoning.
18
+
19
+
The Console renderer reads the key in a follow-up change; until then a board that declares it still draws one lane per option.
Copy file name to clipboardExpand all lines: content/docs/references/api/protocol.mdx
+4-2Lines changed: 4 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -556,6 +556,7 @@ A write-path strip event: caller-supplied fields legally dropped from the payloa
556
556
|**record**|`Record<string, any>`| ✅ | The created record, including server-generated fields (created_at, owner). |
557
557
|**droppedFields**|`{ object: string; fields: string[]; reason: Enum<'readonly' \| 'readonly_when' \| 'primary_key' \| 'computed' \| 'stored_expression'> }[]`| optional | Write-observability: caller-supplied fields that were LEGALLY stripped before the record was written — a non-system create cannot seed a static `readonly` column (the strip runs inside `engine.insert`, after the `beforeInsert` hooks, `isSystem`-gated), so those keys are dropped and the field re-derives its default. Present ONLY when ≥1 field was dropped; the create still succeeded without them (status/success semantics unchanged). REST additionally surfaces this as the `X-ObjectStack-Dropped-Fields` response header. Optional — omit-when-empty keeps the shape backward-compatible for existing clients. |
558
558
|**warnings**|`{ field: string; code: string; message: string; rule?: string; … }[]`| optional | The advisory validation-rule hits of THIS write: each `severity: 'warning'` or `'info'` rule whose verdict on the written record was "violated", with its `rule` name, `severity`, `field`, `code` and author-written `message` — the same element the validate-only preview reports in a row's `warnings`. Advisory rules never block, so the write succeeded; this is the advisory to show the person who made it. Present ONLY when at least one advisory rule hit; a write with no hit omits the key. Hits of a nested write a hook or a flow made inside this one are not included, and a rule that could not be evaluated is not a hit. |
559
+
|**withheldMentions**|`string[]`| optional | The ids of the users this write @mentioned whose mention notification was WITHHELD by the mention recipient gate, so they will not be notified: the gate's own verdict on THIS write, ids only, never the reason. Only a `sys_comment` write fills it, and only on the requester's own answer to the request that made the write; on an update, only a mention the update added is judged. The comment was saved with every mention; the requester is told here that the named users will not be notified. Response-only: never stored on the comment (a stored member would tell every reader who cannot read the thread). Present ONLY when at least one mention was withheld; otherwise the key is omitted. |
|**record**|`Record<string, any>`| ✅ | Updated record |
3042
3043
|**droppedFields**|`{ object: string; fields: string[]; reason: Enum<'readonly' \| 'readonly_when' \| 'primary_key' \| 'computed' \| 'stored_expression'> }[]`| optional | Write-observability: caller-supplied fields the engine LEGALLY stripped from the write before persisting — static `readonly` or a TRUE `readonlyWhen` predicate. Present ONLY when ≥1 field was dropped; the update still succeeded without them (status/success semantics unchanged — stripping is legitimate, not an error). REST additionally surfaces this as the `X-ObjectStack-Dropped-Fields` response header. Optional — omit-when-empty keeps the shape backward-compatible for existing clients that only read `record`. |
3043
3044
|**warnings**|`{ field: string; code: string; message: string; rule?: string; … }[]`| optional | The advisory validation-rule hits of THIS write: each `severity: 'warning'` or `'info'` rule whose verdict on the written record was "violated", with its `rule` name, `severity`, `field`, `code` and author-written `message` — the same element the validate-only preview reports in a row's `warnings`. Advisory rules never block, so the write succeeded; this is the advisory to show the person who made it. Present ONLY when at least one advisory rule hit; a write with no hit omits the key. Hits of a nested write a hook or a flow made inside this one are not included, and a rule that could not be evaluated is not a hit. |
3045
+
|**withheldMentions**|`string[]`| optional | The ids of the users this write @mentioned whose mention notification was WITHHELD by the mention recipient gate, so they will not be notified: the gate's own verdict on THIS write, ids only, never the reason. Only a `sys_comment` write fills it, and only on the requester's own answer to the request that made the write; on an update, only a mention the update added is judged. The comment was saved with every mention; the requester is told here that the named users will not be notified. Response-only: never stored on the comment (a stored member would tell every reader who cannot read the thread). Present ONLY when at least one mention was withheld; otherwise the key is omitted. |
0 commit comments