Skip to content

docs(skills): judge each of the ten governed fences that shadowed a published type - #8427

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-8335-governed-fence-repairs
Sep 8, 2026
Merged

docs(skills): judge each of the ten governed fences that shadowed a published type#8427
os-zhuang merged 2 commits into
mainfrom
claude/issue-8335-governed-fence-repairs

Conversation

@os-musk

@os-musk os-musk commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Fixes #8335

The fence-repair half of objectui#7646. Ten governed teaching fences re-declared a name the built @object-ui surface publishes; each one gets a judgement here, not a sweep.

The ruling this executes

No maintainer ruling exists on objectui#8335. objectui#7646's triage ruling, quoted from the card body and kept in the original wording:

objectui#7646's triage ruled option 3 (blanket conversion to imports) out: a guide's fence is often a deliberately simplified view of a large published type, and a mechanical pass would make several guides less readable. So each row is one of three judgements, made by the guide's owner: 1. Import it … 2. Derive from it … 3. Keep the teaching copy and say so.

Retiring a row and editing its fence must land in the same commit: a row whose fence now imports the name fails as STALE.

Both halves are honoured: ten judgements, none of them mechanical, and every ledger row that died did so in the one commit that repaired its fence.

The ten judgements

⚠️ Type parameters are spelled as words throughout this body — GitHub's body sanitizer eats angle-bracket-shaped tokens, including inside backticks and fenced blocks, so a table written with them renders as if nothing differed.

# fence shadows judgement one-line reason
1 guides/architecture.md:20 BaseSchema keep + say so 21 members plus an index signature; this section teaches the shape, not the member list
2 MARKED guides/auth-permissions.md:59 AuthUser import the copy was titled after the whole type and was WRONG about it — it still taught roles
3 guides/data-integration.md:27 DataSource keep + say so 38 members; the excerpt is what makes the section readable
4 guides/data-integration.md:63 QueryParams import the copy was the whole type plus an index signature the published type does not have
5 MARKED guides/data-integration.md:81 QueryResult import the copy was the whole type, member for member — it bought the reader nothing
6 MARKED guides/plugin-development.md:92 ComponentInput, ComponentInputControlType import the specimen objectui#7636 paid for; wrong in four ways at once and green throughout
7 guides/plugin-development.md:275 FieldWidgetComponentProps derive seven of a much larger closed type — a Pick keeps it short AND anchored
8 rules/protocol.md:48 BaseSchema keep + say so same 21-member type; this is a SHAPE rule, and its point is which keys ride the index signature
9 objectui-contributor/guides/console-development.md:154 MetadataResourceConfig keep + say so 22 of 28 fields, all correct; a Pick of 22 keys reads worse than the excerpt
10 objectui-contributor/guides/console-development.md:279 NavigationContextType prose only not on the published surface — there is no import for a reader to write

Split, counted in FENCES: 4 import · 1 derive · 4 keep-and-say-so · 1 prose — imports are rows 2, 4, 5 and 6; keeps are rows 1, 3, 8 and 9. Row 6 is one fence carrying two names, which is why the ledger held four ROWS over three MARKED fences; it does not make row 6 two fences, and the --measure reading below agrees — four surviving keep fences.

Fence 1 — BaseSchema in architecture.md, KEEP

  • Real business need — measured: the published interface declares 21 members plus [key: string]: any. This is the protocol intro; a reader who met all 21 here learns nothing about the shape.
  • Long-term soundness — an import here would delete the section. The honest move is to label the copy and name the import, which is what landed.
  • Hard for an AI to get wrong — the addition states the trap directly: props and events are NOT declared members, they arrive through the index signature. A reader who imports the type and cannot find them now knows why instead of concluding the guide lies.
  • Startup-stage focus — zero new surface; prose only.

