@smartergpt/lexrunner 1.2.1 → 1.3.0
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/CHANGELOG.md +25 -0
- package/README.mcp.md +79 -8
- package/README.md +12 -6
- package/dist/{adapters-DciX_uRh.d.ts → adapters-r7_TjMwo.d.ts} +2 -0
- package/dist/{autopilot-APNRAJTS.js → autopilot-JXO4MZ6H.js} +3 -3
- package/dist/{chunk-L7YDEIPO.js → chunk-26SCN2LJ.js} +17 -3
- package/dist/{chunk-6TB2UCBZ.js → chunk-VAGHAQT2.js} +1 -1
- package/dist/{chunk-6HASN2LZ.js → chunk-WSJEPY7T.js} +84 -35
- package/dist/cli.cjs +9120 -4362
- package/dist/cli.d.ts +2161 -130
- package/dist/cli.js +12769 -8111
- package/dist/{commandValidator-VHBB2H3K.js → commandValidator-LVEMJTZP.js} +1 -1
- package/dist/errors/index.cjs +17 -3
- package/dist/errors/index.d.ts +7 -1
- package/dist/errors/index.js +1 -1
- package/dist/{planReview-LQM6AUBE.js → planReview-UUMCGZJ6.js} +2 -2
- package/dist/{planViewer-57DTN5YT.js → planViewer-TRSQVMH4.js} +2 -2
- package/dist/{security-BKLPEVOC.js → security-OE4VLUFK.js} +1 -1
- package/mcp-server.mjs +119 -64
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,31 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.3.0] - 2026-07-30
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Windows-to-native-WSL Attempt projection** - Added a read-only containment preflight,
|
|
14
|
+
versioned projection and path-mapping contracts, rollback-safe native provisioning, and
|
|
15
|
+
identity-bound launch, worker, receipt, verification, acceptance, and cleanup behavior.
|
|
16
|
+
- **Shared projection lifecycle** - Added matching CLI
|
|
17
|
+
`attempt projection prepare|status|cleanup|quarantine` operations and MCP lifecycle tools with
|
|
18
|
+
explicit mutation authority, path-private diagnostics, bounded quarantine inspection, and a
|
|
19
|
+
recovery runbook.
|
|
20
|
+
- **Independent merge review gate** - Documented a mandatory-by-default, exact-head review step
|
|
21
|
+
whose verdict is invalidated by candidate changes and whose opt-out requires an explicit warned
|
|
22
|
+
`review_bypassed` receipt.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- **Plan validation diagnostics** - Return the individual validation failures instead of only an
|
|
27
|
+
aggregate count, preserving the dogfood evidence needed to repair invalid plans.
|
|
28
|
+
- **Explicit plan references** - Resolve explicitly selected plan artifacts consistently across
|
|
29
|
+
MCP status, ordering, and gate operations.
|
|
30
|
+
- **Projection durability and recovery** - Fail closed across selection revocation/publication,
|
|
31
|
+
staging ownership, quarantine marker persistence, directory sync failures, source refresh, and
|
|
32
|
+
active-worktree cleanup refusal.
|
|
33
|
+
|
|
9
34
|
---
|
|
10
35
|
|
|
11
36
|
## [1.2.1] - 2026-07-21
|
package/README.mcp.md
CHANGED
|
@@ -113,6 +113,40 @@ This MCP server follows the same architectural pattern as LexBrain and LexMap:
|
|
|
113
113
|
|
|
114
114
|
## Available Tools
|
|
115
115
|
|
|
116
|
+
### preflight_attempt_containment
|
|
117
|
+
|
|
118
|
+
Checks whether the current runtime and declared repository/worktree roots can host LexRunner's
|
|
119
|
+
physical directory-identity boundary before an Attempt packet is constructed. This tool is always
|
|
120
|
+
read-only and does not require `ALLOW_MUTATIONS`.
|
|
121
|
+
|
|
122
|
+
It returns `native_ready`, `broker_required`, or `unsupported` with stable reason codes, bounded
|
|
123
|
+
next actions, verification depth for each root, and a privacy-safe binding digest. Native Windows
|
|
124
|
+
and WSL DrvFS/9P inputs recommend a native-WSL projection without weakening the containment
|
|
125
|
+
boundary.
|
|
126
|
+
|
|
127
|
+
### Native WSL projection lifecycle
|
|
128
|
+
|
|
129
|
+
Four tools share one bounded lifecycle service with the matching
|
|
130
|
+
`lex-pr attempt projection <operation> --input <file|-> --json` commands:
|
|
131
|
+
|
|
132
|
+
- `get_native_wsl_projection_status` reads exact lifecycle state without creating control, lock,
|
|
133
|
+
projection, or SQLite state.
|
|
134
|
+
- `inspect_native_wsl_projection_quarantine` reads privacy-bounded quarantine counts and entry
|
|
135
|
+
digests.
|
|
136
|
+
- `prepare_native_wsl_projection` prepares or exactly reuses the requested committed base.
|
|
137
|
+
- `cleanup_native_wsl_projection` removes an exact idle projection and its matching quarantine
|
|
138
|
+
state; active worktrees are refused.
|
|
139
|
+
|
|
140
|
+
Prepare and cleanup require both `"mutation": {"authorized": true}` in the input and the MCP
|
|
141
|
+
server mutation gate. Status and quarantine inspection remain available when mutations are
|
|
142
|
+
disabled. All results omit source/native paths and expose stable digests, reason codes, bounded
|
|
143
|
+
command evidence, counts, and next actions.
|
|
144
|
+
|
|
145
|
+
On success, pass the returned `selectionDigest` to projected `attempt prepare`. Launch resolves
|
|
146
|
+
the engine-authored selection, emits the machine-verifiable execution path mapping, and preserves
|
|
147
|
+
the request's exact `base_sha` even when the Windows source HEAD or dirty state later changes.
|
|
148
|
+
See the [native Windows-to-WSL projection workflow](docs/workflows/native-wsl-projection.md).
|
|
149
|
+
|
|
116
150
|
### mcp_lexrunner_plan_create
|
|
117
151
|
|
|
118
152
|
Creates a plan from configuration files or auto-discovers from GitHub PRs. This is a **convenience wrapper** that combines PR discovery (`pr_list`), plan generation, validation, and file writing into a single operation.
|
|
@@ -428,11 +462,21 @@ Executes gates for plan items. Can work with either an internal plan (created vi
|
|
|
428
462
|
|
|
429
463
|
> **Deprecated alias:** `gates.run`
|
|
430
464
|
|
|
465
|
+
All overlapping integration tools use the same plan-reference precedence:
|
|
466
|
+
|
|
467
|
+
1. `planFile`, when supplied. The explicit reference is authoritative and never falls through.
|
|
468
|
+
2. `plan.json` in the repository root.
|
|
469
|
+
3. `<profile>/runner/plan.json` as the backward-compatible `plan_create` fallback.
|
|
470
|
+
|
|
471
|
+
Their results include the same path-independent `planArtifact` identity. Compare its canonical
|
|
472
|
+
`digest` to verify that status, merge ordering, gate execution, and merge preview consumed the
|
|
473
|
+
same frozen plan.
|
|
474
|
+
|
|
431
475
|
**Parameters:**
|
|
432
476
|
|
|
433
|
-
- `planFile` (string, optional):
|
|
434
|
-
- `onlyItem` (string, optional):
|
|
435
|
-
- `onlyGate` (string, optional):
|
|
477
|
+
- `planFile` (string, optional): Explicit path to an authored plan
|
|
478
|
+
- `onlyItem` (string, optional): Execute gates for this item only
|
|
479
|
+
- `onlyGate` (string, optional): Execute this gate only
|
|
436
480
|
- `outDir` (string, optional): Output directory for gate results
|
|
437
481
|
|
|
438
482
|
**Returns:**
|
|
@@ -451,11 +495,20 @@ Executes gates for plan items. Can work with either an internal plan (created vi
|
|
|
451
495
|
]
|
|
452
496
|
}
|
|
453
497
|
],
|
|
454
|
-
"allGreen": true
|
|
498
|
+
"allGreen": true,
|
|
499
|
+
"planArtifact": {
|
|
500
|
+
"contract": "plan-artifact-identity-v1",
|
|
501
|
+
"kind": "execution-plan",
|
|
502
|
+
"schema": "lexrunner.execution-plan",
|
|
503
|
+
"schemaVersion": "1.0.0",
|
|
504
|
+
"digest": "sha256:...",
|
|
505
|
+
"target": "main",
|
|
506
|
+
"itemCount": 1
|
|
507
|
+
}
|
|
455
508
|
}
|
|
456
509
|
```
|
|
457
510
|
|
|
458
|
-
**Example (using
|
|
511
|
+
**Example (using repository or profile fallback):**
|
|
459
512
|
|
|
460
513
|
```json
|
|
461
514
|
{
|
|
@@ -481,7 +534,8 @@ Executes gates for plan items. Can work with either an internal plan (created vi
|
|
|
481
534
|
|
|
482
535
|
**Use Cases:**
|
|
483
536
|
|
|
484
|
-
- **
|
|
537
|
+
- **Authored plan**: Run gates directly from a repository-root or explicitly referenced plan
|
|
538
|
+
- **Profile fallback**: Continue using a plan created via `mcp_lexrunner_plan_create`
|
|
485
539
|
- **External orchestration**: Run gates on programmatically-created or externally-managed plan files
|
|
486
540
|
- **Parallel workflows**: Execute gates on multiple independent plans in parallel merge-weave operations
|
|
487
541
|
|
|
@@ -493,14 +547,29 @@ Applies merge operations with environment-based gating.
|
|
|
493
547
|
|
|
494
548
|
**Parameters:**
|
|
495
549
|
|
|
550
|
+
- `planFile` (string, optional): Explicit path to an authored plan, using the same fallback precedence as gate execution
|
|
496
551
|
- `dryRun` (boolean, optional): Simulate merge without making changes (default: `true`)
|
|
497
552
|
|
|
498
553
|
**Returns:**
|
|
499
554
|
|
|
500
555
|
```json
|
|
501
556
|
{
|
|
502
|
-
"
|
|
503
|
-
"
|
|
557
|
+
"mode": "dry-run",
|
|
558
|
+
"dryRun": true,
|
|
559
|
+
"ok": true,
|
|
560
|
+
"status": "preview",
|
|
561
|
+
"totalItems": 1,
|
|
562
|
+
"levels": [["item1"]],
|
|
563
|
+
"artifactRefs": [],
|
|
564
|
+
"planArtifact": {
|
|
565
|
+
"contract": "plan-artifact-identity-v1",
|
|
566
|
+
"kind": "execution-plan",
|
|
567
|
+
"schema": "lexrunner.execution-plan",
|
|
568
|
+
"schemaVersion": "1.0.0",
|
|
569
|
+
"digest": "sha256:...",
|
|
570
|
+
"target": "main",
|
|
571
|
+
"itemCount": 1
|
|
572
|
+
}
|
|
504
573
|
}
|
|
505
574
|
```
|
|
506
575
|
|
|
@@ -510,6 +579,7 @@ Applies merge operations with environment-based gating.
|
|
|
510
579
|
{
|
|
511
580
|
"name": "mcp_lexrunner_weave_apply",
|
|
512
581
|
"arguments": {
|
|
582
|
+
"planFile": "plan.json",
|
|
513
583
|
"dryRun": true
|
|
514
584
|
}
|
|
515
585
|
}
|
|
@@ -580,6 +650,7 @@ const externalGatesResult = await client.callTool("mcp_lexrunner_gate_run", {
|
|
|
580
650
|
|
|
581
651
|
// Check merge eligibility (dry run)
|
|
582
652
|
const mergeResult = await client.callTool("mcp_lexrunner_weave_apply", {
|
|
653
|
+
planFile: "/tmp/merge-batch/plan.json",
|
|
583
654
|
dryRun: true,
|
|
584
655
|
});
|
|
585
656
|
```
|
package/README.md
CHANGED
|
@@ -111,13 +111,14 @@ The checked-in package version is the single source for `lex-pr --version`.
|
|
|
111
111
|
|
|
112
112
|
<!-- BEGIN GENERATED PACKAGE VERSION -->
|
|
113
113
|
|
|
114
|
-
Current repository package version: **1.
|
|
114
|
+
Current repository package version: **1.3.0**. npm availability and dist-tags are separate
|
|
115
115
|
release evidence; inspect the registry rather than inferring publication from source metadata.
|
|
116
116
|
<!-- END GENERATED PACKAGE VERSION -->
|
|
117
117
|
|
|
118
|
-
See the [1.
|
|
119
|
-
[1.2.
|
|
120
|
-
|
|
118
|
+
See the [1.3.0 release notes](docs/releases/1.3.0.md), the
|
|
119
|
+
[1.2.1 publication repair](docs/releases/1.2.1.md), and the underlying
|
|
120
|
+
[1.2.0 compatibility decision](docs/releases/1.2.0.md) for package disposition, semver rationale,
|
|
121
|
+
supported assisted behavior, and deferred guarantees.
|
|
121
122
|
|
|
122
123
|
## Choose a surface
|
|
123
124
|
|
|
@@ -125,8 +126,13 @@ rationale, supported assisted behavior, and deferred headless guarantees.
|
|
|
125
126
|
`weave merge-order`, and `gate run`.
|
|
126
127
|
- **Agent/MCP:** use the matching canonical tools and bounded contracts in
|
|
127
128
|
[`docs/AX.md`](docs/AX.md) and [`README.mcp.md`](README.mcp.md).
|
|
128
|
-
- **Assisted agent work:**
|
|
129
|
-
|
|
129
|
+
- **Assisted agent work:** run `attempt preflight` before packet construction. When it reports
|
|
130
|
+
`broker_required`, use `attempt projection status|prepare` to bind the exact committed base into
|
|
131
|
+
native WSL, then continue with `attempt prepare`, `attempt start`, worker
|
|
132
|
+
attachment/heartbeat, receipt submission, verification, and acceptance. The foreground host
|
|
133
|
+
still owns worker launch. Follow the
|
|
134
|
+
[native Windows-to-WSL projection workflow](docs/workflows/native-wsl-projection.md) for recovery
|
|
135
|
+
and cleanup.
|
|
130
136
|
- **Advanced operators:** read the
|
|
131
137
|
[orchestration primitives](docs/architecture/orchestration-primitives.md),
|
|
132
138
|
[headless supervisor boundary](docs/architecture/headless-supervisor.md), and
|
|
@@ -156,6 +156,8 @@ declare function gitOperationError(ctx: GitOperationContext): AXError;
|
|
|
156
156
|
interface PlanValidationContext {
|
|
157
157
|
errors: string[];
|
|
158
158
|
planPath?: string;
|
|
159
|
+
errorCount?: number;
|
|
160
|
+
errorsTruncated?: boolean;
|
|
159
161
|
}
|
|
160
162
|
/**
|
|
161
163
|
* Create an AXError for plan validation failures
|
|
@@ -16,12 +16,12 @@ import {
|
|
|
16
16
|
hasCapability,
|
|
17
17
|
parseAutopilotConfig,
|
|
18
18
|
validateAutopilotConfig
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-WSJEPY7T.js";
|
|
20
20
|
import "./chunk-4FFGNTAV.js";
|
|
21
21
|
import "./chunk-6A4IE3TI.js";
|
|
22
|
-
import "./chunk-
|
|
22
|
+
import "./chunk-VAGHAQT2.js";
|
|
23
23
|
import "./chunk-UHFMFO54.js";
|
|
24
|
-
import "./chunk-
|
|
24
|
+
import "./chunk-26SCN2LJ.js";
|
|
25
25
|
import "./chunk-TPVORFSN.js";
|
|
26
26
|
import "./chunk-55VWJYZY.js";
|
|
27
27
|
import "./chunk-AXOAVRTU.js";
|
|
@@ -148,15 +148,23 @@ function gitOperationError(ctx) {
|
|
|
148
148
|
}
|
|
149
149
|
function planValidationError(ctx) {
|
|
150
150
|
const nextActions = ["Review the plan file for schema violations"];
|
|
151
|
+
const errorCount = ctx.errorCount ?? ctx.errors.length;
|
|
151
152
|
if (ctx.planPath) {
|
|
152
153
|
nextActions.push(`Plan file: ${ctx.planPath}`);
|
|
153
154
|
}
|
|
154
|
-
|
|
155
|
+
if (ctx.errors.length > 0) {
|
|
156
|
+
nextActions.push(`Errors: ${ctx.errors.join("; ")}`);
|
|
157
|
+
}
|
|
158
|
+
if (ctx.errorsTruncated) {
|
|
159
|
+
nextActions.push(
|
|
160
|
+
`Only the first ${ctx.errors.length} of ${errorCount} validation errors are included`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
155
163
|
return createAXError(
|
|
156
164
|
ErrorCodes.PLAN_VALIDATION_FAILED,
|
|
157
|
-
`Plan validation failed with ${
|
|
165
|
+
`Plan validation failed with ${errorCount} error(s)`,
|
|
158
166
|
nextActions,
|
|
159
|
-
{ ...ctx }
|
|
167
|
+
{ ...ctx, errorCount, errorsTruncated: ctx.errorsTruncated ?? false }
|
|
160
168
|
);
|
|
161
169
|
}
|
|
162
170
|
function toAXError(error, code = ErrorCodes.INTERNAL_ERROR, nextActions) {
|
|
@@ -503,6 +511,12 @@ var ErrorCodes = {
|
|
|
503
511
|
PLAN_VALIDATION_FAILED: "PLAN_VALIDATION_FAILED",
|
|
504
512
|
/** Plan file not found */
|
|
505
513
|
PLAN_NOT_FOUND: "PLAN_NOT_FOUND",
|
|
514
|
+
/** Explicit plan reference is empty, overlong, or otherwise invalid */
|
|
515
|
+
PLAN_REFERENCE_INVALID: "PLAN_REFERENCE_INVALID",
|
|
516
|
+
/** Plan reference exists but cannot be read as a bounded regular file */
|
|
517
|
+
PLAN_UNREADABLE: "PLAN_UNREADABLE",
|
|
518
|
+
/** Plan exceeds the bounded integration artifact limits */
|
|
519
|
+
PLAN_ARTIFACT_LIMIT_EXCEEDED: "PLAN_ARTIFACT_LIMIT_EXCEEDED",
|
|
506
520
|
/** Cycle detected in plan item dependencies */
|
|
507
521
|
PLAN_CYCLE_DETECTED: "PLAN_CYCLE_DETECTED",
|
|
508
522
|
/** Referenced dependency not found in plan */
|
|
@@ -8,14 +8,14 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
MemoryMonitor,
|
|
10
10
|
computeMergeOrder
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-VAGHAQT2.js";
|
|
12
12
|
import {
|
|
13
13
|
canonicalJSONStringify
|
|
14
14
|
} from "./chunk-UHFMFO54.js";
|
|
15
15
|
import {
|
|
16
16
|
securitySarifParseError,
|
|
17
17
|
securityScanFailedError
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-26SCN2LJ.js";
|
|
19
19
|
import {
|
|
20
20
|
emitGateFrame2 as emitGateFrame
|
|
21
21
|
} from "./chunk-TPVORFSN.js";
|
|
@@ -3204,7 +3204,7 @@ function runTaskkill(pid) {
|
|
|
3204
3204
|
}
|
|
3205
3205
|
|
|
3206
3206
|
// src/gates.ts
|
|
3207
|
-
async function executeGate(gate, policy, artifactDir, timeoutMs = 3e4, itemName, skipValidation = false, repoRoot, turnCostTracker) {
|
|
3207
|
+
async function executeGate(gate, policy, artifactDir, timeoutMs = 3e4, itemName, skipValidation = false, repoRoot, turnCostTracker, suppressStdout = false) {
|
|
3208
3208
|
if (!skipValidation && gate.input) {
|
|
3209
3209
|
try {
|
|
3210
3210
|
validateGateInput(gate.name, gate.input);
|
|
@@ -3229,9 +3229,11 @@ async function executeGate(gate, policy, artifactDir, timeoutMs = 3e4, itemName,
|
|
|
3229
3229
|
for (let attempt = 1; attempt <= retryConfig.maxAttempts; attempt++) {
|
|
3230
3230
|
if (attempt > 1 && retryConfig.backoffSeconds > 0) {
|
|
3231
3231
|
const delayMs = retryConfig.backoffSeconds * 1e3;
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3232
|
+
if (!suppressStdout) {
|
|
3233
|
+
console.log(
|
|
3234
|
+
`\u23F3 Retrying gate '${gate.name}' (attempt ${attempt}/${retryConfig.maxAttempts}) after ${retryConfig.backoffSeconds}s delay...`
|
|
3235
|
+
);
|
|
3236
|
+
}
|
|
3235
3237
|
await new Promise((resolve5) => setTimeout(resolve5, delayMs));
|
|
3236
3238
|
}
|
|
3237
3239
|
const result = await executeGateAttempt(gate, artifactDir, attempt, timeoutMs, repoRoot);
|
|
@@ -3258,7 +3260,14 @@ async function executeGate(gate, policy, artifactDir, timeoutMs = 3e4, itemName,
|
|
|
3258
3260
|
console.error(formatErrorForUser(classified));
|
|
3259
3261
|
attemptRecords.push(attemptRecord);
|
|
3260
3262
|
if (itemName && attemptRecords.length > 1) {
|
|
3261
|
-
await writeFlakeReport(
|
|
3263
|
+
await writeFlakeReport(
|
|
3264
|
+
itemName,
|
|
3265
|
+
gate.name,
|
|
3266
|
+
attemptRecords,
|
|
3267
|
+
totalDuration,
|
|
3268
|
+
artifactDir,
|
|
3269
|
+
suppressStdout
|
|
3270
|
+
);
|
|
3262
3271
|
}
|
|
3263
3272
|
return result;
|
|
3264
3273
|
} else if (classified.type === "transient" /* Transient */ && attempt < retryConfig.maxAttempts) {
|
|
@@ -3274,9 +3283,18 @@ async function executeGate(gate, policy, artifactDir, timeoutMs = 3e4, itemName,
|
|
|
3274
3283
|
attemptRecords.push(attemptRecord);
|
|
3275
3284
|
if (result.status === "pass") {
|
|
3276
3285
|
if (attempt > 1) {
|
|
3277
|
-
|
|
3286
|
+
if (!suppressStdout) {
|
|
3287
|
+
console.log(`\u2705 Gate '${gate.name}' succeeded on attempt ${attempt}`);
|
|
3288
|
+
}
|
|
3278
3289
|
if (itemName) {
|
|
3279
|
-
await writeFlakeReport(
|
|
3290
|
+
await writeFlakeReport(
|
|
3291
|
+
itemName,
|
|
3292
|
+
gate.name,
|
|
3293
|
+
attemptRecords,
|
|
3294
|
+
totalDuration,
|
|
3295
|
+
artifactDir,
|
|
3296
|
+
suppressStdout
|
|
3297
|
+
);
|
|
3280
3298
|
}
|
|
3281
3299
|
}
|
|
3282
3300
|
return result;
|
|
@@ -3285,7 +3303,14 @@ async function executeGate(gate, policy, artifactDir, timeoutMs = 3e4, itemName,
|
|
|
3285
3303
|
if (attempt > 1) {
|
|
3286
3304
|
console.error(`\u274C Gate '${gate.name}' failed after ${attempt} attempts`);
|
|
3287
3305
|
if (itemName) {
|
|
3288
|
-
await writeFlakeReport(
|
|
3306
|
+
await writeFlakeReport(
|
|
3307
|
+
itemName,
|
|
3308
|
+
gate.name,
|
|
3309
|
+
attemptRecords,
|
|
3310
|
+
totalDuration,
|
|
3311
|
+
artifactDir,
|
|
3312
|
+
suppressStdout
|
|
3313
|
+
);
|
|
3289
3314
|
}
|
|
3290
3315
|
}
|
|
3291
3316
|
return result;
|
|
@@ -3294,7 +3319,7 @@ async function executeGate(gate, policy, artifactDir, timeoutMs = 3e4, itemName,
|
|
|
3294
3319
|
}
|
|
3295
3320
|
return lastResult;
|
|
3296
3321
|
}
|
|
3297
|
-
async function writeFlakeReport(itemName, gateName, attempts, totalDuration, artifactDir) {
|
|
3322
|
+
async function writeFlakeReport(itemName, gateName, attempts, totalDuration, artifactDir, suppressStdout = false) {
|
|
3298
3323
|
if (attempts.length <= 1) {
|
|
3299
3324
|
return;
|
|
3300
3325
|
}
|
|
@@ -3316,7 +3341,9 @@ async function writeFlakeReport(itemName, gateName, attempts, totalDuration, art
|
|
|
3316
3341
|
const reportPath = path9.join(flakeReportDir, `${itemName}-${gateName}.json`);
|
|
3317
3342
|
const reportContent = canonicalJSONStringify(flakeReport);
|
|
3318
3343
|
fs9.writeFileSync(reportPath, reportContent, "utf-8");
|
|
3319
|
-
|
|
3344
|
+
if (!suppressStdout) {
|
|
3345
|
+
console.log(`\u{1F4CA} Flake report written: ${reportPath}`);
|
|
3346
|
+
}
|
|
3320
3347
|
}
|
|
3321
3348
|
async function executeGateAttempt(gate, artifactDir, attempt, timeoutMs, repoRoot) {
|
|
3322
3349
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -3349,7 +3376,7 @@ async function executeGateAttempt(gate, artifactDir, attempt, timeoutMs, repoRoo
|
|
|
3349
3376
|
}
|
|
3350
3377
|
async function executeLocalGate(gate, artifactDir, attempt, startedAt, startTime, timeoutMs, repoRoot) {
|
|
3351
3378
|
try {
|
|
3352
|
-
const { getCommandValidator } = await import("./commandValidator-
|
|
3379
|
+
const { getCommandValidator } = await import("./commandValidator-LVEMJTZP.js");
|
|
3353
3380
|
const validator = getCommandValidator();
|
|
3354
3381
|
validator.validate(gate.run);
|
|
3355
3382
|
} catch (validationError) {
|
|
@@ -3575,29 +3602,31 @@ ${violations.map((v) => ` - ${v}`).join("\n")}`;
|
|
|
3575
3602
|
lastAttempt: startedAt
|
|
3576
3603
|
};
|
|
3577
3604
|
}
|
|
3578
|
-
async function emitGateExecutionFrame(gateName, itemName, result, runId) {
|
|
3605
|
+
async function emitGateExecutionFrame(gateName, itemName, result, runId, emitReceipt = true) {
|
|
3579
3606
|
if (result.status === "blocked" || result.status === "skipped" || result.status === "retrying") {
|
|
3580
3607
|
return void 0;
|
|
3581
3608
|
}
|
|
3582
3609
|
const passed = result.status === "pass";
|
|
3583
3610
|
const duration = result.duration || 0;
|
|
3584
3611
|
const outcome = passed ? "success" : "failure";
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3612
|
+
if (emitReceipt) {
|
|
3613
|
+
emitGateReceipt(
|
|
3614
|
+
gateName,
|
|
3615
|
+
itemName,
|
|
3616
|
+
passed,
|
|
3617
|
+
duration,
|
|
3618
|
+
runId,
|
|
3619
|
+
{ log: true, json: true },
|
|
3620
|
+
// Include detailed context for failure receipts
|
|
3621
|
+
!passed ? {
|
|
3622
|
+
error: result.stderr,
|
|
3623
|
+
exitCode: result.exitCode,
|
|
3624
|
+
artifacts: result.artifacts,
|
|
3625
|
+
failureKind: result.failureKind,
|
|
3626
|
+
descendantsReaped: result.timeoutCleanup?.descendantsReaped
|
|
3627
|
+
} : void 0
|
|
3628
|
+
);
|
|
3629
|
+
}
|
|
3601
3630
|
if (!runId) {
|
|
3602
3631
|
return void 0;
|
|
3603
3632
|
}
|
|
@@ -3622,6 +3651,9 @@ async function executeItemGates(item, policy, executionState, artifactDir, timeo
|
|
|
3622
3651
|
fs9.mkdirSync(itemArtifactDir, { recursive: true });
|
|
3623
3652
|
}
|
|
3624
3653
|
for (const gate of item.gates) {
|
|
3654
|
+
if (options?.onlyGate && gate.name !== options.onlyGate) {
|
|
3655
|
+
continue;
|
|
3656
|
+
}
|
|
3625
3657
|
if (shouldBlockGate(gate, policy)) {
|
|
3626
3658
|
const blockedResult = {
|
|
3627
3659
|
gate: gate.name,
|
|
@@ -3652,7 +3684,13 @@ async function executeItemGates(item, policy, executionState, artifactDir, timeo
|
|
|
3652
3684
|
options.baseDir
|
|
3653
3685
|
);
|
|
3654
3686
|
}
|
|
3655
|
-
await emitGateExecutionFrame(
|
|
3687
|
+
await emitGateExecutionFrame(
|
|
3688
|
+
gate.name,
|
|
3689
|
+
item.name,
|
|
3690
|
+
result2,
|
|
3691
|
+
options?.runId,
|
|
3692
|
+
options?.emitReceipt !== false
|
|
3693
|
+
);
|
|
3656
3694
|
continue;
|
|
3657
3695
|
}
|
|
3658
3696
|
const result = await executeGate(
|
|
@@ -3663,7 +3701,8 @@ async function executeItemGates(item, policy, executionState, artifactDir, timeo
|
|
|
3663
3701
|
item.name,
|
|
3664
3702
|
skipValidation,
|
|
3665
3703
|
repoRoot,
|
|
3666
|
-
options?.turnCostTracker
|
|
3704
|
+
options?.turnCostTracker,
|
|
3705
|
+
options?.suppressStdout
|
|
3667
3706
|
);
|
|
3668
3707
|
results.push(result);
|
|
3669
3708
|
executionState.updateGateResult(item.name, result);
|
|
@@ -3681,7 +3720,13 @@ async function executeItemGates(item, policy, executionState, artifactDir, timeo
|
|
|
3681
3720
|
);
|
|
3682
3721
|
await promptCounterExampleIfEnabled(result, options);
|
|
3683
3722
|
}
|
|
3684
|
-
await emitGateExecutionFrame(
|
|
3723
|
+
await emitGateExecutionFrame(
|
|
3724
|
+
gate.name,
|
|
3725
|
+
item.name,
|
|
3726
|
+
result,
|
|
3727
|
+
options?.runId,
|
|
3728
|
+
options?.emitReceipt !== false
|
|
3729
|
+
);
|
|
3685
3730
|
}
|
|
3686
3731
|
return results;
|
|
3687
3732
|
}
|
|
@@ -3699,7 +3744,9 @@ async function executeGatesWithPolicy(plan, executionState, artifactDir, timeout
|
|
|
3699
3744
|
try {
|
|
3700
3745
|
const hostilityScore = runEnvironmentQualityCheck({ cwd: workingDir });
|
|
3701
3746
|
const adjustment = calculateHostilityAdjustedTimeout(timeoutMs, hostilityScore);
|
|
3702
|
-
|
|
3747
|
+
if (!options?.suppressStdout) {
|
|
3748
|
+
logTimeoutAdjustment(adjustment, "all_gates");
|
|
3749
|
+
}
|
|
3703
3750
|
effectiveTimeoutMs = adjustment.adjustedTimeoutMs;
|
|
3704
3751
|
} catch {
|
|
3705
3752
|
}
|
|
@@ -3717,7 +3764,9 @@ async function executeGatesWithPolicy(plan, executionState, artifactDir, timeout
|
|
|
3717
3764
|
if (!fs9.existsSync(artifactDir)) {
|
|
3718
3765
|
fs9.mkdirSync(artifactDir, { recursive: true });
|
|
3719
3766
|
}
|
|
3720
|
-
const executionOrder = buildExecutionOrder(plan)
|
|
3767
|
+
const executionOrder = buildExecutionOrder(plan).filter(
|
|
3768
|
+
(itemName) => !options?.onlyItem || itemName === options.onlyItem
|
|
3769
|
+
);
|
|
3721
3770
|
const maxWorkers = policy.maxWorkers;
|
|
3722
3771
|
const pendingNodes = [...executionOrder];
|
|
3723
3772
|
const executing = /* @__PURE__ */ new Set();
|