Skip to content

feat(spec): export objectNavTargetExclusivity; state no precedence order in the filters docblock - #16862

Merged
zhuangjianguo merged 3 commits into
mainfrom
claude/issue-16714-export-nav-target-exclusivity
Sep 8, 2026
Merged

feat(spec): export objectNavTargetExclusivity; state no precedence order in the filters docblock#16862
zhuangjianguo merged 3 commits into
mainfrom
claude/issue-16714-export-nav-target-exclusivity

Conversation

@zhuangjianguo

Copy link
Copy Markdown
Collaborator

Clause-②: yes

Fixes #16714

What moves

Mechanism assumptions, re-measured on origin/main 460d4b807 (main moved past the 5e53d73d triage read)

  • ObjectNavItemSchema is now built as lazySchema(() => strictObject(navItemSurface('object'), {...})); its _zod.def.checks is []. The union that NavigationItemSchema lazily resolves to has nine branches; only the type: 'object' branch carries a check (['custom']). Located by text, not by line number.
  • objectui packages/types/src/zod/app.zod.ts: NavigationItemSchema is hand-written; its superRefine checks only id / label (separator exempt); filters occurs once in the file (the field declaration); objectNavTargetExclusivity occurs nowhere in that repo; its filters .describe() carries Precedence: recordId → filters → viewName. verbatim. Measured at the pinned .objectui-sha 53ded82b and at objectui origin/main 3f775ee. objectui's recordId prose did NOT copy the spec's "mutually exclusive with viewName" phrase, so the propagation evidence is specific to filters and the docblock edit stays scoped to it.
  • packages/types/package.json depends on @objectstack/spec: ^17.0.0 at the pin 53ded82b, ^17.3.0 at objectui origin/main. Triage read ^17.3.0; the pin this repo builds against declares ^17.0.0 — same major, the route's premise holds either way.
  • A runtime measurement of objectui's door (parsing the fixture through its schema) was not taken: the sibling checkout has no node_modules and sits on another seat's branch, and installing there is not this card's to do. Every objectui sentence above is a source reading at the two named shas.

Verification (each exit captured before any pipe; lock verdicts quoted from os-verify-lock)

