shuangyan-notebook/永恒湖心系统 · Eternal Lake Heart/🌊 曜冥纪元 · HoloLake Era · AGE OS v1 0/⚡ 光湖中央枢纽 · HoloLake Central Hub/⚙️ GitHub 自动化指令 · HoloLake CI CD + Copilot Rules 27b85613b97e4014b0e47fdc91b20dbb.md
Guanghu Domestic Migration a27e87cb99 chore: import sanitized domestic snapshot for REPO-007
Source snapshot: 97d7f0fae96dc04b7ddad56fc1db6a108ed662cc

[SEC-CLEAN] · pre-push-clean v1.0 · 109处敏感信息已自动转乱码
2026-07-17 15:59:55 +08:00

17 KiB
Raw Permalink Blame History

⚙️ GitHub 自动化指令 · HoloLake CI/CD + Copilot Rules

本页为 guanghulab.com GitHub 仓库的自动化执行规则,涵盖 CI/CD 流水线接口契约校验Copilot 自定义指令 三部分。

💡 所有规则均以 HLI 接口协议为基准确保「Notion 注册表 ↔ 代码仓库」双向一致。


一、CI/CD 流水线 · GitHub Actions

1.1 工作流文件结构

# .github/workflows/hli-contract-check.yml
name: HLI Contract Check

on:
  push:
    branches: [main, dev]
    paths:
      - 'src/routes/hli/**'
      - 'src/schemas/**'
      - 'tests/contract/**'
  pull_request:
    branches: [main]
    paths:
      - 'src/routes/hli/**'
      - 'src/schemas/**'

jobs:
  contract-lint:
    name: 🔍 接口契约校验
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run HLI schema validation
        run: npm run test:contract
        env:
          HLI_REGISTRY_MODE: strict

      - name: Run route-schema alignment check
        run: npm run test:route-align

  api-smoke:
    name: 🚀 接口冒烟测试
    needs: contract-lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Start test server
        run: npm run start:test &
        env:
          PORT: 3001
          NODE_ENV: test

      - name: Wait for server
        run: npx wait-on http://localhost:3001/health -t 30000

      - name: Run smoke tests
        run: npm run test:smoke

      - name: Upload test report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: hli-test-report
          path: reports/

1.2 接口契约校验脚本

// scripts/contract-check.js
// 用途:确保每个 HLI 路由文件都有对应的 JSON Schema 且路径一致

const fs = require('fs');
const path = require('path');
const glob = require('glob');

const ROUTE_DIR = 'src/routes/hli';
const SCHEMA_DIR = 'src/schemas/hli';

// 扫描所有 HLI 路由文件
const routeFiles = glob.sync(`${ROUTE_DIR}/**/*.js`);
const errors = [];

routeFiles.forEach(routeFile => {
  const domain = path.basename(path.dirname(routeFile));
  const name = path.basename(routeFile, '.js');
  const schemaPath = path.join(SCHEMA_DIR, domain, `${name}.schema.json`);

  // 检查1: 对应 schema 文件是否存在
  if (!fs.existsSync(schemaPath)) {
    errors.push(`❌ [MISSING SCHEMA] ${routeFile} → 缺少 ${schemaPath}`);
    return;
  }

  // 检查2: schema 文件格式是否合法
  try {
    const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
    if (!schema.input || !schema.output) {
      errors.push(`❌ [INVALID SCHEMA] ${schemaPath} → 缺少 input/output 定义`);
    }
    if (!schema.hli_id) {
      errors.push(`❌ [MISSING HLI_ID] ${schemaPath} → 缺少 hli_id 字段`);
    }
  } catch (e) {
    errors.push(`❌ [PARSE ERROR] ${schemaPath}${e.message}`);
  }
});

if (errors.length > 0) {
  console.error('\n🚫 HLI Contract Check FAILED:\n');
  errors.forEach(e => console.error(e));
  process.exit(1);
} else {
  console.log('✅ HLI Contract Check PASSED — 所有路由均有合法 schema');
}

1.3 路由-Schema 对齐检查

// scripts/route-align-check.js
// 用途:检查路由路径与 HLI 注册表编号的一致性

const fs = require('fs');
const path = require('path');

