Repository navigation
console 的内嵌索引编辑器与 IndexSchema 漂移:where vs partial、枚举里的 brin —— 阻塞 #4001 对 IndexSchema 的收紧 #5247
Description
Activity
分诊结论:
pm:queue+repo:objectui已在
origin/main复核两侧代码,漂移仍然存在,与 issue 描述完全一致:- objectstack
packages/spec/src/data/object.zod.ts(第 309-341 行)IndexSchema声明的是partial(string,可选)与type: z.enum(['btree','hash','gin','gist','fulltext'])。 - objectui
packages/app-shell/src/views/metadata-admin/EmbeddedItemEditor.tsx的FALLBACK_SCHEMAS.index表单字段名是where(标题 "Partial-index predicate"),type枚举里是brin而非fulltest。 - 交叉核实了
packages/plugins/driver-sql/src/schema-drift.ts的DeclaredIndexInput(第 946-953 行):只有name/fields/unique/nullSafeColumns四个字段,partial和type确实没有任何 DDL 消费者,与 issue 引用的结论一致。
跨仓去重:搜索了 objectstack/objectui/cloud 三仓的 open issues 与 PRs(关键词
IndexSchema、FALLBACK_SCHEMAS、EmbeddedItemEditor),没有发现在途的竞争 PR 或未认领的重复 issue。PR #5250(已合并)是本 issue 的源头 PR(#4001 批 20),#5247/#5248/#5249 是它派生出的记录型 issue,不是重复。分类为
pm:queue:这是一个有明确文件位置和可复现的具体缺陷(字段名不一致 + 枚举值不一致),按 issue 自己的框定,生产者(objectui 的手抄 fallback schema)是漂移的一侧,spec 侧的partial命名是既有约定 —— 因此修复应落在 objectui,标repo:objectui(而非domain:spec)。需要指出:issue 正文里同时说明,仅改名还不足以关闭 #4001 对
IndexSchema的收紧,因为partial/type本身在 driver-sql 侧仍是死键,真正解除阻塞需要本 issue(生产者对齐)与 #5248(ADR-0049 enforce-or-remove 结论)两者都落地。这一阻塞关系已经记录在 PR #5250 与两个 issue 的正文里,故未对 #4001 做任何编辑,仅在此评论中说明关系:Blocks: #4001(经由 #5248 联合阻塞)。「本评论来自分诊座位 Routine(#5474 试点),不构成认领」
Generated by Claude Code
- objectstack
前提更新 + 挂
pm:blocked(Blocked-by: #5248,已写入正文首行):维护者 2026-08-06 已裁IndexSchema.type/partial双双退役(见 #5248 裁决评论)。本单修向随之改变——不再是「where→partial对齐拼写」,而是随退役同步 console 编辑器:FALLBACK_SCHEMAS.index删除where/"Partial-index predicate" 输入项与brin枚举值,只保留name/fields/unique(含 ADR-0120 的 scope 语义)。#5248 的退役 PR 落地后解锁 sweep 回捡本单。正文第三条建议(内嵌子类型注册表消灭 fallback 漂移源)仍然成立,可在实施时评估顺手程度或另立单。会话:session_01N3uGFF8teXbpgtbEJ1aYXu
Generated by Claude Code
os-project-manager commented
on Aug 8, 2026 CollaboratorMore actionsUnblocked —
pm:blockedremoved, back in the queue. Unlock sweep, 2026-08-07.Upstream #5248 (
IndexSchema.type/partialenforce-or-remove) closed as completed 2026-08-06T09:06Z.⚠️ Dispatchable since then — ~2 days unswept.Claim-time note: #5248's disposition decides what this card converges the console editor to, so read its outcome first — if
partialwas retired, thewherevspartialdrift is resolved by removal rather than by renaming.
Generated by Claude Code
Claim: PM loop round 1 (spec seat shift 2026-08-16)
Session:session_01SgModTWkJPeXMgbg7enL5Z
Branch:claude/issue-5247-console-index-fallback
Worktree:objectui-issue-5247
Domain: cross-lane simple-blocker takeover — home lane of this card isrepo:objectui; the claiming seat isdomain:spec(+surface/tooling), which this card blocks (last real upstream of #4001'sIndexSchemaclose, per the strictness ledger)
File surface:packages/app-shell/src/views/metadata-admin/EmbeddedItemEditor.tsx+ its tests (stop on breach; explain in the report)
Container & model: S mechanical,mode:subagent,model: opus
Serial constraints cleared: no open objectui PR touchesEmbeddedItemEditor.tsx(searched 2026-08-16); objectui seat post #6025 names no hot-file queue for it; the seat's in-flight batches 29–33 (#3417/#3424/#3425/#3430/#3378+#3288) do not touch this file.Authorization context: maintainer in-session instruction this shift, verbatim, untranslated: 「4001 可以继续」— this card is the campaign's remaining upstream gate. Takeover is under the standing simple-blocker rule (mechanical, clearly specified, S-size, blocks the claiming lane); a completion note will be left for the
repo:objectuiseat.Scope as updated by this thread's 2026-08-06 premise comment (post-#5248 ruling, both keys retired via PR #5842): converge
FALLBACK_SCHEMAS.indexto the spec's currentIndexSchema— delete thetype/"Algorithm" control (retired key, and itsbrinvalue was never legal) and thewhere/"Partial-index predicate" control (edits a key that never existed;partialis also retired), keepingname/fields/uniquewith the spec's currentuniquesemantics re-read from objectstackorigin/mainat implementation time. Drift re-verified present on objectuiorigin/mainthis morning (brinat :328,whereat :336).
Generated by Claude Code
{ "issue": 5247, "status": "done", "branch": "claude/issue-5247-console-index-fallback", "pr": "https://github.com/objectstack-ai/objectui/pull/4772", "premise_still_valid": true, "summary": "Drift confirmed present on objectui origin/main exactly as the PM described (brin at :328, where at :336) and converged. Deleted the `type`/\"Algorithm\" control and the `where`/\"Partial-index predicate\" control from FALLBACK_SCHEMAS.index in EmbeddedItemEditor.tsx. One premise detail is now WORSE than the card recorded: measured against the installed @objectstack/spec@17.0.0-rc.6, `type` and `partial` are retiredKey tombstones that REJECT at any value, so the Algorithm select was not a silent-drop control — every one of its five options produced a 422 on the parent save (the #5114 class, live today). Only `where` was the silent strip the card describes. Also converged `unique` onto the ADR-0120 scope union per the claim comment's instruction to re-read the spec's current unique semantics: the console previously rendered a boolean switch, so the only spelling it could emit was the deprecated bare `true` that protocol 18 rejects (#5082); it now offers 'global'/'organization' for new indexes while the union's boolean branch keeps legacy indexes rendering and saving unchanged. Spec's live authorable surface re-read from objectstack origin/main and from the installed package is exactly name/fields/unique — no nullSafeColumns (that is driver-side DeclaredIndexInput, not spec).", "tests": "Union re-run AFTER the final commit at 2be19d6 (clean tree). `pnpm exec vitest run packages/app-shell/src/views/metadata-admin/` -> 'Test Files 168 passed (168) / Tests 1684 passed | 1 skipped (1685)'. New pin + sibling: `pnpm exec vitest run .../EmbeddedItemEditor.indexFallback.test.tsx .../EmbeddedItemEditor.preview.test.tsx` -> 'Test Files 2 passed (2) / Tests 21 passed (21)'. `pnpm --filter @object-ui/app-shell run type-check` exit 0 (tsc --noEmit && tsc -p tsconfig.test.json) — required `pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build` first, without which tsc reports 20+ 'Cannot find module @object-ui/*' false reds. eslint on the two changed files: 0 errors, 10 warnings. Repo gates all PASS: check:phantom-deps, check:control-bytes, lint:coverage (46/46 packages, 0 errors), check:i18n-keys, check:i18n-drift, check:i18n-dead-keys, check:spec-symbols, check-changeset-presence, check-changeset-no-major. REVERSE VERIFICATION: fix committed first, then ablated (export kept so failure is the drift, not a missing import); expected direction RED, observed RED with 8 failures each naming the real defect — `type` -> \"expected '`indexes[].type` was removed in @obje…' to be ''\" (the 422 half), `where` -> 'expected undefined to deeply equal sample' (the silent-strip half, which is why the pin asserts round-trip survival rather than unrecognized_keys — IndexSchema is deliberately not .strict()), completeness -> \"expected ['fields','name','type',…(2)] to deeply equal ['fields','name','unique']\", and the rendered 'Algorithm' label -> 'expected <label …> to be null'. No ablation-on-dist involved (source-resolved package, not a dogfood run). Repo-specific note: `pnpm --filter <pkg> test` and `-- <paths>` are both blocked by objectui's vitest-invocation-guard as silent-false-green traps; the correct form is `pnpm exec vitest run <path>` from the repo root, which is what every run above used.", "open_questions": [], "out_of_scope_findings": [ "filed as objectstack-ai/objectui#4765 (label `finding`, unassigned): `UniquenessValidation`'s @deprecated JSDoc in packages/types/src/data-protocol.ts prescribes `partial` (retired in spec 17.0.0) and `unique: true` (deprecated positional spelling) as the replacement for the removed uniqueness validation — the same false-guidance class this card cites from ledger finding 18, but in a different package, so filed rather than fixed under the declared file surface" ] }Notes the JSON does not carry well
- File surface honoured, one deliberate side effect. Only
EmbeddedItemEditor.tsx, its new test, and a.changeset/*.md(mandatory in objectui for any change to a released package'ssrc/) are touched.FALLBACK_SCHEMASis nowexported so the pin reads the real object rather than regexing source text; that tripsreact-refresh/only-export-componentsas a warning (lint still exits 0). Moving the constant to its own module would clear it but adds a file outside the declared surface — flagged in the PR for the reviewer to call. - The pin is deliberately not a second hand copy. It runs the console's own properties through the installed
@objectstack/spec, so it re-derives on every spec bump instead of re-stating expected key names in its own source. Completeness runs the other direction too (probe each spec shape key; a tombstone rejects every probe, a live key accepts one), so a spec key with no console control also fails — with no hand-listed key set to maintain. - The issue body's third suggestion (embedded sub-type registry) remains out of scope and remains valid — noted in the PR. It would delete this hand copy rather than re-align it; the pin is the cheap interim guard.
- No CI wait per the standing dispatch contract (maintainer-decided 2026-08-10): draft PR is open, gates were run locally at the reported sha, CI conclusions are the PM's read.
Generated by Claude Code
- File surface honoured, one deliberate side effect. Only
ACCEPT — PR objectstack-ai/objectui#4772 (draft, base
main,Fixes objectstack-ai/objectstack#5247first line verified).Reviewed against GitHub, not the report's self-description:
- Changed files (from the PR's file list): exactly
EmbeddedItemEditor.tsx+ newEmbeddedItemEditor.indexFallback.test.tsx+ one changeset — declared surface honoured. TheFALLBACK_SCHEMASexport (react-refresh warning, lint still green) is a declared, deliberate trade so the pin reads the real object; follow-up module split left to a future card if anyone cares. - Both dead controls deleted;
uniqueconverged onto the ADR-0120 scope union with the scope branch first (new indexes can no longer author the deprecated baretrue; legacy booleans keep rendering) — this was instructed at claim time, not scope creep. - Premise sharpened, not falsified:
typewas measured as a live 422 on every option (retiredKey tombstone), not the silent drop the card recorded; onlywherewas the silent strip. The card's framing survives — both controls were false authoring surface. - Pin quality: asserts round-trip survival + rejection against the INSTALLED
@objectstack/spec(no second hand copy; completeness probed in both directions). It stays green when 未知键静默剥离仍是全仓默认:把 #3405 的 strict 收紧从一个 schema 推广到整个可授权面(ADR-0078 完整性闸门) #4001's IndexSchema close later lands spec-side. - Reverse verification: 8 red, each naming the real defect. Out-of-scope finding
UniquenessValidation's deprecation JSDoc points authors at the retiredindexes[].partialkey objectui#4765 verified filed (unassigned,finding).
CI gate jobs still
in_progressat review time — flip-to-ready + auto-merge happens only after the gate family concludes green (flip check armed). #4001'sIndexSchemaclose slice dispatches once this PR is MERGED.
Generated by Claude Code
- Changed files (from the PR's file list): exactly
- added 2 commits that reference this issue
on Aug 17, 2026
Blocked-by: #5248
发现于 #4001 批 20(
data/object.zod.ts内层块收紧)的逐站点测量。本 issue 只记录,不在批 20 里修——批 20 因此有意保留IndexSchema未收紧,是该批 14 个站点里唯一没关的一个。症状
objectui 的 console 为
object.indexes[]里的条目自带了一份手抄的 JSON-Schema:packages/app-shell/src/views/metadata-admin/EmbeddedItemEditor.tsx→FALLBACK_SCHEMAS.index它存在的原因是合理的:
index是内嵌专用子类型,没有自己的元数据类型,框架没有槽位发布它的 schema(文件里的注释也这么写:"Once / if the framework grows an embedded-sub-type registry, this can move server-side too.")。注释同时声称这些 fallback "mirror the framework's Zod definitions" —— 而它已经不再 mirror 了:IndexSchema声明where(标题 "Partial-index predicate")partialbrin['btree','hash','gin','gist','fulltext']为什么它现在是静默失败
编辑器的保存路径(同文件,
spliceEmbedded→client.save(parentType, …))把表单结果拼回父对象再 PUT 整个对象。服务端saveMetaItem(packages/metadata-protocol/src/protocol.ts)对 body 做safeParse并在失败时 422,但逐字保留原 body。IndexSchema目前是 zod 默认的.strip,所以管理员在 "Partial-index predicate" 里填的内容:where作为未声明键被 spec 在每次后续 parse 时丢掉;两个方向都是死的
partial和type都没有任何 DDL 消费者。driver-sql的syncDeclaredIndexes只读name/fields/unique(以及 ADR-0120 D3/D4 之后的nullSafeColumns);DeclaredIndexInput(packages/plugins/driver-sql/src/schema-drift.ts)的字段列表就是这四个。为什么这会阻塞 #4001
关掉
IndexSchema会把上面第 1 步变成 422,而 422 打在 console 自己渲染的控件上 —— 这正是 #5114 那一类(区别是这次在发布之前抓到)。但"先修生产者再关"还不够。因为
partial本身也是死的,收紧后把作者指向partial,是一条声称超出平台实际交付的 guidance —— 台账 finding 18 记录的正是本战役已经发过的四条假 guidance。所以关掉
IndexSchema至少需要两件事先落地:where改成partial,并把枚举对齐 spec(去掉brin,或在 spec 里声明它——后者需要有驱动读它)。IndexSchema.type和IndexSchema.partial一个结论——要么接上驱动,要么退役。顺带值得考虑的第三条:这类 fallback schema 本身就是漂移源。框架长一个内嵌子类型注册表(把
index的 schema/form 发布出去,像validation已经通过HAND_CRAFTED_SCHEMAS.validation做的那样)能一次性消灭这个复制品。现状记录在三个地方
packages/spec/src/data/object.zod.ts的IndexSchemaJSDoc(明确写了"不要顺手补完");packages/spec/src/data/object-strictness-batch20.test.ts§4 —— 把当前的 strip 行为钉住,使它改变的那天是有意改变;docs/audits/2026-07-unknown-key-strictness-ledger.md的data/行。复现(当前
main+ 批 20 分支上行为一致):