Tell the agents where the issues live: AGENTS.md and docs/agents/ #310
@@ -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`.
|
||||
@@ -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…_
|
||||
@@ -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 (`- [ ] #<child>`) and put `Part of #<map>` at the top of the child
|
||||
body. Label with `wayfinder:<type>` (`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: #<n>, #<n>` 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.
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user