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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 38 additions & 4 deletions packages/loopover-miner/docs/cross-repo-evaluation.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,52 @@ fleet run-manifest).
- `--manifest path/to/manifest.json` — alternate benchmark set (e.g. a fixture manifest in tests)
- `--require-majority` — exit `1` unless a strict majority of repos pass (for CI-style gating)

## Full-execution mode (#7634)

`--full-execution` extends the readiness check into the real **discover → plan → code → test** loop, still
**dry-run only**: no live PR submission, no forge API calls, no write access to the third-party repos. Each repo
that passes readiness is copied into a **discardable scratch workspace** (the benchmark clone is never mutated);
the coding agent runs against a synthetic benchmark issue inside that copy, and the repo's own inferred build and
test commands are executed there. The scratch tree is removed afterward in every outcome — execute-locally-and-discard.

```bash
node packages/loopover-miner/scripts/cross-repo-evaluation.mjs --full-execution
```

Prerequisites beyond the readiness mode: a configured coding-agent provider (`MINER_CODING_AGENT_PROVIDER`, see
[`docs/coding-agent-driver.md`](coding-agent-driver.md)). Without one, each repo reports `agent_run_failed`
("No runnable coding-agent driver") rather than silently skipping.

Execution failures extend — never replace — the readiness taxonomy, in the same report format:

| Category | Meaning |
| --- | --- |
| `agent_run_failed` | No runnable driver was configured, or the coding agent errored / did not finish |
| `noop_diff` | The agent reported success but the generated diff changed no files |
| `build_failed` | A diff was generated but the stack's inferred build command failed (didn't compile) |
| `test_failed` | The build was fine but the repo's own test suite failed against the diff |

A repo that passes readiness but has **no inferred test command** fails with the existing `execution_gap`
category — a generated diff that nothing can validate is not a pass. Per-command wall-clock is capped
(`DEFAULT_CROSS_REPO_EXECUTION_COMMAND_TIMEOUT_MS`, 10 minutes each for build and test); the agent's turn budget
defaults to `DEFAULT_CROSS_REPO_EXECUTION_MAX_TURNS` (24).

## Library API

Pure functions live in [`lib/cross-repo-evaluation.js`](../lib/cross-repo-evaluation.js):

