Commit d0156e05fd

d0156e05fde8d489ef45f8637c92a352a9e89a3c

parent: 9fa0534e7d

Unsigned

cmc <hello@cleberg.net> · 2026-08-07 09:27 UTC

Bump to 3.9.0; reflect shipped work in roadmap

Cut 3.9.0 (build 94), bundling this session's features. Update ROADMAP.txt:
mark hub.sr.ht project writes + discovery (#12-#15) done, correct the Q2
ingest note and App Intent gaps, and reconcile the release plan (3.9.0 is
the shipped cut; ingest/deploy keys move to 3.10.0).

Layout: unified · split

Hutch.xcodeproj/project.pbxproj +12 −12
@@ -597,7 +597,7 @@
597597 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
598598 CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements;
599599 CODE_SIGN_STYLE = Automatic;
600 CURRENT_PROJECT_VERSION = 93;
600 CURRENT_PROJECT_VERSION = 94;
601601 DEVELOPMENT_TEAM = ZCNAX3VL9D;
602602 ENABLE_PREVIEWS = YES;
603603 GENERATE_INFOPLIST_FILE = YES;
@@ -614,7 +614,7 @@
614614 "$(inherited)",
615615 "@executable_path/Frameworks",
616616 );
617 MARKETING_VERSION = 3.8.5;
617 MARKETING_VERSION = 3.9.0;
618618 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch;
619619 PRODUCT_NAME = "$(TARGET_NAME)";
620620 STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -634,7 +634,7 @@
634634 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
635635 CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements;
636636 CODE_SIGN_STYLE = Automatic;
637 CURRENT_PROJECT_VERSION = 93;
637 CURRENT_PROJECT_VERSION = 94;
638638 DEVELOPMENT_TEAM = ZCNAX3VL9D;
639639 ENABLE_PREVIEWS = YES;
640640 GENERATE_INFOPLIST_FILE = YES;
@@ -651,7 +651,7 @@
651651 "$(inherited)",
652652 "@executable_path/Frameworks",
653653 );
654 MARKETING_VERSION = 3.8.5;
654 MARKETING_VERSION = 3.9.0;
655655 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch;
656656 PRODUCT_NAME = "$(TARGET_NAME)";
657657 STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -714,7 +714,7 @@
714714 APPLICATION_EXTENSION_API_ONLY = YES;
715715 CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements;
716716 CODE_SIGN_STYLE = Automatic;
717 CURRENT_PROJECT_VERSION = 93;
717 CURRENT_PROJECT_VERSION = 94;
718718 DEVELOPMENT_TEAM = ZCNAX3VL9D;
719719 GENERATE_INFOPLIST_FILE = NO;
720720 INFOPLIST_FILE = HutchWidgetExtension/Info.plist;
@@ -724,7 +724,7 @@
724724 "@executable_path/Frameworks",
725725 "@executable_path/../../Frameworks",
726726 );
727 MARKETING_VERSION = 3.8.5;
727 MARKETING_VERSION = 3.9.0;
728728 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension;
729729 PRODUCT_NAME = "$(TARGET_NAME)";
730730 SKIP_INSTALL = YES;
@@ -743,7 +743,7 @@
743743 APPLICATION_EXTENSION_API_ONLY = YES;
744744 CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements;
745745 CODE_SIGN_STYLE = Automatic;
746 CURRENT_PROJECT_VERSION = 93;
746 CURRENT_PROJECT_VERSION = 94;
747747 DEVELOPMENT_TEAM = ZCNAX3VL9D;
748748 GENERATE_INFOPLIST_FILE = NO;
749749 INFOPLIST_FILE = HutchWidgetExtension/Info.plist;
@@ -753,7 +753,7 @@
753753 "@executable_path/Frameworks",
754754 "@executable_path/../../Frameworks",
755755 );
756 MARKETING_VERSION = 3.8.5;
756 MARKETING_VERSION = 3.9.0;
757757 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension;
758758 PRODUCT_NAME = "$(TARGET_NAME)";
759759 SKIP_INSTALL = YES;
@@ -772,7 +772,7 @@
772772 APPLICATION_EXTENSION_API_ONLY = YES;
773773 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
774774 CODE_SIGN_STYLE = Automatic;
775 CURRENT_PROJECT_VERSION = 93;
775 CURRENT_PROJECT_VERSION = 94;
776776 DEVELOPMENT_TEAM = ZCNAX3VL9D;
777777 GENERATE_INFOPLIST_FILE = NO;
778778 INFOPLIST_FILE = HutchSafariExtension/Info.plist;
@@ -782,7 +782,7 @@
782782 "@executable_path/Frameworks",
783783 "@executable_path/../../Frameworks",
784784 );
785 MARKETING_VERSION = 3.8.5;
785 MARKETING_VERSION = 3.9.0;
786786 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchSafariExtension;
787787 PRODUCT_NAME = "$(TARGET_NAME)";
788788 SKIP_INSTALL = YES;
@@ -801,7 +801,7 @@
801801 APPLICATION_EXTENSION_API_ONLY = YES;
802802 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
803803 CODE_SIGN_STYLE = Automatic;
804 CURRENT_PROJECT_VERSION = 93;
804 CURRENT_PROJECT_VERSION = 94;
805805 DEVELOPMENT_TEAM = ZCNAX3VL9D;
806806 GENERATE_INFOPLIST_FILE = NO;
807807 INFOPLIST_FILE = HutchSafariExtension/Info.plist;
@@ -811,7 +811,7 @@
811811 "@executable_path/Frameworks",
812812 "@executable_path/../../Frameworks",
813813 );
814 MARKETING_VERSION = 3.8.5;
814 MARKETING_VERSION = 3.9.0;
815815 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchSafariExtension;
816816 PRODUCT_NAME = "$(TARGET_NAME)";
817817 SKIP_INSTALL = YES;
ROADMAP.txt +41 −26
@@ -143,17 +143,19 @@ so "breaking change" does not apply. These buckets track *user-visible scale*.
143143| --- | --- | --- |
144144| v3.8.1 | SonarCloud triage; housekeeping | No behaviour change at all |
145145| v3.8.2 | Home system status moved to a title-bar status badge | Small UI relocation, no new surface |
146| v3.9.0 | "What's cooking" ingest; doc truth-up; deploy keys | Ships one feature, corrects the map |
147| v3.10.0 | hub.sr.ht writes: projects, discovery, `mailingListSubscribe` | Provisional — gated on what v3.9.0 finds |
146| v3.9.0 | ~~hub.sr.ht project writes + discovery (#12–#15); multi-language highlighting (#16); App Intents expansion (#17); man-page catalog sync (#7); checklist / recent-activity / pull-to-refresh fixes (#18, #11, #9)~~ | Shipped — the cut this session |
147| v3.10.0 | "What's cooking" ingest; doc truth-up; deploy keys; revisit `mailingListSubscribe` | Ships one feature, corrects the map |
148148| v3.11.0 | Accessibility | Independent, device-verified |
149149| v4.0.0 | Localization *with* translations | The only true re-presentation |
150150| — | Swift 6 language mode; cache reads | Internal; ride along, no tag |
151151
152Ordering is by dependency, not size. v3.9.0 leads because it is the only item
153that corrects the others' inputs: the ingest's real output is a `SCOPE.txt` that
154is true, and v3.10.0 rests entirely on one unverified sentence in a blog post.
155Do not commit v3.10.0's number until the SDL has been read — the bucket may turn
156out to be empty, which is the point of sequencing it second.
1523.9.0 was cut this session, bundling the hub.sr.ht writes with the other
153features listed. That reorders the original plan: the "What's cooking" ingest
154and deploy keys — once slated for 3.9.0 — move to 3.10.0, and the hub.sr.ht
155writes that were provisionally 3.10.0 landed early, because the SDL, once
156actually read, turned out to have the mutations (it was not the empty bucket the
157sequencing had guarded against). The ingest still leads 3.10.0: its real output
158is a `SCOPE.txt` that is true.
157159
158160`KeychainHelper` is deliberately unbucketed; see the SonarCloud hotspots below.
159161
@@ -290,10 +292,11 @@ and it has been discouraging work that is in fact already done.
290292flags two openings:
291293
292294- **hub.sr.ht gained a writable GraphQL API** for managing projects and project
293 resources. Hutch's projects are read-only, and `SCOPE.txt` still rules out
294 discovery on the grounds that hub has no public API. Both claims need
295 rechecking — this may also unblock `mailingListSubscribe`, which Phase 1 left
296 out for exactly that reason.
295 resources. ~~Rechecked and shipped~~: project create/edit, resource
296 link/unlink, and public discovery landed (#12–#15) — see "hub.sr.ht writes"
297 below. `SCOPE.txt` still needs its "hub has no public API / no discovery"
298 claim corrected. `mailingListSubscribe` was *not* unblocked — that needs a
299 per-list subscription field lists.sr.ht still lacks.
297300- **git.sr.ht deploy keys are complete** (`createDeployKey` / `deleteDeployKey`
298301 are in the SDL). Hutch never calls them.
299302
@@ -305,18 +308,28 @@ self-contained feature it has already surfaced and that the SDL confirms exists.
305308That gives the release something a user can see. Everything else the ingest turns
306309up gets filed, not built, and hub.sr.ht gets its own bucket below.
307310
308### hub.sr.ht writes — v3.10.0, provisional
311### hub.sr.ht writes — projects and discovery done
309312
310Everything here rests on a single sentence in the Q2 2026 post: that hub.sr.ht
311gained a writable GraphQL API. If true, three things unblock at once —
312project writes (Hutch's projects are read-only), discovery (which `SCOPE.txt`
313rules out on the grounds hub has no public API), and `mailingListSubscribe`,
314which Phase 1 declined for exactly that reason.
313Reading `api/graph/schema.graphqls` in `hub.sr.ht` settled the Q2 2026 claim:
314the master schema does expose the project write API — `createProject`,
315`updateProject`, `deleteProject`, the `link*` / `unlink*` resource mutations,
316and a public `projects` discovery query. Two of the three items this bucket
317tracked shipped against it:
315318
316All three live or die on the same unverified claim, which is why this is
317sequenced after the ingest rather than planned now. Read
318`api/graph/schema.graphqls` in `hub.sr.ht` before committing the version number.
319The bucket may be empty.
319- ~~Project writes~~ — create (#13), edit (#14), and manage linked
320 repositories, trackers, and mailing lists (#15).
321- ~~Discovery~~ — a browsable directory of public projects (#12).
322
323Built against the master SDL; live deployment on `sr.ht/query` could not be
324confirmed without a token (introspection there is auth-gated), so the mutations
325degrade to a visible error rather than a crash if a field is not yet deployed.
326Verify on a signed-in device.
327
328`mailingListSubscribe` stays out. `MailingList` still has no `subscription`
329field, and public *project* discovery does not help find a mailing list the user
330is *not* subscribed to — the reason Phase 1 declined it. Revisit only if
331lists.sr.ht gains per-list subscription state. `SCOPE.txt` still needs its
332"hub has no public API / no discovery" claim corrected.
320333
321334### App Intent gaps — unscheduled
322335
@@ -326,11 +339,13 @@ search/persistence layer — so neither is schedulable until that lands. (These
326339were the two `swift:S1135` TODOs; promoted here so the code carries a reference
327340rather than a bare `TODO`.)
328341
329- **Global content search.** `SearchHutchIntent` accepts a query but routes to
330 the Lookup screen — sourcehut entity resolution — because Hutch has no
331 full-text search across tickets, repos, and lists. Its own description says
332 "Opens Hutch lookup with a search query." When a real search exists, repoint
333 the `.search` route in `SearchHutchIntent.route`.
342- **Global content search.** `SearchHutchIntent` accepts a query — and now a
343 search *type* (#17) — but still routes to the Lookup screen, sourcehut entity
344 resolution, because Hutch has no full-text search across tickets, repos, and
345 lists. When a real search exists, repoint the `.search` route in
346 `SearchHutchIntent.route`. (#17 also completed Check Status / Check Builds
347 dialogs and added the Clear Recent Activity and Unpin Resource mutating
348 intents; those were shipped, not gaps.)
334349- **`OpenSavedSearchIntent`.** Saved searches are per-tracker only
335350 (`TicketSavedFilterStore`, `ScopedSearchHistoryStore`); there is no global
336351 saved-search store for an intent to open. Add the intent once global