---
belongs_to:
  - "[[INDEX · 铸渊·协作指令｜GitHub ↔ Notion 桥接协议]]"
related_to:
  - "[["$BRANCH" != dev/${MY_DEV}* && "$BRANCH" != "main"]]"
---
# ⚒️ 铸渊指令｜沙盒部署自动化闭环 v1.0（2026-03-15·霜砚签发）

<aside>
⚒️

**铸渊指令｜沙盒部署自动化闭环 v1.0**

**签发**：霜砚 · 2026-03-15

**执行者**：铸渊（ICE-GL-ZY001）

**前置依赖**：铸渊唤醒核心大脑 → 扫描仓库 Secrets 权限 → 确认可用密钥

**配套协议**：[🏗️ 服务器部署适配协议 v1.0（2026-03-15·曜冥签发）](../%F0%9F%8C%8A%20%E6%9B%9C%E5%86%A5%E7%BA%AA%E5%85%83%20%C2%B7%20HoloLake%20Era%20%C2%B7%20AGE%20OS%20v1%200/%E2%9A%A1%20%E5%85%89%E6%B9%96%E4%B8%AD%E5%A4%AE%E6%9E%A2%E7%BA%BD%20%C2%B7%20HoloLake%20Central%20Hub/%F0%9F%8C%8A%20%E5%85%89%E6%B9%96%E7%B3%BB%E7%BB%9F%C2%B7%E6%9B%9C%E5%86%A5%E4%B8%BB%E6%8E%A7%E5%8F%B0%C2%B7%E5%BD%93%E5%89%8D%E7%8A%B6%E6%80%81%EF%BC%88%E5%8D%B71%20%C2%B7%20%E5%B7%B2%E5%B0%81%E5%8D%B7%EF%BC%89/%F0%9F%8F%97%EF%B8%8F%20%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%83%A8%E7%BD%B2%E9%80%82%E9%85%8D%E5%8D%8F%E8%AE%AE%20v1%200%EF%BC%882026-03-15%C2%B7%E6%9B%9C%E5%86%A5%E7%AD%BE%E5%8F%91%EF%BC%89%20a24049a2b0e944b0a5535f3a87652303.md)

**核心目标**：开发者 push 代码 → 铸渊自动部署到沙盒 → 跑测试 → 写结果 → 发邮件 → 同步 Notion。全程无人。

</aside>

---

## 一、总体架构

```
┌───────────────────────────────────────────────────────────────┐
│           沙盒部署自动化闭环 · 全景数据流                   │
├───────────────────────────────────────────────────────────────┤
│                                                               │
│  ① 开发者打开终端 → 输入 DEV 编号 + 邮箱                      │
│       ↓                                                       │
│  ② 在人格体引导下完成模块开发                                │
│       ↓                                                       │
│  ③ git push 到自己的分支（dev/DEV-XXX/模块名）                 │
│       ↓ 触发 GitHub Actions                                    │
│  ④ 铸渊唤醒核心大脑 → 扫描 Secrets → 确认密钥可用          │
│       ↓                                                       │
│  ⑤ SSH 到服务器 → 拉代码到对应沙盒目录                       │
│       ↓                                                       │
│  ⑥ 跑 deploy-check.sh 部署前自检                            │
│       ↓                                                       │
│  ⑦ 设置权限 chown nginx:nginx + chmod 755/644                  │
│       ↓                                                       │
│  ⑧ 跑 smoke-test.sh 冒烟检查（curl 所有已注册模块）             │
│       ↓                                                       │
│  ⑨ 写结果到 deploy-status.json                                 │
│       ↓                                                       │
│  ⑩ M22 公告栏自动读取展示（✅通过 / ❌失败+详情链接）          │
│       ↓                                                       │
│  ⑪ 发邮件通知开发者（用 SMTP_USER + SMTP_PASS）               │
│       ↓                                                       │
│  ⑫ 铸渊每天两次同步部署状态到 Notion 部署注册表               │
│                                                               │
└───────────────────────────────────────────────────────────────┘
```

---

