Skip to content

Conversation

@koriym
Copy link
Member

@koriym koriym commented Jan 24, 2026

Summary

Machine-Readable Logging (BREAKING CHANGE)

  • Change RepositoryLoggerInterface::log() signature to structured format
  • Output JSON format: {"op":"save-value","uri":"...","tags":[...],"ttl":3600}
  • Add JSON Schema for log format validation

LLM Documentation

  • Add docs/llms.txt - Concise project overview
  • Add docs/llms-full.txt - Comprehensive documentation
  • Add docs/schemas/repository-log.json - Log format schema

Test Enhancement

  • Add cache dependency tests with ETag verification
  • Add array parameter tests to RepositoryLoggerTest

Dependencies

  • Require ray/aop ^2.19.1 for PHP 8.5 attribute fix

Files Changed

Category Files
Logger src/RepositoryLogger.php, src/RepositoryLoggerInterface.php
Callers src/ResourceStorage.php, src/QueryRepository.php, src/DonutRepository.php
Docs docs/llms.txt, docs/llms-full.txt, docs/schemas/repository-log.json
Tests tests/RepositoryLoggerTest.php, tests/CacheDependencyTest.php

Breaking Change

// Before
$logger->log('save-value uri:%s tags:%s ttl:%s', $uri, $tags, $ttl);

// After
$logger->log('save-value', ['uri' => $uri, 'tags' => $tags, 'ttl' => $ttl]);

検証:

AIの深い理解を試すために以下のログで質問

シナリオ

=== Cache Dependency Demo ===

This demo executes the following scenarios:

1. Initial access to level-one (3-level chain)
   - LevelOne embeds LevelTwo embeds LevelThree
   - All three will be cache-miss, dependencies registered

2. Re-access level-one
   - Should be cache-hit

3. Purge level-three (grandchild)
   - Should cascade invalidate level-two and level-one

4. Re-access level-one after purge
   - All three should be cache-miss (regenerated)

5. Access ParentA and ParentB
   - Both embed ChildC (shared dependency)

6. Purge child-c
   - Should invalidate both ParentA and ParentB

7. Re-access both parents after purge
   - Both should be cache-miss (regenerated)

ログ

