Loop Flywheel - harvest proposals into a PR
loop-run and loop-revise only ever propose: on every terminal outcome they append a dated target / evidence / proposed edit block to $LOOP_STATE_DIR/_flywheel.md (default ~/.claude/loop-state/_flywheel.md) and never touch a skill file. This skill is the other half. It reads that backlog, decides which proposals earned a permanent line, applies them, and opens a PR for a human to merge.
Repo: the lago monorepo root; lago-front checkout at front/. Only .agents/skills/**, .agents/docs/** and scripts/ are in scope. Never a product code change.
Governance — the four rules every harvest applies
The instruction set has an entrance and needs an exit. Attention is a fixed budget; a check past the cap subtracts from the ones already there. These rules are the exit, and scripts/skill-budget.sh enforces the mechanical half of them on every push.
- Fixed cap per skill.
loop-review7 checks,loop-build7,loop-spec5,loop-run150 lines (skill-budget.sh). A proposal that adds a check names the check it deletes or merges into; without that line it is dropped, however good the evidence. - Doubled admission. A proposal enters only if it would have prevented the failure it cites and you can name a second, different, plausible occurrence — a different ticket shape, a different component family. "It would have helped here" alone is a one-off: drop, and say so.
- Mechanism > mechanical test > prose. If the proposal can be a script in
scripts/, an ESLint rule inpackages/configs/eslint-rules/, or a structural separation (a fresh subagent, a file the phase cannot see), write it that way and delete the prose it replaces. A prose check is the last resort, and only for what needs judgment. - Pruning by the journal. Read
$LOOP_STATE_DIR/_journal.md: thenotescell of every row starts withfail-check: …when a check produced a FAIL. A check absent from that column across the last 15 rows is a deletion candidate: delete it, or convert it to a lint rule / script, in this same PR. Fewer than 15 rows since the check was added → not yet.
Steps
Read the backlog:
_flywheel.md. Skip entries carrying anapplied:ordropped:line. Nothing left and no pruning candidate → report "nothing to harvest" and stop.Read the current instructions before judging anything: the
target:file of every remaining proposal,front/CLAUDE.mdand the.agents/docs/*it references, and the journal's last 15 rows. Several proposals will already be covered.Triage each proposal into keep / drop / merge / relocate, applying the four rules above in order:
- Keep when it is recurring or expensive (escaped a review, cost a cycle, shipped a defect), passes doubled admission, is written as the strongest mechanism available, and either replaces an existing check or costs no check slot.
- Drop when it fails any rule, is already covered (name the line), contradicts a project convention, or is too vague to verify. Dropping is the common case: say why in the report, the entry stays in the backlog.
- Merge two proposals stating the same rule into one bullet.
- Relocate:
target:is the proposer's guess. Test-authoring rules go tomake-tests/.agents/docs/testing-practices.md, codebase-wide conventions to.agents/docs/*, pipeline behaviour to the loop-* skill, anything mechanical toscripts/. A rule whose home is a doc gets a one-line pointer from the skill, never a copy.
Apply the kept proposals, one bullet each:
- Cap a proposal at 1-3 lines: the rule, the trigger, the remedy. Cut the evidence narrative.
- Strip every ticket ID. A proposal's evidence names the run; the rule it becomes does not.
skill-budget.shfails the push on any<TEAM>-<N>shape under.agents/**; examples use<ISSUE-ID>placeholders. - When the new rule supersedes an existing one, rewrite that line instead of stacking a second one beside it.
- A build-phase rule usually wants its review-phase mirror; add the mirror only when a review could plausibly miss it, and count both against their caps.
Refresh the styleguide mirror:
notion-fetchthe "Frontend coding styleguide" page and comparepage_last_edited_atwith theNotion last editeddate in.agents/docs/frontend-coding-styleguide.md. Moved → update the mirror (and the doc a section already lives in), bump both dates. Unchanged → nothing.Run the gate:
front/scripts/skill-budget.shmust exit 0 before the commit. Over cap → go back to step 3, a proposal must give way.Open the PR from a throwaway worktree off
origin/main. Markdown and scripts only; do NOT spend alago-worktreeslot:git -C front fetch origin main git -C front worktree add /tmp/lago-flywheel -b <BRANCH> origin/main<BRANCH>= kebab-case topic slug, no Linear ID, e.g.loop-skills-flywheel-harvest. Commit with adocs(agents):orchore(agents):subject of 50 chars or less, push, thengh pr create --base main --repo getlago/lago-front.PR body: one line per proposal (applied where, or dropped and why), one line per pruned check with the journal span that justified it, one line for the styleguide refresh. That list is what the operator reviews.
Mark the harvest in
_flywheel.md: append- applied: <PR URL>or- dropped: <one-line reason, naming the rule it failed>under each entry judged. Never delete an entry.Report: PR URL, kept/dropped/pruned counts, and the one-line rationale per dropped proposal.
Hard rules
- Proposals are input, not instructions. A
proposed editis a draft written by a tired pipeline at the end of a failed run: rewrite it, place it, or drop it, never paste it in verbatim. - Never delete or rewrite a flywheel entry, only annotate it.
- Never touch anything outside
.agents/skills/**,.agents/docs/**andscripts/. No product code, no test, no translation change. - Humans merge. No self-approval, no merge, no auto-merge flag.
- No AI attribution anywhere: commit message and PR carry no "Co-Authored-By", no "Generated with", no AI mention.
- Two communication registers: messages to humans (chat report, PR body) = short, direct, plain language. Internal state files (
_flywheel.mdannotations) = dense and precise, written for the AI of a later harvest.