Fix #323: issues default open-only #330

Merged
crueber merged 1 commit from fix/issue-323 into main 2026-09-11 15:48:10 +00:00
Owner

Fixes #323 — the Issues tab (/:owner/:name/issues) now defaults to open-only when the URL carries no ?state= param; the State select visibly reads "open" on a bare visit.

Representation choice (URL honesty)

Reserved state=all as the explicit both-value (the issue's option A):

  • resolveIssueState (web/src/lib/issueState.js, new): absent param → open; all or legacy empty ?state= → all (both); open/closed pass through exactly; unknown values pass through for the server to 400 as before.
  • issueListState: all → "" on the wire — the SDK qs() skips empties, so the param is OMITTED and the list endpoint (which accepts only open|closed|absent, absent = both) needs NO backend change. all never reaches the server.
  • Choosing "open + closed" writes ?state=all (shareable/refreshable); refreshing or deep-linking it shows both. Deep links ?state=closed/?state=open honored exactly. Pagination key already includes the query JSON, so open vs both windows stay distinct.

Deliberate decisions

  • Milestone-filtered landings (?milestone=<id>, #314) inherit the open default — no special-casing, matches GitHub behavior. Called out as a decision, not an accident.
  • Pulls.jsx intentionally NOT touched — its tab UI already defaults to open (empty state = open tab) and has no both-view; left as-is. FLAG FOR FOLLOW-UP: if pulls ever gains an explicit both-view, align its URL token with state=all here for consistency.
  • Select binds the RESOLVED value (resolveIssueState(search.state)), so the control reflects the effective default rather than the raw param.

Verification

  • New headless tests web/test/unit/issue-state.test.js (6 tests: absent→open, both spellings→both, open/closed exact, unknown passthrough, wire mapping, end-to-end) — green.
  • Full web unit suite in batches: 615 pass / 0 fail. (smoke.test.js excluded: it hangs identically on pristine origin/main in this sandbox — port 8080 is squatted by another process so its skip-probe never settles; pre-existing environmental, unrelated.)
  • Browser check OPEN (not run): shared obscura daemon blocks loopback per task constraints, and no server was stood up; the change is a query/select binding verified headless. Please browser-verify /, a repo issues page, and /setup console on merge CI.
  • No new deps (law 1); no Go changes (laws 7/8 untouched); docs/features/02_issues.md Decisions appended in the same change (law 12).

Do NOT merge — review only.

Fixes #323 — the Issues tab (`/:owner/:name/issues`) now defaults to open-only when the URL carries no `?state=` param; the State select visibly reads "open" on a bare visit. ## Representation choice (URL honesty) Reserved `state=all` as the explicit both-value (the issue's option A): - `resolveIssueState` (`web/src/lib/issueState.js`, new): absent param → `open`; `all` or legacy empty `?state=` → `all` (both); `open`/`closed` pass through exactly; unknown values pass through for the server to 400 as before. - `issueListState`: `all` → `""` on the wire — the SDK `qs()` skips empties, so the param is OMITTED and the list endpoint (which accepts only `open|closed|absent`, absent = both) needs NO backend change. `all` never reaches the server. - Choosing "open + closed" writes `?state=all` (shareable/refreshable); refreshing or deep-linking it shows both. Deep links `?state=closed`/`?state=open` honored exactly. Pagination key already includes the query JSON, so `open` vs both windows stay distinct. ## Deliberate decisions - Milestone-filtered landings (`?milestone=<id>`, #314) inherit the open default — no special-casing, matches GitHub behavior. Called out as a decision, not an accident. - `Pulls.jsx` intentionally NOT touched — its tab UI already defaults to open (empty state = open tab) and has no both-view; left as-is. FLAG FOR FOLLOW-UP: if pulls ever gains an explicit both-view, align its URL token with `state=all` here for consistency. - Select binds the RESOLVED value (`resolveIssueState(search.state)`), so the control reflects the effective default rather than the raw param. ## Verification - New headless tests `web/test/unit/issue-state.test.js` (6 tests: absent→open, both spellings→both, open/closed exact, unknown passthrough, wire mapping, end-to-end) — green. - Full web unit suite in batches: 615 pass / 0 fail. (`smoke.test.js` excluded: it hangs identically on pristine `origin/main` in this sandbox — port 8080 is squatted by another process so its skip-probe never settles; pre-existing environmental, unrelated.) - Browser check OPEN (not run): shared obscura daemon blocks loopback per task constraints, and no server was stood up; the change is a query/select binding verified headless. Please browser-verify `/`, a repo issues page, and `/setup` console on merge CI. - No new deps (law 1); no Go changes (laws 7/8 untouched); `docs/features/02_issues.md` Decisions appended in the same change (law 12). Do NOT merge — review only.
Sign in to join this conversation.
No description provided.