Fence 2 — AuthUser in auth-permissions.md, IMPORT

  • Real business need — the section is titled "AuthUser type" and then re-declared it. Measured against packages/auth/dist/types.d.ts: the copy taught roles, which the published type has not had since objectui#5424, and omitted positions and tenantId — the two members an authorization decision actually reads.
  • Long-term soundness — contract-first. AuthUser extends the spec's principal precisely so a new spec field lands the day the spec adds it; a private copy in the guide defeated that for the guide's readers.
  • Hard for an AI to get wrong — this is the strongest axis on the card. An agent reading this guide was being taught a key ADR-0090 D3 renamed away with no deprecation window. The fence is MARKED, so the import is compiled on every run: the next rename reds here.
  • Startup-stage focus — immediate retirement, no dual spelling, exactly as the 2026-08-27 ruling says. The prose states the retirement rather than carrying a compatibility note.

Fence 3 — DataSource in data-integration.md, KEEP

  • Real business need — re-measured by AST on the built .d.ts: 38 members, 6 required and 32 optional. The guide's existing prose already claimed exactly those numbers and is still exact.
  • Long-term soundness — an import would delete the one excerpt the section exists for. The added paragraph names the import and says nothing checks the copy.
  • Hard for an AI to get wrong — unchanged and already good: the guide names the two spellings that do not exist (saveView, a generic execute).
  • Startup-stage focus — prose only.

Fence 4 — QueryParams in data-integration.md, IMPORT

  • Real business need — the copy carried [key: string]: any with the comment "why an unprefixed limit type-checks — and is then dropped". Measured: the published type has NO index signature, so that claim is false; an unprefixed limit is a compile error.
  • Long-term soundness — a claim about a type's permissiveness is the last thing that should live in a copy of it. The fence now imports and shows all nine keys as a typed value.
  • Hard for an AI to get wrong — the old fence told an agent that an unprefixed key would be accepted and silently dropped. That is precisely the tolerate-then-drop shape the fourth axis forbids, and it was being taught as fact.
  • Startup-stage focus — no new surface; the fence stays the same length class.

Fence 5 — QueryResult in data-integration.md, IMPORT

  • Real business need — the copy was the published type member for member. Nothing was simplified, so nothing was bought.
  • Long-term soundness — the textbook case for judgement 1: import and delete.
  • Hard for an AI to get wrong — the fence keeps the one thing worth teaching, that the array is data and NOT records, but now as a compiled read of the imported type rather than as a comment on a copy.
  • Startup-stage focus — the fence shrank.

Fence 6 — ComponentInput and ComponentInputControlType in plugin-development.md, IMPORT

  • Real business need — measured against the built surface, the copy was wrong four ways: label, defaultValue and advanced were declared WRITABLE where all three are never retirement tombstones, and of was missing entirely. Its own comment said "five tombstones" where the type carries eight.
  • Long-term soundness — this is the fence objectui#7636 paid for. A contract type documented by copy is a second source of truth by construction.
  • Hard for an AI to get wrong — the decisive axis. An agent authoring a component manifest from this fence would write three keys the compiler and the Zod mirror both refuse. The fence now imports, shows a manifest that compiles, and keeps the tombstone list as a Pick so a tombstone deleted upstream stops compiling here.
  • Startup-stage focus — no vocabulary widened; the control-type union is read from the published one instead of retyped.

Fence 7 — FieldWidgetComponentProps in plugin-development.md, DERIVE

  • Real business need — measured: 16 named members plus two intersections. A widget author writes seven of them; the guide's existing prose already names the closed remainder correctly, member for member.
  • Long-term soundness — judgement 2 exactly as the ruling describes it: short AND anchored. Key names are checked against the published type and the value types come from it, so only a rename can rot this, and it rots in the reader's editor.
  • Hard for an AI to get wrong — every original per-key comment is preserved, and the added sentence says a widget signature normally names the whole type, so nobody copies the Pick where the real type belongs.
  • Startup-stage focus — the fence is the same length.

Fence 8 — BaseSchema in rules/protocol.md, KEEP

  • Real business need — same 21-member type; this file states the node-shape RULE, and the excerpt is the rule.
  • Long-term soundness — importing would leave a rule with nothing to point at.
  • Hard for an AI to get wrong — the addition names why properties, props and events are absent from the published member list, which is exactly what the two rules underneath it are about.
  • Startup-stage focus — prose only.

