<feed xmlns='http://www.w3.org/2005/Atom'>
<title>gloss, branch main</title>
<subtitle>Unnamed repository; edit this file 'description' to name the repository.
</subtitle>
<id>https://git.cjennings.net/gloss/atom?h=main</id>
<link rel='self' href='https://git.cjennings.net/gloss/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/'/>
<updated>2026-04-29T00:13:05+00:00</updated>
<entry>
<title>refactor: switch gloss-fetch result to uniform plist shape</title>
<updated>2026-04-29T00:13:05+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-29T00:13:05+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/commit/?id=01b75599a500d6276a962b47744166abb25d846c'/>
<id>urn:sha1:01b75599a500d6276a962b47744166abb25d846c</id>
<content type='text'>
The previous shape (:ok DEFS) | (:empty :no-defs (...) :failed (...)) was malformed as a plist. The :empty tag at position 0 shifted the plist alignment. plist-get on :no-defs or :failed returned nil. Tests had to use (plist-get (cdr result) ...) as a workaround.

The new shape is a uniform plist with all three keys always present: (:defs DEFS :no-defs (SYM ...) :failed (SYM ...)). Consumers branch on whether :defs is non-empty. There is no tag. plist-get works uniformly across success and empty cases.

Updated gloss-fetch.el (rollup function and docstrings), 7 test files, and the design doc (docs/design/gloss.org § Error Handling).

Tested by `make test`. 65 tests pass in 0.36 seconds.
</content>
</entry>
<entry>
<title>feat: implement gloss-fetch network layer</title>
<updated>2026-04-29T00:09:22+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-28T23:16:28+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/commit/?id=3491d9b799f9678f6095149a348330e2a05a1924'/>
<id>urn:sha1:3491d9b799f9678f6095149a348330e2a05a1924</id>
<content type='text'>
Walks the `gloss-fetch--sources' registry in the order set by the
`gloss-fetch-sources' defcustom and aggregates per-source results into
the public `gloss-fetch-definitions' shape.

The Wiktionary REST fetcher GETs the page-definition endpoint, parses
JSON, walks only English (`en') entries, and HTML-strips each sense via
`libxml-parse-html-region'. A sense whose strip fails is dropped while
the source keeps its `:ok' status with N-1 entries.

The HTTP-status taxonomy is five values: `:ok', `:no-defs' (404 or no
English senses on a 200), `:rate-limited' (429), `:server-error' (5xx,
malformed JSON, schema mismatch, 4xx other than 404 or 429), and
`:unreachable' (nil from `url-retrieve-synchronously', or a signaled
error). The `:reason' string carries technical detail to *gloss-debug*
and never reaches the user.

libxml is probed once per session at first fetch. When absent, online
fetch is disabled package-wide and every call signals `user-error' with
the install hint. `url-retrieve-synchronously' is wrapped with the
`gloss-fetch-timeout' defcustom (default 5 seconds).

