appydave-tools 0.76.0 → 0.76.2

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: 39c3338d4f56e9b453669e54150db39c4449b667fa347a2634b2741fbd272235
4
- data.tar.gz: 5949d3218ac0ca10376e9307ee981fd88a9505d876ba88178a7a6647447b8077
3
+ metadata.gz: 9e0809dc40e0f4940381134c70a57833d755df47f01f8e327a04b278e4d87c5a
4
+ data.tar.gz: a1aa1d00b87481b371edda22ed5f7f0fa63a895358002cbc330b4d4e8cbf1ae8
5
5
  SHA512:
6
- metadata.gz: 9b52b37eb3e5ebda0eb8508f054b53cf607b208e2ba2c227418381bc77940720741c7feea5e9edc47ca8942da0c5bb4e20d79034d8b3e345ad10f0823bedd76a
7
- data.tar.gz: 6aed7f6c4d2d16b7706d6449ba3759e5b24cc08d3ac602c979485babc72625d632588ebf070211e9b68a11d5af5a75151d4b6a92729c50a8335a59a1040d919d
6
+ metadata.gz: 8ef02094f475fc3df1b6b346158dce9596cbc4898c4a5da60e573ae48ddb8ff10663d2b19be0da582adea9b47597a14926e7e55358d33e742762d8cf7b53bf6f
7
+ data.tar.gz: 1c96b7e36178f5f0ec3426934a88686fb23e19d890858c1825264bb3a33e67d6054ed520cd4157ff60906f468a91cd25d2c319696b6a1ae936baaca8a013f627
data/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## [0.76.1](https://github.com/appydave/appydave-tools/compare/v0.76.0...v0.76.1) (2026-03-19)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * align SyncFromSsd#determine_range with ManifestGenerator format; add specs ([3f8cb37](https://github.com/appydave/appydave-tools/commit/3f8cb37a78bd3d9720eace21cf1abbbca285cd66))
7
+ * remove unconditional ssl_verify_peer: false from S3 clients; keep env override ([f49efb1](https://github.com/appydave/appydave-tools/commit/f49efb1c4a97ba904f3f3fc7d4292139a5e6c3fd))
8
+
9
+ # [0.76.0](https://github.com/appydave/appydave-tools/compare/v0.75.0...v0.76.0) (2026-03-19)
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * fix pre-existing rubocop offenses in bin/dam and split jump test helpers to avoid OneClassPerFile ([491b980](https://github.com/appydave/appydave-tools/commit/491b980c335b7e3d205a5368cd0a7b96e5b5a82f))
15
+ * remove debug puts and fix unguarded FileUtils.cd in FileCollector; close BUG-1 ([13d5f87](https://github.com/appydave/appydave-tools/commit/13d5f8793d5185cb9795cab86a5270726763c30e))
16
+
17
+
18
+ ### Features
19
+
20
+ * add AI-friendly help system to GPT Context ([c0b8843](https://github.com/appydave/appydave-tools/commit/c0b884323654a248d660ea137712e7c0e5fc172c))
21
+
1
22
  # [0.75.0](https://github.com/appydave/appydave-tools/compare/v0.74.1...v0.75.0) (2026-02-08)
2
23
 
3
24
 
data/bin/gpt_context.rb CHANGED
@@ -112,7 +112,7 @@ OptionParser.new do |opts|
112
112
  end
113
113
  end.parse!
114
114
 
115
- if options.include_patterns.empty? && options.exclude_patterns.empty? && options.format.nil?
115
+ if options.include_patterns.empty? && options.exclude_patterns.empty?
116
116
  script_name = File.basename($PROGRAM_NAME, File.extname($PROGRAM_NAME))
117
117
 
118
118
  puts 'No options provided to GPT Context. Please specify patterns to include or exclude.'
@@ -1,25 +1,29 @@
1
1
  # Project Backlog — AppyDave Tools
2
2
 
3
- **Last updated**: 2026-03-19 (updated after three-lens audit)
4
- **Total**: 20 | Pending: 14 | Done: 6 | 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
- - [ ] B002 — FR-2: GPT Context AI-friendly help system | Priority: high
14
13
  - [x] B006 — BUG-1: Jump CLI get/remove key lookup | Completed: verified fixed 2026-03-19, regression spec added
15
- - [ ] B017 — Security: ssl_verify_peer disabled unconditionally in S3Operations + ShareOperations | Priority: high
16
14
 
17
15
  ### Medium Priority
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
+ - [ ] B023 — Tests: add file_collector_spec coverage for JSON format, aider format, error paths | Priority: medium
18
20
  - [ ] B018 — Tests: add specs for Jump Commands::Remove, Commands::Add, Commands::Update | Priority: medium
19
21
  - [ ] B019 — Fix: remove debug puts @working_directory from gpt_context/file_collector.rb | Priority: medium
20
22
  - [ ] B001 — FR-1: GPT Context token counting | Priority: medium
21
23
  - [ ] B012 — Arch: add integration tests for brand resolution end-to-end | Priority: medium
22
24
 
25
+ - [ ] B025 — Fix: stale comment in sync_from_ssd.rb line 173 (says 60-69, should say b50-b99) | Priority: low
26
+
23
27
  ### Low Priority
24
28
  - [ ] B007 — Performance: parallel git/S3 status checks for dam list | Priority: low
25
29
  - [ ] B008 — Performance: cache git/S3 status with 5-min TTL | Priority: low
@@ -37,6 +41,10 @@
37
41
  - [x] B005 — NFR-2: Jump Claude Code Skill | Completed: 2025-12-14
38
42
  - [x] B013 — Arch: Extract GitHelper module (90 lines duplication) | Completed: dam-enhancement-sprint (Jan 2025)
39
43
  - [x] B014 — Arch: Create BrandResolver to centralize brand transformation | Completed: dam-enhancement-sprint (Jan 2025)
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)
40
48
 
41
49
  ---
42
50
 
@@ -0,0 +1,329 @@
1
+ # AGENTS.md — bugfix-and-security
2
+
3
+ > Inherited from docs/planning/AGENTS.md + fr2-gpt-context-help campaign learnings.
4
+ > Self-contained — you receive only this file + your work unit prompt.
5
+ > Last updated: 2026-03-19
6
+
7
+ ---
8
+
9
+ ## Project Overview
10
+
11
+ **What:** Ruby gem providing CLI productivity tools for AppyDave's YouTube content creation workflow.
12
+ **Stack:** Ruby 3.4.2, Bundler 2.6.2, RSpec, RuboCop, semantic-release CI/CD.
13
+ **This campaign:** 3 independent bug fixes — security (B017), data integrity (B016), dead code (B021).
14
+ **Commits:** Always use `kfeat`/`kfix` — never `git commit`.
15
+
16
+ ---
17
+
18
+ ## Build & Run Commands
19
+
20
+ ```bash
21
+ eval "$(rbenv init -)"
22
+
23
+ # Run all tests
24
+ RUBYOPT="-W0" bundle exec rspec
25
+
26
+ # Run specific spec files
27
+ bundle exec rspec spec/appydave/tools/dam/s3_operations_spec.rb
28
+ bundle exec rspec spec/appydave/tools/dam/sync_from_ssd_spec.rb
29
+ bundle exec rspec spec/appydave/tools/dam/manifest_generator_spec.rb
30
+ bundle exec rspec spec/appydave/tools/gpt_context/cli_spec.rb
31
+
32
+ # Lint
33
+ bundle exec rubocop --format clang
34
+
35
+ # Commit (never use git commit directly)
36
+ kfix "description of what you fixed"
37
+ ```
38
+
39
+ **Baseline (2026-03-19):** 754 examples, 0 failures, 84.92% line coverage
40
+
41
+ ---
42
+
43
+ ## Directory Structure
44
+
45
+ ```
46
+ lib/appydave/tools/dam/
47
+ s3_operations.rb B017 — configure_ssl_options method (~line 102)
48
+ share_operations.rb B017 — configure_ssl_options method (~line 89)
49
+ s3_scanner.rb B017 — inline ssl_verify_peer in Aws::S3::Client.new (~line 111)
50
+ sync_from_ssd.rb B016 — determine_range method (~line 185)
51
+ manifest_generator.rb B016 — determine_range method (~line 332) — canonical format
52
+ spec/appydave/tools/dam/
53
+ s3_operations_spec.rb Existing spec — do not break
54
+ share_operations_spec.rb Existing spec — do not break
55
+ sync_from_ssd_spec.rb B016 — update determine_range examples (~line 277)
56
+ manifest_generator_spec.rb B016 — add determine_range examples (currently none)
57
+ bin/gpt_context.rb B021 — guard at line 115
58
+ spec/appydave/tools/gpt_context/
59
+ cli_spec.rb B021 — add/update no-args spec
60
+ ```
61
+
62
+ ---
63
+
64
+ ## Work Unit Details
65
+
66
+ ### fix-b017-ssl
67
+
68
+ **Problem:** `ssl_verify_peer: false` is set unconditionally in 3 files, disabling MITM protection on all S3 operations including AWS credential transmission. The comment "safe for AWS S3" is incorrect — HTTPS encryption alone does not protect against a MITM attack without peer verification.
69
+
70
+ **Files to change:**
71
+
72
+ **1. `lib/appydave/tools/dam/s3_operations.rb` (~line 102):**
73
+ ```ruby
74
+ # BEFORE:
75
+ def configure_ssl_options
76
+ if ENV['AWS_SDK_RUBY_SKIP_SSL_VERIFICATION'] == 'true'
77
+ puts '⚠️ WARNING: SSL verification is disabled (development mode)'
78
+ return { ssl_verify_peer: false }
79
+ end
80
+
81
+ # Disable SSL peer verification to work around OpenSSL 3.4.x CRL checking issues
82
+ # This is safe for AWS S3 connections as we're still using HTTPS (encrypted connection)
83
+ {
84
+ ssl_verify_peer: false
85
+ }
86
+ end
87
+
88
+ # AFTER:
89
+ def configure_ssl_options
90
+ return { ssl_verify_peer: false } if ENV['AWS_SDK_RUBY_SKIP_SSL_VERIFICATION'] == 'true'
91
+
92
+ {}
93
+ end
94
+ ```
95
+
96
+ **2. `lib/appydave/tools/dam/share_operations.rb` (~line 89):**
97
+ Same pattern as s3_operations.rb — same fix.
98
+
99
+ **3. `lib/appydave/tools/dam/s3_scanner.rb` (~line 111):**
100
+ ```ruby
101
+ # BEFORE (inline in Aws::S3::Client.new):
102
+ Aws::S3::Client.new(
103
+ credentials: credentials,
104
+ region: brand_info.aws.region,
105
+ http_wire_trace: false,
106
+ ssl_verify_peer: false
107
+ )
108
+
109
+ # AFTER:
110
+ Aws::S3::Client.new(
111
+ credentials: credentials,
112
+ region: brand_info.aws.region,
113
+ http_wire_trace: false
114
+ )
115
+ ```
116
+
117
+ **Commit:** `kfix "remove unconditional ssl_verify_peer: false from S3 clients; keep env override"`
118
+
119
+ **Note on WARNING puts:** The original had `puts '⚠️ WARNING...'` in the env-guard branch. You may keep or remove it — if you keep it, ensure rubocop doesn't flag it (it shouldn't). If RuboCop complains about the `puts`, use `warn` instead.
120
+
121
+ ---
122
+
123
+ ### fix-b016-range
124
+
125
+ **Problem:** Two methods both named `determine_range` produce incompatible folder path strings:
126
+ - `ManifestGenerator#determine_range('b65')` → `"b50-b99"` (letter + 50-number range)
127
+ - `SyncFromSsd#determine_range('b65')` → `"60-69"` (no letter, 10-number range)
128
+
129
+ Both are used to construct `archived/[range]/[project_id]` paths. A project archived via SyncFromSsd lands in `archived/60-69/b65-project/`. ManifestGenerator then looks for it at `archived/b50-b99/b65-project/` — misses it (though glob fallback saves it in practice).
130
+
131
+ **ManifestGenerator format is canonical** — it handles any letter prefix (a*, b*, c*, ...) and uses 50-number ranges which are less granular and more stable.
132
+
133
+ **Fix: Update `SyncFromSsd#determine_range` to match ManifestGenerator's algorithm:**
134
+
135
+ ```ruby
136
+ # lib/appydave/tools/dam/sync_from_ssd.rb
137
+
138
+ # BEFORE (~line 185):
139
+ def determine_range(project_id)
140
+ # FliVideo pattern: b40, b41, ... b99
141
+ if project_id =~ /^b(\d+)/
142
+ tens = (Regexp.last_match(1).to_i / 10) * 10
143
+ "#{tens}-#{tens + 9}"
144
+ else
145
+ '000-099'
146
+ end
147
+ end
148
+
149
+ # AFTER (match ManifestGenerator exactly):
150
+ def determine_range(project_id)
151
+ if project_id =~ /^([a-z])(\d+)/
152
+ letter = Regexp.last_match(1)
153
+ number = Regexp.last_match(2).to_i
154
+ range_start = (number / 50) * 50
155
+ range_end = range_start + 49
156
+ format("#{letter}%02d-#{letter}%02d", range_start, range_end)
157
+ else
158
+ '000-099'
159
+ end
160
+ end
161
+ ```
162
+
163
+ **Update `spec/appydave/tools/dam/sync_from_ssd_spec.rb` — the existing determine_range examples (~line 277) must be updated to the new format:**
164
+
165
+ ```ruby
166
+ describe '#determine_range' do
167
+ it 'determines range for FliVideo pattern b40' do
168
+ range = sync_from_ssd.send(:determine_range, 'b40-test-project')
169
+ expect(range).to eq('b00-b49')
170
+ end
171
+
172
+ it 'determines range for FliVideo pattern b65' do
173
+ range = sync_from_ssd.send(:determine_range, 'b65-guy-monroe')
174
+ expect(range).to eq('b50-b99')
175
+ end
176
+
177
+ it 'determines range for FliVideo pattern b99' do
178
+ range = sync_from_ssd.send(:determine_range, 'b99-final-project')
179
+ expect(range).to eq('b50-b99')
180
+ end
181
+
182
+ it 'returns default range for non-FliVideo pattern' do
183
+ range = sync_from_ssd.send(:determine_range, 'boy-baker')
184
+ expect(range).to eq('000-099')
185
+ end
186
+ end
187
+ ```
188
+
189
+ **Add to `spec/appydave/tools/dam/manifest_generator_spec.rb` — currently zero specs for determine_range:**
190
+
191
+ Look at the existing manifest_generator_spec.rb for the describe block structure and shared context, then add:
192
+ ```ruby
193
+ describe '#determine_range' do
194
+ it 'determines range for b40' do
195
+ range = manifest_generator.send(:determine_range, 'b40-test-project')
196
+ expect(range).to eq('b00-b49')
197
+ end
198
+
199
+ it 'determines range for b65' do
200
+ range = manifest_generator.send(:determine_range, 'b65-guy-monroe')
201
+ expect(range).to eq('b50-b99')
202
+ end
203
+
204
+ it 'determines range for b99' do
205
+ range = manifest_generator.send(:determine_range, 'b99-final-project')
206
+ expect(range).to eq('b50-b99')
207
+ end
208
+
209
+ it 'returns default range for non-FliVideo pattern' do
210
+ range = manifest_generator.send(:determine_range, 'boy-baker')
211
+ expect(range).to eq('000-099')
212
+ end
213
+ end
214
+ ```
215
+
216
+ Read `manifest_generator_spec.rb` first to understand how the `manifest_generator` subject is set up before adding these examples.
217
+
218
+ **Commit:** `kfix "align SyncFromSsd#determine_range with ManifestGenerator format; add specs"`
219
+
220
+ ---
221
+
222
+ ### fix-b021-guard
223
+
224
+ **Problem:** `bin/gpt_context.rb` line 115:
225
+ ```ruby
226
+ if options.include_patterns.empty? && options.exclude_patterns.empty? && options.format.nil?
227
+ ```
228
+ `options.format` defaults to `'content'` in the Options class — it is never nil. The third AND condition is always false, making the whole guard dead when only format is set. In practice the guard fires on empty include+exclude (which is the normal "no args" path), but the dead condition is confusing and could mask future bugs.
229
+
230
+ **Fix:** Remove `&& options.format.nil?` from line 115:
231
+ ```ruby
232
+ # BEFORE:
233
+ if options.include_patterns.empty? && options.exclude_patterns.empty? && options.format.nil?
234
+
235
+ # AFTER:
236
+ if options.include_patterns.empty? && options.exclude_patterns.empty?
237
+ ```
238
+
239
+ **Add/update spec in `spec/appydave/tools/gpt_context/cli_spec.rb`:**
240
+
241
+ Add a context for no-args behavior:
242
+ ```ruby
243
+ describe 'no arguments' do
244
+ it 'exits with an error message when no patterns provided' do
245
+ output = `ruby #{script} 2>&1`
246
+ expect(output).to include('No options provided')
247
+ expect($?.exitstatus).to_not eq(0)
248
+ end
249
+ end
250
+ ```
251
+
252
+ Note: The script currently calls `exit` (no code) on this path — check the actual exit status. If it exits 0, adjust the expectation or leave exit code assertion out and just check the message.
253
+
254
+ **Commit:** `kfix "remove dead format.nil? guard in gpt_context no-args check"`
255
+
256
+ ---
257
+
258
+ ## Success Criteria
259
+
260
+ Every work unit must satisfy ALL before marking `[x]`:
261
+
262
+ - [ ] `RUBYOPT="-W0" bundle exec rspec` — 754+ examples, 0 failures
263
+ - [ ] `bundle exec rubocop --format clang` — 0 offenses
264
+ - [ ] Line coverage stays ≥ 84.92%
265
+ - [ ] All new `.rb` file additions start with `# frozen_string_literal: true`
266
+ - [ ] Commit uses `kfix` (not `git commit`)
267
+
268
+ ---
269
+
270
+ ## Anti-Patterns to Avoid
271
+
272
+ - ❌ Do NOT remove the `ENV['AWS_SDK_RUBY_SKIP_SSL_VERIFICATION']` dev escape hatch — only remove the unconditional fallback below it
273
+ - ❌ Do NOT mock S3 clients when testing ssl options — check what the existing specs do and follow the pattern
274
+ - ❌ Do NOT change ManifestGenerator#determine_range — it is the canonical implementation, SyncFromSsd is the one to fix
275
+ - ❌ Do NOT use `puts` in lib/ code without checking if it violates rubocop (use `warn` for warnings)
276
+ - ❌ Do NOT inline brand transformations — always use BrandResolver
277
+ - ❌ Do NOT use `require 'spec_helper'` in new spec files — auto-required via .rspec
278
+
279
+ ---
280
+
281
+ ## Mock Patterns
282
+
283
+ ### S3 Tests — Check Existing Specs First
284
+
285
+ Before writing any new S3-related test, read the existing spec file for the class you're touching. The existing patterns use WebMock or stub the S3 client at a higher level. Do NOT add new S3 network stubs — just verify the existing test suite still passes after your change.
286
+
287
+ ### DAM Filesystem Tests — Shared Context
288
+
289
+ ```ruby
290
+ include_context 'with vat filesystem and brands', brands: %w[appydave]
291
+ ```
292
+
293
+ ---
294
+
295
+ ## Quality Gates
296
+
297
+ - **Tests:** 754+ examples, 0 failures
298
+ - **Lint:** 0 rubocop offenses
299
+ - **Coverage:** ≥ 84.92%
300
+ - **Commit:** `kfix` only
301
+
302
+ ---
303
+
304
+ ## Learnings (inherited)
305
+
306
+ ### From Three-Lens Audit (2026-03-19)
307
+ - `format.nil?` in gpt_context is always false — do not re-introduce
308
+ - `ssl_verify_peer: false` is not safe for AWS — HTTPS alone doesn't prevent MITM without peer verification
309
+ - ManifestGenerator `determine_range` is the canonical format; SyncFromSsd must match it
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
+
316
+ ### From fr2-gpt-context-help (2026-03-19)
317
+ - `opts.on_tail` vs `opts.on` matters for option ordering in OptionParser
318
+ - Subprocess specs (`ruby #{script} --flag`) are correct for CLI integration tests
319
+ - Pre-conditions from prior commits — always verify live before planning
320
+
321
+ ### From DAM Enhancement Sprint (Jan 2025)
322
+ - BrandResolver is the critical path — all dam commands flow through it
323
+ - `Regexp.last_match` is reset by `.sub()` calls — capture groups BEFORE any string transformation
324
+ - `Config.configure` is memoized — do not add new calls
325
+ - Table format() pattern: always use same format string for headers and data rows
326
+
327
+ ### From Jump Location Tool (Dec 2025)
328
+ - Dependency injection for path validators required for CI compatibility
329
+ - Jump Commands layer has zero dedicated specs (B018 — scheduled separately)
@@ -0,0 +1,26 @@
1
+ # IMPLEMENTATION_PLAN.md — bugfix-and-security
2
+
3
+ **Goal**: Fix 3 blockers: ssl_verify_peer security hole (B017), range string mismatch (B016), dead format guard (B021)
4
+ **Started**: 2026-03-19
5
+ **Target**: All 3 fixes committed; tests pass; rubocop clean; no regressions
6
+
7
+ ## Summary
8
+ - Total: 3 | Complete: 3 | In Progress: 0 | Pending: 0 | Failed: 0
9
+
10
+ ## Pending
11
+
12
+ ## In Progress
13
+
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
+
19
+ ## Failed / Needs Retry
20
+
21
+ ## Notes & Decisions
22
+ - All 3 fixes are independent — run as parallel agents in one wave
23
+ - B017: s3_scanner.rb has ssl_verify_peer inline with NO env guard — remove entirely. s3_operations.rb and share_operations.rb have env guard + unconditional fallback — keep env guard, remove unconditional fallback. Return {} (empty hash) instead.
24
+ - B016: ManifestGenerator format is canonical (letter+50-range, e.g. b50-b99). SyncFromSsd format is wrong (10-range, no letter, e.g. 60-69). Fix SyncFromSsd to match. ManifestGenerator#determine_range has zero specs — add them.
25
+ - B021: options.format defaults to 'content' in Options class — format.nil? is always false. Remove the third AND condition. Update cli_spec or add spec to verify no-args exits with message.
26
+ - Discovered: s3_scanner.rb also affected by B017 (not in original brief)
@@ -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"
@@ -5,14 +5,14 @@
5
5
  **Target**: `--help` shows structured sections; `--version` works; specs pass; rubocop clean
6
6
 
7
7
  ## Summary
8
- - Total: 1 | Complete: 0 | In Progress: 0 | Pending: 1 | Failed: 0
8
+ - Total: 1 | Complete: 1 | In Progress: 0 | Pending: 0 | Failed: 0
9
9
 
10
10
  ## Pending
11
11
 
12
12
  ## In Progress
13
- - [~] fr2-gpt-context-help — Implement AI-friendly help system in bin/gpt_context.rb per docs/specs/fr-002-gpt-context-help-system.md
14
13
 
15
14
  ## Complete
15
+ - [x] fr2-gpt-context-help — Implement AI-friendly help system in bin/gpt_context.rb. 754 examples, 0 failures. v0.76.0 published. Also fixed pre-existing rubocop offenses in bin/dam and split jump_test_helpers (JumpTestLocations → own file).
16
16
 
17
17
  ## Failed / Needs Retry
18
18
 
@@ -0,0 +1,70 @@
1
+ # Assessment: fr2-gpt-context-help
2
+
3
+ **Campaign**: fr2-gpt-context-help
4
+ **Date**: 2026-03-19 → 2026-03-19
5
+ **Results**: 1 complete, 0 failed
6
+ **Version shipped**: v0.76.0
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
+ | B002 — FR-2 GPT Context help system | ✅ Complete | 754 examples, 0 failures. Also fixed pre-existing rubocop offenses in bin/dam and split JumpTestLocations into own file. |
16
+
17
+ ---
18
+
19
+ ## What Worked Well
20
+
21
+ - **Single-file scope held.** `bin/gpt_context.rb` only — no lib/ changes. Agent stayed in bounds.
22
+ - **Side-fixes landed cleanly.** Rubocop offenses in bin/dam (select/reject → partition) and JumpTestLocations split were caught and fixed without scope creep.
23
+ - **Test count and coverage improved.** 748 → 754 examples, 84.88% → 84.92% coverage.
24
+ - **Spec approach was correct.** subprocess integration testing (running the actual script) is the right pattern for CLI help/version tests.
25
+ - **Pre-conditions were pre-cleared.** B015 and B019 fixed in prior commit 13d5f87 meant agent could focus on FR-2 immediately.
26
+
27
+ ---
28
+
29
+ ## What Didn't Work
30
+
31
+ - **cli_spec.rb is too thin (Grade: C from test audit).** 4 tests only verify help text strings are present. No functional tests: `-i`, `-e`, `-f`, `-o` flags completely untested at CLI level. Creates false sense of security.
32
+ - **format.nil? guard is dead code.** `bin/gpt_context.rb` line 115 checks `options.format.nil?` as part of its "no options provided" guard. But `format` defaults to `'content'` in the Options class — it is never nil. The third AND condition is always false, meaning the guard can only trigger if both include_patterns AND exclude_patterns are empty AND format has somehow been set to nil (impossible via normal usage).
33
+ - **file_collector_spec missing formats.** JSON and aider format output paths in FileCollector have zero specs. Error cases also unprotected.
34
+
35
+ ---
36
+
37
+ ## Key Learnings — Application
38
+
39
+ - **OptionParser `opts.on_tail` vs `opts.on` matters for ordering.** `--version` must be `opts.on` (not `on_tail`) to appear before `--help` in output. Anti-pattern documented in AGENTS.md.
40
+ - **format.nil? is a dead guard.** When Options class sets a default for `format`, the nil check in bin/gpt_context.rb line 115 is always false. Any "no args provided" guard must check `include_patterns.empty?` and `exclude_patterns.empty?` only.
41
+ - **Subprocess specs work but need functional assertions.** Using `\`ruby #{script} --flag\`` is correct for CLI integration testing, but tests must verify actual output content and exit codes — not just documentation strings.
42
+
43
+ ---
44
+
45
+ ## Key Learnings — Ralph Loop
46
+
47
+ - **One work unit campaigns are fast.** 1 agent, 1 wave, done. No coordination overhead.
48
+ - **Pre-conditions from prior commits reduce campaign scope.** B015/B019 were in next-round-brief as work units but had already been fixed. Always verify pre-conditions live before writing the plan.
49
+ - **Quality audit caught 3 new backlog items.** B021 (format.nil? dead guard), B022 (cli_spec functional tests), B023 (file_collector JSON/aider/error specs). These were invisible without the audit.
50
+
51
+ ---
52
+
53
+ ## Suggestions for Next Campaign
54
+
55
+ **Recommended next campaign: `bugfix-and-security` — B016, B017, B021**
56
+
57
+ Priority order:
58
+ 1. **B017** — ssl_verify_peer: false (security BLOCKER — remove before adding any S3 features)
59
+ 2. **B016** — ManifestGenerator vs SyncFromSsd range string mismatch (data integrity BLOCKER)
60
+ 3. **B021** — fix format.nil? dead guard in gpt_context (5-minute fix, prevents silent failure)
61
+
62
+ Then schedule soon after:
63
+ - **B022** — expand cli_spec.rb with functional tests (-i, -e, -f, -o, exit codes)
64
+ - **B023** — file_collector_spec: add JSON, aider, error path coverage
65
+ - **B018** — Jump Commands layer specs (Remove/Add/Update)
66
+
67
+ **AGENTS.md updates needed for next campaign:**
68
+ - Add: "format.nil? in gpt_context is always false — do not use as a no-args guard"
69
+ - Add: "S3Operations ssl_verify_peer must be removed — see B017"
70
+ - Add: "ManifestGenerator and SyncFromSsd produce incompatible range strings — see B016 before touching SSD archive paths"
@@ -1,55 +1,42 @@
1
1
  # Next Round Brief
2
2
 
3
3
  **Created:** 2026-03-19
4
- **Updated:** 2026-03-19 (after three-lens audit)
4
+ **Updated:** 2026-03-19 (after bugfix-and-security assessment)
5
5
 
6
6
  ---
7
7
 
8
- ## Recommended Next Campaign: gpt_context Fixes + FR-2 + Jump Verification
8
+ ## Recommended Next Campaign: test-coverage-gaps
9
9
 
10
10
  ### Goal
11
11
 
12
- Fix the two small blockers in `gpt_context/file_collector.rb` (B015, B019), then implement FR-2 (GPT Context AI help system). In parallel, verify whether BUG-1 (Jump get/remove) is still live before planning a Jump campaign.
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
- **Three-lens audit findings changed the priority order:**
16
+ Quality audit after bugfix-and-security found:
17
17
 
18
- 1. FR-2 has two small pre-conditions in `file_collector.rb` that must be fixed first:
19
- - `puts @working_directory` at line 15 (B019 1 line delete)
20
- - `FileUtils.cd` without `ensure` (B015 wrap in block form)
21
- Both are ~5 minutes of work. Do them as the first commit of the FR-2 campaign.
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).
22
24
 
23
- 2. BUG-1 (Jump get/remove) is mislabeled in the original backlog. Static analysis shows `Jump::Config#find` and `Commands::Remove` have correct dual-key guards. The bug may already be fixed or may be environmental. **Verify live before planning any Jump campaign.**
25
+ ### Suggested Work Units (parallel all test-only except B025)
24
26
 
25
- 3. New high-priority bugs found by the audit (B016, B017) should be assessed for inclusion in the next campaign or scheduled shortly after.
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.
26
32
 
27
- ### Suggested Work Units (FR-2 Campaign)
28
-
29
- 1. **Fix B019** — Delete `puts @working_directory` from `file_collector.rb:15` (1-line fix)
30
- 2. **Fix B015** — Wrap `FileUtils.cd` in block form in `file_collector.rb` (3-line fix)
31
- 3. **Implement FR-2** — Read `docs/specs/fr-002-gpt-context-help-system.md`. Option A from the spec (enhanced OptionParser with banner, separators, `--version`) is the correct approach. No changes to lib/ needed — this is a `bin/gpt_context.rb` enhancement.
32
- 4. **Verify BUG-1** — Run `bin/jump.rb get <key>` live. If broken: find actual failure site. If fixed: write regression spec for `Jump::Config#find` round-trip and close.
33
-
34
- ### Pre-Campaign Blockers: None
35
-
36
- Neither fix requires architectural changes. FR-2 is contained to `bin/gpt_context.rb`. The Jump verification is read-only unless the bug is confirmed.
37
-
38
- ### What Agents Need to Know
39
-
40
- - Read `docs/planning/AGENTS.md` — test/lint patterns, commit format, quality gates
41
- - FR-2 spec: `docs/specs/fr-002-gpt-context-help-system.md`
42
- - gpt_context source: `lib/appydave/tools/gpt_context/` + `bin/gpt_context.rb`
43
- - Jump source: `lib/appydave/tools/jump/` (Config, Search, Commands/*)
44
- - Jump tests: `spec/appydave/tools/jump/` + `spec/support/jump_test_helpers.rb`
45
- - BUG-1 call chain: `CLI#run_get` → `Search#get` → `Config#find` → `locations.find { |loc| loc.key == key }`
46
-
47
- ### Also Schedule Soon (from audit)
33
+ ### Mode Recommendation
48
34
 
49
- - B016 ManifestGenerator vs SyncFromSsd range string mismatch (data integrity — schedule next)
50
- - B017 — ssl_verify_peer: false in S3Operations (security — schedule next)
51
- - B018 — Jump Commands layer specs (no specs for Remove/Add/Update)
35
+ **Extend**same stack, same patterns, test-only work. Inherit AGENTS.md.
52
36
 
53
- ### Mode Recommendation
37
+ ### Pre-Campaign Notes
54
38
 
55
- **Extend** stack, patterns, and quality gates are known from prior campaigns. Use Extend, not Plan.
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
@@ -100,17 +100,9 @@ module Appydave
100
100
  end
101
101
 
102
102
  def configure_ssl_options
103
- # Check for explicit SSL verification bypass (for development/testing)
104
- if ENV['AWS_SDK_RUBY_SKIP_SSL_VERIFICATION'] == 'true'
105
- puts '⚠️ WARNING: SSL verification is disabled (development mode)'
106
- return { ssl_verify_peer: false }
107
- end
103
+ return { ssl_verify_peer: false } if ENV['AWS_SDK_RUBY_SKIP_SSL_VERIFICATION'] == 'true'
108
104
 
109
- # Disable SSL peer verification to work around OpenSSL 3.4.x CRL checking issues
110
- # This is safe for AWS S3 connections as we're still using HTTPS (encrypted connection)
111
- {
112
- ssl_verify_peer: false
113
- }
105
+ {}
114
106
  end
115
107
 
116
108
  public
@@ -111,8 +111,7 @@ module Appydave
111
111
  Aws::S3::Client.new(
112
112
  credentials: credentials,
113
113
  region: brand_info.aws.region,
114
- http_wire_trace: false,
115
- ssl_verify_peer: false
114
+ http_wire_trace: false
116
115
  )
117
116
  end
118
117
 
@@ -87,17 +87,9 @@ module Appydave
87
87
  end
88
88
 
89
89
  def configure_ssl_options
90
- # Check for explicit SSL verification bypass (for development/testing)
91
- if ENV['AWS_SDK_RUBY_SKIP_SSL_VERIFICATION'] == 'true'
92
- puts '⚠️ WARNING: SSL verification is disabled (development mode)'
93
- return { ssl_verify_peer: false }
94
- end
90
+ return { ssl_verify_peer: false } if ENV['AWS_SDK_RUBY_SKIP_SSL_VERIFICATION'] == 'true'
95
91
 
96
- # Disable SSL peer verification to work around OpenSSL 3.4.x CRL checking issues
97
- # This is safe for AWS S3 connections as we're still using HTTPS (encrypted connection)
98
- {
99
- ssl_verify_peer: false
100
- }
92
+ {}
101
93
  end
102
94
 
103
95
  public
@@ -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
 
@@ -181,14 +181,17 @@ module Appydave
181
181
  sync_light_files(ssd_path, local_dir, dry_run: dry_run)
182
182
  end
183
183
 
184
- # Determine range folder for project (e.g., b65 → 60-69)
184
+ # Determine range folder for project (e.g., b65 → b50-b99)
185
185
  def determine_range(project_id)
186
- # FliVideo pattern: b40, b41, ... b99
187
- if project_id =~ /^b(\d+)/
188
- tens = (Regexp.last_match(1).to_i / 10) * 10
189
- "#{tens}-#{tens + 9}"
186
+ # FliVideo/Modern pattern: b40, a82, etc.
187
+ if project_id =~ /^([a-z])(\d+)/
188
+ letter = Regexp.last_match(1)
189
+ number = Regexp.last_match(2).to_i
190
+ range_start = (number / 50) * 50
191
+ range_end = range_start + 49
192
+ format("#{letter}%02d-#{letter}%02d", range_start, range_end)
190
193
  else
191
- # Legacy pattern or unknown: use first 3 chars
194
+ # Legacy pattern or unknown
192
195
  '000-099'
193
196
  end
194
197
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Appydave
4
4
  module Tools
5
- VERSION = '0.76.0'
5
+ VERSION = '0.76.2'
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.0",
3
+ "version": "0.76.2",
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.0
4
+ version: 0.76.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Cruwys
@@ -297,9 +297,15 @@ files:
297
297
  - docs/guides/tools/youtube-manager.md
298
298
  - docs/planning/AGENTS.md
299
299
  - docs/planning/BACKLOG.md
300
+ - docs/planning/bugfix-and-security/AGENTS.md
301
+ - docs/planning/bugfix-and-security/IMPLEMENTATION_PLAN.md
302
+ - docs/planning/bugfix-and-security/assessment.md
300
303
  - docs/planning/fr2-gpt-context-help/AGENTS.md
301
304
  - docs/planning/fr2-gpt-context-help/IMPLEMENTATION_PLAN.md
305
+ - docs/planning/fr2-gpt-context-help/assessment.md
302
306
  - docs/planning/next-round-brief.md
307
+ - docs/planning/test-coverage-gaps/AGENTS.md
308
+ - docs/planning/test-coverage-gaps/IMPLEMENTATION_PLAN.md
303
309
  - docs/specs/fr-002-gpt-context-help-system.md
304
310
  - docs/specs/fr-003-jump-location-tool.md
305
311
  - docs/specs/zsh-history-tool.md