fix(testbox): sync source on every lease reuse

This commit is contained in:
Vincent Koc
2026-07-10 20:48:28 -07:00
committed by Vincent Koc
parent 1fa8ec497e
commit cf3443796c
4 changed files with 7 additions and 55 deletions
+3 -4
View File
@@ -28,10 +28,9 @@ After the first successful reuse, the wrapper records the lease's base,
dependency, and Testbox workflow fingerprint under `.crabbox/testbox-leases/`.
Source-only edits keep reusing the warmed box. A changed merge base, lockfile,
package-manager input, wrapper, or Testbox workflow fails closed and requires a
fresh lease. `OPENCLAW_TESTBOX_ALLOW_STALE=1` is only for intentional
diagnostics, not release proof. Repeating a targeted run from the exact clean
HEAD that already succeeded skips source sync; changed gates, a new commit, or
a dirty edit sync normally.
fresh lease. Every run still syncs the current checkout.
`OPENCLAW_TESTBOX_ALLOW_STALE=1` is only for intentional diagnostics, not
release proof.
Local test commands below are for human workflows or an explicit agent fallback
requested by the user. Remote-provider unavailability must be reported; it is
-16
View File
@@ -3184,16 +3184,6 @@ function injectFullCheckoutLeaseReclaim(commandArgs) {
return normalizedArgs;
}
function injectReusableTestboxNoSync(commandArgs) {
if (hasOption(commandArgs, "--no-sync")) {
return commandArgs;
}
const normalizedArgs = [...commandArgs];
const { optionEnd } = runCommandBounds(normalizedArgs);
normalizedArgs.splice(optionEnd, 0, "--no-sync");
return normalizedArgs;
}
function injectRemoteTestboxCi(commandArgs, providerName) {
if (commandArgs[0] !== "run" || canonicalProviderName(providerName) !== "blacksmith-testbox") {
return commandArgs;
@@ -3299,12 +3289,6 @@ try {
provider: canonicalProvider,
repoRoot,
});
if (testboxLeaseFreshness?.skipSync && !isChangedGateCommand(runCommandArgs(normalizedArgs))) {
normalizedArgs = injectReusableTestboxNoSync(normalizedArgs);
console.error(
`[crabbox] Testbox ${optionValue(normalizedArgs, "--id")} already has clean HEAD ${testboxLeaseFreshness.current.headSha}; skipping source sync`,
);
}
} catch (error) {
console.error(`[crabbox] ${error instanceof Error ? error.message : String(error)}`);
process.exit(2);
+3 -13
View File
@@ -86,12 +86,6 @@ export function buildTestboxLeaseFingerprint(repoRoot, args) {
};
}
export function testboxLeaseCanSkipSync(saved, current) {
return Boolean(
saved?.syncedCleanHead && current.workingTreeClean && saved.syncedCleanHead === current.headSha,
);
}
export function testboxLeaseStaleReasons(saved, current) {
if (!saved || saved.version !== STATE_VERSION) {
return ["state schema"];
@@ -121,9 +115,9 @@ export function prepareTestboxLeaseFreshness({ args, env, provider, repoRoot })
`Testbox ${id} is stale (${staleReasons.join(", ")}); stop it and warm a fresh lease, or set OPENCLAW_TESTBOX_ALLOW_STALE=1 for an intentional diagnostic reuse`,
);
}
return { current, path, skipSync: testboxLeaseCanSkipSync(saved, current) };
return { current, path };
}
return { current, path, skipSync: false };
return { current, path };
}
export function recordTestboxLeaseFreshness(prepared) {
@@ -132,10 +126,6 @@ export function recordTestboxLeaseFreshness(prepared) {
}
mkdirSync(resolve(prepared.path, ".."), { recursive: true });
const temporaryPath = `${prepared.path}.tmp-${process.pid}`;
const state = {
...prepared.current,
syncedCleanHead: prepared.current.workingTreeClean ? prepared.current.headSha : "",
};
writeFileSync(temporaryPath, `${JSON.stringify(state, null, 2)}\n`);
writeFileSync(temporaryPath, `${JSON.stringify(prepared.current, null, 2)}\n`);
renameSync(temporaryPath, prepared.path);
}
+1 -22
View File
@@ -1,8 +1,5 @@
import { describe, expect, it } from "vitest";
import {
testboxLeaseCanSkipSync,
testboxLeaseStaleReasons,
} from "../../scripts/testbox-lease-freshness.mjs";
import { testboxLeaseStaleReasons } from "../../scripts/testbox-lease-freshness.mjs";
const fingerprint = {
version: 1,
@@ -21,24 +18,6 @@ describe("Testbox lease freshness", () => {
expect(testboxLeaseStaleReasons(fingerprint, { ...fingerprint })).toEqual([]);
});
it("skips sync only for the exact clean HEAD already proven on the lease", () => {
expect(
testboxLeaseCanSkipSync(
{ ...fingerprint, syncedCleanHead: fingerprint.headSha },
fingerprint,
),
).toBe(true);
expect(
testboxLeaseCanSkipSync(
{ ...fingerprint, syncedCleanHead: fingerprint.headSha },
{ ...fingerprint, workingTreeClean: false },
),
).toBe(false);
expect(
testboxLeaseCanSkipSync({ ...fingerprint, syncedCleanHead: "e".repeat(40) }, fingerprint),
).toBe(false);
});
it("rotates a lease when base, dependency, or workflow inputs drift", () => {
expect(
testboxLeaseStaleReasons(fingerprint, {