Skip to content
This repository was archived by the owner on Aug 8, 2026. It is now read-only.

Show files tracked by standalone .dvc pointer files in directory listings and figures pages - #599

Merged
petebachant merged 4 commits into
mainfrom
copilot/fix-dvc-file-display-issue
May 1, 2026
Merged

Show files tracked by standalone .dvc pointer files in directory listings and figures pages#599
petebachant merged 4 commits into
mainfrom
copilot/fix-dvc-file-display-issue

Conversation

Copilot AI commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

Files tracked via dvc add (standalone .dvc pointer files) were invisible in directory listings and figures auto-detection — only files in dvc.lock (pipeline outputs) were surfaced.

Changes

backend/app/projects.pyget_contents_from_tree

When building a directory listing, scan for .dvc pointer files among the git-tracked children. For each one (e.g., figures/plot.png.dvc), parse the pointer, derive the actual tracked path (figures/plot.png), and add it to the listing with storage="dvc" and size/type from the pointer metadata. Paths already covered by dvc_lock_outs are skipped to avoid duplicates.

# figures/plot.png.dvc is in git; figures/plot.png was previously invisible
dvc_pointer_outs: dict[str, dict] = {}
for p in paths:
    if not p.endswith(".dvc"):
        continue
    actual_path = p[:-4]
    if not actual_path or actual_path in dvc_lock_outs:
        continue
    dvc_file_data = yaml.safe_load(tree.read_text(p))
    ...
    dvc_pointer_outs[actual_path] = out

backend/app/api/routes/projects/core.pyget_project_figures

When traversing git blobs for figure auto-detection, also call _maybe_add_figure on the path derived from any .dvc blob (i.e., strip the .dvc suffix), so figures stored via dvc add are picked up alongside pipeline outputs from dvc.lock.

Tests

  • Directory listing with a .dvc pointer for a file: verifies both the pointer (git) and the actual entry (dvc) appear with correct storage, size, and type.
  • Directory listing with a .dvc pointer for a directory (md5 ending in .dir): verifies type="dir".
  • Figures auto-detection via .dvc pointer blobs, including deduplication against dvc_lock_outs.

Copilot AI and others added 2 commits April 30, 2026 23:50
…igures auto-detection

- In get_contents_from_tree: scan for .dvc pointer files in the git tree and
  add the corresponding actual paths (without .dvc suffix) to directory listings
  with storage="dvc" and metadata parsed from the pointer file.
- In get_project_figures route: also call _maybe_add_figure on paths derived
  from .dvc pointer blobs (in addition to existing dvc.lock auto-detection).
- Add tests for both directory listing and figures auto-detection with .dvc files.

Agent-Logs-Url: https://github.com/calkit/calkit-cloud/sessions/f6c5df3d-c404-46d8-a7fa-f061f922164d

Co-authored-by: petebachant <4604869+petebachant@users.noreply.github.com>
… add diagnostic context to warning

Agent-Logs-Url: https://github.com/calkit/calkit-cloud/sessions/f6c5df3d-c404-46d8-a7fa-f061f922164d

Co-authored-by: petebachant <4604869+petebachant@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix display of files stored in DVC using regular .dvc files Show files tracked by standalone .dvc pointer files in directory listings and figures pages Apr 30, 2026
Copilot AI requested a review from petebachant April 30, 2026 23:52
@petebachant
petebachant marked this pull request as ready for review April 30, 2026 23:54
@petebachant
petebachant requested a review from Copilot April 30, 2026 23:54

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR makes files tracked via standalone DVC pointer files (*.dvc created by dvc add) visible to the backend’s directory listing logic and figure auto-detection, aligning behavior with existing support for pipeline outputs from dvc.lock.

Changes:

  • Extend directory listings to detect *.dvc pointer files and surface the corresponding tracked paths as storage="dvc".
  • Extend figure auto-detection to consider paths derived from *.dvc blobs.
  • Add tests for directory listings and figure auto-detection involving *.dvc pointer files.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
backend/app/projects.py Adds scanning/parsing of *.dvc pointer files during directory listing to surface derived DVC-tracked entries.
backend/app/api/routes/projects/core.py Updates figure auto-detection to also consider paths derived from *.dvc blobs.
backend/app/tests/test_projects.py Adds tests ensuring directory listings include both the pointer (git) and derived tracked path (dvc).
backend/app/tests/api/routes/projects/test_core.py Adds tests for figure auto-detection via *.dvc pointer blobs and deduplication behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/app/projects.py Outdated
continue
outs = dvc_file_data.get("outs")
out = outs[0] if isinstance(outs, list) and outs else {}
dvc_pointer_outs[actual_path] = out