- `parseCrossRepoEvaluationManifest(content)`
- `evaluateRepoReadiness(entry, options)` — inject `existsSync`, `detectRepoStack`, etc. for unit tests
- `runCrossRepoEvaluation(parsed, options)`
- `evaluateRepoExecution(entry, options)` — full-execution mode for one repo (#7634); inject `driver`,
`prepareExecutionWorkspace`, `runCommand`, etc. for unit tests
- `runCrossRepoEvaluation(parsed, options)` / `runCrossRepoFullExecution(parsed, options)`
- `summarizeCrossRepoEvaluation(results)`
- `formatCrossRepoEvaluationReport(results, summary)`

## Wiring

This harness is **readiness-only**: it does not run the coding agent, open PRs, or call forge APIs. A green report
means the miner’s repo-agnostic stack-detection and coding-task-spec path is prepared for the benchmark repo; a live
attempt still needs credentials, governor policy, and queue state as documented in [`DEPLOYMENT.md`](../DEPLOYMENT.md).
The default mode is **readiness-only**: it does not run the coding agent, open PRs, or call forge APIs. A green
readiness report means the miner’s repo-agnostic stack-detection and coding-task-spec path is prepared for the
benchmark repo. `--full-execution` (#7634) additionally runs the agent and the repo's own test suite locally in a
discarded scratch copy — it still never opens a PR or touches any forge API; a live attempt still needs
credentials, governor policy, and queue state as documented in [`DEPLOYMENT.md`](../DEPLOYMENT.md).
84 changes: 84 additions & 0 deletions packages/loopover-miner/lib/cross-repo-evaluation.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type EvaluateRepoReadinessOptions = {
ready: boolean;
verdict?: string;
instructions?: string;
acceptanceCriteriaPath?: string;
};
};
/** Canonical `owner/repo` with exactly one slash and safe segments; anything else → null. */
Expand Down Expand Up @@ -100,4 +101,87 @@ export declare function summarizeCrossRepoEvaluation(results: CrossRepoEvaluatio
* Human-readable pass/fail report for one evaluation run (#4788).
*/
export declare function formatCrossRepoEvaluationReport(results: CrossRepoEvaluationResult[], summary?: CrossRepoEvaluationSummary): string;
/** Execution-specific failure taxonomy (#7634), extending — not replacing — CROSS_REPO_FAILURE_CATEGORY. */
export declare const CROSS_REPO_EXECUTION_FAILURE_CATEGORY: Readonly<{
AGENT_RUN: "agent_run_failed";
NOOP_DIFF: "noop_diff";
BUILD: "build_failed";
TEST: "test_failed";
}>;
/** A benchmark attempt works a small synthetic issue, so a modest turn cap keeps dry-runs bounded without
* starving a real agent; callers tune via options.maxTurns. */
export declare const DEFAULT_CROSS_REPO_EXECUTION_MAX_TURNS: number;
/** Per-command (build, then test) wall-clock cap — generous enough for a cold dependency install on the larger
* benchmark repos, small enough that a hung suite cannot wedge the whole run. */
export declare const DEFAULT_CROSS_REPO_EXECUTION_COMMAND_TIMEOUT_MS: number;
export type CrossRepoExecutionCommandResult = {
code: number | null;
stdout: string;
stderr: string;
timedOut: boolean;
};
export type CrossRepoExecutionRunCommandFn = (command: string, options: {
cwd: string;
timeoutMs: number;
}) => Promise<CrossRepoExecutionCommandResult>;
export type CrossRepoExecutionWorkspace = {
path: string;
cleanup: () => void;
};
/** Structural mirror of the engine's CodingAgentDriver contract — kept local so this module only loads the real
* driver construction (and its engine dependency) lazily, on the one path that actually runs an agent. */
export type CrossRepoExecutionDriver = {
run(task: {
attemptId: string;
workingDirectory: string;
acceptanceCriteriaPath: string;
instructions: string;
maxTurns: number;
}): Promise<{
ok: boolean;
changedFiles: readonly string[];
summary: string;
error?: string | undefined;
}>;
};
export type CrossRepoExecutionDetails = {
attempted: boolean;
changedFileCount: number | null;
buildRan: boolean;
testRan: boolean;
};
export type CrossRepoExecutionEvaluationResult = CrossRepoEvaluationResult & {
execution: CrossRepoExecutionDetails | null;
};
export type EvaluateRepoExecutionOptions = EvaluateRepoReadinessOptions & {
driver?: CrossRepoExecutionDriver;
prepareExecutionWorkspace?: (repoPath: string) => CrossRepoExecutionWorkspace;
runCommand?: CrossRepoExecutionRunCommandFn;
maxTurns?: number;
commandTimeoutMs?: number;
};
/** Copy the benchmark clone into a discardable temp tree — the agent and the repo's test suite only ever touch
* the copy, so the clone stays pristine and cleanup is a single recursive remove. */
export declare function defaultPrepareExecutionWorkspace(repoPath: string): CrossRepoExecutionWorkspace;
/** Command runner for the stack's inferred build/test commands. detectRepoStack only ever emits simple
* `tool subcommand` forms ("npm test", "cargo build", "npm run build"), so the command is tokenized on
* whitespace and exec'd DIRECTLY -- deliberately no `shell: true`, so nothing in a benchmark repo's manifest
* can smuggle shell metacharacters into an interpreted shell line. Mirrors coding-agent-construction's
* createRealCliSubprocessSpawn otherwise: capture both streams and RESOLVE (never reject) on timeout or spawn
* error, so partial output stays diagnosable. Promise resolution is idempotent, so a `close` firing after the
* timeout already resolved needs no guard. */
export declare function createDefaultCrossRepoExecutionRunCommand(): CrossRepoExecutionRunCommandFn;
/**
* Run the full discover -> plan -> code -> test loop for one benchmark repo, dry-run (#7634). Readiness gates
* first (its failures pass through unchanged); execution then happens entirely inside a scratch copy that is
* discarded in every outcome.
*/
export declare function evaluateRepoExecution(entry: CrossRepoEvaluationManifestRepo, options?: EvaluateRepoExecutionOptions): Promise<CrossRepoExecutionEvaluationResult>;
/**
* Run full-execution mode across every repo in a parsed manifest (#7634), sequentially — agent runs and test
* suites are heavyweight, so no parallel fan-out.
*/
export declare function runCrossRepoFullExecution(parsed: ParsedCrossRepoEvaluationManifest, options?: {
repoFilter?: string;
} & EvaluateRepoExecutionOptions): Promise<CrossRepoExecutionEvaluationResult[]>;
export {};
249 changes: 232 additions & 17 deletions packages/loopover-miner/lib/cross-repo-evaluation.js

Large diffs are not rendered by default.

Loading