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
41 changes: 38 additions & 3 deletions packages/loopover-miner/docs/cross-repo-evaluation.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,35 @@ 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` — **dry-run** execution mode (#7634): clone each repo, run the
discover→plan→code→test loop locally, and run the target repo's own tests. See below.

## Full execution mode (dry-run) (#7634)

`--full-execution` opts into a **dry-run** that goes one step past readiness: for every repo that already passes the
readiness gate above, it actually runs the discover→plan→code→test loop **locally** and reports pass/fail with
execution-specific categories. It is strictly **read/execute-locally-and-discard**:

1. **Clone / checkout** the repo locally (via `ensureRepoCloned`; a read-only local clone — never a write-back)
2. **Plan** — recompose the same (leak-free) coding-task spec used by readiness
3. **Code** — run the coding agent to produce a diff (the default is a **non-spawning shadow** that produces no
diff and uses no credentials; inject `runCodingAgent` to drive a real/fake agent)
4. **Test** — run the **target repo's own** inferred test command locally against the produced change

The execution-specific failure categories extend (never replace) the readiness taxonomy:

| Category | Meaning |
| --- | --- |
| `exec_setup_gap` | Clone / checkout of the target repo failed before the loop could start |
| `plan_compile_gap` | Plan formed but the code phase did not produce a compiling change |
| `test_failure` | Change compiled but the target repo's own tests failed (or timed out) |
| `no_op_diff` | Tests passed but the coding agent produced an empty (no-op) diff |

The report gains one line, `dry-run full-execution: N/total entered the code+test loop`, and the readiness format is
otherwise unchanged. **Dry-run safety is structural:** there is no PR-open / forge-write / credential path anywhere
in the execution loop — the clone, coding-agent, and test-run steps are all injectable seams (`cloneRepo`,
`runCodingAgent`, `runTests`) that unit tests replace with fakes for zero real IO. No `gh pr create`, no forge API,
no third-party write ever runs.

## Library API

Expand All @@ -68,11 +97,17 @@ Pure functions live in [`lib/cross-repo-evaluation.js`](../lib/cross-repo-evalua
- `parseCrossRepoEvaluationManifest(content)`
- `evaluateRepoReadiness(entry, options)` — inject `existsSync`, `detectRepoStack`, etc. for unit tests
- `runCrossRepoEvaluation(parsed, options)`
- `evaluateRepoExecution(entry, options)` — dry-run full-execution (#7634); inject `cloneRepo`, `runCodingAgent`,
`runTests` for unit tests
- `runCrossRepoExecution(parsed, options)` — full-execution across a parsed manifest
- `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).
By default 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. `--full-execution` opts into a live-ish **dry-run** that additionally clones, runs the coding agent, and runs
the target repo's own tests locally — but still **never opens a PR or writes to the third-party repo**; a real
attempt that submits a PR still needs credentials, governor policy, and queue state as documented in
[`DEPLOYMENT.md`](../DEPLOYMENT.md).
75 changes: 74 additions & 1 deletion packages/loopover-miner/lib/cross-repo-evaluation.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import type { RepoStackResult } from "./stack-detection.js";
/** Failure taxonomy surfaced in per-repo reports (#4788). */
/** Failure taxonomy surfaced in per-repo reports (#4788). The first five members are the offline
* readiness taxonomy; the four `EXEC_*`/plan/test/no-op members are the dry-run full-execution taxonomy
* (#7634) surfaced only when the `--full-execution` loop actually clones, runs the agent, and runs the
* target repo's own tests. New members are appended in the same frozen literal so existing members are
* never removed. */
export declare const CROSS_REPO_FAILURE_CATEGORY: Readonly<{
STACK_DETECTION: "stack_detection_gap";
EXECUTION: "execution_gap";
GITTENSOR_ASSUMPTION: "loopover_assumption";
CLONE_SETUP: "clone_setup";
OTHER: "other";
EXEC_SETUP: "exec_setup_gap";
PLAN_COMPILE: "plan_compile_gap";
TEST_FAILURE: "test_failure";
NO_OP_DIFF: "no_op_diff";
}>;
/** Instruction substrings that indicate a POSITIVE loopover/LoopOver CI assumption leaked into the agent prompt.
* Lines that explicitly tell the agent *not* to assume these are filtered out before scanning. */
Expand Down Expand Up @@ -41,15 +49,50 @@ export type CrossRepoEvaluationResult = {
line: string;
}>;
stack?: RepoStackResult;
/** Full-execution (dry-run) fields (#7634); absent on readiness-only results. */
executed?: boolean;
changedFiles?: string[];
testCommand?: string | null;
testExitCode?: number | null;
};
export type CrossRepoEvaluationSummary = {
total: number;
passed: number;
failed: number;
majorityPassed: boolean;
withoutLoopoverConfig: number;
/** Repos that entered the dry-run clone+agent+test loop (#7634); 0 for a readiness-only run. */
executedCount: number;
failuresByCategory: Record<string, number>;
};
/** Result of the dry-run clone/checkout seam (#7634). Mirrors repo-clone's EnsureRepoClonedResult. */
export type CrossRepoExecutionCloneResult = {
ok: boolean;
repoPath?: string;
error?: string;
};
/** Context handed to the dry-run coding-agent seam (#7634). */
export type CrossRepoExecutionAgentContext = {
repoFullName: string;
repoPath: string;
instructions: string;
stack: RepoStackResult;
entry: CrossRepoEvaluationManifestRepo;
};
/** Result of the dry-run coding-agent seam (#7634): the produced (canned/real) diff surface. */
export type CrossRepoExecutionAgentResult = {
ok: boolean;
changedFiles?: readonly string[];
summary?: string;
error?: string;
};
/** Result of the dry-run target-repo test seam (#7634). Mirrors the executeLocalWrite subprocess shape. */
export type CrossRepoExecutionTestResult = {
code: number | null;
stdout?: string;
stderr?: string;
timedOut?: boolean;
};
type EvaluateRepoReadinessOptions = {
repoPath?: string;
resolveRepoPath?: (entry: {
Expand All @@ -66,6 +109,11 @@ type EvaluateRepoReadinessOptions = {
verdict?: string;
instructions?: string;
};
fullExecution?: boolean;
testTimeoutMs?: number;
cloneRepo?: (entry: CrossRepoEvaluationManifestRepo, options: EvaluateRepoReadinessOptions) => CrossRepoExecutionCloneResult | Promise<CrossRepoExecutionCloneResult>;
runCodingAgent?: (context: CrossRepoExecutionAgentContext) => CrossRepoExecutionAgentResult | Promise<CrossRepoExecutionAgentResult>;
runTests?: (command: string, cwd: string, timeoutMs: number) => CrossRepoExecutionTestResult | Promise<CrossRepoExecutionTestResult>;
};
/** Canonical `owner/repo` with exactly one slash and safe segments; anything else → null. */
export declare function normalizeCrossRepoFullName(value: unknown): string | null;
Expand All @@ -86,12 +134,37 @@ export declare function scanPositiveLoopoverAssumptions(text: string): Array<{
* Evaluate one benchmark repo's miner readiness without running a live coding agent (#4788).
*/
export declare function evaluateRepoReadiness(entry: CrossRepoEvaluationManifestRepo, options?: EvaluateRepoReadinessOptions): CrossRepoEvaluationResult;
/** 10-minute cap on a target repo's own test suite when the caller does not override `testTimeoutMs` (#7634). */
export declare const DEFAULT_EXECUTION_TEST_TIMEOUT_MS: number;
/**
* DRY-RUN full-execution evaluation of one benchmark repo (#7634). Reuses the readiness gate unchanged, then --
* only for a repo that already passes readiness -- runs the discover->plan->code->test loop LOCALLY:
*
* 1. clone/checkout the repo (setup) -> EXEC_SETUP on failure
* 2. reuse the readiness-composed spec (plan) -- no second (side-effecting) buildCodingTaskSpec call
* 3. run the coding agent to produce a diff -> PLAN_COMPILE when the code phase does not converge
* 4. run the target repo's own tests locally -> TEST_FAILURE when the suite is red
* 5. no-op guard -> NO_OP_DIFF when tests pass but the diff is empty
*
* Every side-effecting step (clone, agent, tests) is behind an injectable `options.*` seam so unit tests drive
* the whole loop with fakes and zero real IO. There is deliberately NO PR-open / forge-write / credential path:
* the harness clones and executes locally, then discards. Readiness-mode behavior is untouched.
*/
export declare function evaluateRepoExecution(entry: CrossRepoEvaluationManifestRepo, options?: EvaluateRepoReadinessOptions): Promise<CrossRepoEvaluationResult>;
/**
* Run the harness across every repo in a parsed manifest (#4788).
*/
export declare function runCrossRepoEvaluation(parsed: ParsedCrossRepoEvaluationManifest, options?: {
repoFilter?: string;
} & EvaluateRepoReadinessOptions): CrossRepoEvaluationResult[];
/**
* DRY-RUN full-execution run across every repo in a parsed manifest (#7634). Sequential (one clone+agent+test
* loop at a time) so the local machine is never hammered. Same options-injection surface as the readiness run,
* plus the clone/agent/test seams. Repos are read/executed-locally-and-discarded; no PR is ever opened.
*/
export declare function runCrossRepoExecution(parsed: ParsedCrossRepoEvaluationManifest, options?: {
repoFilter?: string;
} & EvaluateRepoReadinessOptions): Promise<CrossRepoEvaluationResult[]>;
/**
* Reduce per-repo results to pass/fail counts and whether a strict majority passed (#4788).
*/
Expand Down
Loading