Landing blocker for #61 and #63: the deployment checkout is 12 commits behind and `merge --ff-only` refuses while tracked paths are dirty. Discard is not available here (workspace policy: no `checkout`/`stash` on shared state), so the dirt is committed. - `agent/APPEND_SYSTEM.md`: the point-of-use / one-work-log / PR-link doctrine, already byte-identical to `origin/main` (`cmp` against the trunk blob: no difference). No new prose — the commit exists so the trunk merge can proceed. - `agent/config.yml` and `agent/managed-skills/**`: the machine-live tier the heartbeat owns (AGENTS.md § Live-sync tiers). `ALLOW_SECRETS=1` was used, deliberately and after auditing the staged diff by hand: the only credential-shaped strings in it are two copies of `AKIA0000000000000EXAMPLE` — the inert all-zero AWS example id — and they appear inside the two skill pages that teach that exact placeholder shape. This checkout is still running the pre-#61 hook; the merged gate (value-scoped placeholder filter, `71601f7`) exempts this value, so the refusal dies with this landing. No `--no-verify` was used: the gate ran and its verdict is recorded here. Refs #61
54 lines
2.3 KiB
Markdown
54 lines
2.3 KiB
Markdown
---
|
||
name: gitea-issue-labels-and-pr-curl-traps
|
||
description: "Dankclaw Gitea API curl traps: PR create needs Content-Type application/json (fake \"[Head]: Required\"), issue labels only via POST /issues/N/labels (PATCH labels silently dropped), token from URL-format secret file."
|
||
---
|
||
|
||
Operate Dankclaw Gitea (`http://192.168.0.143:3000`, repo `dank/franksign` and siblings) with curl without the three traps that produce misleading errors.
|
||
|
||
## Token extraction (URL-format store, NOT key=value)
|
||
|
||
`~/.config/secrets/gitea` is one line: `http://user:TOKEN@192.168.0.143:3000`. `cat`-ing it yields the whole URL, not the token.
|
||
|
||
```bash
|
||
TOK=$(sed -n 's|.*://[^:]*:\([^@]*\)@.*|\1|p' ~/.config/secrets/gitea | head -1)
|
||
```
|
||
|
||
Never pass the token as a `-H` argument (world-readable in `/proc/<pid>/cmdline`). Write a 600-perm curl config:
|
||
|
||
```bash
|
||
printf 'header = "Authorization: token %s"\n' "$TOK" > /tmp/g.cfg
|
||
```
|
||
|
||
## Trap 1 — PR create answers `"[Head]: Required"` when the body is the problem
|
||
|
||
Two independent causes, both look like a missing field:
|
||
|
||
1. No `Content-Type: application/json` header → Gitea parses the raw JSON as form data, sees no `head`, returns 422 `[Head]: Required`.
|
||
2. Putting `data = "@/tmp/body.json"` inside a `-K` curl config does **not** expand the `@`.
|
||
|
||
Fix: add `header = "Content-Type: application/json"` to the config and pass `--data @/tmp/body.json` on the command line:
|
||
|
||
```bash
|
||
curl -s -K /tmp/g.cfg -X POST --data @/tmp/pr.json \
|
||
"http://192.168.0.143:3000/api/v1/repos/dank/franksign/pulls"
|
||
```
|
||
|
||
## Trap 2 — issue label assignment via PATCH /issues/N is silently dropped
|
||
|
||
`PATCH /api/v1/repos/{o}/{r}/issues/{n}` with `{"labels":[id]}` returns **200/201 with the issue JSON**, yet a follow-up GET shows `labels: []` (it can even clear existing labels). Labels are a subresource:
|
||
|
||
```bash
|
||
curl -s -K /tmp/g.cfg -X POST --data '{"labels":[4]}' \
|
||
"http://192.168.0.143:3000/api/v1/repos/dank/franksign/issues/18/labels"
|
||
```
|
||
|
||
200 returns the assigned label array. Resolve label names→ids via `GET /repos/{o}/{r}/labels` (franksign canonical five: ids 2–6; `ready-for-agent` = 4).
|
||
|
||
## Verification law
|
||
|
||
Both traps fail *successfully*. After any label/PR write, GET the resource and check the field changed; never trust the status code alone.
|
||
|
||
## Cleanup
|
||
|
||
`rm -f` the temp config + body files (config holds the token).
|