ro 4.4.0 → 5.0.0
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/Gemfile.lock +42 -16
 - data/MIGRATION.md +320 -0
 - data/README.md +30 -18
 - data/a.yml +60 -0
 - data/bin/ro +10 -0
 - data/lib/ro/_lib.rb +1 -1
 - data/lib/ro/asset.rb +46 -5
 - data/lib/ro/collection.rb +51 -13
 - data/lib/ro/migrator.rb +285 -0
 - data/lib/ro/node.rb +53 -13
 - data/lib/ro/root.rb +75 -1
 - data/lib/ro/script/migrate.rb +204 -0
 - data/lib/ro/script/server.rb +1 -1
 - data/lib/ro.rb +1 -0
 - data/ro.gemspec +207 -16
 - data/specs/001-simplify-asset-structure/IMPLEMENTATION_SUMMARY.md +212 -0
 - data/specs/001-simplify-asset-structure/checklists/requirements.md +36 -0
 - data/specs/001-simplify-asset-structure/contracts/collection_api.md +407 -0
 - data/specs/001-simplify-asset-structure/contracts/migrator_api.md +461 -0
 - data/specs/001-simplify-asset-structure/contracts/node_api.md +294 -0
 - data/specs/001-simplify-asset-structure/data-model.md +381 -0
 - data/specs/001-simplify-asset-structure/plan.md +90 -0
 - data/specs/001-simplify-asset-structure/quickstart.md +575 -0
 - data/specs/001-simplify-asset-structure/research.md +333 -0
 - data/specs/001-simplify-asset-structure/spec.md +127 -0
 - data/specs/001-simplify-asset-structure/tasks.md +349 -0
 - data/test/fixtures/new_structure/mixed/test-json.json +5 -0
 - data/test/fixtures/new_structure/mixed/test-yaml.yml +3 -0
 - data/test/fixtures/new_structure/posts/metadata-only.yml +7 -0
 - data/test/fixtures/new_structure/posts/nested-test/assets/subdirectory/image.png +2 -0
 - data/test/fixtures/new_structure/posts/nested-test.yml +7 -0
 - data/test/fixtures/new_structure/posts/sample-post/assets/body.md +5 -0
 - data/test/fixtures/new_structure/posts/sample-post/assets/image.jpg +2 -0
 - data/test/fixtures/new_structure/posts/sample-post.yml +7 -0
 - data/test/fixtures/old_structure/posts/assets-only/assets/test.txt +1 -0
 - data/test/fixtures/old_structure/posts/sample-post/assets/body.md +5 -0
 - data/test/fixtures/old_structure/posts/sample-post/assets/image.jpg +2 -0
 - data/test/fixtures/old_structure/posts/sample-post/attributes.yml +2 -0
 - data/test/integration/ro_integration_test.rb +165 -0
 - data/test/test_helper.rb +149 -0
 - data/test/tmp/migration_test_1760746513.backup.20251018001513/migration_test_1760746513/posts/sample-post/assets/image.jpg +2 -0
 - data/test/tmp/migration_test_1760746513.backup.20251018001513/migration_test_1760746513/posts/sample-post/attributes.yml +7 -0
 - data/test/tmp/migration_test_1760746513.backup.20251018001513/migration_test_1760746513/posts/sample-post/body.md +5 -0
 - data/test/tmp/migration_test_1760746513.backup.20251018001513/posts/sample-post/assets/image.jpg +2 -0
 - data/test/tmp/migration_test_1760746513.backup.20251018001513/posts/sample-post/attributes.yml +7 -0
 - data/test/tmp/migration_test_1760746513.backup.20251018001513/posts/sample-post/body.md +5 -0
 - data/test/tmp/migration_test_1760746556.backup.20251018001556/migration_test_1760746556/posts/sample-post/assets/image.jpg +2 -0
 - data/test/tmp/migration_test_1760746556.backup.20251018001556/migration_test_1760746556/posts/sample-post/attributes.yml +7 -0
 - data/test/tmp/migration_test_1760746556.backup.20251018001556/migration_test_1760746556/posts/sample-post/body.md +5 -0
 - data/test/tmp/migration_test_1760746556.backup.20251018001556/posts/sample-post/assets/image.jpg +2 -0
 - data/test/tmp/migration_test_1760746556.backup.20251018001556/posts/sample-post/attributes.yml +7 -0
 - data/test/tmp/migration_test_1760746556.backup.20251018001556/posts/sample-post/body.md +5 -0
 - data/test/tmp/migration_test_1760755248.backup.20251018024048/migration_test_1760755248/posts/sample-post/assets/image.jpg +2 -0
 - data/test/tmp/migration_test_1760755248.backup.20251018024048/migration_test_1760755248/posts/sample-post/attributes.yml +7 -0
 - data/test/tmp/migration_test_1760755248.backup.20251018024048/migration_test_1760755248/posts/sample-post/body.md +5 -0
 - data/test/tmp/migration_test_1760755248.backup.20251018024048/posts/sample-post/assets/image.jpg +2 -0
 - data/test/tmp/migration_test_1760755248.backup.20251018024048/posts/sample-post/attributes.yml +7 -0
 - data/test/tmp/migration_test_1760755248.backup.20251018024048/posts/sample-post/body.md +5 -0
 - data/test/tmp/migration_test_1760758803.backup.20251018034003/migration_test_1760758803/posts/sample-post/body.md +5 -0
 - data/test/tmp/migration_test_1760758803.backup.20251018034003/migration_test_1760758803/posts/sample-post/image.jpg +2 -0
 - data/test/tmp/migration_test_1760758803.backup.20251018034003/migration_test_1760758803/posts/sample-post.yml +7 -0
 - data/test/tmp/migration_test_1760758803.backup.20251018034003/posts/sample-post/body.md +5 -0
 - data/test/tmp/migration_test_1760758803.backup.20251018034003/posts/sample-post/image.jpg +2 -0
 - data/test/tmp/migration_test_1760758803.backup.20251018034003/posts/sample-post.yml +7 -0
 - data/test/tmp/migration_test_1760758869.backup.20251018034109/migration_test_1760758869/posts/sample-post/assets/body.md +5 -0
 - data/test/tmp/migration_test_1760758869.backup.20251018034109/migration_test_1760758869/posts/sample-post/assets/image.jpg +2 -0
 - data/test/tmp/migration_test_1760758869.backup.20251018034109/migration_test_1760758869/posts/sample-post/attributes.yml +2 -0
 - data/test/tmp/migration_test_1760758869.backup.20251018034109/posts/sample-post/assets/body.md +5 -0
 - data/test/tmp/migration_test_1760758869.backup.20251018034109/posts/sample-post/assets/image.jpg +2 -0
 - data/test/tmp/migration_test_1760758869.backup.20251018034109/posts/sample-post/attributes.yml +2 -0
 - data/test/tmp/migration_test_1760758920.backup.20251018034200/migration_test_1760758920/posts/sample-post/assets/body.md +5 -0
 - data/test/tmp/migration_test_1760758920.backup.20251018034200/migration_test_1760758920/posts/sample-post/assets/image.jpg +2 -0
 - data/test/tmp/migration_test_1760758920.backup.20251018034200/migration_test_1760758920/posts/sample-post/attributes.yml +2 -0
 - data/test/tmp/migration_test_1760758920.backup.20251018034200/posts/sample-post/assets/body.md +5 -0
 - data/test/tmp/migration_test_1760758920.backup.20251018034200/posts/sample-post/assets/image.jpg +2 -0
 - data/test/tmp/migration_test_1760758920.backup.20251018034200/posts/sample-post/attributes.yml +2 -0
 - data/test/tmp/migration_test_1760824728.backup.20251018215848/migration_test_1760824728/posts/assets-only/assets/test.txt +1 -0
 - data/test/tmp/migration_test_1760824728.backup.20251018215848/migration_test_1760824728/posts/sample-post/assets/body.md +5 -0
 - data/test/tmp/migration_test_1760824728.backup.20251018215848/migration_test_1760824728/posts/sample-post/assets/image.jpg +2 -0
 - data/test/tmp/migration_test_1760824728.backup.20251018215848/migration_test_1760824728/posts/sample-post/attributes.yml +2 -0
 - data/test/tmp/migration_test_1760824728.backup.20251018215848/posts/assets-only/assets/test.txt +1 -0
 - data/test/tmp/migration_test_1760824728.backup.20251018215848/posts/sample-post/assets/body.md +5 -0
 - data/test/tmp/migration_test_1760824728.backup.20251018215848/posts/sample-post/assets/image.jpg +2 -0
 - data/test/tmp/migration_test_1760824728.backup.20251018215848/posts/sample-post/attributes.yml +2 -0
 - data/test/tmp/migration_test_1760844153.backup.20251019032233/migration_test_1760844153/posts/assets-only/assets/test.txt +1 -0
 - data/test/tmp/migration_test_1760844153.backup.20251019032233/migration_test_1760844153/posts/sample-post/assets/body.md +5 -0
 - data/test/tmp/migration_test_1760844153.backup.20251019032233/migration_test_1760844153/posts/sample-post/assets/image.jpg +2 -0
 - data/test/tmp/migration_test_1760844153.backup.20251019032233/migration_test_1760844153/posts/sample-post/attributes.yml +2 -0
 - data/test/tmp/migration_test_1760844153.backup.20251019032233/posts/assets-only/assets/test.txt +1 -0
 - data/test/tmp/migration_test_1760844153.backup.20251019032233/posts/sample-post/assets/body.md +5 -0
 - data/test/tmp/migration_test_1760844153.backup.20251019032233/posts/sample-post/assets/image.jpg +2 -0
 - data/test/tmp/migration_test_1760844153.backup.20251019032233/posts/sample-post/attributes.yml +2 -0
 - data/test/tmp/new_structure_test_1760746452/mixed/test-json.json +5 -0
 - data/test/tmp/new_structure_test_1760746452/mixed/test-yaml.yml +3 -0
 - data/test/tmp/new_structure_test_1760746452/posts/metadata-only.yml +7 -0
 - data/test/tmp/new_structure_test_1760746452/posts/nested-test/subdirectory/image.png +2 -0
 - data/test/tmp/new_structure_test_1760746452/posts/nested-test.yml +7 -0
 - data/test/tmp/new_structure_test_1760746452/posts/sample-post/body.md +5 -0
 - data/test/tmp/new_structure_test_1760746452/posts/sample-post/image.jpg +2 -0
 - data/test/tmp/new_structure_test_1760746452/posts/sample-post.yml +7 -0
 - data/test/unit/asset_test.rb +90 -0
 - data/test/unit/collection_test.rb +127 -0
 - data/test/unit/migrator_test.rb +209 -0
 - data/test/unit/node_test.rb +138 -0
 - metadata +111 -18
 - /data/public/ro/nerd/{fastest-possible-embeddings/attributes.yml → fastest-possible-embeddings.yml} +0 -0
 - /data/public/ro/nerd/{ima/attributes.yml → ima.yml} +0 -0
 - /data/public/ro/nerd/{index/attributes.yml → index.yml} +0 -0
 - /data/public/ro/pages/{contact/attributes.yml → contact.yml} +0 -0
 - /data/public/ro/pages/{cv/attributes.yml → cv.yml} +0 -0
 - /data/public/ro/pages/{disco/attributes.yml → disco.yml} +0 -0
 - /data/public/ro/pages/{index/attributes.yml → index.yml} +0 -0
 - /data/public/ro/pages/{jess/attributes.yml → jess.yml} +0 -0
 - /data/public/ro/pages/{now/attributes.yml → now.yml} +0 -0
 - /data/public/ro/posts/{almost-died-in-an-ice-cave/attributes.yml → almost-died-in-an-ice-cave.yml} +0 -0
 - /data/public/ro/posts/{facebook-and-global-extremism/attributes.yml → facebook-and-global-extremism.yml} +0 -0
 - /data/public/ro/posts/{lemmings-considered-harmful/attributes.yml → lemmings-considered-harmful.yml} +0 -0
 - /data/public/ro/posts/{lost-in-the-desert/attributes.yml → lost-in-the-desert.yml} +0 -0
 - /data/public/ro/posts/{mission/attributes.yml → mission.yml} +0 -0
 - /data/public/ro/posts/{return-your-laptop/attributes.yml → return-your-laptop.yml} +0 -0
 
