6.3 KiB
name, description
| name | description |
|---|---|
| gitea-pr-api-cycle | "Open/update/merge PRs and issues on the Dankclaw Gitea API (192.168.0.143:3000) with the token-safe sequence, the form-encoded merge field, the empty-token-returns-404 trap, and post-merge live-checkout fast-forward plus the plugin-package-name upgrade gotcha." |
Gitea PR/issue cycle — Dankclaw API
Friction-earned 2026-10-18 (PRs #15–#20, issue #18) and re-earned 2026-10-19 on dank/dankvoice PRs #4–#6: the merge payload claim below was wrong, and an empty token masqueraded as a missing repo.
Where
- API base:
http://192.168.0.143:3000/api/v1/repos/dank/<repo>/{pulls,issues}— repo varies (command-center,dankvoice,serverAppConfigs); numbering is per-repo, so a footer citing#36means nothing without the repo. - Token: never inline, never in argv (
-H "Authorization: token …"lands in/proc/<pid>/cmdline, world-readable). Extract into a 600-perm curl config and hand it to curl with-K: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" - Empty token answers
404 {"message":"not found"}, NOT 401 — Gitea hides the private repo. A 404 on a path you have used before means auth, not a typo'd repo. The trap that produces the empty token:awk -F'/' … '{print $NF}'on the store line (scheme://user:token@host/) — the host is in$3, the match never fires, you get a silent empty variable. Always assert the length before POSTing. - Multi-line bodies: build JSON in python (
json.dumps({"title":…, "body":open(f).read(),…})) orjq -n --arg b "$(cat b.txt)". NEVER pass---as a bare jq argument (parsed as a flag). Escaped quotes insidepython3 -c '…'break on\"— write the script to a file or use a heredoc. - Always capture status:
-o r.json -w '%{http_code}'; a bare-ohides failures.
Create
POST /pulls with {title, body, base:"main", head:<branch>} — JSON is accepted here (unlike merge).
- 409
pull request already exists for these targets [issue_id: N, …]— a PR for that head/base exists already. Common cause: the push message printed/pulls/new/<branch>and someone (Frank, or anxdg-openyou ran) clicked Create.GET /pulls?state=all, find it, thenPATCH /pulls/Nwith your title+body instead of re-posting. GET /pulls?state=allon this Gitea omitshead/baseper entry — read/pulls/Nfor those, or key offnumber/user.- Issues: same shape,
POST /issues{title, body};PATCH /issues/Nfixes a body retroactively (works for PRs too, since a PR is an issue).
Merge (the 422 trap) — corrected
POST /pulls/N/merge needs form-encoded fields; the merge form field name is lowercase do:
curl -sS -X POST -K "$cfg" \
--data-urlencode 'do=merge' --data-urlencode 'merge_style=no-ff' \
http://192.168.0.143:3000/api/v1/repos/dank/<repo>/pulls/N/merge
JSON {"Do":"merge"} (what this skill used to claim) and header variants all return 422 {"message":"[Do]: Required"}. merge_style ∈ no-ff|merge|rebase|squash. Success returns 200 with an empty body; verify by landing a checkout rather than re-reading the PR. Merge only with Frank's explicit per-PR OK.
Post-merge landing
- Command-center: the checkout IS the deployment —
git -C ~/.omp fetch -q origin && git -C ~/.omp merge --ff-only origin/main. - Package repos (
~/Projects/<pkg>): same--ff-only, then reinstall (omp plugin upgrade <name>) andcmpthe installeddist/index.jsagainst the repodist/index.jsto prove the live artifact is the merged one. Checkls -ld node_modulesafter landing anything that touched it. omp plugin upgradetakes the npm package name, not the short name.omp plugin upgrade dankvoicefails withFailed to upgrade dankvoice: dankvoice is not installedwhile the plugin is plainly installed; the package isomp-dankvoice. Find the truth withls ~/.omp/plugins/node_modulesorfind ~/.omp -path '*<name>/dist/index.js' -not -path '*/Projects/*'— the installed tree is unscoped (~/.omp/plugins/node_modules/omp-dankvoice/dist/index.js), not@dank/dankvoice.cmpexiting 1 BEFORE the upgrade is the expected state (installed bundle is the pre-merge build, diff reported at some char/line). Do not treat it as a failed deploy and do not "fix" the build; run the upgrade, thencmpagain for byte identity, and grep the new bundle for a string only the merged code contains (e.g.grep -c "Heckle instead of summarizing") as a second, semantic confirmation.- Prune:
git worktree remove --force <path>(force: untracked deps link),git branch -D,git push origin --delete <branch>(allowed: local Gitea).
Attribution footer (AGENTS.md)
Every issue/PR body ends with ---, then cwd: <session cwd> · session: <session id>. Read the real values, never invent an id.
- The session id is the UUID suffix of the LIVE transcript
.jsonl, not the session directory name. Transcript dirs are keyed by cwd (e.g.-.omp-wt-wt-20261019-020309-6d1fc1f) and ONE cwd can hold several sessions (resume / continuation forks), sols -t ~/.omp/agent/sessions/cannot tell you which is yours andls -t <dir>is equally blind. Pick the file being appended to right now:ls -l --time-style=+%H:%M:%S <dir>/*.jsonlbesidedate +%H:%M:%S— the mtime that equals the current second is the live session. The id is the part after_in<timestamp>_<uuid>.jsonl. - A
history.dbquery forsession_idfiltered bycwdusually agrees, but it is not authoritative for WHICH of several sessions in that cwd is live — use the jsonl mtime test. local/does NOT disambiguate: a forked transcript dir carries copies of the samelocal://artifacts, so matching artifact names proves nothing about which session is live.cwdis that session's own working directory (the path the harness states), not the build worktree you committed in — when they differ, name the build worktree inside the body and keepcwdthe session's.- Resolve
local://<name>.mdwithread local://<name>.md(its header prints the absolute path) instead of guessing the directory.