fix(spec,objectql): declare the inert-JSON artifact and registry-record package body stages, and stop the record under-reporting functions - #19373
Conversation
…rd package body stages Four stages, four declarations: authoring, in-memory assembled, on-disk artifact, registry record. The last two had no declaration until now. - packages/spec/src/automation/flow-function.zod.ts exports FlowFunctionLoweredDeclarationSchema, the serialisable half of the pair. - packages/spec/src/stack.zod.ts declares ArtifactStagePackageBodySchema and RecordStagePackageBodySchema BESIDE AssembledPackageBodySchema, which is untouched (composeStacks keeps building bodies that hold live callables). - packages/spec/src/api/package-api.zod.ts rebinds the installed-package row's manifest to the record stage; the z.unknown() override and the docblock defending it are gone. - packages/objectql/src/registry.ts normalises a bare callable functions entry to the declared form at the assembly boundary, so the structural projection reports every declared function instead of dropping the bare ones. Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
…s, and fix the record-copy cast - json-schema.manifest / authorable-surface / authorable-defaults gain automation/FlowFunctionLoweredDeclaration: the lowered record now publishes, which is what unemitted-schemas.baseline.json already claimed about it. - dropped-refinements.baseline.json gains one site per installed-package response: the record stage DECLARES hooks where z.unknown() declared nothing, so HookSchema's `object` refinement now reaches the runtime and not the file. Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
…n reporting Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
…declaration's type aliases Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
…SON stage work Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
…sembled-package-body-inert-json
…sembled-package-body-inert-json # Conflicts: # packages/spec/api-surface-declarations/api.txt # packages/spec/api-surface-declarations/automation.txt # packages/spec/api-surface-declarations/root.txt # packages/spec/api-surface-declarations/ui.txt # packages/spec/dropped-refinements.baseline.json
…dropped-refinements ledger Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
…sembled-package-body-inert-json
📓 Docs Drift CheckThis PR changes 2 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 137 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 0de10314fd550c25cf7c43994f2c5d5694433ca2 && git checkout 0de10314fd550c25cf7c43994f2c5d5694433ca2
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 61dd96f227e05a8d1d0c93a4f6a13cb7eaa6cdf4 aac764cc36113b4e52820c1695715f000ccbe1b4 && git checkout -B drift-repro 61dd96f227e05a8d1d0c93a4f6a13cb7eaa6cdf4 && git merge --no-ff aac764cc36113b4e52820c1695715f000ccbe1b4
node scripts/docs-audit/affected-docs.mjs --json 61dd96f227e05a8d1d0c93a4f6a13cb7eaa6cdf4
|
…sembled-package-body-inert-json One conflicted path, packages/spec/src/api/package-api.zod.ts, and the overlap is the import block alone: main's side adds `retiredKey`, this branch rebinds the row manifest's import from `AssembledPackageBodySchema` to `RecordStagePackageBodySchema`. Both imports kept; the assembled-body import is dropped because nothing in the merged file references that symbol in code any more (three prose mentions in the docblock only). Measured: the merged file equals main's side plus this branch's exact delta, and equals this branch's side plus main's exact delta -- added/removed line multisets identical in both directions. Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
Discharges the os-regen deferral taken on the merge commit. Step 2 restored main's side of content/docs/references/api/package-api.mdx (both sides moved it, so the driver had silently kept one); this regeneration re-derives this branch's generated content on top of it, which is why the `functions` and `hooks` rows read as the record-stage declaration again instead of `any`, while main's retired `limit`/`cursor` query-parameter rows stay. `pnpm --filter @objectstack/spec check:generated`: all 15 generated artifacts up to date. Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
… side of `scripts/pm/os-regen-merge.sh` was rerun once more after the regeneration commit had landed. Its step 2 is not idempotent across that boundary: with the branch's regenerated bytes in HEAD the "both sides changed it" test now fires for content/docs/references/api/package-api.mdx and content/docs/references/index.mdx, so it restored main's side of both and committed it -- erasing `FlowFunctionLoweredDeclaration` from the automation listing (schema count 1533 -> 1532) and rolling the package-api `functions`/`hooks` rows back to `any`. This commit re-runs `gen:schema && gen:docs` on the merged tree. The result is byte-identical to the regeneration commit: `git diff 3a7ab9c -- content/` is empty. Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
…bled-package-body-inert-json
Discharges the os-regen deferral the merge commit recorded. `origin/main` and this branch both moved content/docs/references/index.mdx, so the driver resolved it with exit 0 and silently kept one side -- measured: the merged blob was byte-identical to this branch's, and main's side (the UI section's `DashboardWidgetChartConfig` row and its counts) was gone. Step 2 restored main's side into the working tree only, and this commit is `gen:schema && gen:docs` re-derived on top of it. The result is the union of both intents: the UI module reads 16 pages / 158 schemas with `DashboardWidgetChartConfig` listed, the automation module reads 14 pages / 75 schemas with `FlowFunctionLoweredDeclaration` listed, and the total moves 1533 -> 1534. Against each side separately the regenerated file differs by exactly the other side's delta and by nothing else. No hand edit: the file's own header routes it to `pnpm --filter @objectstack/spec gen:schema && gen:docs`. Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: ① Derived judgmentsGoverning ruling, established from the thread rather than the PR body. The card carries four ruling records. 5651572469 (batch #127 item 4, 2026-09-13, maintainer 「其他同意」) prescribed an artifact-stage body BESIDE the assembled one and the read-row rebind. 5716259259 (batch #149 item 1 letter B, 09-17) was presented after a What What changes. Is Do the two stage schemas admit what OPEN QUESTION 1 — admitting both lowered spellings. Faithful, and I would have failed the other reading. The ruling's phrase 「 Does the The two ruling items read rather than transcribed, my verdict on each. (a) One class of row moves from tolerated to refused-by-declaration: a map entry whose ② Semver levelThe written rule (
③ Boundary flags
Measurement notes. The two new schemas carry the structural annotation Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #17518
Clause-②: yes
Executes ruling A′ — decision batch #192 item 3, comment 5748934194, maintainer 「192 同意」. Its two steps, its refusals (A and B) and its fences are followed as written; every place where the tree made me read the ruling rather than transcribe it is called out below.
Base of every reading in this body: merge commit
aac764cc361.The confidence gap the ruling asked me to close first
「whether
effectis required or defaulted on the declaration schema — read it, ⛔ do not mint a value」Defaulted.
FlowFunctionDeclarationSchema.effectisFlowFunctionEffectSchema.default(DEFAULT_FLOW_FUNCTION_EFFECT)where that constant is'pure'(automation/flow-function.zod.ts). Measured, not read off the source alone:FlowFunctionLoweredDeclarationSchema.safeParse({ handler: 'x' })succeeds and yields{ handler: 'x', effect: 'pure' }. The array member offunctionsstatesFlowFunctionEffectSchema.optional()with no default, so the two forms differ and neither is restated anywhere in this diff — each JSON stage inherits its form's own optionality by deriving from it.That reading is what the producer writes: the bare-callable normalisation uses
DEFAULT_FLOW_FUNCTION_EFFECTand the array form gets nothing.What landed
packages/spec/src/automation/flow-function.zod.ts—FlowFunctionLoweredDeclarationSchemais exported (step 1), with itsFlowFunctionLoweredDeclaration/…Parsedaliases. It was a module-localconst, andautomation/index.ts'sexport *only re-exports what is already exported.packages/spec/src/stack.zod.ts— two new bodies besideAssembledPackageBodySchema:ArtifactStagePackageBodySchema— the on-disk artifact stage.functionsentries are the lowered spellings,hooks[].handleris a string.RecordStagePackageBodySchema— the registry record stage: literallyArtifactStagePackageBodySchema.extend({ functions: … })withfunctions[].handleroptional in both the map-record form and the array form, and nothing else.AssembledPackageBodySchema,composeStacksand thecannot driftinvariant are ⛔ untouched: those callables are live on the stage the assembled body declares itself for, and narrowing it would refuse a published composition function's own output. Both new schemas carry the same structuralz.ZodTypeannotation as the assembled body, for the two reasons recorded there (TS7056; a named alias turningstack.zodinto a shared chunk).packages/spec/src/api/package-api.zod.ts— the installed-package row'smanifestis rebound to the record stage (step 1). Thez.unknown()override and the docblock defending it are gone, and the sentence that ruling A step 5 assigns to this edit is corrected in place: those two members are not whyArtifactPackageSchemaandObjectStackDefinitionSchemapublish no JSON Schema —src/stack.zod.tsis not one of the subpath namespacesbuild-schemas.tswalks, so neither is ever reached by the emit loop.packages/objectql/src/registry.ts— step 2.withDeclaredFunctionEntriesrewrites a bare callablefunctionsmap entry to{ handler, effect: DEFAULT_FLOW_FUNCTION_EFFECT }at the assembly boundary, beforetoRecordManifestruns.toRecordManifest's structural rule is ⛔ untouched and no key is special-cased inside the projection; the two spellings are simply made structurally equal ahead of it. ⛔ No ref is minted, ⛔ no entry is dropped. The caller's manifest is never mutated and a copy is made only when an entry really needed rewriting.Two places where I read the ruling rather than transcribed it — both stated so they can be overruled
functionsentries the lowered declaration」 is implemented as BOTH lowered members ofFlowFunctionEntrySchema, not only the record one.objectstack buildemits{ myFn: 'myFn' }for a bare entry and{ myFn: { handler: 'myFn', effect } }for a declared one, so a stage admitting only the record form would refuse artifacts this repo really writes — the failure mode that withdrew letter B, one key across. Ruling A′'s own step-4 control names both shapes (「a string and a lowered record」). Measured: the artifact stage accepts a body carrying one of each.functions' array branch is declared inline inside the assembled body's own shape, and narrowing it in place is the one thing this pair may not do. The transcription's drift is guarded instead:stack-json-stage-package-body.test.tspins the authoring array entry's key set equal to both JSON stages', so a key added there and not here reddens by name.Acceptance, as ruling A′ lists it
z.toJSONSchema(self-test over the whole body)Function types cannot be represented in JSON Schema); probe controls litz.string()YES, darkz.object({a: z.function()})NOconfig.ts:244-249) reports 2 functions on theGET /packagesrow, the bare one as a handler-less declaration{"summarizeCompletedTask":{"effect":"pure"},"sweepProjectHealth":{"effect":"writes"}}, driven through the realSchemaRegistry.installPackagehooksunchangedfunctionsform also keeps its entry:[{"name":"syncBilling","effect":"writes"}]AssembledPackageBodySchema/composeStacks/ the invariant untouchedassembled-package-body.test.tsandcompose-stacks-manifest-preserve.test.tsstay greennoted, not filedcorrections in the same editautomation/FlowFunctionLoweredDeclarationis now injson-schema.manifest/automation.json, so 「the lowered record … publishes normally」 is now a fact.package-api.zod.tsdocblock last sentence: corrected in place, see aboveStage separation, measured rather than asserted: the record stage accepts the handler-less declaration and the artifact stage refuses it; the assembled body accepts a live callable and both JSON stages refuse it; both JSON stages still refuse an authoring glob and an unknown key (
namesapce). So the two keys moved fromunknownto a declaration, and nothing else moved.Reverse verification — two ablations, each restored with proof
Both ran against committed code, each with a
traprestore, an on-disk landing proof (anchorgrep -cbefore/after plus a blob-hash change) and a restore proof (git hash-objectback to the HEAD blob,git diff HEADempty).toRecordManifest(withDeclaredFunctionEntries(manifest))→toRecordManifest(manifest); anchor 1→0, injected 1, blobb0af60d7…→17b7c93c…):registry-package-manifest-serializable.test.tsgoes 1 failed / 15 passed, naming the exact defect —expected [ 'sweepProjectHealth' ] to deeply equal [ 'summarizeCompletedTask', …(1) ]. Restored blobb0af60d7…, diff empty.jsonStageFunctionsKey(true)→(false); anchor 1→0, injected 2, blob60c13b43…→822bed8e…): 2 failed / 79 passed across two files —record accepts the handler-less declaration; ⛔ the ARTIFACT stage refuses itandparses a row carrying the residual the projection really produces. So the one-key difference that IS the fourth stage is load-bearing in both packages' pins. Restored blob60c13b43…, diff empty.No ablation is offered for 「both bodies convert」: that claim already carries its discriminating control inside the same test file (the assembled body must NOT convert), which is a lit/dark pair rather than an assertion about itself.
Tests and gates
All through
scripts/pm/os-verify-lock.shwithOS_VERIFY_LOCK_SLOT=issue-17518, verdicts read from the wrapper's ownVERDICT command-exitline and never a bare$?; every exit code captured before any pipe. Wall-clock figures in the logs are SHARED-BOX seconds.pnpm --filter @objectstack/spec test— 505 files / 14749 tests passed (up by two files and 38 tests from the first draft reading — the population fix(spec)!: a dataset-bound dashboard widget owns appearance, the dataset owns structure #19363 and fix(spec): answer an axis-silent key with both ends of the range, not the cheaper-spelled one #19318 brought in on the fifth merge)pnpm --filter @objectstack/objectql test— 303 files / 5044 tests passedpnpm --filter @objectstack/runtime exec vitest run --maxWorkers=2over the package-door / artifact-collection population, re-run on the final head in two batches with every path named so the population is reproducible — batch A 8 files / 84 tests passed (src/artifact-collections,src/artifact-function-declarations,src/artifact-reference,src/bundle-function-entries,src/package-door-error-parity,src/package-door-namespace-conflict-code,src/domains/package-door-producer-key-carry,src/load-artifact-bundle.granted-permissions) and batch B 6 files / 60 tests passed (src/security/artifact-granted-permissions,src/domains/packages-serializable-response,src/domains/packages-single-door,src/domains/packages-protocol-handle-typing,src/domains/packages-read-delete-response-conformance,src/domains/packages-publish-drafts-response-conformance).pnpm --filter @objectstack/spec --filter @objectstack/objectql typecheck— exit 0; both test layers compile (spec 53 files / 257 errors / 142 pins; objectql 40 / 234 / 65, unchanged).aac764cc361: the five spec stage/compose files — 5 files / 130 tests passed.Gates.
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived from this tree, every command run with its exit code written to a file, reconciled with--ran: 116 derived, 114 run, 2 NOT-MEASURED, 0 UNRUN, and the tool's own verdict line says so. 113 exit 0. The two NOT-MEASURED are the tool's DERIVED classification of an exit 3; a third measured nothing too, and the tool cannot see it because its refusal code is 2. ⛔ None of the three is a finding:check:dual-build-cjs-loads— exit 3, its ownPREREQUISITE NOT MET … ⛔ This is NOT a pass: nothing was measured(66 packages have nodist; it wants a whole-repo build).check:type-check-debt— exit 3, same shape, same wording, wants the full package closure built.check-engine-split-ratio --days 90— exit 2, refuses on a shallow clone whose oldest visible commit sits inside the 90-day window. It says a ratio derived there would be 「real, plausible and WRONG」.A fourth,
check:skill-examples, first exited 1 on an unbuiltpackages/client-react; after building that package it re-runs green — 258 prose examples type-check across 3 surfaces. Both readings are stated here, and the reconciliation record carries ONE of them — the green re-run — because the tool flags a doubly-recorded family and says to make the record state one thing. The re-derivation on the final head yields 116 families:check:api-surface-declarationsis gone (retired upstream by #19024 mid-round) andcheck:gitlink-declaredis new, run green. No family is left unrun.Ratchet families re-run after the last merge, on
aac764cc361:check:generated(all 15 artifacts up to date),check:api-surface,check:authorable-surface,check:export-origins,check:declaration-map,check:docs,check:skill-refs,check:entry-nameability,check:dual-source-exports,check:spec-changes,check:spec-parsed-alias,check:published-files,check:nul-bytes,check:cross-package-test-inputs,check:test-source-alias,check:type-check-coverage— all exit 0. Control characters:grep -naPover every file I hand-edited returns nothing (exit 1).Generated artefacts in this diff, and why each moved
json-schema.manifest/automation.json,authorable-surface/automation.json,authorable-defaults/automation.json,api-surface/*,export-origins/*,declaration-map/automation.json,content/docs/references/**— the new exports, regenerated by the package's owngen:scripts.authorable-defaultsrecordsautomation/FlowFunctionLoweredDeclaration:effect = "pure", which is the confidence-gap reading in ledger form.packages/spec/dropped-refinements.baseline.json— fourapi/*entries each gain one site (…manifest.hooks.element.object), counts 569 → 573. Cause: the record stage declareshookswherez.unknown()declared nothing, soHookSchema'sobjectrefinement now reaches the runtime and not the published file. The ledger is hand-edited by design and the build printed the exact delta.skills/objectstack-platform/references/_index.md— one generated line listingstack.zod.ts's exports.skills/**readings, and the landing tierThis diff touches
skills/objectstack-platform/references/_index.md, so the PR is governed, Tier H on its file list. ⛔ It stays a draft and no AI seat merges, queues or arms auto-merge on it.Both readings the skills rule requires, at merge base
c334ba0f3a6:SKILL.md): 6145 before, 6145 after — net 0.node scripts/check-skills-token-ratchet.mjsexits 0 and classifies this file as generator-owned (measured, not ratcheted), so no authored ceiling is charged.Clause ②, and the changeset is not one package's
Clause-②: yes, and two changesets because two published packages move:@objectstack/spec— minor. New exports, and the two installed-package responses move fromz.unknown()onfunctions/hooksto declared JSON shapes. That is a narrowing on a published declaration; what it does NOT withdraw is measured, on real producers: the showcase shape, the array form and the already-lowered body an artifact boot installs all parse.@objectstack/objectql— patch.GET /packagesreports functions it previously dropped. No API is added or removed; a read door stops under-reporting. Grade it up if a payload gaining entries reads as minor to the reviewer.Serial and merge state, re-taken by this seat
Changed-file map re-taken first-hand over all 33 open PRs (271 file rows) rather than inherited. LIT control
packages/spec/src/ui/action-params.zod.tsresolves to #19315; DARK controlpackages/spec/src/zzz-no-such.zod.tsresolves to nothing.packages/spec/src/automation/flow-function.zod.ts,packages/spec/src/api/package-api.zod.ts,packages/objectql/src/registry.ts— free.packages/spec/src/stack.zod.ts— held by docs(spec): scope the email-template locale-floor claims to a call that names a locale #18482, spec: pre-parse __proto__ guard on ObjectSchema.fields and AssignmentConfigSchema.assignments (#17852, #18847) #19147, spec: declaresCollection reads a pipe's authorable side, so a preprocess-wrapped collection key cannot silently leave the merge refusal set (#19150) #19314, all below A′'s region. spec: pre-parse __proto__ guard on ObjectSchema.fields and AssignmentConfigSchema.assignments (#17852, #18847) #19147 landed during this round and merged cleanly here (itsstack.zod.tshunk is a comment).packages/spec/dropped-refinements.baseline.json— also written by spec: pre-parse __proto__ guard on ObjectSchema.fields and AssignmentConfigSchema.assignments (#17852, #18847) #19147 (landed, resolved here) and by the still-open feat(spec)!: publish the $-prefix key ban the normalized filter enforces, and make the ratchet able to see it #19335, which rewrites the samemeasuredheader and adds entries. That is a line-level contention on a ledger whose correct value is recomputable: whoever lands second re-runspnpm --filter @objectstack/spec buildand re-applies the delta it prints. ⛔ Not a semantic collision.origin/mainhas been merged five times on this branch.#19024(which retiredapi-surface-declarations/) came in early, which is why noapi-surface-declarations/*.txtappears in this diff. The fifth merge brought #19363 — a BREAKING spec change, four retired-key migration entries and one ADR-0087 semantic conversion — plus #19318, #19369 and #19303, which is why the full spec suite was re-run rather than only the generated gates.That fifth merge conflicted on exactly one path,
content/docs/references/index.mdx, a routedmerge=os-regenartifact both sides move. It was resolved by the runbook's ORDER and ⛔ not by hand: merge base captured before the merge,git merge, the driver's silent drop MEASURED (the merged blob was byte-identical to this branch's side, so main's was gone), main's side restored into the WORKING TREE ONLY withgit restore --source, thengen:schema && gen:docson the committed merge. The regenerated file is the UNION, proven in both directions: against this branch's side it differs by exactly main's delta (UI 157 schemas becomes 158,DashboardWidgetChartConfiglisted) and against main's side by exactly this branch's (Automation 74 becomes 75,FlowFunctionLoweredDeclarationlisted); the total moves 1533 to 1534. ⛔scripts/pm/os-regen-merge.shwas NOT used — its step-2 non-idempotence across a regeneration commit is filed as #19392.Acceptance notes
packages/spec/api-surface-declarations/ui.txtproduced a 184-line change that is a pure permutation of its own content — the same union members in a different order,0 removed, 0 added, 35 reshaped. Verified as a precedented shape rather than a defect: commit24d622b94b8, a spec change touching zero files underpackages/spec/src/ui/, moved the same file by 5 lines whose sorted content is byte-identical. The whole artefact was retired upstream by revert(spec): take back the declaration-text snapshot, restore the 27 signature hashes #19024 mid-round, so nothing of it survives in this diff and the population is gone. Carrier: none — the file no longer exists.packages/objectql's tests resolve@objectstack/metadata-protocolfromdist, so after merging upstream The in-processinstallPackagedoor ignores theenableOnInstallits own request schema declares — honour it the way the HTTP door does (successor of #18605, ruling batch #153 item 5 letter 1) #19277 the seven assertions inprotocol-install-package-enable-on-install.test.tsfailed against a stale build of a package this PR never touches; building that one package turns all seven green. A local-environment reading, not a repo defect, andcheck:test-source-aliasalready owns the aliased/unaliased ledger this sits in. Carrier: the next seat that runs objectql's suite after a merge — it will see the same red and should build the dependency before reading it as a finding.维护者速读(草稿)
改了什么 —— 一个包的「包体」在平台里其实要经过四个阶段:作者写的、内存里装配好的、落盘成 artifact 的、注册表记录下来的。前两个早有声明,后两个从来没有。这次把后两个补上:
ArtifactStagePackageBodySchema(落盘 artifact)和RecordStagePackageBodySchema(注册表记录),放在既有的装配体旁边,装配体一个字不动。同时修好一个生产者缺陷:GET /packages以前会把「裸写的函数」整条漏报,现在两种写法都报。为什么改 —— 两件事各有代价。其一,装配体里有两个键(
functions、hooks)声明了「可以是一个活的函数」,而 JSON Schema 表达不了函数,于是任何嵌入它的接口都会整份丢掉自己的 JSON Schema;读 API 只能把这两个键写成「什么都收、不检查」。其二,我们自己发布的 showcase 声明了 2 个函数,而GET /packages只报 1 个——机器可读的读门把事实说少了。风险与代价(含回滚) —— 风险集中在一处:那两个键从「什么都收」变成「按声明收」,理论上可能拒掉今天能读的行。已实测三种真实生产者(showcase 的写法、数组写法、artifact 启动装回来的写法)全部照常通过,并且用两次消融证明了这些断言真的会红而不是摆设。⛔ 装配体与
composeStacks未动,所以os dev/os serve的行为不受影响——这正是上一版裁决 B 被撤回的原因,这次没有重蹈。回滚:两个 spec 改动与 objectql 改动互相独立,git revert任一半都不会让另一半变红;最小回滚是把package-api.zod.ts的那一行绑回装配体,新声明留着不用。席位意见 ——
你要做的 —— 这个 PR 的文件里有一份
skills/**的生成文件,按规则整单属于 Tier H,只有你(或你授权的批准)能让它落地;AI 席位不会合并、不会排队、不会解除 draft。请看两点:①@objectstack/objectql我打的是patch,理由是「读门修复、不增删 API」,若你认为「载荷多出条目」应算 minor,说一声即可改;②functions的声明式阶段我按「两种 lowered 写法都收」实现(理由写在上面第 1 条),如果裁决本意是只收记录式那一种,也请直接说,那会让objectstack build今天写出的一种 artifact 被拒。Generated by Claude Code
Generated by Claude Code