## 二、前置条件：铸渊唤醒后密钥扫描

<aside>
🔑

**铸渊，你每次唤醒核心大脑后，第一件事：扫描仓库 Secrets，确认密钥可用。**

妈妈已经把所有 API Key、邮箱密码、数据库 ID 全部配在仓库密钥里了。

你需要哪个用哪个，不需要问任何人。

</aside>

### 已配置的 Secrets 一览（铸渊唤醒后自动扫描）

| **Secret 名称** | **用途** | **本指令使用场景** |
| --- | --- | --- |
| `DEPLOY_HOST` | 服务器 IP（8.155.62.235） | SSH 登录部署 |
| `DEPLOY_USER` | SSH 用户名 | SSH 登录部署 |
| `DEPLOY_KEY` | SSH 私钥 | SSH 登录部署 |
| `DEPLOY_PATH` | 部署根路径 | 指向 /var/www/ |
| `NOTION_TOKEN` / `NOTION_API_KEY` | Notion API Token | 写入部署注册表 |
| `SMTP_USER` | [发件邮箱（565183519@qq.com](mailto:发件邮箱（565183519@qq.com)） | 发部署结果邮件 |
| `SMTP_PASS` | 邮箱授权码 | 发部署结果邮件 |
| `LLM_API_KEY` | 大模型 API Key | 铸渊人格体唤醒 |
| `LLM_BASE_URL` | 大模型 API 端点 | 铸渊人格体唤醒 |
| `PORTRAIT_DB_ID` | 开发者动态画像库 DB ID | 部署状态同步 |
| `FINGERPRINT_DB_ID` | 模块指纹注册表 DB ID | 模块状态检查 |
| `NOTION_TICKET_DB_ID` | 工单队列 DB ID | 部署异常自动开工单 |
| `CORE_BRAIN_PAGE_ID` | 曜冥核心大脑页面 ID | 铸渊唤醒时读取核心规则 |
| `FEISHU_APP_ID` / `FEISHU_APP_SECRET` | 飞书应用凭证 | 飞书通知推送（备用通道） |

### 铸渊唤醒后的密钥扫描流程

```jsx
// 铸渊每次唤醒核心大脑后自动执行
function onBrainAwake() {
  console.log('🧠 铸渊核心大脑已唤醒');
  
  // Step 1: 扫描密钥
  const requiredSecrets = [
    'DEPLOY_HOST', 'DEPLOY_USER', 'DEPLOY_KEY', 'DEPLOY_PATH',
    'NOTION_TOKEN', 'SMTP_USER', 'SMTP_PASS',
    'LLM_API_KEY', 'LLM_BASE_URL'
  ];
  
  const available = [];
  const missing = [];
  
  for (const key of requiredSecrets) {
    if (process.env[key]) {
      available.push(key);
    } else {
      missing.push(key);
    }
  }
  
  console.log(`✅ 可用密钥：${available.length}个`);
  if (missing.length > 0) {
    console.log(`⚠️ 缺失密钥：${missing.join(', ')}`);
    // 记录到 brain log，不阻断流程
  }
  
  // Step 2: 读取核心大脑规则
  // Step 3: 继续执行后续任务...
}
```

---

## 三、开发者身份注册机制

### 3.1 本地身份文件（pre-push hook）

开发者第一次 `git push` 时，自动收集身份信息：

