make fj_write.py report issue creation and stop routing label writes through fj #29
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/fj-write-create-and-label"
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
fj_write.pyhonour its "never a silent success" guarantee onissue-create, and makepr-label/issue-labelwork regardless of the installedfjversion.Closes: #27
Value: high
Effort: 2h
Why
Two defects reported in #27, both hit while creating and labelling a single
issue.
Silent create.
cmd_create()'s issue branch runsfj issue createandprints its stdout only
if out:.fj issue createemits nothing, so thecommand exits
0having printed zero bytes -- the caller cannot tell successfrom failure and never learns the new issue's number. That directly contradicts
the module docstring ("never silently swallows a failure") and
SKILL.md("prints the resulting state on success, so you act on stdout directly"). The
practical cost in #27 was a whole
fj-workerdispatch just to answer "did thatland, and what number is it?", plus the standing duplicate-filing risk when a
caller assumes failure and retries.
cmd_createis also the only handler thatnever calls
show_state()-- it has no number to re-read with.Labels depend on the
fjbuild.cmd_label()buildsfj <group> edit <N> labels -a <L>. On thefjinside the dispatch containerthat argv fails outright:
so every
issue-labelcall exits5and escalates tofj-worker, which cannotfix it either -- the capability is missing from
fj, not mis-invoked. Note thatthis does not reproduce on every
fj: the build on the mac host doesexpose
fj issue edit <N> labels [-a ADD] [-r RM]andfj pr edit [PR] labels,so the same script silently works in one environment and hard-fails in another.
That version-sensitivity is the real defect, and it is why the fix is to stop
going through
fjfor labels at all rather than to special-case a versionprobe. Forgejo's REST labels endpoints are stable and already the established
pattern here --
cmd_review()bypassesfjfor exactly this reason.Steps
api_post-sibling REST helper for the calls the labels endpoints need -- a JSONGETfor the repo label list and aDELETEthat tolerates an empty 204 body -- reusingfj_read's token/base plumbing andapi_post's error scheme (5on non-2xx,4on network).cmd_label()to resolve label names to IDs against the repo's label list, thenPOST /repos/{repo}/issues/{number}/labelswith the add IDs andDELETE /repos/{repo}/issues/{number}/labels/{id}per removal. The endpoint is shared by issues and PRs, so thepr-/issue-split collapses to one code path.2and the offending name when a requested label does not exist on the repo, instead of the current silent no-op -- an unknown name is a caller typo and must not look like success.--dry-runmeaningful forcmd_label(): print the resolved endpoint(s) and label IDs without issuing a request.cmd_create()toPOST /repos/{repo}/issues(returns the created issue JSON including itsnumber), then callshow_state()on that number so create prints the same block every other handler prints.cmd_create()print resulting state too: parse the PR number out offj pr create's stdout and callshow_state(); if the number cannot be parsed, say so on stderr rather than exiting silent.tests/plugins/forgejo-cli/skills/forgejo-cli/scripts/test_fj_write.pywith unit coverage for the new pure logic -- name-to-ID resolution (hit, miss, case handling) and the PR-number parse -- keeping the stdlib-unittest-under-pytest style already in the file. No network in tests.plugins/forgejo-cli/skills/forgejo-cli/SKILL.mdwhere it describes the write surface: note that label writes and issue creation go through REST (likepr-review), and that unknown label names now fail loudly.Notes
Endpoints. Add is additive and takes IDs, not names:
Forgejo treats a PR as an issue for label purposes, so
/issues/{index}/labelsis correct for both -- which is what lets the two handlers merge.
Name-to-ID lookup.
fj_read.py --list-labelsalready pages the label list;reuse that gather rather than adding a second pager. Org-level labels can appear
alongside repo labels -- include both in the lookup, and if a name is ambiguous
across the two scopes, prefer the repo-level one and say so.
Why not just require a newer
fj. Pinning anfjversion would fix thecontainer and leave the script silently version-coupled for the next capability
gap. The script's stated design is already hybrid: shell out to
fjwhere it isbattle-tested, REST where
fjhas a gap. Labels have now demonstrably been onboth sides of that line, so they belong in the REST half.
Rejected: keep
fjand probe for the subcommand. A--helpprobe per callcosts a subprocess and still leaves two divergent code paths to keep correct.
Out of scope. The issue's third section (the
fj-workersubagent having nonetwork access, and its printing the live token to stdout when blocked) is a
separate concern in
plugins/forgejo-cli/agents/fj-worker.mdand its sandboxconfig, not in
fj_write.py. It should get its own PR -- the token-echo half isworth doing promptly.
Repo note.
public/chiche-pluginshas notask:active/task:blockedlabels, so the implement/review passes cannot set them here. One-time setup via
the web UI if that signalling is wanted; not a blocker.
Resolver started. Worktree
.worktrees/fj-write-create-and-label.Note: this repo has no
task:active/task:blockedlabels, so the pass cannot signal itself with one (one-time setup via the web UI if wanted).session: 57102610-75e1-40c1-97c4-154145a98467
2026-08-09 -- resolver pass on
fix/fj-write-create-and-labelat12057be.cmd_label()rewritten onto the labels endpoint; unknown names now exit 2;--dry-runprints the resolved endpoint + ids;issue-createvia REST POST +show_state();pr-createparses its number and re-reads; unit tests forresolve_label_ids()andparse_created_number(); SKILL.md + module docstring updated).uv run --with pytest pytest tests -qand the--dry-runsmoke command. No test or install was run in this pass.fj. The build on the mac host does exposefj issue edit <N> labelsandfj pr edit [PR] labels; the container's build does not. That version-sensitivity is folded into the PR body's Why, and it is the argument for going to REST rather than pinning anfjversion.resolve_label_ids()also covers org-level labels, with repo scope shadowing an org label of the same name.task:activelabel, so the pass could not signal itself with one.session: 57102610-75e1-40c1-97c4-154145a98467
WIP: make fj_write.py report issue creation and stop routing label writes through fjto make fj_write.py report issue creation and stop routing label writes through fj