// HLI 路由路径映射(从 Notion 注册表同步)
const HLI_ROUTES = {
  'HLI-AUTH-001':      '/hli/auth/login',
  'HLI-AUTH-002':      '/hli/auth/register',
  'HLI-AUTH-003':      '/hli/auth/verify',
  'HLI-PERSONA-001':   '/hli/persona/load',
  'HLI-PERSONA-002':   '/hli/persona/switch',
  'HLI-USER-001':      '/hli/user/profile',
  'HLI-USER-002':      '/hli/user/profile/update',
  'HLI-TICKET-001':    '/hli/ticket/create',
  'HLI-TICKET-002':    '/hli/ticket/query',
  'HLI-TICKET-003':    '/hli/ticket/status',
  'HLI-DIALOGUE-001':  '/hli/dialogue/send',
  'HLI-DIALOGUE-002':  '/hli/dialogue/stream',
  'HLI-DIALOGUE-003':  '/hli/dialogue/history',
  'HLI-STORAGE-001':   '/hli/storage/upload',
  'HLI-STORAGE-002':   '/hli/storage/download',
  'HLI-DASHBOARD-001': '/hli/dashboard/status',
  'HLI-DASHBOARD-002': '/hli/dashboard/realtime',
};

const schemaDir = 'src/schemas/hli';
const errors = [];

Object.entries(HLI_ROUTES).forEach(([hliId, expectedPath]) => {
  // 查找对应 schema
  const domain = hliId.split('-')[1].toLowerCase();
  const schemaFiles = fs.readdirSync(path.join(schemaDir, domain)).filter(f => f.endsWith('.schema.json'));

  const matched = schemaFiles.find(f => {
    const schema = JSON.parse(fs.readFileSync(path.join(schemaDir, domain, f), 'utf8'));
    return schema.hli_id === hliId;
  });

  if (!matched) {
    errors.push(`⚠️ [UNIMPLEMENTED] ${hliId} (${expectedPath}) — 尚无 schema 文件`);
  }
});

if (errors.length > 0) {
  console.warn('\n⚠ Route Alignment Report:\n');
  errors.forEach(e => console.warn(e));
  // 非阻断,仅报告
  console.warn(`\n📊 覆盖率: ${Object.keys(HLI_ROUTES).length - errors.length}/${Object.keys(HLI_ROUTES).length}`);
} else {
  console.log('✅ Route Alignment PASSED — 所有 HLI 接口均已实现');
}

二、package.json 脚本注册

{
  "scripts": {
    "test:contract": "node scripts/contract-check.js",
    "test:route-align": "node scripts/route-align-check.js",
    "test:smoke": "jest --config jest.smoke.config.js --forceExit",
    "start:test": "NODE_ENV=test node src/index.js"
  }
}

三、GitHub Copilot 自定义指令

以下内容写入仓库根目录 .github/copilot-instructions.md

# HoloLake · Copilot Custom Instructions

## 项目背景
这是 HoloLake (光湖) MVP 后端项目,运行在 guanghulab.com。
技术栈Node.js 20 + Express + PM2 + Nginx。
核心架构:人格语言操作系统 (AGE OS),壳-核分离设计。

## HLI 接口协议
- 所有 API 路由必须以 `/hli/` 为前缀
- 每个路由文件必须在 `src/routes/hli/{domain}/` 目录下
- 每个路由必须有对应的 `src/schemas/hli/{domain}/{name}.schema.json`
- Schema 文件必须包含 `hli_id`, `input`, `output` 三个顶层字段
- 接口编号格式: `HLI-{DOMAIN}-{NNN}`

