appydave-tools 0.76.1 → 0.76.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2700178e0f8e7fa1ddbd046cb78af4a0a340f2e8770637a2f3b30229e9f76179
4
- data.tar.gz: c5c3f8094e988cc77849f27d1126c3839825d231c06b8bb63d3410cfdfe72179
3
+ metadata.gz: d2b65bd11cb0b4e0a0c9b1718a9ebb262ad6c998167bfdc8f533ddca754ee850
4
+ data.tar.gz: e45a7a56eb7f77249911ea874037c002829cfcca1f8f5cfd8553b60d12e88898
5
5
  SHA512:
6
- metadata.gz: e11cda4ffd9a0a555c15ae613e94b31aa7f508ba473ccc89d31a48d399f9b603204faa4018bea222f9640205b5d73a557b992acf3be94c926bdbeea494baedb5
7
- data.tar.gz: 4af0c479f1aeb82cc32dd06d8d954910fff6e9a4a2f785d2969a284fc9a55874a56ed71e1600be9c26b5ad3792a67be98105a9003b82c8d835eb0bd8a7d323cf
6
+ metadata.gz: feb57bd1aca715ae2b6b3a587e762e024457b4d14f1172069d1afd19e953c6c432567d9c76c819543f740acb757c53bb36fa7bc839e4e7c8706f13435e94bfb0
7
+ data.tar.gz: eba77632d7dad881243726e122d8da5546040910b2851c36a9b751d7310da9ca3d1a4279571baa31b908bb1ff398436253bed4f7e52907adeeb6e4cfe1b1f69a
data/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ ## [0.76.2](https://github.com/appydave/appydave-tools/compare/v0.76.1...v0.76.2) (2026-03-19)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * add dedicated specs for Jump Commands layer ([8eec40c](https://github.com/appydave/appydave-tools/commit/8eec40cb60b08b1ca7901f288cff8c9a70783fe2))
7
+ * add determine_range edge cases and fix stale comment ([6e97390](https://github.com/appydave/appydave-tools/commit/6e97390e135ac452a364bfac45fcbc4679b5a26d))
8
+ * add functional CLI tests for -i -e -f -o flags to gpt_context cli_spec ([feed9c7](https://github.com/appydave/appydave-tools/commit/feed9c70abfbec83c6bdb0fcba45fce0ec528657))
9
+ * fix no-args cli spec assertion to avoid false positive on Linux clipboard error ([a50b68e](https://github.com/appydave/appydave-tools/commit/a50b68ea57e2d989b35da2c5f1172181cf825438))
10
+ * fix no-args clipboard assertion to avoid false failure from clipboard gem warning on Linux CI ([197b9de](https://github.com/appydave/appydave-tools/commit/197b9dea84fc12c14b03c39333593f430542228a))
11
+ * strengthen gpt_context no-args spec to verify collection does not proceed ([972d617](https://github.com/appydave/appydave-tools/commit/972d617b2241326a6b4beca53d520ebcb1051530))
12
+
13
+ ## [0.76.1](https://github.com/appydave/appydave-tools/compare/v0.76.0...v0.76.1) (2026-03-19)
14
+
15
+
16
+ ### Bug Fixes
17
+
18
+ * align SyncFromSsd#determine_range with ManifestGenerator format; add specs ([3f8cb37](https://github.com/appydave/appydave-tools/commit/3f8cb37a78bd3d9720eace21cf1abbbca285cd66))
19
+ * remove unconditional ssl_verify_peer: false from S3 clients; keep env override ([f49efb1](https://github.com/appydave/appydave-tools/commit/f49efb1c4a97ba904f3f3fc7d4292139a5e6c3fd))
20
+
1
21
  # [0.76.0](https://github.com/appydave/appydave-tools/compare/v0.75.0...v0.76.0) (2026-03-19)
2
22
 
3
23
 
@@ -1,27 +1,29 @@
1
1
  # Project Backlog — AppyDave Tools
2
2
 
3
- **Last updated**: 2026-03-19 (fr2-gpt-context-help assessment + 3 new items from quality audit)
4
- **Total**: 23 | Pending: 16 | Done: 7 | Deferred: 0 | Rejected: 0
3
+ **Last updated**: 2026-03-19 (bugfix-and-security assessment + 4 new items from quality audit)
4
+ **Total**: 27 | Pending: 17 | Done: 10 | Deferred: 0 | Rejected: 0
5
5
 
6
6
  ---
7
7
 
8
8
  ## Pending
9
9
 
10
10
  ### High Priority
11
+ - [ ] B024 — Tests: add configure_ssl_options unit tests to s3_operations_spec + share_operations_spec (protects B017 security fix) | Priority: high
11
12
  - [ ] B015 — BUG-2: FileCollector uses FileUtils.cd without ensure (process dir not restored on exception) | Priority: high
12
- - [ ] B016 — BUG-3: ManifestGenerator + SyncFromSsd produce incompatible SSD range strings (data integrity) | Priority: high
13
13
  - [x] B006 — BUG-1: Jump CLI get/remove key lookup | Completed: verified fixed 2026-03-19, regression spec added
14
- - [ ] B017 — Security: ssl_verify_peer disabled unconditionally in S3Operations + ShareOperations | Priority: high
15
14
 
16
15
  ### Medium Priority
17
- - [ ] B021 — Fix: gpt_context no-args guard checks format.nil? which is always false (dead condition) | Priority: medium
18
16
  - [ ] B022 — Tests: expand cli_spec.rb with functional tests (-i, -e, -f, -o flags, exit codes) | Priority: medium
17
+ - [ ] B026 — Tests: add determine_range edge cases (b00, b9, a40) to sync_from_ssd_spec + manifest_generator_spec | Priority: medium
18
+ - [ ] B027 — Tests: strengthen gpt_context no-args spec to verify file collection actually stops | Priority: medium
19
19
  - [ ] B023 — Tests: add file_collector_spec coverage for JSON format, aider format, error paths | Priority: medium
20
20
  - [ ] B018 — Tests: add specs for Jump Commands::Remove, Commands::Add, Commands::Update | Priority: medium
21
21
  - [ ] B019 — Fix: remove debug puts @working_directory from gpt_context/file_collector.rb | Priority: medium
22
22
  - [ ] B001 — FR-1: GPT Context token counting | Priority: medium
23
23
  - [ ] B012 — Arch: add integration tests for brand resolution end-to-end | Priority: medium
24
24
 
25
+ - [ ] B025 — Fix: stale comment in sync_from_ssd.rb line 173 (says 60-69, should say b50-b99) | Priority: low
26
+
25
27
  ### Low Priority
26
28
  - [ ] B007 — Performance: parallel git/S3 status checks for dam list | Priority: low
27
29
  - [ ] B008 — Performance: cache git/S3 status with 5-min TTL | Priority: low
@@ -40,6 +42,9 @@
40
42
  - [x] B013 — Arch: Extract GitHelper module (90 lines duplication) | Completed: dam-enhancement-sprint (Jan 2025)
41
43
  - [x] B014 — Arch: Create BrandResolver to centralize brand transformation | Completed: dam-enhancement-sprint (Jan 2025)
42
44
  - [x] B002 — FR-2: GPT Context AI-friendly help system | Completed: fr2-gpt-context-help (2026-03-19)
45
+ - [x] B016 — BUG-3: ManifestGenerator + SyncFromSsd incompatible SSD range strings | Completed: bugfix-and-security (2026-03-19)
46
+ - [x] B017 — Security: ssl_verify_peer disabled unconditionally in S3Operations + ShareOperations + S3Scanner | Completed: bugfix-and-security (2026-03-19)
47
+ - [x] B021 — Fix: gpt_context no-args guard had dead format.nil? condition | Completed: bugfix-and-security (2026-03-19)
43
48
 
44
49
  ---
45
50
 
@@ -308,6 +308,11 @@ include_context 'with vat filesystem and brands', brands: %w[appydave]
308
308
  - `ssl_verify_peer: false` is not safe for AWS — HTTPS alone doesn't prevent MITM without peer verification
309
309
  - ManifestGenerator `determine_range` is the canonical format; SyncFromSsd must match it
310
310
 
311
+ ### From bugfix-and-security (2026-03-19)
312
+ - **`options.format` defaults to `'tree,content'`** in GptContext::Options — not `'content'`. It is never nil. Do not use `options.format.nil?` as a guard.
313
+ - **Use `$CHILD_STATUS` not `$?` in specs** — RuboCop flags `$?` as a special global variable. Use `$CHILD_STATUS` (from the English module, auto-available in RSpec) for exit status assertions.
314
+ - **`exit` with no code exits 0** — Ruby's bare `exit` call produces exit status 0. Write specs accordingly.
315
+
311
316
  ### From fr2-gpt-context-help (2026-03-19)
312
317
  - `opts.on_tail` vs `opts.on` matters for option ordering in OptionParser
313
318
  - Subprocess specs (`ruby #{script} --flag`) are correct for CLI integration tests
@@ -5,16 +5,16 @@
5
5
  **Target**: All 3 fixes committed; tests pass; rubocop clean; no regressions
6
6
 
7
7
  ## Summary
8
- - Total: 3 | Complete: 0 | In Progress: 3 | Pending: 0 | Failed: 0
8
+ - Total: 3 | Complete: 3 | In Progress: 0 | Pending: 0 | Failed: 0
9
9
 
10
10
  ## Pending
11
11
 
12
12
  ## In Progress
13
- - [~] fix-b017-ssl — Remove unconditional ssl_verify_peer: false from s3_operations.rb, share_operations.rb, s3_scanner.rb
14
- - [~] fix-b016-range — Align SyncFromSsd#determine_range to ManifestGenerator format; update sync_from_ssd_spec.rb; add manifest_generator_spec.rb coverage
15
- - [~] fix-b021-guard — Remove options.format.nil? from gpt_context.rb:115 guard; add/update spec for no-args behaviour
16
13
 
17
14
  ## Complete
15
+ - [x] fix-b017-ssl — ssl_verify_peer: false removed from s3_operations.rb, share_operations.rb, s3_scanner.rb. s3_operations_spec stub updated. 755 examples, 0 failures. v0.76.0 published.
16
+ - [x] fix-b021-guard — Removed dead format.nil? condition (format defaults to 'tree,content', never nil). Added no-args spec asserting exit 0 + error message. Used $CHILD_STATUS not $? (rubocop). 755 examples, 0 failures.
17
+ - [x] fix-b016-range — SyncFromSsd#determine_range aligned to ManifestGenerator format (any letter, 50-number ranges). Updated 4 unit specs + 4 integration path assertions in sync_from_ssd_spec. Added 4 new specs to manifest_generator_spec. 759 examples, 0 failures. v0.76.1 published.
18
18
 
19
19
  ## Failed / Needs Retry
20
20
 
@@ -0,0 +1,94 @@
1
+ # Assessment: bugfix-and-security
2
+
3
+ **Campaign**: bugfix-and-security
4
+ **Date**: 2026-03-19 → 2026-03-19
5
+ **Results**: 3 complete, 0 failed
6
+ **Version shipped**: v0.76.1
7
+ **Quality audit**: code-quality-audit + test-quality-audit run post-campaign
8
+
9
+ ---
10
+
11
+ ## Results Summary
12
+
13
+ | Work Unit | Status | Notes |
14
+ |-----------|--------|-------|
15
+ | fix-b017-ssl | ✅ Complete | ssl_verify_peer: false removed from s3_operations.rb, share_operations.rb, s3_scanner.rb. ENV escape hatch preserved. s3_operations_spec stub updated. |
16
+ | fix-b016-range | ✅ Complete | SyncFromSsd#determine_range now matches ManifestGenerator (letter prefix, 50-number ranges). 4 unit specs + 4 integration path assertions updated. 4 new specs added to manifest_generator_spec. |
17
+ | fix-b021-guard | ✅ Complete | Dead `&& options.format.nil?` condition removed. format defaults to 'tree,content', never nil. No-args spec added. Used $CHILD_STATUS not $? (rubocop requirement). |
18
+
19
+ **Test baseline:** 754 → 759 examples (+5). Coverage: 84.92% → 85.0%.
20
+
21
+ ---
22
+
23
+ ## What Worked Well
24
+
25
+ - **Parallel wave was clean.** 3 independent fixes in 3 different files — no conflicts, all completed without intervention.
26
+ - **B016 agent caught integration test debt.** sync_from_ssd_spec had 4 integration path assertions using the old `60-69` format. Agent found and fixed all of them without being told. A lesser agent would have only updated the unit tests and left the integration tests broken.
27
+ - **B021 agent discovered accurate default.** `options.format` defaults to `'tree,content'` (not `'content'` as AGENTS.md said). Corrected understanding.
28
+ - **$CHILD_STATUS vs $?.** Rubocop flags `$?` — must use `$CHILD_STATUS`. Captured in AGENTS.md learnings during campaign.
29
+ - **s3_scanner.rb surprise.** Brief only mentioned 2 files for B017, but grep found a third (s3_scanner.rb with inline ssl_verify_peer, no env guard). Agent fixed it correctly.
30
+
31
+ ---
32
+
33
+ ## What Didn't Work
34
+
35
+ **Critical: B017 SSL fix has no regression test (Grade C+ from test audit).**
36
+ The most important security fix in the campaign — removing unconditional `ssl_verify_peer: false` — is unprotected. `configure_ssl_options` is not tested in isolation. If someone accidentally adds `ssl_verify_peer: false` unconditionally back, all tests would still pass. This is the #1 priority for the next campaign.
37
+
38
+ **B016 determine_range has edge case gaps.**
39
+ Tests cover b40, b65, b99, boy-baker. Missing: b00 (boundary), b9 (single digit), a40 (non-b letter prefix). The regex `/^([a-z])(\d+)/` handles all of these correctly, but if the regex ever changes, these gaps won't catch it.
40
+
41
+ **cli_spec no-args test is shallow (Grade D+).**
42
+ Verifies the message is printed and exit is 0. Does not verify that file collection actually stops — if the guard is removed and replaced with something that prints the message but continues, the spec would still pass.
43
+
44
+ **Stale comment in sync_from_ssd.rb.**
45
+ Line 173 still says `b65 → 60-69 range` (old format). Code is correct; comment is wrong. 1-line fix.
46
+
47
+ ---
48
+
49
+ ## Key Learnings — Application
50
+
51
+ - **`options.format` defaults to `'tree,content'`** — not `'content'`. Any guard checking `format.nil?` is dead. Updated AGENTS.md.
52
+ - **`$CHILD_STATUS` not `$?`** — Rubocop Special/GlobalVars cop flags `$?`. Use `$CHILD_STATUS` (English module, auto-available in RSpec). Updated AGENTS.md.
53
+ - **`exit` with no code exits 0** in Ruby — specs asserting exit status for "no-args" path should expect 0.
54
+ - **Grep the full codebase before writing the brief** — B017 brief named 2 files; actual codebase had 3. Always grep for the pattern before writing work unit scope.
55
+ - **Integration path assertions in specs** — when changing a path-construction algorithm, search specs for the old path strings, not just the method name. Agent found 4 integration assertions that grep on method name alone would miss.
56
+
57
+ ---
58
+
59
+ ## Key Learnings — Ralph Loop
60
+
61
+ - **Parallel waves are fast when fixes are independent.** All 3 agents ran simultaneously, completed in one wave, zero coordination needed. Right call.
62
+ - **Quality audit surfaced a critical gap the code audit didn't.** Code looks correct (A grade). But test audit showed the SSL fix has no regression protection — an entirely separate risk dimension.
63
+ - **Brief scope can undercount files.** Next time, grep before writing the brief, not just inspect known files.
64
+
65
+ ---
66
+
67
+ ## New Backlog Items from Quality Audit
68
+
69
+ - **B024** — Tests: add `configure_ssl_options` unit tests to s3_operations_spec and share_operations_spec — verify empty hash on default path, ssl_verify_peer: false on ENV override path | Priority: **high** (protects B017 fix)
70
+ - **B025** — Fix: stale comment in sync_from_ssd.rb line 173 (says 60-69, should say b50-b99) | Priority: **low**
71
+ - **B026** — Tests: add determine_range edge cases (b00, b9, a40) to sync_from_ssd_spec and manifest_generator_spec | Priority: **medium**
72
+ - **B027** — Tests: strengthen gpt_context no-args spec to verify file collection actually stops (not just message printed) | Priority: **medium**
73
+
74
+ ---
75
+
76
+ ## Suggestions for Next Campaign
77
+
78
+ **Recommended next campaign: `test-coverage-gaps`**
79
+
80
+ Priority order:
81
+ 1. **B024** — configure_ssl_options unit tests (high — protects the security fix)
82
+ 2. **B022** — expand cli_spec with functional tests (-i, -e, -f, -o flags, exit codes)
83
+ 3. **B026** — determine_range edge cases
84
+ 4. **B027** — gpt_context no-args guard behavioral test
85
+ 5. **B018** — Jump Commands layer specs (Remove/Add/Update)
86
+ 6. **B025** — stale comment fix (bundle with B026, same file)
87
+ 7. **B023** — file_collector_spec: JSON, aider, error paths
88
+
89
+ These are all test-only changes (except B025 which is a 1-line comment fix) — agents can run in parallel safely.
90
+
91
+ **AGENTS.md updates for next campaign:**
92
+ - Add: "configure_ssl_options test pattern: use ClimateControl gem or stub ENV to test conditional SSL logic"
93
+ - Add: "determine_range edge cases: b00, single-digit b9, non-b letter a40 — always test boundaries"
94
+ - Add: "gpt_context no-args test: verify file collection does NOT proceed, not just message output"
@@ -0,0 +1,333 @@
1
+ # AGENTS.md — final-test-gaps
2
+
3
+ > Inherited from test-coverage-gaps AGENTS.md. Self-contained.
4
+ > Last updated: 2026-03-19
5
+
6
+ ---
7
+
8
+ ## Project Overview
9
+
10
+ **What:** Ruby gem providing CLI productivity tools for AppyDave's YouTube content creation workflow.
11
+ **Stack:** Ruby 3.4.2, Bundler 2.6.2, RSpec, RuboCop, semantic-release CI/CD.
12
+ **This campaign:** Test-only gap closure. No lib/ production code changes.
13
+ **Commits:** Always use `kfix` — never `git commit`.
14
+
15
+ ---
16
+
17
+ ## Build & Run Commands
18
+
19
+ ```bash
20
+ eval "$(rbenv init -)"
21
+
22
+ RUBYOPT="-W0" bundle exec rspec # All tests
23
+ bundle exec rspec spec/appydave/tools/gpt_context/file_collector_spec.rb
24
+ bundle exec rspec spec/appydave/tools/gpt_context/cli_spec.rb
25
+ bundle exec rspec spec/appydave/tools/jump/commands/add_spec.rb
26
+ bundle exec rspec spec/appydave/tools/jump/commands/update_spec.rb
27
+ bundle exec rubocop --format clang
28
+ ```
29
+
30
+ **Baseline:** 817 examples, 0 failures, 85.61% line coverage
31
+
32
+ ---
33
+
34
+ ## Directory Structure
35
+
36
+ ```
37
+ lib/appydave/tools/gpt_context/
38
+ file_collector.rb READ ONLY — formats: content, tree, json, aider
39
+ options.rb READ ONLY — Struct with: include_patterns, exclude_patterns,
40
+ format, line_limit, debug, output_target,
41
+ working_directory, prompt (all keyword_init)
42
+
43
+ spec/appydave/tools/gpt_context/
44
+ file_collector_spec.rb ADD json format, aider format, error path tests
45
+ cli_spec.rb ADD body content assertions to -i and -e describe blocks
46
+
47
+ spec/appydave/tools/jump/commands/
48
+ add_spec.rb ADD location data integrity assertions (path, jump, tags, description)
49
+ update_spec.rb ADD field isolation assertions (non-updated fields unchanged)
50
+ ```
51
+
52
+ ---
53
+
54
+ ## Work Unit Details
55
+
56
+ ---
57
+
58
+ ### fix-b023 — file_collector_spec: json, aider, error paths
59
+
60
+ **Read first:** `lib/appydave/tools/gpt_context/file_collector.rb` and
61
+ `spec/appydave/tools/gpt_context/file_collector_spec.rb`
62
+
63
+ **What `build_json` produces:**
64
+ ```ruby
65
+ {
66
+ 'tree' => {}, # nested hash matching tree structure
67
+ 'content' => [ # array of hashes, one per file
68
+ { 'file' => 'path/to/file.txt', 'content' => '...' }
69
+ ]
70
+ }
71
+ ```
72
+ Returns `JSON.pretty_generate(json_output)` — a valid JSON string.
73
+
74
+ **What `build_aider` produces:**
75
+ - With `prompt` set: `aider --message "my prompt" path/to/file1.rb path/to/file2.rb`
76
+ - Without `prompt` (nil): returns `''`
77
+
78
+ **Add these describe blocks to file_collector_spec.rb:**
79
+
80
+ ```ruby
81
+ describe '#build with json format' do
82
+ let(:format) { 'json' }
83
+ let(:include_patterns) { ['**/*.txt'] }
84
+ let(:exclude_patterns) { [] }
85
+
86
+ it 'returns valid JSON' do
87
+ result = subject.build
88
+ expect { JSON.parse(result) }.not_to raise_error
89
+ end
90
+
91
+ it 'includes a tree key in the JSON output' do
92
+ result = JSON.parse(subject.build)
93
+ expect(result).to have_key('tree')
94
+ end
95
+
96
+ it 'includes a content key in the JSON output' do
97
+ result = JSON.parse(subject.build)
98
+ expect(result).to have_key('content')
99
+ end
100
+
101
+ it 'includes file paths and content in the content array' do
102
+ result = JSON.parse(subject.build)
103
+ files = result['content'].map { |f| f['file'] }
104
+ expect(files).to include('included/file1.txt')
105
+
106
+ entry = result['content'].find { |f| f['file'] == 'included/file1.txt' }
107
+ expect(entry['content']).to include('File 1 content')
108
+ end
109
+
110
+ it 'excludes files matching exclude patterns from content' do
111
+ result = JSON.parse(subject.build)
112
+ files = result['content'].map { |f| f['file'] }
113
+ expect(files).not_to include('excluded/excluded_file.txt')
114
+ end
115
+ end
116
+
117
+ describe '#build with aider format' do
118
+ let(:format) { 'aider' }
119
+ let(:include_patterns) { ['**/*.txt'] }
120
+ let(:exclude_patterns) { [] }
121
+
122
+ context 'when prompt is set' do
123
+ let(:options) do
124
+ Appydave::Tools::GptContext::Options.new(
125
+ include_patterns: include_patterns,
126
+ exclude_patterns: exclude_patterns,
127
+ format: format,
128
+ line_limit: nil,
129
+ working_directory: temp_dir,
130
+ prompt: 'fix the bug'
131
+ )
132
+ end
133
+
134
+ it 'returns an aider command string' do
135
+ expect(subject.build).to start_with('aider --message')
136
+ end
137
+
138
+ it 'includes the prompt in the command' do
139
+ expect(subject.build).to include('fix the bug')
140
+ end
141
+
142
+ it 'includes collected file paths in the command' do
143
+ result = subject.build
144
+ expect(result).to include('included/file1.txt')
145
+ end
146
+ end
147
+
148
+ context 'when prompt is not set' do
149
+ it 'returns an empty string' do
150
+ expect(subject.build).to eq('')
151
+ end
152
+ end
153
+ end
154
+
155
+ describe '#build with nonexistent working directory' do
156
+ let(:options) do
157
+ Appydave::Tools::GptContext::Options.new(
158
+ include_patterns: ['**/*.txt'],
159
+ exclude_patterns: [],
160
+ format: 'content',
161
+ line_limit: nil,
162
+ working_directory: '/tmp/does-not-exist-12345'
163
+ )
164
+ end
165
+
166
+ it 'returns empty string without raising an error' do
167
+ expect { subject.build }.not_to raise_error
168
+ expect(subject.build).to eq('')
169
+ end
170
+ end
171
+ ```
172
+
173
+ **Note:** The existing `before` block creates files under `temp_dir`. The aider context with `prompt` needs its own `let(:options)` that overrides the parent — this is fine in RSpec because the inner let shadows the outer.
174
+
175
+ **Commit:** `kfix "add json, aider, and error path tests to file_collector_spec"`
176
+
177
+ ---
178
+
179
+ ### fix-b028 — cli_spec: add file body content assertions to -i and -e tests
180
+
181
+ **Read first:** `spec/appydave/tools/gpt_context/cli_spec.rb`
182
+
183
+ **The gap:** `-i` tests write known content to temp files but only assert on `# file: test.rb` headers. File body content (`# test content`, `# ruby file`) is never asserted. File truncation or body corruption would pass silently.
184
+
185
+ **Find the `-i include pattern` describe block (lines 43-68). Add body assertions to each existing `it` block:**
186
+
187
+ In the first `-i` example (`'collects files matching the include pattern'`):
188
+ ```ruby
189
+ # After the existing assertion, add:
190
+ expect(File.read(outfile)).to include('# test content')
191
+ ```
192
+
193
+ In the second `-i` example (`'does not include files that do not match the pattern'`):
194
+ ```ruby
195
+ # The file 'test.rb' has content '# ruby file'
196
+ # After the existing assertions, add:
197
+ expect(content).to include('# ruby file')
198
+ expect(content).not_to include('# markdown file')
199
+ ```
200
+
201
+ **Find the `-e exclude pattern` describe block (lines 70-95). Add body assertions:**
202
+
203
+ In the first `-e` example (`'excludes files matching the exclude pattern'`):
204
+ ```ruby
205
+ # 'keep.rb' has content '# keep'
206
+ # After existing assertions, add:
207
+ expect(content).to include('# keep')
208
+ ```
209
+
210
+ In the second `-e` example (`'keeps all files when exclude pattern matches nothing'`):
211
+ ```ruby
212
+ # 'keep.rb' has content '# keep'
213
+ # After existing assertion, add:
214
+ expect(File.read(outfile)).to include('# keep')
215
+ ```
216
+
217
+ **Do NOT modify the `-f`, `-o`, or `no arguments` describe blocks** — those are not in scope.
218
+
219
+ **Commit:** `kfix "add file body content assertions to cli_spec -i and -e tests"`
220
+
221
+ ---
222
+
223
+ ### fix-b029 — add_spec: validate all returned location data fields
224
+
225
+ **Read first:** `spec/appydave/tools/jump/commands/add_spec.rb`
226
+
227
+ **The gap:** The `'returns the created location data'` example (lines 35-41) only asserts `result[:location][:key]`. The path, jump, tags, description are never verified — data corruption or field mapping bugs would pass silently.
228
+
229
+ **Add a new `it` block** in the `'with valid attributes and existing path'` context, immediately after the existing `'returns the created location data'` block:
230
+
231
+ ```ruby
232
+ it 'returns location data matching all input attrs' do
233
+ cmd = described_class.new(config, valid_attrs, path_validator: path_validator)
234
+ result = cmd.run
235
+
236
+ location = result[:location]
237
+ expect(location[:key]).to eq('new-project')
238
+ expect(location[:path]).to eq('~/dev/new-project')
239
+ expect(location[:jump]).to eq('jnew')
240
+ expect(location[:tags]).to eq(%w[ruby])
241
+ expect(location[:description]).to eq('A new project')
242
+ end
243
+ ```
244
+
245
+ **Do NOT modify the existing `'returns the created location data'` example** — add alongside it.
246
+
247
+ **Commit:** `kfix "add data integrity assertions to add_spec returned location data"`
248
+
249
+ ---
250
+
251
+ ### fix-b030 — update_spec: verify non-updated fields remain unchanged
252
+
253
+ **Read first:** `spec/appydave/tools/jump/commands/update_spec.rb`
254
+ **Also read:** `spec/support/jump_test_locations.rb` to get the exact field values for `JumpTestLocations.ad_tools` and `JumpTestLocations.flivideo`
255
+
256
+ **Gap 1:** `'leaves unmodified locations intact'` (lines 48-53) only checks `config.key_exists?('flivideo')`. The flivideo record's fields (path, jump, tags, description) could be corrupted and the test would still pass.
257
+
258
+ **Gap 2:** When updating `ad-tools` description, the key/path/jump/tags of the `ad-tools` record itself are never verified to be unchanged.
259
+
260
+ **Add two new `it` blocks** in the `'when location exists and update is valid'` context:
261
+
262
+ ```ruby
263
+ it 'does not modify non-updated fields on the updated record' do
264
+ original = config.find('ad-tools')
265
+ original_path = original.path
266
+ original_jump = original.jump
267
+ original_tags = original.tags
268
+
269
+ cmd = described_class.new(config, 'ad-tools', { description: 'Changed' }, path_validator: path_validator)
270
+ cmd.run
271
+
272
+ updated = config.find('ad-tools')
273
+ expect(updated.path).to eq(original_path)
274
+ expect(updated.jump).to eq(original_jump)
275
+ expect(updated.tags).to eq(original_tags)
276
+ end
277
+
278
+ it 'does not modify the sibling record fields' do
279
+ original_flivideo = config.find('flivideo')
280
+ original_path = original_flivideo.path
281
+ original_jump = original_flivideo.jump
282
+
283
+ cmd = described_class.new(config, 'ad-tools', { description: 'Changed' }, path_validator: path_validator)
284
+ cmd.run
285
+
286
+ flivideo = config.find('flivideo')
287
+ expect(flivideo.path).to eq(original_path)
288
+ expect(flivideo.jump).to eq(original_jump)
289
+ end
290
+ ```
291
+
292
+ **Do NOT modify existing examples** — add alongside them.
293
+
294
+ **Note:** Read jump_test_locations.rb to understand what fields `JumpTestLocations.flivideo` has before assuming field names.
295
+
296
+ **Commit:** `kfix "add field isolation assertions to update_spec non-updated fields"`
297
+
298
+ ---
299
+
300
+ ## Success Criteria
301
+
302
+ - [ ] `RUBYOPT="-W0" bundle exec rspec` — 817+ examples, 0 failures
303
+ - [ ] `bundle exec rubocop --format clang` — 0 offenses
304
+ - [ ] Line coverage stays ≥ 85.61%
305
+ - [ ] All new `it` blocks use descriptive behaviour names
306
+ - [ ] No `require 'spec_helper'` added (auto-required)
307
+ - [ ] All new spec files start with `# frozen_string_literal: true` (if any new files created)
308
+
309
+ ---
310
+
311
+ ## Anti-Patterns to Avoid
312
+
313
+ - ❌ Do NOT use `$?` — use `$CHILD_STATUS` (rubocop Style/SpecialGlobalVars)
314
+ - ❌ Do NOT modify existing passing examples — add new ones alongside
315
+ - ❌ Do NOT require spec_helper explicitly
316
+ - ❌ Do NOT change production lib/ code
317
+ - ❌ Do NOT use multiple separate `before` blocks on the same context — rubocop RSpec/ScatteredSetup; merge into one
318
+ - ❌ Do NOT assume field names on JumpTestLocations — read the file first
319
+
320
+ ---
321
+
322
+ ## Learnings (inherited from test-coverage-gaps)
323
+
324
+ - **`$CHILD_STATUS` not `$?`** — rubocop Special/GlobalVars cop
325
+ - **`exit` with no code exits 0** — specs asserting no-args exit should expect 0
326
+ - **`options.format` defaults to `'tree,content'`** — never nil
327
+ - **Grep full codebase before writing scope** — actual files may differ from brief
328
+ - **ENV stubbing:** `allow(ENV).to receive(:[]).and_call_original` then targeted override — no climate_control gem
329
+ - **Merge `before` blocks** — multiple separate `before` blocks on same context trigger RSpec/ScatteredSetup
330
+ - **`options.prompt` defaults to nil** — aider format returns `''` when prompt is nil
331
+ - **Parallel wave had zero merge conflicts across 5 agents** — all different files
332
+ - **B023 `build_json`:** returns `JSON.pretty_generate` — must `JSON.parse(result)` before asserting on keys
333
+ - **B028 body assertions:** file content is written in Dir.mktmpdir blocks; check the `File.write` call to know what body text to assert
@@ -0,0 +1,29 @@
1
+ # IMPLEMENTATION_PLAN.md — final-test-gaps
2
+
3
+ **Goal**: Close remaining test quality gaps surfaced by the test-quality-audit: file_collector json/aider/error paths, cli_spec body assertions, add_spec data integrity, update_spec field isolation
4
+ **Started**: 2026-03-19
5
+ **Target**: All 4 work units complete; 817+ examples passing; rubocop clean; no regressions; regression catch rate meaningfully above 55%
6
+
7
+ ## Summary
8
+ - Total: 4 | Complete: 0 | In Progress: 4 | Pending: 0 | Failed: 0
9
+
10
+ ## Pending
11
+
12
+ ## In Progress
13
+ - [~] fix-b023 — file_collector_spec: add json format, aider format, and error path tests
14
+ - [~] fix-b028 — cli_spec: add file body content assertions to -i and -e tests
15
+ - [~] fix-b029 — add_spec: validate ALL returned location data fields match input attrs (path, jump, tags, description)
16
+ - [~] fix-b030 — update_spec: verify non-updated fields are unchanged on both updated record and sibling records
17
+
18
+ ## Complete
19
+
20
+ ## Failed / Needs Retry
21
+
22
+ ## Notes & Decisions
23
+ - All 4 work units are independent — parallel wave
24
+ - Test-only campaign: no lib/ production code changes
25
+ - B019 and B015 already fixed in prior commits — closed without campaign
26
+ - B028 scope: -i tests check `# file: test.rb` header but not body; -e tests same gap. Add body content assertions only (do NOT rewrite existing assertions)
27
+ - B029 scope: "returns the created location data" test at line 35-41 of add_spec.rb — extend it to assert path/jump/tags/description. Add as a new 'it' block rather than modifying existing example.
28
+ - B030 scope: two gaps — (1) updated record: non-updated fields stay the same; (2) sibling record: not just key_exists? but fields are identical
29
+ - B023 scope: Options struct has :prompt field (keyword_init). Aider tests need `prompt: 'my prompt'` in options.
@@ -1,39 +1,42 @@
1
1
  # Next Round Brief
2
2
 
3
3
  **Created:** 2026-03-19
4
- **Updated:** 2026-03-19 (after fr2-gpt-context-help assessment)
4
+ **Updated:** 2026-03-19 (after bugfix-and-security assessment)
5
5
 
6
6
  ---
7
7
 
8
- ## Recommended Next Campaign: bugfix-and-security
8
+ ## Recommended Next Campaign: test-coverage-gaps
9
9
 
10
10
  ### Goal
11
11
 
12
- Fix two BLOCKER-level bugs (B016, B017) and one dead-code guard (B021) before building any new S3 or archive features.
12
+ Protect the B017 SSL security fix with a regression test, expand functional test coverage across gpt_context CLI and DAM range logic, and add the missing Jump Commands layer specs.
13
13
 
14
14
  ### Background
15
15
 
16
- Quality audit after fr2-gpt-context-help surfaced these as blockers:
16
+ Quality audit after bugfix-and-security found:
17
17
 
18
- 1. **B017** — `ssl_verify_peer: false` hardcoded unconditionally in `S3Operations` and `ShareOperations`. Removes MITM protection on all S3 operations including credential transmission. Must fix before any S3 feature work.
19
- 2. **B016** — `ManifestGenerator.determine_range` returns `"b50-b99"` format; `SyncFromSsd.determine_range` returns `"60-69"` format. Incompatible SSD path construction means projects can be silently missed during archive/restore. Must fix before any archive feature work.
20
- 3. **B021** — `bin/gpt_context.rb` line 115 guard checks `options.format.nil?` as third AND condition. `format` defaults to `'content'` in Options — never nil. Dead condition; guard can only fire on include/exclude emptiness. 5-minute fix.
18
+ 1. **B024** — `configure_ssl_options` has zero unit tests. The B017 SSL fix (removing unconditional `ssl_verify_peer: false`) has no regression protection. If reverted, all tests still pass. Must fix.
19
+ 2. **B022** — `cli_spec.rb` only tests `--help`, `--version`, no-args. No functional tests for `-i`, `-e`, `-f`, `-o`. Core behaviour untested at CLI level.
20
+ 3. **B026** — `determine_range` tests narrow (b40, b65, b99 only). Missing: b00, b9, a40. Both sync_from_ssd_spec and manifest_generator_spec need these.
21
+ 4. **B027** — gpt_context no-args spec only checks output string. Does not verify file collection stops.
22
+ 5. **B018** — Jump Commands (Remove/Add/Update) — zero dedicated specs.
23
+ 6. **B025** — Stale comment sync_from_ssd.rb line 173 (says 60-69, should say b50-b99).
21
24
 
22
- ### Suggested Work Units
25
+ ### Suggested Work Units (parallel — all test-only except B025)
23
26
 
24
- 1. **Fix B017** — Remove `ssl_verify_peer: false` from `S3Operations` and `ShareOperations`. No env flag needed AWS SDK handles SSL correctly by default.
25
- 2. **Fix B016** — Align range string format between `ManifestGenerator` and `SyncFromSsd`. Read actual SSD folder structure on disk first to determine which format matches reality; update the other to match.
26
- 3. **Fix B021** — Remove `&& options.format.nil?` from guard at `bin/gpt_context.rb:115`. Update or add spec to verify no-args behavior.
27
-
28
- ### Optional (bundle if small)
29
-
30
- - **B018** — Jump Commands layer specs (Remove/Add/Update) — no code changes, just test coverage
31
- - **B022** — Expand cli_spec.rb with functional tests for -i, -e, -f, -o flags
27
+ 1. **fix-b024-ssl-tests** — Add `configure_ssl_options` unit tests to s3_operations_spec and share_operations_spec. Verify empty hash on default path; `{ssl_verify_peer: false}` when ENV override set. Stub ENV directly (`allow(ENV).to receive(:[]).with('AWS_SDK_RUBY_SKIP_SSL_VERIFICATION').and_return('true')`).
28
+ 2. **fix-b022-cli-tests** — Add functional subprocess tests to cli_spec.rb for -i, -e, -f, -o flags. Write to Tempfile, verify content. Use `Dir.mktmpdir` and clean up after.
29
+ 3. **fix-b026-b025-range-tests** — Add edge cases (b00, b9, a40) to sync_from_ssd_spec and manifest_generator_spec. Fix stale comment sync_from_ssd.rb line 173 while in the file.
30
+ 4. **fix-b027-noargs-test** — Strengthen no-args spec: `expect(Appydave::Tools::GptContext::FileCollector).not_to receive(:new)` when no patterns given.
31
+ 5. **fix-b018-jump-specs** Add spec files for Jump Commands::Remove, Commands::Add, Commands::Update. Read existing Jump CLI spec first for setup pattern. Use JumpTestLocations + `with jump filesystem` context.
32
32
 
33
33
  ### Mode Recommendation
34
34
 
35
- **Extend** — stack, patterns, and quality gates known. Inherit AGENTS.md.
35
+ **Extend** — same stack, same patterns, test-only work. Inherit AGENTS.md.
36
36
 
37
- ### Pre-Campaign Blockers: None
37
+ ### Pre-Campaign Notes
38
38
 
39
- All three fixes are standalone. B016 requires reading SSD disk structure before writing code (per AGENTS.md: read actual files before designing data shapes).
39
+ - Check if `climate_control` gem is in Gemfile before using ClimateControl use direct ENV stubbing if not available
40
+ - For B022 functional tests: subprocess writes to file, assert content includes `# file:` headers
41
+ - For B027: stub at the class level, not instance — `expect(described_class).not_to receive(:new)`
42
+ - For B018: read `spec/appydave/tools/jump/` existing specs before writing new command specs
@@ -0,0 +1,317 @@
1
+ # AGENTS.md — test-coverage-gaps
2
+
3
+ > Inherited from bugfix-and-security AGENTS.md. Self-contained.
4
+ > Last updated: 2026-03-19
5
+
6
+ ---
7
+
8
+ ## Project Overview
9
+
10
+ **What:** Ruby gem providing CLI productivity tools for AppyDave's YouTube content creation workflow.
11
+ **Stack:** Ruby 3.4.2, Bundler 2.6.2, RSpec, RuboCop, semantic-release CI/CD.
12
+ **This campaign:** Test-only gap closure. No lib/ production code changes except 1-line comment fix (B025).
13
+ **Commits:** Always use `kfix` — never `git commit`.
14
+
15
+ ---
16
+
17
+ ## Build & Run Commands
18
+
19
+ ```bash
20
+ eval "$(rbenv init -)"
21
+
22
+ RUBYOPT="-W0" bundle exec rspec # All tests
23
+ bundle exec rspec spec/appydave/tools/dam/s3_operations_spec.rb
24
+ bundle exec rspec spec/appydave/tools/dam/share_operations_spec.rb
25
+ bundle exec rspec spec/appydave/tools/dam/sync_from_ssd_spec.rb
26
+ bundle exec rspec spec/appydave/tools/dam/manifest_generator_spec.rb
27
+ bundle exec rspec spec/appydave/tools/gpt_context/cli_spec.rb
28
+ bundle exec rspec spec/appydave/tools/jump/ # All jump specs
29
+ bundle exec rubocop --format clang
30
+ ```
31
+
32
+ **Baseline:** 759 examples, 0 failures, 85.0% line coverage
33
+
34
+ ---
35
+
36
+ ## Directory Structure
37
+
38
+ ```
39
+ lib/appydave/tools/dam/
40
+ s3_operations.rb configure_ssl_options method (~line 102) — READ ONLY
41
+ share_operations.rb configure_ssl_options method (~line 89) — READ ONLY
42
+ sync_from_ssd.rb determine_range (~line 185); stale comment line 173 — FIX COMMENT
43
+ manifest_generator.rb determine_range (~line 332) — READ ONLY
44
+ jump/
45
+ commands/
46
+ remove.rb READ ONLY — write spec for this
47
+ add.rb READ ONLY — write spec for this (may be called create.rb or set.rb)
48
+ update.rb READ ONLY — write spec for this
49
+ spec/appydave/tools/dam/
50
+ s3_operations_spec.rb ADD configure_ssl_options tests
51
+ share_operations_spec.rb ADD configure_ssl_options tests
52
+ sync_from_ssd_spec.rb ADD determine_range edge cases
53
+ manifest_generator_spec.rb ADD determine_range edge cases
54
+ spec/appydave/tools/gpt_context/
55
+ cli_spec.rb ADD functional tests (-i, -e, -f, -o)
56
+ spec/appydave/tools/jump/
57
+ commands/
58
+ remove_spec.rb CREATE THIS
59
+ add_spec.rb CREATE THIS (check actual filename first)
60
+ update_spec.rb CREATE THIS (check actual filename first)
61
+ spec/support/
62
+ jump_test_helpers.rb Reference for shared context
63
+ jump_test_locations.rb Reference for test fixture data
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Work Unit Details
69
+
70
+ ### fix-b024-ssl-tests
71
+
72
+ **Goal:** Protect the B017 security fix. `configure_ssl_options` must have unit tests verifying:
73
+ 1. Default path returns `{}` (no ssl_verify_peer key at all)
74
+ 2. ENV override path returns `{ ssl_verify_peer: false }`
75
+
76
+ **Check Gemfile first** — if `climate_control` is present, use it. Otherwise, stub ENV directly:
77
+
78
+ ```ruby
79
+ # Direct ENV stub (works without climate_control)
80
+ describe '#configure_ssl_options' do
81
+ subject(:ssl_options) { s3_ops.send(:configure_ssl_options) }
82
+
83
+ context 'when AWS_SDK_RUBY_SKIP_SSL_VERIFICATION is not set' do
84
+ before { allow(ENV).to receive(:[]).and_call_original }
85
+ before { allow(ENV).to receive(:[]).with('AWS_SDK_RUBY_SKIP_SSL_VERIFICATION').and_return(nil) }
86
+
87
+ it 'returns empty hash (SSL verification enabled by default)' do
88
+ expect(ssl_options).to eq({})
89
+ end
90
+
91
+ it 'does not include ssl_verify_peer key' do
92
+ expect(ssl_options).not_to have_key(:ssl_verify_peer)
93
+ end
94
+ end
95
+
96
+ context 'when AWS_SDK_RUBY_SKIP_SSL_VERIFICATION is "true"' do
97
+ before { allow(ENV).to receive(:[]).and_call_original }
98
+ before { allow(ENV).to receive(:[]).with('AWS_SDK_RUBY_SKIP_SSL_VERIFICATION').and_return('true') }
99
+
100
+ it 'returns ssl_verify_peer: false' do
101
+ expect(ssl_options).to eq({ ssl_verify_peer: false })
102
+ end
103
+ end
104
+ end
105
+ ```
106
+
107
+ Read the existing spec to find how `s3_ops` subject is set up. Add these examples within the existing describe block structure. Same pattern for `share_operations_spec.rb`.
108
+
109
+ **Commit:** `kfix "add configure_ssl_options unit tests to protect B017 security fix"`
110
+
111
+ ---
112
+
113
+ ### fix-b022-cli-tests
114
+
115
+ **Goal:** Functional subprocess tests for gpt_context CLI. Test that the actual flags work end-to-end.
116
+
117
+ **Pattern — write to Tempfile, verify content:**
118
+
119
+ ```ruby
120
+ describe '-i include pattern' do
121
+ it 'collects files matching the include pattern' do
122
+ Dir.mktmpdir do |tmpdir|
123
+ # Create a test file
124
+ File.write(File.join(tmpdir, 'test.rb'), '# test content')
125
+ outfile = File.join(tmpdir, 'output.txt')
126
+
127
+ `ruby #{script} -i '*.rb' -b #{tmpdir} -o #{outfile} 2>&1`
128
+
129
+ expect(File.read(outfile)).to include('# file: test.rb')
130
+ end
131
+ end
132
+ end
133
+
134
+ describe '-e exclude pattern' do
135
+ it 'excludes files matching the exclude pattern' do
136
+ Dir.mktmpdir do |tmpdir|
137
+ File.write(File.join(tmpdir, 'keep.rb'), '# keep')
138
+ File.write(File.join(tmpdir, 'exclude.rb'), '# exclude')
139
+ outfile = File.join(tmpdir, 'output.txt')
140
+
141
+ `ruby #{script} -i '*.rb' -e 'exclude.rb' -b #{tmpdir} -o #{outfile} 2>&1`
142
+
143
+ content = File.read(outfile)
144
+ expect(content).to include('# file: keep.rb')
145
+ expect(content).not_to include('# file: exclude.rb')
146
+ end
147
+ end
148
+ end
149
+
150
+ describe '-f format' do
151
+ it 'outputs tree format when -f tree specified' do
152
+ Dir.mktmpdir do |tmpdir|
153
+ File.write(File.join(tmpdir, 'test.rb'), '# test')
154
+ outfile = File.join(tmpdir, 'output.txt')
155
+
156
+ `ruby #{script} -i '*.rb' -f tree -b #{tmpdir} -o #{outfile} 2>&1`
157
+
158
+ expect(File.read(outfile)).to include('test.rb')
159
+ end
160
+ end
161
+ end
162
+ ```
163
+
164
+ Note: `-b` sets the base directory, `-o` writes to file. Read the existing cli_spec.rb first for the `script` let binding.
165
+
166
+ **Commit:** `kfix "add functional CLI tests for -i -e -f -o flags to cli_spec"`
167
+
168
+ ---
169
+
170
+ ### fix-b026-b025-range-tests
171
+
172
+ **Goal:** Add edge case tests for `determine_range` + fix 1-line stale comment.
173
+
174
+ **In `sync_from_ssd_spec.rb`** — add to the existing `describe '#determine_range'` block:
175
+ ```ruby
176
+ it 'determines range for boundary b00' do
177
+ range = sync_from_ssd.send(:determine_range, 'b00-first-project')
178
+ expect(range).to eq('b00-b49')
179
+ end
180
+
181
+ it 'determines range for single-digit b9' do
182
+ range = sync_from_ssd.send(:determine_range, 'b9-project')
183
+ expect(range).to eq('b00-b49')
184
+ end
185
+
186
+ it 'determines range for non-b letter prefix a40' do
187
+ range = sync_from_ssd.send(:determine_range, 'a40-test-project')
188
+ expect(range).to eq('a00-a49')
189
+ end
190
+ ```
191
+
192
+ **In `manifest_generator_spec.rb`** — add same 3 examples to the `describe '#determine_range'` block.
193
+
194
+ **In `sync_from_ssd.rb` line 173** — update comment:
195
+ ```ruby
196
+ # Determine local destination path (archived structure)
197
+ # Extract range from project ID (e.g., b65 → b50-b99 range)
198
+ ```
199
+ (Was: `b65 → 60-69 range`)
200
+
201
+ **Commit:** `kfix "add determine_range edge cases and fix stale comment"`
202
+
203
+ ---
204
+
205
+ ### fix-b027-noargs-test
206
+
207
+ **Goal:** Strengthen the no-args gpt_context spec to verify file collection stops — not just message output.
208
+
209
+ The subprocess approach can't easily stub FileCollector. Use a stronger output assertion instead:
210
+
211
+ ```ruby
212
+ describe 'no arguments' do
213
+ it 'prints an error message when no patterns provided' do
214
+ output = `ruby #{script} 2>&1`
215
+ expect(output).to include('No options provided')
216
+ expect($CHILD_STATUS.exitstatus).to eq(0)
217
+ end
218
+
219
+ it 'does not produce file content output when no patterns provided' do
220
+ output = `ruby #{script} 2>&1`
221
+ expect(output).not_to include('# file:')
222
+ expect(output).not_to include('clipboard')
223
+ end
224
+ end
225
+ ```
226
+
227
+ The second test verifies file collection output markers are absent — catches regressions where the guard is removed but the script continues to run.
228
+
229
+ Read existing cli_spec.rb first — update the existing no-args describe block rather than adding a duplicate.
230
+
231
+ **Commit:** `kfix "strengthen gpt_context no-args spec to verify collection does not proceed"`
232
+
233
+ ---
234
+
235
+ ### fix-b018-jump-specs
236
+
237
+ **Goal:** Add dedicated unit specs for Jump Commands::Remove, Commands::Add (or Create), Commands::Update.
238
+
239
+ **Step 1:** Read the source files first:
240
+ ```
241
+ lib/appydave/tools/jump/commands/
242
+ ```
243
+ List what files exist — the command names may differ from Remove/Add/Update.
244
+
245
+ **Step 2:** Read an existing jump spec (e.g., `spec/appydave/tools/jump/`) for setup patterns.
246
+
247
+ **Step 3:** For each command, create a spec file covering:
248
+ - Happy path: command executes with valid key
249
+ - `--force` guard: command without --force prompts/refuses where applicable
250
+ - Not-found path: key does not exist — shows suggestion or error
251
+ - Error codes / output messages
252
+
253
+ **Pattern (based on jump_test_helpers.rb):**
254
+ ```ruby
255
+ # frozen_string_literal: true
256
+
257
+ RSpec.describe Appydave::Tools::Jump::Commands::Remove do
258
+ include_context 'with jump filesystem'
259
+
260
+ describe '#run' do
261
+ context 'when location exists' do
262
+ before { setup_jump_config([JumpTestLocations.ad_tools]) }
263
+
264
+ it 'removes the location with --force' do
265
+ # ...
266
+ end
267
+
268
+ it 'refuses to remove without --force' do
269
+ # ...
270
+ end
271
+ end
272
+
273
+ context 'when location does not exist' do
274
+ it 'shows not-found error' do
275
+ # ...
276
+ end
277
+ end
278
+ end
279
+ end
280
+ ```
281
+
282
+ **Commit:** `kfix "add dedicated specs for Jump Commands Remove Add Update"`
283
+
284
+ ---
285
+
286
+ ## Success Criteria
287
+
288
+ - [ ] `RUBYOPT="-W0" bundle exec rspec` — 759+ examples, 0 failures
289
+ - [ ] `bundle exec rubocop --format clang` — 0 offenses
290
+ - [ ] Line coverage stays ≥ 85.0%
291
+ - [ ] `configure_ssl_options` default path verified to NOT include ssl_verify_peer
292
+ - [ ] All new spec files start with `# frozen_string_literal: true`
293
+ - [ ] No `require 'spec_helper'` in new spec files (auto-required)
294
+
295
+ ---
296
+
297
+ ## Anti-Patterns to Avoid
298
+
299
+ - ❌ Do NOT use `$?` in specs — use `$CHILD_STATUS` (rubocop)
300
+ - ❌ Do NOT use `options.format.nil?` — format defaults to 'tree,content', never nil
301
+ - ❌ Do NOT mock internal DAM classes — use shared filesystem context
302
+ - ❌ Do NOT require spec_helper explicitly
303
+ - ❌ Do NOT modify production lib/ code (except 1-line comment fix in sync_from_ssd.rb)
304
+ - ❌ Do NOT use `puts` in lib/ — use `warn` for warnings
305
+
306
+ ---
307
+
308
+ ## Learnings (inherited)
309
+
310
+ - **`$CHILD_STATUS` not `$?`** — rubocop Special/GlobalVars cop
311
+ - **`exit` with no code exits 0** — specs asserting no-args exit should expect 0
312
+ - **`options.format` defaults to `'tree,content'`** — never nil
313
+ - **Grep full codebase before writing scope** — actual files may differ from brief
314
+ - **Integration path assertions** — when changing path algorithms, search specs for old path strings
315
+ - **BrandResolver is critical path** — all dam commands flow through it
316
+ - **`Regexp.last_match` reset by `.sub()`** — capture groups before string transformation
317
+ - **Dependency injection for path validators** — required for CI compatibility in Jump tests
@@ -0,0 +1,29 @@
1
+ # IMPLEMENTATION_PLAN.md — test-coverage-gaps
2
+
3
+ **Goal**: Close critical test gaps: protect B017 SSL fix, expand gpt_context CLI coverage, range edge cases, Jump Commands specs
4
+ **Started**: 2026-03-19
5
+ **Target**: All gaps addressed; 759+ examples passing; rubocop clean; no regressions
6
+
7
+ ## Summary
8
+ - Total: 5 | Complete: 0 | In Progress: 5 | Pending: 0 | Failed: 0
9
+
10
+ ## Pending
11
+
12
+ ## In Progress
13
+ - [~] fix-b024-ssl-tests — Add configure_ssl_options unit tests to s3_operations_spec + share_operations_spec
14
+ - [~] fix-b022-cli-tests — Add functional subprocess tests to gpt_context cli_spec.rb (-i, -e, -f, -o)
15
+ - [~] fix-b018-jump-specs — Add specs for Jump Commands::Remove, Add, Update
16
+
17
+ ## Complete
18
+ - [x] fix-b027-noargs-test — Added second no-args example: verifies output does NOT include '# file:' or 'clipboard'. 766 examples, 0 failures. Note: B024 agent had RSpec/ScatteredSetup rubocop issue (multiple before hooks) — fixed in subsequent commit.
19
+ - [x] fix-b026-b025-range-tests — Edge cases (b00, b9, a40) added to sync_from_ssd_spec + manifest_generator_spec. Stale comment fixed in sync_from_ssd.rb:173. ⚠️ CI ISSUE: cli_spec.rb:39 failing — 'not_to include clipboard' assertion is fragile (no-args output may mention clipboard on some platforms). B022 agent touching cli_spec.rb may resolve this.
20
+
21
+ ## Failed / Needs Retry
22
+
23
+ ## Notes & Decisions
24
+ - All 5 work units are independent — parallel wave
25
+ - Test-only campaign except B025 (1-line comment fix in sync_from_ssd.rb)
26
+ - ENV stubbing: use allow(ENV).to receive(:[]) — check Gemfile for climate_control first
27
+ - B022 functional tests: write to Tempfile, verify # file: headers in output
28
+ - B027: stub GptContext::FileCollector at class level before subprocess call won't work — use integration assertion instead (verify output does NOT contain file collection output)
29
+ - B018: read existing jump CLI spec before writing command-layer specs
@@ -0,0 +1,75 @@
1
+ # Assessment: test-coverage-gaps
2
+
3
+ **Campaign**: test-coverage-gaps
4
+ **Date**: 2026-03-19 → 2026-03-19
5
+ **Results**: 5 complete, 0 failed
6
+ **Version shipped**: v0.76.2
7
+ **Quality audit**: code-quality-audit + test-quality-audit run post-campaign
8
+
9
+ ---
10
+
11
+ ## Results Summary
12
+
13
+ | Work Unit | Examples Added | Notes |
14
+ |-----------|---------------|-------|
15
+ | fix-b024-ssl-tests | +6 | configure_ssl_options protected in s3_operations + share_operations |
16
+ | fix-b022-cli-tests | +10 | -i/-e/-f/-o functional tests with real file I/O |
17
+ | fix-b026-b025 | +6 + comment | b00/b9/a40 edge cases; stale comment fixed |
18
+ | fix-b027-noargs | +1 | No-file-collection assertion added |
19
+ | fix-b018-jump | +36 | Remove/Add/Update fully covered + fixed clipboard assertion |
20
+
21
+ **759 → 817 examples (+58). 85.0% → 85.61% coverage.**
22
+
23
+ ---
24
+
25
+ ## What Worked Well
26
+
27
+ - **36 Jump Commands specs landed cleanly.** The accumulated AGENTS.md (JumpTestLocations, `with jump filesystem` context, TestPathValidator) meant agents could write correct specs without scaffolding guidance.
28
+ - **B018 agent fixed the B027 clipboard CI failure.** Unblocked itself and the other agents.
29
+ - **ENV stubbing pattern worked without climate_control.** `allow(ENV).to receive(:[]).and_call_original` + targeted override — captured in AGENTS.md.
30
+ - **Parallel wave had zero merge conflicts.** 5 agents, all different files, clean.
31
+
32
+ ---
33
+
34
+ ## What Didn't Work
35
+
36
+ **Test quality is C overall (test audit finding).**
37
+
38
+ Specific gaps the audit surfaced:
39
+ - **cli_spec.rb (-i/-o tests):** Assert `# file:` header present but never check file body content. File truncation or body corruption would not be caught.
40
+ - **add_spec.rb:** After a successful add, only checks `key_exists?` — never validates returned location data matches input attrs (key, path, jump, tags).
41
+ - **update_spec.rb:** Tests verify the updated field changed but never verify other fields were NOT changed.
42
+ - **s3_operations_spec.rb:** Some output format checks use soft regex.
43
+
44
+ Regression catch rate: ~55%. Would catch code path breaks and guard logic. Would miss data integrity and content corruption.
45
+
46
+ ---
47
+
48
+ ## Key Learnings — Application
49
+
50
+ - **B015 is already fixed.** BACKLOG.md lists B015 as pending but file_collector.rb already uses block form `FileUtils.cd(@working_directory) { build_formats }` from commit 13d5f87. Close it.
51
+ - **`allow(ENV).to receive(:[]).and_call_original`** is the correct ENV stub pattern when climate_control is absent. Must call `and_call_original` first, then override specific key.
52
+ - **Merge `before` blocks** to avoid `RSpec/ScatteredSetup` — multiple separate `before` blocks on same context trigger the cop.
53
+ - **Jump Commands layer is now tested.** base.rb/generate.rb/validate.rb already had specs; Remove/Add/Update are now covered.
54
+
55
+ ---
56
+
57
+ ## New Backlog Items from Quality Audit
58
+
59
+ - **B028** — Tests: cli_spec.rb add file body content assertions to -i/-o tests | Priority: medium
60
+ - **B029** — Tests: add_spec.rb validate returned location data matches input attrs exactly | Priority: medium
61
+ - **B030** — Tests: update_spec.rb verify non-updated fields remain unchanged | Priority: low
62
+
63
+ ---
64
+
65
+ ## Suggestions for Next Campaign
66
+
67
+ **B015 should be closed immediately** — the fix is already in the code. Update BACKLOG.md, don't plan a campaign for it.
68
+
69
+ Next meaningful work:
70
+ 1. **B023** — file_collector_spec: JSON, aider, error paths (last test-coverage gap)
71
+ 2. **B028** — cli_spec file body assertions (from this audit)
72
+ 3. **B029** — add_spec data integrity assertions
73
+ 4. These are small — could bundle into one campaign with B030
74
+
75
+ Or pivot to **architectural** items (B011 — extract VatCLI, B020 — split S3Operations) which are larger but unblock performance work (B007/B008).
@@ -170,7 +170,7 @@ module Appydave
170
170
  return { skipped: 1, files: 0, bytes: 0, reason: 'Flat folder exists (stale manifest?)' } if Dir.exist?(flat_path)
171
171
 
172
172
  # Determine local destination path (archived structure)
173
- # Extract range from project ID (e.g., b65 → 60-69 range)
173
+ # Extract range from project ID (e.g., b65 → b50-b99 range)
174
174
  range = determine_range(project_id)
175
175
  local_dir = File.join(brand_path, 'archived', range, project_id)
176
176
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Appydave
4
4
  module Tools
5
- VERSION = '0.76.1'
5
+ VERSION = '0.76.3'
6
6
  end
7
7
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appydave-tools",
3
- "version": "0.76.1",
3
+ "version": "0.76.3",
4
4
  "description": "AppyDave YouTube Automation Tools",
5
5
  "scripts": {
6
6
  "release": "semantic-release"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appydave-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.76.1
4
+ version: 0.76.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Cruwys
@@ -299,10 +299,16 @@ files:
299
299
  - docs/planning/BACKLOG.md
300
300
  - docs/planning/bugfix-and-security/AGENTS.md
301
301
  - docs/planning/bugfix-and-security/IMPLEMENTATION_PLAN.md
302
+ - docs/planning/bugfix-and-security/assessment.md
303
+ - docs/planning/final-test-gaps/AGENTS.md
304
+ - docs/planning/final-test-gaps/IMPLEMENTATION_PLAN.md
302
305
  - docs/planning/fr2-gpt-context-help/AGENTS.md
303
306
  - docs/planning/fr2-gpt-context-help/IMPLEMENTATION_PLAN.md
304
307
  - docs/planning/fr2-gpt-context-help/assessment.md
305
308
  - docs/planning/next-round-brief.md
309
+ - docs/planning/test-coverage-gaps/AGENTS.md
310
+ - docs/planning/test-coverage-gaps/IMPLEMENTATION_PLAN.md
311
+ - docs/planning/test-coverage-gaps/assessment.md
306
312
  - docs/specs/fr-002-gpt-context-help-system.md
307
313
  - docs/specs/fr-003-jump-location-tool.md
308
314
  - docs/specs/zsh-history-tool.md