jonah-fleet 1.1.0 → 1.4.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 +115 -0
- package/README.md +49 -6
- package/dist/commands/contribute.d.ts +29 -0
- package/dist/commands/contribute.d.ts.map +1 -0
- package/dist/commands/daemon.d.ts +8 -0
- package/dist/commands/daemon.d.ts.map +1 -0
- package/dist/commands/init.d.ts +13 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/monitor.d.ts +16 -0
- package/dist/commands/monitor.d.ts.map +1 -0
- package/dist/commands/run.d.ts +11 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/status.d.ts +9 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/sync.d.ts +7 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/telemetry.d.ts +14 -0
- package/dist/commands/telemetry.d.ts.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1986 -211
- package/dist/lib/daemon.d.ts +34 -0
- package/dist/lib/daemon.d.ts.map +1 -0
- package/dist/lib/dashboard.d.ts +10 -0
- package/dist/lib/dashboard.d.ts.map +1 -0
- package/dist/lib/detector.d.ts +25 -0
- package/dist/lib/detector.d.ts.map +1 -0
- package/dist/lib/diff.d.ts +10 -0
- package/dist/lib/diff.d.ts.map +1 -0
- package/dist/lib/evals.d.ts +72 -0
- package/dist/lib/evals.d.ts.map +1 -0
- package/dist/lib/fleet-query.d.ts +102 -0
- package/dist/lib/fleet-query.d.ts.map +1 -0
- package/dist/lib/global-config.d.ts +10 -0
- package/dist/lib/global-config.d.ts.map +1 -0
- package/dist/lib/installer.d.ts +27 -0
- package/dist/lib/installer.d.ts.map +1 -0
- package/dist/lib/manifest.d.ts +6 -0
- package/dist/lib/manifest.d.ts.map +1 -0
- package/dist/lib/presets.d.ts +52 -0
- package/dist/lib/presets.d.ts.map +1 -0
- package/dist/lib/runner.d.ts +40 -0
- package/dist/lib/runner.d.ts.map +1 -0
- package/dist/lib/telemetry.d.ts +90 -0
- package/dist/lib/telemetry.d.ts.map +1 -0
- package/dist/lib/worktree.d.ts +34 -0
- package/dist/lib/worktree.d.ts.map +1 -0
- package/package.json +17 -5
- package/schema.json +44 -1
- package/templates/docs/AGENTS.template.md +15 -0
- package/templates/prompts/ORCHESTRATION.md +107 -7
- package/templates/prompts/analytics-review.md +91 -0
- package/templates/prompts/autowork.md +15 -4
- package/templates/prompts/issues-housekeeping.md +2 -1
- package/templates/prompts/optimizer.md +46 -8
- package/templates/prompts/peer-review.md +35 -11
- package/templates/prompts/product-planning.md +18 -7
- package/templates/skills/diagnosing-bugs/SKILL.md +9 -0
- package/templates/workflows/autowork-cron.yml +19 -1
- package/templates/workflows/prompt-optimizer-cron.yml +22 -0
- package/templates/workflows/trigger-autowork-manual.yml +169 -0
- package/templates/workflows/trigger-autowork-on-bug.yml +5 -5
- package/templates/workflows/trigger-autowork-on-merge.yml +3 -3
- package/templates/workflows/trigger-review-routine.yml +107 -11
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Objective
|
|
4
4
|
|
|
5
|
-
Turn strategy and evidence into **well-scoped, ready-to-build work** and a **current roadmap** — the "plan" between analytics/measurement and autowork's build phase. Each run reads roadmap priorities, analytics signals, and user feedback to **propose** the next units of product work: new feature issues, formal specs/PRDs for larger epics, backlog re-ranking recommendations, and roadmap updates.
|
|
5
|
+
Turn strategy and evidence into **well-scoped, ready-to-build work** and a **current roadmap** — the "plan" between analytics/measurement and autowork's build phase. Each run reads roadmap priorities, closed measurement trackers (`RECOMMENDATION: [PIVOT | DEPRECATE | ITERATE]`), analytics signals, and user feedback to **propose** the next units of product work: new feature issues, feature pruning and deprecations, formal specs/PRDs for larger epics, backlog re-ranking recommendations, and roadmap updates.
|
|
6
6
|
|
|
7
7
|
This routine runs behind a **human approval gate**: it **never files autowork-ready issues on its own**. It stages proposals in one planning issue for operator approval; only a subsequent operator-approved **Promote** run creates the real issues autowork can claim.
|
|
8
8
|
|
|
@@ -11,9 +11,10 @@ This routine runs behind a **human approval gate**: it **never files autowork-re
|
|
|
11
11
|
This routine runs in two modes: **Propose** (scheduled cron sweep / unapproved fire) and **Promote** (operator-approved fire).
|
|
12
12
|
|
|
13
13
|
In **Propose mode**, SUCCESS requires:
|
|
14
|
-
- [ ] Read current roadmap, domain documentation, and recent feedback/analytics findings
|
|
14
|
+
- [ ] Read current roadmap, domain documentation, closed measurement trackers, and recent feedback/analytics findings
|
|
15
|
+
- [ ] Performed Feature Pruning & Deprecation Audit: evaluated shipped features and measurement outcomes for low-ROI (<2% user adoption or >50% failure rate) features, drafting deprecation, removal, or simplification proposals
|
|
15
16
|
- [ ] Created or updated exactly one dated staging issue (`🗺️ Product Plan — {date}`) containing:
|
|
16
|
-
- Up to 3 well-scoped proposals (Summary/Tasks/Why/Complexity)
|
|
17
|
+
- Up to 3 well-scoped proposals (Summary/Tasks/Why/Complexity), covering additions, pivots, or deprecations
|
|
17
18
|
- Backlog re-ranking recommendations
|
|
18
19
|
- Formal `/to-spec` PRD drafts for any proposal above `size/M`
|
|
19
20
|
- Proposed `ROADMAP.md` updates
|
|
@@ -33,6 +34,13 @@ If any criterion cannot be met, stop immediately and log FAILURE with the reason
|
|
|
33
34
|
- **Max iterations**: 30 — after 30 tool call rounds without completing Definition of Done, STOP. Log FAILURE with category `token_limit`.
|
|
34
35
|
- **Max scope**: planning and staging only. Do not implement code or open feature PRs.
|
|
35
36
|
- **Language Requirement**: All GitHub issue titles, descriptions, task checklists, and comments MUST be written in **English**.
|
|
37
|
+
- **Session link footer**: sign every GitHub post with the Antigravity run footer (`_Generated by [Antigravity](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID})_`).
|
|
38
|
+
|
|
39
|
+
## Negative examples (DO NOT do these)
|
|
40
|
+
|
|
41
|
+
- Do not ignore closed measurement tracker directives (`RECOMMENDATION: [PIVOT | DEPRECATE | ITERATE]`) — incorporate them directly into planning.
|
|
42
|
+
- Do not propose only new features while ignoring dead features with <2% adoption or >50% failure rates — keep the codebase lean by actively proposing deprecations.
|
|
43
|
+
- Do not create unapproved autowork-ready candidate issues directly in Propose mode.
|
|
36
44
|
|
|
37
45
|
## Instructions
|
|
38
46
|
|
|
@@ -43,10 +51,13 @@ If any criterion cannot be met, stop immediately and log FAILURE with the reason
|
|
|
43
51
|
|
|
44
52
|
### Steps 1–3: Propose Mode (Staging Proposals)
|
|
45
53
|
|
|
46
|
-
1. Read `ROADMAP.md`, `AGENTS.md`, and open issues.
|
|
47
|
-
2.
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
1. Read `ROADMAP.md`, `AGENTS.md`, closed measurement trackers with `RECOMMENDATION: [PIVOT | DEPRECATE | ITERATE]`, and open issues.
|
|
55
|
+
2. **Feature Pruning & Deprecation Audit**:
|
|
56
|
+
- Audit shipped features and closed measurement tracker verdicts.
|
|
57
|
+
- For any feature with <2% user adoption, sub-threshold CTR, or >50% failure rate, draft explicit deprecation, removal, or pivot proposals to keep the codebase lean and eliminate maintenance waste.
|
|
58
|
+
3. Draft up to 3 high-impact proposals (including additions, pivots, or deprecations) based on roadmap priorities, measurement outcomes, and user feedback.
|
|
59
|
+
4. For proposals sized `size/M` or above, draft a formal specification using `/to-spec`.
|
|
60
|
+
5. Stage all proposals in a dedicated staging issue: `🗺️ Product Plan — {YYYY-MM-DD}` assigned to the repo maintainer.
|
|
50
61
|
|
|
51
62
|
### Step 4: Promote Mode (Approved Execution)
|
|
52
63
|
|
|
@@ -85,6 +85,15 @@ Done when **every remaining element is load-bearing**: removing any one of them
|
|
|
85
85
|
|
|
86
86
|
Do not proceed until you have reproduced **and** minimised.
|
|
87
87
|
|
|
88
|
+
## Intent vs. Defect Guardrail (Telemetry Rabbit Hole Prevention)
|
|
89
|
+
|
|
90
|
+
When diagnosing an under-performing event, low-conversion funnel, or zero-click CTA:
|
|
91
|
+
|
|
92
|
+
1. **Check for mechanical functionality first.** Run the code path or click handler in isolation: does it execute, route, and emit telemetry without throwing?
|
|
93
|
+
2. **Distinguish user intent from code defects.** If the feature renders above the fold and works when clicked, but telemetry reveals near-zero clicks (<2% adoption), the root cause is **lack of user intent**, not a software bug.
|
|
94
|
+
3. **Prevent the Telemetry Rabbit Hole.** Do NOT spend cycles instrumenting elaborate fallback telemetry, defensive error handling, or retry loops for a feature users simply do not want.
|
|
95
|
+
4. **Handoff to product planning.** Reclassify the issue as a product/UX question (`needs-design` or `roadmap/*`) and hand it off to `product-planning` rather than writing code fixes.
|
|
96
|
+
|
|
88
97
|
## Phase 3: Hypothesise
|
|
89
98
|
|
|
90
99
|
Generate **3–5 ranked hypotheses** before testing any of them. Single-hypothesis generation anchors on the first plausible idea.
|
|
@@ -12,6 +12,10 @@ on:
|
|
|
12
12
|
description: 'Optional issue number to work in Targeted mode (leave blank for Scan mode)'
|
|
13
13
|
required: false
|
|
14
14
|
type: string
|
|
15
|
+
issue_number:
|
|
16
|
+
description: 'Alias for target_issue (e.g. 18)'
|
|
17
|
+
required: false
|
|
18
|
+
type: string
|
|
15
19
|
|
|
16
20
|
concurrency:
|
|
17
21
|
group: autowork-agent-scan
|
|
@@ -106,7 +110,7 @@ jobs:
|
|
|
106
110
|
GOOGLE_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
107
111
|
ANTIGRAVITY_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
108
112
|
GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
|
|
109
|
-
TARGET_ISSUE: ${{ inputs.target_issue }}
|
|
113
|
+
TARGET_ISSUE: ${{ inputs.target_issue || inputs.issue_number }}
|
|
110
114
|
SKILLS_PROMPT: ${{ steps.skills.outputs.skills_prompt }}
|
|
111
115
|
GITHUB_SERVER_URL: ${{ github.server_url }}
|
|
112
116
|
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
@@ -132,6 +136,20 @@ jobs:
|
|
|
132
136
|
cat "$LATEST_LOG" >> "$GITHUB_STEP_SUMMARY"
|
|
133
137
|
fi
|
|
134
138
|
|
|
139
|
+
- name: Emit Fleet Telemetry (Opt-in)
|
|
140
|
+
if: always()
|
|
141
|
+
env:
|
|
142
|
+
GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
|
|
143
|
+
TELEMETRY_ENDPOINT: ${{ secrets.JONAH_FLEET_TELEMETRY_ENDPOINT }}
|
|
144
|
+
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
145
|
+
GITHUB_RUN_ID: ${{ github.run_id }}
|
|
146
|
+
GITHUB_RUN_NUMBER: ${{ github.run_number }}
|
|
147
|
+
run: |
|
|
148
|
+
LATEST_LOG=$(ls -t .github/prompts/logs/autowork/*.md 2>/dev/null | head -n 1)
|
|
149
|
+
if [ -n "$LATEST_LOG" ] && [ -f "$LATEST_LOG" ]; then
|
|
150
|
+
npx --yes jonah-fleet telemetry --emit --log "$LATEST_LOG" --endpoint "$TELEMETRY_ENDPOINT" || true
|
|
151
|
+
fi
|
|
152
|
+
|
|
135
153
|
- name: Verify Atomic Handoff Invariant (PR Existence Guard)
|
|
136
154
|
if: always()
|
|
137
155
|
env:
|
|
@@ -83,3 +83,25 @@ jobs:
|
|
|
83
83
|
--output-format text \
|
|
84
84
|
--print-timeout 35m \
|
|
85
85
|
--dangerously-skip-permissions
|
|
86
|
+
|
|
87
|
+
- name: Emit run summary
|
|
88
|
+
if: always()
|
|
89
|
+
run: |
|
|
90
|
+
LATEST_LOG=$(ls -t .github/prompts/logs/optimizer/*.md 2>/dev/null | head -n 1)
|
|
91
|
+
if [ -n "$LATEST_LOG" ] && [ -n "$GITHUB_STEP_SUMMARY" ]; then
|
|
92
|
+
cat "$LATEST_LOG" >> "$GITHUB_STEP_SUMMARY"
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
- name: Emit Fleet Telemetry (Opt-in)
|
|
96
|
+
if: always()
|
|
97
|
+
env:
|
|
98
|
+
GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
|
|
99
|
+
TELEMETRY_ENDPOINT: ${{ secrets.JONAH_FLEET_TELEMETRY_ENDPOINT }}
|
|
100
|
+
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
101
|
+
GITHUB_RUN_ID: ${{ github.run_id }}
|
|
102
|
+
GITHUB_RUN_NUMBER: ${{ github.run_number }}
|
|
103
|
+
run: |
|
|
104
|
+
LATEST_LOG=$(ls -t .github/prompts/logs/optimizer/*.md 2>/dev/null | head -n 1)
|
|
105
|
+
if [ -n "$LATEST_LOG" ] && [ -f "$LATEST_LOG" ]; then
|
|
106
|
+
npx --yes jonah-fleet telemetry --emit --log "$LATEST_LOG" --endpoint "$TELEMETRY_ENDPOINT" || true
|
|
107
|
+
fi
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
name: Trigger Autowork Manually (Targeted)
|
|
2
|
+
|
|
3
|
+
# Manual execution of the Autowork routine on a specific issue via workflow_dispatch or badge link click.
|
|
4
|
+
|
|
5
|
+
on:
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
issue_number:
|
|
9
|
+
description: 'Target issue number to work in Targeted mode (e.g. 18)'
|
|
10
|
+
required: true
|
|
11
|
+
type: string
|
|
12
|
+
mode:
|
|
13
|
+
description: 'Execution mode (targeted or scan)'
|
|
14
|
+
required: false
|
|
15
|
+
default: 'targeted'
|
|
16
|
+
type: choice
|
|
17
|
+
options:
|
|
18
|
+
- targeted
|
|
19
|
+
- scan
|
|
20
|
+
custom_prompt:
|
|
21
|
+
description: 'Optional custom instructions or overrides'
|
|
22
|
+
required: false
|
|
23
|
+
type: string
|
|
24
|
+
|
|
25
|
+
concurrency:
|
|
26
|
+
group: autowork-manual-${{ inputs.issue_number || github.run_id }}
|
|
27
|
+
cancel-in-progress: false
|
|
28
|
+
|
|
29
|
+
jobs:
|
|
30
|
+
run-autowork:
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
timeout-minutes: 65
|
|
33
|
+
permissions:
|
|
34
|
+
contents: write
|
|
35
|
+
issues: write
|
|
36
|
+
pull-requests: write
|
|
37
|
+
statuses: write
|
|
38
|
+
actions: read
|
|
39
|
+
steps:
|
|
40
|
+
- name: Checkout repository
|
|
41
|
+
uses: actions/checkout@v4
|
|
42
|
+
with:
|
|
43
|
+
fetch-depth: 0
|
|
44
|
+
|
|
45
|
+
- name: Setup Node.js
|
|
46
|
+
uses: actions/setup-node@v4
|
|
47
|
+
with:
|
|
48
|
+
node-version: 22
|
|
49
|
+
|
|
50
|
+
- name: Configure Git author identity
|
|
51
|
+
run: |
|
|
52
|
+
git config --global user.name "github-actions[bot]"
|
|
53
|
+
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
|
54
|
+
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
|
55
|
+
|
|
56
|
+
- name: Cache Antigravity CLI
|
|
57
|
+
id: cache-agy
|
|
58
|
+
uses: actions/cache@v4
|
|
59
|
+
with:
|
|
60
|
+
path: ~/.local/bin/agy
|
|
61
|
+
key: agy-cli-linux-amd64-v1
|
|
62
|
+
|
|
63
|
+
- name: Install Antigravity CLI
|
|
64
|
+
if: steps.cache-agy.outputs.cache-hit != 'true'
|
|
65
|
+
run: |
|
|
66
|
+
curl -fsSL https://antigravity.google/cli/install.sh | bash
|
|
67
|
+
|
|
68
|
+
- name: Add Antigravity CLI to PATH
|
|
69
|
+
run: echo "$HOME/.local/bin" >> $GITHUB_PATH
|
|
70
|
+
|
|
71
|
+
- name: Write Antigravity OAuth Token
|
|
72
|
+
env:
|
|
73
|
+
ANTIGRAVITY_OAUTH_TOKEN: ${{ secrets.ANTIGRAVITY_OAUTH_TOKEN || secrets.GEMINI_API_KEY }}
|
|
74
|
+
run: |
|
|
75
|
+
if [ -z "$ANTIGRAVITY_OAUTH_TOKEN" ]; then
|
|
76
|
+
echo "::error::Neither ANTIGRAVITY_OAUTH_TOKEN nor GEMINI_API_KEY secret is set; cannot authenticate Antigravity CLI." >&2
|
|
77
|
+
exit 1
|
|
78
|
+
fi
|
|
79
|
+
mkdir -p "$HOME/.gemini/antigravity-cli"
|
|
80
|
+
chmod 700 "$HOME/.gemini/antigravity-cli"
|
|
81
|
+
printf '%s' "$ANTIGRAVITY_OAUTH_TOKEN" > "$HOME/.gemini/antigravity-cli/antigravity-oauth-token"
|
|
82
|
+
chmod 600 "$HOME/.gemini/antigravity-cli/antigravity-oauth-token"
|
|
83
|
+
|
|
84
|
+
- name: Preflight validation
|
|
85
|
+
env:
|
|
86
|
+
GH_TOKEN: ${{ github.token }}
|
|
87
|
+
run: |
|
|
88
|
+
if [ ! -s "$HOME/.gemini/antigravity-cli/antigravity-oauth-token" ]; then
|
|
89
|
+
echo "::error::Preflight check failed: Antigravity OAuth token file is missing or empty." >&2
|
|
90
|
+
exit 1
|
|
91
|
+
fi
|
|
92
|
+
if ! gh auth status >/dev/null 2>&1; then
|
|
93
|
+
echo "::error::GitHub CLI authentication check failed with GITHUB_TOKEN." >&2
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
96
|
+
echo "✓ Preflight credential and GitHub CLI validation passed."
|
|
97
|
+
|
|
98
|
+
- name: Discover domain skills
|
|
99
|
+
id: skills
|
|
100
|
+
run: |
|
|
101
|
+
SKILLS_PROMPT=""
|
|
102
|
+
if [ -d ".agents/skills" ]; then
|
|
103
|
+
for skill_path in .agents/skills/*/SKILL.md; do
|
|
104
|
+
if [ -f "$skill_path" ]; then
|
|
105
|
+
SKILLS_PROMPT="${SKILLS_PROMPT}Read and follow ${skill_path}. "
|
|
106
|
+
fi
|
|
107
|
+
done
|
|
108
|
+
fi
|
|
109
|
+
echo "skills_prompt=$SKILLS_PROMPT" >> "$GITHUB_OUTPUT"
|
|
110
|
+
|
|
111
|
+
- name: Run Autowork
|
|
112
|
+
timeout-minutes: 60
|
|
113
|
+
env:
|
|
114
|
+
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
115
|
+
GOOGLE_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
116
|
+
ANTIGRAVITY_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
117
|
+
GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
|
|
118
|
+
TARGET_ISSUE: ${{ inputs.issue_number }}
|
|
119
|
+
MODE: ${{ inputs.mode || 'targeted' }}
|
|
120
|
+
CUSTOM_PROMPT: ${{ inputs.custom_prompt }}
|
|
121
|
+
SKILLS_PROMPT: ${{ steps.skills.outputs.skills_prompt }}
|
|
122
|
+
GITHUB_SERVER_URL: ${{ github.server_url }}
|
|
123
|
+
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
124
|
+
GITHUB_RUN_ID: ${{ github.run_id }}
|
|
125
|
+
run: |
|
|
126
|
+
if [ -n "$TARGET_ISSUE" ] && [ "$MODE" != "scan" ]; then
|
|
127
|
+
PROMPT="You are the Autowork routine for this repository. Read and follow the instructions in .github/prompts/autowork.md exactly. ${SKILLS_PROMPT} Your target is issue #${TARGET_ISSUE}. You are in Targeted mode: work issue #${TARGET_ISSUE} directly, ahead of Phase 1 convergence and priority scan."
|
|
128
|
+
else
|
|
129
|
+
PROMPT="You are the Autowork routine for this repository. Read and follow the instructions in .github/prompts/autowork.md exactly. ${SKILLS_PROMPT} You are in Scan mode: check open PRs for review comments to fix, close merged issues, then pick the highest-priority unclaimed issue."
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
if [ -n "$CUSTOM_PROMPT" ]; then
|
|
133
|
+
PROMPT="${PROMPT} Additional instructions: ${CUSTOM_PROMPT}"
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
agy -p "$PROMPT" \
|
|
137
|
+
--model gemini-3.7-flash-high \
|
|
138
|
+
--output-format text \
|
|
139
|
+
--print-timeout 60m \
|
|
140
|
+
--dangerously-skip-permissions
|
|
141
|
+
|
|
142
|
+
- name: Emit run summary
|
|
143
|
+
if: always()
|
|
144
|
+
run: |
|
|
145
|
+
LATEST_LOG=$(ls -t .github/prompts/logs/autowork/*.md 2>/dev/null | head -n 1)
|
|
146
|
+
if [ -n "$LATEST_LOG" ] && [ -n "$GITHUB_STEP_SUMMARY" ]; then
|
|
147
|
+
cat "$LATEST_LOG" >> "$GITHUB_STEP_SUMMARY"
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
- name: Verify Atomic Handoff Invariant (PR Existence Guard)
|
|
151
|
+
if: always()
|
|
152
|
+
env:
|
|
153
|
+
GH_TOKEN: ${{ github.token }}
|
|
154
|
+
run: |
|
|
155
|
+
LATEST_LOG=$(ls -t .github/prompts/logs/autowork/*.md 2>/dev/null | head -n 1)
|
|
156
|
+
if [ -n "$LATEST_LOG" ]; then
|
|
157
|
+
if grep -q "Result.*SUCCESS" "$LATEST_LOG"; then
|
|
158
|
+
WORKED_BRANCH=$(grep -E '^\s*-\s*(Pushed branch|Branch):' "$LATEST_LOG" | head -n 1 | sed -E 's/.*`([^`]+)`.*/\1/')
|
|
159
|
+
if [ -n "$WORKED_BRANCH" ] && [[ "$WORKED_BRANCH" =~ (feat|fix)/ ]]; then
|
|
160
|
+
echo "Verifying PR existence for branch: $WORKED_BRANCH"
|
|
161
|
+
PR_COUNT=$(gh pr list --head "$WORKED_BRANCH" --state all --json number --jq 'length' 2>/dev/null || echo "0")
|
|
162
|
+
if [ "$PR_COUNT" -eq 0 ]; then
|
|
163
|
+
echo "::error::Atomic Handoff Invariant Violated: Branch '$WORKED_BRANCH' was implemented and pushed, but no pull request exists on GitHub!" >&2
|
|
164
|
+
exit 1
|
|
165
|
+
fi
|
|
166
|
+
echo "✓ Verified pull request exists on GitHub for branch: $WORKED_BRANCH"
|
|
167
|
+
fi
|
|
168
|
+
fi
|
|
169
|
+
fi
|
|
@@ -11,10 +11,10 @@ concurrency:
|
|
|
11
11
|
jobs:
|
|
12
12
|
fire-autowork:
|
|
13
13
|
if: >-
|
|
14
|
-
(github.event.action == 'opened' && contains(github.event.issue.labels.*.name, 'bug') && contains(github.event.issue.labels.*.name, 'priority/P1')) ||
|
|
15
|
-
(github.event.action == 'labeled' && (github.event.
|
|
14
|
+
(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
|
+
(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')))
|
|
16
16
|
runs-on: ubuntu-latest
|
|
17
|
-
timeout-minutes:
|
|
17
|
+
timeout-minutes: 35
|
|
18
18
|
permissions:
|
|
19
19
|
contents: write
|
|
20
20
|
issues: write
|
|
@@ -80,7 +80,7 @@ jobs:
|
|
|
80
80
|
echo "skills_prompt=$SKILLS_PROMPT" >> "$GITHUB_OUTPUT"
|
|
81
81
|
|
|
82
82
|
- name: Run Autowork
|
|
83
|
-
timeout-minutes:
|
|
83
|
+
timeout-minutes: 30
|
|
84
84
|
env:
|
|
85
85
|
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
86
86
|
GOOGLE_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
@@ -97,5 +97,5 @@ jobs:
|
|
|
97
97
|
agy -p "$PROMPT" \
|
|
98
98
|
--model gemini-3.7-flash-high \
|
|
99
99
|
--output-format text \
|
|
100
|
-
--print-timeout
|
|
100
|
+
--print-timeout 30m \
|
|
101
101
|
--dangerously-skip-permissions
|
|
@@ -15,7 +15,7 @@ jobs:
|
|
|
15
15
|
github.event.pull_request.base.ref == 'main' &&
|
|
16
16
|
github.event.pull_request.head.repo.full_name == github.repository
|
|
17
17
|
runs-on: ubuntu-latest
|
|
18
|
-
timeout-minutes:
|
|
18
|
+
timeout-minutes: 35
|
|
19
19
|
permissions:
|
|
20
20
|
contents: write
|
|
21
21
|
issues: write
|
|
@@ -110,7 +110,7 @@ jobs:
|
|
|
110
110
|
|
|
111
111
|
- name: Run Autowork
|
|
112
112
|
if: steps.pick.outputs.skip != 'true'
|
|
113
|
-
timeout-minutes:
|
|
113
|
+
timeout-minutes: 30
|
|
114
114
|
env:
|
|
115
115
|
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
116
116
|
GOOGLE_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
@@ -126,5 +126,5 @@ jobs:
|
|
|
126
126
|
agy -p "$PROMPT" \
|
|
127
127
|
--model gemini-3.7-flash-high \
|
|
128
128
|
--output-format text \
|
|
129
|
-
--print-timeout
|
|
129
|
+
--print-timeout 30m \
|
|
130
130
|
--dangerously-skip-permissions
|
|
@@ -2,21 +2,48 @@ name: Trigger Review Routine
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
pull_request:
|
|
5
|
-
types: [ready_for_review, opened, reopened, synchronize]
|
|
5
|
+
types: [ready_for_review, opened, reopened, synchronize, review_requested]
|
|
6
6
|
paths-ignore:
|
|
7
7
|
- '.github/prompts/logs/**'
|
|
8
|
+
schedule:
|
|
9
|
+
# Periodic scan sweep every 2 hours to catch and recover any orphaned or unreviewed ready PRs
|
|
10
|
+
- cron: '45 */2 * * *'
|
|
11
|
+
workflow_dispatch:
|
|
12
|
+
inputs:
|
|
13
|
+
pr_number:
|
|
14
|
+
description: 'Optional PR number to review in Targeted mode (leave blank for Scan mode)'
|
|
15
|
+
required: false
|
|
16
|
+
type: string
|
|
17
|
+
issue_comment:
|
|
18
|
+
types: [created]
|
|
8
19
|
|
|
9
20
|
concurrency:
|
|
10
|
-
group: review-routine-pr-${{ github.event.pull_request.number }}
|
|
21
|
+
group: review-routine-pr-${{ github.event.pull_request.number || github.event.issue.number || inputs.pr_number || 'scan' }}
|
|
11
22
|
cancel-in-progress: true
|
|
12
23
|
|
|
13
24
|
jobs:
|
|
14
25
|
trigger-routine:
|
|
15
26
|
if: >-
|
|
16
|
-
github.
|
|
17
|
-
|
|
27
|
+
(github.event_name == 'pull_request' &&
|
|
28
|
+
github.event.pull_request.draft == false &&
|
|
29
|
+
github.event.pull_request.head.repo.full_name == github.repository &&
|
|
30
|
+
!startsWith(github.event.pull_request.head.ref, 'release-please--')) ||
|
|
31
|
+
(github.event_name == 'schedule') ||
|
|
32
|
+
(github.event_name == 'workflow_dispatch') ||
|
|
33
|
+
(github.event_name == 'issue_comment' &&
|
|
34
|
+
github.event.issue.pull_request != null &&
|
|
35
|
+
!endsWith(github.event.comment.user.login, '[bot]') &&
|
|
36
|
+
github.event.comment.user.type != 'Bot' &&
|
|
37
|
+
(startsWith(github.event.comment.body, '/review') ||
|
|
38
|
+
startsWith(github.event.comment.body, '/peer-review') ||
|
|
39
|
+
startsWith(github.event.comment.body, '/retrigger') ||
|
|
40
|
+
startsWith(github.event.comment.body, '/re-review') ||
|
|
41
|
+
contains(github.event.comment.body, '/review') ||
|
|
42
|
+
contains(github.event.comment.body, '/peer-review') ||
|
|
43
|
+
contains(github.event.comment.body, '/retrigger') ||
|
|
44
|
+
contains(github.event.comment.body, '/re-review')))
|
|
18
45
|
runs-on: ubuntu-latest
|
|
19
|
-
timeout-minutes:
|
|
46
|
+
timeout-minutes: 35
|
|
20
47
|
permissions:
|
|
21
48
|
contents: write
|
|
22
49
|
actions: read
|
|
@@ -28,7 +55,21 @@ jobs:
|
|
|
28
55
|
id: guard
|
|
29
56
|
env:
|
|
30
57
|
GH_TOKEN: ${{ github.token }}
|
|
58
|
+
PR_NUM: ${{ github.event.pull_request.number || github.event.issue.number || inputs.pr_number }}
|
|
31
59
|
run: |
|
|
60
|
+
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")
|
|
63
|
+
if [ "$open_prs" -eq "0" ]; then
|
|
64
|
+
echo "No open PRs found in ${{ github.repository }} — skipping scheduled review routine scan."
|
|
65
|
+
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
fi
|
|
69
|
+
echo "skip=false" >> "$GITHUB_OUTPUT"
|
|
70
|
+
exit 0
|
|
71
|
+
fi
|
|
72
|
+
|
|
32
73
|
gh_api_retry() {
|
|
33
74
|
local endpoint="$1"
|
|
34
75
|
shift
|
|
@@ -46,18 +87,37 @@ jobs:
|
|
|
46
87
|
return 1
|
|
47
88
|
}
|
|
48
89
|
|
|
49
|
-
if ! pr=$(gh_api_retry "repos/${{ github.repository }}/pulls/${
|
|
90
|
+
if ! pr=$(gh_api_retry "repos/${{ github.repository }}/pulls/${PR_NUM}"); then
|
|
50
91
|
echo "skip=false" >> "$GITHUB_OUTPUT"
|
|
51
92
|
exit 0
|
|
52
93
|
fi
|
|
53
94
|
|
|
54
95
|
state=$(echo "$pr" | jq -r '.state')
|
|
55
96
|
merged=$(echo "$pr" | jq -r '.merged')
|
|
97
|
+
head_ref=$(echo "$pr" | jq -r '.head.ref')
|
|
56
98
|
if [ "$state" = "closed" ] || [ "$merged" = "true" ]; then
|
|
57
99
|
echo "PR is already $state/merged=$merged — skipping review."
|
|
58
100
|
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
59
101
|
exit 0
|
|
60
102
|
fi
|
|
103
|
+
if [[ "$head_ref" == release-please--* ]]; then
|
|
104
|
+
echo "PR head branch $head_ref is an automated release PR — skipping review."
|
|
105
|
+
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
106
|
+
exit 0
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
# Priority-driven dual execution filter on direct pull_request triggers:
|
|
110
|
+
# If PR is labeled priority/P2 or priority/P3, skip immediate cloud review to let local agent daemons review it
|
|
111
|
+
# (cloud scheduled watchdog sweep will catch up if unreviewed > 48h).
|
|
112
|
+
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
|
113
|
+
labels=$(echo "$pr" | jq -r '.labels[].name' 2>/dev/null || echo "")
|
|
114
|
+
if echo "$labels" | grep -qE '^priority/P[23]$'; then
|
|
115
|
+
echo "PR is labeled as lower priority (P2/P3) — skipping immediate cloud review for local peer-review daemon."
|
|
116
|
+
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
117
|
+
exit 0
|
|
118
|
+
fi
|
|
119
|
+
fi
|
|
120
|
+
|
|
61
121
|
echo "skip=false" >> "$GITHUB_OUTPUT"
|
|
62
122
|
|
|
63
123
|
- name: Checkout repository
|
|
@@ -112,26 +172,48 @@ jobs:
|
|
|
112
172
|
|
|
113
173
|
- name: Run Peer Review
|
|
114
174
|
if: steps.guard.outputs.skip != 'true'
|
|
115
|
-
timeout-minutes:
|
|
175
|
+
timeout-minutes: 30
|
|
116
176
|
env:
|
|
117
177
|
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
118
178
|
GOOGLE_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
119
179
|
ANTIGRAVITY_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
120
180
|
GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
|
|
121
|
-
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
122
|
-
PR_URL: ${{ github.event.pull_request.html_url }}
|
|
181
|
+
PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number || inputs.pr_number }}
|
|
182
|
+
PR_URL: ${{ github.event.pull_request.html_url || github.event.issue.html_url }}
|
|
123
183
|
GITHUB_SERVER_URL: ${{ github.server_url }}
|
|
124
184
|
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
125
185
|
GITHUB_RUN_ID: ${{ github.run_id }}
|
|
126
186
|
run: |
|
|
127
|
-
|
|
187
|
+
if [ -n "$PR_NUMBER" ]; then
|
|
188
|
+
if [ -z "$PR_URL" ] || [[ "$PR_URL" == *"/issues/"* ]]; then
|
|
189
|
+
PR_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/pull/${PR_NUMBER}"
|
|
190
|
+
fi
|
|
191
|
+
PROMPT="You are the Peer Review routine for this repository. Read and follow the instructions in .github/prompts/peer-review.md exactly. Your target is pull request #${PR_NUMBER} (${PR_URL}). You are in Targeted mode: review PR #${PR_NUMBER} directly."
|
|
192
|
+
else
|
|
193
|
+
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."
|
|
194
|
+
fi
|
|
128
195
|
|
|
129
196
|
agy -p "$PROMPT" \
|
|
130
197
|
--model gemini-3.7-flash-high \
|
|
131
198
|
--output-format text \
|
|
132
|
-
--print-timeout
|
|
199
|
+
--print-timeout 30m \
|
|
133
200
|
--dangerously-skip-permissions
|
|
134
201
|
|
|
202
|
+
- name: Handle Review Failure & PR Notification
|
|
203
|
+
if: failure() && steps.guard.outputs.skip != 'true'
|
|
204
|
+
env:
|
|
205
|
+
GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
|
|
206
|
+
PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number || inputs.pr_number }}
|
|
207
|
+
GITHUB_SERVER_URL: ${{ github.server_url }}
|
|
208
|
+
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
209
|
+
GITHUB_RUN_ID: ${{ github.run_id }}
|
|
210
|
+
run: |
|
|
211
|
+
if [ -n "$PR_NUMBER" ]; then
|
|
212
|
+
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.
|
|
213
|
+
|
|
214
|
+
_Generated by [Antigravity](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID})_" || true
|
|
215
|
+
fi
|
|
216
|
+
|
|
135
217
|
- name: Emit run summary
|
|
136
218
|
if: always() && steps.guard.outputs.skip != 'true'
|
|
137
219
|
run: |
|
|
@@ -139,3 +221,17 @@ jobs:
|
|
|
139
221
|
if [ -n "$LATEST_LOG" ] && [ -n "$GITHUB_STEP_SUMMARY" ]; then
|
|
140
222
|
cat "$LATEST_LOG" >> "$GITHUB_STEP_SUMMARY"
|
|
141
223
|
fi
|
|
224
|
+
|
|
225
|
+
- name: Emit Fleet Telemetry (Opt-in)
|
|
226
|
+
if: always() && steps.guard.outputs.skip != 'true'
|
|
227
|
+
env:
|
|
228
|
+
GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
|
|
229
|
+
TELEMETRY_ENDPOINT: ${{ secrets.JONAH_FLEET_TELEMETRY_ENDPOINT }}
|
|
230
|
+
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
231
|
+
GITHUB_RUN_ID: ${{ github.run_id }}
|
|
232
|
+
GITHUB_RUN_NUMBER: ${{ github.run_number }}
|
|
233
|
+
run: |
|
|
234
|
+
LATEST_LOG=$(ls -t .github/prompts/logs/peer-review/*.md 2>/dev/null | head -n 1)
|
|
235
|
+
if [ -n "$LATEST_LOG" ] && [ -f "$LATEST_LOG" ]; then
|
|
236
|
+
npx --yes jonah-fleet telemetry --emit --log "$LATEST_LOG" --endpoint "$TELEMETRY_ENDPOINT" || true
|
|
237
|
+
fi
|