forked from farhoodlabs/paperclip
81d18f2d77
## Thinking Path
> - Paperclip orchestrates AI agents through a control-plane repo that
relies on GitHub Actions as part of its release and verification safety
net.
> - The PR workflow in `.github/workflows/pr.yml` is the core CI path
protecting pull requests before merge.
> - Baseline measurement work in [PAPA-335](/PAPA/issues/PAPA-335)
showed the old single `verify` job was the critical-path bottleneck,
with general tests and build serialized together.
> - Follow-up implementation in [PAPA-338](/PAPA/issues/PAPA-338) and
[PAPA-339](/PAPA/issues/PAPA-339) split that work into parallel lanes
and removed redundant clean-runner prebuild work.
> - [PAPA-340](/PAPA/issues/PAPA-340) now needs real post-change PR
workflow evidence, not local inference, to compare against the May 15,
2026 baseline and decide whether phase-2 work is still justified.
> - This pull request publishes the already-implemented CI speedup
branch so GitHub can run the actual `PR` workflow against it.
> - The benefit is that CI timing decisions are based on measured runs
from the exact workflow shape we intend to ship.
## What Changed
- Split the PR workflow so `policy` fans out into separate `Typecheck +
Release Registry`, grouped `General tests`, and `Build` jobs.
- Kept the serialized server matrix, canary dry run, and e2e jobs intact
while removing the old monolithic `verify` bottleneck.
- Reworked grouped general-test execution in
`scripts/run-vitest-stable.mjs` so the workflow can run balanced
non-serialized lanes.
- Replaced redundant clean-runner prebuild gates with the idempotent
`ensure-build-deps` path used by the relevant CI entrypoints.
## Verification
- `ruby -e "require 'yaml'; YAML.load_file('.github/workflows/pr.yml');
puts 'yaml-ok'"`
- `node scripts/run-vitest-stable.mjs --mode general --dry-run`
- `node scripts/run-vitest-stable.mjs --mode general --group
general-server --dry-run`
- `node scripts/run-vitest-stable.mjs --mode general --group
general-workspaces-a --dry-run`
- `node scripts/run-vitest-stable.mjs --mode general --group
general-workspaces-b --dry-run`
- `pnpm test:run:general -- --group general-workspaces-b`
- `pnpm test:run:general -- --group general-workspaces-a`
- `pnpm test:run:general -- --group general-server`
- `pnpm run typecheck:build-gaps`
- `pnpm --filter @paperclipai/plugin-hello-world-example typecheck`
## Risks
- Required-check and branch-protection settings may still reference the
old single `verify` job name.
- Parallel CI lanes can expose hidden ordering assumptions or
clean-runner bootstrap gaps that local grouped dry-runs did not surface.
- Because the branch is behind current `master`, merge conflicts or
unrelated upstream drift could affect the measured runtime until the
branch is rebased.
> Checked `ROADMAP.md`; this work is CI throughput maintenance for the
existing PR verification path, not duplicate feature work.
## Model Used
- OpenAI Codex via Paperclip `codex_local`, GPT-5-class coding agent
with repository read/write, shell execution, and GitHub CLI/tool use.
The runtime does not expose a more specific backend model ID in-session.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
94 lines
2.5 KiB
JavaScript
94 lines
2.5 KiB
JavaScript
#!/usr/bin/env node
|
|
import { readFileSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { spawnSync } from "node:child_process";
|
|
|
|
const repoRoot = process.cwd();
|
|
|
|
function fail(message) {
|
|
console.error(`[typecheck:build-gaps] ${message}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
function run(command, args) {
|
|
const result = spawnSync(command, args, {
|
|
cwd: repoRoot,
|
|
stdio: "inherit",
|
|
});
|
|
|
|
if (result.error) {
|
|
console.error(`[typecheck:build-gaps] Failed to spawn ${command}: ${result.error.message}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
if (result.status !== 0) {
|
|
process.exit(result.status ?? 1);
|
|
}
|
|
}
|
|
|
|
function readJson(filePath) {
|
|
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
}
|
|
|
|
function listWorkspacePackages() {
|
|
const result = spawnSync("pnpm", ["ls", "-r", "--depth", "-1", "--json"], {
|
|
cwd: repoRoot,
|
|
encoding: "utf8",
|
|
});
|
|
|
|
if (result.error) {
|
|
fail(`Unable to spawn pnpm to list workspace packages: ${result.error.message}`);
|
|
}
|
|
|
|
if (result.status !== 0) {
|
|
fail("Unable to list pnpm workspace packages.");
|
|
}
|
|
|
|
return JSON.parse(result.stdout);
|
|
}
|
|
|
|
function buildSkipsTypeScript(pkg) {
|
|
const buildScript = pkg.scripts?.build;
|
|
if (typeof buildScript !== "string") {
|
|
return false;
|
|
}
|
|
|
|
return !/\btsc\b/.test(buildScript);
|
|
}
|
|
|
|
const workspacePackages = listWorkspacePackages();
|
|
const buildGapCandidates = workspacePackages
|
|
.filter((workspacePkg) => workspacePkg.path !== repoRoot)
|
|
.map((workspacePkg) => ({
|
|
name: workspacePkg.name,
|
|
path: workspacePkg.path,
|
|
pkg: readJson(path.join(workspacePkg.path, "package.json")),
|
|
}))
|
|
.filter(({ pkg }) => buildSkipsTypeScript(pkg));
|
|
const packagesMissingTypecheck = buildGapCandidates.filter(
|
|
({ pkg }) => typeof pkg.scripts?.typecheck !== "string",
|
|
);
|
|
if (packagesMissingTypecheck.length > 0) {
|
|
const missingNames = packagesMissingTypecheck.map((workspacePkg) => workspacePkg.name).join(", ");
|
|
fail(
|
|
`Workspace packages with build scripts that skip tsc must define a typecheck script. Missing: ${missingNames}`,
|
|
);
|
|
}
|
|
const buildGapPackages = buildGapCandidates.filter(
|
|
({ pkg }) => typeof pkg.scripts?.typecheck === "string",
|
|
);
|
|
|
|
console.log(
|
|
`[typecheck:build-gaps] typechecking ${buildGapPackages.length} workspace(s): ${buildGapPackages.map(({ name }) => name).join(", ") || "(none)"}`,
|
|
);
|
|
|
|
if (buildGapPackages.length === 0) {
|
|
process.exit(0);
|
|
}
|
|
|
|
run("pnpm", ["--filter", "@paperclipai/plugin-sdk", "ensure-build-deps"]);
|
|
|
|
for (const workspacePkg of buildGapPackages) {
|
|
run("pnpm", ["--filter", workspacePkg.name, "typecheck"]);
|
|
}
|