UNPKG

autosnippet

Version:

Extract code patterns into a knowledge base for AI coding assistants

146 lines (145 loc) 5.13 kB
/** * StagingManager — staging Grace Period 管理 + 自动发布 * * 核心职责: * 1. 条目进入 staging 后记录 deadline * 2. 定时检查:deadline 到期 + 无异议 → 自动转 active * 3. 异常回滚:Guard 检测到冲突 → 回滚到 pending * 4. 发射信号通知 Dashboard * * 分级 Grace Period(由 ConfidenceRouter 决定): * ≥ 0.90 → 24h * 0.85-0.89 → 72h */ import Logger from '../../infrastructure/logging/Logger.js'; import { unixNow } from '../../shared/utils/common.js'; /* ────────────────────── Class ────────────────────── */ export class StagingManager { #knowledgeRepo; #signalBus; #logger = Logger.getInstance(); constructor(knowledgeRepo, options = {}) { this.#knowledgeRepo = knowledgeRepo; this.#signalBus = options.signalBus ?? null; } /** * 将条目推入 staging 状态并记录 deadline */ async enterStaging(entryId, gracePeriodMs, confidence) { const now = Date.now(); const deadline = now + gracePeriodMs; const entry = await this.#knowledgeRepo.findById(entryId); if (!entry) { this.#logger.warn(`StagingManager: entry not found: ${entryId}`); return false; } if (entry.lifecycle !== 'pending') { this.#logger.warn(`StagingManager: entry ${entryId} is "${entry.lifecycle}", not pending`); return false; } await this.#knowledgeRepo.update(entryId, { lifecycle: 'staging', stagingDeadline: deadline, }); if (this.#signalBus) { this.#signalBus.send('lifecycle', 'StagingManager.enter', confidence, { target: entryId, metadata: { action: 'enter_staging', deadline, gracePeriodMs, title: entry.title, }, }); } this.#logger.info(`StagingManager: ${entry.title} → staging (deadline: ${new Date(deadline).toISOString()})`); return true; } /** * 检查所有 staging 条目,执行自动发布或回滚 */ async checkAndPromote() { const now = Date.now(); const result = { promoted: [], rolledBack: [], waiting: [] }; const entries = await this.#knowledgeRepo.findAllByLifecycles(['staging']); for (const e of entries) { const deadline = e.stagingDeadline || 0; const entry = { id: e.id, title: e.title, stagingDeadline: deadline, confidence: 0, }; if (deadline === 0) { result.waiting.push(entry); continue; } if (now < deadline) { result.waiting.push(entry); continue; } await this.#promote(entry, now); result.promoted.push(entry); } if (result.promoted.length > 0) { this.#logger.info(`StagingManager: promoted ${result.promoted.length} entries to active`); } return result; } /** * 回滚 staging 条目到 pending(Guard 检测到冲突时调用) */ async rollback(entryId, reason) { const entry = await this.#knowledgeRepo.findById(entryId); if (!entry || entry.lifecycle !== 'staging') { return false; } await this.#knowledgeRepo.update(entryId, { lifecycle: 'pending', stagingDeadline: null, }); if (this.#signalBus) { this.#signalBus.send('lifecycle', 'StagingManager.rollback', 0.8, { target: entryId, metadata: { action: 'staging_rollback', reason, title: entry.title, }, }); } this.#logger.info(`StagingManager: ${entry.title} rolled back to pending — ${reason}`); return true; } /** * 获取所有 staging 条目及其状态 */ async listStaging() { const entries = await this.#knowledgeRepo.findAllByLifecycles(['staging']); return entries.map((e) => ({ id: e.id, title: e.title, stagingDeadline: e.stagingDeadline || 0, confidence: 0, })); } /* ── Private ── */ async #promote(entry, now) { const nowS = unixNow(); await this.#knowledgeRepo.update(entry.id, { lifecycle: 'active', publishedAt: nowS, stagingDeadline: null, }); if (this.#signalBus) { this.#signalBus.send('lifecycle', 'StagingManager.promote', 1.0, { target: entry.id, metadata: { action: 'auto_publish', title: entry.title, confidence: entry.confidence, }, }); } } }