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:
@@ -99,6 +99,55 @@ afterwards, and `issue_index.py` to refresh the table.
|
||||
If the issue is synced (`origin: gitea`), your edit is local until you run
|
||||
`push.py --update` from `/tea:sync`. Nothing tracks that drift automatically.
|
||||
|
||||
## Writing a proper description
|
||||
|
||||
Issues get filed on the run — "comments aren't pulled", "the guard broke".
|
||||
That is a request, not a statement of work: no reproduction steps, no
|
||||
`path/file:line`, acceptance criteria nobody can check. Rewriting one into the
|
||||
canonical format is a procedure, not improvisation.
|
||||
|
||||
1. **Read the issue whole**, and everything it points at — the ids in
|
||||
`depends:`, the `## Spec` target, the files it names.
|
||||
2. **Determine the type and its template.** The `type/*` label selects one of
|
||||
the templates in [`references/format.md`](references/format.md), and that
|
||||
template's section list is the shape you are aiming at. If the label is
|
||||
missing or wrong, decide it now and fix `labels:`; promoting a `type/draft`
|
||||
to a concrete type is this same step.
|
||||
3. **Locate the anchor points in the code.** Grep the repo for every file,
|
||||
symbol, command, and error string the issue mentions, until you can name
|
||||
lines:
|
||||
```bash
|
||||
grep -rn 'GITEA_LOGIN' hooks/ skills/
|
||||
```
|
||||
Work that does not exist yet still has anchor points — the files the change
|
||||
will land in, and the ones that will call it.
|
||||
4. **Gather the missing context.** What has to be there when you are done:
|
||||
- code references in the `path/file.ext:line` form, for every place the
|
||||
change lands;
|
||||
- reproduction steps — exact commands and their real output (`type/bug`
|
||||
splits them across `## Steps to reproduce` / `## Expected` / `## Actual`);
|
||||
- acceptance criteria that are objectively checkable: a command that exits
|
||||
0, a file that exists, a section that is present — not aspirations;
|
||||
- a real value for `## Spec` — a repo path, a URL, or the literal `none`.
|
||||
|
||||
**A missing fact is either found in the repository or becomes a question to
|
||||
the user. Inventing one is forbidden.** Ask in one batch, and keep `none` in
|
||||
`## Spec` as the legitimate answer it is — never a plausible-looking link.
|
||||
5. **Rewrite the sections** with Edit: every section of the template, in the
|
||||
template's order, English headers and Russian prose. Replace the body; do
|
||||
not append a second telling of the same issue below the old one.
|
||||
6. **Check it:**
|
||||
```bash
|
||||
python3 <skill-base-dir>/scripts/issue_check.py wire-sqlc-appclick
|
||||
```
|
||||
Errors mean malformed, warnings mean the type's template is not fully
|
||||
filled in. Re-run `issue_index.py` if the labels changed.
|
||||
|
||||
The procedure is identical for `origin: local` and `origin: gitea` — it works
|
||||
on `tmp/issues/<id>.md`, and this layer does not know the difference. Getting
|
||||
the rewritten body into the tracker is a separate decision — `push.py --update`
|
||||
in `/tea:sync` — and is no part of this.
|
||||
|
||||
## Dependency graph
|
||||
|
||||
`depends:` is the authoritative edge list; the body's `## Depends on` section
|
||||
|
||||
Reference in New Issue
Block a user