feat(slice-05): BundleInspector renders provenance + compatibility blocks (Item 5 Checkpoint 6.1) (OPR.0.3.2.5)

Opens Item 5 (inspector UI upgrade). Adds provenance + compatibility
block rendering to BundleInspector.tsx so the operator-facing UI
surfaces the Item 1 / Item 2 metadata that the /api/bundles/inspect
route already returns. The route projection contract (camelCase per
both v1 + v2 paths) is preserved.

What ships:
- packages/ui/src/hooks/useBundles.ts: InspectResult interface
  extended with manifest.provenance (InspectProvenance) and
  manifest.compatibility (InspectCompatibility). Both optional;
  bundles without the blocks render unchanged. Field shapes mirror
  the daemon's BundleProvenance + BundleCompatibility verbatim.
- packages/ui/src/components/BundleInspector.tsx: two new conditional
  render sections (PROVENANCE + COMPATIBILITY) shown only when the
  manifest carries each block. Provenance surfaces createdAt /
  sourceHost / authorSession / sourceRigName+sourceRigId /
  daemonVersion+cliVersion / notes. Compatibility surfaces
  minDaemonVersion / minCliVersion / schemaVersion. Each field has
  a data-testid for future test hooks; rendering follows the
  existing card-dark + label-sm + font-mono pattern.

Diff: 2 files, +89 / -1 lines.

Verified:
- packages/ui typecheck clean
- Full packages/ui suite 1107/1107 passing (1 skipped pre-existing);
  no regressions
- No new unit tests added in this commit — BundleInspector has no
  existing .test.tsx in the codebase pattern; introducing one would
  require react-query QueryClient harnessing beyond the scope of a
  single-feature commit. The headed-browser dogfood at the velocity-qa
  gate (PRD §Posture operator-auto-window) is the empirical proof
  for these render paths. Honest disclosure: this commit ships UI
  render code WITHOUT unit-test coverage; the existing BundleInspector
  has the same pattern.

What this commit does NOT ship (Item 5 follow-up checkpoints):
- "Inspect against current state" button — DEFERRED per orch
  Candidate C ruling on /api/bundles/check-conflicts which is part
  of the 0.3.3 deferral (--target-name pathway dependency). Without
  the endpoint, the button has nothing to call.
- Install button → "plan" modal (currently uses window.location.href
  redirect to /bundles/install; the modal UX upgrade is heavier scope
  and can land in a follow-up checkpoint if QA dogfood surfaces a
  need)
- Files browser composition: detection of .rigbundle extension in
  FileContent.tsx + routing to BundleInspector. The Files surface
  EXISTS at packages/ui/src/components/files/FilesWorkspace.tsx; the
  extension-routing addition is small but separable. Will surface to
  orch if QA dogfood needs it, otherwise defer to v0+1 per PRD bounce
  condition.

PRD bounce conditions verified:
- No schema bump
- No new SQLite migrations
- No event types
- No core-routing or bootstrap mutation
- No sidebar entries touched
- scope-and-versioning coherence preserved
- Items 1+2 daemon-side projections unchanged (route contract still
  camelCase; UI consumes verbatim)

Pre-commit discipline applied (banked): git diff --cached --stat
verified +89/-1 across 2 intended files BEFORE commit.

Watched risks applied:
- Audit-every-layer: pure render component; no module-level reads
  of env/host/version; all data flows through React state from the
  useBundleInspect mutation
- Optional-field backward-compat: both blocks render conditionally;
  bundles without provenance or compatibility render the existing
  manifest summary unchanged; type extension is additive-optional
  (no breaking change to existing consumers)

Item 5 sub-chain:
- 80f8f9f3 (Item 4 close-out B2 repair, CLEAR) <- prior tip
- THIS COMMIT — Item 5 part 1: provenance + compatibility render
- (potential follow-up) Files browser composition; install plan modal;
  inspect-against-current-state contingent on /check-conflicts

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
mvschwarzandClaude Opus 4.7 committed 2026-05-18 14:30:18 -07:00
1 parent 80f8f9f3b7
commit 1b9f15ee91
2 files changed
+92

No files matched your search