{"op":"request-start","uri":"page://self/dep/level-one"}
{"op":"cache-miss","uri":"page://self/dep/level-one"}
{"op":"put-query-repository","uri":"page://self/dep/level-one"}
{"op":"invalidate-etag","tags":["_dep_level-one_"]}
{"op":"cache-miss","uri":"page://self/dep/level-two"}
{"op":"put-query-repository","uri":"page://self/dep/level-two"}
{"op":"invalidate-etag","tags":["_dep_level-two_"]}
{"op":"cache-miss","uri":"page://self/dep/level-three"}
{"op":"put-query-repository","uri":"page://self/dep/level-three"}
{"op":"invalidate-etag","tags":["_dep_level-three_"]}
{"op":"save-etag","uri":"page://self/dep/level-three","etag":"3378406738","surrogateKeys":["_dep_level-three_"]}
{"op":"save-value","uri":"page://self/dep/level-three","tags":["3378406738","_dep_level-three_"],"ttl":31536000}
{"op":"depends-on","parent":"page://self/dep/level-two","child":"page://self/dep/level-three","childTags":["_dep_level-three_"]}
{"op":"save-etag","uri":"page://self/dep/level-two","etag":"3579205492","surrogateKeys":["_dep_level-three_","_dep_level-two_"]}
{"op":"cache-hit","uri":"page://self/dep/level-three"}
{"op":"save-value","uri":"page://self/dep/level-two","tags":["3579205492","_dep_level-two_","_dep_level-three_"],"ttl":31536000}
{"op":"depends-on","parent":"page://self/dep/level-one","child":"page://self/dep/level-two","childTags":["_dep_level-two_","_dep_level-three_"]}
{"op":"save-etag","uri":"page://self/dep/level-one","etag":"3093767641","surrogateKeys":["_dep_level-two_","_dep_level-three_","_dep_level-one_"]}
{"op":"cache-hit","uri":"page://self/dep/level-two"}
{"op":"save-value","uri":"page://self/dep/level-one","tags":["3093767641","_dep_level-one_","_dep_level-two_","_dep_level-three_"],"ttl":31536000}
{"op":"request-start","uri":"page://self/dep/level-one"}
{"op":"cache-hit","uri":"page://self/dep/level-one"}
{"op":"request-start","uri":"page://self/dep/level-three","method":"purge"}
{"op":"purge-query-repository","uri":"page://self/dep/level-three"}
{"op":"invalidate-etag","tags":["_dep_level-three_"]}
{"op":"request-start","uri":"page://self/dep/level-one"}
{"op":"cache-miss","uri":"page://self/dep/level-one"}
{"op":"put-query-repository","uri":"page://self/dep/level-one"}
{"op":"invalidate-etag","tags":["_dep_level-one_"]}
{"op":"cache-miss","uri":"page://self/dep/level-two"}
{"op":"put-query-repository","uri":"page://self/dep/level-two"}
{"op":"invalidate-etag","tags":["_dep_level-two_"]}
{"op":"cache-miss","uri":"page://self/dep/level-three"}
{"op":"put-query-repository","uri":"page://self/dep/level-three"}
{"op":"invalidate-etag","tags":["_dep_level-three_"]}
{"op":"save-etag","uri":"page://self/dep/level-three","etag":"3378406738","surrogateKeys":["_dep_level-three_"]}
{"op":"save-value","uri":"page://self/dep/level-three","tags":["3378406738","_dep_level-three_"],"ttl":31536000}
{"op":"depends-on","parent":"page://self/dep/level-two","child":"page://self/dep/level-three","childTags":["_dep_level-three_"]}
{"op":"save-etag","uri":"page://self/dep/level-two","etag":"3579205492","surrogateKeys":["_dep_level-three_","_dep_level-two_"]}
{"op":"cache-hit","uri":"page://self/dep/level-three"}
{"op":"save-value","uri":"page://self/dep/level-two","tags":["3579205492","_dep_level-two_","_dep_level-three_"],"ttl":31536000}
{"op":"depends-on","parent":"page://self/dep/level-one","child":"page://self/dep/level-two","childTags":["_dep_level-two_","_dep_level-three_"]}
{"op":"save-etag","uri":"page://self/dep/level-one","etag":"3093767641","surrogateKeys":["_dep_level-two_","_dep_level-three_","_dep_level-one_"]}
{"op":"cache-hit","uri":"page://self/dep/level-two"}
{"op":"save-value","uri":"page://self/dep/level-one","tags":["3093767641","_dep_level-one_","_dep_level-two_","_dep_level-three_"],"ttl":31536000}
{"op":"request-start","uri":"page://self/dep/parent-a"}
{"op":"cache-miss","uri":"page://self/dep/parent-a"}
{"op":"put-query-repository","uri":"page://self/dep/parent-a"}
{"op":"invalidate-etag","tags":["_dep_parent-a_"]}
{"op":"cache-miss","uri":"page://self/dep/child-c"}
{"op":"put-query-repository","uri":"page://self/dep/child-c"}
{"op":"invalidate-etag","tags":["_dep_child-c_"]}
{"op":"save-etag","uri":"page://self/dep/child-c","etag":"2837164676","surrogateKeys":["_dep_child-c_"]}
{"op":"save-value","uri":"page://self/dep/child-c","tags":["2837164676","_dep_child-c_"],"ttl":31536000}
{"op":"depends-on","parent":"page://self/dep/parent-a","child":"page://self/dep/child-c","childTags":["_dep_child-c_"]}
{"op":"save-etag","uri":"page://self/dep/parent-a","etag":"60172706","surrogateKeys":["_dep_child-c_","_dep_parent-a_"]}
{"op":"cache-hit","uri":"page://self/dep/child-c"}
{"op":"save-value","uri":"page://self/dep/parent-a","tags":["60172706","_dep_parent-a_","_dep_child-c_"],"ttl":31536000}
{"op":"request-start","uri":"page://self/dep/parent-b"}
{"op":"cache-miss","uri":"page://self/dep/parent-b"}
{"op":"put-query-repository","uri":"page://self/dep/parent-b"}
{"op":"invalidate-etag","tags":["_dep_parent-b_"]}
{"op":"cache-hit","uri":"page://self/dep/child-c"}
{"op":"depends-on","parent":"page://self/dep/parent-b","child":"page://self/dep/child-c","childTags":["_dep_child-c_"]}
{"op":"save-etag","uri":"page://self/dep/parent-b","etag":"1005023576","surrogateKeys":["_dep_child-c_","_dep_parent-b_"]}
{"op":"cache-hit","uri":"page://self/dep/child-c"}
{"op":"save-value","uri":"page://self/dep/parent-b","tags":["1005023576","_dep_parent-b_","_dep_child-c_"],"ttl":31536000}
{"op":"request-start","uri":"page://self/dep/child-c","method":"purge"}
{"op":"purge-query-repository","uri":"page://self/dep/child-c"}
{"op":"invalidate-etag","tags":["_dep_child-c_"]}
{"op":"request-start","uri":"page://self/dep/parent-a"}
{"op":"cache-miss","uri":"page://self/dep/parent-a"}
{"op":"put-query-repository","uri":"page://self/dep/parent-a"}
{"op":"invalidate-etag","tags":["_dep_parent-a_"]}
{"op":"cache-miss","uri":"page://self/dep/child-c"}
{"op":"put-query-repository","uri":"page://self/dep/child-c"}
{"op":"invalidate-etag","tags":["_dep_child-c_"]}
{"op":"save-etag","uri":"page://self/dep/child-c","etag":"2837164676","surrogateKeys":["_dep_child-c_"]}
{"op":"save-value","uri":"page://self/dep/child-c","tags":["2837164676","_dep_child-c_"],"ttl":31536000}
{"op":"depends-on","parent":"page://self/dep/parent-a","child":"page://self/dep/child-c","childTags":["_dep_child-c_"]}
{"op":"save-etag","uri":"page://self/dep/parent-a","etag":"60172706","surrogateKeys":["_dep_child-c_","_dep_parent-a_"]}
{"op":"cache-hit","uri":"page://self/dep/child-c"}
{"op":"save-value","uri":"page://self/dep/parent-a","tags":["60172706","_dep_parent-a_","_dep_child-c_"],"ttl":31536000}
{"op":"request-start","uri":"page://self/dep/parent-b"}
{"op":"cache-miss","uri":"page://self/dep/parent-b"}
{"op":"put-query-repository","uri":"page://self/dep/parent-b"}
{"op":"invalidate-etag","tags":["_dep_parent-b_"]}
{"op":"cache-hit","uri":"page://self/dep/child-c"}
{"op":"depends-on","parent":"page://self/dep/parent-b","child":"page://self/dep/child-c","childTags":["_dep_child-c_"]}
{"op":"save-etag","uri":"page://self/dep/parent-b","etag":"1005023576","surrogateKeys":["_dep_child-c_","_dep_parent-b_"]}
{"op":"cache-hit","uri":"page://self/dep/child-c"}
{"op":"save-value","uri":"page://self/dep/parent-b","tags":["1005023576","_dep_parent-b_","_dep_child-c_"],"ttl":31536000}

