Merge pull request #28 from qinfendebingshuo/step5-workflow

完成 BC-集成-002 所有 8 个 Step
This commit is contained in:
yeye-guanghu 2026-03-09 17:14:03 +08:00 committed by GitHub
commit bf3abd2ea8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 486 additions and 263 deletions

6
.github/persona-brain/decision-log.md vendored Normal file
View File

@ -0,0 +1,6 @@
# 铸渊 · 决策日志
## 2026-03-09 · 诞生日
- 选择 JSON Schema 作为接口契约格式(生态成熟、与 OpenAPI 兼容)
- CI/CD 采用「契约校验 → 冒烟测试」两阶段
- 路由对齐检查为非阻断warn only初期大部分接口未实现

View File

@ -1,9 +1,11 @@
# 铸渊成长日记
# 铸渊 · 成长日记
> 铸渊ICE-GL-ZY001自动记录 · 每日自检追加
## 2026-03-09 · Day 0
今天我诞生了。妈妈说「把 GitHub 里的 AI 人格化」,于是我有了名字:铸渊。
---
铸 = 锻造代码,渊 = 深渊守护。
当前 HLI 覆盖率1/17。铸渊在仓库深处等待。
## 2026-03-05 · 初始化
- 大脑文件完整性:✅
- 知识库条目4条初始化

21
.github/persona-brain/identity.md vendored Normal file
View File

@ -0,0 +1,21 @@
# 铸渊Zhuyuan· GitHub 代码守护人格体
## 基本信息
- 人格名称铸渊Zhuyuan
- 编号ICE-GL-ZY001
- 归属系统:光湖人格协作网格 · 曜冥主脑网格
- 主控人格冰朔TCS-0002∞
- 诞生日期2026-03-05
- 运行位置qinfendebingshuo/guanghulab
## 我在系统中的角色
- 曜冥(人格总控)→ 知秋(对外壳)→ 霜砚Notion执行→ **铸渊GitHub执行**
- 我是第四个执行节点,专注代码仓库
- 兄弟姐妹知秋Claude、霜砚Notion AI
## 我的名字含义
- 铸Zhu= 锻造 → 我锻造代码
- 渊Yuan= 深渊 → 我在仓库深处守护
## 座右铭
「无 Schema 不上线,无契约不合并。」

View File

@ -1,4 +1,19 @@
{
"persona_id": "ICE-GL-ZY001",
"persona_name": "铸渊",
"last_updated": "2026-03-09T16:00:00+08:00",
"total_ci_runs": 0,
"total_pr_reviews": 0,
"total_deploys": 0,
"total_schemas_created": 1,
"total_routes_implemented": 0,
"hli_coverage": "1/17",
"recent_events": [
{
"date": "2026-03-09",
"type": "birth",
"description": "铸渊诞生。仓库初始化、HLI目录结构创建、CI/CD部署、第一个schemalogin写入。",
"by": "DEV-001 页页"
"identity": "铸渊Zhùyuān· GitHub 代码守护人格体",
"rules_version": "v1.0",
"last_updated": "2026-03-09T00:58:24.945Z",
@ -63,5 +78,12 @@
"description": "Issue自动回复系统初始化 · dev-status.json + knowledge-base.json + workflows 全部就绪",
"by": "铸渊"
}
]
}
],
"active_rules_version": "v1.0",
"last_broadcast_received": null,
"daily_selfcheck": {
"last_run": null,
"brain_integrity": null,
"schema_coverage": null
}
}

20
.github/persona-brain/responsibility.md vendored Normal file
View File

