refactor: 收口KT工作流护栏
This commit is contained in:
parent
300a34d33e
commit
2372b682a5
22
src/core/cli.ts
Normal file
22
src/core/cli.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import type { GlobalCodeReviewInput } from '../types.js';
|
||||
|
||||
function parseContentScanMode(argv: string[]): 'all' | 'changed' {
|
||||
if (argv.includes('--content-scan-all') || argv.includes('--contentScanMode=all')) {
|
||||
return 'all';
|
||||
}
|
||||
|
||||
const modeIndex = argv.findIndex(
|
||||
(item) => item === '--contentScanMode' || item === '--content-scan-mode',
|
||||
);
|
||||
return modeIndex >= 0 && argv[modeIndex + 1] === 'all' ? 'all' : 'changed';
|
||||
}
|
||||
|
||||
export function parseGlobalReviewCliArgs(
|
||||
argv: string[],
|
||||
): Pick<GlobalCodeReviewInput, 'contentScanMode' | 'includeContentScan' | 'includeRootScan'> {
|
||||
return {
|
||||
contentScanMode: parseContentScanMode(argv),
|
||||
includeContentScan: !argv.includes('--no-content-scan'),
|
||||
includeRootScan: !argv.includes('--no-root-scan'),
|
||||
};
|
||||
}
|
||||
@ -34,6 +34,7 @@ export const taskTypeValues = [
|
||||
'general',
|
||||
'mcp',
|
||||
'page',
|
||||
'refactor',
|
||||
'style',
|
||||
] as const satisfies readonly TaskType[];
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { mkdirSync, writeFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
import { parseGlobalReviewCliArgs } from './core/cli.js';
|
||||
import { registeredToolNames } from './core/constants.js';
|
||||
import { resolveInsideRoot } from './core/workspace.js';
|
||||
import { buildGuardrails } from './tools/guardrails.js';
|
||||
@ -20,6 +21,31 @@ export async function runSelfTest(): Promise<void> {
|
||||
throw new Error('review credential classifier self-check failed');
|
||||
}
|
||||
|
||||
const reviewCliParser = {
|
||||
dashedAll: parseGlobalReviewCliArgs(['node', 'server', '--global-review', '--content-scan-all']).contentScanMode,
|
||||
keyValueAll: parseGlobalReviewCliArgs(['node', 'server', '--global-review', '--contentScanMode=all']).contentScanMode,
|
||||
splitAll: parseGlobalReviewCliArgs(['node', 'server', '--global-review', '--content-scan-mode', 'all']).contentScanMode,
|
||||
};
|
||||
if (
|
||||
reviewCliParser.dashedAll !== 'all' ||
|
||||
reviewCliParser.keyValueAll !== 'all' ||
|
||||
reviewCliParser.splitAll !== 'all'
|
||||
) {
|
||||
throw new Error('global-review CLI content scan mode parser self-check failed');
|
||||
}
|
||||
|
||||
const refactorGuardrails = buildGuardrails({
|
||||
project: 'mcp',
|
||||
taskType: 'refactor',
|
||||
userRequest: '重构 KT 工作流护栏',
|
||||
});
|
||||
const refactorGuardrailOk =
|
||||
refactorGuardrails.guardrails.beforeEdit.some((item) => item.includes('重构前写清楚')) &&
|
||||
refactorGuardrails.guardrails.verification.notes.some((item) => item.includes('重构验证必须覆盖原行为'));
|
||||
if (!refactorGuardrailOk) {
|
||||
throw new Error('refactor guardrail self-check failed');
|
||||
}
|
||||
|
||||
const data = {
|
||||
context: readWorkflowContext({ taskRecordCount: 2 }),
|
||||
guardrails: buildGuardrails({
|
||||
@ -45,7 +71,9 @@ export async function runSelfTest(): Promise<void> {
|
||||
maxFindingsPerProject: 10,
|
||||
projects: ['mcp'],
|
||||
}),
|
||||
reviewCliParser,
|
||||
reviewClassifier,
|
||||
refactorGuardrails,
|
||||
businessTestPlan: buildBusinessTestPlan({
|
||||
flow: 'system-log-visualization',
|
||||
}),
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||
|
||||
import { parseGlobalReviewCliArgs } from './core/cli.js';
|
||||
import { cleanupHistoryArtifacts, parseCliCleanupArgs } from './tools/cleanup.js';
|
||||
import { buildGlobalCodeReview } from './tools/review.js';
|
||||
import { registerTools } from './registerTools.js';
|
||||
@ -12,10 +13,7 @@ if (process.argv.includes('--cleanup-history')) {
|
||||
} else if (process.argv.includes('--global-review')) {
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
await buildGlobalCodeReview({
|
||||
includeContentScan: !process.argv.includes('--no-content-scan'),
|
||||
includeRootScan: !process.argv.includes('--no-root-scan'),
|
||||
}),
|
||||
await buildGlobalCodeReview(parseGlobalReviewCliArgs(process.argv)),
|
||||
null,
|
||||
2,
|
||||
),
|
||||
|
||||
@ -1,20 +1,24 @@
|
||||
import type { GuardrailSet, GuardrailsInput, GuardrailsResult } from '../types.js';
|
||||
import { resolveProject } from '../core/workspace.js';
|
||||
import { buildKarpathyGuardrails } from './karpathy.js';
|
||||
import { createPageTestCase } from './testing.js';
|
||||
import { buildVerificationPlan } from './verification.js';
|
||||
export function buildGuardrails(input: GuardrailsInput): GuardrailsResult {
|
||||
const project = resolveProject(input.project);
|
||||
const taskType = input.taskType || input.changeType || 'general';
|
||||
const paths = input.paths || [];
|
||||
const karpathy = buildKarpathyGuardrails(taskType);
|
||||
const guardrails: GuardrailSet = {
|
||||
beforeEdit: [
|
||||
'先读最近的 AGENTS.md,再读 TASKS.md 和 SKILLS.md。',
|
||||
...karpathy.beforeEdit,
|
||||
'先检查目标仓库状态,识别用户已有改动,不覆盖、不回滚无关文件。',
|
||||
'先快速扫现有代码风格和目录结构,再决定改动方式。',
|
||||
'搜索优先用 rg,路径和接口名尽量保持可 grep。',
|
||||
],
|
||||
duringEdit: [
|
||||
'改动范围收敛到本次需求相关文件。',
|
||||
...karpathy.duringEdit,
|
||||
'复杂度高的地方加简短注释,避免空泛解释。',
|
||||
'后端真实环境配置不写入提交内容;前端 env 只允许客户端公开变量。',
|
||||
'前端实现遵循现有 antdv-next/Vben 约定,不引入无关抽象。',
|
||||
@ -24,6 +28,7 @@ export function buildGuardrails(input: GuardrailsInput): GuardrailsResult {
|
||||
'不要在用户未要求时自动 push。',
|
||||
'不要把真实密钥、数据库密码、生产 env 写进可提交文件。',
|
||||
'不要为了验证长期保留 Node/Vite/浏览器进程。',
|
||||
...karpathy.doNot,
|
||||
],
|
||||
verification: buildVerificationPlan({
|
||||
changeType: taskType,
|
||||
@ -65,6 +70,8 @@ export function buildGuardrails(input: GuardrailsInput): GuardrailsResult {
|
||||
guardrails.verification.notes.push('MCP 变更至少跑 self-test 和真实 SDK Client smoke test。');
|
||||
}
|
||||
|
||||
guardrails.verification.notes.push(...karpathy.verificationNotes);
|
||||
|
||||
return {
|
||||
guardrails,
|
||||
paths,
|
||||
|
||||
48
src/tools/karpathy.ts
Normal file
48
src/tools/karpathy.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import type { TaskType } from '../types.js';
|
||||
|
||||
export interface KarpathyGuardrailItems {
|
||||
beforeEdit: string[];
|
||||
doNot: string[];
|
||||
duringEdit: string[];
|
||||
verificationNotes: string[];
|
||||
}
|
||||
|
||||
export const karpathyPrepareTaskStep =
|
||||
'先写清假设、范围边界和可验证成功标准;需求过宽时先审计并收敛目标。';
|
||||
|
||||
export const karpathyCommitChecklistItem =
|
||||
'确认改动没有引入无验收标准的整体重构或顺手格式化。';
|
||||
|
||||
export const karpathyChangedFileChecklistItem =
|
||||
'提交前确认每个 changed file 都属于本次请求、必要验证修复或 KT 记录维护。';
|
||||
|
||||
export const karpathyFinishNote =
|
||||
'按 karpathy-guidelines 审计:没有明确假设、范围和成功证据的整体优化不应生成提交。';
|
||||
|
||||
export const karpathyStopCondition =
|
||||
'发现全仓格式化、整文件重写或无验收标准重构时停止提交。';
|
||||
|
||||
export function buildKarpathyGuardrails(taskType: TaskType): KarpathyGuardrailItems {
|
||||
const items: KarpathyGuardrailItems = {
|
||||
beforeEdit: [
|
||||
'非平凡编码/评审/重构任务先套用 karpathy-guidelines:明确假设、取最小可行改动、定义可验证成功标准。',
|
||||
'如果需求是笼统优化或整体重构,先把它收敛成具体行为、风险和验收证据;没有明确目标时只做审计不制造 diff。',
|
||||
],
|
||||
doNot: [
|
||||
'不要为了“整体优化”做全仓格式化、整文件重写或无验收标准的重构。',
|
||||
],
|
||||
duringEdit: [
|
||||
'每一行改动都应能追溯到用户请求、必要验证修复或 KT 记录维护。',
|
||||
'不为单次使用新增抽象,不引入未被请求的灵活性或配置项。',
|
||||
],
|
||||
verificationNotes: [],
|
||||
};
|
||||
|
||||
if (taskType === 'refactor') {
|
||||
items.beforeEdit.push('重构前写清楚要保留的行为、要降低的复杂度和不触碰的边界。');
|
||||
items.duringEdit.push('只合并真实重复、删除自己造成的孤儿代码,避免顺手重构邻近模块。');
|
||||
items.verificationNotes.push('重构验证必须覆盖原行为;没有行为证据时不要声称重构完成。');
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
@ -6,6 +6,7 @@ import { detectRepoType } from '../core/project.js';
|
||||
import { formatDateInShanghai, getRecentTaskRecords, getTaskContextIndex, listWorkspaceSkills, readText, resolveInsideRoot, resolveProject, workspaceRoot } from '../core/workspace.js';
|
||||
import { buildGuardrails } from './guardrails.js';
|
||||
import { inspectProject } from './inspect.js';
|
||||
import { karpathyCommitChecklistItem, karpathyPrepareTaskStep } from './karpathy.js';
|
||||
import { scanTaskRisk } from './envRisk.js';
|
||||
import { createPageTestCase } from './testing.js';
|
||||
export async function prepareTask(input: PrepareTaskInput): Promise<Record<string, unknown>> {
|
||||
@ -35,6 +36,7 @@ export async function prepareTask(input: PrepareTaskInput): Promise<Record<strin
|
||||
nextSteps: [
|
||||
'阅读 workPacket.readFirst 中的上下文文件。',
|
||||
'按 guardrails.beforeEdit 做开工前检查。',
|
||||
karpathyPrepareTaskStep,
|
||||
'按最小范围修改。',
|
||||
'按 verification.commands 和 notes 做轻量验证。',
|
||||
'有文件改动时更新 TASKS.md。',
|
||||
@ -113,6 +115,7 @@ export async function createCommitChecklist(input: CommitChecklistInput): Promis
|
||||
return {
|
||||
checks: [
|
||||
'确认只包含本次任务相关文件。',
|
||||
karpathyCommitChecklistItem,
|
||||
'先运行对应轻量验证命令。',
|
||||
'提交前已跑全局 review,且 findings 已修复;确认误报时优先修 ktWorkflow 过滤规则,避免污染后续上下文。',
|
||||
'每个独立 Git 仓库单独 commit。',
|
||||
|
||||
@ -29,14 +29,14 @@ export function buildVerificationPlan(input: VerificationPlanInput): Verificatio
|
||||
|
||||
if (
|
||||
typecheck &&
|
||||
['api', 'frontend', 'general', 'mcp', 'page', 'style'].includes(changeType)
|
||||
['api', 'frontend', 'general', 'mcp', 'page', 'refactor', 'style'].includes(changeType)
|
||||
) {
|
||||
commands.push(typecheck);
|
||||
}
|
||||
|
||||
if (project.alias === 'api') {
|
||||
if (scripts.lint) commands.push(`${packageManager} run lint`);
|
||||
if (scripts.test && ['api', 'backend', 'general'].includes(changeType)) {
|
||||
if (scripts.test && ['api', 'backend', 'general', 'refactor'].includes(changeType)) {
|
||||
commands.push(`${packageManager} test -- --passWithNoTests`);
|
||||
}
|
||||
if (['api', 'backend'].includes(changeType)) {
|
||||
@ -76,6 +76,10 @@ export function buildVerificationPlan(input: VerificationPlanInput): Verificatio
|
||||
);
|
||||
}
|
||||
|
||||
if (changeType === 'refactor') {
|
||||
notes.push('重构验证要证明原行为保持不变,并说明复杂度、重复或职责边界改善点;没有明确收益时不要改。');
|
||||
}
|
||||
|
||||
if (repoType === 'git') commands.push('git diff --check');
|
||||
commands.push(getGlobalReviewCommand());
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ import { tryExecFile, tryPowerShell } from '../core/exec.js';
|
||||
import { detectRepoType, parseGitStatusFiles } from '../core/project.js';
|
||||
import { formatDateInShanghai, getGlobalReviewCommand, resolveProject, toPosix, workspaceRoot } from '../core/workspace.js';
|
||||
import { cleanupHistoryArtifacts } from './cleanup.js';
|
||||
import { karpathyChangedFileChecklistItem, karpathyFinishNote, karpathyStopCondition } from './karpathy.js';
|
||||
import { buildGlobalCodeReview, defaultReviewProjects } from './review.js';
|
||||
import { buildVerificationPlan } from './verification.js';
|
||||
interface RepoSnapshot {
|
||||
@ -113,6 +114,7 @@ export async function buildFinishTask(input: FinishTaskInput = {}): Promise<Reco
|
||||
'默认只生成收尾结果;runValidation=true 时才执行验证命令。',
|
||||
'cleanupHistory=true 时才真实清理历史产物,否则仅预览。',
|
||||
'提交和推送仍需用户明确要求后执行。',
|
||||
karpathyFinishNote,
|
||||
],
|
||||
review,
|
||||
snapshots,
|
||||
@ -120,6 +122,7 @@ export async function buildFinishTask(input: FinishTaskInput = {}): Promise<Reco
|
||||
'review.findings 存在 P1/P2 时先判定真实风险或工具误报;真实风险修代码,工具误报修 ktWorkflow,再复跑 kt_global_code_review。',
|
||||
'validationRuns 中任一 result.ok=false 时先处理失败命令。',
|
||||
'发现真实 env、生产 Secret 或非本次范围文件时停止提交。',
|
||||
karpathyStopCondition,
|
||||
],
|
||||
summary: {
|
||||
changedProjectCount: changedSnapshots.length,
|
||||
@ -138,6 +141,7 @@ export async function buildCommitPlan(input: CommitPlanInput = {}): Promise<Reco
|
||||
return {
|
||||
checks: [
|
||||
'每个独立 Git 仓库单独提交。',
|
||||
karpathyChangedFileChecklistItem,
|
||||
'提交前先跑对应轻量验证和 kt_global_code_review。',
|
||||
'kt_global_code_review findings 必须为 0;确认误报时先修 ktWorkflow 规则,不把误报当作长期上下文。',
|
||||
'后端真实 .env.development/.env.production 不提交;前端 env 可提交但只能包含客户端公开配置。',
|
||||
|
||||
@ -22,6 +22,7 @@ export type TaskType =
|
||||
| 'general'
|
||||
| 'mcp'
|
||||
| 'page'
|
||||
| 'refactor'
|
||||
| 'style';
|
||||
|
||||
export interface ResolvedProject {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user