# ═══════════════════════════════════════════════
# 🔺 Sovereign: TCS-0002∞ | Root: SYS-GLW-0001
# 📜 Copyright: 国作登字-2026-A-00037559
# ═══════════════════════════════════════════════
name: 📡 SYSLOG Issue Pipeline
# 📡 SYSLOG 自助提交系统 · Issue 版全自动闭环
#
# 开发者在 GitHub Issue 提交 SYSLOG 或提问(使用 syslog-submit 模板)
# → Actions 自动解析 → 模块上传验证 → 调用 LLM API 唤醒人格体
# → 模块闭环测试 → 写入 Notion 工单 → 发邮件给开发者 → Issue 回复
#
# 闭环流程:
# ① 开发者提交 SYSLOG → Issue 触发
# ② 铸渊 Agent 解析提交内容
# ③ 铸渊 Agent 检测模块是否上传到仓库
# ④ 唤醒铸渊核心大脑认知(注入模块验证结果)
# ⑤ 核心大脑处理 SYSLOG + 生成广播
# ⑥ 铸渊 Agent 推送 Notion 工单(触发 Notion 侧处理)
# ⑦ 邮件通知开发者
# ⑧ Issue 回复闭环
#
# 依赖 Secrets:
# LLM_API_KEY 第三方 LLM 平台密钥(必须)
# LLM_BASE_URL 第三方 LLM 平台 API 地址(必须,如 https://api.xxx.com/v1)
# NOTION_API_TOKEN Notion API token
# NOTION_TICKET_DB_ID 霜砚工单数据库 ID
# CORE_BRAIN_PAGE_ID 曜冥核心大脑 v4.0 页面 ID(v4.0 协议动态注入)
# PORTRAIT_DB_ID 开发者动态画像库数据库 ID(v4.0 协议动态注入)
# FINGERPRINT_DB_ID 模块指纹注册表数据库 ID(v4.0 协议动态注入)
# SMTP_USER QQ 邮箱地址
# SMTP_PASS QQ 邮箱 SMTP 授权码
on:
issues:
types: [opened, labeled]
jobs:
process:
name: 📡 处理 SYSLOG 提交 / 广播提问 (Issue)
runs-on: ubuntu-latest
if: >
contains(toJSON(github.event.issue.labels), 'syslog') ||
contains(github.event.issue.title, 'SYSLOG') ||
contains(github.event.issue.title, '系统日志') ||
contains(github.event.issue.body, '### 广播编号')
permissions:
contents: write
issues: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci --ignore-scripts
- name: 📋 步骤 1/8 · 接收提交
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
STEP_NUM: '1'
STEP_NAME: '接收提交'
STEP_STATUS: 'ok'
STEP_DETAIL: '系统已收到您的 SYSLOG 提交,管道正在运行中…'
run: node scripts/pipeline-reporter.js
- name: 🔍 Parse submission
id: parse
uses: actions/github-script@v7
with:
script: |
const body = context.payload.issue.body || '';
const title = context.payload.issue.title || '';
function extractField(text, label) {
const regex = new RegExp('###\\s*' + label + '\\s*\\n+([^\\n#]+)', 'i');
const match = text.match(regex);
return match ? match[1].trim() : '';
}
const broadcastId = extractField(body, '广播编号') || '';
const submitType = extractField(body, '类型') || '';
const email = extractField(body, '你的邮箱') || '';
const contentMatch = body.match(/###\s*内容\s*\n+([\s\S]*?)$/i);
const content = contentMatch ? contentMatch[1].trim() : '';
let type = 'question';
if (submitType.includes('SYSLOG') || title.includes('SYSLOG')) {
type = 'syslog';
} else if (submitType.includes('提问') || title.includes('提问')) {
type = 'question';
}
if (!broadcastId) {
core.setFailed('❌ 缺少广播编号,请在提交时填写广播编号');
return;
}
if (!email) {
core.setFailed('❌ 缺少邮箱,请在提交时填写你的邮箱');
return;
}
if (!content) {
core.setFailed('❌ 缺少内容,请粘贴你的 SYSLOG 或问题');
return;
}
core.setOutput('broadcast_id', broadcastId);
core.setOutput('type', type);
core.setOutput('email', email);
core.setOutput('content', content);
core.setOutput('issue_number', context.payload.issue.number);
core.setOutput('author', context.payload.issue.user.login);
console.log(`📡 解析完成: 广播=${broadcastId}, 类型=${type}, 邮箱=${email}`);
- name: 📋 步骤 2/8 · 解析内容(成功)
if: success()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
STEP_NUM: '2'
STEP_NAME: '解析内容'
STEP_STATUS: 'ok'
STEP_DETAIL: '广播编号: ${{ steps.parse.outputs.broadcast_id }} · 类型: ${{ steps.parse.outputs.type }} · 邮箱: ${{ steps.parse.outputs.email }}'
run: node scripts/pipeline-reporter.js
- name: 📋 步骤 2/8 · 解析内容(失败)
if: failure()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
STEP_NUM: '2'
STEP_NAME: '解析内容'
STEP_STATUS: 'error'
STEP_DETAIL: '解析失败,请检查提交格式是否正确(需包含广播编号、类型、邮箱、内容)'
run: node scripts/pipeline-reporter.js
- name: 🔍 铸渊 Agent · 模块上传验证
id: verify
env:
SYSLOG_CONTENT: ${{ steps.parse.outputs.content }}
BROADCAST_ID: ${{ steps.parse.outputs.broadcast_id }}
AUTHOR: ${{ steps.parse.outputs.author }}
run: node scripts/verify-modules.js
- name: 📋 步骤 3/8 · 模块验证(成功)
if: always() && steps.verify.outcome == 'success'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
STEP_NUM: '3'
STEP_NAME: '模块验证'
STEP_STATUS: 'ok'
STEP_DETAIL: '模块上传验证已完成'
run: node scripts/pipeline-reporter.js
- name: 📋 步骤 3/8 · 模块验证(失败)
if: always() && steps.verify.outcome == 'failure'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
STEP_NUM: '3'
STEP_NAME: '模块验证'
STEP_STATUS: 'error'
STEP_DETAIL: '模块验证异常(不影响闭环继续)'
run: node scripts/pipeline-reporter.js
- name: 🧠 Auto-detect and wake up persona
id: persona
env:
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
BROADCAST_ID: ${{ steps.parse.outputs.broadcast_id }}
SUBMIT_TYPE: ${{ steps.parse.outputs.type }}
SUBMIT_CONTENT: ${{ steps.parse.outputs.content }}
AUTHOR: ${{ steps.parse.outputs.author }}
MODULE_VERIFY_RESULT: ${{ steps.verify.outputs.verify_report }}
NOTION_TOKEN: ${{ secrets.NOTION_API_TOKEN }}
CORE_BRAIN_PAGE_ID: ${{ secrets.CORE_BRAIN_PAGE_ID }}
PORTRAIT_DB_ID: ${{ secrets.PORTRAIT_DB_ID }}
FINGERPRINT_DB_ID: ${{ secrets.FINGERPRINT_DB_ID }}
run: node scripts/wake-persona.js
- name: 📋 步骤 4/8 · 唤醒核心大脑(成功)
if: success()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
STEP_NUM: '4'
STEP_NAME: '唤醒核心大脑'
STEP_STATUS: 'ok'
STEP_DETAIL: '铸渊核心大脑已完成处理'
run: node scripts/pipeline-reporter.js
- name: 📋 步骤 4/8 · 唤醒核心大脑(失败)
if: failure() && steps.parse.outcome == 'success'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
STEP_NUM: '4'
STEP_NAME: '唤醒核心大脑'
STEP_STATUS: 'error'
STEP_DETAIL: 'API 调用失败,请检查 Secrets: LLM_API_KEY / LLM_BASE_URL 是否配置正确'
run: node scripts/pipeline-reporter.js
# ✅ 修复点:原来是 if: $ secrets.NOTION_API_TOKEN ,会报 Unrecognized named-value: 'secrets'
# secrets 不能在 if: 里直接用,必须改成 != '' 的形式
- name: 📋 创建标准化 Notion 工单(Phase B1)
id: ticket
if: ${{ secrets.NOTION_API_TOKEN != '' }}
env:
NOTION_TOKEN: ${{ secrets.NOTION_API_TOKEN }}
NOTION_TICKET_DB_ID: ${{ secrets.NOTION_TICKET_DB_ID }}
BROADCAST_ID: ${{ steps.parse.outputs.broadcast_id }}
SUBMIT_TYPE: ${{ steps.parse.outputs.type }}
PERSONA_RESULT: ${{ steps.persona.outputs.result }}
MODULE_VERIFY: ${{ steps.verify.outputs.verify_result }}
MODULES_UPLOADED: ${{ steps.verify.outputs.modules_uploaded }}
DEVELOPER: ${{ steps.parse.outputs.author }}
SYSLOG_RAW: ${{ steps.parse.outputs.content }}
run: node scripts/create-standardized-ticket.js
- name: 📋 步骤 5/8 · 创建 Notion 工单(成功)
if: always() && steps.ticket.outcome == 'success'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
STEP_NUM: '5'
STEP_NAME: '创建 Notion 工单'
STEP_STATUS: 'ok'
STEP_DETAIL: '已写入霜砚工单簿'
run: node scripts/pipeline-reporter.js
- name: 📋 步骤 5/8 · 创建 Notion 工单(跳过)
if: always() && steps.ticket.outcome == 'skipped'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
STEP_NUM: '5'
STEP_NAME: '创建 Notion 工单'
STEP_STATUS: 'skip'
STEP_DETAIL: 'Notion Token 未配置,跳过'
run: node scripts/pipeline-reporter.js
- name: 📋 步骤 5/8 · 创建 Notion 工单(失败)
if: always() && steps.ticket.outcome == 'failure'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
STEP_NUM: '5'
STEP_NAME: '创建 Notion 工单'
STEP_STATUS: 'error'
STEP_DETAIL: 'Notion API 写入失败'
run: node scripts/pipeline-reporter.js
- name: 📡 推送广播到 GitHub(Phase B4)
id: broadcast
if: steps.persona.outcome == 'success' && steps.parse.outputs.type == 'syslog'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BROADCAST_ID: ${{ steps.parse.outputs.broadcast_id }}
DEVELOPER_NAME: ${{ steps.parse.outputs.author }}
BROADCAST_CONTENT: ${{ steps.persona.outputs.result }}
run: node scripts/push-broadcast-to-github.js
- name: 📋 步骤 6/8 · 生成广播(成功)
if: always() && steps.broadcast.outcome == 'success'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
STEP_NUM: '6'
STEP_NAME: '生成广播'
STEP_STATUS: 'ok'
STEP_DETAIL: '新广播已生成并推送'
run: node scripts/pipeline-reporter.js
- name: 📋 步骤 6/8 · 生成广播(跳过)
if: always() && steps.broadcast.outcome == 'skipped'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
STEP_NUM: '6'
STEP_NAME: '生成广播'
STEP_STATUS: 'skip'
STEP_DETAIL: '非 SYSLOG 类型或上游步骤未成功,跳过广播生成'
run: node scripts/pipeline-reporter.js
- name: 📋 步骤 6/8 · 生成广播(失败)
if: always() && steps.broadcast.outcome == 'failure'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
STEP_NUM: '6'
STEP_NAME: '生成广播'
STEP_STATUS: 'error'
STEP_DETAIL: '广播生成或推送失败'
run: node scripts/pipeline-reporter.js
- name: 📧 Send email to developer
id: email
env:
SMTP_USER: ${{ secrets.SMTP_USER }}
SMTP_PASS: ${{ secrets.SMTP_PASS }}
EMAIL_TO: ${{ steps.parse.outputs.email }}
BROADCAST_ID: ${{ steps.parse.outputs.broadcast_id }}
SUBMIT_TYPE: ${{ steps.parse.outputs.type }}
PERSONA_RESULT: ${{ steps.persona.outputs.result }}
run: |
node -e "
const nodemailer = require('nodemailer');
const user = process.env.SMTP_USER || '';
const pass = process.env.SMTP_PASS || '';
const to = process.env.EMAIL_TO || '';
const broadcastId = process.env.BROADCAST_ID || '';
const type = process.env.SUBMIT_TYPE || 'syslog';
const result = process.env.PERSONA_RESULT || '(处理中,请稍后)';
if (!user || !pass) {
console.log('⚠️ SMTP not configured, skipping email');
process.exit(0);
}
if (!to) {
console.log('⚠️ No recipient email, skipping');
process.exit(0);
}
const subjectText = type === 'syslog'
? '[光湖系统] ' + broadcastId + ' · 新广播已生成'
: '[光湖系统] ' + broadcastId + ' · 问题已解答';
const resultHtml = result
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/\n/g, '
');
const html = [
'
HoloLake · 人格语言操作系统
', '📡 ' + broadcastId + '
', '🌀 铸渊 · 代码守护人格体 · 自动发送
', '