Skip to content

Commit 082626d

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-15362-error-handling-unique-violation
2 parents 476535c + 95d5cbb commit 082626d

120 files changed

Lines changed: 8673 additions & 2188 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: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
`IApprovalService.recall`'s contract prose names every actor who may recall, and scopes each one by status (#14670)
6+
7+
**Documentation only — no key, no accepted value, no runtime behaviour moves.** The implementation has been correct since #12775; only the contract's description of it was stale.
8+
9+
The docstring said *"Only the submitter (or a system context) may recall"*, then widened to `returned` requests in a second paragraph. Both halves were wrong, in opposite directions:
10+
11+
- **The list was not exhaustive.** A #3424 override actor — a platform or tenant admin holding no approver slot — may recall a `pending` request. That is the in-product recovery path for an approval routed to an unstaffed position, and this same file already documented it 387 lines above the sentence denying it: the docblock on `ApprovalRequestRow.viewer.can_override` spells the override's levers as `(approve / reject / reassign / recall it)`. One file, two contradicting sentences about the same verb.
12+
- **The ADR-0044 widening read as though it applied to that whole list.** It does not. The override and system arms are ANDed with `status === 'pending'` where they are computed, so neither reaches a `returned` request; an override actor is refused there exactly as any other non-submitter (#12775, maintainer ruling 2026-09-02). Abandoning a revision window is the submitter's alone.
13+
14+
The rewrite makes **status** the axis instead of appending a caveat, so the second defect cannot come back on a re-read: each status carries its own admitted set, and the `returned` bullet says outright that the submitter is alone in it.
15+
16+
`ApprovalRecallInput.actorId` carried the same stale sentence (*"Must be the request's submitter (or a system context)"*) and is corrected with it. Fixing only the method docstring would have left the contradiction alive on the very input type the corrected method takes.
17+
18+
The two sibling docstrings sharing that phrasing are **correct and unchanged**: `ApprovalSendBackInput.actorId` and `ApprovalResubmitInput.actorId`. `isOverrideActor` is called from exactly five places in `plugin-approvals``decideNode`, `reassign`, `recall`, `attachViewers` and `visibleRequestIds` — and neither `sendBack` nor `resubmit` is among them, so no override actor reaches either.
19+
20+
The published prose already described the corrected rule (`content/docs/automation/approvals.mdx`: an admin "may act on any `pending` request — approve, reject, reassign it to a real approver, or recall it"). This docstring was the one surface that had not kept up.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@objectstack/cli': minor
3+
---
4+
5+
Ratify `./hook-body` as a public subpath export — `extractHookBody`, `HookBodyExtractionError`, `HookBodyRefusalKind` and `ExtractedBody` were reachable as a deep `dist/utils/extract-hook-body.js` import until #13123 sealed the surface, and an app's hook-body fidelity harness (hotcrm's `test/helpers/action-sandbox.ts`) consumes them to run the SAME body-only lowering `os build` ships through the real QuickJS runner, so a test executes what production executes rather than a lookalike. The #13123 body names exactly this remedy for an out-of-repo consumer — ratify the subpath as public surface rather than read `dist/` paths — and 17.3.0 applied it to `./console` for cloud's `objectos-runtime`; this applies it to the second consumer (#15325). `@objectstack/cli/hook-body` is a dedicated entry that re-exports those four names and nothing else; the deep `dist/` path stays sealed. Also admits `./package.json`, so the ordinary tooling idiom of reading a dependency's own manifest resolves again.
6+
7+
`minor`, not `patch`: a new subpath on a published package's `exports` map is a purely additive widening of its public surface — a new accepted key — which takes at least `minor` under the maintainer's 2026-09-04 rule (decision batch #35, on #15294) in the Check Changeset step's "WHICH LEVEL" prose; the commit type never lowers it.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
"@objectstack/client": minor
3+
---
4+
5+
fix(client)!: the `oauth.*` family declares the wire shapes better-auth actually sends — four published `Promise< any >` returns narrowed (#14312)
6+
7+
**BREAKING** for a typed caller, and it breaks nothing that ever worked at runtime. No request bytes, no URL and no response handling change: this is a declaration catching up with what the routes have always answered. It ships as `minor` under the lockstep launch-window convention (`scripts/check-changeset-no-major.mjs`) — the version number is not the migration signal here, this entry is.
8+
9+
<!-- adr-0087: not-required (type-surface-only packages/client/src/index.ts#register, packages/client/src/index.ts#getPublic, packages/client/src/index.ts#consent) A published TYPE-SURFACE narrowing. Each member was UNANNOTATED at the merge base, so lib.dom's `Response.json()` published it as an erased `any`; each now declares the shape its route already answered. No method body changed, so no request or response byte moves, and the diff touches no `packages/spec` path and no ADR-0087 shape surface. The affected party is a TypeScript consumer and the compiler delivers the break at their own call site; `objectstack migrate meta`, `spec-changes.json` and the upgrade guide have nothing to rewrite, so a ledger entry would be false data in the one ledger this gate keeps true. DISCLOSURE, not an omission: the fourth narrowed member of this changeset is `oauth.applications.get` (index.ts line 3184; unannotated at base, `Promise` of `OAuthApplication` at HEAD). It satisfies this same predicate on a direct reading, but it is deliberately NOT named above, because the reference `packages/client/src/index.ts#get` does not address it: this file declares 13 members named `get`, predicate 4 reads the FIRST one (line 1928), and that member is unrelated and unannotated at both revs. Naming it would assert a verified fact about the wrong member; the ambiguity is filed as its own card. -->
10+
11+
Card 1 of 3 of the #12104 family, under the maintainer's 2026-08-31 ruling: the wire contract is the only source of truth, and better-auth's own `Date`-typed fields are the pre-serialization SERVER shape, not the wire fact.
12+
13+
## What changed
14+
15+
Four methods ended `return res.json()` with no return annotation, so `lib.dom`'s `Response.json(): Promise< any >` was their published type. Each now declares the shape its route serves, and its `exported-any-returns.json` entry is deleted in the same change:
16+
17+
| method | resolved to (before) | resolves to (now) |
18+
|:--|:--|:--|
19+
| `client.oauth.applications.register(req)` | `any` | `OAuthApplicationRegistration` |
20+
| `client.oauth.applications.get(id)` | `any` | `OAuthApplication` |
21+
| `client.oauth.applications.getPublic(id)` | `any` | `OAuthApplicationPublic` |
22+
| `client.oauth.consent(req)` | `any` | `OAuthConsentResult` |
23+
24+
`OAuthApplication`, `OAuthApplicationRegistration`, `OAuthApplicationPublic` and `OAuthConsentResult` are newly exported from `@objectstack/client`. These four routes are served BARE by better-auth (`auth-route-ledger.ts` records them `source: 'better-auth'`) — there is no `{ success, data }` envelope to unwrap, and none is introduced.
25+
26+
## The exact reads that stop compiling
27+
28+
Everything below compiled before only because `any` is assignable to, and indexable by, everything.
29+
30+
```ts
31+
const app = await client.oauth.applications.get('c_1');
32+
app.data; // was fine; now TS2339 — these routes carry NO envelope
33+
app.anythingAtAll; // was fine; now TS2339
34+
35+
const pub = await client.oauth.applications.getPublic('c_1');
36+
pub.client_secret; // now TS2339 — the public projection hand-picks 7 columns
37+
pub.grant_types; // now TS2339 — same reason
38+
pub.disabled; // now TS2339 — same reason
39+
40+
const decision = await client.oauth.consent({ accept: true });
41+
decision.client_id; // now TS2339 — consent answers `{ redirect, url }`
42+
43+
// Timestamps are RFC 7591 NUMBERS (Unix epoch seconds), so a caller that
44+
// guessed `Date` or ISO `string` now fails:
45+
new Date(app.client_id_issued_at!).toISOString(); // TS2769: number is not a Date arg
46+
app.client_id_issued_at!.slice(0, 10); // TS2339: not a string
47+
new Date(app.client_id_issued_at! * 1000); // the correct rewrite
48+
```
49+
50+
A caller that only read `client_id`, `client_secret`, `redirect_uris` or `url` needs no change.
51+
52+
## Timestamps: `number`, not `Date` and not ISO-8601
53+
54+
The ruling ordered every `Date`-typed field declared as an ISO `string` and forbade both a `Date` declaration and a runtime revival layer. **This family has no `Date` field to convert.** RFC 7591 carries `client_id_issued_at` and `client_secret_expires_at` as Unix-epoch SECONDS, and the provider converts its stored `Date` to a number before serialising, so the wire sends neither a `Date` nor an ISO string. Both are declared `number`, and a type-level pin holds them there. The ruling's prohibitions are satisfied: nothing declares a `Date`, and no revival layer exists.
55+
56+
## Two places better-auth's own types were the wrong answer
57+
58+
Read off the wire against a real server, not off the vendor's `.d.ts`:
59+
60+
- `getPublic` is declared `OAuthClient` — the full row — but its handler hand-picks seven columns. `OAuthApplicationPublic` is that projection, derived with `Pick` so it cannot drift from its parent. Its `redirect_uris` is always `[]` on this route and carries no information.
61+
- `user_id` and `application_type` are declared nullable by the vendor, but the serialiser folds a null column to `undefined`, so `null` is unreachable and is not declared.
62+
63+
## `oauth.applications.delete` is deliberately NOT bound
64+
65+
The fifth method of the family keeps its `Promise< any >` and its ledger entry. Its route answers HTTP 200 with a zero-byte body, so its `res.json()` rejects with a `SyntaxError` on every successful delete. No annotation can be honest while that call stands, and binding it needs a behaviour change — a decision beyond this card's type-narrowing scope. That the shrink-only ledger still carries exactly this one entry is the mechanism working.

.changeset/console-a472b07167a3.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
"@objectstack/console": minor
3+
---
4+
5+
Console (objectui) refreshed to `a472b07167a3`. Frontend changes in this range:
6+
7+
Derived from the changesets objectui declared over the range — 15 releasing of 18 changesets added across 29 non-merge commits; omitted: 3 release-nothing changesets, 11 commits carrying no changeset (they ship no package code).
8+
9+
- **minor****BREAKING** — Converge the lookup/user widget metadata on the spec's camelCase — one concept, one spelling (objectui#7155, maintainer ruling A′ of 2026-09-03, director decision batch #19). (objectui `351eb3181`)
10+
- **minor****BREAKING** — One authority for `KanbanSchema` / `KanbanColumn` / `KanbanCard`: the bare names now belong to `@object-ui/plugin-kanban` (objectui#6172, closing the cross-package half of objectu… (objectui `2c71482ea`)
11+
- **minor** — Retire `ComponentInput.inputType` — the fifth and last key objectui#5905 named (ADR-0049 enforce-or-remove, maintainer ruling 2026-08-31, option B). (objectui `1ec291c0d`)
12+
- **minor**`@object-ui/core` publishes `resolveRecordSourceObjectName`, the ONE reader for "which object is this block bound to" (objectui#7627). (objectui `b041b9c0c`)
13+
- **minor****Published TS surface narrowed:** `DashboardComponentSchema` no longer declares the dashboard-root `title` member (objectui#7623). (objectui `5d0876c5c`)
14+
- **minor****BREAKING** — BREAKING (`@object-ui/components`): the chart primitives — `ChartContainer`, `ChartTooltip`, `ChartTooltipContent`, `ChartLegend`, `ChartLegendContent`, `ChartStyle` and the `Char… (objectui `7bf244bea`)
15+
- **minor** — ListView: fold `data={{ provider: 'object', object }}` onto `objectName`, and read the author's view kind from `specType` / `type` (objectui#7477 — step 6 of #2890, released by th… (objectui `00d2fa682`)
16+
- **minor** — Retire the dashboard-**root** `title` read across all five surfaces (objectui#7509, maintainer ruling 2026-09-04, decision batch #29, option C, under ADR-0049). (objectui `1cca678ba`)
17+
- **minor****BREAKING** — Re-home the breakpoint layout vocabulary and delete the two dead responsive implementations (objectui#7580, maintainer ruling 2026-09-04, option A). (objectui `e62c44e7e`)
18+
- **minor**`@object-ui/types/zod`: the zod const `StylePropsSchema` is renamed to `ClassNameStylePropsSchema` (objectui#5928). **The old name is gone** — there is no deprecated alias and no… (objectui `24e027e93`)
19+
- **patch** — Fix `extractToc` eating the underscores out of a `SCREAMING_SNAKE` heading, so its `#id` links resolve to the heading they name again (objectui#7667). (objectui `a472b0716`)
20+
- **patch** — Remove `src/ui/toast.tsx`, an unreferenced primitive, and the dependency only it imported (objectui `2f61238b9`)
21+
- **patch** — Fix `extractToc` deleting tag-shaped text that lives INSIDE an inline code span, so its `#id` links resolve to the heading they name again (objectui#7658). (objectui `90c6d090d`)
22+
- **patch** — A record-page URL now names the object the clicked rows actually came from, in `ObjectTree` and `ObjectCalendar` (objectui#7638). (objectui `2ce2612df`)
23+
- **patch** — fix(app-shell): the object-field options editor no longer drops `default` and `visibleWhen` on save (objectui `97c3e1972`)
24+
25+
⚠️ 4 of these carry a breaking change: 4 by the author's own breaking annotation in the changeset body — objectui declares no `major` inside a launch window (`scripts/check-changeset-no-major.mjs`). Each is marked **BREAKING** in the list above — read them before compiling the release record.
26+
27+
**In this console build, declared nowhere** — objectui merged 11 commits in this range with no `.changeset/*.md`. The code is inside the pin above and ships here, but nothing upstream declared them, so they appear in no objectui CHANGELOG and in no entry above. Listed by subject rather than counted, because a count cannot tell a dependency bump from a form-behaviour change (objectstack#6174); the upstream gate that would prevent this is objectui#3387.
28+
29+
- _(no changeset)_ fix(scripts): check-doc-links resolves the #fragment, not just the file (objectui#7644) (#7657) (objectui `f7cf7e8a9`)
30+
- _(no changeset)_ docs(plugin-chatbot): document chatbot-floating's seven declared inputs keys (objectui#7594) (#7656) (objectui `8e501cb97`)
31+
- _(no changeset)_ docs(agents): record the never-approve seat rule beside the governed never-list (#7630) (objectui `2e99852ca`)
32+
- _(no changeset)_ refactor(examples): drop the inert root `title` from six catalog dashboards (#7634) (objectui `46cde8264`)
33+
- _(no changeset)_ docs(check-skill-examples): drop the stale zero-jsonc-fences claim (#7631) (objectui `0b24d7f85`)
34+
- _(no changeset)_ docs(governed-guard): replace the retired sha pin with the ruled approval-record predicate (#7616) (objectui `11edab88f`)
35+
- _(no changeset)_ docs(skills): split multi-document JSON fences, drop the `...` elisions, mark every parsing fence (#7608) (objectui `89d6adf37`)
36+
- _(no changeset)_ fix(scripts): judge spec citations at member granularity, and stop the header teaching a retired filter (objectui#7513) (#7617) (objectui `d28d87bf4`)
37+
- _(no changeset)_ fix(governed-guard): an authorised approval record satisfies the queue leg on any commit (#7606) (objectui `0d8fd7ce3`)
38+
- _(no changeset)_ chore(deps): Bump fumadocs-core from 16.14.4 to 16.15.4 (#7059) (objectui `1bae75bb8`)
39+
- _(no changeset)_ docs(claude-md): collapse the two AGENTS.md excerpts to rule + hook + pointer (#7600) (objectui `c70ebaaeb`)
40+
41+
<!-- adr-0087: not-required (no-migration-prescription) All FOUR declared-breaking entries in the range `00d3f09c500c...a472b07167a3` are judged ONE AT A TIME against `packages/spec`'s authorable surface at this HEAD, not as a batch, and every count below was re-measured here rather than quoted from upstream prose. (1) objectui `351eb3181` (objectui#7155) converges objectui's WIDGET metadata bags `LookupFieldMetadata` / `UserFieldMetadata` onto the spec's camelCase, removing the snake members. It moves TOWARD this repo's contract, not away from it: `packages/spec/src/data/field.zod.ts` already declares `displayField` (`:835`, `:1342`), `descriptionField` (`:1343`) and `lookupFilters` (`:1355`), and the snake spellings have ZERO occurrences under `packages/spec/src` — `display_field` 0, `description_field` 0, `lookup_filters` 0, `id_field` 0 (the single apparent `id_field` hit is the substring inside `invalid_field` in an unrelated `api/protocol.test.ts` fixture). The snake dialect was never an ObjectStack-authorable key, so no accepted key moves, no stored `sys_metadata` row can carry a retired spelling, and there is nothing here for `objectstack migrate meta` to act on. (2) objectui `2c71482ea` (objectui#6172) is a TypeScript export rename inside `@object-ui/types` giving the bare Kanban trio to `@object-ui/plugin-kanban`; `KanbanSchema`, `KanbanColumn` and `KanbanCard` have ZERO occurrences under `packages/spec/src`, and upstream states no member, no optionality and no accept/reject behaviour moves with it. (3) objectui `7bf244bea` (objectui#7397) removes the duplicated React chart primitives from `@object-ui/components`. This repo's own `ChartConfigSchema` / `ChartConfig` (`packages/spec/src/ui/chart.zod.ts:539`) is an independently declared ObjectStack metadata schema that shares a NAME with the removed objectui type and nothing else: `packages/spec/src` imports from `@object-ui/*` zero times (re-measured), so none of the removed React exports sits on any ObjectStack surface. (4) objectui `e62c44e7e` (objectui#7580) re-homes `BreakpointName` and `BreakpointColumnMap` into objectui packages and deletes the two dead responsive implementations. This repo retired that whole vocabulary itself in objectstack#11027 and its ADR-0087 ledger entries ALREADY EXIST on this side — `RETIRED_DEFS_BY_MAJOR[18]` carries `ui/BreakpointName`, `ui/BreakpointColumnMap` and `ui/BreakpointOrderMap` — so this entry is objectui catching up to a retirement already registered here, and it prescribes nothing new. None of the four is reachable through `@objectstack/console` in any case, re-measured against `packages/console/package.json` at this HEAD: it publishes a frozen prebuilt SPA whose `files` list is ["dist", "README.md", "CHANGELOG.md"] and whose sole `exports` entry is `./package.json`, so it forwards no `@object-ui/*` module entry point and re-exports none of these types; and no `package.json` in this workspace declares an `@object-ui/*` dependency at all (0 files, re-measured). This diff is `.objectui-sha`, this changeset and the regenerated console provenance records the pin gates require, and nothing else — no `packages/spec` schema, no authorable metadata key and no protocol surface change is in it. This bump adds no ledger entry and claims none. -->
42+
43+
objectui range: `00d3f09c500c...a472b07167a3`

0 commit comments

Comments
 (0)