mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
1a76344685
* docs: restructure AGENTS.md and CONTEXT.md for progressive disclosure
Apply the Claude 5 context-engineering guidance to the repo's agent docs:
keep the always-loaded file to gotchas and invariants, and move situational
guidance one hop away behind a routing table.
AGENTS.md 315 -> 229 lines. Cut generic agent-behavior boilerplate, three-way
duplication (Common Mistakes restated Hard Rules; Finding Source Owners
restated the registry section), and facts visible from the repo itself.
Kept verbatim: the expensive-lessons principles, enforcement gates, Hard
Rules, and environment traps.
Split out docs/agents/{cli-flags,pull-requests,device-verification}.md and
folded the Testing Matrix into docs/agents/testing.md, reframed around
pnpm check:affected so the prose stops duplicating the selector.
CONTEXT.md keeps all 50 terms, now grouped under a section index so a task
loads one section instead of the whole glossary.
* fix(check-affected): move the selector-owning sentinel to the Testing Matrix
The Testing Matrix moved from AGENTS.md to docs/agents/testing.md, but the
affected-check selector still treated only AGENTS.md as selector-owning. A
later matrix edit would have been classified as inert docs and skipped the
fail-open, so the selector could keep deriving gates from a spec that had
changed underneath it.
Move the sentinel with the prose, as a named SELECTOR_OWNING_DOCS set so the
next move is one line, and fix the two in-code comments plus the testing.md
paragraph that still pointed at the AGENTS.md matrix.
* docs: restore two rules dropped by the AGENTS.md split
Review caught two repo-specific rules that did not survive the move. Both are
prose without any backticked identifier, so the identifier-diff used to verify
the split could not see them.
- "Test through public interfaces; do not add unrelated production exports
solely to enable tests" returns next to the behavioral-tests rule in
docs/agents/testing.md, with the reason it exists.
- The guidance-ownership rule (decide whether new guidance/schema/metadata
belongs to the command surface, CLI grammar, CLI help, MCP projection, or
daemon runtime) returns to the always-loaded Docs & skills section, since it
governs all command-surface work and not just the flag case.
Also point the ADR routing row at docs/adr/README.md, which is already the
"read when you touch…" index, rather than at the bare directory.
199 lines
6.2 KiB
TypeScript
199 lines
6.2 KiB
TypeScript
// Catalog for the check-affected selector: how each derived CheckId maps to a
|
|
// runnable command and the authoritative GitHub CI job(s) it mirrors.
|
|
//
|
|
// Commands are resolved from real package.json scripts or Vitest's native
|
|
// affected-test command, so this stays a thin projection over existing
|
|
// aggregate checks rather than a second source of truth for how to run them.
|
|
|
|
import { ALL_CHECKS, type CheckId } from './model.ts';
|
|
|
|
export type CheckKind =
|
|
| { readonly type: 'script'; readonly script: string }
|
|
| { readonly type: 'vitest-related' };
|
|
|
|
export type CheckSpec = {
|
|
readonly id: CheckId;
|
|
readonly label: string;
|
|
readonly kind: CheckKind;
|
|
readonly ciJobs: readonly string[];
|
|
// Whether `--run` should attempt the check locally. Device/emulator lanes and
|
|
// network/toolchain-gated lanes stay authoritative on GitHub CI.
|
|
readonly localRunnable: boolean;
|
|
};
|
|
|
|
export const CHECK_CATALOG: readonly CheckSpec[] = [
|
|
{
|
|
id: 'format',
|
|
label: 'Formatting (oxfmt)',
|
|
kind: { type: 'script', script: 'format:check' },
|
|
ciJobs: ['Lint & Format'],
|
|
localRunnable: true,
|
|
},
|
|
{
|
|
id: 'lint',
|
|
label: 'Lint (oxlint)',
|
|
kind: { type: 'script', script: 'lint' },
|
|
ciJobs: ['Lint & Format'],
|
|
localRunnable: true,
|
|
},
|
|
{
|
|
id: 'typecheck',
|
|
label: 'Typecheck (tsc)',
|
|
kind: { type: 'script', script: 'typecheck' },
|
|
ciJobs: ['Typecheck'],
|
|
localRunnable: true,
|
|
},
|
|
{
|
|
id: 'layering',
|
|
label: 'Import-direction layering guard',
|
|
kind: { type: 'script', script: 'check:layering' },
|
|
ciJobs: ['Layering Guard'],
|
|
localRunnable: true,
|
|
},
|
|
{
|
|
id: 'fallow',
|
|
label: 'Fallow code-quality audit',
|
|
kind: { type: 'script', script: 'check:fallow' },
|
|
ciJobs: ['Fallow Code Quality'],
|
|
localRunnable: true,
|
|
},
|
|
{
|
|
id: 'mcp-metadata',
|
|
label: 'MCP registry metadata sync',
|
|
kind: { type: 'script', script: 'check:mcp-metadata' },
|
|
ciJobs: ['Typecheck'],
|
|
localRunnable: true,
|
|
},
|
|
{
|
|
id: 'build',
|
|
label: 'Build (tsdown + declarations)',
|
|
kind: { type: 'script', script: 'build' },
|
|
ciJobs: ['Packaged CLI Node 22.12'],
|
|
localRunnable: true,
|
|
},
|
|
{
|
|
id: 'vitest-related',
|
|
label: 'Tests related by Vitest module graph',
|
|
kind: { type: 'vitest-related' },
|
|
ciJobs: ['Coverage'],
|
|
localRunnable: true,
|
|
},
|
|
{
|
|
id: 'unit',
|
|
label: 'Unit + smoke suite',
|
|
kind: { type: 'script', script: 'check:unit' },
|
|
ciJobs: ['Coverage', 'Integration Tests'],
|
|
localRunnable: true,
|
|
},
|
|
{
|
|
id: 'coverage',
|
|
label: 'Coverage + provider integration suite',
|
|
kind: { type: 'script', script: 'test:coverage' },
|
|
ciJobs: ['Coverage'],
|
|
localRunnable: true,
|
|
},
|
|
{
|
|
id: 'provider-integration',
|
|
label: 'Provider-backed integration suite',
|
|
kind: { type: 'script', script: 'test:integration:provider' },
|
|
ciJobs: ['Integration Tests', 'Coverage'],
|
|
localRunnable: true,
|
|
},
|
|
{
|
|
id: 'integration-node',
|
|
label: 'Node integration smoke',
|
|
kind: { type: 'script', script: 'test:integration:node' },
|
|
ciJobs: ['Integration Tests'],
|
|
localRunnable: true,
|
|
},
|
|
{
|
|
id: 'integration-progress',
|
|
label: 'Integration architecture-progress gate',
|
|
kind: { type: 'script', script: 'test:integration:progress:check' },
|
|
ciJobs: ['Integration Tests'],
|
|
localRunnable: true,
|
|
},
|
|
{
|
|
id: 'swift-runner',
|
|
label: 'Swift runner build',
|
|
kind: { type: 'script', script: 'build:xcuitest' },
|
|
ciJobs: ['Swift Runner Unit Compile', 'iOS / Smoke Tests', 'macOS / Smoke Tests'],
|
|
localRunnable: false,
|
|
},
|
|
{
|
|
id: 'android-helpers',
|
|
label: 'Android helper builds',
|
|
kind: { type: 'script', script: 'build:android-snapshot-helper' },
|
|
ciJobs: ['Android / Smoke Tests'],
|
|
localRunnable: false,
|
|
},
|
|
{
|
|
id: 'macos-helper',
|
|
label: 'macOS helper build',
|
|
kind: { type: 'script', script: 'build:macos-helper' },
|
|
ciJobs: ['macOS / Smoke Tests'],
|
|
localRunnable: false,
|
|
},
|
|
{
|
|
id: 'web-smoke',
|
|
label: 'Live web platform smoke',
|
|
kind: { type: 'script', script: 'test:smoke:web' },
|
|
ciJobs: ['Web Platform Smoke'],
|
|
localRunnable: false,
|
|
},
|
|
{
|
|
id: 'skillgym',
|
|
label: 'SkillGym command-planning suite',
|
|
kind: { type: 'script', script: 'test:skillgym' },
|
|
// No GitHub workflow runs SkillGym; per the Testing Matrix in
|
|
// docs/agents/testing.md it is a local-only gate (`pnpm test:skillgym`).
|
|
// Keep it locally runnable rather than claiming a CI job that does not
|
|
// exist and silently skipping it.
|
|
ciJobs: [],
|
|
localRunnable: true,
|
|
},
|
|
];
|
|
|
|
export function getCheckSpec(id: CheckId): CheckSpec {
|
|
const spec = CHECK_CATALOG.find((entry) => entry.id === id);
|
|
if (!spec) throw new Error(`No catalog entry for check "${id}".`);
|
|
return spec;
|
|
}
|
|
|
|
// Resolve the runnable command for a check. Script-backed checks are validated
|
|
// against package.json so a renamed/removed script fails loudly instead of
|
|
// silently skipping a gate. `fallow` threads the same --base the audit uses.
|
|
export function resolveCommand(
|
|
spec: CheckSpec,
|
|
scripts: Readonly<Record<string, string>>,
|
|
base: string,
|
|
changedFiles: readonly string[] = [],
|
|
): string[] {
|
|
if (spec.kind.type === 'vitest-related') {
|
|
return ['pnpm', 'exec', 'vitest', 'related', '--run', '--passWithNoTests', ...changedFiles];
|
|
}
|
|
const { script } = spec.kind;
|
|
if (!(script in scripts)) {
|
|
throw new Error(
|
|
`Check "${spec.id}" references package.json script "${script}", which does not exist.`,
|
|
);
|
|
}
|
|
const command = ['pnpm', 'run', script];
|
|
if (spec.id === 'fallow') command.push('--base', base);
|
|
return command;
|
|
}
|
|
|
|
// Guard: the catalog must cover exactly the CheckId universe. The self-test
|
|
// asserts this so a new check cannot ship half-wired.
|
|
export function assertCatalogComplete(): void {
|
|
const catalogIds = new Set(CHECK_CATALOG.map((entry) => entry.id));
|
|
const missing = ALL_CHECKS.filter((id) => !catalogIds.has(id));
|
|
const extra = CHECK_CATALOG.filter((entry) => !ALL_CHECKS.includes(entry.id)).map((e) => e.id);
|
|
if (missing.length > 0 || extra.length > 0) {
|
|
throw new Error(
|
|
`Check catalog out of sync with ALL_CHECKS. Missing: [${missing.join(', ')}]; ` +
|
|
`extra: [${extra.join(', ')}].`,
|
|
);
|
|
}
|
|
}
|