Commit 719e350092

719e350092c95798931dcb71d062619d8c09e362

parent: 045cba839a

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-22 17:26 UTC

docs: spec for the parity follow-up

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
3An 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
5v1.2.0 (server v1.28.1 through v1.34.1, the last unreleased on main)
6found one regression, three gaps the page records, two it does not, and
7one row that is wrong. This spec covers all of them in four releases.
8
9Push notifications (v1.32.0, v1.32.1) are already done on main and are
10not 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
24Finding 1 was confirmed against gitbay.org. `ProfileEdit.flags()`
25appends `--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
28affected.
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
39Each is a squash-merged MR per feature, then a bump MR touching the
40four `MARKETING_VERSION`/`CURRENT_PROJECT_VERSION` lines, then a
41lightweight tag on the bump's merge commit.
42
43## 1.3.0: profile about
44
45The 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`
48and `about_path`. `profile set` and `org profile` take description,
49website and links only.
50
51The web's account page no longer edits the text. It links to the file
52editor and, when there is no file, offers to create the repository with
53a starter README (`internal/httpd/account.go`, the `profile-repo`
54form). 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
78Tests: `ProfileEdit.flags()` carries no `--about-format` or `--file`;
79`saveProfile` sends no stdin; `Profile` decodes `about_path`; the
80create flow issues `repo create` then `repo commit-file` with the
81starter on stdin, and skips `repo create` when `repo show` succeeds. The live
82suite'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
100state, not on "not loading".
101
102### MR labels
103
104A 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
116Tests: `Build` decodes with and without `subject`; the row title
117falls back; the build list sends `--limit 30`, appends the second page
118on `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
123The web previews by posting the draft back to its own form, where the
124server renders it with autolinks resolved for the viewer. No command
125renders markup (the CLI row is `n/a`), and the app resolves no
126autolinks anywhere. The app previews locally with the renderer it
127already uses for READMEs and wiki pages. The preview therefore matches
128what the app shows, not what the web shows: `#N` stays plain text in
129both 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
146Tests: the extension chosen for each format; `FileEditSheet` offers
147preview for the three extensions and not for `.swift`. The live suite
148toggles preview on an issue comment and finds rendered text.
149
150## 1.6.0: admin
151
152Matches the web's `/admin` and `/admin/users` and nothing else. Account
153show, invite, runners, repository administration, the audit log and
154instance 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
169Tests: `queues` decodes and absent means not admin; `admin user list`
170argv per state; each write's argv; demote and disable stay disabled
171until the typed name matches. The live suite skips admin unless the
172signed-in account is an admin; `ios-smoke` is not.
173
174## Upstream: krz/gitbay
175
176One 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
186Later MRs flip rows as each release ships: the build rows at 1.4.0,
187MR labels and filter by label at 1.4.0, the four preview rows at 1.5.0,
188and account list, promote/demote, disable/enable and worker queues at
1891.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`.