name: Notion Callback Pipeline
# 📥 Notion → GitHub 回传闭环管道
#
# 当 Notion 侧核心大脑人格体处理完 SYSLOG 工单后,
# 通过 repository_dispatch 回传处理结果到 GitHub。
# 铸渊 Agent 接收回传,重新唤醒核心大脑认知进行二次核验,
# 确认新广播生成无误后,自动发送邮件通知开发者。
#
# 触发方式:
# Notion 侧 Agent 调用 GitHub API 发送 repository_dispatch 事件
# event_type: notion-syslog-callback
# client_payload: {
# broadcast_id: "BC-XXX-XXX-XX",
# dev_email: "developer@email.com",
# notion_result: "Notion 侧处理结果文本",
# ticket_id: "Notion 工单 ID",
# status: "approved" | "needs_revision"
# }
#
# 闭环流程:
# ① Notion 侧处理完成 → repository_dispatch 回传
# ② 铸渊 Agent 接收并解析回传数据
# ③ 重新唤醒铸渊核心大脑认知(二次核验)
# ④ 核心大脑核对广播生成无误
# ⑤ 铸渊 Agent 发送邮件通知开发者
# ⑥ 闭环结束
#
# 依赖 Secrets:
# LLM_API_KEY 第三方 LLM 平台密钥
# LLM_BASE_URL 第三方 LLM 平台 API 地址
# NOTION_API_TOKEN Notion API token
# CORE_BRAIN_PAGE_ID 曜冥核心大脑 v4.0 页面 ID
# PORTRAIT_DB_ID 开发者动态画像库数据库 ID
# FINGERPRINT_DB_ID 模块指纹注册表数据库 ID
# SMTP_USER QQ 邮箱地址
# SMTP_PASS QQ 邮箱 SMTP 授权码
on:
repository_dispatch:
types: [notion-syslog-callback]
jobs:
verify-and-notify:
name: 📥 Notion 回传 · 二次核验 · 邮件通知
runs-on: ubuntu-latest
permissions:
contents: 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: 📥 Parse Notion callback
id: callback
uses: actions/github-script@v7
with:
script: |
const payload = context.payload.client_payload || {};
const broadcastId = payload.broadcast_id || '';
const devEmail = payload.dev_email || '';
const notionResult = payload.notion_result || '';
const ticketId = payload.ticket_id || '';
const status = payload.status || 'unknown';
if (!broadcastId) {
core.setFailed('❌ 缺少 broadcast_id');
return;
}
core.setOutput('broadcast_id', broadcastId);
core.setOutput('dev_email', devEmail);
core.setOutput('notion_result', notionResult);
core.setOutput('ticket_id', ticketId);
core.setOutput('status', status);
console.log(`📥 Notion 回传: 广播=${broadcastId}, 状态=${status}, 工单=${ticketId}`);
- name: 🔍 铸渊 Agent · 模块二次验证
id: verify
env:
SYSLOG_CONTENT: ${{ steps.callback.outputs.notion_result }}
BROADCAST_ID: ${{ steps.callback.outputs.broadcast_id }}
AUTHOR: notion-callback
run: node scripts/verify-modules.js
- name: 🧠 二次核验 · 唤醒铸渊核心大脑
id: persona
env:
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
BROADCAST_ID: ${{ steps.callback.outputs.broadcast_id }}
SUBMIT_TYPE: syslog
SUBMIT_CONTENT: ${{ steps.callback.outputs.notion_result }}
AUTHOR: notion-callback
MODULE_VERIFY_RESULT: ${{ steps.verify.outputs.verify_report }}
NOTION_CALLBACK_RESULT: ${{ steps.callback.outputs.notion_result }}
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: 📡 推送广播到 GitHub(Phase B4)
if: steps.persona.outcome == 'success' && steps.callback.outputs.status == 'approved'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BROADCAST_ID: ${{ steps.callback.outputs.broadcast_id }}
DEVELOPER_NAME: notion-callback
BROADCAST_CONTENT: ${{ steps.persona.outputs.result }}
run: node scripts/push-broadcast-to-github.js
- name: 📝 回写 Notion 工单状态
if: steps.callback.outputs.ticket_id
env:
NOTION_TOKEN: ${{ secrets.NOTION_API_TOKEN }}
TICKET_ID: ${{ steps.callback.outputs.ticket_id }}
CALLBACK_STATUS: ${{ steps.callback.outputs.status }}
run: |
node -e "
var https = require('https');
var token = process.env.NOTION_TOKEN || '';
var ticketId = process.env.TICKET_ID || '';
var status = process.env.CALLBACK_STATUS || 'unknown';
if (!token || !ticketId) {
console.log('⚠️ 跳过工单状态更新');
process.exit(0);
}
var notionStatus = status === 'approved' ? '✅ 已完成' : '⚠️ 异常·等人工介入';
var body = JSON.stringify({
properties: {
'状态': { select: { name: notionStatus } }
}
});
var opts = {
hostname: 'api.notion.com',
port: 443,
path: '/v1/pages/' + ticketId,
method: 'PATCH',
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28',
'Content-Length': Buffer.byteLength(body)
}
};
var req = https.request(opts, function(res) {
var data = '';
res.on('data', function(c) { data += c; });
res.on('end', function() {
if (res.statusCode >= 200 && res.statusCode < 300) {
console.log('✅ 工单状态已更新为 ' + notionStatus);
} else {
console.log('⚠️ 工单更新失败: ' + res.statusCode);
}
});
});
req.on('error', function(e) { console.log('⚠️ ' + e.message); });
req.write(body);
req.end();
"
- name: 📧 发送最终确认邮件
env:
SMTP_USER: ${{ secrets.SMTP_USER }}
SMTP_PASS: ${{ secrets.SMTP_PASS }}
EMAIL_TO: ${{ steps.callback.outputs.dev_email }}
BROADCAST_ID: ${{ steps.callback.outputs.broadcast_id }}
NOTION_STATUS: ${{ steps.callback.outputs.status }}
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 notionStatus = process.env.NOTION_STATUS || 'unknown';
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 isApproved = notionStatus === 'approved';
const subjectText = isApproved
? '[光湖系统] ' + broadcastId + ' · ✅ 新广播已生成(已核验)'
: '[光湖系统] ' + broadcastId + ' · ⚠️ 广播需修订';
const statusBadge = isApproved
? '✅ 已核验通过'
: '⚠️ 需修订';
const resultHtml = result
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/\n/g, '
');
const html = [
'
HoloLake · 铸渊核心大脑二次核验完成
', '📡 ' + broadcastId + '
', '' + statusBadge + '
', '🌀 铸渊 · 代码守护人格体 · Notion↔GitHub 闭环确认
', '