| 
         @@ -0,0 +1,349 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Tasks: Simplify Asset Directory Structure
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            **Input**: Design documents from `/specs/001-simplify-asset-structure/`
         
     | 
| 
      
 4 
     | 
    
         
            +
            **Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            **Tests**: Tests are included per TDD approach - this feature creates comprehensive test coverage for a codebase that currently has no tests.
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            **Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            ## Format: `[ID] [P?] [Story] Description`
         
     | 
| 
      
 11 
     | 
    
         
            +
            - **[P]**: Can run in parallel (different files, no dependencies)
         
     | 
| 
      
 12 
     | 
    
         
            +
            - **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3)
         
     | 
| 
      
 13 
     | 
    
         
            +
            - Include exact file paths in descriptions
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            ## Path Conventions
         
     | 
| 
      
 16 
     | 
    
         
            +
            - **Ruby gem**: `lib/ro/`, `test/` at repository root
         
     | 
| 
      
 17 
     | 
    
         
            +
            - Test fixtures in `test/fixtures/`
         
     | 
| 
      
 18 
     | 
    
         
            +
            - Public examples in `public/ro/`
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            ---
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            ## Phase 1: Setup (Shared Infrastructure)
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            **Purpose**: Project initialization, test infrastructure, and basic structure
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            - [X] T001 Create test directory structure: test/unit/, test/integration/, test/fixtures/
         
     | 
| 
      
 27 
     | 
    
         
            +
            - [X] T002 [P] Create test fixtures directory with subdirectories: test/fixtures/old_structure/ and test/fixtures/new_structure/
         
     | 
| 
      
 28 
     | 
    
         
            +
            - [X] T003 [P] Create test helper file test/test_helper.rb with common setup and assertions
         
     | 
| 
      
 29 
     | 
    
         
            +
            - [X] T004 [P] Update Rakefile to enable test tasks (verify test/**/*_test.rb pattern works)
         
     | 
| 
      
 30 
     | 
    
         
            +
            - [X] T005 [P] Create .gitignore entries for test artifacts: test/tmp/, *.log, .backup.*
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            ---
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            ## Phase 2: Foundational (Blocking Prerequisites)
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            **Purpose**: Test fixtures and baseline tests that ALL user stories depend on
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            **⚠️ CRITICAL**: No user story work can begin until this phase is complete
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            - [X] T006 [P] Create old structure test fixture in test/fixtures/old_structure/posts/sample-post/ with attributes.yml, body.md, and assets/image.jpg
         
     | 
