appydave-tools 0.76.6 → 0.76.8
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 +4 -4
- data/CHANGELOG.md +15 -0
- data/bin/dam +8 -134
- data/docs/planning/AGENTS.md +9 -3
- data/docs/planning/BACKLOG.md +7 -3
- data/docs/planning/extract-vat-cli/IMPLEMENTATION_PLAN.md +3 -3
- data/docs/planning/extract-vat-cli/assessment.md +107 -0
- data/docs/planning/extract-vat-cli/learnings/wave-learnings.md +36 -0
- data/docs/planning/library-boundary-cleanup/AGENTS.md +653 -0
- data/docs/planning/library-boundary-cleanup/IMPLEMENTATION_PLAN.md +61 -0
- data/lib/appydave/tools/dam/errors.rb +3 -0
- data/lib/appydave/tools/dam/s3_arg_parser.rb +111 -0
- data/lib/appydave/tools/dam/s3_scan_command.rb +2 -4
- data/lib/appydave/tools/version.rb +1 -1
- data/lib/appydave/tools.rb +1 -0
- data/package.json +1 -1
- metadata +6 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 837cfa631ce3f72655208add542964a605efa385aea01abfe49c55fb9110cdbb
|
|
4
|
+
data.tar.gz: 4adfd72dad7dc7a41f909857a21616d3385b259ee11969d94641c75731713924
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2748e05e28b1c0cd4a7e6ec99f13a513c68ec49115d6b90bd0090d25467958eb0dd759cbf6fb8b94f210fc1835ee5d2c77cac79e3d7043cac2127513d982d89f
|
|
7
|
+
data.tar.gz: cf039ebec627c4899f5abc6c309829894c4dbca2ec71daf6ceb1143f61b72a46edb480223c9a8ab6acd5892ef343980f5395d6f6363276352056f111c3753026
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## [0.76.7](https://github.com/appydave/appydave-tools/compare/v0.76.6...v0.76.7) (2026-03-19)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* extract S3ArgParser module from VatCLI; add spec ([9bbd709](https://github.com/appydave/appydave-tools/commit/9bbd7099c26b774b68103f4d37d0a25f22f4abc2))
|
|
7
|
+
|
|
8
|
+
## [0.76.6](https://github.com/appydave/appydave-tools/compare/v0.76.5...v0.76.6) (2026-03-19)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* extract S3ScanCommand class from VatCLI; add smoke-test spec ([cd03606](https://github.com/appydave/appydave-tools/commit/cd03606e3307665c7f69391d6c160f50df1773ba))
|
|
14
|
+
* remove redundant rubocop disable directives from S3ScanCommand (CI rubocop 1.85.1) ([4c9deb4](https://github.com/appydave/appydave-tools/commit/4c9deb4298d4577a99d0e82e7b72918a26275d40))
|
|
15
|
+
|
|
1
16
|
## [0.76.5](https://github.com/appydave/appydave-tools/compare/v0.76.4...v0.76.5) (2026-03-19)
|
|
2
17
|
|
|
3
18
|
|
data/bin/dam
CHANGED
|
@@ -154,7 +154,7 @@ class VatCLI
|
|
|
154
154
|
|
|
155
155
|
# S3 Upload
|
|
156
156
|
def s3_up_command(args)
|
|
157
|
-
options =
|
|
157
|
+
options = Appydave::Tools::Dam::S3ArgParser.parse_s3(args, 's3-up')
|
|
158
158
|
s3_ops = Appydave::Tools::Dam::S3Operations.new(options[:brand], options[:project])
|
|
159
159
|
s3_ops.upload(dry_run: options[:dry_run])
|
|
160
160
|
rescue StandardError => e
|
|
@@ -164,7 +164,7 @@ class VatCLI
|
|
|
164
164
|
|
|
165
165
|
# S3 Download
|
|
166
166
|
def s3_down_command(args)
|
|
167
|
-
options =
|
|
167
|
+
options = Appydave::Tools::Dam::S3ArgParser.parse_s3(args, 's3-down')
|
|
168
168
|
s3_ops = Appydave::Tools::Dam::S3Operations.new(options[:brand], options[:project])
|
|
169
169
|
s3_ops.download(dry_run: options[:dry_run])
|
|
170
170
|
rescue StandardError => e
|
|
@@ -174,7 +174,7 @@ class VatCLI
|
|
|
174
174
|
|
|
175
175
|
# S3 Status
|
|
176
176
|
def s3_status_command(args)
|
|
177
|
-
options =
|
|
177
|
+
options = Appydave::Tools::Dam::S3ArgParser.parse_s3(args, 's3-status')
|
|
178
178
|
s3_ops = Appydave::Tools::Dam::S3Operations.new(options[:brand], options[:project])
|
|
179
179
|
s3_ops.status
|
|
180
180
|
rescue StandardError => e
|
|
@@ -184,7 +184,7 @@ class VatCLI
|
|
|
184
184
|
|
|
185
185
|
# S3 Cleanup Remote
|
|
186
186
|
def s3_cleanup_remote_command(args)
|
|
187
|
-
options =
|
|
187
|
+
options = Appydave::Tools::Dam::S3ArgParser.parse_s3(args, 's3-cleanup-remote')
|
|
188
188
|
s3_ops = Appydave::Tools::Dam::S3Operations.new(options[:brand], options[:project])
|
|
189
189
|
s3_ops.cleanup(force: options[:force], dry_run: options[:dry_run])
|
|
190
190
|
rescue StandardError => e
|
|
@@ -194,7 +194,7 @@ class VatCLI
|
|
|
194
194
|
|
|
195
195
|
# S3 Cleanup Local
|
|
196
196
|
def s3_cleanup_local_command(args)
|
|
197
|
-
options =
|
|
197
|
+
options = Appydave::Tools::Dam::S3ArgParser.parse_s3(args, 's3-cleanup-local')
|
|
198
198
|
s3_ops = Appydave::Tools::Dam::S3Operations.new(options[:brand], options[:project])
|
|
199
199
|
s3_ops.cleanup_local(force: options[:force], dry_run: options[:dry_run])
|
|
200
200
|
rescue StandardError => e
|
|
@@ -204,7 +204,7 @@ class VatCLI
|
|
|
204
204
|
|
|
205
205
|
# Share file via pre-signed URL
|
|
206
206
|
def s3_share_command(args)
|
|
207
|
-
options =
|
|
207
|
+
options = Appydave::Tools::Dam::S3ArgParser.parse_share(args)
|
|
208
208
|
|
|
209
209
|
share_ops = Appydave::Tools::Dam::ShareOperations.new(options[:brand], options[:project])
|
|
210
210
|
share_ops.generate_links(files: options[:file], expires: options[:expires], download: options[:download])
|
|
@@ -215,7 +215,7 @@ class VatCLI
|
|
|
215
215
|
|
|
216
216
|
# Discover files in S3 for a project
|
|
217
217
|
def s3_discover_command(args)
|
|
218
|
-
options =
|
|
218
|
+
options = Appydave::Tools::Dam::S3ArgParser.parse_discover(args)
|
|
219
219
|
files = fetch_s3_files(options[:brand_key], options[:project_id])
|
|
220
220
|
|
|
221
221
|
return if handle_empty_files?(files, options[:brand_key], options[:project_id])
|
|
@@ -228,7 +228,7 @@ class VatCLI
|
|
|
228
228
|
|
|
229
229
|
# Archive project to SSD
|
|
230
230
|
def archive_command(args)
|
|
231
|
-
options =
|
|
231
|
+
options = Appydave::Tools::Dam::S3ArgParser.parse_s3(args, 'archive')
|
|
232
232
|
s3_ops = Appydave::Tools::Dam::S3Operations.new(options[:brand], options[:project])
|
|
233
233
|
s3_ops.archive(force: options[:force], dry_run: options[:dry_run])
|
|
234
234
|
rescue StandardError => e
|
|
@@ -461,132 +461,6 @@ class VatCLI
|
|
|
461
461
|
exit 1
|
|
462
462
|
end
|
|
463
463
|
|
|
464
|
-
# Parse S3 command arguments
|
|
465
|
-
# rubocop:disable Metrics/MethodLength
|
|
466
|
-
def parse_s3_args(args, command)
|
|
467
|
-
dry_run = args.include?('--dry-run')
|
|
468
|
-
force = args.include?('--force')
|
|
469
|
-
args = args.reject { |arg| arg.start_with?('--') }
|
|
470
|
-
|
|
471
|
-
brand_arg = args[0]
|
|
472
|
-
project_arg = args[1]
|
|
473
|
-
|
|
474
|
-
if brand_arg.nil?
|
|
475
|
-
# Auto-detect from PWD
|
|
476
|
-
brand, project_id = Appydave::Tools::Dam::ProjectResolver.detect_from_pwd
|
|
477
|
-
if brand.nil? || project_id.nil?
|
|
478
|
-
puts '❌ Could not auto-detect brand/project from current directory'
|
|
479
|
-
puts "Usage: dam #{command} <brand> <project> [--dry-run]"
|
|
480
|
-
exit 1
|
|
481
|
-
end
|
|
482
|
-
brand_key = brand # Already detected, use as-is
|
|
483
|
-
else
|
|
484
|
-
# Validate brand exists before trying to resolve project
|
|
485
|
-
unless valid_brand?(brand_arg)
|
|
486
|
-
puts "❌ Invalid brand: '#{brand_arg}'"
|
|
487
|
-
puts ''
|
|
488
|
-
puts 'Valid brands:'
|
|
489
|
-
puts ' appydave → v-appydave (AppyDave brand)'
|
|
490
|
-
puts ' voz → v-voz (VOZ client)'
|
|
491
|
-
puts ' aitldr → v-aitldr (AITLDR brand)'
|
|
492
|
-
puts ' kiros → v-kiros (Kiros client)'
|
|
493
|
-
puts ' joy → v-beauty-and-joy (Beauty & Joy)'
|
|
494
|
-
puts ' ss → v-supportsignal (SupportSignal)'
|
|
495
|
-
puts ''
|
|
496
|
-
puts "Usage: dam #{command} <brand> <project> [--dry-run]"
|
|
497
|
-
exit 1
|
|
498
|
-
end
|
|
499
|
-
|
|
500
|
-
brand_key = brand_arg # Use the shortcut/key (e.g., 'appydave')
|
|
501
|
-
brand = Appydave::Tools::Dam::Config.expand_brand(brand_arg) # Expand for path resolution
|
|
502
|
-
project_id = Appydave::Tools::Dam::ProjectResolver.resolve(brand_arg, project_arg)
|
|
503
|
-
end
|
|
504
|
-
|
|
505
|
-
# Set ENV for compatibility with ConfigLoader
|
|
506
|
-
ENV['BRAND_PATH'] = Appydave::Tools::Dam::Config.brand_path(brand)
|
|
507
|
-
|
|
508
|
-
{ brand: brand_key, project: project_id, dry_run: dry_run, force: force }
|
|
509
|
-
end
|
|
510
|
-
# rubocop:enable Metrics/MethodLength
|
|
511
|
-
|
|
512
|
-
def valid_brand?(brand_key)
|
|
513
|
-
Appydave::Tools::Configuration::Config.configure
|
|
514
|
-
brands = Appydave::Tools::Configuration::Config.brands
|
|
515
|
-
brands.key?(brand_key) || brands.shortcut?(brand_key)
|
|
516
|
-
end
|
|
517
|
-
|
|
518
|
-
def parse_share_args(args)
|
|
519
|
-
# Extract --expires flag
|
|
520
|
-
expires = '7d' # default
|
|
521
|
-
if (expires_index = args.index('--expires'))
|
|
522
|
-
expires = args[expires_index + 1]
|
|
523
|
-
args.delete_at(expires_index + 1)
|
|
524
|
-
args.delete_at(expires_index)
|
|
525
|
-
end
|
|
526
|
-
|
|
527
|
-
# Extract --download flag
|
|
528
|
-
download = args.include?('--download')
|
|
529
|
-
|
|
530
|
-
# Remove other flags
|
|
531
|
-
args = args.reject { |arg| arg.start_with?('--') }
|
|
532
|
-
|
|
533
|
-
brand_arg = args[0]
|
|
534
|
-
project_arg = args[1]
|
|
535
|
-
file_arg = args[2]
|
|
536
|
-
|
|
537
|
-
show_share_usage_and_exit if brand_arg.nil? || project_arg.nil? || file_arg.nil?
|
|
538
|
-
|
|
539
|
-
brand_key = brand_arg
|
|
540
|
-
brand = Appydave::Tools::Dam::Config.expand_brand(brand_arg)
|
|
541
|
-
project_id = Appydave::Tools::Dam::ProjectResolver.resolve(brand_arg, project_arg)
|
|
542
|
-
|
|
543
|
-
# Set ENV for compatibility with ConfigLoader
|
|
544
|
-
ENV['BRAND_PATH'] = Appydave::Tools::Dam::Config.brand_path(brand)
|
|
545
|
-
|
|
546
|
-
{ brand: brand_key, project: project_id, file: file_arg, expires: expires, download: download }
|
|
547
|
-
end
|
|
548
|
-
|
|
549
|
-
def show_share_usage_and_exit
|
|
550
|
-
puts 'Usage: dam s3-share <brand> <project> <file> [--expires 7d] [--download]'
|
|
551
|
-
puts ''
|
|
552
|
-
puts 'Options:'
|
|
553
|
-
puts ' --expires TIME Expiry time (default: 7d)'
|
|
554
|
-
puts ' --download Force download instead of viewing in browser'
|
|
555
|
-
puts ''
|
|
556
|
-
puts 'Examples:'
|
|
557
|
-
puts ' dam s3-share appydave b70 video.mp4'
|
|
558
|
-
puts ' dam s3-share appydave b70 video.mp4 --expires 24h'
|
|
559
|
-
puts ' dam s3-share appydave b70 video.mp4 --download'
|
|
560
|
-
puts ' dam s3-share voz boy-baker final-edit.mov --expires 3d --download'
|
|
561
|
-
exit 1
|
|
562
|
-
end
|
|
563
|
-
|
|
564
|
-
def parse_discover_args(args)
|
|
565
|
-
shareable = args.include?('--shareable')
|
|
566
|
-
args = args.reject { |arg| arg.start_with?('--') }
|
|
567
|
-
|
|
568
|
-
brand_arg = args[0]
|
|
569
|
-
project_arg = args[1]
|
|
570
|
-
|
|
571
|
-
if brand_arg.nil? || project_arg.nil?
|
|
572
|
-
puts 'Usage: dam s3-discover <brand> <project> [--shareable]'
|
|
573
|
-
puts ''
|
|
574
|
-
puts 'Examples:'
|
|
575
|
-
puts ' dam s3-discover appydave b70 # List files'
|
|
576
|
-
puts ' dam s3-discover appydave b70 --shareable # Generate share commands'
|
|
577
|
-
exit 1
|
|
578
|
-
end
|
|
579
|
-
|
|
580
|
-
brand_key = brand_arg
|
|
581
|
-
brand = Appydave::Tools::Dam::Config.expand_brand(brand_arg)
|
|
582
|
-
project_id = Appydave::Tools::Dam::ProjectResolver.resolve(brand_arg, project_arg)
|
|
583
|
-
|
|
584
|
-
# Set ENV for compatibility with ConfigLoader
|
|
585
|
-
ENV['BRAND_PATH'] = Appydave::Tools::Dam::Config.brand_path(brand)
|
|
586
|
-
|
|
587
|
-
{ brand_key: brand_key, project_id: project_id, shareable: shareable }
|
|
588
|
-
end
|
|
589
|
-
|
|
590
464
|
def fetch_s3_files(brand_key, project_id)
|
|
591
465
|
s3_ops = Appydave::Tools::Dam::S3Operations.new(brand_key, project_id)
|
|
592
466
|
s3_ops.list_s3_files
|
data/docs/planning/AGENTS.md
CHANGED
|
@@ -45,7 +45,7 @@ kfeat "add feature description" # Minor version bump
|
|
|
45
45
|
kfix "fix bug description" # Patch version bump
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
**Baseline (2026-03-19):**
|
|
48
|
+
**Baseline (2026-03-19):** 847 examples, 0 failures, ~86.21% line coverage
|
|
49
49
|
|
|
50
50
|
---
|
|
51
51
|
|
|
@@ -221,9 +221,9 @@ Appydave::Tools::Configuration::Config.configure
|
|
|
221
221
|
|
|
222
222
|
## Quality Gates
|
|
223
223
|
|
|
224
|
-
- **Tests:** `bundle exec rspec` —
|
|
224
|
+
- **Tests:** `bundle exec rspec` — 847 examples, 0 failures (do not ship if any fail)
|
|
225
225
|
- **Lint:** `bundle exec rubocop --format clang` — 0 offenses (CI will reject)
|
|
226
|
-
- **Coverage:** ≥
|
|
226
|
+
- **Coverage:** ≥ 86.21% line coverage
|
|
227
227
|
- **frozen_string_literal:** Required on every new `.rb` file
|
|
228
228
|
- **Commit format:** `kfeat`/`kfix` only — triggers semantic versioning + CI wait
|
|
229
229
|
|
|
@@ -248,6 +248,12 @@ Appydave::Tools::Configuration::Config.configure
|
|
|
248
248
|
- **Jump Commands layer is undertested:** `Commands::Remove`, `Commands::Add`, `Commands::Update` have zero dedicated specs. Auto-regenerate CLI spec does not substitute for command-layer unit tests verifying `--force` guards, error codes, and suggestion logic (see B018).
|
|
249
249
|
- **Jump report commands** got `--limit` and `--skip-unassigned` flags after initial implementation. Jump tool scope grows incrementally.
|
|
250
250
|
|
|
251
|
+
### From extract-vat-cli (2026-03-19)
|
|
252
|
+
|
|
253
|
+
- **Do NOT carry over rubocop-disable comments when extracting methods.** Run rubocop on the new file first — methods that needed disables in a 1,600-line God class often don't exceed thresholds in a properly-scoped library class. Carrying them over causes CI to flag `Lint/RedundantCopDisableDirective` and requires a second fix commit.
|
|
254
|
+
- **grep for callers before writing the plan.** `format_bytes` had 4 callers in bin/dam, not 3 as counted from memory. The orphaned-projects loop was missed.
|
|
255
|
+
- **`valid_brand?` needs `Config.brands` mock, not just `SettingsConfig` mock.** The shared `'with vat filesystem and brands'` context only mocks `SettingsConfig#video_projects_root`. Any method calling `Config.brands` directly needs an explicit brands config mock.
|
|
256
|
+
|
|
251
257
|
### From Three-Lens Audit (2026-03-19)
|
|
252
258
|
|
|
253
259
|
- **`file_collector.rb` has two landmines before FR-2:** `puts @working_directory` at line 15 pollutes stdout; `FileUtils.cd` without `ensure` leaves process in wrong directory on exception. Fix both before adding any code to this class.
|
data/docs/planning/BACKLOG.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Project Backlog — AppyDave Tools
|
|
2
2
|
|
|
3
|
-
**Last updated**: 2026-03-19 (
|
|
4
|
-
**Total**:
|
|
3
|
+
**Last updated**: 2026-03-19 (extract-vat-cli campaign complete)
|
|
4
|
+
**Total**: 37 | Pending: 11 | Done: 26 | Deferred: 0 | Rejected: 0
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -14,8 +14,12 @@
|
|
|
14
14
|
- [ ] B008 — Performance: cache git/S3 status with 5-min TTL | Priority: low
|
|
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
|
+
- [x] B011 — Arch: extract VatCLI business logic from bin/dam (1,600-line God class) | Completed: extract-vat-cli (2026-03-19)
|
|
18
18
|
- [ ] B020 — Arch: split S3Operations (1,030 lines, mixed I/O + logic) | Priority: low
|
|
19
|
+
- [ ] B034 — Fix: replace exit 1 with typed exceptions in S3ScanCommand + S3ArgParser | Priority: high (blocks B007 + test coverage)
|
|
20
|
+
- [ ] B035 — Fix: remove ENV['BRAND_PATH'] side effect from S3ArgParser | Priority: high (blocks B007 parallelism)
|
|
21
|
+
- [ ] B036 — Tests: improve S3ScanCommand spec from D to B (depends on B034) | Priority: medium
|
|
22
|
+
- [ ] B037 — Tests: LocalSyncStatus :partial case, local_file_count assertion, Zone.Identifier exclusion | Priority: medium
|
|
19
23
|
|
|
20
24
|
---
|
|
21
25
|
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
**Target**: All 4 complete; 831+ examples passing; rubocop 0 offenses; no regressions
|
|
6
6
|
|
|
7
7
|
## Summary
|
|
8
|
-
- Total: 4 | Complete:
|
|
8
|
+
- Total: 4 | Complete: 4 | In Progress: 0 | Pending: 0 | Failed: 0
|
|
9
9
|
|
|
10
10
|
## Pending
|
|
11
11
|
- [x] extract-format-bytes — Replaced 4 callers (plan said 3; orphaned-projects loop in display_s3_scan_table was a 4th). format_bytes deleted. rubocop 0 offenses. Commit: 3cd362f.
|
|
12
12
|
- [x] extract-local-sync-status — LocalSyncStatus module created, 7 specs added (838 total), both methods gone from VatCLI. Side-fix: restored youtube_automation_config require incorrectly removed in prior commit. v0.76.5.
|
|
13
|
-
- [
|
|
14
|
-
- [
|
|
13
|
+
- [x] extract-s3-scan-command — S3ScanCommand created, 2 smoke tests added (840 total), 3 methods gone from VatCLI. Note: rubocop-disable directives became redundant once methods left God class — needed 2nd kfix to remove them. v0.76.6.
|
|
14
|
+
- [x] extract-s3-arg-parser — S3ArgParser module created, 7 specs added (847 total), 5 methods + 8 callers updated, all gone from VatCLI. Note: valid_brand? needed Config.brands mock (shared context only mocks SettingsConfig). v0.76.7.
|
|
15
15
|
|
|
16
16
|
## In Progress
|
|
17
17
|
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Assessment: extract-vat-cli
|
|
2
|
+
|
|
3
|
+
**Campaign**: extract-vat-cli
|
|
4
|
+
**Date**: 2026-03-19 → 2026-03-19
|
|
5
|
+
**Results**: 4/4 complete, 0 failed
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Results Summary
|
|
10
|
+
|
|
11
|
+
| Work Unit | Outcome | Version | Notes |
|
|
12
|
+
|---|---|---|---|
|
|
13
|
+
| extract-format-bytes | ✅ Complete | v0.76.4→ | 4 callers replaced (plan said 3; orphaned-projects loop was 4th) |
|
|
14
|
+
| extract-local-sync-status | ✅ Complete | v0.76.5 | Side-fix: restored youtube_automation_config require removed in prior commit |
|
|
15
|
+
| extract-s3-scan-command | ✅ Complete | v0.76.6 | Needed 2nd kfix: inherited rubocop-disable directives became redundant |
|
|
16
|
+
| extract-s3-arg-parser | ✅ Complete | v0.76.7 | valid_brand? needed BrandsConfig mock; parse_share fully tested |
|
|
17
|
+
|
|
18
|
+
**Suite:** 831 → 847 examples (+16), 0 failures, 86.21% coverage. rubocop 0 offenses. CI green on all 4 commits.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## What Worked Well
|
|
23
|
+
|
|
24
|
+
1. **Sequential wave strategy was correct.** All 4 work units touched bin/dam — wave size = 1 was the only safe option. No conflicts, no merge issues, zero failed agents.
|
|
25
|
+
|
|
26
|
+
2. **AGENTS.md quality was sufficient.** All agents executed cleanly without confusion about which methods to move or which callers to update. The detailed WU-specific instructions in the campaign AGENTS.md were the right level of specificity.
|
|
27
|
+
|
|
28
|
+
3. **format_bytes extraction was genuinely trivial.** FileHelper.format_size already existed and was identical. WU1 was pure deletion + caller update — no logic risk.
|
|
29
|
+
|
|
30
|
+
4. **LocalSyncStatus came out clean.** The module_function pattern worked correctly, the shared filesystem context covered most branches, and the side-fix of youtube_automation_config was a net win.
|
|
31
|
+
|
|
32
|
+
5. **bin/dam went from 1,600 → 1,223 lines (−23%) and 20+ → 5 rubocop-disables.** The remaining 5 are structural (help text dispatch, not logic). Significant improvement.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## What Didn't Work
|
|
37
|
+
|
|
38
|
+
1. **Inherited rubocop-disable directives caused 2nd kfix commits** (WU3, WU4). Methods that needed `Metrics/MethodLength` disable in the God class context don't exceed thresholds in properly-scoped library classes. CI rubocop 1.85.1 flags them as redundant. Rule: don't carry over rubocop-disable comments when extracting — run rubocop fresh on the new file.
|
|
39
|
+
|
|
40
|
+
2. **Caller count was wrong in the plan.** Plan said 3 callers for format_bytes; there were 4 (orphaned-projects loop in display_s3_scan_table). This was a minor issue (caught and fixed by WU1 agent) but points to a planning discipline gap: grep for callers, don't count from memory.
|
|
41
|
+
|
|
42
|
+
3. **exit 1 calls were carried into library code.** `S3ScanCommand` and `S3ArgParser` both call `exit 1` in response to invalid inputs. This was the pre-existing VatCLI pattern — the extraction was faithful — but it now lives in library classes where it's a boundary violation. Makes testing error paths impossible and blocks safe parallelism (B007).
|
|
43
|
+
|
|
44
|
+
4. **S3ScanCommand spec is F-grade (confirmed by independent test audit).** Two `respond_to` tests confirm the class loads; nothing more. 90 lines of orchestration logic — manifest merging, orphan detection, LocalSyncStatus wiring — are completely unprotected. The `exit 1` call at line 55 makes the "no manifest" path untestable without production code changes first.
|
|
45
|
+
|
|
46
|
+
5. **`Zone.Identifier` exclusion in LocalSyncStatus has no test.** The filter for Windows metadata files (line 26) is untested — removal or inversion would pass all current specs silently.
|
|
47
|
+
|
|
48
|
+
6. **`ENV['BRAND_PATH']` side-effect never asserted in S3ArgParser spec.** All three parse methods set it; none of the specs check it. A key-name change would silently break ConfigLoader downstream.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Key Learnings — Application
|
|
53
|
+
|
|
54
|
+
1. **Don't carry over rubocop-disable comments when extracting.** Run rubocop fresh on the new file; only add disables if actually triggered.
|
|
55
|
+
2. **grep for callers before writing the plan.** Don't count from memory or code review alone.
|
|
56
|
+
3. **Library classes must not call `exit`.** Raise typed exceptions (`ConfigurationError`, `UsageError`) — let the CLI layer print and exit. Required before B007.
|
|
57
|
+
4. **`valid_brand?` and anything calling `Config.brands` needs BrandsConfig mock** — the shared context only mocks `SettingsConfig#video_projects_root`.
|
|
58
|
+
5. **ENV['BRAND_PATH'] side effect now in library code.** S3ArgParser sets a process-wide env var as a side effect of argument parsing. Must be resolved before parallelism (B007).
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Key Learnings — Ralph Loop
|
|
63
|
+
|
|
64
|
+
1. **Wave size = 1 was correct for God-class extraction.** When all work units share a single target file, parallel waves cause conflicts. Plan for sequential from the start.
|
|
65
|
+
2. **Detailed WU-specific AGENTS.md sections paid off.** Specifying exact line numbers, method names, and new class signatures eliminated agent confusion. Worth the upfront effort.
|
|
66
|
+
3. **Side-fixes happen.** WU2 found and fixed a pre-existing require omission. Good — agents should fix what they find. Update the plan notes so the next session knows.
|
|
67
|
+
4. **2nd kfix commits were needed on 2/4 work units** — predictable pattern for extractions. Build in a "check for redundant rubocop directives" step in future extraction AGENTS.md.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Promote to Main KDD?
|
|
72
|
+
|
|
73
|
+
- "Don't carry over rubocop-disable when extracting" → yes, promote
|
|
74
|
+
- "grep for callers before planning" → yes, promote
|
|
75
|
+
- "Library classes must not call exit" → yes, promote (already in project AGENTS.md, promote to KDD)
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Suggestions for Next Campaign
|
|
80
|
+
|
|
81
|
+
### Debt introduced by this campaign (should address before B007)
|
|
82
|
+
|
|
83
|
+
**B034 — Fix: replace `exit 1` with typed exceptions in S3ScanCommand + S3ArgParser**
|
|
84
|
+
- `S3ScanCommand#scan_single` calls `exit 1` at line 55 (no manifest path)
|
|
85
|
+
- `S3ArgParser` calls `exit 1` at 4 locations (invalid brand, PWD auto-detect fail, discover missing args, share missing args)
|
|
86
|
+
- Fix: raise `Appydave::Tools::Dam::ConfigurationError` / new `UsageError` subclass
|
|
87
|
+
- VatCLI `rescue StandardError` already handles these at command level
|
|
88
|
+
- Unblocks proper testing of error paths AND safe parallelism
|
|
89
|
+
|
|
90
|
+
**B035 — Fix: remove ENV['BRAND_PATH'] side effect from S3ArgParser**
|
|
91
|
+
- `parse_s3`, `parse_share`, `parse_discover` all set `ENV['BRAND_PATH']` as a side effect
|
|
92
|
+
- Acceptable in a single-process CLI; unsafe in parallel execution
|
|
93
|
+
- Fix: return the brand_path in the result hash and let VatCLI set it, or extract to explicit `S3ArgParser.configure_env!(brand)`
|
|
94
|
+
|
|
95
|
+
**B036 — Tests: improve S3ScanCommand spec from D to B**
|
|
96
|
+
- Depends on B034 (exit → exception) — can't test "no manifest" path until that's fixed
|
|
97
|
+
- Add: mocked S3Scanner + filesystem fixture, assert manifest merge logic, assert LocalSyncStatus wiring
|
|
98
|
+
|
|
99
|
+
**B037 — Tests: add LocalSyncStatus :partial case + local_file_count assertion**
|
|
100
|
+
- Add `:partial` context (1 file in staging, s3 has 3) — assert `:partial` status and `local_file_count: 1`
|
|
101
|
+
- Add assertion that `:synced` case sets `local_file_count` correctly
|
|
102
|
+
|
|
103
|
+
### Mode recommendation for next session
|
|
104
|
+
|
|
105
|
+
**4. Extend** — these are small debt items, same stack, inherit this AGENTS.md.
|
|
106
|
+
|
|
107
|
+
Or, if you want to move forward to B007 (parallelism) instead: address B034 + B035 first (1-wave campaign), then B007 becomes buildable.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Wave Learnings — extract-vat-cli
|
|
2
|
+
|
|
3
|
+
**Campaign:** extract-vat-cli
|
|
4
|
+
**Date:** 2026-03-19
|
|
5
|
+
**Result:** 4/4 complete. 831 → 847 examples (+16). v0.76.3 → v0.76.7.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Application Learnings
|
|
10
|
+
|
|
11
|
+
### 1. format_bytes had 4 callers, not 3
|
|
12
|
+
The orphaned-projects loop in `display_s3_scan_table` had its own `format_bytes` call. Plan said 3 callers; there were 4. Always grep for callers before writing the plan rather than counting from memory.
|
|
13
|
+
|
|
14
|
+
### 2. rubocop-disable directives become redundant when methods leave God class
|
|
15
|
+
WU3 and WU4 both needed a second `kfix` to remove `rubocop:disable Metrics/MethodLength` and `Metrics/CyclomaticComplexity` directives that were valid in the 1,600-line VatCLI context but flagged as redundant by CI rubocop 1.85.1 once the methods were in properly-scoped library classes.
|
|
16
|
+
|
|
17
|
+
**Rule for future extractions:** Do NOT carry over rubocop-disable comments. Run rubocop locally on the new file first — only add disables if rubocop actually flags an offense.
|
|
18
|
+
|
|
19
|
+
### 3. valid_brand? needed Config.brands mock, not just SettingsConfig mock
|
|
20
|
+
The shared context `'with vat filesystem and brands'` only mocks `SettingsConfig#video_projects_root`. Testing `valid_brand?` (which calls `Config.brands`) required an explicit `Config.configure` + `Config.brands` mock with a test `BrandsConfig` instance. Document this in AGENTS.md for future S3ArgParser specs.
|
|
21
|
+
|
|
22
|
+
### 4. youtube_automation_config require was missing (pre-existing)
|
|
23
|
+
WU2 agent discovered that a prior `chore(cleanup)` commit had incorrectly removed `require 'appydave/tools/youtube_automation_config'` from `lib/appydave/tools.rb`. The CI was failing on a `NameError` for `YoutubeAutomationConfig` unrelated to our work. Agent restored it as a side-fix. This was confirmed pre-existing in WU1's CI output.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Loop Meta-Learnings
|
|
28
|
+
|
|
29
|
+
### Wave size = 1 was correct
|
|
30
|
+
All 4 work units touched `bin/dam`. Sequential was the only safe option. No conflicts, no merge issues.
|
|
31
|
+
|
|
32
|
+
### Agents produced clean rubocop output when not inheriting disables
|
|
33
|
+
WU1 (no rubocop disables to inherit) was cleanest. WU2 produced clean output first-pass. WU3 and WU4 needed a second pass due to inherited directives — preventable with the "don't carry over disables" rule above.
|
|
34
|
+
|
|
35
|
+
### 4 agents, all successful, no failures
|
|
36
|
+
Every work unit completed in a single run (plus occasional second kfix for rubocop cleanup). AGENTS.md quality was sufficient — agents didn't get confused about which methods to move or which callers to update.
|