feat(buglog): status field, buglog skill, status-driven gitea-sync

The buglog had no status field, so "is this bug open?" meant reading free
text, and gitea-sync closed any issue whose `fix` text was non-empty --
including "NOT FIXED"/"PENDING"/"PARTIALLY FIXED" ones (bug-1855).

- Backfill `status` (+ status_updated) on all 1,193 entries. The 85 entries
  whose text said not-fixed/pending/out-of-scope were checked against HEAD
  and git history; the rest are `fixed`. Result: 36 open, 10 partial,
  2 investigating, 5 cannot-reproduce, 4 wontfix, 1 duplicate.
  Insert-only diff: existing lines are untouched.
- .claude/rules/buglog-status.md defines the status values (kept out of
  openwolf.md, which `openwolf update` overwrites).
- scripts/buglog.py + buglog skill: add (next free id), status, note,
  recur, list, show, triage. Edits only the touched entry's lines; a full
  json.dumps rewrite churns ~1,200 lines of mixed \u-escaping.
- gitea-sync (script + skill): state and a single bug:<status> label come
  from `status`; reopen supported; issues shared by several entries follow
  the most-open one; only status labels are swapped; per-entry write-back
  with gitea_synced_status so unchanged entries cost no API calls; list-typed
  `fix` no longer crashes; spec issues close on done phases (spec.json has
  no implementation approval); --since no longer floods Gitea after the
  backfill.
- Disable OpenWolf buglog auto-detection: its hook numbers new entries
  len(bugs)+1, which collides with existing ids (bug-1856, upstream).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-09-24 09:40:43 +00:00
parent 8330997c5a
commit 19104f1940
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
7 changed files with 3333 additions and 171 deletions

View file

@ -0,0 +1,64 @@
---
name: buglog
description: Log a new bug, change a bug's status (fixed / partial / cannot-reproduce / ...), record a recurrence, or list open bugs in the OpenWolf buglog (.wolf/buglog.json). Use whenever you log or fix a bug, or are asked for the open-bug list.
argument-hint: add | status <id> <status> | note <id> | recur <id> | list [--open] | show <id> | triage
triggers:
- /buglog
- log a bug
- open bug list
- mark bug fixed
---
# Buglog Skill
Every operation goes through `scripts/buglog.py` (run from the repo root). It
edits only the lines of the entry it touches, re-parses the file before
saving, and writes atomically. **Never** rewrite `.wolf/buglog.json` with
`json.dumps`/`jq`/the Write tool: entries mix raw UTF-8 and `\uXXXX` escapes,
so a re-serialize rewrites ~1,200 unrelated lines. Never read the file whole
either (~27K lines); use `list`/`show`.
Status values and when to use each: `.claude/rules/buglog-status.md`.
## Before fixing a bug
Search for prior art first (project rule): `openwolf bug search "<error text>"`,
or `python3 scripts/buglog.py list --grep '<regex>'`.
## Commands
| Task | Command |
|------|---------|
| Log a new bug | `python3 scripts/buglog.py add --error "<symptom, exact error text>" --root-cause "<cause>" --file <path>[,<path>] --tags scheduler,asan --status open --note "<one line>"` → prints the new id |
| Log a bug you already fixed | same, with `--fix "<what changed>, commit <sha>" --status fixed` |
| Mark fixed | `python3 scripts/buglog.py status <id> fixed --note "<sha>"` |
| Mark partly fixed | `python3 scripts/buglog.py status <id> partial --note "<what remains>"` |
| Couldn't reproduce | `python3 scripts/buglog.py status <id> cannot-reproduce --repro "0/40 runs, 2026-09-24" --note "<what was tried>"` |
| Real failure, cause unknown | `python3 scripts/buglog.py status <id> investigating --note "<next step>"` |
| Not a bug / accepted | `python3 scripts/buglog.py status <id> wontfix --note "<why / who decided>"` |
| Duplicate | `python3 scripts/buglog.py status <id> duplicate --duplicate-of <other-id> --note "<same defect as ...>"` |
| Add findings to an entry | `python3 scripts/buglog.py note <id> "<text>"` (appends an `UPDATE (date):` paragraph to `fix`) |
| Seen again | `python3 scripts/buglog.py recur <id> [--note "<where>"]` (bumps occurrences/last_seen; reopens a `fixed`/`wontfix`/`cannot-reproduce` bug) |
| Open-bug list | `python3 scripts/buglog.py list --open` (add `--tag scheduler`, `--grep`, `--since YYYY-MM-DD`) |
| One entry | `python3 scripts/buglog.py show <id>` |
| Entries missing a status | `python3 scripts/buglog.py triage` |
| Any other single-line field | `python3 scripts/buglog.py set <id> key=<json> ...` |
`status` requires `--note` for every status except `fixed`, and always
rewrites the note, so a stale note never outlives its status.
## Rules
- **Ids**: let `add` pick the id (one past the highest existing). Don't number
entries by hand or by entry count. OpenWolf's post-write hook numbers by
entry count, which collides with existing ids; its auto-detection is turned
off in `.wolf/config.json` for that reason (`openwolf.buglog.auto_detect`).
- **Write the symptom first**: `--error` holds the observed error text or
behavior, not a status banner ("OPEN, NOT FIXED --" goes in `status`).
- **Fixing a bug in code**: after the fix commit, run `status <id> fixed
--note <sha>`. If only part is fixed, use `partial` with what remains.
- **Arrays** (`tags`, `related_bugs`, `files`) can only be set at `add` time;
to change them later, edit that entry's lines by hand and re-check with
`python3 -c "import json; json.load(open('.wolf/buglog.json'))"`.
- **Gitea**: status changes reach Gitea on the next `/gitea-sync`, which opens,
closes or reopens issues from `status`.