diff --git a/.agents/notes/implemented/process/2026-07-06-parallel-github-ci-gates.md b/.agents/notes/implemented/process/2026-07-06-parallel-github-ci-gates.md index 70a8a4d2a1..fbbd8c46d3 100644 --- a/.agents/notes/implemented/process/2026-07-06-parallel-github-ci-gates.md +++ b/.agents/notes/implemented/process/2026-07-06-parallel-github-ci-gates.md @@ -16,7 +16,7 @@ The artifact boundary remains load-bearing. `publint`, `verify-node-next-types`, [scripts/run-gates.ts](../../../../scripts/run-gates.ts) remains the common bounded scheduler, but GitHub supplies explicit shard names for the expensive gate families. [scripts/static-shards.ts](../../../../scripts/static-shards.ts) partitions static gates into foundation, documentation-type, API-contract, catalog, prose, documentation-projection, and documentation-build lanes and rejects a missing or duplicate gate assignment. Lint uses disjoint package-source, package-test, and repository-complement lanes; the complement still starts from `.` so a new top-level lint target cannot disappear between shards, and it owns the single cross-file duplication run. [scripts/coverage-shards.ts](../../../../scripts/coverage-shards.ts) assigns every workspace package to exactly one source-coverage lane; its test expands the live package tree, so a new package makes CI red until it has an owner. Each coverage lane includes only its owned source files, repeats the exhaustive companion topology test, and runs without a preceding build because the complete coverage suite passes from a tree with every generated `lib/` removed. -Snapshot replay uses two explicit multi-file lanes and four scenario partitions of the large ACP file. [scripts/snapshot-shards.ts](../../../../scripts/snapshot-shards.ts) owns that inventory, and its test discovers every file admitted by the snapshot config so a new file cannot land outside CI. Each snapshot job builds the shipped runtime while its Linux runner installs bubblewrap from the hosted image's existing package index, then runs only its assigned replay surface. CI explicitly retains the suite's bounded concurrency of five subprocesses instead of clamping it to the runner's two logical CPUs, because replay spends most of its time waiting on child protocol I/O. Fixture guards still inspect the complete ACP scenario table in every partition. Static, lint, coverage, and snapshot sharding changes only GitHub scheduling: the ordinary local package scripts still run their complete suites. +Snapshot replay uses two explicit multi-file lanes and six scenario partitions of the large ACP file. [scripts/snapshot-shards.ts](../../../../scripts/snapshot-shards.ts) owns that inventory, and its test discovers every file admitted by the snapshot config so a new file cannot land outside CI. Each snapshot job builds the shipped runtime while its Linux runner installs bubblewrap from the hosted image's existing package index, then runs only its assigned replay surface. CI explicitly retains the suite's bounded concurrency of five subprocesses instead of clamping it to the runner's two logical CPUs, because replay spends most of its time waiting on child protocol I/O. Fixture guards still inspect the complete ACP scenario table in every partition. Static, lint, coverage, and snapshot sharding changes only GitHub scheduling: the ordinary local package scripts still run their complete suites. Cold standalone documentation typechecking rebuilds the complete project-reference graph, so a dedicated documentation-type lane builds once and checks Markdown blocks against those declarations. The Linux documentation lane uses VitePress's MPA build to retain page rendering and dead-link validation inside the one-minute budget; the blocking Windows build also produces the normal production SPA, preserving the shipped-site check under its three-minute budget. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82df288a23..45b8cd5407 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -81,21 +81,26 @@ jobs: gate_concurrency: '1' eslint_cache: '1' lint_shard: repository - - lane: coverage-core + - lane: coverage-core-loop command: pnpm run check:ci:coverage gate_concurrency: '1' coverage_max_workers: '4' - coverage_shard: core - - lane: coverage-models-protocol + coverage_shard: core-loop + - lane: coverage-state-session command: pnpm run check:ci:coverage gate_concurrency: '1' coverage_max_workers: '4' - coverage_shard: models-protocol - - lane: coverage-sdk-codex + coverage_shard: state-session + - lane: coverage-models-integrations command: pnpm run check:ci:coverage gate_concurrency: '1' coverage_max_workers: '4' - coverage_shard: sdk-codex + coverage_shard: models-integrations + - lane: coverage-sdk-capabilities + command: pnpm run check:ci:coverage + gate_concurrency: '1' + coverage_max_workers: '4' + coverage_shard: sdk-capabilities - lane: coverage-interfaces command: pnpm run check:ci:coverage gate_concurrency: '1' @@ -126,21 +131,6 @@ jobs: gate_concurrency: '1' coverage_max_workers: '4' coverage_shard: repository - - lane: coverage-integrations-claude - command: pnpm run check:ci:coverage - gate_concurrency: '1' - coverage_max_workers: '4' - coverage_shard: integrations-claude - - lane: coverage-session-scripts - command: pnpm run check:ci:coverage - gate_concurrency: '1' - coverage_max_workers: '4' - coverage_shard: session-scripts - - lane: coverage-capabilities - command: pnpm run check:ci:coverage - gate_concurrency: '1' - coverage_max_workers: '4' - coverage_shard: capabilities - lane: snapshot-support command: pnpm run check:ci:snapshot gate_concurrency: '1' @@ -177,6 +167,18 @@ jobs: snapshot_lane: acp-4 snapshot_max_concurrency: '5' snapshot_prebuilt: '1' + - lane: snapshot-acp-5 + command: pnpm run check:ci:snapshot + gate_concurrency: '1' + snapshot_lane: acp-5 + snapshot_max_concurrency: '5' + snapshot_prebuilt: '1' + - lane: snapshot-acp-6 + command: pnpm run check:ci:snapshot + gate_concurrency: '1' + snapshot_lane: acp-6 + snapshot_max_concurrency: '5' + snapshot_prebuilt: '1' - lane: artifacts-metadata command: pnpm run check:ci:artifacts gate_concurrency: '3' diff --git a/scripts/coverage-shards.ts b/scripts/coverage-shards.ts index f866bbb04d..4bcc0277c1 100644 --- a/scripts/coverage-shards.ts +++ b/scripts/coverage-shards.ts @@ -13,12 +13,44 @@ export interface CoverageShard { /** Exhaustive, non-overlapping ownership of workspace packages in coverage CI. */ export const coverageShards = [ { - name: 'core', - packageRoots: ['core', 'context'], - extraTestRoots: ['packages/examples/cli-demo/tests'], + name: 'core-loop', + packageRoots: ['core/agent', 'core/agent-loop', 'core/tools'], + }, + { + name: 'state-session', + packageRoots: [ + 'core/session', + 'core/scope', + 'core/system-prompt', + 'context', + 'session-persistence', + 'session-query', + 'support/invariants', + ], + extraTestRoots: [ + 'packages/examples/cli-demo/tests', + 'packages/llm/token-meter/tests', + 'scripts', + ], + }, + { + name: 'models-integrations', + packageRoots: ['llm', 'compact', 'hooks/hook-protocol', 'lsp', 'mcp', 'hooks/hooks-claude'], + }, + { + name: 'sdk-capabilities', + packageRoots: [ + 'sdk', + 'hooks/hooks-codex', + 'web', + 'skill', + 'spill', + 'util', + 'guard', + 'todo', + 'timeout', + ], }, - { name: 'models-protocol', packageRoots: ['llm', 'compact', 'hooks/hook-protocol'] }, - { name: 'sdk-codex', packageRoots: ['sdk', 'hooks/hooks-codex'] }, { name: 'interfaces', packageRoots: ['ui', 'examples', 'goal'], @@ -29,7 +61,10 @@ export const coverageShards = [ name: 'workflow', packageRoots: ['workflow/workflow', 'workflow/tool-workflow', 'workflow/tool-ralph'], }, - { name: 'workflow-worker', packageRoots: ['workflow/workflow-workerthread'] }, + { + name: 'workflow-worker', + packageRoots: ['workflow/workflow-workerthread'], + }, { name: 'delegation', packageRoots: ['subagent', 'tasks'] }, { name: 'repository', @@ -41,16 +76,6 @@ export const coverageShards = [ 'support/loader-smoke', ], }, - { - name: 'session-scripts', - packageRoots: ['session-persistence', 'session-query', 'support/invariants'], - extraTestRoots: ['scripts'], - }, - { name: 'integrations-claude', packageRoots: ['lsp', 'mcp', 'hooks/hooks-claude'] }, - { - name: 'capabilities', - packageRoots: ['web', 'skill', 'spill', 'util', 'guard', 'todo', 'timeout'], - }, ] as const satisfies readonly CoverageShard[] /** diff --git a/scripts/snapshot-shards.spec.ts b/scripts/snapshot-shards.spec.ts index dc4bef62ad..7cc37fb7bd 100644 --- a/scripts/snapshot-shards.spec.ts +++ b/scripts/snapshot-shards.spec.ts @@ -26,10 +26,10 @@ describe('snapshot lanes', () => { expect(new Set(ordinary).size).toBe(ordinary.length) expect(acp.map(lane => lane.files)).toEqual(Array.from( - { length: 4 }, + { length: 6 }, () => ['examples/acp-agent/tests/acp.snapshot.ts'], )) - expect(acp.map(lane => lane.scenarioShard)).toEqual(['1/4', '2/4', '3/4', '4/4']) + expect(acp.map(lane => lane.scenarioShard)).toEqual(['1/6', '2/6', '3/6', '4/6', '5/6', '6/6']) expect([...ordinary, 'examples/acp-agent/tests/acp.snapshot.ts'].sort()).toEqual(discovered) }) diff --git a/scripts/snapshot-shards.ts b/scripts/snapshot-shards.ts index 179c2e1517..fae8010261 100644 --- a/scripts/snapshot-shards.ts +++ b/scripts/snapshot-shards.ts @@ -28,10 +28,10 @@ export const snapshotLanes: readonly SnapshotLane[] = [ 'examples/headless-agent/tests/headless.snapshot.ts', ], }, - ...Array.from({ length: 4 }, (_, offset) => ({ + ...Array.from({ length: 6 }, (_, offset) => ({ name: `acp-${offset + 1}`, files: ['examples/acp-agent/tests/acp.snapshot.ts'], - scenarioShard: `${offset + 1}/4`, + scenarioShard: `${offset + 1}/6`, })), ]