From 1a1077b7f77e87a95f4879a54b477b9926fdad53 Mon Sep 17 00:00:00 2001 From: Brummel Date: Wed, 1 Jul 2026 13:51:40 +0200 Subject: [PATCH] feat(issue): add comprehension axis and host-free reference grammar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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//path#user-content- 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. --- boss/SKILL.md | 15 +++--- issue/SKILL.md | 142 ++++++++++++++++++++++++++++++++++++------------- 2 files changed, 112 insertions(+), 45 deletions(-) diff --git a/boss/SKILL.md b/boss/SKILL.md index 1744697..a24da21 100644 --- a/boss/SKILL.md +++ b/boss/SKILL.md @@ -304,8 +304,8 @@ things at once: remains of the dead-ended line. These decision-log comments are issue entries — they obey -`../issue/SKILL.md`: rule 1 (impersonal and tool-neutral) and rule 4 (the -reachability test). A later run or user lands on the **comment**, not the +`../issue/SKILL.md`: rule 1 (impersonal and tool-neutral) and rule 4 +(reachable and comprehensible). A later run or user lands on the **comment**, not the chat that produced it, so each must be self-contained, reachable from the tracker, and free of this loop's own control-flow. @@ -316,9 +316,10 @@ tracker, and free of this loop's own control-flow. - **Reproduce each fork's options and chosen rationale inline.** A bare label ("Approach B") indexes a set the reader cannot see — spell the options out, or name the choice on its own terms. -- **Tie a later comment to an earlier one by that comment's full URL, or - restate the point** — never "above" / "logged above" / "the prior - comment", which a reader on a direct comment-anchor link cannot resolve. +- **Tie a later comment to an earlier one by a host-free relative link to + that comment (`issues/N#issuecomment-M`), or restate the point** — + never "above" / "logged above" / "the prior comment", which a reader on a + direct comment-anchor link cannot resolve. - **Record the decision, not the dispatch.** The comment logs what was decided and why, and may carry a durable, tool-neutral readiness status ("design settled — ready for spec production"). It never writes this @@ -693,7 +694,7 @@ the user wants it — there is no autonomous filing to govern. | "This spec is clearly good — I'll sign it on my judgement" | Your judgement is not the signature; the Step-5 `grounding-check` `PASS` is. Run grounding and sign on the PASS. A no-override grounding `BLOCK` routes to the human — never signed over on confidence. (The old unanimous `spec-skeptic` panel is retired; do not re-introduce it as a gate.) | | "There's an open fork — I'll bounce to `brainstorm` to be safe" | If you can *derive* an answer (sources, code, consistency, risk), decide it boldly and record it in the reference issue. Only a *pure-preference* fork bounces. Bouncing a derivable fork is exactly the timidity the bold stance retired; unsure if your leaning is bias, pull one ad-hoc `spec-skeptic` lens, then decide. | | "I derived a fork but didn't bother logging it on the issue" | The reference-issue comment is mandatory. It is the user's only window into a call made without them — and, because a rollback hard-drops the commits, the only surviving trace of the attempt. No reference issue, no fork decision: create the seeding issue first. | -| "The user was in the discussion, so 'as decided in-context / above' is enough on the issue" | The decision log is read later by a run or user who was NOT in that discussion and may land on the comment alone. Quote the user's words + date inline, reproduce the fork's options and rationale, and link any sibling comment by its full URL — never "above" / "in-context". The chat is not reachable from the tracker (`../issue/SKILL.md` rule 4). | +| "The user was in the discussion, so 'as decided in-context / above' is enough on the issue" | The decision log is read later by a run or user who was NOT in that discussion and may land on the comment alone. Quote the user's words + date inline, reproduce the fork's options and rationale, and link any sibling comment by a host-free relative link (`issues/N#issuecomment-M`) — never "above" / "in-context". The chat is not reachable from the tracker (`../issue/SKILL.md` rule 4). | | "I resolved the fork — I'll note that `specify` runs next so the thread is actionable" | The decision log records the decision and a tool-neutral readiness status, never this loop's dispatch. Directing who runs what next is session control-flow whether the skill is named ("dispatch `implement` next") or periphrased ("hand it to the spec writer") — meaningless to a later reader who isn't the loop, and declarative phrasing doesn't save it; "what's next" lives in the issue's open/labelled state (`../issue/SKILL.md` rule 1). | | "Auto-sign let me continue, so I don't need to notify — it's just progress" | The auto-sign notify is mandatory and carries a decision (the user's veto over a signature made without them). It is the one sanctioned mid-flow notify precisely because it is not progress — it is the audit trail for a delegated gate. | | "I hit a gap in the skill system itself — I should notify the user about it" | No. File it on the plugin's own tracker with an autonomous-provenance body block and continue (§ Skill-system feedback). It needs no mid-run decision, so notifying would be the attention-burning progress ping the notify discipline forbids; the filed issue is the durable record. | @@ -716,7 +717,7 @@ the user wants it — there is no autonomous filing to govern. - About to re-introduce the retired `spec-skeptic` panel as a sign gate (a unanimous five-juror pass as a precondition) — it is now only the optional Step-1.5 bias-breaker; or to run any auto-sign path outside a `/boss` session, or to skip the mandatory auto-sign notify after signing. - About to *decide* a fork that hangs on pure user preference (no derivable better) instead of bouncing it — or its inverse, bouncing a *derivable* fork to `brainstorm` out of timidity. - About to make a fork decision without recording it on the run's reference issue, or to run autonomously with no reference issue at all. -- About to record a fork decision that points at the chat ("settled in the discussion", "decided in-context") or at an earlier comment by "above" / "logged above" — the decision log is tracker-reachable per `../issue/SKILL.md` rule 4: quote the provenance and reproduce the options inline, and link a sibling comment by its full URL. +- About to record a fork decision that points at the chat ("settled in the discussion", "decided in-context") or at an earlier comment by "above" / "logged above" — the decision log is tracker-reachable per `../issue/SKILL.md` rule 4: quote the provenance and reproduce the options inline, and link a sibling comment by a host-free relative link (`issues/N#issuecomment-M`). - About to write this loop's next dispatch into a decision-log comment — whether a skill is named ("enter `specify` from here", "next: dispatch `implement`") or periphrased ("hand it to the spec writer next") — instead of a tool-neutral readiness status; which actor runs next is this loop's Step 3/4, not an issue fact (`../issue/SKILL.md` rule 1). - About to `git reset` below the session anchor, or to `reset` a commit that has been pushed — the rollback sandbox is the orchestrator's *own unpushed* commits above the anchor only; user-ratified and pushed history is forward-only (`git revert`). - About to notify the user about a skill-system deficiency instead of filing it on the plugin's own tracker and continuing (§ Skill-system feedback) — it is captured, never a notify event. diff --git a/issue/SKILL.md b/issue/SKILL.md index 02783de..d0d6c74 100644 --- a/issue/SKILL.md +++ b/issue/SKILL.md @@ -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//…`, `branch/…`) it does not + resolve: + - a file at a pinned commit — + `[what it says](src/commit//docs/design.md#user-content-)`, + or a line range `…/parser.rs#L84-L90`; prefer `src/commit//…` + (immutable) over `src/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 --add-labels a --remove-labels b` | | Comment | `tea comment ""` | | Close / reopen | `tea issues close ` / `tea issues reopen ` | +| List milestones | `tea milestones ls --state open` | +| Create milestone | `tea milestones create -t "" -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