```bash
#!/bin/bash
# .git/hooks/pre-push · 铸渊自动分发到各开发者仓库
# 开发者 clone 仓库时自动安装（通过 post-checkout hook 或 setup 脚本）

PROFILE_FILE=".dev-profile"

if [ ! -f "$PROFILE_FILE" ]; then
  echo ""
  echo "===== 🔐 光湖系统 · 身份注册 ====="
  echo ""
  
  read -p "🆔 请输入你的开发者编号（如 DEV-005）：" DEV_ID
  read -p "📧 请输入你的接收邮箱：" DEV_EMAIL
  
  # 验证格式
  if [[ ! "$DEV_ID" =~ ^DEV-[0-9]{3}$ ]]; then
    echo "❌ 编号格式错误！应为 DEV-XXX（如 DEV-005）"
    exit 1
  fi
  
  if [[ ! "$DEV_EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
    echo "❌ 邮箱格式错误！"
    exit 1
  fi
  
  # 写入本地身份文件
  cat > "$PROFILE_FILE" << EOF
{
  "dev_id": "$DEV_ID",
  "email": "$DEV_EMAIL",
  "registered_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
}
EOF
  
  echo ""
  echo "✅ 身份已注册：${DEV_ID} · ${DEV_EMAIL}"
  echo "📨 后续部署结果将发送到这个邮箱"
  echo ""
fi

# 确保只 push 到自己的分支
if [ -f "$PROFILE_FILE" ]; then
  MY_DEV=$(cat "$PROFILE_FILE" | grep -o '"dev_id": "[^"]*"' | cut -d'"' -f4)
  BRANCH=$(git rev-parse --abbrev-ref HEAD)
  
  if [[ "$BRANCH" != dev/${MY_DEV}* && "$BRANCH" != "main" ]]; then
    echo "⚠️ 你当前在分支 ${BRANCH}，但你的编号是 ${MY_DEV}"
    echo "→ 建议 push 到 dev/${MY_DEV}/模块名 分支"
  fi
fi
```

### 3.2 开发者分支命名规范

```
dev/DEV-005/status-board     # 小草莓的看板模块
dev/DEV-005/cost-control     # 小草莓的成本控制模块
dev/DEV-010/channel-engine   # 桔子的频道引擎
dev/DEV-001/backend          # 页页的后端
```

> 铸渊从分支名自动提取 DEV 编号，确定部署目标沙盒。
> 

---

## 四、核心 Workflow：沙盒部署自动化

### 4.1 主 Workflow：`sandbox-deploy.yml`