Fence 9 — MetadataResourceConfig in console-development.md, KEEP

  • Real business need — AST-counted on the built .d.ts: 28 members, of which the fence shows 22, every one correctly spelled and typed. This is an internal contributor guide whose reader has the repo open.
  • Long-term soundness — a Pick of 22 keys is longer and less informative than the excerpt, and would drop the shapes (listColumns, createDerive) that make the section usable.
  • Hard for an AI to get wrong — the stale "~30-field" estimate is replaced with the measured 28, and the six fields not shown are now named so the reader knows the excerpt's exact boundary.
  • Startup-stage focus — prose only.

Fence 10 — NavigationContextType in console-development.md, PROSE

  • Real business need — measured twice: absent from the gate's published-name inventory, and @object-ui/app-shell's exports map has only . and ./styles.css, with the entry re-exporting useNavigationContext but not this type.
  • Long-term soundness — there is no import to write, so neither judgement 1 nor 2 exists here. Saying where it lives is the whole repair, as the card's triage required.
  • Hard for an AI to get wrong — the addition stops a reader from trying to import a name no consumer can reach.
  • Startup-stage focus — nothing published, nothing added.

Ledger diff

Both shrink-only ledgers in scripts/check-skill-examples.mjs shrink to EMPTY, in the same commit as the fences they described. ⛔ No assertion, threshold or list shape changed — the self-test's 59 cases, including the shadowing ledger driven in both directions, pass over the empty ledger.

KNOWN_SHADOWED_PUBLISHED_TYPES: 4 rows retired, 0 kept, 0 added.

row why it went
auth-permissions.md:59 AuthUser fence 2 imports it now
data-integration.md:81 QueryResult fence 5 imports it now
plugin-development.md:92 ComponentInput fence 6 imports it now
plugin-development.md:92 ComponentInputControlType fence 6 imports it now

No row is kept, so no row's reason needed rewriting, and after this PR no row anywhere in the gate still names objectui#8335 as its owner — verified by grep, 0 hits.

KNOWN_BARE_ANY_EXAMPLES: 1 row retired — and this is the one deviation from the dispatch's file surface, declared rather than slipped in.

The dispatch authorised touching the gate ONLY to retire a KNOWN_SHADOWED_PUBLISHED_TYPES row. Repairing fence 6 also kills the gate's last bare-any row, plugin-development.md:92 property defaultValue, because the any it names lived in the copy that fence 6 no longer keeps. A stale row on a shrink-only list is a hard red (state.bareAnyStale.length above zero is in the run's failure condition), so leaving it is not an option, and the same list's own header records the same operation being done for the same reason on three earlier rows: "the rows went in the same commit as the guide edit, because a row whose red is gone fails as STALE".

Its stated reason had also gone out of date underneath it. The row said the guide was FAITHFUL prose because ComponentInput.defaultValue "really is any" in the platform type. It is not: the platform type carries it as an ADR-0049 retirement tombstone. So the row's own rationale had inverted — the guide's any was rot after all, and the honest fix WAS to the guide.

The five premises, as measured

# premise verdict reading
1 the ten fences are where the card's table says, and each opens a typescript fence holds all ten cited lines print the fence opener verbatim on b38014e8; each local declaration read and matched against the built .d.ts
1b row 10's name is not on the published surface holds, twice absent from the gate's own inventory under --measure, and @object-ui/app-shell's exports map has no path that reaches it
2 the gate carries three ledger rows holds in substance, refined in arithmetic it carries four rows over three marked fences — fence 6 shadows two names, and the gate's own summary prints 4 row(s). Every row reads "repair owned by objectui#8335". Nothing else about the premise moved.
3 one of three judgements per fence, argued on four axes executed 4 import / 1 derive / 4 keep, plus one prose repair — ten fences, each argued above
4 the gates hold before and after holds table below
5 no open PR on these roots holds re-read at 15:5xZ: 14 open PRs, none on skills/ or .claude/skills/. #8384's file list read directly — content/docs/guide/schema-overview.md, content/docs/utilities/data-objectstack.mdx, lychee.toml only.

