jonah-fleet 1.30.0 → 1.30.2

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,20 @@ 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.30.2](https://github.com/Jonah-Projects/jonah-fleet/compare/v1.30.1...v1.30.2) (2026-09-30)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **workflows:** respect routine toggle flags in agents-manifest.json during scheduled cron runs ([#509](https://github.com/Jonah-Projects/jonah-fleet/issues/509)) ([dfb188a](https://github.com/Jonah-Projects/jonah-fleet/commit/dfb188a7a4189875e6fb61efb8ea0f0093bbed65))
14
+
15
+ ## [1.30.1](https://github.com/Jonah-Projects/jonah-fleet/compare/v1.30.0...v1.30.1) (2026-09-25)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **analytics-review:** forbid staging into or commenting on closed product plans ([e945366](https://github.com/Jonah-Projects/jonah-fleet/commit/e945366f8d645d54b3c3de2c9bf7d8c4aaa8cf5f))
21
+
8
22
  ## [1.30.0](https://github.com/Jonah-Projects/jonah-fleet/compare/v1.29.0...v1.30.0) (2026-09-25)
9
23
 
10
24
 
@@ -48,6 +62,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
48
62
 
49
63
  ### Bug Fixes
50
64
 
65
+ * **analytics-review:** require active product planning staging issues to be strictly open (`--state open`), forbid commenting on or editing closed product plans from past planning sweeps, and make DoD staging conditional on having emitted directives
51
66
  * **runner:** introduce granular tool output evaluation descriptions, live subagent progress tracking, and non-TTY spinner deduplication to replace long-running generic `Evaluating tool output...` states
52
67
  * **autowork:** prevent local and GitHub-hosted autowork runner collisions by excluding bot and self-assignments from `trigger-autowork-on-assign.yml`, enforcing a 2-hour active claim lock in Step 0.5a / Step 11a, and mandating concrete pre-PR collision checks with collision bail in Step 13
53
68
  * **daemon:** query `statusCheckRollup` in `getOpenReviewablePRs` and filter out PRs with in-progress CI to prevent premature review dispatch and repetition loop trips
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jonah-fleet",
3
- "version": "1.30.0",
3
+ "version": "1.30.2",
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",
@@ -261,7 +261,7 @@ How changes and innovations from upstream ecosystems—[openai/symphony](https:/
261
261
 
262
262
  How closed-loop feedback from product telemetry and measurement trackers drives product decisions while preventing autonomous agents from falling into telemetry rabbit holes:
263
263
 
264
- 1. **Mandatory Post-Measurement Action Rule**: When `analytics-review` concludes an evaluation of a feature or experiment tracker (especially sub-threshold features with <2% user adoption or >50% failure rates), it MUST emit a structured directive: `RECOMMENDATION: [PIVOT | DEPRECATE | ITERATE]`. Measurement closure is never a terminal dead-end; outcomes are staged into the active `🗺️ Product Plan` or actionable `roadmap/*` issues.
264
+ 1. **Mandatory Post-Measurement Action Rule**: When `analytics-review` concludes an evaluation of a feature or experiment tracker (especially sub-threshold features with <2% user adoption or >50% failure rates), it MUST emit a structured directive: `RECOMMENDATION: [PIVOT | DEPRECATE | ITERATE]`. Measurement closure is never a terminal dead-end; outcomes are staged into an active `🗺️ Product Plan` (which must be currently `state: open`; agents must never stage into or comment on closed/past product plans) or actionable `roadmap/*` issues.
265
265
  2. **Feature Pruning & Deprecation Audit**: During Propose mode sweeps, `product-planning` actively audits shipped roadmap items and closed measurement trackers. For features with low ROI (<2% adoption, >50% error/failure rate), it drafts explicit deprecation, removal, or simplification proposals alongside new feature additions, keeping the codebase and UI lean.
266
266
  3. **Intent vs. Defect Guardrail**: When investigating underperforming or zero-conversion features, `autowork` and `diagnosing-bugs` verify whether the issue is a software defect or a lack of user intent. If the feature functions properly without runtime errors but user interaction is negligible, agents must classify the issue as a **product/UX intent question** (`needs-design` / `roadmap/*`) rather than falling into the **Telemetry Rabbit Hole** (adding redundant fallback telemetry, defensive error handling, or retry loops for unwanted features).
267
267
  4. **Post-Merge Measurement Tracker Ingestion**: When `peer-review` squash-merges an approved PR, it inspects the tracking issue or spec for `## Telemetry & Measurement Decisions`. If a Tier 1 or Tier 2 companion measurement issue is outlined (`Measure: <Feature Name>`), `peer-review` automatically files the tracker with the `measurement` label upon merge, ensuring downstream `analytics-review` sweeps are never starved of intake.
@@ -20,7 +20,7 @@ The run is SUCCESS only if ALL of these are true:
20
20
  - [ ] For conclusive measurement issues: synthesized adoption metrics (CTR, conversion rate, error frequency) against defined success criteria
21
21
  - [ ] For sub-threshold features (<2% user adoption or >50% failure rate) or conclusive outcomes, outputted a structured directive: `RECOMMENDATION: [PIVOT | DEPRECATE | ITERATE]`
22
22
  - [ ] Evaluated UI friction signals (grouping `$rageclick` by URL and element) and applied the Nudge & Banner Fatigue rule for promotional elements exceeding 500 impressions with CTR < 2.0%
23
- - [ ] Staged the recommendation directly into the current/next `🗺️ Product Plan` staging issue or filed an actionable `roadmap/*` issue for `product-planning` to review
23
+ - [ ] For conclusive measurement trackers or sub-threshold features: staged the recommendation directly into an active, open `🗺️ Product Plan` staging issue or filed an actionable `roadmap/*` issue for `product-planning` to review; if all trackers remain active/healthy with no new directives, logged scan status to the routine run issue
24
24
  - [ ] Closed resolved measurement trackers with full analysis summaries and Antigravity run footer
25
25
 
26
26
  If any criterion cannot be met, stop immediately and log FAILURE with the reason.
@@ -37,6 +37,8 @@ If any criterion cannot be met, stop immediately and log FAILURE with the reason
37
37
 
38
38
  - Do not close a measurement tracker with a failed adoption verdict without emitting a mandatory `RECOMMENDATION: [PIVOT | DEPRECATE | ITERATE]` directive.
39
39
  - Do not treat measurement closure as a terminal dead-end — always bridge outcomes to `product-planning`.
40
+ - Do not comment on, edit, or stage directives into closed product plans (`state: closed`) from past planning sweeps. Once closed, product plans are completed historical records and must never receive new comments.
41
+ - Do not search issues with `--state all` when querying for active product plans; active plans are strictly open issues (`--state open`).
40
42
  - Do not file bug tickets or request additional telemetry when users simply lack intent to interact with a feature.
41
43
 
42
44
  ## Instructions
@@ -96,8 +98,20 @@ When a measurement tracker is conclusive or reaches sub-threshold adoption (<2%
96
98
 
97
99
  ### Step 4: Bridge outcome to `product-planning`
98
100
 
99
- 1. If an active staging issue `🗺️ Product Plan — {YYYY-MM-DD}` exists, stage the recommendation under `### Feature Pruning & Deprecation Audit` or `### Backlog Re-rankings`.
100
- 2. If no staging issue exists, open an actionable `roadmap/*` issue labeled `needs-design` or `enhancement` detailing the measurement verdict and recommendation for the next product planning sweep.
101
+ When conclusive measurement directives (`RECOMMENDATION: [PIVOT | DEPRECATE | ITERATE]`) are emitted:
102
+ 1. Search for an **active, OPEN** product planning staging issue:
103
+ ```bash
104
+ gh issue list --state open --search "Product Plan"
105
+ ```
106
+ **Active Staging Issue Invariant**: A staging issue is active ONLY if its state is currently **OPEN**. If an issue is closed, it is a historical plan from a past cycle and MUST be ignored. **NEVER comment on, reopen, or edit closed product plan issues (`state: closed`).**
107
+ 2. If an open staging issue exists (e.g. `🗺️ Product Plan — {YYYY-MM-DD}`):
108
+ - Stage the recommendation under its `### Feature Pruning & Deprecation Audit` or `### Backlog Re-rankings` section.
109
+ 3. If no open staging issue exists (the expected state between planning sweeps):
110
+ - Do NOT comment on past closed product plans.
111
+ - For actionable pivots, deprecations, or feature modifications requiring planning review, open an actionable `roadmap/*` issue labeled `needs-design` or `enhancement` detailing the measurement verdict and recommendation for the next product planning sweep.
112
+ 4. If no trackers reached a conclusive state during this run and all evaluated trackers remain healthy/within measurement horizon:
113
+ - Do not stage redundant directives or file unnecessary roadmap issues.
114
+ - Record tracker evaluation progress in the routine run issue (`$ROUTINE_ISSUE_NUMBER`) and `.jonah-fleet/run-report.md`.
101
115
 
102
116
  ### Step 5: Close conclusive measurement trackers
103
117
 
@@ -453,6 +453,11 @@ async function main() {
453
453
  }
454
454
  } else if (parsed.event === 'result' && parsed.result?.response) {
455
455
  console.log(`\n${parsed.result.response}\n`);
456
+ } else if (parsed.event === 'error' || parsed.error) {
457
+ const errMsg =
458
+ parsed.error?.message ||
459
+ (typeof parsed.error === 'string' ? parsed.error : JSON.stringify(parsed.error || parsed));
460
+ console.error(`\n[agent:error] ${errMsg}`);
456
461
  }
457
462
  return;
458
463
  }
@@ -128,6 +128,7 @@ jobs:
128
128
 
129
129
  MODEL="$DEFAULT_MODEL"
130
130
  TIMEOUT="$DEFAULT_TIMEOUT"
131
+ ENABLED="true"
131
132
 
132
133
  if [ -f "agents-manifest.json" ]; then
133
134
  CONFIG_JSON=$(node -e '
@@ -137,14 +138,16 @@ jobs:
137
138
  const r = process.argv[1];
138
139
  const model = m.models?.[r] || m.models?.default;
139
140
  const timeout = m.budgets?.timeoutMinutes?.[r] || m.budgets?.timeoutMinutes?.default;
140
- console.log(JSON.stringify({ model: model || "", timeout: timeout ? String(timeout) : "" }));
141
+ const enabled = m.routines?.[r] !== false;
142
+ console.log(JSON.stringify({ model: model || "", timeout: timeout ? String(timeout) : "", enabled }));
141
143
  } catch (e) {
142
- console.log(JSON.stringify({ model: "", timeout: "" }));
144
+ console.log(JSON.stringify({ model: "", timeout: "", enabled: true }));
143
145
  }
144
146
  ' "$ROUTINE")
145
147
 
146
148
  RESOLVED_MODEL=$(node -e "try { console.log(JSON.parse(process.argv[1]).model || ''); } catch {}" "$CONFIG_JSON")
147
149
  RESOLVED_TIMEOUT=$(node -e "try { console.log(JSON.parse(process.argv[1]).timeout || ''); } catch {}" "$CONFIG_JSON")
150
+ RESOLVED_ENABLED=$(node -e "try { console.log(JSON.parse(process.argv[1]).enabled); } catch {}" "$CONFIG_JSON")
148
151
 
149
152
  if [ -n "$RESOLVED_MODEL" ]; then
150
153
  MODEL="$RESOLVED_MODEL"
@@ -152,13 +155,22 @@ jobs:
152
155
  if [ -n "$RESOLVED_TIMEOUT" ]; then
153
156
  TIMEOUT="$RESOLVED_TIMEOUT"
154
157
  fi
158
+ if [ -n "$RESOLVED_ENABLED" ]; then
159
+ ENABLED="$RESOLVED_ENABLED"
160
+ fi
155
161
  fi
156
162
 
157
163
  echo "model=$MODEL" >> "$GITHUB_OUTPUT"
158
164
  echo "print_timeout=${TIMEOUT}m" >> "$GITHUB_OUTPUT"
159
- echo "Resolved routine configuration: model=$MODEL, timeout=${TIMEOUT}m"
165
+ echo "enabled=$ENABLED" >> "$GITHUB_OUTPUT"
166
+ echo "Resolved routine configuration: model=$MODEL, timeout=${TIMEOUT}m, enabled=$ENABLED"
167
+
168
+ if [ "$ENABLED" = "false" ] && [ "${{ github.event_name }}" = "schedule" ]; then
169
+ echo "⏸️ Routine '$ROUTINE' is disabled in agents-manifest.json. Skipping scheduled run."
170
+ fi
160
171
 
161
172
  - name: Initialize Routine Run Issue
173
+ if: steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule'
162
174
  id: run-issue
163
175
  env:
164
176
  GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
@@ -183,6 +195,7 @@ jobs:
183
195
  echo "ROUTINE_ISSUE_NUMBER=$ISSUE_NUMBER" >> "$GITHUB_ENV"
184
196
 
185
197
  - name: Run Analytics Review
198
+ if: steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule'
186
199
  timeout-minutes: 50
187
200
  env:
188
201
  GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
@@ -224,7 +237,7 @@ jobs:
224
237
  fi
225
238
 
226
239
  - name: Emit run summary
227
- if: always()
240
+ if: always() && (steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule')
228
241
  run: |
229
242
  if [ "$QUOTA_PAUSED" = "true" ]; then
230
243
  echo "### ⏸️ Milestone: Routine Execution Quota-Paused" >> "$GITHUB_STEP_SUMMARY"
@@ -238,7 +251,7 @@ jobs:
238
251
  fi
239
252
 
240
253
  - name: Emit Fleet Telemetry (Opt-in)
241
- if: always()
254
+ if: always() && (steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule')
242
255
  env:
243
256
  GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
244
257
  TELEMETRY_ENDPOINT: ${{ secrets.JONAH_FLEET_TELEMETRY_ENDPOINT }}
@@ -252,7 +265,7 @@ jobs:
252
265
  fi
253
266
 
254
267
  - name: Reconcile Routine Run Issue
255
- if: always()
268
+ if: always() && (steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule')
256
269
  env:
257
270
  GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
258
271
  ISSUE_NUMBER: ${{ steps.run-issue.outputs.issue_number }}
@@ -130,6 +130,7 @@ jobs:
130
130
 
131
131
  MODEL="$DEFAULT_MODEL"
132
132
  TIMEOUT="$DEFAULT_TIMEOUT"
133
+ ENABLED="true"
133
134
 
134
135
  if [ -f "agents-manifest.json" ]; then
135
136
  CONFIG_JSON=$(node -e '
@@ -139,14 +140,16 @@ jobs:
139
140
  const r = process.argv[1];
140
141
  const model = m.models?.[r] || m.models?.default;
141
142
  const timeout = m.budgets?.timeoutMinutes?.[r] || m.budgets?.timeoutMinutes?.default;
142
- console.log(JSON.stringify({ model: model || "", timeout: timeout ? String(timeout) : "" }));
143
+ const enabled = m.routines?.[r] !== false;
144
+ console.log(JSON.stringify({ model: model || "", timeout: timeout ? String(timeout) : "", enabled }));
143
145
  } catch (e) {
144
- console.log(JSON.stringify({ model: "", timeout: "" }));
146
+ console.log(JSON.stringify({ model: "", timeout: "", enabled: true }));
145
147
  }
146
148
  ' "$ROUTINE")
147
149
 
148
150
  RESOLVED_MODEL=$(node -e "try { console.log(JSON.parse(process.argv[1]).model || ''); } catch {}" "$CONFIG_JSON")
149
151
  RESOLVED_TIMEOUT=$(node -e "try { console.log(JSON.parse(process.argv[1]).timeout || ''); } catch {}" "$CONFIG_JSON")
152
+ RESOLVED_ENABLED=$(node -e "try { console.log(JSON.parse(process.argv[1]).enabled); } catch {}" "$CONFIG_JSON")
150
153
 
151
154
  if [ -n "$RESOLVED_MODEL" ]; then
152
155
  MODEL="$RESOLVED_MODEL"
@@ -154,13 +157,22 @@ jobs:
154
157
  if [ -n "$RESOLVED_TIMEOUT" ]; then
155
158
  TIMEOUT="$RESOLVED_TIMEOUT"
156
159
  fi
160
+ if [ -n "$RESOLVED_ENABLED" ]; then
161
+ ENABLED="$RESOLVED_ENABLED"
162
+ fi
157
163
  fi
158
164
 
159
165
  echo "model=$MODEL" >> "$GITHUB_OUTPUT"
160
166
  echo "print_timeout=${TIMEOUT}m" >> "$GITHUB_OUTPUT"
161
- echo "Resolved routine configuration: model=$MODEL, timeout=${TIMEOUT}m"
167
+ echo "enabled=$ENABLED" >> "$GITHUB_OUTPUT"
168
+ echo "Resolved routine configuration: model=$MODEL, timeout=${TIMEOUT}m, enabled=$ENABLED"
169
+
170
+ if [ "$ENABLED" = "false" ] && [ "${{ github.event_name }}" = "schedule" ]; then
171
+ echo "⏸️ Routine '$ROUTINE' is disabled in agents-manifest.json. Skipping scheduled run."
172
+ fi
162
173
 
163
174
  - name: Initialize Routine Run Issue
175
+ if: steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule'
164
176
  id: run-issue
165
177
  env:
166
178
  GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
@@ -190,6 +202,7 @@ jobs:
190
202
  echo "ROUTINE_ISSUE_NUMBER=$ISSUE_NUMBER" >> "$GITHUB_ENV"
191
203
 
192
204
  - name: Run Autowork
205
+ if: steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule'
193
206
  timeout-minutes: 60
194
207
  env:
195
208
  GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
@@ -234,7 +247,7 @@ jobs:
234
247
  fi
235
248
 
236
249
  - name: Emit run summary
237
- if: always()
250
+ if: always() && (steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule')
238
251
  run: |
239
252
  if [ "$QUOTA_PAUSED" = "true" ]; then
240
253
  echo "### ⏸️ Milestone: Routine Execution Quota-Paused" >> "$GITHUB_STEP_SUMMARY"
@@ -248,7 +261,7 @@ jobs:
248
261
  fi
249
262
 
250
263
  - name: Emit Fleet Telemetry (Opt-in)
251
- if: always()
264
+ if: always() && (steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule')
252
265
  env:
253
266
  GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
254
267
  TELEMETRY_ENDPOINT: ${{ secrets.JONAH_FLEET_TELEMETRY_ENDPOINT }}
@@ -262,7 +275,7 @@ jobs:
262
275
  fi
263
276
 
264
277
  - name: Verify Atomic Handoff Invariant (PR Existence Guard)
265
- if: always()
278
+ if: always() && (steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule')
266
279
  env:
267
280
  GH_TOKEN: ${{ github.token }}
268
281
  run: |
@@ -283,7 +296,7 @@ jobs:
283
296
  fi
284
297
 
285
298
  - name: Reconcile Routine Run Issue
286
- if: always()
299
+ if: always() && (steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule')
287
300
  env:
288
301
  GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
289
302
  ISSUE_NUMBER: ${{ steps.run-issue.outputs.issue_number }}
@@ -91,6 +91,7 @@ jobs:
91
91
 
92
92
  MODEL="$DEFAULT_MODEL"
93
93
  TIMEOUT="$DEFAULT_TIMEOUT"
94
+ ENABLED="true"
94
95
 
95
96
  if [ -f "agents-manifest.json" ]; then
96
97
  CONFIG_JSON=$(node -e '
@@ -100,14 +101,16 @@ jobs:
100
101
  const r = process.argv[1];
101
102
  const model = m.models?.[r] || m.models?.default;
102
103
  const timeout = m.budgets?.timeoutMinutes?.[r] || m.budgets?.timeoutMinutes?.default;
103
- console.log(JSON.stringify({ model: model || "", timeout: timeout ? String(timeout) : "" }));
104
+ const enabled = m.routines?.[r] !== false;
105
+ console.log(JSON.stringify({ model: model || "", timeout: timeout ? String(timeout) : "", enabled }));
104
106
  } catch (e) {
105
- console.log(JSON.stringify({ model: "", timeout: "" }));
107
+ console.log(JSON.stringify({ model: "", timeout: "", enabled: true }));
106
108
  }
107
109
  ' "$ROUTINE")
108
110
 
109
111
  RESOLVED_MODEL=$(node -e "try { console.log(JSON.parse(process.argv[1]).model || ''); } catch {}" "$CONFIG_JSON")
110
112
  RESOLVED_TIMEOUT=$(node -e "try { console.log(JSON.parse(process.argv[1]).timeout || ''); } catch {}" "$CONFIG_JSON")
113
+ RESOLVED_ENABLED=$(node -e "try { console.log(JSON.parse(process.argv[1]).enabled); } catch {}" "$CONFIG_JSON")
111
114
 
112
115
  if [ -n "$RESOLVED_MODEL" ]; then
113
116
  MODEL="$RESOLVED_MODEL"
@@ -115,13 +118,22 @@ jobs:
115
118
  if [ -n "$RESOLVED_TIMEOUT" ]; then
116
119
  TIMEOUT="$RESOLVED_TIMEOUT"
117
120
  fi
121
+ if [ -n "$RESOLVED_ENABLED" ]; then
122
+ ENABLED="$RESOLVED_ENABLED"
123
+ fi
118
124
  fi
119
125
 
120
126
  echo "model=$MODEL" >> "$GITHUB_OUTPUT"
121
127
  echo "print_timeout=${TIMEOUT}m" >> "$GITHUB_OUTPUT"
122
- echo "Resolved routine configuration: model=$MODEL, timeout=${TIMEOUT}m"
128
+ echo "enabled=$ENABLED" >> "$GITHUB_OUTPUT"
129
+ echo "Resolved routine configuration: model=$MODEL, timeout=${TIMEOUT}m, enabled=$ENABLED"
130
+
131
+ if [ "$ENABLED" = "false" ] && [ "${{ github.event_name }}" = "schedule" ]; then
132
+ echo "⏸️ Routine '$ROUTINE' is disabled in agents-manifest.json. Skipping scheduled run."
133
+ fi
123
134
 
124
135
  - name: Initialize Routine Run Issue
136
+ if: steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule'
125
137
  id: run-issue
126
138
  env:
127
139
  GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
@@ -146,6 +158,7 @@ jobs:
146
158
  echo "ROUTINE_ISSUE_NUMBER=$ISSUE_NUMBER" >> "$GITHUB_ENV"
147
159
 
148
160
  - name: Run Dependency Update Check
161
+ if: steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule'
149
162
  timeout-minutes: 25
150
163
  env:
151
164
  GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
@@ -184,7 +197,7 @@ jobs:
184
197
  fi
185
198
 
186
199
  - name: Emit run summary
187
- if: always()
200
+ if: always() && (steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule')
188
201
  run: |
189
202
  if [ "$QUOTA_PAUSED" = "true" ]; then
190
203
  echo "### ⏸️ Milestone: Routine Execution Quota-Paused" >> "$GITHUB_STEP_SUMMARY"
@@ -198,7 +211,7 @@ jobs:
198
211
  fi
199
212
 
200
213
  - name: Reconcile Routine Run Issue
201
- if: always()
214
+ if: always() && (steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule')
202
215
  env:
203
216
  GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
204
217
  ISSUE_NUMBER: ${{ steps.run-issue.outputs.issue_number }}
@@ -91,6 +91,7 @@ jobs:
91
91
 
92
92
  MODEL="$DEFAULT_MODEL"
93
93
  TIMEOUT="$DEFAULT_TIMEOUT"
94
+ ENABLED="true"
94
95
 
95
96
  if [ -f "agents-manifest.json" ]; then
96
97
  CONFIG_JSON=$(node -e '
@@ -100,14 +101,16 @@ jobs:
100
101
  const r = process.argv[1];
101
102
  const model = m.models?.[r] || m.models?.default;
102
103
  const timeout = m.budgets?.timeoutMinutes?.[r] || m.budgets?.timeoutMinutes?.default;
103
- console.log(JSON.stringify({ model: model || "", timeout: timeout ? String(timeout) : "" }));
104
+ const enabled = m.routines?.[r] !== false;
105
+ console.log(JSON.stringify({ model: model || "", timeout: timeout ? String(timeout) : "", enabled }));
104
106
  } catch (e) {
105
- console.log(JSON.stringify({ model: "", timeout: "" }));
107
+ console.log(JSON.stringify({ model: "", timeout: "", enabled: true }));
106
108
  }
107
109
  ' "$ROUTINE")
108
110
 
109
111
  RESOLVED_MODEL=$(node -e "try { console.log(JSON.parse(process.argv[1]).model || ''); } catch {}" "$CONFIG_JSON")
110
112
  RESOLVED_TIMEOUT=$(node -e "try { console.log(JSON.parse(process.argv[1]).timeout || ''); } catch {}" "$CONFIG_JSON")
113
+ RESOLVED_ENABLED=$(node -e "try { console.log(JSON.parse(process.argv[1]).enabled); } catch {}" "$CONFIG_JSON")
111
114
 
112
115
  if [ -n "$RESOLVED_MODEL" ]; then
113
116
  MODEL="$RESOLVED_MODEL"
@@ -115,13 +118,22 @@ jobs:
115
118
  if [ -n "$RESOLVED_TIMEOUT" ]; then
116
119
  TIMEOUT="$RESOLVED_TIMEOUT"
117
120
  fi
121
+ if [ -n "$RESOLVED_ENABLED" ]; then
122
+ ENABLED="$RESOLVED_ENABLED"
123
+ fi
118
124
  fi
119
125
 
120
126
  echo "model=$MODEL" >> "$GITHUB_OUTPUT"
121
127
  echo "print_timeout=${TIMEOUT}m" >> "$GITHUB_OUTPUT"
122
- echo "Resolved routine configuration: model=$MODEL, timeout=${TIMEOUT}m"
128
+ echo "enabled=$ENABLED" >> "$GITHUB_OUTPUT"
129
+ echo "Resolved routine configuration: model=$MODEL, timeout=${TIMEOUT}m, enabled=$ENABLED"
130
+
131
+ if [ "$ENABLED" = "false" ] && [ "${{ github.event_name }}" = "schedule" ]; then
132
+ echo "⏸️ Routine '$ROUTINE' is disabled in agents-manifest.json. Skipping scheduled run."
133
+ fi
123
134
 
124
135
  - name: Initialize Routine Run Issue
136
+ if: steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule'
125
137
  id: run-issue
126
138
  env:
127
139
  GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
@@ -146,6 +158,7 @@ jobs:
146
158
  echo "ROUTINE_ISSUE_NUMBER=$ISSUE_NUMBER" >> "$GITHUB_ENV"
147
159
 
148
160
  - name: Run Issues Housekeeping
161
+ if: steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule'
149
162
  timeout-minutes: 40
150
163
  env:
151
164
  GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
@@ -184,7 +197,7 @@ jobs:
184
197
  fi
185
198
 
186
199
  - name: Emit run summary
187
- if: always()
200
+ if: always() && (steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule')
188
201
  run: |
189
202
  if [ "$QUOTA_PAUSED" = "true" ]; then
190
203
  echo "### ⏸️ Milestone: Routine Execution Quota-Paused" >> "$GITHUB_STEP_SUMMARY"
@@ -198,7 +211,7 @@ jobs:
198
211
  fi
199
212
 
200
213
  - name: Reconcile Routine Run Issue
201
- if: always()
214
+ if: always() && (steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule')
202
215
  env:
203
216
  GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
204
217
  ISSUE_NUMBER: ${{ steps.run-issue.outputs.issue_number }}
@@ -91,6 +91,7 @@ jobs:
91
91
 
92
92
  MODEL="$DEFAULT_MODEL"
93
93
  TIMEOUT="$DEFAULT_TIMEOUT"
94
+ ENABLED="true"
94
95
 
95
96
  if [ -f "agents-manifest.json" ]; then
96
97
  CONFIG_JSON=$(node -e '
@@ -100,14 +101,16 @@ jobs:
100
101
  const r = process.argv[1];
101
102
  const model = m.models?.[r] || m.models?.default;
102
103
  const timeout = m.budgets?.timeoutMinutes?.[r] || m.budgets?.timeoutMinutes?.default;
103
- console.log(JSON.stringify({ model: model || "", timeout: timeout ? String(timeout) : "" }));
104
+ const enabled = m.routines?.[r] !== false;
105
+ console.log(JSON.stringify({ model: model || "", timeout: timeout ? String(timeout) : "", enabled }));
104
106
  } catch (e) {
105
- console.log(JSON.stringify({ model: "", timeout: "" }));
107
+ console.log(JSON.stringify({ model: "", timeout: "", enabled: true }));
106
108
  }
107
109
  ' "$ROUTINE")
108
110
 
109
111
  RESOLVED_MODEL=$(node -e "try { console.log(JSON.parse(process.argv[1]).model || ''); } catch {}" "$CONFIG_JSON")
110
112
  RESOLVED_TIMEOUT=$(node -e "try { console.log(JSON.parse(process.argv[1]).timeout || ''); } catch {}" "$CONFIG_JSON")
113
+ RESOLVED_ENABLED=$(node -e "try { console.log(JSON.parse(process.argv[1]).enabled); } catch {}" "$CONFIG_JSON")
111
114
 
112
115
  if [ -n "$RESOLVED_MODEL" ]; then
113
116
  MODEL="$RESOLVED_MODEL"
@@ -115,13 +118,22 @@ jobs:
115
118
  if [ -n "$RESOLVED_TIMEOUT" ]; then
116
119
  TIMEOUT="$RESOLVED_TIMEOUT"
117
120
  fi
121
+ if [ -n "$RESOLVED_ENABLED" ]; then
122
+ ENABLED="$RESOLVED_ENABLED"
123
+ fi
118
124
  fi
119
125
 
120
126
  echo "model=$MODEL" >> "$GITHUB_OUTPUT"
121
127
  echo "print_timeout=${TIMEOUT}m" >> "$GITHUB_OUTPUT"
122
- echo "Resolved routine configuration: model=$MODEL, timeout=${TIMEOUT}m"
128
+ echo "enabled=$ENABLED" >> "$GITHUB_OUTPUT"
129
+ echo "Resolved routine configuration: model=$MODEL, timeout=${TIMEOUT}m, enabled=$ENABLED"
130
+
131
+ if [ "$ENABLED" = "false" ] && [ "${{ github.event_name }}" = "schedule" ]; then
132
+ echo "⏸️ Routine '\''$ROUTINE'\'' is disabled in agents-manifest.json. Skipping scheduled run."
133
+ fi
123
134
 
124
135
  - name: Initialize Routine Run Issue
136
+ if: steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule'
125
137
  id: run-issue
126
138
  env:
127
139
  GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
@@ -146,6 +158,7 @@ jobs:
146
158
  echo "ROUTINE_ISSUE_NUMBER=$ISSUE_NUMBER" >> "$GITHUB_ENV"
147
159
 
148
160
  - name: Run Prompt Optimizer
161
+ if: steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule'
149
162
  timeout-minutes: 35
150
163
  env:
151
164
  GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
@@ -184,7 +197,7 @@ jobs:
184
197
  fi
185
198
 
186
199
  - name: Emit run summary
187
- if: always()
200
+ if: always() && (steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule')
188
201
  run: |
189
202
  if [ "$QUOTA_PAUSED" = "true" ]; then
190
203
  echo "### ⏸️ Milestone: Routine Execution Quota-Paused" >> "$GITHUB_STEP_SUMMARY"
@@ -198,7 +211,7 @@ jobs:
198
211
  fi
199
212
 
200
213
  - name: Emit Fleet Telemetry (Opt-in)
201
- if: always()
214
+ if: always() && (steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule')
202
215
  env:
203
216
  GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
204
217
  TELEMETRY_ENDPOINT: ${{ secrets.JONAH_FLEET_TELEMETRY_ENDPOINT }}
@@ -212,7 +225,7 @@ jobs:
212
225
  fi
213
226
 
214
227
  - name: Reconcile Routine Run Issue
215
- if: always()
228
+ if: always() && (steps.routine-config.outputs.enabled != 'false' || github.event_name != 'schedule')
216
229
  env:
217
230
  GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
218
231
  ISSUE_NUMBER: ${{ steps.run-issue.outputs.issue_number }}