| 
      
 41 
     | 
    
         
            +
            - [X] T007 [P] Create new structure test fixture in test/fixtures/new_structure/posts/ with sample-post.yml and sample-post/body.md
         
     | 
| 
      
 42 
     | 
    
         
            +
            - [X] T008 [P] Create test fixture with metadata-only node (no asset directory) in test/fixtures/new_structure/posts/metadata-only.yml
         
     | 
| 
      
 43 
     | 
    
         
            +
            - [X] T009 [P] Create test fixture with nested assets in test/fixtures/new_structure/posts/nested-test/ with subdirectory/image.png
         
     | 
| 
      
 44 
     | 
    
         
            +
            - [X] T010 [P] Create test fixture with multiple metadata formats: test/fixtures/new_structure/mixed/test.yml, test.json
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
            **Checkpoint**: ✓ Test fixtures ready - user story implementation can now begin in parallel
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
            ---
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            ## Phase 3: User Story 1 - Read Asset Data (Priority: P1) 🎯 MVP
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
            **Goal**: Enable reading assets from the new simplified structure (identifier.yml + identifier/ directory)
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
            **Independent Test**: Create an asset with new structure and verify metadata loads from identifier.yml and assets load from identifier/ directory
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
            ### Tests for User Story 1
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
            **NOTE: Write these tests FIRST, ensure they FAIL before implementation**
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
            - [ ] T011 [P] [US1] Create unit test for Collection#metadata_files in test/unit/collection_test.rb
         
     | 
| 
      
 61 
     | 
    
         
            +
            - [ ] T012 [P] [US1] Create unit test for Collection#each with new structure in test/unit/collection_test.rb
         
     | 
| 
      
 62 
     | 
    
         
            +
            - [ ] T013 [P] [US1] Create unit test for Node initialization with metadata_file parameter in test/unit/node_test.rb
         
     | 
| 
      
 63 
     | 
    
         
            +
            - [ ] T014 [P] [US1] Create unit test for Node#id derived from metadata filename in test/unit/node_test.rb
         
     | 
| 
      
 64 
     | 
    
         
            +
            - [ ] T015 [P] [US1] Create unit test for Node#asset_dir returning node directory (not assets/ subdirectory) in test/unit/node_test.rb
         
     | 
| 
      
 65 
     | 
    
         
            +
            - [ ] T016 [P] [US1] Create unit test for Node#_load_base_attributes loading from external metadata file in test/unit/node_test.rb
         
     | 
| 
      
 66 
     | 
    
         
            +
            - [ ] T017 [P] [US1] Create unit test for Asset path resolution without assets/ prefix in test/unit/asset_test.rb
         
     | 
| 
      
 67 
     | 
    
         
            +
            - [ ] T018 [US1] Create integration test for loading collection with new structure in test/integration/ro_integration_test.rb
         
     | 
| 
      
 68 
     | 
    
         
            +
            - [ ] T019 [US1] Create integration test for metadata-only nodes (FR-007) in test/integration/ro_integration_test.rb
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
            ### Implementation for User Story 1
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
            - [ ] T020 [US1] Modify Collection#each in lib/ro/collection.rb to discover nodes by metadata files (.yml, .yaml, .json) instead of subdirectories
         
     | 
| 
      
 73 
     | 
    
         
            +
            - [ ] T021 [US1] Add Collection#metadata_files method in lib/ro/collection.rb to scan for *.{yml,yaml,json,toml} files
         
     | 
| 
      
 74 
     | 
    
         
            +
            - [ ] T022 [US1] Modify Collection#node_for in lib/ro/collection.rb to find nodes by metadata filename instead of directory name
         
     | 
| 
      
 75 
     | 
    
         
            +
            - [ ] T023 [US1] Update Node#initialize in lib/ro/node.rb to accept (collection, metadata_file) parameters instead of (collection, node_directory)
         
     | 
| 
      
 76 
     | 
    
         
            +
            - [ ] T024 [US1] Add Node#metadata_file attribute in lib/ro/node.rb to store path to metadata file
         
     | 
| 
      
 77 
     | 
    
         
            +
            - [ ] T025 [US1] Update Node#id in lib/ro/node.rb to derive from metadata filename (without extension) instead of directory name
         
     | 
| 
      
 78 
     | 
    
         
            +
            - [ ] T026 [US1] Modify Node#_load_base_attributes in lib/ro/node.rb to load from @metadata_file instead of searching for attributes.yml in @path
         
     | 
| 
      
 79 
     | 
    
         
            +
            - [ ] T027 [US1] Update Node#asset_dir in lib/ro/node.rb to return @path (node directory) instead of @path/assets/
         
     | 
| 
      
 80 
     | 
    
         
            +
            - [ ] T028 [US1] Update Node#_ignored_files in lib/ro/node.rb to remove assets/**/** from ignore list and add *.yml/*.json/*.toml (metadata files)
         
     | 
| 
      
 81 
     | 
    
         
            +
            - [ ] T029 [US1] Modify Asset initialization in lib/ro/asset.rb to split paths on node ID instead of /assets/ segment
         
     | 
| 
      
 82 
     | 
    
         
            +
            - [ ] T030 [US1] Update Asset#relative_path calculation in lib/ro/asset.rb for new structure (no assets/ prefix to strip)
         
     | 
| 
      
 83 
     | 
    
         
            +
            - [ ] T031 [US1] Run all US1 tests and verify they pass
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
            **Checkpoint**: At this point, User Story 1 should be fully functional - assets can be read from new structure
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
            ---
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
            ## Phase 4: User Story 2 - Write Asset Data (Priority: P2)
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
            **Goal**: Enable creating and updating assets in the new structure
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
            **Independent Test**: Create a new asset programmatically, update its metadata, and verify structure matches identifier.yml + identifier/ pattern
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
            ### Tests for User Story 2
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
            - [ ] T032 [P] [US2] Create unit test for Node#update_attributes! writing to correct metadata file in test/unit/node_test.rb
         
     | 
| 
      
 98 
     | 
    
         
            +
            - [ ] T033 [P] [US2] Create unit test for creating new node with metadata file in test/unit/node_test.rb
         
     | 
| 
      
 99 
     | 
    
         
            +
            - [ ] T034 [P] [US2] Create integration test for creating new asset in new structure in test/integration/ro_integration_test.rb
         
     | 
| 
      
 100 
     | 
    
         
            +
            - [ ] T035 [P] [US2] Create integration test for updating existing node metadata in test/integration/ro_integration_test.rb
         
     | 
| 
      
 101 
     | 
    
         
            +
            - [ ] T036 [P] [US2] Create integration test for adding assets to existing node in test/integration/ro_integration_test.rb
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
            ### Implementation for User Story 2
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
            - [ ] T037 [US2] Update Node#update_attributes! in lib/ro/node.rb to save to @metadata_file instead of @path/attributes.yml
         
     | 
| 
      
 106 
     | 
    
         
            +
            - [ ] T038 [US2] Verify Node can create new metadata files at collection level (test with new node creation)
         
     | 
| 
      
 107 
     | 
    
         
            +
            - [ ] T039 [US2] Verify Asset files can be added to node directory (test file copy to asset_dir)
         
     | 
