From fd201bf3ce6a61322619cad012c36283ffc6face Mon Sep 17 00:00:00 2001 From: Error Lover Date: Sat, 1 Aug 2026 03:31:33 +0300 Subject: [PATCH 01/21] feat: freeze R7 corpus collection protocol --- docs/v0.3/CORPUS_FREEZE_SPEC.md | 47 ++++ docs/v0.3/r7/README.md | 18 ++ docs/v0.3/r7/repositories.json | 41 ++++ extension/package.json | 1 + extension/scripts/r7-corpus.mjs | 22 ++ extension/src/corpus/cli.ts | 52 +++++ extension/src/corpus/freeze.ts | 305 ++++++++++++++++++++++++++ extension/src/corpus/index.ts | 2 + extension/src/corpus/scan.ts | 309 +++++++++++++++++++++++++++ extension/test/corpus-freeze.test.ts | 129 +++++++++++ extension/test/corpus-scan.test.ts | 126 +++++++++++ 11 files changed, 1052 insertions(+) create mode 100644 docs/v0.3/CORPUS_FREEZE_SPEC.md create mode 100644 docs/v0.3/r7/README.md create mode 100644 docs/v0.3/r7/repositories.json create mode 100644 extension/scripts/r7-corpus.mjs create mode 100644 extension/src/corpus/cli.ts create mode 100644 extension/src/corpus/freeze.ts create mode 100644 extension/src/corpus/index.ts create mode 100644 extension/src/corpus/scan.ts create mode 100644 extension/test/corpus-freeze.test.ts create mode 100644 extension/test/corpus-scan.test.ts diff --git a/docs/v0.3/CORPUS_FREEZE_SPEC.md b/docs/v0.3/CORPUS_FREEZE_SPEC.md new file mode 100644 index 0000000..c70ee83 --- /dev/null +++ b/docs/v0.3/CORPUS_FREEZE_SPEC.md @@ -0,0 +1,47 @@ +# R7 corpus freeze contract + +**Status:** frozen before any R7 compiler outcome is inspected +**Protocol:** `r7-typescript-node-v1` + +## Purpose + +The corpus freezer converts repository registrations and independently collected eligibility facts into one canonical 30-patch manifest. It does not compile recovery episodes, expose held-out compiler outcomes, or decide whether a compiler result is successful. + +## Required behavior + +- Repository registration happens before candidate inspection and records URL, OSI license, pinned tip, runtime, package manager, immutable install/test argv, and the exact first-parent history command. +- A candidate is a full target commit plus its single adjacent first parent and its ordinal among the first 60 coarse candidates that changed TypeScript source and a test path. +- Eligibility facts are outcome-free. They contain bounded Git/test/provisioning evidence only and cannot contain episode, probe, judge, rater, or compiler fields. +- Every ineligible candidate receives exactly one exclusion code: the first failing rule in the frozen order below. +- At most the first 60 coarse candidates per repository are accepted. At most the first 10 eligible candidates per repository enter the corpus. +- Freezing fails unless exactly 30 eligible patches from at least three repositories exist. +- The split key is exactly `SHA-256("pureflow/r7-typescript-node-v1\n" + repository URL + "\n" + target commit)`. +- Patches are sorted by the raw UTF-8 bytes of that lowercase key. Positions 1–12 are `development`; positions 13–30 are `held-out`. +- Canonical JSON and its SHA-256 are emitted so the manifest can be reviewed and frozen before compiler execution. + +## First-match exclusion order + +1. `not-adjacent-first-parent` +2. `license-not-approved` +3. `missing-lockfile` +4. `dependency-or-lockfile-change` +5. `unsupported-artifact` +6. `diff-too-large` +7. `snapshot-too-large` +8. `unsupported-typescript-boundary` +9. `missing-attributed-test` +10. `requires-production-capability` +11. `network-required-at-execution` +12. `base-provision-or-test-failed` +13. `target-provision-or-test-failed` +14. `replay-not-deterministic` + +The order is deliberately structural-first: cheap immutable failures are classified before provisioning failures. Changing the order requires a new protocol version, never an edit after outcomes are known. + +## Non-goals + +- downloading or copying third-party source into this repository; +- treating a static heuristic as proof that a test is causally relevant; +- rating compiler output; +- silently replacing missing evidence with defaults; +- claiming that a frozen corpus has already passed R7. diff --git a/docs/v0.3/r7/README.md b/docs/v0.3/r7/README.md new file mode 100644 index 0000000..b977e6d --- /dev/null +++ b/docs/v0.3/r7/README.md @@ -0,0 +1,18 @@ +# R7 corpus artifacts + +This directory contains metadata and hashes only. Third-party source, dependencies, logs with absolute paths, and compiler outcomes do not belong here. + +- `repositories.json` is the registration frozen before eligibility inspection. +- `preflight-.json` is emitted by the read-only Git scanner. +- provisioning evidence is collected separately from clean base/target environments. +- `corpus-manifest.json` may be created only after exactly 30 candidates have complete evidence. + +From `extension/`: + +```powershell +npm run r7:corpus -- scan +npm run r7:corpus -- complete +npm run r7:corpus -- freeze +``` + +Every output command refuses to overwrite an existing artifact. The scanner never checks out a revision or runs repository code. Provisioning and execution remain separate so a static scan cannot manufacture a passing test result. diff --git a/docs/v0.3/r7/repositories.json b/docs/v0.3/r7/repositories.json new file mode 100644 index 0000000..3ded845 --- /dev/null +++ b/docs/v0.3/r7/repositories.json @@ -0,0 +1,41 @@ +[ + { + "schemaVersion": 1, + "repositoryId": "p-queue", + "url": "https://github.com/sindresorhus/p-queue.git", + "licenseSpdx": "MIT", + "pinnedTip": "180ab9e25cd10b6f548767d7176076b50d25e188", + "nodeVersion": "22.18.0", + "packageManager": "npm", + "packageManagerVersion": "10.9.3", + "installArgv": ["npm", "ci", "--ignore-scripts"], + "testArgv": ["npm", "test"], + "candidateHistoryArgv": ["git", "rev-list", "--first-parent", "180ab9e25cd10b6f548767d7176076b50d25e188"] + }, + { + "schemaVersion": 1, + "repositoryId": "ts-pattern", + "url": "https://github.com/gvergnaud/ts-pattern.git", + "licenseSpdx": "MIT", + "pinnedTip": "c92ca435c7e1827e0fd55c539080ef1bfd6fe3f0", + "nodeVersion": "22.18.0", + "packageManager": "npm", + "packageManagerVersion": "10.9.3", + "installArgv": ["npm", "ci", "--ignore-scripts"], + "testArgv": ["npm", "test", "--", "--runInBand"], + "candidateHistoryArgv": ["git", "rev-list", "--first-parent", "c92ca435c7e1827e0fd55c539080ef1bfd6fe3f0"] + }, + { + "schemaVersion": 1, + "repositoryId": "ajv", + "url": "https://github.com/ajv-validator/ajv.git", + "licenseSpdx": "MIT", + "pinnedTip": "f177fe323420ccb23e1a79445fd470cbf80aee7c", + "nodeVersion": "22.18.0", + "packageManager": "npm", + "packageManagerVersion": "10.9.3", + "installArgv": ["npm", "ci", "--ignore-scripts"], + "testArgv": ["npm", "run", "test-spec", "--", "--forbid-only"], + "candidateHistoryArgv": ["git", "rev-list", "--first-parent", "f177fe323420ccb23e1a79445fd470cbf80aee7c"] + } +] diff --git a/extension/package.json b/extension/package.json index 707d027..4ea5f8f 100644 --- a/extension/package.json +++ b/extension/package.json @@ -259,6 +259,7 @@ "watch": "node esbuild.mjs --watch", "check": "tsc --noEmit", "fixture:runtime": "node scripts/provision-fixture-node.mjs", + "r7:corpus": "node scripts/r7-corpus.mjs", "test": "npm run fixture:runtime && vitest run", "package": "npm run build && vsce package --no-dependencies" }, diff --git a/extension/scripts/r7-corpus.mjs b/extension/scripts/r7-corpus.mjs new file mode 100644 index 0000000..b4f0d54 --- /dev/null +++ b/extension/scripts/r7-corpus.mjs @@ -0,0 +1,22 @@ +import { mkdtemp, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { pathToFileURL } from "node:url"; +import { build } from "esbuild"; + +const root = await mkdtemp(join(tmpdir(), "pureflow-r7-corpus-cli-")); +const outfile = join(root, "cli.cjs"); + +try { + await build({ + entryPoints: ["src/corpus/cli.ts"], + bundle: true, + platform: "node", + format: "cjs", + target: "node22", + outfile, + }); + await import(pathToFileURL(outfile).href); +} finally { + await rm(root, { recursive: true, force: true }); +} diff --git a/extension/src/corpus/cli.ts b/extension/src/corpus/cli.ts new file mode 100644 index 0000000..84f54a2 --- /dev/null +++ b/extension/src/corpus/cli.ts @@ -0,0 +1,52 @@ +import { readFile, writeFile } from "node:fs/promises"; +import { resolve } from "node:path"; +import { completeCandidate, scanRepository, type CandidatePreflight, type ProvisionEvidence } from "./scan"; +import { freezeCorpus, type CandidateFacts, type RepositoryRegistration } from "./freeze"; + +void main(); + +async function main(): Promise { + const [command, ...args] = process.argv.slice(2); + + if (command === "scan" && args.length === 4) { + const repositories = await readJson(args[0]!); + const registration = repositories.find(({ repositoryId }) => repositoryId === args[1]); + if (registration === undefined) throw new Error(`Unknown repository registration: ${args[1]}`); + const drafts = await scanRepository(resolve(args[2]!), registration); + await writeNewJson(args[3]!, drafts); + process.stdout.write(`Scanned ${drafts.length} coarse candidates for ${registration.repositoryId}.\n`); + } else if (command === "complete" && args.length === 3) { + const drafts = await readJson(args[0]!); + const evidence = await readJson>(args[1]!); + const completed = drafts.map((draft) => { + const item = evidence[draft.targetCommit]; + if (item === undefined) throw new Error(`Missing provision evidence for ${draft.targetCommit}`); + return completeCandidate(draft, item); + }); + await writeNewJson(args[2]!, completed); + process.stdout.write(`Completed ${completed.length} eligibility records.\n`); + } else if (command === "freeze" && args.length === 3) { + const repositories = await readJson(args[0]!); + const candidates = await readJson(args[1]!); + const manifest = freezeCorpus(repositories, candidates); + await writeNewJson(args[2]!, manifest); + process.stdout.write(`Frozen ${manifest.patches.length} patches as ${manifest.manifestSha256}.\n`); + } else { + process.stderr.write([ + "Usage:", + " npm run r7:corpus -- scan ", + " npm run r7:corpus -- complete ", + " npm run r7:corpus -- freeze ", + "", + ].join("\n")); + process.exitCode = 2; + } +} + +async function readJson(path: string): Promise { + return JSON.parse(await readFile(resolve(path), "utf8")) as T; +} + +async function writeNewJson(path: string, value: unknown): Promise { + await writeFile(resolve(path), `${JSON.stringify(value, null, 2)}\n`, { encoding: "utf8", flag: "wx" }); +} diff --git a/extension/src/corpus/freeze.ts b/extension/src/corpus/freeze.ts new file mode 100644 index 0000000..895cb1e --- /dev/null +++ b/extension/src/corpus/freeze.ts @@ -0,0 +1,305 @@ +import { canonicalJson, compareUtf8, rawSha256 } from "../rnd/canonical"; + +export const r7ProtocolVersion = "r7-typescript-node-v1" as const; + +export type ExclusionCode = + | "not-adjacent-first-parent" + | "license-not-approved" + | "missing-lockfile" + | "dependency-or-lockfile-change" + | "unsupported-artifact" + | "diff-too-large" + | "snapshot-too-large" + | "unsupported-typescript-boundary" + | "missing-attributed-test" + | "requires-production-capability" + | "network-required-at-execution" + | "base-provision-or-test-failed" + | "target-provision-or-test-failed" + | "replay-not-deterministic"; + +export interface RepositoryRegistration { + schemaVersion: 1; + repositoryId: string; + url: string; + licenseSpdx: string; + pinnedTip: string; + nodeVersion: string; + packageManager: "npm" | "pnpm" | "yarn"; + packageManagerVersion: string; + installArgv: string[]; + testArgv: string[]; + candidateHistoryArgv: string[]; +} + +export interface CandidateFacts { + schemaVersion: 1; + repositoryId: string; + ordinal: number; + baseCommit: string; + targetCommit: string; + adjacentFirstParent: boolean; + licenseApproved: boolean; + lockfilePresent: boolean; + dependencyOrLockfileChanged: boolean; + unsupportedArtifactPresent: boolean; + changedLines: number; + sanitizedBytes: number; + supportedTypescriptBoundary: boolean; + hasAttributedTest: boolean; + requiresProductionCapability: boolean; + executionNeedsNetwork: boolean; + basePassed: boolean; + targetPassed: boolean; + deterministicReplayCount: number; + evidenceSha256: string; +} + +export type CandidateClassification = + | { status: "eligible" } + | { status: "excluded"; code: ExclusionCode }; + +interface FrozenPatch { + repositoryId: string; + ordinal: number; + baseCommit: string; + targetCommit: string; + evidenceSha256: string; + splitKey: string; + cohort: "development" | "held-out"; +} + +interface FrozenExclusion { + repositoryId: string; + ordinal: number; + baseCommit: string; + targetCommit: string; + evidenceSha256: string; + code: ExclusionCode; +} + +interface CorpusManifestCore { + schemaVersion: 1; + protocolVersion: typeof r7ProtocolVersion; + repositories: RepositoryRegistration[]; + patches: FrozenPatch[]; + exclusions: FrozenExclusion[]; +} + +export interface CorpusManifest extends CorpusManifestCore { + manifestSha256: string; +} + +const repositoryKeys = [ + "schemaVersion", + "repositoryId", + "url", + "licenseSpdx", + "pinnedTip", + "nodeVersion", + "packageManager", + "packageManagerVersion", + "installArgv", + "testArgv", + "candidateHistoryArgv", +] as const; + +const candidateKeys = [ + "schemaVersion", + "repositoryId", + "ordinal", + "baseCommit", + "targetCommit", + "adjacentFirstParent", + "licenseApproved", + "lockfilePresent", + "dependencyOrLockfileChanged", + "unsupportedArtifactPresent", + "changedLines", + "sanitizedBytes", + "supportedTypescriptBoundary", + "hasAttributedTest", + "requiresProductionCapability", + "executionNeedsNetwork", + "basePassed", + "targetPassed", + "deterministicReplayCount", + "evidenceSha256", +] as const; + +export function classifyCandidate(candidate: CandidateFacts): CandidateClassification { + validateCandidate(candidate); + if (!candidate.adjacentFirstParent) return excluded("not-adjacent-first-parent"); + if (!candidate.licenseApproved) return excluded("license-not-approved"); + if (!candidate.lockfilePresent) return excluded("missing-lockfile"); + if (candidate.dependencyOrLockfileChanged) return excluded("dependency-or-lockfile-change"); + if (candidate.unsupportedArtifactPresent) return excluded("unsupported-artifact"); + if (candidate.changedLines > 500) return excluded("diff-too-large"); + if (candidate.sanitizedBytes > 25 * 1024 * 1024) return excluded("snapshot-too-large"); + if (!candidate.supportedTypescriptBoundary) return excluded("unsupported-typescript-boundary"); + if (!candidate.hasAttributedTest) return excluded("missing-attributed-test"); + if (candidate.requiresProductionCapability) return excluded("requires-production-capability"); + if (candidate.executionNeedsNetwork) return excluded("network-required-at-execution"); + if (!candidate.basePassed) return excluded("base-provision-or-test-failed"); + if (!candidate.targetPassed) return excluded("target-provision-or-test-failed"); + if (candidate.deterministicReplayCount !== 3) return excluded("replay-not-deterministic"); + return { status: "eligible" }; +} + +export function freezeCorpus( + registrations: readonly RepositoryRegistration[], + candidates: readonly CandidateFacts[], +): CorpusManifest { + if (registrations.length < 3) throw new Error("R7 requires at least three registered repositories"); + const repositories = registrations.map(copyRepository); + repositories.sort((left, right) => compareUtf8(left.repositoryId, right.repositoryId)); + const repositoryById = new Map(); + for (const repository of repositories) { + validateRepositoryRegistration(repository); + if (repositoryById.has(repository.repositoryId)) throw new Error(`Duplicate repository: ${repository.repositoryId}`); + repositoryById.set(repository.repositoryId, repository); + } + + const seen = new Set(); + const grouped = new Map(); + for (const candidate of candidates) { + validateCandidate(candidate); + if (!repositoryById.has(candidate.repositoryId)) throw new Error(`Unregistered repository: ${candidate.repositoryId}`); + const identity = `${candidate.repositoryId}\0${candidate.targetCommit}`; + if (seen.has(identity)) throw new Error(`Duplicate candidate: ${candidate.repositoryId}/${candidate.targetCommit}`); + seen.add(identity); + const group = grouped.get(candidate.repositoryId) ?? []; + group.push(structuredClone(candidate)); + grouped.set(candidate.repositoryId, group); + } + + const selected: Array> = []; + const exclusions: FrozenExclusion[] = []; + for (const repository of repositories) { + const group = grouped.get(repository.repositoryId) ?? []; + group.sort((left, right) => left.ordinal - right.ordinal); + let accepted = 0; + for (const candidate of group) { + if (accepted === 10) break; + const result = classifyCandidate(candidate); + if (result.status === "excluded") { + exclusions.push({ + repositoryId: candidate.repositoryId, + ordinal: candidate.ordinal, + baseCommit: candidate.baseCommit, + targetCommit: candidate.targetCommit, + evidenceSha256: candidate.evidenceSha256, + code: result.code, + }); + continue; + } + selected.push({ + repositoryId: candidate.repositoryId, + ordinal: candidate.ordinal, + baseCommit: candidate.baseCommit, + targetCommit: candidate.targetCommit, + evidenceSha256: candidate.evidenceSha256, + }); + accepted += 1; + } + } + + if (selected.length !== 30) throw new Error(`R7 corpus must contain exactly 30 eligible patches; found ${selected.length}`); + if (new Set(selected.map(({ repositoryId }) => repositoryId)).size < 3) { + throw new Error("R7 corpus must contain patches from at least three repositories"); + } + + const patches: FrozenPatch[] = selected.map((candidate) => { + const repository = repositoryById.get(candidate.repositoryId)!; + return { + ...candidate, + splitKey: rawSha256(`pureflow/${r7ProtocolVersion}\n${repository.url}\n${candidate.targetCommit}`), + cohort: "held-out", + }; + }); + patches.sort((left, right) => compareUtf8(left.splitKey, right.splitKey)); + patches.forEach((patch, index) => { + patch.cohort = index < 12 ? "development" : "held-out"; + }); + exclusions.sort((left, right) => + compareUtf8(left.repositoryId, right.repositoryId) || left.ordinal - right.ordinal, + ); + + const core: CorpusManifestCore = { + schemaVersion: 1, + protocolVersion: r7ProtocolVersion, + repositories, + patches, + exclusions, + }; + return { + ...core, + manifestSha256: rawSha256(`pureflow/r7-corpus-manifest-v1\n${canonicalJson(core)}`), + }; +} + +function excluded(code: ExclusionCode): CandidateClassification { + return { status: "excluded", code }; +} + +function copyRepository(repository: RepositoryRegistration): RepositoryRegistration { + assertExactKeys(repository, repositoryKeys, "repository registration"); + return { + ...structuredClone(repository), + installArgv: [...repository.installArgv], + testArgv: [...repository.testArgv], + candidateHistoryArgv: [...repository.candidateHistoryArgv], + }; +} + +export function validateRepositoryRegistration(repository: RepositoryRegistration): void { + assertExactKeys(repository, repositoryKeys, "repository registration"); + if (repository.schemaVersion !== 1) throw new Error("Unsupported repository registration schema"); + if (!/^[a-z0-9][a-z0-9-]{1,62}$/.test(repository.repositoryId)) throw new Error("Invalid repositoryId"); + const url = new URL(repository.url); + if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash) throw new Error("Repository URL must be credential-free HTTPS"); + assertOid(repository.pinnedTip, "pinnedTip"); + if (!repository.licenseSpdx || !repository.nodeVersion || !repository.packageManagerVersion) throw new Error("Repository metadata is incomplete"); + assertArgv(repository.installArgv, "installArgv"); + assertArgv(repository.testArgv, "testArgv"); + assertArgv(repository.candidateHistoryArgv, "candidateHistoryArgv"); + if (repository.candidateHistoryArgv[0] !== "git" || !repository.candidateHistoryArgv.includes("--first-parent")) { + throw new Error("Candidate history command must be a first-parent Git walk"); + } +} + +function validateCandidate(candidate: CandidateFacts): void { + assertExactKeys(candidate, candidateKeys, "candidate facts"); + if (candidate.schemaVersion !== 1) throw new Error("Unsupported candidate schema"); + if (!/^[a-z0-9][a-z0-9-]{1,62}$/.test(candidate.repositoryId)) throw new Error("Invalid repositoryId"); + if (!Number.isSafeInteger(candidate.ordinal) || candidate.ordinal < 1 || candidate.ordinal > 60) { + throw new Error("Candidate ordinal must be between 1 and 60"); + } + assertOid(candidate.baseCommit, "baseCommit"); + assertOid(candidate.targetCommit, "targetCommit"); + if (candidate.baseCommit === candidate.targetCommit) throw new Error("Candidate revisions must differ"); + if (!Number.isSafeInteger(candidate.changedLines) || candidate.changedLines < 0) throw new Error("Invalid changedLines"); + if (!Number.isSafeInteger(candidate.sanitizedBytes) || candidate.sanitizedBytes < 0) throw new Error("Invalid sanitizedBytes"); + if (!Number.isSafeInteger(candidate.deterministicReplayCount) || candidate.deterministicReplayCount < 0) { + throw new Error("Invalid deterministicReplayCount"); + } + if (!/^[0-9a-f]{64}$/.test(candidate.evidenceSha256)) throw new Error("Invalid evidenceSha256"); +} + +function assertArgv(argv: readonly string[], label: string): void { + if (!Array.isArray(argv) || argv.length === 0 || argv.some((part) => typeof part !== "string" || !part || /[\0\r\n]/.test(part))) { + throw new Error(`${label} must be a non-empty argument array`); + } +} + +function assertOid(value: string, label: string): void { + if (!/^[0-9a-f]{40}$/.test(value)) throw new Error(`${label} must be a full lowercase Git SHA-1`); +} + +function assertExactKeys(value: object, allowed: readonly string[], label: string): void { + const expected = new Set(allowed); + const actual = Reflect.ownKeys(value); + if (actual.length !== expected.size || actual.some((key) => typeof key !== "string" || !expected.has(key))) { + throw new Error(`${label} contains unknown or missing fields`); + } +} diff --git a/extension/src/corpus/index.ts b/extension/src/corpus/index.ts new file mode 100644 index 0000000..fa36cae --- /dev/null +++ b/extension/src/corpus/index.ts @@ -0,0 +1,2 @@ +export * from "./freeze"; +export * from "./scan"; diff --git a/extension/src/corpus/scan.ts b/extension/src/corpus/scan.ts new file mode 100644 index 0000000..8652a21 --- /dev/null +++ b/extension/src/corpus/scan.ts @@ -0,0 +1,309 @@ +import { execFile } from "node:child_process"; +import { resolve } from "node:path"; +import { promisify } from "node:util"; +import { GitRevisionDiffReader } from "../change/diff"; +import { extractChangedSymbols } from "../change/symbols"; +import { canonicalHash, canonicalJson, compareUtf8, rawSha256 } from "../rnd/canonical"; +import { validateRepositoryRegistration, type CandidateFacts, type RepositoryRegistration } from "./freeze"; + +const runFile = promisify(execFile); +const lockfiles = ["package-lock.json", "pnpm-lock.yaml", "yarn.lock"] as const; + +export interface CandidatePreflight { + schemaVersion: 1; + repositoryId: string; + ordinal: number; + baseCommit: string; + targetCommit: string; + adjacentFirstParent: boolean; + lockfilePresent: boolean; + dependencyOrLockfileChanged: boolean; + unsupportedArtifactPresent: boolean; + changedLines: number; + sourceTreeBytes: number; + supportedTypescriptBoundary: boolean; + hasAttributedTest: boolean; + changedSourcePaths: string[]; + changedTestPaths: string[]; + preflightSha256: string; +} + +export interface ProvisionEvidence { + schemaVersion: 1; + licenseApproved: boolean; + sanitizedBytes: number; + requiresProductionCapability: boolean; + executionNeedsNetwork: boolean; + basePassed: boolean; + targetPassed: boolean; + deterministicReplayCount: number; + provisionEvidenceSha256: string; +} + +const provisionKeys = [ + "schemaVersion", + "licenseApproved", + "sanitizedBytes", + "requiresProductionCapability", + "executionNeedsNetwork", + "basePassed", + "targetPassed", + "deterministicReplayCount", + "provisionEvidenceSha256", +] as const; + +export function isCoarseCandidate(paths: readonly string[]): boolean { + return paths.some(isSourcePath) && paths.some(isTestPath); +} + +export async function scanRepository( + repositoryPath: string, + registration: RepositoryRegistration, +): Promise { + validateRepositoryRegistration(registration); + const root = resolve(repositoryPath); + const pinnedTip = await git(root, ["rev-parse", "--verify", `${registration.pinnedTip}^{commit}`]); + if (pinnedTip !== registration.pinnedTip) throw new Error("Pinned repository tip is unavailable or changed"); + if (registration.candidateHistoryArgv[0] !== "git") throw new Error("Candidate history executable must be git"); + + const history = (await git(root, registration.candidateHistoryArgv.slice(1))) + .split(/\r?\n/) + .filter(Boolean); + if (history[0] !== registration.pinnedTip) throw new Error("Candidate history does not start at the pinned tip"); + + const drafts: CandidatePreflight[] = []; + let ordinal = 0; + for (const targetCommit of history) { + const parents = (await git(root, ["rev-list", "--parents", "-n", "1", targetCommit])).split(" "); + if (parents.length < 2) continue; + const baseCommit = parents[1]!; + const paths = splitNul(await git(root, ["diff", "--name-only", "-z", baseCommit, targetCommit], false)); + if (!isCoarseCandidate(paths)) continue; + ordinal += 1; + if (ordinal > 60) break; + drafts.push(await inspectCandidate(root, registration.repositoryId, ordinal, baseCommit, targetCommit, parents, paths)); + } + return drafts; +} + +export function completeCandidate(draft: CandidatePreflight, evidence: ProvisionEvidence): CandidateFacts { + assertExactKeys(evidence, provisionKeys, "provision evidence"); + if (evidence.schemaVersion !== 1) throw new Error("Unsupported provision evidence schema"); + if (!Number.isSafeInteger(evidence.sanitizedBytes) || evidence.sanitizedBytes < 0) throw new Error("Invalid sanitizedBytes"); + if (!Number.isSafeInteger(evidence.deterministicReplayCount) || evidence.deterministicReplayCount < 0) { + throw new Error("Invalid deterministicReplayCount"); + } + assertSha256(evidence.provisionEvidenceSha256, "provisionEvidenceSha256"); + const facts = { + schemaVersion: 1 as const, + repositoryId: draft.repositoryId, + ordinal: draft.ordinal, + baseCommit: draft.baseCommit, + targetCommit: draft.targetCommit, + adjacentFirstParent: draft.adjacentFirstParent, + licenseApproved: evidence.licenseApproved, + lockfilePresent: draft.lockfilePresent, + dependencyOrLockfileChanged: draft.dependencyOrLockfileChanged, + unsupportedArtifactPresent: draft.unsupportedArtifactPresent, + changedLines: draft.changedLines, + sanitizedBytes: evidence.sanitizedBytes, + supportedTypescriptBoundary: draft.supportedTypescriptBoundary, + hasAttributedTest: draft.hasAttributedTest, + requiresProductionCapability: evidence.requiresProductionCapability, + executionNeedsNetwork: evidence.executionNeedsNetwork, + basePassed: evidence.basePassed, + targetPassed: evidence.targetPassed, + deterministicReplayCount: evidence.deterministicReplayCount, + }; + return { + ...facts, + evidenceSha256: rawSha256(`pureflow/r7-candidate-evidence-v1\n${canonicalJson({ + facts, + preflightSha256: draft.preflightSha256, + provisionEvidenceSha256: evidence.provisionEvidenceSha256, + })}`), + }; +} + +async function inspectCandidate( + root: string, + repositoryId: string, + ordinal: number, + baseCommit: string, + targetCommit: string, + parents: string[], + paths: string[], +): Promise { + const changedSourcePaths = paths.filter(isSourcePath).sort(compareUtf8); + const changedTestPaths = paths.filter(isTestPath).sort(compareUtf8); + const [baseTree, targetTree, numstat, baseLocks, targetLocks] = await Promise.all([ + inspectTree(root, baseCommit), + inspectTree(root, targetCommit), + git(root, ["diff", "--numstat", baseCommit, targetCommit], false), + existingLockfiles(root, baseCommit), + existingLockfiles(root, targetCommit), + ]); + const sharedLocks = baseLocks.filter((path) => targetLocks.includes(path)); + const dependencyOrLockfileChanged = paths.some((path) => path === "package.json" || lockfiles.includes(path as typeof lockfiles[number])); + const stats = parseNumstat(numstat); + let supportedTypescriptBoundary = false; + let hasAttributedTest = false; + let extractionFailed = false; + + try { + const diff = await new GitRevisionDiffReader().read(root, baseCommit, targetCommit); + const units = diff.files + .filter((file) => isSourcePath(file.afterPath ?? file.beforePath ?? "")) + .flatMap((file) => extractChangedSymbols(file).units); + supportedTypescriptBoundary = units.length > 0; + const symbols = [...new Set(units.map(({ symbol }) => symbol.split(".").at(-1)!).filter((symbol) => symbol !== ""))]; + const tests = diff.files.filter((file) => isTestPath(file.afterPath ?? file.beforePath ?? "")); + hasAttributedTest = tests.some((file) => { + const text = `${file.beforeText ?? ""}\n${file.afterText ?? ""}`; + return symbols.some((symbol) => new RegExp(`\\b${escapeRegex(symbol)}\\b`).test(text)) || + changedSourcePaths.some((path) => text.includes(sourceStem(path))); + }); + } catch { + extractionFailed = true; + } + + const draft = { + schemaVersion: 1 as const, + repositoryId, + ordinal, + baseCommit, + targetCommit, + adjacentFirstParent: parents[0] === targetCommit && parents[1] === baseCommit, + lockfilePresent: sharedLocks.length > 0, + dependencyOrLockfileChanged, + unsupportedArtifactPresent: extractionFailed || stats.binary || baseTree.unsupported || targetTree.unsupported, + changedLines: stats.changedLines, + sourceTreeBytes: targetTree.bytes, + supportedTypescriptBoundary, + hasAttributedTest, + changedSourcePaths, + changedTestPaths, + }; + return { + ...draft, + preflightSha256: canonicalHash("r7-candidate-preflight", draft), + }; +} + +async function inspectTree(root: string, revision: string): Promise<{ bytes: number; unsupported: boolean }> { + const records = splitNul(await git(root, ["ls-tree", "-r", "-l", "-z", revision], false)); + let bytes = 0; + let unsupported = false; + for (const record of records) { + const match = /^(\d{6}) (\w+) ([0-9a-f]+)\s+(-|\d+)\t([\s\S]+)$/.exec(record); + if (!match) throw new Error("Malformed Git tree record"); + const [, mode, type, , size, path] = match; + if (type !== "blob" || mode === "120000" || mode === "160000") unsupported = true; + if (size !== "-") bytes += Number(size); + if (isUnsupportedPath(path!)) unsupported = true; + if (isSnapshotPath(path!) && size !== "-" && Number(size) > 1024 * 1024) unsupported = true; + } + if (!Number.isSafeInteger(bytes)) throw new Error("Repository tree size exceeds safe integer range"); + return { bytes, unsupported }; +} + +async function existingLockfiles(root: string, revision: string): Promise { + const result: string[] = []; + for (const path of lockfiles) { + if (await gitObjectExists(root, `${revision}:${path}`)) result.push(path); + } + return result; +} + +async function gitObjectExists(root: string, spec: string): Promise { + try { + await runFile("git", ["cat-file", "-e", spec], { cwd: root, windowsHide: true, env: gitEnvironment() }); + return true; + } catch { + return false; + } +} + +function parseNumstat(value: string): { changedLines: number; binary: boolean } { + let changedLines = 0; + let binary = false; + for (const line of value.split(/\r?\n/).filter(Boolean)) { + const [added, deleted] = line.split("\t", 3); + if (added === "-" || deleted === "-") { + binary = true; + continue; + } + const count = Number(added) + Number(deleted); + if (!Number.isSafeInteger(count) || count < 0) throw new Error("Malformed Git numstat"); + changedLines += count; + } + return { changedLines, binary }; +} + +function isSourcePath(path: string): boolean { + return /\.(?:ts|tsx)$/.test(path) && !path.endsWith(".d.ts") && !isTestPath(path) && !isUnsupportedPath(path); +} + +function isTestPath(path: string): boolean { + return /(?:^|\/)(?:test|tests|__tests__)(?:\/|$)/i.test(path) || /\.(?:test|spec)\.(?:ts|tsx)$/.test(path); +} + +function isUnsupportedPath(path: string): boolean { + return /(?:^|\/)(?:node_modules|vendor|vendors|dist|build|generated|coverage)(?:\/|$)/i.test(path); +} + +function isSnapshotPath(path: string): boolean { + return /(?:^|\/)(?:__snapshots__)(?:\/|$)/i.test(path) || /\.snap$/i.test(path); +} + +function sourceStem(path: string): string { + return path.split("/").at(-1)!.replace(/\.(?:ts|tsx)$/, ""); +} + +function escapeRegex(value: string): string { + return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} + +function splitNul(value: string): string[] { + const records = value.split("\0"); + if (records.at(-1) === "") records.pop(); + return records; +} + +async function git(root: string, args: string[], trim = true): Promise { + const { stdout } = await runFile("git", args, { + cwd: root, + encoding: "utf8", + windowsHide: true, + maxBuffer: 16 * 1024 * 1024, + env: gitEnvironment(), + }); + return trim ? stdout.trim() : stdout; +} + +function gitEnvironment(): NodeJS.ProcessEnv { + const env: NodeJS.ProcessEnv = { + GIT_CONFIG_GLOBAL: process.platform === "win32" ? "NUL" : "/dev/null", + GIT_CONFIG_NOSYSTEM: "1", + GIT_TERMINAL_PROMPT: "0", + GIT_OPTIONAL_LOCKS: "0", + LANG: "C", + LC_ALL: "C", + }; + for (const name of ["PATH", "Path", "SystemRoot", "WINDIR", "COMSPEC", "PATHEXT", "TMP", "TEMP"]) { + if (process.env[name] !== undefined) env[name] = process.env[name]; + } + return env; +} + +function assertSha256(value: string, label: string): void { + if (!/^[0-9a-f]{64}$/.test(value)) throw new Error(`${label} must be lowercase SHA-256`); +} + +function assertExactKeys(value: object, allowed: readonly string[], label: string): void { + const expected = new Set(allowed); + const actual = Reflect.ownKeys(value); + if (actual.length !== expected.size || actual.some((key) => typeof key !== "string" || !expected.has(key))) { + throw new Error(`${label} contains unknown or missing fields`); + } +} diff --git a/extension/test/corpus-freeze.test.ts b/extension/test/corpus-freeze.test.ts new file mode 100644 index 0000000..e8dbba9 --- /dev/null +++ b/extension/test/corpus-freeze.test.ts @@ -0,0 +1,129 @@ +import { createHash } from "node:crypto"; +import { describe, expect, it } from "vitest"; +import { + classifyCandidate, + freezeCorpus, + type CandidateFacts, + type RepositoryRegistration, +} from "../src/corpus/freeze"; + +describe("R7 corpus freeze", () => { + it("assigns exactly one first-match exclusion code", () => { + const facts = candidate("repo-a", 1); + facts.adjacentFirstParent = false; + facts.targetPassed = false; + + expect(classifyCandidate(facts)).toEqual({ + status: "excluded", + code: "not-adjacent-first-parent", + }); + }); + + it("freezes 30 patches into the preregistered 12/18 split", () => { + const repositories = [repository("repo-a", "a"), repository("repo-b", "b"), repository("repo-c", "c")]; + const candidates = repositories.flatMap((repo, repoIndex) => + Array.from({ length: 10 }, (_, index) => candidate(repo.repositoryId, index + 1, repoIndex * 10 + index + 1)), + ); + + const manifest = freezeCorpus(repositories, candidates); + + expect(manifest.protocolVersion).toBe("r7-typescript-node-v1"); + expect(manifest.patches).toHaveLength(30); + expect(manifest.patches.filter(({ cohort }) => cohort === "development")).toHaveLength(12); + expect(manifest.patches.filter(({ cohort }) => cohort === "held-out")).toHaveLength(18); + expect(manifest.patches.map(({ splitKey }) => splitKey)).toEqual( + [...manifest.patches.map(({ splitKey }) => splitKey)].sort(), + ); + const first = manifest.patches[0]!; + const repo = repositories.find(({ repositoryId }) => repositoryId === first.repositoryId)!; + expect(first.splitKey).toBe(createHash("sha256") + .update(`pureflow/r7-typescript-node-v1\n${repo.url}\n${first.targetCommit}`) + .digest("hex")); + expect(manifest.manifestSha256).toMatch(/^[0-9a-f]{64}$/); + expect(JSON.stringify(manifest)).not.toContain("compiler"); + expect(JSON.stringify(manifest)).not.toContain("judge"); + }); + + it("uses the first ten eligible candidates per repository and preserves exclusions", () => { + const repositories = [repository("repo-a", "a"), repository("repo-b", "b"), repository("repo-c", "c")]; + const candidates = repositories.flatMap((repo, repoIndex) => + Array.from({ length: 12 }, (_, index) => { + const facts = candidate(repo.repositoryId, index + 1, repoIndex * 20 + index + 1); + if (index === 0) facts.hasAttributedTest = false; + return facts; + }), + ); + + const manifest = freezeCorpus(repositories, candidates); + + expect(manifest.patches).toHaveLength(30); + expect(manifest.exclusions).toHaveLength(3); + expect(manifest.exclusions.every(({ code }) => code === "missing-attributed-test")).toBe(true); + for (const repo of repositories) { + expect(manifest.patches.filter(({ repositoryId }) => repositoryId === repo.repositoryId)).toHaveLength(10); + } + }); + + it("refuses an incomplete, over-scanned, duplicate, or outcome-tainted corpus", () => { + const repositories = [repository("repo-a", "a"), repository("repo-b", "b"), repository("repo-c", "c")]; + const incomplete = repositories.flatMap((repo, repoIndex) => + Array.from({ length: repo.repositoryId === "repo-c" ? 9 : 10 }, (_, index) => candidate(repo.repositoryId, index + 1, repoIndex * 20 + index + 1)), + ); + expect(() => freezeCorpus(repositories, incomplete)).toThrow("exactly 30"); + + const overScanned = candidate("repo-a", 61); + expect(() => freezeCorpus(repositories, [...incomplete, overScanned])).toThrow("ordinal"); + + const complete = repositories.flatMap((repo, repoIndex) => + Array.from({ length: 10 }, (_, index) => candidate(repo.repositoryId, index + 1, repoIndex * 20 + index + 1)), + ); + expect(() => freezeCorpus(repositories, [...complete, structuredClone(complete[0]!)])).toThrow("Duplicate candidate"); + + const tainted = structuredClone(complete) as Array; + tainted[0]!.compilerSucceeded = true; + expect(() => freezeCorpus(repositories, tainted)).toThrow("unknown or missing fields"); + }); +}); + +function repository(repositoryId: string, suffix: string): RepositoryRegistration { + return { + schemaVersion: 1, + repositoryId, + url: `https://github.com/example/project-${suffix}.git`, + licenseSpdx: "MIT", + pinnedTip: suffix.repeat(40), + nodeVersion: "22.18.0", + packageManager: "npm", + packageManagerVersion: "10.9.3", + installArgv: ["npm", "ci", "--ignore-scripts"], + testArgv: ["npm", "test", "--", "--runInBand"], + candidateHistoryArgv: ["git", "rev-list", "--first-parent", "--max-count=500", suffix.repeat(40)], + }; +} + +function candidate(repositoryId: string, ordinal: number, seed = ordinal): CandidateFacts { + const targetCommit = seed.toString(16).padStart(40, "0"); + const baseCommit = (seed + 10_000).toString(16).padStart(40, "0"); + return { + schemaVersion: 1, + repositoryId, + ordinal, + baseCommit, + targetCommit, + adjacentFirstParent: true, + licenseApproved: true, + lockfilePresent: true, + dependencyOrLockfileChanged: false, + unsupportedArtifactPresent: false, + changedLines: 42, + sanitizedBytes: 1024, + supportedTypescriptBoundary: true, + hasAttributedTest: true, + requiresProductionCapability: false, + executionNeedsNetwork: false, + basePassed: true, + targetPassed: true, + deterministicReplayCount: 3, + evidenceSha256: seed.toString(16).padStart(64, "0"), + }; +} diff --git a/extension/test/corpus-scan.test.ts b/extension/test/corpus-scan.test.ts new file mode 100644 index 0000000..6e1d4c3 --- /dev/null +++ b/extension/test/corpus-scan.test.ts @@ -0,0 +1,126 @@ +import { execFile } from "node:child_process"; +import { mkdtemp, rm, writeFile, mkdir } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { promisify } from "node:util"; +import { afterEach, describe, expect, it } from "vitest"; +import { + completeCandidate, + isCoarseCandidate, + scanRepository, + type ProvisionEvidence, +} from "../src/corpus/scan"; +import type { RepositoryRegistration } from "../src/corpus/freeze"; + +const run = promisify(execFile); +const roots: string[] = []; + +afterEach(async () => { + await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true }))); +}); + +describe("R7 corpus scanner", () => { + it("recognizes a source plus test patch without counting tests as source", () => { + expect(isCoarseCandidate(["src/cache.ts", "test/cache.test.ts"])).toBe(true); + expect(isCoarseCandidate(["test/cache.ts", "test/cache.test.ts"])).toBe(false); + expect(isCoarseCandidate(["src/cache.ts", "README.md"])).toBe(false); + expect(isCoarseCandidate(["src/types.d.ts", "src/types.test.ts"])).toBe(false); + }); + + it("walks adjacent first-parent patches and emits outcome-free preflight evidence", async () => { + const root = await fixtureRepository(); + const tip = await git(root, ["rev-parse", "HEAD"]); + const registration = repository(tip); + + const result = await scanRepository(root, registration); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + repositoryId: "fixture-repo", + ordinal: 1, + targetCommit: tip, + adjacentFirstParent: true, + lockfilePresent: true, + dependencyOrLockfileChanged: false, + unsupportedArtifactPresent: false, + changedLines: 4, + supportedTypescriptBoundary: true, + hasAttributedTest: true, + changedSourcePaths: ["src/add.ts"], + changedTestPaths: ["test/add.test.ts"], + }); + expect(JSON.stringify(result)).not.toContain("basePassed"); + expect(JSON.stringify(result)).not.toContain("compiler"); + }, 30_000); + + it("requires explicit provisioning evidence before a candidate can be frozen", async () => { + const root = await fixtureRepository(); + const tip = await git(root, ["rev-parse", "HEAD"]); + const [draft] = await scanRepository(root, repository(tip)); + const evidence: ProvisionEvidence = { + schemaVersion: 1, + licenseApproved: true, + sanitizedBytes: 4096, + requiresProductionCapability: false, + executionNeedsNetwork: false, + basePassed: true, + targetPassed: true, + deterministicReplayCount: 3, + provisionEvidenceSha256: "f".repeat(64), + }; + + const completed = completeCandidate(draft!, evidence); + + expect(completed).toMatchObject({ + repositoryId: "fixture-repo", + basePassed: true, + targetPassed: true, + deterministicReplayCount: 3, + }); + expect(completed.evidenceSha256).toMatch(/^[0-9a-f]{64}$/); + expect(() => completeCandidate(draft!, { ...evidence, targetPassed: false, judgePassed: true } as ProvisionEvidence)).toThrow( + "unknown or missing fields", + ); + }, 30_000); +}); + +async function fixtureRepository(): Promise { + const root = await mkdtemp(join(tmpdir(), "pureflow-corpus-scan-")); + roots.push(root); + await git(root, ["init"]); + await git(root, ["config", "user.name", "PureFlow Test"]); + await git(root, ["config", "user.email", "test@pureflow.local"]); + await mkdir(join(root, "src")); + await mkdir(join(root, "test")); + await writeFile(join(root, "package-lock.json"), "{}\n"); + await writeFile(join(root, "src", "add.ts"), "export function add(a: number, b: number) {\n return a + b;\n}\n"); + await writeFile(join(root, "test", "add.test.ts"), "import { add } from '../src/add';\nvoid add(1, 2);\n"); + await git(root, ["add", "."]); + await git(root, ["commit", "-m", "base"]); + await writeFile(join(root, "src", "add.ts"), "export function add(a: number, b: number) {\n return Number(a) + Number(b);\n}\n"); + await writeFile(join(root, "test", "add.test.ts"), "import { add } from '../src/add';\nvoid add('1' as never, 2);\n"); + await git(root, ["add", "."]); + await git(root, ["commit", "-m", "change behavior"]); + return root; +} + +function repository(pinnedTip: string): RepositoryRegistration { + return { + schemaVersion: 1, + repositoryId: "fixture-repo", + url: "https://github.com/example/fixture-repo.git", + licenseSpdx: "MIT", + pinnedTip, + nodeVersion: "22.18.0", + packageManager: "npm", + packageManagerVersion: "10.9.3", + installArgv: ["npm", "ci", "--ignore-scripts"], + testArgv: ["npm", "test"], + candidateHistoryArgv: ["git", "rev-list", "--first-parent", "--max-count=500", pinnedTip], + }; +} + +async function git(root: string, args: string[]): Promise { + const { stdout } = await run("git", args, { cwd: root, encoding: "utf8", windowsHide: true }); + return stdout.trim(); +} From 03d2ed818f8bd0b692cf9a7267936ef598137469 Mon Sep 17 00:00:00 2001 From: Error Lover Date: Sat, 1 Aug 2026 03:43:01 +0300 Subject: [PATCH 02/21] docs: register R7 replacement repositories --- docs/v0.3/r7/README.md | 2 ++ docs/v0.3/r7/repositories.json | 39 ++++++++++++++++++++++++++++ extension/src/corpus/freeze.ts | 4 +-- extension/test/corpus-freeze.test.ts | 17 ++++++++++++ 4 files changed, 60 insertions(+), 2 deletions(-) diff --git a/docs/v0.3/r7/README.md b/docs/v0.3/r7/README.md index b977e6d..6af732e 100644 --- a/docs/v0.3/r7/README.md +++ b/docs/v0.3/r7/README.md @@ -16,3 +16,5 @@ npm run r7:corpus -- freeze compareUtf8(left.repositoryId, right.repositoryId)); const repositoryById = new Map(); for (const repository of repositories) { validateRepositoryRegistration(repository); @@ -176,11 +175,12 @@ export function freezeCorpus( const selected: Array> = []; const exclusions: FrozenExclusion[] = []; for (const repository of repositories) { + if (selected.length === 30) break; const group = grouped.get(repository.repositoryId) ?? []; group.sort((left, right) => left.ordinal - right.ordinal); let accepted = 0; for (const candidate of group) { - if (accepted === 10) break; + if (accepted === 10 || selected.length === 30) break; const result = classifyCandidate(candidate); if (result.status === "excluded") { exclusions.push({ diff --git a/extension/test/corpus-freeze.test.ts b/extension/test/corpus-freeze.test.ts index e8dbba9..4e78829 100644 --- a/extension/test/corpus-freeze.test.ts +++ b/extension/test/corpus-freeze.test.ts @@ -64,6 +64,23 @@ describe("R7 corpus freeze", () => { } }); + it("uses replacement repositories in registration order and stops when 30 exist", () => { + const repositories = [repository("repo-a", "a"), repository("repo-b", "b"), repository("repo-c", "c"), repository("repo-d", "d")]; + const candidates = repositories.flatMap((repo, repoIndex) => + Array.from({ length: 10 }, (_, index) => { + const facts = candidate(repo.repositoryId, index + 1, repoIndex * 20 + index + 1); + if (repo.repositoryId === "repo-a") facts.lockfilePresent = false; + return facts; + }), + ); + + const manifest = freezeCorpus(repositories, candidates); + + expect(manifest.patches.some(({ repositoryId }) => repositoryId === "repo-a")).toBe(false); + expect(manifest.patches.filter(({ repositoryId }) => repositoryId === "repo-d")).toHaveLength(10); + expect(manifest.exclusions).toHaveLength(10); + }); + it("refuses an incomplete, over-scanned, duplicate, or outcome-tainted corpus", () => { const repositories = [repository("repo-a", "a"), repository("repo-b", "b"), repository("repo-c", "c")]; const incomplete = repositories.flatMap((repo, repoIndex) => From ecfa7d40ca4a65f5f33b27e511657d2240228ee0 Mon Sep 17 00:00:00 2001 From: Error Lover Date: Sat, 1 Aug 2026 03:48:57 +0300 Subject: [PATCH 03/21] fix: represent deferred R7 provisioning honestly --- docs/v0.3/r7/README.md | 2 ++ docs/v0.3/r7/repositories.json | 18 ++++++------ extension/src/corpus/cli.ts | 5 ++-- extension/src/corpus/freeze.ts | 21 ++++++++++---- extension/src/corpus/scan.ts | 45 +++++++++++++++++++++++++++--- extension/test/corpus-scan.test.ts | 2 +- 6 files changed, 70 insertions(+), 23 deletions(-) diff --git a/docs/v0.3/r7/README.md b/docs/v0.3/r7/README.md index 6af732e..674d2b0 100644 --- a/docs/v0.3/r7/README.md +++ b/docs/v0.3/r7/README.md @@ -18,3 +18,5 @@ npm run r7:corpus -- freeze { const evidence = await readJson>(args[1]!); const completed = drafts.map((draft) => { const item = evidence[draft.targetCommit]; - if (item === undefined) throw new Error(`Missing provision evidence for ${draft.targetCommit}`); - return completeCandidate(draft, item); + return item === undefined ? deferCandidate(draft) : completeCandidate(draft, item); }); await writeNewJson(args[2]!, completed); process.stdout.write(`Completed ${completed.length} eligibility records.\n`); diff --git a/extension/src/corpus/freeze.ts b/extension/src/corpus/freeze.ts index 7bf5b62..5b23b7e 100644 --- a/extension/src/corpus/freeze.ts +++ b/extension/src/corpus/freeze.ts @@ -47,11 +47,11 @@ export interface CandidateFacts { sanitizedBytes: number; supportedTypescriptBoundary: boolean; hasAttributedTest: boolean; - requiresProductionCapability: boolean; - executionNeedsNetwork: boolean; - basePassed: boolean; - targetPassed: boolean; - deterministicReplayCount: number; + requiresProductionCapability: boolean | null; + executionNeedsNetwork: boolean | null; + basePassed: boolean | null; + targetPassed: boolean | null; + deterministicReplayCount: number | null; evidenceSha256: string; } @@ -138,6 +138,15 @@ export function classifyCandidate(candidate: CandidateFacts): CandidateClassific if (candidate.sanitizedBytes > 25 * 1024 * 1024) return excluded("snapshot-too-large"); if (!candidate.supportedTypescriptBoundary) return excluded("unsupported-typescript-boundary"); if (!candidate.hasAttributedTest) return excluded("missing-attributed-test"); + if ( + candidate.requiresProductionCapability === null || + candidate.executionNeedsNetwork === null || + candidate.basePassed === null || + candidate.targetPassed === null || + candidate.deterministicReplayCount === null + ) { + throw new Error(`Missing provision evidence for structurally eligible candidate ${candidate.repositoryId}/${candidate.targetCommit}`); + } if (candidate.requiresProductionCapability) return excluded("requires-production-capability"); if (candidate.executionNeedsNetwork) return excluded("network-required-at-execution"); if (!candidate.basePassed) return excluded("base-provision-or-test-failed"); @@ -280,7 +289,7 @@ function validateCandidate(candidate: CandidateFacts): void { if (candidate.baseCommit === candidate.targetCommit) throw new Error("Candidate revisions must differ"); if (!Number.isSafeInteger(candidate.changedLines) || candidate.changedLines < 0) throw new Error("Invalid changedLines"); if (!Number.isSafeInteger(candidate.sanitizedBytes) || candidate.sanitizedBytes < 0) throw new Error("Invalid sanitizedBytes"); - if (!Number.isSafeInteger(candidate.deterministicReplayCount) || candidate.deterministicReplayCount < 0) { + if (candidate.deterministicReplayCount !== null && (!Number.isSafeInteger(candidate.deterministicReplayCount) || candidate.deterministicReplayCount < 0)) { throw new Error("Invalid deterministicReplayCount"); } if (!/^[0-9a-f]{64}$/.test(candidate.evidenceSha256)) throw new Error("Invalid evidenceSha256"); diff --git a/extension/src/corpus/scan.ts b/extension/src/corpus/scan.ts index 8652a21..e084372 100644 --- a/extension/src/corpus/scan.ts +++ b/extension/src/corpus/scan.ts @@ -16,6 +16,7 @@ export interface CandidatePreflight { baseCommit: string; targetCommit: string; adjacentFirstParent: boolean; + licenseApproved: boolean; lockfilePresent: boolean; dependencyOrLockfileChanged: boolean; unsupportedArtifactPresent: boolean; @@ -30,7 +31,6 @@ export interface CandidatePreflight { export interface ProvisionEvidence { schemaVersion: 1; - licenseApproved: boolean; sanitizedBytes: number; requiresProductionCapability: boolean; executionNeedsNetwork: boolean; @@ -42,7 +42,6 @@ export interface ProvisionEvidence { const provisionKeys = [ "schemaVersion", - "licenseApproved", "sanitizedBytes", "requiresProductionCapability", "executionNeedsNetwork", @@ -81,7 +80,7 @@ export async function scanRepository( if (!isCoarseCandidate(paths)) continue; ordinal += 1; if (ordinal > 60) break; - drafts.push(await inspectCandidate(root, registration.repositoryId, ordinal, baseCommit, targetCommit, parents, paths)); + drafts.push(await inspectCandidate(root, registration.repositoryId, approvedLicense(registration.licenseSpdx), ordinal, baseCommit, targetCommit, parents, paths)); } return drafts; } @@ -101,7 +100,7 @@ export function completeCandidate(draft: CandidatePreflight, evidence: Provision baseCommit: draft.baseCommit, targetCommit: draft.targetCommit, adjacentFirstParent: draft.adjacentFirstParent, - licenseApproved: evidence.licenseApproved, + licenseApproved: draft.licenseApproved, lockfilePresent: draft.lockfilePresent, dependencyOrLockfileChanged: draft.dependencyOrLockfileChanged, unsupportedArtifactPresent: draft.unsupportedArtifactPresent, @@ -125,9 +124,42 @@ export function completeCandidate(draft: CandidatePreflight, evidence: Provision }; } +export function deferCandidate(draft: CandidatePreflight): CandidateFacts { + const facts = { + schemaVersion: 1 as const, + repositoryId: draft.repositoryId, + ordinal: draft.ordinal, + baseCommit: draft.baseCommit, + targetCommit: draft.targetCommit, + adjacentFirstParent: draft.adjacentFirstParent, + licenseApproved: draft.licenseApproved, + lockfilePresent: draft.lockfilePresent, + dependencyOrLockfileChanged: draft.dependencyOrLockfileChanged, + unsupportedArtifactPresent: draft.unsupportedArtifactPresent, + changedLines: draft.changedLines, + sanitizedBytes: draft.sourceTreeBytes, + supportedTypescriptBoundary: draft.supportedTypescriptBoundary, + hasAttributedTest: draft.hasAttributedTest, + requiresProductionCapability: null, + executionNeedsNetwork: null, + basePassed: null, + targetPassed: null, + deterministicReplayCount: null, + }; + return { + ...facts, + evidenceSha256: rawSha256(`pureflow/r7-candidate-evidence-v1\n${canonicalJson({ + facts, + preflightSha256: draft.preflightSha256, + provisionEvidenceSha256: null, + })}`), + }; +} + async function inspectCandidate( root: string, repositoryId: string, + licenseApproved: boolean, ordinal: number, baseCommit: string, targetCommit: string, @@ -174,6 +206,7 @@ async function inspectCandidate( baseCommit, targetCommit, adjacentFirstParent: parents[0] === targetCommit && parents[1] === baseCommit, + licenseApproved, lockfilePresent: sharedLocks.length > 0, dependencyOrLockfileChanged, unsupportedArtifactPresent: extractionFailed || stats.binary || baseTree.unsupported || targetTree.unsupported, @@ -256,6 +289,10 @@ function isSnapshotPath(path: string): boolean { return /(?:^|\/)(?:__snapshots__)(?:\/|$)/i.test(path) || /\.snap$/i.test(path); } +function approvedLicense(value: string): boolean { + return ["0BSD", "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", "ISC", "MIT"].includes(value); +} + function sourceStem(path: string): string { return path.split("/").at(-1)!.replace(/\.(?:ts|tsx)$/, ""); } diff --git a/extension/test/corpus-scan.test.ts b/extension/test/corpus-scan.test.ts index 6e1d4c3..68cef26 100644 --- a/extension/test/corpus-scan.test.ts +++ b/extension/test/corpus-scan.test.ts @@ -40,6 +40,7 @@ describe("R7 corpus scanner", () => { ordinal: 1, targetCommit: tip, adjacentFirstParent: true, + licenseApproved: true, lockfilePresent: true, dependencyOrLockfileChanged: false, unsupportedArtifactPresent: false, @@ -59,7 +60,6 @@ describe("R7 corpus scanner", () => { const [draft] = await scanRepository(root, repository(tip)); const evidence: ProvisionEvidence = { schemaVersion: 1, - licenseApproved: true, sanitizedBytes: 4096, requiresProductionCapability: false, executionNeedsNetwork: false, From 78b29a29728de1644edc895690e616093d1c20bd Mon Sep 17 00:00:00 2001 From: Error Lover Date: Sat, 1 Aug 2026 03:59:20 +0300 Subject: [PATCH 04/21] feat: add isolated R7 corpus provisioner --- docs/v0.3/CORPUS_FREEZE_SPEC.md | 3 +- docs/v0.3/r7/README.md | 1 + extension/src/corpus/cli.ts | 10 + extension/src/corpus/freeze.ts | 12 +- extension/src/corpus/index.ts | 1 + extension/src/corpus/provision.ts | 248 ++++++++++++++++++++++++ extension/src/corpus/scan.ts | 7 - extension/test/corpus-freeze.test.ts | 1 - extension/test/corpus-provision.test.ts | 73 +++++++ extension/test/corpus-scan.test.ts | 2 - 10 files changed, 337 insertions(+), 21 deletions(-) create mode 100644 extension/src/corpus/provision.ts create mode 100644 extension/test/corpus-provision.test.ts diff --git a/docs/v0.3/CORPUS_FREEZE_SPEC.md b/docs/v0.3/CORPUS_FREEZE_SPEC.md index c70ee83..b1d1ede 100644 --- a/docs/v0.3/CORPUS_FREEZE_SPEC.md +++ b/docs/v0.3/CORPUS_FREEZE_SPEC.md @@ -34,10 +34,11 @@ The corpus freezer converts repository registrations and independently collected 11. `network-required-at-execution` 12. `base-provision-or-test-failed` 13. `target-provision-or-test-failed` -14. `replay-not-deterministic` The order is deliberately structural-first: cheap immutable failures are classified before provisioning failures. Changing the order requires a new protocol version, never an edit after outcomes are known. +Three-run replay belongs to compiler evaluation after the corpus is frozen; it is not an eligibility field and cannot influence corpus membership. + ## Non-goals - downloading or copying third-party source into this repository; diff --git a/docs/v0.3/r7/README.md b/docs/v0.3/r7/README.md index 674d2b0..e1d8669 100644 --- a/docs/v0.3/r7/README.md +++ b/docs/v0.3/r7/README.md @@ -11,6 +11,7 @@ From `extension/`: ```powershell npm run r7:corpus -- scan +npm run r7:corpus -- provision npm run r7:corpus -- complete npm run r7:corpus -- freeze ``` diff --git a/extension/src/corpus/cli.ts b/extension/src/corpus/cli.ts index 83c25e2..754aadc 100644 --- a/extension/src/corpus/cli.ts +++ b/extension/src/corpus/cli.ts @@ -2,6 +2,7 @@ import { readFile, writeFile } from "node:fs/promises"; import { resolve } from "node:path"; import { completeCandidate, deferCandidate, scanRepository, type CandidatePreflight, type ProvisionEvidence } from "./scan"; import { freezeCorpus, type CandidateFacts, type RepositoryRegistration } from "./freeze"; +import { provisionRepository } from "./provision"; void main(); @@ -15,6 +16,14 @@ async function main(): Promise { const drafts = await scanRepository(resolve(args[2]!), registration); await writeNewJson(args[3]!, drafts); process.stdout.write(`Scanned ${drafts.length} coarse candidates for ${registration.repositoryId}.\n`); + } else if (command === "provision" && args.length === 5) { + const repositories = await readJson(args[0]!); + const registration = repositories.find(({ repositoryId }) => repositoryId === args[1]); + if (registration === undefined) throw new Error(`Unknown repository registration: ${args[1]}`); + const drafts = await readJson(args[2]!); + const evidence = await provisionRepository(resolve(args[3]!), registration, drafts); + await writeNewJson(args[4]!, evidence); + process.stdout.write(`Provisioned ${Object.keys(evidence).length} candidates for ${registration.repositoryId}.\n`); } else if (command === "complete" && args.length === 3) { const drafts = await readJson(args[0]!); const evidence = await readJson>(args[1]!); @@ -34,6 +43,7 @@ async function main(): Promise { process.stderr.write([ "Usage:", " npm run r7:corpus -- scan ", + " npm run r7:corpus -- provision ", " npm run r7:corpus -- complete ", " npm run r7:corpus -- freeze ", "", diff --git a/extension/src/corpus/freeze.ts b/extension/src/corpus/freeze.ts index 5b23b7e..1d35d0a 100644 --- a/extension/src/corpus/freeze.ts +++ b/extension/src/corpus/freeze.ts @@ -15,8 +15,7 @@ export type ExclusionCode = | "requires-production-capability" | "network-required-at-execution" | "base-provision-or-test-failed" - | "target-provision-or-test-failed" - | "replay-not-deterministic"; + | "target-provision-or-test-failed"; export interface RepositoryRegistration { schemaVersion: 1; @@ -51,7 +50,6 @@ export interface CandidateFacts { executionNeedsNetwork: boolean | null; basePassed: boolean | null; targetPassed: boolean | null; - deterministicReplayCount: number | null; evidenceSha256: string; } @@ -123,7 +121,6 @@ const candidateKeys = [ "executionNeedsNetwork", "basePassed", "targetPassed", - "deterministicReplayCount", "evidenceSha256", ] as const; @@ -142,8 +139,7 @@ export function classifyCandidate(candidate: CandidateFacts): CandidateClassific candidate.requiresProductionCapability === null || candidate.executionNeedsNetwork === null || candidate.basePassed === null || - candidate.targetPassed === null || - candidate.deterministicReplayCount === null + candidate.targetPassed === null ) { throw new Error(`Missing provision evidence for structurally eligible candidate ${candidate.repositoryId}/${candidate.targetCommit}`); } @@ -151,7 +147,6 @@ export function classifyCandidate(candidate: CandidateFacts): CandidateClassific if (candidate.executionNeedsNetwork) return excluded("network-required-at-execution"); if (!candidate.basePassed) return excluded("base-provision-or-test-failed"); if (!candidate.targetPassed) return excluded("target-provision-or-test-failed"); - if (candidate.deterministicReplayCount !== 3) return excluded("replay-not-deterministic"); return { status: "eligible" }; } @@ -289,9 +284,6 @@ function validateCandidate(candidate: CandidateFacts): void { if (candidate.baseCommit === candidate.targetCommit) throw new Error("Candidate revisions must differ"); if (!Number.isSafeInteger(candidate.changedLines) || candidate.changedLines < 0) throw new Error("Invalid changedLines"); if (!Number.isSafeInteger(candidate.sanitizedBytes) || candidate.sanitizedBytes < 0) throw new Error("Invalid sanitizedBytes"); - if (candidate.deterministicReplayCount !== null && (!Number.isSafeInteger(candidate.deterministicReplayCount) || candidate.deterministicReplayCount < 0)) { - throw new Error("Invalid deterministicReplayCount"); - } if (!/^[0-9a-f]{64}$/.test(candidate.evidenceSha256)) throw new Error("Invalid evidenceSha256"); } diff --git a/extension/src/corpus/index.ts b/extension/src/corpus/index.ts index fa36cae..27e76ed 100644 --- a/extension/src/corpus/index.ts +++ b/extension/src/corpus/index.ts @@ -1,2 +1,3 @@ export * from "./freeze"; export * from "./scan"; +export * from "./provision"; diff --git a/extension/src/corpus/provision.ts b/extension/src/corpus/provision.ts new file mode 100644 index 0000000..fdee09b --- /dev/null +++ b/extension/src/corpus/provision.ts @@ -0,0 +1,248 @@ +import { randomUUID } from "node:crypto"; +import { execFile } from "node:child_process"; +import { mkdir, mkdtemp, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join, resolve } from "node:path"; +import { promisify } from "node:util"; +import { canonicalJson, rawSha256 } from "../rnd/canonical"; +import { R7_NODE_IMAGE } from "../sandbox/toolchains"; +import type { RepositoryRegistration } from "./freeze"; +import type { CandidatePreflight, ProvisionEvidence } from "./scan"; + +const runFile = promisify(execFile); +const dockerExecutable = process.platform === "win32" + ? "C:\\Program Files\\Docker\\Docker\\resources\\bin\\docker.exe" + : "/usr/bin/docker"; +const tarExecutable = process.platform === "win32" ? "C:\\Windows\\System32\\tar.exe" : "/usr/bin/tar"; + +export interface CorpusDockerInvocation { + containerName: string; + workspace: string; + corepackHome: string; + network: "bridge" | "none"; + argv: string[]; +} + +interface CommandReceipt { + exitCode: number | null; + timedOut: boolean; + durationMs: number; + stdoutBytes: number; + stderrBytes: number; + stdoutSha256: string; + stderrSha256: string; +} + +export function selectProvisionCandidates(drafts: readonly CandidatePreflight[]): CandidatePreflight[] { + const ordered = [...drafts].sort((left, right) => left.ordinal - right.ordinal); + const selected: CandidatePreflight[] = []; + for (const draft of ordered) { + if (selected.length === 10) break; + if ( + draft.adjacentFirstParent && + draft.licenseApproved && + draft.lockfilePresent && + !draft.dependencyOrLockfileChanged && + !draft.unsupportedArtifactPresent && + draft.changedLines <= 500 && + draft.sourceTreeBytes <= 25 * 1024 * 1024 && + draft.supportedTypescriptBoundary && + draft.hasAttributedTest + ) { + selected.push(structuredClone(draft)); + } + } + return selected; +} + +export function buildCorpusDockerArgs(invocation: CorpusDockerInvocation): string[] { + if (!/^pureflow-r7-corpus-[0-9a-f]{24}$/.test(invocation.containerName)) throw new Error("Invalid corpus container name"); + if (invocation.network !== "bridge" && invocation.network !== "none") throw new Error("Invalid corpus network mode"); + assertMount(invocation.workspace); + assertMount(invocation.corepackHome); + if (invocation.argv.length < 2 || !["npm", "corepack"].includes(invocation.argv[0]!)) { + throw new Error("Corpus executable is not registered"); + } + if (invocation.argv.some((part) => !part || /[\0\r\n]/.test(part))) throw new Error("Invalid corpus argument"); + + return [ + "container", "run", "--rm", "--name", invocation.containerName, + "--network", invocation.network, + "--read-only", + "--cap-drop", "ALL", + "--security-opt", "no-new-privileges", + "--memory", "1g", + "--memory-swap", "1g", + "--cpus", "2", + "--pids-limit", "512", + "--tmpfs", "/tmp:rw,noexec,nosuid,size=256m", + "--mount", bindMount(invocation.workspace, "/work"), + "--mount", bindMount(invocation.corepackHome, "/corepack"), + "--workdir", "/work", + "--env", "CI=1", + "--env", "HOME=/tmp", + "--env", "COREPACK_HOME=/corepack", + "--env", "npm_config_update_notifier=false", + "--entrypoint", invocation.argv[0]!, + R7_NODE_IMAGE, + ...invocation.argv.slice(1), + ]; +} + +export async function provisionRepository( + repositoryPath: string, + registration: RepositoryRegistration, + drafts: readonly CandidatePreflight[], +): Promise> { + const selected = selectProvisionCandidates(drafts); + const evidence: Record = {}; + for (const draft of selected) { + evidence[draft.targetCommit] = await provisionCandidate(resolve(repositoryPath), registration, draft); + } + return evidence; +} + +async function provisionCandidate( + repository: string, + registration: RepositoryRegistration, + draft: CandidatePreflight, +): Promise { + const root = await mkdtemp(join(tmpdir(), "pureflow-r7-provision-")); + const corepackHome = join(root, "corepack"); + await mkdir(corepackHome); + try { + const base = await provisionRevision(repository, root, "base", draft.baseCommit, registration, corepackHome); + const target = await provisionRevision(repository, root, "target", draft.targetCommit, registration, corepackHome); + const record = { + schemaVersion: 1, + image: R7_NODE_IMAGE, + repositoryId: draft.repositoryId, + baseCommit: draft.baseCommit, + targetCommit: draft.targetCommit, + nodeVersion: registration.nodeVersion, + packageManager: registration.packageManager, + packageManagerVersion: registration.packageManagerVersion, + installArgv: registration.installArgv, + testArgv: registration.testArgv, + base, + target, + }; + return { + schemaVersion: 1, + sanitizedBytes: draft.sourceTreeBytes, + requiresProductionCapability: false, + executionNeedsNetwork: false, + basePassed: base.install.exitCode === 0 && base.test?.exitCode === 0, + targetPassed: target.install.exitCode === 0 && target.test?.exitCode === 0, + provisionEvidenceSha256: rawSha256(`pureflow/r7-provision-evidence-v1\n${canonicalJson(record)}`), + }; + } finally { + await rm(root, { recursive: true, force: true }); + } +} + +async function provisionRevision( + repository: string, + root: string, + label: "base" | "target", + revision: string, + registration: RepositoryRegistration, + corepackHome: string, +): Promise<{ install: CommandReceipt; test: CommandReceipt | null }> { + const workspace = join(root, label); + const archive = join(root, `${label}.tar`); + await mkdir(workspace); + await runFile("git", ["archive", "--format=tar", `--output=${archive}`, revision], { + cwd: repository, + windowsHide: true, + env: hostEnvironment(), + maxBuffer: 1024 * 1024, + }); + await runFile(tarExecutable, ["-xf", archive, "-C", workspace], { + windowsHide: true, + env: hostEnvironment(), + maxBuffer: 1024 * 1024, + }); + const install = await runDocker({ + containerName: containerName(), + workspace, + corepackHome, + network: "bridge", + argv: registration.installArgv, + }, 8 * 60_000); + const test = install.exitCode === 0 + ? await runDocker({ + containerName: containerName(), + workspace, + corepackHome, + network: "none", + argv: registration.testArgv, + }, 8 * 60_000) + : null; + return { install, test }; +} + +async function runDocker(invocation: CorpusDockerInvocation, timeoutMs: number): Promise { + const started = Date.now(); + try { + const { stdout, stderr } = await runFile(dockerExecutable, buildCorpusDockerArgs(invocation), { + encoding: "buffer", + windowsHide: true, + env: dockerEnvironment(), + timeout: timeoutMs, + killSignal: "SIGKILL", + maxBuffer: 1024 * 1024, + }); + return receipt(0, false, started, stdout, stderr); + } catch (error) { + const failure = error as Error & { code?: number | string; killed?: boolean; stdout?: Buffer; stderr?: Buffer }; + const exitCode = typeof failure.code === "number" ? failure.code : null; + return receipt(exitCode, Boolean(failure.killed), started, failure.stdout ?? Buffer.alloc(0), failure.stderr ?? Buffer.from(failure.message)); + } +} + +function receipt(exitCode: number | null, timedOut: boolean, started: number, stdout: Buffer, stderr: Buffer): CommandReceipt { + return { + exitCode, + timedOut, + durationMs: Date.now() - started, + stdoutBytes: stdout.byteLength, + stderrBytes: stderr.byteLength, + stdoutSha256: rawSha256(stdout), + stderrSha256: rawSha256(stderr), + }; +} + +function containerName(): string { + return `pureflow-r7-corpus-${randomUUID().replaceAll("-", "").slice(0, 24)}`; +} + +function bindMount(source: string, target: string): string { + return `type=bind,src=${source},dst=${target}`; +} + +function assertMount(path: string): void { + if (!resolve(path) || /[,\0\r\n]/.test(path)) throw new Error("Unsafe corpus mount path"); +} + +function hostEnvironment(): NodeJS.ProcessEnv { + const env: NodeJS.ProcessEnv = { + GIT_CONFIG_GLOBAL: process.platform === "win32" ? "NUL" : "/dev/null", + GIT_CONFIG_NOSYSTEM: "1", + GIT_TERMINAL_PROMPT: "0", + LANG: "C", + LC_ALL: "C", + }; + for (const name of ["PATH", "Path", "SystemRoot", "WINDIR", "COMSPEC", "PATHEXT", "TMP", "TEMP"]) { + if (process.env[name] !== undefined) env[name] = process.env[name]; + } + return env; +} + +function dockerEnvironment(): NodeJS.ProcessEnv { + const env: NodeJS.ProcessEnv = {}; + for (const name of ["SystemRoot", "WINDIR"]) { + if (process.env[name] !== undefined) env[name] = process.env[name]; + } + return env; +} diff --git a/extension/src/corpus/scan.ts b/extension/src/corpus/scan.ts index e084372..193f8ee 100644 --- a/extension/src/corpus/scan.ts +++ b/extension/src/corpus/scan.ts @@ -36,7 +36,6 @@ export interface ProvisionEvidence { executionNeedsNetwork: boolean; basePassed: boolean; targetPassed: boolean; - deterministicReplayCount: number; provisionEvidenceSha256: string; } @@ -47,7 +46,6 @@ const provisionKeys = [ "executionNeedsNetwork", "basePassed", "targetPassed", - "deterministicReplayCount", "provisionEvidenceSha256", ] as const; @@ -89,9 +87,6 @@ export function completeCandidate(draft: CandidatePreflight, evidence: Provision assertExactKeys(evidence, provisionKeys, "provision evidence"); if (evidence.schemaVersion !== 1) throw new Error("Unsupported provision evidence schema"); if (!Number.isSafeInteger(evidence.sanitizedBytes) || evidence.sanitizedBytes < 0) throw new Error("Invalid sanitizedBytes"); - if (!Number.isSafeInteger(evidence.deterministicReplayCount) || evidence.deterministicReplayCount < 0) { - throw new Error("Invalid deterministicReplayCount"); - } assertSha256(evidence.provisionEvidenceSha256, "provisionEvidenceSha256"); const facts = { schemaVersion: 1 as const, @@ -112,7 +107,6 @@ export function completeCandidate(draft: CandidatePreflight, evidence: Provision executionNeedsNetwork: evidence.executionNeedsNetwork, basePassed: evidence.basePassed, targetPassed: evidence.targetPassed, - deterministicReplayCount: evidence.deterministicReplayCount, }; return { ...facts, @@ -144,7 +138,6 @@ export function deferCandidate(draft: CandidatePreflight): CandidateFacts { executionNeedsNetwork: null, basePassed: null, targetPassed: null, - deterministicReplayCount: null, }; return { ...facts, diff --git a/extension/test/corpus-freeze.test.ts b/extension/test/corpus-freeze.test.ts index 4e78829..81df05a 100644 --- a/extension/test/corpus-freeze.test.ts +++ b/extension/test/corpus-freeze.test.ts @@ -140,7 +140,6 @@ function candidate(repositoryId: string, ordinal: number, seed = ordinal): Candi executionNeedsNetwork: false, basePassed: true, targetPassed: true, - deterministicReplayCount: 3, evidenceSha256: seed.toString(16).padStart(64, "0"), }; } diff --git a/extension/test/corpus-provision.test.ts b/extension/test/corpus-provision.test.ts new file mode 100644 index 0000000..aa7e7ef --- /dev/null +++ b/extension/test/corpus-provision.test.ts @@ -0,0 +1,73 @@ +import { describe, expect, it } from "vitest"; +import { + buildCorpusDockerArgs, + selectProvisionCandidates, + type CorpusDockerInvocation, +} from "../src/corpus/provision"; +import type { CandidatePreflight } from "../src/corpus/scan"; + +describe("R7 corpus provisioning", () => { + it("selects only the first ten structurally viable candidates", () => { + const drafts = Array.from({ length: 15 }, (_, index) => draft(index + 1)); + drafts[0]!.lockfilePresent = false; + drafts[1]!.dependencyOrLockfileChanged = true; + drafts[2]!.hasAttributedTest = false; + + expect(selectProvisionCandidates(drafts).map(({ ordinal }) => ordinal)).toEqual([4, 5, 6, 7, 8, 9, 10, 11, 12, 13]); + }); + + it("uses network only for installation and preserves the exact registered argv", () => { + const install = invocation("bridge", ["corepack", "pnpm", "install", "--frozen-lockfile"]); + const test = invocation("none", ["corepack", "pnpm", "test"]); + + const installArgs = buildCorpusDockerArgs(install); + const testArgs = buildCorpusDockerArgs(test); + + expect(installArgs).toContain("bridge"); + expect(testArgs).toContain("none"); + expect(testArgs).toContain("--read-only"); + expect(testArgs).toContain("no-new-privileges"); + expect(testArgs).toContain("ALL"); + expect(testArgs.slice(-3)).toEqual([expect.stringMatching(/^node@sha256:/), "pnpm", "test"]); + expect(testArgs).not.toContain("sh"); + expect(testArgs).not.toContain("bash"); + }); + + it("rejects unsafe mount and executable material", () => { + expect(() => buildCorpusDockerArgs(invocation("none", ["sh", "-c", "npm test"]))).toThrow("executable"); + expect(() => buildCorpusDockerArgs({ ...invocation("none", ["npm", "test"]), workspace: "C:\\bad,path" })).toThrow("mount"); + }); +}); + +function invocation(network: "bridge" | "none", argv: string[]): CorpusDockerInvocation { + return { + containerName: "pureflow-r7-corpus-0123456789abcdef01234567", + workspace: "C:\\tmp\\corpus", + corepackHome: "C:\\tmp\\corepack", + network, + argv, + }; +} + +function draft(ordinal: number): CandidatePreflight { + const targetCommit = ordinal.toString(16).padStart(40, "0"); + const facts = { + schemaVersion: 1 as const, + repositoryId: "repo-a", + ordinal, + baseCommit: (ordinal + 100).toString(16).padStart(40, "0"), + targetCommit, + adjacentFirstParent: true, + licenseApproved: true, + lockfilePresent: true, + dependencyOrLockfileChanged: false, + unsupportedArtifactPresent: false, + changedLines: 12, + sourceTreeBytes: 2048, + supportedTypescriptBoundary: true, + hasAttributedTest: true, + changedSourcePaths: ["src/a.ts"], + changedTestPaths: ["test/a.test.ts"], + }; + return { ...facts, preflightSha256: "a".repeat(64) }; +} diff --git a/extension/test/corpus-scan.test.ts b/extension/test/corpus-scan.test.ts index 68cef26..5f2badd 100644 --- a/extension/test/corpus-scan.test.ts +++ b/extension/test/corpus-scan.test.ts @@ -65,7 +65,6 @@ describe("R7 corpus scanner", () => { executionNeedsNetwork: false, basePassed: true, targetPassed: true, - deterministicReplayCount: 3, provisionEvidenceSha256: "f".repeat(64), }; @@ -75,7 +74,6 @@ describe("R7 corpus scanner", () => { repositoryId: "fixture-repo", basePassed: true, targetPassed: true, - deterministicReplayCount: 3, }); expect(completed.evidenceSha256).toMatch(/^[0-9a-f]{64}$/); expect(() => completeCandidate(draft!, { ...evidence, targetPassed: false, judgePassed: true } as ProvisionEvidence)).toThrow( From 1ac5bf3ad26da080b3a29a42d1bcf9262f3075bd Mon Sep 17 00:00:00 2001 From: Error Lover Date: Sat, 1 Aug 2026 04:08:01 +0300 Subject: [PATCH 05/21] perf: bound corpus provisioning concurrency --- docs/v0.3/r7/README.md | 2 ++ extension/src/corpus/provision.ts | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/v0.3/r7/README.md b/docs/v0.3/r7/README.md index e1d8669..8bb0a41 100644 --- a/docs/v0.3/r7/README.md +++ b/docs/v0.3/r7/README.md @@ -18,6 +18,8 @@ npm run r7:corpus -- freeze > { const selected = selectProvisionCandidates(drafts); const evidence: Record = {}; - for (const draft of selected) { - evidence[draft.targetCommit] = await provisionCandidate(resolve(repositoryPath), registration, draft); + for (let offset = 0; offset < selected.length; offset += 3) { + const batch = selected.slice(offset, offset + 3); + const results = await Promise.all(batch.map(async (draft) => ({ + targetCommit: draft.targetCommit, + evidence: await provisionCandidate(resolve(repositoryPath), registration, draft), + }))); + for (const result of results) evidence[result.targetCommit] = result.evidence; } return evidence; } From dd003ecbe33429da7ee1abc013412d707735d142 Mon Sep 17 00:00:00 2001 From: Error Lover Date: Sat, 1 Aug 2026 04:16:01 +0300 Subject: [PATCH 06/21] perf: provision corpus in native Docker volumes --- docs/v0.3/r7/README.md | 2 +- extension/src/corpus/provision.ts | 126 ++++++++++++++++-------- extension/test/corpus-provision.test.ts | 6 +- 3 files changed, 89 insertions(+), 45 deletions(-) diff --git a/docs/v0.3/r7/README.md b/docs/v0.3/r7/README.md index 8bb0a41..228eb79 100644 --- a/docs/v0.3/r7/README.md +++ b/docs/v0.3/r7/README.md @@ -18,7 +18,7 @@ npm run r7:corpus -- freeze { const root = await mkdtemp(join(tmpdir(), "pureflow-r7-provision-")); - const corepackHome = join(root, "corepack"); - await mkdir(corepackHome); + const corepackVolume = volumeName("c"); + await createVolume(corepackVolume); try { - const base = await provisionRevision(repository, root, "base", draft.baseCommit, registration, corepackHome); - const target = await provisionRevision(repository, root, "target", draft.targetCommit, registration, corepackHome); + const base = await provisionRevision(repository, root, "base", draft.baseCommit, registration, corepackVolume); + const target = await provisionRevision(repository, root, "target", draft.targetCommit, registration, corepackVolume); const record = { schemaVersion: 1, image: R7_NODE_IMAGE, @@ -142,6 +141,7 @@ async function provisionCandidate( provisionEvidenceSha256: rawSha256(`pureflow/r7-provision-evidence-v1\n${canonicalJson(record)}`), }; } finally { + await removeVolume(corepackVolume); await rm(root, { recursive: true, force: true }); } } @@ -152,39 +152,39 @@ async function provisionRevision( label: "base" | "target", revision: string, registration: RepositoryRegistration, - corepackHome: string, + corepackVolume: string, ): Promise<{ install: CommandReceipt; test: CommandReceipt | null }> { - const workspace = join(root, label); const archive = join(root, `${label}.tar`); - await mkdir(workspace); await runFile("git", ["archive", "--format=tar", `--output=${archive}`, revision], { cwd: repository, windowsHide: true, env: hostEnvironment(), maxBuffer: 1024 * 1024, }); - await runFile(tarExecutable, ["-xf", archive, "-C", workspace], { - windowsHide: true, - env: hostEnvironment(), - maxBuffer: 1024 * 1024, - }); - const install = await runDocker({ - containerName: containerName(), - workspace, - corepackHome, - network: "bridge", - argv: registration.installArgv, - }, 8 * 60_000); - const test = install.exitCode === 0 - ? await runDocker({ - containerName: containerName(), - workspace, - corepackHome, - network: "none", - argv: registration.testArgv, - }, 8 * 60_000) - : null; - return { install, test }; + const workspaceVolume = volumeName("w"); + await createVolume(workspaceVolume); + try { + await seedVolume(workspaceVolume, archive); + const install = await runDocker({ + containerName: containerName(), + workspaceVolume, + corepackVolume, + network: "bridge", + argv: registration.installArgv, + }, 8 * 60_000); + const test = install.exitCode === 0 + ? await runDocker({ + containerName: containerName(), + workspaceVolume, + corepackVolume, + network: "none", + argv: registration.testArgv, + }, 8 * 60_000) + : null; + return { install, test }; + } finally { + await removeVolume(workspaceVolume); + } } async function runDocker(invocation: CorpusDockerInvocation, timeoutMs: number): Promise { @@ -222,12 +222,56 @@ function containerName(): string { return `pureflow-r7-corpus-${randomUUID().replaceAll("-", "").slice(0, 24)}`; } -function bindMount(source: string, target: string): string { - return `type=bind,src=${source},dst=${target}`; +function volumeMount(source: string, target: string): string { + return `type=volume,src=${source},dst=${target}`; +} + +function assertVolume(value: string): void { + if (!/^pureflow-r7-corpus-[cw]-[0-9a-f]{24}$/.test(value)) throw new Error("Unsafe corpus volume mount"); +} + +function volumeName(kind: "c" | "w"): string { + return `pureflow-r7-corpus-${kind}-${randomUUID().replaceAll("-", "").slice(0, 24)}`; +} + +async function createVolume(name: string): Promise { + assertVolume(name); + await runFile(dockerExecutable, ["volume", "create", name], { + windowsHide: true, + env: dockerEnvironment(), + timeout: 15_000, + maxBuffer: 1024 * 1024, + }); +} + +async function removeVolume(name: string): Promise { + assertVolume(name); + await runFile(dockerExecutable, ["volume", "rm", "--force", name], { + windowsHide: true, + env: dockerEnvironment(), + timeout: 15_000, + maxBuffer: 1024 * 1024, + }).catch(() => undefined); } -function assertMount(path: string): void { - if (!resolve(path) || /[,\0\r\n]/.test(path)) throw new Error("Unsafe corpus mount path"); +async function seedVolume(volume: string, archive: string): Promise { + assertVolume(volume); + if (/[,\0\r\n]/.test(archive)) throw new Error("Unsafe corpus archive mount"); + const args = [ + "container", "run", "--rm", "--name", containerName(), + "--network", "none", "--read-only", + "--cap-drop", "ALL", "--security-opt", "no-new-privileges", + "--memory", "256m", "--memory-swap", "256m", "--cpus", "1", "--pids-limit", "64", + "--mount", volumeMount(volume, "/work"), + "--mount", `type=bind,src=${archive},dst=/source.tar,readonly`, + "--entrypoint", "tar", R7_NODE_IMAGE, "-xf", "/source.tar", "-C", "/work", + ]; + await runFile(dockerExecutable, args, { + windowsHide: true, + env: dockerEnvironment(), + timeout: 60_000, + maxBuffer: 1024 * 1024, + }); } function hostEnvironment(): NodeJS.ProcessEnv { diff --git a/extension/test/corpus-provision.test.ts b/extension/test/corpus-provision.test.ts index aa7e7ef..a766ce7 100644 --- a/extension/test/corpus-provision.test.ts +++ b/extension/test/corpus-provision.test.ts @@ -35,15 +35,15 @@ describe("R7 corpus provisioning", () => { it("rejects unsafe mount and executable material", () => { expect(() => buildCorpusDockerArgs(invocation("none", ["sh", "-c", "npm test"]))).toThrow("executable"); - expect(() => buildCorpusDockerArgs({ ...invocation("none", ["npm", "test"]), workspace: "C:\\bad,path" })).toThrow("mount"); + expect(() => buildCorpusDockerArgs({ ...invocation("none", ["npm", "test"]), workspaceVolume: "bad-volume" })).toThrow("mount"); }); }); function invocation(network: "bridge" | "none", argv: string[]): CorpusDockerInvocation { return { containerName: "pureflow-r7-corpus-0123456789abcdef01234567", - workspace: "C:\\tmp\\corpus", - corepackHome: "C:\\tmp\\corepack", + workspaceVolume: "pureflow-r7-corpus-w-0123456789abcdef01234567", + corepackVolume: "pureflow-r7-corpus-c-0123456789abcdef01234567", network, argv, }; From b87355d6b489026a89fb81d4fdd66a2780cce4e3 Mon Sep 17 00:00:00 2001 From: Error Lover Date: Sat, 1 Aug 2026 04:45:54 +0300 Subject: [PATCH 07/21] fix: pin pnpm bootstrap inside corpus volumes --- docs/v0.3/r7/README.md | 2 ++ extension/src/corpus/provision.ts | 36 ++++++++++++++++++++++--- extension/test/corpus-provision.test.ts | 1 + 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/docs/v0.3/r7/README.md b/docs/v0.3/r7/README.md index 228eb79..c6ae590 100644 --- a/docs/v0.3/r7/README.md +++ b/docs/v0.3/r7/README.md @@ -20,6 +20,8 @@ Every output command refuses to overwrite an existing artifact. The scanner neve Provisioning uses at most three isolated native Docker-volume workspaces concurrently. Host bind-mounted dependency trees are forbidden because their Windows filesystem cost distorted setup time; the host provides only a read-only Git archive during seeding. This bound is a throughput setting, not a sampling rule; each container retains its own workspace, exact registered argv, resource limits, and network mode. +For pnpm repositories, the controller installs the registered pnpm version into the candidate's Corepack cache, disables project-version substitution, and creates a disposable shim under `/work/.pureflow-bin`. This bootstrap is hashed separately from the unchanged registered install/test argv. + The first registration pass found no lockfile-backed coarse candidates in `p-queue` or `ajv`. Under the preregistered replacement rule, `ofetch`, `defu`, and `hookable` were appended before any compiler outcome was inspected. Registration array order is sampling order; the freezer stops once 30 eligible patches exist and never uses more than 10 from one repository. Before provisioning, the selected digest was capability-probed as Node `22.17.0`, npm `10.9.2`, and Corepack `0.33.0`; the registration metadata was corrected from the planned Node/npm patch versions to those observed immutable-image versions. No repository tests or PureFlow compiler outcomes had been run. diff --git a/extension/src/corpus/provision.ts b/extension/src/corpus/provision.ts index ea64b37..886cff7 100644 --- a/extension/src/corpus/provision.ts +++ b/extension/src/corpus/provision.ts @@ -81,6 +81,8 @@ export function buildCorpusDockerArgs(invocation: CorpusDockerInvocation): strin "--env", "CI=1", "--env", "HOME=/tmp", "--env", "COREPACK_HOME=/corepack", + "--env", "COREPACK_ENABLE_PROJECT_SPEC=0", + "--env", "PATH=/work/.pureflow-bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "--env", "npm_config_update_notifier=false", "--entrypoint", invocation.argv[0]!, R7_NODE_IMAGE, @@ -153,7 +155,7 @@ async function provisionRevision( revision: string, registration: RepositoryRegistration, corepackVolume: string, -): Promise<{ install: CommandReceipt; test: CommandReceipt | null }> { +): Promise<{ bootstrap: CommandReceipt[]; install: CommandReceipt; test: CommandReceipt | null }> { const archive = join(root, `${label}.tar`); await runFile("git", ["archive", "--format=tar", `--output=${archive}`, revision], { cwd: repository, @@ -165,13 +167,34 @@ async function provisionRevision( await createVolume(workspaceVolume); try { await seedVolume(workspaceVolume, archive); - const install = await runDocker({ + const bootstrap: CommandReceipt[] = []; + if (registration.packageManager === "pnpm") { + bootstrap.push(await runDocker({ + containerName: containerName(), + workspaceVolume, + corepackVolume, + network: "bridge", + argv: ["corepack", "install", "--global", `pnpm@${registration.packageManagerVersion}`], + }, 2 * 60_000)); + if (bootstrap[0]!.exitCode === 0) { + bootstrap.push(await runDocker({ + containerName: containerName(), + workspaceVolume, + corepackVolume, + network: "none", + argv: ["corepack", "enable", "--install-directory", "/work/.pureflow-bin", "pnpm"], + }, 60_000)); + } + } + const bootstrapPassed = bootstrap.every(({ exitCode }) => exitCode === 0) && + (registration.packageManager !== "pnpm" || bootstrap.length === 2); + const install = bootstrapPassed ? await runDocker({ containerName: containerName(), workspaceVolume, corepackVolume, network: "bridge", argv: registration.installArgv, - }, 8 * 60_000); + }, 8 * 60_000) : failedPrerequisite(); const test = install.exitCode === 0 ? await runDocker({ containerName: containerName(), @@ -181,7 +204,7 @@ async function provisionRevision( argv: registration.testArgv, }, 8 * 60_000) : null; - return { install, test }; + return { bootstrap, install, test }; } finally { await removeVolume(workspaceVolume); } @@ -218,6 +241,11 @@ function receipt(exitCode: number | null, timedOut: boolean, started: number, st }; } +function failedPrerequisite(): CommandReceipt { + const empty = Buffer.alloc(0); + return receipt(null, false, Date.now(), empty, Buffer.from("controller bootstrap failed")); +} + function containerName(): string { return `pureflow-r7-corpus-${randomUUID().replaceAll("-", "").slice(0, 24)}`; } diff --git a/extension/test/corpus-provision.test.ts b/extension/test/corpus-provision.test.ts index a766ce7..71b08cc 100644 --- a/extension/test/corpus-provision.test.ts +++ b/extension/test/corpus-provision.test.ts @@ -28,6 +28,7 @@ describe("R7 corpus provisioning", () => { expect(testArgs).toContain("--read-only"); expect(testArgs).toContain("no-new-privileges"); expect(testArgs).toContain("ALL"); + expect(testArgs).toContain("PATH=/work/.pureflow-bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"); expect(testArgs.slice(-3)).toEqual([expect.stringMatching(/^node@sha256:/), "pnpm", "test"]); expect(testArgs).not.toContain("sh"); expect(testArgs).not.toContain("bash"); From dbdf7a1a5a562841d6fa6a2d6d0003bc962c5dbf Mon Sep 17 00:00:00 2001 From: Error Lover Date: Sat, 1 Aug 2026 04:51:02 +0300 Subject: [PATCH 08/21] fix: create observable pnpm test shims --- docs/v0.3/r7/README.md | 2 +- extension/src/corpus/provision.ts | 19 +++++++++++++++++-- extension/src/corpus/scan.ts | 8 ++++++++ extension/test/corpus-provision.test.ts | 3 +++ extension/test/corpus-scan.test.ts | 4 ++++ 5 files changed, 33 insertions(+), 3 deletions(-) diff --git a/docs/v0.3/r7/README.md b/docs/v0.3/r7/README.md index c6ae590..1d91647 100644 --- a/docs/v0.3/r7/README.md +++ b/docs/v0.3/r7/README.md @@ -20,7 +20,7 @@ Every output command refuses to overwrite an existing artifact. The scanner neve Provisioning uses at most three isolated native Docker-volume workspaces concurrently. Host bind-mounted dependency trees are forbidden because their Windows filesystem cost distorted setup time; the host provides only a read-only Git archive during seeding. This bound is a throughput setting, not a sampling rule; each container retains its own workspace, exact registered argv, resource limits, and network mode. -For pnpm repositories, the controller installs the registered pnpm version into the candidate's Corepack cache, disables project-version substitution, and creates a disposable shim under `/work/.pureflow-bin`. This bootstrap is hashed separately from the unchanged registered install/test argv. +For pnpm repositories, the controller installs the registered pnpm version into the candidate's Corepack cache, disables project-version substitution, creates `/work/.pureflow-bin` with an exact shell-free `mkdir` invocation, and enables a disposable shim there. This bootstrap is hashed separately from the unchanged registered install/test argv. Provision artifacts expose typed install/test booleans for diagnosis while retaining command output only as bounded hashes. The first registration pass found no lockfile-backed coarse candidates in `p-queue` or `ajv`. Under the preregistered replacement rule, `ofetch`, `defu`, and `hookable` were appended before any compiler outcome was inspected. Registration array order is sampling order; the freezer stops once 30 eligible patches exist and never uses more than 10 from one repository. diff --git a/extension/src/corpus/provision.ts b/extension/src/corpus/provision.ts index 886cff7..88124e4 100644 --- a/extension/src/corpus/provision.ts +++ b/extension/src/corpus/provision.ts @@ -59,7 +59,9 @@ export function buildCorpusDockerArgs(invocation: CorpusDockerInvocation): strin if (invocation.network !== "bridge" && invocation.network !== "none") throw new Error("Invalid corpus network mode"); assertVolume(invocation.workspaceVolume); assertVolume(invocation.corepackVolume); - if (invocation.argv.length < 2 || !["npm", "corepack"].includes(invocation.argv[0]!)) { + const mkdir = invocation.argv[0] === "mkdir" && + invocation.argv.length === 3 && invocation.argv[1] === "-p" && invocation.argv[2] === "/work/.pureflow-bin"; + if (invocation.argv.length < 2 || (!mkdir && !["npm", "corepack"].includes(invocation.argv[0]!))) { throw new Error("Corpus executable is not registered"); } if (invocation.argv.some((part) => !part || /[\0\r\n]/.test(part))) throw new Error("Invalid corpus argument"); @@ -138,6 +140,10 @@ async function provisionCandidate( sanitizedBytes: draft.sourceTreeBytes, requiresProductionCapability: false, executionNeedsNetwork: false, + baseInstallPassed: base.install.exitCode === 0, + baseTestPassed: base.test?.exitCode === 0, + targetInstallPassed: target.install.exitCode === 0, + targetTestPassed: target.test?.exitCode === 0, basePassed: base.install.exitCode === 0 && base.test?.exitCode === 0, targetPassed: target.install.exitCode === 0 && target.test?.exitCode === 0, provisionEvidenceSha256: rawSha256(`pureflow/r7-provision-evidence-v1\n${canonicalJson(record)}`), @@ -177,6 +183,15 @@ async function provisionRevision( argv: ["corepack", "install", "--global", `pnpm@${registration.packageManagerVersion}`], }, 2 * 60_000)); if (bootstrap[0]!.exitCode === 0) { + bootstrap.push(await runDocker({ + containerName: containerName(), + workspaceVolume, + corepackVolume, + network: "none", + argv: ["mkdir", "-p", "/work/.pureflow-bin"], + }, 60_000)); + } + if (bootstrap[1]?.exitCode === 0) { bootstrap.push(await runDocker({ containerName: containerName(), workspaceVolume, @@ -187,7 +202,7 @@ async function provisionRevision( } } const bootstrapPassed = bootstrap.every(({ exitCode }) => exitCode === 0) && - (registration.packageManager !== "pnpm" || bootstrap.length === 2); + (registration.packageManager !== "pnpm" || bootstrap.length === 3); const install = bootstrapPassed ? await runDocker({ containerName: containerName(), workspaceVolume, diff --git a/extension/src/corpus/scan.ts b/extension/src/corpus/scan.ts index 193f8ee..2c15cf4 100644 --- a/extension/src/corpus/scan.ts +++ b/extension/src/corpus/scan.ts @@ -34,6 +34,10 @@ export interface ProvisionEvidence { sanitizedBytes: number; requiresProductionCapability: boolean; executionNeedsNetwork: boolean; + baseInstallPassed: boolean; + baseTestPassed: boolean; + targetInstallPassed: boolean; + targetTestPassed: boolean; basePassed: boolean; targetPassed: boolean; provisionEvidenceSha256: string; @@ -44,6 +48,10 @@ const provisionKeys = [ "sanitizedBytes", "requiresProductionCapability", "executionNeedsNetwork", + "baseInstallPassed", + "baseTestPassed", + "targetInstallPassed", + "targetTestPassed", "basePassed", "targetPassed", "provisionEvidenceSha256", diff --git a/extension/test/corpus-provision.test.ts b/extension/test/corpus-provision.test.ts index 71b08cc..9605456 100644 --- a/extension/test/corpus-provision.test.ts +++ b/extension/test/corpus-provision.test.ts @@ -36,6 +36,9 @@ describe("R7 corpus provisioning", () => { it("rejects unsafe mount and executable material", () => { expect(() => buildCorpusDockerArgs(invocation("none", ["sh", "-c", "npm test"]))).toThrow("executable"); + expect(buildCorpusDockerArgs(invocation("none", ["mkdir", "-p", "/work/.pureflow-bin"])).slice(-3)).toEqual([ + expect.stringMatching(/^node@sha256:/), "-p", "/work/.pureflow-bin", + ]); expect(() => buildCorpusDockerArgs({ ...invocation("none", ["npm", "test"]), workspaceVolume: "bad-volume" })).toThrow("mount"); }); }); diff --git a/extension/test/corpus-scan.test.ts b/extension/test/corpus-scan.test.ts index 5f2badd..2910334 100644 --- a/extension/test/corpus-scan.test.ts +++ b/extension/test/corpus-scan.test.ts @@ -63,6 +63,10 @@ describe("R7 corpus scanner", () => { sanitizedBytes: 4096, requiresProductionCapability: false, executionNeedsNetwork: false, + baseInstallPassed: true, + baseTestPassed: true, + targetInstallPassed: true, + targetTestPassed: true, basePassed: true, targetPassed: true, provisionEvidenceSha256: "f".repeat(64), From c2eecfc3e200e7b8f1bfd60a2b8332d3593b3192 Mon Sep 17 00:00:00 2001 From: Error Lover Date: Sat, 1 Aug 2026 05:02:28 +0300 Subject: [PATCH 09/21] feat: resume R7 provisioning to ten eligible patches --- docs/v0.3/r7/README.md | 4 +++- extension/src/corpus/cli.ts | 7 ++++--- extension/src/corpus/provision.ts | 20 +++++++++++++----- extension/src/corpus/scan.ts | 28 ++++++++++++++++++------- extension/test/corpus-provision.test.ts | 2 +- 5 files changed, 43 insertions(+), 18 deletions(-) diff --git a/docs/v0.3/r7/README.md b/docs/v0.3/r7/README.md index 1d91647..1eef000 100644 --- a/docs/v0.3/r7/README.md +++ b/docs/v0.3/r7/README.md @@ -11,7 +11,7 @@ From `extension/`: ```powershell npm run r7:corpus -- scan -npm run r7:corpus -- provision +npm run r7:corpus -- provision [prior-evidence.json] npm run r7:corpus -- complete npm run r7:corpus -- freeze ``` @@ -22,6 +22,8 @@ Provisioning uses at most three isolated native Docker-volume workspaces concurr For pnpm repositories, the controller installs the registered pnpm version into the candidate's Corepack cache, disables project-version substitution, creates `/work/.pureflow-bin` with an exact shell-free `mkdir` invocation, and enables a disposable shim there. This bootstrap is hashed separately from the unchanged registered install/test argv. Provision artifacts expose typed install/test booleans for diagnosis while retaining command output only as bounded hashes. +Resume evidence is immutable input, never overwritten. The provisioner skips recorded targets, advances in ordinal order, and sizes each batch to the remaining eligible slots so it never executes a candidate after the tenth passing base/target pair. + The first registration pass found no lockfile-backed coarse candidates in `p-queue` or `ajv`. Under the preregistered replacement rule, `ofetch`, `defu`, and `hookable` were appended before any compiler outcome was inspected. Registration array order is sampling order; the freezer stops once 30 eligible patches exist and never uses more than 10 from one repository. Before provisioning, the selected digest was capability-probed as Node `22.17.0`, npm `10.9.2`, and Corepack `0.33.0`; the registration metadata was corrected from the planned Node/npm patch versions to those observed immutable-image versions. No repository tests or PureFlow compiler outcomes had been run. diff --git a/extension/src/corpus/cli.ts b/extension/src/corpus/cli.ts index 754aadc..76c12eb 100644 --- a/extension/src/corpus/cli.ts +++ b/extension/src/corpus/cli.ts @@ -16,12 +16,13 @@ async function main(): Promise { const drafts = await scanRepository(resolve(args[2]!), registration); await writeNewJson(args[3]!, drafts); process.stdout.write(`Scanned ${drafts.length} coarse candidates for ${registration.repositoryId}.\n`); - } else if (command === "provision" && args.length === 5) { + } else if (command === "provision" && (args.length === 5 || args.length === 6)) { const repositories = await readJson(args[0]!); const registration = repositories.find(({ repositoryId }) => repositoryId === args[1]); if (registration === undefined) throw new Error(`Unknown repository registration: ${args[1]}`); const drafts = await readJson(args[2]!); - const evidence = await provisionRepository(resolve(args[3]!), registration, drafts); + const prior = args[5] === undefined ? {} : await readJson>(args[5]); + const evidence = await provisionRepository(resolve(args[3]!), registration, drafts, prior); await writeNewJson(args[4]!, evidence); process.stdout.write(`Provisioned ${Object.keys(evidence).length} candidates for ${registration.repositoryId}.\n`); } else if (command === "complete" && args.length === 3) { @@ -43,7 +44,7 @@ async function main(): Promise { process.stderr.write([ "Usage:", " npm run r7:corpus -- scan ", - " npm run r7:corpus -- provision ", + " npm run r7:corpus -- provision [prior-evidence.json]", " npm run r7:corpus -- complete ", " npm run r7:corpus -- freeze ", "", diff --git a/extension/src/corpus/provision.ts b/extension/src/corpus/provision.ts index 88124e4..3c53e33 100644 --- a/extension/src/corpus/provision.ts +++ b/extension/src/corpus/provision.ts @@ -36,7 +36,6 @@ export function selectProvisionCandidates(drafts: readonly CandidatePreflight[]) const ordered = [...drafts].sort((left, right) => left.ordinal - right.ordinal); const selected: CandidatePreflight[] = []; for (const draft of ordered) { - if (selected.length === 10) break; if ( draft.adjacentFirstParent && draft.licenseApproved && @@ -96,16 +95,27 @@ export async function provisionRepository( repositoryPath: string, registration: RepositoryRegistration, drafts: readonly CandidatePreflight[], + prior: Readonly> = {}, ): Promise> { const selected = selectProvisionCandidates(drafts); - const evidence: Record = {}; - for (let offset = 0; offset < selected.length; offset += 3) { - const batch = selected.slice(offset, offset + 3); + const evidence: Record = structuredClone(prior); + let accepted = selected.filter(({ targetCommit }) => { + const item = evidence[targetCommit]; + return item?.basePassed === true && item.targetPassed === true; + }).length; + const pending = selected.filter(({ targetCommit }) => evidence[targetCommit] === undefined); + for (let offset = 0; offset < pending.length && accepted < 10;) { + const batchSize = Math.min(3, 10 - accepted); + const batch = pending.slice(offset, offset + batchSize); + offset += batch.length; const results = await Promise.all(batch.map(async (draft) => ({ targetCommit: draft.targetCommit, evidence: await provisionCandidate(resolve(repositoryPath), registration, draft), }))); - for (const result of results) evidence[result.targetCommit] = result.evidence; + for (const result of results) { + evidence[result.targetCommit] = result.evidence; + if (result.evidence.basePassed && result.evidence.targetPassed) accepted += 1; + } } return evidence; } diff --git a/extension/src/corpus/scan.ts b/extension/src/corpus/scan.ts index 2c15cf4..818135e 100644 --- a/extension/src/corpus/scan.ts +++ b/extension/src/corpus/scan.ts @@ -34,10 +34,10 @@ export interface ProvisionEvidence { sanitizedBytes: number; requiresProductionCapability: boolean; executionNeedsNetwork: boolean; - baseInstallPassed: boolean; - baseTestPassed: boolean; - targetInstallPassed: boolean; - targetTestPassed: boolean; + baseInstallPassed?: boolean; + baseTestPassed?: boolean; + targetInstallPassed?: boolean; + targetTestPassed?: boolean; basePassed: boolean; targetPassed: boolean; provisionEvidenceSha256: string; @@ -48,13 +48,17 @@ const provisionKeys = [ "sanitizedBytes", "requiresProductionCapability", "executionNeedsNetwork", + "basePassed", + "targetPassed", + "provisionEvidenceSha256", +] as const; + +const diagnosticProvisionKeys = [ + ...provisionKeys, "baseInstallPassed", "baseTestPassed", "targetInstallPassed", "targetTestPassed", - "basePassed", - "targetPassed", - "provisionEvidenceSha256", ] as const; export function isCoarseCandidate(paths: readonly string[]): boolean { @@ -92,7 +96,9 @@ export async function scanRepository( } export function completeCandidate(draft: CandidatePreflight, evidence: ProvisionEvidence): CandidateFacts { - assertExactKeys(evidence, provisionKeys, "provision evidence"); + if (!hasExactKeys(evidence, provisionKeys) && !hasExactKeys(evidence, diagnosticProvisionKeys)) { + throw new Error("provision evidence contains unknown or missing fields"); + } if (evidence.schemaVersion !== 1) throw new Error("Unsupported provision evidence schema"); if (!Number.isSafeInteger(evidence.sanitizedBytes) || evidence.sanitizedBytes < 0) throw new Error("Invalid sanitizedBytes"); assertSha256(evidence.provisionEvidenceSha256, "provisionEvidenceSha256"); @@ -345,3 +351,9 @@ function assertExactKeys(value: object, allowed: readonly string[], label: strin throw new Error(`${label} contains unknown or missing fields`); } } + +function hasExactKeys(value: object, allowed: readonly string[]): boolean { + const expected = new Set(allowed); + const actual = Reflect.ownKeys(value); + return actual.length === expected.size && actual.every((key) => typeof key === "string" && expected.has(key)); +} diff --git a/extension/test/corpus-provision.test.ts b/extension/test/corpus-provision.test.ts index 9605456..83776ec 100644 --- a/extension/test/corpus-provision.test.ts +++ b/extension/test/corpus-provision.test.ts @@ -13,7 +13,7 @@ describe("R7 corpus provisioning", () => { drafts[1]!.dependencyOrLockfileChanged = true; drafts[2]!.hasAttributedTest = false; - expect(selectProvisionCandidates(drafts).map(({ ordinal }) => ordinal)).toEqual([4, 5, 6, 7, 8, 9, 10, 11, 12, 13]); + expect(selectProvisionCandidates(drafts).map(({ ordinal }) => ordinal)).toEqual([4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); }); it("uses network only for installation and preserves the exact registered argv", () => { From 475528b2ea04a512eeeb0acd1d0c769acc9ffe1b Mon Sep 17 00:00:00 2001 From: Error Lover Date: Sat, 1 Aug 2026 05:15:14 +0300 Subject: [PATCH 10/21] docs: preregister additional R7 replacements --- docs/v0.3/r7/README.md | 2 ++ docs/v0.3/r7/repositories.json | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/docs/v0.3/r7/README.md b/docs/v0.3/r7/README.md index 1eef000..ed22bfd 100644 --- a/docs/v0.3/r7/README.md +++ b/docs/v0.3/r7/README.md @@ -26,4 +26,6 @@ Resume evidence is immutable input, never overwritten. The provisioner skips rec The first registration pass found no lockfile-backed coarse candidates in `p-queue` or `ajv`. Under the preregistered replacement rule, `ofetch`, `defu`, and `hookable` were appended before any compiler outcome was inspected. Registration array order is sampling order; the freezer stops once 30 eligible patches exist and never uses more than 10 from one repository. +That replacement pass produced 19 eligible patches: 10 from `ts-pattern`, 3 from `ofetch`, 2 from `defu`, and 4 from `hookable`. Before inspecting any compiler outcome, `class-transformer` and `class-validator` were appended in that order as MIT-licensed npm replacements with committed lockfiles and deterministic, non-browser Jest commands. Their history is inspected only after this registration change is committed. + Before provisioning, the selected digest was capability-probed as Node `22.17.0`, npm `10.9.2`, and Corepack `0.33.0`; the registration metadata was corrected from the planned Node/npm patch versions to those observed immutable-image versions. No repository tests or PureFlow compiler outcomes had been run. diff --git a/docs/v0.3/r7/repositories.json b/docs/v0.3/r7/repositories.json index b835fd3..a12db14 100644 --- a/docs/v0.3/r7/repositories.json +++ b/docs/v0.3/r7/repositories.json @@ -76,5 +76,31 @@ "installArgv": ["corepack", "pnpm", "install", "--frozen-lockfile", "--ignore-scripts"], "testArgv": ["corepack", "pnpm", "test"], "candidateHistoryArgv": ["git", "rev-list", "--first-parent", "b77477c027039362ee0ec4f39b8998c4f1b21707"] + }, + { + "schemaVersion": 1, + "repositoryId": "class-transformer", + "url": "https://github.com/typestack/class-transformer.git", + "licenseSpdx": "MIT", + "pinnedTip": "a2734a5d154c9b0beb9a10555a04416bc371de06", + "nodeVersion": "22.17.0", + "packageManager": "npm", + "packageManagerVersion": "10.9.2", + "installArgv": ["npm", "ci", "--ignore-scripts"], + "testArgv": ["npm", "run", "test:ci", "--", "--coverage=false"], + "candidateHistoryArgv": ["git", "rev-list", "--first-parent", "a2734a5d154c9b0beb9a10555a04416bc371de06"] + }, + { + "schemaVersion": 1, + "repositoryId": "class-validator", + "url": "https://github.com/typestack/class-validator.git", + "licenseSpdx": "MIT", + "pinnedTip": "2e1a5c27dbd65b80e27fe96b49bd6e6641fa3603", + "nodeVersion": "22.17.0", + "packageManager": "npm", + "packageManagerVersion": "10.9.2", + "installArgv": ["npm", "ci", "--ignore-scripts"], + "testArgv": ["npm", "run", "test:ci", "--", "--coverage=false"], + "candidateHistoryArgv": ["git", "rev-list", "--first-parent", "2e1a5c27dbd65b80e27fe96b49bd6e6641fa3603"] } ] From 98eb1df755726e1d18bb775ae17cabdd08115367 Mon Sep 17 00:00:00 2001 From: Error Lover Date: Sat, 1 Aug 2026 05:25:27 +0300 Subject: [PATCH 11/21] perf: batch R7 history inspection --- extension/src/corpus/scan.ts | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/extension/src/corpus/scan.ts b/extension/src/corpus/scan.ts index 818135e..3378861 100644 --- a/extension/src/corpus/scan.ts +++ b/extension/src/corpus/scan.ts @@ -79,14 +79,32 @@ export async function scanRepository( .split(/\r?\n/) .filter(Boolean); if (history[0] !== registration.pinnedTip) throw new Error("Candidate history does not start at the pinned tip"); + const historyArgs = registration.candidateHistoryArgv.slice(2); + const parentLines = (await git(root, ["rev-list", "--parents", ...historyArgs])) + .split(/\r?\n/) + .filter(Boolean); + const parentsByCommit = new Map(parentLines.map((line) => { + const parents = line.split(" "); + return [parents[0]!, parents] as const; + })); + const pathsByCommit = parseHistoryPaths(await git(root, [ + "log", + ...historyArgs, + "--format=%x1e%H%x00", + "--name-only", + "--diff-merges=first-parent", + "-z", + ], false)); const drafts: CandidatePreflight[] = []; let ordinal = 0; for (const targetCommit of history) { - const parents = (await git(root, ["rev-list", "--parents", "-n", "1", targetCommit])).split(" "); + const parents = parentsByCommit.get(targetCommit); + if (parents === undefined) throw new Error(`Missing parent record for ${targetCommit}`); if (parents.length < 2) continue; const baseCommit = parents[1]!; - const paths = splitNul(await git(root, ["diff", "--name-only", "-z", baseCommit, targetCommit], false)); + const paths = pathsByCommit.get(targetCommit); + if (paths === undefined) throw new Error(`Missing changed-path record for ${targetCommit}`); if (!isCoarseCandidate(paths)) continue; ordinal += 1; if (ordinal > 60) break; @@ -95,6 +113,18 @@ export async function scanRepository( return drafts; } +function parseHistoryPaths(value: string): Map { + const result = new Map(); + for (const record of value.split("\x1e")) { + if (record.length === 0) continue; + const fields = splitNul(record.replace(/^\r?\n/, "")); + const commit = fields.shift(); + if (commit === undefined || !/^[0-9a-f]{40}$/.test(commit)) throw new Error("Malformed Git history record"); + result.set(commit, fields.map((field) => field.replace(/^[\r\n]+/, "")).filter(Boolean)); + } + return result; +} + export function completeCandidate(draft: CandidatePreflight, evidence: ProvisionEvidence): CandidateFacts { if (!hasExactKeys(evidence, provisionKeys) && !hasExactKeys(evidence, diagnosticProvisionKeys)) { throw new Error("provision evidence contains unknown or missing fields"); From ddc386229255198753e4999e840f79e6523cc0a3 Mon Sep 17 00:00:00 2001 From: Error Lover Date: Sat, 1 Aug 2026 05:31:40 +0300 Subject: [PATCH 12/21] docs: preregister final R7 replacement --- docs/v0.3/r7/README.md | 2 ++ docs/v0.3/r7/repositories.json | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/docs/v0.3/r7/README.md b/docs/v0.3/r7/README.md index ed22bfd..d325850 100644 --- a/docs/v0.3/r7/README.md +++ b/docs/v0.3/r7/README.md @@ -28,4 +28,6 @@ The first registration pass found no lockfile-backed coarse candidates in `p-que That replacement pass produced 19 eligible patches: 10 from `ts-pattern`, 3 from `ofetch`, 2 from `defu`, and 4 from `hookable`. Before inspecting any compiler outcome, `class-transformer` and `class-validator` were appended in that order as MIT-licensed npm replacements with committed lockfiles and deterministic, non-browser Jest commands. Their history is inspected only after this registration change is committed. +Provisioning then showed that all six structurally viable `class-transformer` candidates fail the frozen runtime/test boundary, so none can enter the corpus. Before any compiler execution, `io-ts` was appended after `class-validator` as the next MIT-licensed npm replacement. Its registered command runs only the repository's deterministic Vitest suite; lint, documentation generation, release tasks, and performance tests are outside the R7 execution boundary. + Before provisioning, the selected digest was capability-probed as Node `22.17.0`, npm `10.9.2`, and Corepack `0.33.0`; the registration metadata was corrected from the planned Node/npm patch versions to those observed immutable-image versions. No repository tests or PureFlow compiler outcomes had been run. diff --git a/docs/v0.3/r7/repositories.json b/docs/v0.3/r7/repositories.json index a12db14..ccc6167 100644 --- a/docs/v0.3/r7/repositories.json +++ b/docs/v0.3/r7/repositories.json @@ -102,5 +102,18 @@ "installArgv": ["npm", "ci", "--ignore-scripts"], "testArgv": ["npm", "run", "test:ci", "--", "--coverage=false"], "candidateHistoryArgv": ["git", "rev-list", "--first-parent", "2e1a5c27dbd65b80e27fe96b49bd6e6641fa3603"] + }, + { + "schemaVersion": 1, + "repositoryId": "io-ts", + "url": "https://github.com/gcanti/io-ts.git", + "licenseSpdx": "MIT", + "pinnedTip": "864a3a2f03c5d7b974afeb1da0faf46c21758779", + "nodeVersion": "22.17.0", + "packageManager": "npm", + "packageManagerVersion": "10.9.2", + "installArgv": ["npm", "ci", "--ignore-scripts"], + "testArgv": ["npm", "run", "vitest"], + "candidateHistoryArgv": ["git", "rev-list", "--first-parent", "864a3a2f03c5d7b974afeb1da0faf46c21758779"] } ] From b7919ed965fc1fdaf39ecde23c9766779cfadd1b Mon Sep 17 00:00:00 2001 From: Error Lover Date: Sat, 1 Aug 2026 05:47:51 +0300 Subject: [PATCH 13/21] feat: merge immutable R7 candidate evidence --- docs/v0.3/r7/README.md | 1 + extension/src/corpus/cli.ts | 9 ++++++++- extension/src/corpus/freeze.ts | 15 +++++++++++++++ extension/test/corpus-freeze.test.ts | 13 +++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/docs/v0.3/r7/README.md b/docs/v0.3/r7/README.md index d325850..0c6ab8f 100644 --- a/docs/v0.3/r7/README.md +++ b/docs/v0.3/r7/README.md @@ -13,6 +13,7 @@ From `extension/`: npm run r7:corpus -- scan npm run r7:corpus -- provision [prior-evidence.json] npm run r7:corpus -- complete +npm run r7:corpus -- merge [...] npm run r7:corpus -- freeze ``` diff --git a/extension/src/corpus/cli.ts b/extension/src/corpus/cli.ts index 76c12eb..a34af93 100644 --- a/extension/src/corpus/cli.ts +++ b/extension/src/corpus/cli.ts @@ -1,7 +1,7 @@ import { readFile, writeFile } from "node:fs/promises"; import { resolve } from "node:path"; import { completeCandidate, deferCandidate, scanRepository, type CandidatePreflight, type ProvisionEvidence } from "./scan"; -import { freezeCorpus, type CandidateFacts, type RepositoryRegistration } from "./freeze"; +import { freezeCorpus, mergeCandidateSets, type CandidateFacts, type RepositoryRegistration } from "./freeze"; import { provisionRepository } from "./provision"; void main(); @@ -34,6 +34,12 @@ async function main(): Promise { }); await writeNewJson(args[2]!, completed); process.stdout.write(`Completed ${completed.length} eligibility records.\n`); + } else if (command === "merge" && args.length >= 3) { + const output = args.at(-1)!; + const sets = await Promise.all(args.slice(0, -1).map((path) => readJson(path))); + const merged = mergeCandidateSets(sets); + await writeNewJson(output, merged); + process.stdout.write(`Merged ${merged.length} eligibility records.\n`); } else if (command === "freeze" && args.length === 3) { const repositories = await readJson(args[0]!); const candidates = await readJson(args[1]!); @@ -46,6 +52,7 @@ async function main(): Promise { " npm run r7:corpus -- scan ", " npm run r7:corpus -- provision [prior-evidence.json]", " npm run r7:corpus -- complete ", + " npm run r7:corpus -- merge [...] ", " npm run r7:corpus -- freeze ", "", ].join("\n")); diff --git a/extension/src/corpus/freeze.ts b/extension/src/corpus/freeze.ts index 1d35d0a..ac2cc5c 100644 --- a/extension/src/corpus/freeze.ts +++ b/extension/src/corpus/freeze.ts @@ -150,6 +150,21 @@ export function classifyCandidate(candidate: CandidateFacts): CandidateClassific return { status: "eligible" }; } +export function mergeCandidateSets(sets: readonly (readonly CandidateFacts[])[]): CandidateFacts[] { + const merged: CandidateFacts[] = []; + const seen = new Set(); + for (const set of sets) { + for (const candidate of set) { + validateCandidate(candidate); + const identity = `${candidate.repositoryId}\0${candidate.targetCommit}`; + if (seen.has(identity)) throw new Error(`Duplicate candidate: ${candidate.repositoryId}/${candidate.targetCommit}`); + seen.add(identity); + merged.push(structuredClone(candidate)); + } + } + return merged; +} + export function freezeCorpus( registrations: readonly RepositoryRegistration[], candidates: readonly CandidateFacts[], diff --git a/extension/test/corpus-freeze.test.ts b/extension/test/corpus-freeze.test.ts index 81df05a..0d4f7a4 100644 --- a/extension/test/corpus-freeze.test.ts +++ b/extension/test/corpus-freeze.test.ts @@ -3,11 +3,24 @@ import { describe, expect, it } from "vitest"; import { classifyCandidate, freezeCorpus, + mergeCandidateSets, type CandidateFacts, type RepositoryRegistration, } from "../src/corpus/freeze"; describe("R7 corpus freeze", () => { + it("merges completed candidate sets in input order and refuses duplicates", () => { + const first = [candidate("repo-a", 1), candidate("repo-a", 2)]; + const second = [candidate("repo-b", 1, 20)]; + + expect(mergeCandidateSets([first, second]).map(({ repositoryId, ordinal }) => `${repositoryId}/${ordinal}`)).toEqual([ + "repo-a/1", + "repo-a/2", + "repo-b/1", + ]); + expect(() => mergeCandidateSets([first, [structuredClone(first[0]!)]] )).toThrow("Duplicate candidate"); + }); + it("assigns exactly one first-match exclusion code", () => { const facts = candidate("repo-a", 1); facts.adjacentFirstParent = false; From 1d2a578d90ca18b60cf67173153305bcca2cc782 Mon Sep 17 00:00:00 2001 From: Error Lover Date: Sat, 1 Aug 2026 06:26:15 +0300 Subject: [PATCH 14/21] docs: preregister ufo for R7 shortfall --- docs/v0.3/r7/README.md | 2 ++ docs/v0.3/r7/repositories.json | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/docs/v0.3/r7/README.md b/docs/v0.3/r7/README.md index 0c6ab8f..53edb1f 100644 --- a/docs/v0.3/r7/README.md +++ b/docs/v0.3/r7/README.md @@ -31,4 +31,6 @@ That replacement pass produced 19 eligible patches: 10 from `ts-pattern`, 3 from Provisioning then showed that all six structurally viable `class-transformer` candidates fail the frozen runtime/test boundary, so none can enter the corpus. Before any compiler execution, `io-ts` was appended after `class-validator` as the next MIT-licensed npm replacement. Its registered command runs only the repository's deterministic Vitest suite; lint, documentation generation, release tasks, and performance tests are outside the R7 execution boundary. +`class-validator` exhausted 31 provisioned candidates with 8 eligible results, bringing the preregistered total to 27. Static inspection after `io-ts` registration showed its frozen `vitest` script exists for only the newest structurally viable historical candidate; the command is not changed after that observation. Before compiler execution, `ufo` was appended as the next MIT-licensed replacement with a lockfile-pinned pnpm toolchain and a direct Vitest/typecheck command that excludes lint, build, release, and documentation tasks. + Before provisioning, the selected digest was capability-probed as Node `22.17.0`, npm `10.9.2`, and Corepack `0.33.0`; the registration metadata was corrected from the planned Node/npm patch versions to those observed immutable-image versions. No repository tests or PureFlow compiler outcomes had been run. diff --git a/docs/v0.3/r7/repositories.json b/docs/v0.3/r7/repositories.json index ccc6167..6268d58 100644 --- a/docs/v0.3/r7/repositories.json +++ b/docs/v0.3/r7/repositories.json @@ -115,5 +115,18 @@ "installArgv": ["npm", "ci", "--ignore-scripts"], "testArgv": ["npm", "run", "vitest"], "candidateHistoryArgv": ["git", "rev-list", "--first-parent", "864a3a2f03c5d7b974afeb1da0faf46c21758779"] + }, + { + "schemaVersion": 1, + "repositoryId": "ufo", + "url": "https://github.com/unjs/ufo.git", + "licenseSpdx": "MIT", + "pinnedTip": "f06c800d0c59f2a4a1b9ba65eb6cb61a84419be6", + "nodeVersion": "22.17.0", + "packageManager": "pnpm", + "packageManagerVersion": "10.33.2", + "installArgv": ["corepack", "pnpm", "install", "--frozen-lockfile", "--ignore-scripts"], + "testArgv": ["corepack", "pnpm", "exec", "vitest", "run", "--typecheck"], + "candidateHistoryArgv": ["git", "rev-list", "--first-parent", "f06c800d0c59f2a4a1b9ba65eb6cb61a84419be6"] } ] From e3890d8af8a6fe5e5b5c10848d655db81d219075 Mon Sep 17 00:00:00 2001 From: Error Lover Date: Sat, 1 Aug 2026 06:32:34 +0300 Subject: [PATCH 15/21] feat: cap R7 provisioning to remaining corpus slots --- docs/v0.3/r7/README.md | 4 +++- extension/src/corpus/cli.ts | 7 ++++--- extension/src/corpus/provision.ts | 15 +++++++++++++-- extension/test/corpus-provision.test.ts | 8 ++++++++ 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/docs/v0.3/r7/README.md b/docs/v0.3/r7/README.md index 53edb1f..906a1d9 100644 --- a/docs/v0.3/r7/README.md +++ b/docs/v0.3/r7/README.md @@ -11,7 +11,7 @@ From `extension/`: ```powershell npm run r7:corpus -- scan -npm run r7:corpus -- provision [prior-evidence.json] +npm run r7:corpus -- provision [prior-evidence.json] [eligible-limit] npm run r7:corpus -- complete npm run r7:corpus -- merge [...] npm run r7:corpus -- freeze @@ -25,6 +25,8 @@ For pnpm repositories, the controller installs the registered pnpm version into Resume evidence is immutable input, never overwritten. The provisioner skips recorded targets, advances in ordinal order, and sizes each batch to the remaining eligible slots so it never executes a candidate after the tenth passing base/target pair. +The optional `eligible-limit` lowers that ceiling when earlier registrations leave fewer than ten global corpus slots. It must be between 1 and 10 and follows an explicit prior-evidence input, which may be `empty-evidence.json`; this keeps the stop reason auditable. + The first registration pass found no lockfile-backed coarse candidates in `p-queue` or `ajv`. Under the preregistered replacement rule, `ofetch`, `defu`, and `hookable` were appended before any compiler outcome was inspected. Registration array order is sampling order; the freezer stops once 30 eligible patches exist and never uses more than 10 from one repository. That replacement pass produced 19 eligible patches: 10 from `ts-pattern`, 3 from `ofetch`, 2 from `defu`, and 4 from `hookable`. Before inspecting any compiler outcome, `class-transformer` and `class-validator` were appended in that order as MIT-licensed npm replacements with committed lockfiles and deterministic, non-browser Jest commands. Their history is inspected only after this registration change is committed. diff --git a/extension/src/corpus/cli.ts b/extension/src/corpus/cli.ts index a34af93..f6e140c 100644 --- a/extension/src/corpus/cli.ts +++ b/extension/src/corpus/cli.ts @@ -16,13 +16,14 @@ async function main(): Promise { const drafts = await scanRepository(resolve(args[2]!), registration); await writeNewJson(args[3]!, drafts); process.stdout.write(`Scanned ${drafts.length} coarse candidates for ${registration.repositoryId}.\n`); - } else if (command === "provision" && (args.length === 5 || args.length === 6)) { + } else if (command === "provision" && (args.length === 5 || args.length === 6 || args.length === 7)) { const repositories = await readJson(args[0]!); const registration = repositories.find(({ repositoryId }) => repositoryId === args[1]); if (registration === undefined) throw new Error(`Unknown repository registration: ${args[1]}`); const drafts = await readJson(args[2]!); const prior = args[5] === undefined ? {} : await readJson>(args[5]); - const evidence = await provisionRepository(resolve(args[3]!), registration, drafts, prior); + const eligibleLimit = args[6] === undefined ? 10 : Number(args[6]); + const evidence = await provisionRepository(resolve(args[3]!), registration, drafts, prior, eligibleLimit); await writeNewJson(args[4]!, evidence); process.stdout.write(`Provisioned ${Object.keys(evidence).length} candidates for ${registration.repositoryId}.\n`); } else if (command === "complete" && args.length === 3) { @@ -50,7 +51,7 @@ async function main(): Promise { process.stderr.write([ "Usage:", " npm run r7:corpus -- scan ", - " npm run r7:corpus -- provision [prior-evidence.json]", + " npm run r7:corpus -- provision [prior-evidence.json] [eligible-limit]", " npm run r7:corpus -- complete ", " npm run r7:corpus -- merge [...] ", " npm run r7:corpus -- freeze ", diff --git a/extension/src/corpus/provision.ts b/extension/src/corpus/provision.ts index 3c53e33..74bf049 100644 --- a/extension/src/corpus/provision.ts +++ b/extension/src/corpus/provision.ts @@ -53,6 +53,14 @@ export function selectProvisionCandidates(drafts: readonly CandidatePreflight[]) return selected; } +export function provisionBatchSize(accepted: number, eligibleLimit = 10): number { + if (!Number.isSafeInteger(eligibleLimit) || eligibleLimit < 1 || eligibleLimit > 10) { + throw new Error("eligibleLimit must be between 1 and 10"); + } + if (!Number.isSafeInteger(accepted) || accepted < 0) throw new Error("accepted count is invalid"); + return Math.min(3, Math.max(0, eligibleLimit - accepted)); +} + export function buildCorpusDockerArgs(invocation: CorpusDockerInvocation): string[] { if (!/^pureflow-r7-corpus-[0-9a-f]{24}$/.test(invocation.containerName)) throw new Error("Invalid corpus container name"); if (invocation.network !== "bridge" && invocation.network !== "none") throw new Error("Invalid corpus network mode"); @@ -96,6 +104,7 @@ export async function provisionRepository( registration: RepositoryRegistration, drafts: readonly CandidatePreflight[], prior: Readonly> = {}, + eligibleLimit = 10, ): Promise> { const selected = selectProvisionCandidates(drafts); const evidence: Record = structuredClone(prior); @@ -104,8 +113,10 @@ export async function provisionRepository( return item?.basePassed === true && item.targetPassed === true; }).length; const pending = selected.filter(({ targetCommit }) => evidence[targetCommit] === undefined); - for (let offset = 0; offset < pending.length && accepted < 10;) { - const batchSize = Math.min(3, 10 - accepted); + provisionBatchSize(accepted, eligibleLimit); + if (accepted > eligibleLimit) throw new Error("Prior evidence exceeds the eligible limit"); + for (let offset = 0; offset < pending.length && accepted < eligibleLimit;) { + const batchSize = provisionBatchSize(accepted, eligibleLimit); const batch = pending.slice(offset, offset + batchSize); offset += batch.length; const results = await Promise.all(batch.map(async (draft) => ({ diff --git a/extension/test/corpus-provision.test.ts b/extension/test/corpus-provision.test.ts index 83776ec..cc197bf 100644 --- a/extension/test/corpus-provision.test.ts +++ b/extension/test/corpus-provision.test.ts @@ -1,12 +1,20 @@ import { describe, expect, it } from "vitest"; import { buildCorpusDockerArgs, + provisionBatchSize, selectProvisionCandidates, type CorpusDockerInvocation, } from "../src/corpus/provision"; import type { CandidatePreflight } from "../src/corpus/scan"; describe("R7 corpus provisioning", () => { + it("bounds a repository run to the remaining global corpus slots", () => { + expect(provisionBatchSize(0, 2)).toBe(2); + expect(provisionBatchSize(1, 2)).toBe(1); + expect(provisionBatchSize(2, 2)).toBe(0); + expect(() => provisionBatchSize(0, 0)).toThrow("between 1 and 10"); + }); + it("selects only the first ten structurally viable candidates", () => { const drafts = Array.from({ length: 15 }, (_, index) => draft(index + 1)); drafts[0]!.lockfilePresent = false; From f011cb0c37f9261e0fc497f9135fb0dabd6d095a Mon Sep 17 00:00:00 2001 From: Error Lover Date: Sat, 1 Aug 2026 06:47:43 +0300 Subject: [PATCH 16/21] perf: short circuit unavailable R7 test scripts --- docs/v0.3/r7/README.md | 2 + extension/src/corpus/provision.ts | 60 +++++++++++++++++++++++++ extension/test/corpus-provision.test.ts | 8 ++++ 3 files changed, 70 insertions(+) diff --git a/docs/v0.3/r7/README.md b/docs/v0.3/r7/README.md index 906a1d9..ea6f1fa 100644 --- a/docs/v0.3/r7/README.md +++ b/docs/v0.3/r7/README.md @@ -27,6 +27,8 @@ Resume evidence is immutable input, never overwritten. The provisioner skips rec The optional `eligible-limit` lowers that ceiling when earlier registrations leave fewer than ten global corpus slots. It must be between 1 and 10 and follows an explicit prior-evidence input, which may be `empty-evidence.json`; this keeps the stop reason auditable. +For a registered `npm run