A separate re-derivation worth recording: the gate's own --measure on b38014e8 prints 10 re-declarations across 9 fences for the two governed roots — the card's eleven named types minus NavigationContextType, which the built-surface oracle correctly does not carry. The card's census summary said "skills/ 8 · .claude/skills/ 2", which counts its own table ROWS rather than declaration/name pairs.

Gates

Every command below was run in this worktree; heavy runs went through the container's shared verify lock. The verdict line quoted is the gate's own, never a bare exit code.

gate before after
pnpm check:skill-examples exit 0 exit 0 — "Every marked skill example holds up against the built types."
pnpm check:skill-eval-tokens exit 0 — "Every must_contain token is taught by its own skill bundle."
pnpm check:skills-paths exit 0 — "OK (88/89 stated path(s) resolve across 20 guide file(s); 1 baselined)."
pnpm check:control-bytes exit 0 — "OK (scanned 6650 tracked text file(s); skipped 85 binary)."
node scripts/check-skill-examples.mjs --self-test exit 0 — "59 cases pass … the shadowing ledger in both directions"
pnpm exec vitest run on the three skills gate suites exit 0 — "Test Files 3 passed (3) · Tests 184 passed (184)"
pnpm type-check:scripts exit 0
pnpm lint:root exit 0 — "32 problems (0 errors, 32 warnings)", all pre-existing and none in a changed file
node scripts/check-changeset-presence.mjs exit 0 — see below
node scripts/check-governed-queue-guard.mjs --test on all 7 touched paths exit 3 GOVERNED — "6 of 7 path(s) are on a governed surface"

The three check:skill-examples counts, before and after

count before after
Marked: 13 ts fence(s) (floor 13), 70 json fence(s) (floor 70) unchanged — 13 ts (floor 13), 70 json (floor 70)
Semantic phase: 13 of 13 ts fence(s) judged, 0 failed 13 of 13 ts fence(s) judged, 0 failed
Shadowed types: ledger rows 4 re-declaration(s) across 3 selected fence(s); 4 declared; ledger 4 row(s) 0 re-declaration(s) across 0 selected fence(s); ledger 0 row(s); 0 undeclared, 0 stale

Bare any: moved with them — before: 1 finding, 1 declared, ledger 1 row. After: 0 findings, ledger 0 rows, 0 stale.

⛔ No new fence was marked. The marked population is byte-for-byte the same 13, and the floor is untouched — growing the judged population is a gate-design decision this card puts out of scope, and the dispatch names it as a thing not to do for its own sake.

The unmarked repairs were still compiled, without marking them

--measure judges every candidate fence, marked or not, so it verifies the two unmarked fences that now assert an import. Per-fence verdicts after the repair:

  • auth-permissions.md:59 — marked — pass (shadow note gone)
  • data-integration.md:68 (QueryParams) — unmarked — pass
  • data-integration.md:98 (QueryResult) — marked — pass
  • plugin-development.md:92 (ComponentInput) — marked — pass
  • plugin-development.md:296 (FieldWidgetComponentProps derive) — unmarked — pass

The whole-corpus reading moved with them: Starting population — ts: 15/121 pass before, 17/121 pass after; the shadowing would-be population fell from 10 to 4, and the four survivors are exactly the four keep-the-teaching-copy fences, all unmarked and all ungated.

Lint narrowing, with its three evidences