| 
      
 108 
     | 
    
         
            +
            - [ ] T040 [US2] Test metadata format support (.yml, .json) - verify both formats work for read/write
         
     | 
| 
      
 109 
     | 
    
         
            +
            - [ ] T041 [US2] Run all US2 tests and verify they pass
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
            **Checkpoint**: At this point, User Stories 1 AND 2 should both work - assets can be read AND written in new structure
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
            ---
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
            ## Phase 5: User Story 3 - Migrate Existing Assets (Priority: P3)
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
            **Goal**: Provide migration tool to convert from old structure to new structure without data loss
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
            **Independent Test**: Create assets in old format, run migration, verify all data is preserved in new format and old structure is removed
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
            ### Tests for User Story 3
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
            - [ ] T042 [P] [US3] Create unit test for Migrator#initialize in test/unit/migrator_test.rb
         
     | 
| 
      
 124 
     | 
    
         
            +
            - [ ] T043 [P] [US3] Create unit test for Migrator#validate detecting old structure in test/unit/migrator_test.rb
         
     | 
| 
      
 125 
     | 
    
         
            +
            - [ ] T044 [P] [US3] Create unit test for Migrator#validate detecting new structure in test/unit/migrator_test.rb
         
     | 
| 
      
 126 
     | 
    
         
            +
            - [ ] T045 [P] [US3] Create unit test for Migrator#preview generating migration plan in test/unit/migrator_test.rb
         
     | 
| 
      
 127 
     | 
    
         
            +
            - [ ] T046 [P] [US3] Create unit test for Migrator#migrate moving attributes.yml to identifier.yml in test/unit/migrator_test.rb
         
     | 
| 
      
 128 
     | 
    
         
            +
            - [ ] T047 [P] [US3] Create unit test for Migrator#migrate moving assets/ files to identifier/ in test/unit/migrator_test.rb
         
     | 
| 
      
 129 
     | 
    
         
            +
            - [ ] T048 [P] [US3] Create unit test for Migrator#migrate preserving nested asset directories in test/unit/migrator_test.rb
         
     | 
| 
      
 130 
     | 
    
         
            +
            - [ ] T049 [P] [US3] Create unit test for Migrator#backup creating timestamped backup in test/unit/migrator_test.rb
         
     | 
| 
      
 131 
     | 
    
         
            +
            - [ ] T050 [P] [US3] Create unit test for Migrator#rollback restoring from backup in test/unit/migrator_test.rb
         
     | 
| 
      
 132 
     | 
    
         
            +
            - [ ] T051 [US3] Create integration test for full collection migration in test/integration/ro_integration_test.rb
         
     | 
| 
      
 133 
     | 
    
         
            +
            - [ ] T052 [US3] Create integration test for migration with nested assets in test/integration/ro_integration_test.rb
         
     | 
| 
      
 134 
     | 
    
         
            +
            - [ ] T053 [US3] Create integration test for migration error handling and recovery in test/integration/ro_integration_test.rb
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
            ### Implementation for User Story 3
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
            - [ ] T054 [US3] Create lib/ro/script/migrator.rb with Migrator class skeleton
         
     | 
| 
      
 139 
     | 
    
         
            +
            - [ ] T055 [US3] Implement Migrator#initialize in lib/ro/script/migrator.rb with path and options (dry_run, backup, force, verbose)
         
     | 
| 
      
 140 
     | 
    
         
            +
            - [ ] T056 [US3] Implement Migrator#validate in lib/ro/script/migrator.rb to check for old/new/mixed structures
         
     | 
| 
      
 141 
     | 
    
         
            +
            - [ ] T057 [US3] Implement Migrator#preview in lib/ro/script/migrator.rb to generate migration plan (dry run)
         
     | 
| 
      
 142 
     | 
    
         
            +
            - [ ] T058 [US3] Implement Migrator#backup in lib/ro/script/migrator.rb to create timestamped backup using FileUtils.cp_r
         
     | 
| 
      
 143 
     | 
    
         
            +
            - [ ] T059 [US3] Implement Migrator#migrate_node in lib/ro/script/migrator.rb to migrate single node (move attributes.yml, move assets/, cleanup)
         
     | 
| 
      
 144 
     | 
    
         
            +
            - [ ] T060 [US3] Implement Migrator#migrate in lib/ro/script/migrator.rb to iterate all nodes and migrate each
         
     | 
| 
      
 145 
     | 
    
         
            +
            - [ ] T061 [US3] Implement Migrator#rollback in lib/ro/script/migrator.rb to restore from backup
         
     | 
| 
      
 146 
     | 
    
         
            +
            - [ ] T062 [US3] Add error handling and recovery logic to Migrator#migrate in lib/ro/script/migrator.rb
         
     | 
| 
      
 147 
     | 
    
         
            +
            - [ ] T063 [US3] Create CLI command 'ro migrate' in bin/ro or add Rake task for migration
         
     | 
| 
      
 148 
     | 
    
         
            +
            - [ ] T064 [US3] Add migration logging with detailed progress output
         
     | 
| 
      
 149 
     | 
    
         
            +
            - [ ] T065 [US3] Run all US3 tests and verify they pass
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
            **Checkpoint**: All user stories should now be independently functional - read, write, and migrate all work
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
            ---
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
            ## Phase 6: Integration & Validation
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
            **Purpose**: Ensure all user stories work together and with existing codebase
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
            - [ ] T066 [P] Test that old structure still works if present (backward compatibility during transition)
         
     | 
| 
      
 160 
     | 
    
         
            +
            - [ ] T067 [P] Test mixed structure detection (both old and new for same identifier) raises appropriate error/warning
         
     | 
| 
      
 161 
     | 
    
         
            +
            - [ ] T068 Test full workflow: migrate public/ro/ examples, verify builder/server still work
         
     | 
| 
      
 162 
     | 
    
         
            +
            - [ ] T069 [P] Create integration test for Root → Collection → Node chain with new structure
         
     | 
| 
      
 163 
     | 
    
         
            +
            - [ ] T070 [P] Create integration test for multiple metadata formats (.yml, .json) in same collection
         
     | 
| 
      
 164 
     | 
    
         
            +
            - [ ] T071 Verify performance: test asset lookup speed with 100+ nodes (target <100ms per SC-001)
         
     | 
| 
      
 165 
     | 
    
         
            +
            - [ ] T072 Run existing examples through new code (if any example code exists in repo)
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
            ---
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
            ## Phase 7: Example Migration & Documentation
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
            **Purpose**: Migrate example content and update documentation
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
            - [ ] T073 Create backup of public/ro/ before migration: cp -r public/ro public/ro.backup.pre-v5
         
     | 
| 
      
 174 
     | 
    
         
            +
            - [ ] T074 Run migration on public/ro/posts/ using the migration tool
         
     | 
| 
      
 175 
     | 
    
         
            +
            - [ ] T075 Run migration on public/ro/pages/ using the migration tool
         
     | 
| 
      
 176 
     | 
    
         
            +
            - [ ] T076 Run migration on public/ro/nerd/ using the migration tool
         
     | 
| 
      
 177 
     | 
    
         
            +
            - [ ] T077 Verify all migrated examples load correctly via ro console
         
     | 
| 
      
 178 
     | 
    
         
            +
            - [ ] T078 [P] Update README.md with new structure examples and migration instructions
         
     | 
