<feed xmlns='http://www.w3.org/2005/Atom'>
<title>hutch.git/Hutch, branch v3.5.0</title>
<subtitle>ios client for sourcehut. swiftui.
</subtitle>
<id>http://git.krz.sh/krz/hutch.git/atom?h=v3.5.0</id>
<link rel='self' href='http://git.krz.sh/krz/hutch.git/atom?h=v3.5.0'/>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/hutch.git/'/>
<updated>2026-07-16T00:31:17+00:00</updated>
<entry>
<title>fix: give inbox threads identity distinct from their grouping key</title>
<updated>2026-07-16T00:31:17+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-16T00:31:17+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/hutch.git/commit/?id=eff81f34bb51ecc6e0aa5d831567db2cea0527a9'/>
<id>urn:sha1:eff81f34bb51ecc6e0aa5d831567db2cea0527a9</id>
<content type='text'>
InboxThreadSummary.id returned threadGroupingKey, which is listRID plus the
subject with Re:/Fwd: stripped. Two unrelated threads on one list sharing a
subject therefore shared an id — common on sourcehut, where "[PATCH] test" is
an ordinary subject — which collides under Identifiable in every list that
renders these summaries.

Key id on the root Message-ID, which is unique per thread, and leave
threadGroupingKey subject-based so replies still collapse into one
conversation.

Read state moves to threadGroupingKey at each call site. It was already
keyed on that string via id, so persisted keys are unchanged and marking a
conversation read still covers the whole subject group, matching how
HomeViewModel already builds the key for isUnread.
</content>
</entry>
<entry>
<title>fix: render code span contents literally</title>
<updated>2026-07-16T00:29:57+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-16T00:29:57+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/hutch.git/commit/?id=c5247f7021090358e8db70daa9ed09521e9f206a'/>
<id>urn:sha1:c5247f7021090358e8db70daa9ed09521e9f206a</id>
<content type='text'>
processInline protected allowlisted HTML tags before it handled code spans, so
a `&lt;b&gt;` written inside backticks was carried through as a live tag and applied
formatting instead of rendering as text. Every other inline pass ran against
code span contents for the same reason, so `**x**` in backticks was emitted as
bold.

Protect code spans first with their contents escaped, which takes them out of
reach of the tag, emphasis, and link passes.
</content>
</entry>
<entry>
<title>fix: stop shadowing serviceNotProvisioned classification</title>
<updated>2026-07-16T00:26:27+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-16T00:26:27+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/hutch.git/commit/?id=3c99c01ad1c0c855ef1793d4b74a9c77e7445811'/>
<id>urn:sha1:3c99c01ad1c0c855ef1793d4b74a9c77e7445811</id>
<content type='text'>
"No such repository or user found" matched the broad "no such" test for
.notFound, which ran first and made the .serviceNotProvisioned rule below it
unreachable. Users hitting a service they have not activated were told the
content was no longer available rather than that the account needs to enable
the service.

Order the specific check ahead of the general one.
</content>
</entry>
<entry>
<title>fix: allow clearing a repository description</title>
<updated>2026-07-16T00:25:27+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-16T00:25:27+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/hutch.git/commit/?id=7ffef07278e94bd3c268ee5cbd5c9c11cbfa3270'/>
<id>urn:sha1:7ffef07278e94bd3c268ee5cbd5c9c11cbfa3270</id>
<content type='text'>
metadataInputForSave assigned nil through the dictionary subscript to clear a
description. Swift removes the key on a nil subscript assignment, so the
mutation was sent with no `description` field and the old value survived —
clearing a description silently did nothing.

Store the nil with updateValue so the key is retained; AnyCodable already
encodes an unmatched value as a JSON null.
</content>
</entry>
<entry>
<title>feat: add App Intents for Hutch navigation</title>
<updated>2026-05-14T18:27:41+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-05-14T18:18:55+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/hutch.git/commit/?id=51b2eb0e6296734960f37cebd40fee34a1f261f9'/>
<id>urn:sha1:51b2eb0e6296734960f37cebd40fee34a1f261f9</id>
<content type='text'>
- add read-only App Intents for core Hutch workflows
- route intents through the existing Hutch navigation/deep-link model
- expose Work Queue, Recent Activity, System Status, pinned resources, projects, failed builds, assigned tickets, saved searches, and search where supported
- keep App Intents non-mutating for the initial implementation
- preserve existing widget, Safari extension, and hutch:// routing behavior

