<feed xmlns='http://www.w3.org/2005/Atom'>
<title>pearl/tests, branch main</title>
<subtitle>Pearl Edits and Reflects Linear — manage Linear.app issues as org-mode in Emacs
</subtitle>
<id>https://git.cjennings.net/pearl/atom?h=main</id>
<link rel='self' href='https://git.cjennings.net/pearl/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/pearl/'/>
<updated>2026-05-24T22:21:29+00:00</updated>
<entry>
<title>feat: make comment sort order configurable, newest-first by default</title>
<updated>2026-05-24T22:21:29+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T22:21:29+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/pearl/commit/?id=cc599d3e9d37c200d49f723ff8e8ce694acd33f8'/>
<id>urn:sha1:cc599d3e9d37c200d49f723ff8e8ce694acd33f8</id>
<content type='text'>
I added pearl-comment-sort-order to choose how an issue's comments render under the Comments heading: newest-first (the default, most recent on top) or oldest-first (chronological, like an email thread). Both the render sort and the add-comment insertion point honor it, so a new comment lands at the top for newest-first and at the bottom for oldest-first.
</content>
</entry>
<entry>
<title>feat: compose comments and descriptions in an Org buffer</title>
<updated>2026-05-24T22:12:52+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T22:12:52+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/pearl/commit/?id=bb30b0f9146722b279832a991540917c3fa3cb81'/>
<id>urn:sha1:bb30b0f9146722b279832a991540917c3fa3cb81</id>
<content type='text'>
The minibuffer is too cramped to write a real comment or description. I added a shared compose buffer: a focused Org buffer with a read-only instructional header at the top (like a git commit template) and an editable body below, where C-c C-c submits and C-c C-k cancels. It's the sibling of the smerge conflict buffer, built the same way.

Two commands use it. pearl-add-comment, run interactively, now opens the composer and converts the Org body to Markdown before sending. Called with an explicit body (tests, programmatic callers), it still sends that directly. pearl-compose-current-description is new: it pops the issue's current description into the composer and, on submit, writes it back into the body and syncs through the existing conflict gate. Both work from anywhere inside an issue subtree.

The header is genuinely uneditable: read-only text properties, with only the last character rear-nonsticky so the body stays editable while edits inside the header are refused. The body is everything below it, extracted by a marker.

I left pearl-new-issue on the minibuffer for now. Wiring its description into the composer means restructuring that long, untested interactive flow to defer the create into the submit callback, which is worth doing on its own rather than riding along here. Filed as a follow-up.
</content>
</entry>
<entry>
<title>feat: render recent comments in the bulk issue list</title>
<updated>2026-05-24T21:58:49+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T21:58:49+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/pearl/commit/?id=424d7048d5450131283f6bdb99822aa6bccd6b16'/>
<id>urn:sha1:424d7048d5450131283f6bdb99822aa6bccd6b16</id>
<content type='text'>
A populated list used to look like nothing had comments, because only a single-issue refresh fetched them. The bulk and Custom View queries now pull each issue's most recent comments and render them under the Comments heading, with a marker showing how many are shown against the total: 💬 5/18, or 💬 5/25+ once the count passes the cap.

Linear's API has no comment total (no commentCount on Issue, no totalCount on the connection), so I fetch one more than pearl-list-comments-count-cap newest-first: that gives an exact total up to the cap and a "+" beyond it, in a single round trip. pearl-list-comments-shown (default 5) caps how many render. pearl-fetch-comments-in-list (default on) turns the whole thing off to keep the fetch light. The single-issue refresh still shows the full thread, uncapped and unmarked.