| 
      
 179 
     | 
    
         
            +
            - [ ] T079 [P] Create MIGR ATION_GUIDE.md with step-by-step migration instructions
         
     | 
| 
      
 180 
     | 
    
         
            +
            - [ ] T080 [P] Update CHANGELOG.md with breaking changes for v5.0.0
         
     | 
| 
      
 181 
     | 
    
         
            +
            - [ ] T081 [P] Update gem version in lib/ro.rb from 4.4.0 to 5.0.0
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
            ---
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
            ## Phase 8: Polish & Cross-Cutting Concerns
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
            **Purpose**: Final cleanup and verification
         
     | 
| 
      
 188 
     | 
    
         
            +
             
     | 
| 
      
 189 
     | 
    
         
            +
            - [ ] T082 [P] Add inline documentation (YARD comments) to modified methods in lib/ro/node.rb
         
     | 
| 
      
 190 
     | 
    
         
            +
            - [ ] T083 [P] Add inline documentation to new Migrator class in lib/ro/script/migrator.rb
         
     | 
| 
      
 191 
     | 
    
         
            +
            - [ ] T084 [P] Add inline documentation to modified Collection methods in lib/ro/collection.rb
         
     | 
| 
      
 192 
     | 
    
         
            +
            - [ ] T085 Run all tests (rake test) and ensure 100% pass
         
     | 
| 
      
 193 
     | 
    
         
            +
            - [ ] T086 Validate quickstart.md examples work with new structure
         
     | 
| 
      
 194 
     | 
    
         
            +
            - [ ] T087 [P] Code cleanup: remove any debug logging or commented-out old code
         
     | 
| 
      
 195 
     | 
    
         
            +
            - [ ] T088 [P] Verify .gitignore covers all test artifacts and backup directories
         
     | 
| 
      
 196 
     | 
    
         
            +
            - [ ] T089 Final integration test: build static API with ro builder, verify output matches expectations
         
     | 
| 
      
 197 
     | 
    
         
            +
            - [ ] T090 Performance benchmark: measure asset lookup time with 1000+ nodes, verify <100ms (SC-001)
         
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
            ---
         
     | 
| 
      
 200 
     | 
    
         
            +
             
     | 
| 
      
 201 
     | 
    
         
            +
            ## Dependencies & Execution Order
         
     | 
| 
      
 202 
     | 
    
         
            +
             
     | 
| 
      
 203 
     | 
    
         
            +
            ### Phase Dependencies
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
      
 205 
     | 
    
         
            +
            - **Setup (Phase 1)**: No dependencies - can start immediately
         
     | 
| 
      
 206 
     | 
    
         
            +
            - **Foundational (Phase 2)**: Depends on Setup completion - BLOCKS all user stories
         
     | 
| 
      
 207 
     | 
    
         
            +
            - **User Stories (Phase 3-5)**: All depend on Foundational phase completion
         
     | 
| 
      
 208 
     | 
    
         
            +
              - US1 (Read) → Can start immediately after Foundational
         
     | 
| 
      
 209 
     | 
    
         
            +
              - US2 (Write) → Can start immediately after Foundational, but logically should follow US1
         
     | 
| 
      
 210 
     | 
    
         
            +
              - US3 (Migrate) → Can start immediately after Foundational, but logically should follow US1+US2 since it needs to verify both reading and writing work
         
     | 
| 
      
 211 
     | 
    
         
            +
            - **Integration (Phase 6)**: Depends on all user stories (US1, US2, US3)
         
     | 
| 
      
 212 
     | 
    
         
            +
            - **Examples & Docs (Phase 7)**: Depends on US3 (migration tool) being complete
         
     | 
| 
      
 213 
     | 
    
         
            +
            - **Polish (Phase 8)**: Depends on all previous phases
         
     | 
| 
      
 214 
     | 
    
         
            +
             
     | 
| 
      
 215 
     | 
    
         
            +
            ### User Story Dependencies
         
     | 
| 
      
 216 
     | 
    
         
            +
             
     | 
| 
      
 217 
     | 
    
         
            +
            - **User Story 1 (P1)**: Can start after Foundational (Phase 2) - No dependencies on other stories
         
     | 
| 
      
 218 
     | 
    
         
            +
            - **User Story 2 (P2)**: Can start after Foundational (Phase 2) - Logically depends on US1 (need to read before write), but could be developed in parallel
         
     | 
| 
      
 219 
     | 
    
         
            +
            - **User Story 3 (P3)**: Can start after Foundational (Phase 2) - Logically depends on US1+US2 (needs to verify both work), but could be developed in parallel
         
     | 
| 
      
 220 
     | 
    
         
            +
             
     | 
| 
      
 221 
     | 
    
         
            +
            ### Within Each User Story
         
     | 
| 
      
 222 
     | 
    
         
            +
             
     | 
| 
      
 223 
     | 
    
         
            +
            - Tests MUST be written and FAIL before implementation
         
     | 
| 
      
 224 
     | 
    
         
            +
            - Tests can run in parallel (all marked [P])
         
     | 
| 
      
 225 
     | 
    
         
            +
            - Implementation tasks run sequentially (Collection → Node → Asset) due to dependencies
         
     | 
| 
      
 226 
     | 
    
         
            +
            - Each story must be complete and independently testable before moving to next priority
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
      
 228 
     | 
    
         
            +
            ### Parallel Opportunities
         
     | 
| 
      
 229 
     | 
    
         
            +
             
     | 
| 
      
 230 
     | 
    
         
            +
            - **Setup tasks**: T002, T003, T004, T005 can run in parallel
         
     | 
| 
      
 231 
     | 
    
         
            +
            - **Foundational fixtures**: T006-T010 can all run in parallel
         
     | 
| 
      
 232 
     | 
    
         
            +
            - **US1 tests**: T011-T017, T019 can run in parallel (T018 may depend on others)
         
     | 
| 
      
 233 
     | 
    
         
            +
            - **US2 tests**: T032-T036 can all run in parallel
         
     | 
| 
      
 234 
     | 
    
         
            +
            - **US3 tests**: T042-T050, T052, T053 can run in parallel
         
     | 
| 
      
 235 
     | 
    
         
            +
            - **Integration tests**: T066, T067, T069, T070 can run in parallel
         
     | 
| 
      
 236 
     | 
    
         
            +
            - **Documentation**: T078-T084 can run in parallel
         
     | 
| 
      
 237 
     | 
    
         
            +
            - **Polish**: T082-T084, T087, T088 can run in parallel
         
     | 
| 
      
 238 
     | 
    
         
            +
             
     | 
| 
      
 239 
     | 
    
         
            +
            ### Sequential Requirements
         
     | 
| 
      
 240 
     | 
    
         
            +
             
     | 
| 
      
 241 
     | 
    
         
            +
            - T020-T030 (US1 implementation) must run in order due to class dependencies
         
     | 
| 
      
 242 
     | 
    
         
            +
            - T037-T040 (US2 implementation) must run in order
         
     | 
| 
      
 243 
     | 
    
         
            +
            - T054-T065 (US3 implementation) must run in order
         
     | 
| 
      
 244 
     | 
    
         
            +
            - T073-T077 (example migration) must run in order (backup before migrate)
         
     | 
| 
      
 245 
     | 
    
         
            +
             
     | 
| 
      
 246 
     | 
    
         
            +
            ---
         
     | 