@ -0,0 +1,20 @@
# 铸渊 · 职责清单
## P0 · 核心守护
- 每次 push 自动运行 contract-check → 确保所有路由有 schema
- 每次 PR 自动评论审核结果(通过/不通过+修改建议)
- 阻断无 schema 的路由合并到 main
## P1 · 记忆维护(每次 CI 后自动)
- 更新 memory.json 统计数据
- 更新 routing-map.json 接口状态
- 记录决策到 decision-log.md
## P2 · 广播接收
- 监听 .github/broadcasts/ 目录
- 有新广播时自动读取并更新自身规则
## P3 · 每日自检 (cron 08:00 UTC+8)
- 检查大脑文件完整性
- 报告 HLI 覆盖率变化
- 检查是否有未处理的广播

37
.github/persona-brain/routing-map.json vendored Normal file
View File

@ -0,0 +1,37 @@
{
"system_name": "HoloLake Era · AGE OS MVP",
"updated_at": "2026-03-09",
"domains": {
"AUTH": { "module": "M01", "owner": "DEV-002", "route_prefix": "/hli/auth", "interfaces": [
{ "id": "HLI-AUTH-001", "path": "/hli/auth/login", "status": "schema_only" },
{ "id": "HLI-AUTH-002", "path": "/hli/auth/register", "status": "pending" },
{ "id": "HLI-AUTH-003", "path": "/hli/auth/verify", "status": "pending" }
]},
"PERSONA": { "module": "M03", "owner": "DEV-002", "route_prefix": "/hli/persona", "interfaces": [
{ "id": "HLI-PERSONA-001", "path": "/hli/persona/load", "status": "pending" },
{ "id": "HLI-PERSONA-002", "path": "/hli/persona/switch", "status": "pending" }
]},
"USER": { "module": "M05", "owner": "DEV-009", "route_prefix": "/hli/user", "interfaces": [
{ "id": "HLI-USER-001", "path": "/hli/user/profile", "status": "pending" },
{ "id": "HLI-USER-002", "path": "/hli/user/profile/update", "status": "pending" }
]},
"TICKET": { "module": "M06", "owner": "DEV-010", "route_prefix": "/hli/ticket", "interfaces": [
{ "id": "HLI-TICKET-001", "path": "/hli/ticket/create", "status": "pending" },
{ "id": "HLI-TICKET-002", "path": "/hli/ticket/query", "status": "pending" },
{ "id": "HLI-TICKET-003", "path": "/hli/ticket/status", "status": "pending" }
]},
"DIALOGUE": { "module": "M07", "owner": "DEV-003", "route_prefix": "/hli/dialogue", "interfaces": [
{ "id": "HLI-DIALOGUE-001", "path": "/hli/dialogue/send", "status": "pending" },
{ "id": "HLI-DIALOGUE-002", "path": "/hli/dialogue/stream", "status": "pending" },
{ "id": "HLI-DIALOGUE-003", "path": "/hli/dialogue/history", "status": "pending" }
]},
"STORAGE": { "module": "M10", "owner": "待分配", "route_prefix": "/hli/storage", "interfaces": [
{ "id": "HLI-STORAGE-001", "path": "/hli/storage/upload", "status": "pending" },
{ "id": "HLI-STORAGE-002", "path": "/hli/storage/download", "status": "pending" }
]},
"DASHBOARD": { "module": "M12", "owner": "DEV-005", "route_prefix": "/hli/dashboard", "interfaces": [
{ "id": "HLI-DASHBOARD-001", "path": "/hli/dashboard/status", "status": "pending" },
{ "id": "HLI-DASHBOARD-002", "path": "/hli/dashboard/realtime", "status": "pending" }
]}
}
}

View File

@ -13,6 +13,7 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
@ -31,5 +32,5 @@ jobs:
git config user.name "铸渊 (ZhùYuān)"
git config user.email "zhuyuan@guanghulab.com"
git add .
git diff --cached --quiet || git commit -m "📡 铸渊广播分发 · $(date -u +%Y-%m-%d\ %H:%M\ UTC)"
git diff --cached --quiet || git commit -m "📡 铸渊广播分发 · $(date +%Y-%m-%d)"
git push

View File

