@h-rig/runtime 0.0.6-alpha.34 → 0.0.6-alpha.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/rig-agent-dispatch.js +518 -459
- package/dist/bin/rig-agent.js +430 -361
- package/dist/src/control-plane/agent-wrapper.js +523 -464
- package/dist/src/control-plane/harness-main.js +544 -463
- package/dist/src/control-plane/hooks/completion-verification.js +369 -288
- package/dist/src/control-plane/hooks/inject-context.js +158 -99
- package/dist/src/control-plane/hooks/submodule-branch.js +538 -479
- package/dist/src/control-plane/hooks/task-runtime-start.js +538 -479
- package/dist/src/control-plane/materialize-task-config.js +68 -8
- package/dist/src/control-plane/native/git-ops.js +10 -0
- package/dist/src/control-plane/native/harness-cli.js +529 -448
- package/dist/src/control-plane/native/task-ops.js +408 -327
- package/dist/src/control-plane/native/validator.js +159 -100
- package/dist/src/control-plane/native/verifier.js +243 -171
- package/dist/src/control-plane/pi-sessiond/bin.js +0 -7
- package/dist/src/control-plane/pi-sessiond/server.js +0 -7
- package/dist/src/control-plane/pi-sessiond/session-service.js +0 -3
- package/dist/src/control-plane/pi-settings-materializer.js +52 -0
- package/dist/src/control-plane/plugin-host-context.js +59 -0
- package/dist/src/control-plane/runtime/index.js +469 -410
- package/dist/src/control-plane/runtime/isolation/index.js +493 -434
- package/dist/src/control-plane/runtime/isolation.js +493 -434
- package/dist/src/control-plane/runtime/queue.js +411 -352
- package/dist/src/control-plane/tasks/source-lifecycle.js +87 -28
- package/dist/src/index.js +16 -8
- package/dist/src/local-server.js +17 -8
- package/package.json +8 -8
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// packages/runtime/src/control-plane/hooks/completion-verification.ts
|
|
5
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
6
|
-
import { resolve as
|
|
5
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync23, mkdirSync as mkdirSync14, readFileSync as readFileSync14, writeFileSync as writeFileSync14 } from "fs";
|
|
6
|
+
import { resolve as resolve25 } from "path";
|
|
7
7
|
import {
|
|
8
8
|
escapeRegExp as escapeRegExp2,
|
|
9
9
|
resolveBunCli,
|
|
@@ -1073,8 +1073,8 @@ function primeGuardHotPaths() {
|
|
|
1073
1073
|
primeGuardHotPaths();
|
|
1074
1074
|
|
|
1075
1075
|
// packages/runtime/src/control-plane/native/git-ops.ts
|
|
1076
|
-
import { existsSync as
|
|
1077
|
-
import { dirname as
|
|
1076
|
+
import { existsSync as existsSync22, lstatSync, mkdirSync as mkdirSync13, readFileSync as readFileSync13, writeFileSync as writeFileSync13 } from "fs";
|
|
1077
|
+
import { dirname as dirname11, isAbsolute as isAbsolute2, resolve as resolve24 } from "path";
|
|
1078
1078
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1079
1079
|
|
|
1080
1080
|
// packages/runtime/src/control-plane/runtime/baked-secrets.ts
|
|
@@ -1336,8 +1336,8 @@ function isAgentRuntimeContextPath(path) {
|
|
|
1336
1336
|
}
|
|
1337
1337
|
|
|
1338
1338
|
// packages/runtime/src/control-plane/native/task-ops.ts
|
|
1339
|
-
import { appendFileSync, existsSync as
|
|
1340
|
-
import { resolve as
|
|
1339
|
+
import { appendFileSync, existsSync as existsSync21, mkdirSync as mkdirSync12, readFileSync as readFileSync12, writeFileSync as writeFileSync12 } from "fs";
|
|
1340
|
+
import { resolve as resolve23 } from "path";
|
|
1341
1341
|
|
|
1342
1342
|
// packages/runtime/src/build-time-config.ts
|
|
1343
1343
|
function normalizeBuildConfig(value) {
|
|
@@ -1653,6 +1653,8 @@ import { basename as basename3, dirname as dirname6, resolve as resolve9 } from
|
|
|
1653
1653
|
var sharedNativeToolsOutputDir = resolve9(tmpdir4(), "rig-native");
|
|
1654
1654
|
var sharedNativeToolsOutputPath = resolve9(sharedNativeToolsOutputDir, `rig-tools-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
|
|
1655
1655
|
// packages/runtime/src/control-plane/plugin-host-context.ts
|
|
1656
|
+
import { existsSync as existsSync12 } from "fs";
|
|
1657
|
+
import { resolve as resolvePath } from "path";
|
|
1656
1658
|
import { createPluginHost } from "@rig/core";
|
|
1657
1659
|
import { loadConfig } from "@rig/core/load-config";
|
|
1658
1660
|
|
|
@@ -1973,6 +1975,55 @@ async function materializeSkills(projectRoot, entries) {
|
|
|
1973
1975
|
return written;
|
|
1974
1976
|
}
|
|
1975
1977
|
|
|
1978
|
+
// packages/runtime/src/control-plane/pi-settings-materializer.ts
|
|
1979
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
1980
|
+
import { dirname as dirname8, resolve as resolve12 } from "path";
|
|
1981
|
+
var SETTINGS_RELATIVE_PATH = ".pi/settings.json";
|
|
1982
|
+
var MANAGED_RECORD_RELATIVE_PATH = ".rig/state/pi-managed-packages.json";
|
|
1983
|
+
function readJson(path, fallback) {
|
|
1984
|
+
if (!existsSync11(path))
|
|
1985
|
+
return fallback;
|
|
1986
|
+
try {
|
|
1987
|
+
return JSON.parse(readFileSync8(path, "utf-8"));
|
|
1988
|
+
} catch {
|
|
1989
|
+
return fallback;
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
function packageKey(entry) {
|
|
1993
|
+
if (typeof entry === "string")
|
|
1994
|
+
return entry;
|
|
1995
|
+
if (entry && typeof entry === "object" && typeof entry.source === "string") {
|
|
1996
|
+
return entry.source;
|
|
1997
|
+
}
|
|
1998
|
+
return JSON.stringify(entry);
|
|
1999
|
+
}
|
|
2000
|
+
function materializePiPackages(projectRoot, declaredPackages) {
|
|
2001
|
+
const settingsPath = resolve12(projectRoot, SETTINGS_RELATIVE_PATH);
|
|
2002
|
+
const managedRecordPath = resolve12(projectRoot, MANAGED_RECORD_RELATIVE_PATH);
|
|
2003
|
+
const settings = readJson(settingsPath, {});
|
|
2004
|
+
const previouslyManaged = new Set(readJson(managedRecordPath, []));
|
|
2005
|
+
const existing = Array.isArray(settings.packages) ? settings.packages : [];
|
|
2006
|
+
const operatorEntries = existing.filter((entry) => !previouslyManaged.has(packageKey(entry)));
|
|
2007
|
+
const operatorKeys = new Set(operatorEntries.map(packageKey));
|
|
2008
|
+
const managedToAdd = declaredPackages.filter((pkg) => !operatorKeys.has(pkg));
|
|
2009
|
+
const nextPackages = [...operatorEntries, ...managedToAdd];
|
|
2010
|
+
if (nextPackages.length > 0 || existsSync11(settingsPath)) {
|
|
2011
|
+
const nextSettings = { ...settings };
|
|
2012
|
+
if (nextPackages.length > 0) {
|
|
2013
|
+
nextSettings.packages = nextPackages;
|
|
2014
|
+
} else {
|
|
2015
|
+
delete nextSettings.packages;
|
|
2016
|
+
}
|
|
2017
|
+
mkdirSync6(dirname8(settingsPath), { recursive: true });
|
|
2018
|
+
writeFileSync5(settingsPath, `${JSON.stringify(nextSettings, null, 2)}
|
|
2019
|
+
`, "utf-8");
|
|
2020
|
+
}
|
|
2021
|
+
mkdirSync6(dirname8(managedRecordPath), { recursive: true });
|
|
2022
|
+
writeFileSync5(managedRecordPath, `${JSON.stringify(managedToAdd, null, 2)}
|
|
2023
|
+
`, "utf-8");
|
|
2024
|
+
return { settingsPath, packages: managedToAdd };
|
|
2025
|
+
}
|
|
2026
|
+
|
|
1976
2027
|
// packages/runtime/src/control-plane/plugin-host-context.ts
|
|
1977
2028
|
async function buildPluginHostContext(projectRoot) {
|
|
1978
2029
|
let config;
|
|
@@ -2020,6 +2071,14 @@ async function buildPluginHostContext(projectRoot) {
|
|
|
2020
2071
|
} catch (err) {
|
|
2021
2072
|
console.warn(`[plugin-host] skill materialization failed: ${err instanceof Error ? err.message : err}`);
|
|
2022
2073
|
}
|
|
2074
|
+
try {
|
|
2075
|
+
const piPackages = config.runtime?.pi?.packages ?? [];
|
|
2076
|
+
if (piPackages.length > 0 || existsSync12(resolvePath(projectRoot, ".rig/state/pi-managed-packages.json"))) {
|
|
2077
|
+
materializePiPackages(projectRoot, piPackages);
|
|
2078
|
+
}
|
|
2079
|
+
} catch (err) {
|
|
2080
|
+
console.warn(`[plugin-host] Pi package materialization failed: ${err instanceof Error ? err.message : err}`);
|
|
2081
|
+
}
|
|
2023
2082
|
return {
|
|
2024
2083
|
config,
|
|
2025
2084
|
pluginHost,
|
|
@@ -2033,12 +2092,12 @@ async function buildPluginHostContext(projectRoot) {
|
|
|
2033
2092
|
|
|
2034
2093
|
// packages/runtime/src/control-plane/tasks/source-aware-task-config-source.ts
|
|
2035
2094
|
import { spawnSync } from "child_process";
|
|
2036
|
-
import { existsSync as
|
|
2037
|
-
import { basename as basename4, join as join2, resolve as
|
|
2095
|
+
import { existsSync as existsSync14, readFileSync as readFileSync10, readdirSync as readdirSync2, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
2096
|
+
import { basename as basename4, join as join2, resolve as resolve14 } from "path";
|
|
2038
2097
|
|
|
2039
2098
|
// packages/runtime/src/control-plane/tasks/legacy-task-config-source.ts
|
|
2040
|
-
import { existsSync as
|
|
2041
|
-
import { resolve as
|
|
2099
|
+
import { existsSync as existsSync13, readFileSync as readFileSync9 } from "fs";
|
|
2100
|
+
import { resolve as resolve13 } from "path";
|
|
2042
2101
|
|
|
2043
2102
|
// packages/runtime/src/control-plane/tasks/task-record-reader.ts
|
|
2044
2103
|
async function findTaskById(reader, id) {
|
|
@@ -2061,7 +2120,7 @@ class LegacyTaskConfigReadError extends Error {
|
|
|
2061
2120
|
}
|
|
2062
2121
|
}
|
|
2063
2122
|
function createLegacyTaskConfigRecordReader(projectRoot, options = {}) {
|
|
2064
|
-
const configPath = options.configPath ??
|
|
2123
|
+
const configPath = options.configPath ?? resolve13(projectRoot, ".rig", "task-config.json");
|
|
2065
2124
|
const reader = {
|
|
2066
2125
|
async listTasks() {
|
|
2067
2126
|
return readLegacyTaskRecords(projectRoot, configPath);
|
|
@@ -2072,8 +2131,8 @@ function createLegacyTaskConfigRecordReader(projectRoot, options = {}) {
|
|
|
2072
2131
|
};
|
|
2073
2132
|
return reader;
|
|
2074
2133
|
}
|
|
2075
|
-
function readLegacyTaskRecords(projectRoot, configPath =
|
|
2076
|
-
if (!
|
|
2134
|
+
function readLegacyTaskRecords(projectRoot, configPath = resolve13(projectRoot, ".rig", "task-config.json")) {
|
|
2135
|
+
if (!existsSync13(configPath)) {
|
|
2077
2136
|
return [];
|
|
2078
2137
|
}
|
|
2079
2138
|
const rawConfig = readLegacyTaskConfigJson(projectRoot, configPath);
|
|
@@ -2081,7 +2140,7 @@ function readLegacyTaskRecords(projectRoot, configPath = resolve12(projectRoot,
|
|
|
2081
2140
|
}
|
|
2082
2141
|
function readLegacyTaskConfigJson(projectRoot, configPath) {
|
|
2083
2142
|
try {
|
|
2084
|
-
const parsed = JSON.parse(
|
|
2143
|
+
const parsed = JSON.parse(readFileSync9(configPath, "utf8"));
|
|
2085
2144
|
if (isPlainRecord(parsed)) {
|
|
2086
2145
|
return parsed;
|
|
2087
2146
|
}
|
|
@@ -2165,7 +2224,7 @@ function isPlainRecord(candidate) {
|
|
|
2165
2224
|
var STATUS_LABELS = new Set(["ready", "blocked", "in-progress", "under-review", "failed", "cancelled"]);
|
|
2166
2225
|
var FILE_TASK_PATTERN = /\.(task\.)?json$/;
|
|
2167
2226
|
function createSourceAwareTaskConfigRecordReader(projectRoot, options = {}) {
|
|
2168
|
-
const configPath = options.configPath ??
|
|
2227
|
+
const configPath = options.configPath ?? resolve14(projectRoot, ".rig", "task-config.json");
|
|
2169
2228
|
const legacy = createLegacyTaskConfigRecordReader(projectRoot, { configPath });
|
|
2170
2229
|
const spawnFn = options.spawn ?? spawnSync;
|
|
2171
2230
|
const ghBinary = options.ghBinary ?? "gh";
|
|
@@ -2231,7 +2290,7 @@ async function readSourceAwareTaskStatus(projectRoot, taskId, options = {}) {
|
|
|
2231
2290
|
}
|
|
2232
2291
|
}
|
|
2233
2292
|
function updateSourceAwareTaskConfigTask(projectRoot, taskId, update, options = {}) {
|
|
2234
|
-
const configPath = options.configPath ??
|
|
2293
|
+
const configPath = options.configPath ?? resolve14(projectRoot, ".rig", "task-config.json");
|
|
2235
2294
|
const rawEntry = readRawTaskEntry(configPath, taskId);
|
|
2236
2295
|
if (!rawEntry) {
|
|
2237
2296
|
const configuredFilesPath = readConfiguredFilesTaskSourcePath(projectRoot);
|
|
@@ -2284,10 +2343,10 @@ function readMaterializedTaskMetadata(entry) {
|
|
|
2284
2343
|
return metadata;
|
|
2285
2344
|
}
|
|
2286
2345
|
function readConfiguredFilesTaskSourcePath(projectRoot) {
|
|
2287
|
-
const jsonPath =
|
|
2288
|
-
if (
|
|
2346
|
+
const jsonPath = resolve14(projectRoot, "rig.config.json");
|
|
2347
|
+
if (existsSync14(jsonPath)) {
|
|
2289
2348
|
try {
|
|
2290
|
-
const parsed = JSON.parse(
|
|
2349
|
+
const parsed = JSON.parse(readFileSync10(jsonPath, "utf8"));
|
|
2291
2350
|
if (isPlainRecord2(parsed) && isPlainRecord2(parsed.taskSource)) {
|
|
2292
2351
|
const source = parsed.taskSource;
|
|
2293
2352
|
return source.kind === "files" && typeof source.path === "string" ? source.path : null;
|
|
@@ -2296,12 +2355,12 @@ function readConfiguredFilesTaskSourcePath(projectRoot) {
|
|
|
2296
2355
|
return null;
|
|
2297
2356
|
}
|
|
2298
2357
|
}
|
|
2299
|
-
const tsPath =
|
|
2300
|
-
if (!
|
|
2358
|
+
const tsPath = resolve14(projectRoot, "rig.config.ts");
|
|
2359
|
+
if (!existsSync14(tsPath)) {
|
|
2301
2360
|
return null;
|
|
2302
2361
|
}
|
|
2303
2362
|
try {
|
|
2304
|
-
const source =
|
|
2363
|
+
const source = readFileSync10(tsPath, "utf8");
|
|
2305
2364
|
const taskSourceBlock = source.match(/taskSource\s*:\s*\{[\s\S]*?\}/m)?.[0] ?? "";
|
|
2306
2365
|
const kind = taskSourceBlock.match(/kind\s*:\s*["']([^"']+)["']/)?.[1];
|
|
2307
2366
|
if (kind !== "files") {
|
|
@@ -2321,10 +2380,10 @@ function readRawTaskEntry(configPath, taskId) {
|
|
|
2321
2380
|
return isPlainRecord2(entry) ? entry : null;
|
|
2322
2381
|
}
|
|
2323
2382
|
function readRawTaskConfig(configPath) {
|
|
2324
|
-
if (!
|
|
2383
|
+
if (!existsSync14(configPath)) {
|
|
2325
2384
|
return null;
|
|
2326
2385
|
}
|
|
2327
|
-
const parsed = JSON.parse(
|
|
2386
|
+
const parsed = JSON.parse(readFileSync10(configPath, "utf8"));
|
|
2328
2387
|
return isPlainRecord2(parsed) ? parsed : null;
|
|
2329
2388
|
}
|
|
2330
2389
|
function stripLegacyTaskConfigMetadata2(raw) {
|
|
@@ -2341,16 +2400,16 @@ function writeLegacyTaskStatus(configPath, taskId, status) {
|
|
|
2341
2400
|
return;
|
|
2342
2401
|
}
|
|
2343
2402
|
entry.status = status;
|
|
2344
|
-
|
|
2403
|
+
writeFileSync6(configPath, `${JSON.stringify(rawConfig, null, 2)}
|
|
2345
2404
|
`, "utf8");
|
|
2346
2405
|
}
|
|
2347
2406
|
function updateFileBackedTask(projectRoot, sourcePath, taskId, update) {
|
|
2348
|
-
const directory =
|
|
2407
|
+
const directory = resolve14(projectRoot, sourcePath);
|
|
2349
2408
|
const file = findFileBackedTaskFile(directory, taskId);
|
|
2350
2409
|
if (!file) {
|
|
2351
2410
|
return false;
|
|
2352
2411
|
}
|
|
2353
|
-
const raw = JSON.parse(
|
|
2412
|
+
const raw = JSON.parse(readFileSync10(file, "utf8"));
|
|
2354
2413
|
if (!isPlainRecord2(raw)) {
|
|
2355
2414
|
return false;
|
|
2356
2415
|
}
|
|
@@ -2367,13 +2426,13 @@ function updateFileBackedTask(projectRoot, sourcePath, taskId, update) {
|
|
|
2367
2426
|
{ body: update.comment, createdAt: new Date().toISOString(), source: "rig" }
|
|
2368
2427
|
];
|
|
2369
2428
|
}
|
|
2370
|
-
|
|
2429
|
+
writeFileSync6(file, `${JSON.stringify(raw, null, 2)}
|
|
2371
2430
|
`, "utf8");
|
|
2372
2431
|
return true;
|
|
2373
2432
|
}
|
|
2374
2433
|
function listFileBackedTasks(projectRoot, sourcePath) {
|
|
2375
|
-
const directory =
|
|
2376
|
-
if (!
|
|
2434
|
+
const directory = resolve14(projectRoot, sourcePath);
|
|
2435
|
+
if (!existsSync14(directory)) {
|
|
2377
2436
|
return [];
|
|
2378
2437
|
}
|
|
2379
2438
|
const tasks = [];
|
|
@@ -2388,11 +2447,11 @@ function listFileBackedTasks(projectRoot, sourcePath) {
|
|
|
2388
2447
|
return tasks;
|
|
2389
2448
|
}
|
|
2390
2449
|
function readFileBackedTask(projectRoot, sourcePath, taskId, rawEntry) {
|
|
2391
|
-
const file = findFileBackedTaskFile(
|
|
2450
|
+
const file = findFileBackedTaskFile(resolve14(projectRoot, sourcePath), taskId);
|
|
2392
2451
|
if (!file) {
|
|
2393
2452
|
return null;
|
|
2394
2453
|
}
|
|
2395
|
-
const raw = JSON.parse(
|
|
2454
|
+
const raw = JSON.parse(readFileSync10(file, "utf8"));
|
|
2396
2455
|
if (!isPlainRecord2(raw)) {
|
|
2397
2456
|
return null;
|
|
2398
2457
|
}
|
|
@@ -2405,7 +2464,7 @@ function readFileBackedTask(projectRoot, sourcePath, taskId, rawEntry) {
|
|
|
2405
2464
|
};
|
|
2406
2465
|
}
|
|
2407
2466
|
function findFileBackedTaskFile(directory, taskId) {
|
|
2408
|
-
if (!
|
|
2467
|
+
if (!existsSync14(directory)) {
|
|
2409
2468
|
return null;
|
|
2410
2469
|
}
|
|
2411
2470
|
for (const name of readdirSync2(directory)) {
|
|
@@ -2415,7 +2474,7 @@ function findFileBackedTaskFile(directory, taskId) {
|
|
|
2415
2474
|
try {
|
|
2416
2475
|
if (!statSync3(file).isFile())
|
|
2417
2476
|
continue;
|
|
2418
|
-
const raw = JSON.parse(
|
|
2477
|
+
const raw = JSON.parse(readFileSync10(file, "utf8"));
|
|
2419
2478
|
const inferredId = basename4(file).replace(FILE_TASK_PATTERN, "");
|
|
2420
2479
|
const id = isPlainRecord2(raw) && typeof raw.id === "string" ? raw.id : inferredId;
|
|
2421
2480
|
if (id === taskId) {
|
|
@@ -2764,8 +2823,8 @@ function buildTaskRunLifecycleComment(input) {
|
|
|
2764
2823
|
}
|
|
2765
2824
|
|
|
2766
2825
|
// packages/runtime/src/control-plane/native/task-state.ts
|
|
2767
|
-
import { existsSync as
|
|
2768
|
-
import { basename as basename5, resolve as
|
|
2826
|
+
import { existsSync as existsSync15, readFileSync as readFileSync11, readdirSync as readdirSync3, statSync as statSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
2827
|
+
import { basename as basename5, resolve as resolve15 } from "path";
|
|
2769
2828
|
|
|
2770
2829
|
// packages/runtime/src/control-plane/state-sync/types.ts
|
|
2771
2830
|
var CANONICAL_TASK_LIFECYCLE_STATUSES = new Set([
|
|
@@ -2795,7 +2854,7 @@ function readValidationDescriptions(projectRoot) {
|
|
|
2795
2854
|
return readValidationDescriptionMap(raw);
|
|
2796
2855
|
}
|
|
2797
2856
|
function readSourceValidationDescriptions(projectRoot) {
|
|
2798
|
-
const rootRaw = readJsonFile(
|
|
2857
|
+
const rootRaw = readJsonFile(resolve15(projectRoot, "rig", "task-config.json"), {});
|
|
2799
2858
|
const sourcePath = findSourceTaskConfigPath(projectRoot);
|
|
2800
2859
|
const sourceRaw = sourcePath ? readJsonFile(sourcePath, {}) : {};
|
|
2801
2860
|
const rootDescriptions = readValidationDescriptionMap(rootRaw);
|
|
@@ -2886,25 +2945,25 @@ function lookupTask(projectRoot, input) {
|
|
|
2886
2945
|
function artifactDirForId(projectRoot, id) {
|
|
2887
2946
|
const workspaceDir = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
2888
2947
|
if (workspaceDir) {
|
|
2889
|
-
const worktreeArtifacts =
|
|
2890
|
-
if (
|
|
2948
|
+
const worktreeArtifacts = resolve15(workspaceDir, "artifacts", id);
|
|
2949
|
+
if (existsSync15(worktreeArtifacts) || existsSync15(resolve15(workspaceDir, "artifacts"))) {
|
|
2891
2950
|
return worktreeArtifacts;
|
|
2892
2951
|
}
|
|
2893
2952
|
}
|
|
2894
2953
|
try {
|
|
2895
2954
|
const paths = resolveHarnessPaths(projectRoot);
|
|
2896
|
-
return
|
|
2955
|
+
return resolve15(paths.artifactsDir, id);
|
|
2897
2956
|
} catch {
|
|
2898
|
-
return
|
|
2957
|
+
return resolve15(resolveMonorepoRoot2(projectRoot), "artifacts", id);
|
|
2899
2958
|
}
|
|
2900
2959
|
}
|
|
2901
2960
|
function resolveTaskConfigPath(projectRoot) {
|
|
2902
2961
|
const paths = resolveHarnessPaths(projectRoot);
|
|
2903
|
-
if (
|
|
2962
|
+
if (existsSync15(paths.taskConfigPath)) {
|
|
2904
2963
|
return paths.taskConfigPath;
|
|
2905
2964
|
}
|
|
2906
2965
|
for (const candidate of sourceTaskConfigCandidates(projectRoot)) {
|
|
2907
|
-
if (
|
|
2966
|
+
if (existsSync15(candidate)) {
|
|
2908
2967
|
return candidate;
|
|
2909
2968
|
}
|
|
2910
2969
|
}
|
|
@@ -2912,7 +2971,7 @@ function resolveTaskConfigPath(projectRoot) {
|
|
|
2912
2971
|
}
|
|
2913
2972
|
function findSourceTaskConfigPath(projectRoot) {
|
|
2914
2973
|
for (const candidate of sourceTaskConfigCandidates(projectRoot)) {
|
|
2915
|
-
if (
|
|
2974
|
+
if (existsSync15(candidate)) {
|
|
2916
2975
|
return candidate;
|
|
2917
2976
|
}
|
|
2918
2977
|
}
|
|
@@ -2925,7 +2984,7 @@ function readAndSyncSourceTaskConfig(projectRoot) {
|
|
|
2925
2984
|
const synced = synchronizeTaskConfigWithTracker(projectRoot, raw);
|
|
2926
2985
|
if (sourcePath && synced.updated) {
|
|
2927
2986
|
try {
|
|
2928
|
-
|
|
2987
|
+
writeFileSync7(sourcePath, `${JSON.stringify(synced.config, null, 2)}
|
|
2929
2988
|
`, "utf-8");
|
|
2930
2989
|
} catch {}
|
|
2931
2990
|
}
|
|
@@ -2977,12 +3036,12 @@ function shouldRefreshAutoSyncedTaskConfigEntry(entry) {
|
|
|
2977
3036
|
return !candidate.role;
|
|
2978
3037
|
}
|
|
2979
3038
|
function readSourceIssueRecords(projectRoot) {
|
|
2980
|
-
const issuesPath =
|
|
2981
|
-
if (!
|
|
3039
|
+
const issuesPath = resolve15(resolveMonorepoRoot2(projectRoot), ".beads", "issues.jsonl");
|
|
3040
|
+
if (!existsSync15(issuesPath)) {
|
|
2982
3041
|
return [];
|
|
2983
3042
|
}
|
|
2984
3043
|
const records = [];
|
|
2985
|
-
for (const line of
|
|
3044
|
+
for (const line of readFileSync11(issuesPath, "utf-8").split(/\r?\n/)) {
|
|
2986
3045
|
const trimmed = line.trim();
|
|
2987
3046
|
if (!trimmed) {
|
|
2988
3047
|
continue;
|
|
@@ -3038,19 +3097,19 @@ function readConfiguredFileTaskConfig(projectRoot) {
|
|
|
3038
3097
|
if (!sourcePath) {
|
|
3039
3098
|
return {};
|
|
3040
3099
|
}
|
|
3041
|
-
const directory =
|
|
3042
|
-
if (!
|
|
3100
|
+
const directory = resolve15(projectRoot, sourcePath);
|
|
3101
|
+
if (!existsSync15(directory)) {
|
|
3043
3102
|
return {};
|
|
3044
3103
|
}
|
|
3045
3104
|
const config = {};
|
|
3046
3105
|
for (const name of readdirSync3(directory)) {
|
|
3047
3106
|
if (!FILE_TASK_PATTERN2.test(name))
|
|
3048
3107
|
continue;
|
|
3049
|
-
const file =
|
|
3108
|
+
const file = resolve15(directory, name);
|
|
3050
3109
|
try {
|
|
3051
3110
|
if (!statSync4(file).isFile())
|
|
3052
3111
|
continue;
|
|
3053
|
-
const raw = JSON.parse(
|
|
3112
|
+
const raw = JSON.parse(readFileSync11(file, "utf8"));
|
|
3054
3113
|
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
3055
3114
|
continue;
|
|
3056
3115
|
const record = raw;
|
|
@@ -3092,10 +3151,10 @@ function firstStringList2(...candidates) {
|
|
|
3092
3151
|
return [];
|
|
3093
3152
|
}
|
|
3094
3153
|
function readConfiguredFilesTaskSourcePath2(projectRoot) {
|
|
3095
|
-
const jsonPath =
|
|
3096
|
-
if (
|
|
3154
|
+
const jsonPath = resolve15(projectRoot, "rig.config.json");
|
|
3155
|
+
if (existsSync15(jsonPath)) {
|
|
3097
3156
|
try {
|
|
3098
|
-
const parsed = JSON.parse(
|
|
3157
|
+
const parsed = JSON.parse(readFileSync11(jsonPath, "utf8"));
|
|
3099
3158
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
3100
3159
|
const taskSource = parsed.taskSource;
|
|
3101
3160
|
if (taskSource && typeof taskSource === "object" && !Array.isArray(taskSource)) {
|
|
@@ -3107,12 +3166,12 @@ function readConfiguredFilesTaskSourcePath2(projectRoot) {
|
|
|
3107
3166
|
return null;
|
|
3108
3167
|
}
|
|
3109
3168
|
}
|
|
3110
|
-
const tsPath =
|
|
3111
|
-
if (!
|
|
3169
|
+
const tsPath = resolve15(projectRoot, "rig.config.ts");
|
|
3170
|
+
if (!existsSync15(tsPath)) {
|
|
3112
3171
|
return null;
|
|
3113
3172
|
}
|
|
3114
3173
|
try {
|
|
3115
|
-
const source =
|
|
3174
|
+
const source = readFileSync11(tsPath, "utf8");
|
|
3116
3175
|
const taskSourceBlock = source.match(/taskSource\s*:\s*\{[\s\S]*?\}/m)?.[0] ?? "";
|
|
3117
3176
|
const kind = taskSourceBlock.match(/kind\s*:\s*["']([^"']+)["']/)?.[1];
|
|
3118
3177
|
if (kind !== "files") {
|
|
@@ -3126,23 +3185,23 @@ function readConfiguredFilesTaskSourcePath2(projectRoot) {
|
|
|
3126
3185
|
function sourceTaskConfigCandidates(projectRoot) {
|
|
3127
3186
|
const runtimeContext = loadRuntimeContextFromEnv();
|
|
3128
3187
|
return [
|
|
3129
|
-
runtimeContext?.monorepoMainRoot ?
|
|
3130
|
-
process.env.MONOREPO_MAIN_ROOT?.trim() ?
|
|
3131
|
-
|
|
3188
|
+
runtimeContext?.monorepoMainRoot ? resolve15(runtimeContext.monorepoMainRoot, ".rig", "task-config.json") : "",
|
|
3189
|
+
process.env.MONOREPO_MAIN_ROOT?.trim() ? resolve15(process.env.MONOREPO_MAIN_ROOT.trim(), ".rig", "task-config.json") : "",
|
|
3190
|
+
resolve15(resolveMonorepoRoot2(projectRoot), ".rig", "task-config.json")
|
|
3132
3191
|
].filter(Boolean);
|
|
3133
3192
|
}
|
|
3134
3193
|
|
|
3135
3194
|
// packages/runtime/src/control-plane/native/validator.ts
|
|
3136
|
-
import { existsSync as
|
|
3137
|
-
import { resolve as
|
|
3195
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
3196
|
+
import { resolve as resolve20 } from "path";
|
|
3138
3197
|
|
|
3139
3198
|
// packages/runtime/src/control-plane/native/validator-binaries.ts
|
|
3140
|
-
import { existsSync as
|
|
3141
|
-
import { dirname as
|
|
3199
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync8, rmSync as rmSync5, statSync as statSync5 } from "fs";
|
|
3200
|
+
import { dirname as dirname10, resolve as resolve19 } from "path";
|
|
3142
3201
|
|
|
3143
3202
|
// packages/runtime/src/binary-run.ts
|
|
3144
|
-
import { chmodSync as chmodSync2, cpSync, existsSync as
|
|
3145
|
-
import { basename as basename6, dirname as
|
|
3203
|
+
import { chmodSync as chmodSync2, cpSync, existsSync as existsSync16, mkdirSync as mkdirSync7, renameSync as renameSync3, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "fs";
|
|
3204
|
+
import { basename as basename6, dirname as dirname9, resolve as resolve16 } from "path";
|
|
3146
3205
|
import { fileURLToPath } from "url";
|
|
3147
3206
|
import { drainMicrotasks, gcAndSweep } from "bun:jsc";
|
|
3148
3207
|
var runtimeBinaryBuildQueue = Promise.resolve();
|
|
@@ -3168,9 +3227,9 @@ async function buildRuntimeBinary(options) {
|
|
|
3168
3227
|
});
|
|
3169
3228
|
}
|
|
3170
3229
|
async function buildRuntimeBinaryInProcess(options, manifest) {
|
|
3171
|
-
const tempBuildDir =
|
|
3172
|
-
const tempOutputPath =
|
|
3173
|
-
|
|
3230
|
+
const tempBuildDir = resolve16(dirname9(options.outputPath), `.bun-build-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
3231
|
+
const tempOutputPath = resolve16(tempBuildDir, basename6(options.outputPath));
|
|
3232
|
+
mkdirSync7(tempBuildDir, { recursive: true });
|
|
3174
3233
|
await withTemporaryEnv({
|
|
3175
3234
|
...options.env,
|
|
3176
3235
|
...options.define ? { RIG_BUILD_CONFIG_JSON: JSON.stringify(options.define) } : {}
|
|
@@ -3195,7 +3254,7 @@ async function buildRuntimeBinaryInProcess(options, manifest) {
|
|
|
3195
3254
|
`);
|
|
3196
3255
|
throw new Error(`Failed to build ${options.entrypoint}: ${details || "Bun.build() returned errors"}`);
|
|
3197
3256
|
}
|
|
3198
|
-
if (!
|
|
3257
|
+
if (!existsSync16(tempOutputPath)) {
|
|
3199
3258
|
const emitted = buildResult.outputs.map((output) => output.path).join(", ") || "(none)";
|
|
3200
3259
|
throw new Error(`Failed to build ${options.entrypoint}: Bun.build() did not emit ${tempOutputPath}. Emitted: ${emitted}`);
|
|
3201
3260
|
}
|
|
@@ -3227,8 +3286,8 @@ function runtimeBinaryCacheManifestPath(outputPath) {
|
|
|
3227
3286
|
function resolveRuntimeBinaryBuildOptions(options) {
|
|
3228
3287
|
return {
|
|
3229
3288
|
...options,
|
|
3230
|
-
entrypoint:
|
|
3231
|
-
outputPath:
|
|
3289
|
+
entrypoint: resolve16(options.cwd, options.sourcePath),
|
|
3290
|
+
outputPath: resolve16(options.outputPath)
|
|
3232
3291
|
};
|
|
3233
3292
|
}
|
|
3234
3293
|
function shouldUseRuntimeBinaryBuildWorker() {
|
|
@@ -3242,7 +3301,7 @@ function shouldUseRuntimeBinaryBuildWorker() {
|
|
|
3242
3301
|
}
|
|
3243
3302
|
async function buildRuntimeBinaryViaWorker(options) {
|
|
3244
3303
|
const workerSourcePath = resolveRuntimeBinaryBuildWorkerSourcePath(options);
|
|
3245
|
-
if (!workerSourcePath || !
|
|
3304
|
+
if (!workerSourcePath || !existsSync16(workerSourcePath)) {
|
|
3246
3305
|
await buildRuntimeBinaryInProcess(options, {
|
|
3247
3306
|
manifestPath: runtimeBinaryCacheManifestPath(options.outputPath),
|
|
3248
3307
|
buildKey: createRuntimeBinaryBuildKey({
|
|
@@ -3279,7 +3338,7 @@ async function buildRuntimeBinaryViaWorker(options) {
|
|
|
3279
3338
|
}
|
|
3280
3339
|
}
|
|
3281
3340
|
function createRuntimeBinaryBuildWorkerPayloadPath(outputPath) {
|
|
3282
|
-
return
|
|
3341
|
+
return resolve16(dirname9(outputPath), `.bun-build-worker-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);
|
|
3283
3342
|
}
|
|
3284
3343
|
function resolveRuntimeBinaryBuildWorkerSourcePath(options) {
|
|
3285
3344
|
const envRoots = [
|
|
@@ -3288,13 +3347,13 @@ function resolveRuntimeBinaryBuildWorkerSourcePath(options) {
|
|
|
3288
3347
|
process.env.PROJECT_RIG_ROOT?.trim()
|
|
3289
3348
|
].filter(Boolean);
|
|
3290
3349
|
for (const root of envRoots) {
|
|
3291
|
-
const candidate =
|
|
3292
|
-
if (
|
|
3350
|
+
const candidate = resolve16(root, "packages/runtime/src/binary-build-worker.ts");
|
|
3351
|
+
if (existsSync16(candidate)) {
|
|
3293
3352
|
return candidate;
|
|
3294
3353
|
}
|
|
3295
3354
|
}
|
|
3296
|
-
const localCandidate =
|
|
3297
|
-
return
|
|
3355
|
+
const localCandidate = resolve16(import.meta.dir, "binary-build-worker.ts");
|
|
3356
|
+
return existsSync16(localCandidate) ? localCandidate : null;
|
|
3298
3357
|
}
|
|
3299
3358
|
function resolveRuntimeBinaryBuildWorkerInvocation() {
|
|
3300
3359
|
const bunPath = Bun.which("bun");
|
|
@@ -3330,7 +3389,7 @@ function createRuntimeBinaryBuildKey(input) {
|
|
|
3330
3389
|
});
|
|
3331
3390
|
}
|
|
3332
3391
|
async function isRuntimeBinaryBuildFresh(input) {
|
|
3333
|
-
if (!
|
|
3392
|
+
if (!existsSync16(input.outputPath) || !existsSync16(input.manifestPath)) {
|
|
3334
3393
|
return false;
|
|
3335
3394
|
}
|
|
3336
3395
|
let manifest = null;
|
|
@@ -3343,7 +3402,7 @@ async function isRuntimeBinaryBuildFresh(input) {
|
|
|
3343
3402
|
return false;
|
|
3344
3403
|
}
|
|
3345
3404
|
for (const [filePath, expectedDigest] of Object.entries(manifest.inputs || {})) {
|
|
3346
|
-
if (!
|
|
3405
|
+
if (!existsSync16(filePath)) {
|
|
3347
3406
|
return false;
|
|
3348
3407
|
}
|
|
3349
3408
|
if (await sha256File(filePath) !== expectedDigest) {
|
|
@@ -3356,7 +3415,7 @@ async function writeRuntimeBinaryCacheManifest(input) {
|
|
|
3356
3415
|
const inputs = {};
|
|
3357
3416
|
for (const inputPath of Object.keys(input.metafile?.inputs || {}).sort()) {
|
|
3358
3417
|
const normalized = normalizeBuildInputPath(input.cwd, inputPath);
|
|
3359
|
-
if (!normalized || !
|
|
3418
|
+
if (!normalized || !existsSync16(normalized)) {
|
|
3360
3419
|
continue;
|
|
3361
3420
|
}
|
|
3362
3421
|
inputs[normalized] = await sha256File(normalized);
|
|
@@ -3379,7 +3438,7 @@ function normalizeBuildInputPath(cwd, inputPath) {
|
|
|
3379
3438
|
if (inputPath.startsWith("<")) {
|
|
3380
3439
|
return null;
|
|
3381
3440
|
}
|
|
3382
|
-
return
|
|
3441
|
+
return resolve16(cwd, inputPath);
|
|
3383
3442
|
}
|
|
3384
3443
|
async function sha256File(path) {
|
|
3385
3444
|
const hasher = new Bun.CryptoHasher("sha256");
|
|
@@ -3395,8 +3454,8 @@ function sortRecord(value) {
|
|
|
3395
3454
|
async function runSerializedRuntimeBinaryBuild(action) {
|
|
3396
3455
|
const previous = runtimeBinaryBuildQueue;
|
|
3397
3456
|
let release;
|
|
3398
|
-
runtimeBinaryBuildQueue = new Promise((
|
|
3399
|
-
release =
|
|
3457
|
+
runtimeBinaryBuildQueue = new Promise((resolve17) => {
|
|
3458
|
+
release = resolve17;
|
|
3400
3459
|
});
|
|
3401
3460
|
await previous;
|
|
3402
3461
|
try {
|
|
@@ -3441,11 +3500,11 @@ async function withTemporaryCwd(cwd, action) {
|
|
|
3441
3500
|
}
|
|
3442
3501
|
|
|
3443
3502
|
// packages/runtime/src/control-plane/runtime/provisioning-env.ts
|
|
3444
|
-
import { delimiter, resolve as
|
|
3503
|
+
import { delimiter, resolve as resolve18 } from "path";
|
|
3445
3504
|
|
|
3446
3505
|
// packages/runtime/src/control-plane/runtime/runtime-paths.ts
|
|
3447
|
-
import { existsSync as
|
|
3448
|
-
import { resolve as
|
|
3506
|
+
import { existsSync as existsSync17, readdirSync as readdirSync4, realpathSync } from "fs";
|
|
3507
|
+
import { resolve as resolve17 } from "path";
|
|
3449
3508
|
|
|
3450
3509
|
// packages/runtime/src/control-plane/runtime/sandbox/utils.ts
|
|
3451
3510
|
function uniq(values) {
|
|
@@ -3463,7 +3522,7 @@ function resolveBunBinaryPath() {
|
|
|
3463
3522
|
}
|
|
3464
3523
|
const home = process.env.HOME?.trim();
|
|
3465
3524
|
const fallbackCandidates = [
|
|
3466
|
-
home ?
|
|
3525
|
+
home ? resolve17(home, ".bun/bin/bun") : "",
|
|
3467
3526
|
"/opt/homebrew/bin/bun",
|
|
3468
3527
|
"/usr/local/bin/bun",
|
|
3469
3528
|
"/usr/bin/bun"
|
|
@@ -3491,8 +3550,8 @@ function resolveClaudeBinaryPath() {
|
|
|
3491
3550
|
}
|
|
3492
3551
|
const home = process.env.HOME?.trim();
|
|
3493
3552
|
const fallbackCandidates = [
|
|
3494
|
-
home ?
|
|
3495
|
-
home ?
|
|
3553
|
+
home ? resolve17(home, ".local/bin/claude") : "",
|
|
3554
|
+
home ? resolve17(home, ".local/share/claude/local/claude") : "",
|
|
3496
3555
|
"/opt/homebrew/bin/claude",
|
|
3497
3556
|
"/usr/local/bin/claude",
|
|
3498
3557
|
"/usr/bin/claude"
|
|
@@ -3506,51 +3565,51 @@ function resolveClaudeBinaryPath() {
|
|
|
3506
3565
|
throw new Error("claude not found in PATH");
|
|
3507
3566
|
}
|
|
3508
3567
|
function resolveBunInstallDir(bunBinaryPath = resolveBunBinaryPath()) {
|
|
3509
|
-
return
|
|
3568
|
+
return resolve17(bunBinaryPath, "../..");
|
|
3510
3569
|
}
|
|
3511
3570
|
function resolveClaudeInstallDir() {
|
|
3512
3571
|
const realPath = resolveClaudeBinaryPath();
|
|
3513
|
-
return
|
|
3572
|
+
return resolve17(realPath, "..");
|
|
3514
3573
|
}
|
|
3515
3574
|
function resolveNodeInstallDir() {
|
|
3516
3575
|
const preferredNode = resolvePreferredNodeBinary();
|
|
3517
3576
|
if (!preferredNode)
|
|
3518
3577
|
return null;
|
|
3519
3578
|
const explicitNode = process.env.RIG_NODE_BIN?.trim();
|
|
3520
|
-
if (explicitNode &&
|
|
3521
|
-
return preferredNode.endsWith("/bin/node") ?
|
|
3579
|
+
if (explicitNode && resolve17(explicitNode) === resolve17(preferredNode)) {
|
|
3580
|
+
return preferredNode.endsWith("/bin/node") ? resolve17(preferredNode, "../..") : resolve17(preferredNode, "..");
|
|
3522
3581
|
}
|
|
3523
3582
|
try {
|
|
3524
3583
|
const realPath = realpathSync(preferredNode);
|
|
3525
3584
|
if (realPath.endsWith("/bin/node")) {
|
|
3526
|
-
return
|
|
3585
|
+
return resolve17(realPath, "../..");
|
|
3527
3586
|
}
|
|
3528
|
-
return
|
|
3587
|
+
return resolve17(realPath, "..");
|
|
3529
3588
|
} catch {
|
|
3530
|
-
return
|
|
3589
|
+
return resolve17(preferredNode, "..");
|
|
3531
3590
|
}
|
|
3532
3591
|
}
|
|
3533
3592
|
function resolvePreferredNodeBinary() {
|
|
3534
3593
|
const candidates = [];
|
|
3535
3594
|
const envNode = process.env.RIG_NODE_BIN?.trim();
|
|
3536
3595
|
if (envNode) {
|
|
3537
|
-
const explicit =
|
|
3538
|
-
if (
|
|
3596
|
+
const explicit = resolve17(envNode);
|
|
3597
|
+
if (existsSync17(explicit)) {
|
|
3539
3598
|
return explicit;
|
|
3540
3599
|
}
|
|
3541
3600
|
}
|
|
3542
3601
|
const nvmBin = process.env.NVM_BIN?.trim();
|
|
3543
3602
|
if (nvmBin) {
|
|
3544
|
-
candidates.push(
|
|
3603
|
+
candidates.push(resolve17(nvmBin, "node"));
|
|
3545
3604
|
}
|
|
3546
3605
|
const home = process.env.HOME?.trim();
|
|
3547
3606
|
if (home) {
|
|
3548
|
-
const nvmVersionsDir =
|
|
3549
|
-
if (
|
|
3607
|
+
const nvmVersionsDir = resolve17(home, ".nvm/versions/node");
|
|
3608
|
+
if (existsSync17(nvmVersionsDir)) {
|
|
3550
3609
|
try {
|
|
3551
3610
|
const versionDirs = readdirSync4(nvmVersionsDir).map((entry) => entry.trim()).filter((entry) => /^v\d+\.\d+\.\d+$/.test(entry)).sort((a, b) => Bun.semver.order(b.replace(/^v/, ""), a.replace(/^v/, "")));
|
|
3552
3611
|
for (const versionDir of versionDirs) {
|
|
3553
|
-
candidates.push(
|
|
3612
|
+
candidates.push(resolve17(nvmVersionsDir, versionDir, "bin/node"));
|
|
3554
3613
|
}
|
|
3555
3614
|
} catch {}
|
|
3556
3615
|
}
|
|
@@ -3559,8 +3618,8 @@ function resolvePreferredNodeBinary() {
|
|
|
3559
3618
|
if (whichNode) {
|
|
3560
3619
|
candidates.push(whichNode);
|
|
3561
3620
|
}
|
|
3562
|
-
const deduped = uniq(candidates.map((candidate) =>
|
|
3563
|
-
const existing = deduped.filter((candidate) =>
|
|
3621
|
+
const deduped = uniq(candidates.map((candidate) => resolve17(candidate)));
|
|
3622
|
+
const existing = deduped.filter((candidate) => existsSync17(candidate));
|
|
3564
3623
|
if (existing.length === 0) {
|
|
3565
3624
|
return null;
|
|
3566
3625
|
}
|
|
@@ -3574,7 +3633,7 @@ function resolvePreferredNodeBinary() {
|
|
|
3574
3633
|
return existing[0] ?? null;
|
|
3575
3634
|
}
|
|
3576
3635
|
function inferNodeMajor(nodeBinaryPath) {
|
|
3577
|
-
const normalized =
|
|
3636
|
+
const normalized = resolve17(nodeBinaryPath).replace(/\\/g, "/");
|
|
3578
3637
|
const match = normalized.match(/(?:^|\/)(?:node-)?v?(\d+)\.\d+\.\d+(?:\/|$)/);
|
|
3579
3638
|
if (!match) {
|
|
3580
3639
|
return null;
|
|
@@ -3586,8 +3645,8 @@ function normalizeExecutablePath(candidate) {
|
|
|
3586
3645
|
if (!candidate) {
|
|
3587
3646
|
return "";
|
|
3588
3647
|
}
|
|
3589
|
-
const normalized =
|
|
3590
|
-
if (!
|
|
3648
|
+
const normalized = resolve17(candidate);
|
|
3649
|
+
if (!existsSync17(normalized)) {
|
|
3591
3650
|
return "";
|
|
3592
3651
|
}
|
|
3593
3652
|
try {
|
|
@@ -3597,7 +3656,7 @@ function normalizeExecutablePath(candidate) {
|
|
|
3597
3656
|
}
|
|
3598
3657
|
}
|
|
3599
3658
|
function looksLikeRuntimeGateway(candidate) {
|
|
3600
|
-
const normalized =
|
|
3659
|
+
const normalized = resolve17(candidate).replace(/\\/g, "/");
|
|
3601
3660
|
return normalized.includes("/.rig/bin/") || normalized.endsWith("/rig-shell") || normalized.endsWith("/rig-agent");
|
|
3602
3661
|
}
|
|
3603
3662
|
|
|
@@ -3618,7 +3677,7 @@ function runtimeProvisioningEnv(baseEnv = process.env) {
|
|
|
3618
3677
|
try {
|
|
3619
3678
|
return resolveClaudeInstallDir();
|
|
3620
3679
|
} catch {
|
|
3621
|
-
return
|
|
3680
|
+
return resolve18(claudeBinary, "..");
|
|
3622
3681
|
}
|
|
3623
3682
|
})() : "";
|
|
3624
3683
|
const nodeDir = resolveNodeInstallDir();
|
|
@@ -3628,8 +3687,8 @@ function runtimeProvisioningEnv(baseEnv = process.env) {
|
|
|
3628
3687
|
`${bunDir}/bin`,
|
|
3629
3688
|
claudeDir,
|
|
3630
3689
|
nodeDir ? `${nodeDir}/bin` : "",
|
|
3631
|
-
realHome ?
|
|
3632
|
-
realHome ?
|
|
3690
|
+
realHome ? resolve18(realHome, ".local/bin") : "",
|
|
3691
|
+
realHome ? resolve18(realHome, ".cargo/bin") : "",
|
|
3633
3692
|
...inheritedPath,
|
|
3634
3693
|
"/usr/local/bin",
|
|
3635
3694
|
"/usr/local/sbin",
|
|
@@ -3660,9 +3719,9 @@ function runtimeProvisioningEnv(baseEnv = process.env) {
|
|
|
3660
3719
|
// packages/runtime/src/control-plane/native/validator-binaries.ts
|
|
3661
3720
|
function resolveValidatorBinaryPath(projectRoot, binaryName, runtimeContext) {
|
|
3662
3721
|
if (runtimeContext) {
|
|
3663
|
-
return
|
|
3722
|
+
return resolve19(runtimeContext.binDir, "validators", binaryName);
|
|
3664
3723
|
}
|
|
3665
|
-
return
|
|
3724
|
+
return resolve19(resolveHarnessPaths(projectRoot).binDir, "validators", binaryName);
|
|
3666
3725
|
}
|
|
3667
3726
|
async function ensureValidatorBinary(projectRoot, checkId, runtimeContext) {
|
|
3668
3727
|
const match = checkId.match(/^([a-z][\w-]*):([a-z][\w-]*)$/);
|
|
@@ -3677,19 +3736,19 @@ async function ensureValidatorBinary(projectRoot, checkId, runtimeContext) {
|
|
|
3677
3736
|
const binaryName = `${category}-${check}`;
|
|
3678
3737
|
const binaryPath = resolveValidatorBinaryPath(projectRoot, binaryName, runtimeContext);
|
|
3679
3738
|
const hostProjectRoot = runtimeContext?.hostProjectRoot?.trim() || projectRoot;
|
|
3680
|
-
const sourcePath =
|
|
3681
|
-
if (!
|
|
3739
|
+
const sourcePath = resolve19(hostProjectRoot, "packages/runtime/src/control-plane/validators", category, `${check}.ts`);
|
|
3740
|
+
if (!existsSync18(sourcePath)) {
|
|
3682
3741
|
return null;
|
|
3683
3742
|
}
|
|
3684
3743
|
const sourceMtime = statSync5(sourcePath).mtimeMs;
|
|
3685
|
-
const binaryExists =
|
|
3744
|
+
const binaryExists = existsSync18(binaryPath);
|
|
3686
3745
|
const binaryMtime = binaryExists ? statSync5(binaryPath).mtimeMs : 0;
|
|
3687
3746
|
if (!binaryExists || sourceMtime > binaryMtime) {
|
|
3688
3747
|
if (binaryExists) {
|
|
3689
3748
|
rmSync5(binaryPath, { force: true });
|
|
3690
3749
|
rmSync5(`${binaryPath}.build-manifest.json`, { force: true });
|
|
3691
3750
|
}
|
|
3692
|
-
|
|
3751
|
+
mkdirSync8(dirname10(binaryPath), { recursive: true });
|
|
3693
3752
|
await buildRuntimeBinary({
|
|
3694
3753
|
sourcePath: `packages/runtime/src/control-plane/validators/${category}/${check}.ts`,
|
|
3695
3754
|
outputPath: binaryPath,
|
|
@@ -3698,7 +3757,7 @@ async function ensureValidatorBinary(projectRoot, checkId, runtimeContext) {
|
|
|
3698
3757
|
env: runtimeProvisioningEnv()
|
|
3699
3758
|
});
|
|
3700
3759
|
}
|
|
3701
|
-
return
|
|
3760
|
+
return existsSync18(binaryPath) ? binaryPath : null;
|
|
3702
3761
|
}
|
|
3703
3762
|
|
|
3704
3763
|
// packages/runtime/src/control-plane/native/validator.ts
|
|
@@ -3735,20 +3794,20 @@ async function readTaskSourceValidation(projectRoot, taskId) {
|
|
|
3735
3794
|
function resolveValidationPaths(projectRoot, taskId, runtimeContext) {
|
|
3736
3795
|
if (runtimeContext) {
|
|
3737
3796
|
return {
|
|
3738
|
-
taskLogDir:
|
|
3739
|
-
artifactDir:
|
|
3797
|
+
taskLogDir: resolve20(runtimeContext.logsDir, taskId),
|
|
3798
|
+
artifactDir: resolve20(runtimeContext.workspaceDir, "artifacts", taskId)
|
|
3740
3799
|
};
|
|
3741
3800
|
}
|
|
3742
3801
|
const paths = resolveHarnessPaths(projectRoot);
|
|
3743
3802
|
return {
|
|
3744
|
-
taskLogDir:
|
|
3745
|
-
artifactDir:
|
|
3803
|
+
taskLogDir: resolve20(paths.logsDir, taskId),
|
|
3804
|
+
artifactDir: resolve20(paths.artifactsDir, taskId)
|
|
3746
3805
|
};
|
|
3747
3806
|
}
|
|
3748
3807
|
async function runValidatorBinary(projectRoot, taskId, checkId, runtimeContext) {
|
|
3749
3808
|
const binaryName = checkId.replace(":", "-");
|
|
3750
3809
|
const binaryPath = await ensureValidatorBinary(projectRoot, checkId, runtimeContext) ?? resolveValidatorBinaryPath(projectRoot, binaryName, runtimeContext);
|
|
3751
|
-
if (!
|
|
3810
|
+
if (!existsSync19(binaryPath)) {
|
|
3752
3811
|
return {
|
|
3753
3812
|
result: {
|
|
3754
3813
|
id: checkId,
|
|
@@ -3759,7 +3818,7 @@ async function runValidatorBinary(projectRoot, taskId, checkId, runtimeContext)
|
|
|
3759
3818
|
};
|
|
3760
3819
|
}
|
|
3761
3820
|
const validatorCwd = runtimeContext?.workspaceDir || resolveMonorepoRoot(projectRoot);
|
|
3762
|
-
const runtimeShellPath = runtimeContext ?
|
|
3821
|
+
const runtimeShellPath = runtimeContext ? resolve20(runtimeContext.binDir, "rig-shell") : "";
|
|
3763
3822
|
const monorepoMainRoot = runtimeContext?.monorepoMainRoot || process.env.MONOREPO_MAIN_ROOT?.trim() || resolveMonorepoRoot(projectRoot);
|
|
3764
3823
|
const validatorEnv = {
|
|
3765
3824
|
PROJECT_RIG_ROOT: runtimeContext?.hostProjectRoot || projectRoot,
|
|
@@ -3774,7 +3833,7 @@ async function runValidatorBinary(projectRoot, taskId, checkId, runtimeContext)
|
|
|
3774
3833
|
validatorEnv.RIG_LOGS_DIR = runtimeContext.logsDir;
|
|
3775
3834
|
validatorEnv.RIG_RUNTIME_BIN_DIR = runtimeContext.binDir;
|
|
3776
3835
|
}
|
|
3777
|
-
const { exitCode, stdout, stderr } = await runCaptureAsync(runtimeShellPath &&
|
|
3836
|
+
const { exitCode, stdout, stderr } = await runCaptureAsync(runtimeShellPath && existsSync19(runtimeShellPath) ? [runtimeShellPath, "run-binary", binaryPath] : [binaryPath], validatorCwd, validatorEnv);
|
|
3778
3837
|
try {
|
|
3779
3838
|
const result = JSON.parse(stdout.trim());
|
|
3780
3839
|
return { result, exitCode };
|
|
@@ -3814,8 +3873,8 @@ async function validateTask(projectRoot, taskId, runtimeContext, registry, optio
|
|
|
3814
3873
|
const configuredValidation = stringArray(taskConfig[taskId]?.validation);
|
|
3815
3874
|
const commands = resolvedContext?.validation?.length ? resolvedContext.validation : configuredValidation.length > 0 ? configuredValidation : sourceValidation.validation;
|
|
3816
3875
|
const { taskLogDir, artifactDir } = resolveValidationPaths(projectRoot, taskId, resolvedContext);
|
|
3817
|
-
|
|
3818
|
-
|
|
3876
|
+
mkdirSync9(taskLogDir, { recursive: true });
|
|
3877
|
+
mkdirSync9(artifactDir, { recursive: true });
|
|
3819
3878
|
if (commands.length === 0) {
|
|
3820
3879
|
const skipped = {
|
|
3821
3880
|
status: "skipped",
|
|
@@ -3824,7 +3883,7 @@ async function validateTask(projectRoot, taskId, runtimeContext, registry, optio
|
|
|
3824
3883
|
failed: 0,
|
|
3825
3884
|
categories: []
|
|
3826
3885
|
};
|
|
3827
|
-
|
|
3886
|
+
writeFileSync9(resolve20(artifactDir, "validation-summary.json"), `${JSON.stringify(skipped, null, 2)}
|
|
3828
3887
|
`, "utf-8");
|
|
3829
3888
|
return skipped;
|
|
3830
3889
|
}
|
|
@@ -3859,18 +3918,18 @@ async function validateTask(projectRoot, taskId, runtimeContext, registry, optio
|
|
|
3859
3918
|
exit_code: 2,
|
|
3860
3919
|
duration_seconds: 0
|
|
3861
3920
|
});
|
|
3862
|
-
const logFile2 =
|
|
3863
|
-
|
|
3864
|
-
|
|
3921
|
+
const logFile2 = resolve20(taskLogDir, `invalid-entry-validation.log`);
|
|
3922
|
+
mkdirSync9(taskLogDir, { recursive: true });
|
|
3923
|
+
writeFileSync9(logFile2, `=== ${nowIso()} :: ${cmd} ===
|
|
3865
3924
|
Invalid validation entry: not a check-ID. All entries must use format "category:check-name".
|
|
3866
3925
|
`, "utf-8");
|
|
3867
3926
|
continue;
|
|
3868
3927
|
}
|
|
3869
3928
|
const { result, exitCode } = await dispatchValidator(cmd, effectiveRegistry, validatorCtx, (id) => runValidatorBinary(projectRoot, taskId, id, resolvedContext));
|
|
3870
3929
|
const durationSeconds = Math.max(0, Math.round((Date.now() - startedAt) / 1000));
|
|
3871
|
-
const logFile =
|
|
3872
|
-
|
|
3873
|
-
|
|
3930
|
+
const logFile = resolve20(taskLogDir, `${cmd.replace(":", "-")}-validation.log`);
|
|
3931
|
+
mkdirSync9(taskLogDir, { recursive: true });
|
|
3932
|
+
writeFileSync9(logFile, `=== ${nowIso()} :: ${cmd} ===
|
|
3874
3933
|
${JSON.stringify(result, null, 2)}
|
|
3875
3934
|
`, "utf-8");
|
|
3876
3935
|
if (result.passed) {
|
|
@@ -3892,19 +3951,19 @@ ${JSON.stringify(result, null, 2)}
|
|
|
3892
3951
|
failed,
|
|
3893
3952
|
categories
|
|
3894
3953
|
};
|
|
3895
|
-
|
|
3896
|
-
|
|
3954
|
+
mkdirSync9(artifactDir, { recursive: true });
|
|
3955
|
+
writeFileSync9(resolve20(artifactDir, "validation-summary.json"), `${JSON.stringify(summary, null, 2)}
|
|
3897
3956
|
`, "utf-8");
|
|
3898
3957
|
return summary;
|
|
3899
3958
|
}
|
|
3900
3959
|
|
|
3901
3960
|
// packages/runtime/src/control-plane/native/verifier.ts
|
|
3902
|
-
import { existsSync as
|
|
3903
|
-
import { resolve as
|
|
3961
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
|
|
3962
|
+
import { resolve as resolve22 } from "path";
|
|
3904
3963
|
|
|
3905
3964
|
// packages/runtime/src/control-plane/native/pr-review-gate.ts
|
|
3906
|
-
import { mkdirSync as
|
|
3907
|
-
import { resolve as
|
|
3965
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
3966
|
+
import { resolve as resolve21 } from "path";
|
|
3908
3967
|
function parseJsonObject(value) {
|
|
3909
3968
|
if (!value?.trim())
|
|
3910
3969
|
return { value: {}, error: "empty JSON output" };
|
|
@@ -5228,34 +5287,34 @@ function buildStrictPrGateSteeringPrompt(result) {
|
|
|
5228
5287
|
}
|
|
5229
5288
|
function persistPrReviewCycleArtifacts(input) {
|
|
5230
5289
|
const cycleName = input.final ? `${input.cycle}-final` : String(input.cycle);
|
|
5231
|
-
const taskArtifactRoot = input.artifactRoot?.trim() ? input.artifactRoot :
|
|
5232
|
-
const root =
|
|
5233
|
-
|
|
5234
|
-
const finalMergeGateResultPath = input.final ?
|
|
5290
|
+
const taskArtifactRoot = input.artifactRoot?.trim() ? input.artifactRoot : resolve21(input.projectRoot, "artifacts", input.taskId);
|
|
5291
|
+
const root = resolve21(taskArtifactRoot, "pr-review-cycles", cycleName);
|
|
5292
|
+
mkdirSync10(root, { recursive: true });
|
|
5293
|
+
const finalMergeGateResultPath = input.final ? resolve21(taskArtifactRoot, "merge-gate-final.json") : undefined;
|
|
5235
5294
|
const paths = {
|
|
5236
5295
|
root,
|
|
5237
|
-
prTitlePath:
|
|
5238
|
-
prBodyPath:
|
|
5239
|
-
prCommentsPath:
|
|
5240
|
-
reviewThreadsPath:
|
|
5241
|
-
reviewCommentsPath:
|
|
5242
|
-
checkRollupPath:
|
|
5243
|
-
greptileEvidencePath:
|
|
5244
|
-
mergeGateResultPath:
|
|
5245
|
-
steeringPromptPath:
|
|
5296
|
+
prTitlePath: resolve21(root, "pr-title.md"),
|
|
5297
|
+
prBodyPath: resolve21(root, "pr-body.md"),
|
|
5298
|
+
prCommentsPath: resolve21(root, "pr-comments.json"),
|
|
5299
|
+
reviewThreadsPath: resolve21(root, "review-threads.json"),
|
|
5300
|
+
reviewCommentsPath: resolve21(root, "review-comments.json"),
|
|
5301
|
+
checkRollupPath: resolve21(root, "check-rollup.json"),
|
|
5302
|
+
greptileEvidencePath: resolve21(root, "greptile-evidence.json"),
|
|
5303
|
+
mergeGateResultPath: resolve21(root, "merge-gate-result.json"),
|
|
5304
|
+
steeringPromptPath: resolve21(root, "agent-steering-prompt.md"),
|
|
5246
5305
|
...finalMergeGateResultPath ? { finalMergeGateResultPath } : {}
|
|
5247
5306
|
};
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5307
|
+
writeFileSync10(paths.prTitlePath, input.result.evidence.title || "", "utf8");
|
|
5308
|
+
writeFileSync10(paths.prBodyPath, input.result.evidence.body || "", "utf8");
|
|
5309
|
+
writeFileSync10(paths.prCommentsPath, `${JSON.stringify(input.result.evidence.relevantIssueComments, null, 2)}
|
|
5251
5310
|
`, "utf8");
|
|
5252
|
-
|
|
5311
|
+
writeFileSync10(paths.reviewThreadsPath, `${JSON.stringify(input.result.evidence.reviewThreads, null, 2)}
|
|
5253
5312
|
`, "utf8");
|
|
5254
|
-
|
|
5313
|
+
writeFileSync10(paths.reviewCommentsPath, `${JSON.stringify(input.result.evidence.changedFileReviewComments, null, 2)}
|
|
5255
5314
|
`, "utf8");
|
|
5256
|
-
|
|
5315
|
+
writeFileSync10(paths.checkRollupPath, `${JSON.stringify(input.result.evidence.statusCheckRollup, null, 2)}
|
|
5257
5316
|
`, "utf8");
|
|
5258
|
-
|
|
5317
|
+
writeFileSync10(paths.greptileEvidencePath, `${JSON.stringify(input.result.evidence.greptile, null, 2)}
|
|
5259
5318
|
`, "utf8");
|
|
5260
5319
|
const mergeGatePayload = {
|
|
5261
5320
|
approved: input.result.approved,
|
|
@@ -5272,13 +5331,13 @@ function persistPrReviewCycleArtifacts(input) {
|
|
|
5272
5331
|
evidence: input.result.evidence,
|
|
5273
5332
|
cycleArtifactRoot: root
|
|
5274
5333
|
};
|
|
5275
|
-
|
|
5334
|
+
writeFileSync10(paths.mergeGateResultPath, `${JSON.stringify(mergeGatePayload, null, 2)}
|
|
5276
5335
|
`, "utf8");
|
|
5277
5336
|
if (paths.finalMergeGateResultPath) {
|
|
5278
|
-
|
|
5337
|
+
writeFileSync10(paths.finalMergeGateResultPath, `${JSON.stringify(mergeGatePayload, null, 2)}
|
|
5279
5338
|
`, "utf8");
|
|
5280
5339
|
}
|
|
5281
|
-
|
|
5340
|
+
writeFileSync10(paths.steeringPromptPath, input.steeringPrompt, "utf8");
|
|
5282
5341
|
return paths;
|
|
5283
5342
|
}
|
|
5284
5343
|
async function runStrictPrMergeGate(input) {
|
|
@@ -5295,7 +5354,7 @@ async function runStrictPrMergeGate(input) {
|
|
|
5295
5354
|
final: input.final
|
|
5296
5355
|
});
|
|
5297
5356
|
const steeringPrompt = buildStrictPrGateSteeringPrompt({ ...base, artifacts });
|
|
5298
|
-
|
|
5357
|
+
writeFileSync10(artifacts.steeringPromptPath, steeringPrompt, "utf8");
|
|
5299
5358
|
return { ...base, artifacts, steeringPrompt };
|
|
5300
5359
|
}
|
|
5301
5360
|
|
|
@@ -5305,11 +5364,11 @@ async function verifyTask(options) {
|
|
|
5305
5364
|
const taskId = options.taskId;
|
|
5306
5365
|
const normalizedTaskId = lookupTask(options.projectRoot, taskId);
|
|
5307
5366
|
const artifactDir = artifactDirForId(options.projectRoot, taskId);
|
|
5308
|
-
|
|
5309
|
-
const validationSummaryPath =
|
|
5310
|
-
const reviewFeedbackPath =
|
|
5311
|
-
const reviewStatePath =
|
|
5312
|
-
const greptileRawPath =
|
|
5367
|
+
mkdirSync11(artifactDir, { recursive: true });
|
|
5368
|
+
const validationSummaryPath = resolve22(artifactDir, "validation-summary.json");
|
|
5369
|
+
const reviewFeedbackPath = resolve22(artifactDir, "review-feedback.md");
|
|
5370
|
+
const reviewStatePath = resolve22(artifactDir, "review-state.json");
|
|
5371
|
+
const greptileRawPath = resolve22(artifactDir, "review-greptile-raw.json");
|
|
5313
5372
|
const prStates = readPrMetadata(options.projectRoot, taskId);
|
|
5314
5373
|
const prState = prStates[0] || null;
|
|
5315
5374
|
const localReasons = [];
|
|
@@ -5321,7 +5380,7 @@ async function verifyTask(options) {
|
|
|
5321
5380
|
if (!normalizedTaskId && !await hasConfiguredSourceTask(options.projectRoot, taskId)) {
|
|
5322
5381
|
localReasons.push(`[Task Config] Unknown task id '${taskId}' in task-config or configured task source.`);
|
|
5323
5382
|
}
|
|
5324
|
-
if (!
|
|
5383
|
+
if (!existsSync20(validationSummaryPath)) {
|
|
5325
5384
|
localReasons.push(`[Artifact Quality] validation-summary.json not found at ${validationSummaryPath}.`);
|
|
5326
5385
|
} else {
|
|
5327
5386
|
const summary = await parseValidationSummary(validationSummaryPath);
|
|
@@ -5330,13 +5389,13 @@ async function verifyTask(options) {
|
|
|
5330
5389
|
}
|
|
5331
5390
|
}
|
|
5332
5391
|
for (const file of ["task-result.json", "decision-log.md", "next-actions.md", "changed-files.txt"]) {
|
|
5333
|
-
const requiredPath =
|
|
5334
|
-
if (!
|
|
5392
|
+
const requiredPath = resolve22(artifactDir, file);
|
|
5393
|
+
if (!existsSync20(requiredPath)) {
|
|
5335
5394
|
localReasons.push(`[Artifact Quality] Missing required artifact file: ${requiredPath}`);
|
|
5336
5395
|
}
|
|
5337
5396
|
}
|
|
5338
|
-
const taskResultPath =
|
|
5339
|
-
if (
|
|
5397
|
+
const taskResultPath = resolve22(artifactDir, "task-result.json");
|
|
5398
|
+
if (existsSync20(taskResultPath)) {
|
|
5340
5399
|
const taskResult = await readJsonFile2(taskResultPath);
|
|
5341
5400
|
const artifactStatus = typeof taskResult?.status === "string" ? taskResult.status.trim().toLowerCase() : "";
|
|
5342
5401
|
if (artifactStatus === "partial") {
|
|
@@ -5349,8 +5408,8 @@ async function verifyTask(options) {
|
|
|
5349
5408
|
localReasons.push("[Artifact Quality] task-result.json next actions indicate remaining implementation scope.");
|
|
5350
5409
|
}
|
|
5351
5410
|
}
|
|
5352
|
-
const nextActionsPath =
|
|
5353
|
-
if (
|
|
5411
|
+
const nextActionsPath = resolve22(artifactDir, "next-actions.md");
|
|
5412
|
+
if (existsSync20(nextActionsPath)) {
|
|
5354
5413
|
const nextActionsContent = await Bun.file(nextActionsPath).text();
|
|
5355
5414
|
if (nextActionsContent.includes("TODO: Replace this scaffold") || nextActionsContent.includes("bd-<downstream-task-id>")) {
|
|
5356
5415
|
localReasons.push("[Artifact Quality] next-actions.md still contains scaffold placeholder text. Replace with real recommendations.");
|
|
@@ -5381,7 +5440,7 @@ async function verifyTask(options) {
|
|
|
5381
5440
|
aiReasons.push(`[AI Review] Required mode needs a completed Greptile approval; current verdict is ${ai.verdict}.`);
|
|
5382
5441
|
}
|
|
5383
5442
|
if (persistArtifacts && ai.rawResponse) {
|
|
5384
|
-
|
|
5443
|
+
writeFileSync11(greptileRawPath, `${ai.rawResponse}
|
|
5385
5444
|
`, "utf-8");
|
|
5386
5445
|
}
|
|
5387
5446
|
} else if (!options.skipAiReview && reviewMode === "off") {
|
|
@@ -5624,7 +5683,7 @@ function evaluateSourceCloseoutChecks(prState, prLabel) {
|
|
|
5624
5683
|
if (!Array.isArray(checks) || checks.length === 0) {
|
|
5625
5684
|
return [`[Source Issue] PR ${prLabel} must have green check evidence before completion.`];
|
|
5626
5685
|
}
|
|
5627
|
-
const ciGate = evaluatePullRequestCiChecks(checks, "PR", 0);
|
|
5686
|
+
const ciGate = evaluatePullRequestCiChecks(checks, "PR", 0, { mergeStateStatus: prState.mergeStateStatus });
|
|
5628
5687
|
if (ciGate.verdict === "APPROVE") {
|
|
5629
5688
|
return [];
|
|
5630
5689
|
}
|
|
@@ -5720,7 +5779,7 @@ function isAcceptedValidationSummary(summary) {
|
|
|
5720
5779
|
return summary.status === "skipped" && summary.total === 0 && summary.failed === 0;
|
|
5721
5780
|
}
|
|
5722
5781
|
async function loadReviewMode(reviewProfilePath, fallback) {
|
|
5723
|
-
const parsed =
|
|
5782
|
+
const parsed = existsSync20(reviewProfilePath) ? await readJsonFile2(reviewProfilePath) : null;
|
|
5724
5783
|
const mode = parsed?.mode;
|
|
5725
5784
|
if (mode === "off" || mode === "advisory" || mode === "required") {
|
|
5726
5785
|
return mode;
|
|
@@ -5731,7 +5790,7 @@ async function loadReviewMode(reviewProfilePath, fallback) {
|
|
|
5731
5790
|
return "advisory";
|
|
5732
5791
|
}
|
|
5733
5792
|
async function loadReviewProvider(reviewProfilePath, fallback) {
|
|
5734
|
-
const parsed =
|
|
5793
|
+
const parsed = existsSync20(reviewProfilePath) ? await readJsonFile2(reviewProfilePath) : null;
|
|
5735
5794
|
const provider = parsed?.provider;
|
|
5736
5795
|
if (typeof provider === "string" && provider.trim().length > 0) {
|
|
5737
5796
|
return provider;
|
|
@@ -5890,7 +5949,7 @@ function writeFeedbackFile(options) {
|
|
|
5890
5949
|
if (options.aiRawFeedback) {
|
|
5891
5950
|
lines.push("## Raw Reviewer Feedback", "", "```text", options.aiRawFeedback, "```", "");
|
|
5892
5951
|
}
|
|
5893
|
-
|
|
5952
|
+
writeFileSync11(options.output, `${lines.join(`
|
|
5894
5953
|
`)}
|
|
5895
5954
|
`, "utf-8");
|
|
5896
5955
|
}
|
|
@@ -5907,7 +5966,7 @@ function writeReviewStateFile(options) {
|
|
|
5907
5966
|
ai_warnings: options.aiWarnings,
|
|
5908
5967
|
updated_at: nowIso()
|
|
5909
5968
|
};
|
|
5910
|
-
|
|
5969
|
+
writeFileSync11(options.output, `${JSON.stringify(payload, null, 2)}
|
|
5911
5970
|
`, "utf-8");
|
|
5912
5971
|
}
|
|
5913
5972
|
async function runGreptileReviewForPr(options) {
|
|
@@ -5988,7 +6047,7 @@ async function runGreptileReviewForPr(options) {
|
|
|
5988
6047
|
}
|
|
5989
6048
|
await Bun.sleep(options.pollIntervalMs);
|
|
5990
6049
|
}
|
|
5991
|
-
const ciGate = evaluatePullRequestCiChecks(githubCheckRollup, repoName, prNumber);
|
|
6050
|
+
const ciGate = evaluatePullRequestCiChecks(githubCheckRollup, repoName, prNumber, { mergeStateStatus: options.prState.mergeStateStatus });
|
|
5992
6051
|
if (ciGate.verdict !== "APPROVE") {
|
|
5993
6052
|
return {
|
|
5994
6053
|
verdict: ciGate.verdict,
|
|
@@ -6246,7 +6305,7 @@ async function runGithubGreptileFallbackReviewForPr(options) {
|
|
|
6246
6305
|
}
|
|
6247
6306
|
await Bun.sleep(options.pollIntervalMs);
|
|
6248
6307
|
}
|
|
6249
|
-
const ciGate = evaluatePullRequestCiChecks(checkRollup, repoName, prNumber);
|
|
6308
|
+
const ciGate = evaluatePullRequestCiChecks(checkRollup, repoName, prNumber, { mergeStateStatus: options.prState.mergeStateStatus });
|
|
6250
6309
|
if (ciGate.verdict !== "APPROVE") {
|
|
6251
6310
|
return {
|
|
6252
6311
|
verdict: ciGate.verdict,
|
|
@@ -6601,19 +6660,31 @@ function loadGithubPullRequestCheckRollup(projectRoot, repoName, prNumber) {
|
|
|
6601
6660
|
]);
|
|
6602
6661
|
return response.statusCheckRollup || [];
|
|
6603
6662
|
}
|
|
6604
|
-
function evaluatePullRequestCiChecks(checks, repoName, prNumber) {
|
|
6605
|
-
const
|
|
6606
|
-
const label = (check.name || check.context || "").toLowerCase();
|
|
6607
|
-
return label.length > 0 && !label.includes("greptile");
|
|
6608
|
-
});
|
|
6609
|
-
const pending = nonGreptileChecks.filter((check) => {
|
|
6663
|
+
function evaluatePullRequestCiChecks(checks, repoName, prNumber, options = {}) {
|
|
6664
|
+
const isPendingCheck2 = (check) => {
|
|
6610
6665
|
if ((check.__typename || "") === "CheckRun") {
|
|
6611
6666
|
return (check.status || "").toUpperCase() !== "COMPLETED";
|
|
6612
6667
|
}
|
|
6613
6668
|
const state = (check.state || check.status || "").toUpperCase();
|
|
6614
6669
|
return state === "PENDING" || state === "EXPECTED" || state === "QUEUED" || state === "IN_PROGRESS";
|
|
6670
|
+
};
|
|
6671
|
+
const pendingGreptile = checks.filter((check) => {
|
|
6672
|
+
const label = (check.name || check.context || "").toLowerCase();
|
|
6673
|
+
return label.includes("greptile") && isPendingCheck2(check);
|
|
6615
6674
|
});
|
|
6616
|
-
if (
|
|
6675
|
+
if (pendingGreptile.length > 0) {
|
|
6676
|
+
return {
|
|
6677
|
+
verdict: "SKIP",
|
|
6678
|
+
reasons: pendingGreptile.map((check) => `[CI] ${repoName}#${prNumber} mandatory Greptile check is still pending: ${check.name || check.context || "unknown"}.`)
|
|
6679
|
+
};
|
|
6680
|
+
}
|
|
6681
|
+
const nonGreptileChecks = checks.filter((check) => {
|
|
6682
|
+
const label = (check.name || check.context || "").toLowerCase();
|
|
6683
|
+
return label.length > 0 && !label.includes("greptile");
|
|
6684
|
+
});
|
|
6685
|
+
const pending = nonGreptileChecks.filter(isPendingCheck2);
|
|
6686
|
+
const mergeClean = (options.mergeStateStatus || "").toUpperCase() === "CLEAN";
|
|
6687
|
+
if (pending.length > 0 && !mergeClean) {
|
|
6617
6688
|
return {
|
|
6618
6689
|
verdict: "SKIP",
|
|
6619
6690
|
reasons: pending.map((check) => `[CI] ${repoName}#${prNumber} check is still pending: ${check.name || check.context || "unknown"}.`)
|
|
@@ -6694,7 +6765,7 @@ function filterActionableGithubGreptileThreads(threads) {
|
|
|
6694
6765
|
}
|
|
6695
6766
|
function resolvePrRepoRoot(projectRoot, prState) {
|
|
6696
6767
|
const runtimeWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
6697
|
-
if (prState.target === "monorepo" && runtimeWorkspace &&
|
|
6768
|
+
if (prState.target === "monorepo" && runtimeWorkspace && existsSync20(resolve22(runtimeWorkspace, ".git"))) {
|
|
6698
6769
|
return runtimeWorkspace;
|
|
6699
6770
|
}
|
|
6700
6771
|
const paths = resolveHarnessPaths(projectRoot);
|
|
@@ -6762,40 +6833,40 @@ function taskArtifacts(projectRoot, taskId) {
|
|
|
6762
6833
|
throw new Error("No active task.");
|
|
6763
6834
|
}
|
|
6764
6835
|
const paths = resolveHarnessPaths(projectRoot);
|
|
6765
|
-
const artifactDir =
|
|
6766
|
-
|
|
6836
|
+
const artifactDir = resolve23(paths.artifactsDir, activeTask);
|
|
6837
|
+
mkdirSync12(artifactDir, { recursive: true });
|
|
6767
6838
|
const changed = changedFilesForTask(projectRoot, activeTask, true);
|
|
6768
|
-
|
|
6839
|
+
writeFileSync12(resolve23(artifactDir, "changed-files.txt"), `${changed.join(`
|
|
6769
6840
|
`)}
|
|
6770
6841
|
`, "utf-8");
|
|
6771
6842
|
console.log(`changed-files.txt: ${changed.length} files`);
|
|
6772
|
-
const taskResultPath =
|
|
6773
|
-
if (!
|
|
6843
|
+
const taskResultPath = resolve23(artifactDir, "task-result.json");
|
|
6844
|
+
if (!existsSync21(taskResultPath)) {
|
|
6774
6845
|
const template = {
|
|
6775
6846
|
task_id: activeTask,
|
|
6776
6847
|
status: "completed",
|
|
6777
6848
|
summary: "TODO: Write a one-line summary of what you did",
|
|
6778
6849
|
completed_at: nowIso()
|
|
6779
6850
|
};
|
|
6780
|
-
|
|
6851
|
+
writeFileSync12(taskResultPath, `${JSON.stringify(template, null, 2)}
|
|
6781
6852
|
`, "utf-8");
|
|
6782
6853
|
console.log("task-result.json: created (update the summary!)");
|
|
6783
6854
|
} else {
|
|
6784
6855
|
console.log("task-result.json: already exists");
|
|
6785
6856
|
}
|
|
6786
|
-
const decisionLogPath =
|
|
6787
|
-
if (!
|
|
6857
|
+
const decisionLogPath = resolve23(artifactDir, "decision-log.md");
|
|
6858
|
+
if (!existsSync21(decisionLogPath)) {
|
|
6788
6859
|
const content = `# Decision Log: ${activeTask}
|
|
6789
6860
|
|
|
6790
6861
|
Record key decisions here using: rig-agent record decision "..."
|
|
6791
6862
|
`;
|
|
6792
|
-
|
|
6863
|
+
writeFileSync12(decisionLogPath, content, "utf-8");
|
|
6793
6864
|
console.log("decision-log.md: created (record your decisions!)");
|
|
6794
6865
|
} else {
|
|
6795
6866
|
console.log("decision-log.md: already exists");
|
|
6796
6867
|
}
|
|
6797
|
-
const nextActionsPath =
|
|
6798
|
-
if (!
|
|
6868
|
+
const nextActionsPath = resolve23(artifactDir, "next-actions.md");
|
|
6869
|
+
if (!existsSync21(nextActionsPath)) {
|
|
6799
6870
|
const content = [
|
|
6800
6871
|
`# Next Actions: ${activeTask}`,
|
|
6801
6872
|
"",
|
|
@@ -6812,13 +6883,13 @@ Record key decisions here using: rig-agent record decision "..."
|
|
|
6812
6883
|
""
|
|
6813
6884
|
].join(`
|
|
6814
6885
|
`);
|
|
6815
|
-
|
|
6886
|
+
writeFileSync12(nextActionsPath, content, "utf-8");
|
|
6816
6887
|
console.log("next-actions.md: created (add recommendations for downstream tasks!)");
|
|
6817
6888
|
} else {
|
|
6818
6889
|
console.log("next-actions.md: already exists");
|
|
6819
6890
|
}
|
|
6820
|
-
const validationSummaryPath =
|
|
6821
|
-
if (
|
|
6891
|
+
const validationSummaryPath = resolve23(artifactDir, "validation-summary.json");
|
|
6892
|
+
if (existsSync21(validationSummaryPath)) {
|
|
6822
6893
|
console.log("validation-summary.json: already exists");
|
|
6823
6894
|
} else {
|
|
6824
6895
|
console.log("validation-summary.json: not yet created (run: rig-agent validate)");
|
|
@@ -6884,7 +6955,7 @@ function collectTaskChangedFiles(projectRoot, taskId, includeCommitted) {
|
|
|
6884
6955
|
[projectRoot, ""],
|
|
6885
6956
|
[monorepoRepoRoot, ""]
|
|
6886
6957
|
]) {
|
|
6887
|
-
if (!
|
|
6958
|
+
if (!existsSync21(resolve23(repo, ".git"))) {
|
|
6888
6959
|
continue;
|
|
6889
6960
|
}
|
|
6890
6961
|
if (includeCommitted && repo === monorepoRepoRoot) {
|
|
@@ -6909,12 +6980,22 @@ function filterTaskChangedFiles(projectRoot, taskId, files, scoped) {
|
|
|
6909
6980
|
}
|
|
6910
6981
|
function resolveTaskMonorepoRoot(projectRoot) {
|
|
6911
6982
|
const runtimeWorkspace = loadRuntimeContextFromEnv()?.workspaceDir || process.env.RIG_TASK_WORKSPACE?.trim();
|
|
6912
|
-
if (runtimeWorkspace &&
|
|
6913
|
-
return
|
|
6983
|
+
if (runtimeWorkspace && existsSync21(resolve23(runtimeWorkspace, ".git"))) {
|
|
6984
|
+
return resolve23(runtimeWorkspace);
|
|
6914
6985
|
}
|
|
6915
6986
|
return resolveHarnessPaths(projectRoot).monorepoRoot;
|
|
6916
6987
|
}
|
|
6917
6988
|
function collectCommittedMonorepoFiles(projectRoot, repo) {
|
|
6989
|
+
const defaultRef = runCapture(["git", "-C", repo, "rev-parse", "--abbrev-ref", "origin/HEAD"], projectRoot);
|
|
6990
|
+
if (defaultRef.exitCode === 0 && defaultRef.stdout.trim()) {
|
|
6991
|
+
const mergeBase = runCapture(["git", "-C", repo, "merge-base", "HEAD", defaultRef.stdout.trim()], projectRoot);
|
|
6992
|
+
if (mergeBase.exitCode === 0 && mergeBase.stdout.trim()) {
|
|
6993
|
+
const result = runCapture(["git", "-C", repo, "diff", "--name-only", `${mergeBase.stdout.trim()}..HEAD`], projectRoot);
|
|
6994
|
+
if (result.exitCode === 0) {
|
|
6995
|
+
return result.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
6996
|
+
}
|
|
6997
|
+
}
|
|
6998
|
+
}
|
|
6918
6999
|
const initialHeadCommit = resolveRuntimeInitialHeadCommit(projectRoot, repo);
|
|
6919
7000
|
if (initialHeadCommit) {
|
|
6920
7001
|
const result = runCapture(["git", "-C", repo, "diff", "--name-only", `${initialHeadCommit}..HEAD`], projectRoot);
|
|
@@ -6938,7 +7019,7 @@ function resolveRuntimeInitialHeadCommit(projectRoot, repo) {
|
|
|
6938
7019
|
const runtimeContext = loadRuntimeContextFromEnv();
|
|
6939
7020
|
if (runtimeContext?.initialHeadCommits?.monorepo?.trim()) {
|
|
6940
7021
|
const monorepoRoot = resolveTaskMonorepoRoot(projectRoot);
|
|
6941
|
-
if (
|
|
7022
|
+
if (resolve23(monorepoRoot) === resolve23(repo)) {
|
|
6942
7023
|
return runtimeContext.initialHeadCommits.monorepo.trim();
|
|
6943
7024
|
}
|
|
6944
7025
|
}
|
|
@@ -6948,7 +7029,7 @@ function resolveMonorepoBaseCommit(projectRoot, repo) {
|
|
|
6948
7029
|
const runtimeContext = loadRuntimeContextFromEnv();
|
|
6949
7030
|
if (runtimeContext?.monorepoBaseCommit?.trim()) {
|
|
6950
7031
|
const monorepoRoot = resolveTaskMonorepoRoot(projectRoot);
|
|
6951
|
-
if (
|
|
7032
|
+
if (resolve23(monorepoRoot) === resolve23(repo)) {
|
|
6952
7033
|
return runtimeContext.monorepoBaseCommit.trim();
|
|
6953
7034
|
}
|
|
6954
7035
|
}
|
|
@@ -6993,7 +7074,7 @@ function resolveRuntimeDirtyBaseline(projectRoot, repo) {
|
|
|
6993
7074
|
return new Set;
|
|
6994
7075
|
}
|
|
6995
7076
|
const monorepoRoot = resolveTaskMonorepoRoot(projectRoot);
|
|
6996
|
-
const selected =
|
|
7077
|
+
const selected = resolve23(repo) === resolve23(monorepoRoot) ? dirtyFiles.monorepo : resolve23(repo) === resolve23(projectRoot) ? dirtyFiles.project : undefined;
|
|
6997
7078
|
return new Set((selected || []).map((file) => normalizeChangedFilePath(file)).filter(Boolean));
|
|
6998
7079
|
}
|
|
6999
7080
|
function normalizeChangedFilePath(file) {
|
|
@@ -7051,8 +7132,8 @@ function isRuntimeGatewayGhPath(candidate) {
|
|
|
7051
7132
|
}
|
|
7052
7133
|
function resolveOptionalMonorepoRoot(projectRoot) {
|
|
7053
7134
|
const runtimeWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
7054
|
-
if (runtimeWorkspace &&
|
|
7055
|
-
return
|
|
7135
|
+
if (runtimeWorkspace && existsSync22(resolve24(runtimeWorkspace, ".git"))) {
|
|
7136
|
+
return resolve24(runtimeWorkspace);
|
|
7056
7137
|
}
|
|
7057
7138
|
try {
|
|
7058
7139
|
return resolveMonorepoRoot2(projectRoot);
|
|
@@ -7077,7 +7158,7 @@ function resolveGitBinary(projectRoot) {
|
|
|
7077
7158
|
if (!candidate || isRuntimeGatewayGitPath(candidate)) {
|
|
7078
7159
|
continue;
|
|
7079
7160
|
}
|
|
7080
|
-
if (
|
|
7161
|
+
if (existsSync22(candidate)) {
|
|
7081
7162
|
return candidate;
|
|
7082
7163
|
}
|
|
7083
7164
|
}
|
|
@@ -7104,7 +7185,7 @@ function gitSyncBranch(projectRoot, taskId, targetRepo = "monorepo") {
|
|
|
7104
7185
|
}
|
|
7105
7186
|
const repoRoot = targetRepo === "monorepo" ? resolveOptionalMonorepoRoot(projectRoot) || resolveMonorepoRoot2(projectRoot) : projectRoot;
|
|
7106
7187
|
const repoLabel = targetRepo === "monorepo" ? "Monorepo" : "Project";
|
|
7107
|
-
if (!
|
|
7188
|
+
if (!existsSync22(resolve24(repoRoot, ".git"))) {
|
|
7108
7189
|
throw new Error(`${repoLabel} repo not found at ${repoRoot}`);
|
|
7109
7190
|
}
|
|
7110
7191
|
const branchId = resolveTaskBranchId(projectRoot, resolvedTask);
|
|
@@ -7164,7 +7245,7 @@ function gitOpenPr(options) {
|
|
|
7164
7245
|
} else if (taskId) {
|
|
7165
7246
|
gitSyncBranch(options.projectRoot, taskId, "project");
|
|
7166
7247
|
}
|
|
7167
|
-
if (!
|
|
7248
|
+
if (!existsSync22(resolve24(repoRoot, ".git"))) {
|
|
7168
7249
|
throw new Error(`Repository not available for open-pr target ${target}: ${repoRoot}`);
|
|
7169
7250
|
}
|
|
7170
7251
|
const branch = branchName(options.projectRoot, repoRoot);
|
|
@@ -7397,7 +7478,7 @@ function gitMergePr(options) {
|
|
|
7397
7478
|
}
|
|
7398
7479
|
const repoRoot = resolveRepoRoot(options.projectRoot, options.pr.target);
|
|
7399
7480
|
const repoNameWithOwner = resolveRepoNameWithOwner(options.projectRoot, repoRoot);
|
|
7400
|
-
if (!
|
|
7481
|
+
if (!existsSync22(resolve24(repoRoot, ".git"))) {
|
|
7401
7482
|
throw new Error(`Repository not available for merge-pr target ${options.pr.target}: ${repoRoot}`);
|
|
7402
7483
|
}
|
|
7403
7484
|
const prState = readPrViewState(gh, repoRoot, repoNameWithOwner, options.pr.url);
|
|
@@ -7458,12 +7539,12 @@ function assertPrHasNoGitConflicts(prState, repoLabel, baseRef) {
|
|
|
7458
7539
|
}
|
|
7459
7540
|
function writePrMetadata(projectRoot, taskId, result) {
|
|
7460
7541
|
const dir = artifactDirForId(projectRoot, taskId);
|
|
7461
|
-
|
|
7462
|
-
const path =
|
|
7542
|
+
mkdirSync13(dir, { recursive: true });
|
|
7543
|
+
const path = resolve24(dir, "pr-state.json");
|
|
7463
7544
|
let prs = {};
|
|
7464
|
-
if (
|
|
7545
|
+
if (existsSync22(path)) {
|
|
7465
7546
|
try {
|
|
7466
|
-
const parsed = JSON.parse(
|
|
7547
|
+
const parsed = JSON.parse(readFileSync13(path, "utf-8"));
|
|
7467
7548
|
if (parsed && typeof parsed === "object" && parsed.prs && typeof parsed.prs === "object") {
|
|
7468
7549
|
prs = parsed.prs;
|
|
7469
7550
|
}
|
|
@@ -7479,16 +7560,16 @@ function writePrMetadata(projectRoot, taskId, result) {
|
|
|
7479
7560
|
...primary || {},
|
|
7480
7561
|
updated_at: nowIso()
|
|
7481
7562
|
};
|
|
7482
|
-
|
|
7563
|
+
writeFileSync13(path, `${JSON.stringify(artifact, null, 2)}
|
|
7483
7564
|
`, "utf-8");
|
|
7484
7565
|
}
|
|
7485
7566
|
function readPrMetadata(projectRoot, taskId) {
|
|
7486
|
-
const path =
|
|
7487
|
-
if (!
|
|
7567
|
+
const path = resolve24(artifactDirForId(projectRoot, taskId), "pr-state.json");
|
|
7568
|
+
if (!existsSync22(path)) {
|
|
7488
7569
|
return [];
|
|
7489
7570
|
}
|
|
7490
7571
|
try {
|
|
7491
|
-
const parsed = JSON.parse(
|
|
7572
|
+
const parsed = JSON.parse(readFileSync13(path, "utf-8"));
|
|
7492
7573
|
if (!parsed || typeof parsed !== "object") {
|
|
7493
7574
|
return [];
|
|
7494
7575
|
}
|
|
@@ -7560,14 +7641,14 @@ function resolveGithubCliBinary(projectRoot) {
|
|
|
7560
7641
|
}
|
|
7561
7642
|
const explicitPathEntries = (process.env.PATH || "").split(":").map((entry) => entry.trim()).filter(Boolean);
|
|
7562
7643
|
for (const entry of explicitPathEntries) {
|
|
7563
|
-
candidates.add(
|
|
7644
|
+
candidates.add(resolve24(entry, "gh"));
|
|
7564
7645
|
}
|
|
7565
7646
|
const bunResolved = Bun.which("gh");
|
|
7566
7647
|
if (bunResolved) {
|
|
7567
7648
|
candidates.add(bunResolved);
|
|
7568
7649
|
}
|
|
7569
7650
|
for (const candidate of candidates) {
|
|
7570
|
-
if (candidate &&
|
|
7651
|
+
if (candidate && existsSync22(candidate) && !isRuntimeGatewayGhPath(candidate)) {
|
|
7571
7652
|
return candidate;
|
|
7572
7653
|
}
|
|
7573
7654
|
}
|
|
@@ -7597,7 +7678,7 @@ function resolveRepoNameWithOwner(projectRoot, repoRoot) {
|
|
|
7597
7678
|
return resolveGithubRepoNameWithOwnerFromGitRoot(projectRoot, repoRoot, repoRoot, visited);
|
|
7598
7679
|
}
|
|
7599
7680
|
function resolveGithubRepoNameWithOwnerFromGitRoot(projectRoot, gitRoot, cwd, visited) {
|
|
7600
|
-
const normalizedGitRoot =
|
|
7681
|
+
const normalizedGitRoot = resolve24(gitRoot);
|
|
7601
7682
|
if (visited.has(normalizedGitRoot)) {
|
|
7602
7683
|
return "";
|
|
7603
7684
|
}
|
|
@@ -7669,7 +7750,7 @@ function resolveNetworkRemoteName(projectRoot, repoRoot, repoNameWithOwner) {
|
|
|
7669
7750
|
return remotes.includes("origin") ? "origin" : remotes[0];
|
|
7670
7751
|
}
|
|
7671
7752
|
function gitQuery(projectRoot, gitRoot, cwd, ...args) {
|
|
7672
|
-
const gitArgs =
|
|
7753
|
+
const gitArgs = existsSync22(resolve24(gitRoot, ".git")) ? gitCmd(projectRoot, gitRoot, ...args) : [resolveGitBinary(projectRoot), "--git-dir", gitRoot, ...args];
|
|
7673
7754
|
return runCapture2(gitArgs, cwd, projectRoot);
|
|
7674
7755
|
}
|
|
7675
7756
|
function resolveLocalGitRemoteRoot(remoteUrl, gitRoot) {
|
|
@@ -7687,9 +7768,9 @@ function resolveLocalGitRemoteRoot(remoteUrl, gitRoot) {
|
|
|
7687
7768
|
} else if (/^[a-z][a-z0-9+.-]*:\/\//i.test(normalized) || /^[^@]+@[^:]+:.+$/.test(normalized)) {
|
|
7688
7769
|
return "";
|
|
7689
7770
|
} else if (!isAbsolute2(normalized)) {
|
|
7690
|
-
candidate =
|
|
7771
|
+
candidate = resolve24(gitRoot, normalized);
|
|
7691
7772
|
}
|
|
7692
|
-
return
|
|
7773
|
+
return existsSync22(candidate) ? candidate : "";
|
|
7693
7774
|
}
|
|
7694
7775
|
function normalizeGithubRepoNameWithOwner(value) {
|
|
7695
7776
|
const normalized = value.trim();
|
|
@@ -7816,7 +7897,7 @@ function inferReviewerFromChangedFiles(projectRoot, repoRoot, baseRef, branchRef
|
|
|
7816
7897
|
return best;
|
|
7817
7898
|
}
|
|
7818
7899
|
function commitRepo(projectRoot, repo, label, message, allowEmpty, scoped, files, changedFilesManifest) {
|
|
7819
|
-
if (!
|
|
7900
|
+
if (!existsSync22(resolve24(repo, ".git"))) {
|
|
7820
7901
|
console.log(`Skipping ${label}: repo not available (${repo})`);
|
|
7821
7902
|
return;
|
|
7822
7903
|
}
|
|
@@ -7848,18 +7929,18 @@ function commitRepo(projectRoot, repo, label, message, allowEmpty, scoped, files
|
|
|
7848
7929
|
console.log(`Committed ${label}: ${message}`);
|
|
7849
7930
|
}
|
|
7850
7931
|
function readChangedFilesManifest(projectRoot, taskId) {
|
|
7851
|
-
const manifestPath =
|
|
7852
|
-
if (!
|
|
7932
|
+
const manifestPath = resolve24(artifactDirForId(projectRoot, taskId), "changed-files.txt");
|
|
7933
|
+
if (!existsSync22(manifestPath)) {
|
|
7853
7934
|
return [];
|
|
7854
7935
|
}
|
|
7855
|
-
const files =
|
|
7936
|
+
const files = readFileSync13(manifestPath, "utf-8").split(/\r?\n/).map((line) => normalizeChangedFilePath2(line)).filter(Boolean);
|
|
7856
7937
|
return [...new Set(files)];
|
|
7857
7938
|
}
|
|
7858
7939
|
function refreshChangedFilesManifest(projectRoot, taskId) {
|
|
7859
|
-
const manifestPath =
|
|
7860
|
-
|
|
7940
|
+
const manifestPath = resolve24(artifactDirForId(projectRoot, taskId), "changed-files.txt");
|
|
7941
|
+
mkdirSync13(dirname11(manifestPath), { recursive: true });
|
|
7861
7942
|
const changedFiles = changedFilesForTask(projectRoot, taskId, true);
|
|
7862
|
-
|
|
7943
|
+
writeFileSync13(manifestPath, `${changedFiles.join(`
|
|
7863
7944
|
`)}
|
|
7864
7945
|
`, "utf-8");
|
|
7865
7946
|
return manifestPath;
|
|
@@ -7972,7 +8053,7 @@ function repoHasPathChange(projectRoot, repoRoot, relativePath) {
|
|
|
7972
8053
|
return result.exitCode === 0 && result.stdout.trim().length > 0;
|
|
7973
8054
|
}
|
|
7974
8055
|
function stageExcludePathspecs(repoRoot) {
|
|
7975
|
-
const patterns =
|
|
8056
|
+
const patterns = existsSync22(resolve24(repoRoot, ".rig", "task-config.json")) ? [...TASK_RUNTIME_STAGE_EXCLUDES, ...GENERATED_STAGE_EXCLUDES] : [".rig/**", ...GENERATED_STAGE_EXCLUDES];
|
|
7976
8057
|
return patterns.map((pattern) => `:(glob,exclude)${pattern}`);
|
|
7977
8058
|
}
|
|
7978
8059
|
function pathResolvesBeyondSymlink(repoRoot, relativePath) {
|
|
@@ -7982,7 +8063,7 @@ function pathResolvesBeyondSymlink(repoRoot, relativePath) {
|
|
|
7982
8063
|
}
|
|
7983
8064
|
let current = repoRoot;
|
|
7984
8065
|
for (let index = 0;index < parts.length - 1; index += 1) {
|
|
7985
|
-
current =
|
|
8066
|
+
current = resolve24(current, parts[index]);
|
|
7986
8067
|
try {
|
|
7987
8068
|
if (lstatSync(current).isSymbolicLink()) {
|
|
7988
8069
|
return true;
|
|
@@ -8016,7 +8097,7 @@ function resolveTaskBranchId(projectRoot, taskId) {
|
|
|
8016
8097
|
}
|
|
8017
8098
|
} catch {}
|
|
8018
8099
|
const artifactDir = artifactDirForId(projectRoot, taskId);
|
|
8019
|
-
if (
|
|
8100
|
+
if (existsSync22(artifactDir)) {
|
|
8020
8101
|
return taskId;
|
|
8021
8102
|
}
|
|
8022
8103
|
throw new Error(`Unknown task id: ${taskId}`);
|
|
@@ -8052,11 +8133,11 @@ function runCapture2(command, cwd, projectRoot = cwd) {
|
|
|
8052
8133
|
}
|
|
8053
8134
|
function runtimeGitEnv(projectRoot) {
|
|
8054
8135
|
const { ctx, runtimeRoot } = resolveRuntimeMetadata(projectRoot);
|
|
8055
|
-
const runtimeHome = runtimeRoot ?
|
|
8056
|
-
const runtimeTmp = runtimeRoot ?
|
|
8057
|
-
const runtimeCache = runtimeRoot ?
|
|
8058
|
-
const runtimeKnownHosts = runtimeHome ?
|
|
8059
|
-
const runtimeKey = runtimeHome ?
|
|
8136
|
+
const runtimeHome = runtimeRoot ? resolve24(runtimeRoot, "home") : "";
|
|
8137
|
+
const runtimeTmp = runtimeRoot ? resolve24(runtimeRoot, "tmp") : "";
|
|
8138
|
+
const runtimeCache = runtimeRoot ? resolve24(runtimeRoot, "cache") : "";
|
|
8139
|
+
const runtimeKnownHosts = runtimeHome ? resolve24(runtimeHome, ".ssh", "known_hosts") : "";
|
|
8140
|
+
const runtimeKey = runtimeHome ? resolve24(runtimeHome, ".ssh", "rig-agent-key") : "";
|
|
8060
8141
|
const env = {};
|
|
8061
8142
|
if (ctx?.workspaceDir) {
|
|
8062
8143
|
env.PROJECT_RIG_ROOT = projectRoot;
|
|
@@ -8069,14 +8150,14 @@ function runtimeGitEnv(projectRoot) {
|
|
|
8069
8150
|
if (runtimeRoot) {
|
|
8070
8151
|
env.RIG_RUNTIME_HOME = runtimeRoot;
|
|
8071
8152
|
}
|
|
8072
|
-
if (runtimeHome &&
|
|
8153
|
+
if (runtimeHome && existsSync22(runtimeHome)) {
|
|
8073
8154
|
env.HOME = runtimeHome;
|
|
8074
8155
|
env.OPENSSL_CONF = ensureRuntimeOpenSslConfig(runtimeHome);
|
|
8075
8156
|
}
|
|
8076
|
-
if (runtimeTmp &&
|
|
8157
|
+
if (runtimeTmp && existsSync22(runtimeTmp)) {
|
|
8077
8158
|
env.TMPDIR = runtimeTmp;
|
|
8078
8159
|
}
|
|
8079
|
-
if (runtimeCache &&
|
|
8160
|
+
if (runtimeCache && existsSync22(runtimeCache)) {
|
|
8080
8161
|
env.XDG_CACHE_HOME = runtimeCache;
|
|
8081
8162
|
}
|
|
8082
8163
|
const workspaceSecrets = loadDotEnvSecrets(ctx?.workspaceDir || projectRoot, process.env);
|
|
@@ -8120,14 +8201,14 @@ function runtimeGitEnv(projectRoot) {
|
|
|
8120
8201
|
env.GH_TOKEN = env.GH_TOKEN || gitHubToken;
|
|
8121
8202
|
applyGitHubCredentialHelperEnv(env);
|
|
8122
8203
|
}
|
|
8123
|
-
if (runtimeKnownHosts &&
|
|
8204
|
+
if (runtimeKnownHosts && existsSync22(runtimeKnownHosts)) {
|
|
8124
8205
|
const sshParts = [
|
|
8125
8206
|
"ssh",
|
|
8126
8207
|
`-o UserKnownHostsFile="${runtimeKnownHosts}"`,
|
|
8127
8208
|
"-o StrictHostKeyChecking=yes",
|
|
8128
8209
|
"-F /dev/null"
|
|
8129
8210
|
];
|
|
8130
|
-
if (runtimeKey &&
|
|
8211
|
+
if (runtimeKey && existsSync22(runtimeKey)) {
|
|
8131
8212
|
sshParts.splice(1, 0, `-i "${runtimeKey}"`, "-o IdentitiesOnly=yes");
|
|
8132
8213
|
}
|
|
8133
8214
|
env.GIT_SSH_COMMAND = sshParts.join(" ");
|
|
@@ -8148,12 +8229,12 @@ function loadPersistedRuntimeSecrets(runtimeRoot) {
|
|
|
8148
8229
|
if (!runtimeRoot) {
|
|
8149
8230
|
return {};
|
|
8150
8231
|
}
|
|
8151
|
-
const path =
|
|
8152
|
-
if (!
|
|
8232
|
+
const path = resolve24(runtimeRoot, "runtime-secrets.json");
|
|
8233
|
+
if (!existsSync22(path)) {
|
|
8153
8234
|
return {};
|
|
8154
8235
|
}
|
|
8155
8236
|
try {
|
|
8156
|
-
const parsed = JSON.parse(
|
|
8237
|
+
const parsed = JSON.parse(readFileSync13(path, "utf-8"));
|
|
8157
8238
|
const entries = Object.entries(parsed).filter((entry) => typeof entry[1] === "string");
|
|
8158
8239
|
return Object.fromEntries(entries);
|
|
8159
8240
|
} catch {
|
|
@@ -8161,13 +8242,13 @@ function loadPersistedRuntimeSecrets(runtimeRoot) {
|
|
|
8161
8242
|
}
|
|
8162
8243
|
}
|
|
8163
8244
|
function ensureRuntimeOpenSslConfig(runtimeHome) {
|
|
8164
|
-
const sslDir =
|
|
8165
|
-
const sslConfig =
|
|
8166
|
-
if (!
|
|
8167
|
-
|
|
8245
|
+
const sslDir = resolve24(runtimeHome, ".ssl");
|
|
8246
|
+
const sslConfig = resolve24(sslDir, "openssl.cnf");
|
|
8247
|
+
if (!existsSync22(sslDir)) {
|
|
8248
|
+
mkdirSync13(sslDir, { recursive: true });
|
|
8168
8249
|
}
|
|
8169
|
-
if (!
|
|
8170
|
-
|
|
8250
|
+
if (!existsSync22(sslConfig)) {
|
|
8251
|
+
writeFileSync13(sslConfig, `# Rig runtime OpenSSL config placeholder
|
|
8171
8252
|
`);
|
|
8172
8253
|
}
|
|
8173
8254
|
return sslConfig;
|
|
@@ -8185,29 +8266,29 @@ function resolveRuntimeMetadata(projectRoot) {
|
|
|
8185
8266
|
if (contextFile) {
|
|
8186
8267
|
return {
|
|
8187
8268
|
ctx,
|
|
8188
|
-
runtimeRoot:
|
|
8269
|
+
runtimeRoot: dirname11(resolve24(contextFile))
|
|
8189
8270
|
};
|
|
8190
8271
|
}
|
|
8191
8272
|
const inferredContextFile = findRuntimeContextFile2(projectRoot);
|
|
8192
|
-
if (
|
|
8273
|
+
if (existsSync22(inferredContextFile)) {
|
|
8193
8274
|
try {
|
|
8194
8275
|
ctx = loadRuntimeContext(inferredContextFile);
|
|
8195
8276
|
} catch {}
|
|
8196
8277
|
return {
|
|
8197
8278
|
ctx,
|
|
8198
|
-
runtimeRoot:
|
|
8279
|
+
runtimeRoot: dirname11(inferredContextFile)
|
|
8199
8280
|
};
|
|
8200
8281
|
}
|
|
8201
8282
|
return { ctx, runtimeRoot: "" };
|
|
8202
8283
|
}
|
|
8203
8284
|
function findRuntimeContextFile2(startPath) {
|
|
8204
|
-
let current =
|
|
8285
|
+
let current = resolve24(startPath);
|
|
8205
8286
|
while (true) {
|
|
8206
|
-
const candidate =
|
|
8207
|
-
if (
|
|
8287
|
+
const candidate = resolve24(current, "runtime-context.json");
|
|
8288
|
+
if (existsSync22(candidate)) {
|
|
8208
8289
|
return candidate;
|
|
8209
8290
|
}
|
|
8210
|
-
const parent =
|
|
8291
|
+
const parent = dirname11(current);
|
|
8211
8292
|
if (parent === current) {
|
|
8212
8293
|
return "";
|
|
8213
8294
|
}
|
|
@@ -8443,9 +8524,9 @@ async function main() {
|
|
|
8443
8524
|
console.log(`
|
|
8444
8525
|
[post] Auto-merge: skipped (not in policy completion.checks)`);
|
|
8445
8526
|
}
|
|
8446
|
-
const artifactDir =
|
|
8447
|
-
|
|
8448
|
-
|
|
8527
|
+
const artifactDir = resolve25(paths.artifactsDir, taskId);
|
|
8528
|
+
mkdirSync14(artifactDir, { recursive: true });
|
|
8529
|
+
writeFileSync14(resolve25(artifactDir, "review-status.txt"), failed ? `REJECTED
|
|
8449
8530
|
` : `APPROVED
|
|
8450
8531
|
`, "utf-8");
|
|
8451
8532
|
if (!failed) {
|
|
@@ -8492,8 +8573,8 @@ async function runBunTool(args, cwd) {
|
|
|
8492
8573
|
};
|
|
8493
8574
|
}
|
|
8494
8575
|
async function runProtoQualityGate(monorepoRoot) {
|
|
8495
|
-
const protosDir =
|
|
8496
|
-
if (!
|
|
8576
|
+
const protosDir = resolve25(monorepoRoot, "packages", "protos");
|
|
8577
|
+
if (!existsSync23(protosDir)) {
|
|
8497
8578
|
console.log(`FAIL: Proto workspace not found at ${protosDir}`);
|
|
8498
8579
|
return false;
|
|
8499
8580
|
}
|
|
@@ -8541,12 +8622,12 @@ async function runProtoQualityGate(monorepoRoot) {
|
|
|
8541
8622
|
} else {
|
|
8542
8623
|
console.log("OK: Generated TypeScript compiles");
|
|
8543
8624
|
}
|
|
8544
|
-
const workflowPath =
|
|
8545
|
-
if (!
|
|
8625
|
+
const workflowPath = resolve25(monorepoRoot, ".github", "workflows", "pull-request-gate.yml");
|
|
8626
|
+
if (!existsSync23(workflowPath)) {
|
|
8546
8627
|
console.log(`FAIL: Missing workflow gate file at ${workflowPath}`);
|
|
8547
8628
|
ok = false;
|
|
8548
8629
|
} else {
|
|
8549
|
-
const workflow =
|
|
8630
|
+
const workflow = readFileSync14(workflowPath, "utf-8");
|
|
8550
8631
|
if (workflow.includes("if: false && needs.detect.outputs.protos_changed == 'true'")) {
|
|
8551
8632
|
console.log("FAIL: Proto quality CI gate is disabled in pull-request-gate.yml");
|
|
8552
8633
|
ok = false;
|
|
@@ -8575,7 +8656,7 @@ function repoHasPublishedTaskBranch(projectRoot, repoRoot, taskId) {
|
|
|
8575
8656
|
return runCapture(["git", "-C", repoRoot, "ls-remote", "--exit-code", "origin", `refs/heads/${branchRef}`], projectRoot).exitCode === 0;
|
|
8576
8657
|
}
|
|
8577
8658
|
async function readJsonFileIfPresent(path) {
|
|
8578
|
-
if (!
|
|
8659
|
+
if (!existsSync23(path)) {
|
|
8579
8660
|
return null;
|
|
8580
8661
|
}
|
|
8581
8662
|
try {
|
|
@@ -8586,9 +8667,9 @@ async function readJsonFileIfPresent(path) {
|
|
|
8586
8667
|
}
|
|
8587
8668
|
async function recordVerifierFailure(projectRoot, taskId, paths) {
|
|
8588
8669
|
const failedApproachesPath = paths.failedApproachesPath;
|
|
8589
|
-
const artifactDir =
|
|
8590
|
-
const reviewStatePath =
|
|
8591
|
-
const reviewFeedbackPath =
|
|
8670
|
+
const artifactDir = resolve25(paths.artifactsDir, taskId);
|
|
8671
|
+
const reviewStatePath = resolve25(artifactDir, "review-state.json");
|
|
8672
|
+
const reviewFeedbackPath = resolve25(artifactDir, "review-feedback.md");
|
|
8592
8673
|
let summary = "Verifier rejected completion. Read review-feedback.md for required fixes.";
|
|
8593
8674
|
const parsedReviewState = await readJsonFileIfPresent(reviewStatePath);
|
|
8594
8675
|
if (parsedReviewState) {
|
|
@@ -8598,12 +8679,12 @@ async function recordVerifierFailure(projectRoot, taskId, paths) {
|
|
|
8598
8679
|
}
|
|
8599
8680
|
}
|
|
8600
8681
|
let attempts = 1;
|
|
8601
|
-
if (
|
|
8602
|
-
const content =
|
|
8682
|
+
if (existsSync23(failedApproachesPath)) {
|
|
8683
|
+
const content = readFileSync14(failedApproachesPath, "utf-8");
|
|
8603
8684
|
attempts = (content.match(new RegExp(`^## ${escapeRegExp2(taskId)}\\b`, "gm")) || []).length + 1;
|
|
8604
8685
|
} else {
|
|
8605
|
-
|
|
8606
|
-
|
|
8686
|
+
mkdirSync14(resolve25(failedApproachesPath, ".."), { recursive: true });
|
|
8687
|
+
writeFileSync14(failedApproachesPath, `# Failed Approaches
|
|
8607
8688
|
|
|
8608
8689
|
`, "utf-8");
|
|
8609
8690
|
}
|
|
@@ -8641,8 +8722,8 @@ async function recordTaskRepoCommits(projectRoot, taskId, paths) {
|
|
|
8641
8722
|
recorded_at: new Date().toISOString(),
|
|
8642
8723
|
repos
|
|
8643
8724
|
};
|
|
8644
|
-
|
|
8645
|
-
|
|
8725
|
+
mkdirSync14(resolve25(statePath, ".."), { recursive: true });
|
|
8726
|
+
writeFileSync14(statePath, `${JSON.stringify(state, null, 2)}
|
|
8646
8727
|
`, "utf-8");
|
|
8647
8728
|
}
|
|
8648
8729
|
}
|