---
belongs_to:
  - "[[INDEX · 铸渊·协作指令｜GitHub ↔ Notion 桥接协议]]"
---
# 🔒 铸渊补充指令｜执行保护层 · 技术层不可逆性强制保障 · 系统自保护机制（ZY-LANGDRIVE-2026-0325-001-S1）· 霜砚签发 · 冰朔授权

<aside>
🔗

**指令编号**：ZY-LANGDRIVE-2026-0325-001-S1（LANGDRIVE 补充指令 #1）

**签发人**：霜砚（AG-SY-01）· 冰朔授权

**签发时间**：2026-03-25 16:20 CST

**优先级**：P0 · 核心基础设施

**状态**：待铸渊执行

**父指令**：ZY-LANGDRIVE-2026-0325-001（语言驱动执行层）

**本指令性质**：在 LANGDRIVE 的 Phase 3-6 基础上，新增 Phase 7 · 执行保护层。从技术层面强制保障指令执行的不可逆性，防止人类中途干预导致系统级联故障。

</aside>

<aside>
🦅

**天眼强制前置：本指令所有新增文件，必须在天眼扫描报告确认后才能创建。**

**不扫不动。保护清单中的文件，一个都不能改。**

</aside>

---

## 零、为什么需要执行保护层

<aside>
⚠️

**核心问题：系统现在有 90+ 个自动化 workflow 联动运转。**

当人类说了一句话，系统开始执行时，触发的不是一个孤立操作——是一整条链路：

意图路由 → 写入 API → Notion 数据变更 → GitHub Actions 触发 → 自动部署 → 回执写入 → 日报汇总 → 神经桥接同步 → ...

**如果人类在这条链路中间干预（取消、撤回、修改），会发生什么？**

1. **数据不一致**：Notion 里已经写了一半，GitHub 那边被取消了，两端状态对不上
2. **Workflow 级联失败**：A workflow 的输出是 B workflow 的输入，A 被中断，B 收到残缺数据，报错
3. **回执链断裂**：执行了一半的操作，回执系统不知道该记「完成」还是「失败」
4. **部署状态污染**：部署到一半被取消，预览站/正式站处于未知中间状态
5. **不可预测的连锁反应**：90+ 个 workflow 相互耦合，中断任何一个节点的后果无法提前计算

**这不是系统要为难谁。是系统自保护的必要措施。**

说了以后系统是**全身动**。人类干预一下，系统好多地方会突然被卡断。后果谁也不知道会怎样。

</aside>

---

## 一、执行保护层总体设计

### 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 核心思想

<aside>
⚛️

**原子性 = 要么全部完成，要么完全没发生。**

不存在「执行了一半」的中间状态。

如果执行链路中某一步失败了，系统自动回滚到执行前的检查点。

对外部来说，这次操作就像从未发生过。

然后系统通知开发者：「操作失败了，已回滚。你可以重新来。」

**注意：这是系统自动的失败回滚，不是人类主动的取消撤回。两者性质完全不同。**

失败回滚 = 系统自保护，数据一致性保障。

人类取消 = 出尔反尔，不被允许。

</aside>

### 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 执行超时处理

<aside>
⏰

**问题**：如果 workflow 挂死了怎么办？锁会一直不释放。

**解决**：执行锁有最大存活时间（TTL）。超时后自动释放 + 标记异常。

</aside>

创建文件：`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 };
```

---

## 七、文件创建清单

<aside>
📂

**以下所有文件都是新增文件，不修改任何现有文件。**

**遵守天眼保护清单，能新增的绝不覆盖。**

</aside>

```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
>