jonah-fleet 1.6.0 → 1.9.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/README.md +25 -2
  3. package/dist/commands/daemon.d.ts.map +1 -1
  4. package/dist/commands/init.d.ts +3 -0
  5. package/dist/commands/init.d.ts.map +1 -1
  6. package/dist/commands/labels.d.ts +11 -0
  7. package/dist/commands/labels.d.ts.map +1 -0
  8. package/dist/commands/status.d.ts.map +1 -1
  9. package/dist/commands/sync.d.ts.map +1 -1
  10. package/dist/commands/telemetry.d.ts.map +1 -1
  11. package/dist/index.js +2527 -519
  12. package/dist/lib/daemon-keys.d.ts +103 -0
  13. package/dist/lib/daemon-keys.d.ts.map +1 -0
  14. package/dist/lib/daemon.d.ts +8 -2
  15. package/dist/lib/daemon.d.ts.map +1 -1
  16. package/dist/lib/fleet-query.d.ts.map +1 -1
  17. package/dist/lib/labels.d.ts +68 -0
  18. package/dist/lib/labels.d.ts.map +1 -0
  19. package/dist/lib/manifest.d.ts +6 -0
  20. package/dist/lib/manifest.d.ts.map +1 -1
  21. package/dist/lib/presets.d.ts +52 -1
  22. package/dist/lib/presets.d.ts.map +1 -1
  23. package/dist/lib/runner.d.ts +114 -0
  24. package/dist/lib/runner.d.ts.map +1 -1
  25. package/dist/lib/telemetry.d.ts.map +1 -1
  26. package/dist/lib/terminal-card.d.ts +24 -1
  27. package/dist/lib/terminal-card.d.ts.map +1 -1
  28. package/package.json +1 -1
  29. package/schema.json +74 -1
  30. package/templates/prompts/ORCHESTRATION.md +84 -41
  31. package/templates/prompts/_prompt-template.md +10 -13
  32. package/templates/prompts/analytics-review.md +4 -2
  33. package/templates/prompts/autowork.md +76 -35
  34. package/templates/prompts/dependency-update-security-check.md +4 -2
  35. package/templates/prompts/design-review.md +125 -0
  36. package/templates/prompts/issues-housekeeping.md +10 -8
  37. package/templates/prompts/optimizer.md +31 -12
  38. package/templates/prompts/peer-review.md +42 -7
  39. package/templates/prompts/product-planning.md +14 -10
  40. package/templates/workflows/autowork-cron.yml +137 -38
  41. package/templates/workflows/dependency-check-cron.yml +127 -2
  42. package/templates/workflows/design-review-cron.yml +221 -0
  43. package/templates/workflows/issues-housekeeping-cron.yml +127 -2
  44. package/templates/workflows/prompt-optimizer-cron.yml +134 -9
  45. package/templates/workflows/sync-fleet.yml +4 -0
  46. package/templates/workflows/trigger-autowork-manual.yml +154 -11
  47. package/templates/workflows/trigger-autowork-on-bug.yml +177 -9
  48. package/templates/workflows/trigger-autowork-on-merge.yml +178 -9
  49. package/templates/workflows/trigger-review-routine.yml +162 -22
@@ -4,18 +4,17 @@ on:
4
4
  pull_request:
5
5
  types: [closed]
6
6
 
7
- concurrency:
8
- group: autowork-merge-pr-${{ github.event.pull_request.number }}
9
- cancel-in-progress: true
10
-
11
7
  jobs:
12
8
  fire-autowork:
13
9
  if: >-
14
10
  github.event.pull_request.merged == true &&
15
11
  github.event.pull_request.base.ref == 'main' &&
16
12
  github.event.pull_request.head.repo.full_name == github.repository
13
+ concurrency:
14
+ group: autowork-merge-pr-${{ github.event.pull_request.number }}
15
+ cancel-in-progress: true
17
16
  runs-on: ubuntu-latest
18
- timeout-minutes: 35
17
+ timeout-minutes: 70
19
18
  permissions:
20
19
  contents: write
21
20
  issues: write
@@ -50,7 +49,7 @@ jobs:
50
49
  with:
51
50
  fetch-depth: 0
52
51
  token: ${{ secrets.GH_PAT || github.token }}
53
-
52
+ persist-credentials: false
54
53
 
55
54
  - name: Setup Node.js
