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
@@ -29,7 +29,7 @@ concurrency:
29
29
  jobs:
30
30
  run-autowork:
31
31
  runs-on: ubuntu-latest
32
- timeout-minutes: 65
32
+ timeout-minutes: 70
33
33
  permissions:
34
34
  contents: write
35
35
  issues: write
@@ -42,7 +42,7 @@ jobs:
42
42
  with:
43
43
  fetch-depth: 0
44
44
  token: ${{ secrets.GH_PAT || github.token }}
45
-
45
+ persist-credentials: false
46
46
 
47
47
  - name: Setup Node.js
48
48
  uses: actions/setup-node@v4
@@ -50,10 +50,13 @@ jobs:
50
50
  node-version: 22
51
51
 
52
52
  - name: Configure Git author identity
53
+ env:
54
+ GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
53
55
  run: |
54
56
  git config --global user.name "github-actions[bot]"
55
57
  git config --global user.email "github-actions[bot]@users.noreply.github.com"
56
58
  git config --global --add safe.directory "$GITHUB_WORKSPACE"
59
+ gh auth setup-git
57
60
 
58
61
  - name: Cache Antigravity CLI
59
62
  id: cache-agy
@@ -110,6 +113,74 @@ jobs:
110
113
  fi
111
114
  echo "skills_prompt=$SKILLS_PROMPT" >> "$GITHUB_OUTPUT"
112
115
 
