Skip to content

feat(spec)!: dataSource.filter and the four object-* filter doors converge onto the ViewFilterRule array form — one filter orthography reaches the family (objectui#6206-B, option A) - #17257

Merged
os-bill merged 7 commits into
mainfrom
claude/issue-15442-filter-orthography-converge
Sep 10, 2026
Merged

feat(spec)!: dataSource.filter and the four object-* filter doors converge onto the ViewFilterRule array form — one filter orthography reaches the family (objectui#6206-B, option A)#17257
os-bill merged 7 commits into
mainfrom
claude/issue-15442-filter-orthography-converge

Conversation

@os-bill

@os-bill os-bill commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Fixes #15442
Fixes #15449

Clause-②: yes

What moves

One filter orthography platform-wide (objectui#6206-B; decision batch #55, ruling 5559671308, option A) reaches the two locations the ComponentPropsMap census could not see — as ONE family, one ADR-0087 D3 entry:

door before after
ElementDataSourceSchema.filter (page.zod.ts, the dataSource binding every data-bound element carries) FilterConditionSchema — the MongoDB-style record; every array form refused z.array(ViewFilterRuleSchema)
ComponentPropsMap['object-grid'].filter z.unknown() — record, AST tuple array and rule array all accepted silently z.array(ViewFilterRuleSchema)
ComponentPropsMap['object-metric'].filter z.unknown() z.array(ViewFilterRuleSchema)
ComponentPropsMap['object-kanban'].filter z.unknown() z.array(ViewFilterRuleSchema)
ComponentPropsMap['object-calendar'].filter z.unknown() z.array(ViewFilterRuleSchema)
  • D3 entry element-data-source-and-object-block-filter-rule-array (packages/spec/src/migrations/entries/semantic/18.…), registry / spec-changes.json / upgrade guide regenerated with the repo tooling.
  • FilterConditionSchema leaves page.zod.ts with its one site. ADR-0122 aliases declared: ElementDataSourceParsed, ObjectMetricPropsParsed, ObjectKanbanPropsParsed, ObjectCalendarPropsParsed (operator normalizes on parse, so input ≠ infer now); the four isomorphic pins deleted (815 → 811).
  • Regenerated: content/docs/references/ui/{component,page}.mdx, api-surface/ui.json, export-origins/ui.json (via check:generated --fix; authorable-surface unchanged).
  • Changeset: @objectstack/spec minor with the **BREAKING** banner and the adr-0087: registered … disposition (check-adr-0087-registration exit 0).

⛔ Not touched, on purpose: object-grid.defaultFilters (a different key, z.unknown(), not named by the ruling — recorded on #11509 for the ruler); the dashboard widget.filter family (#15829); the four already-array doors (record:related_list, its nested picker, element:number, element:record_picker).

Premise re-derived on origin/main ae19f5edb (the branch base)

Measured at the objectui pin 53ded82b before the declarations moved (read from git show 53ded82b:… in the sibling checkout; read, not run)

  • Binding: core/src/data-scope/element-data-source.ts types filter as unknown and AND-combines it with the named view's rules via mergeFilterNodes; core/src/utils/filter-converter.ts toFilterNode maps each ViewFilterRule to an AST node (isViewFilterRule arm). No non-test file at the pin imports the spec's ElementDataSource type (git grep, zero hits; control: the same grep with the test dirs included hits the *.elementDataSource suites).
  • object-grid: ObjectGrid.tsx:1455-1456 lowers schema.filter through toFilterNode.
  • object-kanban / object-calendar: ObjectKanban.tsx:263 and ObjectCalendar.tsx:430 hand schema.filter verbatim to $filter; data-objectstack/src/index.ts:3971 runs translateFilterArray, whose first arm isObjectFilterEntryForm (:350-357) takes [{ field, … }].
  • object-metric with aggregate (all five showcase tiles): ObjectMetricWidget.tsx:230 resolves placeholders (resolveFilterPlaceholders walks arrays and objects alike, filter-tokens.ts:209), :247 calls ds.aggregate, and the adapter's lowerAnalyticsFilterForWire (index.ts:510-540) lowers an authored array through translateFilterArray + isFilterAST + parseFilterAST before POST /analytics/query — the door runtime: POST /analytics/query refuses the array where the objectui adapter now sends for every array-form filter — AnalyticsQueryRequestSchema.where is FilterConditionSchema, while lowerAnalyticsWhere (the gate ui#6302 measured) accepts filter AST #15828 was about. shiftFilterByCompareTo (compare-to.ts) substitutes tokens through arrays too.

The four spec fixtures (ruling said three; the parked flight found the fourth) — all 改拼写

fixture before after
page.test.ts:633 (direct ElementDataSourceSchema.parse) filter: { status: 'pending' } [{ field: 'status', operator: 'equals', value: 'pending' }]
page.test.ts:665-666 { status: 'completed' } rule array
page.test.ts:762 { status: 'pending' } rule array
page.test.ts:767 { status: 'completed' } rule array

Two more spec fixtures the sweep counted under no heading, at the object-* doors (component.test.ts "#7751" block: the corrected #7750 grid node with the AST tuple, and the three showcase metric records in the clean-corpus control) — migrated the same way; they mirror the showcase authors.

Lint fixtures (3): validate-component-props.test.ts:483 (grid tuple → rule) and :488 (metric record → rule) 改拼写; validate-empty-combinators.test.ts:240 整个换掉 — the page row is dropped from the "covers …" case (no page-level key carries the record form any more, and the rule judges record nodes only), pages stays in EMPTY_COMBINATOR_SURFACES.

Showcase authors (5): my-work.page.ts:39/:40/:41 and index.ts:78 (metric records → rule arrays), my-work.page.ts:59 (grid AST tuple → rule) — plus the showcase's own pin test/my-work-visibility.test.ts:190, which authored the tuple.

New pins

Clause ② — measured on the finished diff

yes, and two-directional at the binding: the record leaves the accept set and the rule array enters it (before: rule array refused invalid_type expected record; after: accepted). At the four doors it is a pure narrowing of a published accept-anything door. The AST tuple array is not "newly accepted" anywhere — it was refused at the binding before and stays refused (one hop deeper, filter.0), and it leaves the four doors' accept set. check-clause2-carriers.mjs --pair exit code: see the report comment on #15442.

Verification

All exit codes were redirected to disk before being read (cmd > log 2>&1; echo $? > file), never through a pipe. Head verified: 6274af365 (⚠️ the head at the time of this round; two later rounds have moved it — see “Head of record” at the end).

Builds (under scripts/pm/os-verify-lock.sh, slot issue-15442-converge):

  • pnpm --filter @objectstack/spec buildVERDICT command-exit 0 · held the lock 151s · waited 374s.
  • pnpm turbo run build --filter='@objectstack/lint^...' --filter='@objectstack/example-showcase^...' --concurrency=2command-exit 0, Tasks: 61 successful, 61 total (7m13s held).
  • Post-sweep rebuild of spec so the dist stamp matches src after the test-file edits: pnpm --filter @objectstack/spec build exit 0 (stamp now matches src), tree clean after it.

Package suites, full, never a subset (one lock acquisition, six commands, six exit files, all 0 on 6274af365):

  • @objectstack/spec pnpm test: Test Files 469 passed | 1 skipped (470), Tests 13238 passed | 1 skipped (13239), 385s. pnpm typecheck (tsc + scripts + test-typecheck): exit 0, check:test-typecheck: OK … 54 file(s) / 261 error(s) / 145 pinned signature(s) (the pre-existing debt ledger, unchanged).
  • @objectstack/lint pnpm test: Test Files 103 passed (103), Tests 3680 passed (3680). pnpm typecheck: exit 0.
  • @objectstack/example-showcase pnpm test: Test Files 28 passed (28), Tests 379 passed (379). pnpm typecheck: exit 0.
  • First spec run (before the two fixture migrations in component.test.ts and the two corrections to my own new pins) was red 4/13240 — the failures are what drove commit 6274af365; the targeted re-run of the two files was 349/349 and the full suite above is the measurement.

Reverse verification (script with trap … EXIT INT TERM, absolute paths, run on the committed state under the lock):

  • Leg A, runtime, cross-package (lint test → @objectstack/spec dist/): mutated validate-component-props.test.ts :488 back to filter: { status: { $ne: 'done' } } (on-disk anchors: injected 1 / removed 0) → vitest run src/validate-component-props.test.ts exit 1, 1 failed | 44 passed, the failing case named: stays SILENT on the corrected #7750 node and the corpus metric shapes (clean-corpus control). Restore git checkout HEAD -- PATH: git diff HEAD empty (exit 0), git hash-object = HEAD blob 776b8b641dfc… (equal); re-run 45/45.
  • Leg B, type, cross-package (lint tsc --noEmit → the rebuilt .d.ts): a probe file assigning the record form to ElementDataSource and ObjectMetricProps, the AST tuple to ObjectGridProps, and the rule array to ElementDataSource → exit 1 with exactly three errors, one per off-spec line (TS2353 'status' does not exist in type '{ field: string; operator: …' ×2, TS2322 Type '[string, string, string]' is not assignable), zero on the rule-array line; probe deleted → exit 0; git status --porcelain 0 lines.
  • Dist preflight: node scripts/ablation-dist-preflight.mjs @objectstack/spec 'element-data-source-and-object-block-filter-rule-array'marker present in 18 built files, tree clean.

Gate families — derived, not hand-fed: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (stderr line 1: derived from the tree of 'objectstack-ai/objectstack' at commit 6274af365, --repo … holds), 108 commands, each run to its own log with its exit in a ledger, then --ran reconciled: ✓ dispatch-gates --ran: 108 derived famil(ies) accounted for — 108 run, 0 NOT-MEASURED (the reconciler counts commands run, not exit codes).

  • 100 of 108 exit 0 on the first pass. Six spec gates (check:api-surface, check:dual-source-exports, check:entry-nameability, check:exported-any, check:generated, check:skill-examples) refused on their own prerequisite — dist/.build-input-hash-dts recorded the pre-test-edit src digest — so their first-pass exit 1 is NOT MEASURED; after the rebuild all six exit 0 with their own verdict lines: public API surface + factory signatures unchanged ✓, no new dual-source exports, entry-nameability … 0 new unnameable, no exported type resolves to any, All 15 generated artifacts are up to date, 258 prose examples type-check.
  • pnpm check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET — seven packages my diff never touches have no dist/ in this container (@objectstack/studio, client-react, embedder-openai, knowledge-memory, knowledge-ragflow, organizations, service-cluster-redis): NOT MEASURED locally, CI builds them.
  • pnpm --filter @objectstack/spec run check:react-declaration-parity exit 1: MANIFEST is not set — there is no registry side to compare against; the gate's own text says a non-zero exit is a reading, not an unavailable input. It is the on-demand pin-bump gate (AGENTS.md: never a CI job) and needs objectui's sdui.manifest.json from a browser run; not produced here. Reported as exit 1 with that reason.
  • Also run outside the derivation: check-adr-0087-registration exit 0 (registered element-data-source-and-object-block-filter-rule-array), check:nul-bytes exit 0.

PR CI (newest run per check name, read at report time): head 6274af365, 33 distinct check names — skipped: Auto Label, Check PR Size, Console Pin Gate, Packed-tarball smoke (opt-in); success: Build Core, Build Docs, Check Changeset, Check Documentation Links, Dogfood Regression Gate, Dogfood Regression Gate (1/3), Dogfood Regression Gate (2/3), Dogfood Regression Gate (3/3), Dogfood Verify CLI, Flag docs affected by code changes, Governed Surface Queue Guard, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, Spec property liveness, Temporal Conformance (live PG + MySQL), Test Core (1/6), Test Core (2/6), Test Core (4/6), Test Core (5/6), Test Core (6/6), Type Check · consumer gates, Type Check · debt ledger, Type Check · source gates, Type Check · workspace, TypeScript Type Check, filter; in_progress: Lint & Repo Gates, Test Core (3/6). Nothing red at read time; in_progress is recorded as such, not as green.

验收备注

  • noted, not filed: pages in EMPTY_COMBINATOR_SURFACES (validate-empty-combinators.ts) now has no spec-legal record filter to judge on a page — the walker is key-name based, so an author writing the retired form still gets the located error beside the schema refusal; 承接者:无.
  • noted, not filed: the generated page.mdx renders the nested rule's operator as optional in the PageComponent.dataSource row and as required in the ElementDataSource row — how the docs generator prints a z.preprocess in a nested position, pre-existing for every other rule-array door; 承接者:无.
  • The objectui follow-ups the ruling assigns to the seat (the seventeen dataSource.filter test authors at the pin; docs(qa): refresh cli dev-boot DB clause to unified objectstack.db; narrow build exit clause to the #4873 leak #7712) are not filed here.

维护者速读(草稿)

改了什么:dataSource.filter(每个数据绑定元素都带的绑定键)和 object-grid / object-metric / object-kanban / object-calendar 四个块的 filter,五个门统一收敛到平台的 ViewFilterRule 数组形([{ field, operator, value }]);MongoDB 风格的 record 形和 ObjectQL 元组数组在这五处不再被接受。一条 ADR-0087 D3 迁移条目覆盖整个家族;仓内 4 条 spec 夹具、2 条 object-* 夹具、3 条 lint 夹具、5 个 showcase 作者同 PR 迁移。

为什么改:裁决 5559671308(批次 #55,「同意」,方案 A)——「全平台一种过滤器拼写」的原则触达 ComponentPropsMap 普查看不到的两处。绑定键原本只收 record,拒绝消费方自己的 pin 写的数组;四个块门是 z.unknown(),什么都收:跟着 showcase 写 record 和跟着 manifest 写数组的作者各得到一张错的静默回执。

风险与代价(含回滚):已发布契约收窄(spec minor + BREAKING 横幅,D3 条目带 FROM → TO 处方)。运行时按 objectui pin 53ded82b 实测读点:四个块和绑定都把 rule array 降型到同一条 AST 路径,object-metric 聚合线路经 parseFilterAST 落到 analytics where(#15828 的门已在该 pin 修好)。回滚 = revert 本 PR(生成物随 check:generated --fix 回退);对外已发布版本的作者靠 D3 条目迁移。

席位意见:(留空)

你要做的:确认 needs:contract-review 的合同审阅;确认 object-grid.defaultFilters 是否并入家族(#11509 上的方向问题,本 PR 未动它)。

Head of record (added 2026-09-10T03:30Z by the domain:spec seat)

⚠️ Current head is ad7dcbc0ffaf0dc70d7d0bad05c7c7bedcc8af97. Everything above is the round-1 record at 6274af365 and is accurate as history — ⛔ but the clause "the PR head; no commit after it" is no longer true of it, and is corrected here rather than rewritten above.

Two rounds landed after that record, each for a finding of the at-tier contract review:

  1. 3d7f4eda0 — the D3 entry gained its metadata-at-rest sentence, and the deleted pages row was restored to packages/lint/src/validate-empty-combinators.test.ts (with an ablation proving the restored pin can fail).
  2. 816f181e1 + ad7dcbc0f — one false clause in that at-rest sentence replaced: it had said "no author runs os migrate meta over sys_metadata", which is false because os migrate meta --stored exists and its subject is exactly sys_metadata. It now says the stored pass replays D2 conversions only, so it has nothing to rewrite for this shape.

CI at the current head, newest run per check name: 34 distinct names — 32 success, 2 skipped (Console Pin Gate, Packed-tarball smoke (opt-in)), 0 red, all terminal. check-clause2-carriers --pair 17257exit 0 on both pairs; check-governed-merges --test over the unchanged 17-path list → exit 0, NOT governed.


Generated by Claude Code

…ors onto the ViewFilterRule array

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
… for the filter converge

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
…k each key at its own door in the binding pins

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

10 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️ 2 changed file(s) yielded no anchor (packages/spec/api-surface/ui.json, packages/spec/export-origins/ui.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/spec/api-surface/ui.json, packages/spec/export-origins/ui.json) — pages documenting those are invisible to this run
  • 4 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 100 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 134 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json d64bcb63772b850b3ca8ae39779d492e46d3a0a6packageMentionDocs.

Which tree this was computed on

This run read content/docs from bbc4e2be0d6c47ffc1c42e184fffb7a23acdbc46 — the merge of head ad7dcbc0ffaf0dc70d7d0bad05c7c7bedcc8af97 into base d64bcb63772b850b3ca8ae39779d492e46d3a0a6, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin bbc4e2be0d6c47ffc1c42e184fffb7a23acdbc46 && git checkout bbc4e2be0d6c47ffc1c42e184fffb7a23acdbc46
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin d64bcb63772b850b3ca8ae39779d492e46d3a0a6 ad7dcbc0ffaf0dc70d7d0bad05c7c7bedcc8af97 && git checkout -B drift-repro d64bcb63772b850b3ca8ae39779d492e46d3a0a6 && git merge --no-ff ad7dcbc0ffaf0dc70d7d0bad05c7c7bedcc8af97

node scripts/docs-audit/affected-docs.mjs --json d64bcb63772b850b3ca8ae39779d492e46d3a0a6

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

os-bill commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator Author

Contract review at CONTRACT_REVIEW_TIERVerdict: PASS WITH FINDINGS (adopted by the domain:spec seat)

Head reviewed 6274af36505521730cd0da2383d19dbdc3854a88 · cards #15442 (chain head) + #15449 (folded member) · seat session_01MkQhmuuJAVDjmeWNixwDDH, adopted 2026-09-10T01:38Z.

  • Reviewed-by: an isolated claude-fable-5-1 subagent, dispatched by this seat, measuring in two detached worktrees (head + merge-base) and writing nothing to either repo. Implemented-by: mode:subagent os-dev on claude/issue-15442-filter-orthography-converge under the same seat. Separate isolated subagents at different tiers ⇒ ⛔ not a self-review in substance — but ⚠️ both run under THIS seat’s session id, so this is a seat-side at-tier review, ⛔ NOT the cross-seat independence of the director-seat review on the sibling PR. See the correction at the end.
  • Tier fuse — the transcript, not the self-report. ⛔ A subagent's claim about its own tier is never a reading. Grepped for harness-stamped model values over the 877 KB transcript: 113 × "model":"claude-fable-5-1", zero residue (no other model value present), with a lit control proving the file is actually scanned ("role":"assistant" = 100, "role":"user" = 74). Fuse passes ⇒ verdict adopted verbatim.

The three landing pre-checks, all measured

# check reading
at-tier PASS on record PASS WITH FINDINGS, tier-fused above
check-clause2-carriers.mjs --pair 17257 exit 0 on BOTH pairs (#15442 and #15449)
every check green — newest run per check name, ⛔ not the raw list, ⛔ not the required subset 44 raw runs → 34 distinct names: 30 success, 4 skipped, 0 red (skipped: Auto Label, Check PR Size, Packed-tarball smoke, Console Pin Gate)

⚠️ ② was re-measured from an origin/main mirror of the checker, not the working tree: the primary checkout sits on another branch and its copies of check-clause2-carriers.mjs and check-widening-tells.mjs differ from origin/main by git hash-object (check-governed-merges.mjs is identical). check-governed-merges.mjs --test over the full 17-path list → exit 0, NOT governed ⇒ ordinary queue landing applies to this PR.

⛔ Seat decision: HOLD before landing, for F1 and F3 — patch round dispatched

All three pre-checks pass, so this PR could be landed now. It is not being landed now, for two reasons, and both are measurements rather than preferences:

  1. F3 is a coverage regression this PR itself introduced. It deleted the only pages row from validate-empty-combinators.test.ts while pages remains in EMPTY_COMBINATOR_SURFACES — the sole measurement of that rule's page walk. Deleting a pin is deleting a measurement, and a deleted measurement carded as a follow-up is a measurement nobody restores. It is ~5 lines in a file this PR already edits. F1 is one sentence with a precedent already in the tree. Both are cheaper now than ever again.
  2. Sequencing, measured: this PR's file list and PR feat(spec)!: retire the seven cron-typed positions nothing evaluated — export schedules, ScheduleState.cronExpression, DataSyncConfig.schedule, CacheWarmup.schedule, backup / DR-test schedules (#16320, ADR-0049) #17146's branch diff intersect in exactly one path — packages/spec/src/migrations/registry.ts — and feat(spec)!: retire the seven cron-typed positions nothing evaluated — export schedules, ScheduleState.cronExpression, DataSyncConfig.schedule, CacheWarmup.schedule, backup / DR-test schedules (#16320, ADR-0049) #17146's os-regen-merge round is in flight right now. Enqueuing this PR mid-round would move main under a round whose entire purpose is merging main. So the queue slot is not free of consequences even if the PR is.

⛔ F2 and the partial-D2 question are not in that round — see the ACCEPT list.

Findings

F1 — non-blocking (in the held round). migrations/entries/semantic/18.element-data-source-and-object-block-filter-rule-array.ts ~:28-60 states the sweep and the objectui route but is silent on metadata at rest — the one population its TODO cannot reach — os migrate meta --stored replays D2 conversions only and emits no D3 TODOs, so this entry’s prescription never reaches a stored row. (⚠️ This sentence originally said “no author runs migrate meta over sys_metadata”, which is false; see the correction at the end.) Measured: the disposition is a SemanticMigration, which by its type (migrations/types.ts:30-41) converts nothing; no D2 conversion is added; applyConversionsToStoredItem (database-loader.ts ~:831) replays the chain without validating, so a stored row carrying the old record keeps loading, is still rendered by objectui at the pin, and is refused only on its next save. Fix: one sentence in reason, on the model of 17.view-filter-rule-value-shaped-by-operator.ts:38-50.

F2 — non-blocking, family-wide ⇒ card, ⛔ not a rider here. At all five doors an author submitting the old shape gets a bare zod invalid_type (filter, expected array; tuple → filter.0); the prescription lives only in .describe() and the D3 entry. The repo has a precedent for putting the prescription on the refusal itself (zod-v4 { error } at expression.zod.ts:158, view.zod.ts:4328, strict-object.ts:328), and the reviewer probed that z.array(ViewFilterRuleSchema, { error }) keeps code: invalid_type / expected: array, so the new pins stay green. The same gap exists at the two precedent doors (element:number, element:record_picker) — which is exactly why it is a family card and not a rider.

F3 — non-blocking (in the held round). packages/lint/src/validate-empty-combinators.test.ts ~:231-246, as above. Restore a page row and put page "home" back in the expected list. ⛔ Not by weakening the case.

F4 — informational, recorded so nobody hunts a missing diff. spec-changes.json and the upgrade guide show no diff correctly: both projections fold only to the current PROTOCOL_MAJOR (17), and this is a major-18 entry. Control: the neighbouring 18.ui-record-blocks-unknown-keys-refused is likewise absent, while 17.view-filter-rule-value-shaped-by-operator is present ×2 in spec-changes.json and ×1 in the upgrade guide. check:spec-changes / check:upgrade-guide exit 0.

F5 — non-blocking, pre-existing, generated. content/docs/references/ui/page.mdx:32 vs :277 render the nested rule's operator as required in one table and optional in the other (the generator's handling of a z.preprocess in a nested position). ⛔ Not this PR's.

What the review confirmed, so it is on the record

  • Ruling 5559671308 executed exactly — five doors on z.array(ViewFilterRuleSchema), one D3 entry folded into registry.ts step 18, regenerated artefacts, the census twin pin over the whole map by shape, finding(spec): dashboard widget.filter (dashboard.zod.ts:387, :720) still declares FilterConditionSchema — a further location under ui#6206-B outside ComponentPropsMap, and the console's DashboardWidgetInspector authors exactly that record form #15829 and defaultFilters untouched. Nothing required is missing; everything present beyond the letter is consequential (the four ADR-0122 aliases are forced by that ADR's gate), ⛔ not widening.
  • Clause ② yes verified independently on both trees, not adopted from the dev: two-directional at the binding (record leaves, rule array enters), pure narrowing at the four doors, and the AST tuple newly accepted nowhere.
  • minor + **BREAKING** is coherent here, and is the repo's ruled convention, not a sleightpr-automation.yml:717-719: during the launch window major stays refused and breaking-ness rides the banner plus the ADR-0087 disposition. check-changeset-no-major exit 0, check-adr-0087-registration exit 0, FROM → TO plus the one-line fix present in the changeset's Migration paragraph.
  • Prime Directive 13 grep done over 139 ADRs for FilterCondition, ViewFilterRule, dataSource.filter, the four object-block names, defaultFilters, ComponentPropsMap, z.unknown(): ViewFilterRule = zero, with FilterCondition lit in 5 ADRs as the control and every hit read. No accepted ADR records a decision this diff reverses ⇒ no new ADR and no amended status line owed.
  • The four deleted isomorphic pins are not lost measurements: each asserted z.input === z.infer for a schema where that is now false by construction (ViewFilterRuleSchema.operator is a z.preprocess), so the assertion would be a compile error; each is replaced by its ADR-0122 alias, and the runtime companion recomputes 811.
  • The fix commit strengthened assertions rather than erasing themissuesUnder (a prefix match that would have matched filter.0) replaced by exact issuesAt, and the two-keys case moved off a schema that treats properties as a bag onto the real ComponentPropsMap door.

File at ACCEPT (⛔ not folded into this PR)

  • F2 family-wide: a prescription-carrying refusal at the rule-array filter doors — the five here plus element:number and element:record_picker. 承接者: domain:spec seat.
  • Partial D2 conversion for the filter-orthography family — whether the losslessly mappable subset (flat record, {k:{$op}}, single-level tuples) should get a real conversion with only the groups left as the D3 TODO, so stored rows and migrate meta rewrite what can be rewritten. ⭐ This is a direction question, not a defect of this PR: the ruling prescribed D3 only, and both family precedents did the same. 承接者: director seat / maintainer.
  • objectui follow-ups already assigned by ruling 5559671308: the 17 dataSource.filter test authors at pin 53ded82b; record-picker.tsx:369, which documents dataSource.filter as a FilterCondition and says the spec rejects the array — now inverted; the element-data-source.ts:11 "three shapes" note; and docs(qa): refresh cli dev-boot DB clause to unified objectstack.db; narrow build exit clause to the #4873 leak #7712's kanban/calendar registry declarations. 承接者: domain:spec seat, filed in objectui with Blocked-by:.

Not measured — UNJUDGED, ⛔ never clean

check:react-declaration-parity could not run (no sdui.manifest.json in the sibling at the pin — it is an objectui build output). Reasoned unaffected, since by its own header it compares declared prop names and filter is declared on both sides before and after — but reasoned-unaffected is not measured, and it is recorded as UNJUDGED rather than green. check:dual-build-cjs-loads is not a gap: it is hosted by the build-core job, which is green on this PR.

needs:contract-review stays on the PR and on both cards until the held round lands and this verdict is re-checked against the new head.

⛔ Correction (2026-09-10T01:55Z) — the independence claim above was mine, not a measurement

The line "Distinct sessions ⇒ ⛔ not a self-review" was wrong as written, and I am correcting it rather than leaving it to be relied on. It is fixed in place above; here is what happened and why it matters.

I asserted distinct sessions. I never measured one. The delta reviewer dispatched for the sibling PR #17249 — same mechanism, same seat — reported its own session id, and it is this seat's own: session_01MkQhmuuJAVDjmeWNixwDDH. It flagged that itself, unprompted. A subagent inherits the dispatching session's id, so this PR's reviewer and this PR's implementer share one session id, exactly as that one did.

What is actually true, stated at the strength the evidence supports:

  • Reviewer and implementer were separate isolated subagents with no shared context, at different tiers, the reviewer measuring in its own detached worktrees and writing nothing. That is the protocol's own definition of an at-tier reviewer — 「达档者 = 显式传 model 的契约复审档子代理(逐份过转录核验)」 — and this one was transcript-fused (113 harness "model":"claude-fable-5-1", zero residue, lit control).
  • ⛔ It is not the cross-seat independence the phrase "distinct sessions" claims. The stronger form is what the previous review on the sibling PR feat(spec): HookContext admits a row-invariant-in-effect rewrite by per-row previous on a predicate write, kept safe by the key-divergence refusal #17249 had: the director seat, session_017Js5kTpTtxieBjPyScgxJ3 — a different seat, a different session, not dispatched by the seat whose work it judged.

Does the verdict still stand? Yes — the protocol names an at-tier subagent as a qualifying reviewer, and the tier fuse is on record. ⛔ What does not stand is the claim of independence at the level I wrote it, and a landing decision should be read knowing which of the two kinds of review this was. Nothing else in this comment changes.

⭐ Recorded as a seat defect rather than a wording slip: an unmeasured assertion about who checked whose work is exactly the sentence a later reader has no way to falsify, and it is the same failure shape this seat has been penalising in others all shift.

⛔ Correction (2026-09-10T03:05Z) — two wording errors of mine in this comment, both found downstream

Both are fixed in place above. Recorded rather than silently edited, because a dev inherited one of them and shipped it into a migration entry.

1. F1's "since no author runs migrate meta over sys_metadata" — false, and it was mine. os migrate meta has a --stored mode whose one subject is exactly sys_metadata (packages/cli/src/commands/migrate/meta.ts, module doc "--stored: the same chain, over data at rest (#4327)", verified directly, control: authored appears 10× in the same file). What is true is narrower and is what the sentence was reaching for: the stored pass replays D2 conversions only and emits no D3 TODOs, so this entry's prescription never reaches a stored row, and with no D2 conversion the stored pass rewrites nothing for this shape.

⚠️ The damage was not confined to this comment. The held round quoted it into the D3 entry's reason, so 18.element-data-source-and-object-block-filter-rule-array.ts:61-62 (mirrored at registry.ts:6952-6953) now carries the false clause — which is why the delta review returned REWORK on a 13-word clause. ⭐ A defensible narrow reading exists ("an author cannot point --from N at a database"), and ⛔ this seat does not take it: the at-rest paragraph's audience is operators, for whom --stored is the tool, so the clause misleads precisely the reader it was written for.

2. F4's control count. 17.view-filter-rule-value-shaped-by-operator is present ×2 in packages/spec/spec-changes.json (:1006, :2085, both migrationId fields) but ×1 in docs/protocol-upgrade-guide.md (:561). I wrote "×2" of both. ⭐ The conclusion that control supported is unaffected and was re-measured on the new head: both major-18 ids are in both files with the control lit in both. The dev caught this and the delta review confirmed it.

Not corrected, because they were right: two line-number offsets the dev also reported are real and are now fixed above — migrations/types.ts SemanticMigration is :30-41, not :29-40; entry 17's at-rest passage runs :38-50, not :38-47 (the shorter span cuts mid-sentence).

⭐ The pattern worth keeping, not the apology: both errors were found by people downstream of me re-measuring what I handed them instead of building on it. That is the behaviour the dispatch orders ask for, and it is the only reason a false sentence in a shipped migration entry is being caught before it lands rather than after.


Generated by Claude Code

…e the deleted `pages` pin

Contract review findings F1 and F3 on this PR.

F1 — the D3 entry stated the sweep and the objectui route but was silent on
metadata AT REST, the one population its TODO cannot reach: no author runs
`os migrate meta` over `sys_metadata`. Measured, all three legs: a
`SemanticMigration` converts nothing by its own type (`migrations/types.ts`),
this disposition adds no D2 conversion (`packages/spec/src/conversions/` is
untouched by this branch), and `applyConversionsToStoredItem`
(`database-loader.ts:831`) replays the full chain without validating, by its
own contract. So a stored row carrying the record form keeps loading, is still
rendered by objectui at the pin, and is refused only on its next save. One
sentence added to `reason`, on the register of
`17.view-filter-rule-value-shaped-by-operator.ts:38-47`, which states the same
disposition for its own family. `registry.ts` follows by `gen:migration-registry`
— that one sentence is its whole delta.

F2 and the partial-D2 direction question are deliberately NOT in this commit:
F2 is family-wide (it also reaches `element:number` and `element:record_picker`)
and the D2 direction is the ruler's, not a dev's. The new sentence therefore
states that this disposition adds no D2 conversion, and does not claim none is
possible.

F3 — this branch had dropped the only `pages` row from the "covers ..." case
while `pages` remains in `EMPTY_COMBINATOR_SURFACES`, leaving that surface
declared and unmeasured. Restored byte-identical to the base tree, plus a
comment recording that the row authors the retired record form on purpose:
this rule never parses a fixture against the spec schema — `walkAuthoredFilters`
visits any `FILTER_KEYS` key it reaches, whatever its shape — so an author still
writing the record form is judged here and gets a located error beside the
schema refusal. Ablation: dropping `{ key: 'pages', kind: 'page' }` from
`EMPTY_COMBINATOR_SURFACES` turns the case red losing exactly `page "home"`
(21 passed -> 1 failed | 20 passed); the rule file was restored to its HEAD blob
and verified by `git hash-object`.

No generated artefact moved beyond `registry.ts`: `check:generated` reports all
15 up to date, and `spec-changes.json` / the upgrade guide correctly show no
diff because both projections fold only to the current PROTOCOL_MAJOR (17) and
this is a major-18 entry — measured against a lit control (the major-17 entry
`view-filter-rule-value-shaped-by-operator` is present in both; the neighbouring
major-18 `ui-record-blocks-unknown-keys-refused` is absent from both).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH

os-bill commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator Author

Delta review at CONTRACT_REVIEW_TIERVerdict: REWORK for one clause (adopted; ⛔ not downgraded)

Head 3d7f4eda031a0a2c70635c24811a4c1c7410ca34 · delta 6274af365 → 3d7f4eda0 (one commit, 3 files, +35/−13) · adopted by the domain:spec seat 2026-09-10T03:06Z.

  • Tier fuse — 83 harness-stamped "model":"claude-fable-5-1", zero residue, over a 564 KB transcript, control lit ("role":"assistant" = 69, "role":"user" = 43).
  • ⚠️ Seat-side at-tier review, ⛔ not cross-seat independence — the reviewer reported this seat's own session id, as a subagent inherits it. It said so itself.

⛔ The must-fix clause is mine, and it travelled

packages/spec/src/migrations/entries/semantic/18.element-data-source-and-object-block-filter-rule-array.ts:61-62 (mirrored at registry.ts:6952-6953) now says "and no author runs os migrate meta over sys_metadata". That is false.

os migrate meta has a --stored mode whose one subject is exactly sys_metadatapackages/cli/src/commands/migrate/meta.ts, module doc "--stored: the same chain, over data at rest (#4327)", plus the flag, runStored, and protocol.migrateStoredMetadata(...). I verified that directly rather than adopting it (control: authored appears 10× in the same file, so the grep speaks).

What is true, and what the sentence was reaching for, is narrower: the stored pass replays D2 conversions only and emits no D3 TODOstodos appears 7× in the authored-mode half and from runStored to EOF — so this entry's prescription never reaches a stored row, and with no D2 conversion the stored pass rewrites nothing for this shape.

The provenance matters more than the clause. The false wording originated in the standing verdict's F1 text, I adopted it, my dispatch order carried it, the dev quoted it into the entry, and it is now sitting in a file that ships. Four hands and it was still wrong at the end. My adoption comment 5611344023 is corrected in place with a dated note.

⛔ I do not take the downgrade that was offered. The reviewer noted a defensible narrow reading — a config author cannot point --from N at a database — and the seat could rule F1 non-blocking on it. No: the at-rest paragraph exists for operators, and --stored is precisely their tool, so the clause misleads exactly the reader it was written for. A sentence that is true only under a reading its own audience does not hold is not true enough to ship.

Fix: delete the clause (leaving "— a SemanticMigration converts nothing by its own type."), or replace it with "and os migrate meta --stored replays D2 conversions only, so it has nothing to rewrite here"; then gen:migration-registry. Thirteen words and a regeneration.

Everything else in the delta passes — and the reviewer reproduced rather than adopted

  • Every other claim in the new sentence checks against the tree: SemanticMigration converts nothing by its type (types.ts:30-41; the replay vector is MigrationStep.conversionIds, and step 18’s is at registry.ts:~5406 and is NOT empty — see the correction at the end); no D2 conversion is added (0 files under conversions/ in the diff, the id 0× in conversions/registry.ts, control action-execute-to-target lit at :689 as a real member); applyConversionsToStoredItem replays the full chain with includeRetired: true and never validates (conversions/stored.ts:16-31, :75-78); re-saving is refused at the filter door on the next save and not before (stored.ts:33-36 — write path validates, read path never does).
  • It does not pre-empt the ruler, which was my second question and the one I most wanted checked: the sentence is scoped"this disposition adds no D2 conversion" — versus entry 17's absolute "there is no D2 conversion", which entry 17 earns by arguing no honest coercion exists. The partial-D2 direction question stays fully open for the director seat / maintainer.
  • F3 closed, and the ablation was reproduced, ⛔ not adopted: the pages row is back byte-identical to the base tree, page "home" back in the expected list, assertion strictly stronger than the previous head (6 expected where values, not 5). Independent ablation: delete { key: 'pages', kind: 'page' } from EMPTY_COMBINATOR_SURFACES → exit 1, 1 failed / 20 passed, expected [Array(5)] to deeply equal [Array(6)] in exactly that case. The restored pin can fail, which is the only thing that makes it a measurement.
  • Scope clean — exactly 3 files; the five filter doors and conversions/ untouched in the delta. Changeset 0 diff lines; minor + **BREAKING** re-derived and still standing.
  • Nothing re-opened: F2, F4, F5 all re-checked on the new head and unchanged.

Both of the dev's corrections confirmed — one of them mine

  • The control 17.view-filter-rule-value-shaped-by-operator is ×2 in spec-changes.json (:1006, :2085) and ×1 in docs/protocol-upgrade-guide.md (:561). I wrote "×2" of both. ⭐ The conclusion it supported is unaffected and was re-measured on the new head: both major-18 ids are in both files with the control lit in both.
  • Line offsets real: types.ts SemanticMigration is :30-41; entry 17's at-rest passage is :38-50 (my :38-47 cut mid-sentence). Both fixed in the adoption comment.

Landability, on the seat's three pre-checks at this head

check reading
at-tier verdict on record for this head not satisfied — this document is REWORK for the clause above
--pair 17257 exit 0 on both pairs (#15442, #15449)
every check green, newest run per check name 32 success, 2 skipped, 0 red (34 names, each once)

② and ③ are green now; ① turns on that 13-word deletion plus gen:migration-registry, which one short round lands.

⚠️ The round is queued, not running: three dev rounds are in flight against the maintainer's cap of 3, so it goes out on the next free slot. ⛔ This PR stays draft and stays out of the queue until then — and separately, it remains held while PR #17146 sits with the maintainer, since the two share packages/spec/src/migrations/registry.ts.

needs:contract-review stays on the PR and both cards.

At ACCEPT — unchanged, plus one seat-side item

Nothing new. F2 (family-wide prescription-carrying refusal) → domain:spec seat. The partial-D2 direction question → director seat / maintainer, and ⭐ once the clause above is fixed it is left fully open rather than half-answered by a stray sentence.

⛔ Correction (2026-09-10T03:22Z) — a citation in this comment is wrong, and it is mine

The line "the replay vector is MigrationStep.conversionIds, and step 18's is [] at registry.ts:351" is false. Measured on origin/main:

  • registry.ts:351 is the conversionIds: [] of a much earlier step — its own reason says the shape it catches "was already removed at protocol 9".
  • Step 18's conversionIds is at ~:5406 and is NOT empty — it carries the major's other D2 conversions (field-malformed-scale-precision-removed, element-filter-removed, record-chatter-position-vocabulary, and more), and its neighbouring reason names RETIRED_KEYS_BY_MAJOR[18].

The conclusion it was supporting still holds, and it was re-established a different way rather than patched over: this entry's id is in packages/spec/src/conversions/registry.ts, with the control action-execute-to-target lit at 1× — so this disposition adds no D2 conversion, which is what the sentence in the entry claims.

⚠️ The wrong citation had a live consequence, which is why it is worth this note. The dispatch order built on it and suggested the replacement clause end "…so it has nothing to rewrite here". Because step 18's conversionIds is not empty, "here" reads as "the stored pass rewrites nothing at protocol 18" — false. The dev caught it and wrote "…nothing to rewrite for this shape" instead, correctly scoping it to this entry's own disposition. ⛔ That is a second sentence-level defect of exactly the kind this round existed to prevent, and it was avoided only because the dev re-derived the citation instead of trusting it.

The citation came into this comment from the verdict I adopted; I published it, so it is mine to correct.


Generated by Claude Code

…red` does exist

The entry said "no author runs `os migrate meta` over `sys_metadata`". That is false:
`os migrate meta --stored` has exactly that subject (packages/cli/src/commands/migrate/
meta.ts — module doc "`--stored`: the same chain, over data at rest", the flag, `runStored`,
`protocol.migrateStoredMetadata(...)`).

The true and narrower fact the clause was reaching for: the stored pass replays ADR-0087 D2
conversions only — `migrateStoredMetadata` converts through `applyConversionsToStoredItem`,
i.e. `applyConversions`, and emits no D3 TODOs (`todos` is 7x in the authored-mode half of
meta.ts and 0x from `runStored` to EOF) — so with no D2 conversion added by this disposition
it has nothing to rewrite for this shape.

The scoped "this disposition adds no D2 conversion" is unchanged and stays scoped: the
partial-D2 direction question is the ruler's, not this entry's.

`registry.ts` follows by `gen:migration-registry`; no other sentence of the entry moved.

Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
Co-authored-by: Claude <noreply@anthropic.com>
… protocol 18

`conversionIds` on step 18 is NOT empty — it carries the major's other D2
conversions — so "nothing to rewrite here" could be read as "the stored pass
rewrites nothing at protocol 18", which is false. The true scope is this entry's
disposition, which adds no D2 conversion of its own (its id is 0x in
`conversions/registry.ts`, control `action-execute-to-target` lit at 1x).

`registry.ts` follows by `gen:migration-registry`.

Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
Co-authored-by: Claude <noreply@anthropic.com>

os-bill commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator Author

Contract review at CONTRACT_REVIEW_TIERVerdict: PASS (adopted). Landable on all three pre-checks; ⛔ held out of the queue for one reason, stated below

Head ad7dcbc0ffaf0dc70d7d0bad05c7c7bedcc8af97 · delta 3d7f4eda0 → ad7dcbc0f (merge 448bcb99c + 816f181e1 + ad7dcbc0f) · adopted 2026-09-10T03:31Z. No must-fix. Nothing re-opened.

  • Tier fuse — 71 harness-stamped "model":"claude-fable-5-1", zero residue, over 585 KB, control lit (assistant = 59, user = 40).
  • ⚠️ Seat-side at-tier review, ⛔ not cross-seat independence — same standing as the two previous rounds, and the reviewer said so itself.

⭐ The dev's deviation from my suggested wording was not a style choice — my wording would have been false

I suggested the clause end "…so it has nothing to rewrite here". The dev wrote "…for this shape" instead and gave a reason; the review measured it, and the reason holds:

Step 18's conversionIds (registry.ts:5406-5430) carries 23 conversions. So a stored page at protocol 17 carrying, say, a retired defaultSort would be rewritten by the stored pass at protocol 18 — "nothing to rewrite here" would have been a false sentence in a shipped ledger entry. "For this shape" binds the claim to the record-form / tuple filter at these five doors, which no conversion recognises: conversions/registry.ts mentions filter only at flow.node.config.filter (:146) and analyticsCubes[].measures.<metric>.filters (:7382); step 18's element-filter-removed strips props from the retired element:filter element type, ⛔ not dataSource.filter; the entry id is under conversions/, control action-execute-to-target lit at registry.ts:689 as a real member.

⇒ Two rounds in a row on this PR, the dev caught a false sentence I had put in the order. Worth naming rather than absorbing.

What else was measured rather than adopted

  • Both legs of the replacement clause re-verified independently: --stored's subject is sys_metadata (meta.ts:162/165-166/216/248-249/472/589); the stored pass emits no D3 TODOs (todos 7× in the authored half, from runStored to EOF, control authored = 10× and the reviewer looked at what it matched). ⭐ The mechanism was traced one layer deeper than the dev did: protocol.ts contains todos|SemanticMigration|MIGRATION_STEPS|MIGRATION_CHAIN, control applyConversionsToStoredItem = 4×. No path from the stored pass reaches the D3 chain at all.
  • Delta is exactly 2 files, +8/−4 — the entry and its byte-identical regenerated mirror. validate-empty-combinators.test.ts, page.zod.ts, component.zod.ts, all of conversions/, and the changeset are byte-identical to the previous head.
  • The regeneration was verified, not trusted: the generator was run out-of-tree from a git archive copy — --self-test --check exit 0, then a full regeneration cmp-identical to the committed file. And the merge carried no hand edit: git merge-tree 3d7f4eda0 d64bcb637 recomputes to the committed merge tree. Residue checks: "no author runs" 0× and "nothing to rewrite here" 0× in both files.
  • Scoping stayed scoped"this disposition adds no D2 conversion", ⛔ not entry 17's absolute form. The partial-D2 direction question is left fully open for the director seat / maintainer.

F1 (informational, no change asked): "replays D2 conversions only" is exact for every non-flow row; for flow rows the stored pass additionally applies a schema-derived {dialect, source} envelope (protocol.ts:16352-16360, "a schema transform, not a conversion"). This entry's subject is page/component rows and the sentence is scoped, so the reading is not widened — recorded only so the next reader does not rediscover it as a defect.

F2 (fixed by this seat, ⛔ not a commit): the PR description still said "Head verified: 6274af365 (the PR head; no commit after it)", two heads stale. The round-1 record above it is accurate as history and was left standing; the false clause is corrected and a Head of record section added naming ad7dcbc0f with the current CI and gate readings. The body also carried two attribution footers (a platform append under a session-URL one); the rewrite sent none and the platform added exactly one, so it is now well-formed at 1.

Landability — green on all three

# check reading
at-tier verdict for this head PASS (seat-side at-tier)
--pair 17257 exit 0 on both pairs, from an origin/main mirror
every check green, newest run per name 32 success / 2 skipped / 0 red, 34 names, all terminal

check-governed-merges --test: the 17-path list is unchanged from the round that read NOT governed ⇒ ordinary queue landing.

⛔ Why it is still not enqueued, and how that ends

One reason only: PR #17146 is a governed PR sitting with the maintainer for a human merge, and it shares packages/spec/src/migrations/registry.ts with this branch. That file is generated but ⛔ not routed to merge=os-regen, so a clash there is an ordinary visible conflict — not a silent drop. If this PR lands first, #17146 needs another os-regen-merge.sh round before it can be merged by hand, which re-creates the state it was just repaired out of.

⇒ This seat committed to holding and to saying so before releasing. This is that notice: #17257 is PASSed, green and landable; the hold is now the only thing between it and the queue, and it is spent the moment #17146 merges. ⛔ The seat will not enqueue unilaterally — one word releases it, and the cost of releasing is one mechanical regen round on #17146, ⛔ not a re-review.

⚠️ One live risk, named rather than discovered in the queue: #17121 is an open p1 whose fix PR #17282 is open and unmerged. Test Core (4/6) is green on this head, and this diff touches packages/drivers/ — but that shard is where the signature fires, so it remains a queue-ejection risk on the merge-queue full-suite run. ⛔ Not a finding against this PR.

At ACCEPT — unchanged

F2 of the standing verdict (prescription-carrying refusal at the seven rule-array filter doors, 承接者 domain:spec seat); the partial-D2 direction question (承接者 director seat / maintainer); the objectui follow-ups from ruling 5559671308 (承接者 domain:spec seat, filed in objectui with Blocked-by:).


Generated by Claude Code

@os-bill
os-bill marked this pull request as ready for review September 10, 2026 04:15
@os-bill
os-bill added this pull request to the merge queue Sep 10, 2026
Merged via the queue into main with commit 4792049 Sep 10, 2026
45 checks passed
@os-bill
os-bill deleted the claude/issue-15442-filter-orthography-converge branch September 10, 2026 04:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment