fix(dashboard): remove Lineage commit associations from Task Changes UI
The lineage commit associations panel surfaced low-value provenance metadata. Remove the UI section, its dead state/fetch wiring in TaskChangesTab, the orphaned .task-lineage-* CSS, and the obsolete tests. The fetchTaskCommitAssociations API helper is retained.
This commit is contained in:
@@ -35,103 +35,3 @@
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.task-lineage-associations {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-sm);
|
||||
margin-bottom: var(--space-md);
|
||||
padding: var(--space-sm);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.task-lineage-associations-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
|
||||
.task-lineage-associations-header h4 {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-xs);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.task-lineage-id,
|
||||
.task-lineage-sha {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.task-lineage-associations-empty {
|
||||
margin: 0;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.task-lineage-associations-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-xs);
|
||||
}
|
||||
|
||||
.task-lineage-association {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-xs);
|
||||
padding: var(--space-sm);
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
background: var(--card);
|
||||
}
|
||||
|
||||
.task-lineage-association--legacy,
|
||||
.task-lineage-association--ambiguous {
|
||||
opacity: 0.78;
|
||||
}
|
||||
|
||||
.task-lineage-association-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
|
||||
.task-lineage-subject {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.task-lineage-association-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-sm);
|
||||
color: var(--text-muted);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.task-lineage-note {
|
||||
margin: 0;
|
||||
color: var(--text-dim);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.task-lineage-associations-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.task-lineage-association-main {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.task-lineage-subject {
|
||||
white-space: normal;
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@ import type { MergeDetails, Column } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import {
|
||||
fetchTaskDiff,
|
||||
fetchTaskCommitAssociations,
|
||||
type TaskDiff,
|
||||
type TaskCommitAssociationRow,
|
||||
} from "../api";
|
||||
import { highlightDiff } from "../utils/highlightDiff";
|
||||
import { ChangesDiffModal } from "./ChangesDiffModal";
|
||||
@@ -129,8 +127,6 @@ export function TaskChangesTab({ taskId, worktree, projectId, column, mergeDetai
|
||||
const [stats, setStats] = useState<{ filesChanged: number; additions: number; deletions: number }>({ filesChanged: 0, additions: 0, deletions: 0 });
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [commitAssociations, setCommitAssociations] = useState<TaskCommitAssociationRow[]>([]);
|
||||
const [lineageId, setLineageId] = useState<string | null>(null);
|
||||
const [expandedFiles, setExpandedFiles] = useState<Set<string>>(new Set());
|
||||
const [currentFileIndex, setCurrentFileIndex] = useState<number | null>(null);
|
||||
const [wordWrap, setWordWrap] = useState(true);
|
||||
@@ -150,9 +146,6 @@ export function TaskChangesTab({ taskId, worktree, projectId, column, mergeDetai
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const associationsData = await fetchTaskCommitAssociations(taskId, projectId);
|
||||
setLineageId(associationsData.lineageId);
|
||||
setCommitAssociations(associationsData.associations);
|
||||
|
||||
if (!canLoad) {
|
||||
setFiles([]);
|
||||
@@ -261,47 +254,6 @@ export function TaskChangesTab({ taskId, worktree, projectId, column, mergeDetai
|
||||
}
|
||||
|
||||
|
||||
const renderCommitAssociations = () => (
|
||||
<section className="task-lineage-associations" aria-label="Task commit associations">
|
||||
<div className="task-lineage-associations-header">
|
||||
<h4>
|
||||
<GitCommit size={16} />
|
||||
Lineage commit associations
|
||||
</h4>
|
||||
{lineageId && (
|
||||
<code className="task-lineage-id">{lineageId}</code>
|
||||
)}
|
||||
</div>
|
||||
{commitAssociations.length === 0 ? (
|
||||
<p className="task-lineage-associations-empty">No associated commits recorded yet.</p>
|
||||
) : (
|
||||
<div className="task-lineage-associations-list">
|
||||
{commitAssociations.map((association) => {
|
||||
const matchedLabel = association.matchedBy.replace(/-/g, " ");
|
||||
return (
|
||||
<article
|
||||
key={`${association.commitSha}-${association.matchedBy}`}
|
||||
className={`task-lineage-association task-lineage-association--${association.confidence}`}
|
||||
>
|
||||
<div className="task-lineage-association-main">
|
||||
<code className="task-lineage-sha">{association.commitSha.slice(0, 7)}</code>
|
||||
<span className="task-lineage-subject">{association.commitSubject}</span>
|
||||
</div>
|
||||
<div className="task-lineage-association-meta">
|
||||
<span>{new Date(association.authoredAt).toLocaleString()}</span>
|
||||
<span>Confidence: {association.confidence}</span>
|
||||
<span>Match: {matchedLabel}</span>
|
||||
<span>Task snapshot: {association.taskIdSnapshot}</span>
|
||||
</div>
|
||||
{association.note && <p className="task-lineage-note">{association.note}</p>}
|
||||
</article>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
|
||||
if (files.length === 0) {
|
||||
if (isDone && !isDoneWithCommit) {
|
||||
const doneFallbackFiles = mergeDetails?.landedFiles && mergeDetails.landedFiles.length > 0
|
||||
@@ -337,7 +289,6 @@ export function TaskChangesTab({ taskId, worktree, projectId, column, mergeDetai
|
||||
|
||||
return (
|
||||
<div className="detail-section task-changes-tab">
|
||||
{renderCommitAssociations()}
|
||||
<div className="task-changes-state task-changes-state--empty">
|
||||
<FileCode size={24} />
|
||||
<p>No files modified.</p>
|
||||
@@ -353,7 +304,6 @@ export function TaskChangesTab({ taskId, worktree, projectId, column, mergeDetai
|
||||
|
||||
return (
|
||||
<div className="detail-section task-changes-tab">
|
||||
{renderCommitAssociations()}
|
||||
{/* Commit metadata for done tasks */}
|
||||
{isDone && mergeDetails && (
|
||||
<div className="commit-diff-meta">
|
||||
|
||||
@@ -6,7 +6,6 @@ import { TaskChangesTab } from "../TaskChangesTab";
|
||||
|
||||
const useTaskDiffStatsMock = vi.fn();
|
||||
const fetchTaskDiffMock = vi.fn();
|
||||
const fetchTaskCommitAssociationsMock = vi.fn();
|
||||
|
||||
vi.mock("../../hooks/useTaskDiffStats", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../../hooks/useTaskDiffStats")>();
|
||||
@@ -21,7 +20,6 @@ vi.mock("../../api", async (importOriginal) => {
|
||||
return {
|
||||
...actual,
|
||||
fetchTaskDiff: (...args: unknown[]) => fetchTaskDiffMock(...args),
|
||||
fetchTaskCommitAssociations: (...args: unknown[]) => fetchTaskCommitAssociationsMock(...args),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -83,8 +81,6 @@ describe("TaskCard/TaskChangesTab files-changed parity", () => {
|
||||
beforeEach(() => {
|
||||
useTaskDiffStatsMock.mockReset();
|
||||
fetchTaskDiffMock.mockReset();
|
||||
fetchTaskCommitAssociationsMock.mockReset();
|
||||
fetchTaskCommitAssociationsMock.mockResolvedValue({ taskId: "FN-4521", lineageId: null, associations: [] });
|
||||
});
|
||||
|
||||
it.each([
|
||||
|
||||
@@ -5,11 +5,9 @@ import { TaskChangesTab } from "../TaskChangesTab";
|
||||
import type { MergeDetails, Column } from "@fusion/core";
|
||||
|
||||
const mockFetchTaskDiff = vi.fn();
|
||||
const mockFetchTaskCommitAssociations = vi.fn();
|
||||
|
||||
vi.mock("../../api", () => ({
|
||||
fetchTaskDiff: (...args: any[]) => mockFetchTaskDiff(...args),
|
||||
fetchTaskCommitAssociations: (...args: any[]) => mockFetchTaskCommitAssociations(...args),
|
||||
}));
|
||||
|
||||
vi.mock("lucide-react", () => ({
|
||||
@@ -51,52 +49,6 @@ const MERGE_DETAILS: MergeDetails = {
|
||||
|
||||
beforeEach(() => {
|
||||
mockFetchTaskDiff.mockReset();
|
||||
mockFetchTaskCommitAssociations.mockReset();
|
||||
mockFetchTaskCommitAssociations.mockResolvedValue({
|
||||
taskId: "FN-001",
|
||||
lineageId: "lineage-1",
|
||||
associations: [],
|
||||
});
|
||||
});
|
||||
|
||||
describe("TaskChangesTab — commit associations", () => {
|
||||
it("renders empty-state copy when no commit associations exist", async () => {
|
||||
mockFetchTaskDiff.mockResolvedValue({ files: [], stats: { filesChanged: 0, additions: 0, deletions: 0 } });
|
||||
|
||||
render(<TaskChangesTab taskId="FN-001" worktree="/path/to/worktree" column="in-progress" />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("No associated commits recorded yet.")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it("renders populated commit association rows with confidence metadata", async () => {
|
||||
mockFetchTaskDiff.mockResolvedValue({ files: [], stats: { filesChanged: 0, additions: 0, deletions: 0 } });
|
||||
mockFetchTaskCommitAssociations.mockResolvedValue({
|
||||
taskId: "FN-001",
|
||||
lineageId: "lineage-1",
|
||||
associations: [
|
||||
{
|
||||
commitSha: "abc1234567",
|
||||
commitSubject: "feat: lineage",
|
||||
authoredAt: "2026-05-11T00:00:00.000Z",
|
||||
matchedBy: "manual-reconciliation",
|
||||
confidence: "ambiguous",
|
||||
taskIdSnapshot: "FN-3953",
|
||||
note: "legacy mismatch",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const { container } = render(<TaskChangesTab taskId="FN-001" worktree="/path/to/worktree" column="in-progress" />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("feat: lineage")).toBeTruthy();
|
||||
});
|
||||
expect(screen.getByText("Confidence: ambiguous")).toBeTruthy();
|
||||
expect(screen.getByText("Match: manual reconciliation")).toBeTruthy();
|
||||
expect(container.querySelector(".task-lineage-association--ambiguous")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("TaskChangesTab — worktree-backed (non-done tasks)", () => {
|
||||
|
||||
Reference in New Issue
Block a user