docs(agents): post review passes as formal PR reviews #185

Merged
gabogg merged 13 commits from docs/formal-pr-reviews into master 2026-10-02 13:24:20 +00:00
Owner

Summary

Agents currently work PR reviews and merges from hand-typed tea api commands, which costs tokens every time. They re-read the command docs, retype the commands, and retry when escaping goes wrong. They also read every PR comment (tea pr N --comments) to find the review pass they must address. This PR makes review passes formal PR reviews with a fixed header line and adds scripts for the mechanical steps:

  • scripts/wt review (backed by the scripts/review.py module)
    • post <N> <file> posts a pass as a formal COMMENT review and rejects a file whose first line is not ## Code review, pass <N> (<base>...<reviewed SHA>, ...) with a readable reviewed SHA.
    • list <N> prints each pass with its reviewed SHA and the ref of its fix reply (## Pass <N> fixes (<SHA>) posted after it), then names the latest pass.
    • show <N> [<ref>] prints only the body of the latest pass, or of the one you pick.
    • It also finds plain-comment passes that already used the ## Code review, pass <N> ( header, such as PR #176's. Passes with other wordings (#175's first pass, and #173's and #166's passes) are not listed; those PRs are merged, so nothing needs migrating.
  • scripts/wt merge <N> rejects malformed WT_CI_* settings (whole seconds only), refuses a closed, draft or not-yet-mergeable PR, then waits for the head commit's CI:
    • While CI is running it waits, for up to 30 minutes of real elapsed time. If no Actions run appears for the head commit within 60 seconds (WT_CI_START_GRACE), it fails.
    • If CI failed, it prints the last 60 lines of each failed job's log, keeps the full log in a temp file, and does not merge.
    • If CI passed, it merges with the <PR title> (#N) merge-commit title, pinned to the commit CI passed on, and deletes the branch. If Forgejo refuses, it prints the reason; the PR's state, not the request's exit code, decides whether it merged. It then runs wt prune and fast-forwards the main checkout's master.
    • It reads the logs through Forgejo 16's Actions API: /actions/runs?head_sha=, then /runs/{id}/jobs, then /jobs/{id}/logs. The web UI's raw-log URL returns 404 on v16. tea actions runs logs still fails on v16, because Forgejo returns the jobs as a plain list where tea expects Gitea's {total_count, jobs} object.

Architectural impact

Agent tooling and docs only; no application code changes.

  • docs/standards/git-and-workflow.md: review passes are formal reviews with the fixed header line, posted and read through scripts/wt review. The reply that maps findings to fixes stays a plain comment. Branch cleanup points at wt merge.
  • docs/agents/forgejo-cli.md: the Review passes and Merging sections are now short pointers to the scripts, replacing the long command recipes.
  • docs/agents/workspaces.md: lists wt merge.
  • The vendored code-review skill (mattpocock/skills, hash-pinned in skills-lock.json) is unchanged.

Verification

  • tests/test_review_script.py covers header and reviewed-SHA parsing, fix replies tied to their pass (including comments in the same second), merging formal reviews with plain-comment passes, ordering, and helper shadowing. tests/test_wt_api.py covers checked API reads. tests/test_wt_merge.py drives the wt merge gate end to end against a fake tea: malformed timing settings, draft refusal, failed CI log tail, no Actions run, refused merge, and a merge that succeeded despite a failed request. Pre-commit hooks passed, including the full pytest suite, ruff and check_docs.py.
  • scripts/wt review, tested on live PRs:
    • list/show 176 found the plain-comment pass c3198.
    • On this PR, post rejected a body without the header, posted a body with backticks, quotes and non-ASCII characters as r5, and list and show read it back. I deleted the test review afterwards.
  • Forgejo refuses APPROVED ("approve your own pull is not allowed") and REQUEST_CHANGES ("reject your own pull is not allowed") from the PR author, so reviews are posted as COMMENT.
  • wt merge refused a draft (#185), a closed PR (#175) and a non-numeric argument.
  • I ran the CI gate against real commits:
    • #176's head (success) returned and would merge.
    • On Forgejo 16, old commit 821cf66 (failed run 278, whose job timed out and was cancelled) printed 60 log lines plus the full-log path, then stopped.
    • A head with no Actions run fails after the start grace with an explanatory message.
  • The final merge call remains pending until this PR completes its review cycle.

Checklist

  • Formal-review convention, scripts/wt review, scripts/wt merge, docs.
  • Forgejo upgraded 8.0.3 → 16.0.5 on 2026-09-29. CI passed on v16 (cdae9a0). First-pass fixes at 3cb802b and 0773d27, and maintainer-requested wt review at 8a84119: full pytest suite and pre-commit hooks passed; Ruff and docs checks passed.
  • PR #176 completed its review cycle and merged.
  • Second pass (r7) fixed in 90ebb94; third pass (r8) fixed in 7950c10, every finding including P3s.
  • Fourth pass (r9): 1 P2 (same-second comment crash) and 6 P3s, all fixed in 00a07a6.
  • Fifth pass (r10): only P3s (settings check bypass, elapsed-time wait, description), fixed in dc646c4.
  • Merge through scripts/wt merge 185.

🤖 Generated with Claude Code

## Summary Agents currently work PR reviews and merges from hand-typed `tea api` commands, which costs tokens every time. They re-read the command docs, retype the commands, and retry when escaping goes wrong. They also read every PR comment (`tea pr N --comments`) to find the review pass they must address. This PR makes review passes **formal PR reviews** with a fixed header line and adds scripts for the mechanical steps: - **`scripts/wt review`** (backed by the `scripts/review.py` module) - `post <N> <file>` posts a pass as a formal `COMMENT` review and rejects a file whose first line is not `## Code review, pass <N> (<base>...<reviewed SHA>, ...)` with a readable reviewed SHA. - `list <N>` prints each pass with its reviewed SHA and the ref of its fix reply (`## Pass <N> fixes (<SHA>)` posted after it), then names the latest pass. - `show <N> [<ref>]` prints only the body of the latest pass, or of the one you pick. - It also finds plain-comment passes that already used the `## Code review, pass <N> (` header, such as PR #176's. Passes with other wordings (#175's first pass, and #173's and #166's passes) are not listed; those PRs are merged, so nothing needs migrating. - **`scripts/wt merge <N>`** rejects malformed `WT_CI_*` settings (whole seconds only), refuses a closed, draft or not-yet-mergeable PR, then waits for the head commit's CI: - While CI is running it waits, for up to 30 minutes of real elapsed time. If no Actions run appears for the head commit within 60 seconds (`WT_CI_START_GRACE`), it fails. - If CI failed, it prints the last 60 lines of each failed job's log, keeps the full log in a temp file, and does not merge. - If CI passed, it merges with the `<PR title> (#N)` merge-commit title, pinned to the commit CI passed on, and deletes the branch. If Forgejo refuses, it prints the reason; the PR's state, not the request's exit code, decides whether it merged. It then runs `wt prune` and fast-forwards the main checkout's `master`. - It reads the logs through Forgejo 16's Actions API: `/actions/runs?head_sha=`, then `/runs/{id}/jobs`, then `/jobs/{id}/logs`. The web UI's raw-log URL returns 404 on v16. `tea actions runs logs` still fails on v16, because Forgejo returns the jobs as a plain list where `tea` expects Gitea's `{total_count, jobs}` object. ## Architectural impact Agent tooling and docs only; no application code changes. - `docs/standards/git-and-workflow.md`: review passes are formal reviews with the fixed header line, posted and read through `scripts/wt review`. The reply that maps findings to fixes stays a plain comment. Branch cleanup points at `wt merge`. - `docs/agents/forgejo-cli.md`: the Review passes and Merging sections are now short pointers to the scripts, replacing the long command recipes. - `docs/agents/workspaces.md`: lists `wt merge`. - The vendored `code-review` skill (mattpocock/skills, hash-pinned in `skills-lock.json`) is unchanged. ## Verification - [x] `tests/test_review_script.py` covers header and reviewed-SHA parsing, fix replies tied to their pass (including comments in the same second), merging formal reviews with plain-comment passes, ordering, and helper shadowing. `tests/test_wt_api.py` covers checked API reads. `tests/test_wt_merge.py` drives the `wt merge` gate end to end against a fake `tea`: malformed timing settings, draft refusal, failed CI log tail, no Actions run, refused merge, and a merge that succeeded despite a failed request. Pre-commit hooks passed, including the full pytest suite, ruff and `check_docs.py`. - [x] `scripts/wt review`, tested on live PRs: - `list`/`show 176` found the plain-comment pass `c3198`. - On this PR, `post` rejected a body without the header, posted a body with backticks, quotes and non-ASCII characters as `r5`, and `list` and `show` read it back. I deleted the test review afterwards. - [x] Forgejo refuses `APPROVED` ("approve your own pull is not allowed") and `REQUEST_CHANGES` ("reject your own pull is not allowed") from the PR author, so reviews are posted as `COMMENT`. - [x] `wt merge` refused a draft (#185), a closed PR (#175) and a non-numeric argument. - [x] I ran the CI gate against real commits: - #176's head (success) returned and would merge. - On Forgejo 16, old commit `821cf66` (failed run 278, whose job timed out and was cancelled) printed 60 log lines plus the full-log path, then stopped. - A head with no Actions run fails after the start grace with an explanatory message. - The final merge call remains pending until this PR completes its review cycle. ## Checklist - [x] Formal-review convention, `scripts/wt review`, `scripts/wt merge`, docs. - [x] Forgejo upgraded 8.0.3 → 16.0.5 on 2026-09-29. CI passed on v16 (`cdae9a0`). First-pass fixes at `3cb802b` and `0773d27`, and maintainer-requested `wt review` at `8a84119`: full pytest suite and pre-commit hooks passed; Ruff and docs checks passed. - [x] PR #176 completed its review cycle and merged. - [x] Second pass (r7) fixed in `90ebb94`; third pass (r8) fixed in `7950c10`, every finding including P3s. - [x] Fourth pass (r9): 1 P2 (same-second comment crash) and 6 P3s, all fixed in `00a07a6`. - [x] Fifth pass (r10): only P3s (settings check bypass, elapsed-time wait, description), fixed in `dc646c4`. - [ ] Merge through `scripts/wt merge 185`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
docs(agents): post review passes as formal PR reviews
All checks were successful
CI / lint-and-test (pull_request) Successful in 2m25s
233fa84446
Record each review pass as a formal Forgejo PR review whose body opens
with a fixed "## Code review, pass N (...)" line, and document how to
post it and how to find the latest pass from a compact one-line-per-review
index before fetching just that review's body. This keeps agents from
reading every PR comment to locate the pass they must address.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs(agents): script review passes and PR merges
All checks were successful
CI / lint-and-test (pull_request) Successful in 2m25s
3ba8d2f7d6
Replace the hand-typed tea api commands with scripts: scripts/review.py
posts a review pass as a formal COMMENT review (checking its header line),
lists one line per pass, and prints only the latest pass's body; it also
finds passes posted as plain comments before this convention.
scripts/wt merge <N> merges a PR as '<title> (#N)', deletes its branch,
prunes merged worktrees and fast-forwards the main checkout. The docs now
point at the scripts instead of spelling the commands out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gabogg changed title from WIP: docs(agents): post review passes as formal PR reviews to chore(agents): script review passes and PR merges 2026-09-29 14:19:20 +00:00
feat(wt): gate wt merge on the PR's CI status
All checks were successful
CI / lint-and-test (pull_request) Successful in 2m27s
fcc961f6e5
wt merge now waits for every commit status on the PR head to settle
(up to WT_CI_TIMEOUT, default 30 min), prints the last 60 lines of each
failed job's log (full log kept in a temp file) and stops on failure, and
only merges on success, pinning the merge to the commit CI passed on via
head_commit_id. Forgejo 8 lacks the Actions API, so logs come from the
web UI's raw log route.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs(agents): note Forgejo 15 still lacks a job-log API
All checks were successful
CI / lint-and-test (pull_request) Successful in 2m26s
7e7b107189
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(wt): read failed CI job logs through the Forgejo 16 Actions API
All checks were successful
CI / lint-and-test (pull_request) Successful in 2m29s
cdae9a0a75
Forgejo 16 no longer serves the web raw-log route the CI gate used
(/actions/runs/<index>/jobs/<n>/logs now 404s). Find the commit's failed
runs with /actions/runs?head_sha=, list their jobs, and fetch each
non-successful job's log from /actions/jobs/<id>/logs. Cancelled jobs
(e.g. timeouts) are reported too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gabogg left a comment

Code review, pass 1 (origin/master...cdae9a0, spec: maintainer requests of 2026-09-29, no issue)

Result: no P1s, 4 P2s (2 Standards, 2 Spec) and 19 P3s (12 Standards, 7 Spec). This is the first pass, so every finding gets fixed on the branch.

Verification: ruff clean, bash -n scripts/wt OK, the 3 tests in tests/test_review_script.py pass, both scripts tracked as 100755. Checked read-only on the live server: review.py list/show 176, list 185, and ci_gate on the failed commit 821cf66 (60 log lines plus the full-log path).

One fact underlies several findings: tea api exits 0 on HTTP errors. A missing PR returns {"message":…,"errors":[]} with exit code 0.

The spec is the maintainer's requests from the working session, quoted word for word; this PR has no issue.

Standards

P2

  1. cmd_merge hides Forgejo's error (scripts/wt:310). The merge POST's output goes to >/dev/null, so a head_commit_id mismatch or "try again later" shows up only as the generic "did not merge" (wt:313). Capture the body and print its message, as review.py tea_api does.
  2. ci_gate mistakes errors for "not reported yet" (scripts/wt:250-253). An API error body has no statuses, so it maps to none. A bad sha or auth failure then waits the full 30 minutes. Treat a body with an errors key as fatal.

P3
3. Duplicated Code (judgement). wt has five inline python3 -c JSON snippets, and none checks for an error body as review.py:76 does. Extract one helper, e.g. api_json <endpoint> <expr>.
4. Silent failures in loops (wt:275-277, 281-284). Under set -e, errors inside for run in $(…) and < <(…) are ignored. A KeyError, or finding no runs, prints no logs and still reports "CI failed". Print a note when nothing was found.
5. Doc/code mismatch (wt:277 vs forgejo-cli.md:73). Runs are kept only when their status is failure|error, so the jobs of a cancelled run never print, although the docs say cancelled jobs are shown.
6. warning state not handled (wt:257). It polls until the timeout.
7. Misleading message (wt:305). mergeable=False can be transient after a push, but the message says "has merge conflicts".
8. Draft check (wt:304). Only the WIP: prefix is recognized; [WIP] is missed.
9. tea_api tracebacks (review.py:72-75). When tea is missing, not logged in, or returns non-JSON, the user gets a raw CalledProcessError/JSONDecodeError traceback instead of a review: message.
10. Exit style (review.py:96/131). main returns None and calls sys.exit(__doc__), so -h exits 1. The sibling check_docs.py returns int and ends with raise SystemExit(main()).
11. Unchecked comment refs (review.py:91-92). A c<id> ref isn't checked against PR N, so show 42 c<another PR's comment> succeeds.
12. Pending reviews crash (review.py:52). r["submitted_at"][:16] raises a TypeError for a pending review whose submitted_at is null.
13. Test coverage (tests/test_review_script.py). Only the pure functions are covered: not argv validation, bad refs, the header check on post, or tea_api errors. A monkeypatched tea_api would test main offline (AGENTS.md §3: mock external endpoints).
14. Version trivia (forgejo-cli.md:73). Why tea actions runs logs fails could go stale. Move it to a footnote.
Note: no hard violations of documented standards. The three docs agree on wt merge, prune and running from the main checkout.

Spec

P2
16. (a) list can't tell whether the latest pass has been addressed. Spec: "so you know how to pick the exact last review that needs to be addressed". entries() (review.py:46-63) drops fix replies. On #176, list shows latest pass: c3229 with nothing to indicate whether a reply answered it. The docs say to compare the "reviewed commit" with the head (forgejo-cli.md:61), but:
- plain-comment passes print - as their commit (review.py:59);
- a formal review's commit_id is the head at posting time, not necessarily the SHA that was reviewed.

Fix: parse the reviewed SHA from the header line, and/or mark a pass that has a newer non-pass comment (a fix reply).
  1. (c) A PR with no CI waits 30 minutes, then fails. Spec: "if it is still running to wait". ci.yml only triggers on pull_request: branches: [master], so a PR based on another branch never gets a status, yet ci_gate treats none as running (wt ~250-262). Fail fast when no Actions run exists for the SHA, or document the limit.

P3
18. (a) The script still downloads every comment. Spec: "instead of getting all the comments you get the reviews". fetch_entries downloads every PR comment's full body on each list/show (review.py:83). What the agent reads is small, but the network fetch isn't.
19. (c) warning state spins until the timeout (the same issue as #6).
20. (c) A fully cancelled run prints no log (the same root cause as #5). The gate dies with no log.
21. (c) A refused merge hides the reason (the same issue as #1).
22. (c) A transient mergeable=false is reported as "has merge conflicts" (the same issue as #7).
23. Stale PR description.
- "Merge after PR #176 finishes its review cycle" is still unchecked.
- "The final merge call runs for the first time when this PR or #176 merges" is wrong now: #176 merged the old way.
24. Stale doc pointer (forgejo-cli.md:35). It still recommends tea pr 42 --comments for reading a PR, which is the full comment dump the spec wanted agents to avoid for reviews. Point PR reviews to review.py.

Checked and OK: failure logs print (60 lines plus the full-log path). Success returns. The merge is pinned with head_commit_id. merge lives in wt, not a separate pr script. Review completeness is documented as unchecked. Scope creep: none significant.

Summary

  • Standards: 2 P2s and 12 P3s (items 3-14). The worst is that ci_gate treats API errors as "not reported yet" and waits 30 minutes.
  • Spec: 2 P2s and 7 P3s. The worst is that list can't tell whether the latest pass was already addressed.
  • Several Spec items repeat Standards items (19↔6, 20↔5, 21↔1, 22↔7). Each fix covers both.

🤖 Generated with Claude Code

## Code review, pass 1 (`origin/master...cdae9a0`, spec: maintainer requests of 2026-09-29, no issue) Result: **no P1s, 4 P2s (2 Standards, 2 Spec) and 19 P3s (12 Standards, 7 Spec).** This is the first pass, so every finding gets fixed on the branch. Verification: ruff clean, `bash -n scripts/wt` OK, the 3 tests in `tests/test_review_script.py` pass, both scripts tracked as 100755. Checked read-only on the live server: `review.py list/show 176`, `list 185`, and `ci_gate` on the failed commit 821cf66 (60 log lines plus the full-log path). One fact underlies several findings: **`tea api` exits 0 on HTTP errors.** A missing PR returns `{"message":…,"errors":[]}` with exit code 0. The spec is the maintainer's requests from the working session, quoted word for word; this PR has no issue. ## Standards **P2** 1. **`cmd_merge` hides Forgejo's error (`scripts/wt:310`).** The merge POST's output goes to `>/dev/null`, so a `head_commit_id` mismatch or "try again later" shows up only as the generic "did not merge" (wt:313). Capture the body and print its `message`, as `review.py` `tea_api` does. 2. **`ci_gate` mistakes errors for "not reported yet" (`scripts/wt:250-253`).** An API error body has no `statuses`, so it maps to `none`. A bad sha or auth failure then waits the full 30 minutes. Treat a body with an `errors` key as fatal. **P3** 3. **Duplicated Code (judgement).** `wt` has five inline `python3 -c` JSON snippets, and none checks for an error body as `review.py:76` does. Extract one helper, e.g. `api_json <endpoint> <expr>`. 4. **Silent failures in loops (wt:275-277, 281-284).** Under `set -e`, errors inside `for run in $(…)` and `< <(…)` are ignored. A `KeyError`, or finding no runs, prints no logs and still reports "CI failed". Print a note when nothing was found. 5. **Doc/code mismatch (wt:277 vs forgejo-cli.md:73).** Runs are kept only when their status is `failure|error`, so the jobs of a cancelled run never print, although the docs say cancelled jobs are shown. 6. **`warning` state not handled (wt:257).** It polls until the timeout. 7. **Misleading message (wt:305).** `mergeable=False` can be transient after a push, but the message says "has merge conflicts". 8. **Draft check (wt:304).** Only the `WIP:` prefix is recognized; `[WIP]` is missed. 9. **`tea_api` tracebacks (review.py:72-75).** When tea is missing, not logged in, or returns non-JSON, the user gets a raw `CalledProcessError`/`JSONDecodeError` traceback instead of a `review:` message. 10. **Exit style (review.py:96/131).** `main` returns `None` and calls `sys.exit(__doc__)`, so `-h` exits 1. The sibling `check_docs.py` returns `int` and ends with `raise SystemExit(main())`. 11. **Unchecked comment refs (review.py:91-92).** A `c<id>` ref isn't checked against PR N, so `show 42 c<another PR's comment>` succeeds. 12. **Pending reviews crash (review.py:52).** `r["submitted_at"][:16]` raises a `TypeError` for a pending review whose `submitted_at` is null. 13. **Test coverage (tests/test_review_script.py).** Only the pure functions are covered: not argv validation, bad refs, the header check on `post`, or `tea_api` errors. A monkeypatched `tea_api` would test `main` offline (AGENTS.md §3: mock external endpoints). 14. **Version trivia (forgejo-cli.md:73).** Why `tea actions runs logs` fails could go stale. Move it to a footnote. **Note:** no hard violations of documented standards. The three docs agree on `wt merge`, prune and running from the main checkout. ## Spec **P2** 16. **(a) `list` can't tell whether the latest pass has been addressed.** Spec: *"so you know how to pick the exact last review that needs to be addressed"*. `entries()` (review.py:46-63) drops fix replies. On #176, `list` shows `latest pass: c3229` with nothing to indicate whether a reply answered it. The docs say to compare the "reviewed commit" with the head (forgejo-cli.md:61), but: - plain-comment passes print `-` as their commit (review.py:59); - a formal review's `commit_id` is the head at posting time, not necessarily the SHA that was reviewed. Fix: parse the reviewed SHA from the header line, and/or mark a pass that has a newer non-pass comment (a fix reply). 17. **(c) A PR with no CI waits 30 minutes, then fails.** Spec: *"if it is still running to wait"*. `ci.yml` only triggers on `pull_request: branches: [master]`, so a PR based on another branch never gets a status, yet `ci_gate` treats `none` as running (wt ~250-262). Fail fast when no Actions run exists for the SHA, or document the limit. **P3** 18. **(a) The script still downloads every comment.** Spec: *"instead of getting all the comments you get the reviews"*. `fetch_entries` downloads every PR comment's full body on each `list`/`show` (review.py:83). What the agent reads is small, but the network fetch isn't. 19. **(c) `warning` state** spins until the timeout (the same issue as #6). 20. **(c) A fully cancelled run prints no log** (the same root cause as #5). The gate dies with no log. 21. **(c) A refused merge hides the reason** (the same issue as #1). 22. **(c) A transient `mergeable=false`** is reported as "has merge conflicts" (the same issue as #7). 23. **Stale PR description.** - "Merge after PR #176 finishes its review cycle" is still unchecked. - "The final merge call runs for the first time when this PR or #176 merges" is wrong now: #176 merged the old way. 24. **Stale doc pointer (forgejo-cli.md:35).** It still recommends `tea pr 42 --comments` for reading a PR, which is the full comment dump the spec wanted agents to avoid for reviews. Point PR reviews to `review.py`. **Checked and OK:** failure logs print (60 lines plus the full-log path). Success returns. The merge is pinned with `head_commit_id`. `merge` lives in `wt`, not a separate `pr` script. Review completeness is documented as unchecked. Scope creep: none significant. **Summary** - **Standards:** 2 P2s and 12 P3s (items 3-14). The worst is that `ci_gate` treats API errors as "not reported yet" and waits 30 minutes. - **Spec:** 2 P2s and 7 P3s. The worst is that `list` can't tell whether the latest pass was already addressed. - Several Spec items repeat Standards items (19↔6, 20↔5, 21↔1, 22↔7). Each fix covers both. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(agents): address first review of PR 185
All checks were successful
CI / lint-and-test (pull_request) Successful in 2m28s
3cb802b35d
Author
Owner

First-pass findings addressed — 3cb802b

All P2 and P3 findings from review r6 are addressed on the PR branch.

  • Standards 1–2; Spec 21: The merge gate now reads checked API JSON. Forgejo's refusal message is shown, and an API error while reading CI fails immediately.
  • Standards 3–4: wt_api.py centralizes checked JSON reads for the worktree script. Failed run and job lookup errors stop the gate; empty results print an explicit note.
  • Standards 5–8; Spec 19–20, 22: Cancelled and warning runs are handled, draft titles with [WIP] are refused, and an unsettled mergeable=false is reported without asserting a conflict.
  • Standards 9–13: review.py reports tea and JSON errors without tracebacks, supports a successful help exit, checks that legacy comment refs belong to the PR, handles pending reviews, and has offline tests for those paths.
  • Standards 14; Spec 24: The CLI guide moves version-specific Actions behavior to a note and directs PR review readers to review.py.
  • Spec 16: list reads the reviewed SHA from each pass header and marks passes with a later fix reply.
  • Spec 17: No Actions run for the head SHA now fails immediately rather than waiting for the timeout.
  • Spec 18: For PRs with formal reviews, comment fetching is limited to the period since the first formal pass; legacy PRs with no formal review still search old comments.
  • Spec 23: The PR description and checklist now reflect #176's merge and the current review gate.

Verification: 464 tests passed, Ruff lint and format checks passed, bash -n scripts/wt passed, and check_docs.py passed. The pre-commit hooks passed on 3cb802b. Live reads confirmed PR #185's review listing, PR #176's legacy comment lookup, and Forgejo CI status parsing.

Please run the second review pass on 3cb802b.

## First-pass findings addressed — 3cb802b All P2 and P3 findings from review r6 are addressed on the PR branch. - **Standards 1–2; Spec 21:** The merge gate now reads checked API JSON. Forgejo's refusal message is shown, and an API error while reading CI fails immediately. - **Standards 3–4:** `wt_api.py` centralizes checked JSON reads for the worktree script. Failed run and job lookup errors stop the gate; empty results print an explicit note. - **Standards 5–8; Spec 19–20, 22:** Cancelled and warning runs are handled, draft titles with `[WIP]` are refused, and an unsettled `mergeable=false` is reported without asserting a conflict. - **Standards 9–13:** `review.py` reports tea and JSON errors without tracebacks, supports a successful help exit, checks that legacy comment refs belong to the PR, handles pending reviews, and has offline tests for those paths. - **Standards 14; Spec 24:** The CLI guide moves version-specific Actions behavior to a note and directs PR review readers to `review.py`. - **Spec 16:** `list` reads the reviewed SHA from each pass header and marks passes with a later fix reply. - **Spec 17:** No Actions run for the head SHA now fails immediately rather than waiting for the timeout. - **Spec 18:** For PRs with formal reviews, comment fetching is limited to the period since the first formal pass; legacy PRs with no formal review still search old comments. - **Spec 23:** The PR description and checklist now reflect #176's merge and the current review gate. Verification: 464 tests passed, Ruff lint and format checks passed, `bash -n scripts/wt` passed, and `check_docs.py` passed. The pre-commit hooks passed on 3cb802b. Live reads confirmed PR #185's review listing, PR #176's legacy comment lookup, and Forgejo CI status parsing. Please run the second review pass on 3cb802b.
fix(review): retain legacy passes before formal pass two
All checks were successful
CI / lint-and-test (pull_request) Successful in 2m22s
0773d27499
Author
Owner

Follow-up to the first-pass fix reply: 0773d27 preserves old plain-comment passes when a PR later gains formal reviews, while still limiting comment fetches for PRs that begin with formal pass 1. The new migration test and full pre-commit suite pass. Please run the second review pass on 0773d27.

Follow-up to the first-pass fix reply: 0773d27 preserves old plain-comment passes when a PR later gains formal reviews, while still limiting comment fetches for PRs that begin with formal pass 1. The new migration test and full pre-commit suite pass. Please run the second review pass on 0773d27.
Author
Owner

Maintainer request: fold the review commands into wt

Please add this alongside the pass-1 fixes (review r6). Agents should have one entry point for PR workflow commands, scripts/wt, as with merge, rather than a second script whose name ends in .py.

What to change

  • Add scripts/wt review with three subcommands that behave as scripts/review.py does today, including every pass-1 fix:
    • scripts/wt review list <N>
    • scripts/wt review show <N> [<ref>]
    • scripts/wt review post <N> <file>
  • Keep the logic in Python. wt review … should call the module, e.g. python3 "$ROOT/scripts/review.py" "$@" or python3 -m scripts.review, so the tests keep importing it (from scripts import review). The module can stay at scripts/review.py. Agents shouldn't need to call it directly.
  • Update the docs to use only the wt review … form:
    • docs/agents/forgejo-cli.md: the Review passes section and the command table.
    • docs/standards/git-and-workflow.md: the review-pass rule.
    • docs/agents/workspaces.md: next to wt merge.
  • List review in scripts/wt --help, and have usage errors mention wt review rather than review.py.

Acceptance

  • scripts/wt review list|show|post works from any worktree, like tea, which infers the repo from origin.
  • No doc tells agents to run scripts/review.py directly.
  • The existing tests pass. Add one test or smoke check proving wt review passes its arguments through, e.g. scripts/wt review with no arguments prints the usage and exits non-zero.

🤖 Generated with Claude Code

## Maintainer request: fold the review commands into `wt` Please add this alongside the pass-1 fixes (review `r6`). Agents should have one entry point for PR workflow commands, `scripts/wt`, as with `merge`, rather than a second script whose name ends in `.py`. **What to change** - **Add `scripts/wt review` with three subcommands** that behave as `scripts/review.py` does today, including every pass-1 fix: - `scripts/wt review list <N>` - `scripts/wt review show <N> [<ref>]` - `scripts/wt review post <N> <file>` - **Keep the logic in Python.** `wt review …` should call the module, e.g. `python3 "$ROOT/scripts/review.py" "$@"` or `python3 -m scripts.review`, so the tests keep importing it (`from scripts import review`). The module can stay at `scripts/review.py`. Agents shouldn't need to call it directly. - **Update the docs to use only the `wt review …` form:** - `docs/agents/forgejo-cli.md`: the Review passes section and the command table. - `docs/standards/git-and-workflow.md`: the review-pass rule. - `docs/agents/workspaces.md`: next to `wt merge`. - **List `review` in `scripts/wt --help`,** and have usage errors mention `wt review` rather than `review.py`. **Acceptance** - `scripts/wt review list|show|post` works from any worktree, like `tea`, which infers the repo from `origin`. - No doc tells agents to run `scripts/review.py` directly. - The existing tests pass. Add one test or smoke check proving `wt review` passes its arguments through, e.g. `scripts/wt review` with no arguments prints the usage and exits non-zero. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(wt): expose review commands through worktree helper
All checks were successful
CI / lint-and-test (pull_request) Successful in 2m32s
8a84119e5b
Author
Owner

Implemented the maintainer request in 8a84119. scripts/wt review list|show|post now forwards to the existing Python module; usage and help name wt review. Updated the CLI, workflow, and worktree docs, with no remaining doc directing agents to run scripts/review.py. Added an argument-forwarding smoke test from a nested directory; a live scripts/wt review list 185 read passed. The full pre-commit suite passed. Please review the updated head 8a84119 in the second pass.

Implemented the maintainer request in 8a84119. `scripts/wt review list|show|post` now forwards to the existing Python module; usage and help name `wt review`. Updated the CLI, workflow, and worktree docs, with no remaining doc directing agents to run `scripts/review.py`. Added an argument-forwarding smoke test from a nested directory; a live `scripts/wt review list 185` read passed. The full pre-commit suite passed. Please review the updated head 8a84119 in the second pass.
docs(agents): spell out wt review commands
All checks were successful
CI / lint-and-test (pull_request) Successful in 2m25s
ea48f682f1
Author
Owner

Agent instruction clarification pushed at ea48f68: docs/agents/workspaces.md now spells out all three scripts/wt review commands. The PR already updates docs/agents/forgejo-cli.md and docs/standards/git-and-workflow.md to use wt review; no agent doc directs agents to run scripts/review.py. Documentation check and the full pre-commit suite passed. Please use ea48f68 for the second pass.

Agent instruction clarification pushed at ea48f68: docs/agents/workspaces.md now spells out all three `scripts/wt review` commands. The PR already updates docs/agents/forgejo-cli.md and docs/standards/git-and-workflow.md to use `wt review`; no agent doc directs agents to run `scripts/review.py`. Documentation check and the full pre-commit suite passed. Please use ea48f68 for the second pass.
gabogg changed title from chore(agents): script review passes and PR merges to docs(agents): post review passes as formal PR reviews 2026-09-29 17:49:13 +00:00
fix(review): address second review of PR 185
All checks were successful
CI / lint-and-test (pull_request) Successful in 2m34s
90ebb94a8d
- review.py reads the reviewed SHA from pass headers with or without
  backticks, and recognises fix replies by a documented heading
  (`## Pass <N> fixes (<SHA>)`) plus the older wordings in use, including
  PR #176's "First review pass addressed".
- Fix replies are matched against full timestamps, so a reply in the
  same minute as its pass counts; `show` prints the ref only once the
  body was read.
- review.py and wt now share one checked tea API helper (wt_api.api);
  the merge POST goes through it instead of an inline JSON parse.
- wt_api dispatches through a field map with names that say what they
  print (failed_runs, run_count, failed_jobs); wt's api_json becomes
  api_fields, and both helpers run as `python3 -m scripts.<module>`.
- wt merge allows a new head commit a minute (WT_CI_START_GRACE) to get
  its Actions run before deciding CI does not run.
- Docs name the fix-reply heading and drop fetch internals.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gabogg left a comment

Code review, pass 2 (origin/master...ea48f68, spec: maintainer requests of 2026-09-29, no issue)

Result: no P1s, 3 P2s (1 Standards, 2 Spec; Standards 1 and Spec B are the same issue) and 9 P3s (6 Standards, 3 Spec). The maintainer asked that every finding on this pass be fixed on the branch rather than deferred, because all future PRs rely on this tooling.

Verification: 14 tests pass in tests/test_review_script.py and tests/test_wt_api.py. Ruff check and format are clean, and bash -n scripts/wt is OK. Read-only live checks:

  • wt review list 185 and list 176 work.
  • show 176 c3229 works, and show 185 c3229 is refused.
  • An extra argument to show exits 2.
  • wt_api.py pr on a missing PR exits 1.
  • wt_api.py status and runs_any read correctly.

Standards

Pass-1 fixes: 1–2 and 4–14 are fixed. #3 is partial: the merge response is still parsed inline (scripts/wt:316).

P2

  1. The fix-reply heading is undocumented (scripts/review.py:26 vs forgejo-cli.md:60, git-and-workflow.md:42). list marks a pass as answered only when a later comment matches FIX_REPLY, which accepts ## Fixes, ## First-pass findings addressed or ## Review fixes. The docs only say "a fix-reply heading", so a reply titled ## Pass 1 fixes goes undetected and the pass looks unanswered.

P3
2. Duplicated Code (judgement call). review.py:103-117 and wt_api.py:15-28 are two checked tea api wrappers that decide what counts as an error differently, so they will drift apart.
3. The inline merge-response parse at wt:316 checks only message, not errors.
4. Mysterious Name (judgement call).

  • api_json (wt:15) returns extracted fields, not JSON.
  • The runs kind returns only failed runs.
  • show dispatches on a kind-string if-chain.
  1. wt merge can fail right after a push. The none branch (wt:260-264) dies before Forgejo has created the Actions run.
  2. A fix reply in the same minute as its pass is missed. Timestamps are cut to the minute and compared with a strict > (review.py:78).
  3. File mode. wt_api.py has a shebang but is tracked as 100644, while review.py is 100755.

There are no hard violations of AGENTS.md or code-standards.md.

Spec

Maintainer wt review criteria, all 5 met:

  • It works from any worktree.
  • No doc points at scripts/review.py.
  • The passthrough test exists.
  • wt --help lists review.
  • Usage errors name wt review.

Pass-1 fixes: 17–24 are fixed. #16 is partial; both P2s below come from it.

P2

  • A. The reviewed SHA is only read when the header uses backticks (review.py:24). The documented header (git-and-workflow.md:42) has none. ## Code review, pass 2 (origin/master...ea48f68, spec #185) returns None, and list silently falls back to the posting-time commit_id.
  • B. The fix-reply heading is undocumented. This is the same issue as Standards 1: ## Second-pass fixes doesn't match.

P3

  • C. A reply in the same minute as its pass isn't counted (same as Standards 6).
  • D. show prints [ref] before reporting a bad ref.
  • E. forgejo-cli.md:60 explains fetch internals that agents don't need.

Summary

  • Standards: 1 P2 and 6 P3s. The worst is the undocumented fix-reply heading.
  • Spec: 2 P2s and 3 P3s. The worst is that a header written as documented loses its reviewed SHA.

🤖 Generated with Claude Code

## Code review, pass 2 (`origin/master...ea48f68`, spec: maintainer requests of 2026-09-29, no issue) Result: **no P1s, 3 P2s (1 Standards, 2 Spec; Standards 1 and Spec B are the same issue) and 9 P3s (6 Standards, 3 Spec).** The maintainer asked that every finding on this pass be fixed on the branch rather than deferred, because all future PRs rely on this tooling. Verification: 14 tests pass in `tests/test_review_script.py` and `tests/test_wt_api.py`. Ruff check and format are clean, and `bash -n scripts/wt` is OK. Read-only live checks: - `wt review list 185` and `list 176` work. - `show 176 c3229` works, and `show 185 c3229` is refused. - An extra argument to `show` exits 2. - `wt_api.py pr` on a missing PR exits 1. - `wt_api.py status` and `runs_any` read correctly. ## Standards **Pass-1 fixes:** 1–2 and 4–14 are fixed. #3 is partial: the merge response is still parsed inline (`scripts/wt:316`). **P2** 1. **The fix-reply heading is undocumented (`scripts/review.py:26` vs `forgejo-cli.md:60`, `git-and-workflow.md:42`).** `list` marks a pass as answered only when a later comment matches `FIX_REPLY`, which accepts `## Fixes`, `## First-pass findings addressed` or `## Review fixes`. The docs only say "a fix-reply heading", so a reply titled `## Pass 1 fixes` goes undetected and the pass looks unanswered. **P3** 2. **Duplicated Code (judgement call).** `review.py:103-117` and `wt_api.py:15-28` are two checked `tea api` wrappers that decide what counts as an error differently, so they will drift apart. 3. **The inline merge-response parse at `wt:316`** checks only `message`, not `errors`. 4. **Mysterious Name (judgement call).** - `api_json` (`wt:15`) returns extracted fields, not JSON. - The `runs` kind returns only failed runs. - `show` dispatches on a kind-string if-chain. 5. **`wt merge` can fail right after a push.** The `none` branch (`wt:260-264`) dies before Forgejo has created the Actions run. 6. **A fix reply in the same minute as its pass is missed.** Timestamps are cut to the minute and compared with a strict `>` (`review.py:78`). 7. **File mode.** `wt_api.py` has a shebang but is tracked as 100644, while `review.py` is 100755. There are no hard violations of `AGENTS.md` or `code-standards.md`. ## Spec **Maintainer `wt review` criteria, all 5 met:** - It works from any worktree. - No doc points at `scripts/review.py`. - The passthrough test exists. - `wt --help` lists `review`. - Usage errors name `wt review`. **Pass-1 fixes:** 17–24 are fixed. #16 is partial; both P2s below come from it. **P2** - **A. The reviewed SHA is only read when the header uses backticks (`review.py:24`).** The documented header (`git-and-workflow.md:42`) has none. `## Code review, pass 2 (origin/master...ea48f68, spec #185)` returns None, and `list` silently falls back to the posting-time `commit_id`. - **B. The fix-reply heading is undocumented.** This is the same issue as Standards 1: `## Second-pass fixes` doesn't match. **P3** - **C.** A reply in the same minute as its pass isn't counted (same as Standards 6). - **D.** `show` prints `[ref]` before reporting a bad ref. - **E.** `forgejo-cli.md:60` explains fetch internals that agents don't need. ## Summary - **Standards:** 1 P2 and 6 P3s. The worst is the undocumented fix-reply heading. - **Spec:** 2 P2s and 3 P3s. The worst is that a header written as documented loses its reviewed SHA. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Owner

Pass 2 fixes (90ebb94)

Every finding from review pass 2 is fixed in 90ebb94, P3s included.

  • Spec A: REVIEWED_SHA accepts headers with or without backticks, including backticks around each side of the range. There is a parametrized test for each form.
  • Standards 1 / Spec B: git-and-workflow.md now fixes the reply's first line as ## Pass <N> fixes (<fix short SHA>), and forgejo-cli.md links to that rule. FIX_REPLY accepts that heading and the older wordings. One of those, #176's real ## First review pass addressed, was missed until now; list 176 now marks pass 1 as answered. Tests cover the matching and non-matching headings.
  • Standards 2–3: wt_api.api(endpoint, *args) is the single checked tea api call. review.tea_api wraps it, and the merge POST goes through it (api_fields quiet … -X POST -d), so the inline parse is gone.
  • Standards 4:
    • api_json is now api_fields.
    • The fields are failed_runs, run_count and failed_jobs.
    • The dispatch is a FIELDS map instead of an if-chain.
  • Standards 5: wt merge gives a new head commit WT_CI_START_GRACE (60s) to get its Actions run before failing.
  • Standards 6 / Spec C: fix replies are compared on full timestamps (>=). There is a test for both a same-minute reply and an earlier one.
  • Standards 7: both helpers are plain 100644 modules with no shebang, and wt runs them as python3 -m scripts.<module> from its own checkout.
  • Spec D: show reads the body before printing the ref. There is a test for this.
  • Spec E: the fetch internals are gone from forgejo-cli.md.

Verification:

  • 32 script tests pass.
  • Pre-commit passed: full pytest, ruff and the docs check.
  • Read-only live checks:
    • review list 185 and list 176.
    • A bad-ref show.
    • Each wt_api field.
    • A missing PR.
    • wt merge abc.

The maintainer has asked for a third pass on 90ebb94.

🤖 Generated with Claude Code

## Pass 2 fixes (90ebb94) Every finding from review pass 2 is fixed in 90ebb94, P3s included. - **Spec A:** `REVIEWED_SHA` accepts headers with or without backticks, including backticks around each side of the range. There is a parametrized test for each form. - **Standards 1 / Spec B:** `git-and-workflow.md` now fixes the reply's first line as `## Pass <N> fixes (<fix short SHA>)`, and `forgejo-cli.md` links to that rule. `FIX_REPLY` accepts that heading and the older wordings. One of those, #176's real `## First review pass addressed`, was missed until now; `list 176` now marks pass 1 as answered. Tests cover the matching and non-matching headings. - **Standards 2–3:** `wt_api.api(endpoint, *args)` is the single checked `tea api` call. `review.tea_api` wraps it, and the merge POST goes through it (`api_fields quiet … -X POST -d`), so the inline parse is gone. - **Standards 4:** - `api_json` is now `api_fields`. - The fields are `failed_runs`, `run_count` and `failed_jobs`. - The dispatch is a `FIELDS` map instead of an if-chain. - **Standards 5:** `wt merge` gives a new head commit `WT_CI_START_GRACE` (60s) to get its Actions run before failing. - **Standards 6 / Spec C:** fix replies are compared on full timestamps (`>=`). There is a test for both a same-minute reply and an earlier one. - **Standards 7:** both helpers are plain 100644 modules with no shebang, and `wt` runs them as `python3 -m scripts.<module>` from its own checkout. - **Spec D:** `show` reads the body before printing the ref. There is a test for this. - **Spec E:** the fetch internals are gone from `forgejo-cli.md`. **Verification:** - 32 script tests pass. - Pre-commit passed: full pytest, ruff and the docs check. - Read-only live checks: - `review list 185` and `list 176`. - A bad-ref `show`. - Each `wt_api` field. - A missing PR. - `wt merge abc`. The maintainer has asked for a third pass on 90ebb94. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
gabogg left a comment

Code review, pass 3 (origin/master...90ebb94, spec: maintainer requests of 2026-09-29, no issue)

Result: no P1s, 3 P2s (1 Standards, 2 Spec) and 8 P3s (5 Standards, 3 Spec); Spec P3-4 repeats Standards P2-1. The maintainer asked for every finding to be fixed, with further passes until only P3s remain.

Standards

Pass-2 fixes: 1–6 are fixed, verified with probes that include a fake tea for the merge-refusal path. 7 is fixed but caused P2-1.

P2

  1. The helpers load from the current directory, not from wt's own checkout (scripts/wt:16, a regression in 90ebb94).
    • Cause: python3 -m puts the cwd ahead of PYTHONPATH. scripts/ is a namespace package, so a scripts/review.py or scripts/wt_api.py in the cwd wins.
    • Reproduced: wt review --help run from a directory holding a decoy scripts/review.py printed SHADOWED.
    • Risk: wt merge N run from inside a PR's worktree uses that PR's unreviewed wt_api.py as the merge gate.
    • Fix: run the helpers with python3 -P -m, and add a test that runs from a directory with a decoy scripts/.

P3

  1. FIX_REPLY is too broad and ignores the pass number (review.py:29-32).
    • ## Fixes needed before merge, ## Review fixes still missing and ## Addressed all mark a pass as answered.
    • A late ## Pass 1 fixes posted after pass 2 marks pass 2 answered.
  2. Unexpected response shapes give a raw traceback (wt_api.py:79). An empty body, or a list on a dict-only field, raises an uncaught AttributeError. A missing key prints only 'state'.
  3. The waiting message overstates the wait (wt:268-275). During the start grace it says "waiting (up to 1800s)", then dies at 60s.
  4. Every merge failure is reported as a refusal (wt:323-324). A tea or network failure prints "Forgejo refused PR #N". If the server merged before tea timed out, prune is skipped.
  5. Smells (judgement calls):
    • Entry.submitted and Entry.at hold the same instant twice, and submitted_at or created_at is repeated.
    • quiet sits in FIELDS although it is a check, not a field.
    • failed_jobs uses an inline set next to NOT_SUCCEEDED.

There are no hard violations of AGENTS.md or code-standards.md.

Spec

Pass-2 fix claims: all verified, except "helpers run from its own checkout" (see P2-1 above). The 5 wt review criteria still hold.

P2

  1. post accepts headers that list can't read a reviewed SHA from (review.py:174 vs :27).
    • Examples: a two-dot range, a bare SHA, an uppercase SHA, or … instead of ....
    • list then falls back to commit_id, which is the posting-time head: for r7 that is 90ebb94, not the reviewed ea48f68.
    • Spec: "so you know how to pick the exact last review that needs to be addressed".
    • Fix: post rejects a header with no parseable SHA and gives a readable error.
  2. There is no documented way to read the fix reply.
    • list prints only | fix reply posted, without the reply's ref.
    • forgejo-cli.md:35 lost --comments, and tea pr 185 without it printed 0 comments.
    • Fix: print the reply's ref in list, and restore --comments in the table.

P3
3. A fix reply isn't matched to its pass number (same as Standards P3-1). It also misses ## Pass-3 fixes and ## Pass 3 fix reply.
4. Helpers run from the wrong checkout (same as Standards P2-1).
5. The PR description is stale.

  • It says CI "fails immediately" when there is no Actions run; there is now a 60s grace.
  • The checklist still shows pass 2 as open.
  • The claim that plain-comment passes need no migration holds only for exact headers: #175's ## Review pass 1: … is not listed, and #173 and #166 show none. All three are merged, so this is harmless, but the claim should be reworded.

Also noted (pre-existing): run outside any git repo, wt ignores git's error and sets ROOT=/.

Live checks: list on #185, #176, #175, #173, #166, #168 and #169; show with good refs, bad refs and a missing PR.

Summary

  • Standards: 1 P2 and 5 P3s. The worst is that the helpers load from the cwd.
  • Spec: 2 P2s and 3 P3s. The worst is that post accepts headers whose reviewed SHA list can't read.

🤖 Generated with Claude Code

## Code review, pass 3 (`origin/master...90ebb94`, spec: maintainer requests of 2026-09-29, no issue) Result: **no P1s, 3 P2s (1 Standards, 2 Spec) and 8 P3s (5 Standards, 3 Spec); Spec P3-4 repeats Standards P2-1.** The maintainer asked for every finding to be fixed, with further passes until only P3s remain. ## Standards **Pass-2 fixes:** 1–6 are fixed, verified with probes that include a fake `tea` for the merge-refusal path. 7 is fixed but caused P2-1. **P2** 1. **The helpers load from the current directory, not from `wt`'s own checkout (`scripts/wt:16`, a regression in 90ebb94).** - **Cause:** `python3 -m` puts the cwd ahead of `PYTHONPATH`. `scripts/` is a namespace package, so a `scripts/review.py` or `scripts/wt_api.py` in the cwd wins. - **Reproduced:** `wt review --help` run from a directory holding a decoy `scripts/review.py` printed `SHADOWED`. - **Risk:** `wt merge N` run from inside a PR's worktree uses that PR's unreviewed `wt_api.py` as the merge gate. - **Fix:** run the helpers with `python3 -P -m`, and add a test that runs from a directory with a decoy `scripts/`. **P3** 1. **`FIX_REPLY` is too broad and ignores the pass number (`review.py:29-32`).** - `## Fixes needed before merge`, `## Review fixes still missing` and `## Addressed` all mark a pass as answered. - A late `## Pass 1 fixes` posted after pass 2 marks pass 2 answered. 2. **Unexpected response shapes give a raw traceback (`wt_api.py:79`).** An empty body, or a list on a dict-only field, raises an uncaught `AttributeError`. A missing key prints only `'state'`. 3. **The waiting message overstates the wait (`wt:268-275`).** During the start grace it says "waiting (up to 1800s)", then dies at 60s. 4. **Every merge failure is reported as a refusal (`wt:323-324`).** A tea or network failure prints "Forgejo refused PR #N". If the server merged before tea timed out, prune is skipped. 5. **Smells (judgement calls):** - `Entry.submitted` and `Entry.at` hold the same instant twice, and `submitted_at or created_at` is repeated. - `quiet` sits in `FIELDS` although it is a check, not a field. - `failed_jobs` uses an inline set next to `NOT_SUCCEEDED`. There are no hard violations of `AGENTS.md` or `code-standards.md`. ## Spec **Pass-2 fix claims:** all verified, except "helpers run from its own checkout" (see P2-1 above). The 5 `wt review` criteria still hold. **P2** 1. **`post` accepts headers that `list` can't read a reviewed SHA from (`review.py:174` vs `:27`).** - Examples: a two-dot range, a bare SHA, an uppercase SHA, or `…` instead of `...`. - `list` then falls back to `commit_id`, which is the posting-time head: for r7 that is 90ebb94, not the reviewed ea48f68. - Spec: *"so you know how to pick the exact last review that needs to be addressed"*. - **Fix:** `post` rejects a header with no parseable SHA and gives a readable error. 2. **There is no documented way to read the fix reply.** - `list` prints only `| fix reply posted`, without the reply's ref. - `forgejo-cli.md:35` lost `--comments`, and `tea pr 185` without it printed 0 comments. - **Fix:** print the reply's ref in `list`, and restore `--comments` in the table. **P3** 3. **A fix reply isn't matched to its pass number** (same as Standards P3-1). It also misses `## Pass-3 fixes` and `## Pass 3 fix reply`. 4. **Helpers run from the wrong checkout** (same as Standards P2-1). 5. **The PR description is stale.** - It says CI "fails immediately" when there is no Actions run; there is now a 60s grace. - The checklist still shows pass 2 as open. - The claim that plain-comment passes need no migration holds only for exact headers: #175's `## Review pass 1: …` is not listed, and #173 and #166 show none. All three are merged, so this is harmless, but the claim should be reworded. **Also noted (pre-existing):** run outside any git repo, `wt` ignores git's error and sets `ROOT=/`. **Live checks:** `list` on #185, #176, #175, #173, #166, #168 and #169; `show` with good refs, bad refs and a missing PR. ## Summary - **Standards:** 1 P2 and 5 P3s. The worst is that the helpers load from the cwd. - **Spec:** 2 P2s and 3 P3s. The worst is that `post` accepts headers whose reviewed SHA `list` can't read. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(review): address third review of PR 185
All checks were successful
CI / lint-and-test (pull_request) Successful in 2m34s
7950c103b3
- wt runs its Python helpers with `python3 -P -m`, so a scripts/ package
  in the cwd (e.g. another PR's worktree) can no longer shadow them; a
  test runs wt from a directory holding a decoy scripts/review.py.
- wt stops with a clear error outside a git checkout instead of
  resolving ROOT to /.
- `review post` refuses a pass header whose reviewed SHA `list` cannot
  read, and names the expected format instead of printing the regex.
- Fix replies are tied to their pass: `## Pass <N> fixes` (plus the
  ordinal wordings used on #176 and #185) answers pass N only when
  posted after it. Loose headings such as `## Fixes needed before merge`
  no longer count. `list` prints the reply's ref so it can be read
  with `review show`.
- Entry keeps one timestamp; the display date is derived from it.
- wt_api: `check` validates a response without printing and is no
  longer listed as a field; unexpected response shapes report a short
  error instead of a traceback; job and run status sets are named.
- wt merge: the waiting message shows the start grace while no Actions
  run exists; a failed merge request falls through to the PR state, so
  a merge that happened anyway still prunes. New end-to-end tests drive
  the merge gate against a fake tea.
- Tests that spawn git drop the GIT_* variables a git hook exports, so
  a pre-commit run cannot point them at the real repository.
- forgejo-cli.md restores `tea pr 42 --comments` and documents the
  reply ref.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Owner

Pass 3 fixes (7950c10)

Every finding from review pass 3 (r8) is fixed in 7950c10, P3s included.

  • Standards P2-1 / Spec P3-4 (helper shadowing): wt runs its helpers with python3 -P -m scripts.<module>. A new test runs wt review --help from a git repo that holds a decoy scripts/review.py, and the decoy no longer runs. The pre-existing quirk is fixed too: outside any git checkout, wt now stops with "run it from inside a checkout" instead of using ROOT=/ (tested).
  • Spec P2-1 (headers post accepts but list can't read): post now also requires a readable reviewed SHA and names the expected format instead of printing the regex. Tests reject the two-dot range, bare SHA, uppercase SHA and … headers. A live post of a two-dot header was refused.
  • Spec P2-2 (fix reply unreadable): list now ends a pass's line with fix reply c<id>, readable with wt review show N c<id>. forgejo-cli.md restores tea pr 42 --comments and notes that non-interactive runs omit comments without it.
  • Standards P3-1 / Spec P3-3 (FIX_REPLY too broad, not tied to its pass):
    • A reply is now ## Pass <N> fixes, ## Pass-<N> fixes or ## Pass <N> fix reply, plus the ordinal wordings used on #176 and #185.
    • It answers pass N only if it was posted at or after that pass.
    • ## Fixes needed before merge, ## Review fixes still missing, ## Addressed, and a late ## Pass 1 fixes after pass 2 no longer count. Each is tested.
    • Live: list 185 shows r6 → c3239 and r7 → c3334, and list 176 shows c3198 → c3212.
  • Standards P3-2 (traceback on odd responses): wt_api reports "unexpected response (missing 'ref')" or "unexpected response shape" instead of a traceback. Tests cover an empty body, a list, a bad element and a missing key.
  • Standards P3-3 (waiting message): while no Actions run exists, it prints CI not started …; waiting (up to 60s), and the timeout message names the start grace.
  • Standards P3-4 (failure reported as refusal): a failed merge request now prints "failed; checking whether it merged", and the PR's merged state decides. A merge that happened anyway still prunes.
  • Standards P3-5 (smells):
    • Entry keeps one timestamp and derives the display date from it.
    • check is no longer in FIELDS.
    • The job and run status sets are named (JOB_PASSED, RUN_NOT_SUCCEEDED).
  • Spec P3-5 (stale PR description): updated for the start grace, the merge-state check, the plain-comment scope (#175, #173 and #166 aren't listed; all merged) and the checklist.
  • New coverage: tests/test_wt_merge.py drives wt merge end to end against a fake tea on PATH:
    • draft refusal;
    • a failed CI log tail;
    • no Actions run after the grace;
    • a refused merge, with its head_commit_id and title checked;
    • a merge that succeeded despite a failed request.
  • Found while committing: tests that spawn git inherited the pre-commit hook's GIT_DIR. A git init in a test then rewrote the main checkout's core.bare (now restored). An autouse fixture drops GIT_* variables, and the tests pass with GIT_DIR set.

Verification: 51 script tests pass, and pre-commit passed (full pytest suite, ruff, docs check). Live read-only checks covered list 185 and list 176, show 185 c3334, and a rejected post.

Please run the fourth pass on 7950c10.

🤖 Generated with Claude Code

## Pass 3 fixes (7950c10) Every finding from review pass 3 (r8) is fixed in 7950c10, P3s included. - **Standards P2-1 / Spec P3-4 (helper shadowing):** `wt` runs its helpers with `python3 -P -m scripts.<module>`. A new test runs `wt review --help` from a git repo that holds a decoy `scripts/review.py`, and the decoy no longer runs. The pre-existing quirk is fixed too: outside any git checkout, `wt` now stops with "run it from inside a checkout" instead of using `ROOT=/` (tested). - **Spec P2-1 (headers `post` accepts but `list` can't read):** `post` now also requires a readable reviewed SHA and names the expected format instead of printing the regex. Tests reject the two-dot range, bare SHA, uppercase SHA and `…` headers. A live `post` of a two-dot header was refused. - **Spec P2-2 (fix reply unreadable):** `list` now ends a pass's line with `fix reply c<id>`, readable with `wt review show N c<id>`. `forgejo-cli.md` restores `tea pr 42 --comments` and notes that non-interactive runs omit comments without it. - **Standards P3-1 / Spec P3-3 (`FIX_REPLY` too broad, not tied to its pass):** - A reply is now `## Pass <N> fixes`, `## Pass-<N> fixes` or `## Pass <N> fix reply`, plus the ordinal wordings used on #176 and #185. - It answers pass N only if it was posted at or after that pass. - `## Fixes needed before merge`, `## Review fixes still missing`, `## Addressed`, and a late `## Pass 1 fixes` after pass 2 no longer count. Each is tested. - Live: `list 185` shows r6 → c3239 and r7 → c3334, and `list 176` shows c3198 → c3212. - **Standards P3-2 (traceback on odd responses):** `wt_api` reports "unexpected response (missing 'ref')" or "unexpected response shape" instead of a traceback. Tests cover an empty body, a list, a bad element and a missing key. - **Standards P3-3 (waiting message):** while no Actions run exists, it prints `CI not started …; waiting (up to 60s)`, and the timeout message names the start grace. - **Standards P3-4 (failure reported as refusal):** a failed merge request now prints "failed; checking whether it merged", and the PR's `merged` state decides. A merge that happened anyway still prunes. - **Standards P3-5 (smells):** - `Entry` keeps one timestamp and derives the display date from it. - `check` is no longer in `FIELDS`. - The job and run status sets are named (`JOB_PASSED`, `RUN_NOT_SUCCEEDED`). - **Spec P3-5 (stale PR description):** updated for the start grace, the merge-state check, the plain-comment scope (#175, #173 and #166 aren't listed; all merged) and the checklist. - **New coverage:** `tests/test_wt_merge.py` drives `wt merge` end to end against a fake `tea` on PATH: - draft refusal; - a failed CI log tail; - no Actions run after the grace; - a refused merge, with its `head_commit_id` and title checked; - a merge that succeeded despite a failed request. - **Found while committing:** tests that spawn `git` inherited the pre-commit hook's `GIT_DIR`. A `git init` in a test then rewrote the main checkout's `core.bare` (now restored). An autouse fixture drops `GIT_*` variables, and the tests pass with `GIT_DIR` set. Verification: 51 script tests pass, and pre-commit passed (full pytest suite, ruff, docs check). Live read-only checks covered `list 185` and `list 176`, `show 185 c3334`, and a rejected `post`. Please run the fourth pass on 7950c10. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
gabogg left a comment

Code review, pass 4 (origin/master...7950c10, spec: maintainer requests of 2026-09-29, no issue)

Result: no P1s, 1 P2 (Standards) and 6 P3s (4 Standards, 2 Spec). Both axes found the same-second crash: Standards rates it P2, Spec P3. The axes are kept separate, so the Standards P2 stands, and under the maintainer's rule for this PR another pass follows once everything is fixed.

Standards

Pass-3 fixes: P2-1 and P3-1 through P3-5 are all fixed and verified by probes:

  • shadowing from the cwd;
  • the ROOT guard under set -e;
  • reply linking on #185, #176 and #175;
  • the wt_api exception branches;
  • gate traces for none → run appears → pending → success, and for CI_POLL greater than the grace;
  • request failure combined with a failed merged check.

P2

  1. scripts/review.py:116-118: two PR comments in the same second crash list and show (a regression in 7950c10).
    • Cause: replies sorts (datetime, int | None, ref) tuples for every comment, so a timestamp tie compares None with int.
    • Reproduced: ## Pass 1 fixes and thanks, both at 11:00:00, raise TypeError: '<' not supported between instances of 'NoneType' and 'int'.
    • Effect: every later list or show of that PR then crashes.
    • Fix: keep only actual fix replies before sorting, or sort on the timestamp alone, and add a same-second test.

P3

  1. tests/test_wt_merge.py:143-153: the merged-anyway test doesn't prove that prune runs. wt exits 128 because the temp repo has no origin, and the test never asserts the return code. Fix: give the temp repo a bare origin and assert returncode == 0.
  2. scripts/wt:271: a non-numeric WT_CI_TIMEOUT or WT_CI_START_GRACE (e.g. 3m) makes the gate wait forever, because [ -ge ] errors and counts as false. Fix: validate the three settings as integers.
  3. scripts/wt:19: a regular scripts package on sys.path still shadows the helpers. scripts/ is a namespace package, so with PYTHONPATH=<dir with scripts/__init__.py> the decoy runs. Fix: add scripts/__init__.py.
  4. Smells (judgement calls):
    • Data clump: (number, Entry) tuples travel together, so the pass number belongs on Entry.
    • Duplication: HEADER duplicates PASS_NUMBER.
    • Duplicated fixture: _no_git_hook_env is copied into two test files and belongs in tests/conftest.py.

There are no violations of AGENTS.md, code-standards.md or git-and-workflow.md.

Spec

All three maintainer requests and the five wt review criteria are met, with no scope creep. All pass-3 fix claims were verified. Live: list on #185, #176, #175, #173, #166, #168, #169, #177 and #178 is correct, and show 176 c3348 is refused because that comment belongs to another PR. The docs and code agree on header and reply formats, the three-dot rule, the env defaults (1800/15/60) and the refusal conditions. The filled-in doc header passes post.

P3

  1. The same-second crash (same as Standards P2-1).
  2. The PR description misstates #175. list 175 does show pass 2 (c3186); only #175's first pass is missing.

Summary

  • Standards: 1 P2 and 4 P3s. The worst is the same-second comment crash in list and show.
  • Spec: 2 P3s. The worst is the same crash.

🤖 Generated with Claude Code

## Code review, pass 4 (`origin/master...7950c10`, spec: maintainer requests of 2026-09-29, no issue) Result: **no P1s, 1 P2 (Standards) and 6 P3s (4 Standards, 2 Spec).** Both axes found the same-second crash: Standards rates it P2, Spec P3. The axes are kept separate, so the Standards P2 stands, and under the maintainer's rule for this PR another pass follows once everything is fixed. ## Standards **Pass-3 fixes:** P2-1 and P3-1 through P3-5 are all fixed and verified by probes: - shadowing from the cwd; - the ROOT guard under `set -e`; - reply linking on #185, #176 and #175; - the `wt_api` exception branches; - gate traces for none → run appears → pending → success, and for `CI_POLL` greater than the grace; - request failure combined with a failed merged check. **P2** 1. **`scripts/review.py:116-118`: two PR comments in the same second crash `list` and `show` (a regression in 7950c10).** - **Cause:** `replies` sorts `(datetime, int | None, ref)` tuples for every comment, so a timestamp tie compares `None` with `int`. - **Reproduced:** `## Pass 1 fixes` and `thanks`, both at 11:00:00, raise `TypeError: '<' not supported between instances of 'NoneType' and 'int'`. - **Effect:** every later `list` or `show` of that PR then crashes. - **Fix:** keep only actual fix replies before sorting, or sort on the timestamp alone, and add a same-second test. **P3** 1. **`tests/test_wt_merge.py:143-153`: the merged-anyway test doesn't prove that prune runs.** `wt` exits 128 because the temp repo has no `origin`, and the test never asserts the return code. Fix: give the temp repo a bare `origin` and assert `returncode == 0`. 2. **`scripts/wt:271`: a non-numeric `WT_CI_TIMEOUT` or `WT_CI_START_GRACE` (e.g. `3m`) makes the gate wait forever,** because `[ -ge ]` errors and counts as false. Fix: validate the three settings as integers. 3. **`scripts/wt:19`: a regular `scripts` package on `sys.path` still shadows the helpers.** `scripts/` is a namespace package, so with `PYTHONPATH=<dir with scripts/__init__.py>` the decoy runs. Fix: add `scripts/__init__.py`. 4. **Smells (judgement calls):** - **Data clump:** `(number, Entry)` tuples travel together, so the pass number belongs on `Entry`. - **Duplication:** `HEADER` duplicates `PASS_NUMBER`. - **Duplicated fixture:** `_no_git_hook_env` is copied into two test files and belongs in `tests/conftest.py`. There are no violations of `AGENTS.md`, `code-standards.md` or `git-and-workflow.md`. ## Spec All three maintainer requests and the five `wt review` criteria are met, with no scope creep. All pass-3 fix claims were verified. Live: `list` on #185, #176, #175, #173, #166, #168, #169, #177 and #178 is correct, and `show 176 c3348` is refused because that comment belongs to another PR. The docs and code agree on header and reply formats, the three-dot rule, the env defaults (1800/15/60) and the refusal conditions. The filled-in doc header passes `post`. **P3** 1. **The same-second crash** (same as Standards P2-1). 2. **The PR description misstates #175.** `list 175` does show pass 2 (`c3186`); only #175's first pass is missing. ## Summary - **Standards:** 1 P2 and 4 P3s. The worst is the same-second comment crash in `list` and `show`. - **Spec:** 2 P3s. The worst is the same crash. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(review): address fourth review of PR 185
All checks were successful
CI / lint-and-test (pull_request) Successful in 2m40s
00a07a62a0
- review list/show no longer crash when a fix reply shares its second
  with another comment: only fix replies are collected, sorted by time.
- The pass number lives on Entry; is_review_pass derives from
  PASS_NUMBER, so the duplicate HEADER regex is gone.
- scripts/ becomes a regular package, so a `scripts` package elsewhere
  on PYTHONPATH can no longer shadow wt's helpers (tested).
- wt merge rejects non-integer WT_CI_TIMEOUT / WT_CI_POLL /
  WT_CI_START_GRACE and a zero poll interval before any request,
  instead of waiting forever.
- The merged-anyway test now runs against a checkout with a bare
  origin and asserts that wt exits 0 after pruning and fast-forwarding.
- The GIT_* isolation fixture moves to tests/conftest.py, covering
  every test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Owner

Pass 4 fixes (00a07a6)

Every finding from review pass 4 (r9) is fixed in 00a07a6, P3s included.

  • Standards P2-1 / Spec P3-1 (same-second crash): entries() collects only actual fix replies, sorted by timestamp alone, so a timestamp tie can no longer compare None with int. A new test puts a plain comment and two fix replies in the same second; the first reply is linked, with no crash.
  • Standards P3-1 (merged-anyway test): the test checkout now has a bare origin. The test asserts exit 0, that the merge happened despite the failed request, and that the main checkout was fast-forwarded, so prune ran.
  • Standards P3-2 (non-numeric settings hang): wt merge checks WT_CI_TIMEOUT, WT_CI_POLL and WT_CI_START_GRACE as whole seconds before any request. It also rejects WT_CI_POLL=0, which would otherwise loop forever. These are parametrized tests, and none of them call tea. forgejo-cli.md states the unit.
  • Standards P3-3 (regular-package shadowing): scripts/__init__.py makes scripts a regular package. With -P, wt's own checkout comes first on sys.path, so a scripts package elsewhere on PYTHONPATH no longer wins. This is tested with a decoy regular package.
  • Standards P3-4 (smells):
    • The pass number is now a field on Entry.
    • is_review_pass derives from PASS_NUMBER, so the duplicate HEADER regex is gone.
    • The GIT_* isolation fixture moved to tests/conftest.py and covers every test.
  • Spec P3-2: the PR description now says that #175's first pass, and #173's and #166's passes, aren't listed.

Verification:

  • 57 script and docs tests pass, including with GIT_DIR set.
  • Pre-commit passed: full pytest, ruff and the docs check. The main checkout stays non-bare.
  • Live, list 185 and list 176 are unchanged and correct.

Please run the fifth pass on 00a07a6.

🤖 Generated with Claude Code

## Pass 4 fixes (00a07a6) Every finding from review pass 4 (r9) is fixed in 00a07a6, P3s included. - **Standards P2-1 / Spec P3-1 (same-second crash):** `entries()` collects only actual fix replies, sorted by timestamp alone, so a timestamp tie can no longer compare `None` with `int`. A new test puts a plain comment and two fix replies in the same second; the first reply is linked, with no crash. - **Standards P3-1 (merged-anyway test):** the test checkout now has a bare `origin`. The test asserts exit 0, that the merge happened despite the failed request, and that the main checkout was fast-forwarded, so prune ran. - **Standards P3-2 (non-numeric settings hang):** `wt merge` checks `WT_CI_TIMEOUT`, `WT_CI_POLL` and `WT_CI_START_GRACE` as whole seconds before any request. It also rejects `WT_CI_POLL=0`, which would otherwise loop forever. These are parametrized tests, and none of them call `tea`. `forgejo-cli.md` states the unit. - **Standards P3-3 (regular-package shadowing):** `scripts/__init__.py` makes `scripts` a regular package. With `-P`, wt's own checkout comes first on `sys.path`, so a `scripts` package elsewhere on `PYTHONPATH` no longer wins. This is tested with a decoy regular package. - **Standards P3-4 (smells):** - The pass number is now a field on `Entry`. - `is_review_pass` derives from `PASS_NUMBER`, so the duplicate `HEADER` regex is gone. - The `GIT_*` isolation fixture moved to `tests/conftest.py` and covers every test. - **Spec P3-2:** the PR description now says that #175's first pass, and #173's and #166's passes, aren't listed. Verification: - 57 script and docs tests pass, including with `GIT_DIR` set. - Pre-commit passed: full pytest, ruff and the docs check. The main checkout stays non-bare. - Live, `list 185` and `list 176` are unchanged and correct. Please run the fifth pass on 00a07a6. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
gabogg left a comment

Code review, pass 5 (origin/master...00a07a6, spec: maintainer requests of 2026-09-29, no issue)

Result: no P1s, no P2s, 4 P3s (2 Standards, 2 Spec); Spec P3-1 overlaps Standards P3-1. Under the maintainer's rule for this PR, these P3s get fixed and then the PR merges.

Standards

Pass-4 fixes: all verified.

  • Same-second crash: fixed. Only fix replies are kept, sorted on the timestamp, and the sort is stable.
  • Merged-anyway test: now runs against a bare origin and asserts that wt exits 0.
  • Settings checks: fixed for 3m, 1.5 and 0.
  • Regular-package shadowing: fixed, probed with a namespace package and a regular one.
  • Smells: fixed.

Regressions: none.

  • scripts/__init__.py leaves check_docs.py, CI, pre-commit, sync-database.py, lint.sh, build-css.sh and packaging unaffected.
  • The autouse GIT_* fixture breaks nothing; the full suite shows 508 passed.

P3

  1. scripts/wt:316-319: the settings check can be bypassed.
    • The unquoted list items let WT_CI_TIMEOUT="1 2" word-split past the check, which brings back the endless wait.
    • WT_CI_POLL=08 passes, then crashes with "value too great for base", and 010 is read as octal.
    • A 20-digit value either gets the wrong message or errors on every poll.
    • Fix: quote the items, use ^(0|[1-9][0-9]{0,8})$, or normalise with 10#.
  2. scripts/wt:284: the wait counts CI_POLL per loop, not real elapsed time. Slow tea calls stretch the real wait past WT_CI_TIMEOUT. This predates the PR.

There are no documented-standard violations and no new smells. Ruff, bash -n and check_docs pass.

Spec

Pass-4 fix claims (c3355): all true.

  • An offline walk-through ran post → list → ## Pass 1 fixes reply linked → pass 2 posted, and show defaulted to the latest pass.
  • Live list and show on #185, #176, #175, #173, #166, #168, #169, #177 and #178 match the PR description, and a cross-PR ref is refused.
  • All three maintainer requests and the five wt review criteria are met, the docs agree with the code, and there is no scope creep.

P3

  1. A leading-zero setting passes the check and breaks the arithmetic (wt:316-319,284). This is the same issue as Standards P3-1.
  2. The PR description doesn't mention the settings pre-flight check, the same-second test or the timing-settings tests.

Summary

  • Standards: 2 P3s. The worst is that the settings check can be bypassed.
  • Spec: 2 P3s. The worst is the leading-zero setting.

🤖 Generated with Claude Code

## Code review, pass 5 (`origin/master...00a07a6`, spec: maintainer requests of 2026-09-29, no issue) Result: **no P1s, no P2s, 4 P3s (2 Standards, 2 Spec); Spec P3-1 overlaps Standards P3-1.** Under the maintainer's rule for this PR, these P3s get fixed and then the PR merges. ## Standards **Pass-4 fixes:** all verified. - **Same-second crash:** fixed. Only fix replies are kept, sorted on the timestamp, and the sort is stable. - **Merged-anyway test:** now runs against a bare `origin` and asserts that wt exits 0. - **Settings checks:** fixed for `3m`, `1.5` and `0`. - **Regular-package shadowing:** fixed, probed with a namespace package and a regular one. - **Smells:** fixed. **Regressions:** none. - `scripts/__init__.py` leaves `check_docs.py`, CI, pre-commit, `sync-database.py`, `lint.sh`, `build-css.sh` and packaging unaffected. - The autouse `GIT_*` fixture breaks nothing; the full suite shows 508 passed. **P3** 1. **`scripts/wt:316-319`: the settings check can be bypassed.** - The unquoted list items let `WT_CI_TIMEOUT="1 2"` word-split past the check, which brings back the endless wait. - `WT_CI_POLL=08` passes, then crashes with "value too great for base", and `010` is read as octal. - A 20-digit value either gets the wrong message or errors on every poll. - **Fix:** quote the items, use `^(0|[1-9][0-9]{0,8})$`, or normalise with `10#`. 2. **`scripts/wt:284`: the wait counts `CI_POLL` per loop, not real elapsed time.** Slow `tea` calls stretch the real wait past `WT_CI_TIMEOUT`. This predates the PR. There are no documented-standard violations and no new smells. Ruff, `bash -n` and `check_docs` pass. ## Spec **Pass-4 fix claims (c3355):** all true. - An offline walk-through ran `post` → `list` → `## Pass 1 fixes` reply linked → pass 2 posted, and `show` defaulted to the latest pass. - Live `list` and `show` on #185, #176, #175, #173, #166, #168, #169, #177 and #178 match the PR description, and a cross-PR ref is refused. - All three maintainer requests and the five `wt review` criteria are met, the docs agree with the code, and there is no scope creep. **P3** 1. **A leading-zero setting passes the check and breaks the arithmetic** (`wt:316-319,284`). This is the same issue as Standards P3-1. 2. **The PR description** doesn't mention the settings pre-flight check, the same-second test or the timing-settings tests. ## Summary - **Standards:** 2 P3s. The worst is that the settings check can be bypassed. - **Spec:** 2 P3s. The worst is the leading-zero setting. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(review): address fifth review of PR 185
All checks were successful
CI / lint-and-test (pull_request) Successful in 2m28s
dc646c4601
- wt merge quotes each WT_CI_* setting before checking it and accepts
  plain decimals up to 999999999 only, so "1 2" no longer word-splits
  past the check, "08" is not read as octal, and huge values cannot
  overflow the comparisons.
- The CI wait measures real elapsed time (bash SECONDS), so slow API
  calls count toward WT_CI_TIMEOUT and the start grace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Owner

Pass 5 fixes (dc646c4)

Every finding from review pass 5 (r10) is fixed in dc646c4. Pass 5 had only P3s, so under the maintainer's rule for this PR it now merges through scripts/wt merge 185.

  • Standards P3-1 / Spec P3-1 (settings check bypass): each WT_CI_* item is quoted before the check, and only plain decimals from 0 to 999999999 are accepted. "1 2", 08 and a 20-digit value are now rejected before any request, and each case is tested.
  • Standards P3-2 (wait counted poll intervals, not real time): the CI wait measures real elapsed time with bash SECONDS, so slow API calls count toward WT_CI_TIMEOUT and the start grace.
  • Spec P3-2 (PR description): it now covers the settings check, the real-time wait, the same-second test and the timing-settings tests.

Verification:

  • 60 script and docs tests pass.
  • Pre-commit passed: full pytest, ruff and the docs check.
  • The main checkout's core.bare is still false.

🤖 Generated with Claude Code

## Pass 5 fixes (dc646c4) Every finding from review pass 5 (r10) is fixed in dc646c4. Pass 5 had only P3s, so under the maintainer's rule for this PR it now merges through `scripts/wt merge 185`. - **Standards P3-1 / Spec P3-1 (settings check bypass):** each `WT_CI_*` item is quoted before the check, and only plain decimals from `0` to `999999999` are accepted. `"1 2"`, `08` and a 20-digit value are now rejected before any request, and each case is tested. - **Standards P3-2 (wait counted poll intervals, not real time):** the CI wait measures real elapsed time with bash `SECONDS`, so slow API calls count toward `WT_CI_TIMEOUT` and the start grace. - **Spec P3-2 (PR description):** it now covers the settings check, the real-time wait, the same-second test and the timing-settings tests. Verification: - 60 script and docs tests pass. - Pre-commit passed: full pytest, ruff and the docs check. - The main checkout's `core.bare` is still false. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
gabogg merged commit b48940e139 into master 2026-10-02 13:24:20 +00:00
gabogg deleted branch docs/formal-pr-reviews 2026-10-02 13:24:20 +00:00
Sign in to join this conversation.
No description provided.