appydave-tools 0.75.0 → 0.76.1

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: e45ff57b507a705f826281a7d361abf0724d719eaf1c1ba4795198f3a4551a7f
4
- data.tar.gz: edfc8fcdb775c290d9c59a588215de3e2c613075ee49332836bf19de92a5126b
3
+ metadata.gz: 2700178e0f8e7fa1ddbd046cb78af4a0a340f2e8770637a2f3b30229e9f76179
4
+ data.tar.gz: c5c3f8094e988cc77849f27d1126c3839825d231c06b8bb63d3410cfdfe72179
5
5
  SHA512:
6
- metadata.gz: d5d45772ed9304ac22cca51417c0d48b0d176d824933feaafbc4a34c50ff8585bbce93a4f423ecadc39ac64aeca5e0e2724556a1407c428a318fa86926a65899
7
- data.tar.gz: ab16611805c302aeb6fe1930e3502d4330cb00833bd282e2631e9cbb25f6fc179eb66c7e6cd4535a89005cd743cc0db6c3e9eaaaeea0a22c671274e5b6f9b43d
6
+ metadata.gz: e11cda4ffd9a0a555c15ae613e94b31aa7f508ba473ccc89d31a48d399f9b603204faa4018bea222f9640205b5d73a557b992acf3be94c926bdbeea494baedb5
7
+ data.tar.gz: 4af0c479f1aeb82cc32dd06d8d954910fff6e9a4a2f785d2969a284fc9a55874a56ed71e1600be9c26b5ad3792a67be98105a9003b82c8d835eb0bd8a7d323cf
data/CHANGELOG.md CHANGED
@@ -1,3 +1,28 @@
1
+ # [0.76.0](https://github.com/appydave/appydave-tools/compare/v0.75.0...v0.76.0) (2026-03-19)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * fix pre-existing rubocop offenses in bin/dam and split jump test helpers to avoid OneClassPerFile ([491b980](https://github.com/appydave/appydave-tools/commit/491b980c335b7e3d205a5368cd0a7b96e5b5a82f))
7
+ * remove debug puts and fix unguarded FileUtils.cd in FileCollector; close BUG-1 ([13d5f87](https://github.com/appydave/appydave-tools/commit/13d5f8793d5185cb9795cab86a5270726763c30e))
8
+
9
+
10
+ ### Features
11
+
12
+ * add AI-friendly help system to GPT Context ([c0b8843](https://github.com/appydave/appydave-tools/commit/c0b884323654a248d660ea137712e7c0e5fc172c))
13
+
14
+ # [0.75.0](https://github.com/appydave/appydave-tools/compare/v0.74.1...v0.75.0) (2026-02-08)
15
+
16
+
17
+ ### Bug Fixes
18
+
19
+ * fix RuboCop violations in jump report commands ([261404e](https://github.com/appydave/appydave-tools/commit/261404ed5cc9e3b93c64db30548728992b353355))
20
+
21
+
22
+ ### Features
23
+
24
+ * add --limit and --skip-unassigned flags to jump report commands ([d0ff2ae](https://github.com/appydave/appydave-tools/commit/d0ff2aeae4edffc81448e62502c3974a0942559f))
25
+
1
26
  ## [0.74.1](https://github.com/appydave/appydave-tools/compare/v0.74.0...v0.74.1) (2026-02-04)
2
27
 
3
28
 
data/CLAUDE.md CHANGED
@@ -2,34 +2,13 @@
2
2
 
3
3
  This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
4
 
5
- ## ⚠️ PENDING: Quality Assurance Process Required
5
+ ## COMPLETE: DAM Enhancement Sprint QA (2025-01-22)
6
6
 
7
- **Action Required:** Two-phase validation for 75-commit DAM enhancement sprint
7
+ Two-phase validation for the 75-commit DAM enhancement sprint (9e49668 → 4228b51) is complete.
8
8
 
9
- ### Phase 1: Behavioral Regression Audit (MUST RUN FIRST)
10
-
11
- - **Instruction Guide:** [docs/ai-instructions/behavioral-regression-audit.md](./docs/ai-instructions/behavioral-regression-audit.md)
12
- - **Commit Range:** `9e49668` (baseline - known working) → `4228b51` (current - unknown state)
13
- - **Purpose:** Verify core functionality unchanged except for intentional UX improvements
14
- - **Method:** Two-snapshot comparison (not 75 individual commits)
15
- - **5 Phases:** Change inventory → Critical path → Behavioral testing → Logic diff → Spot-check
16
- - **Output:** `docs/code-quality/behavioral-audit-2025-01-22.md`
17
- - **Status:** Ready to execute - awaiting user confirmation
18
- - **Priority:** CRITICAL - Must complete before UAT
19
-
20
- ### Phase 2: User Acceptance Testing (RUN AFTER REGRESSION AUDIT)
21
-
22
- - **UAT Plan:** [docs/code-quality/uat-plan-2025-01-22.md](./docs/code-quality/uat-plan-2025-01-22.md)
23
- - **Test Suites:** 4 suites, 20 tests (Core functionality, New features, Edge cases, Performance)
24
- - **Purpose:** Validate final state after regression fixes
25
- - **Status:** Ready to execute - run after Phase 1 completes
26
- - **Priority:** High - Final validation before release
27
-
28
- ### Execution Order
29
-
30
- 1. **Behavioral Regression Audit** → Identify and fix any breaking changes
31
- 2. **User Acceptance Testing** → Validate everything works as designed
32
- 3. **Ship it** → Merge/release with confidence
9
+ - **Phase 1 Behavioral Regression Audit:** SAFE — 0 regressions, 15+ UX improvements validated. See [docs/code-quality/behavioral-audit-2025-01-22.md](./docs/code-quality/behavioral-audit-2025-01-22.md)
10
+ - **Phase 2 — UAT:** ✅ PASSED — 20/20 tests passed, 4 minor formatting issues found and fixed inline. See [docs/code-quality/uat-report-2025-01-22.md](./docs/code-quality/uat-report-2025-01-22.md)
11
+ - **Current baseline:** 748 examples, 0 failures, 84.88% line coverage
33
12
 
34
13
  **Also Available:** Code quality retrospective analysis guide at [docs/ai-instructions/code-quality-retrospective.md](./docs/ai-instructions/code-quality-retrospective.md)
35
14
 
data/bin/dam CHANGED
@@ -297,8 +297,7 @@ class VatCLI
297
297
  puts '📋 Summary - Generated Manifests:'
298
298
  puts ''
299
299
 
300
- successful = results.select { |r| r[:success] }
301
- failed = results.reject { |r| r[:success] }
300
+ successful, failed = results.partition { |r| r[:success] }
302
301
 
303
302
  successful.each do |result|
304
303
  brand_display = result[:brand].ljust(15)
@@ -1567,8 +1566,7 @@ class VatCLI
1567
1566
  puts '📋 Summary - S3 Scans:'
1568
1567
  puts ''
1569
1568
 
1570
- successful = results.select { |r| r[:success] }
1571
- failed = results.reject { |r| r[:success] }
1569
+ successful, failed = results.partition { |r| r[:success] }
1572
1570
 
1573
1571
  successful.each do |result|
1574
1572
  brand_display = result[:brand].ljust(15)
data/bin/gpt_context.rb CHANGED
@@ -19,57 +19,100 @@ options = Appydave::Tools::GptContext::Options.new(
19
19
  )
20
20
 
21
21
  OptionParser.new do |opts|
22
- opts.banner = 'Usage: gather_content.rb [options]'
22
+ opts.banner = <<~BANNER
23
+ GPT Context Gatherer - Collect project files for AI context
23
24
 
24
- opts.on('-i', '--include PATTERN', 'Pattern or file to include (can be used multiple times)') do |pattern|
25
+ SYNOPSIS
26
+ gpt_context [options]
27
+
28
+ DESCRIPTION
29
+ Collects and packages codebase files for AI assistant context.
30
+ Outputs to clipboard (default), file, or stdout.
31
+
32
+ BANNER
33
+
34
+ opts.separator 'OPTIONS'
35
+ opts.separator ''
36
+
37
+ opts.on('-i', '--include PATTERN',
38
+ 'Glob pattern for files to include (repeatable)',
39
+ 'Example: -i "lib/**/*.rb" -i "bin/**/*.rb"') do |pattern|
25
40
  options.include_patterns << pattern
26
41
  end
27
42
 
28
- opts.on('-e', '--exclude PATTERN', 'Pattern or file to exclude (can be used multiple times)') do |pattern|
43
+ opts.on('-e', '--exclude PATTERN',
44
+ 'Glob pattern for files to exclude (repeatable)',
45
+ 'Example: -e "spec/**/*" -e "node_modules/**/*"') do |pattern|
29
46
  options.exclude_patterns << pattern
30
47
  end
31
48
 
32
- opts.on('-f', '--format FORMAT', 'Output format: content, tree, or json, if not provided then both are used') do |format|
49
+ opts.on('-f', '--format FORMATS',
50
+ 'Output format(s): tree, content, json, aider, files',
51
+ 'Comma-separated. Default: content',
52
+ 'Example: -f tree,content') do |format|
33
53
  options.format = format
34
54
  end
35
55
 
36
- opts.on('-l', '--line-limit LIMIT', 'Limit the number of lines included from each file') do |limit|
37
- options.line_limit = limit.to_i
56
+ opts.on('-l', '--line-limit N', Integer,
57
+ 'Limit lines per file (default: unlimited)') do |n|
58
+ options.line_limit = n
38
59
  end
39
60
 
40
- # New option for specifying base directory
41
- opts.on('-b', '--base-dir DIRECTORY', 'Set the base directory to gather files from') do |directory|
61
+ opts.on('-b', '--base-dir DIRECTORY',
62
+ 'Set the base directory to gather files from') do |directory|
42
63
  options.working_directory = directory
43
64
  end
44
65
 
45
- # Debug output options
46
- opts.on('-d', '--debug [MODE]', 'Enable debug mode [none, info, params, debug]', 'none', 'info', 'params', 'debug') do |debug|
66
+ opts.on('-d', '--debug [MODE]', 'Enable debug mode [none, info, params, debug]',
67
+ 'none', 'info', 'params', 'debug') do |debug|
47
68
  options.debug = debug || 'info'
48
69
  end
49
70
 
50
- # Output targets: clipboard or file
51
- opts.on('-o', '--output TARGET', 'Output target: clipboard, or a file path (can be used multiple times)') do |target|
71
+ opts.on('-o', '--output TARGET',
72
+ 'Output target: clipboard, filename, or stdout',
73
+ 'Default: clipboard. Repeatable for multiple targets.') do |target|
52
74
  options.output_target << target
53
75
  end
54
76
 
55
- opts.on('-p', '--prompt MESSAGE', 'Message/prompt to include in aider format output') do |message|
77
+ opts.on('-p', '--prompt TEXT',
78
+ 'Prompt text for aider format output') do |message|
56
79
  options.prompt = message
57
80
  end
58
81
 
59
- opts.on_tail('-h', '--help', 'Show this message') do
60
- puts opts
61
- puts "\nExamples:"
62
- puts " #{File.basename($PROGRAM_NAME)} -i 'lib/**/*.rb' -e 'lib/excluded/**/*.rb' -d"
63
- puts " #{File.basename($PROGRAM_NAME)} --include 'src/**/*.js' --exclude 'src/vendor/**/*.js'"
82
+ opts.separator ''
83
+ opts.separator 'OUTPUT FORMATS'
84
+ opts.separator ' tree - Directory tree structure'
85
+ opts.separator ' content - File contents with headers (default)'
86
+ opts.separator ' json - Structured JSON output'
87
+ opts.separator ' aider - Aider CLI command format (requires -p)'
88
+ opts.separator ' files - File paths only'
89
+ opts.separator ''
90
+ opts.separator 'EXAMPLES'
91
+ opts.separator ' # Gather Ruby library code for AI context'
92
+ opts.separator " gpt_context -i 'lib/**/*.rb' -e 'spec/**/*' -d"
93
+ opts.separator ''
94
+ opts.separator ' # Project structure overview'
95
+ opts.separator " gpt_context -i '**/*' -f tree -e 'node_modules/**/*'"
96
+ opts.separator ''
97
+ opts.separator ' # Save to file with tree and content'
98
+ opts.separator " gpt_context -i 'src/**/*.ts' -f tree,content -o context.txt"
99
+ opts.separator ''
100
+ opts.separator ' # Generate aider command'
101
+ opts.separator " gpt_context -i 'lib/**/*.rb' -f aider -p 'Add logging'"
102
+ opts.separator ''
103
+
104
+ opts.on('-v', '--version', 'Show version') do
105
+ puts "gpt_context version #{Appydave::Tools::VERSION}"
106
+ exit
107
+ end
64
108
 
65
- puts ''
66
- puts ' # Get GPT Context Gatherer code that is found in any folder (bin, lib & spec)'
67
- puts " #{File.basename($PROGRAM_NAME)} -i '**/*gather*.rb'"
109
+ opts.on_tail('-h', '--help', 'Show this help') do
110
+ puts opts
68
111
  exit
69
112
  end
70
113
  end.parse!
71
114
 
72
- if options.include_patterns.empty? && options.exclude_patterns.empty? && options.format.nil?
115
+ if options.include_patterns.empty? && options.exclude_patterns.empty?
73
116
  script_name = File.basename($PROGRAM_NAME, File.extname($PROGRAM_NAME))
74
117
 
75
118
  puts 'No options provided to GPT Context. Please specify patterns to include or exclude.'
data/docs/backlog.md CHANGED
@@ -10,7 +10,7 @@ Requirements tracking for AppyDave Tools development.
10
10
  |---|-------------|-------|--------|
11
11
  | 1 | FR-1: GPT Context token counting (see below) | 2025-12-06 | Pending |
12
12
  | 2 | FR-2: GPT Context AI-friendly help system (see below) | 2025-12-07 | Pending |
13
- | 3 | NFR-1: Improve test coverage for DAM commands | 2025-12-06 | Pending |
13
+ | 3 | NFR-1: Improve test coverage for DAM commands | 2025-12-06 | Addressed in DAM sprint (Jan 2025) — +648 lines, 5 new spec files |
14
14
  | 4 | FR-3: Jump Location Tool (see spec) | 2025-12-13 | ✅ Implemented 2025-12-13 |
15
15
  | 5 | NFR-2: Claude Code Skill for Jump Tool (see below) | 2025-12-14 | ✅ Implemented 2025-12-14 |
16
16
  | 6 | BUG-1: Jump CLI get/remove commands fail to find entries (see below) | 2025-12-15 | Pending |
@@ -34,22 +34,21 @@ This directory contains code quality retrospective analyses and improvement plan
34
34
 
35
35
  ---
36
36
 
37
- ## Current Status
38
-
39
- ### Critical Issues (3)
40
- 1. **Git Operations Duplication** - 90 lines across 3 files
41
- 2. **Configuration Loading Pattern** - 20+ redundant calls (partially fixed)
42
- 3. **Brand Resolution Logic** - Scattered across 5+ files, causing bugs
43
-
44
- ### Moderate Issues (3)
45
- 1. ⏳ **Test Pattern Inconsistency** - Mix of mocking vs real filesystem
46
- 2.**Error Handling Inconsistency** - 3 different patterns
47
- 3. ⏳ **Directory Utils Duplication** - 40 lines duplicated
48
-
49
- ### Implementation Priority
50
- 1. **High Priority** - GitHelper extraction, BrandResolver creation
51
- 2. **Medium Priority** - Exception hierarchy, test refactoring
52
- 3. **Low Priority** - Documentation, minor cleanups
37
+ ## Current Status (updated 2026-03-19)
38
+
39
+ ### Resolved in DAM Enhancement Sprint (Jan 2025)
40
+ 1. **Git Operations Duplication** extracted to `lib/appydave/tools/dam/git_helper.rb`
41
+ 2. **Brand Resolution Logic** centralized in `lib/appydave/tools/dam/brand_resolver.rb`
42
+ 3. **Error Handling Inconsistency** exception hierarchy in `lib/appydave/tools/dam/errors.rb`
43
+ 4. ✅ **Directory Utils Duplication** → extracted to `lib/appydave/tools/dam/file_helper.rb`
44
+ 5. **Test Pattern Inconsistency** → `project_listing_spec.rb` refactored to use shared filesystem context
45
+
46
+ ###Still Outstanding (Low Priority)
47
+ 1. ⏳ **Configuration Loading Redundancy** `Config.configure` called ~7x in config.rb (memoized, safe, but noisy). See BACKLOG.md B011.
48
+ 2. ⏳ **CLI Argument Parsing** — `bin/dam` has multiple 200+ line methods. See BACKLOG.md B011.
49
+ 3. **Integration Tests** — Brand resolution tested unit-only, no end-to-end cross-layer specs. See BACKLOG.md B012.
50
+
51
+ **Baseline:** 748 examples, 0 failures, 84.88% coverage
53
52
 
54
53
  ---
55
54
 
@@ -0,0 +1,245 @@
1
+ # AGENTS.md — AppyDave Tools
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 (derived from actual files, not guesswork)
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 area:** DAM (Digital Asset Management) — `lib/appydave/tools/dam/` — is the primary focus of recent campaigns.
13
+ **Commits:** Trigger automated semantic versioning via GitHub Actions. Always use `kfeat`/`kfix` — never `git commit`.
14
+
15
+ ---
16
+
17
+ ## Build & Run Commands
18
+
19
+ ```bash
20
+ # Initialize rbenv (required if rbenv not in PATH)
21
+ eval "$(rbenv init -)"
22
+
23
+ # Run tests
24
+ bundle exec rspec # All tests
25
+ bundle exec rspec spec/path/to/file_spec.rb # Single file
26
+ RUBYOPT="-W0" bundle exec rspec # Suppress Ruby 3.4 platform warnings
27
+
28
+ # Lint
29
+ bundle exec rubocop --format clang # Standard lint check (matches CI)
30
+
31
+ # Commit (never use git commit directly)
32
+ kfeat "add feature description" # Minor version bump
33
+ kfix "fix bug description" # Patch version bump
34
+ ```
35
+
36
+ **Baseline (2026-03-19):** 748 examples, 0 failures, 84.88% line coverage (7680/9048)
37
+
38
+ ---
39
+
40
+ ## Directory Structure
41
+
42
+ ```
43
+ bin/ CLI scripts (development, .rb extension)
44
+ exe/ Thin wrappers for gem installation (no .rb extension)
45
+ lib/appydave/tools/
46
+ dam/ Digital Asset Management — main active area
47
+ brand_resolver.rb Centralizes ALL brand name transformations (appydave ↔ v-appydave)
48
+ errors.rb Custom exception hierarchy (DamError, BrandNotFoundError, etc.)
49
+ fuzzy_matcher.rb Levenshtein distance for "did you mean?" suggestions
50
+ git_helper.rb Extracted git command wrappers (current_branch, commits_ahead, etc.)
51
+ file_helper.rb File utility methods (directory size, format_size)
52
+ config.rb Delegates brand resolution to BrandResolver; memoized Config loading
53
+ project_resolver.rb Project name resolution with regex pattern matching
54
+ project_listing.rb Table display for `dam list` command (format() for headers + data)
55
+ s3_operations.rb S3 upload/download/status with MD5 comparison
56
+ status.rb Project git/S3 status display
57
+ manifest_generator.rb Video project manifest
58
+ sync_from_ssd.rb SSD sync operations
59
+ ssd_status.rb SSD backup status
60
+ repo_push.rb, repo_status.rb, repo_sync.rb, share_operations.rb, config_loader.rb, s3_scanner.rb
61
+ configuration/ Config file management (settings.json, channels.json)
62
+ gpt_context/ File collection for AI context gathering
63
+ subtitle_processor/ SRT file cleaning and joining
64
+ youtube_manager/ YouTube API integration
65
+ name_manager/ Jump location tool (fuzzy search, CRUD, alias generation)
66
+ types/ BaseModel, ArrayType, HashType, IndifferentAccessHash
67
+ spec/
68
+ appydave/tools/dam/ 14 spec files (one per dam/ class)
69
+ support/
70
+ dam_filesystem_helpers.rb Shared contexts for DAM filesystem testing
71
+ jump_test_helpers.rb Jump tool test helpers
72
+ docs/
73
+ backlog.md Legacy requirements (BACKLOG.md in this folder supersedes)
74
+ code-quality/ Audit reports (behavioral-audit-2025-01-22, uat-report-2025-01-22)
75
+ planning/ Ralphy campaign artifacts (this folder)
76
+ architecture/ CLI patterns, testing patterns, configuration systems
77
+ ```
78
+
79
+ ---
80
+
81
+ ## Success Criteria
82
+
83
+ Every work unit must satisfy ALL of the following before marking `[x]`:
84
+
85
+ - [ ] `bundle exec rspec` — 748+ examples, 0 failures
86
+ - [ ] `bundle exec rubocop --format clang` — 0 offenses
87
+ - [ ] Line coverage stays ≥ 84.88%
88
+ - [ ] Any new functionality has ≥ 1 spec covering it
89
+ - [ ] All new `.rb` files start with `# frozen_string_literal: true`
90
+ - [ ] No hardcoded brand transformation strings (always use BrandResolver)
91
+
92
+ ---
93
+
94
+ ## Reference Patterns
95
+
96
+ ### Shared Context for DAM Specs — THE STANDARD PATTERN
97
+
98
+ ```ruby
99
+ # spec/appydave/tools/dam/some_class_spec.rb
100
+ # frozen_string_literal: true
101
+
102
+ require 'spec_helper'
103
+
104
+ RSpec.describe Appydave::Tools::Dam::SomeClass do
105
+ include_context 'with vat filesystem and brands', brands: %w[appydave voz]
106
+
107
+ before do
108
+ FileUtils.mkdir_p(File.join(appydave_path, 'b65-test-project'))
109
+ FileUtils.mkdir_p(File.join(voz_path, 'boy-baker'))
110
+ end
111
+
112
+ describe '.some_method' do
113
+ it 'does the thing' do
114
+ expect(described_class.some_method('appydave')).to eq('expected')
115
+ end
116
+ end
117
+ end
118
+ ```
119
+
120
+ **Available from shared context:**
121
+ - `temp_folder` — temp root (auto-cleaned after each example)
122
+ - `projects_root` — `/path/to/temp/video-projects`
123
+ - `appydave_path`, `voz_path`, etc. — brand dirs (created on demand)
124
+ - `SettingsConfig#video_projects_root` is mocked to return `projects_root`
125
+
126
+ ### BrandResolver — All Brand Name Transformations
127
+
128
+ ```ruby
129
+ BrandResolver.expand('appydave') # => 'v-appydave'
130
+ BrandResolver.expand('ad') # => 'v-appydave' (shortcut)
131
+ BrandResolver.expand('joy') # => 'v-beauty-and-joy'
132
+ BrandResolver.normalize('v-appydave') # => 'appydave'
133
+ BrandResolver.to_config_key('ad') # => 'appydave'
134
+ BrandResolver.to_display('voz') # => 'v-voz'
135
+ BrandResolver.validate('appydave') # => 'appydave' or raises BrandNotFoundError
136
+ BrandResolver.exists?('appydave') # => true/false
137
+ ```
138
+
139
+ ### Typed Exception Pattern
140
+
141
+ ```ruby
142
+ # Use errors from lib/appydave/tools/dam/errors.rb
143
+ raise ProjectNotFoundError, 'Project name is required' if project_hint.nil?
144
+ raise BrandNotFoundError.new(brand, available_brands, fuzzy_suggestions)
145
+
146
+ # Exception hierarchy:
147
+ # DamError < StandardError
148
+ # BrandNotFoundError < DamError
149
+ # ProjectNotFoundError < DamError
150
+ # ConfigurationError < DamError
151
+ # S3OperationError < DamError
152
+ # GitOperationError < DamError
153
+ ```
154
+
155
+ ### Table Display — Headers Must Use format()
156
+
157
+ ```ruby
158
+ # CORRECT — headers and data use same format string
159
+ header = format('%-45s %12s %15s %10s', 'PROJECT', 'SIZE', 'AGE', 'GIT')
160
+ row = format('%-45s %12s %15s %10s', name, size, age, git_status)
161
+
162
+ # WRONG — hardcoded header string causes misalignment
163
+ header = 'PROJECT SIZE AGE GIT'
164
+ ```
165
+
166
+ ---
167
+
168
+ ## Anti-Patterns to Avoid
169
+
170
+ - ❌ Inline brand transformations — never write `"v-#{brand}"` or `.sub(/^v-/, '')` outside BrandResolver
171
+ - ❌ Mocking Config class methods in DAM specs — use `include_context 'with vat filesystem and brands'` instead
172
+ - ❌ `raise 'string error'` in DAM module — use typed exceptions from `errors.rb`
173
+ - ❌ Duplicating git command methods — use `GitHelper` module
174
+ - ❌ Duplicating file size calculations — use `FileHelper` module
175
+ - ❌ `include FileUtils` (conflicts with Ruby stdlib) — use dam's `FileHelper`
176
+ - ❌ Hardcoded header strings for table output — always use `format()` with same format string as data rows
177
+
178
+ ---
179
+
180
+ ## Mock Patterns
181
+
182
+ ### External Services — DO Mock
183
+
184
+ ```ruby
185
+ # S3 calls — use WebMock or VCR cassettes
186
+ stub_request(:get, /s3\.amazonaws\.com/).to_return(body: '...')
187
+
188
+ # YouTube API — VCR cassettes in spec/fixtures/vcr_cassettes/
189
+ VCR.use_cassette('youtube/get_video') do
190
+ # make API call
191
+ end
192
+ ```
193
+
194
+ ### Internal DAM Classes — DON'T Mock
195
+
196
+ Use real temp filesystem via shared context instead. The only acceptable mock inside DAM specs:
197
+ - `SettingsConfig#video_projects_root` — already mocked by the shared context
198
+ - `Config.available_brands` — acceptable when testing "no brands found" edge case
199
+
200
+ ### Config.configure — Safe to Call Multiple Times
201
+
202
+ ```ruby
203
+ # Config.configure is memoized — idempotent, no-op after first call
204
+ # Called in multiple places across DAM module; this is known/acceptable technical debt
205
+ Appydave::Tools::Configuration::Config.configure
206
+ ```
207
+
208
+ ---
209
+
210
+ ## Quality Gates
211
+
212
+ - **Tests:** `bundle exec rspec` — 748 examples, 0 failures (do not ship if any fail)
213
+ - **Lint:** `bundle exec rubocop --format clang` — 0 offenses (CI will reject)
214
+ - **Coverage:** ≥ 84.88% line coverage
215
+ - **frozen_string_literal:** Required on every new `.rb` file
216
+ - **Commit format:** `kfeat`/`kfix` only — triggers semantic versioning + CI wait
217
+
218
+ ---
219
+
220
+ ## Learnings
221
+
222
+ ### From DAM Enhancement Sprint (Jan 2025 — 75 commits, 9e49668 → 4228b51)
223
+
224
+ - **BrandResolver is the critical path.** All `dam` commands flow through it. Any change to brand resolution must be tested with all shortcuts (ad, joy, ss, voz, aitldr, kiros) and case variations (APPYDAVE, AppyDave).
225
+ - **`Regexp.last_match` is reset by `.sub()` calls.** Always capture regex groups BEFORE any string transformation: `project = ::Regexp.last_match(2)` then `brand_key = BrandResolver.normalize(brand_with_prefix)`.
226
+ - **`Config.configure` is memoized but called redundantly.** ~7 calls in config.rb, plus more in other files. Known issue, not worsened. Don't add new calls; reference existing ones.
227
+ - **FuzzyMatcher threshold is 3 (Levenshtein distance).** Wired via `Config#brand_path` → `FuzzyMatcher.find_matches(brand_key, all_brand_identifiers, threshold: 3)`. Works for "appydav" → "appydave".
228
+ - **Performance: ~2s per project** for git/S3 checks during `dam list`. 13 projects = ~26s total. Acceptable. Grows linearly — flag if brand has > 20 projects.
229
+ - **Table format() pattern is non-obvious.** Headers misaligned 3 times in UAT before fix. Always verify with real data that has long names (e.g., "beauty-and-joy" = 14 chars).
230
+ - **Test strategy:** 5 new spec files (+648 lines) added for DAM helper classes. `project_listing_spec.rb` refactored from 20 Config mocks to shared filesystem context. Task 3.1 complete.
231
+
232
+ ### From Jump Location Tool (Dec 2025)
233
+
234
+ - **Dependency injection for path validators** is required for CI compatibility. Path existence checks fail in CI — inject via parameter, not hardcoded.
235
+ - **BUG-1 status (as of 2026-03-19 audit):** Static analysis shows `Jump::Config#find` and `Commands::Remove` both have correct dual-key guards (`loc.key == key` on Location objects; `loc['key'] == key || loc[:key] == key` on raw hashes). The bug may be environmental or already fixed. Always verify live before writing fix code.
236
+ - **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).
237
+ - **Jump report commands** got `--limit` and `--skip-unassigned` flags after initial implementation. Jump tool scope grows incrementally.
238
+
239
+ ### From Three-Lens Audit (2026-03-19)
240
+
241
+ - **`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.
242
+ - **ManifestGenerator and SyncFromSsd produce incompatible range strings (B016).** `determine_range('b65')` returns `"b50-b99"` in one and `"60-69"` in the other. This is a data integrity bug for SSD archive path construction. Do not add new archive features before this is resolved.
243
+ - **`ssl_verify_peer: false` is set unconditionally in S3Operations and ShareOperations (B017).** Not safe despite the comment. Remove before adding any new S3 functionality.
244
+ - **`Config.configure` fires inside nested call chains** (e.g., `brand_path` → `BrandResolver.to_config_key` → `configure` = 3 calls for one operation). Memoized, so no performance cost, but pattern will spread if not watched. Do not add new `Config.configure` calls; reference from the existing top-level call.
245
+ - **BUG-1 is NOT in `name_manager/`.** Jump's `name_manager/project_name.rb` is a different unrelated class. BUG-1 root is in `lib/appydave/tools/jump/` (Config, Search, Commands).