Skip to content

Commit 4b74366

Browse files
committed
Merge origin/main (post-#6526 retirement sweep) into claude/issue-5728-i18n-label-contract-sweep
Second conflict round on the pin count: the sweep's -7 (755->748) merged in under this branch's +1 (InlineLocaleMapSchema); receipts stacked in merge order, count recomputed from the file = 749. Generated files taken from origin/main verbatim; regenerated wholesale in the follow-up commit.
2 parents 976dc9b + 53ef057 commit 4b74366

106 files changed

Lines changed: 5853 additions & 3708 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: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
fix(spec): 参考页里写在同目录的裸源码路径不再以纯文本落地 (#6484)
6+
7+
参考页开篇那段模块描述由 `packages/spec/scripts/lib/file-description.ts` 渲染。它把
8+
JSDoc 里裸写的 `*.zod.ts` 路径改写成站内链接,而这条机制的**两侧**过去都要求路径里
9+
至少有一个目录段:改写正则的 `[\w-]+/` 分组是必需的,`build-docs.ts`
10+
`sourcePathToDocsRoute()` 也要求那个斜杠、并把第一段读作分类名。
11+
12+
于是作者按最自然的方式引用邻居 —— 写 `auth.zod.ts` 而不是 `identity/auth.zod.ts` ——
13+
两侧都匹配不上,既没成链接,也没回退成代码段,以**纯文本**发布在四张参考页上,共 9 处:
14+
`api/realtime-shared``cloud/package``identity/identity``system/security-context`
15+
16+
缺的从来不是正则,而是**上下文**:`build-docs.ts` 按分类遍历,自己知道正在渲染哪个目录,
17+
却只把一个成员交给渲染方。现在 `FileDescriptionContext` 增加 `fromCategory`,由
18+
`build-docs.ts` 传入,裸文件名在渲染方补全成 `<分类>/<文件>` 后再去解析 —— 与
19+
`schemaHrefFrom(fromCategory)` 是同一道缝。补全放在调用方一侧是有意的:裸名不是身份
20+
(#4696),`auth.zod.ts` 在多个分类下都存在,让解析器自己去全分类搜同名文件只会答出
21+
目录遍历最后到达的那一个。
22+
23+
读者可见的变化是这 9 处:**5 处成为可点链接**(`api/realtime``api/websocket`
24+
`cloud/package-version``cloud/environment-package``system/encryption`),**4 处回退成
25+
代码段**(`auth``audit``compliance``masking` —— 这四个邻居本就不存在,按 #6229
26+
的规矩「目标没有页面就不发链接」)。纯文本是三种结果里唯一错的那种,现在一处不剩。
27+
28+
`sourcePathToDocsRoute()` 同时补上了它文档里一直声明、实现却没做的那一半:分类是真的
29+
不等于页面存在。旧实现只校验分类,这在放宽之前侥幸成立(能匹配上的路径恰好都有页面);
30+
放宽后那 4 个不存在的邻居会各产出一条 404 链接。现在按本次运行真正发出的页面清单判断,
31+
全语料 216 条站内路由、437 个位置,无死链。
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
"@objectstack/service-automation": patch
3+
---
4+
5+
fix(service-automation): the last three `engine.ts` seams stop splicing a driver's failure into the log message, and two of them are re-graded `error` (#6299)
6+
7+
All three catches sit around the `SuspendedRunStore` driver and rendered their
8+
failure by interpolating the thrown value's `.message` into the log MESSAGE.
9+
`ObjectLogger.write()` adds exactly one `<ts> <LEVEL>` head per call, so a
10+
driver error carrying newlines turned ONE record into several physical lines of
11+
which only the first was greppable — and on the `warn` path, inside `serve`'s
12+
boot-quiet window, `BootLogCapture.offer()` keeps only lines with a level head,
13+
so the continuation lines were dropped outright. Measured on the restored
14+
concatenation: a three-line driver error became 3 physical lines and the boot
15+
filter retained 1, and that one carried no driver fact. The cause now goes to
16+
the logger's structured slot (`describeThrownForLog`), so the record stays on
17+
one physical line in every format. This closes the family of #5048 / #5575 /
18+
#5636 / #5661 / #5737 / #5912 / #6230 for this file.
19+
20+
The level was judged per seam (#4632), not batch-copied from #6230:
21+
22+
- **`forgetSuspendedRun` → raised to `error`.** The hot cache is dropped before
23+
the store delete and this is the single choke point every consumption of a
24+
suspension passes through, so a failed `delete` leaves the suspension gone
25+
in-process and the durable row alive. Callers still report success, and the
26+
surviving row is re-listed and re-resumed after the next restart, running a
27+
continuation that already ran.
28+
- **`cancelRun` → raised to `error`.** An unreadable store makes the failed read
29+
read as "no such suspended run", so the method returns `false` — which its
30+
contract calls idempotent success — and the cancellation is silently skipped
31+
while the call reads clean. The run stays parked and durably resumable.
32+
- **`listSuspendedRunsDurable` → stays `warn`.** Nothing claimed-persisted
33+
failed to land: the rows are intact and still resumable by id. The listing
34+
degrades to the in-memory cache alone, so the message now says out loud that
35+
the result is short and that the caller cannot tell.
36+
37+
Operator-visible: two records move from stdout to stderr and from `WARN` to
38+
`ERROR`, and all three messages are reworded to state their consequence. Log
39+
filters or alert rules keyed on the old `warn`-level text for a failed
40+
suspended-run delete or cancel need updating.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
"@objectstack/spec": major
3+
---
4+
5+
refactor(spec)!: retire the L2 ETL layer — `automation/etl.zod.ts` had no executor, and the sync architecture doc was recommending it (#6414)
6+
7+
`ETLPipeline`, `ETLPipelineRun`, `ETLSource`, `ETLDestination`, `ETLTransformation`,
8+
the `ETLEndpointType` / `ETLTransformationType` / `ETLSyncMode` / `ETLRunStatus`
9+
enums and the `ETL` factory are REMOVED under ADR-0049 enforce-or-remove. The whole
10+
file goes, on the same reading #4738 used to retire L1 `DataSyncConfig` one layer up:
11+
**narrative-only**. No engine ever parsed, scheduled or executed an `ETLPipeline`.
12+
13+
Measured on `origin/main` immediately before the removal: the only non-spec
14+
references in this repo are two fumadocs-generated documentation sources
15+
(`apps/docs/.source/*.ts`), not executors; objectui has no reference at all; and
16+
there is no `packages/spec/liveness/etl.json`, so no ADR-0049 gate ever had a reading
17+
on the surface — while the same file family's EXECUTED half does have one
18+
(`liveness/mapping.json`), which is what makes that absence meaningful rather than an
19+
oversight.
20+
21+
FROM → TO, layer by layer — with one gap stated plainly instead of redirected:
22+
23+
| removed | use instead |
24+
|---|---|
25+
| `ETLPipeline.source` + `syncMode` + `schedule` (scheduled extraction from an external system) | `ConnectorSchema.syncConfig` (`integration/connector.zod.ts`) — the live, parsed sync surface: strategy, direction, cron schedule, `conflictResolution`, batching, delete mode |
26+
| `ETLTransformation` of type `map` / `cast`-like per-field work | `mapping.fieldMapping[].transform` (`data/mapping.zod.ts`) — `none`/`constant`/`map`/`split`/`join`/`lookup`, applied row by row by the REST import path |
27+
| `ETLPipeline.schedule` alone | `system/job.zod.ts` |
28+
| `ETLTransformation` of type `join` / `aggregate` / `script` / `merge` / `deduplicate` / … | **nothing.** There is no replacement because there was never an implementation — those ten transformation types named capabilities no runtime had. Do the work where it runs (the destination warehouse's ELT, a `flow`, a scheduled job), and let multi-stage movement return through ADR-0049's ENFORCE route: the engine first, the vocabulary second |
29+
30+
**The fix:** delete the import. Nothing was ever deployed under an `ETLPipeline`
31+
that is the finding, not a consolation — so there is no data migration; `tsc` reports
32+
TS2724/TS2305 at every import of a retired name.
33+
34+
**`packages/spec/docs/SYNC_ARCHITECTURE.md` is rewritten in the same change**, and
35+
that is not incidental. It named `ETLPipeline` as the recommended destination for
36+
authors displaced by the L1 retirement and tabulated ten transformation types with
37+
copyable examples down to `script | Custom JavaScript/Python`. Retiring the schema
38+
while the doc still recommended it would have been self-contradictory, and
39+
forwarding L1's authors to a second layer with no executor was the defect compounding
40+
rather than closing.
41+
42+
**Absorbed:** the #4962 `etl-retry-converged-onto-retry-policy` entry (`retry.maxAttempts`
43+
`maxRetries`, default 3 → 0) — both land in the unreleased protocol 17, so composed,
44+
a rename on a shape that does not survive the major has no observable effect, and its
45+
`retiredKey()` tombstone goes with the shape that carried it.
46+
47+
The retirement kit — route 3: no tombstone, no D2 conversion.
48+
`RETIRED_DEFS_BY_MAJOR[17]` (9 defs) plus the D3 `SemanticMigration`
49+
`etl-pipeline-layer-retired` are the declaration.
50+
51+
<!-- adr-0087: registered etl-pipeline-layer-retired -->
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
"@objectstack/spec": major
3+
---
4+
5+
refactor(spec)!: retire `system/http-server.zod.ts`'s runtime vocabulary — the event, capability and status shapes nothing ever emitted (#5295)
6+
7+
`ServerEventType`, `ServerEventSchema` / `ServerEvent`, `ServerCapabilitiesSchema` /
8+
`ServerCapabilities` / `ServerCapabilitiesParsed` and `ServerStatusSchema` /
9+
`ServerStatus` are REMOVED under ADR-0049 enforce-or-remove. This is the second and
10+
final pass over the file: #4938 removed its CONFIG half (`HttpServerConfigSchema`,
11+
nine keys, zero readers, zero authoring entry), and this removes the RUNTIME half —
12+
a 7-member lifecycle event union, an eight-boolean capability report and a
13+
five-state status record with connection and request counters. Nothing ever emitted,
14+
consumed or parsed any of them.
15+
16+
FROM → TO:
17+
18+
| removed | what actually decides it |
19+
|---|---|
20+
| `ServerEventType` / `ServerEvent(Schema)` | nothing emits a server event feed. Lifecycle is the transport plugin's own start/stop seam; observability is `system/metrics.zod.ts` + `system/logging.zod.ts`, and `OS_SERVER_TIMING` for timings |
21+
| `ServerCapabilities(Schema/Parsed)` | a transport plugin declares what it provides by implementing the kernel plugin contract — the seams it registers ARE the capability statement |
22+
| `ServerStatus(Schema)` | `/health` for liveness, the metrics surface for counters |
23+
24+
**The fix:** delete the import. There is no replacement key, because there was
25+
never a key — none of the four was authorable on any shape. Server-level
26+
configuration that IS authorable is untouched: `defineStack({ server: { trustProxy,
27+
security } })` / `StackServerConfigSchema` (#5006) parses exactly as it did in 16.x,
28+
as does the route-registration half of the same module (`RouteHandlerMetadata`,
29+
`MiddlewareType`, `MiddlewareConfig`).
30+
31+
**Why now, and what unblocked it.** The card was held rather than queued on a real
32+
doubt: a response/capability vocabulary can legitimately be a REFERENCE surface for
33+
host implementers, so "zero consumers in this repo" is weaker evidence for one of
34+
those than for an authorable key. It was lifted by measuring the reference reader
35+
itself — `plugin-hono-server`, the one in-tree host implementation, neither
36+
implements nor reports any of the three: it names no capability record, no status
37+
shape and no event union, and what it registers is routes and middleware. The
38+
control passed in the same sweep (`MiddlewareConfig`, twelve lines away, resolves to
39+
`packages/runtime/src/middleware.ts`).
40+
41+
The retirement kit — route 3 of the retirement playbook, as #4938 was in this same
42+
file: **no `retiredKey()` tombstone and no D2 conversion**, because a prescription
43+
nobody can receive is noise and there is no authored document to rewrite.
44+
`RETIRED_DEFS_BY_MAJOR[17]` (4 defs) plus the D3 `SemanticMigration`
45+
`http-server-runtime-vocabulary-retired` are the declaration; the generated
46+
baselines (`json-schema.manifest/system.json`, `authorable-surface/system.json`,
47+
`api-surface/system.json`) lose their entries in the same change, deliberately.
48+
49+
If host-implementer conformance becomes a real requirement it returns through the
50+
ENFORCE route: an adapter contract with a checker behind it, vocabulary second.
51+
52+
<!-- adr-0087: registered http-server-runtime-vocabulary-retired -->
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
"@objectstack/rest": patch
3+
"@objectstack/objectql": patch
4+
---
5+
6+
fix(rest,objectql): the import dry run asks the engine for its verdict instead of predicting it (#4633 ruling D)
7+
8+
`POST /api/v1/data/:object/import?dryRun=true` green-lit rows the very same
9+
endpoint then rejected. Measured on 17.0.0-rc.1: a CSV cell aimed at a
10+
structured `address` field reported `{ ok: 1, created: 1 }` on the dry run and
11+
`{ errors: 1, code: 'VALIDATION_FAILED' }` on the real write.
12+
13+
The dry run predicted the write's verdict with a hand-copied mirror of a slice
14+
of the engine's rules (`import-coerce.ts`'s `firstMissingRequiredField` and
15+
`firstConstraintViolation`). A copy cannot structurally keep up with the family
16+
it mirrors: ADR-0104 value shapes (`address` / `location` / references / media),
17+
`format` checks, object-level `validations` and the state machine had no
18+
counterpart, and `coerceFieldValue` routes structured shapes through its
19+
pass-through catch-all, so no verdict was formed at all.
20+
21+
**The mirror is retired.** The dry run now calls `DataProtocol.validateData`
22+
(#6037), which runs the same `validateRecord` / `evaluateValidationRules` that
23+
`insert()` runs, under the deployment's own ADR-0104 posture — so a bad value
24+
shape is an error on a self-certified deployment and an admitted warning on a
25+
warn-first one, exactly as on the write. Agreement is by construction, not by a
26+
copy kept in step by hand.
27+
28+
Also in this change:
29+
30+
- **`engine.validate()` now resolves `defaultValue`s and seeds owned roll-up
31+
`summary` fields before validating, on `insert` mode**, because `insert()`
32+
does. Without it a required-but-defaulted column left unmapped was previewed
33+
`failed` and written `created` — a false alarm on the row a preview is meant
34+
to reassure you about. `update` mode still does not default (#2706).
35+
- **A row report failed by validation now names the offending column.** The
36+
engine's `ValidationError` carries `fields[]`, so the row's `field` is set and
37+
its `code` is the field-level code (`required`, `min_value`, `max_length`,
38+
`invalid_type`, …) rather than the wrapper's `VALIDATION_FAILED`. This is the
39+
same vocabulary the dry run and the per-cell coercion failures already spoke;
40+
before, a `min: 0` violation was `min_value` on the dry run and
41+
`VALIDATION_FAILED` on the write.
42+
- **Dry-run rows may carry `warnings[]`** — findings this deployment admits
43+
rather than rejects (ADR-0104 warn-first). The row is `ok`, and the complaint
44+
is visible instead of living only in a server log line.
45+
46+
A protocol that does not implement `validateData` (plugin-auth's identity
47+
import, whose write is better-auth rather than the engine) is not handed a
48+
substitute: its dry run reports coercion and create/update/skip resolution only.
49+
An engine-derived preview of a non-engine write would report findings that write
50+
never produces.

.changeset/lucky-pears-arrive.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
'@objectstack/cli': patch
3+
---
4+
5+
fix(cli): `--json` now owns stdout — kernel boot logs move to stderr (#6217)
6+
7+
Every `os migrate` / `os meta` subcommand that boots a kernel wrote its
8+
machine-readable payload into a stream it shared with ~60 INFO lines. The
9+
kernel logger routes `debug`/`info`/`warn` to stdout and only `error`/`fatal`
10+
to stderr, so `os migrate recorded-by --json | jq .` failed with `parse error:
11+
Invalid numeric literal` while stderr sat completely empty — a `--json` flag
12+
whose only audience is a program, handing that program something it cannot
13+
parse.
14+
15+
With this change, a `--json` run reserves stdout for its payload: everything
16+
the kernel and its plugins write goes to **stderr** instead, including the
17+
`[StandaloneStack] no compiled artifact …` notice that never went through the
18+
logger at all. `JSON.parse(<entire stdout>)` now succeeds with no heuristic
19+
extraction, and no diagnostic is lost — every line an operator used to see is
20+
still printed, on the stream diagnostics belong on.
21+
22+
Covers the whole family that shares the boot seam: `os migrate plan` / `apply`
23+
/ `resume` / `recorded-by` / `summary-nulls` / `value-shapes` /
24+
`files-to-references`, `os migrate meta --stored`, and `os meta resync`.
25+
Human-mode runs are unchanged.

0 commit comments

Comments
 (0)