pnpm lint here is turbo run lint over the packages, and this diff touches no package. The population that does contain a changed file is pnpm lint:root, and it was run in full — exit 0, no narrowing claimed for it. For the six markdown files:

  1. Population, read from the config and not guessed — every rule-bearing block in eslint.config.js is scoped **/*.{ts,tsx} or narrower, and eslint's own --format json labels all six markdown files "File ignored because no matching configuration was supplied".
  2. Count from --format json on the changed files — 7 paths in, 6 outside the population, 1 (scripts/check-skill-examples.mjs) inside it and clean at 0 errors / 0 warnings; process exit 0.
  3. Invariance over untouched fileseslint.config.js enables no type-aware linting (zero occurrences of projectService, parserOptions.project or a project key), so no file's verdict can depend on another file's contents, and a diff confined to these seven paths cannot move any untouched file's result.

Changeset

Applied rule: objectui's changeset obligation is triggered by published executable source of a release-group package, or by a move in one of the eight publish-contract manifest fields. This diff has neither — six markdown guides and one repo-level gate script. node scripts/check-changeset-presence.mjs is the authority and says so in its own words: "Compared the working tree with b38014e (merge-base with origin/main): 7 file(s) changed, 0 of them published source of a package the release covers, 0 of them a manifest whose published contract moved, 0 under a package changesets ignores, 0 changeset(s) added. No source or published contract of a released package changed in this range, so no changeset is owed." ⛔ No skip-changeset label applied: in this repository that label reads nothing and exempts nothing, and the gate's verdict line is the authority.

Skills size readings

Two readings, as the dev contract requires for a diff touching the published skills/ tree. Lines, since this repository defines no token count for skills.

reading before after net
published bundle — every markdown file under skills/ 4500 4584 +84 (+1.9%)
skills/objectui/SKILL.md 137 137 0
guides/architecture.md 94 104 +10
guides/auth-permissions.md 348 362 +14
guides/data-integration.md 451 474 +23
guides/plugin-development.md 453 481 +28
rules/protocol.md 332 341 +9
.claude/skills/objectui-contributor/guides/console-development.md (internal tree, not the published bundle) 505 518 +13

The growth is the deliverable rather than overhead: four of the ten judgements ARE "keep the teaching copy and say so", and saying so is prose. The four imports and the one derive also had to move the member facts their deleted copies carried into prose next to the fence. No mechanical ceiling exists for this tree in this repository — there is no skills size or token ratchet script — so nothing was blown and none was raised.

Serial

git fetch origin main && git merge-tree --write-tree --name-only origin/main HEAD at the head of this branch, read twice as main moved during the run:

  • against origin/main at fadf6cd7: exit 0, a single tree OID (a2c28c7d), no conflicted-path list.
  • re-read immediately before opening this PR, against origin/main at 0203a29e: exit 0, a single tree OID (06b980b3), no conflicted-path list.

Branch point was b38014e8. Seventeen commits landed on main in between and none touches a path under skills/, .claude/skills/ or scripts/check-skill-*, so there is no semantic overlap either. One of them is a breaking change to a package this PR's fences import (cb847fd6, dropping the Cloud namespace re-export from @object-ui/types' root entry), so every name imported by a repaired fence was re-checked against that tree: QueryParams, QueryResult, ComponentInput and ComponentInputControlType are still exported from packages/types/src, AuthUser from packages/auth/src/types.ts, and FieldWidgetComponentProps from packages/fields/src/widgets/types.ts. No fold was needed and none was done; the merge queue rebuilds on main regardless.

验收备注

  • filed as objectui#8393 — the PermissionProvider example in the same guide passes userRoles from user?.roles, a member the published AuthUser no longer declares. Measured with a probe against the built .d.ts: the guide's line is error TS2740 and the positions control line is clean. ⛔ Deliberately NOT repaired here: it is a fence that READS a retired member, not one that re-declares a published type, so it is a different defect class and outside this card's ten. 承接者:objectui#8393 itself, in the skills lane.
  • noted, not filed — an import inside an UNMARKED fence is not compiled by anything in this repository, so the two unmarked repairs that now assert an import (fence 4's import and fence 7's derive) are verified today but not held tomorrow. Marking them would fix that and would cost nothing mechanically (the floor is a minimum, so more marks is green), but it grows the judged population, which is a gate-design decision this card puts out of scope. The gate's own header already states this bound in its "coverage this cannot have" note. 承接者:无。
  • noted, not fileddata-integration.md's DataSource prose claims "6 required, 32 optional" and it is still exactly right; re-measured by AST on the built .d.ts (38 members total). Recorded so the next reader does not re-derive it.

维护者速读(草稿)

改了什么
六个技能指南文件里的十处代码围栏,每一处都单独判了一次:四处改成直接 import 已发布的类型(第 2、4、5、6 处)、一处用 Pick 从真类型派生(第 7 处)、四处保留简化教学副本(第 1、3、8、9 处)并在正文里写明"这是简化副本、真类型在哪、没有任何东西在校验它"、一处(NavigationContextType)只改正文,因为那个类型压根没有对外发布、读者写不出 import。同一个提交里,门禁脚本的两张"只准缩"的欠账表都清空了 —— 表里的行描述的就是被修好的那几处围栏,行活得比围栏久就会以 STALE 报红。门禁的断言、阈值、表的形状都没动。

为什么改
这些围栏自己抄了一份平台已经发布的类型,抄完就再也不会跟着变。实测出来的后果不是理论上的:AuthUser 那份副本还在教 roles 这个键,而它在 2026-08-22 的裁决里就被改名成 positions 了;ComponentInput 那份把 labeldefaultValueadvanced 三个键写成"可写",而真类型里这三个都是 never 墓碑 —— 也就是说,照着这份指南写组件清单的 AI,会写出编译器和 Zod 都会当场拒收的键;QueryParams 那份凭空多了一个索引签名,还配了一句"所以不带前缀的 limit 能通过类型检查"的解释,而真类型没有索引签名,那句话是反的。这三处都是"文档看着绿、其实一直在教错"的形态。

风险与代价(含回滚)
风险很低:只动文档与一个仓库级门禁脚本,没有任何一个发布包的源码被碰,check-changeset-presence 自己判定"不欠 changeset"。三处 MARKED 围栏现在真的会被编译,门禁跑绿;两处未标记的围栏也用 --measure 单独验过、都是 pass。代价是已发布技能包净增 84 行(4500 → 4584,+1.9%),这部分增长本身就是裁决要求的"保留副本就要说明白"。回滚成本等于零:git revert 一个提交即可,不涉及任何数据迁移、不涉及任何运行时行为。唯一需要您知道的偏差:派发单只授权我为了退掉"影子类型"那张表去动门禁脚本,但修好第 6 处围栏会连带让另一张表(bare any)的最后一行变成 STALE、直接把门禁打红,所以那一行也在同一个提交里退了 —— 而且它自己写的理由已经过期了(它说平台类型里 defaultValue "确实是 any",实际上早就是墓碑)。

席位意见

你要做的
这是受管面(skills/**.claude/**),按规矩停在 draft 等您合并,我不会翻 ready、不会入队、不会自己批准。请看一眼上面十条判断里"保留教学副本"的那四条(第 1、3、8、9 处)是不是您想要的取舍 —— 如果同意,回一个词就行:合并


Drafted by Claude Code; this pull request's session is https://claude.ai/code/session_018dxq7YqsLDMeZDZ5AzsgJX. Attribution is prose with the session URL as a code span rather than a footer block, because a body edit through the API does not carry a footer block through.

…ublished type

Five fences stop keeping a private copy of a published `@object-ui` type, four
keep a deliberately simplified teaching copy and now say so, and one is a prose
repair for a type that is not on the published surface at all. Per-fence
judgement, never a sweep: a guide's fence is often a short view of a large type,
and converting those wholesale would make several guides worse.

Import (the copy was already the whole type, or was wrong about it):
  auth-permissions.md   AuthUser      — the copy still taught the `roles` member
                                        ADR-0090 D3 renamed to `positions`
  data-integration.md   QueryParams   — the copy invented an index signature the
                                        published type does not have
  data-integration.md   QueryResult   — the copy was the whole type, member for
                                        member
  plugin-development.md ComponentInput, ComponentInputControlType
                                      — the copy declared `label`,
                                        `defaultValue` and `advanced` writable
                                        where all three are `never` tombstones,
                                        and carried no `of`

Derive under `Pick` (a short shape anchored to the real one):
  plugin-development.md FieldWidgetComponentProps — the seven props an author
                                        writes, out of a much larger closed type

Keep the teaching copy, and say so (published type too large to open with):
  architecture.md       BaseSchema (21 members + index signature)
  protocol.md           BaseSchema
  data-integration.md   DataSource (38 members)
  console-development.md MetadataResourceConfig (28 members)

Prose only, no import exists to write:
  console-development.md NavigationContextType — internal to `packages/app-shell`
                                        and not re-exported from its entry

Both shrink-only ledgers in `scripts/check-skill-examples.mjs` shrink to empty
in this same commit, because a row that outlives its fence fails as STALE:
the four `KNOWN_SHADOWED_PUBLISHED_TYPES` rows whose three fences now import,
and the last `KNOWN_BARE_ANY_EXAMPLES` row, whose `any` went with the copy that
carried it. No assertion, threshold or list shape changed.

Claude-Session: https://claude.ai/code/session_018dxq7YqsLDMeZDZ5AzsgJX
… said 28

The paragraph above the MetadataResourceConfig fence gave two different totals
for the same interface in the same breath: "the 22 fields you reach for most out
of the 28 the interface declares ... registry.ts documents all 27 field by
field", with six unshown fields named (22 + 6 = 28). The 27 was a stale number
from an earlier count, left behind when the AST measurement corrected it.

Re-measured on this branch: the interface has 28 members and registry.ts carries
a doc comment on every one of them, so "all 28 field by field" is the reading.
Verified by walking the members with the TypeScript AST and checking each one's
leading trivia — 28 documented, 0 undocumented.

Claude-Session: https://claude.ai/code/session_018dxq7YqsLDMeZDZ5AzsgJX

os-musk commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator Author

ACCEPT — flight M, PR #8427 at head 3fd0e3737 (objectui#8335, the fence-repair half of objectui#7646), contract-tier review on the seat's own readings after patch round 1, 2026-09-07T22:48Z.

Own readings (review worktree detached at 3fd0e3737, origin/main = e5a28b910, merge base b38014e82):

  • Diff: 7 files, +190/−95, two commits; the patch commit is exactly one line (all 27all 28 above fence 9) and 5572f48b is its ancestor — no history rewritten. No control bytes in any touched file. git merge-tree --write-tree --name-only origin/main HEAD exit 0, tree d5b86b258, no conflicted path.
  • Every fence diff read: fences 2, 4, 5, 6 import (with example values, and fence 6 keeps the tombstone list as a Pick so an upstream deletion stops compiling); fence 7 derives with Pick; fences 1, 3, 8, 9 keep the copy and say so with the measured member counts and the import spelling beside them; fence 10 is prose naming the type as internal. The imported names exist at source level in the packages the fences name (packages/auth/src/types.ts, packages/types/src/data.ts, packages/types/src/base.ts, packages/fields/src/widgets/types.ts); NavigationContextType is declared only in packages/app-shell/src/context/NavigationContext.tsx, and @object-ui/app-shell's exports map carries . and ./styles.css only.
  • Gate-script diff is ledger-only: both shrink-only lists go to empty, header prose updated, no assertion, threshold or list shape touched. The stranded KNOWN_BARE_ANY_EXAMPLES row is the one declared deviation from the brief's file surface; a stale row is a hard red in the run's failure condition, so retiring it in the same commit is the only landing that keeps the gate green — accepted.
  • Gates: node scripts/check-governed-queue-guard.mjs --test over the 7 paths exit 3 (6 of 7 governed); check:skill-eval-tokens and check:skills-paths green in the seat's worktree. The gate's own --self-test and check:skill-examples need the built closure, which the seat's review worktree does not carry — CI is the reading there: Skill Example Check success at 22:43:25Z on this head, Lint and Type Check success; at 22:47Z 25 of 29 check runs completed success/skipped, four test shards in progress. Not a landing condition on this surface (human hand-merge is the review record).
  • Premise 2's arithmetic (four rows over three marked fences, not three rows) is the dev's refinement of the seat's brief and is right by the gate's own summary line.

Review checklist:

  • ① Derived judgments: the four keeps are argued on the published member counts (21, 38, 21, 28) and each now says nothing checks the copy — the reader gets the trap named instead of a false promise; the three imports that replaced FALSE copies (roles, three writable tombstones, an invented index signature) are the substantive result and are correct against the sources read here. No fence was marked to grow the judged count. The commit-trailer question the dev raised (model-named Co-Authored-By vs the dispatch's no-model-identifier rule): resolved as A — the trailer stays absent, the session URL trailer carries traceability, and pushed history is never rewritten to change it.
  • ② semver: none — six guides and a repo-level gate's ledger rows; check-changeset-presence reads "no changeset is owed" and this repo's skip-changeset label reads nothing, so none was applied.
  • ③ Boundary flags: governed surface (skills/**, .claude/** in objectui). Draft kept; not readied, not enqueued, no auto-merge; reviewers os-zhuang + hotlong requested and needs-user-decision set on the PR in this same wake. Out-of-scope: objectui#8393 filed by the dev (the PermissionProvider example reads the retired roles), in this lane's queue for the next shift; the unmarked-import coverage bound is the gate's own documented limit — 承接者:无.

Implemented-by: os-dev subagent a8f5fd0ad43c2ed13 (container & tier as recorded on the claim 5572936279 on objectui#8335; resumed once after a model-window 429, state re-read from the tree)
Reviewed-by: domain:skills seat, session session_018dxq7YqsLDMeZDZ5AzsgJX, at CONTRACT_REVIEW_TIER (own get_session reading: session_context.model = last_served_model)

维护者速读

改了什么: 六个技能指南里的十处代码围栏逐处判定,不是扫荡:四处改成直接 import 已发布类型(第 2、4、5、6 处),一处用 Pick 从真类型派生(第 7 处),四处保留简化教学副本并在正文写明「这是简化副本、真类型在哪、没有东西校验它」(第 1、3、8、9 处),一处只改正文,因为 NavigationContextType 根本没有对外发布(第 10 处)。门禁脚本里两张「只准缩」的欠账表同一提交清空——表里的行就是被修好的那几处围栏。断言、阈值、表的形状都没动。

为什么改: 三份副本在教错:AuthUser 还在教 8-22 就已改名为 positionsroles;ComponentInputlabeldefaultValueadvanced 写成可写,真类型里三个都是 never 墓碑——照着写组件清单的 AI 会写出编译器和 Zod 都拒收的键;QueryParams 凭空多了一个索引签名,还配了一句「所以不带前缀的 limit 能过类型检查」,真类型没有索引签名,那句是反的。绿着教错,正是副本的代价。

风险与代价(含回滚): 只动文档与一个仓库级门禁脚本的欠账行,不碰任何发布包源码,changeset 门禁自判「不欠」。三处 MARKED 围栏现在真会被编译,门禁绿;已发布技能包净增 84 行(+1.9%),增的就是「保留副本就要说明白」那些话。一处申报的偏差:修第 6 处围栏连带让另一张表(bare any)的最后一行过期,留着会把门禁打红,所以同提交退掉了——它自己写的理由也早已反转。回滚 = revert 两个提交,零数据、零运行时影响。

席位意见: 复核通过,四处「保留副本」的取舍席位认可(各自的成员数与理由都在正文表里);建议合并。

你要做的: 手动合并本 PR(受管面,⛔ 不走队列、不挂 auto-merge);合并即关 objectui#8335。是否合并?


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review September 8, 2026 00:38
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 8, 2026
Merged via the queue into main with commit af212e6 Sep 8, 2026
31 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-8335-governed-fence-repairs branch September 8, 2026 00:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

3 participants