Skip to content

Commit 4d72947

Browse files
committed
Merge origin/main into claude/project-thread-firh6t
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FEkQ5nM7fbhbE84nNy6VkE
2 parents fdcf1a3 + ce9bc10 commit 4d72947

38 files changed

Lines changed: 1135 additions & 136 deletions
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@object-ui/components': patch
3+
---
4+
5+
fix(components): the `filter-builder` renderer calls `onChange` with the `FilterGroup` that `FilterBuilderSchema.onChange` declares
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
`FilterBuilderSchema.onChange` declares `(filter: FilterGroup) => void`, but the `filter-builder` renderer typed its own `onChange` as `(event: any) => void` and called it with a form-style `{ target: { name, value } }` event. A handler written from the declaration read `.logic` and `.conditions` off that event and got `undefined`, and the `any` kept the compiler from reporting the mismatch. The renderer now takes the schema's own `onChange` type and passes the group it holds, so the declaration is the contract on both sides. No in-repo host bound the event shape. A host that read `event.target.value` reads the argument itself instead.
11+
12+
The builder's own rows also admit the opt-in `exists` / `notExists` operators, which a `FilterGroup` cannot carry. The renderer no longer forwards a host's `extraOperators` to the builder, since `FilterBuilderSchema` declares no such key, so the node never offers them. The `FilterGroup` zod mirror refuses them in an authored `value`. If a stored value that skipped that validation still carries one, the renderer does not call `onChange` and logs a console error naming the operator, instead of handing on a group the declared type does not describe.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
fix(spec)!: every filter door refuses a `{ $now, addDays }` anchor that carries any other key, as `NowAnchorSchema` already declares, instead of running it on the `addDays` reading alone
6+
7+
`NowAnchorSchema` is closed: `{ $now: true, addDays: -30, subDays: 7 }` fails to parse on its own. But the comparand-type walk every filter door runs (`normalizeFilterComparandTypes`, through `FilterConditionSchema` and `parseFilterAST`) judged an anchor by its `addDays` alone, so the extra key was kept, every executor ignored it, and the query answered the `addDays` reading with no word. The walk now refuses an anchor with any key other than `$now` and `addDays`, with the existing `INVALID_FILTER` / 400 envelope, and the refusal names the key: `it carries an undeclared key "subDays" — the anchor takes $now and addDays only (there is no subDays: a negative addDays subtracts)`.
8+
9+
Clause-②: no (narrowing) — every filter door refuses a `$now` anchor carrying a key beyond `$now` / `addDays`, which it ran before. Nothing is widened.
10+
11+
Metadata-protocol: narrowed (a filter saved through `FilterConditionSchema` may no longer carry a key beside `$now` and `addDays` on a now-anchored comparand)
12+
13+
<!-- adr-0087: not-required (no-migration-prescription) No key of any schema is added, removed or renamed: NowAnchorSchema declared exactly $now and addDays since it shipped, and this aligns the walk with that declaration. An extra key never had a meaning at any executor, so there is no rewrite that keeps what the author meant; the author decides, and the refusal names the key. -->
14+
15+
**BREAKING** accept-set narrowing at the filter comparand-type walk (`@objectstack/spec`), served at every filter door.
16+
17+
- **Before.** `engine.find()` / `count()` / `update()` in object or array form, `aggregate()`'s `where`, `aggregations[i].filter`, `having`, and a filter saved through `FilterConditionSchema` all kept `{ $now: true, addDays: N, subDays: M }` or `{ …, note: 'x' }`. The query ran on `addDays` alone.
18+
- **Now.** Each of them answers `INVALID_FILTER` / 400 (a save fails its parse), naming the undeclared key.
19+
- **Unchanged.** A well-formed anchor, `{ $now: true, addDays: <integer | { $field }> }`, and its resolved form (`$now` holding the pinned ISO instant) run as before at every door. The refusals for a missing or malformed `addDays` keep their wording.
20+
- **Who sees it.** An author who wrote an extra key on an anchor. That key was ignored before, so the rows answered were the `addDays` rows.
21+
22+
**The remedy.** Drop the extra key. To move the instant back, give `addDays` a negative value; to read the offset off a column, use `addDays: { $field: 'numeric_column' }`.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@objectstack/example-showcase': patch
3+
---
4+
5+
Clause-②: no
6+
Metadata-protocol: none
7+
8+
The showcase's record actions now say which record they changed. Mark Done, Recalculate Estimate,
9+
Recalculate Selection, Archive and Submit for Sign-off name the task's title (or the invoice number)
10+
in their success toast, through a template hole over the dispatched record, so "Task marked done."
11+
becomes "PII access review marked done.". When the dispatch carries no title (a list row whose view
12+
does not show the column), each message falls back to the object wording it used before. The zh-CN
13+
bundle entries for the four task actions carry the same hole, so a zh-CN viewer is told the record too.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@objectstack/service-automation': patch
3+
'@objectstack/runtime': patch
4+
---
5+
6+
fix(service-automation): switching a packaged flow off now records which administrator did it
7+
8+
Clause-②: yes (widening)
9+
Metadata-protocol: none
10+
11+
The flow toggle (`POST /api/v1/automation/:name/toggle`) writes a row in the activation ledger (`sys_metadata_activation`). Until now, that row and its audit entry named nobody: `created_by` / `updated_by` on the ledger row and `user_id` / `actor` on the `sys_audit_log` row were null, even when a signed-in administrator made the request. The action and security catalog doors were fixed in the previous release; the flow door reaches the ledger through `IAutomationService.toggleFlow`, which could not carry the caller until its `caller` parameter landed.
12+
13+
- `@objectstack/runtime`: the flow toggle door passes `toggleFlow(name, enabled, { userId })`, the signed-in user's id and nothing else.
14+
- `@objectstack/service-automation`: `AutomationEngine.toggleFlow(name, enabled, caller?)` implements the contract's `caller`. The ledger write runs as that user, elevated and with no positions or permissions, so the engine and the audit plugin record the user's id on both rows. Only `userId` is read from `caller`. With no caller, or a caller without an id, the write stays unattributed, as before. `FlowActivationStore.setActive(row, options?)` takes the same optional `{ context }` as the shared activation store in `@objectstack/core`; a store that ignores it keeps working.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@objectstack/service-settings': patch
3+
---
4+
5+
fix(service-settings): a secret overwrite's audit row records the previous value's keyed digest as `old_hash` (#26534)
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
Ruling-ref: #26534 triage `6436617436`; the server seat 2 ruling, arm (a)
10+
11+
A secret-valued setting's overwrite used to record `old_hash` null on `sys_setting_audit` / `sys_platform_setting_audit`, though the column declares null only when the key was previously unset. It now records the same keyed digest (`hmac-sha256:…`) that `new_hash` records, taken over the previous value, so an overwrite's `old_hash` equals the previous row's `new_hash` and a rewrite of the same secret reads `old_hash == new_hash`. It stays null when the key was unset or reset, when no keyed digest is available, or when the previous value can no longer be read, and it is never an unkeyed hash of the secret. Only a secret overwrite pays for it: one `sys_secret` read and one decrypt of the previous value, before the rotated-away secret is reaped.

‎docs/adr/0010-metadata-protection-model.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
> **Protocol 18 update (2026-10-12, ADR-0131 C5, #22340):** The per-type overlay opt-in on `MetadataTypeRegistryEntry` is spelled `allowEnvironmentOverlay` from protocol 18 (`packages/spec/src/kernel/metadata-plugin.zod.ts#allowEnvironmentOverlay`). [ADR-0131](./0131-total-organization-ownership-no-null-organization-id.md) D6 retired the per-organization overlay axis, so the key now names the one scope it governs — whether an environment may overlay an item of this type that a managed package ships. The old key `allowOrgOverride` stays on the registry entry schema only as a tombstone that refuses at parse with the new name in its prescription: no second key, no alias, no conversion. The lines below that name `allowOrgOverride` describe the field as it was spelled when they were written and are preserved verbatim.
44
5-
**Status**: Proposed
5+
**Status**: Accepted — 2026-10-27 (ruled on objectstack#26553, ADR batch 1; proposed 2026-05-29). Phases 1, 4 and 4.3 implemented (`packages/spec/src/shared/protection.zod.ts`, `allowEnvironmentOverlay` per ADR-0131 C5/D6); Phase 2 (L4 frozen paths + provenance), Phase 3 (L2 + RBAC unlock) and Phase 5 (solution stack) pending.
66
**Shares its number with**: [`0010-nl-to-flow-authoring`](./0010-nl-to-flow-authoring.md) (Natural-Language → Flow Authoring) — an unrelated decision under the same number. Numbers are never reassigned (AGENTS.md Prime Directive 13); cite this record unambiguously as `ADR-0010-metadata-protection-model`.
77
**Date:** 2026-05-29
88
**Supersedes:** —

‎docs/adr/0019-app-as-consumer-unit.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ADR-0019: App as the Consumer-Facing Unit
22

3-
**Status**: Proposed (v3 — narrowed to the consumer-surface decision)
3+
**Status**: Accepted — 2026-10-27 (ruled on objectstack#26553, ADR batch 1; proposed 2026-05-31, v3 narrowed to the consumer-surface decision). Landed in spec: `CONSUMER_INSTALLABLE_TYPES` / `isConsumerInstallable` and the `MarketplaceListingSchema.packageType` narrowing. Pending: the Console marketplace filling `packageType` from the manifest; HotCRM folded to a single App (D3/D4).
44
**Shares its number with**: [`0019-approval-as-flow-node`](./0019-approval-as-flow-node.md) (Collapse Approval into Flow — approval as a durable-pause node) — an unrelated decision under the same number. Numbers are never reassigned (AGENTS.md Prime Directive 13); cite this record unambiguously as `ADR-0019-app-as-consumer-unit`.
55
**Date**: 2026-05-31
66
**Deciders**: ObjectStack Protocol Architects

‎docs/adr/0022-connectors-vs-messaging-channels.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ADR-0022: Connectors vs Messaging Channels — Where "Push to Slack" Lives
22

3-
**Status**: Proposed (2026-05-31) — routing decision recorded; both substrates now exist independently (`connector-slack` built; `service-messaging` notify/outbox/preferences built), but the §3 shared-transport wiring — a Slack `MessagingChannel.send()` delegating to the Slack Connector — is unbuilt (no slack-channel in service-messaging). (2026-07-16 audit)
3+
**Status**: Accepted — 2026-10-27 (ruled on objectstack#26553, ADR batch 1; proposed 2026-05-31) — routing decision recorded; both substrates now exist independently (`connector-slack` built; `service-messaging` notify/outbox/preferences built), but the §3 shared-transport wiring — a Slack `MessagingChannel.send()` delegating to the Slack Connector — is unbuilt (no slack-channel in service-messaging). (2026-07-16 audit)
44
**Deciders**: ObjectStack Protocol Architects
55
**Builds on**: [ADR-0012](./0012-notification-platform.md) (Messaging Platform — outbound `notify` on a generalized outbox), [ADR-0013](./0013-bidirectional-messaging.md) (Bidirectional Messaging — Slack first), [ADR-0015](./0015-external-datasource-federation.md) (open mechanism / enterprise lifecycle split), [ADR-0018](./0018-unified-node-action-registry.md) (`connector_action` as baseline generic dispatch)
66
**Consumers**: `@objectstack/spec` (`integration/`, future `messaging/`), `@objectstack/services/service-automation` (connector registry), future `@objectstack/service-messaging`, future `@objectstack/plugin-messaging-slack`, `@objectstack/connectors/*`

‎docs/adr/0026-client-ui-plugin-distribution.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ADR-0026: Client-Side UI Plugin Distribution
22

3-
**Status**: Proposed
3+
**Status**: Deferred — 2026-10-27 (ruled on objectstack#26553, ADR batch 1; proposed 2026-06-01). No implementation under `packages/` or `ui/`; a customisable console has no business demand for now (objectstack#25922, ruling 6431234244). Restart condition: a third-party UI plugin is wanted, bundled in an App or operator-provisioned.
44
**Deciders**: ObjectStack Protocol Architects
55
**Builds on**: [ADR-0025](./0025-plugin-package-distribution.md) (plugin package distribution — server/runtime plugins), [ADR-0003](./0003-package-as-first-class-citizen.md) (package + versioned releases), [ADR-0010](./0010-metadata-protection-model.md) (protection model)
66
**Related**: [ADR-0019](./0019-app-as-consumer-unit.md) (App is the only consumer-facing unit — UI plugins ship inside Apps or are operator-provisioned, see §3.7)

‎docs/adr/0027-metadata-authoring-lifecycle.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ADR-0027: `@objectstack/metadata-authoring` — Staged Authoring, Publish & Promotion Lifecycle
22

3-
**Status**: Proposed (2026-06-01)
3+
**Status**: Accepted — 2026-10-27 (ruled on objectstack#26553, ADR batch 1; proposed 2026-06-01), narrowed to what landed. M1–M2 (draft staging, publish seal) are implemented in the rest/runtime draft → publish path (`packages/rest/src/meta-item-history-routes.ts`, `packages/runtime/src/domains/meta.ts` cite (E)(5)), not as the `@objectstack/metadata-authoring` package this record names (no such package exists). Not built: M3 promotion + approval gates, M4 advanced migration, the rollback executor.
44
**Deciders**: ObjectStack Protocol Architects
55
**Builds on**: [ADR-0002](./0002-environment-database-isolation.md) (per-environment database), [ADR-0003](./0003-package-as-first-class-citizen.md) (package · version · installation), [ADR-0005](./0005-metadata-customization-overlay.md) (one Zod source per type, org overlay), [ADR-0006 v4](./0006-project-environment-split.v4.md) (unify on package, drop project), [ADR-0008](./0008-metadata-repository-and-change-log.md) (Repository · ChangeLog · Cache · Registry; four write surfaces), [ADR-0010](./0010-metadata-protection-model.md) (L1/L2/L3 protection), [ADR-0016](./0016-studio-package-authoring-and-publish.md) (Studio authoring loop — **this ADR revives its §2 draft-workspace north-star**), [ADR-0019](./0019-approval-as-flow-node.md) (approvals)
66
**Related (boundary)**: [ADR-0025](./0025-plugin-package-distribution.md) (plugin package distribution — code + dependencies) and [ADR-0026](./0026-client-ui-plugin-distribution.md) (client-side UI plugin distribution) own **how a *sealed* package is distributed and installed**; ADR-0027 owns **how a package is authored, staged, sealed, and promoted across environments**. The two meet at the sealed `sys_package_version` artifact: this ADR produces it, those ADRs ship it.

0 commit comments

Comments
 (0)