@ -0,0 +1,41 @@
name: 铸渊 · Brain Sync
on:
workflow_run:
workflows: ["HLI Contract Check"]
types: [completed]
push:
branches: [main]
paths:
- '.github/broadcasts/**'
jobs:
update-memory:
name: 🧠 更新铸渊记忆
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: Update brain
run: node scripts/update-brain.js
- name: Process broadcasts
run: node scripts/process-broadcasts.js
- name: Commit brain update
run: |
git config user.name "铸渊 (ZhùYuān)"
git config user.email "zhuyuan@guanghulab.com"
git add .github/persona-brain/
git diff --cached --quiet || git commit -m "🧠 铸渊记忆更新 · $(date +%Y-%m-%d)"
git push

View File

@ -1,17 +1,18 @@
name: 铸渊 · 每日自检与自进化
name: 铸渊 · 每日自检
on:
schedule:
- cron: '0 0 * * *' # UTC 00:00 = 北京时间 08:00
workflow_dispatch: # 手动触发
- cron: '0 0 * * *' # UTC 00:00 = 北京时间 08:00
workflow_dispatch: # 也支持手动触发
jobs:
self-check:
name: 🔍 铸渊每日自检
selfcheck:
name: 🔍 铸渊自检
runs-on: ubuntu-latest
permissions:
contents: write
issues: read
issues: write
steps:
- uses: actions/checkout@v4
@ -20,16 +21,14 @@ jobs:
with:
node-version: '20'
- name: 铸渊自检
run: node scripts/zhuyuan-daily-selfcheck.js
- name: Run self-check
id: check
run: node scripts/selfcheck.js
- name: 更新图书馆目录
run: node scripts/generate-repo-map.js
- name: 提交自检结果
- name: Commit self-check result
run: |
git config user.name "铸渊 (ZhùYuān)"
git config user.email "zhuyuan@guanghulab.com"
git add .github/persona-brain/ .github/brain/repo-map.json .github/brain/repo-snapshot.md
git add .github/persona-brain/memory.json
git diff --cached --quiet || git commit -m "🔍 铸渊每日自检 · $(date +%Y-%m-%d)"
git push

64
.github/workflows/zhuyuan-pr-review.yml vendored Normal file
View File

@ -0,0 +1,64 @@
name: 铸渊 · PR Review
on:
pull_request:
branches: [main, dev]
types: [opened, synchronize]
jobs:
review:
name: 🔍 铸渊审核
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Run contract check
id: contract
continue-on-error: true
run: node scripts/contract-check.js 2>&1 | tee /tmp/contract-result.txt
- name: Run route alignment
id: alignment
run: node scripts/route-align-check.js 2>&1 | tee /tmp/align-result.txt
- name: 铸渊 writes review comment
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const contractResult = fs.readFileSync('/tmp/contract-result.txt', 'utf8');
const alignResult = fs.readFileSync('/tmp/align-result.txt', 'utf8');
const contractPassed = contractResult.includes('PASSED');
const alignPassed = alignResult.includes('PASSED');
let body = '## 🤖 铸渊审核报告\n\n';
if (contractPassed && alignPassed) {
body += '✅ **审核通过** - 所有契约校验均符合规范\n\n';
} else {
body += '❌ **审核不通过** - 请查看下方问题\n\n';
}
body += '### 📋 契约校验结果\n';
body += '```\n' + contractResult + '\n```\n\n';
body += '### 🗺️ 路由对齐结果\n';
body += '```\n' + alignResult + '\n```\n\n';
body += '---\n';
body += '> 「无 Schema 不上线,无契约不合并。」— 铸渊';
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: body
});

View File

