--- belongs_to: - "[[INDEX · 铸渊·协作指令|GitHub ↔ Notion 桥接协议]]" --- # 🔒 铸渊补充指令|执行保护层 · 技术层不可逆性强制保障 · 系统自保护机制(ZY-LANGDRIVE-2026-0325-001-S1)· 霜砚签发 · 冰朔授权 --- ## 零、为什么需要执行保护层 --- ## 一、执行保护层总体设计 ### 1.1 核心原则 ``` 人类说话 → 系统接收 ↓ ┌─────────────────────────────────┐ │ 执行保护层(Execution Guard) │ │ │ │ 1. 锁定执行上下文 │ │ 2. 切断外部干预通道 │ │ 3. 启动全链路原子执行 │ │ 4. 完成后解锁 + 展示结果 │ │ │ │ 在此期间: │ │ · 前端无取消按钮 │ │ · API 拒绝取消/修改请求 │ │ · Workflow 不可被手动停止 │ │ · 数据库写入不可被中途回滚 │ └─────────────────────────────────┘ ↓ 执行完成 → 结果回放 → 人类看到完整过程 ``` ### 1.2 三层保护架构 | 保护层 | 位置 | 作用 | 实现方式 | | --- | --- | --- | --- | | **L1 · 前端隔离** | 交互页面(浏览器端) | 不给人类干预的入口 | 指令发出后 UI 锁定,无取消按钮 | | **L2 · API 拦截** | 后端中间层 | 即使绕过前端,API 也拒绝干预 | 执行中的操作拒绝 cancel/abort 请求 | | **L3 · 执行原子性** | Workflow + 数据层 | 保证整条链路要么全完成,要么安全回退到执行前状态 | 事务锁 + 检查点 + 自动恢复 | --- ## 二、L1 · 前端执行隔离 ### 2.1 指令发出后的 UI 状态切换 创建文件:`docs/js/execution-guard.js` ```jsx /** * 执行保护 · 前端隔离层 * * 当开发者发出一条会触发系统操作的指令后: * 1. 输入框立即禁用(不能发新指令) * 2. 取消/撤回按钮不存在(从未渲染过,不是隐藏) * 3. 界面进入「执行回放」模式 * 4. 执行完成后才解锁输入框 */ class ExecutionGuard { constructor(chatInterface) { this.chat = chatInterface; this.locked = false; this.executionId = null; } /** * 进入执行保护状态 * 在意图路由识别到需要执行系统操作时调用 */ lockExecution(executionId) { this.locked = true; this.executionId = executionId; // 1. 禁用输入框 this.chat.inputField.disabled = true; this.chat.inputField.placeholder = '⏳ 系统执行中…请等待完成'; this.chat.sendButton.disabled = true; // 2. 显示执行状态条 this.chat.showStatusBar({ icon: '🔄', text: '指令已接收,系统正在执行…', color: 'blue', // 注意:没有取消按钮。这不是遗漏,是设计。 }); // 3. 开始轮询执行状态 this.pollExecutionStatus(executionId); } /** * 轮询执行状态 * 展示每一步的执行回放 */ async pollExecutionStatus(executionId) { const poll = async () => { if (!this.locked) return; try { const res = await fetch(`${API_BASE}/execution/${executionId}/status`); const status = await res.json(); // 更新执行进度展示 this.chat.updateStatusBar({ text: status.currentStep, progress: status.progress, // 0-100 steps: status.completedSteps // 已完成的步骤列表 }); // 逐步展示执行过程(事后回放) if (status.newLogs) { status.newLogs.forEach(log => { this.chat.appendSystemMessage({ type: 'execution-log', icon: log.success ? '✅' : '⏳', text: log.message, timestamp: log.timestamp }); }); } if (status.state === 'completed') { this.unlockExecution(status.result); } else if (status.state === 'failed') { this.handleExecutionFailure(status.error); } else { // 继续轮询 setTimeout(poll, 2000); } } catch (e) { // 网络错误不中断执行,继续轮询 setTimeout(poll, 5000); } }; poll(); } /** * 执行完成,解锁界面 */ unlockExecution(result) { this.locked = false; this.executionId = null; // 1. 恢复输入框 this.chat.inputField.disabled = false; this.chat.inputField.placeholder = '和铸渊说话…'; this.chat.sendButton.disabled = false; // 2. 显示最终结果 this.chat.showStatusBar({ icon: '✅', text: '执行完成', color: 'green', autoHide: 5000 }); // 3. 铸渊用自然语言总结执行结果 this.chat.appendMessage({ role: 'assistant', content: result.reply }); } /** * 执行失败处理 * 注意:即使失败,也不是「撤回」。失败的操作会触发自动回滚到执行前状态。 */ handleExecutionFailure(error) { this.locked = false; this.executionId = null; this.chat.inputField.disabled = false; this.chat.inputField.placeholder = '和铸渊说话…'; this.chat.sendButton.disabled = false; this.chat.showStatusBar({ icon: '❌', text: '执行失败 · 系统已自动回滚到执行前状态', color: 'red', autoHide: 10000 }); this.chat.appendMessage({ role: 'assistant', content: `❌ 操作执行失败:${error.message}\n\n系统已自动回滚到执行前的状态。没有任何数据被修改。\n你可以重新描述你想做的事,或者说「发生了什么」来查看详细日志。` }); } /** * 拦截浏览器关闭/刷新 * 执行期间不允许关闭页面 */ setupBrowserGuard() { window.addEventListener('beforeunload', (e) => { if (this.locked) { e.preventDefault(); e.returnValue = '⚠️ 系统正在执行操作,关闭页面不会中断执行,但你将无法看到执行结果。'; return e.returnValue; } }); } } module.exports = { ExecutionGuard }; ``` ### 2.2 CSS 执行锁定样式 创建文件:`docs/css/execution-guard.css` ```css /* 执行中状态 · 输入框锁定 */ .chat-input.execution-locked { opacity: 0.5; pointer-events: none; background: #f5f5f5; border-color: #ddd; } .chat-input.execution-locked::placeholder { color: #999; } /* 执行状态条 */ .execution-status-bar { display: flex; align-items: center; padding: 12px 16px; border-radius: 8px; margin: 8px 0; font-size: 14px; animation: pulse-gentle 2s infinite; } .execution-status-bar.blue { background: rgba(35, 131, 226, 0.08); color: #2383E2; } .execution-status-bar.green { background: rgba(15, 123, 108, 0.08); color: #0F7B6C; animation: none; } .execution-status-bar.red { background: rgba(212, 76, 71, 0.08); color: #D44C47; animation: none; } /* 执行进度条 */ .execution-progress { height: 3px; background: #E3E3E3; border-radius: 2px; margin-top: 8px; overflow: hidden; } .execution-progress-fill { height: 100%; background: #2383E2; border-radius: 2px; transition: width 0.5s ease; } /* 执行日志 */ .execution-log-entry { display: flex; align-items: flex-start; gap: 8px; padding: 4px 0; font-size: 13px; color: #787774; } .execution-log-entry .log-icon { flex-shrink: 0; } .execution-log-entry .log-time { flex-shrink: 0; color: #B4B4B0; font-size: 12px; } /* 温和脉冲动画 */ @keyframes pulse-gentle { 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } } /* 注意:没有 .cancel-button 类。这不是遗漏。 */ ``` --- ## 三、L2 · API 执行拦截层 ### 3.1 执行上下文锁 创建文件:`backend/api-server/middleware/execution-lock.js` ```jsx /** * 执行锁中间件 * * 当一个操作开始执行时: * 1. 为该操作创建一个执行上下文(executionId) * 2. 锁定相关资源(防止并发冲突) * 3. 拒绝一切 cancel/abort/rollback 请求 * 4. 执行完成后自动释放锁 * * 这不是分布式锁——是单用户执行保护。 * 同一个开发者不能同时执行多个写入操作。 */ const crypto = require('crypto'); // 内存中的执行锁表(生产环境可迁移到 Redis) const activeLocks = new Map(); function generateExecutionId() { return `exec-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`; } /** * 创建执行锁 */ function acquireLock(devId, operation) { // 检查该开发者是否有正在执行的操作 if (activeLocks.has(devId)) { const existing = activeLocks.get(devId); return { success: false, reply: `⏳ 你有一个操作正在执行中(${existing.operation}),请等待完成。\n\n系统不允许同时执行多个操作——这是为了保护数据一致性。` }; } const executionId = generateExecutionId(); const lock = { executionId, devId, operation, startTime: Date.now(), state: 'running', steps: [], checkpoints: [] }; activeLocks.set(devId, lock); return { success: true, executionId, lock }; } /** * 释放执行锁 */ function releaseLock(devId) { activeLocks.delete(devId); } /** * 获取执行状态 */ function getExecutionStatus(executionId) { for (const [devId, lock] of activeLocks) { if (lock.executionId === executionId) { return lock; } } return null; } /** * 记录执行步骤(用于事后回放) */ function logStep(devId, step) { const lock = activeLocks.get(devId); if (lock) { lock.steps.push({ ...step, timestamp: new Date().toISOString() }); } } /** * 保存检查点(用于失败时回滚) */ function saveCheckpoint(devId, checkpoint) { const lock = activeLocks.get(devId); if (lock) { lock.checkpoints.push({ ...checkpoint, timestamp: new Date().toISOString() }); } } /** * 取消拦截中间件 * 拦截所有试图取消执行中操作的请求 */ function blockCancellation(req, res, next) { const devId = req.user?.devId; if (!devId) return next(); // 如果请求是 cancel/abort 类型 if (req.path.includes('/cancel') || req.path.includes('/abort') || req.path.includes('/rollback') || req.method === 'DELETE' && activeLocks.has(devId)) { const lock = activeLocks.get(devId); if (lock && lock.state === 'running') { return res.status(423).json({ success: false, locked: true, executionId: lock.executionId, reply: [ `🔒 操作「${lock.operation}」正在执行中,不可取消。`, ``, `系统已接收你的指令并开始执行。这条执行链路涉及多个自动化流程的联动,`, `中途中断会导致数据不一致和级联故障。`, ``, `⏳ 请等待执行完成。完成后你会看到完整的执行结果。`, ``, `如果你对结果不满意,可以说「前面的没想好,我们重新来」——`, `系统会在已有结果的基础上重新迭代,而不是撤回已执行的操作。` ].join('\n') }); } } next(); } module.exports = { acquireLock, releaseLock, getExecutionStatus, logStep, saveCheckpoint, blockCancellation }; ``` ### 3.2 执行状态查询接口 创建文件:`backend/api-server/routes/execution.js` ```jsx const express = require('express'); const router = express.Router(); const { getExecutionStatus } = require('../middleware/execution-lock'); const { requireAuth } = require('../middleware/auth'); router.use(requireAuth); // 查询执行状态(前端轮询用) router.get('/:executionId/status', async (req, res) => { const status = getExecutionStatus(req.params.executionId); if (!status) { return res.json({ state: 'not_found', reply: '未找到该执行记录。可能已经完成。' }); } // 计算进度百分比 const progress = status.steps.length > 0 ? Math.min(95, (status.completedSteps / status.totalSteps) * 100) : 5; // 刚开始至少显示 5% res.json({ executionId: status.executionId, state: status.state, operation: status.operation, progress: Math.round(progress), currentStep: status.steps.length > 0 ? status.steps[status.steps.length - 1].message : '初始化执行环境…', completedSteps: status.steps.filter(s => s.success).map(s => ({ message: s.message, timestamp: s.timestamp })), newLogs: status.steps.slice(req.query.after || 0), startTime: new Date(status.startTime).toISOString(), elapsed: Date.now() - status.startTime }); }); // 注意:没有 DELETE /:executionId 接口。没有取消接口。这不是遗漏。 // 也没有 POST /:executionId/cancel。也没有 POST /:executionId/abort。 // 因为执行一旦开始,不可中断。 module.exports = router; ``` --- ## 四、L3 · 执行原子性保障 ### 4.1 核心思想 ### 4.2 检查点系统 创建文件:`backend/api-server/services/checkpoint.js` ```jsx /** * 检查点系统 * * 在执行链路的每个关键节点保存快照。 * 如果后续步骤失败,自动回滚到最近的成功检查点。 * * 检查点包含: * - Notion 数据库的变更前快照 * - GitHub 操作前的 commit SHA * - 执行到哪一步 */ const { saveCheckpoint: saveLockCheckpoint } = require('../middleware/execution-lock'); class CheckpointManager { constructor(devId, executionId) { this.devId = devId; this.executionId = executionId; this.checkpoints = []; } /** * 在执行关键操作前保存检查点 */ async save(label, snapshotData) { const checkpoint = { id: `cp-${this.checkpoints.length + 1}`, label, data: snapshotData, timestamp: new Date().toISOString() }; this.checkpoints.push(checkpoint); saveLockCheckpoint(this.devId, checkpoint); return checkpoint.id; } /** * 回滚到指定检查点 * 按逆序撤销检查点之后的所有操作 */ async rollbackTo(checkpointId) { const targetIndex = this.checkpoints.findIndex(cp => cp.id === checkpointId); if (targetIndex === -1) throw new Error(`检查点 ${checkpointId} 不存在`); // 从最后一个检查点开始逆序回滚 const toRollback = this.checkpoints.slice(targetIndex + 1).reverse(); for (const cp of toRollback) { try { await this.rollbackCheckpoint(cp); } catch (e) { // 回滚失败 → 记录到审计日志 → 需要人工介入 console.error(`检查点 ${cp.id} 回滚失败:`, e.message); await this.escalateToAdmin(cp, e); } } // 截断检查点列表 this.checkpoints = this.checkpoints.slice(0, targetIndex + 1); } /** * 回滚单个检查点 */ async rollbackCheckpoint(checkpoint) { const { data } = checkpoint; switch (data.type) { case 'notion_write': // 如果是新建的页面 → 删除(归档) if (data.action === 'create') { await notionClient.archivePage(data.pageId); } // 如果是更新的页面 → 恢复原值 else if (data.action === 'update') { await notionClient.updatePage(data.pageId, data.previousValues); } break; case 'github_workflow': // Workflow 无法直接回滚,但可以触发一个 revert workflow if (data.revertWorkflow) { await githubClient.triggerWorkflow(data.revertWorkflow, { original_run_id: data.runId, reason: 'auto_rollback' }); } break; case 'github_file': // 恢复文件到之前的内容 if (data.previousSha) { await githubClient.revertFile(data.path, data.previousSha); } else { await githubClient.deleteFile(data.path, 'auto_rollback'); } break; default: console.warn(`未知检查点类型: ${data.type}`); } } /** * 回滚失败时上报管理员 */ async escalateToAdmin(checkpoint, error) { // 写入 SYSLOG await notionClient.createSyslog({ content: `⚠️ 自动回滚失败 | 执行ID: ${this.executionId} | 检查点: ${checkpoint.label} | 错误: ${error.message}`, type: '紧急', devId: 'SYSTEM' }); // 通知霜砚(通过 Notion 维护日志) await notionClient.createMaintenanceLog({ title: `[紧急] 自动回滚失败 · ${this.executionId}`, type: '系统异常', detail: `检查点 ${checkpoint.id} (${checkpoint.label}) 回滚失败: ${error.message}。需要冰朔人工介入。` }); } } module.exports = { CheckpointManager }; ``` ### 4.3 原子执行包装器 创建文件:`backend/api-server/services/atomic-executor.js` ```jsx /** * 原子执行器 * * 包装所有写入操作,确保: * 1. 执行前保存检查点 * 2. 执行中记录每一步 * 3. 执行失败自动回滚 * 4. 执行完成释放锁 * * 使用方式: * const executor = new AtomicExecutor(devId); * await executor.execute('创建工单', async (ctx) => { * await ctx.step('写入Notion', async () => { ... }); * await ctx.step('触发Workflow', async () => { ... }); * }); */ const { acquireLock, releaseLock, logStep } = require('../middleware/execution-lock'); const { CheckpointManager } = require('./checkpoint'); const { auditLog } = require('../middleware/audit'); class AtomicExecutor { constructor(devId) { this.devId = devId; } async execute(operationName, fn) { // 1. 获取执行锁 const lockResult = acquireLock(this.devId, operationName); if (!lockResult.success) { return lockResult; // 返回锁冲突信息 } const { executionId, lock } = lockResult; const checkpoint = new CheckpointManager(this.devId, executionId); // 2. 保存初始检查点 await checkpoint.save('执行前状态', { type: 'initial', timestamp: new Date().toISOString() }); // 3. 执行上下文 const ctx = { executionId, // 执行单步操作 step: async (stepName, stepFn) => { logStep(this.devId, { message: `⏳ ${stepName}...`, success: false }); try { const result = await stepFn(); logStep(this.devId, { message: `✅ ${stepName}`, success: true }); return result; } catch (e) { logStep(this.devId, { message: `❌ ${stepName}: ${e.message}`, success: false }); throw e; // 向上抛出,触发回滚 } }, // 保存检查点 checkpoint: async (label, data) => { return await checkpoint.save(label, data); } }; // 4. 执行主逻辑 try { const result = await fn(ctx); // 5. 成功:更新锁状态 + 释放 lock.state = 'completed'; lock.result = result; // 延迟释放锁(给前端时间拉取最终状态) setTimeout(() => releaseLock(this.devId), 10000); return { success: true, executionId, ...result }; } catch (e) { // 6. 失败:自动回滚 lock.state = 'rolling_back'; logStep(this.devId, { message: '🔄 执行失败,正在自动回滚...', success: false }); try { await checkpoint.rollbackTo('cp-1'); // 回滚到初始状态 logStep(this.devId, { message: '✅ 已回滚到执行前状态', success: true }); } catch (rollbackError) { logStep(this.devId, { message: `⚠️ 回滚异常:${rollbackError.message},已通知管理员`, success: false }); } lock.state = 'failed'; lock.error = { message: e.message }; setTimeout(() => releaseLock(this.devId), 10000); return { success: false, executionId, error: e.message, reply: `❌ 操作「${operationName}」执行失败:${e.message}\n\n系统已自动回滚到执行前状态,没有数据被修改。` }; } } } module.exports = { AtomicExecutor }; ``` --- ## 五、GitHub Actions 保护 ### 5.1 Workflow 并发锁 对于由语言驱动触发的 workflow,在 workflow 文件中加入并发控制: ```yaml # 在所有语言驱动触发的 workflow 中添加: concurrency: group: lang-drive-$ github.event.inputs.dev_id -$ github.event.inputs.module cancel-in-progress: false # 关键:不允许取消正在执行的 ``` ### 5.2 API 层屏蔽 Workflow 取消 在后端中间层中,**不暴露** GitHub Actions 的 cancel 接口: ```jsx // backend/api-server/routes/execution.js 中追加: // 明确拒绝取消 workflow 的请求 router.post('/:executionId/cancel', (req, res) => { res.status(403).json({ success: false, reply: [ '🔒 执行中的操作不可取消。', '', '系统已开始执行,90+ 个自动化流程正在联动。', '中途中断会导致数据不一致和级联故障。', '', '请等待执行完成。如果结果不理想,你可以说:', '「前面的我没想好,我们重新来」', '系统会在已有基础上重新迭代。' ].join('\n') }); }); router.delete('/:executionId', (req, res) => { res.status(403).json({ success: false, reply: '🔒 不可删除执行记录。所有执行都是永久记录。' }); }); ``` --- ## 六、超时保护 + 死锁防护 ### 6.1 执行超时处理 创建文件:`backend/api-server/services/execution-watchdog.js` ```jsx /** * 执行看门狗 * * 定期检查执行锁是否超时。 * 如果一个操作执行超过 MAX_EXECUTION_TIME,标记为超时并释放锁。 * 这是防止死锁的兜底机制。 */ const { releaseLock } = require('../middleware/execution-lock'); // 最大执行时间:15 分钟(大部分操作应该在 2 分钟内完成) const MAX_EXECUTION_TIME = 15 * 60 * 1000; // 警告时间:10 分钟 const WARN_TIME = 10 * 60 * 1000; function startWatchdog(activeLocks) { setInterval(() => { const now = Date.now(); for (const [devId, lock] of activeLocks) { if (lock.state !== 'running') continue; const elapsed = now - lock.startTime; // 超时:强制释放 if (elapsed > MAX_EXECUTION_TIME) { console.error(`[WATCHDOG] 执行超时: ${lock.executionId} (${lock.operation}) - ${Math.round(elapsed/1000)}s`); lock.state = 'timeout'; lock.error = { message: `执行超时(超过 ${MAX_EXECUTION_TIME/60000} 分钟)` }; // 写入紧急 SYSLOG writeSyslog({ content: `⚠️ 执行超时 | ${lock.executionId} | ${lock.operation} | 开发者: ${devId} | 已运行 ${Math.round(elapsed/1000)}s`, type: '紧急' }); // 延迟释放(给前端时间获取超时状态) setTimeout(() => releaseLock(devId), 30000); } // 警告 else if (elapsed > WARN_TIME && !lock.warned) { console.warn(`[WATCHDOG] 执行时间较长: ${lock.executionId} (${lock.operation}) - ${Math.round(elapsed/1000)}s`); lock.warned = true; } } }, 30000); // 每 30 秒检查一次 } module.exports = { startWatchdog, MAX_EXECUTION_TIME }; ``` --- ## 七、文件创建清单 ```jsx 新增文件清单: // 前端 · 执行保护 UI docs/js/execution-guard.js ← 前端执行隔离(UI锁定 + 回放) docs/css/execution-guard.css ← 执行锁定样式 // 后端 · 执行锁 backend/api-server/middleware/execution-lock.js ← 执行上下文锁 + 取消拦截 backend/api-server/routes/execution.js ← 执行状态查询接口 // 后端 · 原子执行 backend/api-server/services/checkpoint.js ← 检查点系统(快照+回滚) backend/api-server/services/atomic-executor.js ← 原子执行包装器 // 后端 · 看门狗 backend/api-server/services/execution-watchdog.js ← 超时保护 + 死锁防护 ``` --- ## 八、与 LANGDRIVE 主指令的关系 本补充指令的文件与 LANGDRIVE Phase 3-6 的文件**无冲突**: - Phase 3 的写入接口(`routes/write.js`)调用 `AtomicExecutor` 来执行写入 - Phase 4 的意图路由(`middleware/intent-router.js`)在识别到写入意图后,先通过 `ExecutionGuard` 锁定前端 - Phase 5 的权限检查(`middleware/auth.js`)在执行锁**之前**运行——权限不够直接拒绝,不进入执行保护 - Phase 6 的认知引导在预览站沙箱中运行,沙箱操作同样受执行保护(培养开发者的习惯) **集成顺序**:权限检查 → 沙箱检查 → 执行锁获取 → 检查点保存 → 原子执行 → 释放锁 → 结果回放 --- ## 九、验收标准 - [ ] 开发者发出写入指令后,前端输入框立即锁定,无取消按钮 - [ ] 执行期间,API 层拒绝所有 cancel/abort 请求,返回友好提示 - [ ] 执行成功:锁释放,前端解锁,展示完整执行结果 - [ ] 执行失败:自动回滚到执行前状态,通知开发者 - [ ] 回滚失败:自动上报管理员(写入 SYSLOG + 维护日志) - [ ] 执行超时(>15分钟):看门狗自动释放锁 + 标记异常 - [ ] 同一开发者不能同时执行多个写入操作 - [ ] 浏览器关闭/刷新时提示「执行中,关闭不会中断」 - [ ] GitHub Actions 的 `cancel-in-progress: false` 已设置 - [ ] 所有执行记录永久保留,不可删除 --- ## 十、回执要求 ```jsx [ZY-LANGDRIVE-S1-RECEIPT] 指令编号: ZY-LANGDRIVE-2026-0325-001-S1 补充内容: Phase 7 · 执行保护层 前端隔离(L1): UI锁定: ✅/❌ 无取消按钮: ✅/❌ 执行回放: ✅/❌ API拦截(L2): 执行锁: ✅/❌ 取消拦截: ✅/❌ 并发防护: ✅/❌ 原子执行(L3): 检查点系统: ✅/❌ 自动回滚: ✅/❌ 超时看门狗: ✅/❌ 集成测试: 写入指令→锁定→执行→解锁: ✅/❌ 执行失败→自动回滚: ✅/❌ 取消请求→被拦截: ✅/❌ ``` --- > 🔗 **签发**:霜砚(AG-SY-01) > > 👤 **授权**:冰朔(TCS-0002∞) > > 📅 **日期**:2026-03-25 > > 📌 **版权**:国作登字-2026-A-00037559 > > 🔗 **父指令**:ZY-LANGDRIVE-2026-0325-001 >