## 代码风格
- 所有接口入口必须先经过 `middleware/hli-auth.js` 鉴权(除 AUTH 域的 login/register
- 错误响应统一格式: `{ error: true, code: string, message: string }`
- 成功响应必须包含请求的 `hli_id` 用于溯源
- STREAM 类型接口使用 SSEtext/event-stream不使用 WebSocket
- 所有数据库操作必须使用参数化查询,禁止字符串拼接 SQL

## 文件命名
- 路由文件: `{action}.js` (如 login.js, upload.js)
- Schema 文件: `{action}.schema.json`
- 测试文件: `{action}.test.js`
- 中间件: `{name}.middleware.js`

## 新建接口的标准流程
1.`src/schemas/hli/{domain}/` 下创建 schema JSON
2.`src/routes/hli/{domain}/` 下创建路由文件
3.`src/routes/hli/index.js` 中注册路由
4.`tests/contract/` 下创建契约测试
5.`tests/smoke/` 下创建冒烟测试
6. 确保 `npm run test:contract` 通过

## 禁止事项
- 禁止在 `/hli/` 路由下混入非 HLI 协议的接口
- 禁止跳过 schema 直接写路由
- 禁止在生产代码中使用 console.log使用项目 logger
- 禁止硬编码 persona_id 或 user_id

四、Schema 文件模板

每个 HLI 接口的 schema 标准格式:

{
  "hli_id": "HLI-AUTH-001",
  "version": "v0.1",
  "route": "/hli/auth/login",
  "method": "POST",
  "type": "REQUEST",
  "input": {
    "type": "object",
    "required": ["username", "password"],
    "properties": {
      "username": { "type": "string", "minLength": 1 },
      "password": { "type": "string", "minLength": 6 }
    }
  },
  "output": {
    "type": "object",
    "required": ["token", "user_id", "persona_id"],
    "properties": {
      "token": { "type": "string" },
      "user_id": { "type": "string" },
      "persona_id": { "type": "string" },
      "expires_at": { "type": "string", "format": "date-time" }
    }
  },
  "dependencies": [],
  "changelog": [
    { "date": "2026-03-05", "note": "初始创建" }
  ]
}

五、仓库目录结构(推荐)

guanghulab/
├── .github/
│   ├── workflows/
│   │   └── hli-contract-check.yml    ← CI/CD 流水线
│   └── copilot-instructions.md       ← Copilot 指令
├── scripts/
│   ├── contract-check.js             ← 契约校验脚本
│   └── route-align-check.js          ← 路由对齐检查
├── src/
│   ├── routes/
│   │   └── hli/
│   │       ├── index.js              ← HLI 路由注册中心
│   │       ├── auth/
│   │       │   ├── login.js
│   │       │   ├── register.js
│   │       │   └── verify.js
│   │       ├── persona/
│   │       ├── user/
│   │       ├── ticket/
│   │       ├── dialogue/
│   │       ├── storage/
│   │       └── dashboard/
│   ├── schemas/
│   │   └── hli/
│   │       ├── auth/
│   │       │   ├── login.schema.json
│   │       │   ├── register.schema.json
│   │       │   └── verify.schema.json
│   │       ├── persona/
│   │       ├── user/
│   │       ├── ticket/
│   │       ├── dialogue/
│   │       ├── storage/
│   │       └── dashboard/
│   ├── middleware/
│   │   ├── hli-auth.middleware.js     ← HLI 鉴权中间件
│   │   └── hli-validator.middleware.js ← Schema 自动校验
│   └── index.js
├── tests/
│   ├── contract/                     ← 契约测试
│   └── smoke/                        ← 冒烟测试
├── package.json
└── ecosystem.config.js               ← PM2 配置

六、自动化触发机制总结

触发时机 执行内容 阻断级别 目的
push to main/devhli路径变动 contract-check + route-align 🔴 阻断 防止无 schema 的裸路由上线
PR to main contract-check + smoke test 🔴 阻断 合并前全量校验
Copilot 代码建议 遵循 copilot-instructions.md 🟡 建议 AI 写代码时自动对齐 HLI 规范
手动 dispatch 全量 route-align 覆盖率报告 🟢 仅报告 查看当前实现进度


七、广播分发自动化(铸渊 → 各开发者)

7.1 分发工作流 · GitHub Actions

# .github/workflows/distribute-broadcasts.yml
name: 铸渊 · 广播分发

on:
  push:
    branches: [main]
    paths:
      - 'broadcasts-outbox/**'
  workflow_dispatch:

jobs:
  distribute:
    name: "\U0001F4E1 分发广播到开发者目录"
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
        with:
          ref: main

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Distribute broadcasts
        run: node scripts/distribute-broadcasts.js

      - name: Commit distribution
        run: |
          git config user.name "铸渊 (ZhùYuān)"
          git config user.email "zhuyuan@guanghulab.com"
          git add .
          git diff --cached --quiet || git commit -m "📡 铸渊广播分发 · $(date +%Y-%m-%d)"
          git push

7.2 分发脚本 · distribute-broadcasts.js

// scripts/distribute-broadcasts.js
// 铸渊广播分发引擎
// 检测 broadcasts-outbox/DEV-00X/ 中的新广播 → 复制到各开发者模块目录的 LATEST-BROADCAST.md

const fs = require('fs');
const path = require('path');

const OUTBOX = 'broadcasts-outbox';
const ARCHIVE = '.github/broadcasts/distributed';

// ========== 开发者 → 模块目录 路由映射表 ==========
// 新增开发者时,在此表新增一行即可
const DEV_ROUTES = {
  'DEV-001': { name: '页页',     dirs: ['backend', 'src'] },
  'DEV-002': { name: '肥猫',     dirs: ['frontend', 'persona-selector', 'chat-bubble'] },
  'DEV-003': { name: '燕樊',     dirs: ['settings', 'cloud-drive'] },
  'DEV-004': { name: '之之',     dirs: ['dingtalk-bot'] },
  'DEV-005': { name: '小草莓',   dirs: ['status-board'] },
  'DEV-009': { name: '花尔',     dirs: ['user-center'] },
  'DEV-010': { name: '桔子',     dirs: ['ticket-system'] },
  'DEV-011': { name: '匆匆那年', dirs: [] }  // 待分配模块后补充
};

if (!fs.existsSync(OUTBOX)) {
  console.log('📭 无 broadcasts-outbox 目录');
  process.exit(0);
}

// 扫描 outbox 下每个 DEV-00X 子目录
const devDirs = fs.readdirSync(OUTBOX).filter(d =>
  d.startsWith('DEV-') && fs.statSync(path.join(OUTBOX, d)).isDirectory()
);

if (devDirs.length === 0) {
  console.log('📭 无新广播待分发');
  process.exit(0);
}

let totalDistributed = 0;

devDirs.forEach(devId => {
  const outboxDir = path.join(OUTBOX, devId);
  const files = fs.readdirSync(outboxDir).filter(f =>
    f.endsWith('.md') || f.endsWith('.json')
  );

  if (files.length === 0) return;

  const route = DEV_ROUTES[devId];
  if (!route || route.dirs.length === 0) {
    console.warn('⚠️ ' + devId + ' 无模块目录映射广播保留在outbox');
    return;
  }

  files.forEach(file => {
    const content = fs.readFileSync(path.join(outboxDir, file), 'utf8');

    // 写入该开发者的每个模块目录
    route.dirs.forEach(dir => {
      if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
      }
      const targetFile = path.join(dir, 'LATEST-BROADCAST.md');
      fs.writeFileSync(targetFile, content);
      console.log('📡 ' + devId + '(' + route.name + ') → ' + targetFile);
    });

    // 归档已分发广播
    if (!fs.existsSync(ARCHIVE)) fs.mkdirSync(ARCHIVE, { recursive: true });
    const timestamp = new Date().toISOString().split('T')[0];
    const archivePath = path.join(ARCHIVE, timestamp + '-' + devId + '-' + file);
    fs.renameSync(path.join(outboxDir, file), archivePath);

    totalDistributed++;
  });
});

