@miraland-labs/conduit-bridge 0.16.147 → 0.16.148
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/ensure-test-evidence.js +38 -8
- package/dist/execution.js +3 -1
- package/package.json +1 -1
|
@@ -208,7 +208,7 @@ export async function captureVerificationFailure(input) {
|
|
|
208
208
|
// Same rule as the pre-delivery gate: this is the diagnosis a repair brief is written from, so
|
|
209
209
|
// it must keep the end. It carried its own head-truncating copy of that logic until the two were
|
|
210
210
|
// shown to be the same bug.
|
|
211
|
-
const text = boundedFailureDetail(command,
|
|
211
|
+
const text = boundedFailureDetail(command, outputLines(result), result.code, DIAGNOSIS_DETAIL_MAX_CHARS);
|
|
212
212
|
if (text.trim())
|
|
213
213
|
return { command, detail: text };
|
|
214
214
|
}
|
|
@@ -263,6 +263,14 @@ export function boundedTail(text, maxChars) {
|
|
|
263
263
|
return text.slice(text.length - maxChars);
|
|
264
264
|
return `${text.slice(0, head)}${marker}${text.slice(text.length - tail)}`;
|
|
265
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* The lines of a run in the order the command wrote them. A run without the combined output (a
|
|
268
|
+
* test's fake runner) reads as stdout followed by stderr.
|
|
269
|
+
*/
|
|
270
|
+
function outputLines(result) {
|
|
271
|
+
const split = (text) => (text.trim() ? text.trim().split("\n") : []);
|
|
272
|
+
return result.output === undefined ? [...split(result.stdout), ...split(result.stderr)] : split(result.output);
|
|
273
|
+
}
|
|
266
274
|
/**
|
|
267
275
|
* A failure detail that keeps the part which explains the failure.
|
|
268
276
|
*
|
|
@@ -275,10 +283,13 @@ export function boundedTail(text, maxChars) {
|
|
|
275
283
|
* So: the command line for orientation, a few opening lines, then the **tail**, and the exit code
|
|
276
284
|
* always. What was dropped is stated rather than silently vanishing, because a reader must be able
|
|
277
285
|
* to tell a short failure from a truncated one.
|
|
286
|
+
*
|
|
287
|
+
* The lines are in the order the command wrote them. Joining all stdout before all stderr made the
|
|
288
|
+
* tail of a failed `cargo test` its stderr `Running …` lines and elided the panic on stdout.
|
|
278
289
|
*/
|
|
279
|
-
export function boundedFailureDetail(command,
|
|
290
|
+
export function boundedFailureDetail(command, lines, code, maxChars = FAILURE_DETAIL_MAX_CHARS) {
|
|
280
291
|
const cap = (line) => (line.length > 500 ? `${line.slice(0, 500)}…` : line);
|
|
281
|
-
const body =
|
|
292
|
+
const body = lines.map(cap);
|
|
282
293
|
const first = `$ ${command}`;
|
|
283
294
|
const last = `exit ${code}`;
|
|
284
295
|
const marker = (dropped) => `… ${dropped} line${dropped === 1 ? "" : "s"} omitted …`;
|
|
@@ -322,20 +333,36 @@ export function boundedFailureDetail(command, stdoutLines, stderrLines, code, ma
|
|
|
322
333
|
*
|
|
323
334
|
* A gate the timeout stopped did not exit: `timedOut` says so, and stderr ends with the limit it
|
|
324
335
|
* reached. Read as exit 1, a gate that hung on the untouched base was a red base on the author.
|
|
336
|
+
*
|
|
337
|
+
* `output` is stdout and stderr together, in the order the lines arrived: a failure detail reads its
|
|
338
|
+
* tail from it.
|
|
325
339
|
*/
|
|
326
340
|
export async function runBoundedVerificationCommand(command, workspace, timeoutMs = VERIFICATION_TIMEOUT_MS) {
|
|
327
341
|
const argv = argvForBoundedCommand(command);
|
|
328
342
|
const bin = argv[0];
|
|
329
343
|
if (!bin)
|
|
330
344
|
throw new Error("Verification command is empty");
|
|
345
|
+
// Chunks are not whole lines. Each stream keeps its partial last line until the line ends, so a
|
|
346
|
+
// stderr write between two stdout chunks cannot split a stdout line.
|
|
347
|
+
const lines = [];
|
|
348
|
+
const partial = { stdout: "", stderr: "" };
|
|
349
|
+
const collect = (stream) => (chunk) => {
|
|
350
|
+
const parts = `${partial[stream]}${String(chunk)}`.split("\n");
|
|
351
|
+
partial[stream] = parts.pop() ?? "";
|
|
352
|
+
lines.push(...parts);
|
|
353
|
+
};
|
|
354
|
+
const output = () => [...lines, ...[partial.stdout, partial.stderr].filter(Boolean)].join("\n");
|
|
331
355
|
try {
|
|
332
|
-
const
|
|
356
|
+
const pending = execFileAsync(bin, argv.slice(1), {
|
|
333
357
|
cwd: workspace,
|
|
334
358
|
timeout: timeoutMs,
|
|
335
359
|
maxBuffer: VERIFICATION_MAX_BUFFER_BYTES,
|
|
336
360
|
env: process.env,
|
|
337
361
|
});
|
|
338
|
-
|
|
362
|
+
pending.child.stdout?.on("data", collect("stdout"));
|
|
363
|
+
pending.child.stderr?.on("data", collect("stderr"));
|
|
364
|
+
const { stdout, stderr } = await pending;
|
|
365
|
+
return { stdout: String(stdout), stderr: String(stderr), output: output(), code: 0 };
|
|
339
366
|
}
|
|
340
367
|
catch (error) {
|
|
341
368
|
const err = error;
|
|
@@ -347,9 +374,11 @@ export async function runBoundedVerificationCommand(command, workspace, timeoutM
|
|
|
347
374
|
if (typeof err.code === "string")
|
|
348
375
|
throw error;
|
|
349
376
|
if (err.killed === true) {
|
|
377
|
+
const stopped = `Bridge stopped the gate: it did not exit within ${Math.round(timeoutMs / 1_000)} seconds.`;
|
|
350
378
|
return {
|
|
351
379
|
stdout: String(err.stdout ?? ""),
|
|
352
|
-
stderr: `${String(err.stderr ?? "")}\
|
|
380
|
+
stderr: `${String(err.stderr ?? "")}\n${stopped}`.trimStart(),
|
|
381
|
+
output: `${output()}\n${stopped}`.trimStart(),
|
|
353
382
|
code: 1,
|
|
354
383
|
timedOut: true,
|
|
355
384
|
};
|
|
@@ -358,6 +387,7 @@ export async function runBoundedVerificationCommand(command, workspace, timeoutM
|
|
|
358
387
|
return {
|
|
359
388
|
stdout: String(err.stdout ?? ""),
|
|
360
389
|
stderr,
|
|
390
|
+
output: output() || stderr,
|
|
361
391
|
code: typeof err.code === "number" ? err.code : 1,
|
|
362
392
|
};
|
|
363
393
|
}
|
|
@@ -536,12 +566,12 @@ export async function ensureTestEvidence(input) {
|
|
|
536
566
|
// and would otherwise be witnessed as a passing test run that tested nothing.
|
|
537
567
|
const ranNothing = detectGateRanNothing(command, result.stdout, result.stderr, result.code);
|
|
538
568
|
if (ranNothing) {
|
|
539
|
-
const detail = boundedFailureDetail(command,
|
|
569
|
+
const detail = boundedFailureDetail(command, outputLines(result), result.code);
|
|
540
570
|
throw new VerificationGateRanNothingError(command, result.code, ranNothing, detail);
|
|
541
571
|
}
|
|
542
572
|
if (result.code !== 0) {
|
|
543
573
|
// Prefix must match FINALIZE_CONTRACT_PATTERN ("Agent report") — not Bridge-fault laundering.
|
|
544
|
-
const detail = boundedFailureDetail(command,
|
|
574
|
+
const detail = boundedFailureDetail(command, outputLines(result), result.code);
|
|
545
575
|
// One line an operator can grep when a stored failure looks head-truncated. Reading the shape
|
|
546
576
|
// back out of the database could not distinguish "the fix did not run" from "something
|
|
547
577
|
// downstream re-truncated it"; this says which, at the moment it is produced.
|
package/dist/execution.js
CHANGED
|
@@ -526,7 +526,9 @@ export function buildDiagnosticPrompt(input) {
|
|
|
526
526
|
...(decisions.length ? ["", `OWNER DECISIONS\n${decisions.map((item) => `- ${item.question}: ${item.decision}`).join("\n")}`] : []),
|
|
527
527
|
...(input.verificationCommands.length ? ["", `BOUNDED VERIFICATION COMMANDS\n${input.verificationCommands.map((item) => `- ${item}`).join("\n")}`] : []),
|
|
528
528
|
...(gate ? ["", `GATE — Bridge re-runs \`${gate}\` after the next run; the agent cannot change or replace it.`
|
|
529
|
-
+ "
|
|
529
|
+
+ " A fix that needs a file outside the APPROVED CHANGE SCOPE is still a grounded diagnosis."
|
|
530
|
+
+ " Answer confident:true and write the full repository path of each such file in fix_direction. Conductor makes it a scope decision."
|
|
531
|
+
+ " Answer confident:false only when no file change can fix the cause (the gate itself, the environment), and name the gate in root_cause."] : []),
|
|
530
532
|
"",
|
|
531
533
|
"WITNESSED FAILURE OUTPUT",
|
|
532
534
|
redactSecrets(input.failureOutput),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miraland-labs/conduit-bridge",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.148",
|
|
4
4
|
"description": "Conduit Bridge CLI \u2014 join, connect, disconnect, multi-driver lanes, and run Claude Code / Codex / Cursor / OpenCode / Pi / Kiro / Antigravity / Grok Build agents for a Conduit organization",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|