appydave-tools 0.76.4 → 0.76.5
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 +8 -0
- data/bin/dam +6 -76
- data/docs/planning/AGENTS.md +17 -5
- data/docs/planning/BACKLOG.md +6 -8
- data/docs/planning/extract-vat-cli/AGENTS.md +363 -0
- data/docs/planning/extract-vat-cli/IMPLEMENTATION_PLAN.md +61 -0
- data/docs/planning/micro-cleanup/IMPLEMENTATION_PLAN.md +6 -4
- data/docs/planning/micro-cleanup/assessment.md +68 -0
- data/lib/appydave/tools/dam/local_sync_status.rb +66 -0
- data/lib/appydave/tools/version.rb +1 -1
- data/lib/appydave/tools.rb +1 -2
- data/package.json +1 -1
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0531bc72c6c4aeac9830f72f552a4d683775d7efc61ee483069d882cdbbbf5af
|
|
4
|
+
data.tar.gz: 1c8e032ddc4e8dfc4c196452465abea40958107d2d68c5e4063c1ec2c36a3a9d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1787f781bdf758fae98c3388cce0437aca330a3161937f5c214651885c80e05598682e64afc3adea1e2ffd3b2f70d09287a9acca12c8fada89077b7d48791a39
|
|
7
|
+
data.tar.gz: 6cadabed0aa0f0be9d993b4dbda2c304c9a34f717fcc7b86b8cc059777e7cb0a57a494ddc2c3e5432fd4d4d85990330a6d401aa75c89bb7b0be5f5207c68d12b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## [0.76.4](https://github.com/appydave/appydave-tools/compare/v0.76.3...v0.76.4) (2026-03-19)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* add subprocess test for -f json flag to cli_spec ([de6a28a](https://github.com/appydave/appydave-tools/commit/de6a28a39790d29f5141fbf313fa5e44782b90a6))
|
|
7
|
+
* restore youtube_automation_config require accidentally removed in previous commit ([e4180db](https://github.com/appydave/appydave-tools/commit/e4180db9ad90040ec4faf2811fad713a20e6be8e))
|
|
8
|
+
|
|
1
9
|
## [0.76.3](https://github.com/appydave/appydave-tools/compare/v0.76.2...v0.76.3) (2026-03-19)
|
|
2
10
|
|
|
3
11
|
|
data/bin/dam
CHANGED
|
@@ -515,63 +515,6 @@ class VatCLI
|
|
|
515
515
|
brands.key?(brand_key) || brands.shortcut?(brand_key)
|
|
516
516
|
end
|
|
517
517
|
|
|
518
|
-
# Add local sync status to matched projects data
|
|
519
|
-
# Mutates the matched_projects hash to add :local_status key
|
|
520
|
-
# @param matched_projects [Hash] Map of project_id => S3 data
|
|
521
|
-
# @param brand_key [String] Brand key (e.g., 'appydave', 'ss')
|
|
522
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
523
|
-
def add_local_sync_status!(matched_projects, brand_key)
|
|
524
|
-
matched_projects.each do |project_id, data|
|
|
525
|
-
project_path = Appydave::Tools::Dam::Config.project_path(brand_key, project_id)
|
|
526
|
-
s3_staging_path = File.join(project_path, 's3-staging')
|
|
527
|
-
|
|
528
|
-
if !Dir.exist?(project_path)
|
|
529
|
-
data[:local_status] = :no_project # Project directory doesn't exist
|
|
530
|
-
elsif !Dir.exist?(s3_staging_path)
|
|
531
|
-
data[:local_status] = :no_files # Project exists but no downloads yet
|
|
532
|
-
else
|
|
533
|
-
# Count local files in s3-staging
|
|
534
|
-
local_files = Dir.glob(File.join(s3_staging_path, '**', '*'))
|
|
535
|
-
.select { |f| File.file?(f) }
|
|
536
|
-
.reject { |f| File.basename(f).include?('Zone.Identifier') } # Exclude Windows metadata
|
|
537
|
-
|
|
538
|
-
s3_file_count = data[:file_count]
|
|
539
|
-
local_file_count = local_files.size
|
|
540
|
-
|
|
541
|
-
data[:local_status] = if local_file_count.zero?
|
|
542
|
-
:no_files
|
|
543
|
-
elsif local_file_count == s3_file_count
|
|
544
|
-
:synced # Fully synced
|
|
545
|
-
else
|
|
546
|
-
:partial # Some files downloaded
|
|
547
|
-
end
|
|
548
|
-
|
|
549
|
-
data[:local_file_count] = local_file_count
|
|
550
|
-
end
|
|
551
|
-
end
|
|
552
|
-
end
|
|
553
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
554
|
-
|
|
555
|
-
# Format local sync status for display
|
|
556
|
-
# @param status [Symbol] :synced, :no_files, :partial, :no_project
|
|
557
|
-
# @param local_count [Integer, nil] Number of local files
|
|
558
|
-
# @param s3_count [Integer] Number of S3 files
|
|
559
|
-
# @return [String] Formatted status string
|
|
560
|
-
def format_local_status(status, local_count, s3_count)
|
|
561
|
-
case status
|
|
562
|
-
when :synced
|
|
563
|
-
'✓ Synced'
|
|
564
|
-
when :no_files
|
|
565
|
-
'⚠ None'
|
|
566
|
-
when :partial
|
|
567
|
-
"⚠ #{local_count}/#{s3_count}"
|
|
568
|
-
when :no_project
|
|
569
|
-
'✗ Missing'
|
|
570
|
-
else
|
|
571
|
-
'Unknown'
|
|
572
|
-
end
|
|
573
|
-
end
|
|
574
|
-
|
|
575
518
|
def parse_share_args(args)
|
|
576
519
|
# Extract --expires flag
|
|
577
520
|
expires = '7d' # default
|
|
@@ -693,14 +636,14 @@ class VatCLI
|
|
|
693
636
|
|
|
694
637
|
# Format columns
|
|
695
638
|
file_col = filename.ljust(40)
|
|
696
|
-
size_col =
|
|
639
|
+
size_col = Appydave::Tools::Dam::FileHelper.format_size(size).rjust(10)
|
|
697
640
|
date_col = last_modified ? last_modified.strftime('%Y-%m-%d %H:%M') : 'N/A'.ljust(16)
|
|
698
641
|
|
|
699
642
|
puts "#{file_col} #{size_col} #{date_col}"
|
|
700
643
|
end
|
|
701
644
|
|
|
702
645
|
puts '-' * 72
|
|
703
|
-
puts "Total: #{files.size} #{files.size == 1 ? 'file' : 'files'}, #{
|
|
646
|
+
puts "Total: #{files.size} #{files.size == 1 ? 'file' : 'files'}, #{Appydave::Tools::Dam::FileHelper.format_size(total_bytes)}"
|
|
704
647
|
|
|
705
648
|
# Quick actions section
|
|
706
649
|
puts ''
|
|
@@ -1478,7 +1421,7 @@ class VatCLI
|
|
|
1478
1421
|
File.write(manifest_path, JSON.pretty_generate(manifest))
|
|
1479
1422
|
|
|
1480
1423
|
# Add local sync status to matched projects
|
|
1481
|
-
|
|
1424
|
+
Appydave::Tools::Dam::LocalSyncStatus.enrich!(matched_projects, brand_key)
|
|
1482
1425
|
|
|
1483
1426
|
# Display table
|
|
1484
1427
|
display_s3_scan_table(matched_projects, orphaned_projects, bucket, prefix, region)
|
|
@@ -1507,8 +1450,8 @@ class VatCLI
|
|
|
1507
1450
|
# Display matched projects first (sorted alphabetically)
|
|
1508
1451
|
matched_projects.sort.each do |project_id, data|
|
|
1509
1452
|
files = data[:file_count].to_s.rjust(5)
|
|
1510
|
-
size =
|
|
1511
|
-
local_status =
|
|
1453
|
+
size = Appydave::Tools::Dam::FileHelper.format_size(data[:total_bytes]).rjust(10)
|
|
1454
|
+
local_status = Appydave::Tools::Dam::LocalSyncStatus.format(data[:local_status], data[:local_file_count], data[:file_count])
|
|
1512
1455
|
modified = data[:last_modified] ? Time.parse(data[:last_modified]).strftime('%Y-%m-%d %H:%M') : 'N/A'
|
|
1513
1456
|
|
|
1514
1457
|
puts format('%-36s %5s %10s %-9s %s', project_id, files, size, local_status, modified)
|
|
@@ -1520,7 +1463,7 @@ class VatCLI
|
|
|
1520
1463
|
puts '-' * 92
|
|
1521
1464
|
orphaned_projects.sort.each do |project_id, data|
|
|
1522
1465
|
files = data[:file_count].to_s.rjust(5)
|
|
1523
|
-
size =
|
|
1466
|
+
size = Appydave::Tools::Dam::FileHelper.format_size(data[:total_bytes]).rjust(10)
|
|
1524
1467
|
local_status = 'N/A'
|
|
1525
1468
|
modified = data[:last_modified] ? Time.parse(data[:last_modified]).strftime('%Y-%m-%d %H:%M') : 'N/A'
|
|
1526
1469
|
|
|
@@ -1582,19 +1525,6 @@ class VatCLI
|
|
|
1582
1525
|
puts "Total brands scanned: #{successful.size}/#{results.size}"
|
|
1583
1526
|
end
|
|
1584
1527
|
# rubocop:enable Metrics/MethodLength
|
|
1585
|
-
|
|
1586
|
-
# Format bytes in human-readable format
|
|
1587
|
-
# rubocop:disable Style/FormatStringToken
|
|
1588
|
-
def format_bytes(bytes)
|
|
1589
|
-
return '0 B' if bytes.zero?
|
|
1590
|
-
|
|
1591
|
-
units = %w[B KB MB GB TB]
|
|
1592
|
-
exp = (Math.log(bytes) / Math.log(1024)).to_i
|
|
1593
|
-
exp = [exp, units.length - 1].min
|
|
1594
|
-
|
|
1595
|
-
format('%.1f %s', bytes.to_f / (1024**exp), units[exp])
|
|
1596
|
-
end
|
|
1597
|
-
# rubocop:enable Style/FormatStringToken
|
|
1598
1528
|
end
|
|
1599
1529
|
|
|
1600
1530
|
# Run CLI
|
data/docs/planning/AGENTS.md
CHANGED
|
@@ -14,6 +14,18 @@
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
+
## ⚠️ Pre-Commit Check (Mandatory Every Commit)
|
|
18
|
+
|
|
19
|
+
Before running `kfix`, always run:
|
|
20
|
+
```bash
|
|
21
|
+
git status
|
|
22
|
+
```
|
|
23
|
+
Confirm ONLY the files you intentionally changed are staged. If unexpected files appear, run `git diff` to investigate before proceeding.
|
|
24
|
+
|
|
25
|
+
**Why:** micro-cleanup (2026-03-19) accidentally staged a pre-existing uncommitted change in `lib/appydave/tools.rb` when running `kfix`.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
17
29
|
## Build & Run Commands
|
|
18
30
|
|
|
19
31
|
```bash
|
|
@@ -33,7 +45,7 @@ kfeat "add feature description" # Minor version bump
|
|
|
33
45
|
kfix "fix bug description" # Patch version bump
|
|
34
46
|
```
|
|
35
47
|
|
|
36
|
-
**Baseline (2026-03-19):**
|
|
48
|
+
**Baseline (2026-03-19):** 831 examples, 0 failures, ~85.92% line coverage
|
|
37
49
|
|
|
38
50
|
---
|
|
39
51
|
|
|
@@ -82,9 +94,9 @@ docs/
|
|
|
82
94
|
|
|
83
95
|
Every work unit must satisfy ALL of the following before marking `[x]`:
|
|
84
96
|
|
|
85
|
-
- [ ] `bundle exec rspec` —
|
|
97
|
+
- [ ] `bundle exec rspec` — 831+ examples, 0 failures
|
|
86
98
|
- [ ] `bundle exec rubocop --format clang` — 0 offenses
|
|
87
|
-
- [ ] Line coverage stays ≥
|
|
99
|
+
- [ ] Line coverage stays ≥ 85.92%
|
|
88
100
|
- [ ] Any new functionality has ≥ 1 spec covering it
|
|
89
101
|
- [ ] All new `.rb` files start with `# frozen_string_literal: true`
|
|
90
102
|
- [ ] No hardcoded brand transformation strings (always use BrandResolver)
|
|
@@ -209,9 +221,9 @@ Appydave::Tools::Configuration::Config.configure
|
|
|
209
221
|
|
|
210
222
|
## Quality Gates
|
|
211
223
|
|
|
212
|
-
- **Tests:** `bundle exec rspec` —
|
|
224
|
+
- **Tests:** `bundle exec rspec` — 831 examples, 0 failures (do not ship if any fail)
|
|
213
225
|
- **Lint:** `bundle exec rubocop --format clang` — 0 offenses (CI will reject)
|
|
214
|
-
- **Coverage:** ≥
|
|
226
|
+
- **Coverage:** ≥ 85.92% line coverage
|
|
215
227
|
- **frozen_string_literal:** Required on every new `.rb` file
|
|
216
228
|
- **Commit format:** `kfeat`/`kfix` only — triggers semantic versioning + CI wait
|
|
217
229
|
|
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**: 33 | Pending:
|
|
3
|
+
**Last updated**: 2026-03-19 (micro-cleanup campaign complete)
|
|
4
|
+
**Total**: 33 | Pending: 7 | In Progress: 1 | Done: 25 | Deferred: 0 | Rejected: 0
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -10,16 +10,11 @@
|
|
|
10
10
|
### Medium Priority
|
|
11
11
|
- [ ] B001 — FR-1: GPT Context token counting | Priority: medium
|
|
12
12
|
- [ ] B012 — Arch: add integration tests for brand resolution end-to-end | Priority: medium
|
|
13
|
-
|
|
14
|
-
### Low Priority
|
|
15
|
-
- [ ] B031 — Tests: add_spec.rb assert `type` field in location data integrity test | Priority: low
|
|
16
|
-
- [ ] B032 — Tests: cli_spec.rb add subprocess test for `-f json` flag | Priority: low
|
|
17
|
-
- [ ] B033 — Fix: file_collector.rb line 19 return `''` directly when working_directory doesn't exist | Priority: low
|
|
18
13
|
- [ ] B007 — Performance: parallel git/S3 status checks for dam list | Priority: low
|
|
19
14
|
- [ ] B008 — Performance: cache git/S3 status with 5-min TTL | Priority: low
|
|
20
15
|
- [ ] B009 — UX: progress indicators for dam operations > 5s | Priority: low
|
|
21
16
|
- [ ] B010 — UX: auto-adjust dam table column widths to terminal width | Priority: low
|
|
22
|
-
- [
|
|
17
|
+
- [~] B011 — Arch: extract VatCLI business logic from bin/dam (1,600-line God class) | Campaign: extract-vat-cli
|
|
23
18
|
- [ ] B020 — Arch: split S3Operations (1,030 lines, mixed I/O + logic) | Priority: low
|
|
24
19
|
|
|
25
20
|
---
|
|
@@ -48,6 +43,9 @@
|
|
|
48
43
|
- [x] B028 — Tests: cli_spec file body content assertions in -i/-e tests | Completed: final-test-gaps (2026-03-19)
|
|
49
44
|
- [x] B029 — Tests: add_spec validate all returned location data fields | Completed: final-test-gaps (2026-03-19)
|
|
50
45
|
- [x] B030 — Tests: update_spec verify non-updated fields unchanged | Completed: final-test-gaps (2026-03-19)
|
|
46
|
+
- [x] B031 — Tests: add_spec `type` field assertion | Completed: already in commit 8eec40c; closed micro-cleanup (2026-03-19)
|
|
47
|
+
- [x] B032 — Tests: cli_spec `-f json` subprocess test | Completed: micro-cleanup (2026-03-19), v0.76.4
|
|
48
|
+
- [x] B033 — Fix: file_collector.rb return `''` when working_directory missing | Completed: already in commit 13d5f87; closed micro-cleanup (2026-03-19)
|
|
51
49
|
|
|
52
50
|
---
|
|
53
51
|
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
# AGENTS.md — AppyDave Tools / extract-vat-cli campaign
|
|
2
|
+
|
|
3
|
+
> Operational knowledge for every background agent. Self-contained — you receive only this file + your work unit prompt.
|
|
4
|
+
> Last updated: 2026-03-19 (extract-vat-cli campaign)
|
|
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
|
+
**Active campaign:** extract-vat-cli — extracting business logic from `bin/dam` (1,600-line God class) into proper library classes.
|
|
13
|
+
**Commits:** Trigger automated semantic versioning via GitHub Actions. Always use `kfeat`/`kfix` — never `git commit`.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## ⚠️ Pre-Commit Check (Mandatory Every Commit)
|
|
18
|
+
|
|
19
|
+
Before running `kfix`, always run:
|
|
20
|
+
```bash
|
|
21
|
+
git status
|
|
22
|
+
```
|
|
23
|
+
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.
|
|
24
|
+
|
|
25
|
+
**Why:** Prior campaign (micro-cleanup) accidentally staged a pre-existing uncommitted change in `lib/appydave/tools.rb` when running `kfix`. This required a follow-up fix commit.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Build & Run Commands
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Initialize rbenv (required if rbenv not in PATH)
|
|
33
|
+
eval "$(rbenv init -)"
|
|
34
|
+
|
|
35
|
+
# Run tests
|
|
36
|
+
bundle exec rspec # All tests
|
|
37
|
+
bundle exec rspec spec/path/to/file_spec.rb # Single file
|
|
38
|
+
RUBYOPT="-W0" bundle exec rspec # Suppress Ruby 3.4 platform warnings
|
|
39
|
+
|
|
40
|
+
# Lint
|
|
41
|
+
bundle exec rubocop --format clang # Standard lint check (matches CI)
|
|
42
|
+
|
|
43
|
+
# Commit (never use git commit directly)
|
|
44
|
+
kfeat "add feature description" # Minor version bump
|
|
45
|
+
kfix "fix bug description" # Patch version bump
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Baseline (2026-03-19):** 831 examples, 0 failures, ~85.92% line coverage
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Directory Structure
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
bin/ CLI scripts (development, .rb extension)
|
|
56
|
+
exe/ Thin wrappers for gem installation (no .rb extension)
|
|
57
|
+
lib/appydave/tools/
|
|
58
|
+
dam/ Digital Asset Management — main active area
|
|
59
|
+
brand_resolver.rb Centralizes ALL brand name transformations (appydave ↔ v-appydave)
|
|
60
|
+
errors.rb Custom exception hierarchy (DamError, BrandNotFoundError, etc.)
|
|
61
|
+
fuzzy_matcher.rb Levenshtein distance for "did you mean?" suggestions
|
|
62
|
+
git_helper.rb Extracted git command wrappers (current_branch, commits_ahead, etc.)
|
|
63
|
+
file_helper.rb File utility methods (calculate_directory_size, format_size, format_age)
|
|
64
|
+
config.rb Delegates brand resolution to BrandResolver; memoized Config loading
|
|
65
|
+
project_resolver.rb Project name resolution with regex pattern matching
|
|
66
|
+
project_listing.rb Table display for `dam list` command (format() for headers + data)
|
|
67
|
+
s3_operations.rb S3 upload/download/status with MD5 comparison
|
|
68
|
+
s3_scanner.rb S3 bucket scanner for s3-scan command
|
|
69
|
+
status.rb Project git/S3 status display
|
|
70
|
+
manifest_generator.rb Video project manifest
|
|
71
|
+
sync_from_ssd.rb SSD sync operations
|
|
72
|
+
ssd_status.rb SSD backup status
|
|
73
|
+
share_operations.rb Pre-signed URL generation
|
|
74
|
+
config_loader.rb Loads .video-tools.env per brand
|
|
75
|
+
repo_push.rb, repo_status.rb, repo_sync.rb
|
|
76
|
+
# NEW — being created in this campaign:
|
|
77
|
+
local_sync_status.rb [WU2] Enrich project data with local s3-staging sync status
|
|
78
|
+
s3_scan_command.rb [WU3] S3 scan orchestration + display (extracted from VatCLI)
|
|
79
|
+
s3_arg_parser.rb [WU4] CLI argument parsing for S3 commands (extracted from VatCLI)
|
|
80
|
+
lib/appydave/tools.rb Require file — ADD new dam files here after creating them
|
|
81
|
+
spec/
|
|
82
|
+
appydave/tools/dam/ One spec file per dam/ class
|
|
83
|
+
support/
|
|
84
|
+
dam_filesystem_helpers.rb Shared contexts for DAM filesystem testing
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## This Campaign: What We're Extracting from bin/dam
|
|
90
|
+
|
|
91
|
+
`bin/dam` is a 1,600-line `VatCLI` class with 20+ `rubocop-disable` comments. Four clusters of business logic are being extracted.
|
|
92
|
+
|
|
93
|
+
### Work Unit Dependencies
|
|
94
|
+
|
|
95
|
+
All 4 work units touch `bin/dam`. Run SEQUENTIALLY — never in parallel.
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
WU1: extract-format-bytes (no deps)
|
|
99
|
+
WU2: extract-local-sync-status (no deps — but WU3 depends on it)
|
|
100
|
+
WU3: extract-s3-scan-command (DEPENDS ON WU2 — uses LocalSyncStatus)
|
|
101
|
+
WU4: extract-s3-arg-parser (no deps on WU1-3 — run after WU3 for safety)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### WU1: extract-format-bytes
|
|
105
|
+
|
|
106
|
+
**What:** `VatCLI#format_bytes` is a duplicate of `FileHelper.format_size` (already exists, already tested).
|
|
107
|
+
|
|
108
|
+
**Change:**
|
|
109
|
+
- Replace 3 callers in `bin/dam` with `Appydave::Tools::Dam::FileHelper.format_size(x)`:
|
|
110
|
+
- Line 1510 in `display_s3_scan_table`
|
|
111
|
+
- Line 696 in `display_s3_files`
|
|
112
|
+
- Line 702 in `display_s3_files`
|
|
113
|
+
- Delete `format_bytes` method from VatCLI (lines 1586-1597, including rubocop-disable/enable wrapper)
|
|
114
|
+
- No new spec needed — `FileHelper.format_size` already has specs
|
|
115
|
+
|
|
116
|
+
**Done when:** rubocop 0 offenses, 831 examples passing, `format_bytes` gone from bin/dam.
|
|
117
|
+
|
|
118
|
+
### WU2: extract-local-sync-status
|
|
119
|
+
|
|
120
|
+
**What:** `add_local_sync_status!` and `format_local_status` are VatCLI private methods. They contain business logic (filesystem inspection + status classification) that belongs in a library class.
|
|
121
|
+
|
|
122
|
+
**Create** `lib/appydave/tools/dam/local_sync_status.rb`:
|
|
123
|
+
```ruby
|
|
124
|
+
# frozen_string_literal: true
|
|
125
|
+
|
|
126
|
+
module Appydave
|
|
127
|
+
module Tools
|
|
128
|
+
module Dam
|
|
129
|
+
# Enriches S3 scan project data with local s3-staging sync status
|
|
130
|
+
module LocalSyncStatus
|
|
131
|
+
module_function
|
|
132
|
+
|
|
133
|
+
# Mutates matched_projects hash to add :local_status key
|
|
134
|
+
# @param matched_projects [Hash] Map of project_id => S3 data
|
|
135
|
+
# @param brand_key [String] Brand key (e.g., 'appydave')
|
|
136
|
+
def enrich!(matched_projects, brand_key)
|
|
137
|
+
# (move add_local_sync_status! body here — change Config.project_path call to use Dam::Config)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Format local sync status for display
|
|
141
|
+
# @param status [Symbol] :synced, :no_files, :partial, :no_project
|
|
142
|
+
# @param local_count [Integer, nil] Number of local files
|
|
143
|
+
# @param s3_count [Integer] Number of S3 files
|
|
144
|
+
# @return [String] Formatted status string
|
|
145
|
+
def format(status, local_count, s3_count)
|
|
146
|
+
# (move format_local_status body here)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Update callers in bin/dam:**
|
|
155
|
+
- `scan_single_brand_s3`: replace `add_local_sync_status!(matched_projects, brand_key)` with `Appydave::Tools::Dam::LocalSyncStatus.enrich!(matched_projects, brand_key)`
|
|
156
|
+
- `display_s3_scan_table`: replace `format_local_status(data[:local_status], data[:local_file_count], data[:file_count])` with `Appydave::Tools::Dam::LocalSyncStatus.format(data[:local_status], data[:local_file_count], data[:file_count])`
|
|
157
|
+
|
|
158
|
+
**Register** in `lib/appydave/tools.rb` after line 79: `require 'appydave/tools/dam/local_sync_status'`
|
|
159
|
+
|
|
160
|
+
**Add spec** `spec/appydave/tools/dam/local_sync_status_spec.rb` — test `enrich!` with a temp filesystem (use `include_context 'with vat filesystem and brands'`), test `format` for all 4 status symbols.
|
|
161
|
+
|
|
162
|
+
**Done when:** rubocop 0 offenses, 832+ examples passing (new specs), `add_local_sync_status!` and `format_local_status` gone from bin/dam.
|
|
163
|
+
|
|
164
|
+
### WU3: extract-s3-scan-command
|
|
165
|
+
|
|
166
|
+
**What:** `scan_single_brand_s3`, `scan_all_brands_s3`, and `display_s3_scan_table` are 140 lines of orchestration + display logic in VatCLI. Extract to a new class.
|
|
167
|
+
|
|
168
|
+
**Prerequisite:** WU2 must be complete — `S3ScanCommand` uses `LocalSyncStatus.enrich!` and `LocalSyncStatus.format`.
|
|
169
|
+
|
|
170
|
+
**Create** `lib/appydave/tools/dam/s3_scan_command.rb` — class with:
|
|
171
|
+
- `scan_single(brand_key)` — body from `scan_single_brand_s3`
|
|
172
|
+
- `scan_all` — body from `scan_all_brands_s3`
|
|
173
|
+
- `display_table(matched_projects, orphaned_projects, bucket, prefix, region)` — body from `display_s3_scan_table` (private, called from scan_single)
|
|
174
|
+
|
|
175
|
+
**Update bin/dam:**
|
|
176
|
+
```ruby
|
|
177
|
+
def s3_scan_command(args)
|
|
178
|
+
all_brands = args.include?('--all')
|
|
179
|
+
args = args.reject { |arg| arg.start_with?('--') }
|
|
180
|
+
brand_arg = args[0]
|
|
181
|
+
|
|
182
|
+
if all_brands
|
|
183
|
+
Appydave::Tools::Dam::S3ScanCommand.new.scan_all
|
|
184
|
+
elsif brand_arg
|
|
185
|
+
Appydave::Tools::Dam::S3ScanCommand.new.scan_single(brand_arg)
|
|
186
|
+
else
|
|
187
|
+
# show usage (keep inline — 4 lines)
|
|
188
|
+
end
|
|
189
|
+
rescue StandardError => e
|
|
190
|
+
puts "❌ Error: #{e.message}"
|
|
191
|
+
puts e.backtrace.first(5).join("\n") if ENV['DEBUG']
|
|
192
|
+
exit 1
|
|
193
|
+
end
|
|
194
|
+
```
|
|
195
|
+
Delete `scan_single_brand_s3`, `scan_all_brands_s3`, `display_s3_scan_table` from VatCLI.
|
|
196
|
+
|
|
197
|
+
**Register** in `lib/appydave/tools.rb`: `require 'appydave/tools/dam/s3_scan_command'`
|
|
198
|
+
|
|
199
|
+
**Add spec** `spec/appydave/tools/dam/s3_scan_command_spec.rb` — smoke test that the class loads and methods exist; mock `S3Scanner` and `Config`; no full S3 integration needed.
|
|
200
|
+
|
|
201
|
+
**Done when:** rubocop 0 offenses, 832+ examples passing, 3 methods gone from bin/dam.
|
|
202
|
+
|
|
203
|
+
### WU4: extract-s3-arg-parser
|
|
204
|
+
|
|
205
|
+
**What:** `parse_s3_args`, `valid_brand?`, `parse_share_args`, `show_share_usage_and_exit`, `parse_discover_args` are 130 lines of argument parsing in VatCLI. All set `ENV['BRAND_PATH']` and share the same brand/project resolution chain.
|
|
206
|
+
|
|
207
|
+
**Create** `lib/appydave/tools/dam/s3_arg_parser.rb` — module with `module_function`:
|
|
208
|
+
- `parse_s3(args, command)` — from `parse_s3_args`
|
|
209
|
+
- `parse_share(args)` — from `parse_share_args`
|
|
210
|
+
- `parse_discover(args)` — from `parse_discover_args`
|
|
211
|
+
- `valid_brand?(brand_key)` — from `valid_brand?` (private helper, keep as module_function)
|
|
212
|
+
- `show_share_usage_and_exit` — from `show_share_usage_and_exit` (private helper)
|
|
213
|
+
|
|
214
|
+
**Update callers in bin/dam** (5 methods to update):
|
|
215
|
+
- `s3_up_command`, `s3_down_command`, `s3_status_command`, `s3_cleanup_remote_command`, `s3_cleanup_local_command`, `archive_command`: replace `parse_s3_args(args, '...')` with `Appydave::Tools::Dam::S3ArgParser.parse_s3(args, '...')`
|
|
216
|
+
- `s3_share_command`: replace `parse_share_args(args)` with `Appydave::Tools::Dam::S3ArgParser.parse_share(args)`
|
|
217
|
+
- `s3_discover_command`: replace `parse_discover_args(args)` with `Appydave::Tools::Dam::S3ArgParser.parse_discover(args)`
|
|
218
|
+
|
|
219
|
+
**Register** in `lib/appydave/tools.rb`: `require 'appydave/tools/dam/s3_arg_parser'`
|
|
220
|
+
|
|
221
|
+
**Add spec** `spec/appydave/tools/dam/s3_arg_parser_spec.rb` — test `parse_s3` with brand+project args, with PWD auto-detect (mock `ProjectResolver.detect_from_pwd`), with invalid brand; test `valid_brand?` with known brands.
|
|
222
|
+
|
|
223
|
+
**Done when:** rubocop 0 offenses, 833+ examples passing, 5 methods gone from bin/dam.
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Success Criteria
|
|
228
|
+
|
|
229
|
+
Every work unit must satisfy ALL of the following before marking `[x]`:
|
|
230
|
+
|
|
231
|
+
- [ ] `RUBYOPT="-W0" bundle exec rspec` — 831+ examples, 0 failures
|
|
232
|
+
- [ ] `bundle exec rubocop --format clang` — 0 offenses
|
|
233
|
+
- [ ] Line coverage stays ≥ 85.92%
|
|
234
|
+
- [ ] Any new `.rb` files start with `# frozen_string_literal: true`
|
|
235
|
+
- [ ] New class/module registered in `lib/appydave/tools.rb`
|
|
236
|
+
- [ ] At least 1 spec for each new library file
|
|
237
|
+
- [ ] Extracted methods removed from VatCLI in `bin/dam`
|
|
238
|
+
- [ ] `git status` confirmed clean before `kfix`
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Reference Patterns
|
|
243
|
+
|
|
244
|
+
### Shared Context for DAM Specs — THE STANDARD PATTERN
|
|
245
|
+
|
|
246
|
+
```ruby
|
|
247
|
+
# spec/appydave/tools/dam/some_class_spec.rb
|
|
248
|
+
# frozen_string_literal: true
|
|
249
|
+
|
|
250
|
+
require 'spec_helper'
|
|
251
|
+
|
|
252
|
+
RSpec.describe Appydave::Tools::Dam::SomeClass do
|
|
253
|
+
include_context 'with vat filesystem and brands', brands: %w[appydave voz]
|
|
254
|
+
|
|
255
|
+
before do
|
|
256
|
+
FileUtils.mkdir_p(File.join(appydave_path, 'b65-test-project'))
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
describe '.some_method' do
|
|
260
|
+
it 'does the thing' do
|
|
261
|
+
expect(described_class.some_method('appydave')).to eq('expected')
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**Available from shared context:**
|
|
268
|
+
- `temp_folder` — temp root (auto-cleaned after each example)
|
|
269
|
+
- `projects_root` — `/path/to/temp/video-projects`
|
|
270
|
+
- `appydave_path`, `voz_path`, etc. — brand dirs (created on demand)
|
|
271
|
+
- `SettingsConfig#video_projects_root` is mocked to return `projects_root`
|
|
272
|
+
|
|
273
|
+
### FileHelper — Use These, Don't Duplicate
|
|
274
|
+
|
|
275
|
+
```ruby
|
|
276
|
+
Appydave::Tools::Dam::FileHelper.format_size(bytes) # "1.5 GB"
|
|
277
|
+
Appydave::Tools::Dam::FileHelper.calculate_directory_size(path) # Integer bytes
|
|
278
|
+
Appydave::Tools::Dam::FileHelper.format_age(time) # "3d", "2w", etc.
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### BrandResolver — All Brand Name Transformations
|
|
282
|
+
|
|
283
|
+
```ruby
|
|
284
|
+
BrandResolver.expand('appydave') # => 'v-appydave'
|
|
285
|
+
BrandResolver.expand('ad') # => 'v-appydave' (shortcut)
|
|
286
|
+
BrandResolver.normalize('v-appydave') # => 'appydave'
|
|
287
|
+
BrandResolver.validate('appydave') # => 'appydave' or raises BrandNotFoundError
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Typed Exception Pattern
|
|
291
|
+
|
|
292
|
+
```ruby
|
|
293
|
+
raise ProjectNotFoundError, 'Project name is required' if project_hint.nil?
|
|
294
|
+
raise BrandNotFoundError.new(brand, available_brands, fuzzy_suggestions)
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## Anti-Patterns to Avoid
|
|
300
|
+
|
|
301
|
+
- ❌ Inline brand transformations — never write `"v-#{brand}"` outside BrandResolver
|
|
302
|
+
- ❌ `format_bytes` — does not exist anymore; use `FileHelper.format_size`
|
|
303
|
+
- ❌ Duplicating `format_size` / byte formatting logic — use `FileHelper.format_size`
|
|
304
|
+
- ❌ Mocking Config class methods in DAM specs — use shared filesystem context instead
|
|
305
|
+
- ❌ Multiple `before` blocks in same RSpec context — merge them (triggers RSpec/ScatteredSetup)
|
|
306
|
+
- ❌ `$?` for subprocess status — use `$CHILD_STATUS` (rubocop Style/SpecialGlobalVars)
|
|
307
|
+
- ❌ `raise 'string error'` in DAM module — use typed exceptions from `errors.rb`
|
|
308
|
+
- ❌ `include FileUtils` — use dam's `FileHelper` instead
|
|
309
|
+
- ❌ Hardcoded header strings for table output — always use `format()` matching data row format
|
|
310
|
+
- ❌ Adding new `Config.configure` calls — memoized but called redundantly; don't spread further
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Mock Patterns
|
|
315
|
+
|
|
316
|
+
### ENV Stubbing (if needed in specs)
|
|
317
|
+
|
|
318
|
+
```ruby
|
|
319
|
+
allow(ENV).to receive(:[]).and_call_original
|
|
320
|
+
allow(ENV).to receive(:[]).with('BRAND_PATH').and_return('/tmp/test/v-appydave')
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**Do NOT use climate_control gem** — project doesn't have it.
|
|
324
|
+
|
|
325
|
+
### External Services
|
|
326
|
+
|
|
327
|
+
```ruby
|
|
328
|
+
# S3 calls
|
|
329
|
+
stub_request(:get, /s3\.amazonaws\.com/).to_return(body: '...')
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Quality Gates
|
|
335
|
+
|
|
336
|
+
- **Tests:** `RUBYOPT="-W0" bundle exec rspec` — 831+ examples, 0 failures
|
|
337
|
+
- **Lint:** `bundle exec rubocop --format clang` — 0 offenses (CI will reject)
|
|
338
|
+
- **Coverage:** ≥ 85.92% line coverage
|
|
339
|
+
- **frozen_string_literal:** Required on every new `.rb` file
|
|
340
|
+
- **Commit format:** `kfeat`/`kfix` only — triggers semantic versioning + CI wait
|
|
341
|
+
- **Pre-commit:** Always run `git status` before `kfix` — confirm staged files
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Learnings
|
|
346
|
+
|
|
347
|
+
### From DAM Enhancement Sprint (Jan 2025)
|
|
348
|
+
|
|
349
|
+
- **BrandResolver is the critical path.** All `dam` commands flow through it. Any change to brand resolution must be tested with all shortcuts.
|
|
350
|
+
- **`Regexp.last_match` is reset by `.sub()` calls.** Always capture regex groups BEFORE any string transformation.
|
|
351
|
+
- **`Config.configure` is memoized but called redundantly.** Don't add new calls.
|
|
352
|
+
- **Table format() pattern is non-obvious.** Headers misaligned 3 times in UAT. Always verify with real data.
|
|
353
|
+
|
|
354
|
+
### From micro-cleanup (2026-03-19)
|
|
355
|
+
|
|
356
|
+
- **Dirty working tree + kfix = accidental staging.** Always run `git status` before committing. micro-cleanup accidentally staged a pre-existing `lib/appydave/tools.rb` change.
|
|
357
|
+
- **Pre-existing "already fixed" items:** Check B-items aren't already done before acting (B031, B033 were already committed; B015, B019 also).
|
|
358
|
+
|
|
359
|
+
### From Architectural Review (2026-03-19)
|
|
360
|
+
|
|
361
|
+
- **bin/dam is the primary DAM CLI entry point.** Regressions here affect real workflows. Test every command path after extraction.
|
|
362
|
+
- **`ENV['BRAND_PATH']` is set in 5 places in bin/dam.** Three are in parse methods (being extracted). Two remain in `generate_single_manifest` and `sync_ssd_command` — out of scope.
|
|
363
|
+
- **Do NOT attempt B020 (split S3Operations) in this campaign.** Different class, different risk profile.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# IMPLEMENTATION_PLAN.md — extract-vat-cli
|
|
2
|
+
|
|
3
|
+
**Goal**: Extract 4 clusters of business logic from `bin/dam` (1,600-line VatCLI God class) into proper library classes. Prerequisite for parallelism (B007) and S3Operations split (B020).
|
|
4
|
+
**Started**: 2026-03-19
|
|
5
|
+
**Target**: All 4 complete; 831+ examples passing; rubocop 0 offenses; no regressions
|
|
6
|
+
|
|
7
|
+
## Summary
|
|
8
|
+
- Total: 4 | Complete: 0 | In Progress: 0 | Pending: 4 | Failed: 0
|
|
9
|
+
|
|
10
|
+
## Pending
|
|
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
|
+
- [~] extract-local-sync-status — Extract `add_local_sync_status!` + `format_local_status` → new `LocalSyncStatus` module; add spec; update callers in bin/dam
|
|
13
|
+
- [ ] extract-s3-scan-command — Extract `scan_single_brand_s3` + `scan_all_brands_s3` + `display_s3_scan_table` → new `S3ScanCommand` class; add spec; **depends on extract-local-sync-status completing first**
|
|
14
|
+
- [ ] extract-s3-arg-parser — Extract `parse_s3_args` + `valid_brand?` + `parse_share_args` + `show_share_usage_and_exit` + `parse_discover_args` → new `S3ArgParser` class; add spec
|
|
15
|
+
|
|
16
|
+
## In Progress
|
|
17
|
+
|
|
18
|
+
## Complete
|
|
19
|
+
|
|
20
|
+
## Failed / Needs Retry
|
|
21
|
+
|
|
22
|
+
## Notes & Decisions
|
|
23
|
+
|
|
24
|
+
### Sequencing
|
|
25
|
+
All 4 work units touch `bin/dam` — they MUST run sequentially, not in parallel. Wave size = 1.
|
|
26
|
+
Sequence: extract-format-bytes → extract-local-sync-status → extract-s3-scan-command → extract-s3-arg-parser
|
|
27
|
+
`extract-s3-scan-command` depends on `LocalSyncStatus` created in `extract-local-sync-status`.
|
|
28
|
+
|
|
29
|
+
### New files to create
|
|
30
|
+
- `lib/appydave/tools/dam/local_sync_status.rb`
|
|
31
|
+
- `lib/appydave/tools/dam/s3_scan_command.rb`
|
|
32
|
+
- `lib/appydave/tools/dam/s3_arg_parser.rb`
|
|
33
|
+
|
|
34
|
+
### Register new files
|
|
35
|
+
Add require lines to `lib/appydave/tools.rb` after line 79 (after `repo_push`):
|
|
36
|
+
```ruby
|
|
37
|
+
require 'appydave/tools/dam/local_sync_status'
|
|
38
|
+
require 'appydave/tools/dam/s3_scan_command'
|
|
39
|
+
require 'appydave/tools/dam/s3_arg_parser'
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### format_bytes caller locations in bin/dam
|
|
43
|
+
- Line 1510: `display_s3_scan_table` → `format_bytes(data[:total_bytes])`
|
|
44
|
+
- Line 696: `display_s3_files` → `format_bytes(size)`
|
|
45
|
+
- Line 702: `display_s3_files` → `format_bytes(total_bytes)`
|
|
46
|
+
Replace all with: `Appydave::Tools::Dam::FileHelper.format_size(x)`
|
|
47
|
+
|
|
48
|
+
### FileHelper.format_size vs format_bytes
|
|
49
|
+
Both return identical output. `format_size` uses named format tokens (`%<size>.1f`) which satisfies rubocop Style/FormatStringToken. `format_bytes` used positional tokens (`%.1f`) — hence the rubocop-disable. After extraction, the disable comment goes away.
|
|
50
|
+
|
|
51
|
+
### parse_share_args scope decision
|
|
52
|
+
Included `parse_share_args` + `show_share_usage_and_exit` + `parse_discover_args` in S3ArgParser (same ENV['BRAND_PATH'] pattern, same brand/project resolution chain). All 5 parser methods share identical structure and belong together.
|
|
53
|
+
|
|
54
|
+
### ENV['BRAND_PATH'] residuals
|
|
55
|
+
After S3ArgParser extraction, 2 ENV['BRAND_PATH'] assignments remain in VatCLI:
|
|
56
|
+
- `generate_single_manifest` (line 278)
|
|
57
|
+
- `sync_ssd_command` (line 325)
|
|
58
|
+
These are out of scope — leave for a future campaign.
|
|
59
|
+
|
|
60
|
+
### Do NOT attempt B020 (S3Operations split) in this campaign
|
|
61
|
+
Scope is VatCLI extraction only. S3Operations refactor is a separate campaign.
|
|
@@ -5,14 +5,16 @@
|
|
|
5
5
|
**Target**: All 3 complete; 830+ examples passing; rubocop clean; no regressions
|
|
6
6
|
|
|
7
7
|
## Summary
|
|
8
|
-
- Total: 3 | Complete:
|
|
8
|
+
- Total: 3 | Complete: 3 | In Progress: 0 | Pending: 0 | Failed: 0
|
|
9
9
|
|
|
10
10
|
## Pending
|
|
11
11
|
|
|
12
12
|
## In Progress
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- [
|
|
13
|
+
|
|
14
|
+
## Complete
|
|
15
|
+
- [x] fix-b031 — already committed in prior pass (commit 8eec40c). Assertion `expect(location[:type]).to eq('tool')` found at add_spec.rb line 51. Closed without action.
|
|
16
|
+
- [x] fix-b033 — already fixed in commit 13d5f87 (`return '' unless` already in place). Closed without action.
|
|
17
|
+
- [x] fix-b032 — added `-f json` subprocess test to cli_spec. 831 examples, 0 failures, v0.76.4. Side issue: kfix accidentally staged pre-existing uncommitted changes to lib/appydave/tools.rb (removed youtube_automation_config require). Fixed in follow-up commit.
|
|
16
18
|
|
|
17
19
|
## Complete
|
|
18
20
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Assessment: micro-cleanup
|
|
2
|
+
|
|
3
|
+
**Campaign**: micro-cleanup
|
|
4
|
+
**Date**: 2026-03-19 → 2026-03-19
|
|
5
|
+
**Results**: 3 complete, 0 failed
|
|
6
|
+
**Version shipped**: v0.76.4
|
|
7
|
+
**Quality audit**: code-quality-audit + test-quality-audit run post-campaign
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Results Summary
|
|
12
|
+
|
|
13
|
+
| Work Unit | Action | Notes |
|
|
14
|
+
|-----------|--------|-------|
|
|
15
|
+
| fix-b031 | Already done | `expect(location[:type]).to eq('tool')` found committed in 8eec40c — closed without action |
|
|
16
|
+
| fix-b033 | Already done | `return '' unless` on line 19 found committed in 13d5f87 — closed without action |
|
|
17
|
+
| fix-b032 | +1 example | `-f json` subprocess test added to cli_spec. 831 examples, 0 failures, v0.76.4 |
|
|
18
|
+
|
|
19
|
+
**830 → 831 examples (+1). Coverage stable at ~85.92%.**
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## What Worked Well
|
|
24
|
+
|
|
25
|
+
- **Agents check before acting.** Both B031 and B033 agents read the files, found the work already done, and correctly closed without creating duplicate commits. No false-positive churn.
|
|
26
|
+
- **B032 json test is clean.** `Dir.mktmpdir` + subprocess + `JSON.parse` + key assertions — follows the established cli_spec pattern exactly.
|
|
27
|
+
- **CI caught the staging error immediately.** The accidental require removal was detected by CI on the first push — the safety net worked as intended.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## What Didn't Work
|
|
32
|
+
|
|
33
|
+
**kfix staged pre-existing uncommitted changes.**
|
|
34
|
+
|
|
35
|
+
The B032 agent's `kfix` commit accidentally included local changes to `lib/appydave/tools.rb` that deleted `require 'appydave/tools/configuration/models/youtube_automation_config'` and related lines. These were pre-existing uncommitted modifications floating in the working tree — unrelated to fix-b032. CI failed. A follow-up commit restored the requires and CI passed.
|
|
36
|
+
|
|
37
|
+
**Root cause:** A prior session left the working tree in a dirty state. The agent did not run `git status` before committing, so it didn't catch the unintended changes in the staging area.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Key Learnings — Application
|
|
42
|
+
|
|
43
|
+
- **Run `git status` before `kfix`.** Agents must check what's actually staged before committing. If unexpected files appear, abort and investigate. Add this to AGENTS.md as a mandatory pre-commit step.
|
|
44
|
+
- **kfix commits everything staged** — it does not limit itself to files the agent touched. A dirty working tree is a silent risk on every campaign.
|
|
45
|
+
- **B033 and B031 were already closed by prior agents.** This confirms the prior campaign agents were doing thorough work — they fixed things beyond their assigned scope. Good behaviour, but worth tracking so plans don't redundantly assign already-done work.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## New Backlog Items
|
|
50
|
+
|
|
51
|
+
None. The quality audit found no new gaps. Suite is at B+ for GptContext CLI, B overall.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Suggestions for Next Campaign
|
|
56
|
+
|
|
57
|
+
**Start B011 — extract VatCLI from bin/dam.**
|
|
58
|
+
|
|
59
|
+
The test suite is at B grade (75-80% regression catch rate). The production code is clean. The CI pipeline is reliable. All prerequisites for architectural work are met.
|
|
60
|
+
|
|
61
|
+
**Pre-campaign mandatory steps for B011:**
|
|
62
|
+
1. Run `git status` — confirm working tree is clean before launching agents
|
|
63
|
+
2. Read `bin/dam` in full before writing AGENTS.md — 1,600 lines, surprises expected
|
|
64
|
+
3. Run rubocop on bin/dam to count current offenses (20+ rubocop-disable comments)
|
|
65
|
+
4. Baseline: 831 examples, 0 failures — confirm before starting
|
|
66
|
+
|
|
67
|
+
**Add to AGENTS.md for B011:**
|
|
68
|
+
> Before running `kfix`, always run `git status` and confirm only the expected files appear in the staged/unstaged list. If unexpected files appear, run `git diff` to investigate before committing.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Appydave
|
|
4
|
+
module Tools
|
|
5
|
+
module Dam
|
|
6
|
+
# Enriches S3 scan project data with local s3-staging sync status
|
|
7
|
+
module LocalSyncStatus
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
# Mutates matched_projects hash to add :local_status and :local_file_count keys
|
|
11
|
+
# @param matched_projects [Hash] Map of project_id => S3 data hash
|
|
12
|
+
# @param brand_key [String] Brand key (e.g., 'appydave')
|
|
13
|
+
def enrich!(matched_projects, brand_key)
|
|
14
|
+
matched_projects.each do |project_id, data|
|
|
15
|
+
project_path = Appydave::Tools::Dam::Config.project_path(brand_key, project_id)
|
|
16
|
+
s3_staging_path = File.join(project_path, 's3-staging')
|
|
17
|
+
|
|
18
|
+
if !Dir.exist?(project_path)
|
|
19
|
+
data[:local_status] = :no_project # Project directory doesn't exist
|
|
20
|
+
elsif !Dir.exist?(s3_staging_path)
|
|
21
|
+
data[:local_status] = :no_files # Project exists but no downloads yet
|
|
22
|
+
else
|
|
23
|
+
# Count local files in s3-staging
|
|
24
|
+
local_files = Dir.glob(File.join(s3_staging_path, '**', '*'))
|
|
25
|
+
.select { |f| File.file?(f) }
|
|
26
|
+
.reject { |f| File.basename(f).include?('Zone.Identifier') } # Exclude Windows metadata
|
|
27
|
+
|
|
28
|
+
s3_file_count = data[:file_count]
|
|
29
|
+
local_file_count = local_files.size
|
|
30
|
+
|
|
31
|
+
data[:local_status] = if local_file_count.zero?
|
|
32
|
+
:no_files
|
|
33
|
+
elsif local_file_count == s3_file_count
|
|
34
|
+
:synced # Fully synced
|
|
35
|
+
else
|
|
36
|
+
:partial # Some files downloaded
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
data[:local_file_count] = local_file_count
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Format local sync status symbol for display
|
|
45
|
+
# @param status [Symbol] :synced, :no_files, :partial, :no_project
|
|
46
|
+
# @param local_count [Integer, nil] Number of local files
|
|
47
|
+
# @param s3_count [Integer] Number of S3 files
|
|
48
|
+
# @return [String] Formatted status string
|
|
49
|
+
def format(status, local_count, s3_count)
|
|
50
|
+
case status
|
|
51
|
+
when :synced
|
|
52
|
+
'✓ Synced'
|
|
53
|
+
when :no_files
|
|
54
|
+
'⚠ None'
|
|
55
|
+
when :partial
|
|
56
|
+
"⚠ #{local_count}/#{s3_count}"
|
|
57
|
+
when :no_project
|
|
58
|
+
'✗ Missing'
|
|
59
|
+
else
|
|
60
|
+
'Unknown'
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/appydave/tools.rb
CHANGED
|
@@ -78,6 +78,7 @@ require 'appydave/tools/dam/ssd_status'
|
|
|
78
78
|
require 'appydave/tools/dam/repo_status'
|
|
79
79
|
require 'appydave/tools/dam/repo_sync'
|
|
80
80
|
require 'appydave/tools/dam/repo_push'
|
|
81
|
+
require 'appydave/tools/dam/local_sync_status'
|
|
81
82
|
|
|
82
83
|
require 'appydave/tools/jump/path_validator'
|
|
83
84
|
require 'appydave/tools/jump/location'
|
|
@@ -96,8 +97,6 @@ require 'appydave/tools/jump/commands/report'
|
|
|
96
97
|
require 'appydave/tools/jump/commands/generate'
|
|
97
98
|
require 'appydave/tools/jump/cli'
|
|
98
99
|
|
|
99
|
-
require 'appydave/tools/youtube_automation/gpt_agent'
|
|
100
|
-
|
|
101
100
|
require 'appydave/tools/youtube_manager/models/youtube_details'
|
|
102
101
|
require 'appydave/tools/youtube_manager/models/captions'
|
|
103
102
|
require 'appydave/tools/youtube_manager/youtube_base'
|
data/package.json
CHANGED
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.
|
|
4
|
+
version: 0.76.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Cruwys
|
|
@@ -300,6 +300,8 @@ files:
|
|
|
300
300
|
- docs/planning/bugfix-and-security/AGENTS.md
|
|
301
301
|
- docs/planning/bugfix-and-security/IMPLEMENTATION_PLAN.md
|
|
302
302
|
- docs/planning/bugfix-and-security/assessment.md
|
|
303
|
+
- docs/planning/extract-vat-cli/AGENTS.md
|
|
304
|
+
- docs/planning/extract-vat-cli/IMPLEMENTATION_PLAN.md
|
|
303
305
|
- docs/planning/final-test-gaps/AGENTS.md
|
|
304
306
|
- docs/planning/final-test-gaps/IMPLEMENTATION_PLAN.md
|
|
305
307
|
- docs/planning/final-test-gaps/assessment.md
|
|
@@ -308,6 +310,7 @@ files:
|
|
|
308
310
|
- docs/planning/fr2-gpt-context-help/assessment.md
|
|
309
311
|
- docs/planning/micro-cleanup/AGENTS.md
|
|
310
312
|
- docs/planning/micro-cleanup/IMPLEMENTATION_PLAN.md
|
|
313
|
+
- docs/planning/micro-cleanup/assessment.md
|
|
311
314
|
- docs/planning/test-coverage-gaps/AGENTS.md
|
|
312
315
|
- docs/planning/test-coverage-gaps/IMPLEMENTATION_PLAN.md
|
|
313
316
|
- docs/planning/test-coverage-gaps/assessment.md
|
|
@@ -351,6 +354,7 @@ files:
|
|
|
351
354
|
- lib/appydave/tools/dam/file_helper.rb
|
|
352
355
|
- lib/appydave/tools/dam/fuzzy_matcher.rb
|
|
353
356
|
- lib/appydave/tools/dam/git_helper.rb
|
|
357
|
+
- lib/appydave/tools/dam/local_sync_status.rb
|
|
354
358
|
- lib/appydave/tools/dam/manifest_generator.rb
|
|
355
359
|
- lib/appydave/tools/dam/project_listing.rb
|
|
356
360
|
- lib/appydave/tools/dam/project_resolver.rb
|