feat(issue): add comprehension axis and host-free reference grammar

Rule 4 now tests two axes — reachability (the pointer resolves) and
comprehension (a linearly-reading human understands the entry without
absorbing the target). A reachable-but-opaque reference no longer passes.

References go host-free: absolute URLs with a server name or port are
banned; the canonical clickable forms are Gitea relative links —
src/commit/<sha>/path#user-content-<slug> for a file section/line and
issues/N#issuecomment-M for a comment. Verified against live Gitea 1.26.2.

Bind every author, not just an explicit invocation, and pull milestones
into scope (rules 1/2/4 on the description; the title names the
container). Propagate the comment-link form and the rule-4 rename to boss.
This commit is contained in:
2026-07-01 13:51:40 +02:00
parent c39a817982
commit 1a1077b7f7
2 changed files with 112 additions and 45 deletions
+104 -38
View File
@@ -1,6 +1,6 @@
---
name: issue
description: Use when creating, editing, commenting on, or closing a Gitea issue — filing a bug, feature, or idea into the tracker, or turning a finding into a tracker item. Covers the issue-writing conventions and the tea-CLI mechanics for the operation.
description: Use when creating, editing, commenting on, or closing a Gitea issue or milestone — filing a bug, feature, or idea into the tracker, or turning a finding into a tracker item. Binds every actor that creates or edits an issue or milestone, human or skill, not just an explicit invocation. Covers the issue-writing conventions and the tea-CLI mechanics for the operation.
---
# issue — write and edit Gitea issues
@@ -11,9 +11,10 @@ description: Use when creating, editing, commenting on, or closing a Gitea issue
An issue is a durable artefact in the forward-queue, read later by
humans and by other agents — usually without the conversation that
produced it. It must stand on its own. This skill unifies how issue
text is written so every issue reads the same way, and records the
`tea` mechanics for creating and editing them.
produced it, and a human reads it linearly, holding only what is on the
page. It must stand on its own. This skill unifies how issue text is
written so every issue reads the same way, and records the `tea`
mechanics for creating and editing them.
An issue states facts and clearly-flagged claims about a concern. It
is not a chat message.
@@ -25,15 +26,20 @@ is not a chat message.
| "Open an issue for X" / "file a bug about Y" | Yes |
| "Edit / re-label / comment on issue #N" | Yes |
| "Close #N" (outside a `closes #N` commit) | Yes |
| Create or edit a **milestone** (the container for a big chunk) | Yes |
| Turning a finding or deferred work into a tracker item | Yes |
| A note relevant only to the current conversation | No |
Other skills file issues through these same conventions — e.g.
**This binds every author, not just an explicit invocation.** Any actor
that creates or edits an issue or a milestone — a person at the keyboard or
any skill or agent doing it as a side-effect — writes it to these
conventions; there is no second, looser standard for tracker text written
in passing. Other skills reach the tracker through them constantly:
`brainstorm` parks a deferred spec as a backlog issue, `audit` files
regressions, and `boss` files skill-system deficiencies against the
plugin's own tracker (with an autonomous-provenance body block — see
`../boss/SKILL.md` § Skill-system feedback). This skill is the shared
convention they lean on.
regressions, and `boss` files skill-system deficiencies (with an
autonomous-provenance body block — see `../boss/SKILL.md` § Skill-system
feedback) and logs each fork decision on the run's reference issue (§ The
reference issue). This skill is the shared convention they lean on.
## The four writing rules
@@ -96,22 +102,56 @@ These apply to every issue, on create and on edit.
- Avoid: "parser bug" / "There is a problem with the parser."
- Use: "Preserve backslash escapes in quoted strings"
4. **Self-contained — the reachability test.** The issue stands on its
own, without the conversation that produced it. Apply one test to
every reference: *starting from this entry alone — a reader may have
arrived by a direct comment-anchor link, no chat and no sibling
comments loaded — can they follow it to its target?* If not, it is a
dangling pointer: reproduce the content inline, or swap in a
resolvable locator.
4. **Self-contained — reachable AND comprehensible.** The issue stands on
its own, without the conversation that produced it. Two tests apply to
every reference, and a pointer can pass one while failing the other.
**Reachable — use these:** content stated or quoted inline; an
issue/PR number (`#42`, or cross-repo `Skills #11`); a commit SHA
(`a1b2c3d`, resolves on push); a full URL; a repo-relative path,
optionally with a line range (`parser.rs:84-90`); another comment by
its full URL (the link ending `#issuecomment-NNNN` — the bare fragment
only resolves in-page on that comment's own issue); a same-entry
section marker (`§5`); a convention-resolvable in-repo slug (a
`docs/specs/` slug, a cycle number).
- **(1) Reachability.** *Starting from this entry alone — a reader may
have arrived by a direct comment-anchor link, no chat and no sibling
comments loaded — can they follow it to its target?* If not, it is a
dangling pointer: reproduce the content inline, or swap in a
resolvable locator.
- **(2) Comprehension.** Does the issue make its point *without* the
reader absorbing what the target says? A human reads linearly and
holds no context-wide view; unlike an LLM — which usually has the
referenced sources already in its context — a human has only what is
on the page. So where the issue's substance leans on what a reference
*contains*, state that substance inline beside the pointer: the
reference then corroborates or lets the reader go deeper, it does not
carry the meaning the issue omits. A pointer can pass (1) and fail
(2): `#42` resolves, yet a linear reader still does not know what
`#42` says. A *pure further-reading* pointer — one the issue's point
does not lean on — needs only to resolve.
**Reachable — necessary, not sufficient.** Resolving satisfies test (1)
only; of the forms below, only *content stated or quoted inline* also
passes test (2) unaided.
**No absolute URL carrying a server name or port** — it rots when the
host moves and leaks the deployment; every reference below has a
host-free form:
- content stated or quoted inline;
- an issue/PR number (`#42`; a cross-repo ref needs the `owner/repo#N`
form with no space — `Brummel/Skills#11`, not `Skills #11`, which
binds `#11` to the current repo) or a commit SHA (`a1b2c3d`, resolves
on push) — Gitea auto-links these against the current host;
- a **relative Markdown link** — Gitea resolves it against the repo
root, so it carries no host in the source or the rendered link: the
way to make a file section, a line range, or a specific comment
clickable. The base is the repo root, so a file link needs the `src/`
segment; without it (`commit/<sha>/…`, `branch/…`) it does not
resolve:
- a file at a pinned commit —
`[what it says](src/commit/<sha>/docs/design.md#user-content-<heading-slug>)`,
or a line range `…/parser.rs#L84-L90`; prefer `src/commit/<sha>/…`
(immutable) over `src/branch/<branch>/…` (drifts);
- a specific comment — `[what it says](issues/N#issuecomment-M)`
(a plain `issues/…` path, no `src/`; the bare `#issuecomment-M`
fragment alone only resolves in-page on that comment's own issue);
- a repo-relative path, optionally with a line range (`parser.rs:84-90`),
as a plain locator where a clickable link is not worth building;
- a same-entry section marker (`§5`); a convention-resolvable in-repo
slug (a `docs/specs/` slug, a cycle number).
**Unreachable — rewrite or inline; these never resolve from the
tracker:**
@@ -122,12 +162,13 @@ These apply to every issue, on create and on edit.
decision's words inline is fine — see the carve-out below.)
- **A weak prose pointer** — "above", "below", "earlier", "the prior
comment", "logged above", "as discussed" — with no resolvable
locator: the target comment's full URL, a `#N`, or a *same-entry*
`§` marker. A `§` pointing into another comment or file is itself
unreachable — link that comment's URL.
locator: a relative link to the target comment
(`issues/N#issuecomment-M`), a `#N`, or a *same-entry* `§` marker. A
`§` pointing into another comment or file is itself unreachable —
link that comment with `issues/N#issuecomment-M`.
- **An artifact named by role, not locator** — "the OOS harness
issue", "see the ledger", a `Depends on` target — carrying no `#N` /
SHA / URL / repo path.
SHA / relative link / repo path.
- **A path outside this repo** — `/mnt/...`, `~/.claude/...`, a Claude
auto-memory slug, another project's `BLOCKED.md`. Only a path in
*this* repo is reachable.
@@ -156,7 +197,7 @@ These apply to every issue, on create and on edit.
- Avoid: "Approach B (the mechanism refinement decided + logged
above)." — "logged above" points to a sibling comment with no
resolvable URL, so a reader who landed on this comment can't reach
resolvable locator, so a reader who landed on this comment can't reach
the refinement; the violation is the unresolvable pointer, and it
stands even when the mechanism is restated inline. The bare label
"Approach B" compounds it — it indexes an A/B fork this entry never
@@ -165,12 +206,19 @@ These apply to every issue, on create and on edit.
- Use — drop the chat-native label, name the option on its own terms,
then inline or link the refinement: "The honest per-held-cycle bleed
(accrue each held cycle, dump at close) — refined in
`http://…/issues/148#issuecomment-2142`."
[issue 148's comment](issues/148#issuecomment-2142)."
**Language:** title and body are English — an issue is a repo
artefact mirrored to Gitea ("would it be committed → English", per
`~/dev/CLAUDE.md`).
**Milestones** obey the same conventions, with one adjustment. A milestone
is a container — a big chunk, usually carrying a spec — so its title
*names* the container ("Cycle 7 — streaming rewrite") rather than taking
rule 3's imperative verb form. Its description obeys rules 1 (impersonal
and tool-neutral), 2 (validated or flagged) and 4 (reachable and
comprehensible) exactly as an issue body does.
## tea mechanics
`tea` is pre-authenticated via its own config (`~/.config/tea/`).
@@ -188,6 +236,9 @@ CLAUDE.md project facts name the repo slug and the list/show commands).
| Re-label | `tea issues edit <idx> --add-labels a --remove-labels b` |
| Comment | `tea comment <idx> "<body>"` |
| Close / reopen | `tea issues close <idx>` / `tea issues reopen <idx>` |
| List milestones | `tea milestones ls --state open` |
| Create milestone | `tea milestones create -t "<title>" -d "<description>"` |
| Close / reopen milestone | `tea milestones close "<name>"` / `tea milestones reopen "<name>"` |
Notes:
- The body flag is `--description`/`-d`, **not** `--body`. For a
@@ -206,6 +257,11 @@ Notes:
- Routine closing is done by a `closes #N` line in the commit body on
push (per `~/dev/CLAUDE.md`); `tea issues close` is for closing
without an accompanying commit.
- `tea milestones` has no `edit` subcommand (create / list / close /
reopen / delete only). To change a milestone's title or description,
use the Gitea API (`PATCH /api/v1/repos/<owner>/<repo>/milestones/<id>`,
the numeric `<id>` from `tea milestones ls --fields title,id`); the
reachable-and-comprehensible conventions apply to the new description.
## Common mistakes
@@ -218,10 +274,12 @@ Notes:
| `-d` body with a guessed flag (`--body`) | tea rejects it | `--description`/`-d` |
| Editing without viewing first | Clobbers others' edits | `tea issues <idx>`, then edit |
| Reading/echoing tea's config | Token leak | tea is already authenticated |
| Pointer to the chat that produced the entry ("in-context", "the cycle transcript", a juror code) | The chat is invisible to a tracker reader — never reachable | Quote the words inline, or log the decision to the tracker and cite that comment's full URL |
| "above" / "below" / "the prior comment" with no anchor | A reader on a direct comment link can't tell which entry | Add the comment's full URL, a `#N`, or a *same-entry* `§` marker |
| Artifact or path named without a locator — "the harness issue", "see the ledger", `/mnt/...`, `~/.claude/...` | Nothing to navigate to from the tracker | Supply a `#N`, SHA, URL, or in-repo path — or reproduce the fact inline |
| Pointer to the chat that produced the entry ("in-context", "the cycle transcript", a juror code) | The chat is invisible to a tracker reader — never reachable | Quote the words inline, or log the decision to the tracker and cite that comment with a relative link (`issues/N#issuecomment-M`) |
| "above" / "below" / "the prior comment" with no anchor | A reader on a direct comment link can't tell which entry | Add a relative link to the comment (`issues/N#issuecomment-M`), a `#N`, or a *same-entry* `§` marker |
| Artifact or path named without a locator — "the harness issue", "see the ledger", `/mnt/...`, `~/.claude/...` | Nothing to navigate to from the tracker | Supply a `#N`, SHA, in-repo path, or relative link — or reproduce the fact inline |
| Bare un-glossed code or ordinal (`I7`, `C16`, "decision #5") | Its defining set isn't in the entry | Gloss it inline, or restate the set |
| A reachable ref the point leans on, left un-glossed (`Blocked on #42`; "see `parser.rs:84` for the mechanism") | Resolves (test 1) but a linear reader learns nothing (test 2 fails) | Gloss the substance inline; attach the link as the go-deeper pointer |
| An absolute URL with a server name or port (`http://host:port/…`) | Rots when the host moves; leaks the deployment | Host-free form — `#N`, a relative `issues/N#issuecomment-M`, or `src/commit/<sha>/path#…` |
## Red flags — STOP
@@ -233,18 +291,26 @@ Notes:
*work*, or a readiness status, is not what this flags)
- A factual statement with neither evidence nor a "claim" flag
- A title that is a noun phrase or ends with a period
- A reference with no resolvable locator (`#N`, a commit SHA, a URL, an
existing repo path, a `docs/specs/` slug or cycle number, a same-entry
`§`, or the fact reproduced inline), or a bare never-glossed code whose
defining set isn't in the entry — in particular:
- A reference with no resolvable locator (`#N`, a commit SHA, a host-free
relative link like `issues/N#issuecomment-M` or `src/commit/<sha>/path`,
an existing repo path, a `docs/specs/` slug or cycle number, a
same-entry `§`, or the fact reproduced inline), or a bare never-glossed
code whose defining set isn't in the entry — in particular:
- a pointer to the chat that produced the entry ("in-context", "the
cycle transcript", a juror code); the chat is never tracker-reachable
- "above" / "below" / "the prior comment" / "as discussed" with no
resolvable locator — the comment's full URL, a `#N`, or a same-entry `§`
resolvable locator — a relative link to the comment
(`issues/N#issuecomment-M`), a `#N`, or a same-entry `§`
- an out-of-repo path (`/mnt`, `~/.claude`, a `memory <slug>`,
another project's `.md`) cited as the basis
- a `#`-token that resolves to nothing — a `#(A)` placeholder or a
wrong-format `#0071`
- An absolute URL carrying a server name or port anywhere in title or body
— use the host-free relative form (`issues/N#issuecomment-M`,
`src/commit/<sha>/path#…`)
- A reachable reference the entry's point leans on, its substance not
glossed inline — a linear reader can't grasp the entry without navigating
away (the comprehension test, rule 4)
- About to `tea issues edit` without having viewed the issue
- About to print or `cat` the tea config