Skip to content
Closed
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
6 changes: 4 additions & 2 deletions packages/loopover-miner/benchmarks/cross-repo/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
{
"repoFullName": "expressjs/express",
"stackHint": "nodejs",
"requireTestCommand": true
"requireTestCommand": true,
"fullExecution": true
},
{
"repoFullName": "pallets/flask",
Expand All @@ -28,7 +29,8 @@
{
"repoFullName": "lodash/lodash",
"stackHint": "nodejs",
"requireTestCommand": true
"requireTestCommand": true,
"fullExecution": true
}
]
}
47 changes: 44 additions & 3 deletions packages/loopover-miner/docs/cross-repo-evaluation.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ bare `"owner/repo"` string or an object:
- **`stackHint`** — documentation only (not used by the evaluator)
- **`requireTestCommand`** — when `true`, stack detection must infer a test command or the repo fails with
`execution_gap`
- **`fullExecution`** — when `true`, this entry is part of the full-execution subset (#7634): a bare
`--full-execution` run drives the live attempt against every entry flagged `true` (see below)

Malformed manifest fields degrade to documented defaults with warnings (same tolerant-parser convention as the
fleet run-manifest).
Expand Down Expand Up @@ -60,6 +62,43 @@ fleet run-manifest).
- `--repo owner/repo` — evaluate a single manifest entry
- `--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` — run the live attempt loop instead of the readiness check (see below)

## Full-execution mode (#7634)

