diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..77b8e4e1 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,22 @@ +# AGENTS.md + +Guidance for coding agents working in this repository. + +## Agent skills + +### Issue tracker + +Issues live as Gitea issues in `unom/punktfunk` on `git.unom.io`, driven by the `gitea` MCP server +(`gh`/`glab`/`tea` do not work here), and every write needs the user's go-ahead first. +See `docs/agents/issue-tracker.md`. + +### Triage labels + +The five canonical roles, each label string equal to its name — `needs-triage`, `needs-info`, +`ready-for-agent`, `ready-for-human`, `wontfix` — none of which exist in the tracker yet. +See `docs/agents/triage-labels.md`. + +### Domain docs + +Single-context: one `CONTEXT.md` and one `docs/adr/` at the repo root, covering the whole +workspace. See `docs/agents/domain.md`. diff --git a/docs/agents/domain.md b/docs/agents/domain.md new file mode 100644 index 00000000..0e546b7e --- /dev/null +++ b/docs/agents/domain.md @@ -0,0 +1,58 @@ +# Domain Docs + +How the engineering skills should consume this repo's domain documentation when exploring the +codebase. + +This is a **single-context** repo: one `CONTEXT.md` and one `docs/adr/` at the root, covering the +whole workspace. + +## Before exploring, read these + +- **`CONTEXT.md`** at the repo root. +- **`docs/adr/`** — read ADRs that touch the area you're about to work in. + +If any of these files don't exist, **proceed silently**. Don't flag their absence; don't suggest +creating them upfront. The `/domain-modeling` skill (reached via `/grill-with-docs` and +`/improve-codebase-architecture`) creates them lazily when terms or decisions actually get resolved. + +Neither file exists yet — that is expected, and not something to fix pre-emptively. + +## File structure + +``` +/ +├── CONTEXT.md +├── docs/adr/ +│ ├── 0001-....md +│ └── 0002-....md +├── crates/ ← Rust workspace members (host, capture, encode, decode, presenter, …) +├── clients/ ← per-platform clients (android, apple, linux, cli, decky, …) +├── web/ ← web console +├── sdk/ +└── plugin-kit/ +``` + +The code is split across many crates and client platforms, but they serve one domain — a host +captures, encodes, and streams a session to a client that decodes and presents it. Keep the +glossary unified across them rather than splitting per directory. If a genuinely separate domain +appears later, switch to a root `CONTEXT-MAP.md` pointing at per-context `CONTEXT.md` files and +update this file. + +`docs/` already holds release notes (`docs/releases/`) — those are not domain docs, and ADRs sit +alongside them in `docs/adr/`, not inside them. + +## Use the glossary's vocabulary + +When your output names a domain concept (in an issue title, a refactor proposal, a hypothesis, a +test name), use the term as defined in `CONTEXT.md`. Don't drift to synonyms the glossary +explicitly avoids. + +If the concept you need isn't in the glossary yet, that's a signal — either you're inventing +language the project doesn't use (reconsider) or there's a real gap (note it for +`/domain-modeling`). + +## Flag ADR conflicts + +If your output contradicts an existing ADR, surface it explicitly rather than silently overriding: + +> _Contradicts ADR-0007 (event-sourced orders) — but worth reopening because…_ diff --git a/docs/agents/issue-tracker.md b/docs/agents/issue-tracker.md new file mode 100644 index 00000000..8b86973b --- /dev/null +++ b/docs/agents/issue-tracker.md @@ -0,0 +1,97 @@ +# Issue tracker: Gitea (`git.unom.io`) + +Issues and specs for this repo live as **Gitea issues** on the self-hosted instance at +`git.unom.io`, in the repo **`unom/punktfunk`** (owner `unom`, repo `punktfunk`). + +Confirm with `git remote -v` if in doubt — but note that a worktree of this repo has the same +remote, so `unom/punktfunk` holds regardless of which checkout you are in. + +## Use the `gitea` MCP server — not `gh`, `glab`, or `tea` + +This is **not** GitHub and **not** GitLab. `gh` is installed on this machine but is bound to +github.com and will not see these issues; `glab` and `tea` are not installed at all. Every issue +operation goes through the connected **`gitea` MCP server**. + +Its tools are *deferred* — the names are visible but the schemas are not loaded, so calling one +straight away fails with `InputValidationError`. Load what you need first: + +``` +ToolSearch("select:mcp__gitea__issue_write,mcp__gitea__issue_read,mcp__gitea__list_issues,mcp__gitea__label_read") +``` + +## Conventions + +Every call takes `owner: "unom"`, `repo: "punktfunk"`. + +- **Create an issue**: `mcp__gitea__issue_write` with `method: "create"`, `title`, `body`. +- **Read an issue**: `mcp__gitea__issue_read` with `method: "get"` (details), `"get_comments"` + (discussion), or `"get_labels"`. Read all three when triaging — Gitea returns them separately. +- **List issues**: `mcp__gitea__list_issues` with `state` (`open`/`closed`/`all`), optional + `labels` (an array of label **names** here), `since`/`before`, `page`/`per_page`. +- **Search across repos**: `mcp__gitea__search_issues` with `query`, optional `owner`, `labels` + (comma-separated string), `state`, `type`. +- **Comment**: `issue_write` with `method: "add_comment"`, `issue_number`, `body`. +- **Apply labels**: `issue_write` with `method: "add_labels"` / `"replace_labels"` / + `"remove_label"` / `"clear_labels"`. +- **Close**: `issue_write` with `method: "update"`, `issue_number`, `state: "closed"` — comment + first if you have something to say, since `update` takes no comment. + +### Trap: labels are written by numeric ID, read by name + +`issue_write` takes `labels` as an **array of numeric label IDs**, and `remove_label` takes a +single `label_id`. It will not accept label names. `list_issues`, by contrast, filters on label +**names**. So before applying a label, resolve the name to its ID: + +``` +mcp__gitea__label_read { method: "list_repo_labels", owner: "unom", repo: "punktfunk", per_page: 100 } +``` + +and match on `.name` to get `.id`. If the label does not come back, it does not exist yet — see +`triage-labels.md`; the repo currently has **no labels defined at all**, on the repo or the org. + +## Ask before writing — this tracker is outward-facing + +Reads (`issue_read`, `list_issues`, `search_issues`, `label_read`) are free; run them whenever you +need context. + +**Writes are outward-facing and require the user's go-ahead each time.** Creating an issue, +commenting, applying labels, closing, and creating labels all publish to a shared instance other +people watch, and Gitea emails on activity. Draft the full text, show it to the user, and file it +only once they say to. This applies to subagents too — a subagent may not file on your behalf. + +## Pull requests as a triage surface + +**PRs as a request surface: no.** _(Set to `yes` if this repo should treat external PRs as feature +requests; `/triage` reads this flag.)_ + +If it is ever set to `yes`, the PR equivalents are `mcp__gitea__list_issues` with +`type: "pulls"`, plus `mcp__gitea__pull_request_read` and `mcp__gitea__pull_request_write`. Gitea +shares one number space across issues and PRs, so a bare `#42` may be either — resolve with +`pull_request_read` and fall back to `issue_read`. + +## When a skill says "publish to the issue tracker" + +Create a Gitea issue in `unom/punktfunk` — after asking (see above). + +## When a skill says "fetch the relevant ticket" + +`issue_read` with `method: "get"`, then `method: "get_comments"`. + +## Wayfinding operations + +Used by `/wayfinder`. The **map** is a single issue; **child** issues are the tickets. + +- **Map**: an issue labelled `wayfinder:map`, holding the Notes / Decisions-so-far / Fog body. +- **Child ticket**: Gitea has no sub-issue relationship over this MCP surface. Add each child to a + task list in the map body (`- [ ] #`) and put `Part of #` at the top of the child + body. Label with `wayfinder:` (`research`/`prototype`/`grilling`/`task`). Once claimed, + set `assignees` to the driving dev. +- **Blocking**: Gitea has native issue dependencies in its web UI, but no MCP method reaches them. + Use a `Blocked by: #, #` line at the top of the child body instead. A ticket is unblocked + when every issue named there is closed — check with `issue_read`. +- **Frontier query**: `list_issues` with `state: "open"`, narrowed to the map's task-list children; + drop any with an open blocker or an assignee; first in map order wins. +- **Claim**: `issue_write` with `method: "update"` and `assignees` — the session's first write, so + ask first. +- **Resolve**: `add_comment` with the answer, then `update` to `state: "closed"`, then append a + context pointer to the map's Decisions-so-far. diff --git a/docs/agents/triage-labels.md b/docs/agents/triage-labels.md new file mode 100644 index 00000000..e2797bca --- /dev/null +++ b/docs/agents/triage-labels.md @@ -0,0 +1,29 @@ +# Triage Labels + +The skills speak in terms of five canonical triage roles. This file maps those roles to the actual +label strings used in this repo's issue tracker. + +| Label in mattpocock/skills | Label in our tracker | Meaning | +| -------------------------- | -------------------- | ---------------------------------------- | +| `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue | +| `needs-info` | `needs-info` | Waiting on reporter for more information | +| `ready-for-agent` | `ready-for-agent` | Fully specified, ready for an AFK agent | +| `ready-for-human` | `ready-for-human` | Requires human implementation | +| `wontfix` | `wontfix` | Will not be actioned | + +When a skill mentions a role (e.g. "apply the AFK-ready triage label"), use the corresponding label +string from this table. + +Edit the right-hand column to match whatever vocabulary you actually use. + +## These labels do not exist yet + +As of setup, `unom/punktfunk` has **no labels defined** — not on the repo, not on the `unom` org. +The first triage run has to create them with `mcp__gitea__label_write` +(`method: "create_repo_label"`, `name`, `color` as `#RRGGBB`, optional `description`). + +Creating labels is a write to a shared instance, so it falls under the ask-first rule in +`issue-tracker.md` — propose the five, then create them once the user agrees. + +Remember the ID trap from `issue-tracker.md`: applying a label needs its **numeric ID** from +`label_read`, not the name in the table above.