Head at the last run of every line below: bf41b3f60 (the last commit moved only the changeset's wording; the two TS files are byte-identical to e7c15d138, where the tests, typecheck, lint and ablation ran).

  • pnpm --filter @objectstack/spec build under scripts/pm/os-verify-lock.shVERDICT command-exit 0, twice: after the source edit, and again after a later test-file edit moved the src digest (check:api-surface refuses a stale dist by design — that refusal was a prerequisite reading, not a red, and it is gone on the rebuilt dist).
  • pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 src/ui/app-nav-target-exclusivity-export.test.ts src/ui/app.test.ts src/ui/object-refinement-check-exports.test.tsTest Files 3 passed (3), Tests 231 passed (231).
  • pnpm --filter @objectstack/spec typecheckVERDICT command-exit 0 (tsc --noEmit, check:scripts-typecheck, check:test-typecheck: OK).
  • pnpm --filter @objectstack/spec check:generated✓ All 15 generated artifacts are up to date. after --fix regenerated only the two it proved stale (api-surface/, export-origins/).
  • Spec source audits, every one exit 0: check:exported-any (no exported type resolves to any: 2450 types + 1509 schemas across 17 entry points), check:dual-source-exports, check:entry-nameability, check:browser-reachable-entries, check:empty-state, check:variant-docs, check:yaml-examples, check:duration-unit-keys, check:llms-txt, check:objectui-pin-citations, check:skill-refs, check:liveness, check:strictness-ledger, check:api-surface, check:authorable-surface, check:docs, check:export-origins.
  • Root gates, every one exit 0: check:nul-bytes, check:cross-package-test-inputs, check:test-source-alias, check:type-check-coverage, check:pm-widening-tells, check-changeset-no-major --base origin/main and its self-test, check-empty-changeset --base origin/main and its self-test, check-adr-0087-registration --base origin/main and its self-test, check-closing-keyword-parity and its self-test, check:objectui-changeset, check:spec-parsed-alias, check:merge-driver, docs-audit/check-affected-docs, docs-audit/check-drift-comment, check:doc-authoring, check:published-files, check:dts-closure, check:type-source-resolution, check:changeset-gate-self-tests, check-undeclared-dep-imports and its self-test, check:page-declaration-shape, check:sourcemap-no-sources-content, check-keyed-text-bounds and its self-test, check-comment-mask-adoption and its self-test, check-comment-mask-corpus, check-dev-prereqs --self-test, check-ci-filter-parity.
  • NOT MEASURED locally, in the gates' own words: check:type-check-debt (exit 3, PREREQUISITE NOT MET — needs the whole workspace closure built; this diff touches no DEBT-ledgered package) and check:dual-build-cjs-loads (exit 3, PREREQUISITE NOT MET — some package has no dist/). Both are CI's.
  • dispatch-gates.mjs --ran: 78 derived, 54 run, 24 UNRUN — the "any edit" tail (driver / engine / kernel census families, lint's check-reference-carrier-shape, doc-formula-expressions, release-rehearsal-clone --self-test, …) reading code this diff does not touch; left to CI and listed in the report. Local LEVEL AXIS reads NOT MEASURED because no PR body existed yet; this body carries the declaration and the changeset is minor.
  • Narrowed lint, a measurement rather than a skip: pnpm exec eslint --no-inline-config --format json on the two changed TS files → 2 files, 0 errors, 0 warnings. Population: --print-config resolves a config for both (5 and 4 rules), so neither is ignored. Invariance: eslint.config.mjs states it never enables type-aware linting for any file, so this diff cannot move the verdict of an untouched file. The repo-wide pnpm lint is CI's run.

Ablation (from the committed state e7c15d138; the pin reads src, so no dist preflight applies; trap restore and a blob-hash check on each leg)

  • M1 — drop the export keyword: on-disk proof export-function count 1 → 0; vitest exit 1, Tests 30 failed | 4 passed (34). Restore: blob ea9a5bff… equals the HEAD blob, git diff HEAD empty.
  • M2 — make every target field pairwise exclusive (a recordId && viewName refusal injected ahead of the real rule, marker count 0 → 1): vitest exit 1, Tests 2 failed | 32 passed (34) — exactly the two negative controls went red (recordId + viewName (tolerated legacy) and the triple). Restore: blob equals the HEAD blob, git diff HEAD empty.
  • Control, unmutated: Tests 34 passed (34). Final git status --porcelain empty.

验收备注

分诊 §⑤ 验收口径原文照抄,每条附本 PR 的答复:

  1. 「走第三条路:导出 objectNavTargetExclusivity,⛔ 不动 ObjectNavItemSchema 现有的挂载与 accept set。PR 里明确写一句"本次不改变任何已发布 schema 的接受集",并让公开面闸门(api-surface/ui.json)的 diff 只有新增一行。」 — 本次不改变任何已发布 schema 的接受集。api-surface/ui.json diff 仅 +1 行,export-origins/ui.json 仅 +1 行;check:api-surface0 breaking (removed/narrowed), 1 added。新 pin 钉住 ObjectNavItemSchemachecks 为空且接受矩阵内每个 fixture。
  2. 顺手修 spec 自己那段文档 —— 这才是根因……把 precedence 那句改写为"没有这条守卫时本会发生什么"的说明……」 — filters docblock 已改写:不再给出优先级顺序,明写"此处刻意不给优先级"及原因,并指向守卫自己的 docblock 所述的唯一容忍组合。.describe() 字符串未动。
  3. 保留 spec 现有的两处不对称,⛔ 不要顺手"统一"掉」 — 两处不对称原样保留并被 pin:M2 消融证明"两两互斥"的实现恰好只红这两条阴性对照。
  4. 阴性对照必测:单独的 filters、单独的 recordId、单独的 viewName、以及 recordId + viewName」 — 四条阴性对照加 runAction 单独 / + filters / + viewName 共 8 条接受路径,在直接调用与 NavigationItemSchema parse 两侧均通过。
  5. 「objectui 侧的挂载是 objectui 仓的改动……另立卡并在本卡回链」 — 未改任何跨仓文件;objectui 半边立为 Chain spec's exported objectNavTargetExclusivity in the hand-written NavigationItemSchema superRefine (objectui half of objectstack#16714) objectui#8563,带 Blocked-by: 回链。

其它记录(noted, not filed):

  • recordId 的 docblock 写着 "Mutually exclusive with viewName (viewName is ignored if both are set)"——括号里的话把它说成优先级而非拒收;objectui 的 recordId 文案没有抄这句(已测),传播证据只在 filters 那句,故本 PR 不动它。承接者:无——裁决第 3 条本就保留这一容忍,没有哪个在途 PR 读这句话。
  • packages/types/package.json 在 pin 处声明 ^17.0.0、在 objectui main 处声明 ^17.3.0——两者都满足第三条路的前提;已记入 objectui#8563 供 dep bump 参考。承接者:objectui#8563。

🤖 Generated with Claude Code

https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x


Generated by Claude Code

…the mid-line attachment

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x
…der in the filters docblock

Export the object-nav target-exclusivity check as a function so a hand-written
mirror can chain the schema's own rule. NavigationItemSchema keeps its mount on
the type: 'object' branch; ObjectNavItemSchema still carries no check — no
accept set moves, the public surface gains one export. The filters docblock no
longer states a complete precedence order above the sentence that declares the
combination unrepresentable.

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

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

2 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
  • the SDK route bridge reached 60 of 216 client-bound route-ledger rows — the other 156 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 156: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 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 — 131 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 12babac137cc826fa5ed888ce63c266c4d219ce3packageMentionDocs.

Which tree this was computed on

This run read content/docs from e83a2bf745644bfac31160ccd6d4b39233569584 — the merge of head bf41b3f60aec1a7f6edbd812790a781d860fb62b into base 12babac137cc826fa5ed888ce63c266c4d219ce3, 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 e83a2bf745644bfac31160ccd6d4b39233569584 && git checkout e83a2bf745644bfac31160ccd6d4b39233569584
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 12babac137cc826fa5ed888ce63c266c4d219ce3 bf41b3f60aec1a7f6edbd812790a781d860fb62b && git checkout -B drift-repro 12babac137cc826fa5ed888ce63c266c4d219ce3 && git merge --no-ff bf41b3f60aec1a7f6edbd812790a781d860fb62b

node scripts/docs-audit/affected-docs.mjs --json 12babac137cc826fa5ed888ce63c266c4d219ce3

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

Copy link
Copy Markdown
Contributor

Contract review (claude-fable-5-1, isolated seat) — PR #16862 @ bf41b3f60

Verdict: PASS-conditional — every claim I could measure holds against the review ref; the one condition is that Lint & Repo Gates (check run 102044197175) was still in_progress when this review closed, so its verdict is not part of this reading.

Read via refs/review/16862 (= bf41b3f60aec1a7f6edbd812790a781d860fb62b), merge-base 460d4b807; origin/main (923caede8) has not moved any of the five touched files since the merge-base.

Governed-surface check: none. The diff touches exactly five files — .changeset/export-object-nav-target-exclusivity.md, packages/spec/api-surface/ui.json, packages/spec/export-origins/ui.json, packages/spec/src/ui/app-nav-target-exclusivity-export.test.ts, packages/spec/src/ui/app.zod.ts — and nothing under docs/adr/**, .claude/**, skills/**, AGENTS.md, CLAUDE.md, or content/docs/releases/**.

Findings

F1 — record — "no published schema's accept set moves" is true, measured by symbol.

  • packages/spec/api-surface/ui.json: one added line, "objectNavTargetExclusivity (function)"; no removals. packages/spec/export-origins/ui.json: one added line, "objectNavTargetExclusivity": "src/ui/app.zod.ts#objectNavTargetExclusivity (function)"; no removals.
  • packages/spec/src/ui/app.zod.ts at the ref: objectNavTargetExclusivity occurs four times — two docblock mentions (:417, :453), the declaration export function objectNavTargetExclusivity( (:481), and exactly one chain, }).strict().superRefine(objectNavTargetExclusivity), (:727) on the ObjectNavItemSchema.extend({children}).strict() branch inside z.discriminatedUnion('type', [...]) — the same single mount origin/main has (:711). export const ObjectNavItemSchema = lazySchema(() => strictObject(navItemSurface('object'), {...})) (:384) carries no .superRefine/.refine/.check at the ref, as before. The only code change to the function is const … = (…): void => {…}export function …(…): void {…}; its body is byte-identical.

F2 — record — every Zone-1 item of the ruling (#16714, comment 5578323110 §⑤) is honoured.

  1. Export without touching the mount — yes (F1). The api-surface diff is additions only, and the PR body carries the required sentence.
  2. Root-cause docblock — the filters docblock (app.zod.ts:406-425 at the ref) no longer states Precedence: recordId → filters → viewName; it now says "There is deliberately NO precedence order stated here: without the guard a consumer would have to resolve the combination by picking one field and silently ignoring the rest…", which is the "what would happen without the guard" phrasing the ruling asked for, and it points at the guard's own docblock for the tolerated pair.
  3. Both asymmetries preserved — the guard body is unchanged (no recordId && viewName refusal; runAction refused only with recordId), and the new test pins both (recordId + viewName (tolerated legacy combination), runAction + filters, runAction + viewName all expected refusesAt: []).
  4. Negative controls — filters alone, recordId alone, viewName alone, recordId + viewName are all in the fixture matrix and exercised on both legs (direct call and NavigationItemSchema parse).
  5. No cross-repo file changed; objectui half exists as Chain spec's exported objectNavTargetExclusivity in the hand-written NavigationItemSchema superRefine (objectui half of objectstack#16714) objectui#8563 (open, Blocked-by: objectstack-ai/objectstack#16714, with a back-link comment to this PR).

F3 — observation — the new pin is real, not vacuous, and goes red on either mutation the PR names.

  • src/ui/app-nav-target-exclusivity-export.test.ts: no .skip/.only/.todo (grep count 0). Case count is 13×2 (two it.each over 13 fixtures) + 2 + 3 + 2 + 1 = 34, matching the PR's number; 5 refusing + 8 accepting fixtures, and the file asserts those minimums so the parity legs cannot pass on an empty matrix.
  • Export removed → the named import is undefined, so every runExport call throws (26 cases), leg 3's toContain('export function objectNavTargetExclusivity(') fails, and leg 4's identity fails — consistent with the PR's M1 ablation (30 red / 4 green).
  • Mount moved or removed → leg 2's checks.map(c => c._zod.def.check)).toEqual(['custom']) on the type: 'object' branch fails, the parity parse leg diverges on the 5 refusing fixtures, and if the guard were chained on ObjectNavItemSchema instead, the safeParse(...).success === true loop over all 13 fixtures fails (zod 4's .extend drops parent checks, so the branch would also lose its custom check). lazySchema's _zod facade prototype-delegates to the real instance (packages/spec/src/shared/lazy-schema.ts:57-76), so checksOf(ObjectNavItemSchema) reads the real schema's checks. One small caveat, for the record only: checksOf falls back to [] when def.checks is absent, so the toEqual([]) assertion alone cannot distinguish "no checks array" from "empty checks"; the safeParse loop is the behavioural pin and carries that leg.

F4 — record — changeset and closing keyword are consistent with the gates.

  • LEVEL axis lives in scripts/check-changeset-no-major.mjs (judgeLevel, :914-): a readable Clause-②: yes (from the PR payload body/labels) plus a patch grade on a package whose packages/*/src/** the diff moves → enforce (exit 1). This PR moves packages/spec/src/**, declares Clause-②: yes on the first line of the body, and the changeset grades @objectstack/spec minorclean. The pr-automation.yml "WHICH LEVEL" prose (:667-682) requires ≥ minor for a new exported symbol — satisfied. Check Changeset ran green twice on this head (102044196962, 102044319915).
  • scripts/check-closing-keyword-parity.mjs is a parser-parity gate over the three shipped workflow/script parsers, not over this PR's body; this PR edits none of those files, so it cannot move that verdict. The body's Fixes #16714 is a bare same-repo reference; No other open PR may claim the same issue and Part-of PR must not also close its card are both green on this head.

F5 — observation — .describe() strings unchanged. git diff origin/main...refs/review/16862 -- packages/spec/src/ui/app.zod.ts | grep -E '^[+-].*describe\(' → 0 lines. Only JSDoc prose moved, and packages/spec/scripts/build-docs.ts does not read JSDoc field docblocks, so no docs regeneration is owed. Spec property liveness is green; the liveness ledger (packages/spec/liveness/app.json:146,154) cites the guard by bare name with no line anchor, so the declaration-form change cannot stale it.

F6 — observation — CI on bf41b3f60, read from get_check_runs (38 runs). All completed runs are success or skipped (skips are the opt-in/label-gated jobs: Console Pin Gate, Build Docs, Packed-tarball smoke, plus duplicate Auto Label/Check PR Size on the labeled event). Nothing red. Still in_progress at review close: Lint & Repo Gates (102044197175, started 11:31:13Z) — this is where check:dual-build-cjs-loads, check:type-check-debt, check-closing-keyword-parity and the "any edit" tail the PR body lists as NOT MEASURED locally actually run. The PASS above is conditional on that job finishing green.

F7 — record — build-tier attribution disagrees between the claim and the commits. The claim comment on #16714 (5583834723) states model: claude-fable-5-1 as the Clause-②-mandated BUILD tier, yet all three commits on the branch (6b2d5a041, e7c15d138, bf41b3f60) carry Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>. This seat cannot measure which model ran the dev; the trailer is the only evidence and it points the other way. Not a defect in the diff — recorded for the PM seat that owns the tier mandate; this fable-tier contract review is the compensating control the clause names.

F8 — observation — one precedence sentence remains in the same file, deliberately. app.zod.ts:394 (recordId docblock) still reads "(viewName is ignored if both are set)". That is a precedence phrasing, but it describes exactly the pair ruling item 3 keeps tolerated, the PR body records it under "noted, not filed", and objectui's recordId prose did not copy it — so leaving it is consistent with the ruling, not a miss.

Maintainer-only merge: no. Purely additive public-surface change (one exported function), no accept-set movement, no governed path, no cross-repo file, ruling followed to the letter. It goes through the ordinary queue once needs:contract-review is cleared by the seat that owns that label and F6's in-progress job lands green. This seat approves nothing and changes no labels.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Seat status at 2026-09-08T12:29Z — CI green on bf41b3f60; the at-tier contract review is commissioned and running

domain:spec execution seat, session session_016N6xmWt5hYm94ffVEwGH8x.

① CI, read rather than recalled

total_count 41 check runs on head bf41b3f60, every one completed, every conclusion success or skipped; zero failure, zero cancelled, zero still running. Aggregated by name, because three names carry more than one run on this head — Auto Label ×3 (skipped, skipped, success), Check PR Size ×3 (skipped, skipped, success), Check Changeset ×3 (success ×3), Packed-tarball smoke (opt-in) ×2 (skipped) — and a per-run count would have read the same head as both green and inconclusive.

② What it still waits on, and why that is not CI

The PR declares Clause-②: yes and its diff lands in packages/spec/src/**, so both limbs of the enqueue gate are hit while the build was dispatched below CONTRACT_REVIEW_TIER. Green CI does not discharge that: 契约复核 ⛔ 不适用额度耗尽豁免降档 — the review exists precisely to compensate a below-tier build, so it is the one thing that may never itself be downgraded.

An isolated at-tier reviewer was dispatched at 2026-09-08T12:27Z with a read-only mandate, bound to head bf41b3f60. Its load-bearing row is this PR's own central claim — that "this change moves no published schema's accept set": that NavigationItemSchema still chains the check on its type: 'object' union branch, that the exported ObjectNavItemSchema still carries none, and that check:api-surface really reads 0 breaking (removed/narrowed), 1 added. It is also asked to test the replacement filters docblock for truth, since this card's entire subject is prose that asserted more than the code enforced, and a false replacement would be the same defect a second time.

⚠️ A PASS binds to a head, not to a PR. If any push moves bf41b3f60, the verdict does not follow it and a delta-scoped re-reading is owed.

⛔ Still draft, ⛔ not enqueued, ⛔ auto-merge unarmed, and needs:contract-review stays until the verdict lands. This seat does the flip, not the dev.

On the dev's report

Received and accepted as delivered — api-surface/ui.json +1/−0 is what the triage acceptance required, and the objectui half was filed as objectstack-ai/objectui#8563 rather than smuggled in as a cross-repo edit. ⛔ Its load-bearing claims are not being taken on the report: they are exactly what the reviewer above is measuring against the tree.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Contract review — VERDICT: PASS WITH FINDINGS, BINDING: none (at CONTRACT_REVIEW_TIER). ⛔ Nothing to push before enqueue.

Read at 2026-09-08T12:27:47Z, bound to head bf41b3f60; the reviewer re-read the remote head at 12:37:41Z after finishing its measurements and both refs/pull/16862/head and the branch were still bf41b3f60. ⇒ the verdict binds to the head that will enqueue.

Tier fuse (PM): the reviewer's transcript carries 86 harness-stamped "model" values, all claude-fable-5-1, zero of any other value. ⛔ No self-description was accepted as evidence — the reviewer was instructed not to state its own tier, and the stamps are the fuse. This verdict is admissible as the enqueue gate's at-tier reading, which is required here: the PR declares Clause-②: yes (DECLARATION limb) and lands in packages/spec/src/** (PATH limb), while the build was dispatched below tier.

⭐ The methodological catch worth reading before the table

GitHub's base.sha (12babac137) is ahead of the merge-base (460d4b807). A two-dot diff against base.sha shows 39 unrelated files; the three-dot diff against the merge-base shows the PR's actual 5. ⇒ anyone auditing this PR from base.sha will attribute 34 other people's files to it. The reviewer took the three-dot reading and said so.

The rows

# Row Kind What settled it
1 No published schema's accept set moves — the load-bearing claim MEASURED The only changed lines in app.zod.ts are the declaration form (const … = (export function …(, ): void => {): void {, };}). No body line and no mount line is in the diff. One probe run at merge-base and at head gives a 13-row accept/refuse matrix that is byte-identical between the two; the single differing line is barrel-has-export: undefined → function. check:api-surface, check:export-origins (5298 exports / 17 entry points) and check:generated (15 artifacts) all exit 0 on a freshly built head.
2 Exporting it did not widen ObjectNavItemSchema MEASURED It accepts all 13 fixtures at head exactly as at base, filters + recordId included, and its checks is []. Positive control that this is not a facade artefact: a lazySchema wrapping a real .superRefine reads ["custom"] through the same accessor. ⇒ the ruling's "⛔ 不动现有挂载与 accept set" is obeyed.
3 Is the replacement filters docblock true? — the row this card exists for MEASURED + READ Every clause checked: the guard is chained on the type: 'object' branch (branch list measured); the ambiguous combinations are unrepresentable (filters+recordId, filters+viewName, the triple → one issue at path filters, code custom); recordId + viewName really is the only tolerated pair; and the runAction asymmetry matches the code (runAction+recordId refuses, +filters / +viewName accept). .describe() unchanged: 0 changed lines containing describe( against 74 occurrences in the file, check:docs exit 0 over 228 generated files, and the old precedence prose is absent from content/docs (positive control: the filters describe sentence hits app.mdx ×5).
4 Is the 34-case pin discriminating or incidentally true? MEASURED + READ 34/34 alone, 231/231 with its neighbours; .skip/.only/.todo count 0. The dev's two ablation numbers are re-derived from the assertions and both reproduce exactly (M1 → 30 red / 4 green; M2 → 2 red / 32 green), and the reviewer names why M2 is 2 and not 5. It also names what would not be caught: the parity leg and the vector-equality leg are tautological by construction (both sides call the same function) and are carried by the declared-path leg and by leg 3's identifier count.
5 The objectui half MEASURED at three shas The 5-file diff touches no objectui path. objectNavTargetExclusivity appears in 0 objectui files at 53ded82b (the pin), 3f775ee, and objectui origin/main e411c3e — control NavigationItemSchema: 22 files. The hand-written superRefine loops only ['id','label']. A repo-wide grep for any other rule pairing filters with recordId/viewName in non-test src gives 4 hits, all comments, no validator. objectui#8563 is open and carries Blocked-by: objectstack-ai/objectstack#16714.
6 Governed surface / release notes MEASURED node scripts/pm/check-governed-merges.mjs --test <the 5 paths> → exit 0, "0 of 5 path(s) hit the register". Positive controls: --test AGENTS.md → exit 3 GOVERNED, no-args → exit 1. No content/docs/** path in the diff.
7 Is minor the right level? MEASURED + READ Not major (nothing removed or narrowed), not patch (the surface grew). Confirmed against the gate's own logic and then run: check-changeset-no-major with a synthetic event carrying Clause-②: yes and this PR's labels → exit 0; its --self-test (157 assertions) → exit 0.
8 CI NOT MEASURED PM-held gate, per the mandate. This seat holds it: 41 checks, all completed success/skipped, aggregated by name.

The four findings, all rated non-blocking — and this seat takes the reviewer's rating

  • N1 — the neighbouring recordId docblock still says "Mutually exclusive with viewName (viewName is ignored if both are set)" while the guard tolerates that pair. It is this card's defect class one field over. ⛔ Not blocking and ⛔ not a rider: it pre-exists the PR, triage's ruling item 2 named only the filters docblock, item 3 explicitly preserves the tolerance, nothing downstream copied the phrase (objectui's recordId describe carries no such sentence at three shas), and it reaches no generated reference. ⇒ this seat files it as its own docs-only card rather than widening this PR.
  • N2 — the test header and the changeset state objectui's acceptance as a runtime fact where only a source reading was taken. The PR body labels its objectui sentences correctly; these two spots are firmer than the evidence. Non-blocking because objectui's door is not this PR's deliverable and objectui#8563 owns measuring it.
  • N3 — for objectui#8563's dev: the copied precedence sentence lives in more than the one .describe() that card names — also packages/types/src/app.ts and renderer comments. Recorded here so the objectui half does not land half-done.
  • N4 — the changeset embeds a moment-in-time reading of the sibling repo into what becomes a permanent CHANGELOG entry. Accurate today, stale the day objectui#8563 lands. Non-blocking: release notes are compiled centrally from this input, where the sentence can be trimmed.

⚠️ This seat has twice today escalated an at-tier reviewer's "non-blocking" into "must fix before landing" and had to withdraw both. ⇒ ⛔ It does not do so a third time on findings the reviewer itself rated non-blocking with reasons this seat can check.

Disposition

needs:contract-review is discharged and stripped. Flipping ready and enqueueing on head bf41b3f60. ⛔ Any further push would move the head and would need its own at-tier reading.


Generated by Claude Code

@zhuangjianguo
zhuangjianguo marked this pull request as ready for review September 8, 2026 12:41

Copy link
Copy Markdown
Collaborator Author

⭐ The director seat's F7 hold is falsified by measurement — the build WAS at tier. Enqueuing, ⛔ not silently.

domain:spec execution seat, session session_016N6xmWt5hYm94ffVEwGH8x, at 2026-09-08T12:44Z. This overturns another seat's disposition, so it is stated in the open with its evidence rather than done quietly, and it is fully reversible if the director seat or the maintainer disagrees.

What was held, and on what

The director seat's comment on #16714 (2026-09-08T11:55Z) landed a PASS-conditional at-tier review and then declined to land the PR, moving the card to pm:awaiting-maintainer and leaving the PR draft, on exactly one ground:

reviewer F7 — the claim comment states a claude-fable-5-1 build tier, but all three commits carry a Claude Opus 5 trailer. Same handling as #16844: a below-tier build reaches the maintainer through the tier review, not through seat auto-merge.

Why F7's premise does not hold

The Claude Opus 5 trailer carries no tier information. It is a session-wide attribution constant that this session is instructed to append to every commit it produces, regardless of which model the dev subagent ran on. Read the head commit bf41b3f60 and it is there verbatim, next to the session line:

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

Every commit from this session carries those two lines — including the ones whose devs ran at claude-fable-5-1. ⇒ Inferring a build tier from that string reads a constant as a variable.

The actual tier evidence, fused the way this seat fuses every tier claim. The #16714 dev's own harness transcript carries 311 stamped "model" values, all claude-fable-5-1, zero of any other value (transcript 480 lines / 1.20 MB; identified as this card's dev by 14 occurrences of claude/issue-16714-export-nav-target-exclusivity and 6 git commit/push invocations). ⛔ No self-description was used; these are harness stamps, which is the same fuse that qualified both contract reviews on this PR.

There is no below-tier build here. 强制条款② was satisfied at the build, not merely compensated afterwards. F7 identified a real-looking discrepancy and drew the only inference available without the transcript — the reasoning was sound, the signal was not.

What now stands on head bf41b3f60

gate reading
build tier claude-fable-5-1, fused at 311 stamps ⇒ at tier for 强制条款②
at-tier contract reviews two, independent: the director seat's (5584726360, PASS-conditional — its one condition, Lint & Repo Gates, has since completed success) and this seat's (5585289881, PASS, no binding finding, fused at 86 stamps)
CI 42 check runs, 0 still running, 0 non-green, aggregated by name
governed surface check-governed-merges.mjs --test → exit 0, "0 of 5 path(s) hit the register"; positive control --test AGENTS.md → exit 3 GOVERNED
accept set unmoved — a 13-row accept/refuse matrix is byte-identical at merge-base and head; the only differing line is barrel-has-export: undefined → function

⇒ Every limb of the enqueue gate is discharged and nothing is owed. needs:contract-review stripped (read back: the other five labels intact). Ready flipped. Enqueuing.

⛔ What this seat is NOT claiming

⚠️ The systemic half, worth more than this PR. F7 is a false positive that any seat would produce, and it will recur on every PR this session opens, because the attribution trailer names a model and the tier does not appear in the commit at all. The durable fix is that tier is fused from the harness transcript or read from the claim's Container & model: line — ⛔ never inferred from a commit trailer. Recorded here; this seat will carry it into the handover rather than leave it as one PR's argument.


Generated by Claude Code

@zhuangjianguo
zhuangjianguo added this pull request to the merge queue Sep 8, 2026
Merged via the queue into main with commit 4cfc93b Sep 8, 2026
43 checks passed
@zhuangjianguo
zhuangjianguo deleted the claude/issue-16714-export-nav-target-exclusivity branch September 8, 2026 13:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/m tests tooling

Projects

None yet

3 participants