jonah-fleet 1.12.0 → 1.13.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 CHANGED
@@ -5,6 +5,15 @@ All notable changes to `jonah-fleet` will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.13.0](https://github.com/juliendurandeu/jonah-fleet/compare/v1.12.0...v1.13.0) (2026-09-19)
9
+
10
+
11
+ ### Features
12
+
13
+ * **guard:** implement CLI runner loop-guard and circuit-breaker wrapper ([#222](https://github.com/juliendurandeu/jonah-fleet/issues/222)) ([bedacc0](https://github.com/juliendurandeu/jonah-fleet/commit/bedacc0d9bc2a36276f58901ffa2f2b06e797016))
14
+ * **memory:** introduce opt-in LESSONS.md operational memory tier ([#207](https://github.com/juliendurandeu/jonah-fleet/issues/207)) ([#219](https://github.com/juliendurandeu/jonah-fleet/issues/219)) ([13f74fa](https://github.com/juliendurandeu/jonah-fleet/commit/13f74fa2acc78554bba486b2bf77521106265f03))
15
+ * **orchestration:** standardize 'Why I believe this' structured human escalation card ([#229](https://github.com/juliendurandeu/jonah-fleet/issues/229)) ([997e1d2](https://github.com/juliendurandeu/jonah-fleet/commit/997e1d20d7d0a3970ecca9f209841db745f99979))
16
+
8
17
  ## [1.12.0](https://github.com/juliendurandeu/jonah-fleet/compare/v1.11.0...v1.12.0) (2026-09-18)
9
18
 
10
19
 
@@ -17,7 +26,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
17
26
 
18
27
  * **runner:** prevent premature routine termination on async background tasks in headless mode ([6641b2b](https://github.com/juliendurandeu/jonah-fleet/commit/6641b2b2e46ab363b73d490aac1144a33a1c69f9))
19
28
 
20
- ## [Unreleased] - 2026-09-18
29
+ ## [Unreleased] - 2026-09-19
30
+
31
+ ### Features
32
+
33
+ * **orchestration:** standardize 'Why I believe this' structured human escalation card across autowork, triage, and housekeeping routines ([#209](https://github.com/juliendurandeu/jonah-fleet/issues/209))
34
+ * **guard:** implement CLI runner loop-guard and circuit-breaker wrapper ([#208](https://github.com/juliendurandeu/jonah-fleet/issues/208))
35
+ * **memory:** introduce opt-in LESSONS.md operational memory tier with 25-entry hard cap, schema configuration, and routine prompt gates ([#207](https://github.com/juliendurandeu/jonah-fleet/issues/207)).
21
36
 
22
37
  ### Bug Fixes
23
38
 
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export {};
1
+ export * from './lib/loop-guard.js';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA6IA,cAAc,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -608,6 +608,22 @@ function installFleet(targetDir, manifest, options = {}) {
608
608
  }
609
609
  }
610
610
  }
611
+ const targetScriptsDir = path3.join(targetDir, ".github/scripts");
612
+ fs3.mkdirSync(targetScriptsDir, { recursive: true });
613
+ const baseScripts = ["run-with-loop-guard.js"];
614
+ for (const file of baseScripts) {
615
+ const src = path3.join(templatesDir, "scripts", file);
616
+ const dest = path3.join(targetScriptsDir, file);
617
+ if (fs3.existsSync(src)) {
618
+ if (!fs3.existsSync(dest) || options.force) {
619
+ fs3.copyFileSync(src, dest);
620
+ try {
621
+ fs3.chmodSync(dest, 493);
622
+ } catch {
623
+ }
624
+ }
625
+ }
626
+ }
611
627
  for (const [routineName, isEnabled] of Object.entries(manifest.routines)) {
612
628
  if (!isEnabled) continue;
613
629
  const promptFile = `${routineName}.md`;
@@ -672,6 +688,17 @@ function installFleet(targetDir, manifest, options = {}) {
672
688
  result.docsInstalled.push("AGENTS.md");
673
689
  }
674
690
  }
691
+ const lessonsEnabled = manifest.lessons === true || typeof manifest.lessons === "object" && manifest.lessons?.enabled;
692
+ if (lessonsEnabled) {
693
+ const lessonsPath = path3.join(targetDir, "LESSONS.md");
694
+ if (!fs3.existsSync(lessonsPath) || options.force) {
695
+ const lessonsSrc = path3.join(templatesDir, "docs/LESSONS.template.md");
696
+ if (fs3.existsSync(lessonsSrc)) {
697
+ fs3.copyFileSync(lessonsSrc, lessonsPath);
698
+ result.docsInstalled.push("LESSONS.md");
699
+ }
700
+ }
701
+ }
675
702
  return result;
676
703
  }
677
704
 
@@ -3495,6 +3522,223 @@ var TerminalSpinner = class {
3495
3522
  }
3496
3523
  };
3497
3524
 
3525
+ // src/lib/loop-guard.ts
3526
+ import crypto from "crypto";
3527
+ function canonicalStringify(value) {
3528
+ if (value === null || value === void 0) {
3529
+ return "";
3530
+ }
3531
+ if (typeof value !== "object") {
3532
+ return JSON.stringify(value);
3533
+ }
3534
+ if (Array.isArray(value)) {
3535
+ return "[" + value.map(canonicalStringify).join(",") + "]";
3536
+ }
3537
+ const keys = Object.keys(value).sort();
3538
+ const entries = keys.map((k) => `${JSON.stringify(k)}:${canonicalStringify(value[k])}`);
3539
+ return "{" + entries.join(",") + "}";
3540
+ }
3541
+ function computeActionHash(toolName, args) {
3542
+ const canonicalArgs = canonicalStringify(args);
3543
+ return crypto.createHash("sha256").update(`${toolName}:${canonicalArgs}`).digest("hex");
3544
+ }
3545
+ var LoopGuard = class {
3546
+ repetitionThreshold;
3547
+ pingPongThreshold;
3548
+ consecutiveErrorThreshold;
3549
+ slidingWindowSize;
3550
+ onTrip;
3551
+ history = [];
3552
+ lastErrorHash = null;
3553
+ consecutiveErrorCount = 0;
3554
+ trippedResult = null;
3555
+ constructor(options = {}) {
3556
+ this.repetitionThreshold = options.repetitionThreshold ?? 5;
3557
+ this.pingPongThreshold = options.pingPongThreshold ?? 3;
3558
+ this.consecutiveErrorThreshold = options.consecutiveErrorThreshold ?? 2;
3559
+ this.slidingWindowSize = options.slidingWindowSize ?? 20;
3560
+ this.onTrip = options.onTrip;
3561
+ }
3562
+ setOnTrip(callback) {
3563
+ this.onTrip = callback;
3564
+ }
3565
+ isTripped() {
3566
+ return this.trippedResult !== null;
3567
+ }
3568
+ getTrip() {
3569
+ return this.trippedResult;
3570
+ }
3571
+ getHistory() {
3572
+ return [...this.history];
3573
+ }
3574
+ reset() {
3575
+ this.history = [];
3576
+ this.lastErrorHash = null;
3577
+ this.consecutiveErrorCount = 0;
3578
+ this.trippedResult = null;
3579
+ }
3580
+ /**
3581
+ * Records an action occurrence and evaluates repetition, ping-pong, and error guards.
3582
+ */
3583
+ recordAction(toolName, args, isError = false) {
3584
+ if (this.trippedResult) {
3585
+ return this.trippedResult;
3586
+ }
3587
+ const hash = computeActionHash(toolName, args);
3588
+ const record = {
3589
+ toolName,
3590
+ args,
3591
+ hash,
3592
+ isError,
3593
+ timestamp: Date.now()
3594
+ };
3595
+ if (isError) {
3596
+ if (this.lastErrorHash === hash) {
3597
+ this.consecutiveErrorCount++;
3598
+ } else {
3599
+ this.lastErrorHash = hash;
3600
+ this.consecutiveErrorCount = 1;
3601
+ }
3602
+ if (this.consecutiveErrorCount >= this.consecutiveErrorThreshold) {
3603
+ return this.triggerTrip({
3604
+ reason: "consecutive_errors",
3605
+ message: `Tool circuit breaker tripped: ${this.consecutiveErrorCount} consecutive identical tool errors for '${toolName}' with unchanged arguments.`,
3606
+ toolName,
3607
+ actionHash: hash,
3608
+ count: this.consecutiveErrorCount
3609
+ });
3610
+ }
3611
+ } else {
3612
+ this.lastErrorHash = null;
3613
+ this.consecutiveErrorCount = 0;
3614
+ }
3615
+ this.history.push(record);
3616
+ const windowStart = Math.max(0, this.history.length - this.slidingWindowSize);
3617
+ const currentWindow = this.history.slice(windowStart);
3618
+ let repetitionCount = 0;
3619
+ for (const item of currentWindow) {
3620
+ if (item.hash === hash) {
3621
+ repetitionCount++;
3622
+ }
3623
+ }
3624
+ if (repetitionCount >= this.repetitionThreshold) {
3625
+ return this.triggerTrip({
3626
+ reason: "repetition",
3627
+ message: `Action repetition loop detected: tool '${toolName}' called ${repetitionCount} times with identical parameters within sliding window of ${this.slidingWindowSize}.`,
3628
+ toolName,
3629
+ actionHash: hash,
3630
+ count: repetitionCount
3631
+ });
3632
+ }
3633
+ const requiredPingPongLength = this.pingPongThreshold * 2;
3634
+ if (this.history.length >= requiredPingPongLength) {
3635
+ const pingPongSlice = this.history.slice(-requiredPingPongLength);
3636
+ const hashA = pingPongSlice[0].hash;
3637
+ const hashB = pingPongSlice[1].hash;
3638
+ if (hashA !== hashB) {
3639
+ let isPingPong = true;
3640
+ for (let i = 0; i < requiredPingPongLength; i++) {
3641
+ const expectedHash = i % 2 === 0 ? hashA : hashB;
3642
+ if (pingPongSlice[i].hash !== expectedHash) {
3643
+ isPingPong = false;
3644
+ break;
3645
+ }
3646
+ }
3647
+ if (isPingPong) {
3648
+ return this.triggerTrip({
3649
+ reason: "ping_pong",
3650
+ message: `Alternating ping-pong action loop detected: ${this.pingPongThreshold} consecutive alternating cycles between tools (A-B-A-B-A-B).`,
3651
+ toolName,
3652
+ actionHash: hash,
3653
+ count: this.pingPongThreshold
3654
+ });
3655
+ }
3656
+ }
3657
+ }
3658
+ return null;
3659
+ }
3660
+ /**
3661
+ * Feeds a single line of output (stream-json or raw text) into the guard.
3662
+ */
3663
+ feedLine(line) {
3664
+ if (!line || !line.trim()) return null;
3665
+ try {
3666
+ const parsed = JSON.parse(line.trim());
3667
+ if (parsed && typeof parsed === "object" && parsed.event === "step_update") {
3668
+ const su = parsed.step_update;
3669
+ if (su && su.step_type === "tool") {
3670
+ const toolName = su.tool_name || su.tool_info?.name || "unknown";
3671
+ const params = su.tool_info?.parameters;
3672
+ if (su.state === "ACTIVE") {
3673
+ return this.recordAction(toolName, params, false);
3674
+ }
3675
+ if (su.state === "ERROR") {
3676
+ return this.recordAction(toolName, params, true);
3677
+ }
3678
+ }
3679
+ }
3680
+ } catch {
3681
+ }
3682
+ return null;
3683
+ }
3684
+ triggerTrip(trip) {
3685
+ this.trippedResult = trip;
3686
+ if (this.onTrip) {
3687
+ try {
3688
+ this.onTrip(trip);
3689
+ } catch {
3690
+ }
3691
+ }
3692
+ return trip;
3693
+ }
3694
+ };
3695
+ function formatLoopGuardFailureCard(options) {
3696
+ const serverUrl = options.serverUrl || process.env.GITHUB_SERVER_URL || "https://github.com";
3697
+ const repository = options.repository || process.env.GITHUB_REPOSITORY || "";
3698
+ const runId = options.runId || process.env.GITHUB_RUN_ID || "";
3699
+ const logUrl = runId && repository ? `${serverUrl}/${repository}/actions/runs/${runId}` : "";
3700
+ const lines = [
3701
+ `### \u274C Milestone: Run Interrupted / Failed`,
3702
+ `- **Routine**: \`${options.routine}\``,
3703
+ `- **Status**: Loop guard circuit breaker tripped (\`loop_circuit_breaker\`)`,
3704
+ `- **Root Cause Category**: \`loop_circuit_breaker\``,
3705
+ `- **Trigger**: \`${options.trip.reason}\``,
3706
+ `- **Reason**: ${options.trip.message}`,
3707
+ `- **Action**: Process terminated to prevent runaway token burn`
3708
+ ];
3709
+ if (logUrl) {
3710
+ lines.push(`- **Action Log**: [View Run Logs](${logUrl})`);
3711
+ }
3712
+ return lines.join("\n");
3713
+ }
3714
+ function formatLoopGuardReport(options) {
3715
+ return [
3716
+ `# Run Report`,
3717
+ ``,
3718
+ `## Result`,
3719
+ `FAILURE`,
3720
+ ``,
3721
+ `## Summary`,
3722
+ `| Metric | Value |`,
3723
+ `|---|---|`,
3724
+ `| Routine | \`${options.routine}\` |`,
3725
+ `| Timestamp | \`${options.timestamp}\` |`,
3726
+ `| Result | \`FAILURE\` |`,
3727
+ `| Category | \`loop_circuit_breaker\` |`,
3728
+ `| Trigger | \`${options.trip.reason}\` |`,
3729
+ `| Tool | \`${options.trip.toolName}\` |`,
3730
+ `| Action Hash | \`${options.trip.actionHash}\` |`,
3731
+ ``,
3732
+ `### Failure Details`,
3733
+ `The autonomous execution was halted by the deterministic loop-guard wrapper.`,
3734
+ `- **Category**: \`loop_circuit_breaker\``,
3735
+ `- **Trigger**: \`${options.trip.reason}\``,
3736
+ `- **Tool**: \`${options.trip.toolName}\``,
3737
+ `- **Action Hash**: \`${options.trip.actionHash}\``,
3738
+ `- **Message**: ${options.trip.message}`
3739
+ ].join("\n");
3740
+ }
3741
+
3498
3742
  // src/lib/runner.ts
3499
3743
  import pc10 from "picocolors";
3500
3744
  var execFileAsync4 = promisify4(execFile4);
@@ -4044,6 +4288,33 @@ Timeout: ${printTimeout}`,
4044
4288
  });
4045
4289
  }
4046
4290
  };
4291
+ let loopGuardTrip = null;
4292
+ let activeChild = null;
4293
+ const loopGuard = new LoopGuard({
4294
+ repetitionThreshold: 5,
4295
+ pingPongThreshold: 3,
4296
+ consecutiveErrorThreshold: 2,
4297
+ slidingWindowSize: 20,
4298
+ onTrip: (trip) => {
4299
+ loopGuardTrip = trip;
4300
+ spinner?.stop();
4301
+ if (activeChild && !activeChild.killed) {
4302
+ try {
4303
+ activeChild.kill("SIGTERM");
4304
+ } catch {
4305
+ }
4306
+ const killTimer = setTimeout(() => {
4307
+ try {
4308
+ if (activeChild && !activeChild.killed) {
4309
+ activeChild.kill("SIGKILL");
4310
+ }
4311
+ } catch {
4312
+ }
4313
+ }, 3e3);
4314
+ killTimer.unref();
4315
+ }
4316
+ }
4317
+ });
4047
4318
  const stdoutParser = new LineBufferedStreamParser((line) => {
4048
4319
  const event = parseStreamJsonEvent(line);
4049
4320
  if (event) {
@@ -4053,6 +4324,7 @@ Timeout: ${printTimeout}`,
4053
4324
  const toolName = su.tool_name || su.tool_info?.name || "unknown";
4054
4325
  const toolParams = su.tool_info?.parameters;
4055
4326
  if (su.state === "ACTIVE") {
4327
+ loopGuard.recordAction(toolName, toolParams, false);
4056
4328
  const actionDesc = formatActionDescription(toolName, toolParams);
4057
4329
  lastActionDesc = actionDesc;
4058
4330
  if (spinner) {
@@ -4065,6 +4337,9 @@ Timeout: ${printTimeout}`,
4065
4337
  const formatted = formatVerboseEvent(event);
4066
4338
  if (formatted) console.log(formatted);
4067
4339
  }
4340
+ } else if (su.state === "ERROR") {
4341
+ loopGuard.recordAction(toolName, toolParams, true);
4342
+ lastActionDesc = null;
4068
4343
  } else if (su.state === "DONE") {
4069
4344
  lastActionDesc = null;
4070
4345
  if (su.tool_info?.output) {
@@ -4168,6 +4443,7 @@ Timeout: ${printTimeout}`,
4168
4443
  env: childEnv,
4169
4444
  stdio: ["inherit", "pipe", "pipe"]
4170
4445
  });
4446
+ activeChild = child;
4171
4447
  child.stdout?.on("data", (data) => {
4172
4448
  processChunk(data.toString(), false);
4173
4449
  });
@@ -4204,7 +4480,26 @@ Timeout: ${printTimeout}`,
4204
4480
  exitCode = 1;
4205
4481
  }
4206
4482
  }
4207
- if (!reportContent) {
4483
+ if (loopGuardTrip) {
4484
+ exitCode = 1;
4485
+ reportContent = formatLoopGuardReport({
4486
+ routine,
4487
+ timestamp,
4488
+ trip: loopGuardTrip
4489
+ });
4490
+ try {
4491
+ fs12.writeFileSync(executionReportPath, reportContent, "utf8");
4492
+ fs12.writeFileSync(targetReportPath, reportContent, "utf8");
4493
+ } catch {
4494
+ }
4495
+ if (routineIssueNumber) {
4496
+ const failureCard = formatLoopGuardFailureCard({
4497
+ routine,
4498
+ trip: loopGuardTrip
4499
+ });
4500
+ tryPostLocalRunMilestone(targetDir, routineIssueNumber, failureCard);
4501
+ }
4502
+ } else if (!reportContent) {
4208
4503
  reportContent = formatFallbackRunReport({
4209
4504
  routine,
4210
4505
  timestamp,
@@ -5919,3 +6214,10 @@ program.command("telemetry [repos...]").description("Aggregate and report fleet-
5919
6214
  await runTelemetry({ ...options, repos, action });
5920
6215
  });
5921
6216
  program.parse(process.argv);
6217
+ export {
6218
+ LoopGuard,
6219
+ canonicalStringify,
6220
+ computeActionHash,
6221
+ formatLoopGuardFailureCard,
6222
+ formatLoopGuardReport
6223
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../../src/lib/installer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAA2B,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,aAAa,EAAyC,MAAM,eAAe,CAAC;AAKrF,wBAAgB,eAAe,IAAI,MAAM,CAOxC;AAED,MAAM,WAAW,aAAa;IAC5B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAMlF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGtE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,QAAQ,EAAE,aAAa,EACvB,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,GAAG,SAAS,CAqBpB;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,GAAE,cAAmB,GAAG,aAAa,CA2GpH"}
1
+ {"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../../src/lib/installer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAA2B,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,aAAa,EAAyC,MAAM,eAAe,CAAC;AAKrF,wBAAgB,eAAe,IAAI,MAAM,CAOxC;AAED,MAAM,WAAW,aAAa;IAC5B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAMlF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGtE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,QAAQ,EAAE,aAAa,EACvB,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,GAAG,SAAS,CAqBpB;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,GAAE,cAAmB,GAAG,aAAa,CAyIpH"}
@@ -0,0 +1,80 @@
1
+ export type LoopGuardTripReason = 'repetition' | 'ping_pong' | 'consecutive_errors';
2
+ export interface LoopGuardTrip {
3
+ reason: LoopGuardTripReason;
4
+ message: string;
5
+ toolName: string;
6
+ actionHash: string;
7
+ count: number;
8
+ }
9
+ export interface ActionRecord {
10
+ toolName: string;
11
+ args: any;
12
+ hash: string;
13
+ isError: boolean;
14
+ timestamp: number;
15
+ }
16
+ export interface LoopGuardOptions {
17
+ repetitionThreshold?: number;
18
+ pingPongThreshold?: number;
19
+ consecutiveErrorThreshold?: number;
20
+ slidingWindowSize?: number;
21
+ onTrip?: (trip: LoopGuardTrip) => void;
22
+ }
23
+ /**
24
+ * Deterministically stringifies an object by recursively sorting its keys.
25
+ */
26
+ export declare function canonicalStringify(value: any): string;
27
+ /**
28
+ * Computes a deterministic SHA-256 action hash: SHA-256(tool_name + canonicalString(args))
29
+ */
30
+ export declare function computeActionHash(toolName: string, args: any): string;
31
+ /**
32
+ * Deterministic Loop Guard & Circuit Breaker monitoring tool calls and execution events.
33
+ */
34
+ export declare class LoopGuard {
35
+ private repetitionThreshold;
36
+ private pingPongThreshold;
37
+ private consecutiveErrorThreshold;
38
+ private slidingWindowSize;
39
+ private onTrip?;
40
+ private history;
41
+ private lastErrorHash;
42
+ private consecutiveErrorCount;
43
+ private trippedResult;
44
+ constructor(options?: LoopGuardOptions);
45
+ setOnTrip(callback: (trip: LoopGuardTrip) => void): void;
46
+ isTripped(): boolean;
47
+ getTrip(): LoopGuardTrip | null;
48
+ getHistory(): ActionRecord[];
49
+ reset(): void;
50
+ /**
51
+ * Records an action occurrence and evaluates repetition, ping-pong, and error guards.
52
+ */
53
+ recordAction(toolName: string, args: any, isError?: boolean): LoopGuardTrip | null;
54
+ /**
55
+ * Feeds a single line of output (stream-json or raw text) into the guard.
56
+ */
57
+ feedLine(line: string): LoopGuardTrip | null;
58
+ private triggerTrip;
59
+ }
60
+ export interface LoopGuardFailureCardOptions {
61
+ routine: string;
62
+ trip: LoopGuardTrip;
63
+ runId?: string;
64
+ serverUrl?: string;
65
+ repository?: string;
66
+ }
67
+ /**
68
+ * Formats a structured 5-point milestone interruption card on circuit breaker trip.
69
+ */
70
+ export declare function formatLoopGuardFailureCard(options: LoopGuardFailureCardOptions): string;
71
+ export interface LoopGuardReportOptions {
72
+ routine: string;
73
+ timestamp: string;
74
+ trip: LoopGuardTrip;
75
+ }
76
+ /**
77
+ * Formats a markdown run report for .jonah-fleet/run-report.md when loop guard terminates a run.
78
+ */
79
+ export declare function formatLoopGuardReport(options: LoopGuardReportOptions): string;
80
+ //# sourceMappingURL=loop-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop-guard.d.ts","sourceRoot":"","sources":["../../src/lib/loop-guard.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,mBAAmB,GAAG,YAAY,GAAG,WAAW,GAAG,oBAAoB,CAAC;AAEpF,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,GAAG,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;CACxC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,CAarD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,MAAM,CAGrE;AAED;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,yBAAyB,CAAS;IAC1C,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,MAAM,CAAC,CAAgC;IAE/C,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,qBAAqB,CAAa;IAC1C,OAAO,CAAC,aAAa,CAA8B;IAEnD,YAAY,OAAO,GAAE,gBAAqB,EAMzC;IAEM,SAAS,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,GAAG,IAAI,CAE9D;IAEM,SAAS,IAAI,OAAO,CAE1B;IAEM,OAAO,IAAI,aAAa,GAAG,IAAI,CAErC;IAEM,UAAU,IAAI,YAAY,EAAE,CAElC;IAEM,KAAK,IAAI,IAAI,CAKnB;IAED;;OAEG;IACI,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,GAAE,OAAe,GAAG,aAAa,GAAG,IAAI,CA6F/F;IAED;;OAEG;IACI,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAwBlD;IAED,OAAO,CAAC,WAAW;CAWpB;AAED,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,aAAa,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,2BAA2B,GAAG,MAAM,CAqBvF;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,aAAa,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM,CA0B7E"}
@@ -80,6 +80,11 @@ export interface FleetManifest {
80
80
  labels?: {
81
81
  protected?: string[];
82
82
  };
83
+ lessons?: boolean | LessonsConfig;
84
+ }
85
+ export interface LessonsConfig {
86
+ enabled?: boolean;
87
+ maxEntries?: number;
83
88
  }
84
89
  export declare const DEFAULT_ROUTINE_MODELS: Record<string, string>;
85
90
  export declare const DEFAULT_ROUTINE_TIMEOUTS: Record<string, number>;
@@ -1 +1 @@
1
- {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/lib/presets.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/lib/presets.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/lib/runner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/lib/runner.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jonah-fleet",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "description": "Standalone autonomous agent fleet with Symphony orchestration, claim protocols, and continuous improvement loops",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/schema.json CHANGED
@@ -163,6 +163,32 @@
163
163
  },
164
164
  "additionalProperties": false,
165
165
  "description": "Configuration for label management and protection shields"
166
+ },
167
+ "lessons": {
168
+ "oneOf": [
169
+ {
170
+ "type": "boolean",
171
+ "description": "Enable or disable opt-in LESSONS.md operational memory tier"
172
+ },
173
+ {
174
+ "type": "object",
175
+ "properties": {
176
+ "enabled": {
177
+ "type": "boolean",
178
+ "default": true,
179
+ "description": "Enable opt-in LESSONS.md operational memory tier"
180
+ },
181
+ "maxEntries": {
182
+ "type": "number",
183
+ "default": 25,
184
+ "description": "Maximum number of active operational lesson entries (hard cap: 25)"
185
+ }
186
+ },
187
+ "additionalProperties": false,
188
+ "description": "Configuration for opt-in operational memory tier (LESSONS.md)"
189
+ }
190
+ ],
191
+ "description": "Configuration for opt-in operational memory tier (LESSONS.md)"
166
192
  }
167
193
  },
168
194
  "required": ["version", "preset", "routines", "skills"],
@@ -0,0 +1,7 @@
1
+ # Operational Lessons & Repository Heuristics
2
+ <!-- Invariants: Max 25 entries. Hard cap. Older entries graduate or demote to LESSONS_ARCHIVE.md -->
3
+
4
+ ### [subsystem] Title
5
+ - **Symptom:** <Symptom description or error message>
6
+ - **Root Cause:** <Brief explanation of the underlying cause>
7
+ - **Rule:** <Actionable rule or constraint to follow>
@@ -272,3 +272,29 @@ In headless CLI environments (`agy -p` / GitHub Actions), agent sessions termina
272
272
  1. **Zero-Yield Waiting Invariant**: Agents MUST NEVER call `schedule` or emit a terminal turn with plain text to "wait" for background commands, timers, or long-running checks. In headless mode, yielding the turn halts the process immediately with exit code 0 before reaching the Definition of Done.
273
273
  2. **Active Task Supervision**: If a verification command (`npm test`, `npm run type-check`) is sent to the background by `run_command`, the agent must actively poll `manage_task(Action='status')` or inspect code while waiting within the continuous tool-calling loop.
274
274
  3. **CI Trust Bar & Test Discipline**: Peer review routines should trust green passing remote CI checks (GitHub Actions or Vercel preview deployments) on the PR's head commit rather than initiating slow, background-prone full test runs. Run repository verification locally ONLY if CI status is unconfirmed, missing, or failing.
275
+
276
+ ---
277
+
278
+ ## Structured Human Escalation Card Protocol ("Why I believe this")
279
+
280
+ How autonomous routines escalate decisions, ambiguities, and blockers to human maintainers without unbounded back-and-forth or vague questions:
281
+
282
+ 1. **Mandatory 4-Part Escalation Schema**: Whenever a routine cannot proceed autonomously due to ambiguity, conflicting requirements, unobservable acceptance criteria, or repeated review ping-pong—and applies `needs-human` or `needs-info`—it MUST post a comment structured as the mandatory 4-part escalation card (inspired by Orbital's Workbench Provenance format):
283
+ ```markdown
284
+ ## 🛑 Escalation: Human Decision Required
285
+ - **Decision Needed**: [1 focused question or choice]
286
+ - **Evidence ("Why I believe this")**: [Specific files, lines, test outputs, or conflicting docs]
287
+ - **Evaluated Options & Trade-offs**:
288
+ - *Option A*: [Pros / Cons]
289
+ - *Option B*: [Pros / Cons]
290
+ - **Recommended Path**: [Agent recommendation]
291
+ ```
292
+ 2. **Card Invariants**:
293
+ - **Decision Needed**: Exactly 1 high-leverage question or choice required from the maintainer or reporter. Prohibit question dumps or vague "please provide more details".
294
+ - **Evidence ("Why I believe this")**: Concrete artifacts, specific file paths, line numbers, test outputs, or contradicting specification documents justifying why the routine cannot proceed without human guidance.
295
+ - **Evaluated Options & Trade-offs**: At least two distinct, viable options with concrete pros and cons. Never ask maintainers to solve problems from scratch without agent-evaluated trade-offs.
296
+ - **Recommended Path**: The agent's recommended decision and reasoning, allowing maintainers to unblock execution with a simple confirmation.
297
+ 3. **Cross-Routine Enforcement**:
298
+ - `autowork.md`: Required when tripping the Ambiguity Gate (Step 12), encountering a 2nd-strike permanent blocker (`needs-human`), or hitting the review Ping-Pong Cap (Step 3b).
299
+ - `triage/SKILL.md`: Required when transitioning issues or PRs to `needs-info` or `ready-for-human`.
300
+ - `issues-housekeeping.md`: Required when auditing and escalating ambiguous, stale, or infeasible issues with `needs-human` or `needs-info`.
@@ -45,7 +45,7 @@ If any criterion cannot be met, stop immediately and log FAILURE with the reason
45
45
  - Do not start implementing an issue before claiming it (both assignment AND claim comment).
46
46
  - Do not mark a PR ready while its `mergeable_state` is `dirty` — resolve merge conflicts first.
47
47
  - Do not fall into the **Telemetry Rabbit Hole**: do not spend cycles instrumenting elaborate fallback telemetry or defensive error handling for features that suffer from lack of user intent rather than software bugs.
48
- - Do not guess or invent arbitrary specifications for ambiguous issues — post clarifying questions, label `needs-info`, and release the claim instead of blindly writing code.
48
+ - Do not guess or invent arbitrary specifications for ambiguous issues — post the 4-part escalation card ('Why I believe this'), label `needs-info`, and release the claim instead of blindly writing code.
49
49
  - Do not call `schedule` or yield the turn with plain text while waiting for background verification tasks — stay in the tool loop until the Definition of Done is met.
50
50
 
51
51
  ## Instructions
@@ -90,8 +90,17 @@ a. **Read the target issue and check eligibility.** Eligible = open, unassigned
90
90
  - **Build & type-check verification**: run the repository's test, type-check, and lint commands from `AGENTS.md` (e.g. `npm test`, `npm run type-check`, `npm run lint`, `pytest`, `cargo test`). Confirm zero errors and zero test failures.
91
91
  - **Active Origin Sync & Clean-Merge Gate**: Run `git fetch origin main && git merge origin/main --no-edit` to absorb any newly merged pull requests and resolve any conflicts locally. Verify `git merge-tree origin/main HEAD` reports no conflicts before marking ready.
92
92
  - **Release claim on ready**: mark the PR ready (`gh pr ready <PR>`) and unassign yourself (`gh pr edit <PR> --remove-assignee <login>`) so Peer Review can evaluate without holding stale agent reservation locks.
93
- Only mark the PR ready after passing every check above.
94
- 3b. **Ping-pong cap**: If this same PR has bounced between draft and ready 3 or more times over the same substantive finding, stop re-marking it ready. Post a comment summarizing the disagreement for human resolution and leave the PR in draft.
93
+ Only mark the PR ready after passing every check above.
94
+ 3b. **Ping-pong cap**: If this same PR has bounced between draft and ready 3 or more times over the same substantive finding, stop re-marking it ready. Post the mandatory 4-part escalation card summarizing the disagreement for human resolution and leave the PR in draft:
95
+ ```markdown
96
+ ## 🛑 Escalation: Human Decision Required
97
+ - **Decision Needed**: [1 focused question or choice]
98
+ - **Evidence ("Why I believe this")**: [Specific files, lines, test outputs, or conflicting docs]
99
+ - **Evaluated Options & Trade-offs**:
100
+ - *Option A*: [Pros / Cons]
101
+ - *Option B*: [Pros / Cons]
102
+ - **Recommended Path**: [Agent recommendation]
103
+ ```
95
104
  3c. **Orphaned Ready PR Recovery**: If an open PR authored by this routine is `ready_for_review`, has passing CI, no unaddressed review comments, and has received no review activity for over 2 hours (e.g. because peer review crashed or encountered quota limits), kickstart the review routine by posting `/review` comment or toggling draft and ready (`gh pr ready <PR> --undo && gh pr ready <PR>`).
96
105
  - **Passing CI Verification Gate**: Verify via `gh pr view <PR> --json statusCheckRollup,mergeStateStatus` that all required and existing checks have completed with `conclusion: "SUCCESS"` and `mergeStateStatus` is `CLEAN` (neither `UNSTABLE`, `BLOCKED`, nor `DIRTY`).
97
106
  - **Unapproved/Pending Workflow Invariant**: NEVER post `/review` or toggle draft state if checks are in-progress, failing, or awaiting approval (`conclusion: "ACTION_REQUIRED"`). Doing so creates an infinite comment storm while workflows remain paused awaiting human permissions.
@@ -130,13 +139,23 @@ a. **Read the target issue and check eligibility.** Eligible = open, unassigned
130
139
  - Read the issue description, linked code, and comment thread.
131
140
  - If bug: use `/diagnosing-bugs` to establish reproduction test before fixing.
132
141
  - If large/complex: use `/domain-modeling` and `/codebase-design`.
142
+ - **Pre-Flight Memory Scan**: If `LESSONS.md` exists, grep matching subsystem tags (`grep -E "^### \[(subsystem)\]" LESSONS.md -A 4`) to incorporate known landmines into implementation plans before writing code.
133
143
  - **Ambiguity & Missing Acceptance Criteria Gate**: Challenge underspecified or incomplete requests before writing any code. If the issue lacks observable acceptance criteria, relies on unverified assumptions, or leaves critical technical/UX decisions ambiguous:
134
144
  - Do NOT guess or invent arbitrary requirements to force completion.
135
- - Post a comment on the issue posing 1–3 focused clarifying questions that identify the exact decisions or trade-offs needed.
145
+ - Post the mandatory 4-part escalation card on the issue:
146
+ ```markdown
147
+ ## 🛑 Escalation: Human Decision Required
148
+ - **Decision Needed**: [1 focused question or choice]
149
+ - **Evidence ("Why I believe this")**: [Specific files, lines, test outputs, or conflicting docs]
150
+ - **Evaluated Options & Trade-offs**:
151
+ - *Option A*: [Pros / Cons]
152
+ - *Option B*: [Pros / Cons]
153
+ - **Recommended Path**: [Agent recommendation]
154
+ ```
136
155
  - Apply the `needs-info` label and release the claim (unassign).
137
156
  - Select the next candidate (evaluating ambiguous issues counts toward step 12's infeasible-continuation cap).
138
157
  - **Intent vs. Defect Guardrail**: When investigating issues related to low conversion, zero-click events, or underperforming features: verify whether the issue is a software defect or a lack of user intent. If data indicates the root cause is **lack of user intent** (e.g. button is rendered above fold and functions correctly when clicked, but user interaction rate is <2%) rather than a software defect, do NOT fall into the **telemetry rabbit hole** (adding elaborate fallback telemetry, downstream error handling, or defensive rendering). Categorize the issue as a **product/UX question** (`needs-design` / `roadmap/*`), comment explaining the lack of user intent, release the claim (unassign), and select the next candidate.
139
- - If infeasible: comment explaining blocker, release claim (unassign), and select next candidate (up to 3 infeasible evaluations per run). If permanent blocker on 2nd strike, apply `needs-human` label and tag repo owner.
158
+ - If infeasible: comment explaining blocker, release claim (unassign), and select next candidate (up to 3 infeasible evaluations per run). If permanent blocker on 2nd strike, post the mandatory 4-part escalation card (`## 🛑 Escalation: Human Decision Required`), apply `needs-human` label, and tag repo owner.
140
159
  12a. **Umbrella-issue handoff + batching:** If candidate is an umbrella epic:
141
160
  - Read `🧭 Decomposition plan` comment (or create if first run).
142
161
  - Pick next slice(s), batching up to 3 same-recipe slices into one child issue + PR.
@@ -153,7 +172,9 @@ a. **Read the target issue and check eligibility.** Eligible = open, unassigned
153
172
  13. **Implementation & PR creation:**
154
173
  - Branch from freshly fetched `origin/main` with descriptive name (e.g. `feat/...` or `fix/...`).
155
174
  - Drive implementation via `/tdd` (red-green-refactor).
175
+ - **Diagnostic Reflex**: On unexpected test/build failure during TDD, grep symptom text in `LESSONS.md` before making speculative code edits.
156
176
  - Run repository tests and verification.
177
+ - **Pre-PR Lessons Capture Gate**: If solving the issue required overcoming a non-obvious quirk not caught by tests/linters, append a 3-line structured entry on the active PR branch adhering to the 25-entry hard cap. Never write to `LESSONS.md` directly on `main`.
157
178
  - **Milestone 2 (Verification & Tests)**: Once implementation passes tests and type checks, emit milestone card if `$ROUTINE_ISSUE_NUMBER` is set:
158
179
  ```bash
159
180
  gh issue comment "$ROUTINE_ISSUE_NUMBER" --body "### 🧪 Milestone: Verification & Tests
@@ -39,7 +39,17 @@ If any criterion cannot be met, stop immediately and log FAILURE with the reason
39
39
  3. **Priority review**: Check open P1/P2/P3 issues. Promote critical bugs or unblocked items; demote items that lack immediate priority.
40
40
  4. **Duplicate & consolidation check**: Identify duplicate issues; close duplicates with cross-references. Consolidate small, related micro-tasks into batch issues.
41
41
  5. **Premise-obsolete & stale check**: If an issue's premise was resolved by already-merged PRs or recent refactors, close as completed with evidence.
42
- 6. **Label audit & safe prune**: Ensure open issues carry standard role labels (`needs-triage`, `ready-for-agent`, `needs-human`, etc.). Use `/triage` if classifying incoming issues. Run `npx --yes jonah-fleet labels prune --yes` (or `jonah-fleet labels prune --yes`) to safely prune strictly unused boilerplate labels (`issues: 0`, `pullRequests: 0`, non-protected taxonomy) without deleting historical or fleet taxonomy labels.
42
+ 6. **Label audit & safe prune**: Ensure open issues carry standard role labels (`needs-triage`, `ready-for-agent`, `needs-human`, etc.). Use `/triage` if classifying incoming issues. Whenever applying `needs-human` or `needs-info` to escalate an ambiguous, stale, or infeasible issue, mandate formatting the escalation comment with the 4-part card:
43
+ ```markdown
44
+ ## 🛑 Escalation: Human Decision Required
45
+ - **Decision Needed**: [1 focused question or choice]
46
+ - **Evidence ("Why I believe this")**: [Specific files, lines, test outputs, or conflicting docs]
47
+ - **Evaluated Options & Trade-offs**:
48
+ - *Option A*: [Pros / Cons]
49
+ - *Option B*: [Pros / Cons]
50
+ - **Recommended Path**: [Agent recommendation]
51
+ ```
52
+ Run `npx --yes jonah-fleet labels prune --yes` (or `jonah-fleet labels prune --yes`) to safely prune strictly unused boilerplate labels (`issues: 0`, `pullRequests: 0`, non-protected taxonomy) without deleting historical or fleet taxonomy labels.
43
53
  7. **Closed-loop verification check**: For projects running impact or verification loops, audit recently closed roadmap/feature issues against tracking issues to ensure shipped levers do not remain untracked.
44
54
 
45
55
  ### Phase 3: Summary
@@ -71,6 +71,8 @@ If any criterion cannot be met, stop immediately and log FAILURE with the reason
71
71
  - **Passive Order-Taking Anomaly ("Yes-Man Blindspot")**: The Ambiguity Gate trigger rate across intake runs in `autowork` or `triage` is <5% despite elevated PR review bounces ($\ge 2$) or high iteration usage ($\ge 35$), indicating agents are silently guessing requirements and building flawed implementations rather than interrogating underspecified issues.
72
72
  - **Speculative Runaway Waste**: An agent run consumed >50k tokens on an underspecified issue with 0 clarifying questions asked, and subsequently failed, bounced, or required post-merge rework.
73
73
  6. **Analyze resolved bugs & review comments**: Examine closed bug issues, merged bug-fix PRs, and review feedback for missing checks in authoring (`autowork.md`) or review (`peer-review.md`).
74
+ 7. **Scan & Audit Operational Lessons (`LESSONS.md`)**:
75
+ - Scan `LESSONS.md` during routine optimization sweeps, graduating stable rules to automated linter/CI checks or archiving stale entries to `LESSONS_ARCHIVE.md`.
74
76
 
75
77
  ### 2. Formulate preventative improvements
76
78
 
@@ -81,6 +83,7 @@ Translate findings into concrete preventative improvements and remediation trigg
81
83
  - **Ping-Pong Convergence**: For Review Loop Burn, tighten reviewer trust & noise filtering, enforce clean-merge gates, and apply ping-pong caps to prevent endless bounce cycles.
82
84
  - **Loop Discovery Mechanical Audits**: For Feedback Loop Stagnation, tighten discovery sweeps by mandating deterministic per-issue matching tables and itemized reconciliation against upstream closed issues/PRs rather than allowing un-itemized generic summary assertions.
83
85
  - **Ambiguity Gate & Benchmark Eval Feeding**: For Passive Order-Taking and Speculative Runaway Waste, tighten Step 12 criteria in `autowork.md` and `triage.md` to mandate clarifying questions, and automatically extract the problem issue into a `BenchmarkIssue` test case to feed the automated ambiguity benchmark eval suite (`tests/evals.test.ts`), ensuring future agent prompts are continuously tested against real failure cases.
86
+ - **Operational Memory Graduation & Archiving**: Scan `LESSONS.md` to identify recurring, stable rules for graduation into automated linter rules or CI workflow checks. Move obsolete or overflow entries (>25 cap) to `LESSONS_ARCHIVE.md`.
84
87
  - **Verification & Invariant Tests**: Add automated test cases in `tests/` verifying prompt invariant preservation and schema conformity.
85
88
 
86
89
  ### 3. Open Fix PR (Local or Upstream Bridge)
@@ -105,6 +105,7 @@ Check if `$PR_NUMBER` is set:
105
105
  1. Run `/code-review` over the diff (or delta commits if re-review) evaluating:
106
106
  - **Standards**: Conformance to `AGENTS.md` (or `CLAUDE.md`/`GEMINI.md`), conventions, and architecture.
107
107
  - **Spec Compliance**: Verification against the linked issue's deliverables (`## Tasks`), or against the PR description's summary/changes if no tracking issue is linked.
108
+ - **Operational Memory & Lessons Invariant**: Inspect `LESSONS.md` diffs in PRs to verify the lesson is accurate, non-trivial, follows the 3-line structured schema, and the file adheres to the 25-entry hard cap.
108
109
  2. Run Security Pass: auth gates, permission checks, injection risks, sensitive credentials.
109
110
  3. **Design System & Viewport Density Pass** (if PR modifies frontend/rendered UI):
110
111
  - **Token Purity**: Check for arbitrary CSS/Tailwind sizing overrides (e.g. `text-[...px]`, `w-[...px]`) or bespoke button styling bypassing standard design system tokens.
@@ -0,0 +1,439 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Deterministic Execution Wrapper Script for Antigravity CLI (agy)
5
+ *
6
+ * Intercepts runner stdout/stderr in real-time to enforce:
7
+ * - Repetition Guard: Kills process on 5 identical action hashes within sliding window
8
+ * - Ping-Pong Guard: Kills process on 3 consecutive alternating action pairs (A-B-A-B-A-B)
9
+ * - Tool Circuit Breaker: Kills process on 2 consecutive identical tool call errors
10
+ *
11
+ * On trip, emits a structured failure card with category `loop_circuit_breaker`
12
+ * and writes `.jonah-fleet/run-report.md` before exiting with non-zero status.
13
+ */
14
+
15
+ import { spawn, execSync } from 'node:child_process';
16
+ import fs from 'node:fs';
17
+ import path from 'node:path';
18
+ import os from 'node:os';
19
+ import crypto from 'node:crypto';
20
+
21
+ export function canonicalStringify(value) {
22
+ if (value === null || value === undefined) {
23
+ return '';
24
+ }
25
+ if (typeof value !== 'object') {
26
+ return JSON.stringify(value);
27
+ }
28
+ if (Array.isArray(value)) {
29
+ return '[' + value.map(canonicalStringify).join(',') + ']';
30
+ }
31
+ const keys = Object.keys(value).sort();
32
+ const entries = keys.map((k) => `${JSON.stringify(k)}:${canonicalStringify(value[k])}`);
33
+ return '{' + entries.join(',') + '}';
34
+ }
35
+
36
+ export function computeActionHash(toolName, args) {
37
+ const canonicalArgs = canonicalStringify(args);
38
+ return crypto.createHash('sha256').update(`${toolName}:${canonicalArgs}`).digest('hex');
39
+ }
40
+
41
+ export class LoopGuard {
42
+ constructor(options = {}) {
43
+ this.repetitionThreshold = options.repetitionThreshold ?? 5;
44
+ this.pingPongThreshold = options.pingPongThreshold ?? 3;
45
+ this.consecutiveErrorThreshold = options.consecutiveErrorThreshold ?? 2;
46
+ this.slidingWindowSize = options.slidingWindowSize ?? 20;
47
+ this.onTrip = options.onTrip;
48
+
49
+ this.history = [];
50
+ this.lastErrorHash = null;
51
+ this.consecutiveErrorCount = 0;
52
+ this.trippedResult = null;
53
+ }
54
+
55
+ isTripped() {
56
+ return this.trippedResult !== null;
57
+ }
58
+
59
+ getTrip() {
60
+ return this.trippedResult;
61
+ }
62
+
63
+ recordAction(toolName, args, isError = false) {
64
+ if (this.trippedResult) return this.trippedResult;
65
+
66
+ const hash = computeActionHash(toolName, args);
67
+ const record = {
68
+ toolName,
69
+ args,
70
+ hash,
71
+ isError,
72
+ timestamp: Date.now(),
73
+ };
74
+
75
+ // 1. Tool Circuit Breaker Guard
76
+ if (isError) {
77
+ if (this.lastErrorHash === hash) {
78
+ this.consecutiveErrorCount++;
79
+ } else {
80
+ this.lastErrorHash = hash;
81
+ this.consecutiveErrorCount = 1;
82
+ }
83
+
84
+ if (this.consecutiveErrorCount >= this.consecutiveErrorThreshold) {
85
+ return this.triggerTrip({
86
+ reason: 'consecutive_errors',
87
+ message: `Tool circuit breaker tripped: ${this.consecutiveErrorCount} consecutive identical tool errors for '${toolName}' with unchanged arguments.`,
88
+ toolName,
89
+ actionHash: hash,
90
+ count: this.consecutiveErrorCount,
91
+ });
92
+ }
93
+ } else {
94
+ this.lastErrorHash = null;
95
+ this.consecutiveErrorCount = 0;
96
+ }
97
+
98
+ this.history.push(record);
99
+
100
+ // 2. Repetition Guard within sliding window
101
+ const windowStart = Math.max(0, this.history.length - this.slidingWindowSize);
102
+ const currentWindow = this.history.slice(windowStart);
103
+
104
+ let repetitionCount = 0;
105
+ for (const item of currentWindow) {
106
+ if (item.hash === hash) {
107
+ repetitionCount++;
108
+ }
109
+ }
110
+
111
+ if (repetitionCount >= this.repetitionThreshold) {
112
+ return this.triggerTrip({
113
+ reason: 'repetition',
114
+ message: `Action repetition loop detected: tool '${toolName}' called ${repetitionCount} times with identical parameters within sliding window of ${this.slidingWindowSize}.`,
115
+ toolName,
116
+ actionHash: hash,
117
+ count: repetitionCount,
118
+ });
119
+ }
120
+
121
+ // 3. Ping-Pong Guard: 3 consecutive alternating pairs (length 6)
122
+ const requiredPingPongLength = this.pingPongThreshold * 2;
123
+ if (this.history.length >= requiredPingPongLength) {
124
+ const pingPongSlice = this.history.slice(-requiredPingPongLength);
125
+ const hashA = pingPongSlice[0].hash;
126
+ const hashB = pingPongSlice[1].hash;
127
+
128
+ if (hashA !== hashB) {
129
+ let isPingPong = true;
130
+ for (let i = 0; i < requiredPingPongLength; i++) {
131
+ const expectedHash = i % 2 === 0 ? hashA : hashB;
132
+ if (pingPongSlice[i].hash !== expectedHash) {
133
+ isPingPong = false;
134
+ break;
135
+ }
136
+ }
137
+
138
+ if (isPingPong) {
139
+ return this.triggerTrip({
140
+ reason: 'ping_pong',
141
+ message: `Alternating ping-pong action loop detected: ${this.pingPongThreshold} consecutive alternating cycles between tools (A-B-A-B-A-B).`,
142
+ toolName,
143
+ actionHash: hash,
144
+ count: this.pingPongThreshold,
145
+ });
146
+ }
147
+ }
148
+ }
149
+
150
+ return null;
151
+ }
152
+
153
+ feedLine(line) {
154
+ if (!line || !line.trim()) return null;
155
+ try {
156
+ const parsed = JSON.parse(line.trim());
157
+ if (parsed && typeof parsed === 'object' && parsed.event === 'step_update') {
158
+ const su = parsed.step_update;
159
+ if (su && su.step_type === 'tool') {
160
+ const toolName = su.tool_name || su.tool_info?.name || 'unknown';
161
+ const params = su.tool_info?.parameters;
162
+
163
+ if (su.state === 'ACTIVE') {
164
+ return this.recordAction(toolName, params, false);
165
+ }
166
+ if (su.state === 'ERROR') {
167
+ return this.recordAction(toolName, params, true);
168
+ }
169
+ }
170
+ }
171
+ } catch {}
172
+ return null;
173
+ }
174
+
175
+ triggerTrip(trip) {
176
+ this.trippedResult = trip;
177
+ if (this.onTrip) {
178
+ try {
179
+ this.onTrip(trip);
180
+ } catch {}
181
+ }
182
+ return trip;
183
+ }
184
+ }
185
+
186
+ export function formatLoopGuardFailureCard(options) {
187
+ const serverUrl = options.serverUrl || process.env.GITHUB_SERVER_URL || 'https://github.com';
188
+ const repository = options.repository || process.env.GITHUB_REPOSITORY || '';
189
+ const runId = options.runId || process.env.GITHUB_RUN_ID || '';
190
+ const logUrl = runId && repository ? `${serverUrl}/${repository}/actions/runs/${runId}` : '';
191
+
192
+ const lines = [
193
+ `### ❌ Milestone: Run Interrupted / Failed`,
194
+ `- **Routine**: \`${options.routine}\``,
195
+ `- **Status**: Loop guard circuit breaker tripped (\`loop_circuit_breaker\`)`,
196
+ `- **Root Cause Category**: \`loop_circuit_breaker\``,
197
+ `- **Trigger**: \`${options.trip.reason}\``,
198
+ `- **Reason**: ${options.trip.message}`,
199
+ `- **Action**: Process terminated to prevent runaway token burn`,
200
+ ];
201
+
202
+ if (logUrl) {
203
+ lines.push(`- **Action Log**: [View Run Logs](${logUrl})`);
204
+ }
205
+
206
+ return lines.join('\n');
207
+ }
208
+
209
+ export function formatLoopGuardReport(options) {
210
+ return [
211
+ `# Run Report`,
212
+ ``,
213
+ `## Result`,
214
+ `FAILURE`,
215
+ ``,
216
+ `## Summary`,
217
+ `| Metric | Value |`,
218
+ `|---|---|`,
219
+ `| Routine | \`${options.routine}\` |`,
220
+ `| Timestamp | \`${options.timestamp}\` |`,
221
+ `| Result | \`FAILURE\` |`,
222
+ `| Category | \`loop_circuit_breaker\` |`,
223
+ `| Trigger | \`${options.trip.reason}\` |`,
224
+ `| Tool | \`${options.trip.toolName}\` |`,
225
+ `| Action Hash | \`${options.trip.actionHash}\` |`,
226
+ ``,
227
+ `### Failure Details`,
228
+ `The autonomous execution was halted by the deterministic loop-guard wrapper.`,
229
+ `- **Category**: \`loop_circuit_breaker\``,
230
+ `- **Trigger**: \`${options.trip.reason}\``,
231
+ `- **Tool**: \`${options.trip.toolName}\``,
232
+ `- **Action Hash**: \`${options.trip.actionHash}\``,
233
+ `- **Message**: ${options.trip.message}`,
234
+ ].join('\n');
235
+ }
236
+
237
+ class LineParser {
238
+ constructor(onLine) {
239
+ this.buffer = '';
240
+ this.onLine = onLine;
241
+ }
242
+
243
+ feed(chunk) {
244
+ this.buffer += chunk;
245
+ const lines = this.buffer.split('\n');
246
+ this.buffer = lines.pop() ?? '';
247
+ for (const line of lines) {
248
+ if (line.trim().length > 0) {
249
+ this.onLine(line);
250
+ }
251
+ }
252
+ }
253
+
254
+ flush() {
255
+ if (this.buffer.trim().length > 0) {
256
+ this.onLine(this.buffer);
257
+ this.buffer = '';
258
+ }
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Main execution logic when invoked as CLI script.
264
+ */
265
+ async function main() {
266
+ const rawArgs = process.argv.slice(2);
267
+ const commandArgs = rawArgs[0] === 'agy' ? rawArgs.slice(1) : rawArgs;
268
+
269
+ // Detect routine name from environment or prompt
270
+ const routine = process.env.ROUTINE || 'autowork';
271
+ const routineIssueNumber = process.env.ROUTINE_ISSUE_NUMBER;
272
+ const cwd = process.cwd();
273
+
274
+ // Ensure --output-format stream-json is configured so tool calls can be inspected
275
+ const finalArgs = [];
276
+ let hasOutputFormat = false;
277
+ let requestedTextFormat = false;
278
+
279
+ for (let i = 0; i < commandArgs.length; i++) {
280
+ const arg = commandArgs[i];
281
+ if (arg === '--output-format') {
282
+ hasOutputFormat = true;
283
+ const nextArg = commandArgs[i + 1];
284
+ if (nextArg === 'text') {
285
+ requestedTextFormat = true;
286
+ }
287
+ finalArgs.push('--output-format', 'stream-json');
288
+ i++;
289
+ } else {
290
+ finalArgs.push(arg);
291
+ }
292
+ }
293
+
294
+ if (!hasOutputFormat) {
295
+ finalArgs.push('--output-format', 'stream-json');
296
+ }
297
+
298
+ let childProcess = null;
299
+ let tripped = null;
300
+
301
+ const guard = new LoopGuard({
302
+ repetitionThreshold: 5,
303
+ pingPongThreshold: 3,
304
+ consecutiveErrorThreshold: 2,
305
+ slidingWindowSize: 20,
306
+ onTrip: (trip) => {
307
+ tripped = trip;
308
+ console.error(`\n🚨 ::error::[LoopGuard] ${trip.message}`);
309
+
310
+ if (childProcess && !childProcess.killed) {
311
+ console.error(`🛑 Terminating runner process with SIGTERM...`);
312
+ childProcess.kill('SIGTERM');
313
+
314
+ const killTimer = setTimeout(() => {
315
+ if (childProcess && !childProcess.killed) {
316
+ console.error(`🛑 Runner did not terminate; sending SIGKILL...`);
317
+ try {
318
+ childProcess.kill('SIGKILL');
319
+ } catch {}
320
+ }
321
+ }, 3000);
322
+ killTimer.unref();
323
+ }
324
+ },
325
+ });
326
+
327
+ const stdoutParser = new LineParser((line) => {
328
+ guard.feedLine(line);
329
+
330
+ try {
331
+ const parsed = JSON.parse(line.trim());
332
+ if (parsed && typeof parsed === 'object') {
333
+ if (parsed.event === 'step_update' && parsed.step_update) {
334
+ const su = parsed.step_update;
335
+ if (su.step_type === 'agent_response' || su.step_type === 'thought') {
336
+ if (su.text_delta) {
337
+ process.stdout.write(su.text_delta);
338
+ }
339
+ } else if (su.step_type === 'tool') {
340
+ const name = su.tool_name || su.tool_info?.name || 'tool';
341
+ if (su.state === 'ACTIVE') {
342
+ console.log(`\n[tool:start] ${name}`);
343
+ } else if (su.state === 'DONE') {
344
+ console.log(`[tool:done] ${name}`);
345
+ } else if (su.state === 'ERROR') {
346
+ console.log(`[tool:error] ${name}`);
347
+ }
348
+ }
349
+ } else if (parsed.event === 'result' && parsed.result?.response) {
350
+ console.log(`\n${parsed.result.response}\n`);
351
+ }
352
+ return;
353
+ }
354
+ } catch {}
355
+
356
+ // Non-JSON line from stdout: print directly
357
+ console.log(line);
358
+ });
359
+
360
+ const stderrParser = new LineParser((line) => {
361
+ guard.feedLine(line);
362
+ console.error(line);
363
+ });
364
+
365
+ const runnerBinEnv = process.env.RUNNER_BIN || 'agy';
366
+ const [command, ...prefixArgs] = runnerBinEnv.split(' ');
367
+ const execArgs = [...prefixArgs, ...finalArgs];
368
+
369
+ const exitCode = await new Promise((resolve) => {
370
+ childProcess = spawn(command, execArgs, {
371
+ cwd,
372
+ env: process.env,
373
+ stdio: ['inherit', 'pipe', 'pipe'],
374
+ });
375
+
376
+ childProcess.stdout?.on('data', (chunk) => stdoutParser.feed(chunk.toString()));
377
+ childProcess.stderr?.on('data', (chunk) => stderrParser.feed(chunk.toString()));
378
+
379
+ childProcess.on('close', (code, signal) => {
380
+ stdoutParser.flush();
381
+ stderrParser.flush();
382
+ resolve(code ?? (signal ? 1 : 0));
383
+ });
384
+
385
+ childProcess.on('error', (err) => {
386
+ console.error(`Failed to launch '${command}':`, err.message);
387
+ resolve(1);
388
+ });
389
+ });
390
+
391
+ if (tripped) {
392
+ const timestamp = new Date().toISOString();
393
+ const logDir = path.join(cwd, '.jonah-fleet');
394
+ fs.mkdirSync(logDir, { recursive: true });
395
+ const reportPath = path.join(logDir, 'run-report.md');
396
+
397
+ const reportContent = formatLoopGuardReport({
398
+ routine,
399
+ timestamp,
400
+ trip: tripped,
401
+ });
402
+ fs.writeFileSync(reportPath, reportContent, 'utf8');
403
+
404
+ if (routineIssueNumber) {
405
+ try {
406
+ const failureCard = formatLoopGuardFailureCard({
407
+ routine,
408
+ trip: tripped,
409
+ runId: process.env.GITHUB_RUN_ID,
410
+ serverUrl: process.env.GITHUB_SERVER_URL,
411
+ repository: process.env.GITHUB_REPOSITORY,
412
+ });
413
+ const tmpFile = path.join(os.tmpdir(), `loop-guard-card-${Date.now()}.md`);
414
+ fs.writeFileSync(tmpFile, failureCard, 'utf8');
415
+ try {
416
+ execSync(`gh issue comment ${routineIssueNumber} --body-file ${JSON.stringify(tmpFile)}`, {
417
+ cwd,
418
+ stdio: 'ignore',
419
+ });
420
+ } finally {
421
+ if (fs.existsSync(tmpFile)) fs.unlinkSync(tmpFile);
422
+ }
423
+ } catch (err) {
424
+ console.error('Failed to post failure card to tracking issue:', err.message);
425
+ }
426
+ }
427
+
428
+ process.exit(1);
429
+ }
430
+
431
+ process.exit(exitCode);
432
+ }
433
+
434
+ if (process.argv[1] && process.argv[1].endsWith('run-with-loop-guard.js')) {
435
+ main().catch((err) => {
436
+ console.error('Fatal wrapper error:', err);
437
+ process.exit(1);
438
+ });
439
+ }
@@ -77,8 +77,17 @@ Show counts and a one-line summary per item. Let the maintainer pick.
77
77
 
78
78
  5. **Apply the outcome:**
79
79
  - `ready-for-agent`: post an agent brief comment ([AGENT-BRIEF.md](AGENT-BRIEF.md)).
80
- - `ready-for-human`: same structure as an agent brief, but note why it can't be delegated (judgment calls, external access, design decisions, manual testing).
81
- - `needs-info`: post triage notes (template below).
80
+ - `ready-for-human`: same structure as an agent brief, but note why it can't be delegated (judgment calls, external access, design decisions, manual testing) and format the maintainer escalation using the mandatory 4-part escalation card:
81
+ ```markdown
82
+ ## 🛑 Escalation: Human Decision Required
83
+ - **Decision Needed**: [1 focused question or choice]
84
+ - **Evidence ("Why I believe this")**: [Specific files, lines, test outputs, or conflicting docs]
85
+ - **Evaluated Options & Trade-offs**:
86
+ - *Option A*: [Pros / Cons]
87
+ - *Option B*: [Pros / Cons]
88
+ - **Recommended Path**: [Agent recommendation]
89
+ ```
90
+ - `needs-info`: post triage notes formatted with the mandatory 4-part escalation card (template below).
82
91
  - For `wontfix`, close the issue, with the comment depending on *why*:
83
92
  - **Already implemented**: the change already exists in the codebase. Point to where it lives; do **not** write to `.out-of-scope/` (that KB is for *rejected* requests, not built ones).
84
93
  - **Rejected (bug)**: give a polite explanation, then close.
@@ -92,20 +101,16 @@ If the maintainer says "move #42 to ready-for-agent", trust them and apply the r
92
101
  ## Needs-info template
93
102
 
94
103
  ```markdown
95
- ## Triage Notes
96
-
97
- **What we've established so far:**
98
-
99
- - point 1
100
- - point 2
101
-
102
- **What we still need from you (@reporter):**
103
-
104
- - question 1
105
- - question 2
104
+ ## 🛑 Escalation: Human Decision Required
105
+ - **Decision Needed**: [1 focused question or choice]
106
+ - **Evidence ("Why I believe this")**: [Specific files, lines, test outputs, or conflicting docs]
107
+ - **Evaluated Options & Trade-offs**:
108
+ - *Option A*: [Pros / Cons]
109
+ - *Option B*: [Pros / Cons]
110
+ - **Recommended Path**: [Agent recommendation]
106
111
  ```
107
112
 
108
- Capture everything resolved during grilling under "established so far" so the work isn't lost. Questions must be specific and actionable, not "please provide more info".
113
+ Capture everything resolved during grilling under the evidence and options sections so the work isn't lost. Questions must be specific and actionable, not "please provide more info".
109
114
 
110
115
  ## Resuming a previous session
111
116