fix: keep request payloads out of the issue store

`labels.py` handed `_gitea.api` the issue store as a place to put the
request file, and on a checkout without a store that quietly created
`tmp/issues/.payload/`. Bootstrapping a repository's labels touches no
issue at all, so the one rule the store has — nothing materializes it as
a side effect of a write — was broken by an operation that has no
business knowing the store exists.

Where a request body goes was never the caller's decision to make. It is
now the transport's: `tmp/payload/`, resolved from `_gitea.py`'s own
location the way both domains resolve theirs, so every caller — sync and
wiki alike — writes to one directory whatever it was invoked from, and
`out_root` is gone from `api`, `add_dependency` and all six call sites.
The directory is created by the first write of a run and not before: a
`--dry-run` leaves nothing behind. `tmp/` is already gitignored.

The name carries the distinction the old path lost. A store holds the
only copy of something; this holds debris kept for a retry or a
post-mortem, and deleting it costs nothing. A dotdir sitting among an
issue's files claimed otherwise, and `ls tmp/issues` started lying about
what existed.

tests/test_payload_root.py runs the real `labels.py` in a throwaway repo
against a fake `tea` on PATH: no store appears, the payloads land in
tmp/payload/, a dry run writes nothing, and a run from a subdirectory
still resolves to the repo root. Two source checks keep the callers from
drifting apart again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
naudachu
2026-08-10 17:25:33 +05:00
parent 2ac301550e
commit 596cf853e8
11 changed files with 364 additions and 32 deletions
+20 -1
View File
@@ -64,8 +64,10 @@ If a tracker concept (issue number, login, HTTP call, label color, `sub_url`,
- `skills/sync` — move issues between the local store and Gitea (`/tea:sync`)
- `scripts/map.py` — md ↔ Gitea JSON, pure, no I/O; label colors live here
- `scripts/_gitea.py` — transport: login pin, `tea api`, pagination, filters,
label ids, the remote-id map
label ids, the remote-id map, `tmp/payload/`
- `scripts/pull.py`, `push.py`, `remote.py`, `comment.py`
- `scripts/labels.py` — put the canonical `type/*` and `severity/*` set into a
repository; reads the domain taxonomy, never the store
- `skills/page` — a discussion's artifacts as a page tree (`/tea:page`),
entirely offline
- `references/pages.md` — canonical page-tree format; single source of truth
@@ -183,3 +185,20 @@ organized. Same stance as the issue store, resolved the same way from
disagree, because a page tree is worked on locally and an issue is not.
- The `tea` CLI has no wiki subcommand. `tea api` is the only route, through
`_gitea.py`.
## Request payloads
`tmp/payload/` (gitignored) holds the JSON bodies `tea api -d @file` was given,
one file per named request, kept after the call for a retry or a post-mortem.
It is **not a store and holds nobody's only copy** — deleting it costs nothing.
- One directory for every caller — sync and wiki both — resolved from
`_gitea.py`'s own location, so which command wrote a body does not change
where it landed. `_gitea.api` takes no directory argument; that it once did
is exactly how a label bootstrap came to create `tmp/issues/`.
- It is created lazily, by the first write of a run, and only then: a `--dry-run`
or a run with nothing to send leaves no directory behind.
- **A scratchpad may never sit inside a store.** Store contents are the thing
being tracked; request bodies are debris of the transport. When the two share
a path, an operation that touches no issue at all still materializes the issue
store, and the operator's `ls tmp/issues` starts lying about what exists.