Commit d5dec7be8a

d5dec7be8a88165dcb81c88c6fa23aa7e7360a03

parent: 073fca4510

Unsigned

cmc <hello@cleberg.net> · 2026-07-16 15:04 UTC

docs: bucket the Phase 3 backlog into semantic versions

Layout: unified · split

ROADMAP.md +67 −8
@@ -134,6 +134,28 @@ GraphQL mutation. Treat that boundary as explicit rather than half-building it.
134Unlike Phases 1 and 2, this is not one shippable thing. It is several, and they 134Unlike Phases 1 and 2, this is not one shippable thing. It is several, and they
135are sized very differently — measure before committing to one. 135are sized very differently — measure before committing to one.
136 136
137### Release plan
138
139Hutch is an app with a `MARKETING_VERSION`, not a library with an API contract,
140so "breaking change" does not apply. These buckets track *user-visible scale*.
141
142| Version | Contents | Why here |
143| --- | --- | --- |
144| v3.8.1 | SonarCloud triage; housekeeping | No behaviour change at all |
145| v3.9.0 | "What's cooking" ingest; doc truth-up; deploy keys | Ships one feature, corrects the map |
146| v3.10.0 | hub.sr.ht writes: projects, discovery, `mailingListSubscribe` | Provisional — gated on what v3.9.0 finds |
147| v3.11.0 | Accessibility | Independent, device-verified |
148| v4.0.0 | Localization *with* translations | The only true re-presentation |
149| — | Swift 6 language mode; cache reads | Internal; ride along, no tag |
150
151Ordering is by dependency, not size. v3.9.0 leads because it is the only item
152that corrects the others' inputs: the ingest's real output is a `SCOPE.md` that
153is true, and v3.10.0 rests entirely on one unverified sentence in a blog post.
154Do not commit v3.10.0's number until the SDL has been read — the bucket may turn
155out to be empty, which is the point of sequencing it second.
156
157`KeychainHelper` is deliberately unbucketed; see the SonarCloud hotspots below.
158
137### API features — done (v3.8.0) 159### API features — done (v3.8.0)
138 160
139- ~~`uploadArtifact` / `deleteArtifact`~~ — artifacts were read-only. 161- ~~`uploadArtifact` / `deleteArtifact`~~ — artifacts were read-only.
@@ -152,7 +174,7 @@ Three of the six planned. The other three did not survive contact:
152 on judgement — see [SCOPE.md](SCOPE.md) for the reasoning, so they do not get 174 on judgement — see [SCOPE.md](SCOPE.md) for the reasoning, so they do not get
153 re-proposed. 175 re-proposed.
154 176
155### Localization 177### Localization — v4.0.0, and only with translations
156 178
157The project sets `LOCALIZATION_PREFERS_STRING_CATALOGS = YES` but ships no 179The project sets `LOCALIZATION_PREFERS_STRING_CATALOGS = YES` but ships no
158string catalog, so every user-facing string is hardcoded English. Roughly 634 180string catalog, so every user-facing string is hardcoded English. Roughly 634
@@ -164,12 +186,19 @@ for users until translations exist. It is groundwork, and it is the largest diff
164in the roadmap — it touches nearly every view, with the regression risk that 186in the roadmap — it touches nearly every view, with the regression risk that
165implies. 187implies.
166 188
167### Accessibility 189That combination is why this is bucketed at v4.0.0 *bundled with at least one
190real translation*, rather than shipped alone. An English-only catalog would earn
191the major number on regression risk while delivering nothing — the wrong trade.
192Hold the catalog until a translation lands. If it ever ships unbundled, it is
193groundwork and belongs in a quiet minor, not a 4.0.
194
195### Accessibility — v3.11.0
168 196
169Labels and hints appear in 17 of 89 view files. Mechanical and low-risk, but it 197Labels and hints appear in 17 of 89 view files. Mechanical and low-risk, but it
170cannot be verified from a build — it needs VoiceOver driven on a device. 198cannot be verified from a build — it needs VoiceOver driven on a device.
199Independent of every other bucket, so it can move if a device pass is convenient.
171 200
172### SonarCloud backlog 201### SonarCloud backlog — v3.8.1
173 202
17451 open issues: **0 bugs, 0 vulnerabilities, 51 code smells**, plus 3 security 20351 open issues: **0 bugs, 0 vulnerabilities, 51 code smells**, plus 3 security
175hotspots. The headline number is misleading, so trust the breakdown before 204hotspots. The headline number is misleading, so trust the breakdown before
@@ -193,7 +222,11 @@ The 3 hotspots are the part actually worth thought:
193 `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` with no 222 `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` with no
194 `SecAccessControl`, so it does not require biometric or passcode 223 `SecAccessControl`, so it does not require biometric or passcode
195 authentication to read. That is a genuine product decision — should a stolen, 224 authentication to read. That is a genuine product decision — should a stolen,
196 unlocked phone hand over a sr.ht token? — not a lint nit. 225 unlocked phone hand over a sr.ht token? — not a lint nit. **Unbucketed on
226 purpose:** adding `SecAccessControl` changes what a user must do to read their
227 own token, so it needs a decision first. If the answer is yes, it is a minor
228 bump of its own — a visible auth change should not hide inside a feature
229 release.
197- `ReadmeView:1922` (**LOW**) — unrestricted WebView navigation. Probably a false 230- `ReadmeView:1922` (**LOW**) — unrestricted WebView navigation. Probably a false
198 positive: `isAllowedReadmeNavigationURL` enforces a scheme allowlist. Verify, 231 positive: `isAllowedReadmeNavigationURL` enforces a scheme allowlist. Verify,
199 then annotate. 232 then annotate.
@@ -201,7 +234,12 @@ The 3 hotspots are the part actually worth thought:
201Query it with: 234Query it with:
202`https://sonarcloud.io/api/issues/search?componentKeys=zerolabsco_hutch&resolved=false` 235`https://sonarcloud.io/api/issues/search?componentKeys=zerolabsco_hutch&resolved=false`
203 236
204### Ingest "What's cooking on SourceHut?" 237This is a patch because nothing executes differently afterwards. The 35 hardcoded-URI
238issues are resolved as *Won't Fix* in SonarCloud's web UI — not a commit at all — and
239the rest is three comments and one annotation. If it produces a diff that changes a
240runtime path, something has gone wrong.
241
242### Ingest "What's cooking on SourceHut?" — v3.9.0
205 243
206sr.ht posts a quarterly update to `~sircmpwn/sr.ht-announce`, mirrored at 244sr.ht posts a quarterly update to `~sircmpwn/sr.ht-announce`, mirrored at
207<https://sourcehut.org/blog/>. Nothing in Hutch tracks it, so the API grows and 245<https://sourcehut.org/blog/>. Nothing in Hutch tracks it, so the API grows and
@@ -227,7 +265,25 @@ flags two openings:
227Start from Q1 2026 forward — that is roughly when the current `Docs/API` dumps 265Start from Q1 2026 forward — that is roughly when the current `Docs/API` dumps
228were captured. 266were captured.
229 267
230### Swift 6 language mode 268Research does not ship, so v3.9.0 pairs the ingest with **deploy keys** — the one
269self-contained feature it has already surfaced and that the SDL confirms exists.
270That gives the release something a user can see. Everything else the ingest turns
271up gets filed, not built, and hub.sr.ht gets its own bucket below.
272
273### hub.sr.ht writes — v3.10.0, provisional
274
275Everything here rests on a single sentence in the Q2 2026 post: that hub.sr.ht
276gained a writable GraphQL API. If true, three things unblock at once —
277project writes (Hutch's projects are read-only), discovery (which `SCOPE.md`
278rules out on the grounds hub has no public API), and `mailingListSubscribe`,
279which Phase 1 declined for exactly that reason.
280
281All three live or die on the same unverified claim, which is why this is
282sequenced after the ingest rather than planned now. Read
283`api/graph/schema.graphqls` in `hub.sr.ht` before committing the version number.
284The bucket may be empty.
285
286### Swift 6 language mode — no release of its own
231 287
232The project builds in Swift 5 language mode with 288The project builds in Swift 5 language mode with
233`SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor`. Moving to Swift 6 is blocked on 289`SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor`. Moving to Swift 6 is blocked on
@@ -243,14 +299,17 @@ concurrency diagnostics that are warnings today and errors there:
243 methods, as in `HomeViewModel.loadDashboard` and 299 methods, as in `HomeViewModel.loadDashboard` and
244 `NotificationPreferencesViewModel.load`. 300 `NotificationPreferencesViewModel.load`.
245 301
246### Cache reads that bypass the client 302### Cache reads that bypass the client — no release of its own
247 303
248`BuildListViewModel`, `RepositoryListViewModel`, and `PasteService` still read 304`BuildListViewModel`, `RepositoryListViewModel`, and `PasteService` still read
249`client.responseCache` directly, each falling back across two different cache 305`client.responseCache` directly, each falling back across two different cache
250keys. That predates `APICacheKeys` and should be folded into `cachedPayload`, 306keys. That predates `APICacheKeys` and should be folded into `cachedPayload`,
251which already consults the persistent cache before the memory layer. 307which already consults the persistent cache before the memory layer.
252 308
253## Housekeeping 309Like Swift 6 above, this is internal and rides along with whatever release
310already touches that area. Neither justifies a tag.
311
312## Housekeeping — v3.8.1
254 313
255- `Hutch/Hutch/App/AccountSession.swift` sits in a stray nested directory; 314- `Hutch/Hutch/App/AccountSession.swift` sits in a stray nested directory;
256 `Hutch/HutchTests/` is empty. 315 `Hutch/HutchTests/` is empty.