Skip to content

tier 2 of #5043: compile the README code blocks (+ bidirectional pins for documented interface blocks) — needs a baseline decision before it can be a gate #6214

Description

@yinlianghui-tw

Filed unassigned, not claiming. Follow-up to #5043, whose triage comment explicitly held this out of that card: "Tier 2 (extract-blocks-and-compile + bidirectional pins) is explicitly OUT of this card — it needs a baseline decision first (known pre-existing reds, see the entry-price note); file a follow-up if wanted." This is that follow-up.

Tier 1 shipped in PR #6212 (scripts/check-readme-exports.mjs, pnpm check:readme-exports, README Export Check, zero repo-wide).

What tier 1 provably cannot see

Tier 1 answers exactly one question: does the imported name exist. #5043's own measurements record the class it misses — a README where every name is a real export and the types are wrong:

The two design points already measured, so nobody re-derives them

A documented interface block compiles green no matter what it says. A standalone interface GanttTask { … } in a README is a local declaration unrelated to the real type; feeding it to tsc as-is proves nothing. It needs bidirectional pins appended to the block:

declareconstreal: RealGanttTask;exportconstdocFromReal: GanttTask=real;// real -> documenteddeclareconstdoc: GanttTask;exportconstrealFromDoc: RealGanttTask=doc;// documented -> real

Measured on the old plugin-gantt README, predictions written first, both directions red as predicted: TS2741 Property 'name' is missing in type '…GanttTask' but required in type 'GanttTask' and TS2741 Property 'title' is missing …. One direction pins only half: real→doc alone misses optional keys the doc block invents; doc→real alone misses required keys the doc block drops.

And the typed-example half cannot carry it. Predicted that the old const task: GanttTask = { name: …, start: '2024-01-01' } would report name/title; measured, it reported only two TS2322: Type 'string' is not assignable to type 'Date' — a property-level assignment error short-circuits the missing-property detail. So the key-rename fact is carried only by the pin block. A tier-2 implementation that extracts typed examples without adding pins misses that whole class.

Two engineering details from the same measurement:

  • Multiple blocks in one README each write const schema = { … }, and with no imports each is a global script, so one tsc invocation reports duplicate identifiers. One export {}; per block makes it a module — scope only, no effect on the check.
  • Some blocks in this family are deliberately not valid (bare { type, tasks: …, … } property sketches, bare dependencies: [ … ] fragments). They must be skipped explicitly and listed with the reason, never dropped silently. Classify by a content signature (a regex on the block's first line), not by block index — the same machinery then runs against both the current README and a pre-fix one, whose block indices differ.

Why it is blocked on a baseline decision, not on effort

Running all 15 compilable plugin-gantt blocks under strict, after#5012 landed, still exits rc=2: two TS7006 from onTaskClick: (task) => … written inside an unannotated const schema = { … }. Those are not a missed fix — they are part of the fabricated-schema-key surface filed as #5057.

So tier 1 could require zero repo-wide (and does). Tier 2 cannot on day one: it arrives with a batch of existing reds entangled with the invented-key surface, and needs triage plus a baseline ruling (fix-then-require-zero, or land with a shrink-only ledger like #5867's) before it can gate anything.

A stated bound both tiers share

Neither tier sees authorable-JSON key surfaces. BaseSchema carries [key: string]: any (packages/types/src/base.ts:318) and its Zod mirror is .passthrough() (packages/types/src/zod/base.zod.ts:154), so TS2353 is structurally impossible on ObjectXxxSchema — measured on gantt at 82a94170c: annotate a block containing only keys adjudicated fabricated, exit=0, zero errors. Tier 2 pins only three things on that family: the type literal, missing required keys, and literals of runtime types that do not inherit BaseSchema. That third one is the real capability and it is genuine — it is what caught CalendarEvent — but the authorable key surface needs a third instrument (#5057 / #5074 are its user-visible halves).

Also worth knowing when reading tier-2 output: when one literal has both an excess property and a property-level type error, TS reports only the latter and skips the freshness check, so a naive "compile once, read all errors" pass systematically under-reports excess keys.

Grading

For PM triage. Labelled finding: it is prevention work, and its user-visible instances are filed separately. The first task on it is the baseline ruling, not code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentationdomain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repopm:blockedtooling

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions