Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion ship/biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"rules": {
"recommended": true,
"complexity": {
"noUselessConstructor": "error"
"noUselessConstructor": "error",
"useLiteralKeys": "off"
},
"correctness": {
"noUnusedVariables": "error",
Expand Down
1 change: 1 addition & 0 deletions ship/src/lib/run-dir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
import { basename, join } from "node:path";
import { isPidAlive, readStateFile } from "../engine/kernel/state.ts";
import { runDirFor, shipRunsRoot } from "./runs-root.ts";

// Re-export so downstream consumers can pick the threshold up from the same
// surface that ships the field they gate on.
export { STUCK_THRESHOLD_SECONDS } from "./stuck-threshold.ts";
Expand Down
2 changes: 1 addition & 1 deletion ship/src/lib/runs-root.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ afterEach(() => {
});

describe("shipRunsRoot — precedence", () => {
it("claim 1: default is ${HOME}/.pramana/ship/runs when no env/config override", () => {
it("claim 1: default is <HOME>/.pramana/ship/runs when no env/config override", () => {
// No PRAMANA_SHIP_RUNS_DIR set, no ~/.pramana/config.json present.
expect(shipRunsRoot()).toBe(join(fakeHome, ".pramana", "ship", "runs"));
});
Expand Down
2 changes: 1 addition & 1 deletion ship/src/server-sse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ describe("Claim 2 — connect replays every existing events.jsonl row verbatim",
expect(frames.length).toBe(seeded.length);
for (let i = 0; i < seeded.length; i++) {
expect(frames[i]).toBe(seeded[i]);
expect(rowAndParse(frames[i]!)).toEqual(rowAndParse(seeded[i]!));
expect(rowAndParse(frames[i] as string)).toEqual(rowAndParse(seeded[i] as string));
}
});
});
Expand Down
3 changes: 1 addition & 2 deletions ship/src/topology-evolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,8 @@ interface EvolveTopology extends Topology<EvolveCtx> {

function makeEvolveTopology(payload: string): EvolveTopology {
return {
// eslint-disable-next-line require-yield
// biome-ignore lint/correctness/useYield: single-shot — handler does not use the generator path
async *generator(_ctx: EvolveCtx) {
// Single-shot — handler does not use the generator path.
void _ctx;
return;
},
Expand Down
1 change: 1 addition & 0 deletions ship/src/web/lib/JsonView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ function JsonArray({ value }: { value: Json[] }) {
{open && (
<div className="json-node">
{value.map((v, i) => (
// biome-ignore lint/suspicious/noArrayIndexKey: JSON array order is stable; index is the only key
<div key={i}>
<JsonValue value={v} />
{i < value.length - 1 ? "," : ""}
Expand Down
8 changes: 6 additions & 2 deletions ship/src/web/lib/RealiseDagGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,18 @@ interface BuildResult {
export function buildGraph(audit: RealiseAudit): BuildResult {
const wavePos = new Map<string, { wave: number; row: number }>();
audit.dagWaves.forEach((wave, waveIdx) => {
wave.forEach((id, rowIdx) => wavePos.set(id, { wave: waveIdx, row: rowIdx }));
wave.forEach((id, rowIdx) => {
wavePos.set(id, { wave: waveIdx, row: rowIdx });
});
});

// Items present in itemIds but not in any wave fall back to the last wave.
// (Cycles drop nodes from order — those still get rendered as orphans.)
const fallbackWave = audit.dagWaves.length;
const orphans = audit.itemIds.filter((id) => !wavePos.has(id));
orphans.forEach((id, i) => wavePos.set(id, { wave: fallbackWave, row: i }));
orphans.forEach((id, i) => {
wavePos.set(id, { wave: fallbackWave, row: i });
});

const nodes: Node[] = audit.itemIds.map((id) => {
const pos = wavePos.get(id) ?? { wave: 0, row: 0 };
Expand Down
2 changes: 1 addition & 1 deletion ship/src/web/lib/StageCard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useState } from "react";
import type { StageEntry, StageTelemetry } from "../types";
import { ArtifactPreview } from "./ArtifactPreview";
import { StatePill } from "../ui/StatePill";
import { ArtifactPreview } from "./ArtifactPreview";

function formatDuration(ms?: number): string {
if (ms === undefined) return "—";
Expand Down
2 changes: 1 addition & 1 deletion ship/src/web/pages/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useMemo, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import { api, type PostRunOutcome } from "../api";
import type { RunSummary } from "../types";
import { NowBar, type NowBarRun } from "../ui/NowBar";
import { StatePill } from "../ui/StatePill";
import type { RunSummary } from "../types";

interface IssueGroup {
issue: string;
Expand Down
2 changes: 1 addition & 1 deletion ship/src/web/pages/RunDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { ArtifactPreview } from "../lib/ArtifactPreview";
import { PipelineGraph } from "../lib/PipelineGraph";
import { RealiseDagGraph } from "../lib/RealiseDagGraph";
import { StageCard } from "../lib/StageCard";
import { StatePill } from "../ui/StatePill";
import { MetaGrid } from "../ui/MetaGrid";
import { StatePill } from "../ui/StatePill";

function formatElapsed(ms: number): string {
const total = Math.max(0, Math.floor(ms / 1000));
Expand Down
5 changes: 2 additions & 3 deletions ship/src/web/ui/NowBar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function isoDaysAgo(d: number): string {
}

describe("NowBar — claim 1 (renders when one active run exists)", () => {
it('produces role=region with aria-label="currently active runs" and "1 running" text', () => {
it('renders as <section> with aria-label="currently active runs" and "1 running" text', () => {
render([
{
id: "r1",
Expand All @@ -64,9 +64,8 @@ describe("NowBar — claim 1 (renders when one active run exists)", () => {
secondsSinceLastEvent: 12,
},
]);
const region = container.querySelector('[role="region"]');
const region = container.querySelector('section[aria-label="currently active runs"]');
expect(region).not.toBeNull();
expect(region?.getAttribute("aria-label")).toBe("currently active runs");
expect(region?.textContent ?? "").toContain("1 running");
});

Expand Down
5 changes: 2 additions & 3 deletions ship/src/web/ui/NowBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ export function NowBar({ runs }: { runs: NowBarRun[] }) {
if (runs.length === 0) return null;
const anomalies = runs.filter((r) => r.secondsSinceLastEvent > STUCK_THRESHOLD_SECONDS);
return (
<div
role="region"
<section
aria-label="currently active runs"
style={{
position: "sticky",
Expand Down Expand Up @@ -78,6 +77,6 @@ export function NowBar({ runs }: { runs: NowBarRun[] }) {
);
})}
</span>
</div>
</section>
);
}
2 changes: 1 addition & 1 deletion ship/src/web/ui/StatePill.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// per the discipline documented in ui/StatusNode.ts.

import type { CSSProperties } from "react";
import { tokens, type StateKind } from "../design/tokens";
import { type StateKind, tokens } from "../design/tokens";
import { STATUS_LABEL } from "./StatusNode";

export interface StatePillProps {
Expand Down