Repository navigation
fix(lint)!: the remaining field-name indexes judge an object as served, so a read or write of a field an objectExtensions entry adds is not refused (#30471) - #30533
Conversation
Claude-Session: https://claude.ai/code/session_01WGufzft7TrxiryiTZm4o9p Co-authored-by: Claude <[email protected]>
…angeset Claude-Session: https://claude.ai/code/session_01WGufzft7TrxiryiTZm4o9p Co-authored-by: Claude <[email protected]>
📓 Docs Drift Check13 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 4 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin a5f63984f410b3fbd3e639fc21e0d172504ab1a7 && git checkout a5f63984f410b3fbd3e639fc21e0d172504ab1a7
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 55d9dd448f00d1498ba3ae830368306aa0dd8d8a 80c7ae3a1ff6a02f99bc8faa16749b731857424e && git checkout -B drift-repro 55d9dd448f00d1498ba3ae830368306aa0dd8d8a && git merge --no-ff 80c7ae3a1ff6a02f99bc8faa16749b731857424e
node scripts/docs-audit/affected-docs.mjs --json 55d9dd448f00d1498ba3ae830368306aa0dd8d8a |
|
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K Contract-review-claim: session_01WGufzfgvGexKn9vLHsy89K · head 482ad5c · 2026-11-05T14:27Z Full review of the first head at |
|
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgmentThe shape is right and matches the two prior rounds: one fold, every field-name reader resolving through it, the type questions on Implemented-by: VERDICT: PASS |
…inition; explain texts count extension fields as declared Claude-Session: https://claude.ai/code/session_01WGufzft7TrxiryiTZm4o9p Co-authored-by: Claude <[email protected]>
…#30471 changeset Claude-Session: https://claude.ai/code/session_01WGufzft7TrxiryiTZm4o9p Co-authored-by: Claude <[email protected]>
…ep the declares-nothing skip validate-sortable-fields builds its existence index over objectsAsServed and its sortability projection over objectsWithoutTiedFields, so a page or list-view sort naming an extension-added field passes, and one naming an extension formula field is refused as unsortable rather than unknown. An object that declares no field map keeps its declared answer on both. The three readonly write rules share buildServedReadonlyIndex, which keeps an object that declares no field unjudged, extended or not, as the write rules leave it: before this, an extension adding a readonly field to such an object made the readonly rules refuse a write main left silent. Pins: two sortable rows in the existence table (both legs, byte-equal), and a table of every refusal this change adds (rule, severity, both legs, the declared-only control), with tie and declares-nothing legs. Claude-Session: https://claude.ai/code/session_01L6bhgTqqeHSwZYJmApbDCa Co-authored-by: Claude <[email protected]>
… finding that moves The changeset now carries Clause-② yes (narrowing) with the widening half, Metadata-protocol narrowed, a BREAKING banner and the ADR-0087 disposition, in the shape of the page-sort precedent; it lists every finding a name the fold adds now moves to, measured against main on both CLI legs. Two refusals of a stack main accepted: the hook ctx.api readonly write (warning to error) and the translation keys the fold drops (resolved to error). hook-bodies.mdx says the readonly rule's declarations include extension fields. Pin: the two translation refusals at error, on the replaced-entry shape (both legs) and on a top-level objectExtensions beside packages[]. Claude-Session: https://claude.ai/code/session_01L6bhgTqqeHSwZYJmApbDCa Co-authored-by: Claude <[email protected]>
|
domain:spec#3 · os-sales · session_01L6bhgTqqeHSwZYJmApbDCa Contract-review-claim: session_01L6bhgTqqeHSwZYJmApbDCa · head 7ca6317 · 2026-11-05T17:37Z Full review at Generated by Claude Code |
|
domain:spec#3 · os-sales · session_01L6bhgTqqeHSwZYJmApbDCa Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flagsDev report 6664779568,
④ Design judgmentJudged against the scenario of record — an app package's object extended by a module package's
Must-fix: none. Should-fix: (1) declaration — one clause in the changeset's "Now" list and the PR body's Gate verdicts on this head, read at 2026-11-05T17:51Z: 69 check-runs ( Implemented-by: VERDICT: PASS Generated by Claude Code |
…t skip, pin the re-declared row, state the fold's positional contract The changeset now names the two refusals the contract review found undeclared: an owner field an objectExtensions entry re-declares is judged on the extension's definition, and a translation subtree keyed to an object only a top-level-beside entry extends loses the rung-2b skip. The served fold's refusal table in object-graph.test.ts pins the re-declared shape (a list-view sort of a formula re-declaration, a flow update of a readonly one) on both CLI legs with the declared-only control. objectsAsServed states the one-to-one, in-order contract indexObjectFields joins on; the react page rule and the write index say why handing pre-served objects bypasses the declares-nothing guard and moves no verdict. Docblocks only, no behaviour change. Claude-Session: https://claude.ai/code/session_01L6bhgTqqeHSwZYJmApbDCa Co-authored-by: Claude <[email protected]>
|
domain:spec#3 · os-sales · session_01L6bhgTqqeHSwZYJmApbDCa Contract review — deltaServed-tier: The merge
Should-fix 1 — answered
Should-fix 2 — answered
Should-fix 3 — answered, with a correction to my recordThe react rule imports Changeset semanticsUnchanged where it matters: Verdict and findingsMust-fix: none. Gate verdicts on this head, read at 2026-11-05T19:11Z: 69 check-runs ( mcp_calls: 0 — every GitHub read went by REST ( Implemented-by: VERDICT: PASS Generated by Claude Code |
|
domain:spec#3 · os-sales · session_01L6bhgTqqeHSwZYJmApbDCa 维护者速读
席位意见:契约复审两轮都 PASS。第一轮(6665186290, 席位核对 — ACCEPT · domain:spec#3 · head
|
domain:spec#3 · os-support-ai · session_01WGufzft7TrxiryiTZm4o9p
Requested by Yibo Wang · project thread
Fixes #30471
Clause-②: yes (narrowing) —
os validate/os build/os lintrefuse three shapes they accepted before: a hook body's literalctx.api.object(NAME).update(…)/.updateById(…)/.insert(…)writing a field anobjectExtensionsentry adds and declaresreadonly: truemoves from ahook-body-write-unknown-fieldwarning to ahook-api-update-readonly-fielderror; 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-levelobjectExtensionsbesidepackages[]adds, is nowtranslation-target-unknownaterror, where it resolved, and with that top-level-beside shape no longer read, a translation subtreeobjects.Xkeyed 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 istranslation-target-unknownaterrortoo; and a field anobjectExtensionsentry 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'smergeObjectDefinitions({ ...base.fields, ...extension.fields }), so an owneramount: numberre-declared asformula,readonly: trueortextnow gets that definition'ssort-field-unsortable, readonly-write or expression type-mismatch verdict, each aterror(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 anobjectExtensionsentry 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 forhook-api-update-readonly-field). Measured at7ca63171:git grepof the moved ids (the three unknown-field write ids, the five readonly ids,sort-field-unknown,sort-field-unsortable,page-field-unknown) overskills/andcontent/docs/(releases and references excluded) hits 11 lines in 3 files;content/docs/automation/hooks.mdx:43-47andcontent/docs/deployment/validating-metadata.mdx:177state 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
维护者速读
objectExtensions加的字段。扩展字段不再被报成未知字段,它的定义(只读、能否排序、类型)按声明字段一样判断。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 overobjectsAsServed(stack); the field-type and nullable indexes overobjectsWithoutTiedFields. The walk stays on the declaredobjects, 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.tsindexObjectFields(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, sojudgeableFieldsOfstill 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, asvalidatePageFieldBindingsdoes since lint: twelve field-path rules and two private readers judge object fields without the objectExtensions fold, so a view, report, dataset or page naming an extension-added field is refused #30383.validate-translation-references.ts: its privateobjectExtensionsByTargetis deleted; the universe reads the shared fold, which now carries each entry'svalidations(object-graph.ts,VisibleObjectExtension.validations).buildServedReadonlyIndex(validate-readonly-flow-writes.ts):buildReadonlyIndexoverobjectsWithoutTiedFields(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 writesmainleft silent (measured atcc67f35a: flow and hook aterror, the threereadonlyWhenids atwarning).validate-sortable-fields.ts(open to this card since fix(lint)!: refuse a stored page sort naming an unknown field, on every page sort slot (#30093) #30270 landed atc045c341):indexServedSortFieldsbuilds the existence index (indexObjectSearchTargets' shape) over the served objects, a tied name kept, and the sortability projection (indexObjectSortability) overobjectsWithoutTiedFields; an object that declares no field map keeps its declared answer on both (skip 2).validate-searchable-fields.tsis not touched; the unprovisioned-anchor index is object-level and stays on the declared objects.rule-explanations.tsandcontent/docs/automation/hook-bodies.mdx: "declared" includes the fields anobjectExtensionsentry of the same artifact adds (DECLARED_FIELD_DOOR,page-field-unknown,translation-target-unknown;hook-bodies.mdx:220,:312).[#30471]describe inobject-graph.test.ts(the fold's harness), 69 cases; see the table and Verification.objectsAsServedstates the one-to-one, in-order contractindexObjectFieldsjoins on (objects[i]is the served form ofrecordsOf(stack.objects)[i], early return included);validate-react-page-props.tsat 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 onmaintoo)..changeset/30471-field-name-indexes-extension-fields.md:@objectstack/lintminor, BREAKING banner, ADR-0087 dispositionnot-required (no-migration-prescription).Per site
One artifact: an app package declares
xs_deal(name,amount) andxs_account; a module package extendsxs_deal.validate-expressions.tsbuildFieldIndex(validations, formulas, flow and hook conditions, sharing-rule conditions)recordagainst the served rownowherebyte-for-byte; per-package leg samevalidate-expressions.tsfield-type and nullable indexesvalidate-expressions.tsread-attachment index, hop walkindexObjectFields(hook-body-writes) forflow-node-write-unknown-field,action-body-write-unknown-field,hook-body-write-unknown-fieldnowherebyte-for-byte, per-package leg; an object declaring no field stays unjudgedvalidate-react-page-props.tscolumns(page-field-bindings'indexObjectFields)validate-react-page-props.tssearchableFields(indexObjectSearchTargets)searchable-field-unknownon an extension fieldvalidate-page-field-bindings.tsindexObjectFields:640-641since #30383, react props:1144); #30270 added no declared-only readervalidate-translation-references.tsprivate foldvalidations)packages[]alone when presentfields.region,_validations.region_setresolve; refused without; byte-for-byte; per-package leg; the two refusals the fold adds pinned aterrorbuildReadonlyIndex)validate-sortable-fields.tsexistence (indexObjectSearchTargets)page-field-unknownerroron both CLI legsvalidate-sortable-fields.tssortability (indexObjectSortability)400 INVALID_SORTsort-field-unsortable, both legs, tie, declares-nothing)Every finding that moves, against
mainMeasured with
runAuthoringRules('validate', …)over the built dist oforigin/mainatc23eafa8and of this head, on the union artifact and the per-package leg (identical on both); the field is one anobjectExtensionsentry adds to an object that declares fields of its own. The last four rows were measured in round 2 over the built dist oforigin/mainat5b748dfeand of this head; the no-extension control is identical on both trees, and the re-declared shapes not listed (aformulaortextre-declaration written, areadonlyone sorted or read, an action'sctx.apiwrite) move nothing.mainctx.apiupdate / updateById / insert,readonly: truehook-body-write-unknown-fieldwarninghook-api-update-readonly-fielderrorobjectExtensionsbesidepackages[]translation-target-unknownerrorupdate_record/create_record,readonly: trueflow-node-write-unknown-fielderrorflow-update-readonly-fielderrorformulapage-field-unknown/sort-field-unknownerrorsort-field-unsortableerrorupdate_record,readonlyWhenflow-node-write-unknown-fielderrorflow-update-readonly-when-fieldwarningctx.apiupdate,readonlyWhenhook-body-write-unknown-fieldwarninghook-api-update-readonly-when-fieldwarningctx.apiupdate,readonlyWhenaction-body-write-unknown-fieldwarningaction-api-update-readonly-when-fieldwarningctx.inputstamp, an actionctx.apiwrite of areadonly: truefield, acreate_recordof areadonlyWhenfield, a reactcolumnsprop, a plain sort)amount: number), sorted on a page (interfaceConfig.sort) or list view, re-declaredformulasort-field-unsortableerrorreadonly: true, written by a flowupdate_recordor a hookctx.apiupdateflow-update-readonly-field/hook-api-update-readonly-fielderrortext, multiplied in a validation rule (record.amount * 2 > 10)expression-invaliderror (type mismatchstring * int)objects.Xkeyed to an object the stack does not define, extended only by a top-levelobjectExtensionsbesidepackages[]translation-target-unknownerrorWhy
The runtime serves each object with every body's
objectExtensionsfolded 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
validationsand 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 aterror(declared above and pinned): a key naming a field or rule only a replaced earlier entry of the same package adds, and a top-levelobjectExtensionsbesidepackages[](a shape no CLI leg produces; with nopackages[]the top level is read).fieldMap.size === 0guard as keeping this; on the served index it no longer fired, sobuildServedReadonlyIndexrestores it.defineStack'scollectRelatedListNameErrorsstays additive. Aligning it with the registry's replacement measured as a further narrowing (two shapes accepted today would be refusedSTACK_CROSS_REFERENCE_INVALID), outside this card's declaration; agreed with the seat.Acceptance notes
recordsOf(stack.objects):validate-form-layout.ts(map-shapedfieldsonly),validate-notify-email-field-recipients.tsandvalidate-public-form-file-fields.ts(field types),validate-flow-template-paths.ts(field types),validate-page-visualization-bindings.ts,validate-searchable-fields.tsindexObjectSearchTargets(also behind a react page'ssearchableFields, pinned as still refused), andlint-autonumber-formats.ts:56-63(fieldMetafrom the object's own fields, so an owner's autonumber format naming a field a sibling adds reads as unresolvable).main, not changed here: an object that declares no field, once anobjectExtensionsentry adds to it, getslist-view-field-unknownaterroron a list-view column the extension does not add (the lint: twelve field-path rules and two private readers judge object fields without the objectExtensions fold, so a view, report, dataset or page naming an extension-added field is refused #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.main.fix(lint)!:; the seat added the!to the PR title with this body (precedent fix(lint)!: refuse a stored page sort naming an unknown field, on every page sort slot (#30093) #30270).validateSortableFieldsalso runs atPUT /api/v1/meta/page/NAME; this PR measured the CLI legs only.Verification
Round 2, at head
80c7ae3a(this branch withorigin/mainat5b748dfemerged infed33b86, clean; the merge bringspackages/lintchanges, so the dogfood battery was run again), afterturbo run build --filter='!@objectstack/docs' --concurrency=2underos-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 typecheckunderos-verify-lock: Test Files 195 passed (195), Tests 8172 passed (8172);check:test-typecheck: OK; VERDICT command-exit 0. The[#30471]describe: 69 passed.pnpm --filter @objectstack/dogfood testunder the lock: VERDICT command-exit 0; Test Files 420 passed, 3 skipped (423); Tests 3496 passed, 36 skipped (3532).npx eslintover the 12 changed.tsfiles: exit 0.Round 1, at head
7ca63171(origin/mainatc23eafa8merged incc67f35a), 119/119 build tasks:pnpm --filter @objectstack/lint test && pnpm --filter @objectstack/lint typecheckunderos-verify-lock: Test Files 195 passed (195), Tests 8158 passed (8158);check:test-typecheck: OK; VERDICT command-exit 0. The[#30471]describe: 67 passed.@objectstack/lintwhose 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-protocol317 passed, 4 skipped / 22442 passed, 26 skipped;@objectstack/mcp56 / 678;@objectstack/platform-objects112 / 1528;@objectstack/plugin-approvals111 / 1469;@objectstack/cloud-connection70 / 877;@objectstack/service-mailbox10 / 152;@objectstack/example-showcase46 / 544;@objectstack/objectql552 / 11005;@objectstack/runtime543 / 8655 passed, 19 skipped.(narrowing)arm owes it),pnpm --filter @objectstack/dogfood testunder 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 10000msinpackage-first-authoring.dogfood.test.ts(and its three later suites refused with thealready has a live bootStack kernelcascade a timed-out hook leaves) and insys-file-metadata-write-refusal.dogfood.test.ts;Test timed out in 5000msinactivity-withheld-update.dogfood.test.ts(1) andanalytics-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-configover the 12 changed.tsfiles: exit 0.Before/after against main (the table above)
runAuthoringRules('validate', …)from each tree's builtdist(origin/mainatc23eafa8in a second worktree, this head) on the same stacks: an app body declaringxs_dealandxs_account, an object declaring no field (xs_empty), and a module body extending both with areadonly: truetext field, areadonlyWhennumber field, a plain text field and aformulafield; each write surface (flowupdate_record/create_record, actionctx.apiupdate / insert, hookctx.apiupdate / insert, hookctx.input), each sort surface (interfaceConfig.sort, an elementdataSource.sort, adefineViewlist), the expression passes (validation, formula, sharing rule, hook condition) with a text read, text arithmetic, an unguarded number compare and a date compare, a reactcolumnsprop 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.cc67f35a), the readonly rules refused writes toxs_emptythatmainleft silent:flow-update-readonly-fieldandhook-api-update-readonly-fieldaterror, the threereadonlyWhenids atwarning, on both legs. At this head noxs_emptycase differs frommain.origin/mainat5b748dfeand this head, re-declares the owner'samount: numberin the module body asformula, asreadonly: trueand astext, against a page and a list-view sort, a flowupdate_record, a hook and an actionctx.apiupdate and a validation rulerecord.amount * 2 > 10, plus a translation subtree keyed to an undefined object extended by a top-level entry besidepackages[]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.Ablations (each through scripts/ablation-replace.mjs: anchor hit once, the describe run, restored, blob equal to HEAD, git diff HEAD empty)
The lint tests import
src(relative imports, no alias todist), so no build sits between the mutation and the run.indexServedSortFields: existence overstackinstead 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).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).objectExtensionsByTargetreading the top level besidepackages[]→ 1 failed (the new translation pin); keeping every entry of a package → 2 failed (the replaced-entry pin and the new one).objectsAsServedno 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, blob6d6f1b21equal to HEAD.Gates
80c7ae3a:dispatch-gates --commandsderives the same 88 commands, each run as printed with its exit code recorded. First pass: 87 exit 0 (the teardown fixture fetched again first) andpnpm check:type-check-debtexit 3 (PREREQUISITE NOT MET: its closure build timed out queueing behind a sibling's locked build); re-run underos-verify-lockit 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/mainwith 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.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackat7ca63171: 88 derived commands, each run as printed with its exit code recorded beside it. First pass: 87 exit 0 andcheck-plugin-teardown-shape.mjs --self-testexit 3 (PREREQUISITE NOT MET: its pinned fixture commit621a4876was outside the shallow clone); aftergit fetch origin 621a4876…:refs/issue-30471/teardown-fixtureit 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 EVENTwith this body as thepull_requestpayload: exit 0, "LEVEL AXIS: this PR declares clause-②yes (narrowing)and metadata-protocolnarrowed, and no package … is gradedpatch".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.7ca63171before this body: every check-run concluded, allsuccessorskippedexceptCheck Changeset,failureon the previous body'sMetadata-protocol: noneagainst theminorchangeset (the reading this body replaces).Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01L6bhgTqqeHSwZYJmApbDCa