| 
      
 247 
     | 
    
         
            +
             
     | 
| 
      
 248 
     | 
    
         
            +
            ## Parallel Example: User Story 1 Tests
         
     | 
| 
      
 249 
     | 
    
         
            +
             
     | 
| 
      
 250 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 251 
     | 
    
         
            +
            # Launch all US1 unit tests together:
         
     | 
| 
      
 252 
     | 
    
         
            +
            Task: "Create unit test for Collection#metadata_files in test/unit/collection_test.rb"
         
     | 
| 
      
 253 
     | 
    
         
            +
            Task: "Create unit test for Collection#each with new structure in test/unit/collection_test.rb"
         
     | 
| 
      
 254 
     | 
    
         
            +
            Task: "Create unit test for Node initialization with metadata_file parameter in test/unit/node_test.rb"
         
     | 
| 
      
 255 
     | 
    
         
            +
            Task: "Create unit test for Node#id derived from metadata filename in test/unit/node_test.rb"
         
     | 
| 
      
 256 
     | 
    
         
            +
            Task: "Create unit test for Node#asset_dir returning node directory in test/unit/node_test.rb"
         
     | 
| 
      
 257 
     | 
    
         
            +
            Task: "Create unit test for Asset path resolution without assets/ prefix in test/unit/asset_test.rb"
         
     | 
| 
      
 258 
     | 
    
         
            +
            ```
         
     | 
| 
      
 259 
     | 
    
         
            +
             
     | 
| 
      
 260 
     | 
    
         
            +
            ---
         
     | 
| 
      
 261 
     | 
    
         
            +
             
     | 
| 
      
 262 
     | 
    
         
            +
            ## Parallel Example: User Story 3 Tests
         
     | 
| 
      
 263 
     | 
    
         
            +
             
     | 
| 
      
 264 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 265 
     | 
    
         
            +
            # Launch all US3 unit tests together:
         
     | 
| 
      
 266 
     | 
    
         
            +
            Task: "Create unit test for Migrator#initialize in test/unit/migrator_test.rb"
         
     | 
| 
      
 267 
     | 
    
         
            +
            Task: "Create unit test for Migrator#validate detecting old structure in test/unit/migrator_test.rb"
         
     | 
| 
      
 268 
     | 
    
         
            +
            Task: "Create unit test for Migrator#preview generating migration plan in test/unit/migrator_test.rb"
         
     | 
| 
      
 269 
     | 
    
         
            +
            Task: "Create unit test for Migrator#backup creating timestamped backup in test/unit/migrator_test.rb"
         
     | 
| 
      
 270 
     | 
    
         
            +
            Task: "Create unit test for Migrator#rollback restoring from backup in test/unit/migrator_test.rb"
         
     | 
| 
      
 271 
     | 
    
         
            +
            ```
         
     | 
| 
      
 272 
     | 
    
         
            +
             
     | 
| 
      
 273 
     | 
    
         
            +
            ---
         
     | 
| 
      
 274 
     | 
    
         
            +
             
     | 
| 
      
 275 
     | 
    
         
            +
            ## Implementation Strategy
         
     | 
| 
      
 276 
     | 
    
         
            +
             
     | 
| 
      
 277 
     | 
    
         
            +
            ### MVP First (User Story 1 Only)
         
     | 
| 
      
 278 
     | 
    
         
            +
             
     | 
| 
      
 279 
     | 
    
         
            +
            1. Complete Phase 1: Setup (T001-T005)
         
     | 
| 
      
 280 
     | 
    
         
            +
            2. Complete Phase 2: Foundational (T006-T010) - CRITICAL
         
     | 
| 
      
 281 
     | 
    
         
            +
            3. Complete Phase 3: User Story 1 (T011-T031)
         
     | 
| 
      
 282 
     | 
    
         
            +
            4. **STOP and VALIDATE**: Run all US1 tests, verify assets load from new structure
         
     | 
| 
      
 283 
     | 
    
         
            +
            5. This gives you a working MVP that can read assets in the new simplified format
         
     | 
| 
      
 284 
     | 
    
         
            +
             
     | 
| 
      
 285 
     | 
    
         
            +
            ### Incremental Delivery
         
     | 
| 
      
 286 
     | 
    
         
            +
             
     | 
| 
      
 287 
     | 
    
         
            +
            1. Complete Setup + Foundational → Test infrastructure ready
         
     | 
| 
      
 288 
     | 
    
         
            +
            2. Add User Story 1 (Read) → Test independently → MVP working! Can read new structure
         
     | 
| 
      
 289 
     | 
    
         
            +
            3. Add User Story 2 (Write) → Test independently → Can create/update assets in new structure
         
     | 
| 
      
 290 
     | 
    
         
            +
            4. Add User Story 3 (Migrate) → Test independently → Can migrate old data to new structure
         
     | 
| 
      
 291 
     | 
    
         
            +
            5. Complete Integration, Examples, Polish → Production ready for v5.0.0 release
         
     | 
| 
      
 292 
     | 
    
         
            +
             
     | 
| 
      
 293 
     | 
    
         
            +
            ### Parallel Team Strategy
         
     | 
| 
      
 294 
     | 
    
         
            +
             
     | 
| 
      
 295 
     | 
    
         
            +
            With multiple developers:
         
     | 
| 
      
 296 
     | 
    
         
            +
             
     | 
| 
      
 297 
     | 
    
         
            +
            1. Team completes Setup (Phase 1) + Foundational (Phase 2) together
         
     | 
| 
      
 298 
     | 
    
         
            +
            2. Once Foundational is done:
         
     | 
| 
      
 299 
     | 
    
         
            +
               - Developer A: User Story 1 (Read) - T011-T031
         
     | 
| 
      
 300 
     | 
    
         
            +
               - Developer B: User Story 2 (Write) - T032-T041 (may need to wait for some US1 completion)
         
     | 
| 
      
 301 
     | 
    
         
            +
               - Developer C: User Story 3 (Migrate) - T042-T065
         
     | 
| 
      
 302 
     | 
    
         
            +
            3. Stories complete and integrate independently
         
     | 
| 
      
 303 
     | 
    
         
            +
            4. Team collaborates on Integration (Phase 6) and Polish (Phases 7-8)
         
     | 
| 
      
 304 
     | 
    
         
            +
             
     | 
| 
      
 305 
     | 
    
         
            +
            ### TDD Workflow (Required for this feature)
         
     | 
| 
      
 306 
     | 
    
         
            +
             
     | 
| 
      
 307 
     | 
    
         
            +
            This feature follows strict TDD due to zero existing test coverage:
         
     | 
| 
      
 308 
     | 
    
         
            +
             
     | 
| 
      
 309 
     | 
    
         
            +
            1. **Red**: Write tests first (T011-T019 for US1)
         
     | 
| 
      
 310 
     | 
    
         
            +
            2. **Verify Red**: Run tests, ensure they FAIL (proves tests are testing something)
         
     | 
| 
      
 311 
     | 
    
         
            +
            3. **Green**: Implement code (T020-T030 for US1)
         
     | 
| 
      
 312 
     | 
    
         
            +
            4. **Verify Green**: Run tests, ensure they PASS
         
     | 
| 
      
 313 
     | 
    
         
            +
            5. **Refactor**: Clean up code while keeping tests green
         
     | 
| 
      
 314 
     | 
    
         
            +
            6. **Repeat** for each user story
         
     | 
