Story 27: Coverage tracking (full-stack)
Add end-to-end coverage tracking: backend collects vitest coverage, records metrics with threshold/baseline tracking, and blocks acceptance on regression. Frontend displays coverage in gate/review panels with a "Collect Coverage" button. Includes 20 Rust tests, 17 Vitest tests, and 14 Playwright E2E tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,11 @@ interface GateState {
|
||||
failed: number;
|
||||
};
|
||||
missingCategories: string[];
|
||||
coverageReport: {
|
||||
currentPercent: number;
|
||||
thresholdPercent: number;
|
||||
baselinePercent: number | null;
|
||||
} | null;
|
||||
}
|
||||
|
||||
export function Chat({ projectPath, onCloseProject }: ChatProps) {
|
||||
@@ -54,6 +59,8 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
|
||||
const [proceedSuccess, setProceedSuccess] = useState<string | null>(null);
|
||||
const [lastReviewRefresh, setLastReviewRefresh] = useState<Date | null>(null);
|
||||
const [lastGateRefresh, setLastGateRefresh] = useState<Date | null>(null);
|
||||
const [isCollectingCoverage, setIsCollectingCoverage] = useState(false);
|
||||
const [coverageError, setCoverageError] = useState<string | null>(null);
|
||||
|
||||
const storyId = "26_establish_tdd_workflow_and_gates";
|
||||
const gateStatusColor = isGateLoading
|
||||
@@ -185,6 +192,14 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
|
||||
warning: response.warning ?? null,
|
||||
summary: response.summary,
|
||||
missingCategories: response.missing_categories,
|
||||
coverageReport: response.coverage_report
|
||||
? {
|
||||
currentPercent: response.coverage_report.current_percent,
|
||||
thresholdPercent: response.coverage_report.threshold_percent,
|
||||
baselinePercent:
|
||||
response.coverage_report.baseline_percent ?? null,
|
||||
}
|
||||
: null,
|
||||
});
|
||||
setLastGateRefresh(new Date());
|
||||
})
|
||||
@@ -254,6 +269,14 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
|
||||
warning: response.warning ?? null,
|
||||
summary: response.summary,
|
||||
missingCategories: response.missing_categories,
|
||||
coverageReport: response.coverage_report
|
||||
? {
|
||||
currentPercent: response.coverage_report.current_percent,
|
||||
thresholdPercent: response.coverage_report.threshold_percent,
|
||||
baselinePercent:
|
||||
response.coverage_report.baseline_percent ?? null,
|
||||
}
|
||||
: null,
|
||||
});
|
||||
setLastGateRefresh(new Date());
|
||||
} catch (error) {
|
||||
@@ -268,6 +291,21 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleCollectCoverage = async () => {
|
||||
setIsCollectingCoverage(true);
|
||||
setCoverageError(null);
|
||||
try {
|
||||
await workflowApi.collectCoverage({ story_id: storyId });
|
||||
await refreshGateState(storyId);
|
||||
} catch (error) {
|
||||
const message =
|
||||
error instanceof Error ? error.message : "Failed to collect coverage.";
|
||||
setCoverageError(message);
|
||||
} finally {
|
||||
setIsCollectingCoverage(false);
|
||||
}
|
||||
};
|
||||
|
||||
const refreshReviewQueue = async () => {
|
||||
setIsReviewLoading(true);
|
||||
setReviewError(null);
|
||||
@@ -549,8 +587,11 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
|
||||
gateStatusColor={gateStatusColor}
|
||||
isGateLoading={isGateLoading}
|
||||
gateError={gateError}
|
||||
coverageError={coverageError}
|
||||
lastGateRefresh={lastGateRefresh}
|
||||
onRefresh={() => refreshGateState(storyId)}
|
||||
onCollectCoverage={handleCollectCoverage}
|
||||
isCollectingCoverage={isCollectingCoverage}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user