回答

⏺ まずログスキーマを確認します。                                                                                             02:18 PM  claude-opus-4-5-20251101        
                                                                                                                                                                       
⏺ Read(/Users/akihito/git/BEAR.QueryRepository/docs/schemas/repository-log.json)                                                                                       
  ⎿  Read 212 lines                                                                                                                                                    
  ⎿  PostToolUse:Callback hook succeeded: Success                                                                                                                      
                                                                                                                                                                       
⏺ ログを分析して各質問に回答します。                                                                                         02:19 PM  claude-opus-4-5-20251101        
                                                                                                                                                                       
  ログ分析結果                                                                                                                                                         
                                                                                                                                                                       
  1. level-twoをpurgeしたら、どのリソースが無効化される?                                                                                                              
                                                                                                                                                                       
  level-twoのsave-valueから、タグは["3579205492","_dep_level-two_","_dep_level-three_"]です。                                                                          
                                                                                                                                                                       
  purge時にはinvalidate-etagで_dep_level-two_タグが使われます。                                                                                                        
                                                                                                                                                                       
  無効化されるリソース:                                                                                                                                               
  - level-one - tags に_dep_level-two_を含んでいる                                                                                                                     
  - level-two - 自身のタグ                                                                                                                                             
                                                                                                                                                                       
  level-threeは_dep_level-two_を含まないため無効化されません。                                                                                                         
                                                                                                                                                                       
  ---                                                                                                                                                                  
  2. level-oneとlevel-threeのtagsの違いは?                                                                                                                            
  ┌─────────────┬────────────────────────────────────────────────────────────────────────┐                                                                             
  │  リソース   │                                  tags                                  │                                                                             
  ├─────────────┼────────────────────────────────────────────────────────────────────────┤                                                                             
  │ level-three │ ["3378406738","_dep_level-three_"]                                     │                                                                             
  ├─────────────┼────────────────────────────────────────────────────────────────────────┤                                                                             
  │ level-one   │ ["3093767641","_dep_level-one_","_dep_level-two_","_dep_level-three_"] │                                                                             
  └─────────────┴────────────────────────────────────────────────────────────────────────┘                                                                             
  違い:                                                                                                                                                               
  - level-threeは自身のETagとURIタグのみ(依存なし)                                                                                                                   
  - level-oneは子孫すべてのタグを継承(level-two, level-three)                                                                                                        
  - これにより、level-threeが変更されるとlevel-oneも自動的に無効化される                                                                                               
                                                                                                                                                                       
  ---                                                                                                                                                                  
  3. level-threeは何回生成され、何回キャッシュから返されたか?                                                                                                         
                                                                                                                                                                       
  ログを数えると:                                                                                                                                                     
  ┌──────────────────────────┬──────────────────────────────────────────────────┐                                                                                      
  │           操作           │                       回数                       │                                                                                      
  ├──────────────────────────┼──────────────────────────────────────────────────┤                                                                                      
  │ cache-miss (level-three) │ 4回(初回構築×2セット、purge後の再構築×2セット) │                                                                                      
  ├──────────────────────────┼──────────────────────────────────────────────────┤                                                                                      
  │ cache-hit (level-three)  │ 4回                                              │                                                                                      
  └──────────────────────────┴──────────────────────────────────────────────────┘                                                                                      
  生成:4回、キャッシュヒット:4回                                                                                                                                     
                                                                                                                                                                       
  ※キャッシュヒットは同一リクエスト内で子リソース参照時に発生(depends-on登録後の再参照)                                                                              
                                                                                                                                                                       
  ---                                                                                                                                                                  
  4. _dep_child-c_タグを共有しているリソースは?                                                                                                                       
                                                                                                                                                                       
  save-valueのtagsを確認:                                                                                                                                             
                                                                                                                                                                       
  - child-c: ["2837164676","_dep_child-c_"]                                                                                                                            
  - parent-a: ["60172706","_dep_parent-a_","_dep_child-c_"]                                                                                                            
  - parent-b: ["1005023576","_dep_parent-b_","_dep_child-c_"]                                                                                                          
                                                                                                                                                                       
  3つのリソースが_dep_child-c_を共有                                                                                                                                   
                                                                                                                                                                       
  ---                                                                                                                                                                  
  5. purge child-c の後、なぜ parent-a と parent-b の両方が cache-miss になったか?                                                                                    
                                                                                                                                                                       
  {"op":"purge-query-repository","uri":"page://self/dep/child-c"}                                                                                                      
  {"op":"invalidate-etag","tags":["_dep_child-c_"]}                                                                                                                    
                                                                                                                                                                       
  invalidate-etagで_dep_child-c_タグが無効化されると:                                                                                                                 
                                                                                                                                                                       
  - parent-aのtags: ["60172706","_dep_parent-a_","_dep_child-c_"] ← 含む                                                                                               
  - parent-bのtags: ["1005023576","_dep_parent-b_","_dep_child-c_"] ← 含む                                                                                             
                                                                                                                                                                       
  両方とも_dep_child-c_をタグに持っているため、タグベースの無効化により連鎖的にキャッシュが削除された。                                                                
                                                                                                                                                                       
  ---                                                                                                                                                                  
  6. level-oneのsurrogateKeysに含まれるタグから、何に依存しているか読み取れるか?                                                                                      
                                                                                                                                                                       
  {"op":"save-etag","uri":"page://self/dep/level-one","surrogateKeys":["_dep_level-two_","_dep_level-three_","_dep_level-one                                           
  _"]}                                                                                                                                                                 
                                                                                                                                                                       
  surrogateKeysから読み取れる依存関係:                                                                                                                                
  - _dep_level-two_ → level-twoに依存                                                                                                                                  
  - _dep_level-three_ → level-threeに依存                                                                                                                              
  - _dep_level-one_ → 自身のタグ                                                                                                                                       
                                                                                                                                                                       
  依存の推移的伝播が確認できる:level-one → level-two → level-three                                                                                                    
  の階層構造がフラット化されてsurrogateKeysに含まれている。                                                                                                            
                                                                                                                                                                       
  ---                                                                                                                                                                  
  7. parent-aとparent-bで、child-cへの依存登録のタイミングに違いはあるか?                                                                                             
                                                                                                                                                                       
  parent-a:                                                                                                                                                            
  {"op":"cache-miss","uri":"page://self/dep/child-c"}                                                                                                                  
  {"op":"put-query-repository","uri":"page://self/dep/child-c"}                                                                                                        
  ...                                                                                                                                                                  
  {"op":"save-value","uri":"page://self/dep/child-c",...}                                                                                                              
  {"op":"depends-on","parent":"page://self/dep/parent-a","child":"page://self/dep/child-c",...}                                                                        
  → child-cがcache-missで新規生成された後に依存登録                                                                                                                    
                                                                                                                                                                       
  parent-b:                                                                                                                                                            
  {"op":"cache-hit","uri":"page://self/dep/child-c"}                                                                                                                   
  {"op":"depends-on","parent":"page://self/dep/parent-b","child":"page://self/dep/child-c",...}                                                                        
  → child-cがcache-hit(既存キャッシュ利用)の後に依存登録                                                                                                             
                                                                                                                                                                       
  違い:                                                                                                                                                               
  - parent-a: child-cを新規生成してから依存登録                                                                                                                        
  - parent-b: child-cをキャッシュから取得してから依存登録                                                                                                              
                                                                                                                                                                       
  依存登録自体のタイミング(リソース取得後)は同じだが、child-cの取得方法が異なる。                                                                                    
                                                                                                                                                                       
✻ Brewed for 42s                                                                                                                                                       
                                                                                                                                                                       
❯ 3番の答えを再確認してください                                                                                                                                       
                                                                                                                                                                       
∴ Thinking…                                                                                                                                                            
                                                                                                                                                                       
  ユーザーは3番の答えを再確認するように求めています。ログを再度確認して、level-threeのcache-missとcache-hitの回数を正確に数え直します。                                
                                                                                                                                                                       
  ログを順番に見ていきます:                                                                                                                                           
                                                                                                                                                                       
  {"op":"request-start","uri":"page://self/dep/level-one"}                                                                                                             
  {"op":"cache-miss","uri":"page://self/dep/level-one"}                                                                                                                
  {"op":"put-query-repository","uri":"page://self/dep/level-one"}                                                                                                      
  {"op":"invalidate-etag","tags":["_dep_level-one_"]}                                                                                                                  
  {"op":"cache-miss","uri":"page://self/dep/level-two"}                                                                                                                
  {"op":"put-query-repository","uri":"page://self/dep/level-two"}                                                                                                      
  {"op":"invalidate-etag","tags":["_dep_level-two_"]}                                                                                                                  
  {"op":"cache-miss","uri":"page://self/dep/level-three"}  ← 1回目のcache-miss                                                                                         
  {"op":"put-query-repository","uri":"page://self/dep/level-three"}                                                                                                    
  {"op":"invalidate-etag","tags":["_dep_level-three_"]}                                                                                                                
  {"op":"save-etag","uri":"page://self/dep/level-three","etag":"3378406738","surrogateKeys":["_dep_level-three_"]}                                                     
  {"op":"save-value","uri":"page://self/dep/level-three","tags":["3378406738","_dep_level-three_"],"ttl":31536000}                                                     
  {"op":"depends-on","parent":"page://self/dep/level-two","child":"page://self/dep/level-three","childTags":["_dep_level-three_"]}                                     
  {"op":"save-etag","uri":"page://self/dep/level-two","etag":"3579205492","surrogateKeys":["_dep_level-three_","_dep_level-two_"]}                                     
                                                                                                                                                                       
                                                                                                                                                                       
  {"op":"cache-hit","uri":"page://self/dep/level-three"}  ← 1回目のcache-hit                                                                                           
  {"op":"save-value","uri":"page://self/dep/level-two","tags":["3579205492","_dep_level-two_","_dep_level-three_"],"ttl":31536000}                                     
  {"op":"depends-on","parent":"page://self/dep/level-one","child":"page://self/dep/level-two","childTags":["_dep_level-two_","_dep_level-three_"]}                     
                                                                                                                                                                       
  level-threeについて、最初のリクエストでcache-missが1回発生し、その後キャッシュに保存されます。次のアクセスではcache-hitが記録されています。                          
                                                                                                                                                                       
  level-twoはlevel-threeに依存しており、etag値と複数のサロゲートキーを持つ値として保存されます。level-oneはlevel-twoに依存する親として設定され、その後のリクエストでもc
  ache-hitが確認されます。最終的にlevel-threeに対するpurgeリクエストが開始され、クエリリポジトリの削除処理が実行されます。                                             
                                                                                                                                                                       
  その後、level-oneへの新しいリクエストがcache-missを引き起こし、リポジトリへの書き込みとetagの無効化が連鎖的に発生します。level-twoとlevel-threeも同様にcache-missとな
  り、2回目のmissが記録されます。                                                                                                                                      
                                                                                                                                                                       
  その後、level-threeのetagが保存され、キャッシュ値がTTL付きで格納されます。level-twoとlevel-threeの依存関係が確立され、各レベルのetagが順次保存されていきます。       
                                                                                                                                                                       
  2回目のキャッシュヒットが記録され、level-twoの値がその子要素のタグを含めて保存されます。最終的にlevel-oneがlevel-twoとlevel-threeに依存する構造が形成されます。      
                                                                                                                                                                       
  level-threeのログ分析から、キャッシュミスが2回、キャッシュヒットが2回発生しており、これは生成が2回行われたことを示しています。                                       
                                                                                                                                                                       
  最初の「4回」という答えは誤りでした。                                                                                                                                
                                                                                                                                                                       
⏺ ログを再確認します。                                                                                                       02:21 PM  claude-opus-4-5-20251101        
                                                                                                                                                                       
  level-threeのログを抽出                                                                                                                                              
                                                                                                                                                                       
  {"op":"cache-miss","uri":"page://self/dep/level-three"}      ← 1回目 miss(初回構築)                                                                                
  {"op":"save-value","uri":"page://self/dep/level-three",...}                                                                                                          
  {"op":"cache-hit","uri":"page://self/dep/level-three"}       ← 1回目 hit(level-two保存時の再参照)                                                                  
                                                                                                                                                                       
  ... purge level-three ...                                                                                                                                            
                                                                                                                                                                       
  {"op":"cache-miss","uri":"page://self/dep/level-three"}      ← 2回目 miss(purge後の再構築)                                                                         
  {"op":"save-value","uri":"page://self/dep/level-three",...}                                                                                                          
  {"op":"cache-hit","uri":"page://self/dep/level-three"}       ← 2回目 hit(level-two保存時の再参照)                                                                  
                                                                                                                                                                       
  訂正した回答                                                                                                                                                         
  ┌──────────────────────────┬──────┐                                                                                                                                  
  │           操作           │ 回数 │                                                                                                                                  
  ├──────────────────────────┼──────┤                                                                                                                                  
  │ cache-miss (level-three) │ 2回  │                                                                                                                                  
  ├──────────────────────────┼──────┤                                                                                                                                  
  │ cache-hit (level-three)  │ 2回  │                                                                                                                                  
  └──────────────────────────┴──────┘                                                                                                                                  
  生成:2回、キャッシュヒット:2回                                                                                                                                     
                                                                                                                                                                       
  最初の回答で「4回」としたのは誤りでした。level-oneへのリクエストは2セット(初回 +                                                                                    
  purge後)あり、それぞれでlevel-threeは1回生成され、1回キャッシュヒットしています。                                                                                   

- Add array parameter tests to RepositoryLoggerTest
- Add multiple parents dependency test to CacheDependencyTest
- Add unrelated resources independence test with ETag verification
- Add CACHE_DEPENDENCY_TESTS.md documentation
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 24, 2026

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

This PR modernizes the logging system in BEAR.QueryRepository by replacing printf-style formatted logging with structured context-based logging. The interface signature changes from variadic template parameters to an operation name and context array, with all call sites updated accordingly. Supporting demos, test resources, and documentation are added.

Changes

Cohort / File(s) Summary
Logging System Core
src/RepositoryLogger.php, src/RepositoryLoggerInterface.php
Changed log method signature from log(string $template, ...$values): void to log(string $operation, array $context = []): void. Updated internal log storage from list<string> to list<array<string, mixed>>. Modified output formatting to JSON-encode logs with JSON_UNESCAPED_SLASHES.
Logger Call Sites
src/DonutRepository.php, src/QueryRepository.php, src/ResourceStorage.php
Updated all logging calls to use structured context arrays instead of printf-style formatting. Each log call now passes operation name and associative context (e.g., ['uri' => $uri, 'ttl' => $ttl]).
Test Resources - Multi-parent Dependencies
tests/Fake/fake-app/src/Resource/Page/Dep/ChildC.php, ParentA.php, ParentB.php
Added three new Cacheable resource classes to test cache dependency scenarios: a shared child resource (ChildC) embedded by multiple parents (ParentA, ParentB) for validating multi-parent invalidation behavior.
Test Updates
tests/RepositoryLoggerTest.php, tests/DonutCacheInterceptorTest.php, tests/DonutQueryInterceptorTest.php, tests/CacheDependencyTest.php
Refactored logger assertions to validate JSON-formatted log content via substring checks for individual operations instead of exact string matching. Added five new test methods to RepositoryLoggerTest covering edge cases (arrays, null values, empty context). Added two new cache dependency tests for unrelated resources and multi-parent scenarios.
Demo Scripts
demo/run-dependency.php, demo/run-donut.php
Added two new demonstration scripts: one showing 3-level cache dependency chains with shared dependencies and cascade invalidation, another demonstrating donut caching with dynamic inner content invalidation.
Documentation & Schema
CLAUDE.md, docs/llms.txt, docs/llms-full.txt, docs/schemas/repository-log.json, tests/CACHE_DEPENDENCY_TESTS.md
Added comprehensive documentation: two LLMS doc files introducing QueryRepository concepts, a JSON Schema defining repository log entry structure with operation types and constraints, and test coverage documentation detailing cache dependency patterns. Updated CLAUDE.md with project overview and testing guidance.
Dependencies
composer.json
Updated ray/aop from ^2.16 to ^2.19.1 and ray/di from ^2.17.2 to ^2.20.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • NaokiTsuchiya
  • jingu
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 48.39% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Improve machine-readable logging and add LLM documentation' directly and clearly summarizes the main changes: converting to structured JSON logging and adding comprehensive documentation.
Description check ✅ Passed The description is well-structured and directly related to the changeset. It covers the breaking change to logging, documentation additions, test enhancements, and dependency updates with clear examples and file organization.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

BREAKING CHANGE: RepositoryLoggerInterface signature changed
from log(string $template, ...$values) to log(string $operation, array $context = [])

- Output now uses JSON format: {"op":"save-value","uri":"...","tags":[...]}
- Arrays are properly serialized as JSON arrays
- Easier to parse and analyze programmatically
- Update callers in ResourceStorage, QueryRepository, DonutRepository
- Update tests to use new format
Define schema for all repository log operations:
- save-value, save-view, save-etag, save-donut, save-donut-view
- invalidate-etag, put-query-repository, purge-query-repository
- put-donut, try-donut-view, found-donut-view
- try-donut, no-donut-found, refresh-donut
- llms.txt: Concise project overview
- llms-full.txt: Comprehensive documentation with all attributes, modules, and architecture
- Links to JSON schema on GitHub Pages
@koriym koriym changed the title test: Enhance cache dependency test coverage refactor: Improve machine-readable logging and add LLM documentation Jan 24, 2026
@koriym koriym changed the title refactor: Improve machine-readable logging and add LLM documentation Improve machine-readable logging and add LLM documentation Jan 24, 2026
@codecov
Copy link

codecov bot commented Jan 24, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (9edb995) to head (2a45922).
⚠️ Report is 15 commits behind head on 1.x.

Additional details and impacted files
@@             Coverage Diff             @@
##                 1.x      #170   +/-   ##
===========================================
  Coverage     100.00%   100.00%           
+ Complexity       245       241    -4     
===========================================
  Files             52        52           
  Lines            732       729    -3     
===========================================
- Hits             732       729    -3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@koriym koriym force-pushed the test-enhancement-cache-dependency branch from df1e0e3 to f5fa2ba Compare January 24, 2026 03:23
Demonstrates cache behavior with meaningful log output:
- 3-level dependency chain (LevelOne -> LevelTwo -> LevelThree)
- Multiple parents depending on same child (ParentA/ParentB -> ChildC)
- Cache hit/miss, depends-on, and cascade invalidation logging
Each request now starts with a request-start log entry,
making it easy for AI to identify request boundaries when
analyzing the cache operation logs.
Demonstrates donut caching (static outer + dynamic inner):
- try-donut-view, found-donut-view operations
- put-donut, save-donut, save-donut-view operations
- refresh-donut when inner content is invalidated
- Etag 'r' suffix indicates refresh vs full regeneration
- Add reference to BEAR.Sunday llms-full.txt for conceptual docs
- Add request-start and donut cache operations to log schema
- Add method property for request-start operation
- Update CLAUDE.md with documentation links
@koriym
Copy link
Member Author

koriym commented Jan 24, 2026

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 24, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@tests/CACHE_DEPENDENCY_TESTS.md`:
- Around line 79-80: The table entries reference a misspelled test class name
"UtiTagTest" instead of "UriTagTest"; update both occurrences in the document so
the test references match the actual test class. Locate the rows containing
`UriTag::__invoke()` / `UtiTagTest::testInvoke` and `UriTag::fromAssoc()` /
`UtiTagTest::testFromAssoc` and replace `UtiTagTest` with `UriTagTest` so the
`UriTag::__invoke()` and `UriTag::fromAssoc()` tests are correctly named.
🧹 Nitpick comments (5)
docs/schemas/repository-log.json (1)

12-31: Some operations lack conditional validation rules.

The op enum includes operations like put-query-repository, purge-query-repository, try-donut-view, found-donut-view, try-donut, no-donut-found, and refresh-donut, but these don't have corresponding conditional rules in the allOf section to enforce their required fields.

Consider adding conditional rules for these operations for schema completeness, or document that they have no required fields beyond op.

CLAUDE.md (2)

9-11: Consider using proper Markdown link syntax for URLs.

The bare URLs work but could be formatted as Markdown links for consistency with linting rules and better readability.

♻️ Suggested formatting
 For conceptual documentation, see:
-- BEAR.Sunday: https://bearsunday.github.io/llms-full.txt
+- BEAR.Sunday: [llms-full.txt](https://bearsunday.github.io/llms-full.txt)
 - This package: docs/llms-full.txt

205-209: Same suggestion for Documentation section URLs.

♻️ Suggested formatting
 ## Documentation

-- BEAR.Sunday LLM docs: https://bearsunday.github.io/llms-full.txt
-- Cache manual: https://bearsunday.github.io/manuals/1.0/en/cache.html
+- BEAR.Sunday LLM docs: [llms-full.txt](https://bearsunday.github.io/llms-full.txt)
+- Cache manual: [cache.html](https://bearsunday.github.io/manuals/1.0/en/cache.html)
 - Log schema: docs/schemas/repository-log.json
src/RepositoryLogger.php (1)

34-37: Consider handling json_encode failure explicitly.

json_encode() returns false on failure (e.g., invalid UTF-8 sequences in context values). The (string) cast converts this to an empty string, which could silently produce incomplete logs. Consider using JSON_THROW_ON_ERROR for stricter error handling, or at minimum JSON_INVALID_UTF8_SUBSTITUTE to handle malformed strings gracefully.

♻️ Suggested improvement
         return implode(PHP_EOL, array_map(
-            static fn (array $log): string => (string) json_encode($log, JSON_UNESCAPED_SLASHES),
+            static fn (array $log): string => (string) json_encode($log, JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE),
             $this->logs,
         ));

Or for stricter handling:

static fn (array $log): string => json_encode($log, JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR),
tests/Fake/fake-app/src/Resource/Page/Dep/ChildC.php (1)

1-17: Consider adding declare(strict_types=1) and type declarations for consistency.

The fake resource is functionally correct and follows the same pattern as ParentA and ParentB. For consistency with the main codebase, consider adding strict types and type annotations.

♻️ Optional improvement
 <?php
+
+declare(strict_types=1);

 namespace FakeVendor\HelloWorld\Resource\Page\Dep;

 use BEAR\RepositoryModule\Annotation\Cacheable;
 use BEAR\Resource\ResourceObject;

 #[Cacheable]
 class ChildC extends ResourceObject
 {
-    public $body = ['child-c' => 1];
+    /** `@var` array{child-c: int} */
+    public array $body = ['child-c' => 1];

-    public function onGet()
+    public function onGet(): static
     {
         return $this;
     }
 }

Comment on lines 79 to 80
| `UriTag::__invoke()` | `UtiTagTest::testInvoke` | URI to tag string conversion |
| `UriTag::fromAssoc()` | `UtiTagTest::testFromAssoc` | Generate tags from array data |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Typo in test class name: UtiTagTestUriTagTest

The test class name appears to be misspelled in two places.

📝 Proposed fix
-| `UriTag::__invoke()` | `UtiTagTest::testInvoke` | URI to tag string conversion |
-| `UriTag::fromAssoc()` | `UtiTagTest::testFromAssoc` | Generate tags from array data |
+| `UriTag::__invoke()` | `UriTagTest::testInvoke` | URI to tag string conversion |
+| `UriTag::fromAssoc()` | `UriTagTest::testFromAssoc` | Generate tags from array data |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| `UriTag::__invoke()` | `UtiTagTest::testInvoke` | URI to tag string conversion |
| `UriTag::fromAssoc()` | `UtiTagTest::testFromAssoc` | Generate tags from array data |
| `UriTag::__invoke()` | `UriTagTest::testInvoke` | URI to tag string conversion |
| `UriTag::fromAssoc()` | `UriTagTest::testFromAssoc` | Generate tags from array data |
🤖 Prompt for AI Agents
In `@tests/CACHE_DEPENDENCY_TESTS.md` around lines 79 - 80, The table entries
reference a misspelled test class name "UtiTagTest" instead of "UriTagTest";
update both occurrences in the document so the test references match the actual
test class. Locate the rows containing `UriTag::__invoke()` /
`UtiTagTest::testInvoke` and `UriTag::fromAssoc()` / `UtiTagTest::testFromAssoc`
and replace `UtiTagTest` with `UriTagTest` so the `UriTag::__invoke()` and
`UriTag::fromAssoc()` tests are correctly named.

@koriym
Copy link
Member Author

koriym commented Jan 24, 2026

@coderabbitai Fixed UtiTagTest → UriTagTest in aeda788.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 24, 2026

Tip

For best results, initiate chat on the files or code changes.

@koriym, thank you for fixing that typo! The correction from UtiTagTest to UriTagTest improves consistency and readability. 👍

@koriym koriym merged commit 79bb3f1 into 1.x Jan 24, 2026
34 of 35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants