appydave-tools 0.76.13 → 0.76.14

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: 6081f58b2bf40e25bf4509d591368c361dc00957e924683d3f2179fe3fbc8d10
4
- data.tar.gz: 44c1d63cc7558d323a735009876cb6bebd87c9117cf18a7b5eb25047eeff0d83
3
+ metadata.gz: 2c6c57202f05dffc3f46f1d929522ed55200a85d91ac427d831a2c9e77558693
4
+ data.tar.gz: ec9250e733a5ec31bef797f83c24c77f535e3ac9753954cab7f6e45c66bf7fca
5
5
  SHA512:
6
- metadata.gz: 4d10e99fe30a5c09e803560904896abc0355d696a5b70d859acdeb0b2ca1e572502c85a99bf32a1f9f968e3544017d085b482284013e5b337155781d1b7cb13a
7
- data.tar.gz: d08c8d573a35bf36bdebd3c9106359eb37d2bf5efb69a167a7db005e8340f05443c8b2e7bfc195a2639e4337d1beef79dca45b263ce65f16b73081cce1b900bb
6
+ metadata.gz: 933c28cc9392d407254f320529e3c6166648579794fa1b13fb2fc956bebc4900ccf4048eb97f3230a40ebee2d1ebce45b5588effebb5a05fe3c5e516270655d0
7
+ data.tar.gz: 381302acfc146d12aed9ebb5ee1e9e26de4b0d7c111e2b0c7883e4936bb4b5673e33617ec1c49fc515e89ad2d088acb7a583cc6ed862e946d0265160b917d30a
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [0.76.13](https://github.com/appydave/appydave-tools/compare/v0.76.12...v0.76.13) (2026-03-19)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * remove redundant smoke-test from s3_scan_command_spec; fix RSpec/RepeatedExample CI rubocop offense ([5711e4e](https://github.com/appydave/appydave-tools/commit/5711e4e62d6ab648fc2a93fae32453095a00d4ca))
7
+
1
8
  ## [0.76.12](https://github.com/appydave/appydave-tools/compare/v0.76.11...v0.76.12) (2026-03-19)
2
9
 
3
10
 
@@ -1,7 +1,7 @@
1
1
  # Project Backlog — AppyDave Tools
2
2
 
3
- **Last updated**: 2026-03-19 (library-boundary-cleanup campaign complete)
4
- **Total**: 40 | Pending: 9 | Done: 30 | Deferred: 0 | Rejected: 0
3
+ **Last updated**: 2026-03-20 (env-dead-code-cleanup campaign complete)
4
+ **Total**: 40 | Pending: 7 | Done: 32 | Deferred: 0 | Rejected: 0
5
5
 
6
6
  ---
7
7
 
@@ -15,8 +15,6 @@
15
15
  - [ ] B009 — UX: progress indicators for dam operations > 5s | Priority: low
16
16
  - [ ] B010 — UX: auto-adjust dam table column widths to terminal width | Priority: low
17
17
  - [ ] B020 — Arch: split S3Operations (1,030 lines, mixed I/O + logic) | Priority: low (now unblocked)
18
- - [ ] B038 — Cleanup: remove ENV['BRAND_PATH'] dead code from bin/dam (10 assignments, never read in lib/) | Priority: low
19
- - [ ] B039 — Tests: strengthen s3_scan_command_spec data-value assertions + remove LocalSyncStatus stub | Priority: low
20
18
 
21
19
  ---
22
20
 
@@ -52,6 +50,8 @@
52
50
  - [x] B035 — Fix: remove ENV['BRAND_PATH'] side effect from S3ArgParser; return brand_path: in result hash | Completed: library-boundary-cleanup (2026-03-19), v0.76.9
53
51
  - [x] B036 — Tests: rebuild S3ScanCommand spec from D to B (10 behaviour examples) | Completed: library-boundary-cleanup (2026-03-19), v0.76.10
54
52
  - [x] B037 — Tests: LocalSyncStatus :partial, local_file_count, Zone.Identifier exclusion, unknown format | Completed: library-boundary-cleanup (2026-03-19), v0.76.11
53
+ - [x] B038 — Cleanup: remove ENV['BRAND_PATH'] dead code from bin/dam (10 assignments) | Completed: env-dead-code-cleanup (2026-03-20), v0.76.13
54
+ - [x] B039 — Tests: strengthen s3_scan_command_spec field assertions + remove LocalSyncStatus stub | Completed: env-dead-code-cleanup (2026-03-20), v0.76.12
55
55
 
56
56
  ---
57
57
 
@@ -0,0 +1,408 @@
1
+ # AGENTS.md — AppyDave Tools / batch-a-features campaign
2
+
3
+ > Self-contained operational knowledge. You receive only this file + your work unit prompt.
4
+ > Inherited from: env-dead-code-cleanup (2026-03-20)
5
+
6
+ ---
7
+
8
+ ## Project Overview
9
+
10
+ **Stack:** Ruby 3.4.2, Bundler 2.6.2, RSpec, RuboCop, semantic-release CI/CD.
11
+ **Baseline:** 860 examples, 0 failures, 86.44% line coverage, v0.76.13
12
+ **Commits:** `kfeat "message"` for new features (minor bump), `kfix "message"` for fixes (patch bump). Never `git commit` directly.
13
+
14
+ ---
15
+
16
+ ## ⚠️ kfix/kfeat Staging Behaviour
17
+
18
+ `kfix` and `kfeat` run `git add .` internally — they stage EVERYTHING in the working tree.
19
+
20
+ **Before calling kfix/kfeat:**
21
+ ```bash
22
+ git status # confirm ONLY your intended files are modified
23
+ git diff # review the actual changes
24
+ ```
25
+
26
+ If unintended files appear:
27
+ ```bash
28
+ git stash -- path/to/unintended/file # stash specific file
29
+ git checkout -- path/to/file # discard unintended change
30
+ ```
31
+
32
+ Then call kfix/kfeat once the tree is clean.
33
+
34
+ ---
35
+
36
+ ## Build & Run Commands
37
+
38
+ ```bash
39
+ eval "$(rbenv init -)"
40
+
41
+ RUBYOPT="-W0" bundle exec rspec # Full suite (860 examples baseline)
42
+ bundle exec rspec spec/path/to/file_spec.rb # Single file
43
+ bundle exec rubocop --format clang # Lint (must be 0 offenses)
44
+
45
+ kfeat "add feature description" # new feature — minor version bump
46
+ kfix "fix description" # fix/improvement — patch version bump
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Directory Structure
52
+
53
+ ```
54
+ bin/
55
+ gpt_context.rb GPT context CLI — target of B001
56
+ dam DAM CLI (VatCLI) — target of B009
57
+ lib/appydave/tools/
58
+ gpt_context/
59
+ options.rb Options struct — add show_tokens field (B001)
60
+ file_collector.rb Builds content string from glob patterns
61
+ output_handler.rb Writes content to clipboard/file
62
+ dam/
63
+ project_listing.rb Table display for dam list — target of B010
64
+ brand_resolver.rb Brand name transforms (appydave ↔ v-appydave)
65
+ config.rb Brand path resolution
66
+ project_resolver.rb Project name resolution
67
+ spec/
68
+ appydave/tools/dam/
69
+ brand_resolution_integration_spec.rb NEW — target of B012
70
+ support/
71
+ dam_filesystem_helpers.rb Shared context for DAM specs
72
+ ```
73
+
74
+ ---
75
+
76
+ ## B001 — gpt-context-token-counting
77
+
78
+ **Files:** `lib/appydave/tools/gpt_context/options.rb`, `bin/gpt_context.rb`
79
+
80
+ ### What to build
81
+
82
+ Add a `--tokens` / `-t` flag. When set, after collecting content, print an estimated token count to `$stderr` (so it doesn't pollute the content going to clipboard/file).
83
+
84
+ **Token estimation:** `(content.length / 4.0).ceil` — industry-standard approximation for English/code (4 chars ≈ 1 token).
85
+
86
+ ### Step 1 — options.rb
87
+
88
+ Add `show_tokens` to the Struct:
89
+ ```ruby
90
+ Options = Struct.new(
91
+ :include_patterns,
92
+ :exclude_patterns,
93
+ :format,
94
+ :line_limit,
95
+ :debug,
96
+ :output_target,
97
+ :working_directory,
98
+ :prompt,
99
+ :show_tokens, # ADD THIS
100
+ keyword_init: true
101
+ ) do
102
+ def initialize(**args)
103
+ super
104
+ # existing defaults...
105
+ self.show_tokens ||= false # ADD THIS
106
+ end
107
+ end
108
+ ```
109
+
110
+ ### Step 2 — bin/gpt_context.rb
111
+
112
+ Add the CLI flag (after the `-p` option block, before the separator):
113
+ ```ruby
114
+ opts.on('-t', '--tokens', 'Show estimated token count after collecting context') do
115
+ options.show_tokens = true
116
+ end
117
+ ```
118
+
119
+ After `content = gatherer.build` and before `output_handler = ...`, add:
120
+ ```ruby
121
+ if options.show_tokens
122
+ token_estimate = (content.length / 4.0).ceil
123
+ char_count = content.length
124
+ $stderr.puts ''
125
+ $stderr.puts '── Token Estimate ──────────────────────────'
126
+ $stderr.puts " Characters : #{char_count.to_s.rjust(10)}"
127
+ $stderr.puts " Tokens (~4c): #{token_estimate.to_s.rjust(10)}"
128
+ $stderr.puts ''
129
+ if token_estimate > 200_000
130
+ $stderr.puts ' ⚠️ WARNING: Exceeds 200k tokens — may not fit most LLM context windows'
131
+ elsif token_estimate > 100_000
132
+ $stderr.puts ' ⚠️ NOTICE: Exceeds 100k tokens — check your LLM context limit'
133
+ end
134
+ $stderr.puts '────────────────────────────────────────────'
135
+ $stderr.puts ''
136
+ end
137
+ ```
138
+
139
+ ### Done when B001 is complete
140
+ - `bundle exec rspec spec/appydave/tools/gpt_context/` → all pass
141
+ - Check if options_spec.rb needs a new example for `show_tokens` default — add one if it tests other defaults
142
+ - `bundle exec rubocop --format clang` → 0 offenses
143
+ - `RUBYOPT="-W0" bundle exec rspec` → 860+ examples, 0 failures
144
+ - Commit: `kfeat "add --tokens flag to gpt_context for estimated token count output"`
145
+
146
+ ---
147
+
148
+ ## B009 — dam-progress-indicators
149
+
150
+ **File:** `bin/dam` only.
151
+
152
+ ### What to build
153
+
154
+ Add human-readable progress messages before and after long-running S3 operations. These operations can take 30s–5min with no feedback currently.
155
+
156
+ ### Commands to update
157
+
158
+ **s3_up_command** — wrap the upload:
159
+ ```ruby
160
+ def s3_up_command(args)
161
+ options = Appydave::Tools::Dam::S3ArgParser.parse_s3(args, 's3-up')
162
+ s3_ops = Appydave::Tools::Dam::S3Operations.new(options[:brand], options[:project])
163
+ verb = options[:dry_run] ? '🔍 Dry run:' : '⬆️ Uploading'
164
+ puts "#{verb} #{options[:project]} (#{options[:brand]}) → S3..."
165
+ s3_ops.upload(dry_run: options[:dry_run])
166
+ rescue StandardError => e
167
+ # existing rescue
168
+ end
169
+ ```
170
+
171
+ **s3_down_command:**
172
+ ```ruby
173
+ puts "⬇️ Downloading #{options[:project]} (#{options[:brand]}) from S3..."
174
+ ```
175
+
176
+ **s3_status_command:**
177
+ ```ruby
178
+ puts "🔍 Checking S3 status for #{options[:project]} (#{options[:brand]})..."
179
+ ```
180
+
181
+ **archive_command:**
182
+ ```ruby
183
+ puts "📦 Archiving #{options[:project]} (#{options[:brand]}) to SSD..."
184
+ ```
185
+
186
+ **sync_ssd_command** (brand-level, not project):
187
+ ```ruby
188
+ puts "🔄 Syncing #{brand_arg} from SSD..."
189
+ ```
190
+
191
+ Read `bin/dam` carefully to find the exact method structure and rescue blocks before making changes. Add the `puts` line immediately after the `options =` assignment and before the `s3_ops =` instantiation.
192
+
193
+ ### Done when B009 is complete
194
+ - `RUBYOPT="-W0" bundle exec rspec` → 860 examples, 0 failures (no new specs — bin/dam is untested by spec)
195
+ - `bundle exec rubocop --format clang` → 0 offenses
196
+ - Commit: `kfix "add progress indicators to dam S3 commands"`
197
+
198
+ ---
199
+
200
+ ## B010 — dam-column-widths
201
+
202
+ **File:** `lib/appydave/tools/dam/project_listing.rb` only.
203
+
204
+ ### What to build
205
+
206
+ Replace hardcoded separator line lengths with terminal-width detection. Also add path truncation for the PATH column.
207
+
208
+ ### Step 1 — add terminal_width helper
209
+
210
+ Add a private class method:
211
+ ```ruby
212
+ def self.terminal_width
213
+ IO.console&.winsize&.last || 120
214
+ rescue StandardError
215
+ 120
216
+ end
217
+ ```
218
+
219
+ You'll need `require 'io/console'` — add it at the top of the file (after `# frozen_string_literal: true`):
220
+ ```ruby
221
+ require 'io/console'
222
+ ```
223
+
224
+ ### Step 2 — replace hardcoded separator lengths
225
+
226
+ Currently the file has multiple `puts '-' * N` lines with hardcoded widths (133, 122, 189, 200, etc.).
227
+
228
+ Replace each with `puts '-' * [terminal_width, N].min` where N is the original value. This prevents the separator from overflowing narrow terminals while still using the full width on wide ones.
229
+
230
+ grep for `'-'` in project_listing.rb to find all occurrences.
231
+
232
+ ### Step 3 — add path truncation helper
233
+
234
+ ```ruby
235
+ def self.truncate_path(path, max_width = 35)
236
+ return path if path.nil? || path.length <= max_width
237
+ "...#{path[-(max_width - 3)..]}"
238
+ end
239
+ ```
240
+
241
+ Replace `shorten_path(data[:path])` calls with `truncate_path(shorten_path(data[:path]), 35)` where the format string uses `%-35s`.
242
+
243
+ ### Done when B010 is complete
244
+ - `RUBYOPT="-W0" bundle exec rspec spec/appydave/tools/dam/project_listing_spec.rb` → all pass
245
+ - `RUBYOPT="-W0" bundle exec rspec` → 860+ examples, 0 failures
246
+ - `bundle exec rubocop --format clang` → 0 offenses
247
+ - Commit: `kfix "terminal-width-aware separator lines and path truncation in project_listing"`
248
+
249
+ ---
250
+
251
+ ## B012 — brand-resolution-integration-tests
252
+
253
+ **File:** `spec/appydave/tools/dam/brand_resolution_integration_spec.rb` (new file)
254
+
255
+ ### What to build
256
+
257
+ Integration tests that exercise the BrandResolver → Config → ProjectResolver chain end-to-end using the shared filesystem context.
258
+
259
+ ```ruby
260
+ # frozen_string_literal: true
261
+
262
+ require 'spec_helper'
263
+
264
+ RSpec.describe 'Brand resolution integration' do
265
+ include_context 'with vat filesystem and brands', brands: %w[appydave voz]
266
+
267
+ before do
268
+ FileUtils.mkdir_p(File.join(appydave_path, 'b65-test-project'))
269
+ FileUtils.mkdir_p(File.join(appydave_path, 'b66-another-project'))
270
+ FileUtils.mkdir_p(File.join(voz_path, 'boy-baker'))
271
+ end
272
+
273
+ describe 'BrandResolver → Config.brand_path' do
274
+ it 'resolves appydave shortcut to correct path' do
275
+ path = Appydave::Tools::Dam::Config.brand_path('appydave')
276
+ expect(path).to eq(appydave_path)
277
+ end
278
+
279
+ it 'resolves voz shortcut to correct path' do
280
+ path = Appydave::Tools::Dam::Config.brand_path('voz')
281
+ expect(path).to eq(voz_path)
282
+ end
283
+
284
+ it 'raises BrandNotFoundError for unknown brand' do
285
+ expect { Appydave::Tools::Dam::Config.brand_path('nonexistent') }
286
+ .to raise_error(Appydave::Tools::Dam::BrandNotFoundError)
287
+ end
288
+ end
289
+
290
+ describe 'BrandResolver.expand' do
291
+ it 'expands appydave to v-appydave' do
292
+ expect(Appydave::Tools::Dam::BrandResolver.expand('appydave')).to eq('v-appydave')
293
+ end
294
+
295
+ it 'expands voz to v-voz' do
296
+ expect(Appydave::Tools::Dam::BrandResolver.expand('voz')).to eq('v-voz')
297
+ end
298
+ end
299
+
300
+ describe 'ProjectResolver.resolve' do
301
+ it 'resolves short name b65 to full project name' do
302
+ result = Appydave::Tools::Dam::ProjectResolver.resolve('appydave', 'b65')
303
+ expect(result).to eq('b65-test-project')
304
+ end
305
+
306
+ it 'resolves exact project name' do
307
+ result = Appydave::Tools::Dam::ProjectResolver.resolve('voz', 'boy-baker')
308
+ expect(result).to eq('boy-baker')
309
+ end
310
+
311
+ it 'raises ProjectNotFoundError for missing project' do
312
+ expect { Appydave::Tools::Dam::ProjectResolver.resolve('appydave', 'b99') }
313
+ .to raise_error(Appydave::Tools::Dam::ProjectNotFoundError)
314
+ end
315
+ end
316
+
317
+ describe 'ProjectResolver.detect_from_pwd' do
318
+ it 'detects brand and project from a project directory' do
319
+ Dir.chdir(File.join(appydave_path, 'b65-test-project')) do
320
+ brand, project = Appydave::Tools::Dam::ProjectResolver.detect_from_pwd
321
+ expect(brand).to eq('appydave')
322
+ expect(project).to eq('b65-test-project')
323
+ end
324
+ end
325
+
326
+ it 'returns nil when run outside any known brand directory' do
327
+ Dir.chdir('/tmp') do
328
+ brand, project = Appydave::Tools::Dam::ProjectResolver.detect_from_pwd
329
+ expect(brand).to be_nil
330
+ expect(project).to be_nil
331
+ end
332
+ end
333
+ end
334
+ end
335
+ ```
336
+
337
+ **Read `project_resolver.rb` and `brand_resolver.rb` before writing** to confirm method signatures.
338
+
339
+ **Note:** The shared context mocks `SettingsConfig#video_projects_root` to return `projects_root`, so `Config.brand_path` calls will resolve to the temp filesystem. `instance_double` is NOT needed here — use the real classes against the mocked filesystem.
340
+
341
+ ### Done when B012 is complete
342
+ - `bundle exec rspec spec/appydave/tools/dam/brand_resolution_integration_spec.rb` → all pass
343
+ - `RUBYOPT="-W0" bundle exec rspec` → 866+ examples, 0 failures
344
+ - `bundle exec rubocop --format clang` → 0 offenses
345
+ - Commit: `kfix "add brand resolution integration spec covering BrandResolver, Config, ProjectResolver chain"`
346
+
347
+ ---
348
+
349
+ ## Success Criteria (Every Work Unit)
350
+
351
+ - [ ] `RUBYOPT="-W0" bundle exec rspec` → 860+ examples, 0 failures
352
+ - [ ] `bundle exec rubocop --format clang` → 0 offenses
353
+ - [ ] Coverage ≥ 86.44%
354
+ - [ ] Working tree clean before calling kfix/kfeat (git status check)
355
+
356
+ ---
357
+
358
+ ## Reference Patterns
359
+
360
+ ### Shared Context for DAM Specs
361
+
362
+ ```ruby
363
+ include_context 'with vat filesystem and brands', brands: %w[appydave voz]
364
+ # Provides: temp_folder, projects_root, appydave_path, voz_path
365
+ # SettingsConfig#video_projects_root mocked → projects_root
366
+ ```
367
+
368
+ ### instance_double — Always Full Constant
369
+
370
+ ```ruby
371
+ instance_double(Appydave::Tools::Configuration::Models::BrandsConfig) # ✅
372
+ instance_double('BrandsConfig') # ❌ fails CI
373
+ ```
374
+
375
+ ### Typed Exceptions
376
+
377
+ ```ruby
378
+ raise Appydave::Tools::Dam::BrandNotFoundError.new(brand, available, suggestions)
379
+ raise Appydave::Tools::Dam::ProjectNotFoundError, 'message'
380
+ raise Appydave::Tools::Dam::UsageError, 'message'
381
+ ```
382
+
383
+ ---
384
+
385
+ ## Anti-Patterns to Avoid
386
+
387
+ - ❌ `exit 1` in library code — use typed exceptions
388
+ - ❌ `instance_double('StringForm')` — fails CI on Ubuntu
389
+ - ❌ Multiple `before` blocks in same context — merge them (RSpec/ScatteredSetup)
390
+ - ❌ `$?` for subprocess status — use `$CHILD_STATUS`
391
+ - ❌ `format_bytes` — use `FileHelper.format_size`
392
+ - ❌ Inline brand transforms — use BrandResolver
393
+
394
+ ---
395
+
396
+ ## Learnings
397
+
398
+ ### From env-dead-code-cleanup (2026-03-20)
399
+
400
+ - **kfix runs `git add .` internally.** Clean the working tree before calling kfix. Check `git status` first.
401
+ - **`instance_double` string form fails CI on Ubuntu.** Always use full constant.
402
+ - **`not_to raise_error` is a weak assertion.** Prefer field-value or method-spy assertions.
403
+
404
+ ### From library-boundary-cleanup (2026-03-20)
405
+
406
+ - **`exit 1` in library code → use typed exceptions.** VatCLI rescue blocks catch StandardError.
407
+ - **Config.brands needs separate mock** from shared filesystem context.
408
+ - **S3ScanCommand#scan_all rescues per-brand** — per-brand failures are isolated.
@@ -0,0 +1,37 @@
1
+ # IMPLEMENTATION_PLAN.md — batch-a-features
2
+
3
+ **Goal**: Four independent improvements batched into one parallel wave: token counting for gpt_context, progress indicators for dam S3 commands, terminal-width-aware column separators, and brand resolution integration tests.
4
+ **Started**: 2026-03-20
5
+ **Target**: All 4 complete, 860+ examples passing, rubocop 0 offenses
6
+
7
+ ## Summary
8
+ - Total: 4 | Complete: 0 | In Progress: 4 | Pending: 0 | Failed: 0
9
+
10
+ ## Pending
11
+
12
+ ## In Progress
13
+ - [~] B001 — gpt-context-token-counting — Add --tokens flag to gpt_context; print estimated token count + threshold warnings
14
+ - [~] B009 — dam-progress-indicators — Add before/after progress messages to s3_up, s3_down, s3_status, archive, sync_ssd in bin/dam
15
+ - [~] B010 — dam-column-widths — Terminal-width-aware separator lines + path truncation in project_listing.rb
16
+ - [~] B012 — brand-resolution-integration-tests — Integration spec covering brand→project resolution chain end-to-end
17
+
18
+ ## Complete
19
+
20
+ ## Failed / Needs Retry
21
+
22
+ ## Notes & Decisions
23
+
24
+ ### Wave Plan
25
+ All 4 work units touch different files — run in parallel, one wave.
26
+
27
+ | WU | Files |
28
+ |----|-------|
29
+ | B001 | `lib/gpt_context/options.rb`, `bin/gpt_context.rb` |
30
+ | B009 | `bin/dam` |
31
+ | B010 | `lib/dam/project_listing.rb` |
32
+ | B012 | `spec/appydave/tools/dam/brand_resolution_integration_spec.rb` (new) |
33
+
34
+ ### kfix hygiene
35
+ kfix runs `git add .` internally. Ensure working tree contains ONLY intended files before calling kfix.
36
+ The other agents' in-progress changes will NOT appear in your working tree (each agent works independently).
37
+ Safe to call kfix once your specific files are the only changes present.
@@ -17,22 +17,32 @@
17
17
 
18
18
  ## ⚠️ Pre-Commit Check (Mandatory Every Commit)
19
19
 
20
+ `kfix` and `kfeat` run `git add .` internally — they stage EVERYTHING in the working tree unconditionally. You cannot selectively stage files.
21
+
22
+ **The only safe approach: ensure the working tree contains ONLY your intended changes before calling kfix.**
23
+
20
24
  ```bash
21
- git status
25
+ git status # What files are modified/untracked?
26
+ git diff # What are the actual changes?
22
27
  ```
23
28
 
24
- Confirm ONLY the files you intentionally changed are staged. **Stage by file name — never `git add .` or `git add -A`.**
29
+ If unintended files appear (e.g. planning docs, other specs):
30
+ - `git stash -- path/to/file` to temporarily stash a specific file
31
+ - OR `git checkout -- path/to/file` to discard an unintended change
32
+ - OR `git clean -n` to preview, then `git clean -f path/to/file` for untracked files
25
33
 
34
+ Once the working tree contains ONLY the files you intended to change, call:
26
35
  ```bash
27
- # Correct:
28
- git add bin/dam
29
- kfix "..."
30
-
31
- # Wrong — stages everything including planning files:
32
- git add .
36
+ kfix "your message here"
33
37
  ```
34
38
 
35
- **Why:** Three prior campaigns have had accidental staging from `git add .`. It is the single most common failure mode in this repo.
39
+ kfix/kfeat then:
40
+ 1. `git add .` — stages everything (working tree must be clean of unintended files)
41
+ 2. `git commit -m "fix: ..."` — commits
42
+ 3. `git pull` + `git push` — syncs and pushes
43
+ 4. Waits for CI (`gh run watch`) — blocks until green or red
44
+ 5. On success: `git pull` again to pick up semantic-release version bump + CHANGELOG
45
+ 6. Prints the new version tag
36
46
 
37
47
  ---
38
48
 
@@ -104,7 +114,7 @@ After removing, also check if `options[:brand_path]` was the ONLY use of `option
104
114
  - `grep -n "BRAND_PATH" bin/dam` → 0 results
105
115
  - `RUBYOPT="-W0" bundle exec rspec` → 861 examples, 0 failures
106
116
  - `bundle exec rubocop --format clang` → 0 offenses
107
- - `git status` clean (only bin/dam staged)
117
+ - `git status` clean (working tree contains ONLY bin/dam changes before calling kfix)
108
118
 
109
119
  **Commit:** `kfix "remove ENV BRAND_PATH dead code from bin/dam (10 assignments, never read in lib)"`
110
120
 
@@ -153,7 +163,7 @@ FileUtils.mkdir_p(File.join(appydave_path, 'b65-test-project', 's3-staging'))
153
163
  - `bundle exec rspec spec/appydave/tools/dam/s3_scan_command_spec.rb` → all pass
154
164
  - `RUBYOPT="-W0" bundle exec rspec` → 861 examples, 0 failures (count unchanged — no new specs)
155
165
  - `bundle exec rubocop --format clang` → 0 offenses
156
- - `git status` clean (only spec file staged)
166
+ - `git status` clean (working tree contains ONLY the spec file changes before calling kfix)
157
167
 
158
168
  **Commit:** `kfix "strengthen s3_scan_command_spec field assertions; remove LocalSyncStatus stub"`
159
169
 
@@ -164,7 +174,7 @@ FileUtils.mkdir_p(File.join(appydave_path, 'b65-test-project', 's3-staging'))
164
174
  - [ ] `RUBYOPT="-W0" bundle exec rspec` — 861+ examples, 0 failures
165
175
  - [ ] `bundle exec rubocop --format clang` — 0 offenses
166
176
  - [ ] Line coverage ≥ 86.43%
167
- - [ ] `git status` clean, specific files staged (NOT `git add .`)
177
+ - [ ] `git status` clean (working tree contains ONLY intended changes before calling kfix)
168
178
 
169
179
  ---
170
180
 
@@ -201,7 +211,7 @@ raise Appydave::Tools::Dam::UsageError, 'Usage: dam s3-up <brand> <project>'
201
211
 
202
212
  ## Anti-Patterns to Avoid
203
213
 
204
- - ❌ `git add .` or `git add -A`always stage specific files by name
214
+ - ❌ Calling `kfix`/`kfeat` with unintended files in the working tree clean the tree first, then call kfix
205
215
  - ❌ `exit 1` in library code — use typed exceptions (already fixed in library-boundary-cleanup)
206
216
  - ❌ `ENV['BRAND_PATH'] =` in any file — confirmed dead, being removed in B038
207
217
  - ❌ `instance_double('StringForm')` — use full constant always
@@ -216,7 +226,7 @@ raise Appydave::Tools::Dam::UsageError, 'Usage: dam s3-up <brand> <project>'
216
226
  ### From library-boundary-cleanup (2026-03-20)
217
227
 
218
228
  - **`instance_double` string form fails CI on Ubuntu.** Always use full constant: `instance_double(Fully::Qualified::ClassName)`.
219
- - **`git add .` is the recurring staging bug.** Stage specific files by name. Always. Every commit.
229
+ - **Dirty working tree + kfix = accidental staging.** `kfix` runs `git add .` internally it stages everything. Ensure the working tree contains ONLY intended changes before calling kfix.
220
230
  - **`ENV['BRAND_PATH']` in bin/dam is dead code.** 10 assignments, 0 reads. Being removed in B038.
221
231
  - **VatCLI rescue blocks catch DamError correctly.** `UsageError < DamError < StandardError` — all 17 rescue blocks catch it without modification.
222
232
 
@@ -5,13 +5,13 @@
5
5
  **Target**: Both items complete; 861+ examples passing; rubocop 0 offenses; no regressions
6
6
 
7
7
  ## Summary
8
- - Total: 2 | Complete: 0 | In Progress: 2 | Pending: 0 | Failed: 0
8
+ - Total: 2 | Complete: 2 | In Progress: 0 | Pending: 0 | Failed: 0
9
9
 
10
10
  ## Pending
11
11
 
12
12
  ## In Progress
13
- - [~] B038 — remove-env-dead-code — Remove all 10 `ENV['BRAND_PATH'] =` assignments from bin/dam
14
- - [~] B039 — strengthen-s3-scan-spec — Replace `not_to be_empty` with field-value assertions; remove LocalSyncStatus stub
13
+ - [x] B038 — remove-env-dead-code — All 10 ENV['BRAND_PATH'] assignments removed from bin/dam; 0 cascade removals (options still used for other keys). CI fix needed: git add . staged s3_scan_command_spec.rb with pre-existing RSpec/RepeatedExample offense — fixed via second commit. 860 examples, 0 failures. Commits: 5c11027, 5711e4e.
14
+ - [x] B039 — strengthen-s3-scan-spec — Field-value assertions on both not_to be_empty checks; LocalSyncStatus stub removed (runs for real against fixture filesystem, returns :no_files). 861 examples, 0 failures. v0.76.12. Commit: 7f1fc5a.
15
15
 
16
16
  ## Complete
17
17
 
@@ -0,0 +1,88 @@
1
+ # Assessment: env-dead-code-cleanup
2
+
3
+ **Campaign**: env-dead-code-cleanup
4
+ **Date**: 2026-03-20 → 2026-03-20
5
+ **Results**: 2 complete, 0 failed
6
+ **Final version**: v0.76.13 (from v0.76.11 baseline)
7
+ **Quality audits**: code-quality-audit ✅ | test-quality-audit ✅
8
+
9
+ ---
10
+
11
+ ## Results Summary
12
+
13
+ | Work Unit | Description | Result | Version |
14
+ |-----------|-------------|--------|---------|
15
+ | B038 | remove-env-dead-code | ✅ Complete | v0.76.12/13 |
16
+ | B039 | strengthen-s3-scan-spec | ✅ Complete | v0.76.12 |
17
+
18
+ **Test baseline:** 861 → 860 examples (net -1: one redundant smoke test removed), 0 failures, 86.44% line coverage
19
+
20
+ ---
21
+
22
+ ## What Worked Well
23
+
24
+ 1. **Both work units ran in parallel with zero conflicts.** `bin/dam` and `spec/...` are non-overlapping. Wave 1 parallel pattern continues to be reliable for this type of work.
25
+
26
+ 2. **ENV removal was surgically clean.** All 10 assignments were isolated lines with no downstream reads — each method is 1 line shorter and more readable. Code audit confirmed 0 orphaned variables.
27
+
28
+ 3. **Test grade lifted from B to B+.** Field-value assertions now catch wrong values, not just non-empty. LocalSyncStatus runs for real (via fixture filesystem) and is verified with a method spy. Stronger than before without over-specifying.
29
+
30
+ 4. **Code audit: zero concerns.** 0 rubocop offenses, 0 remaining dead code, library boundaries respected everywhere.
31
+
32
+ ---
33
+
34
+ ## What Didn't Work
35
+
36
+ 1. **B038 agent used `git add .` again** — the fourth campaign in a row with this issue. It staged `s3_scan_command_spec.rb` which had a pre-existing `RSpec/RepeatedExample` offense on CI. Required a second fix commit. The AGENTS.md warning is prominent but agents keep ignoring it.
37
+
38
+ **Root cause:** The `kfix` alias may itself be running `git add .` internally, or agents are running `git add .` before `kfix`. Need to investigate whether `kfix` pre-stages, and if so, update AGENTS.md to reflect that.
39
+
40
+ ---
41
+
42
+ ## Key Learnings — Application
43
+
44
+ 1. **`ENV['BRAND_PATH']` is fully gone.** `grep -rn "BRAND_PATH" bin/ lib/ spec/` → 0 results. Any future S3 operations get `brand_path` through proper parameter passing.
45
+
46
+ 2. **`not_to raise_error` is a weak assertion.** It only verifies no exception — it says nothing about correctness. Replace with field-value or method-spy assertions whenever possible.
47
+
48
+ 3. **LocalSyncStatus integration test pattern:** Use `allow(Config).to receive(:project_path).and_return(appydave_path)` + `FileUtils.mkdir_p(staging_dir)` to let LocalSyncStatus run for real without deep mocking.
49
+
50
+ ---
51
+
52
+ ## Key Learnings — Ralph Loop
53
+
54
+ 1. **`git add .` persists across agents despite AGENTS.md warnings.** The warning is not working. Options for next campaign:
55
+ - Investigate whether `kfix` itself stages all files (check its shell implementation)
56
+ - Add explicit `git add <specific-file>` to commit instructions in AGENTS.md rather than relying on agents to remember not to use `git add .`
57
+
58
+ 2. **Small 2-WU parallel campaigns are very efficient.** Planning took ~5 minutes (brief already written), build ran in one wave, audits confirmed clean. This is the right pattern for closing audit findings.
59
+
60
+ ---
61
+
62
+ ## Code Quality Audit Findings
63
+
64
+ **Grade: APPROVED / Production-ready**
65
+ - 0 BRAND_PATH references anywhere in codebase
66
+ - All removal sites clean — no orphaned variables
67
+ - 0 rubocop offenses in bin/dam
68
+ - 860/860 tests pass, 86.44% coverage
69
+
70
+ ---
71
+
72
+ ## Test Quality Audit Findings
73
+
74
+ **Grade: B+ (improvement from B)**
75
+ - Field-value assertions are not tautological — catch mutations to file_count, total_bytes, last_modified
76
+ - LocalSyncStatus spy verifies correct arguments passed (not just that it doesn't raise)
77
+ - Removed test was genuinely redundant — no coverage loss
78
+ - Appropriate layer separation: S3ScanCommand spec tests orchestration; LocalSyncStatus spec tests computation
79
+
80
+ ---
81
+
82
+ ## Suggestions for Next Campaign
83
+
84
+ 1. **Investigate `kfix` staging behaviour** — check whether the alias runs `git add .` or `git add -A` before committing. If so, update AGENTS.md to use `git add <file> && kfix` pattern explicitly.
85
+
86
+ 2. **Next major work:** B020 (split S3Operations, 1,030 lines) — now the cleanest next step. Library boundaries are solid, dead code is gone, test suite is at B+.
87
+
88
+ 3. **B007 (parallelism)** follows B020 — do not attempt before S3Operations is split.
@@ -19,13 +19,32 @@
19
19
 
20
20
  ## ⚠️ Pre-Commit Check (Mandatory Every Commit)
21
21
 
22
- Before running `kfix`, always run:
22
+ `kfix` and `kfeat` run `git add .` internally — they stage EVERYTHING in the working tree unconditionally. You cannot selectively stage files.
23
+
24
+ **The only safe approach: ensure the working tree contains ONLY your intended changes before calling kfix.**
25
+
26
+ ```bash
27
+ git status # What files are modified/untracked?
28
+ git diff # What are the actual changes?
29
+ ```
30
+
31
+ If unintended files appear (e.g. planning docs, other specs):
32
+ - `git stash -- path/to/file` to temporarily stash a specific file
33
+ - OR `git checkout -- path/to/file` to discard an unintended change
34
+ - OR `git clean -n` to preview, then `git clean -f path/to/file` for untracked files
35
+
36
+ Once the working tree contains ONLY the files you intended to change, call:
23
37
  ```bash
24
- git status
38
+ kfix "your message here"
25
39
  ```
26
- Confirm ONLY the files you intentionally changed are staged. If unexpected files appear, run `git diff` to investigate before proceeding. Never commit files you didn't intentionally change.
27
40
 
28
- **Why:** Prior campaign accidentally staged a pre-existing uncommitted change when running `kfix`. Required a follow-up fix commit.
41
+ kfix/kfeat then:
42
+ 1. `git add .` — stages everything (working tree must be clean of unintended files)
43
+ 2. `git commit -m "fix: ..."` — commits
44
+ 3. `git pull` + `git push` — syncs and pushes
45
+ 4. Waits for CI (`gh run watch`) — blocks until green or red
46
+ 5. On success: `git pull` again to pick up semantic-release version bump + CHANGELOG
47
+ 6. Prints the new version tag
29
48
 
30
49
  ---
31
50
 
@@ -648,7 +667,7 @@ end
648
667
  ### From library-boundary-cleanup (2026-03-20)
649
668
 
650
669
  - **`instance_double` string form fails CI on Ubuntu.** Always use full constant: `instance_double(Fully::Qualified::ClassName)`. String form passes locally but Ubuntu CI enforces constant lookup. A CI failure after a green local run is almost always this.
651
- - **`git add .` is the recurring staging bug.** Three campaigns have had accidental staging from `git add .`. Stage specific files by name: `git add lib/appydave/tools/dam/local_sync_status.rb spec/...`. Never `git add .` or `git add -A` in this repo.
670
+ - **Dirty working tree + kfix = accidental staging.** `kfix` runs `git add .` internally it stages everything. Ensure the working tree contains ONLY intended changes before calling kfix. Three prior campaigns have been burned by this.
652
671
  - **`ENV['BRAND_PATH']` in bin/dam is dead code.** 10 assignments in bin/dam, 0 reads in lib/. S3Operations receives brand_path as a constructor parameter. Tracked as B038 for cleanup.
653
672
 
654
673
  ### From extract-vat-cli (2026-03-19)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Appydave
4
4
  module Tools
5
- VERSION = '0.76.13'
5
+ VERSION = '0.76.14'
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.13",
3
+ "version": "0.76.14",
4
4
  "description": "AppyDave YouTube Automation Tools",
5
5
  "scripts": {
6
6
  "release": "semantic-release"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appydave-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.76.13
4
+ version: 0.76.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Cruwys
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-03-19 00:00:00.000000000 Z
11
+ date: 2026-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -297,11 +297,14 @@ files:
297
297
  - docs/guides/tools/youtube-manager.md
298
298
  - docs/planning/AGENTS.md
299
299
  - docs/planning/BACKLOG.md
300
+ - docs/planning/batch-a-features/AGENTS.md
301
+ - docs/planning/batch-a-features/IMPLEMENTATION_PLAN.md
300
302
  - docs/planning/bugfix-and-security/AGENTS.md
301
303
  - docs/planning/bugfix-and-security/IMPLEMENTATION_PLAN.md
302
304
  - docs/planning/bugfix-and-security/assessment.md
303
305
  - docs/planning/env-dead-code-cleanup/AGENTS.md
304
306
  - docs/planning/env-dead-code-cleanup/IMPLEMENTATION_PLAN.md
307
+ - docs/planning/env-dead-code-cleanup/assessment.md
305
308
  - docs/planning/extract-vat-cli/AGENTS.md
306
309
  - docs/planning/extract-vat-cli/IMPLEMENTATION_PLAN.md
307
310
  - docs/planning/extract-vat-cli/assessment.md