shell-redesign V1 polish slice Phase 5.1 P5.1-3: pod name truncation fix

V1 founder-walk surfaced a rendering bug: pod names display with
the first 2-3 characters truncated (e.g., "discovery" rendering as
"covery", "planning" as "anning") across topology surfaces.

Root cause: lib/display-name.ts displayPodName called shortId(podId, 6)
which returns the LAST 6 chars of any string — designed for 26-char
ULIDs (random tail more distinguishable than timestamp head) but
WRONG for human-readable pod namespaces. "discovery" (9 chars) → last
6 = "covery"; "planning" (8 chars) → "anning"; "kernel" (6 chars)
unchanged because shortId returns id verbatim when len ≤ 6.

Fix: displayPodName returns podId verbatim (no shortId call). Pod
IDs in OpenRig are namespace strings ("discovery" / "planning" /
"kernel" / "product-lab" / etc.), not ULIDs.

shortId import dropped from display-name.ts; the helper is still
exported for actual ULID display surfaces (rig IDs, qitem tails)
elsewhere in the codebase.

NEW packages/ui/test/p5-1-pod-name-truncation.test.ts (12 tests):
- "discovery" → "discovery"
- "planning" → "planning"
- "kernel" / "orch" / "product-lab" / multi-hyphen pass-through
- null/empty → "ungrouped" fallback preserved
- Source-assertion (ritual #9): displayPodName does NOT call shortId
  in code (comment-stripped grep)
- inferPodName + displayAgentName neighbor-function preserved
  (split-on-dot semantics unchanged)

Verification:
- pnpm -F @openrig/ui exec tsc --noEmit: clean
- vitest p5-1-pod-name-truncation: 12/12 pass
- vitest full UI suite: 672/674 PASS (pre-existing library-review
  baseline failure UNCHANGED; 1 skipped)

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
mvschwarzandClaude Opus 4.7 committed 2026-05-06 20:11:36 -07:00
1 parent f3780c0656
commit 49108fea01
2 files changed
+96 -2

No files matched your search

+14 -2
View File
@@ -1,4 +1,14 @@
import { shortId } from "./display-id.js";
// V1 polish slice Phase 5.1 P5.1-3: pod name truncation bug fix.
// Founder noticed pod names rendering as "covery" / "anning" — first 2-3
// chars cut. Root cause: displayPodName was calling shortId(podId, 6)
// which returns the LAST 6 chars of any string — fine for 26-char ULIDs
// (random tail more distinguishable than timestamp head) but WRONG for
// human-readable pod namespaces ("discovery" / "planning" / "kernel"
// etc.). Pod IDs in OpenRig are namespace strings, not ULIDs; the
// shortId path should never have applied. Returning the podId verbatim.
//
// shortId is still imported elsewhere for actual ULID display surfaces
// (rig IDs, qitem tails, etc.) — this fix only touches the pod path.
export function inferPodName(logicalId: string | null | undefined): string | null {
if (!logicalId) return null;
@@ -8,7 +18,9 @@ export function inferPodName(logicalId: string | null | undefined): string | nul
}
export function displayPodName(podId: string | null | undefined): string {
return podId && podId.length > 0 ? shortId(podId) : "ungrouped";
// Pod IDs are human-readable namespace strings, not ULIDs — return
// verbatim so "discovery" stays "discovery" (NOT "covery").
return podId && podId.length > 0 ? podId : "ungrouped";
}
export function displayAgentName(logicalId: string | null | undefined): string {
@@ -0,0 +1,82 @@
// V1 polish slice Phase 5.1 P5.1-3 — pod name truncation regression guard.
//
// Founder noticed pod names rendering as "covery" / "anning" / etc.
// at the live VM walk post-V1-ship. Root cause: displayPodName was
// calling shortId(podId, 6) which returns the LAST 6 chars of any
// input — designed for 26-char ULIDs but WRONG for human-readable pod
// namespaces. The fix returns podId verbatim. This test permanently
// guards the symptoms the founder reported plus the underlying contract.
import { describe, it, expect } from "vitest";
import { displayPodName, inferPodName, displayAgentName } from "../src/lib/display-name.js";
describe("displayPodName P5.1-3 regression: human-readable pod names pass through verbatim", () => {
it("'discovery' stays 'discovery' (NOT 'covery')", () => {
expect(displayPodName("discovery")).toBe("discovery");
});
it("'planning' stays 'planning' (NOT 'anning')", () => {
expect(displayPodName("planning")).toBe("planning");
});
it("'kernel' stays 'kernel'", () => {
expect(displayPodName("kernel")).toBe("kernel");
});
it("'orch' stays 'orch'", () => {
expect(displayPodName("orch")).toBe("orch");
});
it("'product-lab' stays 'product-lab'", () => {
expect(displayPodName("product-lab")).toBe("product-lab");
});
it("'recursive-self-improvement-v2' stays full (NOT '-v2')", () => {
expect(displayPodName("recursive-self-improvement-v2")).toBe(
"recursive-self-improvement-v2",
);
});
it("null / empty returns 'ungrouped' fallback", () => {
expect(displayPodName(null)).toBe("ungrouped");
expect(displayPodName(undefined)).toBe("ungrouped");
expect(displayPodName("")).toBe("ungrouped");
});
it("source-assertion: displayPodName does NOT call shortId for pod names (ritual #9)", async () => {
const { readFileSync } = await import("node:fs");
const path = await import("node:path");
const src = readFileSync(
path.resolve(__dirname, "../src/lib/display-name.ts"),
"utf8",
);
// Strip comments to ignore historical mention.
const codeOnly = src
.replace(/\/\*[\s\S]*?\*\//g, "")
.replace(/^\s*\/\/[^\n]*\n/gm, "");
// Locate displayPodName function body.
const fnMatch = codeOnly.match(/export function displayPodName[\s\S]*?^}/m);
expect(fnMatch).not.toBeNull();
const body = fnMatch![0];
expect(body).not.toMatch(/shortId\s*\(/);
});
});
describe("inferPodName + displayAgentName preserved (P5.1-3 cleanup didn't regress neighbors)", () => {
it("inferPodName splits 'discovery.intake-router' → 'discovery'", () => {
expect(inferPodName("discovery.intake-router")).toBe("discovery");
});
it("displayAgentName splits 'discovery.intake-router' → 'intake-router'", () => {
expect(displayAgentName("discovery.intake-router")).toBe("intake-router");
});
it("inferPodName returns the whole id when no dot", () => {
expect(inferPodName("solo-agent")).toBe("solo-agent");
});
it("inferPodName returns null for null/undefined", () => {
expect(inferPodName(null)).toBe(null);
expect(inferPodName(undefined)).toBe(null);
});
});