Commit d5dec7be8a
Unsigned
Layout: unified · split
ROADMAP.md +67 −8
| @@ -134,6 +134,28 @@ GraphQL mutation. Treat that boundary as explicit rather than half-building it. | |||
| 134 | Unlike Phases 1 and 2, this is not one shippable thing. It is several, and they | 134 | Unlike Phases 1 and 2, this is not one shippable thing. It is several, and they |
| 135 | are sized very differently — measure before committing to one. | 135 | are sized very differently — measure before committing to one. |
| 136 | 136 | ||
| 137 | ### Release plan | ||
| 138 | |||
| 139 | Hutch is an app with a `MARKETING_VERSION`, not a library with an API contract, | ||
| 140 | so "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 | |||
| 151 | Ordering is by dependency, not size. v3.9.0 leads because it is the only item | ||
| 152 | that corrects the others' inputs: the ingest's real output is a `SCOPE.md` that | ||
| 153 | is true, and v3.10.0 rests entirely on one unverified sentence in a blog post. | ||
| 154 | Do not commit v3.10.0's number until the SDL has been read — the bucket may turn | ||
| 155 | out 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 | ||
| 157 | The project sets `LOCALIZATION_PREFERS_STRING_CATALOGS = YES` but ships no | 179 | The project sets `LOCALIZATION_PREFERS_STRING_CATALOGS = YES` but ships no |
| 158 | string catalog, so every user-facing string is hardcoded English. Roughly 634 | 180 | string 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 | |||
| 164 | in the roadmap — it touches nearly every view, with the regression risk that | 186 | in the roadmap — it touches nearly every view, with the regression risk that |
| 165 | implies. | 187 | implies. |
| 166 | 188 | ||
| 167 | ### Accessibility | 189 | That combination is why this is bucketed at v4.0.0 *bundled with at least one |
| 190 | real translation*, rather than shipped alone. An English-only catalog would earn | ||
| 191 | the major number on regression risk while delivering nothing — the wrong trade. | ||
| 192 | Hold the catalog until a translation lands. If it ever ships unbundled, it is | ||
| 193 | groundwork and belongs in a quiet minor, not a 4.0. | ||
| 194 | |||
| 195 | ### Accessibility — v3.11.0 | ||
| 168 | 196 | ||
| 169 | Labels and hints appear in 17 of 89 view files. Mechanical and low-risk, but it | 197 | Labels and hints appear in 17 of 89 view files. Mechanical and low-risk, but it |
| 170 | cannot be verified from a build — it needs VoiceOver driven on a device. | 198 | cannot be verified from a build — it needs VoiceOver driven on a device. |
| 199 | Independent 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 | ||
| 174 | 51 open issues: **0 bugs, 0 vulnerabilities, 51 code smells**, plus 3 security | 203 | 51 open issues: **0 bugs, 0 vulnerabilities, 51 code smells**, plus 3 security |
| 175 | hotspots. The headline number is misleading, so trust the breakdown before | 204 | hotspots. 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: | |||
| 201 | Query it with: | 234 | Query 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?" | 237 | This is a patch because nothing executes differently afterwards. The 35 hardcoded-URI |
| 238 | issues are resolved as *Won't Fix* in SonarCloud's web UI — not a commit at all — and | ||
| 239 | the rest is three comments and one annotation. If it produces a diff that changes a | ||
| 240 | runtime path, something has gone wrong. | ||
| 241 | |||
| 242 | ### Ingest "What's cooking on SourceHut?" — v3.9.0 | ||
| 205 | 243 | ||
| 206 | sr.ht posts a quarterly update to `~sircmpwn/sr.ht-announce`, mirrored at | 244 | sr.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: | |||
| 227 | Start from Q1 2026 forward — that is roughly when the current `Docs/API` dumps | 265 | Start from Q1 2026 forward — that is roughly when the current `Docs/API` dumps |
| 228 | were captured. | 266 | were captured. |
| 229 | 267 | ||
| 230 | ### Swift 6 language mode | 268 | Research does not ship, so v3.9.0 pairs the ingest with **deploy keys** — the one |
| 269 | self-contained feature it has already surfaced and that the SDL confirms exists. | ||
| 270 | That gives the release something a user can see. Everything else the ingest turns | ||
| 271 | up gets filed, not built, and hub.sr.ht gets its own bucket below. | ||
| 272 | |||
| 273 | ### hub.sr.ht writes — v3.10.0, provisional | ||
| 274 | |||
| 275 | Everything here rests on a single sentence in the Q2 2026 post: that hub.sr.ht | ||
| 276 | gained a writable GraphQL API. If true, three things unblock at once — | ||
| 277 | project writes (Hutch's projects are read-only), discovery (which `SCOPE.md` | ||
| 278 | rules out on the grounds hub has no public API), and `mailingListSubscribe`, | ||
| 279 | which Phase 1 declined for exactly that reason. | ||
| 280 | |||
| 281 | All three live or die on the same unverified claim, which is why this is | ||
| 282 | sequenced after the ingest rather than planned now. Read | ||
| 283 | `api/graph/schema.graphqls` in `hub.sr.ht` before committing the version number. | ||
| 284 | The bucket may be empty. | ||
| 285 | |||
| 286 | ### Swift 6 language mode — no release of its own | ||
| 231 | 287 | ||
| 232 | The project builds in Swift 5 language mode with | 288 | The 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 |
| 250 | keys. That predates `APICacheKeys` and should be folded into `cachedPayload`, | 306 | keys. That predates `APICacheKeys` and should be folded into `cachedPayload`, |
| 251 | which already consults the persistent cache before the memory layer. | 307 | which already consults the persistent cache before the memory layer. |
| 252 | 308 | ||
| 253 | ## Housekeeping | 309 | Like Swift 6 above, this is internal and rides along with whatever release |
| 310 | already 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. |