Skip to content

Commit 4ebde56

Browse files
committed
Merge origin/main into claude/issue-16000-type-source-resolution-paths-guidance
Claude-Session: https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8 Co-authored-by: Claude <noreply@anthropic.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 parents 0f81717 + 8647c87 commit 4ebde56

20 files changed

Lines changed: 1978 additions & 70 deletions
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
"@objectstack/cli": patch
3+
---
4+
5+
`os init -t app`, `os init -t plugin` and `os g object` now emit an object file that compiles. All three wrote `const … : Data.Object`, and `@objectstack/spec/data` exports no member named `Object`, so the first command a new user runs produced a project that failed its own `pnpm typecheck`.
6+
7+
Measured against the **published** package a real user installs (`npm pack @objectstack/spec@17.3.0`, extracted and linked into a driven emission), not against the workspace:
8+
9+
```
10+
error TS2694: Namespace '.../@objectstack/spec/dist/data/index' has no exported member 'Object'
11+
tsc exit 2
12+
```
13+
14+
Identical at TypeScript 5.3.3, 5.8.3 and 6.0.3, so it was never a compiler-version effect. `os create example` type-checked clean on the same tarball in the same run — the failure was specific to these emissions.
15+
16+
The annotation is now `Data.ServiceObject`. That name was not chosen here — it is what [ADR-0122](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0122-schema-type-alias-naming-convention.md) D1 already ruled: for a schema `XSchema`, the **bare** alias denotes the author state (`z.input<typeof XSchema>`), and it is "the name documentation, examples, skills and AI authoring surfaces use for the thing an author writes". An emitted scaffold is the thing an author writes, so the bare alias is the one it owes. The sibling generators were already on that convention — `UI.View`, `UI.Action`, `UI.Dashboard` and `Automation.Flow` are each the bare alias of their own schema — and only the object emitters had drifted off it.
17+
18+
**Nothing was added to `@objectstack/spec`**: `ServiceObject` has been exported from `@objectstack/spec/data` throughout.
19+
20+
The parsed-state alias is not an alternative here. Annotating the same emitted literal `Data.ServiceObjectParsed` fails all three cases with `error TS2740`, because every field literal is then missing the keys the schema supplies by default — which is exactly the author-state/parsed-state distinction ADR-0122 D2 draws.
21+
22+
`content/docs/deployment/cli.mdx` taught the broken spelling too, and is corrected with them — a reader copying from the docs wrote the same uncompilable line.
23+
24+
The whole emitter roster was swept rather than the three reported sites: driving every `os init` template and every `os g` generator through `tsc --noEmit` under the tsconfig the scaffolder itself writes, `Data.Object` was the only non-existent member any of them named. In particular `UI.View` and `Automation.Flow` — named alongside `Data.Object` in the docs line and explicitly not swept when this was reported — are genuinely exported, and their generators compile at exit 0.
25+
26+
Why nothing caught this: both existing scaffold sweeps are runtime pins that load the emitted TypeScript through esbuild, which erases type annotations **without checking them**, so a broken annotation transpiles to byte-identical JavaScript and is invisible to them by construction. The scaffolds parsed, validated and loaded; they simply did not compile. A new pin runs the emitted projects through a real `tsc` program, with a canary that must fail with TS2694 so the harness cannot pass by resolving nothing.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
"@objectstack/metadata": minor
3+
---
4+
5+
A completed run of the ADR-0030 notification cut-over now records itself in the `sys_migration` deployment ledger, per the ruled claim matrix.
6+
7+
`migrateSysNotificationToEvent` reports `migrated` / `already_done` / `not_applicable` / `error` to its caller and — until now — recorded nothing anywhere. Once that line had scrolled, "did this cut-over run here, and when" had no answer in the deployment even in principle. The ledger row is what answers it, and what a run of this migration may claim under `NOTIFICATION_EVENT_MIGRATION_ID` is stated on that constant in `@objectstack/spec/system`:
8+
9+
- `last_run_at` — stamped on every completed non-`error` run (`migrated`, `already_done`, `not_applicable` alike).
10+
- `applied_at` — stamped only on `migrated`. Never cleared: a later `already_done` leaves an earlier backfill's stamp alone, because the backfill really did happen.
11+
- `verified_at` — never written, in either direction. This migration has no self-check, and `verified_at` means a self-check passed. On a store created after the cut-over the row already exists and `attestFreshDatastore` set `verified_at` at birth; that certificate survives a run untouched, because the column is omitted from the update rather than sent as `null`.
12+
- `blocking: 0`, and `details` carrying `{ outcome }` verbatim.
13+
- An `error` run writes no claim at all — it does not know what it did, so it does not say.
14+
15+
**Receipt, not gate.** Nothing reads a row under this id as a precondition and nothing may: a gate would need the self-check that does not exist. The row is what an operator reads, in the shape `sys_migration` already documents for the seed-tenancy repair.
16+
17+
Two additions to the published surface of `@objectstack/metadata/migrations`, both driven by that: a new `SysNotificationMigrationReceipt` type, and a new `receipt` member on `SysNotificationMigrationResult` reporting what became of the claim (`inserted` / `updated` / `not-claimed` / `no-ledger` / `failed`, with a reason on the last two). This directory takes no logger and reports to its caller, so the claim's own fate is reported the same way the migration's is — a receipt that could not be written is never swallowed. Reading a result is unaffected; code that CONSTRUCTS a `SysNotificationMigrationResult` by hand (a test double) now supplies `receipt`.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
"@objectstack/metadata-protocol": patch
3+
---
4+
5+
A dashboard bound to a dataset you just saved now publishes, without restarting the runtime.
6+
7+
The author-time gate that runs on every `active` metadata publish resolves a widget's `dataset` (and a `type: 'page'` view's `pageName`, and the sibling collections the cross-collection security rules compare against) against a resolution universe the host gathers per write. That gather read the SchemaRegistry alone. The registry is filled at boot by code packages, and for every metadata type except `object` a runtime write does not reach it — so a dataset saved through `PUT /api/v1/meta/dataset` was invisible to the gate until the process restarted, while `GET /api/v1/meta/dataset` returned it in the same instant with `_diagnostics.valid: true`.
8+
9+
Measured on the reported shape, in one process with no restart between the steps: the row is in `sys_metadata`, the read API lists six datasets, the registry lists the five code-package ones, and a three-widget board bound to the new dataset was refused `422` with three `widget-dataset-unknown` issues whose hint enumerated every dataset except the one just authored. The same request answered `200` after a restart, nothing else changed.
10+
11+
The gather now folds the stored half onto the registry half for every collection it carries. What that does and does not do:
12+
13+
- **Additive.** A stored row contributes a name the registry does not already carry and never displaces a registry entry — an object's registry copy is its resolved schema (base plus `extend` contributors) and a raw `sys_metadata` row is the base layer alone, so replacing it would trade this phantom for a subtler one. Where an org overlay redefines a code-package item, the gate still judges that item's content from the registry's version.
14+
- **Active rows only.** A draft does not resolve. The refuse-at-publish ruling exists so an author can write the widget first and the dataset second; a draft dataset that satisfied a published board would invert it.
15+
- **Scoped to the write's own partition** — environment-wide rows plus, when the write has one, its own organization. No other organization's overlays are visible to the gate, on any kernel.
16+
- **A failed store read is reported, not swallowed.** Context gathering still never fails a write, but a read that fails for any reason other than an unprovisioned `sys_metadata` now says so once, naming the consequence — a gather that silently shrinks is how a phantom refusal is manufactured in the first place.
17+
18+
The rules themselves are unchanged: a reference that resolves in neither home is still refused, with the same code, status and key path.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
"@objectstack/platform-objects": patch
3+
---
4+
5+
fix(platform-objects): 21 zh-CN metadata-form leaves say what their source says
6+
7+
`zh-CN.metadata-forms.generated.ts` carries 615 leaves that differ from `en` and hold no
8+
digest in `zh-CN.source-hashes.generated.ts` — LEGACY-TRUSTED values carried in from a
9+
pre-consolidation hand vocabulary (`e0077ea36` deleted a 746-line
10+
`src/metadata-translations/zh-CN.ts` and imported its strings) and never reconciled
11+
against the English the same commit range seeded. A census of all 613 (as the population
12+
then stood) found 26 that assert something the source does not, or drop a distinct concept
13+
the source names. Five of the 26 — the whole `dashboard` subtree — landed in `9f57f1e31`.
14+
These are the remaining 21.
15+
16+
They are not stale fills and no gate can see them: a stale fill is a byte copy of a
17+
previous source revision, detectable by cross-locale agreement or a recorded digest, and
18+
these are neither. Nor does re-extraction correct them — bundle merge fills gaps only, and
19+
a present-but-wrong leaf is not a gap.
20+
21+
Three defect kinds, all decided against this bundle's own usage:
22+
23+
- **Asserts an input that does not exist.** `skill.sections.triggers.description` promised
24+
「触发关键词」 for a section holding only `triggerConditions` (`triggerPhrases` was
25+
removed with the key); `email_template.fields.variables.helpText` promised a per-variable
26+
「默认值」 that `EmailTemplateDefinitionVariableSchema` does not declare;
27+
`action.sections.advanced.description` promised 「批量」 after `bulkEnabled` was removed
28+
from that section.
29+
- **Names the wrong technology.** `action.fields.body.helpText` said the body is
30+
「JavaScript 代码」; an L1 expression is not JavaScript. It now reads
31+
「L1 表达式或 L2 沙箱 JS 体」 — verbatim the sibling `hook.fields.body.helpText`, which
32+
translates the identical source sentence correctly.
33+
- **Drops a distinct concept the source names.** `object.fields.isSystem.helpText` dropped
34+
「共享默认为公开」; `view.fields.filter.helpText` reduced a sentence about the shared
35+
visual builder to 「筛选规则」; `permission.sections.identity.description` dropped both
36+
sentences explaining how permission sets stack on profiles.
37+
38+
zh-CN only: es-ES and ja-JP are untouched here. The 18 looser paraphrases the census
39+
excluded are also untouched.

