diff --git a/CLAUDE.md b/CLAUDE.md index f529029..08340d2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -365,6 +365,14 @@ route guards). To hit a real API in dev, run the backend and set `VITE_API_PROXY - Log every silent failure: a `catch` may swallow only if the caller surfaces the failure another way (an `errors[]` roll-up, a non-2xx result); otherwise it must log (server: `ILogger`; degradations that report "success" hide the cause). +- Comments explain **why**, never restate **what**. A comment may record a + decision, a constraint, a non-obvious consequence, a units/ordering/vocabulary + contract, or a domain fact not visible in the code. It must not paraphrase the + line below it — that's two statements of one fact with nothing keeping them in + sync (this concealed a real authorization bug here — a comment claimed a gate + the code didn't have). If a comment is needed just to say *what* the code does, + **rename the identifier** until it isn't. Treat a comment that contradicts its + code as a suspected bug (fix the code, not the comment), not a style nit. **Don't** - Don't redesign, restyle, or add screens/sections not in the prototype — diff --git a/server/Integrations/Jira.cs b/server/Integrations/Jira.cs index 1b271ed..917eb2c 100644 --- a/server/Integrations/Jira.cs +++ b/server/Integrations/Jira.cs @@ -561,7 +561,6 @@ public static async Task PullOpsItemsAsync(AtlasDbContext db, IConfiguratio return imported; } - // Jira issue type → Ops work-item type. static string MapOpsType(string? t) => (t ?? "").ToLowerInvariant() switch { "bug" or "incident" or "fault" => "Incident", @@ -569,7 +568,6 @@ public static async Task PullOpsItemsAsync(AtlasDbContext db, IConfiguratio "task" or "story" or "sub-task" or "subtask" => "Request", _ => "Other", }; - // Jira status category → Ops work-item status. static string MapOpsStatus(string? cat) => (cat ?? "").ToLowerInvariant() switch { "done" => "Done", diff --git a/server/Kernel/Dtos.cs b/server/Kernel/Dtos.cs index bdd16d3..e136892 100644 --- a/server/Kernel/Dtos.cs +++ b/server/Kernel/Dtos.cs @@ -241,7 +241,6 @@ public record OpsServiceDto(int Id, string Ref, string Name, string Category, st bool Archived = false, string JiraProjectKey = "", List? LinkedTasks = null); public record OpsSummaryDto(int Services, int OpenItems, int Blocked, int ImpactedProjects, int PeopleEngaged); public record OpsBoardDto(bool CanEdit, List Services, OpsSummaryDto Summary); -// Ops load impacting a single project (shown on Project Detail). public record OpsImpactRowDto(int Id, string Title, string ServiceName, string Type, string Priority, string Status, string Assignee, int Alloc, string ImpactNote); public record OpsImpactDto(int Alloc, List Items); diff --git a/src/components/ui.tsx b/src/components/ui.tsx index ff6565f..7cdd106 100644 --- a/src/components/ui.tsx +++ b/src/components/ui.tsx @@ -55,7 +55,6 @@ export function ProgressBar({ pct, fill = color.primary, height = 6, track = cha ); } -// Small empty-state block shown inside a card body when there's no data. export function EmptyBlock({ message, minHeight = 96 }: { message: string; minHeight?: number }) { return (
1 diff --git a/src/realtime/Presence.tsx b/src/realtime/Presence.tsx index c041649..82092a4 100644 --- a/src/realtime/Presence.tsx +++ b/src/realtime/Presence.tsx @@ -9,7 +9,6 @@ import { color } from "@/theme"; import { Icon } from "@/components/Icon"; import type { Peer, PeerCursor } from "./useRoomRealtime"; -// A small "Live"/"Offline" pill reflecting the hub connection. export function LiveDot({ connected }: { connected: boolean }) { return (
diff --git a/src/screens/gantt/model.ts b/src/screens/gantt/model.ts index 1d18879..cd39dd4 100644 --- a/src/screens/gantt/model.ts +++ b/src/screens/gantt/model.ts @@ -16,7 +16,7 @@ export interface Win { start: number; span: number } // Current month as an absolute month (impure — reads the clock). export const nowAbs = (): number => { const d = new Date(); return d.getFullYear() * 12 + d.getMonth(); }; -// Month-of-year (0..11) of an ISO date, or null if empty/invalid. +// 0-based (0..11); null if the date is empty/invalid. export const monthOfIso = (s: string): number | null => { if (!s) return null; const d = new Date(s); @@ -43,7 +43,6 @@ export const absToYm = (abs: number): string => // Short month name of an absolute month (wraps negatives correctly). export const monthAbbr = (abs: number): string => MONTHS[((abs % 12) + 12) % 12]; -// Calendar year of an absolute month. export const yearOf = (abs: number): number => Math.floor(abs / 12); // Anchor a bare month-of-year to a base absolute month (its context's start). diff --git a/src/screens/project/Quality.tsx b/src/screens/project/Quality.tsx index 2816132..4dc2578 100644 --- a/src/screens/project/Quality.tsx +++ b/src/screens/project/Quality.tsx @@ -142,7 +142,6 @@ export function Quality({ projectId }: { projectId: string | null }) { ); } -// A short date like "12 Mar" from an ISO date, or "" — for the compact task row. function shortDate(iso: string) { if (!iso) return ""; const d = new Date(iso); @@ -154,7 +153,6 @@ function PlanTasks({ projectId, plan, canEdit }: { projectId: string; plan: Test const [modal, setModal] = useState(false); const [openTask, setOpenTask] = useState(null); const invalidate = () => qc.invalidateQueries({ queryKey: ["quality", projectId] }); - // Quick inline status change without opening the full window. const setStatus = useMutation({ mutationFn: (v: { id: number; status: string }) => api(`/test-plan-tasks/${v.id}`, { method: "PATCH", body: JSON.stringify({ status: v.status }) }), onSuccess: invalidate, onError: (e) => toastError(e), diff --git a/src/whiteboard/Whiteboard.tsx b/src/whiteboard/Whiteboard.tsx index adbeb8d..daa782d 100644 --- a/src/whiteboard/Whiteboard.tsx +++ b/src/whiteboard/Whiteboard.tsx @@ -307,8 +307,7 @@ export default function Whiteboard({ scope }: { scope: { kind: string; id: strin } }; - // Start a drag-to-connect wire from a node's connector handle. - const startWire = (e: React.PointerEvent, nodeId: string) => { + const startWire =(e: React.PointerEvent, nodeId: string) => { e.stopPropagation(); if (!canEdit) return; wire.current = { from: nodeId }; interacting.current = true; @@ -378,8 +377,7 @@ export default function Whiteboard({ scope }: { scope: { kind: string; id: strin } else if (selEdge) { setScene((s) => removeEdge(s, selEdge)); pushDelEdge(selEdge); setSelEdge(null); } }, [selIds, selEdge, pushDelNode, pushDelEdge, clearSel]); - // Pick a shape/pen/connector tool (clears the others). - const armShape = (k: NodeKind) => { setPending({ kind: k }); setTool("select"); setLinkFrom(null); setIconMenu(false); }; + const armShape =(k: NodeKind) => { setPending({ kind: k }); setTool("select"); setLinkFrom(null); setIconMenu(false); }; const armIcon = (icon: string) => { setPending({ kind: "icon", icon }); setTool("select"); setLinkFrom(null); setIconMenu(false); }; const armTool = (t: "select" | "connector" | "pen") => { setTool(t); setPending(null); setLinkFrom(null); setIconMenu(false); }; @@ -737,7 +735,6 @@ function NodeView({ node, selected, canEdit, editing, linkSource, onSelect, onDo ); } -// Flatten [x0,y0,x1,y1,…] into an SVG points attribute. function pointsAttr(pts: number[]): string { let s = ""; for (let i = 0; i + 1 < pts.length; i += 2) s += `${pts[i]},${pts[i + 1]} `; @@ -764,7 +761,6 @@ function ActorGlyph({ color: c }: { color: string }) { ); } -// Tiny glyph shown on each shape tool button. function ShapeGlyph({ kind }: { kind: NodeKind }) { const s = 16; const outline = `2px solid ${color.text}`; diff --git a/src/whiteboard/exportScene.ts b/src/whiteboard/exportScene.ts index 2764598..99b4438 100644 --- a/src/whiteboard/exportScene.ts +++ b/src/whiteboard/exportScene.ts @@ -81,7 +81,6 @@ function nodeSvg(n: WbNode): string { } } -// Build a standalone SVG string for the whole scene, tightly cropped with a margin. export function sceneToSvg(scene: Scene): string { const M = 40; let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; @@ -116,7 +115,6 @@ export function jsonToScene(text: string): Scene { return { nodes: Array.isArray(raw.nodes) ? raw.nodes : [], edges: Array.isArray(raw.edges) ? raw.edges : [] }; } -// Trigger a browser download of a blob. function download(name: string, blob: Blob) { const url = URL.createObjectURL(blob); const a = document.createElement("a"); diff --git a/src/whiteboard/scene.ts b/src/whiteboard/scene.ts index 4c998bc..2ed3c63 100644 --- a/src/whiteboard/scene.ts +++ b/src/whiteboard/scene.ts @@ -47,7 +47,7 @@ export function createStroke(points: number[], color: string): WbNode { }; } -// Translate a node by (dx,dy); for freehand nodes the polyline points move too. +// Freehand nodes also shift their polyline points, not just x/y. export function translateNode(node: WbNode, dx: number, dy: number): Partial { if (node.kind === "draw" && node.points) { return { x: node.x + dx, y: node.y + dy, points: node.points.map((v, i) => v + (i % 2 === 0 ? dx : dy)) };