open-design/apps/daemon/tests/plugins-bundled-atoms-roster.test.ts
Cursor Agent 030d5ce7b2
feat(plugins): Phase 6 + 7 + 8 atom SKILL.md substrate
Plan M3 + M4 / spec §21.4 / §23.3.2.

Lands the prompt-fragment substrate for every atom spec §10
reserves under (planned) ids. The directory now ships nine new
atom plugins under plugins/_official/atoms/, each a SKILL.md +
open-design.json pair the daemon's bundled boot walker registers
on startup:

  Phase 6 (figma-migration native):
    figma-extract  pull node tree + tokens + assets into project cwd
    token-map      crosswalk source tokens onto active DESIGN.md

  Phase 7 (code-migration native):
    code-import    normalise repo into code/index.json snapshot
    design-extract lift tokens out of source / Figma / screenshots
    rewrite-plan   author the multi-file plan with ownership tiers
    patch-edit     execute one plan step per turn with ownership guards
    diff-review    surface unified diff + capture accept/reject
    build-test     run framework-default test commands; emit
                   build.passing + tests.passing signals

  Phase 8 (production code delivery):
    handoff        push artifact to cli/desktop/web/docker/github/
                   figma/code-agent surfaces; append to
                   ArtifactManifest.exportTargets[]

The implementations stay deliberately prose-only in v1 — the
fragments teach the agent what each atom expects, the spec §22.5
promotion path covers iterating each one out-of-tree first. The
matching FIRST_PARTY_ATOMS catalog rows already mark them
'planned'; today's commit gives the bundled boot walker something
to register so a plugin author who references one of these ids in
od.pipeline.stages[*].atoms[] sees the SKILL.md inside the
container without the doctor warning.

apps/daemon/tests/plugins-bundled-atoms-roster.test.ts (2 cases)
pins the on-disk inventory so a future PR can't drop or rename an
atom folder without touching the spec §10 / §21 / §23 tables in
the same patch.

Daemon tests: 1519 → 1521 (+2 cases on the new roster suite).

Co-authored-by: Tom Huang <1043269994@qq.com>
2026-05-09 14:22:18 +00:00

69 lines
2.3 KiB
TypeScript

// Plan §3.M3 + §3.M4 — bundled atoms roster contract.
//
// The repo's plugins/_official/atoms/ directory is the v1 source of
// truth for first-party atom SKILL.md fragments. This test pins the
// roster so a future PR can't accidentally drop an atom (or rename
// its folder) without touching the spec §10 / §21 / §23 tables in
// the same patch.
//
// We assert the on-disk inventory rather than the FIRST_PARTY_ATOMS
// catalog — the latter is the daemon-side metadata, the former is
// the publishable plugin substrate spec §23 reserves.
import path from 'node:path';
import url from 'node:url';
import { readdir, stat } from 'node:fs/promises';
import { describe, expect, it } from 'vitest';
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
const repoRoot = path.resolve(__dirname, '../../..');
const atomsRoot = path.join(repoRoot, 'plugins', '_official', 'atoms');
// Per spec §10 and §21 — implemented atoms (Phase 4 default)
const PHASE_4_ATOMS = [
'discovery-question-form',
'direction-picker',
'todo-write',
'critique-theater',
];
// Phase 6 (figma-migration native, spec §21.4)
const PHASE_6_ATOMS = ['figma-extract', 'token-map'];
// Phase 7 (code-migration native, spec §21.4)
const PHASE_7_ATOMS = [
'code-import',
'design-extract',
'rewrite-plan',
'patch-edit',
'diff-review',
'build-test',
];
// Phase 8 (production code delivery, spec §21.4)
const PHASE_8_ATOMS = ['handoff'];
const EXPECTED_ATOMS = [
...PHASE_4_ATOMS,
...PHASE_6_ATOMS,
...PHASE_7_ATOMS,
...PHASE_8_ATOMS,
].sort();
describe('plugins/_official/atoms roster', () => {
it('contains exactly the spec §10 / §21 / §23 reserved ids', async () => {
const entries = await readdir(atomsRoot, { withFileTypes: true });
const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
expect(dirs).toEqual(EXPECTED_ATOMS);
});
it('every atom folder ships SKILL.md + open-design.json (the spec §3 cross-catalog floor)', async () => {
for (const id of EXPECTED_ATOMS) {
const folder = path.join(atomsRoot, id);
const skill = await stat(path.join(folder, 'SKILL.md'));
const manifest = await stat(path.join(folder, 'open-design.json'));
expect(skill.isFile()).toBe(true);
expect(manifest.isFile()).toBe(true);
}
});
});