References: https://todo.sr.ht/~ccleberg/hutch/71
</content>
</entry>
<entry>
<title>feat: add Safari extension for sourcehut deep links</title>
<updated>2026-05-14T17:41:55+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-05-14T17:41:55+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/hutch.git/commit/?id=0d61e05e98f6f020722ee4d43f024c1da348d2a9'/>
<id>urn:sha1:0d61e05e98f6f020722ee4d43f024c1da348d2a9</id>
<content type='text'>
</content>
</entry>
<entry>
<title>feat(cache): persist read-only API responses</title>
<updated>2026-05-07T01:41:49+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-05-07T01:41:49+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/hutch.git/commit/?id=205cf2687ce48f3d004792ff223422a86cc6bdbc'/>
<id>urn:sha1:205cf2687ce48f3d004792ff223422a86cc6bdbc</id>
<content type='text'>
Add a bounded stale-while-revalidate cache at the Sourcehut API boundary with
stable keys, centralized TTLs, request coalescing, payload hashing, and LRU disk
pruning.

Cache high-value read-only repo, build, ticket, project, profile, paste, and
Home/Work Queue data while keeping mutations network-only and invalidating
related prefixes after successful writes.

Add focused cache tests and implementation notes.
</content>
</entry>
<entry>
<title>feat: add persistent stale-while-revalidate API cache</title>
<updated>2026-05-07T01:30:34+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-05-07T01:30:34+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/hutch.git/commit/?id=57e4f34b4613c09beb0cb757ac2ba2b43cc04daf'/>
<id>urn:sha1:57e4f34b4613c09beb0cb757ac2ba2b43cc04daf</id>
<content type='text'>
Introduce an actor-backed persistent cache layer at the SRHTClient boundary
for read-only SourceHut data. Cache entries now store stable metadata
including key, resource type, fetched/expires/access timestamps, payload
hash, schema version, and payload size, with bounded memory and disk usage.

Add centralized cache key builders and TTL defaults for repository, file,
ticket, build, log, profile, status, and list-style resources. Support
networkOnly, cacheOnly, cacheFirstThenRefresh, and refreshIgnoringCache
policies, plus request coalescing for duplicate in-flight cache keys.

Integrate first-pass caching into high-value low-risk read paths:
- build detail and completed/active build logs
- ticket detail
- README lookup
- repository tree, blob, and linked file reads

Keep mutation paths network-only and add simple prefix invalidation after
ticket and build mutations. Add compact cached/stale UI status rows and a
Settings action to clear the persistent cache.

Add focused cache tests covering round trips, expiration, stale fallback,
policy behavior, request coalescing, prefix invalidation, size limits, LRU
pruning, expired pruning, and mutation bypass behavior. Document storage,
key, TTL, invalidation, limitations, and next recommended targets.
</content>
</entry>
<entry>
<title>Fix broken personal access token link</title>
<updated>2026-05-04T19:55:44+00:00</updated>
<author>
<name>Chris DeLuca</name>
<email>code@chrisdeluca.me</email>
</author>
<published>2026-05-04T18:39:01+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/hutch.git/commit/?id=0b93fb3cd7613a2befeebb94b41d051b70f6da28'/>
<id>urn:sha1:0b93fb3cd7613a2befeebb94b41d051b70f6da28</id>
<content type='text'>
</content>
</entry>
<entry>
<title>fix: profile loading error and user-timeline prefs update</title>
<updated>2026-04-23T22:39:13+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-04-23T22:39:13+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/hutch.git/commit/?id=66601a8091a73f3e31f4fb534c38b3eca4756e93'/>
<id>urn:sha1:66601a8091a73f3e31f4fb534c38b3eca4756e93</id>
<content type='text'>
</content>
</entry>
</feed>
