feat: script Forgejo writes; fj-worker becomes recovery-only #9
No reviewers
Labels
No labels
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
public/chiche-plugins!9
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/fj-write-script"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Goal: Make routine Forgejo writes fast, visible, and never-silent by scripting them (a new
fj_write.pymirroringfj_read.py), instead of routing every write through thefj-workerSonnet subagent. The only model left in the loop -- the recovery fallback -- runs on the cheap Haiku tier.Value: med
Effort: half-day
Why
The #8 refactor routed ALL
fjwrites through an isolated Sonnet subagent. That makes writes:Reads do not have these problems because #8 scripted them:
fj_read.pyruns in the main context, deterministic, prints verbatim output immediately. Writes never got the same treatment. This task gives writes the same scripted path, and demotesfj-workerto an error-recovery-only fallback.Measured:
fj_read.pyis ~0.33s and thefjbinary ~0.13s. The deterministic calls were never the bottleneck -- any model in the loop is. So the design is: deterministic script (orfjbinary) for every read/write, and the one remaining model (recovery) on the cheap tier.Steps
fj_write.pytoplugins/forgejo-cli/skills/forgejo-cli/scripts/, mirroringfj_read.pyconventions: stdlib-only, host/repo inferred from cwdorigin, token fromkeys.json(XDG + macOS paths), exit codes0/2/3/4plus one new code for "mutation failed -> escalate to worker".fjfor ops it does well (comment, label, title, body, merge, close, create) with correctly-constructed argv; use RESTPOST(stored token) for the review-write gapfjcannot do (approve / request-changes); verify results via a REST read (reusefj_read's gather helpers).--dry-runthat prints the exactfjargv / REST call without executing (safety valve + the manual verification hook).pr-merge:git fetch && git merge <base> && git pushbeforefj pr merge(the #1 cause of Forgejo's generic 405); on a remaining 405, print the verbatim error + known causes + "escalate to fj-worker".plugins/forgejo-cli/skills/forgejo-cli/SKILL.mdinto three named paths in priority order: Reads ->fj_read.py(unchanged); Writes ->fj_write.py(new section: command surface,--body-fileconvention, note that it always prints state so the caller acts on stdout, no worker round-trip); Recovery -> dispatchfj-workerONLY on non-zero exit, handing it the verbatim error.plugins/forgejo-cli/agents/fj-worker.mdto the fallback framing; keep the full reference (it stays the canonical source + recovery knowledge). Add a one-line pointer fromfj_write.pyback tofj-worker.md.task+taskpy): the Forgejo branch of every PR/issue read/write stops spelling out inlinefj ...and defers to the forgejo-cli skill (which owns both scripts). The GitHubgh ...branch stays inline. Files:task/skills/task-new,task-orchestrate(SKILL +test-fix-loop.md),task-implement(flow.md+worktree-and-branches.md),task-review, and thetaskpymirrors of each.fj-workerrecovery agent to Haiku (was Sonnet). Routine reads/writes are deterministic sub-second script calls, so the only model in the loop is the recovery fallback -- it does not need a frontier tier. (fj-worker.mdfrontmatter + description; SKILL.md recovery note.)task/taskpyimplement Step 5/5a, orchestrate Step 3 (reordered so push precedes the tick), eachtest-fix-loop.md, plus matching Common Mistakes rows. The remote body must only ever show- [x]for work already on the branch.task-implement+taskpy-implementSKILL.md) still ticked boxes via inlinefj pr edit body; route their inlinefjPR/issue writes throughfj_write.pyand their reads throughfj_read.py(GitHubghbranches stay inline).Notes
Command surface:
Net effect on the three complaints: routine writes lose the subagent entirely -> fast; run in the main context -> live progress; always print resulting state -> no more silent success. The subagent appears only when something actually went wrong -- exactly when an isolated, careful diagnosis is wanted, and now on the cheap Haiku tier.
Why hybrid (shell-out + REST) rather than pure-REST or pure-shell:
fj_read.pyalready proved REST-direct works (it had to, sincefjcannot show reviews). For writes,fjis battle-tested for the mutations it supports, so shell out to it and let the script encapsulate the CLI footguns (positional title,--titleignored, additive label flags, draft-flag resync). REST is reserved for the one gapfjhas no subcommand for: formal reviews (approve / request-changes). Result verification uses REST reads becausefj pr statusis broken and CLI output is not machine-stable.Mid-flight spec changes (folded in): after the first 8 Steps landed, the user added R1-R3 above. R1+R2 are direct requests (cheap recovery model; the commit+push-before-tick rule). R3 is a consistency gap found while applying R2 -- the implement SKILLs were outside the original rewiring file list but still ran inline
fjwrites. See the dated PR comment for the why.Out of scope (conscious cuts):
gh_write.py-- GitHub is not the problem;ghis well-behaved.--dry-runis the verification hook.fjcapabilities beyond the listed surface.Resolver started. Worktree /Users/jeromechiche/Documents/skills/.worktrees/fj-write-script (branch feat/fj-write-script). Note: repo has no task:active label, so the active-pass marker is skipped this pass.
2026-06-26 -- resolver pass on feat/fj-write-script at
11af720.2026-06-26 -- spec amended mid-flight (R1-R3), all folded into the body.
R1 (user request). The deterministic paths are sub-second (
fj_read.py~0.33s,fjbinary ~0.13s) -- the only slow path is ever a model. Sofj-workeris pinned to Haiku (was Sonnet): it is the recovery-only fallback and does not need a frontier tier. Routine reads/writes stay deterministic script calls with no model in the loop.R2 (user request). "Commit + push before ticking a PR-body checkbox" is now a hard invariant in both task suites (implement Step 5/5a, orchestrate Step 3 reordered so push precedes the tick, each
test-fix-loop.md, plus Common Mistakes rows). The remote body must only ever show- [x]for work already on the branch.R3 (consistency gap found while applying R2). The implement SKILLs (
task-implement+taskpy-implement) were outside the original rewiring file list but still ticked boxes via inlinefj pr edit body. Their inlinefjwrites now route throughfj_write.py, reads throughfj_read.py;ghbranches stay inline.Verification.
fj_write.pycompiles; the full--dry-runcommand surface produces correct argv; the exit-code contract (0 ok / 2 usage) holds; and a livefj_write.py pr-bodyround-trip (this PR's body edit) exercised the shell-out mutation + REST verify end-to-end on this host. This comment was itself posted viafj_write.py pr-comment.WIP: feat: script Forgejo writes; fj-worker becomes recovery-onlyto feat: script Forgejo writes; fj-worker becomes recovery-only/task-review verdict:
okLOC: +976/-419
No findings.
jerome referenced this pull request2026-06-26 12:45:26 +03:00