Skip to content

Commit 571a496

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/project-thread-v8ndrp
2 parents b0fe67b + 91e4652 commit 571a496

99 files changed

Lines changed: 3624 additions & 259 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: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/service-automation': minor
4+
---
5+
6+
feat(spec): `tencentcloud-tc3-hmac-sha256` joins the `signed` connector-instance auth schemes (ADR-0143 D2.3, #26710)
7+
8+
Clause-②: yes (widening)
9+
Metadata-protocol: added
10+
11+
<!-- adr-0087: not-required (no-migration-prescription) One member is added to the closed signing-scheme list and one shape to the `signed` union; the signer seam's `scheme` member is typed as that list instead of any string; that interface is in no release yet (it ships with the pending `26710-signed-connector-auth-aliyun` changeset), so no published surface narrows. Nothing an author wrote before is refused or re-shaped, so there is nothing for a migration to rewrite. -->
12+
13+
A declarative connector instance can now declare Tencent Cloud's API 3.0 signature (v3) as its signing scheme. It joins Alibaba Cloud's V3 signature, which shipped with the `signed` kind.
14+
15+
- **`@objectstack/spec`**
16+
- `ConnectorSigningSchemeSchema` gains `tencentcloud-tc3-hmac-sha256`.
17+
- The `signed` union gains its closed shape, `{ type: 'signed', scheme: 'tencentcloud-tc3-hmac-sha256', keyId, credentialRef }`. `keyId` is the SecretId. The SecretKey is only ever a `credentialRef`.
18+
- The same publish refusals apply as for the first scheme: an inline key spelling by name, an authored `signature`, and `signed` on `provider: 'mcp'`. `secretId` is answered with `keyId`. An unknown scheme's refusal now lists both shipped schemes.
19+
- `ConnectorRequestSigner.scheme` is typed as `ConnectorSigningScheme` rather than `string`. The interface ships for the first time in this release, together with the first scheme.
20+
- **`@objectstack/service-automation`**
21+
- The signer registry binds a `tencentcloud-tc3-hmac-sha256` instance's key to its TC3-HMAC-SHA256 signer.
22+
- Every attempt is signed with a fresh `X-TC-Timestamp`, over `content-type` and `host`. The scope's service is the first label of the endpoint host.
23+
- A request without a content type is signed and sent as `application/x-www-form-urlencoded`.
24+
- The key never leaves the host.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/skills': minor
4+
---
5+
6+
feat(spec): `currencyConfig.homeAmount: { field, rateDate, rateField }` — a record-mode amount declares its stored home-currency amount by pointer (ADR-0144 D5)
7+
8+
Clause-②: yes (widening)
9+
Metadata-protocol: added
10+
11+
⛔ Not released alone: this ships in the same release as the write-time home-currency projection that fills these fields (ADR-0144 D9, release R2: "the `homeAmount` key never ships ahead of the engine").
12+
13+
An amount in `currencyMode: 'record'` can now declare where its value is stored after conversion into the organization's home currency (the `localization.currency` setting), which date picks the exchange rate, and where the rate used is stored. Consolidated totals then aggregate the home field like any other stored currency field.
14+
15+
```ts
16+
fields: {
17+
currency_code: Field.text({ label: 'Currency', valueDomain: 'iso_4217_currency' }),
18+
invoice_date: Field.date({ label: 'Invoice date' }),
19+
amount: Field.currency({
20+
label: 'Amount',
21+
currencyConfig: {
22+
currencyMode: 'record',
23+
currencyField: 'currency_code',
24+
homeAmount: { field: 'amount_home', rateDate: 'invoice_date', rateField: 'amount_rate' },
25+
},
26+
}),
27+
amount_home: Field.currency({ label: 'Amount (home)', readonly: true }),
28+
amount_rate: Field.number({ label: 'Rate used', readonly: true }),
29+
}
30+
```
31+
32+
- **Three required pointers.** When `homeAmount` is declared, `field`, `rateDate` and `rateField` must all be given, and none may be empty. There is no default, no injected column and no fixed field name. The document date is never taken from `created_at`. `homeAmount` is refused under `dynamic` and `fixed`.
33+
- **What the pointers name, refused at parse with a remedy and the object's candidates.** Each refusal is located at `fields.AMOUNT.currencyConfig.homeAmount.POINTER`.
34+
- `field` must name another `currency` field of the same object in `dynamic` mode (it displays in the home currency, so metadata never names that currency). No other amount may already store into it.
35+
- `rateDate` must name a `date` or `datetime` field.
36+
- `rateField` must name a `number` field. Two amounts may share it only when they have the same `currencyField` and `rateDate`.
37+
- The amount itself must be a stored `currency` field. A `formula` amount is refused.
38+
- **The home and rate fields are written by the platform only.** They must declare `readonly: true`, the existing mark that drops a client's write of the field, and no `expression`.
39+
- **Skills:** `objectstack-data/rules/field-types.md` teaches `homeAmount`. Its field names (`amount_home`, `amount_rate`) are examples, not a contract.
40+
- **Nothing to migrate.** `homeAmount` is new, and no existing config changes meaning.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
'@objectstack/service-datasource': patch
3+
'@objectstack/cli': patch
4+
'@objectstack/spec': patch
5+
---
6+
7+
feat(service-datasource): a datasource defined in code reads its credential at connect from `OS_DATASOURCE_<NAME>_PASSWORD`
8+
9+
Clause-②: yes (widening) — `@objectstack/service-datasource` exports `datasourceEnvCredentialVar`, `resolveDatasourceEnvCredentialsSwitch`, `DATASOURCE_ENV_CREDENTIALS_SWITCH` and the `DatasourceEnvCredentialSource` type, and `DatasourceConnectionServiceConfig` gains the optional `envCredentials`; `@objectstack/spec/data` exports `CODE_DEFINED_CREDENTIAL_ROUTE`. Nothing an author writes is added or refused.
10+
Metadata-protocol: none
11+
12+
A datasource declared in code (`defineDatasource`, or a package's `*.datasource.ts`) had no way to get its password: the driver contracts refuse `config.password` and a `user:password@` URL, the Setup → Datasources form cannot edit a code datasource, and nothing mints a `sys_secret:` handle for one. Now the credential stays out of the declaration and is read at connect from a variable derived from the datasource name: `warehouse` reads `OS_DATASOURCE_WAREHOUSE_PASSWORD` (upper-cased; every character outside `A-Z0-9` becomes `_`). The value is the connection's secret: the password for postgres, mysql and mongodb, the `authToken` for turso.
13+
14+
- **A host switch.** `OS_DATASOURCE_ENV_CREDENTIALS` takes `on` or `off`; any other value refuses the boot with a sentence. `os serve` (and so `os start` and `os dev`) turns it on unless the operator set it. Every other composition leaves it off, so a host that loads several tenants' artifacts into one process never reads the variable.
15+
- **Code datasources only, by provenance.** The variable is read on the boot's declared connect, for a name in the host's code-datasource set: the booted artifact's datasources and those of a package installed locally. A datasource created through Setup or the API under the same name never reads it, and neither does the `default` datasource, which keeps `OS_DATABASE_URL`. A record's own `origin` is never consulted.
16+
- **A declared credential wins.** With `external.credentialsRef` or `external.connector` declared the variable is not read, and a reference that fails to resolve stays fail-closed.
17+
- **Unset.** The connect goes ahead without a secret as before; when the database then refuses, the reason names the variable to set.
18+
- **Two names deriving one variable** are refused at their connect.
19+
- **Who can read it.** Any code datasource the host registers under a name reads that name's variable, including one in an installed package. Install only packages you trust with the credentials in the process environment.
20+
21+
The credential refusals (`config.password`, a URL userinfo password, a credential query parameter, the mongo `options` passthrough) and the two `password` guidance entries on `DatasourceSchema` / `ExternalDatasourceSettingsSchema` end with one shared sentence naming this route, after the Setup and `external.credentialsRef` routes they already name. `planCredentialMigration`'s remedy for a code-defined row names the row's variable.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@objectstack/platform-objects': patch
3+
---
4+
5+
feat(platform-objects): the platform identity actions an agent may reach declare it — additive acts exposed, destructive lifecycle acts exposed behind confirmation, credential and ownership acts human-only
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The `sys_*` identity objects' actions now carry the ADR-0011 `ai` block by risk, as ADR-0011's 2026-10-30 addendum records (58 actions assessed, 38 declared):
11+
12+
- **Exposed (21).** Additive or self-scoped acts declare `ai: { exposed: true, description }`: the four `invite_user` mirrors, resending and accepting an invitation, adding a member to an organization or a team, creating, editing and renaming a team or an organization, switching the active organization, re-enabling an OAuth application, enabling two-factor authentication, updating your own profile, resending the verification email, and requesting SSO domain verification.
13+
- **Exposed, confirmation required (17).** Destructive or privilege-changing lifecycle acts also declare `requiresConfirmation: true`, so MCP `run_action` refuses them `428 ACTION_CONFIRMATION_REQUIRED`, with nothing run, until the call carries `confirm: true`: banning, unbanning and unlocking a user, setting a user's manager, revoking an API key or a session, signing out other devices, cancelling or declining an invitation, removing a member or a team member, deleting a team, an OAuth application or an SSO provider, disabling an OAuth application, leaving an organization, and unlinking a sign-in provider.
14+
- **Human-only (20).** Acts that mint a credential or a session, change ownership, a role or a sign-in factor, or destroy an account or an organization (impersonating a user, setting a password, creating a user or an API key, transferring ownership, changing a member's role, deleting an organization, and the rest the addendum names) declare no `ai` block and are not listed over MCP. `sys_invitation.copy_invitation_link` declares none either; it was not part of the assessment.
15+
16+
Over MCP these actions are listed and run behind every existing gate: the capability gate, the installation's action switch, the param contract, the confirmation gate and the identity route's own authorization. They are `type: 'api'` actions forwarded to identity routes that accept a signed-in session only, so an agent connected over OAuth sees them listed and is refused at the route when it runs one.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
feat(spec): a calendar view can declare the date it opens on, `calendar.initialDate`
6+
7+
Clause-②: yes (widening)
8+
Metadata-protocol: added
9+
10+
A calendar view used to open on the current month, always, so a calendar whose records all fall months ahead (a renewals calendar) opened on an empty grid and the author had no key to say otherwise. `CalendarConfigSchema` (the list view's `calendar` block, and the `object-calendar` node's `calendar` prop, which is the same block by reference) gains one optional key:
11+
12+
- **`initialDate`, one of two forms.** A record anchor: `'first_record'` opens on the period containing the earliest start date (the `startDateField` value) among the records the view selects, and `'next_record'` on the period containing the earliest start date on or after today among those records; when no record qualifies, the calendar opens on today. Or a wrapped date-macro token, the same formula vocabulary filter values and a dashboard date filter's `defaultValue` already accept (`DATE_MACRO_TOKENS` and the parameterised `{N_unit_ago}` / `{N_unit_from_now}` grammar): `'{today}'`, `'{next_month_start}'`, `'{current_quarter_start}'`, `'{3_months_from_now}'`, resolved each time the calendar opens. The period is the month, week or day the active view mode shows.
13+
- **Absent means today.** The key has no spec-side default, so a calendar stored before it existed parses to exactly the block it stored and opens where it always did. There is no bare `'today'`: `'{today}'` is its one spelling.
14+
- **What is refused.** A fixed calendar date (`'2026-11-01'`) or timestamp, an unwrapped token (`'today'`), an unknown token (`'{next_moon}'`) and any other spelling are refused at `calendar.initialDate`. The message names both forms and points to `DATE_MACRO_TOKENS`, and a bare known token is told the wrapped spelling to write. A fixed date goes stale as time passes; a macro does not.
15+
16+
The Console renderer reads the key in a follow-up change; until then a calendar that declares it still opens on today.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
'@object-ui/i18n': patch
4+
---
5+
6+
fix(console): a metadata form row the served form marks required holds Save when it is left empty
7+
8+
Clause-②: no
9+
Metadata-protocol: none
10+
11+
A served metadata form row's `required: true` is a layout override, and the type's schema may leave the field optional: the permission set's Label is the case today. The Console drew such a row with a required star only, so the create page saved it empty, because its checks read the schema and the server accepts the body.
12+
13+
Now the metadata create and edit page judges those rows itself, for every metadata type. A row the form offers (in a visible section, not hidden, editable) whose served row says `required: true` and whose value is empty shows "This field is required." at that field, and Save sends no request until it is filled. A row in a collapsed section or another tab still counts; a hidden or read-only row never does, so Save is never held on a field the author cannot fill. A field the schema itself requires keeps the verdict it had before, and a field the schema gives a default is never empty. The server's accept sets do not change.
14+
15+
`SchemaForm` gains an optional `onRequiredRowFieldsChange(fields)` prop that reports those rows.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@objectstack/skills': patch
3+
---
4+
5+
docs(skills): the field-types guide names the backfill for an amount switched to record mode; the dashboards guide says a record-mode amount aggregate is served only grouped by its code field
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **`objectstack-data/rules/field-types.md`**: an amount field with stored rows that is switched to `currencyMode: 'record'` is backfilled with `os migrate record-currency-codes`. The derived-measure sentence moves out of this guide; it already sits in the dashboards guide, which the line now points to.
11+
- **`objectstack-ui/rules/dashboards.md`**: a record-mode amount refuses `sum` / `avg` / `min` / `max` unless grouped by its code field. The old "ungrouped" read as if any grouping would do.

‎content/docs/ai/connect-mcp.mdx‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,20 @@ can reach is the **default exposure policy (v1)**:
261261
The stored-metadata tables (`sys_metadata`, `sys_metadata_history`) stay
262262
refused even when flagged. Each action `list_actions` returns carries
263263
`aiExposed: true`, the server's attestation of that opt-in.
264+
- **The platform's own identity actions declare it by risk.** Additive acts
265+
(inviting someone, adding a member to a team, creating or renaming a team or
266+
an organization, re-enabling an OAuth application) are exposed. Destructive
267+
lifecycle acts (banning a user, revoking a session or an API key, removing a
268+
member, deleting a team, leaving an organization) are exposed with
269+
`ai.requiresConfirmation: true`, so `run_action` refuses them until the call
270+
carries `confirm: true`. Acts that mint a credential or a session, change
271+
ownership or a sign-in factor, or destroy an account or an organization
272+
(impersonating a user, setting a password, creating an API key, transferring
273+
ownership, deleting an organization) declare nothing and are not listed. The
274+
full list is ADR-0011's 2026-10-30 addendum. These actions forward to
275+
identity routes that accept a signed-in session only, so an agent connected
276+
over OAuth sees them listed, but a call that reaches the identity route is
277+
refused there (`401`) and changes nothing.
264278

265279
<Callout type="info">
266280
This is the default policy, not a ceiling. A finer, **metadata-declared** exposure

0 commit comments

Comments
 (0)