Skip to content

Commit 3ee2fbc

Browse files
committed
Revert "skills(cutover): move the objectui skill bundle into the root skills/ catalog, and point its readers at it (decision C, objectstack#25803)"
This reverts commit 19c9bce. Decision C stands, its timing moves: inside the root catalog the bundle meets three ratchets the cutover cannot pay for tonight without touching Tier H surfaces beyond the import itself -- the published-skills token ratchet (27 unpriced authored files, SKILL.md ceiling count 10 vs 11), the reserved-word "role" docs ratchet (38 occurrences across 7 files, ADR-0090 D3) and the ci-filter-parity self-test's skills/** input count. The move lands as its own card after the import, with the ceilings, the wording and the counts paid in that PR. Until then ui/skills/objectui stays where objectui kept it; the next commit hides it from the root skills CLI so the skills-catalog boundary (create-objectstack) holds. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RSDL3SFtJaKzcymvCD8RsU
1 parent cba73d0 commit 3ee2fbc

46 files changed

Lines changed: 69 additions & 214 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎packages/spec/scripts/check-skill-examples.ts‎

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -415,18 +415,7 @@ const DOCS_DIR = path.resolve(REPO_ROOT, 'content/docs');
415415
const SDK_DOCS_PAGES = ['api/client-sdk.mdx', 'kernel/runtime-services/data-service.mdx'];
416416

