Skip to content

Commit d548ee9

Browse files
committed
Merge origin/main into claude/issue-28404-override-declaration
Regenerate the spec artefacts from the merged tree. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
2 parents 862035a + 9b1dc99 commit d548ee9

111 files changed

Lines changed: 3255 additions & 287 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/skills': patch
3+
---
4+
5+
docs(skills): the `os package publish` row of objectstack-platform's operations reference points app authors at the Marketplace handbook
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Before.** The platform skill's operations reference described `os package publish` as an upload to the cloud catalog and nothing more. An agent authoring an app from the skills never found the page that says what a listing is made of (`objectstack.manifest.json`, the README), what review checks, and what the customer's administrator goes through after installing.
11+
- **Now.** That row names the listing file, the README, review and the administrator's screens, and links `https://objectstack.ai/docs/deployment/marketplace-handbook`.
12+
- Paid for under the skills token ratchet by deleting the cloud-connection note's tail (the `LocalManifestSource` ledger and runtime-identity bind v2), plugin internals an app author never acts on. The file stays under its ceiling; no ceiling moves, no frontmatter, schema, key or behaviour changes.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
'@object-ui/core': patch
3+
'@object-ui/react': patch
4+
'@object-ui/fields': patch
5+
---
6+
7+
fix(core,react): an action's `description`, a param's `helpText` and an `outcomeMessages` entry read the `template` envelope
8+
9+
Clause-②: no
10+
Metadata-protocol: none
11+
12+
objectstack#27948 step 2 types these three as `template` slots, so a served action can carry `{ dialect: 'template', source }` where it carried a string.
13+
14+
- **Param dialog.** The runner hands the param-collection handler the action and params with `description` and each `helpText` reduced to their text (`actionCopyText`), so the dialog draws the sentence, never `[object Object]`; a locale map is handed on unchanged for the host to localize.
15+
- **Outcome toast.** `useActionTextLocalizer` keeps an `outcomeMessages` envelope's shape (a bundle entry replaces its `source`) instead of collapsing it to the word "template", and the runner fills the envelope's holes as it fills the string's.
16+
17+
- **Param fields.** `@object-ui/fields`' `resolveActionParams` reads a param's `helpText` through `actionCopySource`, so an envelope yields its sentence (a field-backed param with no text falls back to the field's own help) and `ActionParam` stays assignable to its `RawActionParam`.
18+
19+
- **Studio's action preview** (`@object-ui/app-shell`, unpublished) draws a param's `helpText`, `confirmText` and `successMessage` from the envelope's `source`: it drew `helpText` raw (an object as a React child) and read the other two as a locale map, which showed the word "template".
20+
21+
What to do: nothing. `${…}` copy written before the migration keeps reading.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/lint': minor
4+
'@objectstack/platform-objects': patch
5+
'@objectstack/runtime': patch
6+
'@objectstack/cli': patch
7+
---
8+
9+
feat(spec)!: an action's `description`, each param's `helpText` and each `outcomeMessages` entry are `template` slots; the doors refuse `${…}` there
10+
11+
Clause-②: yes (narrowing)
12+
Metadata-protocol: narrowed
13+
14+
<!-- adr-0087: registered action-dialog-outcome-copy-template-holes -->
15+
16+
Step 2 of #27948 (ruling A): the five action copy keys speak one hole syntax, ADR-0032's `{{ }}`. `confirmText` and `successMessage` already did (#24814); the param dialog's `description`, each param's `helpText` and each `outcomeMessages` entry now join them.
17+
18+
- **The parse.** Each of the three is typed as `confirmText` is: a bare string is shorthand for `{ dialect: 'template', source }` and parses to that envelope; the envelope is taken; `description` and an `outcomeMessages` entry keep the inline locale map they took as an `I18nLabel`, and `helpText` stays without one (it was a plain string). A blank string and a `${…}` sentence still parse, so a stored action loads.
19+
- **The roots.** `description` and `helpText` read `confirmText`'s roots: `record`, the record the action runs against (a registered action's `params` is the input list the dialog collects, so it binds nothing). An `outcomeMessages` entry reads `successMessage`'s: `result`, the handler's answer, and `record`.
20+
- **The doors.** `objectstack validate`, `compile` and the publish gate (422) judge each sentence through the same judge as `confirmText`: a `${…}` token, a single-brace `{x}` token, a hole over a root the runner does not bind, a hole holding logic or an unknown formatter, and a blank sentence are errors, each named at its own path (`description`, `params[N].helpText`, `outcomeMessages.OUTCOME`) with its `{{ }}` spelling. An inline `properties.action` is judged on its params' `helpText`.
21+
- **The readers.** The CLI's i18n extraction seeds the envelope's text exactly as the bare string's, and the MCP `list_actions` param description reads `helpText`'s text, never the object.
22+
- **The platform's own copy** is rewritten: the invite actions' outcomes (`Invitation sent to {{ result.email }}`), `resend_invitation`'s and `update_member_role`'s descriptions, the role-change `permission_set_granted` outcomes, and their four translation bundles.
23+
24+
What to do (FROM → TO, on `description`, `params[].helpText` and `outcomeMessages` entries):
25+
26+
- `Resend the invitation to ${record.email}?` → `Resend the invitation to {{ record.email }}?`
27+
- `Invitation sent to ${result.email}` → `Invitation sent to {{ result.email }}`
28+
- `${record.owner?.name ?? 'nobody'}` → `{{ record.owner.name | default:'nobody' }}` (the `default` formatter also covers an empty value)
29+
- other logic in a `${…}` token moves into the handler's answer and is read from `result`.
30+
31+
The console keeps filling `${…}` in a stored sentence with no `{{ }}` hole, so stored copy reads as before until its next save, which is judged. `os migrate meta` lists the change (`action-dialog-outcome-copy-template-holes`); no conversion is registered, since a `${…}` token held an expression and a hole holds only a path and formatters.
32+
33+
A TypeScript reader of a parsed `ActionSchema` now meets the envelope on these three keys: read its text with `templateSlotSource` (`@objectstack/spec/automation`).
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/objectql': patch
4+
'@objectstack/metadata-protocol': patch
5+
'@objectstack/rest': patch
6+
---
7+
8+
feat: the object read names the objects a delete can cascade into, beside the item
9+
10+
Clause-②: yes (widening)
11+
Metadata-protocol: added
12+
Skills-docs: none — `cascadeDeleteObjects` is a served-only key beside the object read with no authorable spelling; no file under `skills/**` teaches the object read's envelope keys (a `git grep` of `skills/` for `cascadeDeleteObjects` and `disabledActions` names nothing), and every taught `deleteBehavior` use stays true.
13+
14+
- **Served beside the object** (`@objectstack/metadata-protocol`, `@objectstack/rest`). `GET /api/v1/meta/object/:name` serves `cascadeDeleteObjects` beside `item`: the names of the objects that deleting a record of this object can also delete. That is every object whose relation into it cascades (a `master_detail` by default, a `lookup` or `tree` that declares `deleteBehavior: 'cascade'`), and the objects that cascade into those in turn, each once. A `restrict` or `set_null` relation removes nothing and is not named. The default cached door carries it too, and the list is folded into that door's ETag, so an object registered later that cascades into this one changes the ETag. The key is absent where no engine answered, on a draft read, on the list read and on every type other than `object`; `[]` means nothing cascades. `item` is unchanged. A delete confirmation can now name what else a delete removes without keeping its own copy of the rule.
15+
- **The fact it serves** (`@objectstack/objectql`). `ObjectQL.getCascadeDeleteObjects(object)` answers that list from the delete walk's own relation scan, which the delete and this read now share. It reads the registry and no row, so it never says which records or how many. It returns `undefined` for an object the registry does not hold.
16+
- **The declaration** (`@objectstack/spec`). `GetMetaItemResponseSchema` and `GetMetaItemCachedResponseSchema` gain the optional `cascadeDeleteObjects` (an array of object names), and `IObjectQLEngine` gains the optional member `getCascadeDeleteObjects(objectName)`, which the metadata read calls through the kernel's service table. Nothing about it is authorable.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/lint': minor
4+
---
5+
6+
feat(spec): a form's thank-you panel can name the record's engine-generated fields, and the public submit answer has a declared shape
7+
8+
Clause-②: yes (widening)
9+
Metadata-protocol: added
10+
11+
A public form's confirmation panel could not tell a web-to-case visitor their case number: `title` and `message` were plain text, and `POST /api/v1/forms/SLUG/submit` answers `{ id }` only.
12+
13+
- **The slot.** A form's `submitBehavior: { kind: 'thank-you', title, message }` may name a field of the record just submitted as `{{record.field_name}}`, the same token the `redirect` behavior's `url` takes. Any other brace is refused at parse, naming the offending text: `{record.x}`, `{{ record.x }}` and `{{case_number}}` are not the token.
14+
- **Only engine-generated fields.** A named field must be one whose value the engine generates on insert: its type is in `RUNTIME_OWNED_FIELD_TYPES` (today `autonumber`). Any other stored value may have been set by a `beforeInsert` / `afterInsert` hook from records an anonymous visitor cannot read. `os validate`, `os build`, `os lint` and the publish gate on a `view` or `page` write refuse any other name (rule `thank-you-record-field`), naming the field and its type. This covers every form view shape and a page `object-form` component.
15+
- **The answer shape.** `PublicFormSubmitResponseSchema` (`@objectstack/spec/api`) declares the submit answer as `id` plus an optional `fields` map holding exactly the named fields, and nothing else. `thankYouRecordFieldNames` (`@objectstack/spec/ui`) reads which fields a panel names.
16+
17+
The server fills `fields` and the console fills the tokens in follow-up releases. Until then, a panel that names a field shows the token text as written.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/skills': patch
3+
---
4+
5+
docs(skills): the skills teach the third `fixed`-currency refusal, a derived measure's own `currency` against its operands' one `fixed` code
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- `objectstack-data` `rules/field-types.md` (Currency resolution) and `objectstack-ui` `rules/dashboards.md` (the dataset currency comment) taught two `fixed` refusals and missed the third: a derived `sum` or `difference` whose monetary operands all resolve to one `fixed` code refuses another `currency` at dataset compile, with `DATASET_INVALID` / 400. Both files now teach it beside the other two, with the shipped prescription: delete the measure's `currency` (the column takes the operands' code), or restate that code.
11+
- Paid for inside each file: `field-types.md` drops the `currency_code` naming convention its own example already shows; `dashboards.md` drops the retired `categoryGranularity` parenthetical, which the parse refusal and the Dataset-Bound Widgets paragraph already carry. Neither ratchet row moves.
12+
- No schema, key or behaviour changes.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/skills': patch
3+
---
4+
5+
docs(skills): objectstack-data teaches the code-defined datasource credential route (`OS_DATASOURCE_<NAME>_PASSWORD`, behind the host switch)
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Before.** `rules/datasources.md` taught `credentialsRef` and `connector` as the only credential channels. A datasource defined in code has no `sys_secret:` handle to point at (the Setup form refuses a code-origin datasource, and nothing else mints one), so an agent following the rule wrote a reference the connect could never resolve.
11+
- **Now.** The credential section says a code-declared datasource reads its password at connect from `OS_DATASOURCE_<NAME>_PASSWORD` (the name upper-cased) while the host switch `OS_DATASOURCE_ENV_CREDENTIALS` is `on`; that `os serve`, `os start` and `os dev` default it on, a library composition leaves it off, and the switch takes `on` or `off` only; that a declared `credentialsRef` or `connector` still wins and never falls back to the variable; and the operator's caution that an installed package may declare a datasource of any free name and so read that variable.
12+
- The file stays under its token ceiling: the new text is paid for by deleting restatements in the same file (the field-reference pointer the references index already carries, the `writable` and `columnMap` restatements of their own sections, the `datasource` field default the SKILL.md field table carries, and two detail clauses). No ceiling moves, no frontmatter, schema or behaviour changes.

