Skip to content

Commit 24bc89f

Browse files
feat: add Regen command for unattended library regeneration
- Implements a new command to regenerate the oldest spec's libraries - Automates PR creation with quality scoring and labeling - Bypasses Cloud AI review for efficiency
1 parent 065875a commit 24bc89f

1 file changed

Lines changed: 236 additions & 0 deletions

File tree

agentic/commands/regen.md

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
# Regen Oldest Spec
2+
3+
> Picks the oldest non-recently-updated spec and regenerates all its library implementations locally — same flow as
4+
> `/update`, but unattended and without dispatching the Cloud AI review. PRs are auto-approved (or marked
5+
> `quality-poor`) based on the local quality score, so `impl-merge.yml` handles the rest. Works identically with the
6+
> default Claude Code config (Sonnet) and with a locally-configured model — the skill makes no model assumptions.
7+
8+
## Context
9+
10+
@CLAUDE.md
11+
@pyproject.toml
12+
13+
## Instructions
14+
15+
You are the **regen-lead**. Your job is to:
16+
17+
1. Pick the single oldest spec by latest implementation `updated` timestamp.
18+
2. Coordinate per-library updater agents (same mechanics as `/update`).
19+
3. Ship per-library PRs **without** triggering the Cloud AI review — instead label PRs directly so
20+
`impl-merge.yml` auto-merges (score ≥ 50) or leaves them open for manual review (score < 50).
21+
22+
**Prerequisite:** This command uses agent teams (experimental). Ensure `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` is set
23+
in your environment or Claude Code settings.
24+
25+
**Model-agnostic by design:** The skill never inspects or sets the LLM endpoint. Whatever Claude Code is currently
26+
configured for (Anthropic Sonnet by default, or any local OpenAI/Anthropic-compatible endpoint via `ANTHROPIC_BASE_URL`
27+
+ `ANTHROPIC_AUTH_TOKEN`) is what spawned sub-agents will use.
28+
29+
---
30+
31+
### Phase 1: Pick the oldest spec
32+
33+
Run this Python snippet to find the spec whose newest per-library `updated` timestamp is the oldest. Specs without any
34+
metadata are treated as "ancient" and picked first.
35+
36+
```bash
37+
SPEC_INFO=$(uv run python -c "
38+
from datetime import datetime, timezone
39+
from pathlib import Path
40+
import yaml
41+
42+
candidates = []
43+
for spec_dir in sorted(Path('plots').iterdir()):
44+
if not spec_dir.is_dir() or spec_dir.name.startswith('.'):
45+
continue
46+
meta_dir = spec_dir / 'metadata' / 'python'
47+
if not meta_dir.is_dir():
48+
continue
49+
latest = None
50+
for yf in meta_dir.glob('*.yaml'):
51+
try:
52+
d = yaml.safe_load(yf.read_text(encoding='utf-8')) or {}
53+
except Exception:
54+
continue
55+
u = d.get('updated') or d.get('created')
56+
if u and (latest is None or str(u) > str(latest)):
57+
latest = u
58+
if latest is None:
59+
candidates.append((datetime.min.replace(tzinfo=timezone.utc), spec_dir.name))
60+
continue
61+
try:
62+
dt = datetime.fromisoformat(str(latest).replace('Z', '+00:00'))
63+
if dt.tzinfo is None:
64+
dt = dt.replace(tzinfo=timezone.utc)
65+
candidates.append((dt, spec_dir.name))
66+
except Exception:
67+
continue
68+
candidates.sort()
69+
if candidates:
70+
dt, name = candidates[0]
71+
print(f'{name}\t{dt.isoformat()}')
72+
")
73+
74+
SPEC_ID=$(echo "$SPEC_INFO" | cut -f1)
75+
SPEC_LATEST=$(echo "$SPEC_INFO" | cut -f2)
76+
```
77+
78+
If `SPEC_ID` is empty, abort with a clear message — the repo has no eligible specs.
79+
80+
**Discover existing libraries:** Scan `plots/$SPEC_ID/implementations/python/` for `*.py` files (excluding
81+
`__init__.py`) — these are the libraries to regenerate. If the directory is empty, abort.
82+
83+
**Confirm with user:**
84+
85+
```
86+
Oldest spec: {SPEC_ID}
87+
Latest implementation update: {SPEC_LATEST}
88+
Libraries to regenerate: {comma-separated list}
89+
90+
Proceed? (y/n)
91+
```
92+
93+
Wait for explicit `y` / `yes` / `ja` / `passt`. Anything else aborts.
94+
95+
---
96+
97+
### Phase 2: Spawn per-library agents
98+
99+
Mirror Phase 3 of `/update` (`agentic/commands/update.md`). Read that file once and follow the same agent-spawning
100+
mechanics:
101+
102+
1. **Read the spec:** `plots/{SPEC_ID}/specification.md` and `plots/{SPEC_ID}/specification.yaml` to extract
103+
`{SPEC_TITLE}` and the primary `{PLOT_TYPE}` tag.
104+
2. **Create team:** `TeamCreate` with name `regen-{SPEC_ID}`.
105+
3. **For each library**, create a task via `TaskCreate`:
106+
- Subject: `Regen {library} implementation for {SPEC_ID}`
107+
- Description: `Unattended regeneration — no specific user request. Perform a comprehensive review and improve.`
108+
4. **Spawn one `general-purpose` agent per library** via the `Agent` tool with:
109+
- `team_name`: `regen-{SPEC_ID}`
110+
- `name`: `{library}`
111+
- `subagent_type`: `general-purpose`
112+
- `model`: `opus`
113+
- **Prompt:** Use the exact `Library Agent Prompt` from `agentic/commands/update.md` (the section starting at
114+
`## Library Agent Prompt`). Fill `{SPEC_ID}`, `{LIBRARY}`, `{CONTEXT7_LIBRARY}`, `{PLOT_TYPE}`, `{SPEC_TITLE}` as
115+
in `/update`. For `{DESCRIPTION}`, pass:
116+
`No specific user request — perform a comprehensive review across all dimensions (code quality, data choice,
117+
visual design, spec compliance, library feature usage, transferability) and improve where genuinely needed. If you
118+
find nothing meaningful to improve, report "no improvements needed" and leave the code unchanged.`
119+
120+
The library agent's Phase 7 (local quality scoring + up-to-2-iteration repair) runs unchanged — that's the local
121+
substitute for the Cloud `impl-review.yml`/`impl-repair.yml` chain.
122+
5. **Assign each task** to its agent immediately via `TaskUpdate` with `owner: "{library}"`.
123+
124+
All agents run in parallel.
125+
126+
**Skip these phases from `/update`:**
127+
- Phase 2 (Spec Optimization) — `/regen` is unattended.
128+
- Phase 4 / 5 (Collect-Present-Iterate with user feedback) — go directly to shipping once all agents report
129+
`STATUS: done` or `STATUS: conflict`. Conflicts are logged but do not block: if an agent reports a conflict, **skip
130+
that library** (don't ship it) and continue with the others.
131+
132+
---
133+
134+
### Phase 3: Ship (modified `/update` Phase 6)
135+
136+
For shipping, follow `/update`'s Phase 6 in `agentic/commands/update.md` — sub-phases **6a, 6b, 6c, 6d, 6e, 6f, 6g
137+
Step 1**.
138+
139+
**6g Step 1 override:** in the per-library worktree block, when creating the PR replace `/update`'s review-trigger:
140+
141+
```bash
142+
# /update.md does this (Cloud AI review):
143+
gh api repos/{owner}/{repo}/dispatches \
144+
-f event_type=review-pr \
145+
-f 'client_payload[pr_number]='"$PR_NUMBER"
146+
```
147+
148+
**…with this label-based dispatch (no Cloud AI call):**
149+
150+
```bash
151+
PR_NUMBER=$(gh pr view --json number -q '.number')
152+
SCORE={score reported by the library agent in its STATUS: done message}
153+
154+
# Always tag the PR with its locally-computed quality score.
155+
gh pr edit "$PR_NUMBER" --add-label "quality:${SCORE}"
156+
157+
if [ "$SCORE" -ge 50 ]; then
158+
# Auto-approve → impl-merge.yml triggers → squash-merge + GCS staging→prod + impl:{lib}:done.
159+
gh pr edit "$PR_NUMBER" --add-label "ai-approved"
160+
echo "::notice::PR #$PR_NUMBER ai-approved (score=$SCORE)"
161+
else
162+
# Score too low for auto-merge — flag for manual review.
163+
gh pr edit "$PR_NUMBER" --add-label "quality-poor"
164+
echo "::warning::PR #$PR_NUMBER flagged quality-poor (score=$SCORE) — left open for manual review"
165+
fi
166+
```
167+
168+
The PR body itself already includes the score and category breakdown (set by `/update` Phase 6g Step 1's `gh pr create`
169+
template).
170+
171+
**6g Step 2** (cleanup worktrees) and **Phase 7a** (shut down team, `TeamDelete`, remove `.update-preview/`) run
172+
unchanged.
173+
174+
---
175+
176+
### Phase 4: Brief monitor
177+
178+
Print a one-shot status table — no polling loop. The CI does the rest.
179+
180+
Wait 60 seconds (so `impl-merge.yml` has time to start), then for each PR:
181+
182+
```bash
183+
gh pr view "$PR_NUMBER" --json number,state,mergedAt,labels \
184+
--jq '[.number, .state, (.mergedAt // "—"), ([.labels[].name] | join(","))] | @tsv'
185+
```
186+
187+
Render a table:
188+
189+
```
190+
| Library | PR | State | Merged | Labels |
191+
|--------------|-------|-------|--------|-------------------------------------|
192+
| matplotlib | #1234 | MERGED| 14:02Z | quality:88, ai-approved |
193+
| seaborn | #1235 | OPEN | — | quality:42, quality-poor |
194+
| ... |
195+
```
196+
197+
Tell the user:
198+
- Which PRs auto-merged (success).
199+
- Which are `quality-poor` (need manual review).
200+
- That **no Cloud AI review/repair was triggered** — verifiable via:
201+
```bash
202+
gh run list --workflow=impl-review.yml --limit 5
203+
gh run list --workflow=impl-repair.yml --limit 5
204+
```
205+
(no new runs for this `SPEC_ID` should appear).
206+
207+
Then exit. The user can re-run `/regen` later to pick the next-oldest spec.
208+
209+
---
210+
211+
## Notes
212+
213+
- **Token cost:** All Claude Code calls (lead + agents) hit whatever endpoint Claude Code is configured for. With the
214+
default config that's Anthropic Sonnet (useful for end-to-end testing). With a local model configured via
215+
`ANTHROPIC_BASE_URL` + `ANTHROPIC_AUTH_TOKEN`, cost is effectively zero. **No code path differs between modes.**
216+
- **Cloud workflows skipped:** `impl-review.yml` and `impl-repair.yml` only trigger on explicit dispatch (see
217+
`.github/workflows/impl-review.yml` triggers — `workflow_dispatch` + `repository_dispatch types: [review-pr]`).
218+
Skipping the dispatch in Phase 3 is sufficient to bypass them entirely.
219+
- **Cloud workflows still active:** `impl-merge.yml` triggers on the `ai-approved` label, so it still squash-merges,
220+
promotes GCS staging → production, sets `impl:{library}:done` on the parent issue, and triggers `sync-postgres.yml`.
221+
This is intentional — we want those side effects.
222+
- **Failure modes:**
223+
- Agent reports `STATUS: conflict` → skip that library, continue with the others.
224+
- Library agent crashes (script error, lint failure after 3 retries) → the `/update` agent prompt already handles
225+
this; library is reported as a failure and skipped.
226+
- `gh` not authenticated or GCS creds missing → Phase 3 fails fast in the first worktree's push step.
227+
- **Re-run safe:** Running `/regen` twice in a row picks a different spec the second time (the freshly merged one is
228+
no longer oldest).
229+
230+
## Usage
231+
232+
```
233+
/regen
234+
```
235+
236+
No arguments. Always picks the single oldest spec.

0 commit comments

Comments
 (0)