You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(spec): regenerate drifted skill references + react-blocks contract, and gate them in CI (#3138)
skills/*/references/_index.md and the objectstack-ui react-blocks contract are
generated from packages/spec/src and committed, but nothing regenerated them in
CI — no workflow referenced either generator, the spec `build` script does not
run them, and neither had a `check:` mode. They drifted. Unlike the reference
docs (#3134), these ship to third parties via `npx skills add`, so the drift is
served straight to consumers' agents.
An independent oracle over the committed indexes: 6 of 113 schema pointers named
files that no longer exist — automation/workflow.zod.ts (deleted by #1398),
system/masking.zod.ts (deleted), data/dataset.zod.ts (renamed to data/seed.zod.ts
by #1620). After this change: 126 pointers, 0 broken.
Regenerating alone was not enough. SKILL_MAP still named data/dataset.zod.ts, and
the generator warned-and-skipped on the missing file, so a plain regen would have
quietly dropped seed coverage from objectstack-data — whose own SKILL.md
advertises defineSeed() authoring — and the new gate would then have locked that
in as correct. Point the map at data/seed.zod.ts and make a dangling entry a hard
failure in both modes.
Gate mechanics follow build-docs.ts (#3134): every write goes through emit(),
every wholesale-regenerated folder through manageDir(), nothing touches disk
until flush() — so write and --check share all generation logic and differ only
in disposition. Extracted to scripts/lib/generated-output.ts. manageDir() takes
an ownership predicate because a skill's references/ folder is only partially
generated: _index.md is ours, but react-blocks.md (sibling generator) and
hand-written notes must survive.
Placed in lint.yml's "TypeScript Type Check" (no paths filter + required), not
ci.yml's check-generated: that job's filter lists specific spec paths but no
schema dirs, so a PR touching src/data/** or src/ui/** never triggers it, and it
is not required either. Both gates need no build, so they run before the
workspace build and fail in seconds.
Each gate was proven to go red before being trusted (13/13): stale content,
missing file, stale untracked leftover, dangling SKILL_MAP entry, missing skill
dir, and a spec change the contract had not absorbed. The stale-leftover case is
why manageDir exists — git diff --exit-code returns 0 on it.
Follow-up: #3139 (import regex is blind to multi-line imports).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
// zod v4 may wrap the root in { $ref: '#/$defs/X', $defs: { X: {...}}}.
@@ -100,7 +105,7 @@ const contract = {
100
105
note: "Props each component accepts in kind:'react' page source. Reference blocks by their PascalCase tag. kind: data=declarative config (from the spec schema) · binding=connects to data · controlled=React state · callback=React function. These blocks are for DATA. Live data: const adapter = useAdapter(); adapter.find/findOne/create/update. STYLING (ADR-0065) — a page's source is runtime metadata, so the console's build-time Tailwind NEVER scans it: utility classNAMES silently produce no CSS. Do NOT use Tailwind className in page source. (a) Layout/chrome: inline style={} with hsl(var(--token)) theme colors — e.g. color:'hsl(var(--foreground))', background:'hsl(var(--card))', border:'1px solid hsl(var(--border))', and px/flex for layout. (b) Overlays: render <ObjectForm formType='drawer'|'modal' open onOpenChange> (a pre-styled Sheet/Dialog) — never hand-roll a fixed inset-0 backdrop.",
L.push('`React` · `useAdapter` · `data` · `variables` · `page`. Kanban/calendar/gantt/timeline/map of an object = `<ListView navigation={…} />` with the matching visualization, or `<Block type="object-kanban" …/>`.');
0 commit comments