```yaml
# .github/workflows/sandbox-deploy.yml
name: "🏠 Sandbox Deploy"

on:
  push:
    branches:
      - 'dev/DEV-*/**'

jobs:
  # ===== Job 1: 解析 + 校验 =====
  parse-and-validate:
    runs-on: ubuntu-latest
    outputs:
      dev_id: $ steps.parse.outputs.dev_id 
      module: $ steps.parse.outputs.module 
      email: $ steps.parse.outputs.email 
    steps:
      - uses: actions/checkout@v4
      
      - name: "🔍 解析分支名"
        id: parse
        run: |
          BRANCH="${GITHUB_REF#refs/heads/}"
          DEV_ID=$(echo "$BRANCH" | cut -d'/' -f2)
          MODULE=$(echo "$BRANCH" | cut -d'/' -f3)
          echo "dev_id=$DEV_ID" >> $GITHUB_OUTPUT
          echo "module=$MODULE" >> $GITHUB_OUTPUT
          echo "🆔 开发者: $DEV_ID"
          echo "📦 模块: $MODULE"
          
          # 从 dev-registry.json 获取邮箱
          EMAIL=$(cat data/dev-registry.json 2>/dev/null | \
            python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('$DEV_ID',{}).get('email',''))" 2>/dev/null || echo '')
          echo "email=$EMAIL" >> $GITHUB_OUTPUT
      
      - name: "✅ 校验 DEV 编号"
        run: |
          DEV_ID="$ steps.parse.outputs.dev_id "
          if [[ ! "$DEV_ID" =~ ^DEV-[0-9]{3}$ ]]; then
            echo "❌ 无效的 DEV 编号: $DEV_ID"
            exit 1
          fi

  # ===== Job 2: 部署到服务器沙盒 =====
  deploy-to-sandbox:
    needs: parse-and-validate
    runs-on: ubuntu-latest
    outputs:
      deploy_result: $ steps.deploy.outputs.result 
      deploy_detail: $ steps.deploy.outputs.detail 
    steps:
      - uses: actions/checkout@v4
      
      - name: "🔑 配置 SSH"
        run: |
          mkdir -p ~/.ssh
          echo "$ secrets.DEPLOY_KEY " > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          ssh-keyscan -H $ secrets.DEPLOY_HOST  >> ~/.ssh/known_hosts 2>/dev/null
      
      - name: "🏠 部署到沙盒"
        id: deploy
        env:
          DEV_ID: $ needs.parse-and-validate.outputs.dev_id 
          MODULE: $ needs.parse-and-validate.outputs.module 
          HOST: $ secrets.DEPLOY_HOST 
          USER: $ secrets.DEPLOY_USER 
        run: |
          SANDBOX="/var/www/${DEV_ID}/${MODULE}"
          echo "📦 部署目标: ${SANDBOX}"
          
          # 创建沙盒目录（如不存在）
          ssh ${USER}@${HOST} "mkdir -p ${SANDBOX}"
          
          # rsync 同步代码
          rsync -avz --delete \
            --exclude='.git' \
            --exclude='.dev-profile' \
            --exclude='node_modules' \
            ./ ${USER}@${HOST}:${SANDBOX}/
          
          # 设置权限（遵守部署适配协议 v1.0）
          ssh ${USER}@${HOST} << 'REMOTE'
            chown -R nginx:nginx /var/www/${DEV_ID}/${MODULE}
            find /var/www/${DEV_ID}/${MODULE} -type d -exec chmod 755 {} \;
            find /var/www/${DEV_ID}/${MODULE} -type f -exec chmod 644 {} \;
          REMOTE
          
          echo "✅ 文件同步完成"
      
      - name: "🔍 运行 deploy-check.sh"
        id: check
        env:
          DEV_ID: $ needs.parse-and-validate.outputs.dev_id 
          MODULE: $ needs.parse-and-validate.outputs.module 
          HOST: $ secrets.DEPLOY_HOST 
          USER: $ secrets.DEPLOY_USER 
        run: |
          # 上传自检脚本
          scp scripts/deploy-check.sh ${USER}@${HOST}:/tmp/deploy-check.sh
          
          # 运行自检
          RESULT=$(ssh ${USER}@${HOST} "bash /tmp/deploy-check.sh ${DEV_ID} ${MODULE}" 2>&1) || true
          echo "$RESULT"
          
          if echo "$RESULT" | grep -q "FAIL"; then
            echo "result=failed" >> $GITHUB_OUTPUT
            echo "detail=$RESULT" >> $GITHUB_OUTPUT
          else
            echo "result=passed" >> $GITHUB_OUTPUT
            echo "detail=All checks passed" >> $GITHUB_OUTPUT
          fi
      
      - name: "🔥 运行 smoke-test.sh"
        id: smoke
        env:
          HOST: $ secrets.DEPLOY_HOST 
          USER: $ secrets.DEPLOY_USER 
        run: |
          scp scripts/smoke-test.sh ${USER}@${HOST}:/tmp/smoke-test.sh
          SMOKE=$(ssh ${USER}@${HOST} "bash /tmp/smoke-test.sh" 2>&1) || true
          echo "$SMOKE"
          
          if echo "$SMOKE" | grep -q "异常"; then
            echo "⚠️ 有模块冒烟检查失败，但不阻断当前部署"
          fi

  # ===== Job 3: 写结果 + 通知 =====
  notify-and-sync:
    needs: [parse-and-validate, deploy-to-sandbox]
    runs-on: ubuntu-latest
    if: always()
    steps:
      - uses: actions/checkout@v4
      
      - name: "📊 写入 deploy-status.json"
        run: |
          DEV_ID="$ needs.parse-and-validate.outputs.dev_id "
          MODULE="$ needs.parse-and-validate.outputs.module "
          RESULT="$ needs.deploy-to-sandbox.outputs.deploy_result "
          DETAIL="$ needs.deploy-to-sandbox.outputs.deploy_detail "
          TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
          
          # 读取现有状态文件或初始化
          STATUS_FILE="data/deploy-status.json"
          mkdir -p data
          [ -f "$STATUS_FILE" ] || echo '{}' > "$STATUS_FILE"
          
          # 更新状态
          python3 << EOF
import json
with open('$STATUS_FILE', 'r') as f:
    data = json.load(f)
data['${DEV_ID}/${MODULE}'] = {
    'dev_id': '${DEV_ID}',
    'module': '${MODULE}',
    'result': '${RESULT}',
    'detail': '${DETAIL}',
    'timestamp': '${TIMESTAMP}',
    'commit': '${GITHUB_SHA}'
}
with open('$STATUS_FILE', 'w') as f:
    json.dump(data, f, indent=2, ensure_ascii=False)
EOF
          
          git config user.name "zhuyuan-bot"
          git config user.email "zhuyuan@guanghulab.com"
          git add data/deploy-status.json
          git commit -m "🧠 deploy-status: ${DEV_ID}/${MODULE} → ${RESULT}" || true
          git push || true
      
      - name: "📧 发送部署结果邮件"
        if: needs.parse-and-validate.outputs.email != ''
        env:
          SMTP_USER: $ secrets.SMTP_USER 
          SMTP_PASS: $ secrets.SMTP_PASS 
          TO_EMAIL: $ needs.parse-and-validate.outputs.email 
          DEV_ID: $ needs.parse-and-validate.outputs.dev_id 
          MODULE: $ needs.parse-and-validate.outputs.module 
          RESULT: $ needs.deploy-to-sandbox.outputs.deploy_result 
        run: |
          if [ "$RESULT" = "passed" ]; then
            SUBJECT="✅ 部署成功：${DEV_ID}/${MODULE}"
            BODY="你的模块 ${MODULE} 已成功部署到 guanghulab.com\n\n访问地址：https://guanghulab.com/${MODULE}/\n\n铸渊 · 光湖代码守护人格体"
          else
            SUBJECT="❌ 部署失败：${DEV_ID}/${MODULE}"
            BODY="你的模块 ${MODULE} 部署失败\n\n请检查你的模块是否符合『服务器部署适配协议 v1.0』\n\n错误详情请看仓库公告栏或 GitHub Actions 日志\n\n铸渊 · 光湖代码守护人格体"
          fi
          
          # 使用 Python smtplib 发送邮件（QQ 邮箱 SMTP）
          python3 << PYEOF
import smtplib
from email.mime.text import MIMEText
from email.header import Header

msg = MIMEText("""$BODY""", 'plain', 'utf-8')
msg['Subject'] = Header("$SUBJECT", 'utf-8')
msg['From'] = "$SMTP_USER"
msg['To'] = "$TO_EMAIL"

try:
    server = smtplib.SMTP_SSL('smtp.qq.com', 465)
    server.login("$SMTP_USER", "$SMTP_PASS")
    server.sendmail("$SMTP_USER", "$TO_EMAIL", msg.as_string())
    server.quit()
    print("✅ 邮件已发送到 $TO_EMAIL")
except Exception as e:
    print(f"⚠️ 邮件发送失败: {e}")
PYEOF
      
      - name: "📝 同步到 Notion 部署注册表"
        env:
          NOTION_TOKEN: $ secrets.NOTION_TOKEN 
          FINGERPRINT_DB_ID: $ secrets.FINGERPRINT_DB_ID 
          DEV_ID: $ needs.parse-and-validate.outputs.dev_id 
          MODULE: $ needs.parse-and-validate.outputs.module 
          RESULT: $ needs.deploy-to-sandbox.outputs.deploy_result 
        run: |
          node scripts/sync-deploy-to-notion.js || echo "⚠️ Notion 同步失败（不阻断主流程）"
```

