Skip to content

[Bugfix #1137] Fix gitea forge preset against the real tea CLI - #1146

Open
pseudoseed wants to merge 5 commits into
cluesmith:mainfrom
pseudoseed:builder/bugfix-1137
Open

[Bugfix #1137] Fix gitea forge preset against the real tea CLI#1146
pseudoseed wants to merge 5 commits into
cluesmith:mainfrom
pseudoseed:builder/bugfix-1137

Conversation

@pseudoseed

Copy link
Copy Markdown

Fixes #1137.

Bugfix-protocol re-do of the earlier SPIR-style PR #1138 (now closed), per maintainer request. Same root cause, plus a real regression test.

Problem

The gitea forge preset was authored against the Gitea REST API JSON shape, but the scripts invoke the tea CLI, whose output shape differs — and several concepts referenced flags/fields/subcommands tea doesn't have. Per the in-repo #920 note, tea wasn't available in the authoring environment, so the preset was never run end-to-end.

Fix

Route the read concepts through tea api (raw REST passthrough returning the shape forge-contracts.ts + the jq normalizers expect):

  • user-identity: tea api user | jq .login (tea whoami has no --output json)
  • pr-view: tea api repos/<repo>/pulls/NPrViewResult (incl. additions/deletions)
  • pr-list: tea api repos/<repo>/pulls?state=openPrListItem[]
  • pr-exists: tea api repos/<repo>/pulls?state=all with nested .head.ref / .merged
  • issue-view: tea api repos/<repo>/issues/N + a second call for the comments array (Gitea reports comments as an int count, which would crash .comments.filter(...))
  • recently-merged: tea api repos/<repo>/pulls?state=closed, filter .merged, using real .merged_at
  • issue-comment: tea comments add (tea issues has no comment subcommand)

tea api needs an explicit owner/repo path segment, so each api-based script derives owner/repo from the origin remote (honoring CODEV_REPO when set).

Testing

🤖 Generated with Claude Code

pseudoseed and others added 4 commits July 6, 2026 13:06
The gitea preset invoked `tea <entity> list/view/whoami/comment`, whose
flattened `--fields` output (or missing flags/subcommands) doesn't match the
Gitea REST shape that forge-contracts.ts and the jq normalizers assume. Route
the read concepts through `tea api`, the raw REST passthrough that returns
exactly that shape:

- user-identity: `tea api user | jq .login` (`tea whoami` has no --output json)
- pr-view:  `tea api repos/<repo>/pulls/N` → PrViewResult
- pr-list:  `tea api repos/<repo>/pulls?state=open` → PrListItem[]
            (now also populates real reviewRequests/isDraft/body)
- pr-exists: `tea api repos/<repo>/pulls?state=all` with nested .head.ref/.merged
- issue-view: `tea api repos/<repo>/issues/N` + a second call for the comments
             ARRAY (Gitea's issue object reports `comments` as an int count,
             which would crash consumers' `.comments.filter(...)`)
- recently-merged: `tea api repos/<repo>/pulls?state=closed`, filter .merged,
             using the real .merged_at
- issue-comment: `tea comments add` (`tea issues` has no `comment` subcommand)

`tea api` needs an explicit owner/repo path segment (unlike `tea <entity>`,
which auto-detects it from the local git remote), and most concepts are invoked
without CODEV_REPO set, so each api-based script derives owner/repo from the
origin remote, honoring CODEV_REPO when present.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stubs a fake `tea` on PATH answering `api <endpoint>` with captured Gitea REST
fixtures (tea isn't in CI, per cluesmith#920), points the scripts at a throwaway repo
with a gitea remote, runs each real script, and asserts the normalized output
conforms to forge-contracts.ts — incl. comments-as-array, merged-only filtering,
open/merged/closed pr-exists cases, and CODEV_REPO override.

Also updates the cluesmith#568 pr-exists assertion for gitea to match the new
`state=all` query param (was `--state all` flag).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@waleedkadous waleedkadous left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent work — thank you for the disciplined re-do, and apologies for the review latency. This is what a model bugfix PR looks like: every tea-CLI deficiency documented in-script with reasoning, the REST passthrough returning exactly the shape forge-contracts.ts expects, and a genuinely well-built regression suite (fake tea on PATH serving captured REST fixtures, the real scripts executed, contract-shape assertions, the comments-as-int crash and null-login team reviewers both covered). We verified every output mapping field-by-field against the contracts — all conform — and the switch from string-interpolated jq to --arg in pr-exists is a quiet security improvement worth crediting.

One substantive question before merge, and two optional polish items:

1. Pagination cap (the one we'd like addressed or answered). Gitea servers cap page size at max_response_items (default 50), so ?limit=200 likely returns 50 items with no client-side pagination in the raw passthrough. That means pr-exists?state=all can false-negative for a branch whose PR isn't in the most recent ~50 (which would block a porch pr_exists gate), and recently-merged (previously --limit 1000) can miss on a busy repo. A pagination loop (page=1..N until a short page) would settle it — or at minimum a comment documenting the server-side cap and the false-negative window, so the next debugger isn't blind. Happy with either; we'd just like the behavior to be chosen rather than inherited.

2. (Polish, optional) With no origin remote or an unusual URL, REPO silently becomes empty/garbage and tea api "repos//…" fails with a confusing 404. An explicit [ -n "$REPO" ] || { echo "…set CODEV_REPO" >&2; exit 1; } naming the remedy would fit this repo's fail-fast convention — ideally factored once since the derivation appears in five scripts.

3. (Polish, optional) A failed comments fetch silently yields comments: [] — indistinguishable from "no comments" for consumers reading issue discussion. A stderr warning on the degraded path would keep the graceful behavior while leaving a trace.

Verdict: approve once item 1 is addressed (fix or documented caveat — your choice). Items 2–3 are welcome in this PR or a follow-up, contributor's choice.

…ast, warn on degraded comments

Addresses PR cluesmith#1146 review feedback:

1. Pagination (blocking). Gitea caps list responses at max_response_items
   (default 50), so the raw `&limit=200` passthrough silently truncated —
   pr-exists could false-negative a PR beyond the first ~50 (blocking a porch
   pr_exists gate) and recently-merged could miss on a busy repo. New shared
   helper `_lib.sh#tea_api_paged` walks page=1..N at limit=50, concatenates the
   arrays, and stops on a short/empty page with a hard 100-page ceiling.
   Chosen behavior: paginates, ceiling 100 pages. Wired into pr-exists,
   pr-list, recently-merged; output shape unchanged (same jq normalizers).

2. REPO derivation, fail-fast + factored. The CODEV_REPO/origin-derivation was
   duplicated in five scripts. Factored into `_lib.sh#gitea_repo`, sourced by
   issue-view, pr-exists, pr-list, pr-view, recently-merged. It now validates
   the result is a clean owner/repo and, if not, prints a stderr message naming
   CODEV_REPO as the remedy and exits non-zero (was a confusing `repos//…` 404).
   POSIX sh, $0-relative source; not a forge concept (KNOWN_CONCEPTS allowlist).

3. Degraded comments warn. issue-view still degrades a failed comments fetch to
   [], but now writes a stderr warning so it's distinguishable from a genuinely
   uncommented issue. stdout stays pure JSON (parsed by forge.ts).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@pseudoseed

Copy link
Copy Markdown
Author

Thanks for the thorough review — all three items addressed in 3c2e3c2b.

1. Pagination — fixed (real page loop, not a comment). You're right that ?limit=200 inherited Gitea's max_response_items cap (default 50) and silently truncated. Added a shared tea_api_paged helper in a new scripts/forge/gitea/_lib.sh that requests page=1,2,3… at an explicit limit=50, concatenates each page's array (jq -s add), and stops when a page comes back shorter than the limit (or empty). Chosen behavior: it paginates, with a hard ceiling of 100 pages (100 × 50 = 5000 items) so a misbehaving server can't spin forever — documented in the helper. Wired into all three list reads: pr-exists (state=all), pr-list (state=open), recently-merged (state=closed). Output shape is unchanged — the concatenated array feeds the existing jq normalizers untouched. New tests serve a full 50-item page 1 + a short page 2 and assert an item that exists only on page 2 is found by each of the three scripts (pr-exists returns true for it, pr-list/recently-merged include it).

2. REPO fail-fast, factored once — done. The CODEV_REPO/origin-derivation line (duplicated in five scripts) now lives in _lib.sh#gitea_repo, sourced via . "$(dirname "$0")/_lib.sh" by issue-view, pr-exists, pr-list, pr-view, and recently-merged. It validates the result is a clean owner/repo; if not (no origin, unusual URL), it prints set CODEV_REPO=owner/repo to stderr and exits non-zero instead of letting tea api "repos//…" 404. Verified before factoring: forge.ts builds presets from the explicit KNOWN_CONCEPTS allowlist (a leading-underscore file is never registered as a concept), package.json files ships scripts/forge so _lib.sh is packaged, it's POSIX sh (no bashisms), and $0-relative sourcing works when the script is invoked by absolute path (how forge runs it via sh -c). Tests cover missing-origin and garbage-URL → non-zero exit + the stderr remedy.

3. Degraded comments warn — done. issue-view still degrades a failed comments fetch to [], but now writes gitea forge: comments fetch failed for issue N; reporting 0 comments to stderr while stdout stays pure JSON. Test asserts both (comments: [] on stdout and the stderr warning).

Full suite green in the worktree: 3449 passed | 48 skipped, 0 failures (pnpm build + pnpm test). The #568 pr-exists state=all assertion stays green (the helper is still called with state=all).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gitea forge preset is broken against the real tea CLI (0.14.2)

2 participants