Commit 719e350092
Verified · cmc
Layout: unified · split
docs/superpowers/specs/2026-09-22-parity-followup-design.md added +197
| @@ -0,0 +1,197 @@ | |||
| 1 | # Parity follow-up: gitbay v1.28.1 to v1.34.1 | ||
| 2 | |||
| 3 | An audit on 2026-09-22 of the Parity wiki page (`krz/gitbay`, | ||
| 4 | `.gitbay/wiki/Parity.org`) and of every gitbay release since the app's | ||
| 5 | v1.2.0 (server v1.28.1 through v1.34.1, the last unreleased on main) | ||
| 6 | found one regression, three gaps the page records, two it does not, and | ||
| 7 | one row that is wrong. This spec covers all of them in four releases. | ||
| 8 | |||
| 9 | Push notifications (v1.32.0, v1.32.1) are already done on main and are | ||
| 10 | not restated here; see `2026-09-20-push-notifications-design.md`. | ||
| 11 | |||
| 12 | ## Findings | ||
| 13 | |||
| 14 | | # | Finding | Source | Kind | | ||
| 15 | |---|---------|--------|------| | ||
| 16 | | 1 | Profile and org-profile save fail: `unknown flag "--about-format"`, exit 2 | v1.31.0 | regression | | ||
| 17 | | 2 | `build list` returns `subject`; the app does not decode it | v1.33.0 | unrowed gap | | ||
| 18 | | 3 | `build list` pages with `--limit`/`--cursor`; the app does not | v1.33.0 | wrong row (page says yes) | | ||
| 19 | | 4 | MR labels: `mr label --add/--remove` | page | gap | | ||
| 20 | | 5 | MR filter by label: `mr list --label` | page | gap | | ||
| 21 | | 6 | Preview body markup: issue, MR, file editor, release notes | page, v1.30.0 | gap (4 rows) | | ||
| 22 | | 7 | Admin: account list and state filter, promote/demote, disable/enable, worker queues | page, v1.30.0 | gap (4 rows) | | ||
| 23 | |||
| 24 | Finding 1 was confirmed against gitbay.org. `ProfileEdit.flags()` | ||
| 25 | appends `--about-format <f> --file -` on every save, and | ||
| 26 | `parseProfileFlags` in `internal/control/profile.go` accepts only | ||
| 27 | `--description`, `--website` and `--link`. The shipped 1.2.0 is | ||
| 28 | affected. | ||
| 29 | |||
| 30 | ## Releases | ||
| 31 | |||
| 32 | | Version | Carries | | ||
| 33 | |---------|---------| | ||
| 34 | | 1.3.0 (12) | push (on main) and finding 1 | | ||
| 35 | | 1.4.0 (13) | findings 2–5 | | ||
| 36 | | 1.5.0 (14) | finding 6 | | ||
| 37 | | 1.6.0 (15) | finding 7 | | ||
| 38 | |||
| 39 | Each is a squash-merged MR per feature, then a bump MR touching the | ||
| 40 | four `MARKETING_VERSION`/`CURRENT_PROJECT_VERSION` lines, then a | ||
| 41 | lightweight tag on the bump's merge commit. | ||
| 42 | |||
| 43 | ## 1.3.0: profile about | ||
| 44 | |||
| 45 | The about text has been a file since v1.31.0: `profile/README.md`, | ||
| 46 | `.org` or `.markdown`, resolved in that order, on the default branch of | ||
| 47 | `<owner>/.gitbay`. `profile show` returns it as `about`, `about_format` | ||
| 48 | and `about_path`. `profile set` and `org profile` take description, | ||
| 49 | website and links only. | ||
| 50 | |||
| 51 | The web's account page no longer edits the text. It links to the file | ||
| 52 | editor and, when there is no file, offers to create the repository with | ||
| 53 | a starter README (`internal/httpd/account.go`, the `profile-repo` | ||
| 54 | form). The app does the same. | ||
| 55 | |||
| 56 | - `ProfileEdit` drops `about`, `aboutFormat` and `aboutStdin`, and | ||
| 57 | `flags()` drops the `--about-format`/`--file -` tail. | ||
| 58 | `ProfileViewModel.saveProfile` stops passing stdin. The same argv | ||
| 59 | serves `profile set` and `org profile <org>`. | ||
| 60 | - `ProfileEditSheet` drops the about `TextEditor` and format picker. | ||
| 61 | - `Profile` decodes `about_path`. `aboutFile` is deleted; | ||
| 62 | `ReadmeView` receives `aboutPath`, so the file's extension picks the | ||
| 63 | renderer. | ||
| 64 | - `ProfileView`'s about section, for a viewer where `canEdit` is true, | ||
| 65 | gains an Edit action that pushes | ||
| 66 | `.file(repo: "<owner>/.gitbay", path: aboutPath, ref: nil)`. The file | ||
| 67 | screen's existing edit action and `repo commit-file` do the write. | ||
| 68 | - With no `about_path` and `canEdit` true, the section offers "Create | ||
| 69 | about file". It runs `repo show <owner>/.gitbay`; on exit 3 (not | ||
| 70 | found) it runs `repo create <owner>/.gitbay`. It then runs | ||
| 71 | `repo commit-file <owner>/.gitbay profile/README.md --ref main | ||
| 72 | --message "add profile about" --file -` with the web's starter text | ||
| 73 | (`# <owner>\n\nThis is the about text on your profile.\n`), and | ||
| 74 | reloads. Any other failure is shown and stops the flow. Whether `repo create` accepts `<org>/.gitbay` from an org | ||
| 75 | admin is checked first in implementation; if it does not, the offer | ||
| 76 | is shown for users only. | ||
| 77 | |||
| 78 | Tests: `ProfileEdit.flags()` carries no `--about-format` or `--file`; | ||
| 79 | `saveProfile` sends no stdin; `Profile` decodes `about_path`; the | ||
| 80 | create flow issues `repo create` then `repo commit-file` with the | ||
| 81 | starter on stdin, and skips `repo create` when `repo show` succeeds. The live | ||
| 82 | suite's profile step saves a description and asserts success. | ||
| 83 | |||
| 84 | ## 1.4.0: build list and MR labels | ||
| 85 | |||
| 86 | ### Build list | ||
| 87 | |||
| 88 | - `Build` gains `subject: String?`. | ||
| 89 | - `BuildRow` leads with `#<n> <subject>`, falling back to | ||
| 90 | `#<n> <shortSHA>` when the commit is gone. The meta line carries job, | ||
| 91 | ref and `shortSHA`. | ||
| 92 | - `BuildListViewModel` replaces `readList` with | ||
| 93 | `PagedListModel<Build>(pageSize: 30)`, the web's page size. The | ||
| 94 | `sorted { $0.number > $1.number }` goes: cursor pages arrive in | ||
| 95 | server order and a client sort would reorder across page boundaries. | ||
| 96 | A filter change sets `argv` and calls `reload()`. | ||
| 97 | - `BuildListView` gains `PageFooter`, as the issue and MR lists have. | ||
| 98 | |||
| 99 | `load()` never set `.loading` before; reload tests wait on the final | ||
| 100 | state, not on "not loading". | ||
| 101 | |||
| 102 | ### MR labels | ||
| 103 | |||
| 104 | A copy of the issue implementation. | ||
| 105 | |||
| 106 | - `MRDetailViewModel` loads `label list <repo>` for the picker and | ||
| 107 | gains `addLabel`/`removeLabel`, sending | ||
| 108 | `mr label <repo> <n> --add|--remove <l>`, as `IssueDetailViewModel` | ||
| 109 | does for `issue label`. | ||
| 110 | - `MRView` shows the label chips and the add/remove menu `IssueView` | ||
| 111 | has. | ||
| 112 | - `MRFilter` gains `label: String?`, rendered as `--label`. Its doc | ||
| 113 | comment, which says the command takes no `--label`, is corrected. | ||
| 114 | The MR filter sheet gains the label picker the issue sheet has. | ||
| 115 | |||
| 116 | Tests: `Build` decodes with and without `subject`; the row title | ||
| 117 | falls back; the build list sends `--limit 30`, appends the second page | ||
| 118 | on `loadMore`, and does not reorder; `MRFilter.flags()` with a label; | ||
| 119 | `mr label` argv for add and remove. | ||
| 120 | |||
| 121 | ## 1.5.0: markup previews | ||
| 122 | |||
| 123 | The web previews by posting the draft back to its own form, where the | ||
| 124 | server renders it with autolinks resolved for the viewer. No command | ||
| 125 | renders markup (the CLI row is `n/a`), and the app resolves no | ||
| 126 | autolinks anywhere. The app previews locally with the renderer it | ||
| 127 | already uses for READMEs and wiki pages. The preview therefore matches | ||
| 128 | what the app shows, not what the web shows: `#N` stays plain text in | ||
| 129 | both the preview and the thread. | ||
| 130 | |||
| 131 | - New `MarkupEditor` in `gitbay/Views/Shared/`: a Write/Preview | ||
| 132 | segmented control over a `TextEditor`. Preview renders | ||
| 133 | `ReadmeView(name: "draft.<ext>", content: text)`, with `<ext>` `org` | ||
| 134 | for org and `md` otherwise. Each call site passes its existing | ||
| 135 | modifiers (font, autocorrection, accessibility identifier) through. | ||
| 136 | - Call sites: | ||
| 137 | - `ComposeSheet` body: issue and MR create and edit, and release | ||
| 138 | edit. The extension follows the sheet's `format` binding. | ||
| 139 | - Comment fields in `IssueView` and `MRView`: always `md`, since a | ||
| 140 | comment with no `--format` is markdown. | ||
| 141 | - Release notes in `ReleaseListView`'s create sheet: `md`. | ||
| 142 | - `FileEditSheet`: the real file name, and the control only when it | ||
| 143 | ends in `.md`, `.markdown` or `.org`. `ReadmeView` treats every | ||
| 144 | other name as markdown, so the gate is the sheet's. | ||
| 145 | |||
| 146 | Tests: the extension chosen for each format; `FileEditSheet` offers | ||
| 147 | preview for the three extensions and not for `.swift`. The live suite | ||
| 148 | toggles preview on an issue comment and finds rendered text. | ||
| 149 | |||
| 150 | ## 1.6.0: admin | ||
| 151 | |||
| 152 | Matches the web's `/admin` and `/admin/users` and nothing else. Account | ||
| 153 | show, invite, runners, repository administration, the audit log and | ||
| 154 | instance statistics stay `no` on both surfaces. | ||
| 155 | |||
| 156 | - `DashboardModels` decodes `queues` and `server`. The `dashboard` | ||
| 157 | command sends `queues` to instance admins only, so its presence is | ||
| 158 | the admin signal, as it is for the web (`internal/httpd/admin.go`). | ||
| 159 | - The account menu shows Admin when `queues` is present. The admin | ||
| 160 | screen shows the queues and the server commit, and links to | ||
| 161 | accounts. | ||
| 162 | - `AdminUsersViewModel` pages `admin user list [--state | ||
| 163 | active|pending|disabled|admin]` through `PagedListModel`, and sends | ||
| 164 | `admin user promote|demote|disable|enable <username>` per row. | ||
| 165 | - Demote and disable ask for the username to be typed before they | ||
| 166 | send, as the web does. Promote and enable use the app's standard | ||
| 167 | `confirmationDialog`. A row shows only the actions its state allows. | ||
| 168 | |||
| 169 | Tests: `queues` decodes and absent means not admin; `admin user list` | ||
| 170 | argv per state; each write's argv; demote and disable stay disabled | ||
| 171 | until the typed name matches. The live suite skips admin unless the | ||
| 172 | signed-in account is an admin; `ios-smoke` is not. | ||
| 173 | |||
| 174 | ## Upstream: krz/gitbay | ||
| 175 | |||
| 176 | One MR alongside 1.3.0: | ||
| 177 | |||
| 178 | - Parity: iOS `build list paging (limit, cursor)` from `yes` to `no`; | ||
| 179 | a row for "build row names its commit" (cli yes, web yes, ios no); | ||
| 180 | a sentence under the preview paragraph that the iOS preview is the | ||
| 181 | app's own rendering and resolves no autolinks. | ||
| 182 | - CHANGELOG, v1.31.0: the about file is `profile/README.{md,org,markdown}` | ||
| 183 | in `<owner>/.gitbay`, not `about.org`/`about.md`. The release notes | ||
| 184 | for v1.31.0 are edited to match with `release edit`. | ||
| 185 | |||
| 186 | Later MRs flip rows as each release ships: the build rows at 1.4.0, | ||
| 187 | MR labels and filter by label at 1.4.0, the four preview rows at 1.5.0, | ||
| 188 | and account list, promote/demote, disable/enable and worker queues at | ||
| 189 | 1.6.0. | ||
| 190 | |||
| 191 | ## Not in scope | ||
| 192 | |||
| 193 | - Autolink resolution in the app. | ||
| 194 | - A server-side `render` command. | ||
| 195 | - Admin beyond what the web carries. | ||
| 196 | - Bookmarks as a profile section: the app keeps them as their own | ||
| 197 | screen, and the Parity row stays `no`. | ||