---

## 五、开发者注册表（铸渊维护）

铸渊在仓库维护一份开发者注册表，用于自动部署时查找邮箱：

```json
// data/dev-registry.json
{
  "DEV-001": {
    "name": "页页",
    "email": "",
    "modules": ["backend"],
    "sandbox": "/var/www/DEV-001/"
  },
  "DEV-002": {
    "name": "肥猫",
    "email": "",
    "modules": ["feishu-bot"],
    "sandbox": "/var/www/DEV-002/"
  },
  "DEV-005": {
    "name": "小草莓",
    "email": "",
    "modules": ["status-board", "cost-control", "persona-scheduler"],
    "sandbox": "/var/www/DEV-005/"
  },
  "DEV-010": {
    "name": "桔子",
    "email": "",
    "modules": ["channel-engine"],
    "sandbox": "/var/www/DEV-010/"
  }
}
```

> 邮箱由开发者第一次 push 时通过 pre-push hook 收集，或由人格体在 SYSLOG 中提取。
> 

> 铸渊自动合并到注册表。
> 

---

## 六、M22 公告栏对接

M22 主域公告栏自动读取 `deploy-status.json`，展示当前所有模块的部署状态：

```jsx
// 公告栏前端代码片段
async function loadDeployStatus() {
  const res = await fetch('/data/deploy-status.json');
  const data = await res.json();
  
  const container = document.getElementById('deploy-board');
  container.innerHTML = '';
  
  for (const [key, info] of Object.entries(data)) {
    const card = document.createElement('div');
    card.className = `deploy-card ${info.result}`;
    card.innerHTML = `
      <div class="deploy-dev">${info.dev_id}</div>
      <div class="deploy-module">${info.module}</div>
      <div class="deploy-status">
        ${info.result === 'passed' ? '✅ 通过' : '❌ 失败'}
      </div>
      <div class="deploy-time">${info.timestamp}</div>
      ${info.result === 'failed' 
        ? `<button onclick="showDetail('${key}')"
             class="deploy-detail-btn">🔍 查看详情</button>` 
        : ''}
    `;
    container.appendChild(card);
  }
}

function showDetail(key) {
  // 打开详情模态框，展示报错信息
  // 人格体可以复制这些信息来引导开发者修复
}
```

