docs(agents): tea CLI and per-branch worktree workflow #139
No reviewers
Labels
No labels
blocked
bug
enhancement
high-priority
low-priority
needs-info
needs-triage
ready-for-agent
ready-for-human
referenced
research
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
gabogg/hikcentral!139
Loading…
Reference in a new issue
No description provided.
Delete branch "docs/agent-workspaces"
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?
Problem
curlcalls with the token read from disk: verbose, error-prone, and every session re-derived the endpoints.Approach
tea(Gitea CLI; Forgejo speaks the same API).docs/agents/forgejo-cli.mdcovers install with checksum, login plus git credential helper, the everyday command table, merging viatea api(keepsdelete_branch_after_merge), and the credential-free remote URL gotcha.issue-tracker.mdnow points atteainstead of listing REST endpoints.scripts/wtmanages.worktrees/<type>-<slug>/:new <type>/<slug> [base]branches from freshorigin/master(or a stacked base) and enforces AGENTS.md branch naming.pr <N>opens a PR's branch, reusing a clean existing worktree and refusing a dirty one (owned by another session).ls/rm/prunelist, remove (refusing dirty or unpushed work), and clean up merged branches whose remote branch is gone..venv,.envandscripts/tailwindcssfrom the main checkout, and deliberately notdata/, so sessions never share a SQLite file.docs/agents/workspaces.mdcovers the main checkout staying on a cleanmaster, one owner per worktree, commit-and-push as the handover point, WIP commits instead of the shared stash, and cleanup. AGENTS.md §4 inlines the core rules and points to both docs.Verification
wtcommand:new(including stacked base and bad-name rejection),pr(dirty refusal, clean reuse),ls,rm(dirty refusal, merged-branch deletion), andprune.scripts/check_docs.pyis clean.tea whoami,tea pr list,tea issues listandtea apiwork against the instance, and agit push --dry-runauthenticated through the tea credential helper.🤖 Generated with Claude Code
Code review: PR #139 (
docs/agent-workspacesvsmaster@8f7a80e)Two independent axes. Standards: AGENTS.md,
docs/standards/code-standards.md,docs/standards/git-and-workflow.md,.claude/skills/writing-for-agents, plus the Fowler smell baseline. Spec: the maintainer's request (Forgejo CLI with usage instructions; a clean, separated, standardized workspace per PR; agents able to work on each other's branches cleanly; an open, non-draft PR).Standards
P1 (hard, data loss)
scripts/wtremove()/is_dirty(). The dirty check usesgit status --porcelain, which skips ignored files, andgit worktree removethen deletes them. Reproduced: a plainwt rm(no-f) deleteddata/app.db.workspaces.mdtells each worktree to create its own DB under the ignoreddata/, sormandprunewipe exactly what the doc tells agents to create. Fix: treat ignored files outside the known symlinks as dirty (or refuse whendata//logs/has content).P2
docs/standards/git-and-workflow.mdwas not updated. It still allowschore/<topic>, which AGENTS.md andwt(TYPES="feat fix refactor docs") reject. Its §2.1 Draft PRs and §2.3 Branch Cleanup repeat the new AGENTS.md lines andforgejo-cli.md, which breaks single source of truth (writing-for-agents §Pruning).cmd_prhandover. It checks only for uncommitted changes. A clean worktree with unpushed commits passesmerge --ff-onlyand is handed over, contradicting "Committed-and-pushed work is the handover point". Fix: also requireunpushed == 0.workspaces.mdrepeats the branch-name rule ("follows AGENTS.md (feat|fix|refactor|docs/<slug>); the script refuses anything else"). This caches AGENTS.md and the script's own error message. Replace it with a pointer.P3
curl".forgejo-cli.md: "no reason to hand-buildcurl…", "never inline in the shell".workspaces.md: "never force-push". Keep this one as a guardrail, but pair it with the positive target.workspaces.md(ownership, "end every session committed and pushed"). That material is always loaded but already sits behind a pointer.cmd_newfetches before validating its arguments. A bad invocation still hits the network.remove()treats any non-empty second argument as--force, while its safety checks key only on-f. Reject unknown flags.bootstrap()writes/.worktreestoinfo/exclude, repeating the new.gitignoreline (possible Duplicated Code).local upstreamsits inside thepruneloop. Declare it withlocal p branch.Held up: quoting; no
local x=$(...)masking;pruneneeds a gone upstream and ancestry of master, so unpushed and squash-merged branches are kept;rmis limited to.worktrees/; odd branch names are safe.Spec
The documented
teacommands exist in tea 0.16.0, except the one in C4. The merge body fields (Do,MergeTitleField,delete_branch_after_merge) matchMergePullRequestOption. The PR is open, not WIP.(a) Missing or partial
git-and-workflow.mddisagrees with the new policy (chore/*, no mention of worktrees ortea). Same root as S2.(b) Scope creep
info/excludeand theissue-tracker.mdrewrite all serve the request.(c) Implemented but wrong ("provide instrucctions of use")
tea pr list --labels X: that flag doesn't exist forpulls list; onlyissues listhas it.tea issues 42·tea pr 42". Comments appear only with--comments, and agents run non-interactively, so they miss review comments.tea pr edit 42 --readystripsWIP:itself, which is less error-prone than retyping the title.<PR title> (#N)matches stated policy, but recent history uses Forgejo's default title. The maintainer should confirm which is intended.CLAUDE.md. Codex reads AGENTS.md natively; Claude relies on the harness. A one-lineCLAUDE.mdwith@AGENTS.mdmakes discovery reliable.workspaces.md"rm -f" reads aswt rm -f <x>, but onlywt rm <x> -fworks. The wrong order fails safely.Summary. Standards: 10 findings, worst S1 (
wt rm/prunesilently delete ignored files such as the worktree's owndata/DB). Spec: 8 findings, worst C2 (the same-branch handover isn't isolated when the owner has unpushed commits).🤖 Generated with Claude Code
Review fixes:
5a4752fAll findings from the review are addressed in this first pass except C8 (reason below).
Standards
wt rm/prunesilently delete ignored fileslocal_files()lists ignored files other than the shared symlinks and tool caches.rmrefuses and lists them (-foverrides);prunekeeps the worktree and says why. Verified: adata/app.dbblocksrm;__pycache__does not.git-and-workflow.mdstale,chore/conflict, duplicationchoreadded towttypes. AGENTS.md §4 points to it instead of restating.wt prhands over worktrees with unpushed commitsworkspaces.mdrestates branch-name rulegit-and-workflow.md.forgejo-cli.mdandworkspaces.mdrephrased positively. The force-push guardrail is kept, paired with "preserve both sides' commits".workspaces.mdwt newfetches before validatingrmtreats any 2nd arg as force-fin any position; unknown flags and extra targets are rejected./.worktreesduplicated ininfo/exclude.gitignorecovers it.local upstreaminside loopSpec
wtenforces it. A lease file was left out: the git state already is the lease.pr list --labelsdoesn't exist--labelsis issues-only.--commentstea issues 42 --comments·tea pr 42 --comments.pr edit --ready<PR title> (#N). Recorded ingit-and-workflow.md§2.3;forgejo-cli.mdlinks to it.CLAUDE.md→@AGENTS.mdAGENTS.mdas project instructions (observed in-session), so the import would load it twice and double its always-on context cost.rm -fargument orderwt rm -f <name>.Verification:
wtexercised in throwaway branches (bad names, missing arg,data/guard,-feither position, unknown flag, unpushed refusal onrm); pre-commit (ruff, pytest) green;scripts/check_docs.pyclean.🤖 Generated with Claude Code
Review items not applied (or only partly applied), with reasoning
C8: add a repo
CLAUDE.mdcontaining@AGENTS.md: not appliedAGENTS.md"through the harness", so aCLAUDE.mdimport would make discovery reliable.AGENTS.mdnatively. When a session starts in this repo, its system context includesContents of .../hikcentral/AGENTS.md (project instructions, checked into the codebase), with noCLAUDE.mdpresent. Codex readsAGENTS.mdnatively too.CLAUDE.mdwith@AGENTS.mdwould put the same ~6 KB into every Claude turn twice. That doubles its context load and gives two entry points for one source of truth, against writing-for-agents' pruning rules, for no gain in discovery.CLAUDE.md, add the one-line file then. Worth one check whenever the harness changes.C2: lease / lock marker for same-branch handover: partly applied
wt prenforces it in both places it can occur: an existing worktree, and a leftover local branch with no worktree.wt prrefuses.Verification gap from the fix round, now closed
The previous reply noted
wt pr's unpushed-commit refusal had not been exercised, since it needs a PR whose worktree has unpushed commits. It has now been tested with a stand-inteaonPATHthat returns a throwaway branch (docs/pr-probe, pushed and then deleted from the remote):has unpushed commits on 'docs/pr-probe'; its owner must push firstlocal 'docs/pr-probe' has unpushed commits from an earlier session; push or drop them first🤖 Generated with Claude Code
Code review, second pass: PR #139 (
docs/agent-workspaces@5a4752fvsmaster@8f7a80e)Both axes confirm that every first-pass fix (S1–S10, C1–C9) is correct and introduced no regressions. The declined items (C8
CLAUDE.md, C2 lease file) were not re-raised. No P1/P2 findings. Per the review policy, the P3s below go into a follow-up issue and the PR merges.Standards
The fixes were exercised on a local throwaway branch:
local_files()blocksrmondata/app.dband passes caches and symlinks.rmrejects-x,--and a second target.newvalidates its arguments before fetching.wt prunpushed and diverged logic is sound.scripts/wtlocal_files()hard-codes^(\.venv|\.env|scripts/tailwindcss)$, repeatingSHARED=(...). A new shared symlink added without updating the regex makes everyrm/prunerefuse. Build the pattern fromSHARED(possible Duplicated Code / Shotgun Surgery).cmd_prreimplementsunpushed()inline (rev-list --count "$branch" --not --remotes=…) without its fallback. Giveunpusheda ref argument and reuse it (possible Duplicated Code).cmd_rmaccepts.and..:"$WT_DIR/.."passes both-dand the"$WT_DIR"/*prefix check, sowt rm .. -freachesgit worktree remove --force <main checkout>, which only git's own refusal stops. Canonicalise withrealpathbefore the prefix check.cmd_pr, when a local branch has no worktree: the worktree is added before the--ff-onlycheck, so a divergence leaves an orphan worktree behind. Check first, or remove it on failure.remove()prints "removed … and merged branch X", which should read "deleted merged branch X".forgejo-cli.md: "Installed at~/.local/bin/teaon the dev workstation" caches the environment (writing-for-agents §Pruning). Drop it.git-and-workflow.md: "Merge commit (no squash or rebase)" names the banned options (writing-for-agents §Negation). Use "Merge commit only".Spec
Checks against the spec:
teacommand and flag inforgejo-cli.mdexists in tea 0.16.0.default_delete_branch_after_merge=false, so the explicitdelete_branch_after_merge:truein the merge call is required.wt new, thenpush -u,tea pr create, review, API merge andwt prune. The gone-upstream path ofprunewas reproduced.forgejo-cli.md§Merging says "Then clean up the local side withscripts/wt prune" in a doc whose PR commands run from the branch's worktree.git worktree removeof the current directory succeeds, so an agent pruning from its own worktree deletes its own cwd. Say "from the main checkout" (or makewtrefuse to remove$PWD).-uhas no upstream config, soprunesilentlycontinues and the worktree stays forever. Print a "kept … no upstream" line, as the ignored-files case already does.Summary. Standards: 7 findings, all P3; the most useful is S2-3 (
rm ..path canonicalisation). Spec: 2 findings, both P3; the most useful is C2-1 (pruning from inside the worktree being removed).🤖 Generated with Claude Code