Skip to content

Commit c7f0010

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/project-thread-v03a57
2 parents b9c396e + c739de7 commit c7f0010

42 files changed

Lines changed: 1610 additions & 209 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
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.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
'@objectstack/metadata-core': patch
3+
---
4+
5+
fix(metadata-core): an exclusive protocol comparator (`'>18'`, `'<18'`) names the major it admits in the `OS_PROTOCOL_INCOMPATIBLE` refusal
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The refusal for a package whose `engines.protocol` excludes this runtime names a target major (`targetMajor` in the diagnostic and in `error.details`) and, for a package behind the runtime, `objectstack migrate meta --from N`. It used to find the target by peeling every leading operator off the range as if it were `^`, so on a protocol-18 runtime both `'>18'` (19 and up) and `'<18'` (17 and below) answered `migrate meta --from 18`, a migration from the runtime's own major.
11+
12+
Now the target is read from the majors the range admits, with the same grammar that decides whether it admits the runtime:
13+
14+
- **`'>18'` on an 18 runtime** targets 19. The package is ahead, so the answer is the upgrade remedy from the previous release: "Upgrade the runtime to protocol 19", and no `migrateCommand`. On a 17 runtime it now names protocol 19 too, where it used to name 18, which `'>18'` does not admit.
15+
- **`'<18'` and `'<18.0.0'`** target 17: `migrate meta --from 17`.
16+
- **A compound range** targets its floor in either order: `'<18 >=15'` targets 15 (it named 18), and `'>15 <18'` targets 16 (it named 15).
17+
- **A `||` union** still targets its lowest alternative, now read the same way: `'<17 || >19'` targets 16.
18+
- **A range that admits no major** (`'>=19 <15'`, `'<0'`) has `targetMajor: null` and the bare `objectstack migrate meta`.
19+
20+
`^`, `~`, `>=`, `<=`, wildcards, exact versions, hyphen ranges and partial bounds (`'>18.1'`, `'<18.5'`) answer as before. No range changes between admitted and refused: only the refusal's target and prescription change.
21+
22+
Two CLI readers of `targetMajor` follow: `objectstack migrate meta` now rewrites a `'<18'` range to `'^18'` when it migrates a stack to protocol 18 (it used to leave it for the load to refuse), and the protocol-version advisory now speaks for such a range.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/plugin-security': patch
3+
---
4+
5+
fix(plugin-security): the seven built-in positions are served in Chinese, Japanese and Spanish
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
Setup → Positions lists the platform's built-in positions (Platform Admin, Organization Owner, Organization Admin, Organization Member, Organization External Member, Everyone, Guest) beside the app's own. Under zh-CN the app's positions read in Chinese while these seven stayed English, because this plugin's translation bundle carried no `positions` group. The bundle now carries `positions.<name>.label` and `.description` for each built-in position in zh-CN, ja-JP and es-ES, the locales its permission-set translations cover, so `GET /api/v1/meta/position` serves them in the requested locale. English is unchanged, and nothing else of a position changes.

‎.gitattributes‎

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -95,17 +95,25 @@
9595
# the three hottest artifacts; each gate sums its shards when it reads them.
9696
#
9797
# The elevation census page (`content/docs/permissions/system-context.mdx`) was
98-
# routed here from #13646 to #22927 and is not any more: nothing on it is
99-
# generated. It joined as a `file:line` anchor table whose correct merged values
100-
# were on NEITHER side of a conflict, became a MIXED row when #14064 measured that
101-
# a whole-side deferral dropped its hand-written prose (`mixed: 'line-anchors'`),
102-
# lost its line numbers to `path#symbol` anchors at #15921, and lost its last
103-
# generated half — the census totals — to #22927's ruling. It text-merges like
104-
# its prose neighbours now. ⚠️ What still catches a merge that leaves it wrong is
105-
# `scripts/check-system-context-census.mjs`, which re-derives the census from the
106-
# tree on every PR in the required `Lint & Repo Gates` job: a read no row anchors,
107-
# or an anchor that no longer resolves. The `mixed` field and its comparator stay
108-
# for the next mixed artifact (scripts/regen-artifacts.mjs, scripts/git-merge-regen.mjs).
98+
# routed here from #13646 to #22927 and is not any more. It joined as a `file:line`
99+
# anchor table whose correct merged values were on NEITHER side of a conflict,
100+
# became a MIXED row when #14064 measured that a whole-side deferral dropped its
101+
# hand-written prose (`mixed: 'line-anchors'`), lost its line numbers to
102+
# `path#symbol` anchors at #15921, and lost its census totals to #22927's ruling,
103+
# which took this row and `gen:system-context-census` with them. #26442 gave it a
104+
# generated half again: the site register between its BEGIN/END GENERATED
105+
# markers, rebuilt by `node scripts/check-system-context-census.mjs --write`. It
106+
# stays unrouted, as `tenant-audit-census.mdx` beside it (a generated region that
107+
# `node scripts/tenant-audit-census.mjs --write` rebuilds) always has: both are
108+
# MIXED pages that git text-merges, so neither side's prose is dropped, and the
109+
# register carries one line per symbol and no total, so two read-adding branches
110+
# merge apart. ⚠️ What catches a merge that leaves a region wrong is that page's
111+
# gate, which holds the region equal to its `--write` output on every PR in the
112+
# required `Lint & Repo Gates` job (`scripts/check-system-context-census.mjs`,
113+
# `scripts/check-tenant-audit-census.mjs`). Why neither is routed is recorded in
114+
# NOT_DRIVER_MANAGED's `content/docs/permissions/**` entry; the `mixed` field and
115+
# its comparator stay for the next mixed artifact (scripts/regen-artifacts.mjs,
116+
# scripts/git-merge-regen.mjs).
109117

110118
#
111119
# #13731 enumerated this file's blind spot and closed it: `check:merge-driver` now

‎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)

0 commit comments

Comments
 (0)