The marker sits on the Comments heading rather than the issue title, so it stays out of the title-sync hash. I relaxed the append locator to match a marked heading, so adding a comment still finds the existing subtree instead of creating a second one. Verified live: the capped fragment is accepted and the markers render correctly.
</content>
</entry>
<entry>
<title>feat: render issue headings with the identifier prefix and title case</title>
<updated>2026-05-24T21:43:00+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T21:43:00+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/pearl/commit/?id=d03d5582197def92ad72e113815a3c4836da1330'/>
<id>urn:sha1:d03d5582197def92ad72e113815a3c4836da1330</id>
<content type='text'>
I added two display-only heading tweaks, each a defcustom defaulting on. pearl-show-identifier-in-heading prefixes the title with the Linear identifier (** TODO [#B] SE-401: Fix the Bug). pearl-title-case-headings renders the title in smart title case, keeping minor words lowercase unless first or last and leaving words that already carry an uppercase letter (acronyms, identifiers) untouched.

Both stay display-only. The LINEAR-TITLE-SHA256 hash covers the rendered (cased, un-prefixed) title, and the title-sync reader strips the identifier prefix before hashing, so an unedited heading is a no-op and neither the casing nor the prefix ever pushes to Linear. A render-then-read round-trip test locks that invariant.
</content>
</entry>
<entry>
<title>test: add an end-to-end acceptance flow</title>
<updated>2026-05-24T20:34:17+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T20:34:17+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/pearl/commit/?id=e48e9b75160fae5641ac5818e7e381cbcfdb8a5e'/>
<id>urn:sha1:e48e9b75160fae5641ac5818e7e381cbcfdb8a5e</id>
<content type='text'>
Stubs the single GraphQL chokepoint and drives the full integration path against a temp active file: run a saved query, render with the source header, read the source back, refresh the same source with an in-place merge, switch sources while the buffer is dirty (the edit is preserved, not clobbered), then from inside a rendered issue subtree sync an edited description, add a comment, and set priority. Everything above the wire — filter compilation, normalization, sort, render, header round-trip, merge, the conflict gate, comment append, and the field setter — runs for real, so the query and org-representation layers are exercised together rather than in isolation. 388 tests green (384 unit + 4 integration).
</content>
</entry>
<entry>
<title>test: cover sort and render ordering boundary cases</title>
<updated>2026-05-24T20:28:25+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T20:28:25+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/pearl/commit/?id=9c0dbf985cdc3360b47adc24f50964f8cf04b2cc'/>
<id>urn:sha1:9c0dbf985cdc3360b47adc24f50964f8cf04b2cc</id>
<content type='text'>
Added ERT tests for the deterministic-ordering guarantees: title sort folds nil and empty titles to the empty key (ordered first ascending); a nil (none) priority sorts last ascending and first descending; equal keys keep input order ascending and reverse it descending (Emacs sort is stable, then nreverse); and the render boundary sorts by priority after normalization even when a node omits the field. 384 tests green.
</content>
</entry>
<entry>
<title>test: cover comment insertion and extraction boundary cases</title>
<updated>2026-05-24T20:24:12+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T20:24:12+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/pearl/commit/?id=11109b45094eaecc1dc87a2e99afab4040732c8f'/>
<id>urn:sha1:11109b45094eaecc1dc87a2e99afab4040732c8f</id>
<content type='text'>
Added ERT tests: append targets the current issue's Comments subtree even when a later issue already has its own Comments; add-comment from inside a comment subtree refuses (no issue id at that heading); edit-current-comment refuses a comment missing LINEAR-COMMENT-ID with no network call; and a comment body containing drawer-looking (:note:) text reads back intact. 380 tests green.
</content>
</entry>
<entry>
<title>test: lock markdown round-trip identity across boundary cases</title>
<updated>2026-05-24T20:19:09+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T20:19:09+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/pearl/commit/?id=95bd7f4661ec5f43ba2eb7e8545b3cf9287e55c8'/>
<id>urn:sha1:95bd7f4661ec5f43ba2eb7e8545b3cf9287e55c8</id>
<content type='text'>
Added a round-trip test for the markdown boundary cases the coverage audit flagged: multiple links on a line, parens inside a URL, inline code whose contents look like markdown, non-ASCII prose, and unclosed / trailing fenced code. All round-trip to identity, so a fetch with no edit cannot change the remote on push. Two cases (paren-URL and markdown-looking inline code) round-trip safely despite an imperfect intermediate Org form, noted in the test and filed as a display-fidelity follow-up. 376 tests green.
</content>
</entry>
<entry>
<title>test: cover malformed and unknown source headers</title>
<updated>2026-05-24T20:15:55+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T20:15:55+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/pearl/commit/?id=0a65ae7c57aecbb948a3cf9675564b731efee55d'/>
<id>urn:sha1:0a65ae7c57aecbb948a3cf9675564b731efee55d</id>
<content type='text'>
Added ERT tests for the generated #+LINEAR-SOURCE header: a syntactically malformed source line reads back nil (no reader error) and refresh-current-view surfaces the "no source recorded" user error; a valid-but-unknown :type reaches the "Unknown Linear source type" user error; and a source whose name and filter carry quotes, parens, colons, and non-ASCII round-trips through build-org-content and read-active-source unchanged. 375 tests green.
</content>
</entry>
<entry>
<title>test: cover cache and resolver failure cases</title>
<updated>2026-05-24T20:13:53+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T20:13:53+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/pearl/commit/?id=6fca176d45b91d281bab36b51260e0ca1a788084'/>
<id>urn:sha1:6fca176d45b91d281bab36b51260e0ca1a788084</id>
<content type='text'>
Added ERT tests that the per-team collection cache and the resolvers fail soft: pearl--team-collection returns nil and caches nothing on a malformed (missing nodes) or nil response, so a retry refetches and a later success populates the cache; pearl--resolve-team-id returns nil rather than erroring when its backing fetch fails; pearl--custom-views likewise does not cache nil and recovers on a later call. Also dropped a stale pearl--cache-issues binding from this file's cache-reset macro — that variable was removed earlier. 371 tests green.
</content>
</entry>
</feed>