56
55
  if: steps.pick.outputs.skip != 'true'
@@ -60,10 +59,13 @@ jobs:
60
59
 
61
60
  - name: Configure Git author identity
62
61
  if: steps.pick.outputs.skip != 'true'
62
+ env:
63
+ GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
63
64
  run: |
64
65
  git config --global user.name "github-actions[bot]"
65
66
  git config --global user.email "github-actions[bot]@users.noreply.github.com"
66
67
  git config --global --add safe.directory "$GITHUB_WORKSPACE"
68
+ gh auth setup-git
67
69
 
68
70
  - name: Cache Antigravity CLI
69
71
  if: steps.pick.outputs.skip != 'true'
@@ -110,15 +112,84 @@ jobs:
110
112
  fi
111
113
  echo "skills_prompt=$SKILLS_PROMPT" >> "$GITHUB_OUTPUT"
112
114
 
115
+ - name: Resolve routine configuration
116
+ if: steps.pick.outputs.skip != 'true'
117
+ id: routine-config
118
+ run: |
119
+ ROUTINE="autowork"
120
+ DEFAULT_MODEL="gemini-3.8-flash-high"
121
+ DEFAULT_TIMEOUT="60"
122
+
123
+ MODEL="$DEFAULT_MODEL"
124
+ TIMEOUT="$DEFAULT_TIMEOUT"
125
+
126
+ if [ -f "agents-manifest.json" ]; then
127
+ CONFIG_JSON=$(node -e '
128
+ try {
129
+ const fs = require("fs");
130
+ const m = JSON.parse(fs.readFileSync("agents-manifest.json", "utf8"));
131
+ const r = process.argv[1];
132
+ const model = m.models?.[r] || m.models?.default;
133
+ const timeout = m.budgets?.timeoutMinutes?.[r] || m.budgets?.timeoutMinutes?.default;
134
+ console.log(JSON.stringify({ model: model || "", timeout: timeout ? String(timeout) : "" }));
135
+ } catch (e) {
136
+ console.log(JSON.stringify({ model: "", timeout: "" }));
137
+ }
138
+ ' "$ROUTINE")
139
+
140
+ RESOLVED_MODEL=$(node -e "try { console.log(JSON.parse(process.argv[1]).model || ''); } catch {}" "$CONFIG_JSON")
141
+ RESOLVED_TIMEOUT=$(node -e "try { console.log(JSON.parse(process.argv[1]).timeout || ''); } catch {}" "$CONFIG_JSON")
142
+
143
+ if [ -n "$RESOLVED_MODEL" ]; then
144
+ MODEL="$RESOLVED_MODEL"
145
+ fi
146
+ if [ -n "$RESOLVED_TIMEOUT" ]; then
147
+ TIMEOUT="$RESOLVED_TIMEOUT"
148
+ fi
149
+ fi
150
+
151
+ echo "model=$MODEL" >> "$GITHUB_OUTPUT"
152
+ echo "print_timeout=${TIMEOUT}m" >> "$GITHUB_OUTPUT"
153
+ echo "Resolved routine configuration: model=$MODEL, timeout=${TIMEOUT}m"
154
+
155
+ - name: Initialize Routine Run Issue
156
+ if: steps.pick.outputs.skip != 'true'
157
+ id: run-issue
158
+ env:
159
+ GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
160
+ PR_NUM: ${{ github.event.pull_request.number }}
161
+ EVENT_NAME: ${{ github.event_name }}
162
+ RUN_ID: ${{ github.run_id }}
163
+ SERVER_URL: ${{ github.server_url }}
164
+ REPOSITORY: ${{ github.repository }}
165
+ run: |
166
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H-%M-%SZ")
167
+ TITLE="[autowork] run ${TIMESTAMP} (on PR #${PR_NUM} merge)"
168
+ BODY="### Autonomous Routine Execution in Progress
169
+ - **Routine**: \`autowork\`
170
+ - **Timestamp**: \`${TIMESTAMP}\`
171
+ - **Trigger**: \`${EVENT_NAME}\` (PR #${PR_NUM} merged)
172
+ - **Run ID**: [\`${RUN_ID}\`](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})
173
+
174
+ ---
175
+ _Generated by [Antigravity](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})_"
176
+ ISSUE_URL=$(gh issue create --title "$TITLE" --body "$BODY" --label "routine-log,routine:autowork,status:running,runner:github-actions")
177
+ ISSUE_NUMBER=$(echo "$ISSUE_URL" | grep -oE '[0-9]+$')
178
+ echo "issue_number=$ISSUE_NUMBER" >> "$GITHUB_OUTPUT"
179
+ echo "ROUTINE_ISSUE_NUMBER=$ISSUE_NUMBER" >> "$GITHUB_ENV"
180
+
113
181
  - name: Run Autowork
114
182
  if: steps.pick.outputs.skip != 'true'
115
- timeout-minutes: 30
183
+ timeout-minutes: 60
116
184
  env:
117
185
  GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
118
186
  GOOGLE_API_KEY: ${{ secrets.GEMINI_API_KEY }}
119
187
  ANTIGRAVITY_API_KEY: ${{ secrets.GEMINI_API_KEY }}
120
188
  GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
189
+ ROUTINE_ISSUE_NUMBER: ${{ steps.run-issue.outputs.issue_number }}
121
190
  SKILLS_PROMPT: ${{ steps.skills.outputs.skills_prompt }}
191
+ MODEL: ${{ steps.routine-config.outputs.model || 'gemini-3.8-flash-high' }}
192
+ PRINT_TIMEOUT: ${{ steps.routine-config.outputs.print_timeout || '60m' }}
122
193
  GITHUB_SERVER_URL: ${{ github.server_url }}
123
194
  GITHUB_REPOSITORY: ${{ github.repository }}
124
195
  GITHUB_RUN_ID: ${{ github.run_id }}
@@ -126,7 +197,105 @@ jobs:
126
197
  PROMPT="You are the Autowork routine for this repository. A PR was recently merged. Read and follow .github/prompts/autowork.md. ${SKILLS_PROMPT} You are in Scan mode: converge on open PRs, close merged issues, and pick the next unblocked priority issue."
127
198
 
128
199
  agy -p "$PROMPT" \
129
- --model gemini-3.7-flash-high \
200
+ --model "$MODEL" \
130
201
  --output-format text \
131
- --print-timeout 30m \
202
+ --print-timeout "$PRINT_TIMEOUT" \
132
203
  --dangerously-skip-permissions
204
+
205
+ - name: Emit run summary
206
+ if: always() && steps.pick.outputs.skip != 'true'
207
+ run: |
208
+ REPORT_FILE=".jonah-fleet/run-report.md"
209
+ if [ -f "$REPORT_FILE" ] && [ -n "$GITHUB_STEP_SUMMARY" ]; then
210
+ cat "$REPORT_FILE" >> "$GITHUB_STEP_SUMMARY"
211
+ fi
212
+
213
+ - name: Emit Fleet Telemetry (Opt-in)
214
+ if: always() && steps.pick.outputs.skip != 'true'
215
+ env:
216
+ GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
217
+ TELEMETRY_ENDPOINT: ${{ secrets.JONAH_FLEET_TELEMETRY_ENDPOINT }}
218
+ GITHUB_REPOSITORY: ${{ github.repository }}
219
+ GITHUB_RUN_ID: ${{ github.run_id }}
220
+ GITHUB_RUN_NUMBER: ${{ github.run_number }}
221
+ run: |
222
+ REPORT_FILE=".jonah-fleet/run-report.md"
223
+ if [ -f "$REPORT_FILE" ]; then
224
+ npx --yes jonah-fleet telemetry --emit --log "$REPORT_FILE" --endpoint "$TELEMETRY_ENDPOINT" || true
225
+ fi
226
+
227
+ - name: Verify Atomic Handoff Invariant (PR Existence Guard)
228
+ if: always() && steps.pick.outputs.skip != 'true'
229
+ env:
230
+ GH_TOKEN: ${{ github.token }}
231
+ run: |
232
+ REPORT_FILE=".jonah-fleet/run-report.md"
233
+ if [ -f "$REPORT_FILE" ]; then
234
+ if grep -q "Result.*SUCCESS" "$REPORT_FILE"; then
235
+ WORKED_BRANCH=$(grep -E '^\s*-\s*(Pushed branch|Branch):' "$REPORT_FILE" | head -n 1 | sed -E 's/.*`([^`]+)`.*/\1/')
236
+ if [ -n "$WORKED_BRANCH" ] && [[ "$WORKED_BRANCH" =~ (feat|fix)/ ]]; then
237
+ echo "Verifying PR existence for branch: $WORKED_BRANCH"
238
+ PR_COUNT=$(gh pr list --head "$WORKED_BRANCH" --state all --json number --jq 'length' 2>/dev/null || echo "0")
239
+ if [ "$PR_COUNT" -eq 0 ]; then
240
+ echo "::error::Atomic Handoff Invariant Violated: Branch '$WORKED_BRANCH' was implemented and pushed, but no pull request exists on GitHub!" >&2
241
+ exit 1
242
+ fi
243
+ echo "✓ Verified pull request exists on GitHub for branch: $WORKED_BRANCH"
244
+ fi
245
+ fi
246
+ fi
247
+
248
+ - name: Reconcile Routine Run Issue
249
+ if: always() && steps.pick.outputs.skip != 'true'
250
+ env:
251
+ GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
252
+ ISSUE_NUMBER: ${{ steps.run-issue.outputs.issue_number }}
253
+ JOB_STATUS: ${{ job.status }}
254
+ EVENT_NAME: ${{ github.event_name }}
255
+ RUN_ID: ${{ github.run_id }}
256
+ SERVER_URL: ${{ github.server_url }}
257
+ REPOSITORY: ${{ github.repository }}
258
+ run: |
259
+ if [ -z "$ISSUE_NUMBER" ]; then
260
+ echo "No routine issue to reconcile."
261
+ exit 0
262
+ fi
263
+
264
+ REPORT_FILE=".jonah-fleet/run-report.md"
265
+
266
+ if [ -f "$REPORT_FILE" ]; then
267
+ BODY_CONTENT=$(cat "$REPORT_FILE")
268
+ else
269
+ BODY_CONTENT="### Autonomous Routine Execution Completed (${JOB_STATUS})
270
+ - **Routine**: \`autowork\`
271
+ - **Run ID**: [\`${RUN_ID}\`](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})
272
+ - **Trigger**: \`${EVENT_NAME}\`"
273
+ fi
274
+
275
+ FINAL_BODY="${BODY_CONTENT}
276
+
277
+ ---
278
+ _Generated by [Antigravity](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})_"
279
+
280
+ TEMP_BODY=$(mktemp)
281
+ echo "$FINAL_BODY" > "$TEMP_BODY"
282
+ gh issue edit "$ISSUE_NUMBER" --body-file "$TEMP_BODY"
283
+ rm -f "$TEMP_BODY"
284
+
285
+ if [ "$JOB_STATUS" = "success" ]; then
286
+ gh issue edit "$ISSUE_NUMBER" --add-label "status:success" --remove-label "status:running"
287
+ gh issue close "$ISSUE_NUMBER" --reason completed
288
+ echo "✓ Routine run issue #$ISSUE_NUMBER closed as completed."
289
+ else
290
+ INTERRUPTION_BODY="### ❌ Milestone: Run Interrupted / Failed
291
+ - **Routine**: \`autowork\`
292
+ - **Status**: Workflow job failed or timed out (\`${JOB_STATUS}\`)
293
+ - **Step**: Actions execution halted before normal completion
294
+ - **Action Log**: [View GitHub Actions Run](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})
295
+
296
+ ---
297
+ _Generated by [Antigravity](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})_"
298
+ gh issue comment "$ISSUE_NUMBER" --body "$INTERRUPTION_BODY" || true
299
+ gh issue edit "$ISSUE_NUMBER" --add-label "status:failure,needs-attention" --remove-label "status:running"
300
+ echo "⚠️ Routine run issue #$ISSUE_NUMBER left open for triage (status: $JOB_STATUS)."
301
+ fi
@@ -3,8 +3,6 @@ name: Trigger Review Routine
3
3
  on:
4
4
  pull_request:
5
5
  types: [ready_for_review, opened, reopened, synchronize, review_requested]
6
- paths-ignore:
7
- - '.github/prompts/logs/**'
8
6
  schedule:
9
7
  # Periodic scan sweep every 2 hours to catch and recover any orphaned or unreviewed ready PRs
10
8
  - cron: '45 */2 * * *'
@@ -17,10 +15,6 @@ on:
17
15
  issue_comment:
18
16
  types: [created]
19
17
 
20
- concurrency:
21
- group: review-routine-pr-${{ github.event.pull_request.number || github.event.issue.number || inputs.pr_number || 'scan' }}
22
- cancel-in-progress: true
23
-
24
18
  jobs:
25
19
  trigger-routine:
26
20
  if: >-
@@ -42,8 +36,11 @@ jobs:
42
36
  contains(github.event.comment.body, '/peer-review') ||
43
37
  contains(github.event.comment.body, '/retrigger') ||
44
38
  contains(github.event.comment.body, '/re-review')))
39
+ concurrency:
40
+ group: review-routine-pr-${{ github.event.pull_request.number || github.event.issue.number || inputs.pr_number || 'scan' }}
41
+ cancel-in-progress: true
45
42
  runs-on: ubuntu-latest
46
- timeout-minutes: 35
43
+ timeout-minutes: 65
47
44
  permissions:
48
45
  contents: write
49
46
  actions: read
@@ -56,12 +53,14 @@ jobs:
56
53
  env:
57
54
  GH_TOKEN: ${{ github.token }}
58
55
  PR_NUM: ${{ github.event.pull_request.number || github.event.issue.number || inputs.pr_number }}
56
+ EVENT_NAME: ${{ github.event_name }}
57
+ REPOSITORY: ${{ github.repository }}
59
58
  run: |
60
59
  if [ -z "$PR_NUM" ]; then
61
- if [ "${{ github.event_name }}" = "schedule" ]; then
62
- open_prs=$(gh api "repos/${{ github.repository }}/pulls?state=open&per_page=1" --jq 'length' 2>/dev/null || echo "0")
60
+ if [ "$EVENT_NAME" = "schedule" ]; then
61
+ open_prs=$(gh api "repos/${REPOSITORY}/pulls?state=open&per_page=1" --jq 'length' 2>/dev/null || echo "0")
63
62
  if [ "$open_prs" -eq "0" ]; then
64
- echo "No open PRs found in ${{ github.repository }} — skipping scheduled review routine scan."
63
+ echo "No open PRs found in ${REPOSITORY} — skipping scheduled review routine scan."
65
64
  echo "skip=true" >> "$GITHUB_OUTPUT"
66
65
  exit 0
67
66
  fi
@@ -87,7 +86,7 @@ jobs:
87
86
  return 1
88
87
  }
89
88
 
90
- if ! pr=$(gh_api_retry "repos/${{ github.repository }}/pulls/${PR_NUM}"); then
89
+ if ! pr=$(gh_api_retry "repos/${REPOSITORY}/pulls/${PR_NUM}"); then
91
90
  echo "skip=false" >> "$GITHUB_OUTPUT"
92
91
  exit 0
93
92
  fi
@@ -110,7 +109,7 @@ jobs:
110
109
  # Strict allowlist: only trigger immediate cloud review if explicitly labeled priority/P0 or priority/P1.
111
110
  # Untagged PRs and lower-priority PRs (P2/P3) skip immediate cloud review and are handled by local peer-review daemons
112
111
  # (cloud scheduled watchdog sweep will catch up if unreviewed > 48h).
113
- if [ "${{ github.event_name }}" = "pull_request" ]; then
112
+ if [ "$EVENT_NAME" = "pull_request" ]; then
114
113
  labels=$(echo "$pr" | jq -r '.labels[].name' 2>/dev/null || echo "")
115
114
  if ! echo "$labels" | grep -qE '^priority/P[01]$'; then
116
115
  echo "PR is not explicitly labeled priority/P0 or priority/P1 — skipping immediate cloud review for local peer-review daemon."
@@ -126,6 +125,7 @@ jobs:
126
125
  uses: actions/checkout@v4
127
126
  with:
128
127
  fetch-depth: 0
128
+ persist-credentials: false
129
129
 
130
130
  - name: Setup Node.js
131
131
  if: steps.guard.outputs.skip != 'true'
@@ -135,10 +135,13 @@ jobs:
135
135
 
136
136
  - name: Configure Git author identity
137
137
  if: steps.guard.outputs.skip != 'true'
138
+ env:
139
+ GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
138
140
  run: |
139
141
  git config --global user.name "github-actions[bot]"
140
142
  git config --global user.email "github-actions[bot]@users.noreply.github.com"
141
143
  git config --global --add safe.directory "$GITHUB_WORKSPACE"
144
+ gh auth setup-git
142
145
 
143
146
  - name: Cache Antigravity CLI
144
147
  if: steps.guard.outputs.skip != 'true'
@@ -171,9 +174,79 @@ jobs:
171
174
  printf '%s' "$ANTIGRAVITY_OAUTH_TOKEN" > "$HOME/.gemini/antigravity-cli/antigravity-oauth-token"
172
175
  chmod 600 "$HOME/.gemini/antigravity-cli/antigravity-oauth-token"
173
176
 
177
+ - name: Resolve routine configuration
178
+ if: steps.guard.outputs.skip != 'true'
179
+ id: routine-config
180
+ run: |
181
+ ROUTINE="peer-review"
182
+ DEFAULT_MODEL="gemini-3.8-flash-high"
183
+ DEFAULT_TIMEOUT="55"
184
+
185
+ MODEL="$DEFAULT_MODEL"
186
+ TIMEOUT="$DEFAULT_TIMEOUT"
187
+
188
+ if [ -f "agents-manifest.json" ]; then
189
+ CONFIG_JSON=$(node -e '
190
+ try {
191
+ const fs = require("fs");
192
+ const m = JSON.parse(fs.readFileSync("agents-manifest.json", "utf8"));
193
+ const r = process.argv[1];
194
+ const model = m.models?.[r] || m.models?.default;
195
+ const timeout = m.budgets?.timeoutMinutes?.[r] || m.budgets?.timeoutMinutes?.default;
196
+ console.log(JSON.stringify({ model: model || "", timeout: timeout ? String(timeout) : "" }));
197
+ } catch (e) {
198
+ console.log(JSON.stringify({ model: "", timeout: "" }));
199
+ }
200
+ ' "$ROUTINE")
201
+
202
+ RESOLVED_MODEL=$(node -e "try { console.log(JSON.parse(process.argv[1]).model || ''); } catch {}" "$CONFIG_JSON")
203
+ RESOLVED_TIMEOUT=$(node -e "try { console.log(JSON.parse(process.argv[1]).timeout || ''); } catch {}" "$CONFIG_JSON")
204
+
205
+ if [ -n "$RESOLVED_MODEL" ]; then
206
+ MODEL="$RESOLVED_MODEL"
207
+ fi
208
+ if [ -n "$RESOLVED_TIMEOUT" ]; then
209
+ TIMEOUT="$RESOLVED_TIMEOUT"
210
+ fi
211
+ fi
212
+
213
+ echo "model=$MODEL" >> "$GITHUB_OUTPUT"
214
+ echo "print_timeout=${TIMEOUT}m" >> "$GITHUB_OUTPUT"
215
+ echo "Resolved routine configuration: model=$MODEL, timeout=${TIMEOUT}m"
216
+
217
+ - name: Initialize Routine Run Issue
218
+ if: steps.guard.outputs.skip != 'true'
219
+ id: run-issue
220
+ env:
221
+ GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
222
+ PR_ARG: ${{ github.event.pull_request.number || github.event.issue.number || inputs.pr_number }}
223
+ EVENT_NAME: ${{ github.event_name }}
224
+ RUN_ID: ${{ github.run_id }}
225
+ SERVER_URL: ${{ github.server_url }}
226
+ REPOSITORY: ${{ github.repository }}
227
+ run: |
228
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H-%M-%SZ")
229
+ if [ -n "$PR_ARG" ]; then
230
+ TITLE="[peer-review] run ${TIMESTAMP} (PR #${PR_ARG})"
231
+ else
232
+ TITLE="[peer-review] run ${TIMESTAMP}"
233
+ fi
234
+ BODY="### Autonomous Routine Execution in Progress
235
+ - **Routine**: \`peer-review\`
236
+ - **Timestamp**: \`${TIMESTAMP}\`
237
+ - **Trigger**: \`${EVENT_NAME}\`
238
+ - **Run ID**: [\`${RUN_ID}\`](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})
239
+
240
+ ---
241
+ _Generated by [Antigravity](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})_"
242
+ ISSUE_URL=$(gh issue create --title "$TITLE" --body "$BODY" --label "routine-log,routine:peer-review,status:running,runner:github-actions")
243
+ ISSUE_NUMBER=$(echo "$ISSUE_URL" | grep -oE '[0-9]+$')
244
+ echo "issue_number=$ISSUE_NUMBER" >> "$GITHUB_OUTPUT"
245
+ echo "ROUTINE_ISSUE_NUMBER=$ISSUE_NUMBER" >> "$GITHUB_ENV"
246
+
174
247
  - name: Run Peer Review
175
248
  if: steps.guard.outputs.skip != 'true'
176
- timeout-minutes: 30
249
+ timeout-minutes: 55
177
250
  env:
178
251
  GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
179
252
  GOOGLE_API_KEY: ${{ secrets.GEMINI_API_KEY }}
@@ -181,6 +254,9 @@ jobs:
181
254
  GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
182
255
  PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number || inputs.pr_number }}
183
256
  PR_URL: ${{ github.event.pull_request.html_url || github.event.issue.html_url }}
257
+ ROUTINE_ISSUE_NUMBER: ${{ steps.run-issue.outputs.issue_number }}
258
+ MODEL: ${{ steps.routine-config.outputs.model || 'gemini-3.8-flash-high' }}
259
+ PRINT_TIMEOUT: ${{ steps.routine-config.outputs.print_timeout || '55m' }}
184
260
  GITHUB_SERVER_URL: ${{ github.server_url }}
185
261
  GITHUB_REPOSITORY: ${{ github.repository }}
186
262
  GITHUB_RUN_ID: ${{ github.run_id }}
@@ -194,10 +270,14 @@ jobs:
194
270
  PROMPT="You are the Peer Review routine for this repository. Read and follow the instructions in .github/prompts/peer-review.md exactly. You are in Scan mode: check open PRs and select the highest-priority PR to review."
195
271
  fi
196
272
 
273
+ if [ -n "$ROUTINE_ISSUE_NUMBER" ]; then
274
+ PROMPT="$PROMPT Tracking run log issue: #${ROUTINE_ISSUE_NUMBER}."
275
+ fi
276
+
197
277
  agy -p "$PROMPT" \
198
- --model gemini-3.7-flash-high \
278
+ --model "$MODEL" \
199
279
  --output-format text \
200
- --print-timeout 30m \
280
+ --print-timeout "$PRINT_TIMEOUT" \
201
281
  --dangerously-skip-permissions
202
282
 
203
283
  - name: Handle Review Failure & PR Notification
@@ -205,12 +285,17 @@ jobs:
205
285
  env:
206
286
  GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
207
287
  PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number || inputs.pr_number }}
288
+ ROUTINE_ISSUE_NUMBER: ${{ steps.run-issue.outputs.issue_number }}
208
289
  GITHUB_SERVER_URL: ${{ github.server_url }}
209
290
  GITHUB_REPOSITORY: ${{ github.repository }}
210
291
  GITHUB_RUN_ID: ${{ github.run_id }}
211
292
  run: |
212
293
  if [ -n "$PR_NUMBER" ]; then
213
- gh pr comment "$PR_NUMBER" --body "⚠️ **Peer Review Routine Notice**: The automated review session encountered an execution error (e.g., API rate/quota limit or runner interruption). The review will be retried automatically on the next scheduled scan sweep or can be re-triggered immediately by commenting \`/review\` on this PR.
294
+ LOG_REF=""
295
+ if [ -n "$ROUTINE_ISSUE_NUMBER" ]; then
296
+ LOG_REF=" · [Run Log #${ROUTINE_ISSUE_NUMBER}](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/issues/${ROUTINE_ISSUE_NUMBER})"
297
+ fi
298
+ gh pr comment "$PR_NUMBER" --body "⚠️ **Peer Review Routine Notice**: The automated review session encountered an execution error (e.g., API rate/quota limit or runner interruption)${LOG_REF}. The review will be retried automatically on the next scheduled scan sweep or can be re-triggered immediately by commenting \`/review\` on this PR.
214
299
 
215
300
  _Generated by [Antigravity](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID})_" || true
216
301
  fi
@@ -218,9 +303,9 @@ jobs:
218
303
  - name: Emit run summary
219
304
  if: always() && steps.guard.outputs.skip != 'true'
220
305
  run: |
221
- LATEST_LOG=$(ls -t .github/prompts/logs/peer-review/*.md 2>/dev/null | head -n 1)
222
- if [ -n "$LATEST_LOG" ] && [ -n "$GITHUB_STEP_SUMMARY" ]; then
223
- cat "$LATEST_LOG" >> "$GITHUB_STEP_SUMMARY"
306
+ REPORT_FILE=".jonah-fleet/run-report.md"
307
+ if [ -f "$REPORT_FILE" ] && [ -n "$GITHUB_STEP_SUMMARY" ]; then
308
+ cat "$REPORT_FILE" >> "$GITHUB_STEP_SUMMARY"
224
309
  fi
225
310
 
226
311
  - name: Emit Fleet Telemetry (Opt-in)
@@ -232,7 +317,62 @@ jobs:
232
317
  GITHUB_RUN_ID: ${{ github.run_id }}
233
318
  GITHUB_RUN_NUMBER: ${{ github.run_number }}
234
319
  run: |
235
- LATEST_LOG=$(ls -t .github/prompts/logs/peer-review/*.md 2>/dev/null | head -n 1)
236
- if [ -n "$LATEST_LOG" ] && [ -f "$LATEST_LOG" ]; then
237
- npx --yes jonah-fleet telemetry --emit --log "$LATEST_LOG" --endpoint "$TELEMETRY_ENDPOINT" || true
320
+ REPORT_FILE=".jonah-fleet/run-report.md"
321
+ if [ -f "$REPORT_FILE" ]; then
322
+ npx --yes jonah-fleet telemetry --emit --log "$REPORT_FILE" --endpoint "$TELEMETRY_ENDPOINT" || true
323
+ fi
324
+
325
+ - name: Reconcile Routine Run Issue
326
+ if: always() && steps.guard.outputs.skip != 'true'
327
+ env:
328
+ GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
329
+ ISSUE_NUMBER: ${{ steps.run-issue.outputs.issue_number }}
330
+ JOB_STATUS: ${{ job.status }}
331
+ EVENT_NAME: ${{ github.event_name }}
332
+ RUN_ID: ${{ github.run_id }}
333
+ SERVER_URL: ${{ github.server_url }}
334
+ REPOSITORY: ${{ github.repository }}
335
+ run: |
336
+ if [ -z "$ISSUE_NUMBER" ]; then
337
+ echo "No routine issue to reconcile."
338
+ exit 0
339
+ fi
340
+
341
+ REPORT_FILE=".jonah-fleet/run-report.md"
342
+
343
+ if [ -f "$REPORT_FILE" ]; then
344
+ BODY_CONTENT=$(cat "$REPORT_FILE")
345
+ else
346
+ BODY_CONTENT="### Autonomous Routine Execution Completed (${JOB_STATUS})
347
+ - **Routine**: \`peer-review\`
348
+ - **Run ID**: [\`${RUN_ID}\`](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})
349
+ - **Trigger**: \`${EVENT_NAME}\`"
350
+ fi
351
+
352
+ FINAL_BODY="${BODY_CONTENT}
353
+
354
+ ---
355
+ _Generated by [Antigravity](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})_"
356
+
357
+ TEMP_BODY=$(mktemp)
358
+ echo "$FINAL_BODY" > "$TEMP_BODY"
359
+ gh issue edit "$ISSUE_NUMBER" --body-file "$TEMP_BODY"
360
+ rm -f "$TEMP_BODY"
361
+
362
+ if [ "$JOB_STATUS" = "success" ]; then
363
+ gh issue edit "$ISSUE_NUMBER" --add-label "status:success" --remove-label "status:running"
364
+ gh issue close "$ISSUE_NUMBER" --reason completed
365
+ echo "✓ Routine run issue #$ISSUE_NUMBER closed as completed."
366
+ else
367
+ INTERRUPTION_BODY="### ❌ Milestone: Run Interrupted / Failed
368
+ - **Routine**: \`peer-review\`
369
+ - **Status**: Workflow job failed or timed out (\`${JOB_STATUS}\`)
370
+ - **Step**: Actions execution halted before normal completion
371
+ - **Action Log**: [View GitHub Actions Run](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})
372
+
373
+ ---
374
+ _Generated by [Antigravity](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})_"
375
+ gh issue comment "$ISSUE_NUMBER" --body "$INTERRUPTION_BODY" || true
376
+ gh issue edit "$ISSUE_NUMBER" --add-label "status:failure,needs-attention" --remove-label "status:running"
377
+ echo "⚠️ Routine run issue #$ISSUE_NUMBER left open for triage (status: $JOB_STATUS)."
238
378
  fi