Repository navigation
Commit 482e089
domain:spec#3 · os-support-ai · session_01WGufzft7TrxiryiTZm4o9p
<!-- claude-projects-attribution: {"github_login":"os-support-ai"} -->
_Requested by **Yibo Wang** · [project
thread](https://claude.ai/code/project/chan_01BVUyyPie8gHwoPM18iABwB?thread=cmsg_01BVUyyPie8gHwoPM18iABwBUnyh7Bt4mU16ZEEJTzLZHv)_
Fixes #30471
Clause-②: yes (narrowing) — `os validate` / `os build` / `os lint`
refuse three shapes they accepted before: a hook body's literal
`ctx.api.object(NAME).update(…)` / `.updateById(…)` / `.insert(…)`
writing a field an `objectExtensions` entry adds and declares `readonly:
true` moves from a `hook-body-write-unknown-field` warning to a
`hook-api-update-readonly-field` error; and a translation key naming a
field or validation rule that only a replaced earlier entry of the same
package adds, or only a top-level `objectExtensions` beside `packages[]`
adds, is now `translation-target-unknown` at `error`, where it resolved,
and with that top-level-beside shape no longer read, a translation
subtree `objects.X` keyed to an object the stack does not define and
only such an entry extends loses the extended-object skip (rung 2b,
`validate-translation-references.ts` :1859) and is
`translation-target-unknown` at `error` too; and a field an
`objectExtensions` entry RE-DECLARES on its owner's object is judged on
the extension's definition, which replaces the owner's whole in the fold
as in the registry's `mergeObjectDefinitions` (`{ ...base.fields,
...extension.fields }`), so an owner `amount: number` re-declared as
`formula`, `readonly: true` or `text` now gets that definition's
`sort-field-unsortable`, readonly-write or expression type-mismatch
verdict, each at `error` (ADR-0146 D10 already refuses that shape on the
extension; no CLI door does). The widening half: a formula, validation,
flow condition, hook condition, sharing-rule condition, flow-node,
action-body or hook-body write, react page field prop, translation key,
page sort or list-view sort naming a field an `objectExtensions` entry
of the same artifact adds is no longer refused as an unknown field.
Metadata-protocol: narrowed
Skills-docs: updated here — content/docs/automation/hook-bodies.mdx
(`:220`, the write rules' declarations include extension fields; `:312`,
the same for `hook-api-update-readonly-field`). Measured at `7ca63171`:
`git grep` of the moved ids (the three unknown-field write ids, the five
readonly ids, `sort-field-unknown`, `sort-field-unsortable`,
`page-field-unknown`) over `skills/` and `content/docs/` (releases and
references excluded) hits 11 lines in 3 files;
`content/docs/automation/hooks.mdx:43-47` and
`content/docs/deployment/validating-metadata.mdx:177` state the rules
without a declared-only claim ("a field the bound object lacks") and
read true; no skill names them.
Prior rulings read:
objectextensions,buildfieldindex,indexobjectfields,buildreadonlyindex,indexobjectsortability
→ 2 hits; ADR-0029 D3, ADR-0146 D10; thread: not read (no --card); repo:
objectstack-ai/objectstack
## 维护者速读
- 改了什么:lint 里剩下的字段名索引(表达式检查、流程节点/动作体/钩子体三条写入规则、React
页面字段属性、翻译键、页面与列表视图排序、三条只读写入规则)都改读同一份“按注册表实际提供的对象”折叠:声明字段,加上同一制品里任何包通过
`objectExtensions` 加的字段。扩展字段不再被报成未知字段,它的定义(只读、能否排序、类型)按声明字段一样判断。
- 为什么改:HotCRM 这类多包制品里,模块包给 app 包的对象加了字段,lint
却把读、写、排序这个字段一律报“未知字段”,而运行时明明提供它;扩展字段真正的问题(写只读字段会被运行时静默丢掉、按公式字段排序每次都
400)也被“未知字段”盖住了。
- 风险与回滚:这是收窄,`os validate` / `os build` / `os lint` 会拒绝三类以前放行的制品:① 钩子体用
`ctx.api` 写一个扩展加的 `readonly: true`
字段,从警告(`hook-body-write-unknown-field`)升为错误(`hook-api-update-readonly-field`),因为运行时会静默剥掉这次写入;②
翻译键指向的字段或校验规则只出现在同一包被后一条替换掉的旧扩展条目里,或只出现在与 `packages[]` 并列的顶层
`objectExtensions` 里,现在报 `translation-target-unknown` 错误(注册表不提供它);同理,翻译里
`objects.X` 指向本制品没定义、只被这种顶层条目扩展的对象,不再按“被扩展的对象”跳过,也报这个错误;③ 扩展条目重新声明 owner
对象上已有的字段(如 owner 的 `amount: number` 被改成 `formula` / `readonly: true` /
`text`)时,按扩展的定义判断,与注册表 `mergeObjectDefinitions` 整个替换字段一致,于是排序报
`sort-field-unsortable`、流程或钩子写入报只读错误、校验规则里乘法报类型不符,均为错误(ADR-0146 D10
在注册时就拒绝这种写法,CLI 此前没有任何一道门拦它)。其余变化只是换规则编号且同级或降级:流程节点写只读扩展字段
错误→错误(`flow-update-readonly-field`),页面和列表视图按公式扩展字段排序
错误→错误(`sort-field-unsortable`),三处 `readonlyWhen`
写入为警告。两个包在同一优先级写同一字段时不判它的定义;一个字段都没声明的对象照旧不判。回滚 = revert 本 PR。
- 先看:`packages/lint/src/validate-readonly-flow-writes.ts` 的
`buildServedReadonlyIndex`(三条只读规则共用,含“对象一个字段都没声明就不判”);`packages/lint/src/validate-sortable-fields.ts`
的 `indexServedSortFields`;`packages/lint/src/object-graph.test.ts` 末尾
`[#30471]` 里 “each refusal this change adds” 一组。
- 两分钟验证:运行 `pnpm --filter @objectstack/lint exec vitest run
src/object-graph.test.ts -t 30471`,预期 69 例全绿;其中 “each refusal this
change adds” 的 9 行各自断言新的规则编号与级别,并断言只看声明字段时(即 main 的视角)仍报原来的未知字段;另 2
行(重新声明 owner 字段:列表视图按 formula 排序、流程写 readonly)断言新的错误,只看声明字段时什么也不报。
- 需要你做的:无,席位落地。
## What changes
- **`validate-expressions.ts`**: the field-name index
(`buildFieldIndex`) is built over `objectsAsServed(stack)`; the
field-type and nullable indexes over `objectsWithoutTiedFields`. The
walk stays on the declared `objects`, so every finding keeps its
declared path and an extension's own formula or predicate is not walked.
The read-attachment index is object-level and stays on the declared
objects.
- **`validate-hook-body-writes.ts` `indexObjectFields`** (read by the
flow-node, action-body and hook-body write rules): the served field
names; an object declaring no field keeps its empty set, so
`judgeableFieldsOf` still leaves it unjudged when an extension adds to
it. A tied name is kept (existence only).
- **`validate-react-page-props.ts`**: indexes the served objects, as
`validatePageFieldBindings` does since #30383.
- **`validate-translation-references.ts`**: its private
`objectExtensionsByTarget` is deleted; the universe reads the shared
fold, which now carries each entry's `validations` (`object-graph.ts`,
`VisibleObjectExtension.validations`).
- **The three readonly write rules** share a new
`buildServedReadonlyIndex` (`validate-readonly-flow-writes.ts`):
`buildReadonlyIndex` over
`objectsWithoutTiedFields(objectsAsServed(stack))`, an object that
declares no field keeping its own empty map. Without that index, moving
the existence question would have left a write to a readonly extension
field silent; without the empty-map guard, an extension adding a
readonly field to an object that declares none made the readonly rules
refuse writes `main` left silent (measured at `cc67f35a`: flow and hook
at `error`, the three `readonlyWhen` ids at `warning`).
- **`validate-sortable-fields.ts`** (open to this card since #30270
landed at `c045c341`): `indexServedSortFields` builds the existence
index (`indexObjectSearchTargets`' shape) over the served objects, a
tied name kept, and the sortability projection
(`indexObjectSortability`) over `objectsWithoutTiedFields`; an object
that declares no field map keeps its declared answer on both (skip 2).
`validate-searchable-fields.ts` is not touched; the unprovisioned-anchor
index is object-level and stays on the declared objects.
- **`rule-explanations.ts`** and
**`content/docs/automation/hook-bodies.mdx`**: "declared" includes the
fields an `objectExtensions` entry of the same artifact adds
(`DECLARED_FIELD_DOOR`, `page-field-unknown`,
`translation-target-unknown`; `hook-bodies.mdx:220`, `:312`).
- **Tests**: the `[#30471]` describe in `object-graph.test.ts` (the
fold's harness), 69 cases; see the table and Verification.
- **Docblocks** (no behaviour change): `objectsAsServed` states the
one-to-one, in-order contract `indexObjectFields` joins on (`objects[i]`
is the served form of `recordsOf(stack.objects)[i]`, early return
included); `validate-react-page-props.ts` at the served index and the
write index's docblock say that handing pre-served objects bypasses the
declares-nothing guard by construction, and why no verdict moves (the
react rule judged that empty set on `main` too).
- `.changeset/30471-field-name-indexes-extension-fields.md`:
`@objectstack/lint` **minor**, BREAKING banner, ADR-0087 disposition
`not-required (no-migration-prescription)`.
### Per site
One artifact: an app package declares `xs_deal` (`name`, `amount`) and
`xs_account`; a module package extends `xs_deal`.
| Site | Moved or kept | Why | Pin |
| --- | --- | --- | --- |
| `validate-expressions.ts` `buildFieldIndex` (validations, formulas,
flow and hook conditions, sharing-rule conditions) | moved | the runtime
evaluates `record` against the served row | an extension field read
raises nothing; refused without the extension; `nowhere` byte-for-byte;
per-package leg same |
| `validate-expressions.ts` field-type and nullable indexes | moved,
ties dropped | type and nullability are the served definition's; a tie's
winner is registration order | tie (either order, both legs) raises
nothing; the single-writer controls raise the type mismatch and the null
guard |
| `validate-expressions.ts` read-attachment index, hop walk | kept on
declared | object-level; the walk carries declared paths | n/a |
| `indexObjectFields` (hook-body-writes) for
`flow-node-write-unknown-field`, `action-body-write-unknown-field`,
`hook-body-write-unknown-field` | moved | the write lands on the served
object | each: passes, refused without, `nowhere` byte-for-byte,
per-package leg; an object declaring no field stays unjudged |
| `validate-react-page-props.ts` `columns` (page-field-bindings'
`indexObjectFields`) | moved | the renderer binds the served object |
passes, refused without, byte-for-byte, per-package leg |
| `validate-react-page-props.ts` `searchableFields`
(`indexObjectSearchTargets`) | kept on declared | its type question is
the searchable-fields rule's, carried | still `searchable-field-unknown`
on an extension field |
| `validate-page-field-bindings.ts` `indexObjectFields` | nothing to
move | both callers hand it served objects (`:640-641` since #30383,
react props `:1144`); #30270 added no declared-only reader | covered by
#30383's pins and the react row |
| `validate-translation-references.ts` private fold | retired onto the
shared fold (carries `validations`) | one fold; the registry's one entry
per package per target, `packages[]` alone when present |
`fields.region`, `_validations.region_set` resolve; refused without;
byte-for-byte; per-package leg; the two refusals the fold adds pinned at
`error` |
| readonly flow, hook and action rules (`buildReadonlyIndex`) | moved,
ties dropped, declares-nothing kept | readonly is the served
definition's; an object with no own field is an introspected schema |
the refusal table (9 rows × 3 legs) plus the per-rule describe |
| `validate-sortable-fields.ts` existence (`indexObjectSearchTargets`) |
moved, tie name kept, declares-nothing kept | the list door judges the
served schema; measured on the merged tree: a page sort naming an
extension field was `page-field-unknown` `error` on both CLI legs | page
sort and list-view sort rows in the existence table (passes, refused
without, byte-for-byte, per-package leg) |
| `validate-sortable-fields.ts` sortability (`indexObjectSortability`) |
moved, ties dropped, declares-nothing kept | without it a sort by an
extension formula field would turn silent where the door answers `400
INVALID_SORT` | refusal table rows (formula → `sort-field-unsortable`,
both legs, tie, declares-nothing) |
### Every finding that moves, against `main`
Measured with `runAuthoringRules('validate', …)` over the built dist of
`origin/main` at `c23eafa8` and of this head, on the union artifact and
the per-package leg (identical on both); the field is one an
`objectExtensions` entry adds to an object that declares fields of its
own. The last four rows were measured in round 2 over the built dist of
`origin/main` at `5b748dfe` and of this head; the no-extension control
is identical on both trees, and the re-declared shapes not listed (a
`formula` or `text` re-declaration written, a `readonly` one sorted or
read, an action's `ctx.api` write) move nothing.
| Write or read of the extension field | `main` | this head |
| --- | --- | --- |
| hook `ctx.api` update / updateById / insert, `readonly: true` |
`hook-body-write-unknown-field` warning |
**`hook-api-update-readonly-field` error** |
| translation key naming only a replaced same-package entry's field or
rule, or only a top-level `objectExtensions` beside `packages[]` |
resolves | **`translation-target-unknown` error** |
| flow `update_record` / `create_record`, `readonly: true` |
`flow-node-write-unknown-field` error | `flow-update-readonly-field`
error |
| page sort (every slot) / list-view sort, `formula` |
`page-field-unknown` / `sort-field-unknown` error |
`sort-field-unsortable` error |
| flow `update_record`, `readonlyWhen` | `flow-node-write-unknown-field`
error | `flow-update-readonly-when-field` warning |
| hook `ctx.api` update, `readonlyWhen` |
`hook-body-write-unknown-field` warning |
`hook-api-update-readonly-when-field` warning |
| action `ctx.api` update, `readonlyWhen` |
`action-body-write-unknown-field` warning |
`action-api-update-readonly-when-field` warning |
| expression with a type or null-guard problem | unknown-field error |
the type mismatch / null guard: error in validations and hook
conditions, warning in formulas and sharing-rule conditions |
| any other read, write or sort (a hook `ctx.input` stamp, an action
`ctx.api` write of a `readonly: true` field, a `create_record` of a
`readonlyWhen` field, a react `columns` prop, a plain sort) |
unknown-field error or warning | nothing |
| the same on an object that declares no field of its own | nothing |
nothing |
| an OWNER field an extension re-declares (owner `amount: number`),
sorted on a page (`interfaceConfig.sort`) or list view, re-declared
`formula` | nothing | **`sort-field-unsortable` error** |
| the same owner field re-declared `readonly: true`, written by a flow
`update_record` or a hook `ctx.api` update | nothing |
**`flow-update-readonly-field` / `hook-api-update-readonly-field`
error** |
| the same owner field re-declared `text`, multiplied in a validation
rule (`record.amount * 2 > 10`) | nothing | **`expression-invalid` error
(type mismatch `string * int`)** |
| a translation subtree `objects.X` keyed to an object the stack does
not define, extended only by a top-level `objectExtensions` beside
`packages[]` | skipped (rung 2b) | **`translation-target-unknown`
error** |
## Why
The runtime serves each object with every body's `objectExtensions`
folded in. After #30383 the field-path rules read that served shape, but
these field-name indexes still read declared objects only, so on a
multi-package artifact (HotCRM, hotcrm#2315) a formula, validation,
flow, hook, write, page prop, sort or translation key naming an
extension-added field was refused as unknown, and the extension field's
real problem (a readonly strip, an unsortable formula) went unjudged.
## Decisions
- **Translation references.** The shared fold carries each entry's
`validations` and the private fold is retired. Carrying one more key
changes nothing about which entries survive. The two measured
differences from the retired reader are the registry's reading and are
refusals at `error` (declared above and pinned): a key naming a field or
rule only a replaced earlier entry of the same package adds, and a
top-level `objectExtensions` beside `packages[]` (a shape no CLI leg
produces; with no `packages[]` the top level is read).
- **The declares-nothing skip** is kept on the readonly and sort rules
as on the write rules: an object that declares no field is an
introspected schema, and an extension adding to it does not make it
judgeable. The contract review read the readonly rules' `fieldMap.size
=== 0` guard as keeping this; on the served index it no longer fired, so
`buildServedReadonlyIndex` restores it.
- **`defineStack`'s `collectRelatedListNameErrors` stays additive.**
Aligning it with the registry's replacement measured as a further
narrowing (two shapes accepted today would be refused
`STACK_CROSS_REFERENCE_INVALID`), outside this card's declaration;
agreed with the seat.
## Acceptance notes
- **Census beyond the card** (by reading; not changed here, carried to
the seat): field indexes still on `recordsOf(stack.objects)`:
`validate-form-layout.ts` (map-shaped `fields` only),
`validate-notify-email-field-recipients.ts` and
`validate-public-form-file-fields.ts` (field types),
`validate-flow-template-paths.ts` (field types),
`validate-page-visualization-bindings.ts`,
`validate-searchable-fields.ts` `indexObjectSearchTargets` (also behind
a react page's `searchableFields`, pinned as still refused), and
`lint-autonumber-formats.ts` `:56-63` (`fieldMeta` from the object's own
fields, so an owner's autonumber format naming a field a sibling adds
reads as unresolvable).
- **Observed on `main`, not changed here:** an object that declares no
field, once an `objectExtensions` entry adds to it, gets
`list-view-field-unknown` at `error` on a list-view column the extension
does not add (the #30383 graph reads the served object as the
extension's fields alone); unextended, the same view is skipped.
Reported to the seat with the reading.
- **Ties inside the existence rules** (writes, page props, sorts) accept
the name, as #30383's existence rules do; only the definition questions
drop a tie.
- **The nullable index drops a tie even when every tied definition is
nullable.** Before this PR an extension field had no null-guard verdict
at all, so nothing is lost against `main`.
- **Title.** The changeset is BREAKING and its summary reads
`fix(lint)!:`; the seat added the `!` to the PR title with this body
(precedent #30270).
- **The page publish door.** `validateSortableFields` also runs at `PUT
/api/v1/meta/page/NAME`; this PR measured the CLI legs only.
## Verification
Round 2, at head `80c7ae3a` (this branch with `origin/main` at
`5b748dfe` merged in `fed33b86`, clean; the merge brings `packages/lint`
changes, so the dogfood battery was run again), after `turbo run build
--filter='!@objectstack/docs' --concurrency=2` under `os-verify-lock`
(118/118 tasks, VERDICT command-exit 0),
`NODE_OPTIONS=--max-old-space-size=4096`.
- `pnpm --filter @objectstack/lint test && pnpm --filter
@objectstack/lint typecheck` under `os-verify-lock`: Test Files 195
passed (195), Tests 8172 passed (8172); `check:test-typecheck: OK`;
VERDICT command-exit 0. The `[#30471]` describe: 69 passed.
- Full dogfood battery, `pnpm --filter @objectstack/dogfood test` under
the lock: VERDICT command-exit 0; Test Files 420 passed, 3 skipped
(423); Tests 3496 passed, 36 skipped (3532).
- `npx eslint` over the 12 changed `.ts` files: exit 0.
Round 1, at head `7ca63171` (`origin/main` at `c23eafa8` merged in
`cc67f35a`), 119/119 build tasks:
- `pnpm --filter @objectstack/lint test && pnpm --filter
@objectstack/lint typecheck` under `os-verify-lock`: Test Files 195
passed (195), Tests 8158 passed (8158); `check:test-typecheck: OK`;
VERDICT command-exit 0. The `[#30471]` describe: 67 passed.
- Consumers of `@objectstack/lint` whose tests run the rule registry or
the moved rules on fixtures, each under the lock, exit 0:
`@objectstack/cli` (`--project unit`; the integration layer is CI's) 389
files / 5430 tests; `@objectstack/metadata-protocol` 317 passed, 4
skipped / 22442 passed, 26 skipped; `@objectstack/mcp` 56 / 678;
`@objectstack/platform-objects` 112 / 1528;
`@objectstack/plugin-approvals` 111 / 1469;
`@objectstack/cloud-connection` 70 / 877; `@objectstack/service-mailbox`
10 / 152; `@objectstack/example-showcase` 46 / 544;
`@objectstack/objectql` 552 / 11005; `@objectstack/runtime` 543 / 8655
passed, 19 skipped.
- Full dogfood battery (the `(narrowing)` arm owes it), `pnpm --filter
@objectstack/dogfood test` under the lock: VERDICT command-exit 1; Test
Files 416 passed, 4 failed, 3 skipped (423); Tests 3482 passed, 3
failed, 47 skipped (3532). Every failure is a timeout, none an
assertion: `Hook timed out in 10000ms` in
`package-first-authoring.dogfood.test.ts` (and its three later suites
refused with the `already has a live bootStack kernel` cascade a
timed-out hook leaves) and in
`sys-file-metadata-write-refusal.dogfood.test.ts`; `Test timed out in
5000ms` in `activity-withheld-update.dogfood.test.ts` (1) and
`analytics-engine-middleware-objects.dogfood.test.ts` (2). The four
files re-run alone under the lock at load average 7.8: Test Files 4
passed (4), Tests 23 passed (23), VERDICT command-exit 0. None of them
reaches a moved rule's verdict.
- `npx eslint --no-inline-config` over the 12 changed `.ts` files: exit
0.
<details><summary>Before/after against main (the table above)</summary>
- Two throwaway probes (scratch, outside the repo) call
`runAuthoringRules('validate', …)` from each tree's built `dist`
(`origin/main` at `c23eafa8` in a second worktree, this head) on the
same stacks: an app body declaring `xs_deal` and `xs_account`, an object
declaring no field (`xs_empty`), and a module body extending both with a
`readonly: true` text field, a `readonlyWhen` number field, a plain text
field and a `formula` field; each write surface (flow `update_record` /
`create_record`, action `ctx.api` update / insert, hook `ctx.api` update
/ insert, hook `ctx.input`), each sort surface (`interfaceConfig.sort`,
an element `dataSource.sort`, a `defineView` list), the expression
passes (validation, formula, sharing rule, hook condition) with a text
read, text arithmetic, an unguarded number compare and a date compare, a
react `columns` prop and translation keys on the replaced-entry and
top-level-beside shapes; on the union artifact, the per-package leg and
a no-extension control.
- At the merged head before this round's code (`cc67f35a`), the readonly
rules refused writes to `xs_empty` that `main` left silent:
`flow-update-readonly-field` and `hook-api-update-readonly-field` at
`error`, the three `readonlyWhen` ids at `warning`, on both legs. At
this head no `xs_empty` case differs from `main`.
- Every remaining difference is a row of the table above; the
no-extension control is identical on both trees for every case.
- Round 2: a third probe, the same way over `origin/main` at `5b748dfe`
and this head, re-declares the owner's `amount: number` in the module
body as `formula`, as `readonly: true` and as `text`, against a page and
a list-view sort, a flow `update_record`, a hook and an action `ctx.api`
update and a validation rule `record.amount * 2 > 10`, plus a
translation subtree keyed to an undefined object extended by a top-level
entry beside `packages[]` and by a package body. The differences are the
four new rows of the table; the no-extension control and the
package-body translation control are identical on both trees.
</details>
<details><summary>Ablations (each through scripts/ablation-replace.mjs:
anchor hit once, the describe run, restored, blob equal to HEAD, git
diff HEAD empty)</summary>
The lint tests import `src` (relative imports, no alias to `dist`), so
no build sits between the mutation and the run.
- `indexServedSortFields`: existence over `stack` instead of the served
objects → **8 failed** / 59 passed (the two sortable existence rows on
both legs, the sort rows of the refusal table and their tie legs).
- sortability over `stack` → **4 failed** (the two sort refusal rows and
their tie legs); sortability over the served objects with ties kept →
**2 failed** (the two sort tie legs); the declares-nothing loop skipped
→ **2 failed** (the two sort declares-nothing legs).
- `buildServedReadonlyIndex`: the empty-map guard removed → **7 failed**
(the seven readonly declares-nothing legs); ties kept → **10 failed**
(every readonly tie leg, old and new).
- each readonly rule set back to the declared index: hook → **8
failed**, flow → **8 failed**, action → **4 failed** (that rule's
refusal rows, tie legs and the per-rule describe rows).
- `objectExtensionsByTarget` reading the top level beside `packages[]` →
**1 failed** (the new translation pin); keeping every entry of a package
→ **2 failed** (the replaced-entry pin and the new one).
- Round 2, `objectsAsServed` no longer replacing an owner's field an
extension re-declares (the fold keeps the owner's definition) → **2
failed** / 67 passed (the two re-declared rows), restored, blob
`6d6f1b21` equal to HEAD.
</details>
<details><summary>Gates</summary>
- Round 2, at `80c7ae3a`: `dispatch-gates --commands` derives the same
88 commands, each run as printed with its exit code recorded. First
pass: 87 exit 0 (the teardown fixture fetched again first) and `pnpm
check:type-check-debt` exit 3 (PREREQUISITE NOT MET: its closure build
timed out queueing behind a sibling's locked build); re-run under
`os-verify-lock` it exits 0 (0 raw tsc errors, none above its recorded
number). `dispatch-gates --ran`: "88 derived, 88 run, 0 NOT-MEASURED",
exit 0. `check-changeset-no-major --base origin/main` with this body as
the event: exit 0, the same LEVEL AXIS line.
`check-adr-0087-registration --base origin/main`: exit 0, one
declared-breaking changeset
`[BREAKING+bang+clause-②-narrowing+metadata-protocol-narrowed]`,
`not-required (no-migration-prescription)`. `check-empty-changeset
--base origin/main`: exit 0.
- Round 1, `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` at `7ca63171`: 88 derived commands, each run
as printed with its exit code recorded beside it. First pass: 87 exit 0
and `check-plugin-teardown-shape.mjs --self-test` exit 3 (PREREQUISITE
NOT MET: its pinned fixture commit `621a4876` was outside the shallow
clone); after `git fetch origin
621a487…:refs/issue-30471/teardown-fixture` it exits 0 (48 cases).
- `dispatch-gates --ran`: "88 derived, 88 run, 0 NOT-MEASURED, 0 UNRUN",
exit 0.
- `node scripts/check-changeset-no-major.mjs --base origin/main --event
EVENT` with this body as the `pull_request` payload: exit 0, "LEVEL
AXIS: this PR declares clause-② `yes (narrowing)` and metadata-protocol
`narrowed`, and no package … is graded `patch`".
- `node scripts/check-adr-0087-registration.mjs --base origin/main`:
exit 0, one declared-breaking changeset, `not-required
(no-migration-prescription)`.
- `node scripts/check-empty-changeset.mjs --base origin/main`: exit 0.
- CI at `7ca63171` before this body: every check-run concluded, all
`success` or `skipped` except `Check Changeset`, `failure` on the
previous body's `Metadata-protocol: none` against the `minor` changeset
(the reading this body replaces).
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01L6bhgTqqeHSwZYJmApbDCa)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01L6bhgTqqeHSwZYJmApbDCa
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 2046d21 commit 482e089
14 files changed
Lines changed: 579 additions & 93 deletions
File tree
- .changeset
- content/docs/automation
- packages/lint/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
217 | 217 | | |
218 | 218 | | |
219 | 219 | | |
220 | | - | |
| 220 | + | |
221 | 221 | | |
222 | 222 | | |
223 | 223 | | |
| |||
309 | 309 | | |
310 | 310 | | |
311 | 311 | | |
312 | | - | |
| 312 | + | |
313 | 313 | | |
314 | 314 | | |
315 | 315 | | |
| |||
0 commit comments