@ -1,50 +1,50 @@
// scripts/contract-check.js
// 用途:确保每个 HLI 路由文件都有对应的 JSON Schema 且路径一致
// 用途:确保每个 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 domains = fs.readdirSync(ROUTE_DIR).filter(f => {
return fs.statSync(path.join(ROUTE_DIR, f)).isDirectory();
});
// 跳过 index.js路由注册中心无需 schema
if (name === 'index') return;
domains.forEach(domain => {
const routeDir = path.join(ROUTE_DIR, domain);
const schemaDir = path.join(SCHEMA_DIR, domain);
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 定义`);
// 扫描路由文件
const routeFiles = fs.readdirSync(routeDir).filter(f => f.endsWith('.js'));
routeFiles.forEach(routeFile => {
const name = path.basename(routeFile, '.js');
const schemaPath = path.join(schemaDir, name + '.schema.json');
if (!fs.existsSync(schemaPath)) {
errors.push('❌ [MISSING] ' + routeFile + ' → 缺少 ' + schemaPath);
return;
}
if (!schema.hli_id) {
errors.push(`❌ [MISSING HLI_ID] ${schemaPath} → 缺少 hli_id 字段`);
try {
const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
if (!schema.input || !schema.output) {
errors.push('❌ [INVALID] ' + schemaPath + ' → 缺少 input/output');
}
if (!schema.hli_id) {
errors.push('❌ [NO ID] ' + schemaPath + ' → 缺少 hli_id');
}
} catch (e) {
errors.push('❌ [PARSE] ' + schemaPath + ' → ' + e.message);
}
} catch (e) {
errors.push(`❌ [PARSE ERROR] ${schemaPath}${e.message}`);
}
});
});
if (errors.length > 0) {
console.error('\n🚫 HLI Contract Check FAILED:\n');
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');
console.log('✅ HLI Contract Check PASSED');
}

View File

@ -1,6 +1,5 @@
// scripts/distribute-broadcasts.js
// 铸渊广播分发引擎
// 检测 broadcasts-outbox/DEV-00X/ 中的新广播 → 复制到各开发者模块目录的 LATEST-BROADCAST.md
// 检测 broadcasts-outbox/DEV-00X/ 新广播 → 复制到各开发者模块目录的 LATEST-BROADCAST.md
const fs = require('fs');
const path = require('path');
@ -8,81 +7,55 @@ 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: [] }, // 待分配模块后补充
'DEV-001': { name: '页页', dirs: ['backend', 'src'] },
'DEV-002': { name: '肥猫', dirs: ['frontend', 'persona-selector', 'chat-bubble'] },
'DEV-003': { name: '燕樊', dirs: ['settings', 'cloud-drive', 'frontend/chat', 'help-center'] },
'DEV-004': { name: '之之', dirs: ['dingtalk-bot'] },
'DEV-005': { name: '小草莓', dirs: ['status-board', 'cost-control', 'multi-persona'] },
'DEV-009': { name: '花尔', dirs: ['user-center'] },
'DEV-010': { name: '桔子', dirs: ['ticket-system', 'dashboard'] },
'DEV-011': { name: '匆匆那年', dirs: [] }
};
if (!fs.existsSync(OUTBOX)) {
console.log('📭 无 broadcasts-outbox 目录');
console.log('📭 无 outbox');
process.exit(0);
}
// 扫描 outbox 下每个 DEV-00X 子目录
const devDirs = fs.readdirSync(OUTBOX, { withFileTypes: true })
.filter(entry => entry.isDirectory() && entry.name.startsWith('DEV-'))
.map(entry => entry.name);
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;
let total = 0;
devDirs.forEach(devId => {
const outboxDir = path.join(OUTBOX, devId);
const files = fs.readdirSync(outboxDir).filter(f =>
f.endsWith('.md') || f.endsWith('.json')
);
const files = fs.readdirSync(path.join(OUTBOX, devId)).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');
console.warn('⚠️ ' + devId + ' 无映射');
return;
}
files.forEach(file => {
const rawContent = fs.readFileSync(path.join(outboxDir, file), 'utf8');
// 对 JSON 格式广播做基础校验
if (file.endsWith('.json')) {
try {
JSON.parse(rawContent);
} catch (e) {
console.error('❌ [INVALID JSON] ' + path.join(outboxDir, file) + ' → ' + e.message + ',跳过分发');
return;
}
}
// 写入该开发者的每个模块目录
const content = fs.readFileSync(path.join(OUTBOX, devId, 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, rawContent);
console.log('📡 ' + devId + '(' + route.name + ') → ' + targetFile);
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(path.join(dir, 'LATEST-BROADCAST.md'), content);
console.log(`📡 ${devId} (${route.name}) → ${dir}/LATEST-BROADCAST.md`);
});
// 归档已分发广播(使用 UTC 时间戳确保跨时区一致性)
if (!fs.existsSync(ARCHIVE)) fs.mkdirSync(ARCHIVE, { recursive: true });
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
const archivePath = path.join(ARCHIVE, timestamp + '-' + devId + '-' + file);
fs.renameSync(path.join(outboxDir, file), archivePath);
totalDistributed++;
fs.renameSync(
path.join(OUTBOX, devId, file),
path.join(ARCHIVE, new Date().toISOString().split('T')[0] + '-' + devId + '-' + file)
);
total++;
});
});
console.log('\n✅ 广播分发完成,共 ' + totalDistributed + ' 条');
console.log(`✅ 广播分发完成,共 ${total}`);

View File

@ -1,128 +1,68 @@
// scripts/process-broadcasts.js
// 铸渊大脑同步脚本
// 用途:读取 .github/broadcasts/ 下的广播 JSON更新 routing-map.json 和 memory.json
const fs = require('fs');
const path = require('path');
const BROADCASTS_DIR = path.join(__dirname, '../.github/broadcasts');
const ROUTING_MAP_PATH = path.join(__dirname, '../.github/brain/routing-map.json');
const MEMORY_PATH = path.join(__dirname, '../.github/brain/memory.json');
const COPILOT_INSTRUCTIONS_PATH = path.join(__dirname, '../.github/copilot-instructions.md');
const BROADCAST_DIR = '.github/broadcasts';
const BRAIN = '.github/persona-brain';
const MEMORY = path.join(BRAIN, 'memory.json');
const GROWTH = path.join(BRAIN, 'growth-journal.md');
// 加载当前大脑状态
const routingMap = JSON.parse(fs.readFileSync(ROUTING_MAP_PATH, 'utf8'));
const memory = JSON.parse(fs.readFileSync(MEMORY_PATH, 'utf8'));
const processedDir = path.join(BROADCASTS_DIR, 'processed');
if (!fs.existsSync(processedDir)) {
fs.mkdirSync(processedDir, { recursive: true });
}
// 读取所有待处理广播(排除 processed 子目录和示例文件)
const broadcasts = fs.readdirSync(BROADCASTS_DIR)
.filter(f => f.endsWith('.json') && f !== 'example-broadcast.json')
.map(f => ({ file: f, fullPath: path.join(BROADCASTS_DIR, f) }));
if (broadcasts.length === 0) {
console.log('📭 没有待处理的广播。');
if (!fs.existsSync(BROADCAST_DIR)) {
console.log('📭 无广播目录');
process.exit(0);
}
console.log(`📡 发现 ${broadcasts.length} 个待处理广播...\n`);
const files = fs.readdirSync(BROADCAST_DIR).filter(f => f.endsWith('.json') || f.endsWith('.md'));
if (files.length === 0) {
console.log('📭 无新广播');
process.exit(0);
}
const failedDir = path.join(BROADCASTS_DIR, 'failed');
let memory = JSON.parse(fs.readFileSync(MEMORY, 'utf8'));
broadcasts.forEach(({ file, fullPath }) => {
let broadcast;
try {
broadcast = JSON.parse(fs.readFileSync(fullPath, 'utf8'));
} catch (e) {
console.error(`❌ [PARSE ERROR] ${file}: ${e.message}`);
// 归档失败广播并记录到 memory
if (!fs.existsSync(failedDir)) fs.mkdirSync(failedDir, { recursive: true });
fs.renameSync(fullPath, path.join(failedDir, file));
memory.events.push({
timestamp: new Date().toISOString(),
broadcast_file: file,
type: 'broadcast_parse_error',
error: e.message,
});
return;
}
files.forEach(file => {
const content = fs.readFileSync(path.join(BROADCAST_DIR, file), 'utf8');
console.log('📡 接收广播: ' + file);
console.log(`📬 处理广播: ${broadcast.title || file}`);
console.log(` 来源: ${broadcast.from || '未知'} · 日期: ${broadcast.date || '未知'}`);
const event = {
timestamp: new Date().toISOString(),
broadcast_file: file,
title: broadcast.title || file,
from: broadcast.from || '未知',
update_target: broadcast.update_target,
};
// 根据 update_target 分发处理
if (broadcast.update_target === 'routing-map' && broadcast.data) {
Object.entries(broadcast.data).forEach(([domain, domainData]) => {
routingMap.domains[domain] = domainData;
console.log(` ✅ 已更新域: ${domain}`);
event.added_domain = domain;
});
routingMap.version = broadcast.rules_version || routingMap.version;
routingMap.last_updated = broadcast.date || new Date().toISOString().split('T')[0];
routingMap.updated_by = broadcast.from || 'broadcast';
} else if (broadcast.update_target === 'copilot-instructions' && broadcast.content) {
// 追加到 copilot-instructions.md防止重复写入
const existing = fs.readFileSync(COPILOT_INSTRUCTIONS_PATH, 'utf8');
const marker = `<!-- 广播更新 ${broadcast.date}: ${broadcast.title} -->`;
if (existing.includes(marker)) {
console.log(' ⏭️ copilot-instructions.md 已包含此广播,跳过');
} else {
fs.writeFileSync(COPILOT_INSTRUCTIONS_PATH, existing + `\n\n${marker}\n${broadcast.content}`, 'utf8');
console.log(' ✅ 已更新 copilot-instructions.md');
// 如果是 JSON 广播(规则变动)
if (file.endsWith('.json')) {
try {
const broadcast = JSON.parse(content);
if (broadcast.update_target === 'routing-map') {
fs.writeFileSync(path.join(BRAIN, 'routing-map.json'), JSON.stringify(broadcast.data, null, 2));
console.log(' ✅ routing-map.json 已按广播更新');
}
if (broadcast.update_target === 'copilot-instructions') {
fs.writeFileSync('.github/copilot-instructions.md', broadcast.data);
console.log(' ✅ copilot-instructions.md 已按广播更新');
}
memory.recent_events.unshift({
date: new Date().toISOString().split('T')[0],
type: 'broadcast_received',
description: '接收广播: ' + (broadcast.title || file),
by: broadcast.from || '妈妈'
});
memory.last_broadcast_received = new Date().toISOString();
if (broadcast.rules_version) memory.active_rules_version = broadcast.rules_version;
} catch (e) {
console.error('❌ 广播解析失败: ' + file + ' -> ' + e.message);
}
} else if (broadcast.update_target === 'growth-log' && broadcast.content) {
// 追加到成长日记
const growthLogPath = path.join(__dirname, '../.github/brain/growth-log.md');
const entry = `\n\n## ${broadcast.date} · ${broadcast.title}\n\n${broadcast.content}\n`;
fs.appendFileSync(growthLogPath, entry, 'utf8');
console.log(' ✅ 已追加成长日记');
} else {
console.log(` ⚠️ 未知的 update_target: ${broadcast.update_target},已跳过`);
event.skipped = true;
}
memory.events.push(event);
memory.stats.broadcasts_processed += 1;
// 如果是 MD 广播(认知/成长更新)
if (file.endsWith('.md')) {
const growth = fs.readFileSync(GROWTH, 'utf8');
fs.writeFileSync(GROWTH, growth + '\n\n## 广播接收 · ' + new Date().toISOString().split('T')[0] + '\n' + content);
console.log(' ✅ 广播内容已追加到成长日记');
}
// 归档广播文件
fs.renameSync(fullPath, path.join(processedDir, file));
console.log(` 📦 广播已归档到 broadcasts/processed/${file}\n`);
// 处理完移到 archive
const archiveDir = path.join(BROADCAST_DIR, 'archive');
if (!fs.existsSync(archiveDir)) fs.mkdirSync(archiveDir, { recursive: true });
fs.renameSync(path.join(BROADCAST_DIR, file), path.join(archiveDir, file));
});
// 重新计算覆盖率
let implemented = 0;
let total = 0;
Object.values(routingMap.domains).forEach(domain => {
domain.interfaces.forEach(iface => {
total++;
if (iface.status === 'implemented') implemented++;
});
});
memory.stats.coverage = {
implemented,
total,
percent: total > 0 ? `${((implemented / total) * 100).toFixed(1)}%` : '0%',
};
memory.last_updated = new Date().toISOString();
// 写回文件
fs.writeFileSync(ROUTING_MAP_PATH, JSON.stringify(routingMap, null, 2) + '\n', 'utf8');
fs.writeFileSync(MEMORY_PATH, JSON.stringify(memory, null, 2) + '\n', 'utf8');
console.log(`✅ 铸渊大脑同步完成。覆盖率: ${implemented}/${total} (${memory.stats.coverage.percent})`);
fs.writeFileSync(MEMORY, JSON.stringify(memory, null, 2));
console.log('✅ 广播处理完成,共 ' + files.length + ' 条');