417417
const SKILLS_DOCS_ROOTS: SourceRoot[] = [
418-
{
419-
dir: path.resolve(REPO_ROOT, 'skills'),
420-
ext: '.md',
421-
label: 'skills',
422-
marker: '<!-- os:check -->',
423-
// The objectui bundle joined this catalog at the cutover (objectstack#25803)
424-
// with its own reading of the same marker: it marks `json` fences as well as
425-
// `ts`/`tsx`, and compiles them against `@object-ui/*`. Its gate is
426-
// `ui/scripts/check-skill-examples.mjs`, which still judges every marked fence
427-
// in it; read here, its 70 `json` markers are orphans by this gate's rule.
428-
exclude: [path.resolve(REPO_ROOT, 'skills/objectui')],
429-
},
418+
{ dir: path.resolve(REPO_ROOT, 'skills'), ext: '.md', label: 'skills', marker: '<!-- os:check -->' },
430419
{
431420
dir: DOCS_DIR,
432421
ext: '.mdx',

‎scripts/check-skill-identifier-liveness.mjs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ const HERE = dirname(fileURLToPath(import.meta.url));
257257
const REPO_ROOT = join(HERE, '..');
258258

259259
const ROOTS = ['skills'];
260-
const IMPL_ROOTS = ['packages', 'ui/packages'];
260+
const IMPL_ROOTS = ['packages'];
261261
const SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', 'coverage', '.turbo']);
262262
const EXTENSIONS = new Set(['.md']);
263263
const IMPL_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json']);

‎ui/content/docs/guide/agent-skills.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ description: "Install the ObjectUI Agent Skill into Claude Code, Cursor, Codex,
77

88
ObjectUI ships an official **Agent Skill** — a structured bundle of rules, guides, and evals that teaches AI coding agents how to build pages, plugins, and integrations the *right* way (schema-first, no-touch zones respected, English-only, expression-aware).
99

10-
The skill is published on [skills.sh](https://skills.sh/objectstack-ai/objectui) and lives in [`skills/objectui/`](https://github.com/objectstack-ai/objectstack/tree/main/skills/objectui) inside this repository.
10+
The skill is published on [skills.sh](https://skills.sh/objectstack-ai/objectui) and lives in [`skills/objectui/`](https://github.com/objectstack-ai/objectui/tree/main/skills/objectui) inside this repository.
1111

1212
[![skills.sh](https://skills.sh/b/objectstack-ai/objectui)](https://skills.sh/objectstack-ai/objectui)
1313

@@ -103,4 +103,4 @@ The CLI is idempotent — running it again refreshes the local copy in place.
103103

104104
## Contributing
105105

106-
The skill source lives in [`skills/objectui/`](https://github.com/objectstack-ai/objectstack/tree/main/skills/objectui). PRs are welcome — see [`skills/objectui/README.md`](https://github.com/objectstack-ai/objectstack/blob/main/skills/objectui/README.md) for the maintenance rules (guide ↔ eval naming, English-only, eval assertion format).
106+
The skill source lives in [`skills/objectui/`](https://github.com/objectstack-ai/objectui/tree/main/skills/objectui). PRs are welcome — see [`skills/objectui/README.md`](https://github.com/objectstack-ai/objectui/blob/main/skills/objectui/README.md) for the maintenance rules (guide ↔ eval naming, English-only, eval assertion format).

‎ui/packages/components/src/__tests__/skill-guide-data-table-binding.test.tsx‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,6 @@ import type { DataSource } from '@object-ui/types';
9292

9393
const here = path.dirname(fileURLToPath(import.meta.url));
9494
const repoRoot = path.resolve(here, '../../../..');
95-
// The published skill bundle is in the objectstack monorepo's `skills/` catalog
96-
// since the cutover (decision C, objectstack#25803), one level above this tree,
97-
// so its `skills/objectui/...` paths are read from there.
98-
const skillsBase = path.resolve(repoRoot, '..');
9995

10096
const GUIDES = {
10197
'skills/objectui/guides/schema-expressions.md': 'schema-expressions',
@@ -106,7 +102,7 @@ const GUIDES = {
106102
const GUIDE_PATHS = Object.keys(GUIDES) as (keyof typeof GUIDES)[];
107103

108104
function readGuide(rel: string): string {
109-
return fs.readFileSync(path.join(skillsBase, rel), 'utf8');
105+
return fs.readFileSync(path.join(repoRoot, rel), 'utf8');
110106
}
111107

112108
/**

‎ui/packages/components/src/__tests__/skill-guide-provider-envelope.test.tsx‎

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,7 @@ import type { DataSource } from '@object-ui/types';
8181

8282
const here = path.dirname(fileURLToPath(import.meta.url));
8383
const repoRoot = path.resolve(here, '../../../..');
84-
// The published skill bundle is in the objectstack monorepo's `skills/` catalog
85-
// since the cutover (decision C, objectstack#25803), one level above this tree,
86-
// so its `skills/objectui/...` paths are read from there.
87-
const skillsBase = path.resolve(repoRoot, '..');
88-
const skillsRoot = path.join(skillsBase, 'skills/objectui');
84+
const skillsRoot = path.join(repoRoot, 'skills/objectui');
8985

9086
// The DECLARED column keys (`TableColumn`: `header` + `accessorKey`). These
9187
// mirror the two published guides' `data-table` example, which is why they
@@ -127,15 +123,15 @@ function publishedFiles(): string[] {
127123
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
128124
const full = path.join(dir, entry.name);
129125
if (entry.isDirectory()) walk(full);
130-
else out.push(path.relative(skillsBase, full));
126+
else out.push(path.relative(repoRoot, full));
131127
}
132128
};
133129
walk(skillsRoot);
134130
return out.sort();
135131
}
136132

137133
function readSkillFile(rel: string): string {
138-
return fs.readFileSync(path.join(skillsBase, rel), 'utf8');
134+
return fs.readFileSync(path.join(repoRoot, rel), 'utf8');
139135
}
140136

141137
describe('#5372 behaviour — a provider `dataSource` into a `data-table`', () => {

‎ui/packages/permissions/src/__tests__/skill-guide-permission-config.test.tsx‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,7 @@ import { usePermissions } from '../usePermissions';
4747

4848
const here = path.dirname(fileURLToPath(import.meta.url));
4949
const repoRoot = path.resolve(here, '../../../..');
50-
// The published skill bundle is in the objectstack monorepo's `skills/` catalog
51-
// since the cutover (decision C, objectstack#25803), one level above this tree,
52-
// so its `skills/objectui/...` paths are read from there.
53-
const skillsBase = path.resolve(repoRoot, '..');
54-
const GUIDE = path.join(skillsBase, 'skills/objectui/guides/auth-permissions.md');
50+
const GUIDE = path.join(repoRoot, 'skills/objectui/guides/auth-permissions.md');
5551

5652
// --- 8< --- guide example begin --- 8< ---
5753
// A role definition carries identity and inheritance only. Its grants live in
@@ -119,7 +115,7 @@ describe('objectui#4812 — the guide example is the shape the types declare', (
119115
const start = guide.findIndex((l) => l === want[0]);
120116
expect(
121117
start,
122-
`first line of the pinned example not found in ${path.relative(skillsBase, GUIDE)}`,
118+
`first line of the pinned example not found in ${path.relative(repoRoot, GUIDE)}`,
123119
).toBeGreaterThan(-1);
124120
expect(guide.slice(start, start + want.length)).toEqual(want);
125121
});

‎ui/packages/types/src/__tests__/list-bind-only-11405.test.ts‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,6 @@ const stripJsonComments: (source: string) => string = stripUntyped;
6363
/** Rooted at THIS file, never at `process.cwd()`. */
6464
const HERE = dirname(fileURLToPath(import.meta.url));
6565
const REPO_ROOT = resolve(HERE, '..', '..', '..', '..');
66-
// The published skill bundle is in the objectstack monorepo's `skills/` catalog
67-
// since the cutover (decision C, objectstack#25803), one level above this tree,
68-
// so its `skills/objectui/...` paths are read from there.
69-
const SKILLS_BASE = resolve(REPO_ROOT, '..');
7066

7167
const GUIDES = [
7268
'skills/objectui/guides/data-integration.md',
@@ -137,7 +133,7 @@ type Fence = { guide: string; fenceLine: number; teachesWrong: boolean; node: Re
137133
function bindOnlyListFences(): Fence[] {
138134
const out: Fence[] = [];
139135
for (const guide of GUIDES) {
140-
const { fences } = scanSkillFences(readFileSync(resolve(SKILLS_BASE, guide), 'utf8'));
136+
const { fences } = scanSkillFences(readFileSync(resolve(REPO_ROOT, guide), 'utf8'));
141137
for (const fence of fences) {
142138
if (!fence.marked || fence.kind !== 'json') continue;
143139
// The marker's own gate already requires every marked fence to parse.

‎ui/scripts/__tests__/check-doc-json-fences.test.ts‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import {
3333
FRAGMENT_MARKER as SNIPPET_FRAGMENT_MARKER,
3434
FRAGMENT_MARKER_EXAMPLES as SNIPPET_FRAGMENT_MARKER_EXAMPLES,
3535
} from '../check-doc-snippet-types.mjs';
36-
import { labelPath } from '../skill-bundle-root.mjs';
3736
import { packageReadmePages as familyPackageReadmes, ROOT_PAGES as FAMILY_ROOT_PAGES } from '../check-doc-component-types.mjs';
3837
// The sibling gate over the skill bundle — imported HERE, never by the gate: the
3938
// gate spells its opt-in line itself, and this file pins the two spellings equal.
@@ -434,7 +433,7 @@ describe('the scan surface — the published skill bundle (objectui#12204)', ()
434433
// Non-vacuous: the bundle carries declared counter-examples; none would make the loop below pass over nothing.
435434
expect(pages.size, 'no declared fragment under the bundle').toBeGreaterThan(0);
436435
for (const doc of pages) {
437-
const scan = scanSkillFences(fs.readFileSync(labelPath(repoRoot, doc), 'utf8'));
436+
const scan = scanSkillFences(fs.readFileSync(path.join(repoRoot, doc), 'utf8'));
438437
expect(scan.orphans, `${doc}: orphaned opt-in lines`).toEqual([]);
439438
}
440439
});

‎ui/scripts/__tests__/check-governed-queue-guard.test.ts‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313
GOVERNED_SURFACES,
1414
governedPathsIn,
1515
} from '../check-governed-queue-guard.mjs';
16-
import { skillBundleBase } from '../skill-bundle-root.mjs';
1716

1817
import { selfTestCases, stripAnsi } from './helpers/child-verdict';
1918

@@ -169,11 +168,8 @@ describe('the governed surface is the ruled definition, and it is real', () => {
169168
// these types from the `.mjs` (allowJs, checkJs off), so the optional-chain
170169
// spelling is a real type error here rather than a stylistic preference.
171170
const target = 'prefix' in surface ? surface.prefix : surface.exact;
172-
// The published `skills/` catalog is the monorepo root's since the cutover
173-
// (decision C, `scripts/skill-bundle-root.mjs`), so that row is read from there.
174-
const base = surface.id === 'skills-catalog' ? skillBundleBase(ROOT) : ROOT;
175171
expect(
176-
fs.existsSync(path.join(base, target)),
172+
fs.existsSync(path.join(ROOT, target)),
177173
`GOVERNED_SURFACES declares ${target}, which does not exist. A governed path nobody can ` +
178174
`edit is a rule about nothing; either the surface moved (update the register) or the ` +
179175
`entry was speculative (drop it).`,

‎ui/scripts/__tests__/check-skill-examples.test.ts‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ import {
4343
rootDeclaredSpecifiers,
4444
specifierRoot,
4545
} from '../check-doc-snippet-types.mjs';
46-
import { labelPath } from '../skill-bundle-root.mjs';
4746

4847
/**
4948
* objectui#7359 — the test for `scripts/check-skill-examples.mjs`.
@@ -310,7 +309,7 @@ describe('the corpus this gate governs', () => {
310309
const guides = listGuides(repoRoot) as string[];
311310
const scans = guides.map((g) => ({
312311
guide: g,
313-
scan: scanSkillFences(fs.readFileSync(labelPath(repoRoot, g), 'utf8')) as Scan,
312+
scan: scanSkillFences(fs.readFileSync(path.join(repoRoot, g), 'utf8')) as Scan,
314313
}));
315314

316315
it('carries no orphan marker', () => {
@@ -352,7 +351,7 @@ describe('the corpus this gate governs', () => {
352351
// reads as a marker and claims nothing. It would surface as an orphan only
353352
// if it matched the orphan scan's own literal — which it does not.
354353
for (const guide of guides) {
355-
const source = fs.readFileSync(labelPath(repoRoot, guide), 'utf8');
354+
const source = fs.readFileSync(path.join(repoRoot, guide), 'utf8');
356355
for (const [index, line] of source.split('\n').entries()) {
357356
if (!/os:check/.test(line)) continue;
358357
expect(line, `${guide}:${index + 1} carries a near-spelling of the marker`).toBe(MARKER);
@@ -378,7 +377,7 @@ describe('the shrink-only floor on the marked population', () => {
378377
type Breach = { category: string; count: number; floor: number };
379378
const corpusMarked = (listGuides(repoRoot) as string[]).flatMap(
380379
(guide) =>
381-
(scanSkillFences(fs.readFileSync(labelPath(repoRoot, guide), 'utf8')) as Scan).fences.filter(
380+
(scanSkillFences(fs.readFileSync(path.join(repoRoot, guide), 'utf8')) as Scan).fences.filter(
382381
(f) => f.marked,
383382
),
384383
);
@@ -899,7 +898,7 @@ describe('the ROOT BOUND, and its declared debt (objectui#7463 item 2)', () => {
899898
const lastColon = site.lastIndexOf(':');
900899
const guide = site.slice(0, lastColon);
901900
const line = Number(site.slice(lastColon + 1));
902-
const source = fs.readFileSync(labelPath(repoRoot, guide), 'utf8');
901+
const source = fs.readFileSync(path.join(repoRoot, guide), 'utf8');
903902
const { fences: all } = scanSkillFences(source) as {
904903
fences: { fenceLine: number; marked: boolean }[];
905904
};
@@ -1343,7 +1342,7 @@ describe('the shadowed-published-type assertion (objectui#7646)', () => {
13431342
const guides = new Map(
13441343
(listGuides(repoRoot) as string[]).map((g) => [
13451344
g,
1346-
scanSkillFences(fs.readFileSync(labelPath(repoRoot, g), 'utf8')) as Scan,
1345+
scanSkillFences(fs.readFileSync(path.join(repoRoot, g), 'utf8')) as Scan,
13471346
]),
13481347
);
13491348
for (const row of (KNOWN_SHADOWED_PUBLISHED_TYPES as Map<string, string>).keys()) {

0 commit comments

Comments
 (0)