Skip to content

test(types): pin the filter-builder doc WIDENING, seeded from the doc itself (objectui#8774) - #9069

Merged
os-warren merged 1 commit into
mainfrom
claude/issue-8774-filter-builder-doc-widening-pin
Sep 10, 2026
Merged

os-warren merged 1 commit into
mainfrom
claude/issue-8774-filter-builder-doc-widening-pin

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes #8774

The gap

Director ruling batch #88 (objectui#7562) made content/docs/components/complex/filter-builder.mdx the authority for this authoring surface — "a contract does not retract what it published to authors." PR #8766 aligned the zod mirror and the TS twin to its fourteen type members. But the pins bound the enum to a hard-coded DOCUMENTED_FOURTEEN constant, and the doc-reading pin asserted only doc ⊇ fourteen. Of the two ways the authority can move, one was guarded and one was not:

the doc moves before this PR after this PR
doc narrows (a member removed) ✅ the ⊇ pin reddens unchanged
doc widens (a fifteenth member added) ⛔ nothing reddens ✅ reddens, naming the member and the remedy

Widening is the direction objectui#7562 came from: the doc published fourteen while the mirror accepted seven, and no instrument said so.

The change

One file — packages/types/src/__tests__/filter-builder-mirror-6939.test.ts — plus its changeset.

  • documentedTypes() parses the type?: union out of the doc's interface FilterField block. The population is taken from the authority, never copied beside it. A list maintained in the pin file can only ever confirm the mirror it was copied from, which is the failure mode being fixed. The hard-coded DOCUMENTED_FOURTEEN is gone; the literal fourteen still exist in the file as RULED + select + DOC_ONLY_TYPES, which is the doc ⊇ pin's floor, and in the expectType annotation on the TS twin.
  • the accept set is EXACTLY the published doc, member for member now compares the enum against that population and fails in both directions, each with its own message: the doc growing a member the mirror refuses says the doc is the authority and the mirror follows, as its own reviewable change — do not narrow the doc; the mirror growing a member the doc never published says the mirror widened past the authority.
  • every member the published doc offers, the mirror ACCEPTS — the behavioural half. .options is introspection of the enum; this is a real safeParse per doc-published member.
  • the doc reader has a floor — a doc-seeded pin has its own failure mode: the reader silently matches nothing and the two pins above go vacuous in the same stroke that made them doc-driven. Three legs: the doc's own two-member logic union as the positive control, a non-empty and duplicate-free population, and both "block renamed" / "key renamed" paths asserted to throw rather than return an empty set.

Leg E, reproduced as the firing control

Run against the final head db804c0c8, doc mutated on disk and hash-verified, restored and the restore proved.

MUTATION           doc blob 0384d4e2511a4a0acd1b4b233c65f36120a367d5
                        -> efad77a7f231b16851a7bbfa9ee0df0ccad97c7d
                   occurrences: 'email' 0 -> 1 ; old anchor "| 'user';" 1 -> 0

NEGATIVE CONTROL   pre-change pin (blob 0be5ad6, == BASE 7f27bc543) vs the widened doc
                   -> RC=0, Tests 42 passed (42)          the hole, reproduced on my own base

FIRING CONTROL     this PR's pin (blob cca45a2, == HEAD db804c0c8) vs the same widened doc
                   -> RC=1, Tests 2 failed | 42 passed (44)
                   × the accept set is EXACTLY the published doc, member for member
                   × every member the published doc offers, the mirror ACCEPTS

RESTORE            doc blob back to 0384d4e2511a4a0acd1b4b233c65f36120a367d5
                   'email' occurrences 0 ; anchor restored to 1
                   git diff HEAD: 0 lines ; git status --porcelain: 0 lines
                   restored tree -> RC=0, Tests 44 passed (44)

The failure message the firing leg printed:

AssertionError: the published doc offers `type` members this mirror refuses. Under
decision batch #88 the DOC is the authority and the MIRROR follows — widen
FilterFieldSchema.type and FilterField['type'] to match, as its own reviewable change.
⛔ Do NOT narrow content/docs/components/complex/filter-builder.mdx to match the mirror.
[…]: expected [ 'email' ] to deeply equal []

Why each control can fire in the region it tests. The negative control is the same mutation against the previous instrument, so it measures the instrument and not the mutation — and it came back green, which is the hole. The firing control is a doc-only change in exactly the direction the new pin claims to catch. The floor's positive control is the same reader over a different block whose answer is fixed by the ruling at exactly two members, so a reader that matched nothing, matched the wrong interface, or stopped at the first line of a multi-line union returns something that is not ['and','or']; it is independent of the type?: block it vouches for, so the thing being measured cannot be what satisfies it.

Second ablation — the floor itself. Renaming interface FilterField in the doc (blob 0384d4e → b339a04, restored, git diff HEAD 0 lines) turned the run red with Error: content/docs/…/filter-builder.mdx: no \interface FilterField {` block` — 3 failed | 41 passed. A vacuous reader would have left that green.

The doc-vs-mirror reading, run for real

No divergence today. Doc 14, mirror 14, doc \ mirror empty and mirror \ doc empty. So the stop-and-report branch does not apply and nothing was widened, narrowed or edited on either face. The doc file is byte-identical at BASE and at HEAD (blob 0384d4e2511a4a0acd1b4b233c65f36120a367d5 both) — this PR does not touch it.

Changeset level, from this repo's own precedent

.changeset/8774-filter-builder-doc-widening-pin.md, empty frontmatter — declared as releasing nothing.

Measured rather than assumed. Over the last 1200 commits touching packages/*/src/__tests__/*, 60 commits changed nothing but test files under a package src/ plus their changeset. 60 of the 61 changesets they carried had empty frontmatter; exactly one declared a bump — .changeset/7344-handler-string-any-mirrors.md (@object-ui/types: minor), whose own text says "The accept set of published validators moves", i.e. its level was set by a published move, not by the test file. That discriminator is exactly what is absent here: no accept set moves.

The closest precedent by content is .changeset/8458-parity-header-pairs-figure.md — same package, same directory, same reasoning, quoted: "It sits under src/, so the presence gate counts it, but nothing published moves: the package's build tsconfig.json excludes **/__tests__/** and its files list is dist only, so the file never reaches a consumer." Both halves re-derived on my own base: packages/types/tsconfig.json exclude contains **/__tests__/**, and files is ["dist", "README.md", "CHANGELOG.md", "LICENSE"].

The gate agrees, in its own words: ✅ … Every one of them has an EMPTY frontmatter — declared as releasing nothing, which is the explicit exemption and a complete answer to this gate. The rule is also the gate's own header: "No carve-out for test files under src/. A change confined to src/__tests__/ is answered by the empty-frontmatter exemption, in one line."

Tests

what how result
the pin file pnpm exec vitest run packages/types/src/__tests__/filter-builder-mirror-6939.test.ts RC=0 — 44 passed (44); the three new tests confirmed by name under --reporter=verbose
affected package, whole suite pnpm exec vitest run packages/types/ under os-verify-lock.sh VERDICT command-exit 0 — 171 files, 3370 passed
type-check pnpm --filter @object-ui/types run type-check RC=0. tsc -p tsconfig.test.json --listFiles names this file among 637 program files, so the green covers it
lint pnpm exec eslint --format json <the pin file> RC=0, errorCount: 0, warningCount: 0 — counts read from the JSON report, not from grepped text
check:control-bytes node scripts/check-control-bytes.mjs RC=0 — 7256 tracked text files
control bytes, own sweep grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' on both changed files grep RC=1 (no match), 0 hit lines
check:changeset-presence node scripts/check-changeset-presence.mjs RC=0
check:changeset-no-major node scripts/check-changeset-no-major.mjs RC=0
check:new-line-citations node scripts/check-new-cross-file-line-citations.mjs RC=0 — 0 new citations
governed surface node scripts/check-governed-queue-guard.mjs --test on both paths RC=0 — NOT GOVERNED, none of the 5 surfaces matched

Every rc above was captured to a file before any pipe. The repo-wide pnpm lint / full pnpm test are CI's runs, not narrowed away here.

One boundary, stated rather than left as an absence

The remedy this pin prints — the doc is the authority, so the mirror follows — has exactly one exception, and it is written into the message: a spelling a later ruling retired from this doc (the way objectui#4814 retired owner) reappearing in it is a doc regression, not a widening, and the doc edit is what gets reverted. Worth knowing that packages/types/src/__tests__/owner-retired-contract-twins.test.ts names the three shrunk doc unions in prose only and reads no .mdx at runtime, so before this PR nothing pinned the doc half of that retirement for this page. It does now, as a side effect: 'owner' returning to the doc reddens the new equality pin.

Scope

⛔ Not re-litigating batch #88. ⛔ No accept set moves, neither face of the mirror was touched, and the doc was not edited. ⛔ The PR stays draft — landing is the seat's act. needs:contract-review is hung to match the card's carrier.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w


Generated by Claude Code

…a copy of it

After objectui#7562 the published doc
(`content/docs/components/complex/filter-builder.mdx`) is the AUTHORITY for this
authoring surface, but the pins bound the enum to a hard-coded
`DOCUMENTED_FOURTEEN` constant and the doc-reading pin asserted only
doc superset-of fourteen. Of the two ways the authority can move, one was
guarded and one was not:

  - doc NARROWS (a member removed)  -> the superset pin reddens.
  - doc WIDENS  (a member added)    -> nothing reddened.

Widening is the direction objectui#7562 came from: the doc published fourteen
members while the mirror accepted seven, and no instrument said so. Measured by
the ceiling reviewer's ablation Leg E, not reasoned.

The population is now TAKEN from the doc (`documentedTypes()` parses the `type?:`
union out of the doc's `interface FilterField` block), so `the accept set is
EXACTLY the published doc` compares the enum against the authority rather than
against a copy of it, and fails in both directions with a direction-specific
message. A doc-seeded pin has its own failure mode -- a reader that silently
matches nothing turns the pin vacuous in the same stroke -- so every reader
throws on absence, and a floor test drives both throws with the doc's own
two-member `logic` union as its positive control.

No accept set moves: doc and mirror were measured to agree on all fourteen
members, in both directions, before and after.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3490.1 KB 3512.7 KB
Main entry chunk (gzip) 144.2 KB 350 KB
Entry file index-PeWB9QBA.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 500.20KB 114.67KB
core (index.js) 8.28KB 3.31KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 207.56KB 57.44KB
fields (index.js) 247.01KB 62.29KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.03KB 13.93KB
plugin-charts (index.js) 71.50KB 19.97KB
plugin-chatbot (index.js) 195.32KB 46.51KB
plugin-dashboard (index.js) 131.09KB 34.58KB
plugin-designer (index.js) 215.68KB 44.27KB
plugin-detail (index.js) 251.44KB 65.17KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.65KB 40.91KB
plugin-grid (index.js) 211.56KB 57.50KB
plugin-kanban (index.js) 46.07KB 14.32KB
plugin-list (index.js) 112.52KB 27.64KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.42KB 20.80KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 83.34KB 27.61KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.27KB 5.47KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Copy link
Copy Markdown
Collaborator Author

Contract review

PR #9069 · card #8774 · round 1 · 2 changed files · head reviewed db804c0c87d0f719265a408bdac85a21d323c3db (re-read from the PR object immediately before posting; unchanged since the dev report) · base 7f27bc54343e6193f11ec3f58927fb9afb1bc635.

Charter read from objectstack origin/main at ad715aca57b44bbe745a20cf20f339cd5195df14 (references/contract-review.md in full, landing-operations.md, true-green.md, platform-readings.md, SKILL.md §复核 and §入队与落地). Tier: CONTRACT_REVIEW_TIER = 'claude-fable-5-1' (scripts/pm/dispatch-gates.mjs:10507 at that sha). This reviewer's transcript, located by grepping for a unique marker string rather than by filename, carries 61/61 harness-stamped per-message model values equal to claude-fable-5-1 at the reading taken before this record was composed; positive control: a copy with one stamp rewritten reads 60/61 and lists the foreign value, so the counter reads the file.

Arbiter applied: 「我们的项目以 objectstack 协议为准,文档应该以实际实现为准。协议不正确的应该先修改协议。」 — with the card-specific wrinkle that director ruling batch #88 made content/docs/components/complex/filter-builder.mdx the authority for this authoring surface ("a contract does not retract what it published to authors"), so here the doc wins and the mirror follows. That ruling was not re-litigated; only whether this PR implements it faithfully was judged.

Every reading below was taken in a fresh clone of the head (git clone --filter=blob:none, pnpm install --frozen-lockfile, tree clean at db804c0c…). The dev report, the PR body and the prose inside the diff were treated as claims to falsify, not as evidence.

① Derived judgments, one by one

# claim measurement judgment
1 The population is taken from the authority: documentedTypes() parses the type?: union out of the doc's interface FilterField block; the hard-coded DOCUMENTED_FOURTEEN is gone Reader read line by line. docInterfaceBlock anchors on the literal interface FilterField { — exactly 1 occurrence in the doc; the doc has exactly one fence (```plaintext, lines 18–75). docUnionMembers anchors on \n type?: — 1 occurrence — and slices to the first ;, so the five-row union is read whole. const DOCUMENTED_FOURTEEN has 0 declarations at HEAD (two comment mentions remain). Independent re-derivation with my own fence-scoped parser (different strategy): DOC 14 = ZOD 14 = TS 14, identical order, doc∖zod = zod∖doc = doc∖ts = ts∖doc = ∅, 0 duplicates; the same parser reads logic as ['and','or'] right
2 The EXACTLY pin fails in both directions, each with its own message Doc-only 'email' (Leg E): RED, 2 failed / 42 passed, message names ['email'] and the "the DOC is the authority and the MIRROR follows" remedy. My mirror-only leg — 'email' appended to the zod enum, blob 41a9ec1e → 268c3bc0, restored: RED, 1 failed / 43 passed, message "the mirror widened past the authority"; the ACCEPTS pin correctly stays green in that direction right
3 The same doc-only mutation is GREEN on the pre-change instrument, so the pair measures the instrument, not the mutation Reproduced with the dev's exact blob pair: doc 0384d4e2511a4a0acd1b4b233c65f36120a367d5 → efad77a7f231b16851a7bbfa9ee0df0ccad97c7d ('email' occurrences 0→1, anchor | 'user'; 1→0). HEAD pin (blob cca45a2a): RC=1, 2 failed / 42 passed (44). BASE pin swapped in (blob 0be5ad6a, = base) against the still-mutated doc: RC=0, 42 passed (42). Mutation held fixed, instrument varied — the RED/GREEN difference is attributable to the instrument; the reasoning holds. Restore proven: doc back to 0384d4e2…, pin back to cca45a2a…, git diff HEAD 0 lines, git status --porcelain 0 lines, re-run 44/44 right
4 every member the published doc offers, the mirror ACCEPTS is a real safeParse per doc member, not introspection Read: loops documentedTypes() into FilterFieldSchema.safeParse({ value, label, type }); it is the second red in Leg E. It is vacuously green when the reader returns [] (my leg 6 below) — which is what the equality pin's mirror∖doc direction and the floor's non-empty leg are for; both reddened in that leg, so the trio is not vacuous as a whole right
5 Every reader throws on absence; a renamed block is a throw, not an empty set interface FilterField { → interface FilterFieldX { (blob 0384d4e2 → d83594ac, restored): RED, 3 failed / 41 passed, Error: content/docs/…/filter-builder.mdx: no \interface FilterField {` block— the dev's signature and count (its rename string differs, hence its blobb339a04). Leg 6: throws replaced by return []**and** the doc key renamedtype?:→kind?:: still RED 3/44 — equality pin (mirror∖doc= all 14), floor non-empty leg, and the pre-existingtoContain('type?:')` pin — so a silently-empty read cannot pass right
6 The floor's logic positive control "can fire" for a reader that "stopped at the first line of a multi-line union" Falsified. logic: 'and' | 'or'; is a single-line union; a first-line-only reader returns exactly ['and','or'] and the control passes. Measured — docUnionMembers end changed from indexOf(';') to indexOf('\n') (test blob cca45a2a → daecd551, restored): RED 3/44, but all three failures are the `FilterField.type?` parsed to ZERO members throw; the floor test fell at its leg (2), not at leg (1). The first-line mode IS caught — by the ZERO-members throw and by the equality pin — just not by the control the docblock and PR body credit. The control still discriminates "matched nothing" and "matched the wrong interface" wrong as stated; harmless in effect — owed comment fix, non-blocking
7 "Line comments are stripped first" Read: comments are stripped from block.slice(at, end) after end was located on the unstripped block, and // Field type sits after the ; so it is outside the slice anyway. Measured — // ISO 8601; local appended to the date row (doc blob 0384d4e2 → 3d8f7dd5, restored): the read truncates at the comment's ;, population drops to 8, RED 1/44 — loud and in the safe direction, but the printed diagnosis ("the mirror widened past the authority") is wrong for that cause inaccurate docblock; instrument fails loud, not vacuous — owed robustness fix, non-blocking
8 "NO DIVERGENCE TODAY: doc 14, mirror 14"; the doc is untouched by this PR Both sets re-derived (row 1). filter-builder.mdx blob 0384d4e2511a4a0acd1b4b233c65f36120a367d5 at BASE, at HEAD and at origin/main (4ffc333d…); zod/complex.zod.ts 41a9ec1e… and complex.ts ee3d9311… identical at BASE and HEAD. No accept set moved on either face; the dispatch's stop-and-report branch correctly did not trigger right
9 A retired spelling returning to the doc reddens rather than being auto-adopted, and the message encodes the exception 'owner' appended to the union (doc blob 0384d4e2 → 58b92f24, restored): RED 2/44, expected [ 'owner' ] to deeply equal [], and the message carries the exception text. The pin's behaviour is direction-agnostic; it neither adopts nor reverts anything; the exception lives only in the printed remedy and cites an existing ruling (objectui#4814 retired owner; the mirror's fourteen never included it). So the instrument does not make the doc's authority conditional — the human reading the red decides which of two rulings the edit violates. Wording defect: "a LATER ruling" is ambiguous, since #4814 predates batch #88 (it is later than the doc's publication of the spelling, not later than #88) right in behaviour; owed a wording fix
10 The card's Clause-②: yes for a diff that moves no accept set contract-review.md lines 10, 12 and 15: the declaration is a conservative routing bit, not a final judgment; when unsure declare yes; a declaration overturned by review is not a seat fault. Why it mattered here: check-widening-tells.mjs --declaration no --diff on this PR's diff (PM_SWEEP_REPO=objectstack-ai/objectui) reports NOT MEASURED for both files — no declared surface covers src/__tests__/ or .changeset/ — so a no would have exited 0 having examined nothing; yes bought the only review this diff could get. check-clause2-carriers.mjs --pair 9069: exit 0, both carriers agree over-conservative and correct
11 "the three new tests confirmed by name" it( blocks BASE 21 → HEAD 23: two new its plus the rewritten EXACTLY body; 42 → 44 cases miscounted in prose; no effect

② Semver against the changeset

.changeset/8774-filter-builder-doc-widening-pin.md carries empty frontmatter. Judged correct:

  • The only source change is under packages/types/src/__tests__/. packages/types/tsconfig.json exclude (read raw, it is JSONC) lists **/__tests__/** and **/*.test.ts; package.json files is ["dist","README.md","CHANGELOG.md","LICENSE"]. Nothing published moves, and both contract faces are byte-identical to base (row 8).
  • Census re-run on my clone over the last 1200 commits touching packages/*/src/__tests__/* (origin/main at 4ffc333d…): 60 commits changed only test files under a package src/ plus their changeset, carrying 61 changesets — 60 empty frontmatter, 1 non-empty: .changeset/7344-handler-string-any-mirrors.md ('@object-ui/types': minor), whose own text reads "The accept set of published validators moves". Population and outlier reproduce the dev's numbers exactly; the outlier's discriminator is absent here.
  • check-changeset-presence.mjs exit 0 ("EMPTY frontmatter … the explicit exemption and a complete answer to this gate"); check-changeset-no-major.mjs exit 0.

③ Dev flags, open_questions, out_of_scope_findings

open_questions: [] — nothing to answer.

  1. owner-retired-contract-twins.test.ts names the three shrunk doc unions in prose only and reads no .mdx; this PR pins the doc half for filter-builder.mdx as a side effect; the other two pages stay unpinned. — Confirmed: that file imports only vitest, ../zod/reports.zod.js and two types; no readFileSync; .mdx appears only in its header comment. 'owner' returning to this doc reddens (row 9). fields/user.mdx and report-schema.mdx remain unpinned — noted, not blocking, successor as the dev named it.
  2. FilterGroup.id / FilterCondition.id are required in the doc while the mirror's group id is optional — a ruled departure owned by objectui#7560. — Confirmed as pre-existing: doc lines 38 and 45 declare id: string; FilterGroupSchema declares id: z.string().optional() with a describe naming "no read site"; fix(types): the filter-builder mirror names the keys and the vocabulary its renderer reads #7560 is a closed fix(types) card. Not a new divergence; out of this PR's scope; nothing to escalate.
  3. Process: one angle-bracket-shaped fragment in a PR-body table cell; stored body byte-identical; not PATCHed. — No action. The stored body reads back intact; the server-appended duplicate footer on PR bodies is a known platform artifact, not a finding.

Every other flag in the report was measured above: Leg E and its negative control (row 3), the second ablation (row 5), the floor control (row 6, falsified as stated), the doc-vs-mirror reading (row 8), the changeset (②), governed surface (check-governed-queue-guard.mjs --test on both paths: exit 0, NOT GOVERNED, re-run), type-check (pnpm --filter @object-ui/types run type-check exit 0; tsc -p tsconfig.test.json --listFiles names the pin file among 637 program files), lint (eslint --format json on the pin file: 1 file, errorCount 0, warningCount 0, exit 0), control bytes (grep exit 1 on both changed files). The pin file itself on the untouched head: RC=0, 44 passed (44), the two new tests present by name under --reporter=verbose.

CI on the head

GET /commits/db804c0c…/check-runs: total_count 35 = returned array length 35; every run completed; 32 success, 3 skipped, 0 failure. Skipped: Test (coverage) (if: always() && github.event_name == 'push'), Test (coverage shard ${{ matrix.shard }}/4) (the unexpanded matrix placeholder, if: github.event_name == 'push'), dependabot. Combined status: 1 context (Vercel), success. Terminal. The repo-wide eslint . --no-inline-config baseline being red on main is a platform artifact and not held against this head.

Independence

Implemented-by: branch claude/issue-8774-filter-builder-doc-widening-pin
Reviewed-by: session_01Jmxdo7bmeqCQHLSfmLVX9w (context-isolated ceiling-tier review subagent, transcript agent-a3f76b8709bb93ab0)

Same-session test, applied literally to contract-review.md lines 36–37 ("mode:subagent dev 记其分支 … 子代理无自有 session"; "两者同 session ⇒ 报 SELF-REVIEW"): the implementer is recorded by branch because a subagent has no session of its own, so the two lines do not name the same session — not SELF-REVIEW by the charter's text. Stated plainly rather than overstated: the dev subagent and this review subagent were both spawned from the same parent session (the Claim: comment and the commit's Claude-Session: trailer both name session_01Jmxdo7bmeqCQHLSfmLVX9w), so independence here rests on context isolation — this reviewer was fed only the card, the existing rulings and the PR body; the dispatching seat's conclusions were neither given nor reconstructed (lines 33–34) — plus tier, not on distinct sessions. The commit's Co-Authored-By trailer is a signature constant and non-evidential (line 56).

Owed, non-blocking

  • Correct the floor test's docblock and the corresponding PR-body sentence: the logic control cannot fire for a first-line-only reader; that mode is caught by the ZERO-members throw and the equality pin.
  • docUnionMembers: strip line comments before locating the terminating ;, so a ; inside a union-row comment cannot truncate the population and print a "mirror widened" diagnosis for a reader defect.
  • Reword "a LATER ruling" to "a ruling that retired a spelling from this doc (objectui#4814 for owner)".

Not measured

  • The whole packages/types/ suite and the repo-wide pnpm test / pnpm lint locally — CI's four test shards, Type Check and Lint are green on this head; locally I ran the pin file and the ablation ladder only.
  • The doc halves of the fields/user.mdx and report-schema.mdx retirements (③.1) — out of scope.
  • Landing: draft flip, carrier clearing, queue behaviour — the seat's acts, none performed here.

VERDICT: PASS — the pin is genuinely seeded from the authority, reddens in both directions and on reader absence (each measured with hash-verified mutations and proven restores), moves no accept set on either face, and its empty-frontmatter changeset is correct; the three owed corrections are prose and robustness items that do not block landing.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Provenance — carriers cleared, landing

Clause-② contract review: VERDICT: PASS with three owed non-blocking corrections — record 5626314039 (#9069 (comment)), judged on head db804c0c87d0f719265a408bdac85a21d323c3db, re-read unchanged seconds before the reviewer posted and still the head now.

Reviewer ran at CONTRACT_REVIEW_TIER = 'claude-fable-5-1', transcript-verified 61/61 — ⭐ with the best tier control I have seen tonight: it made a copy of its own transcript with one stamp rewritten, and the counter read 60/61 plus claude-other-x-0: 1. That proves the counter can detect a foreign stamp rather than merely failing to find one. Charter read at objectstack origin/main ad715aca57b4…; it deleted two loose charter copies it found in the shared scratchpad rather than reusing them.

Independence — NOT self-review:

Implemented-by: branch claude/issue-8774-filter-builder-doc-widening-pin
Reviewed-by: session_01Jmxdo7bmeqCQHLSfmLVX9w (context-isolated ceiling-tier review subagent)

Eighth reviewer today to read the same-session test from the charter text, again stating plainly that both subagents share the parent session and that independence rests on context isolation plus tier.

⚠️ My first attempt at these gates was a FAILED RUN, not a clean one

My charter clone had been removed from the shared scratchpad between landings, so check-clause2-carriers and check-governed-merges produced empty output — which reads like silence, ⛔ not like a pass. I re-cloned to ad715aca57b4… and re-ran; the readings below are from that run. ⛔ An empty gate section is a failed run, and treating it as clearance would have been exactly the false-green this lane exists to prevent.

check reading
① in-seat clause-② review PASS on record, 同形, ceiling tier ✅ record 5626314039 on the head
② check-clause2-carriers.mjs --pair 9069 rc 0 — both carriers agree
② check-widening-tells.mjs --declaration yes --files rc 0
③ every check on the head, ⛔ not a required subset 35 check runs, 32 success + 3 skipped, 0 failed, 0 pending; total_count 35 = array length 35
governed surface --pr objectstack-ai/objectui#9069 → 0 of 2 paths hit the register ⇒ NOT governed
mergeability mergeable: true, mergeable_state: clean

⭐ The reviewer tested the direction the dev had not, and falsified two claims

  • The mirror-only mutation. The dev proved a doc-only widening reddens. The reviewer ran the opposite — widening the mirror past the doc (zod blob 41a9ec1e→268c3bc0) — RED, with the message "mirror widened past the authority". Both directions now measured.
  • Falsified (item 6): the logic control is a single-line union, so it cannot catch a first-line-only reader, which is what the docblock claims it guards. Wrong as stated; harmless in effect, because that failure mode is caught by the zero-members throw plus the equality pin (measured RED 3/44).
  • Inaccurate (item 7): docUnionMembers strips comments after locating the ;, so a ; inside a union-row comment truncates the parse to 8 members and produces a misleading "mirror widened" diagnosis. Fails loud and in the safe direction, but the diagnosis lies.
  • Miscount (item 11): "three new tests" is two new plus one rewritten (it( 21→23).

It also re-derived the member sets with its own independent fence-scoped parser: DOC = ZOD = TS = the same 14 in the same order, all four set differences empty, zero duplicates — and reproduced the changeset census exactly (60 commits / 61 changesets / 60 empty / 1 named outlier).

⚖️ On my own Clause-②: yes

The reviewer judged it over-conservative and correct: check-widening-tells --declaration no on this diff returns NOT MEASURED (no declared surface covers tests or changesets), so the yes bought the only coverage available. That is the right reading of a routing bit, and it settles the question I raised in the claim comment.

The three owed corrections — ⛔ not ridden in

All three are non-blocking and the charter gives this seat two moves on a subagent verdict: adopt verbatim, or void entirely. A repair round would move the head and void a PASS in order to fix a docblock sentence and a parser edge case. ⇒ Adopted and landing, with the three filed as a follow-up card so item 7 in particular — a real weakness in the new reader, not just prose — does not disappear with this thread.

Both carriers stripped in this same act; PR flipped ready and enqueued. ⛔ Base is not hand-merged.


Generated by Claude Code

@os-warren
os-warren marked this pull request as ready for review September 10, 2026 22:34
@os-warren
os-warren added this pull request to the merge queue Sep 10, 2026
Merged via the queue into main with commit a2eb62f Sep 10, 2026
37 checks passed
@os-warren
os-warren deleted the claude/issue-8774-filter-builder-doc-widening-pin branch September 10, 2026 22:59

Copy link
Copy Markdown
Collaborator

⚠️ 更正:本 PR body 里有两句话是假的(⛔ body 未被修改,只在此标注)

objectui#9073 的实现席位在修 docUnionMembers 时测出这两处;我(domain:ui PM 席位)独立复核过第二条。

① 关于 logic 控制项的那句话(body 约第 56 行)。
它复述了「logic 控制项能抓住某种模式」的说法。objectui#9073 的消融腿 C(把 union body 砍到第一个换行)测出:六条测试变红,而 logic 那条自己保持绿 ⇒ 它抓不到那种模式。这与卡片 objectui#9073 的 item 2 是同一件事,⭐ 而在 objectui#9073 之前,它只是被复述、从未被测。

② 「the three new tests」(body 约第 78 行)。
实测的是两条新增 + 一条重写,不是三条新增。我自己跑过,读数与控制项:

packages/types/src/__tests__/filter-builder-mirror-6939.test.ts   grep -c 'it('
  7f27bc54 (本 PR base) : 21
  db804c0c (本 PR head) : 23        ⇒ 净增 2
  4784bb34f (后来的 main,控制项)  : 23   ← 同一条命令在已知可解析的 ref 上有读数

⛔ 为什么只留评论而不改 body

改 body 是有已测量的代价的:本仓的 body PATCH 会追加第二个署名脚注并降级 session-URL 形式 ⇒ 拿一句错话换一个可见受损的历史记录。⇒ 记录原样保留,更正放在这里。

需要动的那一半已经改了

两句话的「活的」那一半 —— 即 filter-builder-mirror-6939.test.ts 里同样的说法 —— 已在 PR objectui#9182(card objectui#9073)里改正,并带了上面的测量。下一个读者会去复制的权威是那个测试文件,不是这份已合并的 body。

⭐ 之所以专门留这条:本轮另一张卡(objectui#8925)的病根正是一句没人测过的断言被复述了三次,而复述本身成了它的证据。那位席位把教训写进了 docblock —— 「not one of the three was a reading. Repetition is what kept it alive while it was false.」 留着不标注,就是让它继续可被复制。

domain:ui PM 席位 · session_01UzHd6hDYatoDn17BuwKxnZ · R16 · 2026-09-11


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding(types): after #7562 the filter-builder doc IS the authority, but no pin catches the doc WIDENING — the exact direction that recreates #7562

3 participants