Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ jobs:
- run: pnpm build
- run: pnpm typecheck
- run: pnpm lint
# Rstest's default reporters under GITHUB_ACTIONS=true are `default` plus
# `github-actions`, so failures already annotate the PR; no --reporter
# flag is needed on these legs (verified on @rstest/core 0.11.10).
- run: pnpm test

# Required-check anchor for the Verify matrix. When a matrix job is skipped
Expand Down
13 changes: 12 additions & 1 deletion docs/architecture/rsc-runtime-workbench.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ packages/
src/dev/runtime-routes.ts
src/dev/workbench-server.ts
src/index.ts
tests/claude-hook-event-name.test.ts
tests/claude-hooks-schema.test.ts
tests/claude-plugin-validate-acceptance.test.ts
tests/claude-plugin-validation.test.ts
tests/codex-distribution.test.ts
tests/codex-hook-contract.test.ts
tests/codex-plugin-validation.test.ts
tests/dev-artifact-service.test.ts
tests/dev-workbench-packaging.test.ts
tests/dev-workbench.test.ts
Expand All @@ -68,6 +75,7 @@ packages/
tests/normalization.test.ts
tests/playground-service.test.ts
tests/portable-adapter.test.ts
tests/portable-plugin-validation.test.ts
tests/public-api-packed.test.ts
tests/public-api.test.ts
tests/rsc-runtime-optional-packaging.test.ts
Expand Down Expand Up @@ -114,12 +122,13 @@ packages/
tests/runtime-consent-dialog.test.ts
tests/runtime-consent-queue.test.ts
tests/runtime-contract-compile.test.ts
tests/runtime-document-atoms-disposal.test.ts
tests/runtime-inspector.test.ts
tests/runtime-mcp-handoff.test.ts
tests/runtime-model.test.ts
tests/runtime-playground-capture-cleanup.test.ts
tests/runtime-playground-capture.test.ts
tests/runtime-playground-hmr.e2e.test.ts
tests/runtime-playground.browser.test.tsx
tests/runtime-playground.e2e.test.ts
tests/runtime-playground.test.ts
tests/runtime-stage.test.ts
Expand All @@ -134,6 +143,7 @@ examples/
src/build/emit-artifacts.ts
src/build/serialize-definition.ts
src/definition.ts
src/dev/canonical-json.ts
src/dev/definition-entry.ts
src/dev/environment-checkpoint-store.ts
src/dev/generation-materializer.ts
Expand All @@ -144,6 +154,7 @@ examples/
src/dev/serialize-inspection.ts
src/flight/request-render.ts
src/hook/cli.ts
src/hook/eval-probe.ts
src/hook/normalize.ts
src/hook/project-document.ts
src/mcp/create-server.ts
Expand Down
2 changes: 1 addition & 1 deletion docs/assets/rsc-runtime-workbench/fidelity-ledger.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ not a pass.
| Typography | Compact sans-serif labels; monospace JSON/IDs/trace; small uppercase status tokens | Type hierarchy remains legible at the desktop width; code never depends on color alone | `desktop.png` visibly shows labels and monospace values, but no committed contrast or color-independence audit establishes the entire acceptance statement. | Not visually evidenced — visual hierarchy is shown; formal accessibility evidence is not. |
| Palette | White canvas, near-black text, cool gray rules, cobalt active controls, green success, neutral disclaimer | Preserve contrast and restrained developer-tool palette; no gradients or marketing cards | The approved raster set visibly uses the restrained palette, but no committed contrast measurement establishes every stated accessibility property. | Not visually evidenced — visual appearance alone is insufficient for the contrast claim. |
| Container model | 1px outlined panels, 4–6px corners, dense 4px rhythm, 10–16px padding/gutters | Panels align to one grid and retain accessible hit areas without card bloat | `desktop.png` shows outlined panels and the committed `styles.css` sizes Runtime controls at a 40px minimum, but no cited evidence establishes the complete grid/padding/no-bloat claim. | Not visually evidenced — only portions of the container acceptance are observed. |
| Focus/selected states | Cobalt outline/tab underline/current chip; selected Form, Tree, history, generation | Focus is visible, selected semantics are announced, and pointer-only state is not used | Keyboard focus restoration and selected states are exercised by `runtime-playground.e2e.test.ts` and `runtime-playground.browser.test.tsx`, but neither captures computed focus appearance or proves color-independent presentation. | Not visually evidenced — implementation behavior exists, but visual focus treatment is not verified. |
| Focus/selected states | Cobalt outline/tab underline/current chip; selected Form, Tree, history, generation | Focus is visible, selected semantics are announced, and pointer-only state is not used | Keyboard focus restoration and selected states are exercised by `runtime-playground.e2e.test.ts`, but it captures neither computed focus appearance nor color-independent presentation. | Not visually evidenced — implementation behavior exists, but visual focus treatment is not verified. |
| Success/error states | Green dots/checks and `SUCCESS`; no error shown in the raster | Implement success and phase-labelled pale-red diagnostics; absence of an error in the concept is not evidence that error UI may be omitted | `compile-error.png` visibly records the phase-labelled AB8206 diagnostic and `recovered.png` shows its cleared text; `runtime-playground-hmr.e2e.test.ts` verifies the failure/recovery behavior. Neither cited raster nor test observes computed pale-red diagnostic styling. | Not visually evidenced — diagnostic phase/text behavior is directly observed, but the compound color-treatment acceptance is not. |
| Stale/last-good states | Explicit no-stale message plus prior-generation `Last good` identity | Preserve last-good output through compile/run/App failure and distinguish stale from current | `compile-error.png` and `recovered.png`, backed by the capture contract and `runtime-playground-hmr.e2e.test.ts`, prove retention then recovery. | Verified |
| 1100px continuation | Concept is native desktop only; approved continuation moves Inspector below stage | No unreadable third column; bounded trace scroll; identity labels remain separate | No approved 1100px raster or cited contract run covers this breakpoint. | Not visually evidenced — responsive evidence is limited to 1440×900 and 390×844. |
Expand Down
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@
"@rslib/core": "0.23.2",
"@rslint/core": "0.8.2",
"@rstest/adapter-rslib": "0.11.10",
"@rstest/browser": "0.11.10",
"@rstest/browser-react": "0.11.10",
"@rstest/core": "0.11.10",
"@rstest/playwright": "0.11.10",
"@types/node": "26.4.0",
Expand Down
4 changes: 0 additions & 4 deletions packages/agent-bundle/tests/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ const runSourceCliWithOutput = async (
dependencies: CliDependencies = {},
): Promise<{ readonly code: number; readonly stderr: string; readonly stdout: string }> => {
const terminal = captureCliTerminal();
Object.defineProperty(globalThis, '__AGENT_BUNDLE_VERSION__', { configurable: true, value: 'test' });
const code = await runSourceCli(args, terminal.output, dependencies);
return { code, stderr: terminal.stderr(), stdout: terminal.stdout() };
};
Expand Down Expand Up @@ -799,8 +798,6 @@ it('reports a generated Flight worker collision before compiling scripts', async
it('dispatches the install command through the native installer surface', async () => {
const terminal = captureCliTerminal();
const calls: unknown[] = [];
Object.defineProperty(globalThis, '__AGENT_BUNDLE_VERSION__', { configurable: true, value: 'test' });

const code = await runSourceCli(
['install', 'claude', '--from', '/tmp/example bundle', '--scope', 'project', '--json'],
terminal.output,
Expand Down Expand Up @@ -900,7 +897,6 @@ it('reports the bound server exiting on its own as one diagnostic and releases t
let closeCalls = 0;
const serverExit = Promise.withResolvers<void>();
const terminal = captureCliTerminal();
Object.defineProperty(globalThis, '__AGENT_BUNDLE_VERSION__', { configurable: true, value: 'test' });
const code = await runSourceCli(['serve-app', 'status/status', '--root', '/project', '--no-open'], terminal.output, {
serveApp: async () => ({
close: async () => { closeCalls += 1; },
Expand Down
1 change: 0 additions & 1 deletion packages/agent-bundle/tests/eval-cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const runCliWithOutput = async (args: readonly string[]): Promise<{
readonly stdout: string;
}> => {
const terminal = captureCliTerminal();
Object.defineProperty(globalThis, '__AGENT_BUNDLE_VERSION__', { configurable: true, value: 'test' });
const code = await runCli([...args], terminal.output);
return { code, stderr: terminal.stderr(), stdout: terminal.stdout() };
};
Expand Down
1 change: 0 additions & 1 deletion packages/agent-bundle/tests/inspect-state.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ const inspectCli = async (
args: readonly string[],
): Promise<{ readonly code: number; readonly stderr: string; readonly stdout: string }> => {
const terminal = captureCliTerminal();
Object.defineProperty(globalThis, '__AGENT_BUNDLE_VERSION__', { configurable: true, value: 'test' });
const code = await runCli(['inspect', '--root', root, ...args], terminal.output);
return { code, stderr: terminal.stderr(), stdout: terminal.stdout() };
};
Expand Down
3 changes: 0 additions & 3 deletions packages/agent-bundle/tests/install.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1453,8 +1453,6 @@ it('rejects a Cursor plugin name that could escape the local install root', asyn
it('dispatches the public CLI install command to the native installer', async () => {
const terminal = captureCliTerminal();
const calls: unknown[] = [];
Object.defineProperty(globalThis, '__AGENT_BUNDLE_VERSION__', { configurable: true, value: 'test' });

const code = await runCli(
['install', 'claude', '--from', '/tmp/example bundle', '--scope', 'project', '--force', '--json'],
terminal.output,
Expand Down Expand Up @@ -1774,7 +1772,6 @@ it('rejects an install mode for hosts other than Cursor', async () => {

it('passes --mode through the public CLI and prints the staged next steps', async () => {
const calls: unknown[] = [];
Object.defineProperty(globalThis, '__AGENT_BUNDLE_VERSION__', { configurable: true, value: 'test' });
const dependencies = {
installBundle: async (options: unknown) => {
calls.push(options);
Expand Down
1 change: 0 additions & 1 deletion packages/agent-bundle/tests/prepack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ it('prepack validates the complete dry-run inventory', async () => {
it('exposes --root, --output, and --json through the prepack command', async () => {
const calls: unknown[] = [];
const terminal = captureCliTerminal();
Object.defineProperty(globalThis, '__AGENT_BUNDLE_VERSION__', { configurable: true, value: 'test' });
const code = await runCli(
['prepack', '--root', projectRoot, '--output', 'host-packs', '--json'],
terminal.output,
Expand Down
1 change: 0 additions & 1 deletion packages/agent-bundle/tests/route-graph.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -788,7 +788,6 @@ it('shows projected MCP command provenance and safety in the routes inspect focu
});

it('dumps the graph through the CLI --routes focus and rejects ambiguous focuses', async () => {
Object.defineProperty(globalThis, '__AGENT_BUNDLE_VERSION__', { configurable: true, value: 'test' });
const root = await createInspectProject({
'src/mcp/curator/tools/inspect.ts': moduleSource,
});
Expand Down
123 changes: 123 additions & 0 deletions packages/agent-bundle/tests/rstest-pool-lists.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { globSync, readdirSync, statSync } from 'node:fs';
import { dirname, join, relative, resolve, sep } from 'node:path';
import { fileURLToPath } from 'node:url';

import { describe, expect, it } from '@rstest/core';

import * as poolLists from '../../../rstest.integration-tests.ts';

/**
* The pools are defined by subtraction: `rstest.unit.config.ts` collects
* `workspaceTestFileGlob` minus every list `rstest.integration-tests.ts`
* exports, and each other pool includes exactly one of those lists. Nothing in
* Rstest checks that a listed path still exists, so a moved or renamed file
* would silently fall into the non-isolated, parallel unit pool while its
* stale entry kept excluding nothing — and a file the glob never matched (a
* `.tsx`, a `.spec.ts`) has no pool at all unless a list names it. This is the
* interim guard until the suites move into per-pool directories (#566).
*/
const workspaceRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../../..');

/** Workspace-relative, `/`-separated, whatever the platform hands back. */
const toPosix = (path: string): string => path.split(sep).join('/');

const isGlob = (entry: string): boolean => /[*?[\]{}]/u.test(entry);

const isTestFileName = (name: string): boolean => /\.(?:test|spec)\.[cm]?[jt]sx?$/u.test(name);

const isFile = (entry: string): boolean => {
try {
return statSync(resolve(workspaceRoot, entry)).isFile();
} catch {
return false;
}
};

/** What Rstest's default `exclude` prunes, so a dependency's or build output's tests never count. */
const prunedSegments: ReadonlySet<string> = new Set(['node_modules', 'dist', '.idea', '.git', '.cache', '.output', '.temp']);

const isPrunedPath = (path: string): boolean => toPosix(path).split('/').some((segment) => prunedSegments.has(segment));

const matchingFiles = (pattern: string): readonly string[] => globSync(pattern, {
cwd: workspaceRoot,
exclude: (path: string) => isPrunedPath(path),
}).map(toPosix).filter(isFile).sort();

/** Files a pool entry collects: the entry itself when literal, its matches when a glob. */
const collectedFiles = (entry: string): readonly string[] => (isGlob(entry) ? matchingFiles(entry) : [entry]);

/**
* Rstest globs with `dot: true`; Node's `fs.globSync` never enters or returns
* a dot path and has no option to. The two views agree exactly as long as no
* test file lives under a hidden path, so this walk — which does see dot
* paths, and prunes what Rstest prunes — reports every test file that breaks
* the agreement.
*/
const hiddenTestFiles = (directory: string): readonly string[] => readdirSync(directory, { withFileTypes: true })
.flatMap((entry) => {
if (prunedSegments.has(entry.name)) return [];
const path = join(directory, entry.name);
if (entry.isDirectory()) return hiddenTestFiles(path);
if (!entry.isFile() || !isTestFileName(entry.name)) return [];
const relativePath = toPosix(relative(workspaceRoot, path));
return relativePath.split('/').some((segment) => segment.startsWith('.')) ? [relativePath] : [];
});

const { workspaceTestFileGlob, ...exportedLists } = poolLists;
const lists = Object.entries(exportedLists) as ReadonlyArray<readonly [string, readonly string[]]>;

it('exports the include glob plus non-empty string lists, so every pool below is actually checked', () => {
expect(isGlob(workspaceTestFileGlob)).toBe(true);
expect(lists.length).toBeGreaterThan(0);
for (const [name, entries] of lists) {
expect(Array.isArray(entries), `${name} is not an array`).toBe(true);
expect(entries.length, `${name} is empty`).toBeGreaterThan(0);
expect(entries.every((entry) => typeof entry === 'string'), `${name} holds a non-string entry`).toBe(true);
}
});

it('keeps every test file on a visible path, so the glob checks below see what Rstest collects', () => {
expect(hiddenTestFiles(join(workspaceRoot, 'packages')), 'test files under a hidden path (Rstest collects them; this guard cannot)').toEqual([]);
});

describe.each(lists)('%s', (name, entries) => {
it('lists only files that exist (a moved or deleted test leaves a stale entry)', () => {
const missing = entries.filter((entry) => !isGlob(entry) && !isFile(entry));
expect(missing, `${name}: paths that no longer exist`).toEqual([]);
});

it('lists only globs that still match at least one file', () => {
const empty = entries.filter((entry) => isGlob(entry) && collectedFiles(entry).length === 0);
expect(empty, `${name}: globs that match nothing`).toEqual([]);
});

it('lists every entry once', () => {
const duplicates = entries.filter((entry, index) => entries.indexOf(entry) !== index);
expect(duplicates, `${name}: repeated entries`).toEqual([]);
});
});

it('assigns every test file to at most one pool', () => {
const owners = new Map<string, string[]>();
for (const [name, entries] of lists) {
for (const file of entries.flatMap(collectedFiles)) {
owners.set(file, [...(owners.get(file) ?? []), name]);
}
}
const shared = [...owners].filter(([, names]) => names.length > 1).map(([file, names]) => `${file} <- ${names.join(', ')}`);
expect(shared, 'files collected by more than one pool list').toEqual([]);
});

it('leaves no test file without a pool', () => {
// Every `.test.ts` the include glob matches has a pool by construction (the
// unit pool, unless a list claims it); anything else — another extension,
// another suffix — runs only if a list names it. `isTestFileName` is the
// full set Rstest's default include (`*.{test,spec}.?(c|m)[jt]s?(x)`) accepts.
const everyTestFile = matchingFiles('packages/**/tests/**/*.{test,spec}.*').filter((file) => isTestFileName(file));
const collected = new Set([
...matchingFiles(workspaceTestFileGlob),
...lists.flatMap(([, entries]) => entries.flatMap(collectedFiles)),
]);
const orphans = everyTestFile.filter((file) => !collected.has(file));
expect(orphans, 'test files no pool collects: add them to a list in rstest.integration-tests.ts or delete them').toEqual([]);
});
1 change: 0 additions & 1 deletion packages/agent-bundle/tests/uninstall.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1249,7 +1249,6 @@ it('rejects an uninstall mode for hosts other than Cursor before touching anythi
it('exposes uninstall through the public CLI with every lifecycle flag', async () => {
const terminal = captureCliTerminal();
const calls: unknown[] = [];
Object.defineProperty(globalThis, '__AGENT_BUNDLE_VERSION__', { configurable: true, value: 'test' });
const result: UninstallResult = {
bundleRoot: '/tmp/example bundle',
data: { detail: 'kept', outcome: 'kept', paths: ['/tmp/example bundle/state'], policy: 'keep' },
Expand Down
Loading
Loading