Implements item 11 of epic #204 — job dependency visualisation. Ships
both a primary surface and a stretch surface per the proposal at
`docs/proposals/jobs-dependencies-viz.md`:
PRIMARY — pure-SVG topological-layered DAG inline on the JobDetail
Dependencies tab. Color-coded by status (succeeded / running
/ failed / pending), click-a-node to navigate, keyboard
accessible (Enter / Space). 350-450px clamp.
STRETCH — fullscreen Gantt timeline at /sovereign/provision/$id/jobs/
timeline. One row per job, bars from startedAt → finishedAt
(or now if running), nice-tick time axis, hover tooltips.
New files:
• src/widgets/job-deps-graph/JobDependenciesGraph.tsx — SVG DAG widget,
structurally typed against any Job-like shape so it works with both
today's jobs.ts model and the evolved-but-not-yet-merged backend
contract from #205.
• src/shared/lib/depsLayout.ts — pure topological-layered layout. Kahn
topo-sort + cycle break + within-layer sort by descendant count.
Zero external graph deps (no reactflow / cytoscape / d3-dag — per
the issue hard rules + INVIOLABLE-PRINCIPLES.md #2).
• src/pages/sovereign/JobsTimeline.tsx — Gantt page chrome.
• src/test/fixtures/deps-graph.fixture.ts — shared mock graph for
sibling agents per the contract in the epic.
• src/pages/designs/JobsDepsVizDemo.tsx — visual lock-in surface at
/sovereign/designs/jobs-deps-viz for reviewer eye-checks.
• docs/proposals/jobs-dependencies-viz.md — recommendation rationale.
Integration into the merged JobDetail surface (PR #208):
• src/components/JobDependencies.tsx — replaces the placeholder list-
only surface with DAG-on-top-of-list. List preserved for keyboard
accessibility + screen readers.
Tests:
• depsLayout.test.ts (15 cases): topo order, no overlap, cycle break,
unknown-id drop, custom options, edge geometry.
• JobDependenciesGraph.test.tsx (8 cases): render counts, status data
attribute, click + keyboard handlers, height clamp.
Cosmetic verification: 4 screenshots at 1440px under
`.playwright-mcp/jobs-deps-viz/` showing the DAG (5-job + 3-node
fixtures), the integrated Dependencies tab on a real JobDetail page,
and the Gantt timeline route.
Refs #204Closes#206
Co-authored-by: hatiyildiz <hatice.yildiz@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captures the new admin surface in mid-flight (Hetzner-infra DONE,
Cluster-bootstrap RUNNING, mixed application states) using the dev
server with mocked /events buffer responses, then walks every tab on
two representative ApplicationPages — Cilium (no upstream deps,
bootstrap-kit, INSTALLED) and Harbor (3 component dependencies, mid-
provisioning).
Six PNGs under .playwright-mcp/admin-evidence/:
01-admin-page-grid.png AdminPage — top bar, sidebar
(deployment metadata + family
rollup), phase banners, bootstrap
kit grid + selected applications.
02-app-page-logs-tab.png Cilium · Logs tab (filtered by
event.component === 'bp-cilium').
03-app-page-dependencies-tab.png Cilium · Dependencies tab — the
CNI sits at the bottom of the
dep graph; "no upstream
dependencies" copy verifies the
empty-state surface.
04-app-page-status-tab.png Cilium · Status tab — install
state, helm release, namespace,
chart version from per-component
event payload.
05-app-page-overview-tab.png Cilium · Overview tab — long-form
copy from marketplaceCopy.ts
(SPINE family tagline + "What it
does" positioning).
06-app-page-dependencies-harbor.png Harbor · Dependencies tab —
three component deps rendered as
clickable mini-cards (CloudNative
PG, SeaweedFS, Valkey) with
family chips.
Verified gates: tsc clean, vite build clean, vitest 188/188 green.
Route /sovereign/provision/$deploymentId/app/$componentId reachable
via card click; /sovereign/provision/$deploymentId still mounts
AdminPage so StepReview's redirect target is preserved.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Claude Agent SDK does not handle OAuth token refresh — it reads the
accessToken from .credentials.json and uses it directly. When the token
expires (~8h), Axon returns 401 until manually refreshed.
Adds a CronJob (every 4h by default) that:
1. Reads the refreshToken from the K8s secret
2. Calls Anthropic's OAuth token endpoint to get a fresh accessToken
3. Updates the K8s secret with the new credentials
4. Restarts the Axon deployment to pick up the new token
Includes ServiceAccount, Role, and RoleBinding for least-privilege access.
Disabled by default (axon.tokenRefresh.enabled: false).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>