Readiness answers *"can the miner form a plan for this repo."* **Full-execution mode** answers the real question
behind [#4810](https://github.com/JSONbored/loopover/issues/4810)'s launch-readiness bar — *"does the miner
actually produce working, correct code for this repo"* — by driving the live **discover → plan → code → test**
loop against a subset of the same benchmark repos:

```bash
# Runs every manifest entry flagged "fullExecution": true; --repo overrides and runs one entry.
MINER_CODING_AGENT_PROVIDER=... node packages/loopover-miner/scripts/cross-repo-evaluation.mjs --full-execution
```

It is **dry-run only**, the same read/execute-locally-and-discard posture as the readiness harness, one step
further: the coding agent edits a **throwaway local clone**, the harness captures the resulting diff with `git`
and runs the target repo's **own** build + test commands (from stack detection) locally. There is **no live
GitHub PR submission, no write access to the third-party repos, and no credentials beyond the local clone** — a
PR that adds real PR-submission against a benchmark repo does not satisfy this mode.

Each subset repo still receives one **pass/fail** line in the same report format. A repo **passes** only when the
agent produced a real (non-empty) diff, it built, and the target test suite passed. The failure taxonomy extends
the readiness categories with execution-specific ones:

| Category | Meaning |
| --- | --- |
| `execution_no_diff` | The agent ran (or could not be launched) but produced no usable diff |
| `execution_compile_gap` | The generated diff did not build |
| `execution_test_failure` | The diff built but the target repo's own test suite failed |
| `execution_noop_diff` | Tests passed only because the diff was a no-op (no file changes) |

Readiness-stage failures (`stack_detection_gap`, `clone_setup`, `loopover_assumption`, `execution_gap`) still
apply and short-circuit before the agent runs — a repo the miner cannot even plan for never reaches execution.

Library entry points: `executeRepoAttempt(entry, options)` and `runCrossRepoExecution(parsed, options)` classify
the outcome; the coding-agent step, build, and test runners are all injectable (the CLI wires the real
driver-backed executor; unit tests inject fakes).

## Library API

Expand All @@ -73,6 +112,8 @@ Pure functions live in [`lib/cross-repo-evaluation.js`](../lib/cross-repo-evalua

## 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
report means the miner’s repo-agnostic stack-detection and coding-task-spec path is prepared for the benchmark
repo. `--full-execution` (#7634) goes one step further and runs the agent locally against a throwaway clone, but
still opens no PRs and performs no forge writes. A production attempt against a real target still needs
credentials, governor policy, and queue state as documented in [`DEPLOYMENT.md`](../DEPLOYMENT.md).
97 changes: 96 additions & 1 deletion packages/loopover-miner/lib/cross-repo-evaluation.d.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { spawnSync } from "node:child_process";
import type { RepoStackResult } from "./stack-detection.js";
/** Failure taxonomy surfaced in per-repo reports (#4788). */
/** Failure taxonomy surfaced in per-repo reports. Readiness-mode categories (#4788) plus the full-execution
* categories (#7634) that classify how a live discover → plan → code → test attempt fell short. */
export declare const CROSS_REPO_FAILURE_CATEGORY: Readonly<{
STACK_DETECTION: "stack_detection_gap";
EXECUTION: "execution_gap";
GITTENSOR_ASSUMPTION: "loopover_assumption";
CLONE_SETUP: "clone_setup";
EXECUTION_NO_DIFF: "execution_no_diff";
EXECUTION_COMPILE: "execution_compile_gap";
EXECUTION_TEST: "execution_test_failure";
EXECUTION_NOOP: "execution_noop_diff";
OTHER: "other";
}>;
/** Instruction substrings that indicate a POSITIVE loopover/LoopOver CI assumption leaked into the agent prompt.
Expand All @@ -21,6 +27,9 @@ export type CrossRepoEvaluationManifestRepo = {
stackHint?: string;
requireTestCommand?: boolean;
fixturePath?: string;
/** Opt this entry into full-execution mode (#7634): `--full-execution` runs the live attempt against every
* entry flagged `true` (a `--repo` filter overrides the flag and runs that one entry regardless). */
fullExecution?: boolean;
};
export type ParsedCrossRepoEvaluationManifest = {
present: boolean;
Expand Down Expand Up @@ -67,6 +76,62 @@ type EvaluateRepoReadinessOptions = {
instructions?: string;
};
};
/** Result of running a local shell command (build/test) during a full-execution attempt (#7634). */
export type LocalCommandResult = {
ok: boolean;
/** True when there was no command to run (e.g. the stack inferred no build command) — treated as a pass. */
skipped?: boolean;
code?: number | null;
output?: string;
};
/** Everything a coding-agent executor needs to run one benchmark repo's live attempt (#7634). */
export type CodingAttemptContext = {
repoFullName: string;
repoPath: string;
stack: RepoStackResult;
instructions: string;
attemptId: string;
maxTurns: number;
};
/** What a coding-agent executor reports back: the real unified diff it produced (empty string when it made no
* change), plus whether the agent itself considered the run successful (#7634). */
export type CodingAttemptOutcome = {
ok: boolean;
diff: string;
summary?: string;
error?: string;
};
export type ExecuteRepoAttemptOptions = EvaluateRepoReadinessOptions & {
/** Discover → plan → code step: run the coding agent against the clone and return its diff. The CLI injects a
* real driver-backed executor; unit tests inject a fake. Left unset, the harness reports `execution_no_diff`
* rather than pretending an agent ran. */
runCodingAttempt?: (context: CodingAttemptContext) => CodingAttemptOutcome | Promise<CodingAttemptOutcome>;
/** Build/compile the clone after the diff is applied. Defaults to running `stack.buildCommand` locally. */
compileRepo?: (context: {
repoPath: string;
stack: RepoStackResult;
}) => LocalCommandResult | Promise<LocalCommandResult>;
/** Run the target repo's own test suite locally. Defaults to running `stack.testCommand`. */
runRepoTests?: (context: {
repoPath: string;
stack: RepoStackResult;
}) => LocalCommandResult | Promise<LocalCommandResult>;
/** Shared spawn used by the default compile/test runners; defaults to a real `spawnSync` in the clone dir. */
runLocalCommand?: (command: string, context: {
cwd: string;
env?: NodeJS.ProcessEnv;
}) => LocalCommandResult;
/** Turn budget handed to the coding agent (CLI default: 30). */
maxTurns?: number;
};
export type CrossRepoExecutionResult = CrossRepoEvaluationResult & {
/** False when the attempt never reached the live agent (readiness gated it out). */
executed: boolean;
compilePassed?: boolean | null;
testsPassed?: boolean | null;
/** Character length of the produced diff (0 for a no-op); null when no agent ran. */
diffChars?: number | null;
};
/** Canonical `owner/repo` with exactly one slash and safe segments; anything else → null. */
export declare function normalizeCrossRepoFullName(value: unknown): string | null;
/**
Expand All @@ -92,6 +157,36 @@ export declare function evaluateRepoReadiness(entry: CrossRepoEvaluationManifest
export declare function runCrossRepoEvaluation(parsed: ParsedCrossRepoEvaluationManifest, options?: {
repoFilter?: string;
} & EvaluateRepoReadinessOptions): CrossRepoEvaluationResult[];
/** Real default local-command runner: split the detected command into an argv and spawn it directly in the clone
* dir with NO shell (`shell: false`), so shell metacharacters in untrusted repo metadata (e.g. a crafted
* package.json script name) cannot inject extra commands (#7641). Stack commands are plain whitespace-separated
* argv (`npm run build`, `cargo test`, `go build ./...`). Resolve-not-throw: a non-zero exit becomes `ok: false`
* with its captured output rather than an exception (#7634). Exported for direct unit coverage. */
export declare function defaultRunLocalCommand(command: string, context: {
cwd: string;
env?: NodeJS.ProcessEnv;
}, spawn?: typeof spawnSync): LocalCommandResult;
/**
* Run one benchmark repo's full-execution attempt (#7634): gate on readiness, then run the live discover → plan
* → code → test loop against the local clone and classify the outcome. Dry-run only — no forge writes, no PR
* submission; the agent edits a throwaway clone and the diff/results are discarded by the caller.
*
* The pipeline mirrors the failure taxonomy the issue calls for, in order:
* 1. readiness fails → the existing readiness category (attempt never starts; `executed: false`)
* 2. agent errors / no diff → `execution_no_diff`
* 3. diff doesn't build → `execution_compile_gap`
* 4. builds but tests fail → `execution_test_failure`
* 5. tests pass but diff no-op → `execution_noop_diff`
* 6. tests pass, real diff → PASS
*/
export declare function executeRepoAttempt(entry: CrossRepoEvaluationManifestRepo, options?: ExecuteRepoAttemptOptions): Promise<CrossRepoExecutionResult>;
/**
* Run the full-execution harness across a parsed manifest (#7634). Without a `repoFilter`, only entries flagged
* `fullExecution: true` run (the curated subset); a `repoFilter` overrides the flag and runs that one entry.
*/
export declare function runCrossRepoExecution(parsed: ParsedCrossRepoEvaluationManifest, options?: {
repoFilter?: string;
} & ExecuteRepoAttemptOptions): Promise<CrossRepoExecutionResult[]>;
/**
* Reduce per-repo results to pass/fail counts and whether a strict majority passed (#4788).
*/
Expand Down
Loading
Loading