--- type: reference title: Issue tracker for engineering skills description: Where work items live for dank/command-center — Gitea on Dankclaw — with the exact API mechanics, traps, and laws the engineering skills must follow. tags: - skills - gitea - sdlc - agents timestamp: 2026-10-20 verified: 2026-10-20 --- # Issue tracker: Gitea on Dankclaw Issues and PRs for this repo live in Gitea: `dank/command-center` at `http://192.168.0.143:3000`. There is no `gh` and no `tea` on any box — talk to the REST API with `curl`. ## Authentication The token lives in `~/.config/secrets/gitea` (never embedded in a remote URL, never committed). Hand it to curl as a `600`-permission config file: `-H` puts it in world-readable `/proc//cmdline`. ```bash tok=$(sed -nE 's#^https?://[^:/@]+:([^@]+)@192\.168\.0\.143.*#\1#p' ~/.config/secrets/gitea | head -1) [ "${#tok}" -ge 40 ] || { echo "token extraction failed"; exit 1; } cfg=$(mktemp); chmod 600 "$cfg"; printf 'header = "Authorization: token %s"\n' "$tok" > "$cfg" curl -sS -K "$cfg" … ; shred -u "$cfg" ``` Assert the length before posting anything. **An empty token answers `404 not found`, not `401`** — Gitea hides the private repo, so a 404 on a path that worked yesterday is an auth failure rather than a typo. The awk-style alternative that splits the store line on `/` silently yields an empty variable, because the host sits in field three. Deeper detail: [Gitea client access](/tools/gitea/client-access.md). Multi-line bodies: build the JSON with `python3 -c 'json.dumps(...)'` reading the body from a file. Never pass `---` as a bare `jq` argument (it parses as a flag), and never escape quotes inside a single-quoted `python3 -c`. ## Endpoints the skills use | Action | Call | |---|---| | Open a work item | `POST /api/v1/repos/dank/command-center/issues` `{title, body}` | | Correct a body afterwards | `PATCH …/issues/N` (works on PRs too — a PR is an issue) | | Open a PR | `POST …/pulls` `{title, body, base: "main", head: }` — JSON accepted here | | Merge | `POST …/pulls/N/merge` **form-encoded**: `do=merge`, `merge_style=no-ff` | | Labels | `GET` / `POST …/labels` | Three traps that cost time before: - `POST /pulls` answers `409 … already exists for these targets` when the branch already has a PR (Frank clicks Create from the push message). `GET /pulls?state=all`, find it, `PATCH` it — do not re-post. - A JSON merge body (`{"Do":"merge"}`) returns `422 {"message":"[Do]: Required"}`. The field name is lowercase `do` and the body must be form-encoded. - A successful merge returns `200` with an **empty body**. Verify by landing a checkout (`git merge --ff-only origin/main`), never by re-reading the PR. ## Laws inherited from AGENTS.md A skill working in this repo does not get to renegotiate these: - A work item is a Gitea issue. Anything with more than one moving part gets the issue before the branch. Branch naming `/-`, PRs target `main` directly, merge with `--no-ff`. - **Never merge without Frank's explicit per-PR OK**, and `xdg-open` the PR URL into his browser the moment it exists — he should not have to hunt for it. - Every issue and PR body ends with `---`, then `cwd: · session: `. The session id is the UUID of the transcript file being appended to right now (`ls -l --time-style=+%H:%M:%S ~/.omp/agent/sessions//*.jsonl`), not the session directory name — one cwd can hold several sessions. - `git * --no-verify` is forbidden; the pre-commit hook is the secret gate.