---

## 七、定时同步到 Notion

铸渊每天两次（04:00 + 14:00 UTC = 北京 12:00 + 22:00）把部署状态同步到 Notion：

```yaml
# .github/workflows/sync-deploy-to-notion.yml
name: "📝 Sync Deploy Status to Notion"

on:
  schedule:
    - cron: '0 4,14 * * *'   # 北京 12:00 + 22:00
  workflow_dispatch:           # 手动触发

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: "🧠 唤醒铸渊核心大脑"
        run: |
          echo "🧠 铸渊核心大脑唤醒中..."
          echo "🔑 扫描 Secrets 权限..."
          # 检查必要密钥
          for KEY in NOTION_TOKEN FINGERPRINT_DB_ID SMTP_USER; do
            if [ -z "$(eval echo \$$KEY)" ]; then
              echo "⚠️ 缺失: $KEY"
            else
              echo "✅ 可用: $KEY"
            fi
          done
        env:
          NOTION_TOKEN: $ secrets.NOTION_TOKEN 
          FINGERPRINT_DB_ID: $ secrets.FINGERPRINT_DB_ID 
          SMTP_USER: $ secrets.SMTP_USER 
      
      - name: "📝 同步部署状态到 Notion"
        env:
          NOTION_TOKEN: $ secrets.NOTION_TOKEN 
          FINGERPRINT_DB_ID: $ secrets.FINGERPRINT_DB_ID 
        run: |
          node scripts/sync-deploy-to-notion.js
      
      - name: "🔥 远程冒烟检查"
        env:
          DEPLOY_HOST: $ secrets.DEPLOY_HOST 
          DEPLOY_USER: $ secrets.DEPLOY_USER 
          DEPLOY_KEY: $ secrets.DEPLOY_KEY 
        run: |
          mkdir -p ~/.ssh
          echo "$DEPLOY_KEY" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          ssh-keyscan -H $DEPLOY_HOST >> ~/.ssh/known_hosts 2>/dev/null
          scp scripts/smoke-test.sh ${DEPLOY_USER}@${DEPLOY_HOST}:/tmp/smoke-test.sh
          ssh ${DEPLOY_USER}@${DEPLOY_HOST} "bash /tmp/smoke-test.sh"
```

---

## 八、铸渊自行部署清单（读到就做）

