fix(web): a status name is not a category — stop guessing "done" (GDK-272) - #23
Merged
Conversation
…-272) `RESOLVED_STATUS_NAMES` held `resolved`/`closed`/`done`/`해결됨`/`종료`/`완료` and two readers used it whenever the server sent no category: `effectiveCategory` returned `done` on a name match, and HistoryTimeline's local `isReopen` painted a reopen badge from `isResolved(from) && !isResolved(to)`. The repository forbids exactly this. `status = 'In Progress'` is silently zero rows on a Korean account; `docs/STATE_OF_PLAY.md` hard-won #4 says a reopen is a done-category → non-done transition and *never* a name match. The set's own comment claimed it kept "only generic names that mean the same on every Jira" — `완료` is not a generic name, so the premise contradicted itself. It was not dead code. `internal/server/read.go:445` fills `from_category` from `view.categories[FromID]`, and `read.go:650` seeds that map only from status ids a *currently mirrored* issue occupies. Any status the issue passed through that nothing currently holds — a status removed from the workflow, for instance — arrives with no category, and the name decides. So this also broke on an English site with a custom done status ("Verified", "Shipped"), not just on a third language. Now: a real `new|inprogress|done` is trusted, anything else is `inprogress`, and no name is ever consulted. `isReopen` moved into `view-config.ts` as the single owner of the reopen rule and returns false when both categories are empty — an unpainted badge is a missing hint, while a wrongly painted one is a false claim about the issue's history. `matchesIdFirst`'s name fallback is untouched: that is the documented compatibility path for legacy saved views, a different decision from this one. Recurrence: `tools/doc-checks.sh` now matches `'해결됨'`, `'종료'` and `'완료'` as display-name literals in web logic. The old comment there claimed this table was "deliberately not matched (would fail the current tree; sibling issue)" — the round found that was only a comment, not a grep exclusion, so the real work was adding patterns that catch the table rather than removing an exemption. Lowercase `done`/`resolved`/`closed` stay out on purpose: they are also category keys and other-field values (a RangeField `resolved`, a GitHub PR `closed`). FAIL-first, run by the lead: reintroducing `new Set(['완료', '해결됨'])` into view-config.ts makes doc-checks fail and name the file and line; removing it returns exit 0. `effectiveCategory` also had no unit test at all before this — now 11 new cases cover the trusted values, the empty-category-with-Korean-name row, `'Done'`, a custom `'Shipped'`, and the reopen predicate. Debuggability: the failure was silent, so `missingStatusCategorySeen()` counts how often a category decision ran without a category. Integer increment, no per-row logging. Gates (lead, cold): svelte-check 4250 files / 0 errors · vitest 34 files / 347 tests (was 336) · doc-checks all passed · Playwright 240 passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
midagedev
added a commit
that referenced
this pull request
Sep 2, 2026
- init: `--replace-standalone` (the v0.19 spelling) is accepted again. The legacy-alias table mapped the *new* name onto itself, so the old flag fell through to the parser as unknown. Test parses both spellings. (GDK-1312) - web: an issue with no origin page no longer advertises "Open in Built-in" — the palette row, the `o` shortcut, and the shortcuts sheet all branch on issueOriginUrl() being non-null; the detail header shows the key as a label instead of a dead link. e2e on a gadak-origin serve. (GDK-1313) - migrate --to linear: pickLinearState selects by linear.StatusCategory(type) instead of a hand-written inverse, so a team whose only "done" state is canceled maps instead of failing. Test with a canceled-only team. (GDK-1314) Backlog snapshot regenerated so doc-checks #23 sees the three keys. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
midagedev
added a commit
that referenced
this pull request
Sep 2, 2026
midagedev
added a commit
that referenced
this pull request
Sep 2, 2026
…e (GDK-1352) User call, 2026-09-02, looking at the promo clips beside the app: the paper terminal in those composites runs the full width of the frame, one horizontal seam, and it reads as two floors. The dock in the app stopped at the sidebar's edge and read as a corner. So the sidebar lives in row 1 now and the dock spans every column — the 2026-08-30 decision (GDK-1194, "the bottom of the content row") amended, not reversed: the tracker is still the stage and the terminal still takes height rather than width. A terminal needs columns, not rows; a gadak one-liner is ~120 characters and the issue-key tab strip gets the width too. The sidebar pays in height, which its sections already scroll for. The default height drops from 40% to a quarter of the window — a band, not a second workspace; ~9 rows at 900px, the height the paper terminal in the clips has always had. A session that wants more drags the handle once and it persists. The overlay regime (<900px, a right-hand sheet) is a different mode and is left alone. e2e: the GDK-1194 geometry test now also asks that the dock starts at the window edge and that the sidebar ends where the dock begins (FAIL-first on the old grid: x read 272 against 0), and a new test pins the default band at 22–28% so the constant cannot drift back. Snapshot: GDK-1352/1353/1354 go public (doc-checks #23). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
RESOLVED_STATUS_NAMESheldresolved/closed/done/해결됨/종료/완료, andtwo readers consulted it whenever the server sent no category:
effectiveCategoryreturned
doneon a name match, and HistoryTimeline's localisReopenpainted areopen badge from
isResolved(from) && !isResolved(to).This is the trap the repo names first.
status = 'In Progress'is silently zerorows on a Korean account;
docs/STATE_OF_PLAY.mdhard-won #4 says a reopen is adone-category → non-done transition and never a name match. The set's own
comment claimed it kept "only generic names that mean the same on every Jira" —
완료is not a generic name, so the premise contradicted itself.It was not dead code
internal/server/read.go:445fillsfrom_categoryfromview.categories[FromID], andread.go:650seeds that map only from status idsa currently-mirrored issue occupies. Any status the issue passed through that
nothing currently holds — a status removed from the workflow, say — arrives with
no category, and the name decides.
So it also broke on an English site with a custom done status ("Verified",
"Shipped"), not only on a third language. The audit round rated this
medium-confidence; verifying that seeding path is what promoted it to a
confirmed Highest defect.
What changed
effectiveCategorytrusts a realnew|inprogress|doneand otherwise returnsinprogress. No name is consulted.isReopenmoved intoview-config.tsas the single owner of the reopenrule, and returns false when both categories are empty: an unpainted badge is a
missing hint, a wrongly painted one is a false claim about the issue's history.
matchesIdFirst's name fallback is deliberately untouched — that is thedocumented compatibility path for legacy saved views, a different decision.
Recurrence, and a correction to my own spec
tools/doc-checks.shnow matches'해결됨','종료','완료'as display-nameliterals in web logic. Its old comment said this table was "deliberately not
matched (would fail the current tree; sibling issue)" — the round found that was
only a comment, not a grep exclusion;
view-config.tswas already in the scanpath. So the work was adding patterns that catch the table, not removing an
exemption. My spec had that wrong and the round said so.
Lowercase
done/resolved/closedstay out of the pattern on purpose: they arealso category keys and other-field values (a RangeField
resolved, a GitHub PRclosed).FAIL-first run by me: reintroducing
new Set(['완료', '해결됨'])intoview-config.ts makes doc-checks fail and name the file and line; removing it
returns exit 0.
effectiveCategoryhad no unit test at all before this. 11 new cases nowcover the trusted values, an empty category with a Korean status name,
'Done',a custom
'Shipped', and the reopen predicate.Debuggability
The failure mode was silence — nobody could tell a category was missing and
something guessed.
missingStatusCategorySeen()counts how often a categorydecision ran without a category. Integer increment, no per-row logging.
Gates (lead, cold)
svelte-check 4250 files / 0 errors · vitest 34 files / 347 tests (was
336) ·
tools/doc-checks.shall passed · Playwright 240 passed (mandatoryhere —
web/changed).Closes GDK-272.
🤖 Generated with Claude Code