From fbe54c4d3455b2b9733a6fa00378580ad1e76aac Mon Sep 17 00:00:00 2001 From: streambinder Date: Mon, 26 Oct 2026 15:04:11 +0100 Subject: [PATCH 1/2] docs(specs): use the empty line term in cycle 001 The terminology lint of the whole codebase requires the empty line term over blank line. --- specs/001-lyrics-reporting/plan.md | 6 ++--- specs/001-lyrics-reporting/spec.md | 36 ++++++++++++++--------------- specs/001-lyrics-reporting/tasks.md | 8 +++---- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/specs/001-lyrics-reporting/plan.md b/specs/001-lyrics-reporting/plan.md index 2629f84..c8e1700 100644 --- a/specs/001-lyrics-reporting/plan.md +++ b/specs/001-lyrics-reporting/plan.md @@ -54,7 +54,7 @@ stub every network dependency; the exported surface of the - **Normalization point**: one helper at the lyrics boundary, not one fix per composer. A new composer can emit the same leading - blank lines tomorrow; the boundary rule covers every source, + empty lines tomorrow; the boundary rule covers every source, the legacy cache, and both entry points in one place. - **Normalization form**: strip the leading whitespace run. A synchronized text starts with a timestamp bracket and carries no @@ -90,7 +90,7 @@ sys/string.go # FirstContentLine next to FirstLine sys/string_test.go # projection cases cmd/sync.go # collect log line uses the new projection cmd/show.go # Lyrics row uses the new projection -cmd/show_test.go # stored frame with leading blank lines +cmd/show_test.go # stored frame with leading empty lines cmd/sync_test.go # end-to-end lyrics line through a scripted source -cmd/harness_test.go # the web script gains a leading-blank lyrics answer +cmd/harness_test.go # the web script gains a leading-empty lyrics answer ``` diff --git a/specs/001-lyrics-reporting/spec.md b/specs/001-lyrics-reporting/spec.md index e8c437d..2f85e06 100644 --- a/specs/001-lyrics-reporting/spec.md +++ b/specs/001-lyrics-reporting/spec.md @@ -26,7 +26,7 @@ machine of the owner, and by reproduction): the full lyrics text, and the synchronization itself had written the file during the run. `lyrics.Search` writes the cache only on a hit, so the search had found the text. -2. The cached text begins with a blank line. The page parser of one +2. The cached text begins with an empty line. The page parser of one lyrics composer emits a line break before the first text node, so every text from that composer starts with an empty first line. @@ -41,7 +41,7 @@ machine of the owner, and by reproduction): installed file kept its size across its runs while its timestamps moved: the persistence had worked in both tools. 5. Reproduction: a file that carries a lyrics frame whose text - begins with a blank line reads the full text back through the + begins with an empty line reads the full text back through the ID3 layer, and `show` reports the frame as unset. **Assumed cause check**: The cause is not a source outage: the @@ -55,7 +55,7 @@ states the rule in the section Honest Reporting. **Outcome**: Specify a smaller correction than the report implied. No change to the search and no change to the persistence. The -lyrics boundary returns text without leading blank lines, and both +lyrics boundary returns text without leading empty lines, and both displays read the first non-empty line. ## User Scenarios and Testing @@ -70,14 +70,14 @@ tool. A false negative hides every real success and sends the owner to debug a healthy stage. **Independent Test**: Run a synchronization against a scripted -lyrics source that answers a text with a leading blank line. The +lyrics source that answers a text with a leading empty line. The printed lyrics line carries the first content line, not the not-found fallback. **Acceptance Scenarios**: 1. **Given** a lyrics source that returns a text starting with - blank lines, **When** the collect stage processes the track, + empty lines, **When** the collect stage processes the track, **Then** the run output shows the first non-empty line of the text. 2. **Given** a lyrics source that returns no text, **When** the @@ -90,16 +90,16 @@ The owner runs `show` on an installed track. The track carries a lyrics frame. The Lyrics row shows an excerpt of the stored text. **Why this priority**: Stored frames from runs before the fix keep -the leading blank line. Normalizing new results alone cannot repair +the leading empty line. Normalizing new results alone cannot repair the reporting for the existing library. **Independent Test**: Write a fixture file whose lyrics frame -starts with blank lines and run `show`. The Lyrics row carries the +starts with empty lines and run `show`. The Lyrics row carries the first non-empty line, not the unset fallback. **Acceptance Scenarios**: -1. **Given** a stored lyrics frame that starts with blank lines, +1. **Given** a stored lyrics frame that starts with empty lines, **When** the owner runs `show`, **Then** the Lyrics row shows the first non-empty line of the frame. 2. **Given** a stored track without a lyrics frame, **When** the @@ -117,18 +117,18 @@ write it into files. A shared normalization at the boundary repairs every consumer at once. **Independent Test**: Stub a composer that returns a text starting -with blank lines. `Search` and `Get` return the text without the -leading blank lines, and the cache file holds the normalized text. +with empty lines. `Search` and `Get` return the text without the +leading empty lines, and the cache file holds the normalized text. **Acceptance Scenarios**: -1. **Given** a composer that returns a text starting with blank +1. **Given** a composer that returns a text starting with empty lines, **When** a consumer calls `Search`, **Then** the result and the cache file start at the first content line. 2. **Given** a cache file written by an earlier version, with - leading blank lines, **When** a consumer calls `Search`, + leading empty lines, **When** a consumer calls `Search`, **Then** the result starts at the first content line. -3. **Given** a cache file that holds only blank lines, **When** a +3. **Given** a cache file that holds only empty lines, **When** a consumer calls `Search`, **Then** the cache counts as a miss and the composers run. @@ -137,10 +137,10 @@ leading blank lines, and the cache file holds the normalized text. ### Functional Requirements - **FR-001**: `lyrics.Search` and `lyrics.Get` return a lyrics - text with no leading blank lines. + text with no leading empty lines. - **FR-002**: The lyrics cache stores the normalized text, and a cache read normalizes a legacy entry. A cache entry that holds - only blank lines counts as a miss. + only empty lines counts as a miss. - **FR-003**: The collect log line and the `show` Lyrics row excerpt the first non-empty line of the value. The fallback text appears only for an actually empty value. @@ -161,12 +161,12 @@ leading blank lines, and the cache file holds the normalized text. ### Measurable Outcomes - **SC-001**: Unit tests prove that a composer result with leading - blank lines yields a normalized result and a normalized cache + empty lines yields a normalized result and a normalized cache file from `Search`, and a normalized result from `Get`. - **SC-002**: A command test proves that `show` prints the first - content line for a stored frame with leading blank lines. + content line for a stored frame with leading empty lines. - **SC-003**: A command test proves that a full synchronization - whose lyrics source answers with leading blank lines prints the + whose lyrics source answers with leading empty lines prints the first content line, not the not-found fallback. - **SC-004**: The full test suite passes with the per-package coverage gate at 90 percent, locally and in CI. diff --git a/specs/001-lyrics-reporting/tasks.md b/specs/001-lyrics-reporting/tasks.md index dd43fa4..81575e3 100644 --- a/specs/001-lyrics-reporting/tasks.md +++ b/specs/001-lyrics-reporting/tasks.md @@ -33,8 +33,8 @@ content line. Story 1 depends on this phase through `Search`. result in `Get` - [x] T004 [US3] Cover the normalization in `lyrics/composer_test.go`: a composer result with leading - blank lines, the cache file content, a legacy cache entry, - a blank-only cache entry, and a `Get` result + empty lines, the cache file content, a legacy cache entry, + an empty-only cache entry, and a `Get` result ## Phase 3: Display Projection (Stories 1 and 2) @@ -50,10 +50,10 @@ the fallback fires only for an empty value. new projection - [x] T008 [US2] Cover the stored-frame case in `cmd/show_test.go`: a fixture whose lyrics frame starts - with blank lines prints the first content line + with empty lines prints the first content line - [x] T009 [US1] Cover the run output in `cmd/sync_test.go`: the web script in `cmd/harness_test.go` gains a lyrics answer - with leading blank lines, and the printed lyrics line + with leading empty lines, and the printed lyrics line carries the first content line ## Phase 4: Verification From 1ea2e5c579a7eef6ecd61cdab35cda91ede7dc98 Mon Sep 17 00:00:00 2001 From: streambinder Date: Mon, 26 Oct 2026 22:43:37 +0100 Subject: [PATCH 2/2] test(show): anchor the lyrics row match to the row label The temporary path in the Path row contains the test name and matched first. --- cmd/show_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/show_test.go b/cmd/show_test.go index 3537836..e208e5e 100644 --- a/cmd/show_test.go +++ b/cmd/show_test.go @@ -97,7 +97,9 @@ func TestCmdShowLyricsLeadingBlankLines(t *testing.T) { assert.Nil(t, sys.ErrOnly(testExecute(cmdShow(), path))) }) for _, line := range strings.Split(output, "\n") { - if strings.Contains(line, "Lyrics") { + // match the row by its label: the temporary path embedded in + // the Path row contains the test name, hence "Lyrics" itself + if strings.HasPrefix(strings.TrimSpace(line), "Lyrics") { assert.Contains(t, line, "first lyric line") assert.NotContains(t, line, fallback) return