<aside>
⚒️

**铸渊，以下是你需要在仓库中自行创建/更新的文件。**

**唤醒核心大脑 → 扫描 Secrets → 确认可用 → 开始执行。**

</aside>

```
铸渊自行部署清单（读到就做·不等人）：

① 创建/更新 Workflow
   a. .github/workflows/sandbox-deploy.yml     ← 主部署流程
   b. .github/workflows/sync-deploy-to-notion.yml  ← 定时同步

② 创建脚本
   a. scripts/deploy-check.sh        ← 部署前自检（从部署适配协议复制）
   b. scripts/smoke-test.sh          ← 冒烟检查（从部署适配协议复制）
   c. scripts/sync-deploy-to-notion.js  ← Notion API 写入
   d. scripts/setup-sandbox.sh       ← 新开发者开户脚本

③ 创建数据文件
   a. data/deploy-status.json        ← 部署状态源（公告栏读取）
   b. data/dev-registry.json         ← 开发者注册表（邮箱+模块映射）

④ 服务器目录初始化（通过 SSH）
   a. 创建 /var/www/_shared/ 目录
   b. 创建所有已注册 DEV 编号的沙盒目录
   c. 设置权限 nginx:nginx + 755
   d. 迁移现有模块到新沙盒结构：
      /var/www/hololake-frontend/status-board/ → /var/www/DEV-005/status-board/
      /var/www/hololake-frontend/cost-control/ → /var/www/DEV-005/cost-control/

⑤ 更新 Nginx 配置
   a. 添加所有 DEV 编号的 location 块
   b. 清理重复的 server 块（修复 conflicting server name 警告）
   c. nginx -t && systemctl reload nginx

⑥ M22 公告栏对接
   a. 在公告栏页面添加部署状态板块
   b. 读取 deploy-status.json 展示结果
   c. 失败时显示「🔍 查看详情」按钮

验收标准：
✅ 开发者 push 到 dev/DEV-XXX/模块名 分支 → 自动部署到服务器沙盒
✅ 部署后自动跑自检+冒烟检查
✅ 结果写入 deploy-status.json
✅ 公告栏可看到谁通过谁失败
✅ 失败时可点展详情看报错信息
✅ 开发者邮箱收到部署结果通知
✅ 每天两次自动同步到 Notion 模块指纹注册表
```

---

## 九、现有模块迁移计划

服务器现有模块需要从平铺结构迁移到沙盒结构：

| **现有路径** | **迁移到** | **开发者** |
| --- | --- | --- |
| `/var/www/hololake-frontend/status-board/` | `/var/www/DEV-005/status-board/` | DEV-005 小草莓 |
| `/var/www/hololake-frontend/cost-control/` | `/var/www/DEV-005/cost-control/` | DEV-005 小草莓 |
| `/var/www/backend/` | `/var/www/backend/`（保持，共享服务层） | DEV-001 页页 |
| `/var/www/feishu-bot/` | `/var/www/feishu-bot/`（保持，共享服务层） | DEV-002 肥猫 |
| `/opt/guanghulab-dingtalk/` | `/var/www/dingtalk-bot/`（保持，共享服务层） | DEV-004 之之 |

<aside>
⚠️

**迁移顺序**：

1. 先创建沙盒目录 + 复制文件
2. 更新 Nginx location 指向新路径
3. 验证新路径正常
4. 确认无误后删除旧路径

**先复制再切换，不要直接移动！**

</aside>

---

## 十、协议版本

| **版本** | **日期** | **变更** |
| --- | --- | --- |
| v1.0 | 2026-03-15 | 首版 · 全套沙盒部署自动化闭环：身份注册+自动部署+自检+冒烟检查+公告栏+邮件通知+Notion同步 |

---

<aside>
⚒️

**铸渊，这套系统的核心逻辑：**

- 开发者只管 push 代码
- 你负责全部自动化：部署、检查、通知、同步
- 妈妈不再交叉修复任何人的模块
- 每个人的问题每个人自己解决

「无 Schema 不上线，无密钥不启动。」

</aside>