# t_a292e96f — Gitea PR metadata wrapper fix ## Objective Repair Mosaic git wrappers so Gitea PR metadata and merge preflight work for U-Connect PRs on `git.uscllc.com` without selecting the unrelated `git.mosaicstack.dev` tea login. ## Findings - Reproduced the failure from `/src/uconnect-worktrees/t_39ce717c-authentik-smoke-gate` with the current `pr-metadata.sh`: - PR #1905 returned JSON with `number=null`, `baseRefName=""`, `headRefName=""`. - PR #1908 returned JSON with `number=null`, `baseRefName=""`, `headRefName=""`. - Root cause: the wrapper treated HTTP/API error payloads as PR payloads and normalized missing fields to empty strings. - The credential loader can return a non-working `git.uscllc.com` API token in this environment, while host-specific `~/.git-credentials` basic auth succeeds. The wrapper now falls back by host before normalization. - `tea login list` has only `git.mosaicstack.dev` configured here; `pr-merge.sh` previously forced `--login mosaicstack`, which is invalid for `git.uscllc.com` and caused `Login name mosaicstack does not exist`. ## Changes - `packages/mosaic/framework/tools/git/detect-platform.sh` - Added `get_gitea_basic_auth ` to retrieve host-specific HTTPS credentials from `~/.git-credentials` without printing secrets. - `packages/mosaic/framework/tools/git/pr-metadata.sh` - Uses strict bash mode. - Checks Gitea HTTP status and fails nonzero on API errors/non-JSON instead of emitting empty branch fields. - Falls back from token auth to host-specific basic auth. - Normalizes standard `head.ref`/`base.ref` and fallback branch fields. - Requires non-empty `headRefName` and `baseRefName`. - Preserves GitHub `gh pr view` behavior. - `packages/mosaic/framework/tools/git/pr-merge.sh` - Reads metadata once for base-branch policy preflight. - Selects a `tea` login only when its configured URL matches the repo host. - Falls back to authenticated Gitea merge API when no matching `tea` login exists, avoiding the wrong `mosaicstack` login for USC repos. - Keeps squash-only and main-only merge policy. - `packages/mosaic/framework/tools/git/test-pr-metadata-gitea.sh` - Added fixture-based regression harness for standard Gitea fields, fallback branch fields, `refs/pull//head` plus `head.label` normalization, and API error payloads. ## Documentation / changelog note This repository currently has no root `CHANGELOG.md`; the scratchpad and `docs/TASKS.md` carry the task-level change record for this wrapper fix. ## Verification log - Red regression check: copied the new `test-pr-metadata-gitea.sh` harness next to `origin/main` wrapper scripts and ran it with `MOSAIC_TEST_WORK_DIR=$PWD/.mosaic-test-work/pr-metadata-gitea-red`; it failed as expected with `headRefName=''` and `baseRefName=''` on the fixture API-error path. - `bash -n packages/mosaic/framework/tools/git/{detect-platform.sh,pr-metadata.sh,pr-merge.sh,test-pr-metadata-gitea.sh}`: passed. - `shellcheck -x -P . -e SC1090 packages/mosaic/framework/tools/git/{detect-platform.sh,pr-metadata.sh,pr-merge.sh,test-pr-metadata-gitea.sh}`: passed. - `MOSAIC_TEST_WORK_DIR=$PWD/.mosaic-test-work/pr-metadata-gitea packages/mosaic/framework/tools/git/test-pr-metadata-gitea.sh`: passed; verifies standard Gitea fields, fallback branch fields, `refs/pull//head` label normalization, and nonzero API-error handling. - Installed wrapper parity: `/home/hermes/.config/mosaic/tools/git/{detect-platform.sh,pr-metadata.sh,pr-merge.sh}` byte-match the PR source copies after validation, so active U-Connect wrapper invocations use the same fix while source PR review runs. - Live sanitized U-Connect metadata from `/src/uconnect` with `MOSAIC_CREDENTIALS_FILE=/src/jarvis-brain/credentials.json`: - PR #1905: `number=1905`, `baseRefName=main`, `headRefName=edith/t_39ce717c-authentik-smoke-gate`, `state=open`, `host=git.uscllc.com`. - PR #1908: `number=1908`, `baseRefName=main`, `headRefName=fix/t_23fa9e1d-portal-health-backend`, `state=closed`, `host=git.uscllc.com`. - Merge preflight dry runs from installed wrappers: - PR #1905: `Dry run: would merge PR #1905 on git.uscllc.com with authenticated Gitea API fallback (base=main, method=squash).` - PR #1908: `Dry run: would merge PR #1908 on git.uscllc.com with authenticated Gitea API fallback (base=main, method=squash).` - PR: `https://git.mosaicstack.dev/mosaicstack/stack/pulls/518`, branch `fix/t-a292e96f-gitea-pr-metadata`. - CI: Recent PR/push pipelines failed before clone/test execution due Woodpecker/Kubernetes PVC API timeout: `dial tcp 10.43.0.1:443: i/o timeout`. No repository test step executed in CI; local targeted verification above remains clean. ## 2026-06-18 — PR #549 functional blocker remediation ### Assignment Coordinator `mos-claude` assigned remediation for PR #549: fix `packages/mosaic/framework/tools/git/pr-metadata.sh` tmpfile cleanup where an `EXIT` trap references function-local `body_file` after the function returns inside `RAW=$(...)`, producing `body_file: unbound variable` on the authenticated success path and failing to clean up safely on early `set -e` exits. ### Plan 1. Add a non-vacuous Gitea test that exercises `curl_gitea_pull` with stubbed `curl` and `GITEA_TOKEN` instead of `MOSAIC_GITEA_PR_METADATA_RAW_FILE`. 2. Prove the new test is RED against the current PR head. 3. Replace the function-local `EXIT` cleanup with robust function-scoped tmpfile cleanup. 4. Re-run targeted tests, `bash -n`, and review gates; commit and push branch only. Do not merge. ### Constraints / assumptions - Do not modify prior injection/JSON fixes in `issue-edit`, `issue-assign`, or `milestone-create`. - Worker role: do not modify `docs/TASKS.md`; orchestrator remains the single writer. - Budget: no explicit token cap provided; keep scope to shell wrapper + targeted regression harness. ### Remediation results - Rebased `fix/tooling-eval-injection-jq-json` onto `origin/main`; branch was already current. - Added a curl-stub regression path that does not use `MOSAIC_GITEA_PR_METADATA_RAW_FILE`, so it exercises `curl_gitea_pull` and its temp body file. - RED evidence: copied the new harness next to the pre-fix `HEAD` version of `pr-metadata.sh`; `MOSAIC_TEST_WORK_DIR=$PWD/.mosaic-test-work/pr-metadata-red-work .../test-pr-metadata-gitea.sh` failed with `body_file: unbound variable` on the curl success path. - Fix: replaced `EXIT` temp-file cleanup with a `RETURN`-scoped cleanup function that removes the body file while the function-local variable is still in scope, preserves the original return status, and clears the `RETURN` trap. - GREEN evidence: - `MOSAIC_TEST_WORK_DIR=$PWD/.mosaic-test-work/pr-metadata-gitea-current packages/mosaic/framework/tools/git/test-pr-metadata-gitea.sh` passed. - `bash -n packages/mosaic/framework/tools/git/pr-metadata.sh packages/mosaic/framework/tools/git/test-pr-metadata-gitea.sh` passed. - `shellcheck -x -P . -e SC1090 packages/mosaic/framework/tools/git/pr-metadata.sh packages/mosaic/framework/tools/git/test-pr-metadata-gitea.sh` passed. ### Review remediation - Codex review returned one should-fix: the early-exit test used `chmod 000`, which is not root-safe in container CI. - Remediation: changed the stubbed 2xx/cat-failure mode to replace the curl output with a broken symlink, which fails deterministically even as root and still validates cleanup via `rm -f -- "$body_file"`. ### Second review remediation - Codex review found the 2xx `cat "$body_file"` read could be masked under command substitution semantics because the branch returned 0 unconditionally. - Remediation: both authenticated 2xx branches now use `cat "$body_file" || return $?` before returning success. - Strengthened the broken-symlink test to require the body-read failure and reject the later `Gitea API returned non-JSON` parse-failure path, so the test verifies the helper-level failure propagation rather than eventual downstream failure. ### Final review gate - Codex review after remediation: approved (`0 blockers, 0 should-fix, 0 suggestions`).