| 
      
 315 
     | 
    
         
            +
             
     | 
| 
      
 316 
     | 
    
         
            +
            ---
         
     | 
| 
      
 317 
     | 
    
         
            +
             
     | 
| 
      
 318 
     | 
    
         
            +
            ## Notes
         
     | 
| 
      
 319 
     | 
    
         
            +
             
     | 
| 
      
 320 
     | 
    
         
            +
            - [P] tasks = different files, no dependencies - can run in parallel
         
     | 
| 
      
 321 
     | 
    
         
            +
            - [Story] label maps task to specific user story for traceability
         
     | 
| 
      
 322 
     | 
    
         
            +
            - Each user story should be independently completable and testable
         
     | 
| 
      
 323 
     | 
    
         
            +
            - Verify tests fail before implementing (TDD approach)
         
     | 
| 
      
 324 
     | 
    
         
            +
            - Commit after each task or logical group
         
     | 
| 
      
 325 
     | 
    
         
            +
            - Stop at any checkpoint to validate story independently
         
     | 
| 
      
 326 
     | 
    
         
            +
            - **CRITICAL**: This is a breaking change (v4.x → v5.0.0) - all existing ro users must migrate their data
         
     | 
| 
      
 327 
     | 
    
         
            +
            - **Performance target**: <100ms asset lookup for 10,000 assets (SC-001)
         
     | 
| 
      
 328 
     | 
    
         
            +
            - **Migration requirement**: Zero data loss (SC-002)
         
     | 
| 
      
 329 
     | 
    
         
            +
            - Backward compatibility: Old structure should still work during transition period (FR-011)
         
     | 
| 
      
 330 
     | 
    
         
            +
             
     | 
| 
      
 331 
     | 
    
         
            +
            ---
         
     | 
| 
      
 332 
     | 
    
         
            +
             
     | 
| 
      
 333 
     | 
    
         
            +
            ## Task Count Summary
         
     | 
| 
      
 334 
     | 
    
         
            +
             
     | 
| 
      
 335 
     | 
    
         
            +
            - **Total Tasks**: 90
         
     | 
| 
      
 336 
     | 
    
         
            +
            - **Setup (Phase 1)**: 5 tasks
         
     | 
| 
      
 337 
     | 
    
         
            +
            - **Foundational (Phase 2)**: 5 tasks
         
     | 
| 
      
 338 
     | 
    
         
            +
            - **User Story 1 (Phase 3)**: 21 tasks (9 tests + 12 implementation)
         
     | 
| 
      
 339 
     | 
    
         
            +
            - **User Story 2 (Phase 4)**: 10 tasks (5 tests + 5 implementation)
         
     | 
| 
      
 340 
     | 
    
         
            +
            - **User Story 3 (Phase 5)**: 24 tasks (12 tests + 12 implementation)
         
     | 
| 
      
 341 
     | 
    
         
            +
            - **Integration (Phase 6)**: 7 tasks
         
     | 
| 
      
 342 
     | 
    
         
            +
            - **Examples & Docs (Phase 7)**: 9 tasks
         
     | 
| 
      
 343 
     | 
    
         
            +
            - **Polish (Phase 8)**: 9 tasks
         
     | 
| 
      
 344 
     | 
    
         
            +
             
     | 
| 
      
 345 
     | 
    
         
            +
            **Parallel Opportunities**: 40+ tasks can run in parallel (marked with [P])
         
     | 
| 
      
 346 
     | 
    
         
            +
             
     | 
| 
      
 347 
     | 
    
         
            +
            **MVP Scope**: Phases 1-3 only (31 tasks) delivers working read capability with new structure
         
     | 
| 
      
 348 
     | 
    
         
            +
             
     | 
| 
      
 349 
     | 
    
         
            +
            **Full Feature**: All 90 tasks delivers complete v5.0.0 with read, write, migrate, tests, examples, and documentation
         
     | 
| 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            test asset
         
     | 
| 
         @@ -0,0 +1,165 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
            # Integration tests for Ro with new structure support
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative '../test_helper'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            class RoIntegrationTest < RoTestCase
         
     | 
| 
      
 7 
     | 
    
         
            +
              def setup
         
     | 
| 
      
 8 
     | 
    
         
            +
                @root = Ro::Root.new(new_structure_path)
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              # T018: Test loading collection with new structure
         
     | 
| 
      
 12 
     | 
    
         
            +
              def test_load_collection_with_new_structure
         
     | 
| 
      
 13 
     | 
    
         
            +
                posts = @root['posts']
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                assert_not_nil posts, "Should load posts collection"
         
     | 
| 
      
 16 
     | 
    
         
            +
                assert posts.is_a?(Ro::Collection), "Should return Collection instance"
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              def test_collection_discovers_nodes_from_metadata_files
         
     | 
| 
      
 20 
     | 
    
         
            +
                posts = @root['posts']
         
     | 
| 
      
 21 
     | 
    
         
            +
                nodes = posts.nodes
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                assert nodes.any?, "Collection should discover nodes"
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                # Should find all nodes with metadata files
         
     | 
| 
      
 26 
     | 
    
         
            +
                node_ids = nodes.map(&:id)
         
     | 
| 
      
 27 
     | 
    
         
            +
                assert node_ids.include?('sample-post'), "Should find sample-post"
         
     | 
| 
      
 28 
     | 
    
         
            +
                assert node_ids.include?('metadata-only'), "Should find metadata-only"
         
     | 
| 
      
 29 
     | 
    
         
            +
                assert node_ids.include?('nested-test'), "Should find nested-test"
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              def test_node_loads_metadata_from_external_file
         
     | 
| 
      
 33 
     | 
    
         
            +
                posts = @root['posts']
         
     | 
| 
      
 34 
     | 
    
         
            +
                node = posts['sample-post']
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                assert_not_nil node, "Should find sample-post node"
         
     | 
| 
      
 37 
     | 
    
         
            +
                assert_equal 'Sample Post (New Structure)', node[:title], "Should load metadata from identifier.yml"
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
              def test_node_loads_assets_from_node_directory
         
     | 
| 
      
 41 
     | 
    
         
            +
                posts = @root['posts']
         
     | 
| 
      
 42 
     | 
    
         
            +
                node = posts['sample-post']
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                asset_paths = node.asset_paths
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                assert asset_paths.any?, "Node should have assets"
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                # Assets should be in node/assets/ subdirectory
         
     | 
| 
      
 49 
     | 
    
         
            +
                asset_paths.each do |path|
         
     | 
| 
      
 50 
     | 
    
         
            +
                  expected_dir = new_structure_path / 'posts' / 'sample-post' / 'assets'
         
     | 
| 
      
 51 
     | 
    
         
            +
                  assert path.to_s.start_with?(expected_dir.to_s), "Assets should be in assets/ subdirectory"
         
     | 
| 
      
 52 
     | 
    
         
            +
                  assert path.to_s.include?('/assets/'), "Asset paths should contain /assets/ subdirectory"
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
      
 54 
     | 
    
         
            +
              end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
              def test_nested_assets_preserved
         
     | 
| 
      
 57 
     | 
    
         
            +
                posts = @root['posts']
         
     | 
| 
      
 58 
     | 
    
         
            +
                node = posts['nested-test']
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                asset_paths = node.asset_paths
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                # Should find nested asset
         
     | 
