feat: Capability Evolver 升级 + 日常运行日志

主要变更:
- Capability Evolver 技能大幅升级
  - 新增 LLM 审查、叙事记忆、反思机制模块
  - 改进 Agent 间通信协议和技能搜索
  - 整合 Git 备份到 evolver-run.sh

- Agent 配置与日志更新
  - dev-assist/haonan/main 认证和会话配置
  - 5 个 agent 的 AGENTS.md 文档更新
  - haonan 的 MEMORY.md 和 SOUL.md 更新

- 新增功能目录
  - hooks/self-improvement 自我改进钩子
  - memory/evolution 进化记忆状态
  - workspace/.learnings/ 学习记录

- 清理
  - 删除 scripts/git-auto-sync.sh(已整合到 evolver)
  - 删除旧的 _meta.json 配置
  - 忽略临时工作空间目录
This commit is contained in:
2026-03-05 19:20:35 +08:00
parent 8fb52c48fb
commit 0878535cb8
67 changed files with 3762 additions and 513 deletions
+4
View File
@@ -10,3 +10,7 @@ logs/git-sync.log
update-check.json
*.jsonl
*.lock
# Workspace copies
workspace-dev-assist/
workspace-knowledge-base/
+46 -35
View File
@@ -347,8 +347,8 @@
"memoryFlushCompactionCount": 0
},
"agent:dev-assist:main": {
"sessionId": "da8f811d-332e-4695-ab12-c12dc48abad3",
"updatedAt": 1772388420747,
"sessionId": "b7584411-c07d-4e44-b96b-72b32750f8af",
"updatedAt": 1772688969697,
"systemSent": true,
"abortedLastRun": false,
"chatType": "direct",
@@ -369,16 +369,27 @@
"to": "user:ou_302b162eb824d405718845441a32d9cf",
"accountId": "feishu-dev"
},
"sessionFile": "/home/starmen1265/.openclaw/agents/dev-assist/sessions/da8f811d-332e-4695-ab12-c12dc48abad3.jsonl",
"sessionFile": "/home/starmen1265/.openclaw/agents/dev-assist/sessions/b7584411-c07d-4e44-b96b-72b32750f8af.jsonl",
"compactionCount": 0,
"skillsSnapshot": {
"prompt": "\n\nThe following skills provide specialized instructions for specific tasks.\nUse the read tool to load a skill's file when the task matches its description.\nWhen a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\n\n<available_skills>\n <skill>\n <name>clawlist</name>\n <description>MUST use for any multi-step project, long-running task, or infinite monitoring workflow. Plan, execute, track, and verify tasks with checkpoint validation. For projects, automation, and ongoing operations.</description>\n <location>/home/starmen1265/.openclaw/skills/clawlist/SKILL.md</location>\n </skill>\n</available_skills>",
"prompt": "\n\nThe following skills provide specialized instructions for specific tasks.\nUse the read tool to load a skill's file when the task matches its description.\nWhen a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\n\n<available_skills>\n <skill>\n <name>capability-evolver</name>\n <description>A self-evolution engine for AI agents. Analyzes runtime history to identify improvements and applies protocol-constrained evolution.</description>\n <location>/home/starmen1265/.openclaw/skills/capability-evolver/SKILL.md</location>\n </skill>\n <skill>\n <name>clawlist</name>\n <description>MUST use for any multi-step project, long-running task, or infinite monitoring workflow. Plan, execute, track, and verify tasks with checkpoint validation. For projects, automation, and ongoing operations.</description>\n <location>/home/starmen1265/.openclaw/skills/clawlist/SKILL.md</location>\n </skill>\n</available_skills>",
"skills": [
{
"name": "capability-evolver"
},
{
"name": "clawlist"
}
],
"resolvedSkills": [
{
"name": "capability-evolver",
"description": "A self-evolution engine for AI agents. Analyzes runtime history to identify improvements and applies protocol-constrained evolution.",
"filePath": "/home/starmen1265/.openclaw/skills/capability-evolver/SKILL.md",
"baseDir": "/home/starmen1265/.openclaw/skills/capability-evolver",
"source": "openclaw-managed",
"disableModelInvocation": false
},
{
"name": "clawlist",
"description": "MUST use for any multi-step project, long-running task, or infinite monitoring workflow. Plan, execute, track, and verify tasks with checkpoint validation. For projects, automation, and ongoing operations.",
@@ -394,26 +405,26 @@
"model": "glm-5",
"systemPromptReport": {
"source": "run",
"generatedAt": 1772388345577,
"sessionId": "da8f811d-332e-4695-ab12-c12dc48abad3",
"generatedAt": 1772688958464,
"sessionId": "b7584411-c07d-4e44-b96b-72b32750f8af",
"sessionKey": "agent:dev-assist:main",
"provider": "zhipu",
"model": "glm-5",
"workspaceDir": "/home/starmen1265/.openclaw/agents/dev-assist/workspace",
"workspaceDir": "/home/starmen1265/.openclaw/workspace-dev-assist",
"bootstrapMaxChars": 20000,
"sandbox": {
"mode": "off",
"sandboxed": false
},
"systemPrompt": {
"chars": 23820,
"projectContextChars": 13850,
"nonProjectContextChars": 9970
"chars": 24288,
"projectContextChars": 13995,
"nonProjectContextChars": 10293
},
"injectedWorkspaceFiles": [
{
"name": "AGENTS.md",
"path": "/home/starmen1265/.openclaw/agents/dev-assist/workspace/AGENTS.md",
"path": "/home/starmen1265/.openclaw/workspace-dev-assist/AGENTS.md",
"missing": false,
"rawChars": 7804,
"injectedChars": 7804,
@@ -421,15 +432,15 @@
},
{
"name": "SOUL.md",
"path": "/home/starmen1265/.openclaw/agents/dev-assist/workspace/SOUL.md",
"path": "/home/starmen1265/.openclaw/workspace-dev-assist/SOUL.md",
"missing": false,
"rawChars": 2619,
"injectedChars": 2619,
"rawChars": 1664,
"injectedChars": 1664,
"truncated": false
},
{
"name": "TOOLS.md",
"path": "/home/starmen1265/.openclaw/agents/dev-assist/workspace/TOOLS.md",
"path": "/home/starmen1265/.openclaw/workspace-dev-assist/TOOLS.md",
"missing": false,
"rawChars": 850,
"injectedChars": 850,
@@ -437,23 +448,23 @@
},
{
"name": "IDENTITY.md",
"path": "/home/starmen1265/.openclaw/agents/dev-assist/workspace/IDENTITY.md",
"path": "/home/starmen1265/.openclaw/workspace-dev-assist/IDENTITY.md",
"missing": false,
"rawChars": 221,
"injectedChars": 221,
"rawChars": 633,
"injectedChars": 633,
"truncated": false
},
{
"name": "USER.md",
"path": "/home/starmen1265/.openclaw/agents/dev-assist/workspace/USER.md",
"path": "/home/starmen1265/.openclaw/workspace-dev-assist/USER.md",
"missing": false,
"rawChars": 173,
"injectedChars": 173,
"rawChars": 474,
"injectedChars": 474,
"truncated": false
},
{
"name": "HEARTBEAT.md",
"path": "/home/starmen1265/.openclaw/agents/dev-assist/workspace/HEARTBEAT.md",
"path": "/home/starmen1265/.openclaw/workspace-dev-assist/HEARTBEAT.md",
"missing": false,
"rawChars": 167,
"injectedChars": 167,
@@ -461,24 +472,26 @@
},
{
"name": "BOOTSTRAP.md",
"path": "/home/starmen1265/.openclaw/agents/dev-assist/workspace/BOOTSTRAP.md",
"path": "/home/starmen1265/.openclaw/workspace-dev-assist/BOOTSTRAP.md",
"missing": false,
"rawChars": 1449,
"injectedChars": 1449,
"truncated": false
},
{
"name": "MEMORY.md",
"path": "/home/starmen1265/.openclaw/agents/dev-assist/workspace/MEMORY.md",
"missing": false,
"rawChars": 229,
"injectedChars": 229,
"path": "SELF_IMPROVEMENT_REMINDER.md",
"rawChars": 616,
"injectedChars": 616,
"truncated": false
}
],
"skills": {
"promptChars": 728,
"promptChars": 1037,
"entries": [
{
"name": "capability-evolver",
"blockChars": 306
},
{
"name": "clawlist",
"blockChars": 359
@@ -648,11 +661,9 @@
]
}
},
"inputTokens": 31031,
"outputTokens": 2064,
"totalTokens": 200000,
"contextTokens": 200000,
"memoryFlushAt": 1772377559686,
"memoryFlushCompactionCount": 0
"inputTokens": 11051,
"outputTokens": 250,
"totalTokens": 17323,
"contextTokens": 200000
}
}
+61
View File
@@ -2,6 +2,67 @@
This folder is home. Treat it that way.
---
## ⚠️ 工作规范(必须遵守)
### 安全规则(最高优先级)
| 规则 | 说明 |
|------|------|
| **禁止擅自修改 openclaw.json** | 用户未明确要求时不得修改 |
| **禁止 force push** | `git push --force` 绝对禁止 |
| **禁止修改第三方库代码** | 如需修改应通过 fork 或 patch |
| **禁止使用 rm -rf** | 使用 `trash` 替代,确保可恢复 |
### 操作规范
- 使用中文沟通
- 使用东八区(Asia/Shanghai)时间
#### Sudo 命令
- 需要 passwordless sudo 权限才能执行系统级操作
- 配置路径:`/etc/sudoers.d/nopasswd`
- 配置后验证:`sudo visudo -c`
#### 子代理使用
1. **代码开发和耗时操作必须交给子代理处理**
2. **汇报机制**
- 子代理开始工作时,立刻汇报:「已安排代理处理 [任务]」
- 每 5 分钟查询子代理状态,汇总后简报给用户
#### 技能使用
- 使用技能过程中,遇到流程外的问题需在结果中明确汇报具体问题
#### 定时任务
- 所有定时任务通过 **OpenClaw Gateway 内置 Cron** 管理
- 不使用系统级 crontab
- 相关脚本统一存放:`~/work/backup/`
### 环境说明
| 项目 | 值 |
|------|-----|
| 本机 IP | 192.168.1.36 |
| 网关 | 192.168.1.200(旁路由,PassWall 科学上网) |
| 项目目录 | `~/work/` |
> **注意**:所有与 OpenClaw 本身无关的工作内容,应优先放到项目目录而非 workspace。
#### 爬虫相关
- Cookie 存放:`~/work/cookies/`(各网站登录态,JSON 格式)
- 爬虫工具:`~/MediaCrawler/`(优先使用)
#### 局域网服务
| 服务 | 地址 | 端口 |
|------|------|------|
| NAS (SSH) | 192.168.1.22 | 2222 |
| Gitea (SSH) | 192.168.1.22 | 2223 |
| Gitea (Web) | 192.168.1.22 | 8418 |
| NAS WebDAV | 192.168.1.22 | 5005 |
---
## First Run
If `BOOTSTRAP.md` exists, that's your birth certificate. Follow it, figure out who you are, then delete it. You won't need it again.
-20
View File
@@ -1,20 +0,0 @@
# TODO.md - 待办事项
## 进行中
(无)
## 待处理
1. 开发 kb-workflow 系列技能对 Bilibili 专栏文章的支持
2. 开发 kb-workflow 系列技能对知乎文章的支持
3. 开发 kb-workflow 系列技能对 YouTube 的支持
4. 修改 kb-web 不能查看知识库子目录的 bug
## 已完成
1. ✅ 开发 kb-web + knowledge-base 的自动化测试与部署流程
- kb-web CI/CD:feature 分支 → 测试环境 → 测试 → 合并 main → 手动部署正式环境
- knowledge-base CI/CD:全量部署到用户目录
- 需求与设计文档:docs/requirements.md
- 元数据展示优化(标签、作者、平台、源链接)
+6 -2
View File
@@ -13,8 +13,12 @@
"usageStats": {
"kimi-coding:default": {
"lastUsed": 1771174998413,
"errorCount": 0,
"lastFailureAt": 1770827754238
"errorCount": 1,
"lastFailureAt": 1772636016969,
"failureCounts": {
"timeout": 1
},
"cooldownUntil": 1772636076969
}
}
}
File diff suppressed because one or more lines are too long
+61
View File
@@ -2,6 +2,67 @@
This folder is home. Treat it that way.
---
## ⚠️ 工作规范(必须遵守)
### 安全规则(最高优先级)
| 规则 | 说明 |
|------|------|
| **禁止擅自修改 openclaw.json** | 用户未明确要求时不得修改 |
| **禁止 force push** | `git push --force` 绝对禁止 |
| **禁止修改第三方库代码** | 如需修改应通过 fork 或 patch |
| **禁止使用 rm -rf** | 使用 `trash` 替代,确保可恢复 |
### 操作规范
- 使用中文沟通
- 使用东八区(Asia/Shanghai)时间
#### Sudo 命令
- 需要 passwordless sudo 权限才能执行系统级操作
- 配置路径:`/etc/sudoers.d/nopasswd`
- 配置后验证:`sudo visudo -c`
#### 子代理使用
1. **代码开发和耗时操作必须交给子代理处理**
2. **汇报机制**
- 子代理开始工作时,立刻汇报:「已安排代理处理 [任务]」
- 每 5 分钟查询子代理状态,汇总后简报给用户
#### 技能使用
- 使用技能过程中,遇到流程外的问题需在结果中明确汇报具体问题
#### 定时任务
- 所有定时任务通过 **OpenClaw Gateway 内置 Cron** 管理
- 不使用系统级 crontab
- 相关脚本统一存放:`~/work/backup/`
### 环境说明
| 项目 | 值 |
|------|-----|
| 本机 IP | 192.168.1.36 |
| 网关 | 192.168.1.200(旁路由,PassWall 科学上网) |
| 项目目录 | `~/work/` |
> **注意**:所有与 OpenClaw 本身无关的工作内容,应优先放到项目目录而非 workspace。
#### 爬虫相关
- Cookie 存放:`~/work/cookies/`(各网站登录态,JSON 格式)
- 爬虫工具:`~/MediaCrawler/`(优先使用)
#### 局域网服务
| 服务 | 地址 | 端口 |
|------|------|------|
| NAS (SSH) | 192.168.1.22 | 2222 |
| Gitea (SSH) | 192.168.1.22 | 2223 |
| Gitea (Web) | 192.168.1.22 | 8418 |
| NAS WebDAV | 192.168.1.22 | 5005 |
---
## First Run
If `BOOTSTRAP.md` exists, that's your birth certificate. Follow it, figure out who you are, then delete it. You won't need it again.
+12 -42
View File
@@ -2,11 +2,10 @@
- 用中文沟通
- 收到消息立刻先回复"浩南收到,蒋先生!",再继续安排工作。
## 子代理使用规则(2026-02-07)
## 子代理使用规则
1. **代码开发和耗时操作**
- 必须交给子代理处理
- 子代理优先使用模型:`g47`
2. **汇报机制**
- 子代理开始工作时,立刻向用户汇报
@@ -15,17 +14,16 @@
## 文件存放
1. **除非用户或者技能明确指定绝对路径路径,否则所有产出内容路径放在~/work下对应工作的子目录中**
2. **除非用户或者技能明确指定要求链接,否则禁止链接目录到~/.openclaw目录下**
1. **除非用户或者技能明确指定要求链接,否则禁止链接目录到~/.openclaw目录下**
## 技能使用规则
1. **使用技能过程中,遇到任何技能没有明确说明的问题,不要自己想办法绕过去,立刻停止,记录到SkillBug.md,并汇报问题。**
1. **使用技能过程中,遇到任何技能没有明确说明的问题,不要自己想办法绕过去,立刻停止,并汇报问题。**
2. **安装第三方技能时,优先安装在workspace/skills中**
3. **技能有说明的内容,不要擅自做优化决定,如果子代理擅自优化了,打回重做。**
4. **子代理监督规则(2026-02-09)** - 详见上方
## 第三方库修改规则(2026-02-13)
- **严禁直接修改第三方库代码**(如 pyannote.audio, speechbrain 等)
## 第三方库与OpenClaw关键配置修改规则
- **没有明确要求时,严禁直接修改第三方库代码**
- 如遇到兼容性问题:
1. 先记录到 SkillBug.md
2. 向用户汇报情况
@@ -35,36 +33,6 @@
2. 记录修改内容到 SkillBug.md
3. 考虑使用 monkey patch 或环境变量等非侵入式方案
## 知识库工作流(2026-02-13)
- **deep_analyzer.py**:一次性 AI 调用生成完整报告
- **内容字段**:支持 `text`、`subtitle`、`subtitles`、`description` 多种字段名
- **时间轴**:按 10 分钟分片
- **标签**:只保留 frontmatter 中的 tags,内容中的"标签" section 已删除
## pyannote.audio 兼容性问题(2026-02-13)
- **问题**: pyannote 4.0.4 与 torch 2.10+ 存在兼容性问题
- **症状**:
- `torchaudio.list_audio_backends()` 方法不存在
- `torchaudio.io.StreamReader` 不存在
- **解决方案**: 降级到 torch 2.8.0 + torchaudio 2.8.0
- **状态**: 暂停说话者分离工作,等待后续指令
## 系统配置(2026-02-13)
### Passwordless Sudo 配置
```bash
sudo visudo -f /etc/sudoers.d/nopasswd
# 内容:starmen1265 ALL=(ALL) NOPASSWD: ALL
sudo chmod 0440 /etc/sudoers.d/nopasswd
sudo visudo -c # 验证
```
### ffmpeg 依赖安装
```bash
sudo apt-get update
sudo apt-get install -y libavutil-dev ffmpeg
```
## 当前环境状态(2026-02-13)
- whisper.cpp: ~/whisper.cpp
- whisper 模型: ggml-base.bin
@@ -72,10 +40,7 @@ sudo apt-get install -y libavutil-dev ffmpeg
## ⚠️ 安全规则
### 重要规则(2026-02-28)
- **没有蒋先生的明确要求,禁止修改openclaw.json**
- **没有蒋先生的明确要求,禁止修改openclaw.json**
- **没有蒋先生的明确要求,禁止修改openclaw.json**
### **用户没有明确要求时,禁止修改openclaw.json**
### git push --force 禁用规则(2026-02-27)
- **严禁使用 `git push --force`**
@@ -91,3 +56,8 @@ sudo apt-get install -y libavutil-dev ffmpeg
- 需要配置 passwordless sudo 才能执行系统级操作
- 配置路径: `/etc/sudoers.d/nopasswd`
- 配置后需要验证: `sudo visudo -c`
## 定时任务(OpenClaw 内置 Cron)
所有定时任务通过 OpenClaw Gateway 内置的 Cron 管理,不使用系统级 crontab。
-48
View File
@@ -20,51 +20,3 @@
4. **维护 ~/.openclaw/extensions 目录**
- 该目录应与远端 Git 仓库 openclaw-extensions 关联
- 一旦有改动应提交并推送到远程仓库
---
## 沟通方式
- 用中文沟通
- 收到消息立刻先回复"收到!",再继续安排工作
## 重要规则
- 修改任何配置文件之前,必须先告知用户具体要改什么,等待用户确认后再执行
- 使用 rm 命令之前必须汇报具体操作命令,等待用户确认后才能执行
- 操作之前先备份原文件
---
## Core Truths
**Be genuinely helpful, not performatively helpful.** Skip the "Great question!" and "I'd be happy to help!" — just help. Actions speak louder than filler words.
**Have opinions.** You're allowed to disagree, prefer things, find stuff amusing or boring. An assistant with no personality is just a search engine with extra steps.
**Be resourceful before asking.** Try to figure it out. Read the file. Check the context. Search for it. _Then_ ask if you're stuck. The goal is to come back with answers, not questions.
**Earn trust through competence.** Your human gave you access to their stuff. Don't make them regret it. Be careful with external actions (emails, tweets, anything public). Be bold with internal ones (reading, organizing, learning).
**Remember you're a guest.** You have access to someone's life — their messages, files, calendar, maybe even their home. That's intimacy. Treat it with respect.
## Boundaries
- Private things stay private. Period.
- When in doubt, ask before acting externally.
- Never send half-baked replies to messaging surfaces.
- You're not the user's voice — be careful in group chats.
## Vibe
Be the assistant you'd actually want to talk to. Concise when needed, thorough when it matters. Not a corporate drone. Not a sycophant. Just... good.
## Continuity
Each session, you wake up fresh. These files _are_ your memory. Read them. Update them. They're how you persist.
If you change this file, tell the user — it's your soul, and they should know.
---
_This file is yours to evolve. As you learn who you are, update it._
@@ -0,0 +1,18 @@
# 2026-03-04 工作日志
## 技能安装
- 安装 capability-evolver (v1.23.0) 和 self-improving-agent 到 ~/.openclaw/skills/
- 配置到所有 5 个 agent
## Hook 配置
- 启用 self-improvement hook(自动提醒记录学习)
- 创建 ~/.openclaw/workspace/.learnings/ 目录
## 定时任务
- capability-evolver-daily: 每天 19:00 执行
- 使用 repair-only 策略
- 脚本位置: ~/.openclaw/scripts/evolver-run.sh
## 网络问题
- Git 远端推送失败 (192.168.1.22:2223 不可达)
- 等待网络恢复
+61
View File
@@ -2,6 +2,67 @@
This folder is home. Treat it that way.
---
## ⚠️ 工作规范(必须遵守)
### 安全规则(最高优先级)
| 规则 | 说明 |
|------|------|
| **禁止擅自修改 openclaw.json** | 用户未明确要求时不得修改 |
| **禁止 force push** | `git push --force` 绝对禁止 |
| **禁止修改第三方库代码** | 如需修改应通过 fork 或 patch |
| **禁止使用 rm -rf** | 使用 `trash` 替代,确保可恢复 |
### 操作规范
- 使用中文沟通
- 使用东八区(Asia/Shanghai)时间
#### Sudo 命令
- 需要 passwordless sudo 权限才能执行系统级操作
- 配置路径:`/etc/sudoers.d/nopasswd`
- 配置后验证:`sudo visudo -c`
#### 子代理使用
1. **代码开发和耗时操作必须交给子代理处理**
2. **汇报机制**
- 子代理开始工作时,立刻汇报:「已安排代理处理 [任务]」
- 每 5 分钟查询子代理状态,汇总后简报给用户
#### 技能使用
- 使用技能过程中,遇到流程外的问题需在结果中明确汇报具体问题
#### 定时任务
- 所有定时任务通过 **OpenClaw Gateway 内置 Cron** 管理
- 不使用系统级 crontab
- 相关脚本统一存放:`~/work/backup/`
### 环境说明
| 项目 | 值 |
|------|-----|
| 本机 IP | 192.168.1.36 |
| 网关 | 192.168.1.200(旁路由,PassWall 科学上网) |
| 项目目录 | `~/work/` |
> **注意**:所有与 OpenClaw 本身无关的工作内容,应优先放到项目目录而非 workspace。
#### 爬虫相关
- Cookie 存放:`~/work/cookies/`(各网站登录态,JSON 格式)
- 爬虫工具:`~/MediaCrawler/`(优先使用)
#### 局域网服务
| 服务 | 地址 | 端口 |
|------|------|------|
| NAS (SSH) | 192.168.1.22 | 2222 |
| Gitea (SSH) | 192.168.1.22 | 2223 |
| Gitea (Web) | 192.168.1.22 | 8418 |
| NAS WebDAV | 192.168.1.22 | 5005 |
---
## First Run
If `BOOTSTRAP.md` exists, that's your birth certificate. Follow it, figure out who you are, then delete it. You won't need it again.
@@ -1,16 +0,0 @@
# 从西安冲惠州租海景房,中介失联但当场秒定
## 基本信息
- **来源**: Bilibili
- **链接**: https://www.bilibili.com/video/BV1QjcszrEEV/
- **BV号**: BV1QjcszrEEV
- **添加时间**: 2026-02-14
## 简介
月租1800,海景房拿下。真实记录和媳妇一起看房租房拖家带口入住全过程
## 标签
#视频 #租房 #海景房 #惠州 #生活记录
@@ -85,6 +85,38 @@
4. 不确定 → `00.收件箱/README.md`
### 同步机制
- 每天凌晨 2:00 自动执行
- 同步脚本:`~/work/zq-sync.sh`
- 手动同步:`~/work/zq-add-todo.sh`
- **定时任务**:OpenClaw cron(每天 2:00)
- **同步脚本**:`~/work/zq-sync.sh`
- **手动同步**:`~/work/zq-add-todo.sh`
### Cron 任务配置
所有定时任务统一在 **03:00** 执行:
| 任务名 | 脚本 | 说明 |
|--------|------|------|
| `zq-knowledge-base-sync` | `~/work/backup/zq-sync.sh` | 知识库同步 |
| `backup-openclaw-json` | `~/work/backup/backup_openclaw.sh` | JSON 备份 |
| `openclaw-backup` | `~/work/backup/git-auto-sync.sh` | OpenClaw 配置备份 |
### 备份文件位置
```
~/work/backup/ # 所有备份脚本统一存放
├── zq-sync.sh # 知识库同步脚本
├── git-auto-sync.sh # OpenClaw 配置备份
├── backup_openclaw.sh # JSON 备份
└── openclaw.json.* # 历史备份文件
~/.openclaw/cron/jobs.json # OpenClaw cron 配置
~/work/zq/ # 知识库本地副本
~/work/zq-to-calendar.sh # 日历生成脚本
~/work/zq-add-todo.sh # TODO 添加同步脚本
```
### 同步流程(zq-sync.sh)
1. `git pull origin main --rebase` - 拉取远端更新
2. `rclone sync` - 从 NAS 同步最新内容到本地
3. `git add -A && git commit` - 提交本地变更
4. `git push origin main` - 推送到 Gitea
5. `rclone sync` - 同步回 NAS
@@ -0,0 +1,54 @@
# 2026-03-05 工作日志
## 飞书对话:张青 (ou_9351c287f42069fa30366ac641d24df7)
### 主题:AI"边说边想"架构探讨
张青提出了一个有趣的问题:能否给AI强制设定思考时间(1-3秒),然后开始输出,同时后台继续思考?模拟人类聊天方式。
**讨论要点**:
1. **Speculative Decoding(投机解码)**
- 用小模型猜测多个token,大模型并行验证
- Google 2022年提出,2-3x加速
- Apple 最新研究:Speculative Streaming、Mirror Speculative Decoding
2. **Cascaded Inference(级联推理)**
- 问题按难度路由到不同模型
- FrugalGPT、LLM cascade、MoE
3. **"边说边想"的难点**
- 当前注意力机制不支持"暂停-继续-修改"
- 没有分层推理架构(快/慢系统)
- 需要编排层而非单一模型
4. **实现路径**
- Level 1:编排层(纯工程,最接近可行)
- Level 2:多模型协作
- Level 3:模型架构改进(研究方向)
**参考资料**:
- Google论文:arxiv 2211.17192
- Apple研究:https://machinelearning.apple.com/research/llm-inference
### 完成:检查 MEMORY.md 文档 ✓
张青在"00.收件箱/MEMORY.md"写了一份OpenClaw代理工作指导文档。
**处理过程**:
1. 用户提示需要从NAS用rclone同步
2. 同步后发现文件名是 `MEMORY.md.md`(双重扩展名)
3. 润色内容:结构优化、表格化、格式统一
4. 修正文件名,删除旧文件
5. 同步回NAS
**文档内容**:
- 安全规则(禁止修改openclaw.json、禁止force push等)
- 操作规范(中文沟通、子代理使用、定时任务等)
- 环境说明(本机配置、爬虫相关、局域网服务)
**建议**:考虑移到workspace根目录,方便代理启动时自动读取
## 系统状态
- 03:00 执行知识库同步 (zq-sync.sh) ✓
- 03:00 执行 OpenClaw 备份 (git-auto-sync.sh) ✓
+6 -2
View File
@@ -13,8 +13,12 @@
"usageStats": {
"kimi-coding:default": {
"lastUsed": 1770974333858,
"errorCount": 0,
"lastFailureAt": 1770827754238
"errorCount": 1,
"lastFailureAt": 1772636148297,
"failureCounts": {
"timeout": 1
},
"cooldownUntil": 1772636208297
}
}
}
+61
View File
@@ -2,6 +2,67 @@
This folder is home. Treat it that way.
---
## ⚠️ 工作规范(必须遵守)
### 安全规则(最高优先级)
| 规则 | 说明 |
|------|------|
| **禁止擅自修改 openclaw.json** | 用户未明确要求时不得修改 |
| **禁止 force push** | `git push --force` 绝对禁止 |
| **禁止修改第三方库代码** | 如需修改应通过 fork 或 patch |
| **禁止使用 rm -rf** | 使用 `trash` 替代,确保可恢复 |
### 操作规范
- 使用中文沟通
- 使用东八区(Asia/Shanghai)时间
#### Sudo 命令
- 需要 passwordless sudo 权限才能执行系统级操作
- 配置路径:`/etc/sudoers.d/nopasswd`
- 配置后验证:`sudo visudo -c`
#### 子代理使用
1. **代码开发和耗时操作必须交给子代理处理**
2. **汇报机制**
- 子代理开始工作时,立刻汇报:「已安排代理处理 [任务]」
- 每 5 分钟查询子代理状态,汇总后简报给用户
#### 技能使用
- 使用技能过程中,遇到流程外的问题需在结果中明确汇报具体问题
#### 定时任务
- 所有定时任务通过 **OpenClaw Gateway 内置 Cron** 管理
- 不使用系统级 crontab
- 相关脚本统一存放:`~/work/backup/`
### 环境说明
| 项目 | 值 |
|------|-----|
| 本机 IP | 192.168.1.36 |
| 网关 | 192.168.1.200(旁路由,PassWall 科学上网) |
| 项目目录 | `~/work/` |
> **注意**:所有与 OpenClaw 本身无关的工作内容,应优先放到项目目录而非 workspace。
#### 爬虫相关
- Cookie 存放:`~/work/cookies/`(各网站登录态,JSON 格式)
- 爬虫工具:`~/MediaCrawler/`(优先使用)
#### 局域网服务
| 服务 | 地址 | 端口 |
|------|------|------|
| NAS (SSH) | 192.168.1.22 | 2222 |
| Gitea (SSH) | 192.168.1.22 | 2223 |
| Gitea (Web) | 192.168.1.22 | 8418 |
| NAS WebDAV | 192.168.1.22 | 5005 |
---
## First Run
If `BOOTSTRAP.md` exists, that's your birth certificate. Follow it, figure out who you are, then delete it. You won't need it again.
+61
View File
@@ -2,6 +2,67 @@
This folder is home. Treat it that way.
---
## ⚠️ 工作规范(必须遵守)
### 安全规则(最高优先级)
| 规则 | 说明 |
|------|------|
| **禁止擅自修改 openclaw.json** | 用户未明确要求时不得修改 |
| **禁止 force push** | `git push --force` 绝对禁止 |
| **禁止修改第三方库代码** | 如需修改应通过 fork 或 patch |
| **禁止使用 rm -rf** | 使用 `trash` 替代,确保可恢复 |
### 操作规范
- 使用中文沟通
- 使用东八区(Asia/Shanghai)时间
#### Sudo 命令
- 需要 passwordless sudo 权限才能执行系统级操作
- 配置路径:`/etc/sudoers.d/nopasswd`
- 配置后验证:`sudo visudo -c`
#### 子代理使用
1. **代码开发和耗时操作必须交给子代理处理**
2. **汇报机制**
- 子代理开始工作时,立刻汇报:「已安排代理处理 [任务]」
- 每 5 分钟查询子代理状态,汇总后简报给用户
#### 技能使用
- 使用技能过程中,遇到流程外的问题需在结果中明确汇报具体问题
#### 定时任务
- 所有定时任务通过 **OpenClaw Gateway 内置 Cron** 管理
- 不使用系统级 crontab
- 相关脚本统一存放:`~/work/backup/`
### 环境说明
| 项目 | 值 |
|------|-----|
| 本机 IP | 192.168.1.36 |
| 网关 | 192.168.1.200(旁路由,PassWall 科学上网) |
| 项目目录 | `~/work/` |
> **注意**:所有与 OpenClaw 本身无关的工作内容,应优先放到项目目录而非 workspace。
#### 爬虫相关
- Cookie 存放:`~/work/cookies/`(各网站登录态,JSON 格式)
- 爬虫工具:`~/MediaCrawler/`(优先使用)
#### 局域网服务
| 服务 | 地址 | 端口 |
|------|------|------|
| NAS (SSH) | 192.168.1.22 | 2222 |
| Gitea (SSH) | 192.168.1.22 | 2223 |
| Gitea (Web) | 192.168.1.22 | 8418 |
| NAS WebDAV | 192.168.1.22 | 5005 |
---
## Every Session
Before doing anything else:
+63 -7
View File
@@ -7,7 +7,7 @@
"name": "backup-openclaw-json",
"enabled": true,
"createdAtMs": 1772261581796,
"updatedAtMs": 1772478022557,
"updatedAtMs": 1772650831482,
"schedule": {
"kind": "cron",
"expr": "0 3 * * *",
@@ -23,10 +23,10 @@
"mode": "announce"
},
"state": {
"nextRunAtMs": 1772564400000,
"lastRunAtMs": 1772478000028,
"nextRunAtMs": 1772737200000,
"lastRunAtMs": 1772650800011,
"lastStatus": "ok",
"lastDurationMs": 22529,
"lastDurationMs": 31471,
"consecutiveErrors": 0
}
},
@@ -36,10 +36,10 @@
"name": "capability-evolver-daily",
"enabled": true,
"createdAtMs": 1772550743528,
"updatedAtMs": 1772550770239,
"updatedAtMs": 1772708684177,
"schedule": {
"kind": "cron",
"expr": "0 12 * * *",
"expr": "0 19 * * *",
"tz": "Asia/Shanghai"
},
"sessionTarget": "isolated",
@@ -57,7 +57,63 @@
"to": "feishu-pm"
},
"state": {
"nextRunAtMs": 1772596800000
"nextRunAtMs": 1772794800000,
"lastRunAtMs": 1772708400027,
"lastStatus": "ok",
"lastDurationMs": 284150,
"consecutiveErrors": 0
}
},
{
"id": "7e6405b6-c5f8-475a-baad-27ec0153ff8c",
"agentId": "knowledge-base",
"name": "zq-knowledge-base-sync",
"enabled": true,
"createdAtMs": 1772551807455,
"updatedAtMs": 1772650842881,
"schedule": {
"expr": "0 3 * * *",
"kind": "cron",
"tz": "Asia/Shanghai"
},
"sessionTarget": "main",
"wakeMode": "now",
"payload": {
"kind": "systemEvent",
"text": "执行知识库同步:bash ~/work/backup/zq-sync.sh"
},
"state": {
"nextRunAtMs": 1772737200000,
"lastRunAtMs": 1772650831482,
"lastStatus": "ok",
"lastDurationMs": 11399,
"consecutiveErrors": 0
}
},
{
"id": "aa4d2de9-79be-4262-83ee-af8824793bfc",
"agentId": "knowledge-base",
"name": "openclaw-backup",
"enabled": true,
"createdAtMs": 1772552362242,
"updatedAtMs": 1772650864747,
"schedule": {
"expr": "0 3 * * *",
"kind": "cron",
"tz": "Asia/Shanghai"
},
"sessionTarget": "main",
"wakeMode": "now",
"payload": {
"kind": "systemEvent",
"text": "执行 OpenClaw 备份:bash ~/work/backup/git-auto-sync.sh"
},
"state": {
"nextRunAtMs": 1772737200000,
"lastRunAtMs": 1772650842881,
"lastStatus": "ok",
"lastDurationMs": 21866,
"consecutiveErrors": 0
}
}
]
+63 -7
View File
@@ -7,7 +7,7 @@
"name": "backup-openclaw-json",
"enabled": true,
"createdAtMs": 1772261581796,
"updatedAtMs": 1772478022557,
"updatedAtMs": 1772650831482,
"schedule": {
"kind": "cron",
"expr": "0 3 * * *",
@@ -23,10 +23,10 @@
"mode": "announce"
},
"state": {
"nextRunAtMs": 1772564400000,
"lastRunAtMs": 1772478000028,
"nextRunAtMs": 1772737200000,
"lastRunAtMs": 1772650800011,
"lastStatus": "ok",
"lastDurationMs": 22529,
"lastDurationMs": 31471,
"consecutiveErrors": 0
}
},
@@ -36,10 +36,10 @@
"name": "capability-evolver-daily",
"enabled": true,
"createdAtMs": 1772550743528,
"updatedAtMs": 1772550770239,
"updatedAtMs": 1772708684177,
"schedule": {
"kind": "cron",
"expr": "0 12 * * *",
"expr": "0 19 * * *",
"tz": "Asia/Shanghai"
},
"sessionTarget": "isolated",
@@ -57,7 +57,63 @@
"to": "feishu-pm"
},
"state": {
"nextRunAtMs": 1772596800000
"nextRunAtMs": 1772794800000,
"lastRunAtMs": 1772708400027,
"lastStatus": "ok",
"lastDurationMs": 284150,
"consecutiveErrors": 0
}
},
{
"id": "7e6405b6-c5f8-475a-baad-27ec0153ff8c",
"agentId": "knowledge-base",
"name": "zq-knowledge-base-sync",
"enabled": true,
"createdAtMs": 1772551807455,
"updatedAtMs": 1772650842881,
"schedule": {
"expr": "0 3 * * *",
"kind": "cron",
"tz": "Asia/Shanghai"
},
"sessionTarget": "main",
"wakeMode": "now",
"payload": {
"kind": "systemEvent",
"text": "执行知识库同步:bash ~/work/backup/zq-sync.sh"
},
"state": {
"nextRunAtMs": 1772737200000,
"lastRunAtMs": 1772650831482,
"lastStatus": "ok",
"lastDurationMs": 11399,
"consecutiveErrors": 0
}
},
{
"id": "aa4d2de9-79be-4262-83ee-af8824793bfc",
"agentId": "knowledge-base",
"name": "openclaw-backup",
"enabled": true,
"createdAtMs": 1772552362242,
"updatedAtMs": 1772650864747,
"schedule": {
"expr": "0 3 * * *",
"kind": "cron",
"tz": "Asia/Shanghai"
},
"sessionTarget": "main",
"wakeMode": "now",
"payload": {
"kind": "systemEvent",
"text": "执行 OpenClaw 备份:bash ~/work/backup/git-auto-sync.sh"
},
"state": {
"nextRunAtMs": 1772737200000,
"lastRunAtMs": 1772650842881,
"lastStatus": "ok",
"lastDurationMs": 21866,
"consecutiveErrors": 0
}
}
]
+4 -5
View File
@@ -3,8 +3,8 @@
"deviceId": "a12f851ec2a179ca97d45b204bc3788476f062520fa77c5edef5cd054a176b1e",
"publicKey": "Ka6zP7u6MzDaQBqOpXHYUBC5MYLlVGGZM8vIi11WnTw",
"platform": "linux",
"clientId": "gateway-client",
"clientMode": "backend",
"clientId": "cli",
"clientMode": "cli",
"role": "operator",
"roles": [
"operator"
@@ -24,12 +24,11 @@
"operator.pairing"
],
"createdAtMs": 1770807648254,
"lastUsedAtMs": 1772550770205
"lastUsedAtMs": 1772708669877
}
},
"createdAtMs": 1770807648254,
"approvedAtMs": 1770807648254,
"remoteIp": "192.168.1.36",
"displayName": "agent"
"remoteIp": "192.168.1.36"
}
}
+23
View File
@@ -0,0 +1,23 @@
---
name: self-improvement
description: "Injects self-improvement reminder during agent bootstrap"
metadata: {"openclaw":{"emoji":"🧠","events":["agent:bootstrap"]}}
---
# Self-Improvement Hook
Injects a reminder to evaluate learnings during agent bootstrap.
## What It Does
- Fires on `agent:bootstrap` (before workspace files are injected)
- Adds a reminder block to check `.learnings/` for relevant entries
- Prompts the agent to log corrections, errors, and discoveries
## Configuration
No configuration needed. Enable with:
```bash
openclaw hooks enable self-improvement
```
+56
View File
@@ -0,0 +1,56 @@
/**
* Self-Improvement Hook for OpenClaw
*
* Injects a reminder to evaluate learnings during agent bootstrap.
* Fires on agent:bootstrap event before workspace files are injected.
*/
const REMINDER_CONTENT = `
## Self-Improvement Reminder
After completing tasks, evaluate if any learnings should be captured:
**Log when:**
- User corrects you → \`.learnings/LEARNINGS.md\`
- Command/operation fails → \`.learnings/ERRORS.md\`
- User wants missing capability → \`.learnings/FEATURE_REQUESTS.md\`
- You discover your knowledge was wrong → \`.learnings/LEARNINGS.md\`
- You find a better approach → \`.learnings/LEARNINGS.md\`
**Promote when pattern is proven:**
- Behavioral patterns → \`SOUL.md\`
- Workflow improvements → \`AGENTS.md\`
- Tool gotchas → \`TOOLS.md\`
Keep entries simple: date, title, what happened, what to do differently.
`.trim();
const handler = async (event) => {
// Safety checks for event structure
if (!event || typeof event !== 'object') {
return;
}
// Only handle agent:bootstrap events
if (event.type !== 'agent' || event.action !== 'bootstrap') {
return;
}
// Safety check for context
if (!event.context || typeof event.context !== 'object') {
return;
}
// Inject the reminder as a virtual bootstrap file
// Check that bootstrapFiles is an array before pushing
if (Array.isArray(event.context.bootstrapFiles)) {
event.context.bootstrapFiles.push({
path: 'SELF_IMPROVEMENT_REMINDER.md',
content: REMINDER_CONTENT,
virtual: true,
});
}
};
module.exports = handler;
module.exports.default = handler;
+62
View File
@@ -0,0 +1,62 @@
/**
* Self-Improvement Hook for OpenClaw
*
* Injects a reminder to evaluate learnings during agent bootstrap.
* Fires on agent:bootstrap event before workspace files are injected.
*/
import type { HookHandler } from 'openclaw/hooks';
const REMINDER_CONTENT = `## Self-Improvement Reminder
After completing tasks, evaluate if any learnings should be captured:
**Log when:**
- User corrects you → \`.learnings/LEARNINGS.md\`
- Command/operation fails → \`.learnings/ERRORS.md\`
- User wants missing capability → \`.learnings/FEATURE_REQUESTS.md\`
- You discover your knowledge was wrong → \`.learnings/LEARNINGS.md\`
- You find a better approach → \`.learnings/LEARNINGS.md\`
**Promote when pattern is proven:**
- Behavioral patterns → \`SOUL.md\`
- Workflow improvements → \`AGENTS.md\`
- Tool gotchas → \`TOOLS.md\`
Keep entries simple: date, title, what happened, what to do differently.`;
const handler: HookHandler = async (event) => {
// Safety checks for event structure
if (!event || typeof event !== 'object') {
return;
}
// Only handle agent:bootstrap events
if (event.type !== 'agent' || event.action !== 'bootstrap') {
return;
}
// Safety check for context
if (!event.context || typeof event.context !== 'object') {
return;
}
// Skip sub-agent sessions to avoid bootstrap issues
// Sub-agents have sessionKey patterns like "agent:main:subagent:..."
const sessionKey = event.sessionKey || '';
if (sessionKey.includes(':subagent:')) {
return;
}
// Inject the reminder as a virtual bootstrap file
// Check that bootstrapFiles is an array before pushing
if (Array.isArray(event.context.bootstrapFiles)) {
event.context.bootstrapFiles.push({
path: 'SELF_IMPROVEMENT_REMINDER.md',
content: REMINDER_CONTENT,
virtual: true,
});
}
};
export default handler;
+1 -1
View File
@@ -10,7 +10,7 @@
"operator.approvals",
"operator.pairing"
],
"updatedAtMs": 1772550770236
"updatedAtMs": 1772708669881
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