‎content/docs/api/data-api.mdx‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -518,12 +518,18 @@ monetary measure column takes the measure's own `currency`; then, only when the
518518
field is `currencyMode: 'fixed'`, that field's `currencyConfig.defaultCurrency`; then the
519519
tenant default currency (the `localization.currency` setting), if one is set. A
520520
`dynamic` field (the default mode) never lends its `defaultCurrency` — only `fixed` does —
521-
so its column shows the tenant default. The cube query on this page carries no column
522-
`currency`.
521+
so its column shows the tenant default. A derived `sum` or `difference` whose monetary
522+
operands all resolve to one `fixed` code takes that code at the field's step; any other
523+
derived measure (a `ratio`, a `product`, operands in the tenant's currency) is monetary
524+
only by its own `currency`, else its column carries none. The cube query on this page
525+
carries no column `currency`.
523526

524527
Over a `fixed` field the measure's `currency` may only restate the field's code. A
525528
different code is refused when the dataset compiles, with `DATASET_INVALID` / 400: delete
526-
the measure's `currency`, and the column takes the field's code. A derived measure whose
529+
the measure's `currency`, and the column takes the field's code. A derived `sum` or
530+
`difference` whose operands resolve to one `fixed` code is held to the same rule: a
531+
different `currency` is refused with `DATASET_INVALID` / 400 — delete the measure's
532+
`currency` (the column takes the operands' code), or restate it. A derived measure whose
527533
monetary operands resolve to two different `fixed` codes is refused the same way, whatever
528534
the operation: derive it from measures in one currency, or drop the operand in the other.
529535
Neither a `fixed` code against the tenant default nor a measure `currency` over a

‎content/docs/data-modeling/currency.mdx‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,12 @@ that column. A `count` column never carries `currencyField`. See the
178178
- **A derived measure whose monetary operands resolve to two different `fixed` codes** is
179179
refused at compile with `DATASET_INVALID` / 400, whatever the operation. Derive it from
180180
measures in one currency, or drop the operand in the other. A derived `sum` or
181-
`difference` over one `fixed` code carries that code, so a measure built on it is judged
182-
the same way.
181+
`difference` over one `fixed` code carries that code, on its result column too, so a
182+
measure built on it is judged the same way.
183+
- **A derived `sum` or `difference` whose `currency` differs from its operands' one `fixed`
184+
code** is refused at compile with `DATASET_INVALID` / 400: `currency: 'USD'` over two
185+
measures fixed to `EUR` would print euros as dollars. Delete the measure's `currency`, and
186+
the column takes the operands' code, or restate that code.
183187
- **Not judged:** a `fixed` code against the tenant currency, which compile does not know,
184188
and a measure `currency` over a `dynamic` field, which wins as the top of the currency
185189
chain.

‎content/docs/deployment/cli.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,7 @@ os compile --json # JSON output for CI pipelines
771771
→ Normalizing stack definition...
772772
→ Lowering inline handlers...
773773
→ Validating protocol compliance...
774-
→ Running author-time rules (56)...
774+
→ Running author-time rules (57)...
775775
→ Checking that every required capability has a provider installable in this edition...
776776
→ Collecting package docs (ADR-0046)... 0 collected
777777
→ Writing artifact...

0 commit comments

Comments
 (0)