Tested with `make test'. 60 of 62 tests pass. The two pending failures
load Wiktionary fixtures via `gloss-test--load-wiktionary-fixture',
which is provided on a parallel branch and will pass once both branches
land. The implementation has been verified against the captured
fixtures end-to-end (anaphora returns 4 senses, SBIR returns 2,
matching the design's expected counts).
</content>
</entry>
<entry>
<title>test: add gloss-fetch test suite (red phase)</title>
<updated>2026-04-29T00:09:22+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-28T23:10:17+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/commit/?id=dc0db0f0e12d8af6d1d54a5dde1cd16cf890a33d'/>
<id>urn:sha1:dc0db0f0e12d8af6d1d54a5dde1cd16cf890a33d</id>
<content type='text'>
Eight test files cover the network layer's public and internal contract.
The boundary mock is `url-retrieve-synchronously', wrapped by a small
`testutil-gloss-fetch' helper that builds response buffers in the shape
the url library returns.

Tests cover the 200 happy paths (anaphora and SBIR fixtures), 404 to
:no-defs, 5xx and 4xx-other and malformed JSON to :server-error, 429 to
:rate-limited, nil-from-url to :unreachable, the libxml availability
probe (one-shot, signals user-error when absent), the registry walker
ordering, and the pure HTML strip helper across N/B/E.

Tests fail on missing `gloss-fetch--*' functions, as expected for red
phase.
</content>
</entry>
<entry>
<title>test: add Wiktionary fixture loader helper</title>
<updated>2026-04-28T23:08:09+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-28T23:08:09+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/commit/?id=3a846506399dc12ab219bfa8047947c122dd1d04'/>
<id>urn:sha1:3a846506399dc12ab219bfa8047947c122dd1d04</id>
<content type='text'>
Append `gloss-test--load-wiktionary-fixture' to tests/testutil-gloss.el. It takes a fixture name (e.g. "anaphora") and returns the raw JSON body from tests/fixtures/wiktionary-NAME.json, or signals `error' with the full path when the file isn't there. The helper resolves the fixtures directory from a `defconst' captured at load time. That way it works the same whether a test file requires testutil-gloss directly or pulls it in transitively through `make test'.

Three ERT cases under tests/test-testutil-gloss--load-wiktionary-fixture.el cover Normal (anaphora loads as a non-empty JSON string), Boundary (the smallest fixture, 404, loads), and Error (a missing fixture raises with the path embedded in the message).

Verified with `make test': 35 passed, 0 unexpected.
</content>
</entry>
<entry>
<title>chore: capture Wiktionary REST fixtures for replay</title>
<updated>2026-04-28T23:07:00+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-28T23:07:00+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/commit/?id=166d3bde50718eacd51c75cb82246988d9ec0151'/>
<id>urn:sha1:166d3bde50718eacd51c75cb82246988d9ec0151</id>
<content type='text'>
Save raw response bodies from the Wiktionary REST endpoint under tests/fixtures/. The fetch layer can replay them with a cl-letf on url-retrieve-synchronously instead of hitting the network in tests.

The five fixtures cover the cases that matter for the parser. anaphora is the simple single-sense English entry. SBIR is an acronym with multiple senses. API is highly polysemous and multi-language (en, fr, id, la, pt). hapax-legomenon is the multi-word case, so it exercises URL-encoding for the space. The 404 fixture captures the JSON error body Wiktionary returns when a term isn't there.
</content>
</entry>
<entry>
<title>refactor: extract missing-glossary test helper</title>
<updated>2026-04-28T19:31:34+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-28T19:31:34+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/commit/?id=1e2da6b1a463492ada31ce473414289e761519ca'/>
<id>urn:sha1:1e2da6b1a463492ada31ce473414289e761519ca</id>
<content type='text'>
Four tests across lookup, list, find-buffer-position, and first-call-creates-file shared the same boilerplate. Each let-bound gloss-file to a randomized nonexistent path, wrapped in unwind-protect, reset the cache, and cleaned up file and buffer afterward.

Extracted as gloss-test--with-missing-glossary in testutil-gloss.el, parallel to the existing gloss-test--with-temp-glossary. The four call sites drop from 8-10 lines each to 2-3.

Tested by running the full 32-test suite. All 32 pass in 0.21 seconds.
</content>
</entry>
<entry>
<title>feat: implement gloss-core data layer</title>
<updated>2026-04-28T19:30:41+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-28T19:30:41+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/commit/?id=08cd3a12730d26cfc2eae8510da132747643da9e'/>
<id>urn:sha1:08cd3a12730d26cfc2eae8510da132747643da9e</id>
<content type='text'>
Public API: gloss-core-lookup, gloss-core-save, gloss-core-list, gloss-core-find-buffer-position. Save inserts entries at the alphabetical position (case-insensitive compare), creates the file and parent directory on first call, prompts on collision via completing-read over Replace/Append/Cancel, and updates the in-memory cache directly.

Lookup checks gloss-file's mtime against the cached load time. If disk is newer than the buffer, it reverts the buffer first. Out-of-band edits land on the next read. Parser failures during reload preserve the existing cache and surface a one-line message.

Tested by the 32-test suite from the previous commit. All 32 pass in 0.16 seconds.

The defgroup and defcustoms (gloss-file, gloss-debug) live here rather than in gloss.el. That keeps the data layer self-contained when tests load it directly without the orchestration layer.
</content>
</entry>
<entry>
<title>test: add gloss-core test suite (red phase)</title>
<updated>2026-04-28T19:21:18+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-28T19:21:18+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/commit/?id=4cd1e8e63d885d8de2728dc76d4f35f0eb597037'/>
<id>urn:sha1:4cd1e8e63d885d8de2728dc76d4f35f0eb597037</id>
<content type='text'>
The commit lands eight per-function test files and a shared testutil. 32 tests across Normal/Boundary/Error categories cover the public API (lookup, save, list, find-buffer-position) and the internals that need observable behavior tests (mtime invalidation, corrupt-file resilience, alphabetical insert, first-call file creation).

All 32 fail with void-function on the gloss-core symbols. That is the intended red-phase signal. The next commit lands the implementation that turns them green.

testutil-gloss provides a with-temp-glossary macro. It binds gloss-file to a temp file, resets the cache before and after, and cleans up the visiting buffer.
</content>
</entry>
<entry>
<title>chore: scaffold gloss package</title>
<updated>2026-04-28T18:56:06+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-28T18:56:06+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/commit/?id=71ccfdd0e6216356ec6cac90bc627fe02dbfdeb1'/>
<id>urn:sha1:71ccfdd0e6216356ec6cac90bc627fe02dbfdeb1</id>
<content type='text'>
Five layered files per the design at docs/design/gloss.org. gloss-core for the data layer, gloss-fetch for the network layer, gloss-display for the UI, gloss-drill for the spaced-repetition export, and gloss.el as the entry point. All five are skeletons. Implementation comes next.

The Makefile delegates to ert with the usual unit, integration, and per-file targets. It also runs paren and lint passes. The package is licensed GPL-3.0-or-later. README is a placeholder pointing at the design doc.
</content>
</entry>
</feed>
