Skip to content

Commit d30f1ce

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/project-thread-ivr552
2 parents 2cb3bad + fbc1656 commit d30f1ce

81 files changed

Lines changed: 5183 additions & 1714 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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@object-ui/i18n': patch
3+
---
4+
5+
feat(i18n): the flow designer and the rest of the Studio chrome move into the `studio` namespace
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Sixth slice of the move ruled on objectstack#25922** (6431032267, Q2 = A; kept by 6431234244). Every row still left in `@object-ui/app-shell`'s private designer table, except `engine.translationPreview.*`, now lives in `@object-ui/i18n/namespaces/studio`, text unchanged: the flow designer (`engine.flowSim.*`, `engine.flowCanvas.*`, `engine.flowRuns.*` and the other `engine.flow*` families), the page-block canvas, the doc and book previews, `engine.studio.inspector.*` and the remaining Studio chrome (538 keys in `en`, 583 in `zh`). The `engine.flowNode.*` and `engine.enum.*` rows stay zh-only, as before: their English is the flow node descriptor or the enum value itself. Studio reads the same strings; a Chinese viewer sees these labels in English for the moment the `zh` catalogue takes to load, as with the earlier slices. No export changes.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@objectstack/plugin-auth': patch
3+
---
4+
5+
fix(plugin-auth): a session stores its client address in RFC 5952 text, and is born with `last_activity_at`
6+
7+
Clause-②: yes (widening)
8+
Metadata-protocol: none
9+
10+
Setup › Sessions printed a localhost session's IP as `0000:0000:0000:0000:0000:0000:0000:0000` and left Last Activity empty until the session's first request. Both are fixed in the data a sign-in stores; the list still prints the columns as plain text.
11+
12+
- **The address is the client's, in RFC 5952 text.** `sys_session.ip_address` is now the address the server established for the request (the client-address trust rule: forwarded headers only under `server.trustProxy`, the connection's peer otherwise), with IPv6 compressed — `::1`, `2001:db8::1` — and IPv4 unchanged. better-auth's own reader used to fill the column with its rate-limit bucket instead: every IPv6 address expanded to eight groups and masked to its /64 network, so `::1` read as `0000:…:0000` and `2001:db8::1` as `2001:0db8:0000:0000:0000:0000:0000:0000`. An IPv4-mapped address is stored as the IPv4 address it maps (`::ffff:203.0.113.7` → `203.0.113.7`), so an IPv4 client reads the same whether a dual-stack or an IPv4 listener accepted it; `last_login_ip` and the refused-sign-in audit rows, which carried the mapped text, now carry that one too. A session minted with no established address (a programmatic `auth.api.*` call) keeps better-auth's value, in the same text. An unparseable value is stored as it came.
13+
- **One text on every row of a sign-in.** The `login_failed` / `account_locked` audit rows and `sys_user.last_login_ip` carry the same text as the session row and its `login` row. `signInClientFingerprint(ctx, establishedAddress?)` takes the established address as an optional second argument.
14+
- **`last_activity_at` is written at creation**, equal to `created_at`. The activity heartbeat is unchanged; its first write now comes on the first request more than a minute after sign-in rather than on the very first request.
15+
- **Unchanged:** better-auth's per-address rate limiter still keys on its own bucket, the ADR-0069 D5 allow-list still judges the trust rule's answer as it came, and existing session rows are not rewritten.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@object-ui/plugin-list': patch
3+
'@object-ui/plugin-grid': patch
4+
'@object-ui/i18n': patch
5+
---
6+
7+
feat(plugin-list,plugin-grid,i18n): the Display menu's Open records section — the reader's drawer | split preference, or the object's declared full page — and a host node beside each row's record link (#26681)
8+
9+
Clause-②: no
10+
Metadata-protocol: none
11+
12+
- **`@object-ui/plugin-list`**: `ListView` takes a `recordOpen` host prop and hands it to the Display menu (`ViewSettingsPopover`), which draws an Open records section after Layout. For a drawer-mode object it offers two segments, Drawer | Split, writing the reader's own preference through the host's `setPreference`, with a note that the choice is the reader's and that both expand to the full page. For an object or view that declares `navigation.mode: 'page'` it draws one read-only line instead; for `new_window` and `none`, nothing. A host that passes no `recordOpen` draws no section, so every other host is unchanged. It is a host prop, not a view key: no metadata is added, and the section neither counts toward the Display trigger's changed-settings count nor returns under "Reset to view default".
13+
- **`@object-ui/plugin-list`** and **`@object-ui/plugin-grid`**: `ListView` and `ObjectGrid` take a `recordLinkAdornment` host prop — a node drawn beside each row's record link, after the anchor and never inside it: beside the first column the view links (`link: true`), else beside the auto-linked primary field. `rowAdornment` is unchanged: it sits beside the row's title, which need not be where the record opens from. The console passes an "open as full page" arrow through it for a row that opens the drawer. Absent, the link cell is exactly the anchor it was.
14+
- **`@object-ui/i18n`**: the section's five `list.*` strings (`recordOpen`, `recordOpenDrawer`, `recordOpenSplit`, `recordOpenNote`, `recordOpenDeclaredPage`) and `console.objectView.openAsPage`, the tooltip of the console list's open-as-full-page arrow, in all ten packs.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@object-ui/console': patch
3+
---
4+
5+
fix(console): the approvals inbox names the flow, the step and the object in the language the user chose
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Before.** A user whose chosen language differed from the browser's (a zh-CN user on an en-US browser) read every inbox row's flow, step and object in the authoring language, while the request page showed the same request translated. The inbox read the approvals routes with a bare `fetch`, so the approvals door picked its labels by the browser's `Accept-Language`.
11+
- **Now.** Every approvals call the inbox makes goes through the console's authenticated fetch, the channel the request page already reads over. It sends the session's language as `Accept-Language`, and the door answers `process_label`, `step_label` and `object_label` from the app's pack (`flows.FLOW.label`, `flows.FLOW.approvals.NODE.label`, `objects.OBJECT.label`). A flow, step or object the pack does not cover keeps its stored label. No second translator is added. The inbox's object filter now lists each object by that label instead of its machine name, and search matches the object label too.
12+
- **Unchanged.** What is stored on the request row. The Setup › Approval Requests object list reads the generic data door, which serves the stored labels.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@object-ui/i18n': patch
3+
---
4+
5+
fix(studio): Interfaces lists a package's pages, dashboards, reports and views whether or not a navigation entry names them
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- Studio › Interfaces used to list only what the app's navigation names, so a list view off the menu, a record page, or any item of a package with no app could not be opened in Studio at all. Under the navigation tree the rail now has one section per type ("Not in navigation": Pages, Dashboards, Reports, Views) listing the package's items no entry opens, published and draft, and each row opens its item in the same canvas and inspector, read-only on an installed package. A package with no app keeps its "no app yet" notice and lists what it ships under it. An item a navigation entry opens keeps its row in the tree and is not listed again.
11+
- The `studio` namespace gains the five `engine.studio.if.offNav.*` strings, in `en` and `zh`. No export changes.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@objectstack/objectql': patch
3+
---
4+
5+
fix(objectql): a predicate delete walks the rows it matches as one set: each relation pointing at the object is asked once per 500 matched rows, not once per row (#27004)
6+
7+
Clause-②: yes (widening) — a predicate delete (`engine.delete(object, { where, multi: true })`, a flow's `delete_record` with `multi: true`) whose matched rows are held only by OTHER matched rows (a report and its manager through a `restrict` self-lookup, both matched) now deletes them; the row-by-row walk refused it, or not, depending on which row it met first. Nothing is narrowed.
8+
Metadata-protocol: none
9+
10+
- **Before.** The predicate delete ran the by-id referential walk once per matched row, so its cost was matched rows times referencing relations: 1,001 rows of an object three relations point at took 4,004 dependents probes, about 1.7 s on SQLite.
11+
- **Now.** One walk, rooted at the whole matched set (a by-id delete is a set of one). Each referencing relation is probed once per 500 matched rows with `$in` (a multiple lookup ORs each id's `$contains`), and each row is then judged and written as before: `restrict` refuses, `cascade` deletes (each child through its own delete, hooks and all), `set_null` clears, a multiple lookup loses only the deleted members and an emptied set is written as `[]`. The same 1,001-row delete takes 12 probes.
12+
- **Unchanged.** A refusal's code and wording: it still names one record and counts the rows that refuse over that record (for a required multiple lookup, the rows the removal would empty). The atomic and split plans, the 500-id `$in` delete batches, the roll-up recompute after the delete, and the filter of every probe a by-id delete sends.
13+
- **Who sees it.** A bulk delete that removes rows holding each other: a matched row that another matched row holds is in the delete's own set, so it neither refuses nor is cleared first (the rule a cascade already applies to the rows it deletes). A holder outside the matched rows still refuses.

‎docs/adr/0038-build-verification-loop.md‎

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

33
> **🔶 Cloud-owned — superseded in part by cloud ADR-0025 (2026-06-25).** The in-UI AI runtime and the `ask` / `build` agents described here moved to the **cloud / Enterprise** distribution (`@objectstack/service-ai` → `cloud/packages/service-ai`, closed); the open framework exposes AI only via `@objectstack/mcp` (BYO-AI) and ships no in-product `ask` / `build` chat. Retained as historical design context, now **cloud-owned** — see [`cloud/docs/adr/0025-service-ai-to-cloud-open-mcp-only`](https://github.com/objectstack-ai/cloud/blob/main/docs/adr/0025-service-ai-to-cloud-open-mcp-only.md).
44
5-
**Status**: Proposed (2026-06-11) — cloud-owned; framework slice partially implemented (2026-07-16 audit): L3 runtime probes shipped and tested (`metadata-protocol/src/build-probes.ts`, `RuntimeBuildIssue` contract); L2 half-present (dataset compile only); L1/L4/L5 + `ai_eval_*` storage are cloud-only and not verifiable in this repo.
5+
**Status**: Accepted — 2026-10-28 (ruled on objectstack#26553, ADR batch 2; proposed 2026-06-11). cloud-owned; implemented: L1 graph lint and L4 `verify_build` self-correction in cloud `service-ai-studio` (`verify/graph-lint.ts`), L3 runtime probes in the framework (`metadata-protocol/src/build-probes.ts`, `RuntimeBuildIssue` contract, post-publish probe report in `protocol.zod.ts`), L5 golden-prompt evals (cloud `eval-golden-prompts.yml` on `ai_eval_cases` / `ai_eval_runs`); pending: L2 renderability contract (dataset compile only today).
66
**Deciders**: ObjectStack Protocol Architects
77
**Builds on**: [ADR-0033](./0033-ai-assisted-metadata-authoring.md) (drafts as the staging layer — this ADR **replaces its human-approval assumption for AI builds** with a machine gate; HITL stays for destructive actions), [ADR-0021](./0021-analytics-dataset-semantic-layer.md) (datasets — what most verification probes exercise), ADR-0037 / [framework#1694](https://github.com/objectstack-ai/objectstack/pull/1694) (Live Canvas — the *human-visibility* complement to this ADR's *machine-verification*)
88
**Consumers**: `@objectstack/service-analytics` + `@objectstack/objectql` (verification probes), `../cloud/service-ai-studio` (graph lint, `verify_build` tool, self-correction protocol, eval intelligence), `../objectui` (build health card in chat), `ai_eval_cases`/`ai_eval_runs` (existing, currently-unused storage)

‎docs/adr/0046-package-docs-as-metadata.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ADR-0046: Package documentation as metadata — flat `src/docs/*.md` compiled into the manifest
22

3-
**Status**: Proposed (2026-06-12, revised — simplified from the original directory/docSet design)
3+
**Status**: Accepted — 2026-10-28 (ruled on objectstack#26553, ADR batch 2; proposed 2026-06-12, revised — simplified from the original directory/docSet design). implemented: P1 (`DocSchema`, `os build` collection of flat `src/docs/*.md`, publish lint, kernel `doc`), P2 (console documentation portal and help menu, Studio doc editor), P3a/P3b (`book` spine, `GET /meta/book/:name/tree`, §6.7 audience gating in the REST read gate); pending: P3c (Studio drag-edit of `doc.order` / groups, anonymous library via `apps/docs`) and P3d (portal taxonomy, book-level search, §5 tags).
44
· **Superseded in part (2026-06-13, ADR-0048)** — the bare-name overwrite premise; note at §3.2.
55
**Deciders**: ObjectStack Protocol Architects
66
**Builds on**: [ADR-0003](./0003-package-as-first-class-citizen.md) (package + versioned releases), [ADR-0016](./0016-studio-package-authoring-and-publish.md) (publish pipeline, `manifest_json` snapshot), [ADR-0025](./0025-plugin-package-distribution.md) (artifact distribution, trust boundary), [ADR-0033](./0033-ai-assisted-metadata-authoring.md) (AI as primary author)

‎docs/adr/0051-inline-metadata-views-in-docs.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ADR-0051: Inline metadata views in docs — the ` ```metadata ` fenced block
22

3-
**Status**: Proposed (2026-06-16) — P1 authoring surface implemented (2026-07-16 audit): `ElementMetadataViewerPropsSchema` + `element:metadata_viewer` component entry + publish lint with reference-liveness checks (`cli/utils/collect-docs.ts lintMetadataEmbeds`); the console SDUI fence-lift and the state_machine/flow/permission renderers are NOT implemented — no runtime rendering of the embed yet.
3+
**Status**: Accepted — 2026-10-28 (ruled on objectstack#26553, ADR batch 2; proposed 2026-06-16). P1–P3 implemented: the `metadata` fenced block lifts to `element:metadata_viewer` in the markdown renderer, the viewer renders `state_machine`, `flow` and `permission` (`ui/packages/components/src/renderers/basic/metadata-viewer.tsx`), publish lint with reference-liveness checks (`cli/utils/collect-docs.ts lintMetadataEmbeds`); `object` embeds and the general `objectui` fence stay deferred (§5).
44
**Deciders**: ObjectStack Protocol Architects
55
**Builds on**: [ADR-0046](./0046-package-docs-as-metadata.md) (`doc`/`book` as metadata — §3.4 syntax boundary, §3.5 *derived content is rendered, never written*, §6.7 anonymous live-embed guardrail), [ADR-0020](./0020-state-machine-converge-and-enforce.md) (a record state machine is a `state_machine` validation rule on an object, not a standalone type), [ADR-0047](./0047-object-ui-run-modes.md) (ObjectUI is the server-driven UI runtime), [ADR-0048](./0048-cross-package-metadata-collision.md) (package-scoped single-item resolution), [ADR-0049](./0049-no-unenforced-security-properties.md) (enforce-or-remove posture for governed properties), [ADR-0033](./0033-ai-assisted-metadata-authoring.md) (AI as primary author)
66
**Consumers**: `@objectstack/spec` (`DocSchema` unchanged; new `element:metadata_viewer` entry in `ComponentPropsMap`; the fenced-block body schema), `@objectstack/cli` (publish lint: parse + validate body + reference-liveness), `@objectstack/console` (the SDUI doc renderer lifts ` ```metadata ` fences into the component), `apps/docs` portal (anonymous degradation per ADR-0046 §6.7), `@objectstack/core` (kernel **unchanged** — `doc` stays inert)

‎docs/adr/0052-audit-is-not-the-activity-feed.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ADR-0052: Audit is not the activity feed — decompose collaboration, activity, and audit into bounded contexts
22

3-
**Status**: Proposed (2026-06-16) — partially implemented (2026-07-16 audit): §5b declarative activity (`trackHistory` + milestone templates), ActivityPointer fields, and `service-feed` retirement are done; the headline bounded-context decomposition is NOT — `sys_activity`/`sys_comment` still live in plugin-audit, the audit-writer still dual-writes (no event-bus spine), `sys_notification` move deferred (P1/P2).
3+
**Status**: Accepted — 2026-10-28 (ruled on objectstack#26553, ADR batch 2; proposed 2026-06-16). implemented: §5b declarative activity (`trackHistory` + object-level milestone templates), P0b ownership moves (`sys_attachment` and `sys_notification` are no longer registered by plugin-audit), P1 (`sys_comment` is the one collaboration backend with reactions and threads; `service-feed` retired); pending: P2 domain event spine — the audit writer still dual-writes `sys_audit_log` and `sys_activity`, and `sys_activity` / `sys_comment` still live in plugin-audit (the headline bounded-context decomposition).
44
**Deciders**: ObjectStack Protocol Architects
55
**Builds on**: [ADR-0030](./0030-notification-platform-convergence.md) (single-ingress `NotificationService.emit` — *no producer writes a per-user inbox row directly*; this ADR applies the same "one canonical service per context, producers emit through a seam" posture to activity and collaboration), [ADR-0012](./0012-notification-platform.md) (messaging outbox owns `sys_notification`), [ADR-0049](./0049-no-unenforced-security-properties.md) (enforce-or-remove posture for governed properties — audit immutability must be enforced at its own boundary, not diluted by co-tenant objects), [ADR-0042](./0042-approval-sla-escalation.md) (uses `sys_audit_log` rows as an idempotency primitive — a second reason audit must stay a clean, append-only ledger)
66
**Consumers**: `@objectstack/plugin-audit` (sheds 4 of its 5 objects; keeps only `sys_audit_log`), `@objectstack/service-feed` (becomes the canonical collaboration+timeline backend, or is retired into it), `@objectstack/service-storage` (takes `sys_attachment`), `@objectstack/service-messaging` (takes `sys_notification`, per ADR-0030), `@objectstack/platform-objects` (object ownership map), `@objectstack/cli` (`ALWAYS_ON_CAPABILITIES` / capability resolver), `@objectstack/console` (ChatterPanel data source), `@objectstack/spec` (`IFeedService` / event contracts)

0 commit comments

Comments
 (0)