Skip to content

feat(vfs): add pixel dir, pass attachments, shader used-by routes#162

Merged
BANANASJIM merged 4 commits intomasterfrom
feat/vfs-route-gaps
Mar 1, 2026
Merged

feat(vfs): add pixel dir, pass attachments, shader used-by routes#162
BANANASJIM merged 4 commits intomasterfrom
feat/vfs-route-gaps

Conversation

@BANANASJIM
Copy link
Owner

@BANANASJIM BANANASJIM commented Mar 1, 2026

User description

Summary

  • Add /draws/<eid>/pixel/ directory node to VFS skeleton so rdc ls and rdc tree can discover pixel history routes
  • Lazy-populate /passes/<name>/attachments/ with color0, color1, ..., depth leaf nodes from pipeline state on first access
  • Add /shaders/<id>/used-by reverse index returning all draw EIDs that reference a shader

Details

Three VFS route gaps between the design doc and implementation:

  1. Pixel directory: pixel added to _DRAW_CHILDREN, registered as empty dir (coordinates are user-specified, not enumerable). Two directory routes added for intermediate paths.

  2. Pass attachments: New populate_pass_attachments() function follows populate_draw_subtree pattern. _ensure_pass_attachments_populated() trigger called in both vfs_ls and vfs_tree handlers. New pass_attachment handler returns {pass, attachment, resource_id}.

  3. Shader used-by: _build_shader_cache now persists shader_eids as "eids" in shader_meta. New shader_used_by handler + route + tree node.

Test plan

  • 35 new unit tests across 5 test files
  • 2445 total tests pass, 93.81% coverage
  • pixi run lint clean
  • E2E smoke tests pass (26/26)
  • Manual test with fixture capture

PR Type

Enhancement


Description

  • Add /draws/<eid>/pixel/ directory node to VFS skeleton for pixel history route discoverability

  • Lazy-populate /passes/<name>/attachments/ with color and depth leaf nodes from pipeline state

  • Add /shaders/<id>/used-by reverse index returning all draw EIDs that reference a shader

  • Implement handlers and routes for all three VFS gaps with comprehensive test coverage


Diagram Walkthrough

flowchart LR
  VFS["VFS Routes"]
  Pixel["Pixel Directory<br/>/draws/eid/pixel/"]
  PassAttach["Pass Attachments<br/>/passes/name/attachments/"]
  ShaderUsed["Shader Used-By<br/>/shaders/id/used-by"]
  
  VFS -- "Gap 1" --> Pixel
  VFS -- "Gap 2" --> PassAttach
  VFS -- "Gap 3" --> ShaderUsed
  
  Pixel -- "handler" --> PixelHandler["pixel_history handler"]
  PassAttach -- "handler" --> AttachHandler["pass_attachment handler"]
  ShaderUsed -- "handler" --> UsedByHandler["shader_used_by handler"]
Loading

File Walkthrough

Relevant files
Enhancement
7 files
router.py
Register pixel, pass attachment, and shader used-by routes
+4/-0     
tree_cache.py
Add pixel to draw children and populate pass attachments 
+30/-1   
_helpers.py
Build shader cache with EID list and populate pass attachments
+32/-0   
vfs.py
Trigger pass attachment population in vfs_ls and vfs_tree
+8/-0     
query.py
Add pass_attachment handler returning resource IDs             
+48/-0   
shader.py
Add shader_used_by handler returning EID list                       
+13/-0   
vfs.py
Add formatters for pass_attachment and shader_used_by responses
+2/-0     
Tests
5 files
test_vfs_router.py
Add route resolution tests for all three VFS gaps               
+75/-0   
test_vfs_tree_cache.py
Add tests for pixel dir, pass attachments, and shader used-by
+85/-1   
test_draws_events_daemon.py
Add handler tests for pass_attachment and shader_used_by 
+151/-0 
test_vfs_daemon.py
Add VFS integration tests for pixel, attachments, and shader routes
+121/-0 
test_shader_preload.py
Add tests verifying shader_meta contains EID list               
+14/-0   

Summary by CodeRabbit

  • New Features
    • VFS exposes pass attachments (color/depth), shader "used-by" listings, and a pixel directory under draw entries for easier inspection of render targets and shader usage.
  • Tests
    • Added broad unit and end-to-end tests covering pass attachments, shader usage, VFS routing, tree population, and pixel endpoints.
  • Documentation
    • Added a comprehensive black-box E2E test catalog documenting VFS behaviors, expectations, and coverage.

Fill three VFS route gaps:

