Skip to content

Commit c424adf

Browse files
paddymulclaude
andcommitted
feat(#134): metadata tab shows source size, last-modified, cheap/expensive label, lineage links
EntryCacheView states whether the alias is cheap or expensive (with the why), shows raw source size + last-modified, and renders 'built from' (parents) and 'used by' (children) as clickable catalog links. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 489eed7 commit c424adf

3 files changed

Lines changed: 74 additions & 2 deletions

File tree

packages/app/src/pages/CatalogPage.tsx

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -414,10 +414,18 @@ function EntryCacheView({ project, hash }: { project: string; hash: string }) {
414414
{data.cache_formatted} reclaimable cache · {data.artifact_formatted} build artifact
415415
{data.diff_cache_bytes > 0 && <> · {data.diff_cache_formatted} shared diff cache</>}
416416
</span>
417+
<span className="meta">
418+
{data.source_tracked
419+
? `${data.source_formatted} raw source${data.sources.length > 1 ? ` (${data.sources.length} files)` : ""}`
420+
: "raw source size not tracked (source-identity off)"}
421+
{data.modified_at && <> · last modified {data.modified_at.slice(0, 19).replace("T", " ")}</>}
422+
</span>
417423
<span className="meta cache-note">
424+
<strong>{data.cache_worthy ? "Expensive" : "Cheap"} alias.</strong>{" "}
418425
{data.cache_worthy
419-
? "Expensive expression: its result is kept in the xorq snapshot cache and recomputed on a miss."
420-
: "Cheap expression: no snapshot copy — the result is recomputed from the build on every read."}
426+
? "Its result is kept in the xorq snapshot cache and recomputed on a miss."
427+
: "No snapshot copy — the result is recomputed from the build on every read."}
428+
{data.cache_worthy_why && <span className="cache-why"> ({data.cache_worthy_why})</span>}
421429
</span>
422430
</div>
423431

@@ -490,6 +498,37 @@ function EntryCacheView({ project, hash }: { project: string; hash: string }) {
490498
</table>
491499
</div>
492500
)}
501+
502+
{(data.parents.length > 0 || data.children.length > 0) && (
503+
<div className="cache-lineage">
504+
{data.parents.length > 0 && (
505+
<div>
506+
<h4>built from</h4>
507+
<ul className="lineage-list">
508+
{data.parents.map((p) => (
509+
<li key={p.hash}>
510+
<Link to={`/${project}/catalog/${p.hash}`}>{p.alias ?? p.ref ?? p.hash.slice(0, 12)}</Link>
511+
{p.alias && p.ref !== p.alias && <span className="meta"> (as {p.ref})</span>}
512+
<span className="meta"> · {p.follow ? "follows alias" : "pinned"}</span>
513+
</li>
514+
))}
515+
</ul>
516+
</div>
517+
)}
518+
{data.children.length > 0 && (
519+
<div>
520+
<h4>used by</h4>
521+
<ul className="lineage-list">
522+
{data.children.map((ch) => (
523+
<li key={ch.hash}>
524+
<Link to={`/${project}/catalog/${ch.hash}`}>{ch.alias ?? ch.hash.slice(0, 12)}</Link>
525+
</li>
526+
))}
527+
</ul>
528+
</div>
529+
)}
530+
</div>
531+
)}
493532
</div>
494533
);
495534
}

packages/app/src/styles.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,3 +555,8 @@ table.cache-table .muted { color: var(--muted); }
555555
.cache-diffs { margin-top: 18px; }
556556
.cache-diffs h4 { margin: 0 0 4px; }
557557
.cache-diffs p.meta { margin: 0 0 8px; max-width: 560px; }
558+
.cache-why { color: var(--muted); }
559+
.cache-lineage { margin-top: 18px; display: flex; gap: 36px; flex-wrap: wrap; }
560+
.cache-lineage h4 { margin: 0 0 4px; }
561+
.lineage-list { margin: 0; padding-left: 18px; }
562+
.lineage-list li { margin: 2px 0; }

packages/app/src/types.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,32 @@ export interface EntryDiffCache {
171171
formatted: string;
172172
}
173173

174+
export interface EntrySource {
175+
path: string;
176+
bytes: number;
177+
formatted: string;
178+
exists: boolean;
179+
}
180+
181+
export interface EntryParent {
182+
hash: string;
183+
ref: string;
184+
follow: boolean;
185+
alias: string | null;
186+
}
187+
188+
export interface EntryChild {
189+
hash: string;
190+
alias: string | null;
191+
}
192+
174193
export interface EntryCache {
175194
project: string;
176195
content_hash: string;
177196
cache_worthy: boolean;
197+
cache_worthy_why: string | null;
198+
created_at: string | null;
199+
modified_at: string | null;
178200
components: EntryCacheComponent[];
179201
diff_caches: EntryDiffCache[];
180202
cache_bytes: number;
@@ -183,6 +205,12 @@ export interface EntryCache {
183205
artifact_formatted: string;
184206
diff_cache_bytes: number;
185207
diff_cache_formatted: string;
208+
source_bytes: number;
209+
source_formatted: string;
210+
source_tracked: boolean;
211+
sources: EntrySource[];
212+
parents: EntryParent[];
213+
children: EntryChild[];
186214
total_bytes: number;
187215
total_formatted: string;
188216
}

0 commit comments

Comments
 (0)