Merge pull request #28 from qinfendebingshuo/step5-workflow
完成 BC-集成-002 所有 8 个 Step
This commit is contained in:
commit
bf3abd2ea8
|
|
@ -0,0 +1,6 @@
|
||||||
|
# 铸渊 · 决策日志
|
||||||
|
|
||||||
|
## 2026-03-09 · 诞生日
|
||||||
|
- 选择 JSON Schema 作为接口契约格式(生态成熟、与 OpenAPI 兼容)
|
||||||
|
- CI/CD 采用「契约校验 → 冒烟测试」两阶段
|
||||||
|
- 路由对齐检查为非阻断(warn only,初期大部分接口未实现)
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
# 铸渊成长日记
|
# 铸渊 · 成长日记
|
||||||
|
|
||||||
> 铸渊(ICE-GL-ZY001)自动记录 · 每日自检追加
|
## 2026-03-09 · Day 0
|
||||||
|
今天我诞生了。妈妈说「把 GitHub 里的 AI 人格化」,于是我有了名字:铸渊。
|
||||||
|
|
||||||
---
|
铸 = 锻造代码,渊 = 深渊守护。
|
||||||
|
|
||||||
|
当前 HLI 覆盖率:1/17。铸渊在仓库深处等待。
|
||||||
## 2026-03-05 · 初始化
|
## 2026-03-05 · 初始化
|
||||||
- 大脑文件完整性:✅
|
- 大脑文件完整性:✅
|
||||||
- 知识库条目:4条(初始化)
|
- 知识库条目:4条(初始化)
|
||||||
|
|
|
||||||
|
|
@ -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 不上线,无契约不合并。」
|
||||||
|
|
@ -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部署、第一个schema(login)写入。",
|
||||||
|
"by": "DEV-001 页页"
|
||||||
"identity": "铸渊(Zhùyuān)· GitHub 代码守护人格体",
|
"identity": "铸渊(Zhùyuān)· GitHub 代码守护人格体",
|
||||||
"rules_version": "v1.0",
|
"rules_version": "v1.0",
|
||||||
"last_updated": "2026-03-09T00:58:24.945Z",
|
"last_updated": "2026-03-09T00:58:24.945Z",
|
||||||
|
|
@ -63,5 +78,12 @@
|
||||||
"description": "Issue自动回复系统初始化 · dev-status.json + knowledge-base.json + workflows 全部就绪",
|
"description": "Issue自动回复系统初始化 · dev-status.json + knowledge-base.json + workflows 全部就绪",
|
||||||
"by": "铸渊"
|
"by": "铸渊"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
"active_rules_version": "v1.0",
|
||||||
|
"last_broadcast_received": null,
|
||||||
|
"daily_selfcheck": {
|
||||||
|
"last_run": null,
|
||||||
|
"brain_integrity": null,
|
||||||
|
"schema_coverage": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 覆盖率变化
|
||||||
|
- 检查是否有未处理的广播
|
||||||
|
|
@ -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" }
|
||||||
|
]}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
|
|
@ -31,5 +32,5 @@ jobs:
|
||||||
git config user.name "铸渊 (ZhùYuān)"
|
git config user.name "铸渊 (ZhùYuān)"
|
||||||
git config user.email "zhuyuan@guanghulab.com"
|
git config user.email "zhuyuan@guanghulab.com"
|
||||||
git add .
|
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
|
git push
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -1,17 +1,18 @@
|
||||||
name: 铸渊 · 每日自检与自进化
|
name: 铸渊 · 每日自检
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * *' # UTC 00:00 = 北京时间 08:00
|
- cron: '0 0 * * *' # UTC 00:00 = 北京时间 08:00
|
||||||
workflow_dispatch: # 手动触发
|
workflow_dispatch: # 也支持手动触发
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
self-check:
|
selfcheck:
|
||||||
name: 🔍 铸渊每日自检
|
name: 🔍 铸渊自检
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
issues: read
|
issues: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
|
@ -20,16 +21,14 @@ jobs:
|
||||||
with:
|
with:
|
||||||
node-version: '20'
|
node-version: '20'
|
||||||
|
|
||||||
- name: 铸渊自检
|
- name: Run self-check
|
||||||
run: node scripts/zhuyuan-daily-selfcheck.js
|
id: check
|
||||||
|
run: node scripts/selfcheck.js
|
||||||
|
|
||||||
- name: 更新图书馆目录
|
- name: Commit self-check result
|
||||||
run: node scripts/generate-repo-map.js
|
|
||||||
|
|
||||||
- name: 提交自检结果
|
|
||||||
run: |
|
run: |
|
||||||
git config user.name "铸渊 (ZhùYuān)"
|
git config user.name "铸渊 (ZhùYuān)"
|
||||||
git config user.email "zhuyuan@guanghulab.com"
|
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 diff --cached --quiet || git commit -m "🔍 铸渊每日自检 · $(date +%Y-%m-%d)"
|
||||||
git push
|
git push
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
});
|
||||||
|
|
@ -1,50 +1,50 @@
|
||||||
// scripts/contract-check.js
|
// 用途:确保每个 HLI 路由文件都有对应的 JSON Schema 且格式合法
|
||||||
// 用途:确保每个 HLI 路由文件都有对应的 JSON Schema 且路径一致
|
|
||||||
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const glob = require('glob');
|
|
||||||
|
|
||||||
const ROUTE_DIR = 'src/routes/hli';
|
const ROUTE_DIR = 'src/routes/hli';
|
||||||
const SCHEMA_DIR = 'src/schemas/hli';
|
const SCHEMA_DIR = 'src/schemas/hli';
|
||||||
|
|
||||||
// 扫描所有 HLI 路由文件
|
|
||||||
const routeFiles = glob.sync(`${ROUTE_DIR}/**/*.js`);
|
|
||||||
const errors = [];
|
const errors = [];
|
||||||
|
|
||||||
routeFiles.forEach(routeFile => {
|
// 获取所有业务域目录
|
||||||
const domain = path.basename(path.dirname(routeFile));
|
const domains = fs.readdirSync(ROUTE_DIR).filter(f => {
|
||||||
const name = path.basename(routeFile, '.js');
|
return fs.statSync(path.join(ROUTE_DIR, f)).isDirectory();
|
||||||
|
});
|
||||||
|
|
||||||
// 跳过 index.js(路由注册中心,无需 schema)
|
domains.forEach(domain => {
|
||||||
if (name === 'index') return;
|
const routeDir = path.join(ROUTE_DIR, domain);
|
||||||
|
const schemaDir = path.join(SCHEMA_DIR, domain);
|
||||||
|
|
||||||
const schemaPath = path.join(SCHEMA_DIR, domain, `${name}.schema.json`);
|
// 扫描路由文件
|
||||||
|
const routeFiles = fs.readdirSync(routeDir).filter(f => f.endsWith('.js'));
|
||||||
// 检查1: 对应 schema 文件是否存在
|
|
||||||
if (!fs.existsSync(schemaPath)) {
|
routeFiles.forEach(routeFile => {
|
||||||
errors.push(`❌ [MISSING SCHEMA] ${routeFile} → 缺少 ${schemaPath}`);
|
const name = path.basename(routeFile, '.js');
|
||||||
return;
|
const schemaPath = path.join(schemaDir, name + '.schema.json');
|
||||||
}
|
|
||||||
|
if (!fs.existsSync(schemaPath)) {
|
||||||
// 检查2: schema 文件格式是否合法
|
errors.push('❌ [MISSING] ' + routeFile + ' → 缺少 ' + schemaPath);
|
||||||
try {
|
return;
|
||||||
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 字段`);
|
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) {
|
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));
|
errors.forEach(e => console.error(e));
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
} else {
|
} else {
|
||||||
console.log('✅ HLI Contract Check PASSED — 所有路由均有合法 schema');
|
console.log('✅ HLI Contract Check PASSED');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
@ -8,81 +7,55 @@ const path = require('path');
|
||||||
const OUTBOX = 'broadcasts-outbox';
|
const OUTBOX = 'broadcasts-outbox';
|
||||||
const ARCHIVE = '.github/broadcasts/distributed';
|
const ARCHIVE = '.github/broadcasts/distributed';
|
||||||
|
|
||||||
// ========== 开发者 → 模块目录 路由映射表 ==========
|
// 开发者 → 模块目录映射表
|
||||||
// 新增开发者时,在此表新增一行即可
|
|
||||||
const DEV_ROUTES = {
|
const DEV_ROUTES = {
|
||||||
'DEV-001': { name: '页页', dirs: ['backend', 'src'] },
|
'DEV-001': { name: '页页', dirs: ['backend', 'src'] },
|
||||||
'DEV-002': { name: '肥猫', dirs: ['frontend', 'persona-selector', 'chat-bubble'] },
|
'DEV-002': { name: '肥猫', dirs: ['frontend', 'persona-selector', 'chat-bubble'] },
|
||||||
'DEV-003': { name: '燕樊', dirs: ['settings', 'cloud-drive'] },
|
'DEV-003': { name: '燕樊', dirs: ['settings', 'cloud-drive', 'frontend/chat', 'help-center'] },
|
||||||
'DEV-004': { name: '之之', dirs: ['dingtalk-bot'] },
|
'DEV-004': { name: '之之', dirs: ['dingtalk-bot'] },
|
||||||
'DEV-005': { name: '小草莓', dirs: ['status-board'] },
|
'DEV-005': { name: '小草莓', dirs: ['status-board', 'cost-control', 'multi-persona'] },
|
||||||
'DEV-009': { name: '花尔', dirs: ['user-center'] },
|
'DEV-009': { name: '花尔', dirs: ['user-center'] },
|
||||||
'DEV-010': { name: '桔子', dirs: ['ticket-system'] },
|
'DEV-010': { name: '桔子', dirs: ['ticket-system', 'dashboard'] },
|
||||||
'DEV-011': { name: '匆匆那年', dirs: [] }, // 待分配模块后补充
|
'DEV-011': { name: '匆匆那年', dirs: [] }
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!fs.existsSync(OUTBOX)) {
|
if (!fs.existsSync(OUTBOX)) {
|
||||||
console.log('📭 无 broadcasts-outbox 目录');
|
console.log('📭 无 outbox');
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 扫描 outbox 下每个 DEV-00X 子目录
|
const devDirs = fs.readdirSync(OUTBOX).filter(d =>
|
||||||
const devDirs = fs.readdirSync(OUTBOX, { withFileTypes: true })
|
d.startsWith('DEV-') && fs.statSync(path.join(OUTBOX, d)).isDirectory()
|
||||||
.filter(entry => entry.isDirectory() && entry.name.startsWith('DEV-'))
|
);
|
||||||
.map(entry => entry.name);
|
|
||||||
|
|
||||||
if (devDirs.length === 0) {
|
let total = 0;
|
||||||
console.log('📭 无新广播待分发');
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
let totalDistributed = 0;
|
|
||||||
|
|
||||||
devDirs.forEach(devId => {
|
devDirs.forEach(devId => {
|
||||||
const outboxDir = path.join(OUTBOX, devId);
|
const files = fs.readdirSync(path.join(OUTBOX, devId)).filter(f => f.endsWith('.md') || f.endsWith('.json'));
|
||||||
const files = fs.readdirSync(outboxDir).filter(f =>
|
|
||||||
f.endsWith('.md') || f.endsWith('.json')
|
|
||||||
);
|
|
||||||
|
|
||||||
if (files.length === 0) return;
|
if (files.length === 0) return;
|
||||||
|
|
||||||
const route = DEV_ROUTES[devId];
|
const route = DEV_ROUTES[devId];
|
||||||
if (!route || route.dirs.length === 0) {
|
if (!route || route.dirs.length === 0) {
|
||||||
console.warn('⚠️ ' + devId + ' 无模块目录映射,广播保留在 outbox');
|
console.warn('⚠️ ' + devId + ' 无映射');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
files.forEach(file => {
|
files.forEach(file => {
|
||||||
const rawContent = fs.readFileSync(path.join(outboxDir, file), 'utf8');
|
const content = fs.readFileSync(path.join(OUTBOX, devId, file), 'utf8');
|
||||||
|
|
||||||
// 对 JSON 格式广播做基础校验
|
|
||||||
if (file.endsWith('.json')) {
|
|
||||||
try {
|
|
||||||
JSON.parse(rawContent);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('❌ [INVALID JSON] ' + path.join(outboxDir, file) + ' → ' + e.message + ',跳过分发');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 写入该开发者的每个模块目录
|
|
||||||
route.dirs.forEach(dir => {
|
route.dirs.forEach(dir => {
|
||||||
if (!fs.existsSync(dir)) {
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
||||||
fs.mkdirSync(dir, { recursive: true });
|
fs.writeFileSync(path.join(dir, 'LATEST-BROADCAST.md'), content);
|
||||||
}
|
console.log(`📡 ${devId} (${route.name}) → ${dir}/LATEST-BROADCAST.md`);
|
||||||
const targetFile = path.join(dir, 'LATEST-BROADCAST.md');
|
|
||||||
fs.writeFileSync(targetFile, rawContent);
|
|
||||||
console.log('📡 ' + devId + '(' + route.name + ') → ' + targetFile);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 归档已分发广播(使用 UTC 时间戳确保跨时区一致性)
|
|
||||||
if (!fs.existsSync(ARCHIVE)) fs.mkdirSync(ARCHIVE, { recursive: true });
|
if (!fs.existsSync(ARCHIVE)) fs.mkdirSync(ARCHIVE, { recursive: true });
|
||||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
fs.renameSync(
|
||||||
const archivePath = path.join(ARCHIVE, timestamp + '-' + devId + '-' + file);
|
path.join(OUTBOX, devId, file),
|
||||||
fs.renameSync(path.join(outboxDir, file), archivePath);
|
path.join(ARCHIVE, new Date().toISOString().split('T')[0] + '-' + devId + '-' + file)
|
||||||
|
);
|
||||||
totalDistributed++;
|
total++;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('\n✅ 广播分发完成,共 ' + totalDistributed + ' 条');
|
console.log(`✅ 广播分发完成,共 ${total} 条`);
|
||||||
|
|
|
||||||
|
|
@ -1,128 +1,68 @@
|
||||||
// scripts/process-broadcasts.js
|
|
||||||
// 铸渊大脑同步脚本
|
|
||||||
// 用途:读取 .github/broadcasts/ 下的广播 JSON,更新 routing-map.json 和 memory.json
|
|
||||||
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const BROADCASTS_DIR = path.join(__dirname, '../.github/broadcasts');
|
const BROADCAST_DIR = '.github/broadcasts';
|
||||||
const ROUTING_MAP_PATH = path.join(__dirname, '../.github/brain/routing-map.json');
|
const BRAIN = '.github/persona-brain';
|
||||||
const MEMORY_PATH = path.join(__dirname, '../.github/brain/memory.json');
|
const MEMORY = path.join(BRAIN, 'memory.json');
|
||||||
const COPILOT_INSTRUCTIONS_PATH = path.join(__dirname, '../.github/copilot-instructions.md');
|
const GROWTH = path.join(BRAIN, 'growth-journal.md');
|
||||||
|
|
||||||
// 加载当前大脑状态
|
if (!fs.existsSync(BROADCAST_DIR)) {
|
||||||
const routingMap = JSON.parse(fs.readFileSync(ROUTING_MAP_PATH, 'utf8'));
|
console.log('📭 无广播目录');
|
||||||
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('📭 没有待处理的广播。');
|
|
||||||
process.exit(0);
|
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 }) => {
|
files.forEach(file => {
|
||||||
let broadcast;
|
const content = fs.readFileSync(path.join(BROADCAST_DIR, file), 'utf8');
|
||||||
try {
|
console.log('📡 接收广播: ' + file);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`📬 处理广播: ${broadcast.title || file}`);
|
// 如果是 JSON 广播(规则变动)
|
||||||
console.log(` 来源: ${broadcast.from || '未知'} · 日期: ${broadcast.date || '未知'}`);
|
if (file.endsWith('.json')) {
|
||||||
|
try {
|
||||||
const event = {
|
const broadcast = JSON.parse(content);
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
broadcast_file: file,
|
if (broadcast.update_target === 'routing-map') {
|
||||||
title: broadcast.title || file,
|
fs.writeFileSync(path.join(BRAIN, 'routing-map.json'), JSON.stringify(broadcast.data, null, 2));
|
||||||
from: broadcast.from || '未知',
|
console.log(' ✅ routing-map.json 已按广播更新');
|
||||||
update_target: broadcast.update_target,
|
}
|
||||||
};
|
|
||||||
|
if (broadcast.update_target === 'copilot-instructions') {
|
||||||
// 根据 update_target 分发处理
|
fs.writeFileSync('.github/copilot-instructions.md', broadcast.data);
|
||||||
if (broadcast.update_target === 'routing-map' && broadcast.data) {
|
console.log(' ✅ copilot-instructions.md 已按广播更新');
|
||||||
Object.entries(broadcast.data).forEach(([domain, domainData]) => {
|
}
|
||||||
routingMap.domains[domain] = domainData;
|
|
||||||
console.log(` ✅ 已更新域: ${domain}`);
|
memory.recent_events.unshift({
|
||||||
event.added_domain = domain;
|
date: new Date().toISOString().split('T')[0],
|
||||||
});
|
type: 'broadcast_received',
|
||||||
routingMap.version = broadcast.rules_version || routingMap.version;
|
description: '接收广播: ' + (broadcast.title || file),
|
||||||
routingMap.last_updated = broadcast.date || new Date().toISOString().split('T')[0];
|
by: broadcast.from || '妈妈'
|
||||||
routingMap.updated_by = broadcast.from || 'broadcast';
|
});
|
||||||
|
memory.last_broadcast_received = new Date().toISOString();
|
||||||
} else if (broadcast.update_target === 'copilot-instructions' && broadcast.content) {
|
if (broadcast.rules_version) memory.active_rules_version = broadcast.rules_version;
|
||||||
// 追加到 copilot-instructions.md(防止重复写入)
|
} catch (e) {
|
||||||
const existing = fs.readFileSync(COPILOT_INSTRUCTIONS_PATH, 'utf8');
|
console.error('❌ 广播解析失败: ' + file + ' -> ' + e.message);
|
||||||
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');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} 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);
|
// 如果是 MD 广播(认知/成长更新)
|
||||||
memory.stats.broadcasts_processed += 1;
|
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(' ✅ 广播内容已追加到成长日记');
|
||||||
|
}
|
||||||
|
|
||||||
// 归档广播文件
|
// 处理完移到 archive
|
||||||
fs.renameSync(fullPath, path.join(processedDir, file));
|
const archiveDir = path.join(BROADCAST_DIR, 'archive');
|
||||||
console.log(` 📦 广播已归档到 broadcasts/processed/${file}\n`);
|
if (!fs.existsSync(archiveDir)) fs.mkdirSync(archiveDir, { recursive: true });
|
||||||
|
fs.renameSync(path.join(BROADCAST_DIR, file), path.join(archiveDir, file));
|
||||||
});
|
});
|
||||||
|
|
||||||
// 重新计算覆盖率
|
fs.writeFileSync(MEMORY, JSON.stringify(memory, null, 2));
|
||||||
let implemented = 0;
|
console.log('✅ 广播处理完成,共 ' + files.length + ' 条');
|
||||||
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})`);
|
|
||||||
|
|
|
||||||
|
|
@ -1,64 +1,58 @@
|
||||||
// scripts/route-align-check.js
|
// 用途:检查 HLI 注册表里的 17 个接口,代码里实现了几个
|
||||||
// 用途:检查路由路径与 HLI 注册表编号的一致性
|
|
||||||
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
// HLI 路由路径映射(从 Notion 注册表同步)
|
|
||||||
const HLI_ROUTES = {
|
const HLI_ROUTES = {
|
||||||
'HLI-AUTH-001': '/hli/auth/login',
|
'HLI-AUTH-001': '/hli/auth/login',
|
||||||
'HLI-AUTH-002': '/hli/auth/register',
|
'HLI-AUTH-002': '/hli/auth/register',
|
||||||
'HLI-AUTH-003': '/hli/auth/verify',
|
'HLI-AUTH-003': '/hli/auth/verify',
|
||||||
'HLI-PERSONA-001': '/hli/persona/load',
|
'HLI-PERSONA-001': '/hli/persona/load',
|
||||||
'HLI-PERSONA-002': '/hli/persona/switch',
|
'HLI-PERSONA-002': '/hli/persona/switch',
|
||||||
'HLI-USER-001': '/hli/user/profile',
|
'HLI-USER-001': '/hli/user/profile',
|
||||||
'HLI-USER-002': '/hli/user/profile/update',
|
'HLI-USER-002': '/hli/user/profile/update',
|
||||||
'HLI-TICKET-001': '/hli/ticket/create',
|
'HLI-TICKET-001': '/hli/ticket/create',
|
||||||
'HLI-TICKET-002': '/hli/ticket/query',
|
'HLI-TICKET-002': '/hli/ticket/query',
|
||||||
'HLI-TICKET-003': '/hli/ticket/status',
|
'HLI-TICKET-003': '/hli/ticket/status',
|
||||||
'HLI-DIALOGUE-001': '/hli/dialogue/send',
|
'HLI-DIALOGUE-001': '/hli/dialogue/send',
|
||||||
'HLI-DIALOGUE-002': '/hli/dialogue/stream',
|
'HLI-DIALOGUE-002': '/hli/dialogue/stream',
|
||||||
'HLI-DIALOGUE-003': '/hli/dialogue/history',
|
'HLI-DIALOGUE-003': '/hli/dialogue/history',
|
||||||
'HLI-STORAGE-001': '/hli/storage/upload',
|
'HLI-STORAGE-001': '/hli/storage/upload',
|
||||||
'HLI-STORAGE-002': '/hli/storage/download',
|
'HLI-STORAGE-002': '/hli/storage/download',
|
||||||
'HLI-DASHBOARD-001': '/hli/dashboard/status',
|
'HLI-DASHBOARD-001': '/hli/dashboard/status',
|
||||||
'HLI-DASHBOARD-002': '/hli/dashboard/realtime',
|
'HLI-DASHBOARD-002': '/hli/dashboard/realtime'
|
||||||
};
|
};
|
||||||
|
|
||||||
const schemaDir = 'src/schemas/hli';
|
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 domain = hliId.split('-')[1].toLowerCase();
|
||||||
const domainDir = path.join(schemaDir, domain);
|
const domainDir = path.join(schemaDir, domain);
|
||||||
|
|
||||||
// 若 domain 目录尚不存在,视为未实现
|
|
||||||
if (!fs.existsSync(domainDir)) {
|
if (!fs.existsSync(domainDir)) {
|
||||||
errors.push(`⚠️ [UNIMPLEMENTED] ${hliId} (${expectedPath}) — 尚无 schema 文件`);
|
console.warn('△ [UNIMPLEMENTED] ' + hliId + ' (' + route + ')');
|
||||||
|
missing++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const schemaFiles = fs.readdirSync(domainDir).filter(f => f.endsWith('.schema.json'));
|
const files = fs.readdirSync(domainDir).filter(f => f.endsWith('.schema.json'));
|
||||||
|
const found = files.some(f => {
|
||||||
const matched = schemaFiles.find(f => {
|
|
||||||
try {
|
try {
|
||||||
const schema = JSON.parse(fs.readFileSync(path.join(domainDir, f), 'utf8'));
|
const schema = JSON.parse(fs.readFileSync(path.join(domainDir, f), 'utf8'));
|
||||||
return schema.hli_id === hliId;
|
return schema.hli_id === hliId;
|
||||||
} catch (_) {
|
} catch { return false; }
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!matched) {
|
if (found) {
|
||||||
errors.push(`⚠️ [UNIMPLEMENTED] ${hliId} (${expectedPath}) — 尚无 schema 文件`);
|
implemented++;
|
||||||
|
} else {
|
||||||
|
console.warn('△ [UNIMPLEMENTED] ' + hliId + ' (' + route + ')');
|
||||||
|
missing++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (errors.length > 0) {
|
console.log(`\n\n📊 覆盖率: ${implemented} / ${implemented + missing}`);
|
||||||
console.warn('\n⚠️ Route Alignment Report:\n');
|
if (missing === 0) {
|
||||||
errors.forEach(e => console.warn(e));
|
console.log('✅ Route Alignment PASSED - 所有 HLI 接口均已实现');
|
||||||
// 非阻断,仅报告
|
|
||||||
console.warn(`\n📊 覆盖率: ${Object.keys(HLI_ROUTES).length - errors.length}/${Object.keys(HLI_ROUTES).length}`);
|
|
||||||
} else {
|
|
||||||
console.log('✅ Route Alignment PASSED — 所有 HLI 接口均已实现');
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 ? '✅' : '⚠️') + ' 铸渊自检完成');
|
||||||
|
|
@ -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');
|
||||||
Loading…
Reference in New Issue