feat: work the sync backlog — comments, labels, refs, closed issues

Five tracker issues, all in the bridge layer except the last.

pull.py fetches comments by default (#6). The thread was reachable only
through --comments, and only for a single issue, so a bulk pull left every
local copy silently incomplete: a missing <id>.comments.md could mean "no
comments" or "never asked". Now every written issue gets its thread, in key
and filter mode alike; an empty one costs no request (the count rides in the
list payload) and writes no file, and a file left over from an earlier pull
is deleted. --cached skips the thread along with the body. The --comments
flag is gone.

labels.py bootstraps the canonical label set (#7). Labels used to appear as a
side effect of the first push that happened to use them, so a repo could not
be filtered by type/bug until somebody pushed a bug. The set is finite and
already described by the domain taxonomy — 6 type/* and 5 severity/* — which
makes it a run, not a decision. Names and exclusivity come from issue.TYPES /
SEVERITIES / EXCLUSIVE_NS, colors from map.label_specs; no list is duplicated.
An exact name is never re-created or patched. Lookalikes (bug, Bug, "type:
bug", kind/bug) are reported with their id and left alone — renaming somebody
else's label is a decision, not a migration. Color or exclusive drift is
printed, and changed only under --fix.

branch: carries Gitea's ref (#8). map.to_payload sends ref only when the field
is non-empty, since ref="" would clear whatever the server has; from_api reads
it back; push fills an empty one from `git rev-parse --abbrev-ref HEAD` and
writes it into the issue file. A hand-written value is never overwritten, on
create or on --update. Detached HEAD and running outside a repo warn and send
no ref. Reading the branch is the only thing these scripts ask of git. The
domain needs no change: unknown keys already ride in Issue.extra and render
after the domain fields.

Bulk pulls no longer store closed issues (#10). Filter mode wrote every
payload the server returned, so --state all dragged the closed backlog into a
store that gets read whole — INDEX.md, grep over tmp/issues/*.md. They are
still enumerated, the number left out goes to stderr, and an issue already on
disk is refreshed either way so the local copy learns it was closed instead of
staying open forever. --state closed stores them, and key mode is exempt: an
address is not a bulk read.

/tea:issue gains a "Writing a proper description" procedure (#9). Six steps
from reading an issue to issue_check.py, the rule that a missing fact is found
in the repository or asked about rather than invented, and the note that the
procedure is identical for origin: local and origin: gitea while delivery to
the tracker belongs to /tea:sync. No new script.

Verified: labels.py run for real against claude-skills/tea (9 created, 2
already present) and idempotent on a second run; pull.py exercised live for
the closed-skip, --state closed, key-mode and comment paths; the push write
path covered offline with the transport stubbed. skills/issue/scripts/ still
imports stdlib only, with no subprocess.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
naudachu
2026-08-10 00:37:57 +05:00
parent 091dceec1d
commit 9234d8004f
7 changed files with 457 additions and 42 deletions
+2
View File
@@ -34,6 +34,7 @@ assignees: [naudachu]
milestone: v0.2
depends: [migrate-schema]
origin: gitea
branch: feat/wire-sqlc
gitea: claude-skills/tea#42
remote-updated: 2026-08-09T18:24:01Z
synced: 2026-08-09T18:40:00Z
@@ -55,6 +56,7 @@ url: https://git.noodles.cam/claude-skills/tea/issues/42
| `depends` | domain | ids this issue depends on — **the authoritative graph** |
| `origin` | domain | `local`, or the name of a tracker this also lives in |
| `gitea` | sync | the handle in that tracker: `owner/repo#N` |
| `branch` | sync | the tracker's branch link (Gitea `ref`); push fills an empty one with the current git branch, and never overwrites a filled one |
| `url`, `synced`, `remote-updated`, `comments` | sync | bookkeeping |
Domain fields render first, in the order above; sync fields follow, sorted.