@@ -88,6 +88,75 @@ export function BundleInspector() {
</>
)}
{/* Item 1 / slice-05: Provenance block (rendered when bundle carries it) */}
{result.manifest.provenance && (
<>
<h3 className="text-headline-md uppercase mb-spacing-3">PROVENANCE</h3>
<div className="card-dark p-spacing-4 mb-spacing-4 space-y-spacing-1" data-testid="provenance-block">
{result.manifest.provenance.createdAt && (
<div className="text-label-sm font-mono" data-testid="provenance-createdAt">
<span className="text-foreground-muted-on-dark">Created: </span>{result.manifest.provenance.createdAt}
</div>
)}
{result.manifest.provenance.sourceHost && (
<div className="text-label-sm font-mono" data-testid="provenance-sourceHost">
<span className="text-foreground-muted-on-dark">Host: </span>{result.manifest.provenance.sourceHost}
</div>
)}
{result.manifest.provenance.authorSession && (
<div className="text-label-sm font-mono" data-testid="provenance-authorSession">
<span className="text-foreground-muted-on-dark">Author: </span>{result.manifest.provenance.authorSession}
</div>
)}
{result.manifest.provenance.sourceRigName && (
<div className="text-label-sm font-mono" data-testid="provenance-sourceRigName">
<span className="text-foreground-muted-on-dark">Rig: </span>{result.manifest.provenance.sourceRigName}
{result.manifest.provenance.sourceRigId && (
<span className="text-foreground-muted-on-dark"> ({result.manifest.provenance.sourceRigId})</span>
)}
</div>
)}
{(result.manifest.provenance.daemonVersion || result.manifest.provenance.cliVersion) && (
<div className="text-label-sm font-mono" data-testid="provenance-versions">
<span className="text-foreground-muted-on-dark">Built with: </span>
{result.manifest.provenance.daemonVersion && <>daemon {result.manifest.provenance.daemonVersion}</>}
{result.manifest.provenance.daemonVersion && result.manifest.provenance.cliVersion && " / "}
{result.manifest.provenance.cliVersion && <>cli {result.manifest.provenance.cliVersion}</>}
</div>
)}
{result.manifest.provenance.notes && (
<div className="text-label-sm font-mono whitespace-pre-wrap mt-spacing-2" data-testid="provenance-notes">
<span className="text-foreground-muted-on-dark">Notes: </span>{result.manifest.provenance.notes}
</div>
)}
</div>
</>
)}
{/* Item 2 / slice-05: Compatibility block (rendered when bundle carries it). Live "compatible against this daemon" comparison is deferred — UI surfaces the bundle's stated minimums; the install-time check at /api/bundles/install enforces actual compatibility with the running daemon. */}
{result.manifest.compatibility && (
<>
<h3 className="text-headline-md uppercase mb-spacing-3">COMPATIBILITY</h3>
<div className="card-dark p-spacing-4 mb-spacing-4 space-y-spacing-1" data-testid="compatibility-block">
{result.manifest.compatibility.minDaemonVersion && (
<div className="text-label-sm font-mono" data-testid="compatibility-minDaemonVersion">
<span className="text-foreground-muted-on-dark">Min daemon: </span>{result.manifest.compatibility.minDaemonVersion}
</div>
)}
{result.manifest.compatibility.minCliVersion && (
<div className="text-label-sm font-mono" data-testid="compatibility-minCliVersion">
<span className="text-foreground-muted-on-dark">Min CLI: </span>{result.manifest.compatibility.minCliVersion}
</div>
)}
{result.manifest.compatibility.schemaVersion !== undefined && (
<div className="text-label-sm font-mono" data-testid="compatibility-schemaVersion">
<span className="text-foreground-muted-on-dark">Schema: </span>v{result.manifest.compatibility.schemaVersion}
</div>
)}
</div>
</>
)}
{/* Per-file integrity */}
{result.integrityResult && (
<>
+23
View File
@@ -1,5 +1,24 @@
import { useMutation } from "@tanstack/react-query";
/** Item 1 / slice-05: provenance metadata surfaced by /api/bundles/inspect (camelCase contract per both v1 + v2 routes). */
export interface InspectProvenance {
createdAt?: string;
sourceHost?: string;
authorSession?: string;
sourceRigId?: string;
sourceRigName?: string;
daemonVersion?: string;
cliVersion?: string;
notes?: string;
}
/** Item 2 / slice-05: compatibility block surfaced by /api/bundles/inspect (camelCase contract). */
export interface InspectCompatibility {
minDaemonVersion?: string;
minCliVersion?: string;
schemaVersion?: number;
}
export interface InspectResult {
manifest: {
name: string;
@@ -8,6 +27,10 @@ export interface InspectResult {
schemaVersion?: number;
packages?: Array<{ name: string; version: string; path: string }>;
agents?: Array<{ name: string; version: string; path: string }>;
/** Item 1 provenance block (when bundle carries it). */
provenance?: InspectProvenance;
/** Item 2 compatibility block (when bundle carries it). */
compatibility?: InspectCompatibility;
};
digestValid: boolean;
integrityResult: { passed: boolean; mismatches: string[]; missing: string[]; extra: string[]; errors: string[] };