feat(onboarding): 接入包数据可信度提示(commit 规范化比例)
get_commit_stats 暴露项目 commit 规范化统计(total/conventional/rework);治理面板常驻展示规范化比例 + 返工数,比例 <60% 提示种 lefthook。接入包 A/B/C1/C2/D 全部完成。
This commit is contained in:
parent
150535ffcf
commit
58d2c79c74
@ -93,8 +93,8 @@ git 是「过去时 + 量化」(做了什么),蓝图是「将来时 + 语
|
||||
- files: src-tauri/src/commands/buff.rs, src-tauri/src/commands/blueprint.rs
|
||||
- acceptance: check_project 用 `last_commit..HEAD` 取 commit message(git log,复用现有 WSL 路径处理)→ 调 C1 解析写 commit_metrics;首次接入(last_commit 空)限解析最近 N 条;append_usage_snapshot 的 rework_count 改查 commit_metrics(is_rework=1 AND is_ci_auto=0)取代 parse_notes_metrics
|
||||
|
||||
### 🔵 D 数据可信度提示
|
||||
- status: planned
|
||||
### ✅ D 数据可信度提示
|
||||
- status: done
|
||||
- complexity: S
|
||||
- depends: onboarding-pack(卡 C1、卡 B)
|
||||
- files: src-tauri/src/commands/onboarding.rs, src-tauri/src/commands/commit_metrics.rs
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
//! 取代枯竭的「复盘笔记解析」路径。纯解析逻辑(`parse_conventional`)与 DB 无关,可独立单测。
|
||||
|
||||
use rusqlite::Connection;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// 一条 commit 的解析结果。
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@ -114,7 +115,6 @@ pub fn rework_count(conn: &Connection, project_id: &str) -> Result<u32, String>
|
||||
}
|
||||
|
||||
/// commit 规范化统计:(总数, conventional 数)。供数据可信度提示(卡 D)使用。
|
||||
#[allow(dead_code)]
|
||||
pub fn conventional_stats(conn: &Connection, project_id: &str) -> Result<(u32, u32), String> {
|
||||
let total: i64 = conn
|
||||
.query_row(
|
||||
@ -133,6 +133,27 @@ pub fn conventional_stats(conn: &Connection, project_id: &str) -> Result<(u32, u
|
||||
Ok((total as u32, conv as u32))
|
||||
}
|
||||
|
||||
/// 项目 commit 执行质量统计(供前端数据可信度展示)。
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct CommitStats {
|
||||
pub total: u32,
|
||||
pub conventional: u32,
|
||||
pub rework: u32,
|
||||
}
|
||||
|
||||
/// 查询某项目的 commit 执行质量统计(总数 / conventional 数 / 有效返工数)。
|
||||
#[tauri::command]
|
||||
pub fn get_commit_stats(project_id: String) -> Result<CommitStats, String> {
|
||||
let conn = crate::db::pool().get().map_err(|e| e.to_string())?;
|
||||
let (total, conventional) = conventional_stats(&conn, &project_id)?;
|
||||
let rework = rework_count(&conn, &project_id)?;
|
||||
Ok(CommitStats {
|
||||
total,
|
||||
conventional,
|
||||
rework,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@ -267,6 +267,7 @@ pub fn run() {
|
||||
list_buffed_projects,
|
||||
// onboarding pack
|
||||
commands::onboarding::apply_onboarding_pack,
|
||||
commands::commit_metrics::get_commit_stats,
|
||||
// mentor
|
||||
get_mentor_context,
|
||||
get_project_notes,
|
||||
|
||||
@ -23,6 +23,7 @@ import {
|
||||
generateMuhePrompt,
|
||||
archiveProjectDocs,
|
||||
applyOnboardingPack,
|
||||
getCommitStats,
|
||||
removeBlueprintBuff,
|
||||
getBuffStatus,
|
||||
getUserConventionsStatus,
|
||||
@ -39,6 +40,7 @@ import {
|
||||
type StalledProject,
|
||||
type BuffStatus,
|
||||
type TemplateResult,
|
||||
type CommitStats,
|
||||
type UserConventionsStatus,
|
||||
} from "../../lib/commands";
|
||||
|
||||
@ -1061,6 +1063,13 @@ function GovernancePanel({
|
||||
staleTime: 5000,
|
||||
});
|
||||
|
||||
const { data: commitStats } = useQuery<CommitStats | null>({
|
||||
queryKey: ["commit-stats-governance", projectId],
|
||||
queryFn: () => (projectId ? getCommitStats(projectId) : Promise.resolve(null)),
|
||||
enabled: !!projectId,
|
||||
staleTime: 5000,
|
||||
});
|
||||
|
||||
const [syncing, setSyncing] = useState(false);
|
||||
const [syncResult, setSyncResult] = useState<SyncResult | null>(null);
|
||||
const [syncError, setSyncError] = useState<string | null>(null);
|
||||
@ -1521,6 +1530,19 @@ function GovernancePanel({
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{buffStatus?.status === "active" && commitStats && commitStats.total > 0 && (
|
||||
<div className="pt-1.5 border-t border-gray-100 space-y-0.5">
|
||||
<p className="text-[10px] text-gray-500">
|
||||
commit 规范化 {commitStats.conventional}/{commitStats.total}(
|
||||
{Math.round((commitStats.conventional / commitStats.total) * 100)}%) · 返工 {commitStats.rework}
|
||||
</p>
|
||||
{commitStats.conventional / commitStats.total < 0.6 && (
|
||||
<p className="text-[10px] text-amber-600 leading-relaxed">
|
||||
⚠ 规范化偏低,飞轮数据可信度低——种 lefthook(接入包已含)后逐步转准
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 项目信息 */}
|
||||
|
||||
@ -1615,6 +1615,15 @@ export interface OnboardingReport {
|
||||
export const applyOnboardingPack = (projectPath: string, projectId: string) =>
|
||||
invoke<OnboardingReport>("apply_onboarding_pack", { projectPath, projectId });
|
||||
|
||||
export interface CommitStats {
|
||||
total: number;
|
||||
conventional: number;
|
||||
rework: number;
|
||||
}
|
||||
|
||||
export const getCommitStats = (projectId: string) =>
|
||||
invoke<CommitStats>("get_commit_stats", { projectId });
|
||||
|
||||
// ── Beast Global Status (read-only) ───────────────────────────────────────────
|
||||
|
||||
export interface BeastGlobalStatus {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user