fix(workflow): 收紧清理闭环门禁
This commit is contained in:
parent
432320dd9d
commit
b5f24b3ee0
16
README.md
16
README.md
@ -18,7 +18,7 @@
|
||||
- 生成卡点固化记录:把超时、卡进程、远程命令误写、重复失败整理成“问题点 / 稳定解法 / 后续入口 / 验证证据”,避免原样重试。
|
||||
- 生成改动文档同步计划:按变更文件自动提示需要同步的 README、API、AGENTS、docs、Obsidian、skill 和 ktWorkflow 入口。
|
||||
- 生成多仓库提交/推送计划:按仓库分组、建议提交信息、列出提交和推送前检查。
|
||||
- 生成远程只读健康检查和数据库同步安全向导:覆盖飞牛 NAS 服务探测、GTID、备份和行数校验。
|
||||
- 生成远程只读健康检查和数据库同步安全向导:覆盖飞牛 NAS 服务探测、GTID、`.kt-workspace/db-sync` 转储、备份库和行数校验。
|
||||
- 生成专项组件工作流:KtTable、BlogArgon、AdminAuth、QQBot、FF14Plugin、NapCatLogin、SystemLog、Knife4jSwagger、FnosK8s 的防踩坑清单和验证点。
|
||||
- 生成验证进程清理计划:按项目路径和端口给出 PowerShell 检查命令,不直接杀进程。
|
||||
- 清理历史产物:统一治理 `.kt-workspace` 下的测试/验证产物,按目录最近修改时间只保留最近 3 轮,模板目录永久保留;CLI 默认 dry-run,真实清理必须显式传 `--execute`。
|
||||
@ -37,7 +37,7 @@ pnpm run self-test
|
||||
pnpm run obsidian-context -- --module ktWorkflow
|
||||
pnpm run obsidian-validate
|
||||
pnpm run obsidian-sync
|
||||
pnpm run workstream-closeout -- --title "发布闭环" --verification "Jenkins SUCCESS" --problem "无新卡点" --solution "无新增稳定解法"
|
||||
pnpm run workstream-closeout -- --title "发布闭环" --verification "Jenkins SUCCESS" --doc-sync "无需文档更新" --cleanup "cleanup-history dry-run deleted=0" --cleanup-final-deleted 0 --review "global-review findings=0" --problem "无新卡点" --solution "无新增稳定解法"
|
||||
pnpm run cleanup-history -- --dry-run
|
||||
pnpm run cleanup-history -- --execute
|
||||
pnpm run admin-login -- --url http://127.0.0.1:5999/#/auth/login
|
||||
@ -85,8 +85,8 @@ pnpm run admin-login -- --url http://127.0.0.1:5999/#/auth/login
|
||||
| `kt_risk_scan` | 扫描当前或传入变更文件的偏差风险 |
|
||||
| `kt_global_code_review` | 对 KT 全部子仓库做只读全局 CodeReview 扫描,默认仅深扫变更文件 |
|
||||
| `kt_finish_task` | 生成任务收尾包,可选执行验证和历史清理 |
|
||||
| `kt_workstream_closeout` | 生成大方向完结闭环审计,检查测试证据、问题记录、稳定解法和 ktWorkflow 升级 |
|
||||
| `kt_workflow_loop_audit` | 审计上下文锚定、测试证据、文档同步、历史清理、问题固化、ktWorkflow 升级和全局 review |
|
||||
| `kt_workstream_closeout` | 生成大方向完结闭环审计,检查测试证据、文档同步、历史清理、全局 review、问题记录、稳定解法和 ktWorkflow 升级 |
|
||||
| `kt_workflow_loop_audit` | 审计上下文锚定、测试证据、文档同步、历史清理最终 `deleted=0`、问题固化、ktWorkflow 升级和全局 review |
|
||||
| `kt_change_doc_sync` | 根据改动路径生成 README/API/AGENTS/docs/Obsidian/skill/ktWorkflow 文档同步清单 |
|
||||
| `kt_commit_plan` | 生成多仓库提交计划、建议 commit message 和检查项 |
|
||||
| `kt_push_plan` | 生成多仓库推送计划和远程异常提醒 |
|
||||
@ -111,7 +111,7 @@ pnpm run admin-login -- --url http://127.0.0.1:5999/#/auth/login
|
||||
| `pnpm run obsidian-validate` | 校验 KT Obsidian vault 结构和链接;默认 warning 不让脚本失败,需要严格模式时传 `--fail-on-warnings`。 |
|
||||
| `pnpm run obsidian-sync` | 审计 Obsidian 工作流入口是否连通,并联动执行 validate。 |
|
||||
| `pnpm run cleanup-history` | 预览 `.kt-workspace` 测试/验证历史产物清理;真实清理用 `pnpm run cleanup-history -- --execute`,只保留最近 3 轮并保留模板目录。 |
|
||||
| `pnpm run workstream-closeout` | 生成大方向完结闭环审计;识别测试证据、问题记录、稳定解法和 ktWorkflow 升级缺口。 |
|
||||
| `pnpm run workstream-closeout` | 生成大方向完结闭环审计;识别测试证据、文档同步、历史清理、全局 review、问题记录、稳定解法和 ktWorkflow 升级缺口。 |
|
||||
|
||||
## 项目别名
|
||||
|
||||
@ -146,12 +146,12 @@ pnpm run admin-login -- --url http://127.0.0.1:5999/#/auth/login
|
||||
- 大方向结束前调用 `kt_workstream_closeout` 或 `pnpm run workstream-closeout`;如果识别到测试流、卡点、误报、清理、部署观测或命令模板,要先升级对应 ktWorkflow 规则再报告完成。
|
||||
- 文件改动完成并验证后调用 `kt_global_code_review`,或运行 `pnpm run global-review`;它只读扫描,不删除文件、不提交代码。`findings` 先判定真实风险或工具误报,真实风险修业务代码,误报修 `mcp/ktWorkflow/src/tools/review.ts` 并复跑。
|
||||
- 代码或配置改动后调用 `kt_change_doc_sync`,把需要同步的 README/API/AGENTS/docs/Obsidian/skill/ktWorkflow 入口补齐;无需同步时把原因写进收尾证据。
|
||||
- 报告非平凡任务完成前调用 `kt_workflow_loop_audit`,确认测试证据、文档同步、历史清理、问题固化、ktWorkflow 升级和全局 review 都过门。
|
||||
- 报告非平凡任务完成前调用 `kt_workflow_loop_audit`,确认测试证据、文档同步、历史清理最终 `deleted=0`、问题固化、ktWorkflow 升级和全局 review 都过门;清理证据必须显式传入,不能依赖缺省值。
|
||||
- 要提交或推送时先调用 `kt_commit_plan` / `kt_push_plan`,按仓库分组确认范围。
|
||||
- 要测真实业务链路时调用 `kt_business_test_plan`,选择 `admin-login`、`qqbot-auto-reply`、`qqbot-login-sse`、`fflogs-command`、`system-log-visualization` 等 flow。
|
||||
- 遇到同一命令或同一远程步骤重复卡住时调用 `kt_blocker_resolution`;第二次仍失败时先写入 `TASKS.md` 或补成脚本,再继续。
|
||||
- 远程服务排查先调用 `kt_remote_health_check`,默认只生成只读命令;需要执行时显式传 `execute=true`。
|
||||
- 数据库同步前调用 `kt_db_sync_plan`,先确认源库、目标库、备份库和校验点。
|
||||
- 数据库同步前调用 `kt_db_sync_plan`,先确认源库、目标库、`.kt-workspace/db-sync` 转储目录、带时间戳的备份库和校验点。
|
||||
- 改 KtTable、BlogArgon、AdminAuth、QQBot、FF14Plugin、NapCatLogin、SystemLog、Knife4jSwagger、FnosK8s 前调用 `kt_component_workflow`,先看专项禁区。
|
||||
- 不确定任务边界时调用 `kt_guardrails`,先拿到“能做什么、不能做什么、怎么验证”。
|
||||
- 验证前调用 `kt_suggest_verification`,避免盲跑全量构建。
|
||||
@ -160,7 +160,7 @@ pnpm run admin-login -- --url http://127.0.0.1:5999/#/auth/login
|
||||
- Admin 页面测试前可先执行 `pnpm run admin-login -- --url <Admin登录页>` 固化登录态,输出的 `storageState` 可作为后续 Playwright 用例前置状态。
|
||||
- 接口改动后调用 `kt_api_test_plan`,并真实请求一次接口。
|
||||
- 验证启动过本地服务后调用 `kt_cleanup_process_plan`,清掉本次进程。
|
||||
- 每轮测试/验证结束后先调用 `kt_cleanup_history`,或运行 `pnpm run cleanup-history -- --dry-run`;如果预览里 `deleted` 非空,确认范围后执行 `pnpm run cleanup-history -- --execute`,让 `.kt-workspace` 历史产物只保留最近 3 轮并保留模板目录。
|
||||
- 每轮测试/验证结束后先调用 `kt_cleanup_history`,或运行 `pnpm run cleanup-history -- --dry-run`;如果预览里 `deleted` 非空,确认范围后执行 `pnpm run cleanup-history -- --execute`,再复跑 dry-run 确认 `deleted=0`,让 `.kt-workspace` 历史产物只保留最近 3 轮并保留模板目录。`kt_workstream_closeout` / `kt_workflow_loop_audit` 只认可明确的 `deleted=0` / `deleted=[]` 文本证据或结构化 `cleanupFinalDeleted=0`。
|
||||
- 更新 Obsidian 图谱、模块页、文档矩阵或 `.obsidian` 配置后运行 `kt_obsidian_validate` / `pnpm run obsidian-validate`;整理工作流入口时再跑 `kt_obsidian_sync`。
|
||||
- 改完文件后用 `kt_append_task_record` 先 `dryRun` 预览记录,再决定是否写入。
|
||||
- 提交前调用 `kt_commit_checklist`,确认文件范围和提交信息。
|
||||
|
||||
@ -65,11 +65,18 @@ export function parseWorkstreamCloseoutCliArgs(argv: string[]): WorkstreamCloseo
|
||||
const patternSet = new Set<string>(workstreamReusablePatternValues);
|
||||
const patterns = readOptionList(argv, ['--pattern', '--reusable-pattern'])
|
||||
.filter((pattern): pattern is WorkstreamReusablePattern => patternSet.has(pattern));
|
||||
const cleanupFinalDeleted = Number(
|
||||
readOption(argv, ['--cleanup-final-deleted', '--cleanupFinalDeleted']),
|
||||
);
|
||||
|
||||
return {
|
||||
changedProjects: readOptionList(argv, ['--project', '--changed-project']),
|
||||
cleanupEvidence: readOptionList(argv, ['--cleanup', '--cleanup-evidence']),
|
||||
cleanupFinalDeleted: Number.isFinite(cleanupFinalDeleted) ? cleanupFinalDeleted : undefined,
|
||||
docSyncEvidence: readOptionList(argv, ['--doc-sync', '--doc-sync-evidence']),
|
||||
ktWorkflowUpdated: argv.includes('--kt-workflow-updated'),
|
||||
problemRecords: readOptionList(argv, ['--problem', '--problem-record']),
|
||||
reviewEvidence: readOptionList(argv, ['--review', '--review-evidence']),
|
||||
reusablePatterns: patterns,
|
||||
stableSolutions: readOptionList(argv, ['--solution', '--stable-solution']),
|
||||
title: readOption(argv, ['--title']),
|
||||
|
||||
@ -249,8 +249,12 @@ export function registerTools(server: McpServer): void {
|
||||
description: '生成大方向完结闭环审计:开发、测试证据、问题记录、稳定解法和 ktWorkflow 升级是否齐备。',
|
||||
inputSchema: {
|
||||
changedProjects: z.array(z.string()).default([]),
|
||||
cleanupEvidence: z.array(z.string()).default([]),
|
||||
cleanupFinalDeleted: z.number().int().min(0).optional(),
|
||||
docSyncEvidence: z.array(z.string()).default([]),
|
||||
ktWorkflowUpdated: z.boolean().default(false),
|
||||
problemRecords: z.array(z.string()).default([]),
|
||||
reviewEvidence: z.array(z.string()).default([]),
|
||||
reusablePatterns: z.array(z.enum(workstreamReusablePatternValues)).default(['none']),
|
||||
stableSolutions: z.array(z.string()).default([]),
|
||||
title: z.string().optional(),
|
||||
@ -269,7 +273,9 @@ export function registerTools(server: McpServer): void {
|
||||
inputSchema: {
|
||||
changedProjects: z.array(z.string()).default([]),
|
||||
cleanupExecuted: z.boolean().default(false),
|
||||
cleanupPreviewDeleted: z.number().int().min(0).default(0),
|
||||
cleanupEvidence: z.array(z.string()).default([]),
|
||||
cleanupFinalDeleted: z.number().int().min(0).optional(),
|
||||
cleanupPreviewDeleted: z.number().int().min(0).optional(),
|
||||
docSyncEvidence: z.array(z.string()).default([]),
|
||||
failureRecords: z.array(z.string()).default([]),
|
||||
ktWorkflowUpdated: z.boolean().default(false),
|
||||
|
||||
@ -101,6 +101,9 @@ export async function runSelfTest(): Promise<void> {
|
||||
}
|
||||
|
||||
const closeoutNeedsUpgrade = buildWorkstreamCloseout({
|
||||
cleanupEvidence: ['cleanup-history dry-run deleted=0。'],
|
||||
docSyncEvidence: ['kt_change_doc_sync requiredDocs 已处理。'],
|
||||
reviewEvidence: ['global-review findings=0。'],
|
||||
problemRecords: ['Jenkins 状态需要结合日志和 K8s 状态确认。'],
|
||||
reusablePatterns: ['deploy-observation'],
|
||||
stableSolutions: ['收尾时固定观测 build、commit、镜像、Deployment、Pod 和 smoke。'],
|
||||
@ -112,8 +115,11 @@ export async function runSelfTest(): Promise<void> {
|
||||
}
|
||||
|
||||
const closeoutComplete = buildWorkstreamCloseout({
|
||||
cleanupEvidence: ['cleanup-history dry-run deleted=0。'],
|
||||
docSyncEvidence: ['无需文档更新,已记录原因。'],
|
||||
ktWorkflowUpdated: true,
|
||||
problemRecords: ['无新卡点。'],
|
||||
reviewEvidence: ['global-review findings=0。'],
|
||||
reusablePatterns: ['none'],
|
||||
stableSolutions: ['无新增稳定解法。'],
|
||||
title: '文档收尾',
|
||||
@ -123,6 +129,20 @@ export async function runSelfTest(): Promise<void> {
|
||||
throw new Error('workstream closeout complete self-check failed');
|
||||
}
|
||||
|
||||
const closeoutInvalidCleanup = buildWorkstreamCloseout({
|
||||
cleanupEvidence: ['cleanup-history dry-run deleted=3。'],
|
||||
docSyncEvidence: ['kt_change_doc_sync requiredDocs 已处理。'],
|
||||
problemRecords: ['无新卡点。'],
|
||||
reviewEvidence: ['global-review findings=0。'],
|
||||
reusablePatterns: ['none'],
|
||||
stableSolutions: ['无新增稳定解法。'],
|
||||
title: '无效清理证据',
|
||||
verificationEvidence: ['typecheck 通过。'],
|
||||
});
|
||||
if ((closeoutInvalidCleanup.canReportComplete as boolean) !== false) {
|
||||
throw new Error('workstream closeout cleanup evidence self-check failed');
|
||||
}
|
||||
|
||||
const obsidianContext = readObsidianContext({
|
||||
maxDocuments: 6,
|
||||
module: 'ktWorkflow',
|
||||
@ -178,6 +198,7 @@ export async function runSelfTest(): Promise<void> {
|
||||
|
||||
const loopAuditComplete = buildWorkflowLoopAudit({
|
||||
cleanupExecuted: true,
|
||||
cleanupFinalDeleted: 0,
|
||||
cleanupPreviewDeleted: 3,
|
||||
docSyncEvidence: ['kt_change_doc_sync requiredDocs 已处理。'],
|
||||
ktWorkflowUpdated: true,
|
||||
@ -201,6 +222,28 @@ export async function runSelfTest(): Promise<void> {
|
||||
throw new Error('workflow loop blocked self-check failed');
|
||||
}
|
||||
|
||||
const loopAuditMissingCleanup = buildWorkflowLoopAudit({
|
||||
docSyncEvidence: ['kt_change_doc_sync requiredDocs 已处理。'],
|
||||
reviewEvidence: ['global-review findings=0。'],
|
||||
taskTitle: '缺清理证据闭环',
|
||||
testCases: ['self-test'],
|
||||
verificationEvidence: ['typecheck 通过。'],
|
||||
});
|
||||
if ((loopAuditMissingCleanup.canReportComplete as boolean) !== false) {
|
||||
throw new Error('workflow loop missing cleanup evidence self-check failed');
|
||||
}
|
||||
|
||||
const dbSyncPlan = buildDbSyncPlan({});
|
||||
const dbSyncCommands = (dbSyncPlan.commands as string[]).join('\n');
|
||||
const backupName = dbSyncPlan.backupName as string;
|
||||
if (
|
||||
!backupName.includes('_') ||
|
||||
!dbSyncCommands.includes('.kt-workspace/db-sync') ||
|
||||
dbSyncCommands.includes('CREATE DATABASE IF NOT EXISTS')
|
||||
) {
|
||||
throw new Error('DB sync plan safety self-check failed');
|
||||
}
|
||||
|
||||
const data = {
|
||||
context: readWorkflowContext({ taskRecordCount: 2 }),
|
||||
guardrails: buildGuardrails({
|
||||
@ -258,7 +301,7 @@ export async function runSelfTest(): Promise<void> {
|
||||
componentWorkflow: buildComponentWorkflow({
|
||||
target: 'SystemLog',
|
||||
}),
|
||||
dbSyncPlan: buildDbSyncPlan({}),
|
||||
dbSyncPlan,
|
||||
finishTask: await buildFinishTask({
|
||||
cleanupHistory: false,
|
||||
projects: ['mcp'],
|
||||
|
||||
13
src/tools/cleanupEvidence.ts
Normal file
13
src/tools/cleanupEvidence.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export function cleanupEvidenceShowsDeletedZero(values: string[]): boolean {
|
||||
const text = values.join('\n');
|
||||
return [
|
||||
/\bdeleted\s*=\s*0\b/i,
|
||||
/\bdeleted\s*:\s*0\b/i,
|
||||
/["']deleted["']\s*:\s*\[\s*]/i,
|
||||
/\bdeleted\s*[=:]\s*\[\s*]/i,
|
||||
].some((pattern) => pattern.test(text));
|
||||
}
|
||||
|
||||
export function normalizeCleanupDeletedCount(value?: number): number | undefined {
|
||||
return value === undefined ? undefined : Math.max(0, value);
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
import type { WorkstreamCloseoutInput, WorkstreamReusablePattern } from '../types.js';
|
||||
import { workstreamReusablePatternValues } from '../core/constants.js';
|
||||
import { cleanupEvidenceShowsDeletedZero, normalizeCleanupDeletedCount } from './cleanupEvidence.js';
|
||||
import { defaultReviewProjects } from './review.js';
|
||||
|
||||
interface UpgradeTarget {
|
||||
@ -72,8 +73,14 @@ function buildPhase(name: string, evidence: string[], emptyHint: string): Record
|
||||
export function buildWorkstreamCloseout(input: WorkstreamCloseoutInput = {}): Record<string, unknown> {
|
||||
const title = input.title?.trim() || '';
|
||||
const changedProjects = normalizeList(input.changedProjects);
|
||||
const cleanupEvidence = normalizeList(input.cleanupEvidence);
|
||||
const cleanupFinalDeleted = normalizeCleanupDeletedCount(input.cleanupFinalDeleted);
|
||||
const cleanupOk =
|
||||
cleanupFinalDeleted === 0 || cleanupEvidenceShowsDeletedZero(cleanupEvidence);
|
||||
const docSyncEvidence = normalizeList(input.docSyncEvidence);
|
||||
const verificationEvidence = normalizeList(input.verificationEvidence);
|
||||
const problemRecords = normalizeList(input.problemRecords);
|
||||
const reviewEvidence = normalizeList(input.reviewEvidence);
|
||||
const stableSolutions = normalizeList(input.stableSolutions);
|
||||
const patterns = normalizePatterns(input.reusablePatterns);
|
||||
const upgradeNeeded = patterns.some((pattern) => pattern !== 'none');
|
||||
@ -81,6 +88,9 @@ export function buildWorkstreamCloseout(input: WorkstreamCloseoutInput = {}): Re
|
||||
const missingItems = [
|
||||
title ? '' : '填写本轮大方向名称或目标。',
|
||||
verificationEvidence.length > 0 ? '' : '补充测试或 smoke 验证证据;只有计划不算闭环完成。',
|
||||
docSyncEvidence.length > 0 ? '' : '补充文档同步证据;无需更新时也要显式写明原因。',
|
||||
cleanupOk ? '' : '补充历史产物清理证据;至少包含 cleanup-history dry-run deleted=0 或结构化 cleanupFinalDeleted=0。',
|
||||
reviewEvidence.length > 0 ? '' : '补充全局 review 证据。',
|
||||
problemRecords.length > 0 ? '' : '补充问题记录;如无新卡点,显式写“无新卡点”。',
|
||||
stableSolutions.length > 0 ? '' : '补充稳定解法;如无新解法,显式写“无新增稳定解法”。',
|
||||
upgradeNeeded && !ktWorkflowUpdated
|
||||
@ -98,6 +108,21 @@ export function buildWorkstreamCloseout(input: WorkstreamCloseoutInput = {}): Re
|
||||
closeoutOrder: [
|
||||
buildPhase('development', title ? [title] : [], '写清本轮完成的开发目标。'),
|
||||
buildPhase('test/smoke verification', verificationEvidence, '补一条可复验的验证证据。'),
|
||||
buildPhase('documentation sync', docSyncEvidence, '补充 kt_change_doc_sync 证据或无需更新说明。'),
|
||||
{
|
||||
evidence: [
|
||||
...cleanupEvidence,
|
||||
...(cleanupFinalDeleted === undefined
|
||||
? []
|
||||
: [`cleanupFinalDeleted=${cleanupFinalDeleted}`]),
|
||||
],
|
||||
name: 'history cleanup',
|
||||
status: cleanupOk ? 'ok' : 'missing',
|
||||
suggestion: cleanupOk
|
||||
? undefined
|
||||
: '补充 cleanup-history dry-run deleted=0 证据或传入 cleanupFinalDeleted=0。',
|
||||
},
|
||||
buildPhase('global review', reviewEvidence, '补充 kt_global_code_review 或 global-review 证据。'),
|
||||
buildPhase('problem record', problemRecords, '记录问题点;没有问题也要显式写明。'),
|
||||
buildPhase('stable solution', stableSolutions, '记录稳定解法;没有新增解法也要显式写明。'),
|
||||
{
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { WorkflowLoopAuditInput } from '../types.js';
|
||||
import { cleanupEvidenceShowsDeletedZero, normalizeCleanupDeletedCount } from './cleanupEvidence.js';
|
||||
|
||||
interface LoopGate {
|
||||
evidence: string[];
|
||||
@ -43,8 +44,18 @@ export function buildWorkflowLoopAudit(input: WorkflowLoopAuditInput = {}): Reco
|
||||
const problemRecords = normalizeList(input.problemRecords);
|
||||
const stableSolutions = normalizeList(input.stableSolutions);
|
||||
const reviewEvidence = normalizeList(input.reviewEvidence);
|
||||
const cleanupPreviewDeleted = Math.max(0, input.cleanupPreviewDeleted ?? 0);
|
||||
const cleanupOk = cleanupPreviewDeleted === 0 || input.cleanupExecuted === true;
|
||||
const cleanupEvidence = normalizeList(input.cleanupEvidence);
|
||||
const cleanupPreviewDeleted = normalizeCleanupDeletedCount(
|
||||
input.cleanupPreviewDeleted,
|
||||
);
|
||||
const cleanupFinalDeleted = normalizeCleanupDeletedCount(
|
||||
input.cleanupFinalDeleted,
|
||||
);
|
||||
const cleanupEvidenceOk = cleanupEvidenceShowsDeletedZero(cleanupEvidence);
|
||||
const cleanupOk =
|
||||
cleanupEvidenceOk ||
|
||||
cleanupFinalDeleted === 0 ||
|
||||
cleanupPreviewDeleted === 0;
|
||||
const hasFailure = failureRecords.length > 0;
|
||||
const reusablePattern = input.reusablePattern === true;
|
||||
const needsSolidification = hasFailure || reusablePattern;
|
||||
@ -59,11 +70,19 @@ export function buildWorkflowLoopAudit(input: WorkflowLoopAuditInput = {}): Reco
|
||||
cleanupOk,
|
||||
cleanupOk
|
||||
? [
|
||||
cleanupPreviewDeleted === 0
|
||||
cleanupEvidenceOk
|
||||
? 'cleanup-history evidence deleted=0'
|
||||
: cleanupPreviewDeleted === 0
|
||||
? 'cleanup-history dry-run deleted=0'
|
||||
: 'cleanup-history executed after stale artifacts were found',
|
||||
: 'cleanup-history executed and final dry-run deleted=0',
|
||||
]
|
||||
: [`cleanup-history dry-run deleted=${cleanupPreviewDeleted}`],
|
||||
: [
|
||||
cleanupFinalDeleted === undefined
|
||||
? cleanupPreviewDeleted === undefined
|
||||
? 'cleanup-history evidence missing'
|
||||
: `cleanup-history dry-run deleted=${cleanupPreviewDeleted}; final dry-run missing`
|
||||
: `cleanup-history final dry-run deleted=${cleanupFinalDeleted}`,
|
||||
],
|
||||
'执行 kt_cleanup_history dryRun=false 或 pnpm run cleanup-history -- --execute,并复跑 dry-run 确认 deleted=0。',
|
||||
),
|
||||
buildRequiredGate('documentation sync evidence', docSyncEvidence, '运行 kt_change_doc_sync 并同步 README/API/AGENTS/docs/Obsidian/skills/ktWorkflow 中需要更新的入口;无需更新时写明原因。'),
|
||||
@ -136,7 +155,9 @@ export function buildWorkflowLoopAudit(input: WorkflowLoopAuditInput = {}): Reco
|
||||
stage,
|
||||
summary: {
|
||||
changedProjects,
|
||||
cleanupFinalDeleted,
|
||||
cleanupPreviewDeleted,
|
||||
cleanupEvidenceCount: cleanupEvidence.length,
|
||||
docSyncEvidenceCount: docSyncEvidence.length,
|
||||
failureCount: failureRecords.length,
|
||||
problemRecordCount: problemRecords.length,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { CommitPlanInput, ComponentWorkflowInput, DbSyncPlanInput, ExecResult, FinishTaskInput, PushPlanInput, RemoteHealthCheckInput, RepoType, ResolvedProject } from '../types.js';
|
||||
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 { 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';
|
||||
@ -21,6 +21,25 @@ function normalizeProjectKeys(projects?: string[]): string[] {
|
||||
return Array.from(new Set(selected.filter((project) => project !== 'root')));
|
||||
}
|
||||
|
||||
function formatShanghaiTimestamp(date = new Date()): string {
|
||||
const parts = new Intl.DateTimeFormat('en-CA', {
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
hour12: false,
|
||||
minute: '2-digit',
|
||||
month: '2-digit',
|
||||
second: '2-digit',
|
||||
timeZone: 'Asia/Shanghai',
|
||||
year: 'numeric',
|
||||
}).formatToParts(date);
|
||||
const map = Object.fromEntries(parts.map((part) => [part.type, part.value]));
|
||||
return `${map.year}${map.month}${map.day}_${map.hour}${map.minute}${map.second}`;
|
||||
}
|
||||
|
||||
function normalizeDbIdentifier(value: string): string {
|
||||
return value.replace(/[^a-zA-Z0-9_]/g, '_').replace(/_+/g, '_').slice(0, 64);
|
||||
}
|
||||
|
||||
async function collectRepoSnapshot(projectKey: string): Promise<RepoSnapshot> {
|
||||
const project = resolveProject(projectKey);
|
||||
const repoType = detectRepoType(project.path);
|
||||
@ -274,17 +293,22 @@ export async function buildRemoteHealthCheck(input: RemoteHealthCheckInput = {})
|
||||
export function buildDbSyncPlan(input: DbSyncPlanInput = {}): Record<string, unknown> {
|
||||
const source = input.source || 'local';
|
||||
const target = input.target || 'remote';
|
||||
const backupName = input.backupName || `kt_template_backup_${formatDateInShanghai().replaceAll('-', '')}`;
|
||||
const backupName = normalizeDbIdentifier(
|
||||
input.backupName ||
|
||||
`kt_template_backup_${formatShanghaiTimestamp()}_${Math.random().toString(36).slice(2, 8)}`,
|
||||
);
|
||||
const dumpPath = `.kt-workspace/db-sync/${backupName}/kt_template.sql`;
|
||||
|
||||
return {
|
||||
backupName,
|
||||
commands: [
|
||||
'从 Node/kt-template-online-api/.env.development 和 .env.production 读取连接信息,只在本机 shell 环境使用,不写入 Git。',
|
||||
'KT 当前约定:127.0.0.1:3306 是本地 MySQL,127.0.0.1:3307 是远程 NAS 打洞连接;3307 不通时先建立 ssh -N -L 127.0.0.1:3307:127.0.0.1:3306 nas。',
|
||||
'mysqldump --set-gtid-purged=OFF --single-transaction --routines --triggers <source-conn> kt_template > .codex-db-sync/kt_template.sql',
|
||||
`mysql <target-conn> -e "CREATE DATABASE IF NOT EXISTS ${backupName} DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"`,
|
||||
`mkdir -p .kt-workspace/db-sync/${backupName}`,
|
||||
`mysqldump --set-gtid-purged=OFF --single-transaction --routines --triggers <source-conn> kt_template > ${dumpPath}`,
|
||||
`mysql <target-conn> -e "CREATE DATABASE ${backupName} DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"`,
|
||||
`mysqldump --set-gtid-purged=OFF --single-transaction --routines --triggers <target-conn> kt_template | mysql <target-conn> ${backupName}`,
|
||||
'mysql <target-conn> kt_template < .codex-db-sync/kt_template.sql',
|
||||
`mysql <target-conn> kt_template < ${dumpPath}`,
|
||||
'对比源库/目标库表数量、视图/函数/过程数量和关键表行数。',
|
||||
`验证通过后再执行:mysql <target-conn> -e "DROP DATABASE ${backupName};"`,
|
||||
],
|
||||
|
||||
@ -253,8 +253,12 @@ export interface WorkflowContextInput {
|
||||
|
||||
export interface WorkstreamCloseoutInput {
|
||||
changedProjects?: string[];
|
||||
cleanupEvidence?: string[];
|
||||
cleanupFinalDeleted?: number;
|
||||
docSyncEvidence?: string[];
|
||||
ktWorkflowUpdated?: boolean;
|
||||
problemRecords?: string[];
|
||||
reviewEvidence?: string[];
|
||||
reusablePatterns?: WorkstreamReusablePattern[];
|
||||
stableSolutions?: string[];
|
||||
title?: string;
|
||||
@ -284,6 +288,8 @@ export interface ObsidianContextInput {
|
||||
export interface WorkflowLoopAuditInput {
|
||||
changedProjects?: string[];
|
||||
cleanupExecuted?: boolean;
|
||||
cleanupEvidence?: string[];
|
||||
cleanupFinalDeleted?: number;
|
||||
cleanupPreviewDeleted?: number;
|
||||
docSyncEvidence?: string[];
|
||||
failureRecords?: string[];
|
||||
|
||||
Loading…
Reference in New Issue
Block a user