content/docs/deployment/cli.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1299,7 +1299,7 @@ third-party extension primitive, authored as `src/skills/<name>.skill.ts` with
12991299
- `--dry-run` — Preview without writing files
13001300

13011301
**What it does:**
1302-
1. Creates a typed TypeScript file using `Data.Object`, `UI.View`, `Automation.Flow`, etc.
1302+
1. Creates a typed TypeScript file using `Data.ServiceObject`, `UI.View`, `Automation.Flow`, etc.
13031303
2. Creates or updates the barrel `index.ts` in the target directory
13041304
3. Shows a hint to run `objectstack validate`
13051305

packages/cli/src/commands/generate.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const GENERATORS: Record<string, {
5757
/**
5858
* ${toTitleCase(name)} Object
5959
*/
60-
const ${toCamelCase(name)}: Data.Object = {
60+
const ${toCamelCase(name)}: Data.ServiceObject = {
6161
name: '${toSnakeCase(name)}',
6262
label: '${toTitleCase(name)}',
6363
pluralLabel: '${toTitleCase(name)}s',

packages/cli/src/commands/init.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ export default defineStack({
562562
`,
563563
'src/objects/__name___item.object.ts': (_name, namespace) => `import * as Data from '@objectstack/spec/data';
564564
565-
const ${toCamelCase(namespace)}Item: Data.Object = {
565+
const ${toCamelCase(namespace)}Item: Data.ServiceObject = {
566566
name: '${namespace}_item',
567567
label: '${toTitleCase(namespace)} Item',
568568
fields: {
@@ -650,7 +650,7 @@ export default defineStack({
650650
`,
651651
'src/objects/__name___item.object.ts': (_name, namespace) => `import * as Data from '@objectstack/spec/data';
652652
653-
const ${toCamelCase(namespace)}Item: Data.Object = {
653+
const ${toCamelCase(namespace)}Item: Data.ServiceObject = {
654654
name: '${namespace}_item',
655655
label: '${toTitleCase(namespace)} Item',
656656
fields: {

0 commit comments

Comments
 (0)