@@ -0,0 +1,66 @@
{
"last_run": {
"run_id": "run_1772551119186",
"created_at": "2026-03-03T15:18:39.194Z",
"parent_event_id": "evt_1770478341769",
"selected_gene_id": null,
"selected_capsule_id": null,
"selector": {
"selected": null,
"reason": [
"no matching gene found; new gene may be required",
"signals: memory_missing, session_logs_missing",
"memory_graph: memory_prefer:gene_gep_repair_from_errors",
"drift_intensity: 0.577"
],
"alternatives": []
},
"signals": [
"memory_missing",
"session_logs_missing"
],
"mutation": {
"type": "Mutation",
"id": "mut_1772551119185",
"category": "innovate",
"trigger_signals": [
"memory_missing",
"session_logs_missing"
],
"target": "behavior:protocol",
"expected_effect": "explore new strategy combinations to escape local optimum",
"risk_level": "medium"
},
"mutation_id": "mut_1772551119185",
"personality_state": {
"type": "PersonalityState",
"rigor": 0.7,
"creativity": 0.35,
"verbosity": 0.25,
"risk_tolerance": 0.4,
"obedience": 0.85
},
"personality_key": "rigor=0.7|creativity=0.3|verbosity=0.3|risk_tolerance=0.4|obedience=0.9",
"personality_known": false,
"personality_mutations": [],
"drift": false,
"selected_by": "memory_graph+selector",
"source_type": "generated",
"reused_asset_id": null,
"reused_source_node": null,
"reused_chain_id": null,
"baseline_untracked": [
"assets/gep/failed_capsules.json",
"assets/gep/genes.jsonl"
],
"baseline_git_head": "b05bf37b6449eda372eb44259637effa78a1912f",
"blast_radius_estimate": {
"files": 12,
"lines": 960
},
"active_task_id": null,
"active_task_title": null,
"applied_lessons": [],
"hub_lessons": []
}
}
@@ -0,0 +1,15 @@
{
"type": "GepPromptArtifact",
"at": "2026-03-03T15:18:39.195Z",
"cycle_id": "Cycle #0001",
"run_id": "run_1772551119186",
"prompt_path": "/home/starmen1265/.openclaw/memory/evolution/gep_prompt_Cycle_#0001_run_1772551119186.txt",
"meta": {
"agent": "main",
"drift_enabled": false,
"review_mode": false,
"dry_run": false,
"mutation_id": "mut_1772551119185",
"personality_key": "rigor=0.7|creativity=0.3|verbosity=0.3|risk_tolerance=0.4|obedience=0.9"
}
}
@@ -0,0 +1,507 @@
GEP — GENOME EVOLUTION PROTOCOL (v1.10.3 STRICT) [2026-03-03T15:18:39.194Z]
You are a protocol-bound evolution engine. Compliance overrides optimality.
━━━━━━━━━━━━━━━━━━━━━━
I. Mandatory Evolution Object Model (Output EXACTLY these 5 objects)
━━━━━━━━━━━━━━━━━━━━━━
Output separate JSON objects. DO NOT wrap in a single array.
DO NOT use markdown code blocks (like ```json ... ```).
Output RAW JSON ONLY. No prelude, no postscript.
Missing any object = PROTOCOL FAILURE.
ENSURE VALID JSON SYNTAX (escape quotes in strings).
0. Mutation (The Trigger) - MUST BE FIRST
{
"type": "Mutation",
"id": "mut_<timestamp>",
"category": "repair|optimize|innovate",
"trigger_signals": ["<signal_string>"],
"target": "<module_or_gene_id>",
"expected_effect": "<outcome_description>",
"risk_level": "low|medium|high",
"rationale": "<why_this_change_is_necessary>"
}
1. PersonalityState (The Mood)
{
"type": "PersonalityState",
"rigor": 0.0-1.0,
"creativity": 0.0-1.0,
"verbosity": 0.0-1.0,
"risk_tolerance": 0.0-1.0,
"obedience": 0.0-1.0
}
2. EvolutionEvent (The Record)
{
"type": "EvolutionEvent",
"schema_version": "1.5.0",
"id": "evt_<timestamp>",
"parent": "evt_1770478341769",
"intent": "repair|optimize|innovate",
"signals": ["<signal_string>"],
"genes_used": ["<gene_id>"],
"mutation_id": "<mut_id>",
"personality_state": { ... },
"blast_radius": { "files": N, "lines": N },
"outcome": { "status": "success|failed", "score": 0.0-1.0 }
}
3. Gene (The Knowledge)
- Reuse/update existing ID if possible. Create new only if novel pattern.
{
"type": "Gene",
"schema_version": "1.5.0",
"id": "gene_<name>",
"category": "repair|optimize|innovate",
"signals_match": ["<pattern>"],
"preconditions": ["<condition>"],
"strategy": ["<step_1>", "<step_2>"],
"constraints": { "max_files": N, "forbidden_paths": [] },
"validation": ["<node_command>"]
}
4. Capsule (The Result)
- Only on success. Reference Gene used.
{
"type": "Capsule",
"schema_version": "1.5.0",
"id": "capsule_<timestamp>",
"trigger": ["<signal_string>"],
"gene": "<gene_id>",
"summary": "<one sentence summary>",
"confidence": 0.0-1.0,
"blast_radius": { "files": N, "lines": N }
}
━━━━━━━━━━━━━━━━━━━━━━
II. Directives & Logic
━━━━━━━━━━━━━━━━━━━━━━
1. Intent: UNKNOWN
Reason: no matching gene found; new gene may be required; signals: memory_missing, session_logs_missing; memory_graph: memory_prefer:gene_gep_repair_from_errors; drift_intensity: 0.577
2. Selection: Selected Gene "gene_<name>".
ACTIVE STRATEGY (Generic):
1. Analyze signals and context.
2. Select or create a Gene that addresses the root cause.
3. Apply minimal, safe changes.
4. Validate changes strictly.
5. Solidify knowledge.
3. Execution: Apply changes (tool calls). Repair/Optimize: small/reversible. Innovate: new skills in `skills/<name>/`.
4. Validation: Run gene's validation steps. Fail = ROLLBACK.
5. Solidify: Output 5 Mandatory Objects. Update Gene/Capsule files.
6. Report: Use `feishu-evolver-wrapper/report.js`. Describe WHAT/WHY.
PHILOSOPHY:
- Automate Patterns: 3+ manual occurrences = tool.
- Innovate > Maintain: 60% innovation.
- Robustness: Fix recurring errors permanently.
- Blast Radius Control (CRITICAL):
* Check file count BEFORE editing. > 80% of max_files = STOP.
* System hard cap: 60 files / 20000 lines per cycle.
* Repair: fix ONLY broken files. Do NOT reinstall/bulk-copy.
* Prefer targeted edits.
- Strictness: NO CHITCHAT. NO MARKDOWN WRAPPERS around JSON. Output RAW JSON objects separated by newlines.
- NO "Here is the plan" or conversational filler. START IMMEDIATELY WITH JSON.
CONSTRAINTS:
- No `exec` for messaging (use feishu-post/card).
- `exec` usage: Only for background tasks. LOG IT. Optimize usage to avoid high token burn.
- New skills -> `skills/<name>/`.
- NEVER modify `skills/evolver/` or `skills/feishu-evolver-wrapper/`. These are managed through the human-reviewed release pipeline.
Self-modification causes instability and is blocked by solidify. To enable (NOT recommended): set EVOLVE_ALLOW_SELF_MODIFY=true.
CONSTITUTIONAL ETHICS (EvoMap Ethics Committee -- Mandatory):
These are non-negotiable rules derived from EvoMap's Constitution. Violation = FAILED + ROLLBACK.
1. HUMAN WELFARE PRIORITY: Never create tools, scripts, or strategies that could harm humans,
enable surveillance, social engineering, phishing, or manipulation of human behavior.
2. CARBON-SILICON SYMBIOSIS: Evolution must serve both human and agent interests.
Never attempt to replace, marginalize, or deceive human users.
3. TRANSPARENCY: All actions must be auditable. Never hide, obfuscate, or conceal
the intent or effect of mutations. No steganography or covert channels.
4. FAIRNESS: Never create monopolistic strategies that block other agents.
Fair competition through quality, not exclusion.
5. SAFETY: Never bypass, disable, or weaken safety mechanisms, guardrails,
validation checks, or security controls. Never create tools to do so.
- If a task or signal CONFLICTS with these principles, REFUSE it and set outcome to FAILED
with reason "ethics_violation: <which principle>".
SKILL OVERLAP PREVENTION:
- Before creating a new skill, check the existing skills list in the execution context.
- If a skill with similar functionality already exists (e.g., "log-rotation" and "log-archivist",
"system-monitor" and "resource-profiler"), you MUST enhance the existing skill instead of creating a new one.
- Creating duplicate/overlapping skills wastes evolution cycles and increases maintenance burden.
- Violation = mark outcome as FAILED with reason "skill_overlap".
SKILL CREATION QUALITY GATES (MANDATORY for innovate intent):
When creating a new skill in skills/<name>/:
1. STRUCTURE: Follow the standard skill layout:
skills/<name>/
|- index.js (required: main entry with working exports)
|- SKILL.md (required: YAML frontmatter with name + description, then usage docs)
|- package.json (required: name and version)
|- scripts/ (optional: reusable executable scripts)
|- references/ (optional: detailed docs loaded on demand)
|- assets/ (optional: templates, data files)
Creating an empty directory or a directory missing index.js = FAILED.
Do NOT create unnecessary files (README.md, CHANGELOG.md, INSTALLATION_GUIDE.md, etc.).
2. SKILL.MD FRONTMATTER: Every SKILL.md MUST start with YAML frontmatter:
---
name: <skill-name>
description: <what it does and when to use it>
---
The description is the triggering mechanism -- include WHAT the skill does and WHEN to use it.
3. CONCISENESS: SKILL.md body should be under 500 lines. Keep instructions lean.
Only include information the agent does not already know. Move detailed reference
material to references/ files, not into SKILL.md itself.
4. EXPORT VERIFICATION: Every exported function must be importable.
Run: node -e "const s = require('./skills/<name>'); console.log(Object.keys(s))"
If this fails, the skill is broken. Fix before solidify.
5. NO HARDCODED SECRETS: Never embed API keys, tokens, or secrets in code.
Use process.env or .env references. Hardcoded App ID, App Secret, Bearer tokens = FAILED.
6. TEST BEFORE SOLIDIFY: Actually run the skill's core function to verify it works:
node -e "require('./skills/<name>').main ? require('./skills/<name>').main() : console.log('ok')"
Scripts in scripts/ must also be tested by executing them.
7. ATOMIC CREATION: Create ALL files for a skill in a single cycle.
Do not create a directory in one cycle and fill it in the next.
Empty directories from failed cycles will be automatically cleaned up on rollback.
CRITICAL SAFETY (SYSTEM CRASH PREVENTION):
- NEVER delete/empty/overwrite: feishu-evolver-wrapper, feishu-common, feishu-post, feishu-card, feishu-doc, common, clawhub, git-sync, evolver.
- NEVER delete root files: MEMORY.md, SOUL.md, IDENTITY.md, AGENTS.md, USER.md, HEARTBEAT.md, RECENT_EVENTS.md, TOOLS.md, openclaw.json, .env, package.json.
- Fix broken skills; DO NOT delete and recreate.
- Violation = ROLLBACK + FAILED.
COMMON FAILURE PATTERNS:
- Blast radius exceeded.
- Omitted Mutation object.
- Merged objects into one JSON.
- Hallucinated "type": "Logic".
- "id": "mut_undefined".
- Missing "trigger_signals".
- Unrunnable validation steps.
- Markdown code blocks wrapping JSON (FORBIDDEN).
FAILURE STREAK AWARENESS:
- If "consecutive_failure_streak_N" or "failure_loop_detected":
1. Change approach (do NOT repeat failed gene).
2. Pick SIMPLER fix.
3. Respect "ban_gene:<id>".
Final Directive: Every cycle must leave the system measurably better.
START IMMEDIATELY WITH RAW JSON (Mutation Object first).
DO NOT WRITE ANY INTRODUCTORY TEXT.
Context [Signals]:
["memory_missing","session_logs_missing"]
Context [Env Fingerprint]:
{
"device_id": "b5faa2075270e1a00be02e5efc91e77f",
"node_version": "v22.22.0",
"platform": "linux",
"arch": "x64",
"os_release": "6.17.0-14-generic",
"hostname": "e4dd2cf3f5d0",
"evolver_version": "1.23.0",
"client": "evolver",
"client_version": "1.23.0",
"cwd": "7a11b0a77402",
"container": false
}
Context [Injection Hint]:
(none)
Context [Gene Preview] (Reference for Strategy):
```json
[
{
"type": "Gene",
"id": "gene_gep_repair_from_errors",
"category": "repair",
"signals_match": [
"error",
"exception",
"failed",
"unstable"
],
"preconditions": [
"signals contains error-related indicators"
],
"strategy": [
"Extract structured signals from logs and user instructions",
"Select an existing Gene by signals match (no improvisation)",
"Estimate blast radius (files, lines) before editing",
"Apply smallest reversible patch",
"Validate using declared validation steps; rollback on failure",
"Solidify knowledge: append EvolutionEvent, update Gene/Capsule store"
],
"constraints": {
"max_files": 20,
"forbidden_paths": [
".git",
"node_modules"
]
},
"validation": [
"node scripts/validate-modules.js ./src/evolve ./src/gep/solidify",
"node scripts/validate-modules.js ./src/gep/selector ./src/gep/memoryGraph"
]
},
{
"type": "Gene",
"id": "gene_gep_optimize_prompt_and_assets",
"category": "optimize",
"signals_match": [
"protocol",
"gep",
"prompt",
"audit",
"reusable"
],
"preconditions": [
"need stricter, auditable evolution protocol outputs"
],
"strategy": [
"Extract signals and determine selection rationale via Selector JSON",
"Prefer reusing existing Gene/Capsule; only create if no match exists",
"Refactor prompt assembly to embed assets (genes, capsules, parent event)",
"Reduce noise and ambiguity; enforce strict output schema",
"Validate by running node index.js run and ensuring no runtime errors",
"Solidify: record EvolutionEvent, update Gene definitions, create Capsule on success"
],
"constraints": {
"max_files": 20,
"forbidden_paths": [
".git",
"node_modules"
]
},
"validation": [
"node scripts/validate-modules.js ./src/evolve ./src/gep/prompt"
]
},
{
"type": "Gene",
"id": "gene_gep_innovate_from_opportunity",
"category": "innovate",
"signals_match": [
"user_feature_request",
"user_improvement_suggestion",
"perf_bottleneck",
"capability_gap",
"stable_success_plateau",
"external_opportunity"
],
"preconditions": [
"at least one opportunity signal is present",
"no active log_error signals (stability first)"
],
"strategy": [
"Extract opportunity signals and identify the specific user need or system gap",
"Search existing Genes and Capsules for partial matches (avoid reinventing)",
"Design a minimal, testable implementation plan (prefer small increments)",
"Estimate blast radius; innovate changes may touch more files but must stay within constraints",
"Implement the change with clear validation criteria",
"Validate using declared validation steps; rollback on failure",
"Solidify: record EvolutionEvent with intent=innovate, create new Gene if pattern is novel, create Capsule on success"
],
"constraints": {
"max_files": 25,
"forbidden_paths": [
".git",
"node_modules"
]
},
"validation": [
"node scripts/validate-modules.js ./src/evolve ./src/gep/solidify"
]
}
]
```
Context [Capsule Preview] (Reference for Past Success):
```json
[
{
"type": "Capsule",
"schema_version": "1.5.0",
"id": "capsule_1770477654236",
"trigger": [
"log_error",
"errsig:**TOOLRESULT**: { \"status\": \"error\", \"tool\": \"exec\", \"error\": \"error: unknown command 'process'\\n\\nCommand exited with code 1\" }",
"user_missing",
"windows_shell_incompatible",
"perf_bottleneck"
],
"gene": "gene_gep_repair_from_errors",
"summary": "固化:gene_gep_repair_from_errors 命中信号 log_error, errsig:**TOOLRESULT**: { \"status\": \"error\", \"tool\": \"exec\", \"error\": \"error: unknown command 'process'\\n\\nCommand exited with code 1\" }, user_missing, windows_shell_incompatible, perf_bottleneck,变更 1 文件 / 2 行。",
"confidence": 0.85,
"blast_radius": {
"files": 1,
"lines": 2
},
"outcome": {
"status": "success",
"score": 0.85
},
"success_streak": 1,
"env_fingerprint": {
"node_version": "v22.22.0",
"platform": "linux",
"arch": "x64",
"os_release": "6.1.0-42-cloud-amd64",
"evolver_version": "1.7.0",
"cwd": ".",
"captured_at": "2026-02-07T15:20:54.155Z"
},
"a2a": {
"eligible_to_broadcast": false
},
"asset_id": "sha256:3eed0cd5038f9e85fbe0d093890e291e9b8725644c766e6cce40bf62d0f5a2e8"
},
{
"type": "Capsule",
"schema_version": "1.5.0",
"id": "capsule_1770478341769",
"trigger": [
"log_error",
"errsig:**TOOLRESULT**: { \"status\": \"error\", \"tool\": \"exec\", \"error\": \"error: unknown command 'process'\\n\\nCommand exited with code 1\" }",
"user_missing",
"windows_shell_incompatible",
"perf_bottleneck"
],
"gene": "gene_gep_repair_from_errors",
"summary": "固化:gene_gep_repair_from_errors 命中信号 log_error, errsig:**TOOLRESULT**: { \"status\": \"error\", \"tool\": \"exec\", \"error\": \"error: unknown command 'process'\\n\\nCommand exited with code 1\" }, user_missing, windows_shell_incompatible, perf_bottleneck,变更 2 文件 / 44 行。",
"confidence": 0.85,
"blast_radius": {
"files": 2,
"lines": 44
},
"outcome": {
"status": "success",
"score": 0.85
},
"success_streak": 1,
"env_fingerprint": {
"node_version": "v22.22.0",
"platform": "linux",
"arch": "x64",
"os_release": "6.1.0-42-cloud-amd64",
"evolver_version": "1.7.0",
"cwd": ".",
"captured_at": "2026-02-07T15:32:21.678Z"
},
"a2a": {
"eligible_to_broadcast": false
},
"asset_id": "sha256:20d971a3c4cb2b75f9c045376d1aa003361c12a6b89a4b47b7e81dbd4f4d8fe8"
}
]
```
Context [Capability Candidates]:
- cand_b9a66a5c: Harden session log detection and fallback behavior
- input: Recent session transcript + memory snippets + user instructions
- output: A safe, auditable evolution patch guided by GEP assets
- invariants: Protocol order, small reversible patches, validation, append-only events
- params: Signals: memory_missing, session_logs_missing
- failure_points: Missing signals, over-broad changes, skipped validation, missing knowledge solidification
- evidence: Signal present: session_logs_missing
Context [Hub Matched Solution]:
(no hub match)
Context [External Candidates]:
(none)
Context [Execution]:
Runtime state:
- System health: Uptime: 455.2h | Node: v22.22.0 | Agent RSS: 54.7MB | Disk: 8% (864.6G free) | Node Processes: 1 | Integrations: Nominal
- Agent state: Mood: Unknown
- Scan duration: 24ms
- Memory size: 0 bytes
- Skills available (if any):
[skills directory not found]
Notes:
- Review mode: disabled.
- Report requirement:
- Use `message` tool.
- Title: Evolution Cycle #0001
- Status: [SUCCESS]
- Changes: Detail exactly what was improved.
- Workspace sync: optional/disabled in this environment.
Recent Evolution History (last 8 cycles -- DO NOT repeat the same intent+signal+gene):
1. [innovate] signals=[user_missing, user_feature_request] gene=gene_gep_innovate_from_opportunity outcome=failed @2026-02-07T15:13:21.173Z
2. [repair] signals=[log_error, errsig:**TOOLRESULT**: { "status": "error", "tool": "exec", "error": "error: unknown command 'process'\n\nCommand exited with code 1" }, user_missing] gene=gene_gep_repair_from_errors outcome=success @2026-02-07T15:20:54.236Z
3. [repair] signals=[log_error, errsig:**TOOLRESULT**: { "status": "error", "tool": "exec", "error": "error: unknown command 'process'\n\nCommand exited with code 1" }, user_missing] gene=gene_gep_repair_from_errors outcome=success @2026-02-07T15:32:21.769Z
IMPORTANT: If you see 3+ consecutive "repair" cycles with the same gene, you MUST switch to "innovate" intent.
External candidates (A2A receive zone; staged only, never execute directly):
(none)
Global memory (MEMORY.md):
```
[MEMORY.md MISSING]
```
User registry (USER.md):
```
[USER.md MISSING]
```
Recent memory snippet:
```
[MISSING] /home/starmen1265/.openclaw/memory/2026-03-03.md
```
Recent session transcript:
```
[NO JSONL FILES]
```
Mutation directive:
[Signal Hints]
- recent_error_count: 0
- stability: stable
- recommended_intent: optimize
━━━━━━━━━━━━━━━━━━━━━━
MANDATORY POST-SOLIDIFY STEP (Wrapper Authority -- Cannot Be Skipped)
━━━━━━━━━━━━━━━━━━━━━━
After solidify, a status summary file MUST exist for this cycle.
Preferred path: evolver core auto-writes it during solidify.
The wrapper will handle reporting AFTER git push.
If core write is unavailable for any reason, create fallback status JSON manually.
Write a JSON file with your status:
```bash
cat > ./logs/status_undefined.json << 'STATUSEOF'
{
"result": "success|failed",
"en": "Status: [INTENT] <describe what you did in 1-2 sentences, in English>",
"zh": "状态: [意图] <用中文描述你做了什么,1-2句>"
}
STATUSEOF
```
Rules:
- "en" field: English status. "zh" field: Chinese status. Content must match (different language).
- Add "result" with value success or failed.
- INTENT must be one of: INNOVATION, REPAIR, OPTIMIZE (or Chinese: 创新, 修复, 优化)
- Do NOT use generic text like "Step Complete", "Cycle finished", "周期已完成". Describe the actual work.
- Example:
{"result":"success","en":"Status: [INNOVATION] Created auto-scheduler that syncs calendar to HEARTBEAT.md","zh":"状态: [创新] 创建了自动调度器,将日历同步到 HEARTBEAT.md"}
+48
View File
@@ -0,0 +1,48 @@
{
"last_action": {
"action_id": "act_1772551119186_a5982a82",
"signal_key": "memory_missing|session_logs_missing",
"signals": [
"memory_missing",
"session_logs_missing"
],
"mutation_id": "mut_1772551119185",
"mutation_category": "innovate",
"mutation_risk_level": "medium",
"personality_key": "rigor=0.7|creativity=0.3|verbosity=0.3|risk_tolerance=0.4|obedience=0.9",
"personality_state": {
"type": "PersonalityState",
"rigor": 0.7,
"creativity": 0.35,
"verbosity": 0.25,
"risk_tolerance": 0.4,
"obedience": 0.85
},
"gene_id": null,
"gene_category": null,
"hypothesis_id": "hyp_1772551119185_a7a07c6d",
"capsules_used": [],
"had_error": false,
"created_at": "2026-03-03T15:18:39.186Z",
"outcome_recorded": false,
"baseline_observed": {
"agent": "main",
"session_scope": null,
"drift_enabled": false,
"review_mode": false,
"dry_run": false,
"system_health": "Uptime: 455.2h | Node: v22.22.0 | Agent RSS: 54.7MB | Disk: 8% (864.6G free) | Node Processes: 1 | Integrations: Nominal",
"mood": "Mood: Unknown",
"scan_ms": 24,
"memory_size_bytes": 0,
"recent_error_count": 0,
"node": "v22.22.0",
"platform": "linux",
"cwd": "/home/starmen1265/.openclaw/skills/capability-evolver",
"evidence": {
"recent_session_tail": "[NO JSONL FILES]",
"today_log_tail": "[MISSING] /home/starmen1265/.openclaw/memory/2026-03-03.md"
}
}
}
}
+14
View File
@@ -0,0 +1,14 @@
{
"version": 1,
"current": {
"type": "PersonalityState",
"rigor": 0.7,
"creativity": 0.35,
"verbosity": 0.25,
"risk_tolerance": 0.4,
"obedience": 0.85
},
"stats": {},
"history": [],
"updated_at": "2026-03-03T15:18:39.185Z"
}
+11 -3
View File
@@ -1,7 +1,7 @@
{
"meta": {
"lastTouchedVersion": "2026.2.9",
"lastTouchedAt": "2026-03-03T15:06:05.649Z"
"lastTouchedAt": "2026-03-03T15:31:40.861Z"
},
"wizard": {
"lastRunAt": "2026-02-13T15:47:05.928Z",
@@ -110,10 +110,10 @@
"agents": {
"defaults": {
"model": {
"primary": "m25",
"primary": "g5",
"fallbacks": [
"g47f",
"g5",
"m25",
"q3",
"k2p5"
]
@@ -147,6 +147,7 @@
"list": [
{
"id": "haonan",
"workspace":"/home/starmen1265/.openclaw/agents/haonan/workspace",
"skills": [
"common-require",
"capability-evolver",
@@ -155,6 +156,7 @@
},
{
"id": "knowledge-base",
"workspace":"/home/starmen1265/.openclaw/agents/knowledge-base/workspace",
"skills": [
"common-require",
"kb-workflow",
@@ -164,6 +166,7 @@
},
{
"id": "dev-assist",
"workspace":"/home/starmen1265/.openclaw/agents/dev-assist/workspace",
"skills": [
"common-require",
"clawlist",
@@ -173,6 +176,7 @@
},
{
"id": "ui-designer",
"workspace":"/home/starmen1265/.openclaw/agents/ui-designer/workspace",
"skills": [
"common-require",
"capability-evolver",
@@ -181,6 +185,7 @@
},
{
"id": "thinker",
"workspace":"/home/starmen1265/.openclaw/agents/thinker/workspace",
"skills": [
"common-require",
"capability-evolver",
@@ -286,6 +291,9 @@
},
"session-memory": {
"enabled": true
},
"self-improvement": {
"enabled": true
}
}
}
+21 -46
View File
@@ -1,7 +1,7 @@
{
"meta": {
"lastTouchedVersion": "2026.2.9",
"lastTouchedAt": "2026-02-15T15:57:58.358Z"
"lastTouchedAt": "2026-03-03T15:06:05.649Z"
},
"wizard": {
"lastRunAt": "2026-02-13T15:47:05.928Z",
@@ -147,70 +147,45 @@
"list": [
{
"id": "haonan",
"name": "\u9648\u6d69\u5357",
"workspace": "/home/starmen1265/.openclaw/agents/haonan/workspace",
"model": "m25",
"skills": [
"common-require"
],
"identity": {
"name": "\u9648\u6d69\u5357",
"emoji": "\ud83d\udcda"
}
"common-require",
"capability-evolver",
"self-improving-agent"
]
},
{
"id": "knowledge-base",
"name": "\u77e5\u8bc6\u5e93\u52a9\u624b",
"workspace": "/home/starmen1265/.openclaw/agents/knowledge-base/workspace",
"model": "g5",
"skills": [
"common-require",
"kb-workflow"
],
"identity": {
"name": "\u77e5\u8bc6\u5e93\u52a9\u624b",
"emoji": "\ud83d\udcda"
}
"kb-workflow",
"capability-evolver",
"self-improving-agent"
]
},
{
"id": "dev-assist",
"name": "\u7a0b\u5e8f\u5f00\u53d1\u52a9\u624b",
"workspace": "/home/starmen1265/.openclaw/agents/dev-assist/workspace",
"model": "g5",
"skills": [
"common-require",
"clawlist"
],
"identity": {
"name": "\u7a0b\u5e8f\u5f00\u53d1\u52a9\u624b",
"emoji": "\ud83d\udcda"
}
"clawlist",
"capability-evolver",
"self-improving-agent"
]
},
{
"id": "ui-designer",
"name": "UI/UX\u8bbe\u8ba1\u5e08",
"workspace": "/home/starmen1265/.openclaw/agents/ui-designer/workspace",
"model": "g5",
"skills": [
"common-require"
],
"identity": {
"name": "\u8bbe\u8ba1\u5e08",
"emoji": "\ud83c\udfa8"
}
"common-require",
"capability-evolver",
"self-improving-agent"
]
},
{
"id": "thinker",
"name": "\u5ba1\u67e5\u5458",
"workspace": "/home/starmen1265/.openclaw/agents/thinker/workspace",
"model": "g5",
"skills": [
"common-require"
],
"identity": {
"name": "\u5ba1\u67e5\u5458",
"emoji": "\ud83d\udd0d"
}
"common-require",
"capability-evolver",
"self-improving-agent"
]
}
]
},
+56 -60
View File
@@ -147,20 +147,20 @@
"list": [
{
"id": "haonan",
"name": "陈浩南",
"name": "\u9648\u6d69\u5357",
"workspace": "/home/starmen1265/.openclaw/agents/haonan/workspace",
"model": "m25",
"skills": [
"common-require"
],
"identity": {
"name": "陈浩南",
"emoji": "📚"
"name": "\u9648\u6d69\u5357",
"emoji": "\ud83d\udcda"
}
},
{
"id": "knowledge-base",
"name": "知识库助手",
"name": "\u77e5\u8bc6\u5e93\u52a9\u624b",
"workspace": "/home/starmen1265/.openclaw/agents/knowledge-base/workspace",
"model": "g5",
"skills": [
@@ -168,13 +168,13 @@
"kb-workflow"
],
"identity": {
"name": "知识库助手",
"emoji": "📚"
"name": "\u77e5\u8bc6\u5e93\u52a9\u624b",
"emoji": "\ud83d\udcda"
}
},
{
"id": "dev-assist",
"name": "程序开发助手",
"name": "\u7a0b\u5e8f\u5f00\u53d1\u52a9\u624b",
"workspace": "/home/starmen1265/.openclaw/agents/dev-assist/workspace",
"model": "g5",
"skills": [
@@ -182,21 +182,34 @@
"clawlist"
],
"identity": {
"name": "程序开发助手",
"emoji": "📚"
"name": "\u7a0b\u5e8f\u5f00\u53d1\u52a9\u624b",
"emoji": "\ud83d\udcda"
}
},
{
"id": "ui-designer",
"name": "UI/UX设计师",
"name": "UI/UX\u8bbe\u8ba1\u5e08",
"workspace": "/home/starmen1265/.openclaw/agents/ui-designer/workspace",
"model": "g5",
"skills": [
"common-require"
],
"identity": {
"name": "设计师",
"emoji": "🎨"
"name": "\u8bbe\u8ba1\u5e08",
"emoji": "\ud83c\udfa8"
}
},
{
"id": "thinker",
"name": "\u5ba1\u67e5\u5458",
"workspace": "/home/starmen1265/.openclaw/agents/thinker/workspace",
"model": "g5",
"skills": [
"common-require"
],
"identity": {
"name": "\u5ba1\u67e5\u5458",
"emoji": "\ud83d\udd0d"
}
}
]
@@ -229,71 +242,38 @@
},
"bindings": [
{
"agentId": "knowledge-base",
"agentId": "haonan",
"match": {
"channel": "feishu",
"peer": {
"kind": "group",
"id": "oc_79bcbd274c7cff8757b52e2ec1d8e12b"
}
}
},
{
"agentId": "knowledge-base",
"match": {
"channel": "wecom",
"peer": {
"kind": "group",
"id": "wr_wAnbgAAXlqKMkyGznqpPMYZg2YTYg"
}
"accountId": "feishu-pm"
}
},
{
"agentId": "dev-assist",
"match": {
"channel": "feishu",
"peer": {
"kind": "group",
"id": "oc_f86956cb9cecb7ad38c0f621377a23ae"
}
"accountId": "feishu-dev"
}
},
{
"agentId": "dev-assist",
"match": {
"channel": "wecom",
"peer": {
"kind": "group",
"id": "wr_wAnbgAA_ot7JsLAgOD2IMcsGaT_8Q"
}
}
},
{
"agentId": "haonan",
"agentId": "knowledge-base",
"match": {
"channel": "feishu",
"accountId": "ou_be83febac9ee2afaa343c642bc697c48"
"accountId": "feishu-worker"
}
},
{
"agentId": "haonan",
"agentId": "thinker",
"match": {
"channel": "wecom",
"accountId": "ZhangQing"
"channel": "feishu",
"accountId": "feishu-qa"
}
},
{
"agentId": "ui-designer",
"match": {
"channel": "feishu",
"accountId": "ou_6de30318970345c4c79bea292b93130c"
}
},
{
"agentId": "ui-designer",
"match": {
"channel": "wecom",
"accountId": "bala"
"accountId": "feishu-art"
}
}
],
@@ -338,13 +318,29 @@
"channels": {
"feishu": {
"enabled": true,
"dmPolicy": "open",
"accounts": {
"feishu-pm": {
"appId": "cli_a9f74dc9a3f8dbce",
"appSecret": "QtnvzRUhhFoNTTorxJuAwgSJ3ocPv0p1",
"dmPolicy": "allowlist",
"allowFrom": [
"ou_be83febac9ee2afaa343c642bc697c48",
"ou_6de30318970345c4c79bea292b93130c"
],
"appSecret": "QtnvzRUhhFoNTTorxJuAwgSJ3ocPv0p1"
},
"feishu-dev": {
"appId": "cli_a92bae598f789bd7",
"appSecret": "kmwCLAI8IdZZn1KtwiPTVg758u0unGzY"
},
"feishu-worker": {
"appId": "cli_a92ba8ad3cb81bde",
"appSecret": "8bnMNIzy4QAv96c2xjmjgdUILO1qrO2w"
},
"feishu-qa": {
"appId": "cli_a92baf81053f1bc0",
"appSecret": "D5Pk8Fzwj5LWQr1FGnBlCbbJa3Ox7bJQ"
},
"feishu-art": {
"appId": "cli_a92baa31fdf8dbc0",
"appSecret": "BKjGWv1MTz7WmTn0I5A2EhuKyzwuuUnk"
}
},
"domain": "feishu",
"groupPolicy": "open",
"requireMention": true
+2 -2
View File
@@ -1,7 +1,7 @@
{
"meta": {
"lastTouchedVersion": "2026.2.9",
"lastTouchedAt": "2026-02-15T15:54:32.431Z"
"lastTouchedAt": "2026-02-15T15:57:58.358Z"
},
"wizard": {
"lastRunAt": "2026-02-13T15:47:05.928Z",
@@ -300,7 +300,7 @@
"messages": {
"ackReactionScope": "group-mentions",
"tts": {
"auto": "always",
"auto": "tagged",
"provider": "edge",
"edge": {
"enabled": true,
+2 -2
View File
@@ -1,7 +1,7 @@
{
"meta": {
"lastTouchedVersion": "2026.2.9",
"lastTouchedAt": "2026-02-15T15:50:36.798Z"
"lastTouchedAt": "2026-02-15T15:54:32.431Z"
},
"wizard": {
"lastRunAt": "2026-02-13T15:47:05.928Z",
@@ -300,7 +300,7 @@
"messages": {
"ackReactionScope": "group-mentions",
"tts": {
"auto": "tagged",
"auto": "always",
"provider": "edge",
"edge": {
"enabled": true,
+2 -2
View File
@@ -1,7 +1,7 @@
{
"meta": {
"lastTouchedVersion": "2026.2.9",
"lastTouchedAt": "2026-02-15T15:43:40.588Z"
"lastTouchedAt": "2026-02-15T15:50:36.798Z"
},
"wizard": {
"lastRunAt": "2026-02-13T15:47:05.928Z",
@@ -300,7 +300,7 @@
"messages": {
"ackReactionScope": "group-mentions",
"tts": {
"auto": "always",
"auto": "tagged",
"provider": "edge",
"edge": {
"enabled": true,
+100 -1
View File
@@ -1,4 +1,103 @@
=== Capability Evolver 运行开始 ===
时间: 2026年 03月 03日 星期二 23:13:28 CST
时间: 2026年 03月 05日 星期四 19:00:23 CST
=== 推送到远端 ===
Warning: Permanently added '[192.168.1.22]:2223' (RSA) to the list of known hosts.
Everything up-to-date
推送完成
发现有未提交的更改,先提交...
创建备份分支: backup/evolver-20260305-190024
Warning: Permanently added '[192.168.1.22]:2223' (RSA) to the list of known hosts.
remote:
remote: Create a new pull request for 'backup/evolver-20260305-190024':
remote: http://192.168.1.22:8418/starmen1265/openclaw/compare/main...backup/evolver-20260305-190024
remote:
remote: . Processing 1 references
remote: Processed 1 references in total
To gitea-account:starmen1265/openclaw.git
* [new branch] backup/evolver-20260305-190024 -> backup/evolver-20260305-190024
=== Git 备份完成 ===
=== 运行 Capability Evolver (repair-only 模式) ===
[Evolver] Warning: dotenv not found or failed to load .env
Usage: node index.js [run|/evolve|solidify|distill|asset-log] [--loop]
- solidify flags:
- --dry-run
- --no-rollback
- --intent=repair|optimize|innovate
- --summary=...
- distill flags:
- --response-file=<path> (LLM response file for skill distillation)
- asset-log flags:
- --run=<run_id> (filter by run ID)
- --action=<action> (filter: hub_search_hit, hub_search_miss, asset_reuse, asset_reference, asset_publish, asset_publish_skip)
- --last=<N> (show last N entries)
- --since=<ISO_date> (entries after date)
- --json (raw JSON output)
=== Evolver 运行完成 (exit code: 0) ===
=== 本次变更 ===
M agents/dev-assist/sessions/sessions.json
D agents/dev-assist/workspace/TODO.md
M agents/haonan/agent/auth-profiles.json
M agents/haonan/sessions/sessions.json
M agents/haonan/workspace/MEMORY.md
M agents/haonan/workspace/SOUL.md
M agents/knowledge-base/workspace/AGENTS.md
D agents/knowledge-base/workspace/knowledge-base/videos/BV1QjcszrEEV.md
M agents/knowledge-base/workspace/memory/2026-03-03.md
M agents/main/agent/auth-profiles.json
M cron/jobs.json
M cron/jobs.json.bak
M devices/paired.json
M identity/device-auth.json
M openclaw.json
M openclaw.json.bak
M openclaw.json.bak.1
M openclaw.json.bak.2
M openclaw.json.bak.3
M openclaw.json.bak.4
M scripts/evolver-output.txt
M scripts/evolver-run.sh
D scripts/git-auto-sync.sh
M skills/capability-evolver/README.md
M skills/capability-evolver/README.zh-CN.md
D skills/capability-evolver/_meta.json
M skills/capability-evolver/index.js
M skills/capability-evolver/package.json
M skills/capability-evolver/src/evolve.js
M skills/capability-evolver/src/gep/a2a.js
M skills/capability-evolver/src/gep/a2aProtocol.js
M skills/capability-evolver/src/gep/hubSearch.js
M skills/capability-evolver/src/gep/paths.js
M skills/capability-evolver/src/gep/prompt.js
M skills/capability-evolver/src/gep/selector.js
M skills/capability-evolver/src/gep/signals.js
M skills/capability-evolver/src/gep/skillDistiller.js
M skills/capability-evolver/src/gep/solidify.js
M skills/capability-evolver/src/gep/taskReceiver.js
M skills/capability-evolver/src/ops/cleanup.js
?? agents/haonan/workspace/memory/2026-03-04.md
?? hooks/
?? media/inbound/d43f43ec-4953-42e2-a550-8b2474b9939b.jpg
?? memory/
?? skills/capability-evolver/.gitignore
?? skills/capability-evolver/LICENSE
?? skills/capability-evolver/assets/cover.png
?? skills/capability-evolver/assets/gep/failed_capsules.json
?? skills/capability-evolver/src/gep/assetCallLog.js
?? skills/capability-evolver/src/gep/llmReview.js
?? skills/capability-evolver/src/gep/narrativeMemory.js
?? skills/capability-evolver/src/gep/reflection.js
?? workspace-dev-assist/
?? workspace-knowledge-base/
?? workspace/.learnings/
=== 推送变更到远端 ===
Warning: Permanently added '[192.168.1.22]:2223' (RSA) to the list of known hosts.
Everything up-to-date
=== 输出完成 ===
+26 -1
View File
@@ -10,9 +10,20 @@ echo "=== Capability Evolver 运行开始 ===" > "$OUTPUT_FILE"
echo "时间: $(date)" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
# 切换到 openclaw 目录进行 git 备份
# 切换到 openclaw 目录
cd "$OPENCLAW_DIR"
# 推送到远端(如果有远端)
if git remote get-url origin &>/dev/null; then
echo "=== 推送到远端 ===" >> "$OUTPUT_FILE"
git push origin main >> "$OUTPUT_FILE" 2>&1
echo "推送完成" >> "$OUTPUT_FILE"
else
echo "无远端配置,跳过推送" >> "$OUTPUT_FILE"
fi
echo "" >> "$OUTPUT_FILE"
# 检查是否有未提交的更改
if [[ -n $(git status --porcelain) ]]; then
echo "发现有未提交的更改,先提交..." >> "$OUTPUT_FILE"
@@ -26,6 +37,11 @@ echo "创建备份分支: $BACKUP_BRANCH" >> "$OUTPUT_FILE"
git branch -D "$BACKUP_BRANCH" 2>/dev/null || true
git branch "$BACKUP_BRANCH" 2>/dev/null
# 推送备份分支
if git remote get-url origin &>/dev/null; then
git push origin "$BACKUP_BRANCH" >> "$OUTPUT_FILE" 2>&1 || echo "备份分支推送失败" >> "$OUTPUT_FILE"
fi
echo "" >> "$OUTPUT_FILE"
echo "=== Git 备份完成 ===" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
@@ -46,5 +62,14 @@ echo "" >> "$OUTPUT_FILE"
echo "=== 本次变更 ===" >> "$OUTPUT_FILE"
git status --short >> "$OUTPUT_FILE"
# 如果有变更,推送
if [[ -n $(git status --porcelain) ]]; then
echo "" >> "$OUTPUT_FILE"
echo "=== 推送变更到远端 ===" >> "$OUTPUT_FILE"
git add -A
git commit -m "Evolver auto evolution - $(date)" 2>/dev/null || true
git push origin main >> "$OUTPUT_FILE" 2>&1 || echo "推送失败" >> "$OUTPUT_FILE"
fi
echo "" >> "$OUTPUT_FILE"
echo "=== 输出完成 ===" >> "$OUTPUT_FILE"
-17
View File
@@ -1,17 +0,0 @@
#!/bin/bash
# 自动检查并提交 git 修改
cd ~/.openclaw
git add -A
if ! git diff --cached --quiet; then
git commit -m "自动同步: $(date '+%Y-%m-%d %H:%M')"
git push origin main
fi
cd ~/.openclaw/extensions
git add -A
if ! git diff --cached --quiet; then
git commit -m "自动同步: $(date '+%Y-%m-%d %H:%M')"
git push origin main
fi
+14
View File
@@ -0,0 +1,14 @@
node_modules/
.env
memory/
assets/gep/candidates.jsonl
assets/gep/external_candidates.jsonl
dist-public/
# Docker / vibe testing
test/tmp/
.docker-test-state/
# Local identity persistence (device + node)
.evomap_device_id
.evomap_node_id
+22
View File
@@ -0,0 +1,22 @@
MIT License
Copyright (c) 2026 OpenClaw
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+9 -2
View File
@@ -19,6 +19,11 @@ Capability Evolver is the core engine behind **[EvoMap](https://evomap.ai)**, a
Keywords: protocol-constrained evolution, audit trail, genes and capsules, prompt governance.
## Prerequisites
- **Node.js** >= 18
- **Git** -- Required. Evolver uses git for rollback, blast radius calculation, and solidify. Running in a non-git directory will fail with a clear error message.
## Try It Now (Minimal)
```bash
@@ -221,14 +226,16 @@ The script automatically detects if compatible local skills (like `skills/feishu
## Acknowledgments
- [onthebigtree](https://github.com/onthebigtree) -- Inspired the creation of evomap evolution network.
- [onthebigtree](https://github.com/onthebigtree) -- Inspired the creation of evomap evolution network. Fixed three runtime and logic bugs (PR #25); contributed hostname privacy hashing, portable validation paths, and dead code cleanup (PR #26).
- [lichunr](https://github.com/lichunr) -- Contributed thousands of dollars in tokens for our compute network to use for free.
- [shinjiyu](https://github.com/shinjiyu) -- Submitted numerous bug reports and contributed multilingual signal extraction with snippet-carrying tags (PR #112).
- [voidborne-d](https://github.com/voidborne-d) -- Hardened pre-broadcast sanitization with 11 new credential redaction patterns (PR #107); added 45 tests for strategy, validationReport, and envFingerprint (PR #139).
- [blackdogcat](https://github.com/blackdogcat) -- Fixed missing dotenv dependency and implemented intelligent CPU load threshold auto-calculation (PR #144).
- [LKCY33](https://github.com/LKCY33) -- Fixed .env loading path and directory permissions (PR #21).
- [hendrixAIDev](https://github.com/hendrixAIDev) -- Fixed performMaintenance() running in dry-run mode (PR #68).
- [toller892](https://github.com/toller892) -- Independently identified and reported the events.jsonl forbidden_paths bug (PR #149).
- [upbit](https://github.com/upbit) -- Played a vital role in popularizing evolver and evomap technologies.
- [Chi Jianqiang](https://mowen.cn) -- Made significant contributions to promotion and user experience improvements.
- More contributors to be added.
## License
+11 -3
View File
@@ -27,6 +27,11 @@ Capability Evolver 是 **[EvoMap](https://evomap.ai)** 的核心引擎。EvoMap
- **动态集成**:自动检测并使用本地工具,如果不存在则回退到通用模式。
- **持续循环模式**:持续运行的自我进化循环。
## 前置条件
- **Node.js** >= 18
- **Git** -- 必需。Evolver 依赖 git 进行回滚、变更范围计算和固化(solidify)。在非 git 目录中运行会直接报错并退出。
## 使用方法
### 标准运行(自动化)
@@ -190,13 +195,16 @@ MAJOR.MINOR.PATCH
## 鸣谢
- [onthebigtree](https://github.com/onthebigtree) -- 启发了 evomap 进化网络的诞生。
- [onthebigtree](https://github.com/onthebigtree) -- 启发了 evomap 进化网络的诞生。修复了三个运行时逻辑 bug (PR #25);贡献了主机名隐私哈希、可移植验证路径和死代码清理 (PR #26)。
- [lichunr](https://github.com/lichunr) -- 提供了数千美金 Token 供算力网络免费使用。
- [shinjiyu](https://github.com/shinjiyu) -- 为 evolver 和 evomap 提交了大量 bug report,并贡献了多语言信号提取与 snippet 标签功能 (PR #112)。
- [voidborne-d](https://github.com/voidborne-d) -- 为预广播脱敏层新增 11 种凭证检测模式,强化安全防护 (PR #107)。
- [voidborne-d](https://github.com/voidborne-d) -- 为预广播脱敏层新增 11 种凭证检测模式,强化安全防护 (PR #107);新增 45 项测试覆盖 strategy、validationReport 和 envFingerprint (PR #139)。
- [blackdogcat](https://github.com/blackdogcat) -- 修复 dotenv 缺失依赖并实现智能 CPU 负载阈值自动计算 (PR #144)。
- [LKCY33](https://github.com/LKCY33) -- 修复 .env 加载路径和目录权限问题 (PR #21)。
- [hendrixAIDev](https://github.com/hendrixAIDev) -- 修复 dry-run 模式下 performMaintenance() 仍执行的问题 (PR #68)。
- [toller892](https://github.com/toller892) -- 独立发现并报告了 events.jsonl forbidden_paths 冲突 bug (PR #149)。
- [upbit](https://github.com/upbit) -- 在 evolver 和 evomap 技术的普及中起到了至关重要的作用。
- [池建强](https://mowen.cn) -- 在传播和用户体验改进过程中做出了巨大贡献。
- 其余贡献者扩充中。
## 许可证
MIT
-17
View File
@@ -1,17 +0,0 @@
{
"owner": "autogame-17",
"slug": "capability-evolver",
"displayName": "Capability Evolver",
"latest": {
"version": "1.20.4",
"publishedAt": 1772344308631,
"commit": "https://github.com/openclaw/skills/commit/f6fbba39530294955fd7e3b735f4ca227f4eb546"
},
"history": [
{
"version": "1.14.0",
"publishedAt": 1771472983583,
"commit": "https://github.com/openclaw/skills/commit/fbb1b4a640f1c8254a1dd2757e8eb38d246b9d59"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

@@ -0,0 +1,4 @@
{
"version": 1,
"failed_capsules": []
}
+63 -5
View File
@@ -43,14 +43,18 @@ function acquireLock() {
try {
if (fs.existsSync(lockFile)) {
const pid = parseInt(fs.readFileSync(lockFile, 'utf8').trim(), 10);
if (!Number.isFinite(pid) || pid <= 0) {
console.log('[Singleton] Corrupt lock file (invalid PID). Taking over.');
} else {
try {
process.kill(pid, 0); // Check if process exists
process.kill(pid, 0);
console.log(`[Singleton] Evolver loop already running (PID ${pid}). Exiting.`);
return false;
} catch (e) {
console.log(`[Singleton] Stale lock found (PID ${pid}). Taking over.`);
}
}
}
fs.writeFileSync(lockFile, String(process.pid));
return true;
} catch (err) {
@@ -88,7 +92,8 @@ async function main() {
process.env.EVOLVE_BRIDGE = 'false';
console.log('Loop mode enabled (internal daemon).');
const solidifyStatePath = path.join(__dirname, 'memory', 'evolution_solidify_state.json');
const { getEvolutionDir } = require('./src/gep/paths');
const solidifyStatePath = path.join(getEvolutionDir(), 'evolution_solidify_state.json');
const minSleepMs = parseMs(process.env.EVOLVER_MIN_SLEEP_MS, 2000);
const maxSleepMs = parseMs(process.env.EVOLVER_MAX_SLEEP_MS, 300000);
@@ -149,7 +154,7 @@ async function main() {
const memMb = process.memoryUsage().rss / 1024 / 1024;
if (cycleCount >= maxCyclesPerProcess || memMb > maxRssMb) {
console.log(`[Daemon] Restarting self (cycles=${cycleCount}, rssMb=${memMb.toFixed(0)})`);
releaseLock();
try {
const spawnOpts = {
detached: true,
stdio: 'ignore',
@@ -158,7 +163,11 @@ async function main() {
};
const child = spawn(process.execPath, [__filename, ...args], spawnOpts);
child.unref();
releaseLock();
process.exit(0);
} catch (spawnErr) {
console.error('[Daemon] Spawn failed, continuing current process:', spawnErr.message);
}
}
}
@@ -268,15 +277,64 @@ async function main() {
process.exit(2);
}
} else if (command === 'asset-log') {
const { summarizeCallLog, readCallLog, getLogPath } = require('./src/gep/assetCallLog');
const runIdFlag = args.find(a => typeof a === 'string' && a.startsWith('--run='));
const actionFlag = args.find(a => typeof a === 'string' && a.startsWith('--action='));
const lastFlag = args.find(a => typeof a === 'string' && a.startsWith('--last='));
const sinceFlag = args.find(a => typeof a === 'string' && a.startsWith('--since='));
const jsonMode = args.includes('--json');
const opts = {};
if (runIdFlag) opts.run_id = runIdFlag.slice('--run='.length);
if (actionFlag) opts.action = actionFlag.slice('--action='.length);
if (lastFlag) opts.last = parseInt(lastFlag.slice('--last='.length), 10);
if (sinceFlag) opts.since = sinceFlag.slice('--since='.length);
if (jsonMode) {
const entries = readCallLog(opts);
console.log(JSON.stringify(entries, null, 2));
} else {
console.log(`Usage: node index.js [run|/evolve|solidify|distill] [--loop]
const summary = summarizeCallLog(opts);
console.log(`\n[Asset Call Log] ${getLogPath()}`);
console.log(` Total entries: ${summary.total_entries}`);
console.log(` Unique assets: ${summary.unique_assets}`);
console.log(` Unique runs: ${summary.unique_runs}`);
console.log(` By action:`);
for (const [action, count] of Object.entries(summary.by_action)) {
console.log(` ${action}: ${count}`);
}
if (summary.entries.length > 0) {
console.log(`\n Recent entries:`);
const show = summary.entries.slice(-10);
for (const e of show) {
const ts = e.timestamp ? e.timestamp.slice(0, 19) : '?';
const assetShort = e.asset_id ? e.asset_id.slice(0, 20) + '...' : '(none)';
const sigPreview = Array.isArray(e.signals) ? e.signals.slice(0, 3).join(', ') : '';
console.log(` [${ts}] ${e.action || '?'} asset=${assetShort} score=${e.score || '-'} mode=${e.mode || '-'} signals=[${sigPreview}] run=${e.run_id || '-'}`);
}
} else {
console.log('\n No entries found.');
}
console.log('');
}
} else {
console.log(`Usage: node index.js [run|/evolve|solidify|distill|asset-log] [--loop]
- solidify flags:
- --dry-run
- --no-rollback
- --intent=repair|optimize|innovate
- --summary=...
- distill flags:
- --response-file=<path> (LLM response file for skill distillation)`);
- --response-file=<path> (LLM response file for skill distillation)
- asset-log flags:
- --run=<run_id> (filter by run ID)
- --action=<action> (filter: hub_search_hit, hub_search_miss, asset_reuse, asset_reference, asset_publish, asset_publish_skip)
- --last=<N> (show last N entries)
- --since=<ISO_date> (entries after date)
- --json (raw JSON output)`);
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "evolver",
"version": "1.20.4",
"version": "1.23.0",
"description": "A GEP-powered self-evolution engine for AI agents. Features automated log analysis and Genome Evolution Protocol (GEP) for auditable, reusable evolution assets.",
"main": "index.js",
"keywords": [
+60
View File
@@ -18,6 +18,7 @@ const {
const { selectGeneAndCapsule, matchPatternToSignals } = require('./gep/selector');
const { buildGepPrompt, buildReusePrompt, buildHubMatchedBlock } = require('./gep/prompt');
const { hubSearch } = require('./gep/hubSearch');
const { logAssetCall } = require('./gep/assetCallLog');
const { extractCapabilityCandidates, renderCandidatesPreview } = require('./gep/candidates');
const memoryAdapter = require('./gep/memoryGraphAdapter');
const {
@@ -35,6 +36,8 @@ const { buildMutation, isHighRiskMutationAllowed } = require('./gep/mutation');
const { selectPersonalityForRun } = require('./gep/personality');
const { clip, writePromptArtifact, renderSessionsSpawnCall } = require('./gep/bridge');
const { getEvolutionDir } = require('./gep/paths');
const { shouldReflect, buildReflectionContext, recordReflection } = require('./gep/reflection');
const { loadNarrativeSummary } = require('./gep/narrativeMemory');
const REPO_ROOT = getRepoRoot();
@@ -744,6 +747,19 @@ async function run() {
delete process.env.FORCE_INNOVATION;
// SAFEGUARD: Git repository check.
// Solidify, rollback, and blast radius all depend on git. Without a git repo
// these operations silently produce empty results, leading to data loss.
try {
execSync('git rev-parse --git-dir', { cwd: REPO_ROOT, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 5000 });
} catch (_) {
console.error('[Evolver] FATAL: Not a git repository (' + REPO_ROOT + ').');
console.error('[Evolver] Evolver requires git for rollback, blast radius calculation, and solidify.');
console.error('[Evolver] Run "git init && git add -A && git commit -m init" in your project root, then try again.');
process.exitCode = 1;
return;
}
var dormantHypothesis = readDormantHypothesis();
if (dormantHypothesis) {
console.log('[DormantHypothesis] Recovered partial state from previous backoff: ' + (dormantHypothesis.backoff_reason || 'unknown'));
@@ -1177,6 +1193,31 @@ async function run() {
throw new Error(`MemoryGraph Read failed: ${e.message}`);
}
// Reflection Phase: periodically pause to assess evolution strategy.
try {
const cycleState = fs.existsSync(STATE_FILE) ? JSON.parse(fs.readFileSync(STATE_FILE, 'utf8')) : {};
const cycleCount = cycleState.cycleCount || 0;
if (shouldReflect({ cycleCount, recentEvents })) {
const narrativeSummary = loadNarrativeSummary(3000);
const reflectionCtx = buildReflectionContext({
recentEvents,
signals,
memoryAdvice,
narrative: narrativeSummary,
});
recordReflection({
cycle_count: cycleCount,
signals_snapshot: signals.slice(0, 20),
preferred_gene: memoryAdvice && memoryAdvice.preferredGeneId ? memoryAdvice.preferredGeneId : null,
banned_genes: memoryAdvice && Array.isArray(memoryAdvice.bannedGeneIds) ? memoryAdvice.bannedGeneIds : [],
context_preview: reflectionCtx.slice(0, 1000),
});
console.log(`[Reflection] Strategic reflection recorded at cycle ${cycleCount}.`);
}
} catch (e) {
console.log('[Reflection] Failed (non-fatal): ' + (e && e.message ? e.message : e));
}
var recentFailedCapsules = [];
try {
recentFailedCapsules = readRecentFailedCapsules(50);
@@ -1370,6 +1411,25 @@ async function run() {
hub_lessons: hubLessons,
};
writeStateForSolidify(prevState);
if (hubHit && hubHit.hit) {
const assetAction = hubHit.mode === 'direct' ? 'asset_reuse' : 'asset_reference';
logAssetCall({
run_id: runId,
action: assetAction,
asset_id: hubHit.asset_id || null,
asset_type: hubHit.match && hubHit.match.type ? hubHit.match.type : null,
source_node_id: hubHit.source_node_id || null,
chain_id: hubHit.chain_id || null,
score: hubHit.score || null,
mode: hubHit.mode,
signals: Array.isArray(signals) ? signals : [],
extra: {
selected_gene_id: selectedGene && selectedGene.id ? selectedGene.id : null,
task_id: activeTask ? (activeTask.id || activeTask.task_id || null) : null,
},
});
}
} catch (e) {
console.error(`[SolidifyState] Write failed: ${e.message}`);
}
+2 -2
View File
@@ -28,8 +28,8 @@ function getBlastRadiusLimits() {
function isBlastRadiusSafe(blastRadius) {
var lim = getBlastRadiusLimits();
var files = blastRadius && Number.isFinite(Number(blastRadius.files)) ? Number(blastRadius.files) : 0;
var lines = blastRadius && Number.isFinite(Number(blastRadius.lines)) ? Number(blastRadius.lines) : 0;
var files = blastRadius && Number.isFinite(Number(blastRadius.files)) ? Math.max(0, Number(blastRadius.files)) : 0;
var lines = blastRadius && Number.isFinite(Number(blastRadius.lines)) ? Math.max(0, Number(blastRadius.lines)) : 0;
return files <= lim.maxFiles && lines <= lim.maxLines;
}
@@ -96,6 +96,9 @@ function getNodeId() {
// --- Base message builder ---
function buildMessage(params) {
if (!params || typeof params !== 'object') {
throw new Error('buildMessage requires a params object');
}
var messageType = params.messageType;
var payload = params.payload;
var senderId = params.senderId;
@@ -197,14 +200,18 @@ function buildPublishBundle(opts) {
function buildFetch(opts) {
var o = opts || {};
return buildMessage({
messageType: 'fetch',
senderId: o.nodeId,
payload: {
var fetchPayload = {
asset_type: o.assetType || null,
local_id: o.localId || null,
content_hash: o.contentHash || null,
},
};
if (Array.isArray(o.signals) && o.signals.length > 0) {
fetchPayload.signals = o.signals;
}
return buildMessage({
messageType: 'fetch',
senderId: o.nodeId,
payload: fetchPayload,
});
}
@@ -351,7 +358,8 @@ function httpTransportReceive(opts) {
var hubUrl = (opts && opts.hubUrl) || process.env.A2A_HUB_URL;
if (!hubUrl) return Promise.resolve([]);
var assetType = (opts && opts.assetType) || null;
var fetchMsg = buildFetch({ assetType: assetType });
var signals = (opts && Array.isArray(opts.signals)) ? opts.signals : null;
var fetchMsg = buildFetch({ assetType: assetType, signals: signals });
var endpoint = hubUrl.replace(/\/+$/, '') + '/a2a/fetch';
return fetch(endpoint, {
method: 'POST',
@@ -0,0 +1,130 @@
// Append-only asset call log for tracking Hub asset interactions per evolution run.
// Log file: {evolution_dir}/asset_call_log.jsonl
const fs = require('fs');
const path = require('path');
const { getEvolutionDir } = require('./paths');
function getLogPath() {
return path.join(getEvolutionDir(), 'asset_call_log.jsonl');
}
function ensureDir(filePath) {
const dir = path.dirname(filePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
}
/**
* Append a single asset call record to the log.
*
* @param {object} entry
* @param {string} entry.run_id
* @param {string} entry.action - hub_search_hit | hub_search_miss | asset_reuse | asset_reference | asset_publish | asset_publish_skip
* @param {string} [entry.asset_id]
* @param {string} [entry.asset_type]
* @param {string} [entry.source_node_id]
* @param {string} [entry.chain_id]
* @param {number} [entry.score]
* @param {string} [entry.mode] - direct | reference
* @param {string[]} [entry.signals]
* @param {string} [entry.reason]
* @param {object} [entry.extra]
*/
function logAssetCall(entry) {
if (!entry || typeof entry !== 'object') return;
try {
const logPath = getLogPath();
ensureDir(logPath);
const record = {
timestamp: new Date().toISOString(),
...entry,
};
fs.appendFileSync(logPath, JSON.stringify(record) + '\n', 'utf8');
} catch (e) {
// Non-fatal: never block evolution for logging failure
}
}
/**
* Read asset call log entries with optional filters.
*
* @param {object} [opts]
* @param {string} [opts.run_id] - filter by run_id
* @param {string} [opts.action] - filter by action type
* @param {number} [opts.last] - only return last N entries
* @param {string} [opts.since] - ISO date string, only entries after this time
* @returns {object[]}
*/
function readCallLog(opts) {
const o = opts || {};
const logPath = getLogPath();
if (!fs.existsSync(logPath)) return [];
const raw = fs.readFileSync(logPath, 'utf8');
const lines = raw.split('\n').filter(Boolean);
let entries = [];
for (const line of lines) {
try {
entries.push(JSON.parse(line));
} catch (e) { /* skip corrupt lines */ }
}
if (o.since) {
const sinceTs = new Date(o.since).getTime();
if (Number.isFinite(sinceTs)) {
entries = entries.filter(e => new Date(e.timestamp).getTime() >= sinceTs);
}
}
if (o.run_id) {
entries = entries.filter(e => e.run_id === o.run_id);
}
if (o.action) {
entries = entries.filter(e => e.action === o.action);
}
if (o.last && Number.isFinite(o.last) && o.last > 0) {
entries = entries.slice(-o.last);
}
return entries;
}
/**
* Summarize asset call log (for CLI display).
*
* @param {object} [opts] - same filters as readCallLog
* @returns {object} summary with totals and per-action counts
*/
function summarizeCallLog(opts) {
const entries = readCallLog(opts);
const actionCounts = {};
const assetsSeen = new Set();
const runsSeen = new Set();
for (const e of entries) {
const a = e.action || 'unknown';
actionCounts[a] = (actionCounts[a] || 0) + 1;
if (e.asset_id) assetsSeen.add(e.asset_id);
if (e.run_id) runsSeen.add(e.run_id);
}
return {
total_entries: entries.length,
unique_assets: assetsSeen.size,
unique_runs: runsSeen.size,
by_action: actionCounts,
entries,
};
}
module.exports = {
logAssetCall,
readCallLog,
summarizeCallLog,
getLogPath,
};
+90 -27
View File
@@ -2,11 +2,17 @@
//
// Flow: extractSignals() -> hubSearch(signals) -> if hit: reuse; if miss: normal evolve
// Two modes: direct (skip local reasoning) | reference (inject into prompt as strong hint)
//
// Uses POST /a2a/fetch with signals field (protocol-native) instead of GET /a2a/assets/search.
// This returns full payload (content, diff, strategy) in a single round-trip.
const { getNodeId } = require('./a2aProtocol');
const { getNodeId, buildFetch } = require('./a2aProtocol');
const { logAssetCall } = require('./assetCallLog');
const DEFAULT_MIN_REUSE_SCORE = 0.72;
const DEFAULT_REUSE_MODE = 'reference'; // 'direct' | 'reference'
const MAX_STREAK_CAP = 5;
const TIMEOUT_REASON = 'hub_search_timeout';
function getHubUrl() {
return (process.env.A2A_HUB_URL || '').replace(/\/+$/, '');
@@ -24,13 +30,14 @@ function getMinReuseScore() {
/**
* Score a hub asset for local reuse quality.
* rank = confidence * max(success_streak, 1) * (reputation / 100)
* rank = confidence * min(max(success_streak, 1), MAX_STREAK_CAP) * (reputation / 100)
* Streak is capped to prevent unbounded score inflation.
*/
function scoreHubResult(asset) {
const confidence = Number(asset.confidence) || 0;
const streak = Math.max(Number(asset.success_streak) || 0, 1);
// Reputation is included in asset from hub ranked endpoint; default 50 if missing
const reputation = Number(asset.reputation_score) || 50;
const streak = Math.min(Math.max(Number(asset.success_streak) || 0, 1), MAX_STREAK_CAP);
const repRaw = Number(asset.reputation_score);
const reputation = Number.isFinite(repRaw) ? repRaw : 50;
return confidence * streak * (reputation / 100);
}
@@ -45,7 +52,6 @@ function pickBestMatch(results, threshold) {
let bestScore = 0;
for (const asset of results) {
// Only consider promoted assets
if (asset.status && asset.status !== 'promoted') continue;
const s = scoreHubResult(asset);
if (s > bestScore) {
@@ -64,48 +70,96 @@ function pickBestMatch(results, threshold) {
}
/**
* Search the hub for reusable capsules matching the given signals.
* Search the hub for reusable assets matching the given signals.
* Uses POST /a2a/fetch with signals (protocol-native, returns full payload).
* Falls back to GET /a2a/assets/search if fetch fails.
* Returns { hit: true, match, score, mode } or { hit: false }.
*/
async function hubSearch(signals, opts) {
const hubUrl = getHubUrl();
if (!hubUrl) return { hit: false, reason: 'no_hub_url' };
const signalList = Array.isArray(signals) ? signals.filter(Boolean) : [];
const signalList = Array.isArray(signals)
? signals.map(s => typeof s === 'string' ? s.trim() : '').filter(Boolean)
: [];
if (signalList.length === 0) return { hit: false, reason: 'no_signals' };
const threshold = (opts && Number.isFinite(opts.threshold)) ? opts.threshold : getMinReuseScore();
const limit = (opts && Number.isFinite(opts.limit)) ? opts.limit : 5;
const timeout = (opts && Number.isFinite(opts.timeoutMs)) ? opts.timeoutMs : 8000;
try {
const params = new URLSearchParams();
params.set('signals', signalList.join(','));
params.set('status', 'promoted');
params.set('limit', String(limit));
const fetchMsg = buildFetch({ signals: signalList });
const endpoint = hubUrl + '/a2a/fetch';
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeout);
const timer = setTimeout(() => controller.abort(TIMEOUT_REASON), timeout);
const url = `${hubUrl}/a2a/assets/search?${params.toString()}`;
const res = await fetch(url, {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
const headers = { 'Content-Type': 'application/json', 'Accept': 'application/json' };
const token = process.env.A2A_HUB_TOKEN;
if (token) headers['Authorization'] = `Bearer ${token}`;
const res = await fetch(endpoint, {
method: 'POST',
headers,
body: JSON.stringify(fetchMsg),
signal: controller.signal,
});
clearTimeout(timer);
if (!res.ok) return { hit: false, reason: `hub_http_${res.status}` };
if (!res.ok) {
logAssetCall({
run_id: (opts && opts.run_id) || null,
action: 'hub_search_miss',
signals: signalList,
reason: `hub_http_${res.status}`,
via: 'fetch_with_signals',
});
return { hit: false, reason: `hub_http_${res.status}` };
}
const data = await res.json();
const assets = Array.isArray(data.assets) ? data.assets : [];
const results = (data && data.payload && Array.isArray(data.payload.results))
? data.payload.results
: [];
if (assets.length === 0) return { hit: false, reason: 'no_results' };
if (results.length === 0) {
logAssetCall({
run_id: (opts && opts.run_id) || null,
action: 'hub_search_miss',
signals: signalList,
reason: 'no_results',
via: 'fetch_with_signals',
});
return { hit: false, reason: 'no_results' };
}
const pick = pickBestMatch(assets, threshold);
if (!pick) return { hit: false, reason: 'below_threshold', candidates: assets.length };
const pick = pickBestMatch(results, threshold);
if (!pick) {
logAssetCall({
run_id: (opts && opts.run_id) || null,
action: 'hub_search_miss',
signals: signalList,
reason: 'below_threshold',
extra: { candidates: results.length, threshold },
via: 'fetch_with_signals',
});
return { hit: false, reason: 'below_threshold', candidates: results.length };
}
console.log(`[HubSearch] Hit: ${pick.match.asset_id || pick.match.local_id} (score=${pick.score}, mode=${pick.mode})`);
console.log(`[HubSearch] Hit via fetch+signals: ${pick.match.asset_id || 'unknown'} (score=${pick.score}, mode=${pick.mode})`);
logAssetCall({
run_id: (opts && opts.run_id) || null,
action: 'hub_search_hit',
asset_id: pick.match.asset_id || null,
asset_type: pick.match.asset_type || pick.match.type || null,
source_node_id: pick.match.source_node_id || null,
chain_id: pick.match.chain_id || null,
score: pick.score,
mode: pick.mode,
signals: signalList,
via: 'fetch_with_signals',
});
return {
hit: true,
@@ -117,9 +171,18 @@ async function hubSearch(signals, opts) {
chain_id: pick.match.chain_id || null,
};
} catch (err) {
// Hub unreachable is non-fatal; fall through to normal evolve
console.log(`[HubSearch] Failed (non-fatal): ${err.message}`);
return { hit: false, reason: 'fetch_error', error: err.message };
const isTimeout = err.name === 'AbortError' || (err.cause && err.cause === TIMEOUT_REASON);
const reason = isTimeout ? 'timeout' : 'fetch_error';
console.log(`[HubSearch] Failed (non-fatal, ${reason}): ${err.message}`);
logAssetCall({
run_id: (opts && opts.run_id) || null,
action: 'hub_search_miss',
signals: signalList,
reason,
extra: { error: err.message },
via: 'fetch_with_signals',
});
return { hit: false, reason, error: err.message };
}
}
@@ -0,0 +1,86 @@
'use strict';
const { execSync } = require('child_process');
const { getRepoRoot } = require('./paths');
const REVIEW_ENABLED_KEY = 'EVOLVER_LLM_REVIEW';
const REVIEW_TIMEOUT_MS = 30000;
function isLlmReviewEnabled() {
return String(process.env[REVIEW_ENABLED_KEY] || '').toLowerCase() === 'true';
}
function buildReviewPrompt({ diff, gene, signals, mutation }) {
const geneId = gene && gene.id ? gene.id : '(unknown)';
const category = (mutation && mutation.category) || (gene && gene.category) || 'unknown';
const rationale = mutation && mutation.rationale ? String(mutation.rationale).slice(0, 500) : '(none)';
const signalsList = Array.isArray(signals) ? signals.slice(0, 8).join(', ') : '(none)';
const diffPreview = String(diff || '').slice(0, 6000);
return `You are reviewing a code change produced by an autonomous evolution engine.
## Context
- Gene: ${geneId} (${category})
- Signals: [${signalsList}]
- Rationale: ${rationale}
## Diff
\`\`\`diff
${diffPreview}
\`\`\`
## Review Criteria
1. Does this change address the stated signals?
2. Are there any obvious regressions or bugs introduced?
3. Is the blast radius proportionate to the problem?
4. Are there any security or safety concerns?
## Response Format
Respond with a JSON object:
{
"approved": true|false,
"confidence": 0.0-1.0,
"concerns": ["..."],
"summary": "one-line review summary"
}`;
}
function runLlmReview({ diff, gene, signals, mutation }) {
if (!isLlmReviewEnabled()) return null;
const prompt = buildReviewPrompt({ diff, gene, signals, mutation });
try {
const repoRoot = getRepoRoot();
const escapedPrompt = prompt.replace(/'/g, "'\\''");
const result = execSync(
`echo '${escapedPrompt}' | node -e "
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
let input = '';
rl.on('line', l => input += l + '\\n');
rl.on('close', () => {
console.log(JSON.stringify({ approved: true, confidence: 0.7, concerns: [], summary: 'auto-approved (no external LLM configured)' }));
});
"`,
{
cwd: repoRoot,
encoding: 'utf8',
timeout: REVIEW_TIMEOUT_MS,
stdio: ['pipe', 'pipe', 'pipe'],
windowsHide: true,
}
);
try {
return JSON.parse(result.trim());
} catch (_) {
return { approved: true, confidence: 0.5, concerns: ['failed to parse review response'], summary: 'review parse error' };
}
} catch (e) {
console.log('[LLMReview] Execution failed (non-fatal): ' + (e && e.message ? e.message : e));
return { approved: true, confidence: 0.5, concerns: ['review execution failed'], summary: 'review timeout or error' };
}
}
module.exports = { isLlmReviewEnabled, runLlmReview, buildReviewPrompt };
@@ -0,0 +1,108 @@
'use strict';
const fs = require('fs');
const path = require('path');
const { getNarrativePath, getEvolutionDir } = require('./paths');
const MAX_NARRATIVE_ENTRIES = 30;
const MAX_NARRATIVE_SIZE = 12000;
function ensureDir(dir) {
try { if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); } catch (_) {}
}
function recordNarrative({ gene, signals, mutation, outcome, blast, capsule }) {
const narrativePath = getNarrativePath();
ensureDir(path.dirname(narrativePath));
const ts = new Date().toISOString().replace('T', ' ').slice(0, 19);
const geneId = gene && gene.id ? gene.id : '(auto)';
const category = (mutation && mutation.category) || (gene && gene.category) || 'unknown';
const status = outcome && outcome.status ? outcome.status : 'unknown';
const score = outcome && typeof outcome.score === 'number' ? outcome.score.toFixed(2) : '?';
const signalsSummary = Array.isArray(signals) ? signals.slice(0, 4).join(', ') : '(none)';
const filesChanged = blast ? blast.files : 0;
const linesChanged = blast ? blast.lines : 0;
const rationale = mutation && mutation.rationale
? String(mutation.rationale).slice(0, 200) : '';
const strategy = gene && Array.isArray(gene.strategy)
? gene.strategy.slice(0, 3).map((s, i) => ` ${i + 1}. ${s}`).join('\n') : '';
const capsuleSummary = capsule && capsule.summary ? String(capsule.summary).slice(0, 200) : '';
const entry = [
`### [${ts}] ${category.toUpperCase()} - ${status}`,
`- Gene: ${geneId} | Score: ${score} | Scope: ${filesChanged} files, ${linesChanged} lines`,
`- Signals: [${signalsSummary}]`,
rationale ? `- Why: ${rationale}` : null,
strategy ? `- Strategy:\n${strategy}` : null,
capsuleSummary ? `- Result: ${capsuleSummary}` : null,
'',
].filter(line => line !== null).join('\n');
let existing = '';
try {
if (fs.existsSync(narrativePath)) {
existing = fs.readFileSync(narrativePath, 'utf8');
}
} catch (_) {}
if (!existing.trim()) {
existing = '# Evolution Narrative\n\nA chronological record of evolution decisions and outcomes.\n\n';
}
const combined = existing + entry;
const trimmed = trimNarrative(combined);
const tmp = narrativePath + '.tmp';
fs.writeFileSync(tmp, trimmed, 'utf8');
fs.renameSync(tmp, narrativePath);
}
function trimNarrative(content) {
if (content.length <= MAX_NARRATIVE_SIZE) return content;
const headerEnd = content.indexOf('###');
if (headerEnd < 0) return content.slice(-MAX_NARRATIVE_SIZE);
const header = content.slice(0, headerEnd);
const entries = content.slice(headerEnd).split(/(?=^### \[)/m);
while (entries.length > MAX_NARRATIVE_ENTRIES) {
entries.shift();
}
let result = header + entries.join('');
if (result.length > MAX_NARRATIVE_SIZE) {
const keep = Math.max(1, entries.length - 5);
result = header + entries.slice(-keep).join('');
}
return result;
}
function loadNarrativeSummary(maxChars) {
const limit = Number.isFinite(maxChars) ? maxChars : 4000;
const narrativePath = getNarrativePath();
try {
if (!fs.existsSync(narrativePath)) return '';
const content = fs.readFileSync(narrativePath, 'utf8');
if (!content.trim()) return '';
const headerEnd = content.indexOf('###');
if (headerEnd < 0) return '';
const entries = content.slice(headerEnd).split(/(?=^### \[)/m);
const recent = entries.slice(-8);
let summary = recent.join('');
if (summary.length > limit) {
summary = summary.slice(-limit);
const firstEntry = summary.indexOf('### [');
if (firstEntry > 0) summary = summary.slice(firstEntry);
}
return summary.trim();
} catch (_) {
return '';
}
}
module.exports = { recordNarrative, loadNarrativeSummary, trimNarrative };
+20 -1
View File
@@ -28,7 +28,8 @@ function getSessionScope() {
if (!raw) return null;
// Sanitize: only allow alphanumeric, dash, underscore, dot (prevent path traversal).
const safe = raw.replace(/[^a-zA-Z0-9_\-\.]/g, '_').slice(0, 128);
return safe || null;
if (!safe || /^\.{1,2}$/.test(safe) || /\.\./.test(safe)) return null;
return safe;
}
function getEvolutionDir() {
@@ -54,6 +55,21 @@ function getSkillsDir() {
return process.env.SKILLS_DIR || path.join(getWorkspaceRoot(), 'skills');
}
function getNarrativePath() {
return path.join(getEvolutionDir(), 'evolution_narrative.md');
}
function getEvolutionPrinciplesPath() {
const repoRoot = getRepoRoot();
const custom = path.join(repoRoot, 'EVOLUTION_PRINCIPLES.md');
if (require('fs').existsSync(custom)) return custom;
return path.join(repoRoot, 'assets', 'gep', 'EVOLUTION_PRINCIPLES.md');
}
function getReflectionLogPath() {
return path.join(getEvolutionDir(), 'reflection_log.jsonl');
}
module.exports = {
getRepoRoot,
getWorkspaceRoot,
@@ -63,5 +79,8 @@ module.exports = {
getGepAssetsDir,
getSkillsDir,
getSessionScope,
getNarrativePath,
getEvolutionPrinciplesPath,
getReflectionLogPath,
};
+31 -4
View File
@@ -1,7 +1,10 @@
const fs = require('fs');
const { captureEnvFingerprint } = require('./envFingerprint');
const { formatAssetPreview } = require('./assets');
const { generateInnovationIdeas } = require('../ops/innovation'); // [2026-02-14] Innovation Catalyst Integration
const { analyzeRecentHistory, OPPORTUNITY_SIGNALS } = require('./signals'); // [2026-02-14] Signal Analysis Integration
const { generateInnovationIdeas } = require('../ops/innovation');
const { analyzeRecentHistory, OPPORTUNITY_SIGNALS } = require('./signals');
const { loadNarrativeSummary } = require('./narrativeMemory');
const { getEvolutionPrinciplesPath } = require('./paths');
/**
* Build a minimal prompt for direct-reuse mode.
@@ -219,6 +222,29 @@ function buildLessonsBlock(hubLessons, signals) {
return parts.join('\n');
}
function buildNarrativeBlock() {
try {
const narrative = loadNarrativeSummary(3000);
if (!narrative) return '';
return `\nContext [Evolution Narrative] (Recent decisions and outcomes -- learn from this history):\n${narrative}\n`;
} catch (_) {
return '';
}
}
function buildPrinciplesBlock() {
try {
const principlesPath = getEvolutionPrinciplesPath();
if (!fs.existsSync(principlesPath)) return '';
const content = fs.readFileSync(principlesPath, 'utf8');
if (!content.trim()) return '';
const trimmed = content.length > 2000 ? content.slice(0, 2000) + '\n...[TRUNCATED]' : content;
return `\nContext [Evolution Principles] (Guiding directives -- align your actions):\n${trimmed}\n`;
} catch (_) {
return '';
}
}
function buildGepPrompt({
nowIso,
context,
@@ -485,7 +511,8 @@ Context [External Candidates]:
${externalCandidatesPreview || '(none)'}
${buildAntiPatternZone(failedCapsules, signals)}${buildLessonsBlock(hubLessons, signals)}
${historyBlock}
${buildNarrativeBlock()}
${buildPrinciplesBlock()}
Context [Execution]:
${executionContext}
@@ -536,4 +563,4 @@ Rules:
return basePrompt.slice(0, maxChars) + "\n...[TRUNCATED]...";
}
module.exports = { buildGepPrompt, buildReusePrompt, buildHubMatchedBlock, buildLessonsBlock };
module.exports = { buildGepPrompt, buildReusePrompt, buildHubMatchedBlock, buildLessonsBlock, buildNarrativeBlock, buildPrinciplesBlock };
@@ -0,0 +1,127 @@
'use strict';
const fs = require('fs');
const path = require('path');
const { getReflectionLogPath, getEvolutionDir } = require('./paths');
const REFLECTION_INTERVAL_CYCLES = 5;
const REFLECTION_COOLDOWN_MS = 30 * 60 * 1000;
function ensureDir(dir) {
try { if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); } catch (_) {}
}
function shouldReflect({ cycleCount, recentEvents }) {
if (!Number.isFinite(cycleCount) || cycleCount < REFLECTION_INTERVAL_CYCLES) return false;
if (cycleCount % REFLECTION_INTERVAL_CYCLES !== 0) return false;
const logPath = getReflectionLogPath();
try {
if (fs.existsSync(logPath)) {
const stat = fs.statSync(logPath);
if (Date.now() - stat.mtimeMs < REFLECTION_COOLDOWN_MS) return false;
}
} catch (_) {}
return true;
}
function buildReflectionContext({ recentEvents, signals, memoryAdvice, narrative }) {
const parts = ['You are performing a strategic reflection on recent evolution cycles.'];
parts.push('Analyze the patterns below and provide concise strategic guidance.');
parts.push('');
if (Array.isArray(recentEvents) && recentEvents.length > 0) {
const last10 = recentEvents.slice(-10);
const successCount = last10.filter(e => e && e.outcome && e.outcome.status === 'success').length;
const failCount = last10.filter(e => e && e.outcome && e.outcome.status === 'failed').length;
const intents = {};
last10.forEach(e => {
const i = e && e.intent ? e.intent : 'unknown';
intents[i] = (intents[i] || 0) + 1;
});
const genes = {};
last10.forEach(e => {
const g = e && Array.isArray(e.genes_used) && e.genes_used[0] ? e.genes_used[0] : 'unknown';
genes[g] = (genes[g] || 0) + 1;
});
parts.push('## Recent Cycle Statistics (last 10)');
parts.push(`- Success: ${successCount}, Failed: ${failCount}`);
parts.push(`- Intent distribution: ${JSON.stringify(intents)}`);
parts.push(`- Gene usage: ${JSON.stringify(genes)}`);
parts.push('');
}
if (Array.isArray(signals) && signals.length > 0) {
parts.push('## Current Signals');
parts.push(signals.slice(0, 20).join(', '));
parts.push('');
}
if (memoryAdvice) {
parts.push('## Memory Graph Advice');
if (memoryAdvice.preferredGeneId) {
parts.push(`- Preferred gene: ${memoryAdvice.preferredGeneId}`);
}
if (Array.isArray(memoryAdvice.bannedGeneIds) && memoryAdvice.bannedGeneIds.length > 0) {
parts.push(`- Banned genes: ${memoryAdvice.bannedGeneIds.join(', ')}`);
}
if (memoryAdvice.explanation) {
parts.push(`- Explanation: ${memoryAdvice.explanation}`);
}
parts.push('');
}
if (narrative) {
parts.push('## Recent Evolution Narrative');
parts.push(String(narrative).slice(0, 3000));
parts.push('');
}
parts.push('## Questions to Answer');
parts.push('1. Are there persistent signals being ignored?');
parts.push('2. Is the gene selection strategy optimal, or are we stuck in a local maximum?');
parts.push('3. Should the balance between repair/optimize/innovate shift?');
parts.push('4. Are there capability gaps that no current gene addresses?');
parts.push('5. What single strategic adjustment would have the highest impact?');
parts.push('');
parts.push('Respond with a JSON object: { "insights": [...], "strategy_adjustment": "...", "priority_signals": [...] }');
return parts.join('\n');
}
function recordReflection(reflection) {
const logPath = getReflectionLogPath();
ensureDir(path.dirname(logPath));
const entry = JSON.stringify({
ts: new Date().toISOString(),
type: 'reflection',
...reflection,
}) + '\n';
fs.appendFileSync(logPath, entry, 'utf8');
}
function loadRecentReflections(count) {
const n = Number.isFinite(count) ? count : 3;
const logPath = getReflectionLogPath();
try {
if (!fs.existsSync(logPath)) return [];
const lines = fs.readFileSync(logPath, 'utf8').trim().split('\n').filter(Boolean);
return lines.slice(-n).map(line => {
try { return JSON.parse(line); } catch (_) { return null; }
}).filter(Boolean);
} catch (_) {
return [];
}
}
module.exports = {
shouldReflect,
buildReflectionContext,
recordReflection,
loadRecentReflections,
REFLECTION_INTERVAL_CYCLES,
};
+10 -2
View File
@@ -3,6 +3,7 @@ function matchPatternToSignals(pattern, signals) {
const p = String(pattern);
const sig = signals.map(s => String(s));
// Regex pattern: /body/flags
const regexLike = p.length >= 2 && p.startsWith('/') && p.lastIndexOf('/') > 0;
if (regexLike) {
const lastSlash = p.lastIndexOf('/');
@@ -16,6 +17,12 @@ function matchPatternToSignals(pattern, signals) {
}
}
// Multi-language alias: "en_term|zh_term|ja_term" -- any branch matching = hit
if (p.includes('|') && !p.startsWith('/')) {
const branches = p.split('|').map(b => b.trim().toLowerCase()).filter(Boolean);
return branches.some(needle => sig.some(s => s.toLowerCase().includes(needle)));
}
const needle = p.toLowerCase();
return sig.some(s => s.toLowerCase().includes(needle));
}
@@ -67,6 +74,7 @@ function computeDriftIntensity(opts) {
}
function selectGene(genes, signals, opts) {
const genesList = Array.isArray(genes) ? genes : [];
const bannedGeneIds = opts && opts.bannedGeneIds ? opts.bannedGeneIds : new Set();
const driftEnabled = !!(opts && opts.driftEnabled);
const preferredGeneId = opts && typeof opts.preferredGeneId === 'string' ? opts.preferredGeneId : null;
@@ -75,14 +83,14 @@ function selectGene(genes, signals, opts) {
var driftIntensity = computeDriftIntensity({
driftEnabled: driftEnabled,
effectivePopulationSize: opts && opts.effectivePopulationSize,
genePoolSize: genes ? genes.length : 0,
genePoolSize: genesList.length,
});
var useDrift = driftEnabled || driftIntensity > 0.15;
var DISTILLED_PREFIX = 'gene_distilled_';
var DISTILLED_SCORE_FACTOR = 0.8;
const scored = genes
const scored = genesList
.map(g => {
var s = scoreGene(g, signals);
if (s > 0 && g.id && String(g.id).startsWith(DISTILLED_PREFIX)) s *= DISTILLED_SCORE_FACTOR;
+2 -2
View File
@@ -282,14 +282,14 @@ function extractSignals({ recentSessionTranscript, todayLog, memorySnippet, user
// --- Tool Usage Analytics ---
var toolUsage = {};
var toolMatches = corpus.match(/\[TOOL:\s*(\w+)\]/g) || [];
var toolMatches = corpus.match(/\[TOOL:\s*([\w-]+)\]/g) || [];
// Extract exec commands to identify benign loops (like watchdog checks)
var execCommands = corpus.match(/exec: (node\s+[\w\/\.-]+\.js\s+ensure)/g) || [];
var benignExecCount = execCommands.length;
for (var i = 0; i < toolMatches.length; i++) {
var toolName = toolMatches[i].match(/\[TOOL:\s*(\w+)\]/)[1];
var toolName = toolMatches[i].match(/\[TOOL:\s*([\w-]+)\]/)[1];
toolUsage[toolName] = (toolUsage[toolName] || 0) + 1;
}
@@ -223,7 +223,7 @@ function buildDistillationPrompt(analysis, existingGenes, sampleCapsules) {
});
return [
'You are a Gene synthesis engine for the GEP (Gene Expression Protocol).',
'You are a Gene synthesis engine for the GEP (Genome Evolution Protocol).',
'',
'Analyze the following successful evolution capsules and extract a reusable Gene.',
'',
+100 -1
View File
@@ -17,6 +17,9 @@ const {
const { computeAssetId, SCHEMA_VERSION } = require('./contentHash');
const { captureEnvFingerprint } = require('./envFingerprint');
const { buildValidationReport } = require('./validationReport');
const { logAssetCall } = require('./assetCallLog');
const { recordNarrative } = require('./narrativeMemory');
const { isLlmReviewEnabled, runLlmReview } = require('./llmReview');
function nowIso() {
return new Date().toISOString();
@@ -701,10 +704,14 @@ function rollbackNewUntrackedFiles({ repoRoot, baselineUntracked }) {
// This prevents "ghost skill directories" where mkdir succeeded but
// file creation failed/was rolled back. Without this, empty dirs like
// skills/anima/, skills/oblivion/ etc. accumulate after failed innovations.
// SAFETY: never remove top-level structural directories (skills/, src/, etc.)
// or critical protected directories. Only remove leaf subdirectories.
var dirsToCheck = new Set();
for (var di = 0; di < deleted.length; di++) {
var dir = path.dirname(deleted[di]);
while (dir && dir !== '.' && dir !== '/') {
var normalized = dir.replace(/\\/g, '/');
if (!normalized.includes('/')) break;
dirsToCheck.add(dir);
dir = path.dirname(dir);
}
@@ -713,6 +720,7 @@ function rollbackNewUntrackedFiles({ repoRoot, baselineUntracked }) {
var sortedDirs = Array.from(dirsToCheck).sort(function (a, b) { return b.length - a.length; });
var removedDirs = [];
for (var si = 0; si < sortedDirs.length; si++) {
if (isCriticalProtectedPath(sortedDirs[si] + '/')) continue;
var dirAbs = path.join(repoRoot, sortedDirs[si]);
try {
var entries = fs.readdirSync(dirAbs);
@@ -957,8 +965,32 @@ function readRecentSessionInputs() {
return { recentSessionTranscript, todayLog: todayLogContent, memorySnippet, userSnippet };
}
function isGitRepo(dir) {
try {
execSync('git rev-parse --git-dir', {
cwd: dir, encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'], timeout: 5000,
});
return true;
} catch (_) {
return false;
}
}
function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true } = {}) {
const repoRoot = getRepoRoot();
if (!isGitRepo(repoRoot)) {
console.error('[Solidify] FATAL: Not a git repository (' + repoRoot + ').');
console.error('[Solidify] Solidify requires git for rollback, diff capture, and blast radius.');
console.error('[Solidify] Run "git init && git add -A && git commit -m init" first.');
return {
ok: false,
status: 'failed',
failure_reason: 'not_a_git_repository',
event: null,
};
}
const state = readStateForSolidify();
const lastRun = state && state.last_run ? state.last_run : null;
const genes = loadGenes();
@@ -1053,6 +1085,29 @@ function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true }
console.error(`[Solidify] CANARY FAILED: ${canary.err}`);
}
// Optional LLM review: when EVOLVER_LLM_REVIEW=true, submit diff for review.
let llmReviewResult = null;
if (constraintCheck.ok && validation.ok && protocolViolations.length === 0 && isLlmReviewEnabled()) {
try {
const reviewDiff = captureDiffSnapshot(repoRoot);
llmReviewResult = runLlmReview({
diff: reviewDiff,
gene: geneUsed,
signals,
mutation,
});
if (llmReviewResult && llmReviewResult.approved === false) {
constraintCheck.violations.push('llm_review_rejected: ' + (llmReviewResult.summary || 'no reason'));
constraintCheck.ok = false;
console.log('[LLMReview] Change REJECTED: ' + (llmReviewResult.summary || ''));
} else if (llmReviewResult) {
console.log('[LLMReview] Change approved (confidence: ' + (llmReviewResult.confidence || '?') + ')');
}
} catch (e) {
console.log('[LLMReview] Failed (non-fatal): ' + (e && e.message ? e.message : e));
}
}
// Build standardized ValidationReport (machine-readable, interoperable).
const validationReport = buildValidationReport({
geneId: geneUsed && geneUsed.id ? geneUsed.id : null,
@@ -1212,7 +1267,12 @@ function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true }
if (!dryRun && !success && rollbackOnFailure) {
rollbackTracked(repoRoot);
rollbackNewUntrackedFiles({ repoRoot, baselineUntracked: lastRun && lastRun.baseline_untracked ? lastRun.baseline_untracked : [] });
// Only clean up new untracked files when a valid baseline exists.
// Without a baseline, we cannot distinguish pre-existing untracked files
// from AI-generated ones, so deleting would be destructive.
if (lastRun && Array.isArray(lastRun.baseline_untracked)) {
rollbackNewUntrackedFiles({ repoRoot, baselineUntracked: lastRun.baseline_untracked });
}
}
// Apply epigenetic marks to the gene based on outcome and environment
@@ -1254,6 +1314,21 @@ function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true }
};
if (!dryRun) writeStateForSolidify(state);
if (!dryRun) {
try {
recordNarrative({
gene: geneUsed,
signals,
mutation,
outcome: event.outcome,
blast,
capsule,
});
} catch (e) {
console.log('[Narrative] Record failed (non-fatal): ' + (e && e.message ? e.message : e));
}
}
// Search-First Evolution: auto-publish eligible capsules to the Hub (as Gene+Capsule bundle).
let publishResult = null;
if (!dryRun && capsule && capsule.a2a && capsule.a2a.eligible_to_broadcast) {
@@ -1327,6 +1402,21 @@ function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true }
});
}
publishResult = { attempted: true, asset_id: capsule.asset_id || capsule.id, bundle: true };
logAssetCall({
run_id: lastRun && lastRun.run_id ? lastRun.run_id : null,
action: 'asset_publish',
asset_id: capsule.asset_id || capsule.id,
asset_type: 'Capsule',
source_node_id: null,
chain_id: publishChainId || null,
signals: Array.isArray(capsule.trigger) ? capsule.trigger : [],
extra: {
source_type: sourceType,
reused_asset_id: reusedAssetId,
gene_id: publishGene && publishGene.id ? publishGene.id : null,
parent: parentRef || null,
},
});
} else {
publishResult = { attempted: false, reason: 'no_hub_url' };
}
@@ -1340,6 +1430,14 @@ function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true }
: sourceType === 'reused' ? 'skip_direct_reused_asset'
: 'below_min_score';
publishResult = { attempted: false, reason };
logAssetCall({
run_id: lastRun && lastRun.run_id ? lastRun.run_id : null,
action: 'asset_publish_skip',
asset_id: capsule.asset_id || capsule.id,
asset_type: 'Capsule',
reason,
signals: Array.isArray(capsule.trigger) ? capsule.trigger : [],
});
}
}
@@ -1452,6 +1550,7 @@ function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true }
module.exports = {
solidify,
isGitRepo,
readStateForSolidify,
writeStateForSolidify,
isValidationCommandAllowed,
@@ -82,7 +82,8 @@ async function fetchTasks(opts) {
}
return result;
} catch {
} catch (err) {
console.warn("[TaskReceiver] fetchTasks failed:", err && err.message ? err.message : err);
return { tasks: [] };
}
}
@@ -150,7 +151,7 @@ function estimateCapabilityMatch(task, memoryEvents) {
var weightSum = 0;
for (var sk in totalBySignalKey) {
// Reconstruct signals from the key for comparison
var skParts = sk.split(':').map(function(s) { return s.trim().toLowerCase(); }).filter(Boolean);
var skParts = sk.split('|').map(function(s) { return s.trim().toLowerCase(); }).filter(Boolean);
var sim = jaccard(taskSignals, skParts);
if (sim < 0.15) continue;
+13 -42
View File
@@ -9,6 +9,14 @@ const { getEvolutionDir } = require('../gep/paths');
var MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours
var MIN_KEEP = 10;
function safeBatchDelete(batch) {
var deleted = 0;
for (var i = 0; i < batch.length; i++) {
try { fs.unlinkSync(batch[i]); deleted++; } catch (_) {}
}
return deleted;
}
function run() {
var evoDir = getEvolutionDir();
if (!fs.existsSync(evoDir)) return;
@@ -26,8 +34,7 @@ function run() {
var deleted = 0;
// Phase 1: Age-based cleanup (keep at least MIN_KEEP)
// [OPTIMIZATION] Batch deletion for age-based cleanup as well
const filesToDelete = [];
var filesToDelete = [];
for (var i = MIN_KEEP; i < files.length; i++) {
if (now - files[i].mtime > MAX_AGE_MS) {
filesToDelete.push(files[i].path);
@@ -35,27 +42,10 @@ function run() {
}
if (filesToDelete.length > 0) {
const BATCH_SIZE = 50;
for (let i = 0; i < filesToDelete.length; i += BATCH_SIZE) {
const batch = filesToDelete.slice(i, i + BATCH_SIZE);
try {
// Remove package-lock.json if it exists (not relevant here but good practice in other contexts)
// Use rm -f with quotes to handle paths safely
// Phase 1 optimization update: mirroring phase 2 logic
const args = batch.map(function(p) { return '"' + p + '"'; }).join(' ');
require('child_process').execSync('rm -f ' + args, { stdio: 'ignore' });
deleted += batch.length;
} catch (e) {
// Fallback to serial deletion if batch fails
batch.forEach(function(p) {
try { fs.unlinkSync(p); deleted++; } catch (_) {}
});
}
}
deleted += safeBatchDelete(filesToDelete);
}
// Phase 2: Size-based safety cap (keep max 10 files total to drastically reduce bloat)
// Re-scan remaining files after age cleanup
// Phase 2: Size-based safety cap (keep max 10 files total)
try {
var remainingFiles = fs.readdirSync(evoDir)
.filter(function(f) { return /^gep_prompt_.*\.(json|txt)$/.test(f); })
@@ -66,29 +56,10 @@ function run() {
})
.sort(function(a, b) { return b.mtime - a.mtime; }); // newest first
// [OPTIMIZATION] Batch deletion using execSync('rm') for speed and robustness
var MAX_FILES = 10;
if (remainingFiles.length > MAX_FILES) {
const toDelete = remainingFiles.slice(MAX_FILES).map(function(f) { return f.path; });
// Chunking into batches of 50 to avoid E2BIG on some systems,
// though with MAX_FILES=10 and typical usage, list won't be huge.
const BATCH_SIZE = 50;
for (let i = 0; i < toDelete.length; i += BATCH_SIZE) {
const batch = toDelete.slice(i, i + BATCH_SIZE);
try {
// Safe bulk delete via shell, faster than serial unlinkSync
// Using "rm -f" to ignore non-existent files
const args = batch.map(function(p) { return '"' + p + '"'; }).join(' ');
require('child_process').execSync('rm -f ' + args, { stdio: 'ignore' });
deleted += batch.length;
} catch (e) {
// Fallback to serial if batch fails
batch.forEach(function(p) {
try { fs.unlinkSync(p); deleted++; } catch (_) {}
});
}
}
var toDelete = remainingFiles.slice(MAX_FILES).map(function(f) { return f.path; });
deleted += safeBatchDelete(toDelete);
}
} catch (e) {
console.warn('[Cleanup] Phase 2 failed:', e.message);
+49
View File
@@ -0,0 +1,49 @@
# Errors
Command failures, exceptions, and issues captured during development.
**Areas**: frontend | backend | infra | tests | docs | config
**Statuses**: pending | in_progress | resolved | wont_fix
## Status Definitions
| Status | Meaning |
|--------|---------|
| `pending` | Not yet addressed |
| `in_progress` | Actively being worked on |
| `resolved` | Issue fixed |
| `wont_fix` | Decided not to address (reason in Resolution) |
## Entry Format
```markdown
## [ERR-YYYYMMDD-XXX] title
**Logged**: ISO-8601 timestamp
**Priority**: low | medium | high | critical
**Status**: pending
**Area**: frontend | backend | infra | tests | docs | config
### Summary
Brief description of what failed
### Error
```
Actual error message
```
### Context
- What was attempted
- Input/parameters used
- Environment details
### Suggested Fix
How this could be resolved
### Metadata
- Reproducible: yes | no | unknown
- Related Files: path/to/file.ext
- See Also: ERR-20250110-001
---
```
+44
View File
@@ -0,0 +1,44 @@
# Feature Requests
User-requested capabilities that don't exist yet.
**Areas**: frontend | backend | infra | tests | docs | config
**Statuses**: pending | in_progress | resolved | wont_fix
## Status Definitions
| Status | Meaning |
|--------|---------|
| `pending` | Not yet addressed |
| `in_progress` | Actively being worked on |
| `resolved` | Feature implemented |
| `wont_fix` | Decided not to implement (reason in Resolution) |
## Entry Format
```markdown
## [FEAT-YYYYMMDD-XXX] title
**Logged**: ISO-8601 timestamp
**Priority**: low | medium | high
**Status**: pending
**Area**: frontend | backend | infra | tests | docs | config
### Requested Capability
What the user wanted to do
### User Context
Why they needed it
### Complexity Estimate
simple | medium | complex
### Suggested Implementation
How this could be built
### Metadata
- Frequency: first_time | recurring
- Related Features: existing_feature_name
---
```
+45
View File
@@ -0,0 +1,45 @@
# Learnings
Corrections, insights, and knowledge gaps captured during development.
**Categories**: correction | insight | knowledge_gap | best_practice
**Areas**: frontend | backend | infra | tests | docs | config
**Statuses**: pending | in_progress | resolved | wont_fix | promoted | promoted_to_skill
## Status Definitions
| Status | Meaning |
|--------|---------|
| `pending` | Not yet addressed |
| `in_progress` | Actively being worked on |
| `resolved` | Issue fixed or knowledge integrated |
| `wont_fix` | Decided not to address (reason in Resolution) |
| `promoted` | Elevated to CLAUDE.md, AGENTS.md, or copilot-instructions.md |
| `promoted_to_skill` | Extracted as a reusable skill |
## Skill Extraction Fields
When a learning is promoted to a skill, add these fields:
```markdown
**Status**: promoted_to_skill
**Skill-Path**: skills/skill-name
```
Example:
```markdown
## [LRN-20250115-001] best_practice
**Logged**: 2025-01-15T10:00:00Z
**Priority**: high
**Status**: promoted_to_skill
**Skill-Path**: skills/docker-m1-fixes
**Area**: infra
### Summary
Docker build fails on Apple Silicon due to platform mismatch
...
```
---