Skip to content

Commit 4e24064

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-18572-suggester-opposite-sibling
Resolves 4 delete/modify conflicts in packages/spec/api-surface-declarations/ (api.txt, kernel.txt, root.txt, security.txt) by taking main's deletion: #19024 retired the whole declaration-text snapshot mechanism (script, package.json scripts, check:generated gate, files) and this branch had only modified those now-retired files. Verified check:api-surface and check:generated still pass, and that the branch's new polarity-axes.ts exports are not part of the public barrel (not re-exported from shared/index.ts), so no export-recording artifact needs a change. Co-Authored-By: Claude <noreply@anthropic.com>
2 parents 72bf22a + 1a2bb9e commit 4e24064

135 files changed

Lines changed: 8822 additions & 239524 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.

‎.changeset/16045-spec-declaration-text-snapshots.md‎

Lines changed: 0 additions & 18 deletions
This file was deleted.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
'@objectstack/platform-objects': minor
3+
'@objectstack/spec': minor
4+
---
5+
6+
Studio's property-panel repeater tables name their columns in the author's own language: every repeater enumerates its row properties in the owning `*.form.ts`, and all four platform catalogs carry a translated name for each one
7+
8+
Clause-②: no
9+
10+
A `type: 'repeater'` renders as a table whose column heads come from the form's declared row children when it declares any, and from the served JSON Schema `items.properties[k].title` when it does not. `os i18n extract` only emits a `metadataForms.<type>.fields['<path>.<prop>']` key for a **declared** child, so a repeater that enumerated none had no localisation channel at all — #17232 (PR #17500) authored English titles on thirteen item schemas, #17505 and #17506 on four more, and every one of those column heads reached a Chinese, Japanese or Spanish author in English.
11+
12+
Both halves land together, because either alone is a half-state: 112 row properties across fifteen repeaters are now enumerated, each with a `label` equal to the item schema's own `.meta({ title })`, and the `en` / `zh-CN` / `ja-JP` / `es-ES` catalogs gain a leaf for each. Nothing in the accept set moves — the same author input parses identically before and after, and no row child declares a `type`, so the row widgets stay schema-derived.
13+
14+
Terms reuse the word each catalog already uses for the concept (`Label` → 显示名称 / 表示名 / Etiqueta, `Filter` → 筛选 / フィルター / Filtro, `Timeout (ms)` → 超时(毫秒)/ タイムアウト(ms)/ Tiempo de espera (ms)), and `field.options.*` mirrors its `object.fields.options.*` twin verbatim.
15+
16+
`page.variables.source` is the one existing string that moves. Its children were enumerated without labels, so the extractor emitted the humanized path `"Source"` as the English source and the bundle overlay then wrote that over the schema's authored `"Written By"`. The form now declares the label, the `en` leaf becomes `Written By`, and its three translations are re-authored with it (写入组件 / 書き込み元 / Escrito por).
17+
18+
`view.columns` / `view.sort` / `view.tabs` are untitled and enumerate no children — they are #17507's, and are untouched here. `object.fields.options` stays the curated four-key subset its reconciliation-ledger entry declares.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
"@objectstack/client": minor
3+
---
4+
5+
fix(client): the four `packages` READ members declare the stage their door is declared at (#17536)
6+
7+
Clause-②: yes
8+
9+
**BREAKING** for TypeScript consumers — a published TYPE-surface WIDENING, shipped as `minor` under the launch-window convention (`check-changeset-no-major` refuses `major` until GA; breaking-ness is carried by this banner and the ADR-0087 disposition below, never by the level). No runtime behaviour changes, and none is possible here: only a declaration moved, and the values these four methods resolve to are the values they have always resolved to.
10+
11+
`ObjectStackClient.packages.list` / `.get` and their `ScopedEnvironmentClient` twins returned `InstalledPackage` — the AUTHORING manifest stage, imported from `@objectstack/spec/kernel`. Since PR #17517 both read doors have been declared at EITHER stage: `ListInstalledPackagesResponseSchema.packages` is `z.array(InstalledPackageAtEitherStageSchema)` and `GetInstalledPackageResponseSchema.data` is that same schema. ⇒ A response the server is declared able to send was one this SDK's own types said could not arrive.
12+
13+
`packages/spec` is the one contract between producers and consumers, and `packages/client` is a consumer of it, so the consumer's declaration is what moves. All four now declare `InstalledPackageAtEitherStage` from `@objectstack/spec/api`.
14+
15+
**What the union is.** It is a union over the two manifest stages — `InstalledPackageSchema` and `AssembledInstalledPackageSchema` — which differ in exactly one key, `manifest`. Every other member of the row (`id`, `name`, `version`, `status`, `enabled`, `installedAt`, …) is common to both branches and reads exactly as it did, so code that reads only those members needs no change at all.
16+
17+
**Where the RUNTIME and the TYPE disagree, measured at this head.** The runtime schema is the strict half: `InstalledPackageAtEitherStageSchema.safeParse(row)` answers `success: false` for a row whose `manifest` belongs to neither stage — measured on a `{ bogus: 1, objects: 'not-even-an-array' }` manifest and on an empty `{}` one. The published TYPE is NOT that strict: on the assembled branch `manifest` is declared `Record<string, unknown>`, so both of those same rows COMPILE against the declared return type. ⛔ Do not read this widening as a type-level guarantee about `manifest` — the guarantee is the parse's. The type-level tolerance is a known gap, tracked as **#19324**; its root cause is the deliberate `z.ZodType<Record<string, unknown>, …>` annotation at `packages/spec/src/stack.zod.ts:1283` (#14513 — TS7056 and a declaration-chunk ceiling), and it is ⛔ not this change's to fix. It is recorded as a pin in `packages/client/src/return-type-precision.test.ts`, which reddens the day the gap closes.
18+
19+
**What a consumer does, concretely.** A member read off `manifest` on the union arrives as `unknown` (measured: `pkg.manifest.objects` is `unknown`). ⇒ a caller that reaches INTO `manifest` narrows by PARSING the row with a `packages/spec` schema and reading the parse's output:
20+
21+
```ts
22+
import { AssembledInstalledPackageSchema } from '@objectstack/spec/api';
23+
import { InstalledPackageSchema } from '@objectstack/spec/kernel';
24+
25+
const row = await client.packages.get(id);
26+
const parsed = AssembledInstalledPackageSchema.safeParse(row);
27+
if (parsed.success) {
28+
// parsed.data.manifest — the ASSEMBLED stage, object definitions
29+
} else {
30+
const authoring = InstalledPackageSchema.parse(row);
31+
// authoring.manifest.objects — the AUTHORING stage, glob strings
32+
}
33+
```
34+
35+
⛔ Do NOT narrow with `Array.isArray(pkg.manifest.objects)`, or with any other structural guess. It separates the stages on NEITHER level: at the type level `pkg.manifest` is the same union inside both branches of that `if`, and at runtime BOTH stages' `objects` are arrays — `z.array(z.string())` at the authoring stage against `z.array(ObjectSchema)` at the assembled one. (Measured: a row carrying no `objects` at all parses as either stage, which is the right answer for it — the key such a guess would read is not there.)
36+
37+
In this repository the whole consumer cost is zero sites outside `packages/client` itself: no other workspace package calls either read member.
38+
39+
**The WRITE members did not move** and stay declared at the authoring stage — all four of them: `install`, `enable`, `disable`, `update` still answer `Promise<InstalledPackage>`. `install` answers the row its own request contract produced (`PackageInstallRequestSchema` declares `manifest: ManifestSchema`), and PR #17517 moved the read doors alone. That asymmetry is the measurement, not an oversight, and it is pinned.
40+
41+
The type is reached the same way `InstalledPackage` always was, from `@objectstack/spec` rather than re-exported here: this SDK has never re-exported the package row, and this change does not start.
42+
43+
<!-- adr-0087: not-required (no-migration-prescription) Nothing authorable moves. No metadata key, no authored property, no config field, no accepted request shape and no stored artifact changes spelling or shape: the edit is four declared RETURN TYPES on one SDK class pair plus their docblocks, so `objectstack migrate meta` has nothing to rewrite, `spec-changes.json` has nothing to project and the upgrade guide has no row to gain. The party addressed is a TYPESCRIPT CONSUMER and the delivery channel is the compiler at their own call site — the audience the ADR-0087 ledger explicitly does not serve. The "what a consumer does" paragraph above is a source-code statement about narrowing a union, not a stored-metadata rewrite, which is the distinction #13080 records this refusal cannot make on its own.
44+
`type-surface-only` is NOT claimed here, and it is unavailable on the merits rather than on a resolution defect — measured by driving the gate, not assumed. That category was added for a published TYPE-surface NARROWING, and its predicate 4 (`narrowed-from-erased`) reads the base annotation through `isErasedType`, whose line is "the type IS `any` / `unknown`". At the merge base all four members carried a CONCRETE annotation (`Promise<InstalledPackage>`, `Promise<{ packages: InstalledPackage[]; total: number }>`), so nothing moved off an erased type; and this change runs in the opposite direction from the one the category names. The **BREAKING** banner is carried rather than dropped — that erosion is exactly what #13080 was filed about. -->
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
**ADR-0137 makes field-rule predicate fault semantics part of the contract** (#17778): what SUBMIT and RENDER do when a predicate cannot run.
6+
7+
Clause-②: no
8+
9+
**The predicate fault-semantics contract is recorded, not enforced, by this release.** ADR-0137 states what a field-rule predicate does when it cannot RUN: at SUBMIT a faulting predicate refuses the write and names the field and the rule (D2); at RENDER visibility stays fail-OPEN, so a rule that could not run never hides a control and lets the form write `null` over a column the user never saw (D3); a blank or faulting GATE predicate is diagnosed, never a silent `true` (D4); and the evaluation helper's fallback stays freely specifiable (D5), because fault-to-flag and fault-to-throw both exist only because it is a parameter. Those are consequences CONSUMERS deliver — `packages/spec` carries no business logic — and they land in the ObjectUI half. D1's authoring refusal (an `ast`-only envelope and a blank `source` are refused at authoring) is ruled by decision batch #122 item 2 and ships with the evaluated-slot narrowing that owns it, under that change's own ADR-0087 entry.
10+
11+
**ADR-0089 gains an addendum, not a reopening.** It unified the `visibleWhen` / `visibleOn` / `visibility` family under one name; ADR-0137 owns what that family does when a predicate cannot run, and ADR-0089 itself is unchanged by this release.
12+
13+
**Not carried by this entry: the `cel` / `expression` return-type narrowing to `EvaluatedExpression`.** This card touched that signature too, but main shipped the identical narrowing first, under #18638 (card #15811) — see that release's own changeset for the `EvaluatedExpression` story and the TS2322 it fixes. Restating it here would announce, a second time, a fact this release has already shipped under a different entry.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
**BREAKING** for authored metadata — `ObjectSchema.fields` refuses a key named `__proto__`, `constructor` or `prototype`, and `AssignmentConfigSchema.assignments` (the `assignment` flow node's variable map) refuses a key named `__proto__` — both refused with a named, located error at parse time, rather than silently accepted and then silently mishandled (objectstack#17852, objectstack#18847).
6+
7+
## Why
8+
9+
zod's `z.record()` skips a `__proto__` own key entirely, above its own key schema — the record parser's `if (key === "__proto__") continue;` runs before `def.keyType._zod.run`, so no key grammar (a regex, `.refine()`, `.superRefine()`, even a key schema that rejects every string) can ever see that key. A document whose `fields` (or `assignments`) carried a `__proto__` own key — which `JSON.parse` produces routinely — used to parse as SUCCESS with that key silently missing from the output: the validator accepted a document and handed back a *different* document. `os build` writes the release artifact from that returned document, so the failure shape is success, silent, and irreversible into the shipped artifact.
10+
11+
Two independent mechanisms close this, one per name class, because they are not reachable the same way:
12+
13+
- `__proto__` is refused by a **pre-parse guard** that reads the raw input's own keys before the record ever parses, at both `ObjectSchema.fields` and `AssignmentConfigSchema.assignments`.
14+
- `constructor` and `prototype` — which, unlike `__proto__`, DO reach the key schema unskipped — are refused by `ObjectSchema.fields`' own key grammar (they were ordinary lowercase words its regex already admitted). They are **not** refused at `AssignmentConfigSchema.assignments`: that slot's key type carries no grammar at all (`z.string().min(1)`), both names are legal flow-VARIABLE names measured to survive parse intact today, and no ruling narrows that slot's accept set for them — only its `__proto__` half moves.
15+
16+
Measured: zero authored use of any of the three names as a `fields` key or an `assignments` variable name, across this repo, `examples/` and `objectui`.
17+
18+
## Known gap, left open on purpose
19+
20+
The guard runs at parse time only. It does not project into the published JSON Schema (`packages/spec/json-schema/**`) — the general gap that closes is tracked separately (objectstack#18670) and stays open after this change.
21+
22+
Clause-②: yes (narrowing)
23+
24+
<!-- adr-0087: not-required (no-migration-prescription) zero authored use of `__proto__`, `constructor` or `prototype` as a `fields` key or an `assignments` variable name across this repo, examples/ and objectui — nobody has anything to rewrite, so there is no prescription to give. -->

0 commit comments

Comments
 (0)