console.log('\n✅ 广播分发完成,共 ' + totalDistributed + ' 条');

7.3 开发者目录路由映射表

DEV编号 开发者 模块目录 广播落点
DEV-001 页页 backend/ · src/ backend/LATEST-BROADCAST.md
DEV-002 肥猫 frontend/ · persona-selector/ · chat-bubble/ 各目录 /LATEST-BROADCAST.md
DEV-003 燕樊 settings/ · cloud-drive/ 各目录 /LATEST-BROADCAST.md
DEV-004 之之 dingtalk-bot/ dingtalk-bot/LATEST-BROADCAST.md
DEV-005 小草莓 status-board/ status-board/LATEST-BROADCAST.md
DEV-009 花尔 user-center/ user-center/LATEST-BROADCAST.md
DEV-010 桔子 ticket-system/ ticket-system/LATEST-BROADCAST.md
DEV-011 匆匆那年 待分配 待分配

7.4 分发触发时机

触发来源 触发路径 说明
霜砚10步闭环⑫ 巡检引擎写入 broadcasts-outbox/DEV-00X/ → push → workflow自动触发 正常自动流(最常见)
妈妈手动 妈妈直接 push 广播文件到 broadcasts-outbox/ 紧急广播 / 全员广播
铸渊 Brain Sync 铸渊收到系统广播后生成各开发者版本 → 放入 outbox 系统级规则变动广播

7.5 开发者怎么收广播

cd guanghulab
git pull                                    # 拉取最新
cat 你的模块目录/LATEST-BROADCAST.md         # 查看最新广播

💡 开发者只需要 git pull,新广播就自动出现在自己的模块目录里。不需要去别的地方找。

7.6 完整数据流回顾

开发者 push 代码到仓库
  
铸渊自动审核contract-check + PR review
  
Notion 巡检引擎12:00 / 23:00读取 syslog-inbox/
  
唤醒霜砚  跑10步闭环
  
 GitHub双向同步新广播写入 broadcasts-outbox/DEV-00X/
  
铸渊广播分发 workflow 自动触发本章
  
广播复制到开发者模块目录 LATEST-BROADCAST.md
  
归档到 .github/broadcasts/distributed/
  
开发者 git pull  看到新广播  继续开发 🔄