116
+ - name: Resolve routine configuration
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
+ id: run-issue
157
+ env:
158
+ GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
159
+ TARGET_ARG: ${{ inputs.issue_number }}
160
+ EVENT_NAME: ${{ github.event_name }}
161
+ RUN_ID: ${{ github.run_id }}
162
+ SERVER_URL: ${{ github.server_url }}
163
+ REPOSITORY: ${{ github.repository }}
164
+ run: |
165
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H-%M-%SZ")
166
+ if [ -n "$TARGET_ARG" ]; then
167
+ TITLE="[autowork] run ${TIMESTAMP} (Issue #${TARGET_ARG})"
168
+ else
169
+ TITLE="[autowork] run ${TIMESTAMP}"
170
+ fi
171
+ BODY="### Autonomous Routine Execution in Progress
172
+ - **Routine**: \`autowork\`
173
+ - **Timestamp**: \`${TIMESTAMP}\`
174
+ - **Trigger**: \`${EVENT_NAME}\`
175
+ - **Run ID**: [\`${RUN_ID}\`](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})
176
+
177
+ ---
178
+ _Generated by [Antigravity](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})_"
179
+ ISSUE_URL=$(gh issue create --title "$TITLE" --body "$BODY" --label "routine-log,routine:autowork,status:running,runner:github-actions")
180
+ ISSUE_NUMBER=$(echo "$ISSUE_URL" | grep -oE '[0-9]+$')
181
+ echo "issue_number=$ISSUE_NUMBER" >> "$GITHUB_OUTPUT"
182
+ echo "ROUTINE_ISSUE_NUMBER=$ISSUE_NUMBER" >> "$GITHUB_ENV"
183
+
113
184
  - name: Run Autowork
114
185
  timeout-minutes: 60
115
186
  env:
@@ -118,9 +189,12 @@ jobs:
118
189
  ANTIGRAVITY_API_KEY: ${{ secrets.GEMINI_API_KEY }}
119
190
  GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
120
191
  TARGET_ISSUE: ${{ inputs.issue_number }}
192
+ ROUTINE_ISSUE_NUMBER: ${{ steps.run-issue.outputs.issue_number }}
121
193
  MODE: ${{ inputs.mode || 'targeted' }}
122
194
  CUSTOM_PROMPT: ${{ inputs.custom_prompt }}
123
195
  SKILLS_PROMPT: ${{ steps.skills.outputs.skills_prompt }}
196
+ MODEL: ${{ steps.routine-config.outputs.model || 'gemini-3.8-flash-high' }}
197
+ PRINT_TIMEOUT: ${{ steps.routine-config.outputs.print_timeout || '60m' }}
124
198
  GITHUB_SERVER_URL: ${{ github.server_url }}
125
199
  GITHUB_REPOSITORY: ${{ github.repository }}
126
200
  GITHUB_RUN_ID: ${{ github.run_id }}
@@ -136,17 +210,31 @@ jobs:
136
210
  fi
137
211
 
138
212
  agy -p "$PROMPT" \
139
- --model gemini-3.7-flash-high \
213
+ --model "$MODEL" \
140
214
  --output-format text \
141
- --print-timeout 60m \
215
+ --print-timeout "$PRINT_TIMEOUT" \
142
216
  --dangerously-skip-permissions
143
217
 
144
218
  - name: Emit run summary
145
219
  if: always()
146
220
  run: |
147
- LATEST_LOG=$(ls -t .github/prompts/logs/autowork/*.md 2>/dev/null | head -n 1)
148
- if [ -n "$LATEST_LOG" ] && [ -n "$GITHUB_STEP_SUMMARY" ]; then
149
- cat "$LATEST_LOG" >> "$GITHUB_STEP_SUMMARY"
221
+ REPORT_FILE=".jonah-fleet/run-report.md"
222
+ if [ -f "$REPORT_FILE" ] && [ -n "$GITHUB_STEP_SUMMARY" ]; then
223
+ cat "$REPORT_FILE" >> "$GITHUB_STEP_SUMMARY"
224
+ fi
225
+
226
+ - name: Emit Fleet Telemetry (Opt-in)
227
+ if: always()
228
+ env:
229
+ GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
230
+ TELEMETRY_ENDPOINT: ${{ secrets.JONAH_FLEET_TELEMETRY_ENDPOINT }}
231
+ GITHUB_REPOSITORY: ${{ github.repository }}
232
+ GITHUB_RUN_ID: ${{ github.run_id }}
233
+ GITHUB_RUN_NUMBER: ${{ github.run_number }}
234
+ run: |
235
+ REPORT_FILE=".jonah-fleet/run-report.md"
236
+ if [ -f "$REPORT_FILE" ]; then
237
+ npx --yes jonah-fleet telemetry --emit --log "$REPORT_FILE" --endpoint "$TELEMETRY_ENDPOINT" || true
150
238
  fi
151
239
 
152
240
  - name: Verify Atomic Handoff Invariant (PR Existence Guard)
@@ -154,10 +242,10 @@ jobs:
154
242
  env:
155
243
  GH_TOKEN: ${{ github.token }}
156
244
  run: |
157
- LATEST_LOG=$(ls -t .github/prompts/logs/autowork/*.md 2>/dev/null | head -n 1)
158
- if [ -n "$LATEST_LOG" ]; then
159
- if grep -q "Result.*SUCCESS" "$LATEST_LOG"; then
160
- WORKED_BRANCH=$(grep -E '^\s*-\s*(Pushed branch|Branch):' "$LATEST_LOG" | head -n 1 | sed -E 's/.*`([^`]+)`.*/\1/')
245
+ REPORT_FILE=".jonah-fleet/run-report.md"
246
+ if [ -f "$REPORT_FILE" ]; then
247
+ if grep -q "Result.*SUCCESS" "$REPORT_FILE"; then
248
+ WORKED_BRANCH=$(grep -E '^\s*-\s*(Pushed branch|Branch):' "$REPORT_FILE" | head -n 1 | sed -E 's/.*`([^`]+)`.*/\1/')
161
249
  if [ -n "$WORKED_BRANCH" ] && [[ "$WORKED_BRANCH" =~ (feat|fix)/ ]]; then
162
250
  echo "Verifying PR existence for branch: $WORKED_BRANCH"
163
251
  PR_COUNT=$(gh pr list --head "$WORKED_BRANCH" --state all --json number --jq 'length' 2>/dev/null || echo "0")
@@ -169,3 +257,58 @@ jobs:
169
257
  fi
170
258
  fi
171
259
  fi
260
+
261
+ - name: Reconcile Routine Run Issue
262
+ if: always()
263
+ env:
264
+ GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
265
+ ISSUE_NUMBER: ${{ steps.run-issue.outputs.issue_number }}
266
+ JOB_STATUS: ${{ job.status }}
267
+ EVENT_NAME: ${{ github.event_name }}
268
+ RUN_ID: ${{ github.run_id }}
269
+ SERVER_URL: ${{ github.server_url }}
270
+ REPOSITORY: ${{ github.repository }}
271
+ run: |
272
+ if [ -z "$ISSUE_NUMBER" ]; then
273
+ echo "No routine issue to reconcile."
274
+ exit 0
275
+ fi
276
+
277
+ REPORT_FILE=".jonah-fleet/run-report.md"
278
+
279
+ if [ -f "$REPORT_FILE" ]; then
280
+ BODY_CONTENT=$(cat "$REPORT_FILE")
281
+ else
282
+ BODY_CONTENT="### Autonomous Routine Execution Completed (${JOB_STATUS})
283
+ - **Routine**: \`autowork\`
284
+ - **Run ID**: [\`${RUN_ID}\`](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})
285
+ - **Trigger**: \`${EVENT_NAME}\`"
286
+ fi
287
+
288
+ FINAL_BODY="${BODY_CONTENT}
289
+
290
+ ---
291
+ _Generated by [Antigravity](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})_"
292
+
293
+ TEMP_BODY=$(mktemp)
294
+ echo "$FINAL_BODY" > "$TEMP_BODY"
295
+ gh issue edit "$ISSUE_NUMBER" --body-file "$TEMP_BODY"
296
+ rm -f "$TEMP_BODY"
297
+
298
+ if [ "$JOB_STATUS" = "success" ]; then
299
+ gh issue edit "$ISSUE_NUMBER" --add-label "status:success" --remove-label "status:running"
300
+ gh issue close "$ISSUE_NUMBER" --reason completed
301
+ echo "✓ Routine run issue #$ISSUE_NUMBER closed as completed."
302
+ else
303
+ INTERRUPTION_BODY="### ❌ Milestone: Run Interrupted / Failed
304
+ - **Routine**: \`autowork\`
305
+ - **Status**: Workflow job failed or timed out (\`${JOB_STATUS}\`)
306
+ - **Step**: Actions execution halted before normal completion
307
+ - **Action Log**: [View GitHub Actions Run](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})
308
+
309
+ ---
310
+ _Generated by [Antigravity](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})_"
311
+ gh issue comment "$ISSUE_NUMBER" --body "$INTERRUPTION_BODY" || true
312
+ gh issue edit "$ISSUE_NUMBER" --add-label "status:failure,needs-attention" --remove-label "status:running"
313
+ echo "⚠️ Routine run issue #$ISSUE_NUMBER left open for triage (status: $JOB_STATUS)."
314
+ fi
@@ -4,17 +4,16 @@ on:
4
4
  issues:
5
5
  types: [labeled, opened]
6
6
 
7
- concurrency:
8
- group: autowork-bug-${{ github.event.issue.number }}
9
- cancel-in-progress: true
10
-
11
7
  jobs:
12
8
  fire-autowork:
13
9
  if: >-
14
10
  (github.event.action == 'opened' && contains(github.event.issue.labels.*.name, 'bug') && (contains(github.event.issue.labels.*.name, 'priority/P1') || contains(github.event.issue.labels.*.name, 'priority/P0'))) ||
15
11
  (github.event.action == 'labeled' && contains(github.event.issue.labels.*.name, 'bug') && (contains(github.event.issue.labels.*.name, 'priority/P1') || contains(github.event.issue.labels.*.name, 'priority/P0')))
12
+ concurrency:
13
+ group: autowork-bug-${{ github.event.issue.number }}
14
+ cancel-in-progress: true
16
15
  runs-on: ubuntu-latest
17
- timeout-minutes: 35
16
+ timeout-minutes: 70
18
17
  permissions:
19
18
  contents: write
20
19
  issues: write
@@ -27,7 +26,7 @@ jobs:
27
26
  with:
28
27
  fetch-depth: 0
29
28
  token: ${{ secrets.GH_PAT || github.token }}
30
-
29
+ persist-credentials: false
31
30
 
32
31
  - name: Setup Node.js
33
32
  uses: actions/setup-node@v4
@@ -35,10 +34,13 @@ jobs:
35
34
  node-version: 22
36
35
 
37
36
  - name: Configure Git author identity
37
+ env:
38
+ GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
38
39
  run: |
39
40
  git config --global user.name "github-actions[bot]"
40
41
  git config --global user.email "github-actions[bot]@users.noreply.github.com"
41
42
  git config --global --add safe.directory "$GITHUB_WORKSPACE"
43
+ gh auth setup-git
42
44
 
43
45
  - name: Cache Antigravity CLI
44
46
  id: cache-agy
@@ -81,15 +83,83 @@ jobs:
81
83
  fi
82
84
  echo "skills_prompt=$SKILLS_PROMPT" >> "$GITHUB_OUTPUT"
83
85
 
86
+ - name: Resolve routine configuration
87
+ id: routine-config
88
+ run: |
89
+ ROUTINE="autowork"
90
+ DEFAULT_MODEL="gemini-3.8-flash-high"
91
+ DEFAULT_TIMEOUT="60"
92
+
93
+ MODEL="$DEFAULT_MODEL"
94
+ TIMEOUT="$DEFAULT_TIMEOUT"
95
+
96
+ if [ -f "agents-manifest.json" ]; then
97
+ CONFIG_JSON=$(node -e '
98
+ try {
99
+ const fs = require("fs");
100
+ const m = JSON.parse(fs.readFileSync("agents-manifest.json", "utf8"));
101
+ const r = process.argv[1];
102
+ const model = m.models?.[r] || m.models?.default;
103
+ const timeout = m.budgets?.timeoutMinutes?.[r] || m.budgets?.timeoutMinutes?.default;
104
+ console.log(JSON.stringify({ model: model || "", timeout: timeout ? String(timeout) : "" }));
105
+ } catch (e) {
106
+ console.log(JSON.stringify({ model: "", timeout: "" }));
107
+ }
108
+ ' "$ROUTINE")
109
+
110
+ RESOLVED_MODEL=$(node -e "try { console.log(JSON.parse(process.argv[1]).model || ''); } catch {}" "$CONFIG_JSON")
111
+ RESOLVED_TIMEOUT=$(node -e "try { console.log(JSON.parse(process.argv[1]).timeout || ''); } catch {}" "$CONFIG_JSON")
112
+
113
+ if [ -n "$RESOLVED_MODEL" ]; then
114
+ MODEL="$RESOLVED_MODEL"
115
+ fi
116
+ if [ -n "$RESOLVED_TIMEOUT" ]; then
117
+ TIMEOUT="$RESOLVED_TIMEOUT"
118
+ fi
119
+ fi
120
+
121
+ echo "model=$MODEL" >> "$GITHUB_OUTPUT"
122
+ echo "print_timeout=${TIMEOUT}m" >> "$GITHUB_OUTPUT"
123
+ echo "Resolved routine configuration: model=$MODEL, timeout=${TIMEOUT}m"
124
+
125
+ - name: Initialize Routine Run Issue
126
+ id: run-issue
127
+ env:
128
+ GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
129
+ ISSUE_NUM: ${{ github.event.issue.number }}
130
+ EVENT_NAME: ${{ github.event_name }}
131
+ RUN_ID: ${{ github.run_id }}
132
+ SERVER_URL: ${{ github.server_url }}
133
+ REPOSITORY: ${{ github.repository }}
134
+ run: |
135
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H-%M-%SZ")
136
+ TITLE="[autowork] run ${TIMESTAMP} (Issue #${ISSUE_NUM})"
137
+ BODY="### Autonomous Routine Execution in Progress
138
+ - **Routine**: \`autowork\`
139
+ - **Timestamp**: \`${TIMESTAMP}\`
140
+ - **Trigger**: \`${EVENT_NAME}\` (High-priority bug)
141
+ - **Target**: Issue #${ISSUE_NUM}
142
+ - **Run ID**: [\`${RUN_ID}\`](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})
143
+
144
+ ---
145
+ _Generated by [Antigravity](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})_"
146
+ ISSUE_URL=$(gh issue create --title "$TITLE" --body "$BODY" --label "routine-log,routine:autowork,status:running,runner:github-actions")
147
+ ISSUE_NUMBER=$(echo "$ISSUE_URL" | grep -oE '[0-9]+$')
148
+ echo "issue_number=$ISSUE_NUMBER" >> "$GITHUB_OUTPUT"
149
+ echo "ROUTINE_ISSUE_NUMBER=$ISSUE_NUMBER" >> "$GITHUB_ENV"
150
+
84
151
  - name: Run Autowork
85
- timeout-minutes: 30
152
+ timeout-minutes: 60
86
153
  env:
87
154
  GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
88
155
  GOOGLE_API_KEY: ${{ secrets.GEMINI_API_KEY }}
89
156
  ANTIGRAVITY_API_KEY: ${{ secrets.GEMINI_API_KEY }}
90
157
  GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
91
158
  TARGET_ISSUE: ${{ github.event.issue.number }}
159
+ ROUTINE_ISSUE_NUMBER: ${{ steps.run-issue.outputs.issue_number }}
92
160
  SKILLS_PROMPT: ${{ steps.skills.outputs.skills_prompt }}
161
+ MODEL: ${{ steps.routine-config.outputs.model || 'gemini-3.8-flash-high' }}
162
+ PRINT_TIMEOUT: ${{ steps.routine-config.outputs.print_timeout || '60m' }}
93
163
  GITHUB_SERVER_URL: ${{ github.server_url }}
94
164
  GITHUB_REPOSITORY: ${{ github.repository }}
95
165
  GITHUB_RUN_ID: ${{ github.run_id }}
@@ -97,7 +167,105 @@ jobs:
97
167
  PROMPT="You are the Autowork routine for this repository. A high-priority bug was filed. Read and follow .github/prompts/autowork.md. ${SKILLS_PROMPT} Your target is issue #${TARGET_ISSUE}. You are in Targeted mode: work issue #${TARGET_ISSUE} directly."
98
168
 
99
169
  agy -p "$PROMPT" \
100
- --model gemini-3.7-flash-high \
170
+ --model "$MODEL" \
101
171
  --output-format text \
102
- --print-timeout 30m \
172
+ --print-timeout "$PRINT_TIMEOUT" \
103
173
  --dangerously-skip-permissions
174
+
175
+ - name: Emit run summary
176
+ if: always()
177
+ run: |
178
+ REPORT_FILE=".jonah-fleet/run-report.md"
179
+ if [ -f "$REPORT_FILE" ] && [ -n "$GITHUB_STEP_SUMMARY" ]; then
180
+ cat "$REPORT_FILE" >> "$GITHUB_STEP_SUMMARY"
181
+ fi
182
+
183
+ - name: Emit Fleet Telemetry (Opt-in)
184
+ if: always()
185
+ env:
186
+ GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
187
+ TELEMETRY_ENDPOINT: ${{ secrets.JONAH_FLEET_TELEMETRY_ENDPOINT }}
188
+ GITHUB_REPOSITORY: ${{ github.repository }}
189
+ GITHUB_RUN_ID: ${{ github.run_id }}
190
+ GITHUB_RUN_NUMBER: ${{ github.run_number }}
191
+ run: |
192
+ REPORT_FILE=".jonah-fleet/run-report.md"
193
+ if [ -f "$REPORT_FILE" ]; then
194
+ npx --yes jonah-fleet telemetry --emit --log "$REPORT_FILE" --endpoint "$TELEMETRY_ENDPOINT" || true
195
+ fi
196
+
197
+ - name: Verify Atomic Handoff Invariant (PR Existence Guard)
198
+ if: always()
199
+ env:
200
+ GH_TOKEN: ${{ github.token }}
201
+ run: |
202
+ REPORT_FILE=".jonah-fleet/run-report.md"
203
+ if [ -f "$REPORT_FILE" ]; then
204
+ if grep -q "Result.*SUCCESS" "$REPORT_FILE"; then
205
+ WORKED_BRANCH=$(grep -E '^\s*-\s*(Pushed branch|Branch):' "$REPORT_FILE" | head -n 1 | sed -E 's/.*`([^`]+)`.*/\1/')
206
+ if [ -n "$WORKED_BRANCH" ] && [[ "$WORKED_BRANCH" =~ (feat|fix)/ ]]; then
207
+ echo "Verifying PR existence for branch: $WORKED_BRANCH"
208
+ PR_COUNT=$(gh pr list --head "$WORKED_BRANCH" --state all --json number --jq 'length' 2>/dev/null || echo "0")
209
+ if [ "$PR_COUNT" -eq 0 ]; then
210
+ echo "::error::Atomic Handoff Invariant Violated: Branch '$WORKED_BRANCH' was implemented and pushed, but no pull request exists on GitHub!" >&2
211
+ exit 1
212
+ fi
213
+ echo "✓ Verified pull request exists on GitHub for branch: $WORKED_BRANCH"
214
+ fi
215
+ fi
216
+ fi
217
+
218
+ - name: Reconcile Routine Run Issue
219
+ if: always()
220
+ env:
221
+ GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
222
+ ISSUE_NUMBER: ${{ steps.run-issue.outputs.issue_number }}
223
+ JOB_STATUS: ${{ job.status }}
224
+ EVENT_NAME: ${{ github.event_name }}
225
+ RUN_ID: ${{ github.run_id }}
226
+ SERVER_URL: ${{ github.server_url }}
227
+ REPOSITORY: ${{ github.repository }}
228
+ run: |
229
+ if [ -z "$ISSUE_NUMBER" ]; then
230
+ echo "No routine issue to reconcile."
231
+ exit 0
232
+ fi
233
+
234
+ REPORT_FILE=".jonah-fleet/run-report.md"
235
+
236
+ if [ -f "$REPORT_FILE" ]; then
237
+ BODY_CONTENT=$(cat "$REPORT_FILE")
238
+ else
239
+ BODY_CONTENT="### Autonomous Routine Execution Completed (${JOB_STATUS})
240
+ - **Routine**: \`autowork\`
241
+ - **Run ID**: [\`${RUN_ID}\`](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})
242
+ - **Trigger**: \`${EVENT_NAME}\`"
243
+ fi
244
+
245
+ FINAL_BODY="${BODY_CONTENT}
246
+
247
+ ---
248
+ _Generated by [Antigravity](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})_"
249
+
250
+ TEMP_BODY=$(mktemp)
251
+ echo "$FINAL_BODY" > "$TEMP_BODY"
252
+ gh issue edit "$ISSUE_NUMBER" --body-file "$TEMP_BODY"
253
+ rm -f "$TEMP_BODY"
254
+
255
+ if [ "$JOB_STATUS" = "success" ]; then
256
+ gh issue edit "$ISSUE_NUMBER" --add-label "status:success" --remove-label "status:running"
257
+ gh issue close "$ISSUE_NUMBER" --reason completed
258
+ echo "✓ Routine run issue #$ISSUE_NUMBER closed as completed."
259
+ else
260
+ INTERRUPTION_BODY="### ❌ Milestone: Run Interrupted / Failed
261
+ - **Routine**: \`autowork\`
262
+ - **Status**: Workflow job failed or timed out (\`${JOB_STATUS}\`)
263
+ - **Step**: Actions execution halted before normal completion
264
+ - **Action Log**: [View GitHub Actions Run](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})
265
+
266
+ ---
267
+ _Generated by [Antigravity](${SERVER_URL}/${REPOSITORY}/actions/runs/${RUN_ID})_"
268
+ gh issue comment "$ISSUE_NUMBER" --body "$INTERRUPTION_BODY" || true
269
+ gh issue edit "$ISSUE_NUMBER" --add-label "status:failure,needs-attention" --remove-label "status:running"
270
+ echo "⚠️ Routine run issue #$ISSUE_NUMBER left open for triage (status: $JOB_STATUS)."
271
+ fi