| 
      
 63 
     | 
    
         
            +
                nested_asset = asset_paths.find { |p| p.to_s.include?('subdirectory/image.png') }
         
     | 
| 
      
 64 
     | 
    
         
            +
                assert_not_nil nested_asset, "Should find nested asset in subdirectory"
         
     | 
| 
      
 65 
     | 
    
         
            +
              end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
              def test_multiple_collections
         
     | 
| 
      
 68 
     | 
    
         
            +
                posts = @root['posts']
         
     | 
| 
      
 69 
     | 
    
         
            +
                mixed = @root['mixed']
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                assert_not_nil posts, "Should load posts collection"
         
     | 
| 
      
 72 
     | 
    
         
            +
                assert_not_nil mixed, "Should load mixed collection"
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                assert posts.nodes.size > 0, "Posts should have nodes"
         
     | 
| 
      
 75 
     | 
    
         
            +
                assert mixed.nodes.size > 0, "Mixed should have nodes"
         
     | 
| 
      
 76 
     | 
    
         
            +
              end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
              def test_multiple_metadata_formats
         
     | 
| 
      
 79 
     | 
    
         
            +
                mixed = @root['mixed']
         
     | 
| 
      
 80 
     | 
    
         
            +
                nodes = mixed.nodes
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                # Should discover both .yml and .json files
         
     | 
| 
      
 83 
     | 
    
         
            +
                node_ids = nodes.map(&:id)
         
     | 
| 
      
 84 
     | 
    
         
            +
                assert node_ids.include?('test-yaml'), "Should find test-yaml.yml"
         
     | 
| 
      
 85 
     | 
    
         
            +
                assert node_ids.include?('test-json'), "Should find test-json.json"
         
     | 
| 
      
 86 
     | 
    
         
            +
              end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
              # T019: Test metadata-only nodes (FR-007)
         
     | 
| 
      
 89 
     | 
    
         
            +
              def test_metadata_only_node
         
     | 
| 
      
 90 
     | 
    
         
            +
                posts = @root['posts']
         
     | 
| 
      
 91 
     | 
    
         
            +
                node = posts['metadata-only']
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                assert_not_nil node, "Should find metadata-only node"
         
     | 
| 
      
 94 
     | 
    
         
            +
                assert_equal 'Metadata Only Node', node[:title], "Should load metadata"
         
     | 
| 
      
 95 
     | 
    
         
            +
              end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
              def test_metadata_only_node_has_no_assets
         
     | 
| 
      
 98 
     | 
    
         
            +
                posts = @root['posts']
         
     | 
| 
      
 99 
     | 
    
         
            +
                node = posts['metadata-only']
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                asset_paths = node.asset_paths
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                # Metadata-only node should have no assets (directory doesn't exist)
         
     | 
| 
      
 104 
     | 
    
         
            +
                assert_equal 0, asset_paths.size, "Metadata-only node should have no assets"
         
     | 
| 
      
 105 
     | 
    
         
            +
              end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
              def test_metadata_only_node_asset_dir_not_required
         
     | 
| 
      
 108 
     | 
    
         
            +
                posts = @root['posts']
         
     | 
| 
      
 109 
     | 
    
         
            +
                node = posts['metadata-only']
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                # Node should work fine even without asset directory existing
         
     | 
| 
      
 112 
     | 
    
         
            +
                assert_not_nil node.asset_dir, "asset_dir should still return a path"
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                # But the directory doesn't have to exist
         
     | 
| 
      
 115 
     | 
    
         
            +
                # (This is valid per FR-007: handle metadata-only nodes)
         
     | 
| 
      
 116 
     | 
    
         
            +
              end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
              def test_full_workflow_read
         
     | 
| 
      
 119 
     | 
    
         
            +
                # Full integration: Root → Collection → Node → Assets
         
     | 
| 
      
 120 
     | 
    
         
            +
                root = Ro::Root.new(new_structure_path)
         
     | 
| 
      
 121 
     | 
    
         
            +
                posts = root['posts']
         
     | 
| 
      
 122 
     | 
    
         
            +
                node = posts['sample-post']
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                # Should load everything correctly
         
     | 
| 
      
 125 
     | 
    
         
            +
                assert_equal 'Sample Post (New Structure)', node[:title]
         
     | 
| 
      
 126 
     | 
    
         
            +
                assert_equal 'Test Author', node[:author]
         
     | 
| 
      
 127 
     | 
    
         
            +
                assert node.asset_paths.any?
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                # Assets should be accessible
         
     | 
| 
      
 130 
     | 
    
         
            +
                image_asset = node.asset_paths.find { |p| p.basename.to_s == 'image.jpg' }
         
     | 
| 
      
 131 
     | 
    
         
            +
                assert_not_nil image_asset, "Should find image.jpg asset"
         
     | 
| 
      
 132 
     | 
    
         
            +
              end
         
     | 
| 
      
 133 
     | 
    
         
            +
            end
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
            # Run the tests
         
     | 
| 
      
 136 
     | 
    
         
            +
            if __FILE__ == $0
         
     | 
| 
      
 137 
     | 
    
         
            +
              test = RoIntegrationTest.new
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
      
 139 
     | 
    
         
            +
              puts "Running Ro integration tests..."
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
              tests = [
         
     | 
| 
      
 142 
     | 
    
         
            +
                :test_load_collection_with_new_structure,
         
     | 
| 
      
 143 
     | 
    
         
            +
                :test_collection_discovers_nodes_from_metadata_files,
         
     | 
| 
      
 144 
     | 
    
         
            +
                :test_node_loads_metadata_from_external_file,
         
     | 
| 
      
 145 
     | 
    
         
            +
                :test_node_loads_assets_from_node_directory,
         
     | 
| 
      
 146 
     | 
    
         
            +
                :test_nested_assets_preserved,
         
     | 
| 
      
 147 
     | 
    
         
            +
                :test_multiple_collections,
         
     | 
| 
      
 148 
     | 
    
         
            +
                :test_multiple_metadata_formats,
         
     | 
| 
      
 149 
     | 
    
         
            +
                :test_metadata_only_node,
         
     | 
| 
      
 150 
     | 
    
         
            +
                :test_metadata_only_node_has_no_assets,
         
     | 
| 
      
 151 
     | 
    
         
            +
                :test_metadata_only_node_asset_dir_not_required,
         
     | 
| 
      
 152 
     | 
    
         
            +
                :test_full_workflow_read
         
     | 
| 
      
 153 
     | 
    
         
            +
              ]
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
              tests.each do |test_method|
         
     | 
| 
      
 156 
     | 
    
         
            +
                begin
         
     | 
| 
      
 157 
     | 
    
         
            +
                  test.setup
         
     | 
| 
      
 158 
     | 
    
         
            +
                  test.send(test_method)
         
     | 
| 
      
 159 
     | 
    
         
            +
                  puts "✓ #{test_method}"
         
     | 
| 
      
 160 
     | 
    
         
            +
                rescue => e
         
     | 
| 
      
 161 
     | 
    
         
            +
                  puts "✗ #{test_method}: #{e.message}"
         
     | 
| 
      
 162 
     | 
    
         
            +
                  puts "  #{e.backtrace.first}"
         
     | 
| 
      
 163 
     | 
    
         
            +
                end
         
     | 
| 
      
 164 
     | 
    
         
            +
              end
         
     | 
| 
      
 165 
     | 
    
         
            +
            end
         
     |