Commit 30150d90cb

30150d90cbf30b2d3af720a730752ea6d3136117

parent: 302a2d2e7e

Unsigned

cmc <hello@cleberg.net> · 2026-07-16 05:03 UTC

chore: bump to 3.8.0 and record Phase 3 API features

MARKETING_VERSION 3.7.0 -> 3.8.0, build 89 -> 90.

SCOPE.md gains the items that did not survive contact with the API: archiveMessage
and mailingListSubscribe are blocked, while webhooks, shareSecret, and build
groups are reachable but declined on judgement. The reasoning is recorded so
they do not get re-proposed as gaps.

ROADMAP.md notes that Phase 3 is several releases rather than one, with the
measured size of each.

Layout: unified · split

Hutch.xcodeproj/project.pbxproj +12 −12
@@ -597,7 +597,7 @@
597 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 597 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
598 CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements; 598 CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements;
599 CODE_SIGN_STYLE = Automatic; 599 CODE_SIGN_STYLE = Automatic;
600 CURRENT_PROJECT_VERSION = 89; 600 CURRENT_PROJECT_VERSION = 90;
601 DEVELOPMENT_TEAM = ZCNAX3VL9D; 601 DEVELOPMENT_TEAM = ZCNAX3VL9D;
602 ENABLE_PREVIEWS = YES; 602 ENABLE_PREVIEWS = YES;
603 GENERATE_INFOPLIST_FILE = YES; 603 GENERATE_INFOPLIST_FILE = YES;
@@ -614,7 +614,7 @@
614 "$(inherited)", 614 "$(inherited)",
615 "@executable_path/Frameworks", 615 "@executable_path/Frameworks",
616 ); 616 );
617 MARKETING_VERSION = 3.7.0; 617 MARKETING_VERSION = 3.8.0;
618 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch; 618 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch;
619 PRODUCT_NAME = "$(TARGET_NAME)"; 619 PRODUCT_NAME = "$(TARGET_NAME)";
620 STRING_CATALOG_GENERATE_SYMBOLS = YES; 620 STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -634,7 +634,7 @@
634 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 634 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
635 CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements; 635 CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements;
636 CODE_SIGN_STYLE = Automatic; 636 CODE_SIGN_STYLE = Automatic;
637 CURRENT_PROJECT_VERSION = 89; 637 CURRENT_PROJECT_VERSION = 90;
638 DEVELOPMENT_TEAM = ZCNAX3VL9D; 638 DEVELOPMENT_TEAM = ZCNAX3VL9D;
639 ENABLE_PREVIEWS = YES; 639 ENABLE_PREVIEWS = YES;
640 GENERATE_INFOPLIST_FILE = YES; 640 GENERATE_INFOPLIST_FILE = YES;
@@ -651,7 +651,7 @@
651 "$(inherited)", 651 "$(inherited)",
652 "@executable_path/Frameworks", 652 "@executable_path/Frameworks",
653 ); 653 );
654 MARKETING_VERSION = 3.7.0; 654 MARKETING_VERSION = 3.8.0;
655 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch; 655 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch;
656 PRODUCT_NAME = "$(TARGET_NAME)"; 656 PRODUCT_NAME = "$(TARGET_NAME)";
657 STRING_CATALOG_GENERATE_SYMBOLS = YES; 657 STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -714,7 +714,7 @@
714 APPLICATION_EXTENSION_API_ONLY = YES; 714 APPLICATION_EXTENSION_API_ONLY = YES;
715 CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements; 715 CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements;
716 CODE_SIGN_STYLE = Automatic; 716 CODE_SIGN_STYLE = Automatic;
717 CURRENT_PROJECT_VERSION = 89; 717 CURRENT_PROJECT_VERSION = 90;
718 DEVELOPMENT_TEAM = ZCNAX3VL9D; 718 DEVELOPMENT_TEAM = ZCNAX3VL9D;
719 GENERATE_INFOPLIST_FILE = NO; 719 GENERATE_INFOPLIST_FILE = NO;
720 INFOPLIST_FILE = HutchWidgetExtension/Info.plist; 720 INFOPLIST_FILE = HutchWidgetExtension/Info.plist;
@@ -724,7 +724,7 @@
724 "@executable_path/Frameworks", 724 "@executable_path/Frameworks",
725 "@executable_path/../../Frameworks", 725 "@executable_path/../../Frameworks",
726 ); 726 );
727 MARKETING_VERSION = 3.7.0; 727 MARKETING_VERSION = 3.8.0;
728 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension; 728 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension;
729 PRODUCT_NAME = "$(TARGET_NAME)"; 729 PRODUCT_NAME = "$(TARGET_NAME)";
730 SKIP_INSTALL = YES; 730 SKIP_INSTALL = YES;
@@ -743,7 +743,7 @@
743 APPLICATION_EXTENSION_API_ONLY = YES; 743 APPLICATION_EXTENSION_API_ONLY = YES;
744 CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements; 744 CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements;
745 CODE_SIGN_STYLE = Automatic; 745 CODE_SIGN_STYLE = Automatic;
746 CURRENT_PROJECT_VERSION = 89; 746 CURRENT_PROJECT_VERSION = 90;
747 DEVELOPMENT_TEAM = ZCNAX3VL9D; 747 DEVELOPMENT_TEAM = ZCNAX3VL9D;
748 GENERATE_INFOPLIST_FILE = NO; 748 GENERATE_INFOPLIST_FILE = NO;
749 INFOPLIST_FILE = HutchWidgetExtension/Info.plist; 749 INFOPLIST_FILE = HutchWidgetExtension/Info.plist;
@@ -753,7 +753,7 @@
753 "@executable_path/Frameworks", 753 "@executable_path/Frameworks",
754 "@executable_path/../../Frameworks", 754 "@executable_path/../../Frameworks",
755 ); 755 );
756 MARKETING_VERSION = 3.7.0; 756 MARKETING_VERSION = 3.8.0;
757 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension; 757 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension;
758 PRODUCT_NAME = "$(TARGET_NAME)"; 758 PRODUCT_NAME = "$(TARGET_NAME)";
759 SKIP_INSTALL = YES; 759 SKIP_INSTALL = YES;
@@ -772,7 +772,7 @@
772 APPLICATION_EXTENSION_API_ONLY = YES; 772 APPLICATION_EXTENSION_API_ONLY = YES;
773 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 773 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
774 CODE_SIGN_STYLE = Automatic; 774 CODE_SIGN_STYLE = Automatic;
775 CURRENT_PROJECT_VERSION = 89; 775 CURRENT_PROJECT_VERSION = 90;
776 DEVELOPMENT_TEAM = ZCNAX3VL9D; 776 DEVELOPMENT_TEAM = ZCNAX3VL9D;
777 GENERATE_INFOPLIST_FILE = NO; 777 GENERATE_INFOPLIST_FILE = NO;
778 INFOPLIST_FILE = HutchSafariExtension/Info.plist; 778 INFOPLIST_FILE = HutchSafariExtension/Info.plist;
@@ -782,7 +782,7 @@
782 "@executable_path/Frameworks", 782 "@executable_path/Frameworks",
783 "@executable_path/../../Frameworks", 783 "@executable_path/../../Frameworks",
784 ); 784 );
785 MARKETING_VERSION = 3.7.0; 785 MARKETING_VERSION = 3.8.0;
786 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchSafariExtension; 786 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchSafariExtension;
787 PRODUCT_NAME = "$(TARGET_NAME)"; 787 PRODUCT_NAME = "$(TARGET_NAME)";
788 SKIP_INSTALL = YES; 788 SKIP_INSTALL = YES;
@@ -801,7 +801,7 @@
801 APPLICATION_EXTENSION_API_ONLY = YES; 801 APPLICATION_EXTENSION_API_ONLY = YES;
802 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 802 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
803 CODE_SIGN_STYLE = Automatic; 803 CODE_SIGN_STYLE = Automatic;
804 CURRENT_PROJECT_VERSION = 89; 804 CURRENT_PROJECT_VERSION = 90;
805 DEVELOPMENT_TEAM = ZCNAX3VL9D; 805 DEVELOPMENT_TEAM = ZCNAX3VL9D;
806 GENERATE_INFOPLIST_FILE = NO; 806 GENERATE_INFOPLIST_FILE = NO;
807 INFOPLIST_FILE = HutchSafariExtension/Info.plist; 807 INFOPLIST_FILE = HutchSafariExtension/Info.plist;
@@ -811,7 +811,7 @@
811 "@executable_path/Frameworks", 811 "@executable_path/Frameworks",
812 "@executable_path/../../Frameworks", 812 "@executable_path/../../Frameworks",
813 ); 813 );
814 MARKETING_VERSION = 3.7.0; 814 MARKETING_VERSION = 3.8.0;
815 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchSafariExtension; 815 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchSafariExtension;
816 PRODUCT_NAME = "$(TARGET_NAME)"; 816 PRODUCT_NAME = "$(TARGET_NAME)";
817 SKIP_INSTALL = YES; 817 SKIP_INSTALL = YES;
README.md +4 −1
@@ -14,13 +14,16 @@ The app currently includes:
14- Home dashboard with assigned tickets, recent builds, and projects 14- Home dashboard with assigned tickets, recent builds, and projects
15- Repository browsing for Git and Mercurial repositories 15- Repository browsing for Git and Mercurial repositories
16- Repository details including README, references, commits, diffs, files, artifacts, and settings 16- Repository details including README, references, commits, diffs, files, artifacts, and settings
17- Artifact upload and deletion on release tags
17- Tracker and ticket browsing, ticket detail views, and tracker creation 18- Tracker and ticket browsing, ticket detail views, and tracker creation
18- Ticket editing and deletion, with subscriptions for tickets and trackers 19- Ticket editing and deletion, with subscriptions for tickets and trackers
19- Build job browsing, build detail views, and build submission 20- Build job browsing, build detail views, and build submission
20- Inbox and mailing list reading flows 21- Inbox and mailing list reading flows
21- Patchset review: cover letters, per-patch diffs, checks, version chains, and status changes 22- Patchset review: cover letters, per-patch diffs, checks, version chains, and status changes
23- Mailing list creation, settings, and deletion
24- Ticket activity feed across every tracker you follow
22- Paste browsing, creation, and detail views 25- Paste browsing, creation, and detail views
23- Profile and account settings, including SSH keys, PGP keys, and personal access token management 26- Profile and account settings, including SSH keys, PGP keys, personal access token management, and the audit log
24- Email preferences for todo.sr.ht and lists.sr.ht 27- Email preferences for todo.sr.ht and lists.sr.ht
25- Deep links for repositories, tickets, and build jobs 28- Deep links for repositories, tickets, and build jobs
26 29
ROADMAP.md +34 −16
@@ -131,22 +131,40 @@ GraphQL mutation. Treat that boundary as explicit rather than half-building it.
131 131
132## Phase 3: Polish and reach 132## Phase 3: Polish and reach
133 133
134- **Localization.** The project sets `LOCALIZATION_PREFERS_STRING_CATALOGS = 134Unlike Phases 1 and 2, this is not one shippable thing. It is several, and they
135 YES` but ships no string catalog, so every user-facing string is hardcoded 135are sized very differently — measure before committing to one.
136 English. 136
137- **Accessibility.** Labels and hints appear in only 16 of roughly 130 view 137### API features — done (v3.8.0)
138 files. 138
139- `uploadArtifact` / `deleteArtifact` — artifacts are read-only today. 139- ~~`uploadArtifact` / `deleteArtifact`~~ — artifacts were read-only.
140- Webhook management. Zero calls to any `create*Webhook` across every service. 140- ~~`auditLog` (meta.sr.ht)~~ — surfaced under the tokens in Profile.
141 Push notifications are out of scope because they need a relay server (see 141- ~~Mailing list creation and settings~~ (`createMailingList`,
142 [SCOPE.md](SCOPE.md)), but webhook management is client-side only and is a 142 `updateMailingList`, `deleteMailingList`).
143 prerequisite if that relay ever ships. 143- ~~`events` feed (todo.sr.ht)~~ — a ticket activity feed under More.
144- `auditLog` (meta.sr.ht) — unused security surface. 144
145- Build groups (`createGroup`, `startGroup`) and secret management 145Four of the six planned. The other two did not survive contact:
146 (`shareSecret`, the `secrets` query). Today `secrets` is only a submit toggle. 146
147- Mailing list creation and settings (`createMailingList`, `updateMailingList`, 147- `archiveMessage` is `@internal` and inaccessible.
148 `deleteMailingList`). 148- Webhook management, `shareSecret`, and build groups are reachable but declined
149- `events` feed (todo.sr.ht) and `archiveMessage` (lists.sr.ht). 149 on judgement — see [SCOPE.md](SCOPE.md) for the reasoning, so they do not get
150 re-proposed.
151
152### Localization
153
154The project sets `LOCALIZATION_PREFERS_STRING_CATALOGS = YES` but ships no
155string catalog, so every user-facing string is hardcoded English. Roughly 634
156literals: 239 `Text(`, 150 `Label(`, 117 `Button(`, 77 `Section(`, 51
157`navigationTitle(`.
158
159Worth knowing before starting: a catalog containing only English changes nothing
160for users until translations exist. It is groundwork, and it is the largest diff
161in the roadmap — it touches nearly every view, with the regression risk that
162implies.
163
164### Accessibility
165
166Labels and hints appear in 17 of 89 view files. Mechanical and low-risk, but it
167cannot be verified from a build — it needs VoiceOver driven on a device.
150 168
151### Swift 6 language mode 169### Swift 6 language mode
152 170
SCOPE.md +21
@@ -7,3 +7,24 @@
7- Explore / search (hub.sr.ht) (no public discovery API) 7- Explore / search (hub.sr.ht) (no public discovery API)
8- Pronouns on profile (not in GraphQL schema) 8- Pronouns on profile (not in GraphQL schema)
9- Revoke personal access tokens (`@internal` in schema, inaccessible) 9- Revoke personal access tokens (`@internal` in schema, inaccessible)
10- Archive a message to a list (`archiveMessage` is `@internal`, inaccessible)
11- Subscribe to a mailing list (`mailingListSubscribe` exists, but `MailingList`
12 has no `subscription` field and sr.ht has no discovery API, so there is no way
13 to find a list you are not already subscribed to — see hub.sr.ht above)
14- Submitting patches (a `git send-email` flow, not a GraphQL mutation; Hutch
15 reviews patchsets but cannot send them)
16
17## Declined rather than blocked
18
19These are reachable in the API. They are left out on judgement, not capability.
20
21- **Webhook management** (24 fields across five services). A webhook needs an
22 HTTPS endpoint you control to receive POSTs. Without the relay above, this
23 only serves someone already running their own endpoint, and that person is not
24 managing it from a phone. Reconsider if `hutch-notify` ever ships.
25- **`shareSecret`.** Shares a build secret — an SSH key or PAT — with another
26 user. A mistap grants someone else a credential, and nothing in the app can
27 take it back. That belongs on the web behind a full-size confirmation. The
28 read-only `secrets` list would be fine on its own.
29- **Build groups** (`createGroup`, `startGroup`). Multi-job pipelines are
30 authored in `.build.yml`, not composed on a phone.