Copilot AI Apr 30, 2026

Copy link

Choose a reason for hiding this comment

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

out = outs[0] ... is stored into dvc_pointer_outs without validating it is a dict. If a malformed .dvc file has outs: [<non-mapping>], the later dvc_out.get(...) call will raise an AttributeError and break directory listing. Guard with isinstance(out, dict) (or coerce to {}) before storing.

Suggested change
dvc_pointer_outs[actual_path] = out
dvc_pointer_outs[actual_path] = out if isinstance(out, dict) else {}

Copilot uses AI. Check for mistakes.
Comment thread backend/app/api/routes/projects/core.py Outdated
Comment on lines +1424 to +1427
if blob_path.endswith(".dvc"):
actual_path = blob_path[:-4]
if actual_path:
_maybe_add_figure(actual_path)

Copilot AI Apr 30, 2026

Copy link

Choose a reason for hiding this comment

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

Figures auto-detection derives the “actual” path by stripping the .dvc suffix from the blob path, but the authoritative tracked path is inside the pointer YAML (outs[0].path). If a pointer file is renamed or its outs[0].path differs, this logic will miss (or mislabel) figures. Consider parsing the .dvc blob and building the derived path from outs[0].path relative to the pointer file’s directory (and skipping if it can’t be parsed).

Copilot uses AI. Check for mistakes.
Comment on lines +177 to +186
# Create a .dvc pointer file for a file in a subdirectory
figures_dir = repo_dir / "figures"
figures_dir.mkdir()
dvc_pointer = figures_dir / "plot.png.dvc"
dvc_pointer.write_text(
"outs:\n"
"- md5: abc123def456abc123def456abc12345\n"
" size: 42000\n"
" path: plot.png\n"
)

Copilot AI Apr 30, 2026

Copy link

Choose a reason for hiding this comment

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

Current tests only cover the common case where the pointer filename matches the tracked output (e.g. plot.png.dvc -> plot.png). Since the implementation should ideally use outs[0].path, add a test case where the .dvc pointer filename is different from the tracked outs[0].path (renamed pointer) to prevent regressions.

Copilot uses AI. Check for mistakes.
Comment thread backend/app/projects.py Outdated
Comment on lines +401 to +409
actual_path = p[:-4]
if not actual_path or actual_path in dvc_lock_outs:
continue
try:
dvc_file_data = yaml.safe_load(tree.read_text(p))
if not isinstance(dvc_file_data, dict):
continue
outs = dvc_file_data.get("outs")
out = outs[0] if isinstance(outs, list) and outs else {}

Copilot AI Apr 30, 2026

Copy link

Choose a reason for hiding this comment

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

actual_path = p[:-4] assumes the pointer filename always matches the tracked output path. DVC’s canonical tracked path is outs[0].path; if the .dvc file is renamed or tracks a different path, this will add an incorrect entry to the listing. Consider deriving actual_path from the pointer’s outs[0].path joined to the pointer file’s directory, and only falling back to stripping the suffix when that field is missing.

Suggested change
actual_path = p[:-4]
if not actual_path or actual_path in dvc_lock_outs:
continue
try:
dvc_file_data = yaml.safe_load(tree.read_text(p))
if not isinstance(dvc_file_data, dict):
continue
outs = dvc_file_data.get("outs")
out = outs[0] if isinstance(outs, list) and outs else {}
try:
dvc_file_data = yaml.safe_load(tree.read_text(p))
if not isinstance(dvc_file_data, dict):
continue
outs = dvc_file_data.get("outs")
out = outs[0] if isinstance(outs, list) and outs else {}
out_path = out.get("path") if isinstance(out, dict) else None
if isinstance(out_path, str) and out_path:
actual_path = os.path.normpath(
os.path.join(os.path.dirname(p), out_path)
)
else:
actual_path = p[:-4]
if not actual_path or actual_path in dvc_lock_outs:
continue

Copilot uses AI. Check for mistakes.
@petebachant
petebachant merged commit 311c1ee into main May 1, 2026
4 checks passed
@petebachant
petebachant deleted the copilot/fix-dvc-file-display-issue branch May 1, 2026 01:18
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

All files and figures pages should display files stored in DVC using regular .dvc files (not in dvc.lock)

3 participants