jonah-fleet 1.19.0 → 1.21.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,28 @@ 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.21.0](https://github.com/juliendurandeu/jonah-fleet/compare/v1.20.0...v1.21.0) (2026-09-20)
9
+
10
+
11
+ ### Features
12
+
13
+ * **guard:** exempt manage_task status polling from loop guard and expedite routine failure pruning ([#305](https://github.com/juliendurandeu/jonah-fleet/issues/305)) ([d6e5670](https://github.com/juliendurandeu/jonah-fleet/commit/d6e567029016d4bebaaab8d517ce1daea60b5a3c))
14
+
15
+ ## [Unreleased]
16
+
17
+ ### Features
18
+
19
+ * **guard:** exempt `manage_task` status and list polling from action repetition and ping-pong circuit breaker trips in `run-with-loop-guard.js`.
20
+ * **housekeeping:** close `status:failure` routine log issues immediately (0-hour delay) if target is already closed or merged, and lower untargeted runner crash threshold to 12 hours.
21
+ * **autowork:** update Step 9 idle sweep to close resolved-target failures immediately and untargeted crashes after 12 hours.
22
+
23
+ ## [1.20.0](https://github.com/juliendurandeu/jonah-fleet/compare/v1.19.0...v1.20.0) (2026-09-20)
24
+
25
+
26
+ ### Features
27
+
28
+ * **orchestration:** introduce question-driven telemetry guardrails and 3-tier measurement taxonomy ([#293](https://github.com/juliendurandeu/jonah-fleet/issues/293)) ([23cd994](https://github.com/juliendurandeu/jonah-fleet/commit/23cd994c7e928e2d57b785965a507e115cda1cc7))
29
+
8
30
  ## [1.19.0](https://github.com/juliendurandeu/jonah-fleet/compare/v1.18.0...v1.19.0) (2026-09-20)
9
31
 
10
32
 
@@ -26,19 +48,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
26
48
 
27
49
  * **orchestration:** adopt 4-part human escalation card in peer-review routine ([#235](https://github.com/juliendurandeu/jonah-fleet/issues/235)) ([#256](https://github.com/juliendurandeu/jonah-fleet/issues/256)) ([053ef9b](https://github.com/juliendurandeu/jonah-fleet/commit/053ef9b48b7d19b1a503f59c9755b74b99624cc8))
28
50
 
29
- ## [Unreleased]
30
-
31
- ### Features
32
-
33
- * **guard:** execute routine GitHub Actions workflows via run-with-loop-guard wrapper script ([#227](https://github.com/juliendurandeu/jonah-fleet/issues/227))
34
- * **orchestration:** adopt 4-part human escalation card in peer-review routine on round-5 escalation ([#235](https://github.com/juliendurandeu/jonah-fleet/issues/235))
35
- * **orchestration:** add Routine Run Failure Ingestion & Auto-Closure Protocol to `ORCHESTRATION.md`, unifying prior failure memory and resolution lifecycle across `autowork` and `peer-review`.
36
- * **prompts:** add Dynamic Target Binding in Scan mode to `autowork.md` and `peer-review.md`, dynamically updating `$ROUTINE_ISSUE_NUMBER` titles with `(Issue #N)` or `(PR #M)` upon claiming so crashed runs are never anonymous.
37
- * **prompts:** add Prior Failure Ingestion Protocol to `autowork.md` and `peer-review.md`, inspecting past open failed routine issues to extract error logs and crash milestones before implementation or review.
38
- * **prompts:** add resolution-triggered auto-closure to `autowork.md` and `peer-review.md`, automatically commenting on and closing past failed routine issues for a target once work is marked ready or merged.
39
- * **housekeeping:** expand `issues-housekeeping.md` to sweep and close stale untargeted `status:failure` routine issues older than 48 hours or whose targets are resolved, eliminating backlog clutter.
40
- * **autowork:** add opportunistic operational log sweep (idle GC) to Step 9 in `autowork.md`, cleaning up stale untargeted failures when the issue backlog is clear.
41
-
42
51
  ## [1.16.0](https://github.com/juliendurandeu/jonah-fleet/compare/v1.15.1...v1.16.0) (2026-09-19)
43
52
 
44
53
 
package/dist/index.js CHANGED
@@ -3585,6 +3585,7 @@ var LoopGuard = class {
3585
3585
  if (this.trippedResult) {
3586
3586
  return this.trippedResult;
3587
3587
  }
3588
+ const isStatusPolling = toolName === "manage_task" && (args?.Action === "status" || args?.action === "status" || args?.Action === "list" || args?.action === "list");
3588
3589
  const hash = computeActionHash(toolName, args);
3589
3590
  const record = {
3590
3591
  toolName,
@@ -3616,43 +3617,50 @@ var LoopGuard = class {
3616
3617
  this.history.push(record);
3617
3618
  const windowStart = Math.max(0, this.history.length - this.slidingWindowSize);
3618
3619
  const currentWindow = this.history.slice(windowStart);
3619
- let repetitionCount = 0;
3620
- for (const item of currentWindow) {
3621
- if (item.hash === hash) {
3622
- repetitionCount++;
3623
- }
3624
- }
3625
- if (repetitionCount >= this.repetitionThreshold) {
3626
- return this.triggerTrip({
3627
- reason: "repetition",
3628
- message: `Action repetition loop detected: tool '${toolName}' called ${repetitionCount} times with identical parameters within sliding window of ${this.slidingWindowSize}.`,
3629
- toolName,
3630
- actionHash: hash,
3631
- count: repetitionCount
3632
- });
3620
+ if (!isStatusPolling) {
3621
+ let repetitionCount = 0;
3622
+ for (const item of currentWindow) {
3623
+ if (item.hash === hash) {
3624
+ repetitionCount++;
3625
+ }
3626
+ }
3627
+ if (repetitionCount >= this.repetitionThreshold) {
3628
+ return this.triggerTrip({
3629
+ reason: "repetition",
3630
+ message: `Action repetition loop detected: tool '${toolName}' called ${repetitionCount} times with identical parameters within sliding window of ${this.slidingWindowSize}.`,
3631
+ toolName,
3632
+ actionHash: hash,
3633
+ count: repetitionCount
3634
+ });
3635
+ }
3633
3636
  }
3634
3637
  const requiredPingPongLength = this.pingPongThreshold * 2;
3635
3638
  if (this.history.length >= requiredPingPongLength) {
3636
3639
  const pingPongSlice = this.history.slice(-requiredPingPongLength);
3637
- const hashA = pingPongSlice[0].hash;
3638
- const hashB = pingPongSlice[1].hash;
3639
- if (hashA !== hashB) {
3640
- let isPingPong = true;
3641
- for (let i = 0; i < requiredPingPongLength; i++) {
3642
- const expectedHash = i % 2 === 0 ? hashA : hashB;
3643
- if (pingPongSlice[i].hash !== expectedHash) {
3644
- isPingPong = false;
3645
- break;
3640
+ const hasStatusPolling = pingPongSlice.some(
3641
+ (item) => item.toolName === "manage_task" && (item.args?.Action === "status" || item.args?.action === "status" || item.args?.Action === "list" || item.args?.action === "list")
3642
+ );
3643
+ if (!hasStatusPolling) {
3644
+ const hashA = pingPongSlice[0].hash;
3645
+ const hashB = pingPongSlice[1].hash;
3646
+ if (hashA !== hashB) {
3647
+ let isPingPong = true;
3648
+ for (let i = 0; i < requiredPingPongLength; i++) {
3649
+ const expectedHash = i % 2 === 0 ? hashA : hashB;
3650
+ if (pingPongSlice[i].hash !== expectedHash) {
3651
+ isPingPong = false;
3652
+ break;
3653
+ }
3654
+ }
3655
+ if (isPingPong) {
3656
+ return this.triggerTrip({
3657
+ reason: "ping_pong",
3658
+ message: `Alternating ping-pong action loop detected: ${this.pingPongThreshold} consecutive alternating cycles between tools (A-B-A-B-A-B).`,
3659
+ toolName,
3660
+ actionHash: hash,
3661
+ count: this.pingPongThreshold
3662
+ });
3646
3663
  }
3647
- }
3648
- if (isPingPong) {
3649
- return this.triggerTrip({
3650
- reason: "ping_pong",
3651
- message: `Alternating ping-pong action loop detected: ${this.pingPongThreshold} consecutive alternating cycles between tools (A-B-A-B-A-B).`,
3652
- toolName,
3653
- actionHash: hash,
3654
- count: this.pingPongThreshold
3655
- });
3656
3664
  }
3657
3665
  }
3658
3666
  }
@@ -1 +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"}
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,CAiH/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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jonah-fleet",
3
- "version": "1.19.0",
3
+ "version": "1.21.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",
@@ -47,6 +47,15 @@ tests/ # Test suites
47
47
  - **Zero-Guesswork Ambiguity Gate**: Never make silent assumptions about underspecified requirements, schemas, edge cases, or UX flows. Stop and ask clarifying questions before implementation.
48
48
  - **Structured Clarifications & Grilling**: When asking questions, propose 1–2 high-leverage questions at a time with concrete options or trade-offs. For complex features or fuzzy requirements, invoke `/grill-me` to stress-test the design before writing code.
49
49
 
50
+ ## Telemetry & Measurement Intent
51
+
52
+ - **Question-First Principle**: Every custom analytics event must answer an explicit question that influences a product, architectural, or growth decision. If observing an event would never change what is built, prioritized, or retired, do NOT instrument it.
53
+ - **3-Tier Telemetry Taxonomy**:
54
+ - **Tier 1 (Core Funnel & Growth Levers)**: Acquisition, activation, retention, referral, revenue (North Star metrics). Instrumentation is **mandatory and rigorous**: must include impression denominator (`_shown`), interaction CTA (`_clicked`), outcome/completion (`_completed`), and structured failure/abandonment branches (`_failed` with `reason`). Must pair with a companion `Measure: ...` tracking issue.
55
+ - **Tier 2 (Exploratory Product UX & Feature Flags)**: Secondary features and discovery surfaces. Instrument only when tied to an explicit hypothesis or UX discovery question in the issue specification.
56
+ - **Tier 3 (Passive Chrome & Utility / Easter Eggs)**: Cosmetic toggles, credits popovers, dismiss buttons, informational tooltips. **Explicitly exempt from custom events**; rely on autocapture, web vitals, or session replay if diagnostic data is ever needed.
57
+ - **Zero Vanity Tracking Guardrail**: Never emit telemetry purely for checklist compliance. Avoid adding arbitrary click trackers to passive or cosmetic UI surfaces.
58
+
50
59
  ## Conventions
51
60
 
52
61
  - **Language**: All code, comments, commit messages, and PR titles must be written in **English**.
@@ -168,9 +168,9 @@ Single source of truth for handling routine run failures, context extraction, an
168
168
  ```
169
169
  (or `PR #<PR_NUMBER>`). If prior failed runs exist, the runner extracts error messages, failing test names, or crash milestones from the Interruption Card and incorporates this context into Milestone 1 and its reproduction strategy, preventing repeated identical failures.
170
170
  3. **Resolution-Triggered Auto-Closure**: When a target issue or PR reaches terminal completion (a ready PR opened, review decision executed, or PR merged), the routine iterates through any open past failed routine issues for that target, posts a resolution comment referencing the successful run (including the Antigravity run footer), and closes them via `gh issue close <ISSUE> --reason completed`.
171
- 4. **Garbage Collection for Untargeted / Stale Crashes (Housekeeping & Idle Autowork)**: Routine runs that fail before claiming a target (e.g. runner VM startup errors, GitHub CLI auth failures) or whose target has already been resolved are audited and closed once older than 48 hours by:
172
- - `issues-housekeeping.md` during scheduled backlog maintenance sweeps.
173
- - `autowork.md` Step 9 as an opportunistic idle sweep when no eligible unclaimed work exists, ensuring repositories running minimal presets without dedicated housekeeping crons stay pristine.
171
+ 4. **Garbage Collection for Untargeted / Stale Crashes (Housekeeping & Idle Autowork)**: Routine failure runs are audited and closed by `issues-housekeeping.md` (scheduled sweeps) and `autowork.md` Step 9 (opportunistic idle sweep):
172
+ - **Target Resolved**: If the routine failure references a target issue or PR that is already closed or merged, it is closed **immediately** (0-hour delay).
173
+ - **Untargeted Runner Crash**: Routine runs that fail before claiming a target (e.g. runner VM startup errors, GitHub CLI auth failures, or generic crashes without a target) are closed once older than **12 hours**.
174
174
 
175
175
  ---
176
176
 
@@ -104,6 +104,7 @@ a. **Read the target issue and check eligibility.** Eligible = open, unassigned
104
104
  - **Automated review passes**: run `/code-review` (evaluating along Standards in `AGENTS.md` and Spec in the issue's `## Tasks`) and security review over the diff. Fix what they flag.
105
105
  - **Repository conventions scan**: read and verify all rules and conventions specified in `AGENTS.md` (or `CLAUDE.md`/`GEMINI.md`), project-level skills in `.agents/skills/`, and project documentation.
106
106
  - **Design System & Viewport Pre-flight** (for frontend/UI diffs): self-audit diffs against design tokens (no arbitrary class overrides), WCAG AA 4.5:1 contrast ratios on dark/light surfaces, single primary CTA hierarchy per screen, and mobile viewport crowding (avoid stacked nudges/banners above the fold at ~390px).
107
+ - **Telemetry Contract Audit**: verify that any new interactive surfaces adhere to the 3-Tier Telemetry Taxonomy in `AGENTS.md`: Tier 1/2 flows must have question-backed events with impression denominators (`_shown`) and documented properties in the event catalog (`ARCHITECTURE.md`), while Tier 3 utility/cosmetic surfaces must be explicitly exempt with zero vanity click-trackers.
107
108
  - **Documentation accuracy**: update relevant docs (`ARCHITECTURE.md`, `CODEMAP.md`, `API.md`, `CHANGELOG.md` if maintained by repo).
108
109
  - **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.
109
110
  - **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.
@@ -171,10 +172,10 @@ _Generated by [Antigravity](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/ru
171
172
  STALE_FAILURES=$(gh issue list --label routine-log,status:failure --state open --limit 20 --json number,title,body,createdAt || true)
172
173
  ```
173
174
  For each stale failure issue:
174
- - Check if it is untargeted (no `(Issue #N)` or `(PR #M)` in title/body) and older than 48 hours, OR if its referenced target issue or PR is already closed or merged.
175
+ - Check if it is untargeted (no `(Issue #N)` or `(PR #M)` in title/body) and older than 12 hours, OR if its referenced target issue or PR is already closed or merged.
175
176
  - If eligible, close it with a resolution note:
176
177
  ```bash
177
- gh issue comment "$stale_num" --body "Closed by autowork idle sweep: routine failure superseded or untargeted crash older than 48 hours.\n\n_Generated by [Antigravity](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID:-})_" || true
178
+ gh issue comment "$stale_num" --body "Closed by autowork idle sweep: routine failure superseded or untargeted crash.\n\n_Generated by [Antigravity](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID:-})_" || true
178
179
  gh issue close "$stale_num" --reason completed || true
179
180
  ```
180
181
  - STOP — run is SUCCESS with "No unclaimed work available; swept stale operational logs".
@@ -220,6 +221,10 @@ _Generated by [Antigravity](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/ru
220
221
  - Apply the `needs-info` label and release the claim (unassign).
221
222
  - Select the next candidate (evaluating ambiguous issues counts toward step 12's infeasible-continuation cap).
222
223
  - **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.
224
+ - **Telemetry Design & Measurement Contract Gate**: When implementing new features or interactive surfaces, classify them according to the 3-Tier Telemetry Taxonomy in `AGENTS.md`:
225
+ - **Tier 1 (Core Funnel & Levers)**: You MUST implement the complete measurement contract: impression denominator (`_shown`), interaction CTA (`_clicked`), completion (`_completed`), and structured failure/abandonment branches (`_failed` with `reason`). Update the repository's custom events catalog (`ARCHITECTURE.md` / `DATA_CONTEXT.md`).
226
+ - **Tier 2 (Exploratory UX)**: Instrument only if the issue specification explicitly defines a hypothesis, metric question, or companion `Measure: ...` issue.
227
+ - **Tier 3 (Passive Chrome & Utility / Easter Eggs)**: Do NOT add custom events or click trackers for cosmetic easter eggs, footer attribution popovers, theme toggles, or standard modal dismissals. Keep the codebase free from vanity tracking noise.
223
228
  - 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.
224
229
  12a. **Umbrella-issue handoff + batching:** If candidate is an umbrella epic:
225
230
  - Read `🧭 Decomposition plan` comment (or create if first run).
@@ -23,7 +23,7 @@ The run is SUCCESS only if ALL of these are true:
23
23
  - [ ] Label audit completed: every open issue has consistent type, size, and priority labels
24
24
  - [ ] Dependency check completed: issues with `## Dependencies` verified against blocker status
25
25
  - [ ] Orphaned-claim sweep completed: stale autowork claims (per `ORCHESTRATION.md`) released back to the unclaimed pool
26
- - [ ] Stalled routine run audit and stale failure sweep completed: routine log issues in `status:running` older than 6 hours marked as `status:failure`; untargeted or already-resolved `status:failure` routine log issues older than 48 hours closed with resolution summary
26
+ - [ ] Stalled routine run audit and stale failure sweep completed: routine log issues in `status:running` older than 6 hours marked as `status:failure`; `status:failure` routine log issues referencing an already closed/merged target closed immediately; untargeted `status:failure` runner crash issues older than 12 hours closed with resolution summary
27
27
  - [ ] Summary posted listing all changes made
28
28
 
29
29
  If any criterion cannot be met, stop immediately and log FAILURE with the reason.
@@ -41,7 +41,9 @@ If any criterion cannot be met, stop immediately and log FAILURE with the reason
41
41
 
42
42
  1. **Stalled routine run and stale failure sweep**:
43
43
  - **Stalled runs**: Check open issues with labels `routine-log` and `status:running`. If an issue has been in `status:running` for > 6 hours without updates, add label `status:failure`, remove `status:running`, add label `needs-attention`, and comment noting the runner timeout or crash.
44
- - **Stale / untargeted failures**: Check open issues with labels `routine-log` and `status:failure`. If an issue is older than 48 hours and either: (i) has no target issue or PR in its title or body (an untargeted infrastructure crash), or (ii) references a target issue or PR that has already been closed or merged, close the routine-log issue with comment "Closed: routine failure superseded or resolved.\n\n_Generated by [Antigravity](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID:-})_" and reason completed.
44
+ - **Stale / untargeted failures**: Check open issues with labels `routine-log` and `status:failure`. Reconcile and close them as follows:
45
+ - If the routine log references a target issue or PR that has already been closed or merged: close the routine-log issue **immediately** (0-hour delay) with comment "Closed: target issue/PR has been resolved.\n\n_Generated by [Antigravity](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID:-})_" and reason completed.
46
+ - If the routine log has no target issue or PR in its title or body (an untargeted runner crash): if older than 12 hours, close the routine-log issue with comment "Closed: untargeted routine failure superseded.\n\n_Generated by [Antigravity](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID:-})_" and reason completed.
45
47
  2. **Orphaned-claim sweep**: Sweep assigned issues. If an issue meets the 3 stale-claim conditions in `ORCHESTRATION.md` (autowork claim comment, no open PR, comment > 6 hours old), re-read immediately before writing, unassign the dead owner, and post a release comment.
46
48
 
47
49
  ### Phase 2: Backlog Hygiene
@@ -131,8 +131,11 @@ Check if `$PR_NUMBER` is set:
131
131
  - **WCAG AA Contrast**: Enforce minimum 4.5:1 contrast on readable text (e.g. `text-gray-400` minimum on dark surfaces; avoid muted unreadable text).
132
132
  - **CTA Hierarchy**: Verify at most one primary forward CTA (`.btn-primary`) per active screen/tab.
133
133
  - **Mobile Viewport Budget**: Verify that new banners, nudges, or sticky elements do not stack concurrently above the fold on mobile viewports (~390px).
134
- 4. If PR modifies rendered UI, verify screenshots or visual components if tooling/scripts are available.
135
- 5. Run repository verification commands (tests, type-check) ONLY if CI status is unconfirmed or failing. If remote CI (such as GitHub Actions or Vercel preview) is already green and passing on the head commit, treat CI as verified. If verification commands must be run locally, specify `WaitMsBeforeAsync: 10000` and NEVER call `schedule` or yield the turn with plain text while waiting for background tasks.
134
+ 4. **Telemetry & Measurement Contract Pass**:
135
+ - **Question-First Validation**: Verify that any new custom analytics events answer a clear, decision-driving question per the 3-Tier Taxonomy in `AGENTS.md`. Flag vanity click-trackers on Tier 3 passive chrome/easter eggs as non-blocking cleanup (recommend removal).
136
+ - **Funnel Completeness**: For new Tier 1 user funnels or critical workflows, verify the telemetry contract: impression denominator (`_shown`), interaction (`_clicked`), and failure states (`_failed` with `reason`) are captured, and events are documented in the custom events catalog (`ARCHITECTURE.md` / `DATA_CONTEXT.md`). Flag missing denominators on core funnels as blocking.
137
+ 5. If PR modifies rendered UI, verify screenshots or visual components if tooling/scripts are available.
138
+ 6. Run repository verification commands (tests, type-check) ONLY if CI status is unconfirmed or failing. If remote CI (such as GitHub Actions or Vercel preview) is already green and passing on the head commit, treat CI as verified. If verification commands must be run locally, specify `WaitMsBeforeAsync: 10000` and NEVER call `schedule` or yield the turn with plain text while waiting for background tasks.
136
139
 
137
140
  ### Step 5: Classify Findings & Make Decision
138
141
 
@@ -63,6 +63,13 @@ export class LoopGuard {
63
63
  recordAction(toolName, args, isError = false) {
64
64
  if (this.trippedResult) return this.trippedResult;
65
65
 
66
+ const isStatusPolling =
67
+ toolName === 'manage_task' &&
68
+ (args?.Action === 'status' ||
69
+ args?.action === 'status' ||
70
+ args?.Action === 'list' ||
71
+ args?.action === 'list');
72
+
66
73
  const hash = computeActionHash(toolName, args);
67
74
  const record = {
68
75
  toolName,
@@ -98,51 +105,64 @@ export class LoopGuard {
98
105
  this.history.push(record);
99
106
 
100
107
  // 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
+ if (!isStatusPolling) {
109
+ const windowStart = Math.max(0, this.history.length - this.slidingWindowSize);
110
+ const currentWindow = this.history.slice(windowStart);
111
+
112
+ let repetitionCount = 0;
113
+ for (const item of currentWindow) {
114
+ if (item.hash === hash) {
115
+ repetitionCount++;
116
+ }
108
117
  }
109
- }
110
118
 
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
+ if (repetitionCount >= this.repetitionThreshold) {
120
+ return this.triggerTrip({
121
+ reason: 'repetition',
122
+ message: `Action repetition loop detected: tool '${toolName}' called ${repetitionCount} times with identical parameters within sliding window of ${this.slidingWindowSize}.`,
123
+ toolName,
124
+ actionHash: hash,
125
+ count: repetitionCount,
126
+ });
127
+ }
119
128
  }
120
129
 
121
130
  // 3. Ping-Pong Guard: 3 consecutive alternating pairs (length 6)
122
131
  const requiredPingPongLength = this.pingPongThreshold * 2;
123
132
  if (this.history.length >= requiredPingPongLength) {
124
133
  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;
134
+ const hasStatusPolling = pingPongSlice.some(
135
+ (item) =>
136
+ item.toolName === 'manage_task' &&
137
+ (item.args?.Action === 'status' ||
138
+ item.args?.action === 'status' ||
139
+ item.args?.Action === 'list' ||
140
+ item.args?.action === 'list')
141
+ );
142
+
143
+ if (!hasStatusPolling) {
144
+ const hashA = pingPongSlice[0].hash;
145
+ const hashB = pingPongSlice[1].hash;
146
+
147
+ if (hashA !== hashB) {
148
+ let isPingPong = true;
149
+ for (let i = 0; i < requiredPingPongLength; i++) {
150
+ const expectedHash = i % 2 === 0 ? hashA : hashB;
151
+ if (pingPongSlice[i].hash !== expectedHash) {
152
+ isPingPong = false;
153
+ break;
154
+ }
135
155
  }
136
- }
137
156
 
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
- });
157
+ if (isPingPong) {
158
+ return this.triggerTrip({
159
+ reason: 'ping_pong',
160
+ message: `Alternating ping-pong action loop detected: ${this.pingPongThreshold} consecutive alternating cycles between tools (A-B-A-B-A-B).`,
161
+ toolName,
162
+ actionHash: hash,
163
+ count: this.pingPongThreshold,
164
+ });
165
+ }
146
166
  }
147
167
  }
148
168
  }
@@ -64,6 +64,19 @@ A list of testing decisions that were made. Include:
64
64
  - Which modules will be tested
65
65
  - Prior art for the tests (i.e. similar types of tests in the codebase)
66
66
 
67
+ ## Telemetry & Measurement Decisions
68
+
69
+ A list of measurement decisions for this feature based on the 3-Tier Telemetry Taxonomy:
70
+
71
+ - **Tier Classification**: State whether this surface is Tier 1 (Core Funnel & Growth Levers), Tier 2 (Exploratory Product UX), or Tier 3 (Passive Chrome & Utility / Easter Eggs).
72
+ - **If Tier 1 or Tier 2**:
73
+ - **Question this answers**: What concrete product, growth, or UX decision will this data influence?
74
+ - **Metric & Denominator**: What is the impression denominator (e.g. `_shown`) and target metric defining success or failure?
75
+ - **Events & Properties**: Required event names (`snake_case`), triggers, key properties, and structured failure/abandonment states (`reason`).
76
+ - **Measurement issue**: Outline the companion `Measure: ...` tracking issue to be filed upon merge.
77
+ - **If Tier 3**:
78
+ - Explicitly declare: `Telemetry: None (Tier 3 utility/cosmetic — no decision value).` Do NOT add vanity click trackers.
79
+
67
80
  ## Out of Scope
68
81
 
69
82
  A description of the things that are out of scope for this spec.