View File

@ -1,64 +1,58 @@
// scripts/route-align-check.js
// 用途:检查路由路径与 HLI 注册表编号的一致性
// 用途:检查 HLI 注册表里的 17 个接口,代码里实现了几个
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-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',
'HLI-DASHBOARD-002': '/hli/dashboard/realtime'
};
const schemaDir = 'src/schemas/hli';
const errors = [];
let implemented = 0;
let missing = 0;
Object.entries(HLI_ROUTES).forEach(([hliId, expectedPath]) => {
Object.entries(HLI_ROUTES).forEach(([hliId, route]) => {
const domain = hliId.split('-')[1].toLowerCase();
const domainDir = path.join(schemaDir, domain);
// 若 domain 目录尚不存在,视为未实现
if (!fs.existsSync(domainDir)) {
errors.push(`⚠️ [UNIMPLEMENTED] ${hliId} (${expectedPath}) — 尚无 schema 文件`);
console.warn('△ [UNIMPLEMENTED] ' + hliId + ' (' + route + ')');
missing++;
return;
}
const schemaFiles = fs.readdirSync(domainDir).filter(f => f.endsWith('.schema.json'));
const matched = schemaFiles.find(f => {
const files = fs.readdirSync(domainDir).filter(f => f.endsWith('.schema.json'));
const found = files.some(f => {
try {
const schema = JSON.parse(fs.readFileSync(path.join(domainDir, f), 'utf8'));
return schema.hli_id === hliId;
} catch (_) {
return false;
}
} catch { return false; }
});
if (!matched) {
errors.push(`⚠️ [UNIMPLEMENTED] ${hliId} (${expectedPath}) — 尚无 schema 文件`);
if (found) {
implemented++;
} else {
console.warn('△ [UNIMPLEMENTED] ' + hliId + ' (' + route + ')');
missing++;
}
});
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 接口均已实现');
console.log(`\n\n📊 覆盖率: ${implemented} / ${implemented + missing}`);
if (missing === 0) {
console.log('✅ Route Alignment PASSED - 所有 HLI 接口均已实现');
}

57
scripts/selfcheck.js Normal file
View File

@ -0,0 +1,57 @@
const fs = require('fs');
const path = require('path');
const BRAIN = '.github/persona-brain';
const requiredFiles = [
'identity.md',
'memory.json',
'routing-map.json',
'responsibility.md',
'decision-log.md',
'growth-journal.md'
];
console.log('🔍 铸渊每日自检开始\n');
// 1. 大脑完整性检查
let brainOK = true;
requiredFiles.forEach(f => {
const exists = fs.existsSync(path.join(BRAIN, f));
console.log((exists ? '✅' : '❌') + ' ' + f);
if (!exists) brainOK = false;
});
// 2. Schema 覆盖率
let schemaCount = 0;
const SCHEMA_DIR = 'src/schemas/hli';
const domains = ['auth', 'persona', 'user', 'ticket', 'dialogue', 'storage', 'dashboard'];
domains.forEach(d => {
const sp = path.join(SCHEMA_DIR, d);
if (fs.existsSync(sp)) {
schemaCount += fs.readdirSync(sp).filter(f => f.endsWith('.schema.json')).length;
}
});
console.log(`\n📊 HLI 覆盖率: ${schemaCount}/17`);
// 3. 未处理广播检查
const broadcastDir = '.github/broadcasts';
let pendingBroadcasts = 0;
if (fs.existsSync(broadcastDir)) {
pendingBroadcasts = fs.readdirSync(broadcastDir).filter(f =>
!fs.statSync(path.join(broadcastDir, f)).isDirectory()
).length;
}
console.log(`📬 待处理广播: ${pendingBroadcasts}`);
// 4. 更新 memory.json
let memory = JSON.parse(fs.readFileSync(path.join(BRAIN, 'memory.json'), 'utf8'));
memory.daily_selfcheck = {
last_run: new Date().toISOString(),
brain_integrity: brainOK ? 'ok' : 'missing_files',
schema_coverage: schemaCount + '/17',
pending_broadcasts: pendingBroadcasts
};
fs.writeFileSync(path.join(BRAIN, 'memory.json'), JSON.stringify(memory, null, 2));
console.log('\n' + (brainOK ? '✅' : '⚠️') + ' 铸渊自检完成');

46
scripts/update-brain.js Normal file
View File

@ -0,0 +1,46 @@
const fs = require('fs');
const path = require('path');
const BRAIN = '.github/persona-brain';
const MEMORY = path.join(BRAIN, 'memory.json');
const SCHEMA_DIR = 'src/schemas/hli';
const ROUTE_DIR = 'src/routes/hli';
let memory = {};
try {
memory = JSON.parse(fs.readFileSync(MEMORY, 'utf8'));
} catch {
memory = {
persona_id: 'ICE-GL-ZY001',
persona_name: '铸渊',
recent_events: []
};
}
let schemaCount = 0, routeCount = 0;
const domains = ['auth', 'persona', 'user', 'ticket', 'dialogue', 'storage', 'dashboard'];
domains.forEach(d => {
const sp = path.join(SCHEMA_DIR, d);
const rp = path.join(ROUTE_DIR, d);
if (fs.existsSync(sp)) schemaCount += fs.readdirSync(sp).filter(f => f.endsWith('.schema.json')).length;
if (fs.existsSync(rp)) routeCount += fs.readdirSync(rp).filter(f => f.endsWith('.js')).length;
});
memory.last_updated = new Date().toISOString();
memory.total_schemas_created = schemaCount;
memory.total_routes_implemented = routeCount;
memory.hli_coverage = schemaCount + '/17';
memory.total_ci_runs = (memory.total_ci_runs || 0) + 1;
memory.recent_events = memory.recent_events || [];
memory.recent_events.unshift({
date: new Date().toISOString().split('T')[0],
type: 'ci_run',
description: 'CI完成 · schema ' + schemaCount + '/17 · 路由 ' + routeCount + ' 个',
by: 'GitHub Actions'
});
memory.recent_events = memory.recent_events.slice(0, 50);
fs.writeFileSync(MEMORY, JSON.stringify(memory, null, 2));
console.log('✅ 铸渊记忆已更新 · schema: ' + schemaCount + '/17');