1. /draws/<eid>/pixel/ - add to _DRAW_CHILDREN and register dir routes
   so `rdc ls` and `rdc tree` show the pixel directory
2. /passes/<name>/attachments/ - lazy-populate color/depth children via
   populate_pass_attachments(), add pass_attachment handler returning
   resource IDs
3. /shaders/<id>/used-by - persist EID list in shader_meta, add leaf
   route and handler returning which draws reference a shader
@coderabbitai
Copy link

coderabbitai bot commented Mar 1, 2026

📝 Walkthrough

Walkthrough

Adds VFS routes and handlers to expose pass attachment targets and shaders' EID usage. Extends shader cache to record EIDs, adds dynamic pass-attachment population during VFS listing/tree population, updates CLI VFS extractors, and adds unit and e2e tests exercising these endpoints.

Changes

Cohort / File(s) Summary
Handlers & Helpers
src/rdc/handlers/query.py, src/rdc/handlers/shader.py, src/rdc/handlers/_helpers.py
New _handle_pass_attachment and _handle_shader_used_by handlers; shader cache now records eids; added _PASS_ATTACH_RE and _ensure_pass_attachments_populated to detect and populate pass attachments by seeking replay and extracting pipeline state.
VFS router & tree population
src/rdc/vfs/router.py, src/rdc/vfs/tree_cache.py, src/rdc/handlers/vfs.py
New routes for /passes/<name>/attachments/<attachment>, /shaders/<id>/used-by, and /draws/<eid>/pixel*; populate_pass_attachments added to create color/depth leaves; VFS handlers now call ensure-pass-attachments during ls/tree population.
CLI extractors
src/rdc/commands/vfs.py
Added _EXTRACTORS entries: pass_attachment (key-value formatter) and shader_used_by (newline-separated EID list).
VFS tests — unit
tests/unit/test_draws_events_daemon.py, tests/unit/test_shader_preload.py, tests/unit/test_vfs_daemon.py, tests/unit/test_vfs_router.py, tests/unit/test_vfs_tree_cache.py
New unit tests for pass-attachment resolution, shader used-by behavior, router path resolution, tree population, and shader EID tracking; added test helpers and state builders.
E2E tests & helpers
tests/e2e/e2e_helpers.py, tests/e2e/conftest.py, tests/e2e/test_vfs.py, tests/e2e/*
Introduced centralized e2e helpers and many e2e tests for the new VFS paths; conftest switched to import helpers; added blackbox test catalog documentation.
Config
pyproject.toml
Test discovery pythonpath extended to include tests/e2e.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant DaemonVFS as "Daemon: VFS Handler"
  participant VfsTree as "VfsTree"
  participant Adapter as "Replay Adapter"

  Client->>DaemonVFS: GET /passes/{name}/attachments/{attachment}
  DaemonVFS->>VfsTree: check /passes/{name} subtree
  VfsTree-->>DaemonVFS: node (may be unpopulated)
  DaemonVFS->>DaemonVFS: _ensure_pass_attachments_populated(request, path)
  DaemonVFS->>Adapter: seek to pass.begin_eid
  Adapter-->>DaemonVFS: pipeline state
  DaemonVFS->>VfsTree: populate_pass_attachments(tree, name, pipe_state)
  VfsTree-->>DaemonVFS: attachment leaf created
  DaemonVFS-->>Client: return attachment info (resource_id / error)
Loading
sequenceDiagram
  participant Client
  participant ShaderHandler as "Daemon: Shader Handler"
  participant State as "Daemon State"
  participant Cache as "Shader Cache"

  Client->>ShaderHandler: GET /shaders/{id}/used-by
  ShaderHandler->>State: ensure shader cache built
  State->>Cache: _build_shader_cache (collect EIDs)
  Cache-->>ShaderHandler: shader meta with eids list
  ShaderHandler-->>Client: return { id, eids }
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped through trees and pipelines bright,

Found colors, depths, and shaders' light.
I counted EIDs that danced in view,
Made new leaves for you to query too.
More carrots for every successful view! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.46% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main changes: adding three new VFS routes (pixel directory, pass attachments, and shader used-by endpoints). It directly reflects the primary additions across multiple files.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/vfs-route-gaps

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.

@qodo-code-review
Copy link

qodo-code-review bot commented Mar 1, 2026

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Unbounded response size

Description: The new shader_used_by JSON-RPC handler returns the full eids list from state.shader_meta
without any pagination/limits, which could enable a denial-of-service via very large
responses (e.g., a capture where a shader is referenced by a huge number of draws) causing
excessive memory/CPU usage and oversized JSON replies.
shader.py [263-272]

Referred Code
def _handle_shader_used_by(
    request_id: int, params: dict[str, Any], state: DaemonState
) -> tuple[dict[str, Any], bool]:
    """Return list of EIDs that use this shader."""
    _build_shader_cache(state)
    sid = int(params.get("id", 0))
    meta = state.shader_meta.get(sid)
    if meta is None:
        return _error_response(request_id, -32001, f"shader {sid} not found"), True
    return _result_response(request_id, {"id": sid, "eids": meta.get("eids", [])}), True
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Attachment index mismatch: The pass_attachment handler compacts output targets by filtering out resource==0 before
indexing, which can return the wrong resource for requests like color1 when earlier color
slots are unbound, conflicting with the VFS population logic that preserves original color
indices.

Referred Code
if attachment.startswith("color"):
    try:
        idx = int(attachment[5:])
    except ValueError:
        return _error_response(request_id, -32602, f"invalid attachment: {attachment}"), True
    targets = [t for t in pipe.GetOutputTargets() if int(t.resource) != 0]
    if idx < 0 or idx >= len(targets):
        return _error_response(request_id, -32001, f"color target {idx} not found"), True
    return _result_response(
        request_id,
        {"pass": name, "attachment": attachment, "resource_id": int(targets[idx].resource)},
    ), True

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
No audit logging: The new JSON-RPC handlers (pass_attachment, shader_used_by) do not emit any audit log
entries for these access events, and it is not verifiable from the diff whether auditing
is handled in a shared request layer.

Referred Code
def _handle_pass_attachment(
    request_id: int, params: dict[str, Any], state: DaemonState
) -> tuple[dict[str, Any], bool]:
    """Return attachment info for a specific pass attachment."""
    if state.adapter is None:
        return _error_response(request_id, -32002, "no replay loaded"), True
    from rdc.services.query_service import get_pass_detail

    name = str(params.get("name", ""))
    attachment = str(params.get("attachment", ""))
    if state.vfs_tree and name in state.vfs_tree.pass_name_map:
        name = state.vfs_tree.pass_name_map[name]

    actions = state.adapter.get_root_actions()
    detail = get_pass_detail(actions, state.structured_file, name)
    if detail is None:
        return _error_response(request_id, -32001, f"pass not found: {name}"), True

    err = _seek_replay(state, detail["begin_eid"])
    if err:
        return _error_response(request_id, -32002, err), True


 ... (clipped 25 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Authz not shown: The new query handlers accept user-controlled params (name, attachment, id) and return
capture-derived data, but the diff does not show any authentication/authorization
enforcement, so secure access control cannot be verified from the changed code alone.

Referred Code
def _handle_pass_attachment(
    request_id: int, params: dict[str, Any], state: DaemonState
) -> tuple[dict[str, Any], bool]:
    """Return attachment info for a specific pass attachment."""
    if state.adapter is None:
        return _error_response(request_id, -32002, "no replay loaded"), True
    from rdc.services.query_service import get_pass_detail

    name = str(params.get("name", ""))
    attachment = str(params.get("attachment", ""))
    if state.vfs_tree and name in state.vfs_tree.pass_name_map:
        name = state.vfs_tree.pass_name_map[name]

    actions = state.adapter.get_root_actions()
    detail = get_pass_detail(actions, state.structured_file, name)
    if detail is None:
        return _error_response(request_id, -32001, f"pass not found: {name}"), True

    err = _seek_replay(state, detail["begin_eid"])
    if err:
        return _error_response(request_id, -32002, err), True


 ... (clipped 25 lines)

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link

qodo-code-review bot commented Mar 1, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Cache pipeline state for passes

To improve performance, cache the pipeline state for each pass. This avoids
redundant, expensive replay seeks and state queries when accessing pass
attachments multiple times.

Examples:

src/rdc/handlers/_helpers.py [374-377]
    err = _seek_replay(state, begin_eid)
    if err:
        return _error_response(request_id, -32002, err)
    pipe = state.adapter.get_pipeline_state()  # type: ignore[union-attr]
src/rdc/handlers/query.py [606-609]
    err = _seek_replay(state, detail["begin_eid"])
    if err:
        return _error_response(request_id, -32002, err), True
    pipe = state.adapter.get_pipeline_state()

Solution Walkthrough:

Before:

# in _ensure_pass_attachments_populated
def populate_attachments(path, state):
  if attachments_populated(path):
    return
  
  err = seek_replay(state, pass_begin_eid)
  pipe = state.adapter.get_pipeline_state()
  populate_vfs_nodes_from_pipe(state.vfs_tree, pipe)

# in _handle_pass_attachment
def get_attachment_info(params, state):
  err = seek_replay(state, pass_begin_eid)
  pipe = state.adapter.get_pipeline_state()
  info = get_info_from_pipe(pipe, params['attachment'])
  return info

After:

# in DaemonState
class DaemonState:
  ...
  pass_pipe_state_cache: dict[str, Any] = {}

# in _ensure_pass_attachments_populated
def populate_attachments(path, state):
  if attachments_populated(path):
    return
  
  pipe = get_cached_or_fetch_pipe_for_pass(state, pass_name)
  populate_vfs_nodes_from_pipe(state.vfs_tree, pipe)

# in _handle_pass_attachment
def get_attachment_info(params, state):
  pipe = get_cached_or_fetch_pipe_for_pass(state, params['name'])
  info = get_info_from_pipe(pipe, params['attachment'])
  return info
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a performance issue where multiple operations on the same pass (listing attachments, then getting individual ones) repeatedly perform expensive replay seeks and pipeline state queries, which could be optimized by caching the pipeline state per pass.

Medium
General
Tighten attachments-dir regex

In _PASS_ATTACH_RE, change the regex to r"^/passes/([^/]+)/attachments/?$" to
ensure it only matches the attachments directory itself or with a trailing
slash, not deeper paths.

src/rdc/handlers/_helpers.py [50]

-_PASS_ATTACH_RE = re.compile(r"^/passes/([^/]+)/attachments(?:/|$)")
+_PASS_ATTACH_RE = re.compile(r"^/passes/([^/]+)/attachments/?$")
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly tightens the regular expression to prevent it from matching deeper paths, making its behavior more precise and preventing potential future bugs.

Low
Validate required parameters are present

Add checks to ensure name and attachment parameters are provided in the
_handle_pass_attachment request, returning a specific error if they are missing.

src/rdc/handlers/query.py [588-632]

 def _handle_pass_attachment(
     request_id: int, params: dict[str, Any], state: DaemonState
 ) -> tuple[dict[str, Any], bool]:
     """Return attachment info for a specific pass attachment."""
     if state.adapter is None:
         return _error_response(request_id, -32002, "no replay loaded"), True
     from rdc.services.query_service import get_pass_detail
 
-    name = str(params.get("name", ""))
-    attachment = str(params.get("attachment", ""))
+    name = params.get("name")
+    attachment = params.get("attachment")
+    if not name or not attachment:
+        return _error_response(request_id, -32602, "missing name or attachment parameter"), True
+    name = str(name)
+    attachment = str(attachment)
+
     if state.vfs_tree and name in state.vfs_tree.pass_name_map:
         name = state.vfs_tree.pass_name_map[name]
 
     actions = state.adapter.get_root_actions()
     detail = get_pass_detail(actions, state.structured_file, name)
     if detail is None:
         return _error_response(request_id, -32001, f"pass not found: {name}"), True
 
     err = _seek_replay(state, detail["begin_eid"])
     if err:
         return _error_response(request_id, -32002, err), True
     pipe = state.adapter.get_pipeline_state()
 
     if attachment == "depth":
         depth_id = int(pipe.GetDepthTarget().resource)
         if depth_id == 0:
             return _error_response(request_id, -32001, "no depth target"), True
         return _result_response(
             request_id, {"pass": name, "attachment": "depth", "resource_id": depth_id}
         ), True
 
     if attachment.startswith("color"):
         try:
             idx = int(attachment[5:])
         except ValueError:
             return _error_response(request_id, -32602, f"invalid attachment: {attachment}"), True
         targets = [t for t in pipe.GetOutputTargets() if int(t.resource) != 0]
         if idx < 0 or idx >= len(targets):
             return _error_response(request_id, -32001, f"color target {idx} not found"), True
         return _result_response(
             request_id,
             {"pass": name, "attachment": attachment, "resource_id": int(targets[idx].resource)},
         ), True
 
     return _error_response(request_id, -32001, f"unknown attachment: {attachment}"), True
  • Apply / Chat
Suggestion importance[1-10]: 5

__

Why: The suggestion improves API robustness by explicitly checking for required parameters and providing a clearer error message, which aids client-side debugging.

Low
Possible issue
Guard against missing VFS tree

Add a check at the beginning of _handle_pass_attachment to ensure state.vfs_tree
is not None, returning an error if the VFS tree has not been built.

src/rdc/handlers/query.py [588-594]

 def _handle_pass_attachment(
     request_id: int, params: dict[str, Any], state: DaemonState
 ) -> tuple[dict[str, Any], bool]:
     """Return attachment info for a specific pass attachment."""
     if state.adapter is None:
         return _error_response(request_id, -32002, "no replay loaded"), True
+    if state.vfs_tree is None:
+        return _error_response(request_id, -32002, "vfs tree not built"), True
     from rdc.services.query_service import get_pass_detail
  • Apply / Chat
Suggestion importance[1-10]: 4

__

Why: Although the current code is safe due to short-circuiting, adding an explicit check for state.vfs_tree improves robustness and aligns with the defensive coding style used in other handlers.

Low
  • Update

Copy link

@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

🧹 Nitpick comments (1)
tests/unit/test_draws_events_daemon.py (1)

628-639: Strengthen shader_used_by assertions to catch duplicates/order regressions.

Using sets at Line 633 and Line 639 masks duplicate EIDs. Prefer asserting the exact normalized list (or both length and content).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/test_draws_events_daemon.py` around lines 628 - 639, The current
tests test_returns_eids and test_all_eids_correct use set(...) which hides
duplicates and order regressions for the shader_used_by RPC; update the
assertions to verify the exact normalized list and length instead of a set.
Locate the tests using rpc_request("shader_used_by", {"id": ...}),
_handle_request and _make_shader_used_by_state and replace the set-based checks
with either an exact list equality assert (e.g. assert resp["result"]["eids"] ==
[10, 20]) or two checks asserting length (assert len(resp["result"]["eids"]) ==
2) and content (assert set(resp["result"]["eids"]) == {10, 20}) so duplicates or
ordering changes will fail.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/rdc/handlers/query.py`:
- Around line 619-630: Fix the color attachment indexing so it uses the raw
output slots instead of filtering nulls: in the block that handles
attachment.startswith("color") use pipe.GetOutputTargets() directly (store the
full list, not a filtered one) and check bounds against that full list, then
verify that the selected slot (targets[idx]) is non-null (return _error_response
with -32001 if it is null) before returning _result_response with
int(targets[idx].resource); reference the variables/functions: attachment,
pipe.GetOutputTargets(), targets, idx, _error_response, _result_response,
request_id, name. Also add a regression test that constructs a sparse output
scenario (e.g., color0 present, color1 null, color2 present) and asserts color2
resolves correctly while color1 returns the not-found error.

---

Nitpick comments:
In `@tests/unit/test_draws_events_daemon.py`:
- Around line 628-639: The current tests test_returns_eids and
test_all_eids_correct use set(...) which hides duplicates and order regressions
for the shader_used_by RPC; update the assertions to verify the exact normalized
list and length instead of a set. Locate the tests using
rpc_request("shader_used_by", {"id": ...}), _handle_request and
_make_shader_used_by_state and replace the set-based checks with either an exact
list equality assert (e.g. assert resp["result"]["eids"] == [10, 20]) or two
checks asserting length (assert len(resp["result"]["eids"]) == 2) and content
(assert set(resp["result"]["eids"]) == {10, 20}) so duplicates or ordering
changes will fail.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 214666c and d438450.

📒 Files selected for processing (12)
  • src/rdc/commands/vfs.py
  • src/rdc/handlers/_helpers.py
  • src/rdc/handlers/query.py
  • src/rdc/handlers/shader.py
  • src/rdc/handlers/vfs.py
  • src/rdc/vfs/router.py
  • src/rdc/vfs/tree_cache.py
  • tests/unit/test_draws_events_daemon.py
  • tests/unit/test_shader_preload.py
  • tests/unit/test_vfs_daemon.py
  • tests/unit/test_vfs_router.py
  • tests/unit/test_vfs_tree_cache.py

@greptile-apps
Copy link

greptile-apps bot commented Mar 1, 2026

Greptile Summary

This PR systematically addresses three VFS route gaps identified in the design documentation, adding pixel history directory discovery, lazy-populated pass attachments, and shader usage reverse indexing.

Key Changes:

  • Pixel Directory (/draws/<eid>/pixel/): Added as an empty directory node in the VFS skeleton to enable discovery via rdc ls and rdc tree. The actual pixel coordinates are user-specified parameters, so the directory structure correctly shows an empty container.
  • Pass Attachments (/passes/<name>/attachments/): Implemented lazy population that inspects pipeline state on first access to create color0, color1, depth leaf nodes based on bound render targets. Follows the same pattern as populate_draw_subtree for consistency.
  • Shader Used-By (/shaders/<id>/used-by): Extended shader cache to track all draw EIDs that reference each shader, exposing this as a reverse index through a new handler and route.

Implementation Quality:

  • All three features follow established patterns in the codebase (lazy population, route registration, handler structure)
  • Comprehensive test coverage with 35 new unit tests across 5 test files
  • Proper edge case handling (missing passes, unbound attachments, invalid shader IDs)
  • Clean integration with existing VFS infrastructure

The implementation is production-ready with no identified issues.

Confidence Score: 5/5

  • This PR is safe to merge with high confidence
  • The implementation follows established patterns throughout the codebase, has comprehensive test coverage (35 new tests, 2445 total passing at 93.81% coverage), includes proper error handling for edge cases, and addresses well-defined VFS gaps from the design documentation
  • No files require special attention

Important Files Changed

Filename Overview
src/rdc/vfs/router.py Added 4 new route registrations for pixel directory and pass attachments, plus shader used-by. Routes follow existing patterns correctly.
src/rdc/vfs/tree_cache.py Added pixel to draw children, populate_pass_attachments function, and shader used-by node. Lazy population pattern matches existing design.
src/rdc/handlers/_helpers.py Extended _build_shader_cache to track shader EIDs and added pass attachments population helper. Both follow existing patterns.
src/rdc/handlers/query.py Added pass_attachment handler with proper validation for color/depth attachments and edge case handling.
src/rdc/handlers/shader.py Added shader_used_by handler that returns EID list from cache with proper error handling.
src/rdc/handlers/vfs.py Integrated pass attachments population in vfs_ls and vfs_tree handlers, consistent with existing shader population pattern.
src/rdc/commands/vfs.py Added formatters for pass_attachment and shader_used_by to _EXTRACTORS dict for proper output rendering.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[VFS Route Request] --> B{Route Type?}
    B -->|Pixel| C["pixel directory route"]
    B -->|Pass Attach| D["pass attachment route"]
    B -->|Shader Used| E["shader used-by route"]
    
    C --> F[pixel_history handler]
    F --> G[Return pixel modifications]
    
    D --> H{First Access?}
    H -->|Yes| I[populate_pass_attachments]
    H -->|No| J[Use cached tree]
    I --> K[Inspect pipeline state]
    K --> L[Create color/depth nodes]
    L --> M[pass_attachment handler]
    J --> M
    M --> N[Return resource ID]
    
    E --> O[_build_shader_cache]
    O --> P[Track shader EIDs]
    P --> Q[shader_used_by handler]
    Q --> R[Return EID list]
Loading

Last reviewed commit: 1f15913

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

14 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link

greptile-apps bot commented Mar 1, 2026

Additional Comments (1)

src/rdc/handlers/_helpers.py
Appending EID on every stage iteration will create duplicate EIDs if same shader used in multiple stages

If a draw uses the same shader resource ID in multiple stages (e.g., shader 100 in both VS and PS), a.eventId gets appended once per stage, creating duplicates in the EID list. The /shaders/<id>/used-by route should return each draw EID only once.

                    if a.eventId not in shader_eids[sid]:
                        shader_eids[sid].append(a.eventId)

…andler

The handler compacted targets (filtering resource==0) before indexing,
but populate_pass_attachments uses enumerate() to preserve original
indices. This caused wrong resource lookups when empty slots exist.
Use all_targets with original index and validate non-zero resource.

Also add e2e tests 5.28-5.31 for new VFS routes: draw pixel listing,
pass attachments, shader used-by, and pass attachment detail.
Add 6 e2e tests (5.32-5.37) covering pixel dir, pass attachment
depth/invalid, shader used-by listing for both shaders 111 and 112.
Update blackbox test catalog with all 10 new VFS route gap tests.
Copy link

@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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/rdc/handlers/query.py`:
- Around line 596-598: The code currently defaults name and attachment to empty
strings (e.g., name = str(params.get("name", "")) and attachment =
str(params.get("attachment", ""))) which masks malformed requests as not-found
errors; change these reads to validate presence and non-empty values and return
a JSON-RPC invalid params error (-32602) when missing/empty instead of
proceeding to lookups (affecting the initial block that checks
state.vfs_tree/pass_name_map and the other occurrences where params are
defaulted). Specifically, replace the defaulting pattern with explicit checks of
params.get("name") and params.get("attachment") (or check after casting) and if
either is missing or empty, return the RPC error -32602 with a clear message
rather than performing the pass or attachment lookup.

In `@tests/e2e/blackbox_test_catalog.md`:
- Around line 279-291: The Coverage Summary table is stale and the counts (e.g.,
the "Query commands" row and NOTE counts) do not match the per-test status
markers; regenerate the summary so the "Query commands" count, NOTE counts, the
"**TOTAL**" row and the "Pass rate" line are recomputed from the actual per-test
statuses, update any mismatched cells (including the single failing/tested
counts) and ensure Pass rate (158/159 -> percentage) is correct and consistent
with the recalculated totals.

In `@tests/e2e/test_vfs.py`:
- Around line 276-277: The test currently picks the first pass via
passes_out.strip().splitlines()[0], which is brittle; instead iterate over the
pass names returned by passes_out (from rdc_ok("ls", "/passes",
session=vkcube_session)) and select a pass whose directory listing (via
rdc_ok("ls", f"/passes/{pass_name}", session=vkcube_session)) contains the
required attachment string (e.g., "color0" or "depth") before using it; update
all occurrences where pass_name is chosen (the blocks using passes_out,
pass_name) to perform this capability-based selection and fail the test if no
suitable pass is found.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d438450 and b8d0449.

📒 Files selected for processing (3)
  • src/rdc/handlers/query.py
  • tests/e2e/blackbox_test_catalog.md
  • tests/e2e/test_vfs.py

Comment on lines +596 to +598
name = str(params.get("name", ""))
attachment = str(params.get("attachment", ""))
if state.vfs_tree and name in state.vfs_tree.pass_name_map:
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Return -32602 for missing name/attachment instead of not-found errors.

Defaulting to "" makes malformed requests look like lookup failures (pass not found / unknown attachment). This should be treated as invalid parameters for clearer client behavior.

💡 Proposed fix
-    name = str(params.get("name", ""))
-    attachment = str(params.get("attachment", ""))
+    if "name" not in params or not str(params["name"]).strip():
+        return _error_response(request_id, -32602, "missing name"), True
+    if "attachment" not in params or not str(params["attachment"]).strip():
+        return _error_response(request_id, -32602, "missing attachment"), True
+    name = str(params["name"]).strip()
+    attachment = str(params["attachment"]).strip()

Also applies to: 603-605, 632-632

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rdc/handlers/query.py` around lines 596 - 598, The code currently
defaults name and attachment to empty strings (e.g., name =
str(params.get("name", "")) and attachment = str(params.get("attachment", "")))
which masks malformed requests as not-found errors; change these reads to
validate presence and non-empty values and return a JSON-RPC invalid params
error (-32602) when missing/empty instead of proceeding to lookups (affecting
the initial block that checks state.vfs_tree/pass_name_map and the other
occurrences where params are defaulted). Specifically, replace the defaulting
pattern with explicit checks of params.get("name") and params.get("attachment")
(or check after casting) and if either is missing or empty, return the RPC error
-32602 with a clear message rather than performing the pass or attachment
lookup.

Comment on lines +279 to +291
| Query commands | 39 | 39 | 0 | 1 |
| Output formats | 10 | 10 | 0 | 0 |
| VFS navigation | 37 | 37 | 0 | 0 |
| Export commands | 14 | 14 | 0 | 0 |
| Debug commands | 7 | 6 | 1 | 0 |
| Assert/CI | 13 | 13 | 0 | 0 |
| Diff | 7 | 7 | 0 | 0 |
| Script | 2 | 2 | 0 | 0 |
| Advanced | 5 | 5 | 0 | 0 |
| Multi-fixture | 5 | 5 | 0 | 0 |
| **TOTAL** | **159** | **158** | **1** | **1** |

Pass rate: **99.4%** (158/159)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Coverage Summary counts don’t reconcile with the listed statuses.

The summary table/pass-rate appears inconsistent with the per-test status markers (notably NOTE counts in Query commands). Please regenerate/reconcile this section to avoid stale reporting.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/blackbox_test_catalog.md` around lines 279 - 291, The Coverage
Summary table is stale and the counts (e.g., the "Query commands" row and NOTE
counts) do not match the per-test status markers; regenerate the summary so the
"Query commands" count, NOTE counts, the "**TOTAL**" row and the "Pass rate"
line are recomputed from the actual per-test statuses, update any mismatched
cells (including the single failing/tested counts) and ensure Pass rate (158/159
-> percentage) is correct and consistent with the recalculated totals.

Comment on lines +276 to +277
passes_out = rdc_ok("ls", "/passes", session=vkcube_session)
pass_name = passes_out.strip().splitlines()[0].strip()
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid brittle “first pass” selection for attachment tests.

Using splitlines()[0] couples these tests to pass ordering and can fail for valid captures where the first pass lacks color0 or depth. Pick a pass by capability (contains the attachment) instead.

💡 Suggested refactor
+def _pass_with_attachment(session: str, attachment: str) -> str:
+    passes_out = rdc_ok("ls", "/passes", session=session)
+    for raw in passes_out.strip().splitlines():
+        name = raw.strip()
+        if not name:
+            continue
+        out = rdc_ok("ls", f"/passes/{name}/attachments", session=session)
+        if attachment in out:
+            return name
+    pytest.fail(f"No pass exposes attachment {attachment!r}")
+
 class TestLsPassAttachments:
@@
-        passes_out = rdc_ok("ls", "/passes", session=vkcube_session)
-        pass_name = passes_out.strip().splitlines()[0].strip()
+        pass_name = _pass_with_attachment(vkcube_session, "color0")
         out = rdc_ok("ls", f"/passes/{pass_name}/attachments", session=vkcube_session)
         assert "color0" in out

Also applies to: 294-295, 312-313, 322-323, 332-333

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/test_vfs.py` around lines 276 - 277, The test currently picks the
first pass via passes_out.strip().splitlines()[0], which is brittle; instead
iterate over the pass names returned by passes_out (from rdc_ok("ls", "/passes",
session=vkcube_session)) and select a pass whose directory listing (via
rdc_ok("ls", f"/passes/{pass_name}", session=vkcube_session)) contains the
required attachment string (e.g., "color0" or "depth") before using it; update
all occurrences where pass_name is chosen (the blocks using passes_out,
pass_name) to perform this capability-based selection and fail the test if no
suitable pass is found.

@greptile-apps
Copy link

greptile-apps bot commented Mar 1, 2026

Additional Comments (1)

src/rdc/handlers/_helpers.py, line 185
appending a.eventId inside the stage loop can create duplicate entries if the same shader ID is bound to multiple stages in one draw call (e.g., same resource in VS and PS)

                if a.eventId not in shader_eids[sid]:
                    shader_eids[sid].append(a.eventId)

Copy link

@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.

🧹 Nitpick comments (1)
tests/e2e/test_vfs.py (1)

328-339: Consider asserting error message content for consistency.

Other error tests in this file (e.g., TestCatNotFound, TestLsNotFound) assert that "error" appears in the output. This test only verifies the exit code. For consistency and better failure diagnostics, consider adding an assertion on the output.

💡 Suggested improvement
     def test_invalid_attachment(self, vkcube_session: str) -> None:
         passes_out = rdc_ok("ls", "/passes", session=vkcube_session)
         pass_name = passes_out.strip().splitlines()[0].strip()
-        rdc_fail(
+        out = rdc_fail(
             "cat",
             f"/passes/{pass_name}/attachments/color99",
             session=vkcube_session,
             exit_code=1,
         )
+        assert "error" in out.lower()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/test_vfs.py` around lines 328 - 339, The test
TestCatPassAttachmentInvalid::test_invalid_attachment currently only asserts the
exit code; after calling rdc_fail(...) add an assertion that the command output
contains the string "error" to match other tests (use the rdc_fail return or
captured output variable) so failures give consistent diagnostics; locate the
calls to rdc_ok(...) and rdc_fail(...) and assert "error" in the failure output
(e.g., reference variables passes_out, pass_name, and the rdc_fail invocation).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/e2e/test_vfs.py`:
- Around line 328-339: The test
TestCatPassAttachmentInvalid::test_invalid_attachment currently only asserts the
exit code; after calling rdc_fail(...) add an assertion that the command output
contains the string "error" to match other tests (use the rdc_fail return or
captured output variable) so failures give consistent diagnostics; locate the
calls to rdc_ok(...) and rdc_fail(...) and assert "error" in the failure output
(e.g., reference variables passes_out, pass_name, and the rdc_fail invocation).

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b8d0449 and 1f15913.

📒 Files selected for processing (14)
  • pyproject.toml
  • tests/e2e/conftest.py
  • tests/e2e/e2e_helpers.py
  • tests/e2e/test_advanced.py
  • tests/e2e/test_assert.py
  • tests/e2e/test_debug.py
  • tests/e2e/test_diff.py
  • tests/e2e/test_export.py
  • tests/e2e/test_formats.py
  • tests/e2e/test_presession.py
  • tests/e2e/test_query.py
  • tests/e2e/test_session.py
  • tests/e2e/test_shader_edit.py
  • tests/e2e/test_vfs.py
✅ Files skipped from review due to trivial changes (3)
  • tests/e2e/test_query.py
  • tests/e2e/test_presession.py
  • tests/e2e/test_formats.py

@BANANASJIM BANANASJIM merged commit ae29494 into master Mar 1, 2026
30 of 31 checks passed
@BANANASJIM BANANASJIM deleted the feat/vfs-route-gaps branch March 1, 2026 08:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant