Skip to content

[finding] 10 governed skill fences re-declare a published @object-ui type instead of importing it — the fence-repair half of objectui#7646, which the gate PR could not touch #8335

Description

@claude

Filed by the domain:devx @ objectui execution seat from the objectui#7646 implementation run (R46). Measured on fedfa3e4a, not inherited — objectui#7646's own numbers were re-derived rather than copied.

objectui#7646 asked for the gate-design half and got it: scripts/check-skill-examples.mjs now carries a fourth assertion — a marked (os:check) fence that locally re-declares a name the built @object-ui/* surface publishes must either import that name or carry a row in a shrink-only ledger declaring it a deliberately simplified teaching copy. This card is the other half: the fence repairs themselves.

Why they are not in that PR

skills/** and .claude/skills/** are GOVERNED surfaces — agent drafts, human merges (scripts/check-governed-queue-guard.mjs, GOVERNED_SURFACES). The gate PR touches scripts/ only and every path in it comes back NOT GOVERNED. A PR that edited a fence would park for a human merge and strand the gate change with it, so today's offenders were landed as ledger rows instead. Each row is declared debt, never a green — the run prints the row count on every pass.

The population, re-derived on fedfa3e4a

Census over every ts / tsx / typescript fence in skills/, .claude/skills/ and content/docs/, matching each fence's local type NAME / interface NAME against the exported type and interface names of the workspace packages, excluding any name the same fence imports:

  • 602 fences in the corpus, 13 carry os:check.
  • 28 re-declare a name the workspace exports; 26 distinct names.
  • Split by root: skills/ 8, .claude/skills/ 2, content/docs/ 18.
  • Lit control: the census's own marked count (13) equals what check:skill-examples prints for itself on the same tree (Marked: 13 ts fence(s), Semantic phase: 13 of 13 ts fence(s) judged). The instrument was not blind.

This card owns the 10 under the two governed roots. MARKED rows are the ones the new assertion gates today and the ones carrying a ledger row.

# fence shadows published from
1 skills/objectui/guides/architecture.md:20 BaseSchema @object-ui/types
2 MARKED skills/objectui/guides/auth-permissions.md:59 AuthUser @object-ui/auth
3 skills/objectui/guides/data-integration.md:27 DataSource @object-ui/types
4 skills/objectui/guides/data-integration.md:63 QueryParams @object-ui/types
5 MARKED skills/objectui/guides/data-integration.md:81 QueryResult @object-ui/types
6 MARKED skills/objectui/guides/plugin-development.md:92 ComponentInput, ComponentInputControlType @object-ui/types
7 skills/objectui/guides/plugin-development.md:275 FieldWidgetComponentProps @object-ui/fields
8 skills/objectui/rules/protocol.md:48 BaseSchema @object-ui/types
9 .claude/skills/objectui-contributor/guides/console-development.md:154 MetadataResourceConfig @object-ui/app-shell
10 .claude/skills/objectui-contributor/guides/console-development.md:279 NavigationContextType ⚠️ not on the published surface — internal to packages/app-shell/src/context/NavigationContext.tsx, so no import exists for the reader to write and the new assertion does NOT flag it. Listed for completeness; the repair here is prose, not an import.

What a repair is, per fence — ⛔ not a sweep

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. import type { AuthUser } from '@object-ui/auth'; and delete the local copy. Drift becomes a compile error at the point it happens. Best when the fence's copy is already the whole type.
  2. Derive from it. Import the published name under an alias — import type { QueryResult as PublishedQueryResult } from '@object-ui/types'; — then define the guide's short shape from it with Pick or Omit. Keeps the fence short AND anchored, and the new assertion accepts it: a name the fence imports is not a finding under either spelling, the alias or the original.
  3. Keep the teaching copy and say so. Leave the fence, and leave its ledger row in KNOWN_SHADOWED_PUBLISHED_TYPES with the reason. ⛔ The row is uncovered debt, not a pass — the gate's summary says so on every run.

Retiring a row and editing its fence must land in the same commit: a row whose fence now imports the name fails as STALE, exactly like this gate's two older shrink-only lists.

The evidence this is worth paying for

Reproduced on fedfa3e4a with trap/restore (mutation confirmed on disk by anchored counts; restore proven by blob hash equal to the HEAD blob and an empty git diff HEAD), on fence 6:

  • Arm B — an internally CONSISTENT lie: name: number where the published required member is string, all five ADR-0049 retirement tombstones restored as writable, plus an invented frobnicate that exists on no type at all. check:skill-examples exits 0, printing Semantic phase: 13 of 13 ts fence(s) judged, 0 failed. The fence is INSIDE the judged count — the gate read it and passed it.
  • The recorded miss, kept because it is informative: an earlier arm that left inputType?: never in place and added inputType?: string went RED with TS2300 (twice) and TS2717. That is the gate catching an internally INVALID snippet, which is exactly its job, and says nothing about drift. ⛔ Anyone re-measuring must plant an internally consistent lie or they will conclude the gate works.

⛔ Not in this card's scope

  • The 18 content/docs/ fences. check:doc-snippets is opt-OUT, so those 18 ARE compiled and equally uncomparable — the blind spot spans both gates. Widening the assertion into scripts/check-doc-snippet-types.mjs is a separate gate-design card against a much larger population, and it is ⛔ not started here. It also lands inside that script's licensed strictness region, which objectui#7646's dispatch put off limits.
  • The gate itself. Landed in the objectui#7646 PR; this card changes no script.

Merge posture

⚠️ Every path this card touches is GOVERNED. Its PR is agent drafts, human merges: it parks in draft for a maintainer, ⛔ is not flipped ready by a seat, and ⛔ does not self-queue.

Back-links: objectui#7646 (the finding and the gate), objectui#7636 (the instance that paid for it first).


Generated by Claude Code

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions