From 1309a734739ca0e47b478bdea8c28c0c47424112 Mon Sep 17 00:00:00 2001 From: Hanako Maintainer Date: Wed, 24 Jun 2026 18:46:16 +0800 Subject: [PATCH 1/4] chore: add complexity governance --- ARCHITECTURE.md | 3 +- README.md | 10 +- docs/COMPLEXITY_BUDGET.md | 73 ++++ docs/COMPLEXITY_DEBT.md | 154 +++++++++ docs/COMPLEXITY_REPORT.md | 81 +++++ lib/common.js | 2 - lib/complexity.js | 165 +++++++++ lib/json-io.js | 8 +- lib/release-readiness.js | 23 +- package.json | 2 + scripts/complexity-check.js | 73 ++++ scripts/complexity-report.js | 133 ++++++++ .../control-handlers.characterization.test.js | 314 ++++++++++++++++++ tests/control-parameters.test.js | 62 ++++ tests/control-redaction.test.js | 118 +++++++ tests/control-summaries.test.js | 135 ++++++++ tests/release-readiness.test.js | 2 +- tools/control-handlers/events.js | 27 ++ tools/control-handlers/skill-policy.js | 28 ++ tools/control-parameters.js | 63 ++++ tools/control-summaries.js | 52 +++ tools/control.js | 137 +------- 22 files changed, 1529 insertions(+), 136 deletions(-) create mode 100644 docs/COMPLEXITY_BUDGET.md create mode 100644 docs/COMPLEXITY_DEBT.md create mode 100644 docs/COMPLEXITY_REPORT.md create mode 100644 lib/complexity.js create mode 100644 scripts/complexity-check.js create mode 100644 scripts/complexity-report.js create mode 100644 tests/control-handlers.characterization.test.js create mode 100644 tests/control-parameters.test.js create mode 100644 tests/control-redaction.test.js create mode 100644 tests/control-summaries.test.js create mode 100644 tools/control-handlers/events.js create mode 100644 tools/control-handlers/skill-policy.js create mode 100644 tools/control-parameters.js create mode 100644 tools/control-summaries.js diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 521827b..b1e3a40 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,6 +1,6 @@ # 架构说明 -Runtime Self-Learning `v4.3.22` 当前由 `1` 个入口点、`87` 个 `lib` 模块和一组工具面组成。整体目标不是“尽量自动化”,而是“在不放宽边界的前提下,把本地经验整理成后续可复用的受限提示和低风险动作”。 +Runtime Self-Learning `v4.3.22` 当前由 `1` 个入口点、`88` 个 `lib` 模块和一组工具面组成。整体目标不是“尽量自动化”,而是“在不放宽边界的前提下,把本地经验整理成后续可复用的受限提示和低风险动作”。 --- @@ -188,6 +188,7 @@ flowchart TD | `event-log.js` | 审计事件追加、回放和校验。 | | `config-defaults.js` | 默认配置。 | | `hana-runtime-compat.js` | Hanako 插件系统兼容层。 | +| `complexity.js` | 复杂度预算的单一事实源(扫描、限额、违规判定),供 CLI 与发布门共用。 | --- diff --git a/README.md b/README.md index 9d10878..720351a 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ license platform node - tests + tests

Runtime Self-Learning 会观察本地 Hanako 对话中的重复工作流、用户纠正、常见报错和大上下文使用模式,把经过证据约束的经验整理成后续会话可用的保守提示。 @@ -176,10 +176,12 @@ flowchart LR ```powershell npm run check # 语法与源代码检查 -npm test # 606 个测试 +npm test # 665 个测试 npm run benchmark # 17 个内置基准场景 npm run perf # 热路径微基准 -npm run release:check # 发布元数据与 LTS 契约检查 +npm run complexity:check # 复杂度预算门禁(超 hard limit 即失败) +npm run complexity:report # 生成 docs/COMPLEXITY_REPORT.md +npm run release:check # 发布元数据与 LTS 契约检查(含复杂度预算) ``` 当前热路径基线(以有界运行规模 `N=100 = MAX_PATTERN_COUNT * 2` 为准): @@ -213,7 +215,7 @@ npm run release:check ```text package version: 4.3.22 npm run check: passed -npm test: 606 tests, 601 passed, 5 skipped +npm test: 665 tests, 660 passed, 5 skipped npm run benchmark: passed, 17 scenarios npm run perf: passed, no threshold breaches npm run release:check: Score 100 diff --git a/docs/COMPLEXITY_BUDGET.md b/docs/COMPLEXITY_BUDGET.md new file mode 100644 index 0000000..1b36b41 --- /dev/null +++ b/docs/COMPLEXITY_BUDGET.md @@ -0,0 +1,73 @@ +# Complexity Budget (v4.x LTS) + +本文件定义 Runtime Self-Learning 在 v4.x LTS 维护期的复杂度预算与治理规则。 +它是 hard limit / soft target 的**权威说明**;可机读的实际数值定义在 +[`lib/complexity.js`](../lib/complexity.js) 的 `COMPLEXITY_HARD_LIMITS` / +`COMPLEXITY_SOFT_TARGETS` 常量中,两者必须保持一致。 + +- 状态报告:`npm run complexity:report` → [COMPLEXITY_REPORT.md](COMPLEXITY_REPORT.md) +- 门禁检查:`npm run complexity:check`(超出 hard limit 时 `exit 1`) +- 发布集成:`npm run release:check` 含 `complexity.within_budget` 检查项 + +## 设计原则 + +v4.x 已进入 LTS 维护期,复杂度预算的目的是**防止膨胀**,不是强迫重构。 + +1. **冻结优先**:维护期不为新功能放宽预算。新增复杂度需要明确依据。 +2. **可机读、可验证**:预算落在代码常量与脚本里,不只是散文约定。 +3. **headroom 而非现状卡死**:hard limit 高于当前最大值,留出维护余量; + soft target 贴近现状,作为优先治理信号。 +4. **零运行时依赖不可动摇**:复杂度治理本身不得引入任何运行时依赖。 + +## 扫描范围 + +`lib/`、`scripts/`、`tests/`、`tools/` 下的 `.js` / `.cjs` / `.mjs` 文件。 +度量为轻量启发式(非完整 AST 解析):LOC、import+require 数、export 数、TODO/FIXME 数。 +TODO/FIXME 仅统计约定式标记(`TODO:` / `FIXME:` / `TODO(author):`), +代码或字符串中单纯出现这两个词(含本治理工具自身)不计入,避免自指误报。 + +## Hard limits(超出即 `release:check` 失败) + +| 维度 | Hard limit | +|---|---| +| 单文件 LOC | 900 | +| 单文件 import + require 数 | 35 | +| 单文件 export 数 | 25 | +| TODO/FIXME 总数 | 40 | +| `lib/` 模块数 | 110 | + +## Soft targets(超出记为债务,不阻断发布) + +| 维度 | Soft target | +|---|---| +| 单文件 LOC | 600 | +| 单文件 import + require 数 | 20 | +| 单文件 export 数 | 18 | +| TODO/FIXME 总数 | 10 | +| `lib/` 模块数 | 95 | + +超出 soft target 但在 hard limit 内的项,会在复杂度报告中列为 soft 警告,并应登记到 +[COMPLEXITY_DEBT.md](COMPLEXITY_DEBT.md)。 + +## 模块新增规则 + +1. **禁止新增无依据模块**。任何新增 `lib/` 模块必须有明确依据(治理/安全/兼容), + 并在 PR 描述与 CHANGELOG 中说明,否则视为预算违规。 +2. **one-in-one-out**:维护期内每新增一个非必要模块,应同步合并/删除一个等量模块, + 使 `lib/` 模块数保持稳定(目标 ≤ soft target 95)。 +3. 治理/基础设施工具(如本复杂度治理自身)可作为一次性有依据的例外,但仍计入 `lib/` 模块数预算。 + +## 依赖规则 + +- **不允许新增运行时依赖**,除非经过明确批准并记录在案。 +- 复杂度治理工具仅使用 Node 内置模块(`fs` / `path`),不引入任何第三方包。 +- `package.json` 不得出现 `dependencies` 字段;如需新增请先走批准流程。 + +## 调整预算的流程 + +收紧或放宽任一 limit 都是**显式治理动作**: + +1. 修改 `lib/complexity.js` 中的常量。 +2. 同步更新本文件对应表格。 +3. 在 CHANGELOG 记录原因。 +4. 确认 `npm run check`、`npm test`、`npm run complexity:check`、`npm run release:check` 全部通过。 diff --git a/docs/COMPLEXITY_DEBT.md b/docs/COMPLEXITY_DEBT.md new file mode 100644 index 0000000..f0ea788 --- /dev/null +++ b/docs/COMPLEXITY_DEBT.md @@ -0,0 +1,154 @@ +# Complexity Debt Ledger + +复杂度债务清单。记录已知的复杂度热点,便于维护期有计划地治理,而**不是**要求在 LTS 期立刻重构。 + +- 预算与规则:[COMPLEXITY_BUDGET.md](COMPLEXITY_BUDGET.md) +- 实时数据:[COMPLEXITY_REPORT.md](COMPLEXITY_REPORT.md)(`npm run complexity:report` 刷新) + +## 记录格式 + +每条债务包含以下字段: + +- **ID**:`C-NNN`,单调递增,永不复用。 +- **Area**:所属区域 / 文件。 +- **Symptom**:复杂度表现。 +- **Evidence**:可验证的度量(来自复杂度报告)。 +- **Risk**:放任不管的风险。 +- **Fix**:建议的低风险治理方向(不强制立刻执行)。 +- **Status**:`open` / `accepted`(接受为 LTS 期常态)/ `in-progress` / `resolved`。 + +债务超出 hard limit 时必须升级处理(会阻断 `release:check`);仅超出 soft target 时记为 +`open` 或 `accepted`,作为优先治理对象。 + +--- + +## C-001 — tools/control.js 控制分发器过大 + +- **Area**: `tools/control.js` +- **Symptom**: 单文件 LOC 与 import 数初始为全仓最高,承担过多控制面动作分发职责。 +- **Evidence(LOC 轨迹)**: 初始 712 LOC / 28 imports(均超 soft 600/20,仍在 hard 900/35 内)→ 3a 后 667 / 29 → 3b 后 620 / 30 → 3c(仅测试)620 / 30 → 拆分试点 610 / 31 → events 域迁移 **601 / 32**。**累计 712 → 601 LOC(−111,−15.6%)**;imports 28 → 32(每次只读抽取需在 control.js import 回用,故 imports 不降反小升,已多次预判并验证)。control.js **已不再是全仓最大文件**(现最大为 `tests/pattern-detector.test.js` 648 LOC)。 +- **Risk**: 控制面入口持续累积分支;imports 偏高但稳定处于 hard limit(35)内,距上限仍有余量。 +- **Status**: **closed-low-risk(第十阶段 2026-06-24 阶段性收口)** —— 低风险治理全部完成;高风险/收益递减部分明确 deferred(见下"收口结论")。LTS 维护期不再继续拆分。 + +### 第三阶段职责地图(2026-06-24,纯审计,未改代码) + +| 区域 | 行号 | 职责 | 依赖/外部模块 | 被哪些动作使用 | 适合抽出? | +|---|---|---|---|---|---| +| imports | 1–28 | 27 条 import,覆盖 24 个源模块 | 几乎所有 lib 子系统 + doctor/_shared | 全部 | 否(是症状,非原因;只有拆 handler 才会下降) | +| 共享/skill 助手 | 30–57 | `buildSkill`/`regenerateSkill`(skill 渲染+落盘)、`redactConfig`+`SENSITIVE_CONFIG_KEYS`、`readPluginVersion` | skill-lifecycle、common、fs | 多个 mutation 动作 + status | 部分(见下) | +| 纯汇总/格式化 | 59–103 | `countByStatus`、`summarizeDecoratedPatterns`、`countWaitingAgentTasks`、`validationNextAction`、`reviewPanelNextActions` | 无(纯函数) | status / review_panel / validate_proposal | **是(最佳候选)** | +| HANDLERS 动作表 | 105–559 | ~45 个动作处理器(读/改/审批/迁移/报告/外部模型/诊断) | 全部子系统 | execute 路由 | 否(action 执行链,本期排除) | +| 工具元信息 | 560–562 | `name` / `description` | — | 宿主契约 | 否 | +| **安全分类(边界)** | 564–635 | `*_ACTIONS` 五个集合 + `describeControlSideEffect` + `sessionPermission` | — | 宿主权限门 | **否(安全边界,明确不抽)** | +| parameters schema | 637–696 | 输入 JSON Schema(action enum + ~50 属性) | 引用 `Object.keys(HANDLERS)` | 宿主契约 | 是(纯数据,属性表可抽) | +| execute 调度 | 698–712 | 加载 config/patterns、查表、包装结果 | common | 入口 | 否(核心路由) | + +**关键结论**:control.js 的两个头部指标里,**712 LOC 可由纯函数/schema 抽取适度降低,但 28 imports 不会**——import 数由 HANDLERS 需要几乎所有子系统驱动,只有把 handler 按域拆分(属 action 执行链,本期排除)才能下降。因此本期只规划「降 LOC、不动执行链与边界」的安全抽取,import 治理留待后续专项。 + +- **Fix plan(分级,低风险优先)**: + 1. **3a 抽纯汇总/格式化函数 ✅ 已完成(第四阶段 2026-06-24)** → 新建 `tools/control-summaries.js`,迁移 `countByStatus` / `summarizeDecoratedPatterns` / `countWaitingAgentTasks` / `validationNextAction` / `reviewPanelNextActions` 5 个纯函数(函数体逐字不变),control.js import 回用。新增 `tests/control-summaries.test.js`(18 个直接单测)。control.js 712 → 667 LOC;测试 606 → 624(计数已同步 README 徽章/文案、release-readiness 默认与夹具)。四门全绿、0 violations。 + 2. **3b 抽 parameters 属性表 ✅ 已完成(第五阶段 2026-06-24)** → 新建 `tools/control-parameters.js` 导出 `CONTROL_PARAM_PROPERTIES`(除 `action` 外的全部属性,逐字迁移)。control.js 保留 `action`(含 `enum: Object.keys(HANDLERS)`)并以 `properties: { action, ...CONTROL_PARAM_PROPERTIES }` 展开,键顺序不变、schema 等价。新增 `tests/control-parameters.test.js`(7 个单测,校验字段/顺序/required/enum 等价)。control.js 667 → 620 LOC;测试 624 → 631(计数已同步)。**注**:原 schema 不含 `additionalProperties`,为保持行为未新增该字段(不改变输入校验契约)。四门全绿、0 violations。 + 3. **3c 评估型处理 ✅ 已完成(第六阶段 2026-06-24,仅测试+文档,未改生产代码)**: + - **`redactConfig`**:确认与 `lib/audit-bundle.js` 同名函数**语义不同(见下表)**,**保留分离、不合并**。新增 `tests/control-redaction.test.js`(7 个特征测试),分别经公共入口 `execute({action:"status"})` 与 `buildAuditBundle({config})` 锁定两者行为,未导出/未移动任何涉密私有函数。 + - **`readPluginVersion`**:纯只读、显式 `pluginDir` 入参、无 control 上下文依赖 → 原则上可抽;但抽出会给本就 import 偏多的 `control.js` **再加一条 import**(仅省 ~2 LOC),得不偿失,**保持原地**,留待 handler 按域拆分专项时随 import 重组一并处理。 + 4. **后续专项(非本期)**:HANDLERS 按域拆子模块(proposals / reviews / agent-tasks / transfers / reports / skill-promotion),每组自带 import——这是唯一能同时降 LOC 和 imports 的路径,但属 action 执行链,须先补 characterization 测试再分组,单独评审。 + - **回归网准备 ✅(第七阶段 2026-06-24)**:新增 `tests/control-handlers.characterization.test.js`(27 个用例),经 `execute()` 锁定拆分前各 handler 域的当前行为:status/doctor/list、proposals/reviews 查询、events、agent-tasks、transfers、skill-promotion/policy-profiles、set_config(成功回显 + 校验拒绝)、run_model_advisor(disabled / 无 key,均不触网)、安全拒绝路径(conservative 挡 apply_proposal、未知 review、trust_project_scripts 无脚本拒绝、unknown action)。未改任何生产代码。 + - **拆分试点 ✅(第八阶段 2026-06-24)**:迁移**一个低风险只读域**到 `tools/control-handlers/skill-policy.js`(导出 `skillPolicyHandlers`:`list_skill_candidates` / `list_active_skills` / `list_policy_profiles`,函数体逐字不变),control.js 以 `...skillPolicyHandlers` 挂回 HANDLERS 汇总表。子模块仅实现 handler 体,**不持有权限判定**;`execute` / `*_ACTIONS` / `describeControlSideEffect` / `sessionPermission` 全部留在 control.js 未改。control.js 620 → 610 LOC;imports 30 → 31(此只读域的 `loadSkillCandidates`/`loadActiveSkills` 仍被 `status` 使用,故不随迁移移出——**印证审计结论:只有“该域 import 变为子模块独占”时 control.js imports 才会下降**;本试点目的是验证迁移机制而非降 imports)。子模块 28 LOC / 2 imports。第七阶段 27 个 characterization 全绿、四门全绿、0 violations。后续可按此模式迁移更多**纯只读且 import 可独占**的域(如 events、transfers 查询)以真正压低 control.js imports;写副作用/安全相关 handler 仍不迁移。 + - **候选审计 + events 域迁移 ✅(第九阶段 2026-06-24)**:审计 events / transfers 查询 / agent-tasks 查询三域,结论——三者迁移后 control.js imports **均减 0**(各域至少一个函数仍被 `status` 或 `export_audit_bundle` 使用,故 lib import 行保留;LOC 减幅均 <25)。三者仅满足准入条件 3(新模块 imports ≤5 且边界清晰)。选**收益最高、风险最低**的 events 域迁移到 `tools/control-handlers/events.js`(`eventHandlers`:`list_events` / `event_summary` / `verify_event_log`,纯只读、逐字不变)。control.js 610 → 601 LOC;imports 31 → 32(+1 子模块 import;`event-log.js` 行保留供 `readEvents`/`appendEvent`/`replayEventState` 用,仅移除 control.js 中已无引用的 `verifyEventLog` 绑定)。子模块 27 LOC / 1 import。第七阶段 events 域 3 个 characterization 全绿、四门全绿、0 violations。 + + **候选审计表(第九阶段)** + + | 域 | handler | 新模块 imports | 该域 import 是否 control.js 独占 | control.js imports 变化 | control.js LOC 变化 | 写副作用/网络/credential/安全 | 准入 | + |---|---|---|---|---|---|---|---| + | events | list_events / event_summary / verify_event_log | 1 | 否(readEvents/replayEventState 仍用于 export_audit_bundle;appendEvent 全局) | 0(净 +1:新增子模块 import) | −9 | 无(纯读) | 条件 3 ✓ → **已迁移** | + | transfers 查询 | list_transfer_candidates / show_transfer_candidate | 1 | 否(listTransferCandidateRecords 用于 status+audit;summarizeTransferCandidate 用于写 handler) | 0 | ~−7 | 无(纯读) | 条件 3 ✓(未选) | + | agent-tasks 查询 | list_agent_tasks / show_agent_task | 1 | 否(listAgentTaskStates 用于 status) | 0 | ~−8 | 无(纯读) | 条件 3 ✓(未选) | + + **结论**:纯只读域因依赖与 status/audit-bundle 共享,逐域迁移**无法降 control.js imports**,只能小幅降 LOC。要真正压低 imports,需把 status/export_audit_bundle 对这些函数的使用一并纳入领域聚合(更大重构,超出"单域只读迁移"边界)。后续单域迁移收益递减,建议达成既定 LOC 目标后停止增量拆分,或另立专项处理聚合。 + - **未覆盖/留待 fixture 的 handler**:含写副作用且需多步 fixture 的 happy-path(`apply_proposal`/`apply_review` 成功、`preview_proposal`/`validate_proposal`、`approve`/`reject`、`regenerate_skill`/`regenerate_memfs`、`rollback`、`run_benchmarks`/`export_audit_bundle`/`generate_audit_dashboard`/`release_readiness`、`run_skill_promotion_loop`、agent-task 审批/恢复、transfer 注册/校验/过期)。其中 proposals/reviews 工作流已由 `tests/review-governance.test.js` 行为覆盖,release/benchmark 由 `tests/control-runtime-package.test.js` 覆盖,credential 路径由 `tests/control-credentials.test.js` 覆盖;其余(agent-task 恢复链、transfer 全生命周期、audit-bundle/dashboard 内容断言)需较重 fixture,**登记为拆分专项启动前需补的回归项**,本期不写脆弱测试。 +- **不建议现在抽**:HANDLERS 执行体、安全分类集合 + `describeControlSideEffect` + `sessionPermission`、`execute` 调度、credential 处理(`run_model_advisor`/`set_config`)、脚本信任(`trust_project_scripts`)、proposal apply(`apply_proposal`/`apply_review`)。 +- **现有测试覆盖**:`control-credentials` / `control-runtime-package` / `runtime-e2e` / `review-governance` / `audit-dashboard` / `disk-sync` / `event-log` 经 `execute()` 行为级覆盖,可作为后续抽取的回归网(但未直接单测上述纯函数)。 + +#### 两处 `redactConfig` 行为差异(已由 `tests/control-redaction.test.js` 锁定,禁止合并) + +| 维度 | `tools/control.js` redactConfig | `lib/audit-bundle.js` redactConfig | +|---|---|---| +| 屏蔽哪些 key | 固定白名单:`modelAdvisorApiKey`、`semanticEmbeddingApiKey` | 正则 `/api.?key\|token\|secret\|password/i` 匹配的任意 key | +| 屏蔽掩码 | `"***"` | `"[redacted]"` | +| URL/endpoint | **不处理**(原样保留) | 匹配 `/url\|endpoint/i` 的字符串值 → 取 `new URL(...).origin` | +| token/secret/password | 不单独处理(除非命中固定 2 key) | 处理(正则命中) | +| 空值(falsy) | 不屏蔽(仅 truthy 才屏蔽) | 不替换(`out[key]=out[key]`,等效保留) | +| 调用入口 | `status` / `set_config` 的输出 `config` 字段 | `buildAuditBundle(...).config` | + +两者服务不同场景(控制面回显 vs 审计包导出),掩码风格与覆盖面均不同,**合并会改变两侧输出语义且涉密,明确不做**。 + +#### 收口结论(第十阶段 2026-06-24) + +C-001 的**低风险治理已全部完成并收口**,进入 LTS 维护期常态;高风险/收益递减部分明确 deferred。 + +**已完成** + +| 子项 | 状态 | 落点 | +|---|---|---| +| 3a 纯汇总/格式化抽取 | ✅ | `tools/control-summaries.js` + 单测 | +| 3b parameters 属性表抽取 | ✅ | `tools/control-parameters.js` + 单测 | +| 3c redact/readPluginVersion 评估 | ✅ | 行为锁定测试 `tools/control-redaction` + 文档;redactConfig 保留分离、readPluginVersion 留原地 | +| HANDLERS characterization 回归网 | ✅ | `tests/control-handlers.characterization.test.js`(27 用例) | +| skill/policy handler 拆分试点 | ✅ | `tools/control-handlers/skill-policy.js` | +| events handler 域迁移 | ✅ | `tools/control-handlers/events.js` | + +**Deferred(LTS 维护期不做)** + +- **transfers / agent-tasks 小域迁移**:暂缓。审计已证明这两域迁移后 control.js imports **减 0**、LOC 仅减 ~7/~8,收益递减;每迁一域反而新增一条子模块 import。与已迁移的 events 相比无额外结构收益,不值得继续制造改动面。 +- **import 聚合专项**:deferred。control.js imports 偏高的根因是 `status` / `export_audit_bundle` 这两个"聚合型" handler 跨域引用了几乎所有子系统的函数,使各只读域的依赖无法被子模块独占。要真正压低 imports,必须重组 status/audit-bundle 的共享依赖(聚合层重构),**风险高于当前 LTS 维护目标**,且 imports 当前 32 仍稳处 hard limit(35)内、距上限有余量——不构成发布阻断。故延后,待有明确必要时另立专项评审。 + +**当前结论** + +- control.js 已从 **712 → 601 LOC(−15.6%)**,退出"全仓最大文件"。 +- imports 仍偏高(32),但在 hard limit(35)内,非发布阻断项。 +- 低风险部分完成,高风险/低收益部分延后;C-001 以 `closed-low-risk` 收口,不再继续拆分。 + +## C-002 — 大型 test 文件 + +- **Area**: `tests/pattern-detector.test.js` 等 +- **Symptom**: 个别测试文件体量大,单文件覆盖过多场景,定位与维护成本上升。 +- **Evidence**: `tests/pattern-detector.test.js` 648 LOC(> soft 600);`tests/common.test.js` 530;`tests/observer.test.js` 456。 +- **Risk**: 测试文件膨胀降低可读性,但不影响运行时安全;优先级低于 lib/tools。 +- **Fix**: 按行为分组拆分为更小的 `*.test.js`;纯测试改动,零运行时风险。 +- **Status**: accepted + +## C-003 — 核心运行时模块体量偏大 + +- **Area**: `lib/observer.js`、`lib/action-registry.js` +- **Symptom**: 核心模块接近 soft target,属架构中心节点,改动半径大。 +- **Evidence**: `lib/observer.js` 496 LOC、`lib/action-registry.js` 476 LOC(soft 600 以内但偏高)。 +- **Risk**: 属 v4.x 核心架构,LTS 期**禁止大改**;强行拆分风险高于收益。 +- **Fix**: 维护期仅监控,不主动拆分;若未来逼近 soft/hard 再评估抽取纯函数辅助模块。 +- **Status**: accepted + +## C-004 — 基础设施 export 面偏宽 + +- **Area**: `lib/helpers.js`、`lib/json-io.js` +- **Symptom**: grab-bag 式工具模块导出项多,接近单文件 export soft target。 +- **Evidence**: 初始 `lib/helpers.js` 与 `lib/json-io.js` 各 17 exports(soft target 18,逼近)。 +- **Risk**: 工具模块持续吸附新导出,越过 soft target 后成为隐性耦合中心。 +- **Fix**: 新增工具函数前先评估归属,避免无依据堆积到 `helpers.js`;必要时按主题拆分。 +- **Status**: in-progress + +### 第二阶段处置(2026-06-24) + +逐个统计了两模块每个 export 的引用位置后,只做了零行为变更、零签名变更的整理: + +- **`lib/json-io.js` 17 → 15**:`hanakoPreferencesPath`、`normalizeLogSessionRow` 两个 export + **零外部消费者**(仅被本模块内部调用,且经 `common.js` facade 无谓透传,未列入冻结 API), + 降为模块私有函数并从 `common.js` 重导出列表移除。沿用 v4.3.2 审计「对仅内部使用的函数 + 去掉多余 export」的既有实践。 +- **`lib/helpers.js` 保持 17**:全部 17 个 export 均有 ≥1 个真实外部消费者,按 + 「不删除仍被引用的 export」一律保留。其中 `sessionTargetDisplay` / `toolCategory` / + `sanitizeAdvice` / `isUsageFailure` 为单一消费者函数,理论上可内聚到调用方,但: + ① 内聚会拆散 session 三件套等内聚分组、反降可读性;② 指标已在 soft target(18)以内。 + 权衡后**维护期不做搬迁**,仅在此登记为低优先候选。 + +风险已降低:移除了 2 个对外暴露却无人使用的 export,缩小了 facade 公共面;`helpers.js` +单一消费者函数留待未来专门重构再评估。验证:`check` / `test`(606) / `complexity:check` / +`release:check`(100) 全绿。 diff --git a/docs/COMPLEXITY_REPORT.md b/docs/COMPLEXITY_REPORT.md new file mode 100644 index 0000000..78449fa --- /dev/null +++ b/docs/COMPLEXITY_REPORT.md @@ -0,0 +1,81 @@ +# Complexity Report + +> 自动生成,请勿手工编辑。运行 `npm run complexity:report` 刷新。 +> 预算与规则见 [COMPLEXITY_BUDGET.md](COMPLEXITY_BUDGET.md),债务清单见 [COMPLEXITY_DEBT.md](COMPLEXITY_DEBT.md)。 + +Generated at: 2026-06-24T10:44:26.734Z +Scan scope: lib, scripts, tests, tools +Status: within budget + +## 摘要 + +| 指标 | 当前值 | hard limit | soft target | +|---|---|---|---| +| 文件数 | 185 | - | - | +| lib 模块数 | 88 | 110 | 95 | +| 总 LOC | 26920 | - | - | +| 总代码 LOC | 22461 | - | - | +| 单文件最大 LOC | 648 | 900 | 600 | +| 单文件最大 imports | 32 | 35 | 20 | +| 单文件最大 exports | 17 | 25 | 18 | +| TODO/FIXME 总数 | 0 | 40 | 10 | +| soft 警告数 | 3 | - | 0 | +| hard 违规数 | 0 | 0 | - | + +## Top 10 最大文件 (LOC) + +| 文件 | LOC | 代码 LOC | +|---|---|---| +| tests/pattern-detector.test.js | 648 | 556 | +| tools/control.js | 602 | 533 | +| tests/common.test.js | 530 | 436 | +| lib/observer.js | 496 | 379 | +| lib/action-registry.js | 476 | 428 | +| tests/observer.test.js | 456 | 365 | +| tools/doctor.js | 430 | 361 | +| lib/scope-gate.js | 428 | 305 | +| lib/proposals.js | 426 | 372 | +| lib/pattern-detector.js | 406 | 274 | + +## Top 10 import 最多文件 + +| 文件 | imports | +|---|---| +| tools/control.js | 32 | +| tests/runtime-e2e.test.js | 15 | +| tests/action-runtime.test.js | 14 | +| tests/review-governance.test.js | 14 | +| tests/agent-resume.test.js | 12 | +| lib/action-executor.js | 11 | +| lib/evaluation-runner.js | 11 | +| tests/audit-dashboard.test.js | 11 | +| tools/doctor.js | 11 | +| tools/search.js | 11 | + +## Top 10 export 最多文件 + +| 文件 | exports | +|---|---| +| lib/helpers.js | 17 | +| lib/json-io.js | 15 | +| lib/proposals.js | 15 | +| lib/action-types.js | 10 | +| lib/scoring.js | 10 | +| lib/credentials.js | 9 | +| lib/action-registry.js | 8 | +| lib/action-runtime.js | 8 | +| lib/model-advisor.js | 8 | +| lib/review-queue.js | 8 | + +## TODO / FIXME 统计 + +总计 0 处,分布于 0 个文件。 + +## Soft target 警告 + +以下项目超出 soft target 但仍在 hard limit 内,是优先治理对象(参见 COMPLEXITY_DEBT.md)。 + +- tests/pattern-detector.test.js has 648 LOC > soft target 600 +- tools/control.js has 602 LOC > soft target 600 +- tools/control.js has 32 imports > soft target 20 + diff --git a/lib/common.js b/lib/common.js index c1ddc1f..125ed79 100644 --- a/lib/common.js +++ b/lib/common.js @@ -14,11 +14,9 @@ export { countValues, describeOfficialUtilityModel, hanakoHome, - hanakoPreferencesPath, inspectSessionIdentityCoverage, learnerDir, loadLearnerConfig, - normalizeLogSessionRow, readHanakoPreferences, readJson, readRecentJsonl, diff --git a/lib/complexity.js b/lib/complexity.js new file mode 100644 index 0000000..02de751 --- /dev/null +++ b/lib/complexity.js @@ -0,0 +1,165 @@ +import fs from "fs"; +import path from "path"; + +// Complexity governance for v4.x LTS. +// +// This module is the single source of truth for the complexity budget. Both the +// CLI tooling (scripts/complexity-check.js, scripts/complexity-report.js) and the +// release-readiness gate (lib/release-readiness.js) consume the same pure scan so +// that "what the report says" and "what the gate enforces" can never drift. +// +// Limits are deliberately set with headroom above the current code base so that +// turning the gate on does not retroactively block an already-shipping LTS line. +// Tightening a limit is a deliberate governance act; see docs/COMPLEXITY_BUDGET.md. + +export const COMPLEXITY_SCAN_DIRS = ["lib", "scripts", "tests", "tools"]; + +export const COMPLEXITY_HARD_LIMITS = Object.freeze({ + fileLoc: 900, // per-file total lines + fileImports: 35, // per-file import + require count + fileExports: 25, // per-file export count + totalTodos: 40, // TODO/FIXME across all scanned files + libModuleCount: 110, // module count under lib/ +}); + +export const COMPLEXITY_SOFT_TARGETS = Object.freeze({ + fileLoc: 600, + fileImports: 20, + fileExports: 18, + totalTodos: 10, + libModuleCount: 95, +}); + +const JS_EXTENSIONS = new Set([".js", ".cjs", ".mjs"]); + +function listJsFiles(root, dir) { + const base = path.join(root, dir); + let entries; + try { + entries = fs.readdirSync(base, { withFileTypes: true }); + } catch { + return []; + } + const out = []; + for (const entry of entries) { + const rel = path.posix.join(dir, entry.name); + if (entry.isDirectory()) out.push(...listJsFiles(root, rel)); + else if (entry.isFile() && JS_EXTENSIONS.has(path.extname(entry.name))) out.push(rel); + } + return out; +} + +// Lightweight, dependency-free source metrics. Intentionally heuristic: this is a +// governance signal, not a parser. It must never throw on odd input. +export function analyzeSource(text) { + const lines = String(text).split(/\r?\n/); + const loc = lines.length; + let codeLoc = 0; + let inBlockComment = false; + for (const raw of lines) { + const line = raw.trim(); + if (line === "") continue; + if (inBlockComment) { + if (line.includes("*/")) inBlockComment = false; + continue; + } + if (line.startsWith("//")) continue; + if (line.startsWith("/*")) { + if (!line.includes("*/")) inBlockComment = true; + continue; + } + codeLoc += 1; + } + const imports = (text.match(/^\s*import\b/gm) || []).length + (text.match(/\brequire\s*\(/g) || []).length; + const exports = (text.match(/^\s*export\b/gm) || []).length; + // Count only tag-form markers: the word, an optional (author) group, then a colon. + // Merely mentioning the words in code/strings (including this tool) is not flagged. + const todos = (text.match(/\b(?:TODO|FIXME)(?:\([^)\n]*\))?:/g) || []).length; + return { loc, codeLoc, imports, exports, todos }; +} + +function pushViolation(list, entry) { + list.push(entry); +} + +export function scanComplexity(projectRoot = process.cwd(), options = {}) { + const root = path.resolve(projectRoot); + const dirs = options.dirs || COMPLEXITY_SCAN_DIRS; + const hardLimits = { ...COMPLEXITY_HARD_LIMITS, ...(options.hardLimits || {}) }; + const softTargets = { ...COMPLEXITY_SOFT_TARGETS, ...(options.softTargets || {}) }; + + const files = []; + for (const dir of dirs) { + for (const rel of listJsFiles(root, dir)) { + let text; + try { + text = fs.readFileSync(path.join(root, rel), "utf-8"); + } catch { + continue; + } + files.push({ path: rel, dir, ...analyzeSource(text) }); + } + } + + const sum = (key) => files.reduce((acc, f) => acc + f[key], 0); + const max = (key) => files.reduce((acc, f) => Math.max(acc, f[key]), 0); + const totals = { + fileCount: files.length, + libModuleCount: files.filter((f) => f.dir === "lib").length, + loc: sum("loc"), + codeLoc: sum("codeLoc"), + imports: sum("imports"), + exports: sum("exports"), + todos: sum("todos"), + maxLoc: max("loc"), + maxImports: max("imports"), + maxExports: max("exports"), + }; + + const violations = []; + const softWarnings = []; + for (const f of files) { + if (f.loc > hardLimits.fileLoc) pushViolation(violations, { kind: "file_loc", path: f.path, value: f.loc, limit: hardLimits.fileLoc, message: `${f.path} has ${f.loc} LOC > hard limit ${hardLimits.fileLoc}` }); + else if (f.loc > softTargets.fileLoc) softWarnings.push({ kind: "file_loc", path: f.path, value: f.loc, target: softTargets.fileLoc, message: `${f.path} has ${f.loc} LOC > soft target ${softTargets.fileLoc}` }); + if (f.imports > hardLimits.fileImports) pushViolation(violations, { kind: "file_imports", path: f.path, value: f.imports, limit: hardLimits.fileImports, message: `${f.path} has ${f.imports} imports > hard limit ${hardLimits.fileImports}` }); + else if (f.imports > softTargets.fileImports) softWarnings.push({ kind: "file_imports", path: f.path, value: f.imports, target: softTargets.fileImports, message: `${f.path} has ${f.imports} imports > soft target ${softTargets.fileImports}` }); + if (f.exports > hardLimits.fileExports) pushViolation(violations, { kind: "file_exports", path: f.path, value: f.exports, limit: hardLimits.fileExports, message: `${f.path} has ${f.exports} exports > hard limit ${hardLimits.fileExports}` }); + else if (f.exports > softTargets.fileExports) softWarnings.push({ kind: "file_exports", path: f.path, value: f.exports, target: softTargets.fileExports, message: `${f.path} has ${f.exports} exports > soft target ${softTargets.fileExports}` }); + } + if (totals.todos > hardLimits.totalTodos) pushViolation(violations, { kind: "total_todos", value: totals.todos, limit: hardLimits.totalTodos, message: `total TODO/FIXME ${totals.todos} > hard limit ${hardLimits.totalTodos}` }); + else if (totals.todos > softTargets.totalTodos) softWarnings.push({ kind: "total_todos", value: totals.todos, target: softTargets.totalTodos, message: `total TODO/FIXME ${totals.todos} > soft target ${softTargets.totalTodos}` }); + if (totals.libModuleCount > hardLimits.libModuleCount) pushViolation(violations, { kind: "lib_module_count", value: totals.libModuleCount, limit: hardLimits.libModuleCount, message: `lib module count ${totals.libModuleCount} > hard limit ${hardLimits.libModuleCount}` }); + else if (totals.libModuleCount > softTargets.libModuleCount) softWarnings.push({ kind: "lib_module_count", value: totals.libModuleCount, target: softTargets.libModuleCount, message: `lib module count ${totals.libModuleCount} > soft target ${softTargets.libModuleCount}` }); + + return { + schemaVersion: 1, + generatedAt: new Date().toISOString(), + projectRoot: root, + dirs, + hardLimits, + softTargets, + files, + totals, + violations, + softWarnings, + ok: violations.length === 0, + }; +} + +// Machine-readable digest for release-readiness and CI artifacts. +export function summarizeComplexity(scan) { + return { + schemaVersion: scan.schemaVersion, + generatedAt: scan.generatedAt, + ok: scan.ok, + totals: scan.totals, + hardLimits: scan.hardLimits, + softTargets: scan.softTargets, + violations: scan.violations, + softWarningCount: scan.softWarnings.length, + }; +} + +export function topFilesBy(scan, key, limit = 10) { + return [...scan.files].sort((a, b) => b[key] - a[key] || a.path.localeCompare(b.path)).slice(0, limit); +} diff --git a/lib/json-io.js b/lib/json-io.js index 743bc38..ffec507 100644 --- a/lib/json-io.js +++ b/lib/json-io.js @@ -10,7 +10,9 @@ export function hanakoHome() { return process.env.HANA_HOME || path.join(os.homedir(), ".hanako"); } -export function hanakoPreferencesPath() { +// Internal-only: resolves the Hanako preferences file. Used by readHanakoPreferences +// below; not part of the public facade (no external consumers). +function hanakoPreferencesPath() { const home = hanakoHome(); const candidates = [ process.env.HANAKO_PREFERENCES_FILE, @@ -133,7 +135,9 @@ export function readRecentJsonl(file, cutoff, { maxLines = 5000 } = {}) { return rows; } -export function normalizeLogSessionRow(row = {}) { +// Internal-only: normalizes a raw JSONL log row's session identity. Used by the +// readers/summarizers in this module; not part of the public facade. +function normalizeLogSessionRow(row = {}) { const session = normalizeSessionTarget(row, row.session, row.sessionTarget, row.attribution); const sessionKey = sessionIdentityKey(session); return { diff --git a/lib/release-readiness.js b/lib/release-readiness.js index 25ab146..f1b06e8 100644 --- a/lib/release-readiness.js +++ b/lib/release-readiness.js @@ -1,6 +1,7 @@ import fs from "fs"; import path from "path"; import { loadBenchmarkCorpus } from "./benchmark-corpus.js"; +import { scanComplexity } from "./complexity.js"; export const REQUIRED_LTS_DOCS = [ "docs/ACTION_API.md", @@ -170,6 +171,23 @@ function checkBenchmarkCorpus(projectRoot, minBenchmarkScenarios) { } } +function checkComplexityBudget(projectRoot, options = {}) { + try { + const scan = scanComplexity(projectRoot, options.complexity || {}); + const t = scan.totals; + return makeCheck( + "complexity.within_budget", + scan.ok, + scan.ok + ? `complexity within budget: ${t.fileCount} files, max ${t.maxLoc} LOC, ${t.todos} TODO/FIXME (${scan.softWarnings.length} soft warning(s))` + : `complexity budget exceeded: ${scan.violations.map((v) => v.message).join("; ")}`, + { totals: t, violations: scan.violations, softWarningCount: scan.softWarnings.length, dirs: scan.dirs }, + ); + } catch (err) { + return makeCheck("complexity.within_budget", false, `complexity check failed: ${err.message}`, { error: err.message }); + } +} + export function buildReleaseReadiness(projectRoot = process.cwd(), options = {}) { const root = path.resolve(projectRoot); const packageJsonPath = path.join(root, "package.json"); @@ -231,11 +249,12 @@ export function buildReleaseReadiness(projectRoot = process.cwd(), options = {}) { baseline: "benchmarks/baseline-v4.0.9.json", thresholds: "benchmarks/thresholds.json" }, ), checkReadmeVersionBadge(root, version), - checkReadmeTestBadge(root, options.expectedTestCount ?? 606), + checkReadmeTestBadge(root, options.expectedTestCount ?? 665), checkReadmeCloneBranch(root, version), checkManifestVersion(root, version), checkApiFreezeVersion(root, version), - checkReadmeTestCount(root, options.expectedTestCount ?? 606), + checkReadmeTestCount(root, options.expectedTestCount ?? 665), + checkComplexityBudget(root, options), ]; const failed = checks.filter((check) => !check.ok); diff --git a/package.json b/package.json index 8f5ab06..06485d0 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,8 @@ "test": "node scripts/run.js test", "benchmark": "node scripts/run-benchmarks.js", "perf": "node scripts/perf-bench.js", + "complexity:check": "node scripts/complexity-check.js", + "complexity:report": "node scripts/complexity-report.js", "release:check": "node scripts/release-readiness.js" } } diff --git a/scripts/complexity-check.js b/scripts/complexity-check.js new file mode 100644 index 0000000..bc9b123 --- /dev/null +++ b/scripts/complexity-check.js @@ -0,0 +1,73 @@ +#!/usr/bin/env node +import fs from "fs"; +import path from "path"; +import { scanComplexity, summarizeComplexity } from "../lib/complexity.js"; + +function parseArgs(argv) { + const args = { projectRoot: process.cwd(), json: false, out: null }; + for (let i = 0; i < argv.length; i += 1) { + const arg = argv[i]; + if (arg === "--project-root") args.projectRoot = argv[++i]; + else if (arg === "--json") args.json = true; + else if (arg === "--out") args.out = argv[++i]; + else if (arg === "--help") args.help = true; + else throw new Error(`unknown argument: ${arg}`); + } + return args; +} + +function printHelp() { + console.log( + [ + "Usage: node scripts/complexity-check.js [options]", + "", + "Scans lib/, scripts/, tests/, tools/ and enforces the v4.x complexity budget.", + "Exits 1 when any hard limit is exceeded; 0 otherwise.", + "", + "Options:", + " --project-root Project root. Default: cwd", + " --json Print the machine-readable summary as JSON", + " --out Also write the JSON summary to ", + ].join("\n"), + ); +} + +function formatHuman(scan) { + const t = scan.totals; + const lines = []; + lines.push("Complexity check"); + lines.push(` scope: ${scan.dirs.join(", ")}`); + lines.push(` files: ${t.fileCount} (lib modules: ${t.libModuleCount})`); + lines.push(` total LOC: ${t.loc} (code LOC: ${t.codeLoc})`); + lines.push(` max file LOC: ${t.maxLoc} / hard ${scan.hardLimits.fileLoc} / soft ${scan.softTargets.fileLoc}`); + lines.push(` max imports: ${t.maxImports} / hard ${scan.hardLimits.fileImports} / soft ${scan.softTargets.fileImports}`); + lines.push(` max exports: ${t.maxExports} / hard ${scan.hardLimits.fileExports} / soft ${scan.softTargets.fileExports}`); + lines.push(` TODO/FIXME markers: ${t.todos} / hard ${scan.hardLimits.totalTodos} / soft ${scan.softTargets.totalTodos}`); + lines.push(` soft warnings: ${scan.softWarnings.length}`); + if (scan.violations.length > 0) { + lines.push(" VIOLATIONS:"); + for (const v of scan.violations) lines.push(` - ${v.message}`); + } + lines.push(` status: ${scan.ok ? "OK" : "FAILED"}`); + return lines.join("\n"); +} + +try { + const args = parseArgs(process.argv.slice(2)); + if (args.help) { + printHelp(); + process.exit(0); + } + const scan = scanComplexity(args.projectRoot); + const summary = summarizeComplexity(scan); + if (args.out) { + fs.mkdirSync(path.dirname(path.resolve(args.out)), { recursive: true }); + fs.writeFileSync(path.resolve(args.out), `${JSON.stringify(summary, null, 2)}\n`, "utf-8"); + } + if (args.json) console.log(JSON.stringify(summary, null, 2)); + else console.log(formatHuman(scan)); + process.exit(scan.ok ? 0 : 1); +} catch (err) { + console.error(err.stack || err.message); + process.exit(2); +} diff --git a/scripts/complexity-report.js b/scripts/complexity-report.js new file mode 100644 index 0000000..b6f21f1 --- /dev/null +++ b/scripts/complexity-report.js @@ -0,0 +1,133 @@ +#!/usr/bin/env node +import fs from "fs"; +import path from "path"; +import { scanComplexity, topFilesBy } from "../lib/complexity.js"; + +function parseArgs(argv) { + const args = { projectRoot: process.cwd(), out: null }; + for (let i = 0; i < argv.length; i += 1) { + const arg = argv[i]; + if (arg === "--project-root") args.projectRoot = argv[++i]; + else if (arg === "--out") args.out = argv[++i]; + else if (arg === "--help") args.help = true; + else throw new Error(`unknown argument: ${arg}`); + } + return args; +} + +function printHelp() { + console.log( + [ + "Usage: node scripts/complexity-report.js [options]", + "", + "Generates docs/COMPLEXITY_REPORT.md from a fresh complexity scan.", + "Read-only with respect to business logic; only writes the report file.", + "", + "Options:", + " --project-root Project root. Default: cwd", + " --out Output path. Default: docs/COMPLEXITY_REPORT.md", + ].join("\n"), + ); +} + +function table(header, rows) { + const lines = []; + lines.push(`| ${header.join(" | ")} |`); + lines.push(`|${header.map(() => "---").join("|")}|`); + for (const row of rows) lines.push(`| ${row.join(" | ")} |`); + return lines.join("\n"); +} + +function buildReport(scan) { + const t = scan.totals; + const lines = []; + lines.push("# Complexity Report"); + lines.push(""); + lines.push("> 自动生成,请勿手工编辑。运行 `npm run complexity:report` 刷新。"); + lines.push("> 预算与规则见 [COMPLEXITY_BUDGET.md](COMPLEXITY_BUDGET.md),债务清单见 [COMPLEXITY_DEBT.md](COMPLEXITY_DEBT.md)。"); + lines.push(""); + lines.push(`Generated at: ${scan.generatedAt}`); + lines.push(`Scan scope: ${scan.dirs.join(", ")}`); + lines.push(`Status: ${scan.ok ? "within budget" : "OVER BUDGET"}`); + lines.push(""); + lines.push("## 摘要"); + lines.push(""); + lines.push( + table( + ["指标", "当前值", "hard limit", "soft target"], + [ + ["文件数", String(t.fileCount), "-", "-"], + ["lib 模块数", String(t.libModuleCount), String(scan.hardLimits.libModuleCount), String(scan.softTargets.libModuleCount)], + ["总 LOC", String(t.loc), "-", "-"], + ["总代码 LOC", String(t.codeLoc), "-", "-"], + ["单文件最大 LOC", String(t.maxLoc), String(scan.hardLimits.fileLoc), String(scan.softTargets.fileLoc)], + ["单文件最大 imports", String(t.maxImports), String(scan.hardLimits.fileImports), String(scan.softTargets.fileImports)], + ["单文件最大 exports", String(t.maxExports), String(scan.hardLimits.fileExports), String(scan.softTargets.fileExports)], + ["TODO/FIXME 总数", String(t.todos), String(scan.hardLimits.totalTodos), String(scan.softTargets.totalTodos)], + ["soft 警告数", String(scan.softWarnings.length), "-", "0"], + ["hard 违规数", String(scan.violations.length), "0", "-"], + ], + ), + ); + lines.push(""); + + lines.push("## Top 10 最大文件 (LOC)"); + lines.push(""); + lines.push(table(["文件", "LOC", "代码 LOC"], topFilesBy(scan, "loc").map((f) => [f.path, String(f.loc), String(f.codeLoc)]))); + lines.push(""); + + lines.push("## Top 10 import 最多文件"); + lines.push(""); + lines.push(table(["文件", "imports"], topFilesBy(scan, "imports").map((f) => [f.path, String(f.imports)]))); + lines.push(""); + + lines.push("## Top 10 export 最多文件"); + lines.push(""); + lines.push(table(["文件", "exports"], topFilesBy(scan, "exports").map((f) => [f.path, String(f.exports)]))); + lines.push(""); + + lines.push("## TODO / FIXME 统计"); + lines.push(""); + const todoFiles = scan.files.filter((f) => f.todos > 0).sort((a, b) => b.todos - a.todos); + lines.push(`总计 ${t.todos} 处,分布于 ${todoFiles.length} 个文件。`); + lines.push(""); + if (todoFiles.length > 0) { + lines.push(table(["文件", "TODO/FIXME"], todoFiles.map((f) => [f.path, String(f.todos)]))); + lines.push(""); + } + + if (scan.violations.length > 0) { + lines.push("## Hard limit 违规"); + lines.push(""); + for (const v of scan.violations) lines.push(`- ${v.message}`); + lines.push(""); + } + + if (scan.softWarnings.length > 0) { + lines.push("## Soft target 警告"); + lines.push(""); + lines.push("以下项目超出 soft target 但仍在 hard limit 内,是优先治理对象(参见 COMPLEXITY_DEBT.md)。"); + lines.push(""); + for (const w of scan.softWarnings.sort((a, b) => b.value - a.value)) lines.push(`- ${w.message}`); + lines.push(""); + } + + return `${lines.join("\n")}\n`; +} + +try { + const args = parseArgs(process.argv.slice(2)); + if (args.help) { + printHelp(); + process.exit(0); + } + const scan = scanComplexity(args.projectRoot); + const outPath = path.resolve(args.out || path.join(args.projectRoot, "docs", "COMPLEXITY_REPORT.md")); + fs.mkdirSync(path.dirname(outPath), { recursive: true }); + fs.writeFileSync(outPath, buildReport(scan), "utf-8"); + console.log(`Complexity report written to: ${outPath}`); + console.log(`Status: ${scan.ok ? "within budget" : "OVER BUDGET"} (${scan.violations.length} violation(s), ${scan.softWarnings.length} soft warning(s))`); +} catch (err) { + console.error(err.stack || err.message); + process.exit(2); +} diff --git a/tests/control-handlers.characterization.test.js b/tests/control-handlers.characterization.test.js new file mode 100644 index 0000000..22322d3 --- /dev/null +++ b/tests/control-handlers.characterization.test.js @@ -0,0 +1,314 @@ +/** + * Characterization tests for tools/control.js HANDLERS (C-001 phase 4 / pre-split). + * + * Purpose: lock the CURRENT observable behavior of control handler domains that + * are not already covered elsewhere, so a future "split HANDLERS by domain" + * refactor has a regression net. These assert behavior as-is; they do not change it. + * + * Already covered elsewhere (NOT duplicated here): + * - proposals/reviews happy + reject paths: tests/review-governance.test.js + * - run_model_advisor mock-key decrypt path + set_config non-persist: + * tests/control-credentials.test.js + * - redactConfig behaviors: tests/control-redaction.test.js + * - release_readiness / runtime-package resolution: tests/control-runtime-package.test.js + * + * No network is exercised; no real user directory is written (HANA_HOME → tmp). + */ +import { describe, it, before, after } from "node:test"; +import assert from "node:assert/strict"; +import fs from "fs"; +import os from "os"; +import path from "path"; +import { execute } from "../tools/control.js"; +import { parseToolResult, unwrapToolResult } from "./_test-utils.js"; + +const savedHanaHome = process.env.HANA_HOME; +const savedFetch = globalThis.fetch; + +before(() => { + // Any network attempt during these tests is a failure. + globalThis.fetch = async () => { throw new Error("network must not be reached in characterization tests"); }; +}); + +after(() => { + if (savedHanaHome === undefined) delete process.env.HANA_HOME; + else process.env.HANA_HOME = savedHanaHome; + globalThis.fetch = savedFetch; +}); + +/** + * Run `fn(ctx)` against a fresh, isolated learner home. Optionally seeds + * runtime-config.json with `config`. Restores HANA_HOME afterward. + */ +async function withLearner({ config } = {}, fn) { + const home = fs.mkdtempSync(path.join(os.tmpdir(), `control-char-${process.pid}-${Date.now()}-`)); + process.env.HANA_HOME = home; + const learner = path.join(home, "self-learning"); + fs.mkdirSync(learner, { recursive: true }); + fs.writeFileSync(path.join(learner, "patterns.json"), "[]", "utf-8"); + if (config) fs.writeFileSync(path.join(learner, "runtime-config.json"), JSON.stringify(config), "utf-8"); + try { + return await fn({ pluginDir: home, learner }); + } finally { + fs.rmSync(home, { recursive: true, force: true }); + } +} + +const run = (action, extra = {}, ctx) => execute({ action, ...extra }, ctx); +const json = async (action, extra, ctx) => parseToolResult(await run(action, extra, ctx)); + +// ── Domain 1: status / doctor read-only output ── + +describe("status / doctor read-only output", () => { + it("status reports a zeroed snapshot with redacted config on a fresh store", async () => { + await withLearner({}, async (ctx) => { + const out = await json("status", {}, ctx); + assert.equal(out.patterns, 0); + assert.equal(out.injectable, 0); + assert.deepEqual(out.proposals, { pending: 0, applied: 0, rejected: 0, dir: out.proposals.dir }); + assert.deepEqual(out.reviews, { queued: 0, blocked: 0, approved: 0 }); + assert.deepEqual(out.agentTasks, { total: 0, waiting: 0 }); + assert.deepEqual(out.transferCandidates, { total: 0, pending: 0, validated: 0, failed: 0 }); + assert.deepEqual(out.skillPromotion, { candidates: 0, active: 0 }); + assert.equal(out.config.governanceProfile, "balanced"); + assert.ok(out.dataDir.endsWith(path.join("self-learning")) || out.dataDir.includes("self-learning")); + }); + }); + + it("doctor format=json returns a structured report (status good on empty store)", async () => { + await withLearner({}, async (ctx) => { + const report = await json("doctor", { format: "json" }, ctx); + assert.equal(report.status, "good"); + assert.equal(report.score, 100); + assert.deepEqual(report.issues, []); + assert.equal(typeof report.generatedAt, "string"); + }); + }); + + it("doctor default format returns a non-JSON text report", async () => { + await withLearner({}, async (ctx) => { + const text = unwrapToolResult(await run("doctor", {}, ctx)); + assert.equal(typeof text, "string"); + assert.ok(text.length > 0); + assert.ok(!text.trimStart().startsWith("{"), "default doctor output should be text, not JSON"); + }); + }); + + it("list returns an empty array on a fresh store", async () => { + await withLearner({}, async (ctx) => { + assert.deepEqual(await json("list", {}, ctx), []); + }); + }); +}); + +// ── Domain 2: proposals / reviews query output (empty-state shape) ── + +describe("proposals / reviews query output", () => { + it("list_proposals returns ok + empty list + nextAction", async () => { + await withLearner({}, async (ctx) => { + assert.deepEqual(await json("list_proposals", {}, ctx), { + ok: true, proposals: [], nextAction: "show_proposal or preview_proposal", + }); + }); + }); + + it("list_reviews returns ok + empty list + nextAction", async () => { + await withLearner({}, async (ctx) => { + assert.deepEqual(await json("list_reviews", {}, ctx), { + ok: true, reviews: [], nextAction: "show_proposal then preview_proposal", + }); + }); + }); + + it("review_panel recommends no action on an empty store", async () => { + await withLearner({}, async (ctx) => { + const panel = await json("review_panel", {}, ctx); + assert.deepEqual(panel.recommendedNextActions, ["no review action needed"]); + }); + }); + + it("show_proposal without an id is rejected", async () => { + await withLearner({}, async (ctx) => { + await assert.rejects(() => run("show_proposal", {}, ctx), /proposalId is required/); + }); + }); +}); + +// ── Domain 3: events / event log ── + +describe("events / event log output", () => { + it("list_events returns an empty list", async () => { + await withLearner({}, async (ctx) => { + assert.deepEqual(await json("list_events", {}, ctx), { ok: true, events: [] }); + }); + }); + + it("event_summary returns an empty replay summary", async () => { + await withLearner({}, async (ctx) => { + assert.deepEqual(await json("event_summary", {}, ctx), { + ok: true, summary: { count: 0, byType: {}, entities: {} }, + }); + }); + }); + + it("verify_event_log reports an intact (empty) chain", async () => { + await withLearner({}, async (ctx) => { + const out = await json("verify_event_log", {}, ctx); + assert.equal(out.ok, true); + assert.equal(out.events, 0); + assert.equal(out.brokenAt, null); + }); + }); +}); + +// ── Domain 4: agent-tasks query + waiting state ── + +describe("agent-tasks query output", () => { + it("list_agent_tasks returns ok + empty tasks", async () => { + await withLearner({}, async (ctx) => { + assert.deepEqual(await json("list_agent_tasks", {}, ctx), { + ok: true, tasks: [], nextAction: "show_agent_task", + }); + }); + }); + + it("show_agent_task without a taskId is rejected", async () => { + await withLearner({}, async (ctx) => { + await assert.rejects(() => run("show_agent_task", {}, ctx), /taskId is required/); + }); + }); +}); + +// ── Domain 5: transfers query ── + +describe("transfers query output", () => { + it("list_transfer_candidates returns ok + empty candidates", async () => { + await withLearner({}, async (ctx) => { + assert.deepEqual(await json("list_transfer_candidates", {}, ctx), { + ok: true, candidates: [], nextAction: "show_transfer_candidate or record_transfer_validation", + }); + }); + }); + + it("show_transfer_candidate without a candidateId is rejected", async () => { + await withLearner({}, async (ctx) => { + await assert.rejects(() => run("show_transfer_candidate", {}, ctx), /candidateId is required/); + }); + }); +}); + +// ── Domain 6: skill promotion queries + policy profiles ── + +describe("skill promotion / policy profile output", () => { + it("list_skill_candidates returns ok + empty candidates", async () => { + await withLearner({}, async (ctx) => { + assert.deepEqual(await json("list_skill_candidates", {}, ctx), { + ok: true, candidates: [], nextAction: "run_skill_promotion_loop or list_active_skills", + }); + }); + }); + + it("list_active_skills returns ok + empty skills", async () => { + await withLearner({}, async (ctx) => { + assert.deepEqual(await json("list_active_skills", {}, ctx), { + ok: true, skills: [], nextAction: "export_audit_bundle", + }); + }); + }); + + it("list_policy_profiles lists the three profiles with balanced current by default", async () => { + await withLearner({}, async (ctx) => { + const out = await json("list_policy_profiles", {}, ctx); + assert.equal(out.ok, true); + assert.equal(out.current, "balanced"); + assert.deepEqual(out.profiles.map((p) => p.name), ["conservative", "balanced", "autonomous"]); + }); + }); +}); + +// ── Domain 7: set_config sensitive output / config boundary ── + +describe("set_config output and validation boundary", () => { + it("applies a valid numeric config and echoes the persisted value", async () => { + await withLearner({}, async (ctx) => { + const out = await json("set_config", { minInjectCount: 5 }, ctx); + assert.equal(out.ok, true); + assert.equal(out.config.minInjectCount, 5); + assert.ok(out.validation); + // persisted: a follow-up status reflects it + const status = await json("status", {}, ctx); + assert.equal(status.config.minInjectCount, 5); + }); + }); + + it("rejects an invalid config value without partial application", async () => { + await withLearner({}, async (ctx) => { + await assert.rejects(() => run("set_config", { minInjectScore: "bad" }, ctx), /config validation failed/); + }); + }); +}); + +// ── Domain 8: run_model_advisor disabled / no-key (no network) ── + +describe("run_model_advisor without network", () => { + it("short-circuits when the advisor is disabled (default config)", async () => { + await withLearner({}, async (ctx) => { + const out = await json("run_model_advisor", {}, ctx); + assert.equal(out.ok, false); + assert.equal(out.error, "disabled"); + }); + }); + + it("reports a missing key when enabled for a private endpoint with no key", async () => { + await withLearner({ config: { modelAdvisorEnabled: true, modelAdvisorSource: "private", modelAdvisorBaseUrl: "https://x.example.com", modelAdvisorModel: "m" } }, async (ctx) => { + const out = await json("run_model_advisor", {}, ctx); + assert.equal(out.ok, false); + assert.match(out.error, /api key missing/i); + }); + }); +}); + +// ── Domain 9: safe-rejection / security boundary paths ── + +describe("safe-rejection and security boundary paths", () => { + it("apply_proposal is blocked under the conservative governance profile", async () => { + await withLearner({ config: { governanceProfile: "conservative" } }, async (ctx) => { + await assert.rejects( + () => run("apply_proposal", { proposalId: "p-does-not-exist" }, ctx), + /conservative profile requires review-first flow/, + ); + }); + }); + + it("apply_proposal without an id is rejected", async () => { + await withLearner({}, async (ctx) => { + await assert.rejects(() => run("apply_proposal", {}, ctx), /proposalId is required/); + }); + }); + + it("approve_review / apply_review reject unknown reviews", async () => { + await withLearner({}, async (ctx) => { + await assert.rejects(() => run("approve_review", {}, ctx), /id or proposalId is required/); + await assert.rejects(() => run("approve_review", { id: "review:nope" }, ctx), /review not found/); + await assert.rejects(() => run("apply_review", { id: "review:nope" }, ctx), /review not found/); + }); + }); + + it("trust_project_scripts refuses a workspace whose package.json has no scripts", async () => { + await withLearner({}, async (ctx) => { + const ws = fs.mkdtempSync(path.join(os.tmpdir(), "control-char-ws-")); + fs.writeFileSync(path.join(ws, "package.json"), JSON.stringify({ name: "x" }), "utf-8"); + try { + await assert.rejects(() => run("trust_project_scripts", { workspaceRoot: ws }, ctx), /no scripts found in package\.json/); + } finally { + fs.rmSync(ws, { recursive: true, force: true }); + } + }); + }); + + it("an unknown action is rejected by execute", async () => { + await withLearner({}, async (ctx) => { + await assert.rejects(() => run("__no_such_action__", {}, ctx), /unknown action/); + }); + }); +}); diff --git a/tests/control-parameters.test.js b/tests/control-parameters.test.js new file mode 100644 index 0000000..6ce32f4 --- /dev/null +++ b/tests/control-parameters.test.js @@ -0,0 +1,62 @@ +/** + * Unit tests for tools/control-parameters.js — the parameter schema property + * table extracted from tools/control.js (C-001 phase 3b). + * Verifies the extracted properties and that control.js still composes an + * equivalent schema (same fields, same required, action enum intact). + * Run: node --test tests/control-parameters.test.js + */ + +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; +import { CONTROL_PARAM_PROPERTIES } from "../tools/control-parameters.js"; +import { parameters } from "../tools/control.js"; + +describe("CONTROL_PARAM_PROPERTIES", () => { + it("contains the key non-action fields with unchanged shapes", () => { + assert.deepEqual(CONTROL_PARAM_PROPERTIES.id, { type: "string", description: "Pattern id for approve/reject." }); + assert.deepEqual(CONTROL_PARAM_PROPERTIES.proposalId, { type: "string", description: "Proposal id for show/apply/reject proposal actions." }); + assert.deepEqual(CONTROL_PARAM_PROPERTIES.validationStatus, { type: "string", enum: ["passed", "failed"], description: "Target validation status for record_transfer_validation." }); + assert.deepEqual(CONTROL_PARAM_PROPERTIES.evidence, { type: "array", items: { type: "string" }, description: "Validation evidence lines for transfer registry actions." }); + assert.deepEqual(CONTROL_PARAM_PROPERTIES.format, { type: "string", enum: ["text", "json"], description: "Output format for the doctor action. Default text." }); + assert.deepEqual(CONTROL_PARAM_PROPERTIES.governanceProfile, { type: "string", enum: ["conservative", "balanced", "autonomous"], description: "Governance policy profile to apply." }); + assert.deepEqual(CONTROL_PARAM_PROPERTIES.semanticCacheMaxEntries, { type: "number" }); + }); + + it("does NOT contain the action property (action stays in control.js)", () => { + assert.equal("action" in CONTROL_PARAM_PROPERTIES, false); + }); +}); + +describe("control.js parameters schema after extraction", () => { + it("keeps the top-level shape: object + required action", () => { + assert.equal(parameters.type, "object"); + assert.deepEqual(parameters.required, ["action"]); + }); + + it("preserves the pre-extraction behavior of not declaring additionalProperties", () => { + // The original schema had no `additionalProperties` key; adding one would be + // a contract change, so extraction must leave it absent. + assert.equal("additionalProperties" in parameters, false); + }); + + it("keeps the action property with its HANDLERS-derived enum", () => { + assert.equal(parameters.properties.action.type, "string"); + assert.equal(parameters.properties.action.description, "Control action to run."); + assert.ok(Array.isArray(parameters.properties.action.enum)); + assert.ok(parameters.properties.action.enum.includes("status")); + assert.ok(parameters.properties.action.enum.includes("set_config")); + assert.ok(parameters.properties.action.enum.length > 20); + }); + + it("lists action first, then every CONTROL_PARAM_PROPERTIES field in order", () => { + const keys = Object.keys(parameters.properties); + assert.equal(keys[0], "action"); + assert.deepEqual(keys.slice(1), Object.keys(CONTROL_PARAM_PROPERTIES)); + }); + + it("references the extracted properties object (every non-action field present and identical)", () => { + for (const [key, value] of Object.entries(CONTROL_PARAM_PROPERTIES)) { + assert.deepEqual(parameters.properties[key], value, `field ${key} differs`); + } + }); +}); diff --git a/tests/control-redaction.test.js b/tests/control-redaction.test.js new file mode 100644 index 0000000..1a594d5 --- /dev/null +++ b/tests/control-redaction.test.js @@ -0,0 +1,118 @@ +/** + * Characterization tests that LOCK the two distinct config-redaction behaviors + * (C-001 phase 3c). They are deliberately separate functions with different + * semantics and must NOT be merged: + * + * - tools/control.js redactConfig (via execute "status"): masks only a fixed + * two-key allowlist with "***"; leaves URLs and everything else untouched. + * - lib/audit-bundle.js redactConfig (via buildAuditBundle): regex-masks any + * api-key/token/secret/password key with "[redacted]", and reduces url/ + * endpoint values to their origin. + * + * Both are exercised through their public entry points, so no private function + * is exported and no redaction logic is moved. + */ +import { describe, it, before, after } from "node:test"; +import assert from "node:assert/strict"; +import fs from "fs"; +import path from "path"; +import os from "os"; +import { buildAuditBundle } from "../lib/audit-bundle.js"; + +// ── control.js redactConfig (fixed allowlist, "***", URLs untouched) ── + +const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), `control-redact-${process.pid}-${Date.now()}`)); +const savedHanaHome = process.env.HANA_HOME; +process.env.HANA_HOME = tmpHome; + +let control; +const learnerDir = path.join(tmpHome, "self-learning"); + +before(async () => { + fs.mkdirSync(learnerDir, { recursive: true }); + control = await import("../tools/control.js"); + fs.writeFileSync(path.join(learnerDir, "patterns.json"), "[]", "utf-8"); + fs.writeFileSync(path.join(learnerDir, "runtime-config.json"), JSON.stringify({ + modelAdvisorApiKey: "sk-secret-advisor", + semanticEmbeddingApiKey: "sk-secret-embedding", + modelAdvisorBaseUrl: "https://api.example.com/v1/private-path", + modelAdvisorEnabled: true, + }), "utf-8"); +}); + +after(() => { + if (savedHanaHome === undefined) delete process.env.HANA_HOME; + else process.env.HANA_HOME = savedHanaHome; + fs.rmSync(tmpHome, { recursive: true, force: true }); +}); + +async function statusConfig() { + const raw = await control.execute({ action: "status" }, { pluginDir: tmpHome }); + const text = raw?.content?.[0]?.text ?? (typeof raw === "string" ? raw : JSON.stringify(raw)); + return JSON.parse(text).config; +} + +describe("control.js redactConfig (via execute status)", () => { + it("masks exactly the two sensitive keys with '***'", async () => { + const cfg = await statusConfig(); + assert.equal(cfg.modelAdvisorApiKey, "***"); + assert.equal(cfg.semanticEmbeddingApiKey, "***"); + }); + + it("does NOT touch URL/base-url values (key difference vs audit-bundle)", async () => { + const cfg = await statusConfig(); + assert.equal(cfg.modelAdvisorBaseUrl, "https://api.example.com/v1/private-path"); + }); + + it("leaves non-sensitive keys unchanged", async () => { + const cfg = await statusConfig(); + assert.equal(cfg.modelAdvisorEnabled, true); + }); +}); + +// ── audit-bundle.js redactConfig (regex mask "[redacted]", URL→origin) ── + +describe("lib/audit-bundle.js redactConfig (via buildAuditBundle)", () => { + function redactedConfig(config) { + return buildAuditBundle({ config }).config; + } + + it("regex-masks api-key / token / secret / password keys with '[redacted]'", () => { + const out = redactedConfig({ + modelAdvisorApiKey: "sk-x", + authToken: "tok-y", + mySecret: "s", + password: "p", + }); + assert.equal(out.modelAdvisorApiKey, "[redacted]"); + assert.equal(out.authToken, "[redacted]"); + assert.equal(out.mySecret, "[redacted]"); + assert.equal(out.password, "[redacted]"); + }); + + it("reduces url/endpoint string values to their origin", () => { + const out = redactedConfig({ + modelAdvisorBaseUrl: "https://api.example.com/v1/private-path?token=abc", + someEndpoint: "https://host.example.org:8443/deep/path", + }); + assert.equal(out.modelAdvisorBaseUrl, "https://api.example.com"); + assert.equal(out.someEndpoint, "https://host.example.org:8443"); + }); + + it("keeps falsy sensitive values and non-matching keys unchanged", () => { + const out = redactedConfig({ + modelAdvisorApiKey: "", // sensitive but falsy → left as-is + modelAdvisorEnabled: true, // non-matching → untouched + plainNumber: 42, + }); + assert.equal(out.modelAdvisorApiKey, ""); + assert.equal(out.modelAdvisorEnabled, true); + assert.equal(out.plainNumber, 42); + }); + + it("uses a different mask token than control.js ('[redacted]' not '***')", () => { + const out = redactedConfig({ modelAdvisorApiKey: "sk-x" }); + assert.notEqual(out.modelAdvisorApiKey, "***"); + assert.equal(out.modelAdvisorApiKey, "[redacted]"); + }); +}); diff --git a/tests/control-summaries.test.js b/tests/control-summaries.test.js new file mode 100644 index 0000000..c15649a --- /dev/null +++ b/tests/control-summaries.test.js @@ -0,0 +1,135 @@ +/** + * Unit tests for tools/control-summaries.js — pure summary/formatting helpers + * extracted from tools/control.js (C-001 phase 3a). + * Run: node --test tests/control-summaries.test.js + */ + +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; +import { + countByStatus, + summarizeDecoratedPatterns, + countWaitingAgentTasks, + validationNextAction, + reviewPanelNextActions, +} from "../tools/control-summaries.js"; + +describe("countByStatus", () => { + it("returns an empty object for an empty array", () => { + assert.deepEqual(countByStatus([]), {}); + }); + + it("returns an empty object when called with no arguments", () => { + assert.deepEqual(countByStatus(), {}); + }); + + it("counts mixed statuses", () => { + const rows = [ + { status: "pending" }, + { status: "applied" }, + { status: "pending" }, + { status: "rejected" }, + ]; + assert.deepEqual(countByStatus(rows), { pending: 2, applied: 1, rejected: 1 }); + }); + + it("buckets missing/empty status under 'unknown'", () => { + const rows = [{ status: "pending" }, {}, { status: "" }, null]; + assert.deepEqual(countByStatus(rows), { pending: 1, unknown: 3 }); + }); + + it("supports counting by an alternate field", () => { + const rows = [{ state: "a" }, { state: "b" }, { state: "a" }]; + assert.deepEqual(countByStatus(rows, "state"), { a: 2, b: 1 }); + }); +}); + +describe("summarizeDecoratedPatterns", () => { + it("returns a zeroed summary for an empty array", () => { + assert.deepEqual(summarizeDecoratedPatterns([]), { + total: 0, injectable: 0, pending: 0, approved: 0, rejected: 0, + }); + }); + + it("aggregates totals, injectable flag, and per-status counts", () => { + const patterns = [ + { injectable: true, status: "pending" }, + { injectable: false, status: "approved" }, + { injectable: true, status: "rejected" }, + { injectable: true, status: "pending" }, + { status: "other" }, // counts toward total only + ]; + assert.deepEqual(summarizeDecoratedPatterns(patterns), { + total: 5, injectable: 3, pending: 2, approved: 1, rejected: 1, + }); + }); +}); + +describe("countWaitingAgentTasks", () => { + it("returns 0 for an empty array", () => { + assert.equal(countWaitingAgentTasks([]), 0); + }); + + it("counts only tasks waiting_for_human among a mix of states", () => { + const tasks = [ + { state: "waiting_for_human" }, + { state: "pending" }, + { state: "done" }, + { state: "waiting_for_human" }, + { state: "running" }, + ]; + assert.equal(countWaitingAgentTasks(tasks), 2); + }); + + it("returns 0 when no task is waiting_for_human", () => { + assert.equal(countWaitingAgentTasks([{ state: "pending" }, { state: "done" }]), 0); + }); +}); + +describe("validationNextAction", () => { + it("recommends the approve/apply path when validation is ok", () => { + assert.equal(validationNextAction({ ok: true }), "approve_review then apply_review"); + }); + + it("recommends fix/reject when validation is not ok", () => { + assert.equal(validationNextAction({ ok: false }), "fix proposal or reject_proposal"); + }); + + it("treats missing/undefined validation as not ok", () => { + assert.equal(validationNextAction(undefined), "fix proposal or reject_proposal"); + assert.equal(validationNextAction({}), "fix proposal or reject_proposal"); + }); +}); + +describe("reviewPanelNextActions", () => { + it("returns the no-op action when there is nothing to do", () => { + assert.deepEqual(reviewPanelNextActions(), ["no review action needed"]); + assert.deepEqual(reviewPanelNextActions({ counts: {} }), ["no review action needed"]); + }); + + it("surfaces blocked reviews", () => { + const actions = reviewPanelNextActions({ counts: { blockedReviews: 2 } }); + assert.deepEqual(actions, ["validate blocked reviews, then fix or reject them"]); + }); + + it("surfaces pending reviews", () => { + const actions = reviewPanelNextActions({ counts: { pendingReviews: 1 } }); + assert.deepEqual(actions, ["preview queued reviews, then approve_review or reject_review"]); + }); + + it("surfaces pending proposals", () => { + const actions = reviewPanelNextActions({ counts: { pendingProposals: 3 } }); + assert.deepEqual(actions, ["validate_proposal for pending proposals not yet reviewed"]); + }); + + it("combines all branches in order when all are present", () => { + const actions = reviewPanelNextActions({ + counts: { blockedReviews: 1, pendingReviews: 1, pendingProposals: 1 }, + }); + assert.deepEqual(actions, [ + "validate blocked reviews, then fix or reject them", + "preview queued reviews, then approve_review or reject_review", + "validate_proposal for pending proposals not yet reviewed", + ]); + }); +}); diff --git a/tests/release-readiness.test.js b/tests/release-readiness.test.js index 163443b..f14e57f 100644 --- a/tests/release-readiness.test.js +++ b/tests/release-readiness.test.js @@ -10,7 +10,7 @@ function write(filePath, content) { fs.writeFileSync(filePath, content, "utf-8"); } -function makeProject({ version = "4.0.18-lts", lockVersion = version, scenarios = 16, omitAcceptance = false, testCount = 606 } = {}) { +function makeProject({ version = "4.0.18-lts", lockVersion = version, scenarios = 16, omitAcceptance = false, testCount = 665 } = {}) { const root = fs.mkdtempSync(path.join(os.tmpdir(), "hanako-release-readiness-")); const baseVersion = version.replace(/-lts$/, ""); write(path.join(root, "package.json"), JSON.stringify({ name: "hanako-runtime-learner", version }, null, 2)); diff --git a/tools/control-handlers/events.js b/tools/control-handlers/events.js new file mode 100644 index 0000000..9649099 --- /dev/null +++ b/tools/control-handlers/events.js @@ -0,0 +1,27 @@ +// Event-log read-only control handlers (C-001 HANDLERS split — events domain). +// +// Extracted verbatim from tools/control.js. Pure read handlers: they take +// (input, p), read the event log under p.learnerDir, and return a JSON string. +// They mutate nothing and own NO permission/side-effect decisions — control.js +// keeps the action dispatch, the *_ACTIONS classification sets, +// describeControlSideEffect and sessionPermission. This module only implements +// the handler bodies and is spread back into the control HANDLERS table under +// the same action names. + +import { readEvents, replayEventState, verifyEventLog } from "../../lib/event-log.js"; + +export const eventHandlers = { + list_events(input, p) { + return JSON.stringify({ ok: true, events: readEvents(p.learnerDir, { limit: input.limit || 50, entityId: input.id || null }) }, null, 2); + }, + + event_summary(input, p) { + const events = readEvents(p.learnerDir, { limit: input.limit || 5000, entityId: input.id || null }); + return JSON.stringify({ ok: true, summary: replayEventState(events) }, null, 2); + }, + + verify_event_log(input, p) { + const result = verifyEventLog(p.learnerDir); + return JSON.stringify({ ...result, nextAction: result.ok ? "export_audit_bundle or continue" : "inspect event_log.jsonl and restore from trusted backup" }, null, 2); + }, +}; diff --git a/tools/control-handlers/skill-policy.js b/tools/control-handlers/skill-policy.js new file mode 100644 index 0000000..0ea1898 --- /dev/null +++ b/tools/control-handlers/skill-policy.js @@ -0,0 +1,28 @@ +// Skill-promotion & policy read-only control handlers (C-001 HANDLERS split pilot). +// +// Extracted verbatim from tools/control.js. These are pure read handlers: they +// take (input, p, config), read from p.learnerDir, and return a JSON string. +// They own NO permission/side-effect decisions — control.js keeps the action +// dispatch, the *_ACTIONS classification sets, describeControlSideEffect and +// sessionPermission. This module only implements the handler bodies and is +// spread back into the control HANDLERS table under the same action names. + +import { loadActiveSkills, loadSkillCandidates } from "../../lib/skill-promotion-loop.js"; +import { listPolicyProfiles } from "../../lib/policy-profiles.js"; + +export const skillPolicyHandlers = { + list_skill_candidates(input, p) { + const store = loadSkillCandidates(p.learnerDir); + const candidates = store.candidates.slice(0, input.limit || 50).map((c) => ({ id: c.id, status: c.status, rule: c.rule, evidence: c.evidence, scope: c.scope, updatedAt: c.updatedAt })); + return JSON.stringify({ ok: true, candidates, nextAction: "run_skill_promotion_loop or list_active_skills" }, null, 2); + }, + + list_active_skills(input, p) { + const registry = loadActiveSkills(p.learnerDir); + return JSON.stringify({ ok: true, skills: registry.skills.slice(0, input.limit || 50), nextAction: "export_audit_bundle" }, null, 2); + }, + + list_policy_profiles(input, p, config) { + return JSON.stringify({ ok: true, profiles: listPolicyProfiles(), current: config.governanceProfile || "balanced" }, null, 2); + }, +}; diff --git a/tools/control-parameters.js b/tools/control-parameters.js new file mode 100644 index 0000000..718ff83 --- /dev/null +++ b/tools/control-parameters.js @@ -0,0 +1,63 @@ +// Parameter schema properties extracted from tools/control.js (C-001 phase 3b). +// +// Pure declarative data: the JSON-schema property definitions for the +// self_learning_control tool's input, minus the `action` property. `action` +// stays in control.js because its enum is `Object.keys(HANDLERS)` and must not +// depend on the handler table from here. control.js composes the final schema as: +// properties: { action: { ... }, ...CONTROL_PARAM_PROPERTIES } +// which preserves the original key order (action first, then these in order). +// +// Field names, descriptions, types, items, enums and defaults are unchanged from +// control.js. No behavior change. + +export const CONTROL_PARAM_PROPERTIES = { + id: { type: "string", description: "Pattern id for approve/reject." }, + proposalId: { type: "string", description: "Proposal id for show/apply/reject proposal actions." }, + taskId: { type: "string", description: "Agent task id for agent task show/approve/reject/resume actions." }, + candidateId: { type: "string", description: "Cross-project transfer candidate id for transfer registry actions." }, + benchmarkId: { type: "string", description: "Optional benchmark scenario id for run_benchmarks." }, + benchmarkOutputDir: { type: "string", description: "Optional output directory for benchmark reports." }, + benchmarkRunsDir: { type: "string", description: "Optional benchmark-runs directory for audit dashboard lookup." }, + benchmarkReportPath: { type: "string", description: "Optional explicit benchmark-report.json path for audit dashboard lookup." }, + releaseOutputDir: { type: "string", description: "Optional output directory for release readiness reports." }, + projectRoot: { type: "string", description: "Optional source checkout root for release/benchmark actions." }, + sourceRoot: { type: "string", description: "Alias of projectRoot for runtime package source checkout resolution." }, + candidate: { type: "object", description: "Cross-project transfer candidate object for register_transfer_candidate." }, + validationStatus: { type: "string", enum: ["passed", "failed"], description: "Target validation status for record_transfer_validation." }, + evidence: { type: "array", items: { type: "string" }, description: "Validation evidence lines for transfer registry actions." }, + requestId: { type: "string", description: "Approval request id for agent task approval actions." }, + reason: { type: "string", description: "Optional reason for proposal rejection." }, + status: { type: "string", description: "Optional proposal status filter: pending, applied, or rejected." }, + format: { type: "string", enum: ["text", "json"], description: "Output format for the doctor action. Default text." }, + governanceProfile: { type: "string", enum: ["conservative", "balanced", "autonomous"], description: "Governance policy profile to apply." }, + limit: { type: "number", description: "Maximum number of events/reviews to return for list actions." }, + autoInjectHighConfidence: { type: "boolean" }, + autoApproveHighConfidence: { type: "boolean" }, + minInjectScore: { type: "number" }, + minInjectCount: { type: "number" }, + decayHalfLifeDays: { type: "number" }, + includePendingPreferences: { type: "boolean" }, + learnFromUsage: { type: "boolean" }, + includeUsageInAdvisorPrompt: { type: "boolean" }, + officialMemoryBridgeEnabled: { type: "boolean" }, + officialMemoryBridgeMaxResults: { type: "number" }, + durableMemoryMaxCount: { type: "number" }, + largeUsageTokenThreshold: { type: "number" }, + officialUtilityModelDisplay: { type: "string" }, + modelAdvisorEnabled: { type: "boolean" }, + modelAdvisorSource: { type: "string", enum: ["official", "private", "off"] }, + modelAdvisorBaseUrl: { type: "string" }, + modelAdvisorApiKey: { type: "string" }, + modelAdvisorModel: { type: "string" }, + modelAdvisorMaxTokens: { type: "number" }, + modelAdvisorMinIntervalMinutes: { type: "number" }, + workStatusEnabled: { type: "boolean" }, + workStatusText: { type: "string" }, + proposalChatNotificationsEnabled: { type: "boolean" }, + requireReviewForAutoApply: { type: "boolean" }, + semanticSearchEnabled: { type: "boolean" }, + semanticEmbeddingBaseUrl: { type: "string" }, + semanticEmbeddingApiKey: { type: "string" }, + semanticEmbeddingModel: { type: "string" }, + semanticCacheMaxEntries: { type: "number" }, +}; diff --git a/tools/control-summaries.js b/tools/control-summaries.js new file mode 100644 index 0000000..6f684a6 --- /dev/null +++ b/tools/control-summaries.js @@ -0,0 +1,52 @@ +// Pure summary / formatting helpers extracted from tools/control.js (C-001 phase 3a). +// +// These functions are side-effect free: they only aggregate or map plain data +// for the control tool's JSON responses. They were module-private in control.js +// and had no external consumers; moving them here shrinks the control dispatcher +// and makes them directly unit-testable. Bodies are unchanged from control.js. + +export function countByStatus(rows = [], field = "status") { + const counts = {}; + for (const row of rows) { + const key = row?.[field] || "unknown"; + counts[key] = (counts[key] || 0) + 1; + } + return counts; +} + +export function summarizeDecoratedPatterns(patterns = []) { + const summary = { total: 0, injectable: 0, pending: 0, approved: 0, rejected: 0 }; + for (const pattern of patterns) { + summary.total += 1; + if (pattern.injectable) summary.injectable += 1; + if (pattern.status === "pending") summary.pending += 1; + else if (pattern.status === "approved") summary.approved += 1; + else if (pattern.status === "rejected") summary.rejected += 1; + } + return summary; +} + +export function countWaitingAgentTasks(tasks = []) { + let waiting = 0; + for (const task of tasks) { + if (task.state === "waiting_for_human") waiting += 1; + } + return waiting; +} + +export function validationNextAction(validation) { + return validation?.ok + ? "approve_review then apply_review" + : "fix proposal or reject_proposal"; +} + +export function reviewPanelNextActions(panel = {}) { + const actions = []; + const blocked = panel.counts?.blockedReviews || 0; + const pending = panel.counts?.pendingReviews || 0; + if (blocked > 0) actions.push("validate blocked reviews, then fix or reject them"); + if (pending > 0) actions.push("preview queued reviews, then approve_review or reject_review"); + if (panel.counts?.pendingProposals > 0) actions.push("validate_proposal for pending proposals not yet reviewed"); + if (!actions.length) actions.push("no review action needed"); + return actions; +} diff --git a/tools/control.js b/tools/control.js index 613e818..60b5349 100644 --- a/tools/control.js +++ b/tools/control.js @@ -7,12 +7,12 @@ import { listProposals, readProposal, rejectProposal, previewProposalDiff, verif import { applyProposalSafely } from "../lib/proposal-apply-safe.js"; import { validateConfigPatch, validateProposal } from "../lib/validation-gate.js"; import { enqueueReviewForProposal, listReviews, readReview, reviewPanel, updateReviewStatus } from "../lib/review-queue.js"; -import { readEvents, appendEvent, replayEventState, verifyEventLog } from "../lib/event-log.js"; +import { readEvents, appendEvent, replayEventState } from "../lib/event-log.js"; import { writeSkillIfChanged } from "../lib/skill-lifecycle.js"; import { runDoctorFromDisk, formatReport } from "./doctor.js"; import { generateMemFS } from "../lib/memfs.js"; import { loadFacts } from "../lib/facts.js"; -import { applyPolicyProfile, listPolicyProfiles } from "../lib/policy-profiles.js"; +import { applyPolicyProfile } from "../lib/policy-profiles.js"; import { buildAuditBundle, exportAuditBundle } from "../lib/audit-bundle.js"; import { buildAuditDashboard, exportAuditDashboard } from "../lib/audit-dashboard.js"; import { extractAndSaveCredentials, mergeCredentials, sanitizeCredentialPatch } from "../lib/credentials.js"; @@ -26,6 +26,10 @@ import { exportReleaseReadiness, formatReleaseReadinessReport } from "../lib/rel import { resolveProjectRoot } from "../lib/project-root.js"; import { normalizeSessionTarget } from "../lib/helpers.js"; import { toolPaths } from "./_shared.js"; +import { countByStatus, summarizeDecoratedPatterns, countWaitingAgentTasks, validationNextAction, reviewPanelNextActions } from "./control-summaries.js"; +import { CONTROL_PARAM_PROPERTIES } from "./control-parameters.js"; +import { skillPolicyHandlers } from "./control-handlers/skill-policy.js"; +import { eventHandlers } from "./control-handlers/events.js"; const MAX_SKILL_HISTORY = 20; @@ -56,52 +60,6 @@ function readPluginVersion(pluginDir) { try { return JSON.parse(fs.readFileSync(path.join(pluginDir, "package.json"), "utf-8")).version; } catch { return "unknown"; } } -function countByStatus(rows = [], field = "status") { - const counts = {}; - for (const row of rows) { - const key = row?.[field] || "unknown"; - counts[key] = (counts[key] || 0) + 1; - } - return counts; -} - -function summarizeDecoratedPatterns(patterns = []) { - const summary = { total: 0, injectable: 0, pending: 0, approved: 0, rejected: 0 }; - for (const pattern of patterns) { - summary.total += 1; - if (pattern.injectable) summary.injectable += 1; - if (pattern.status === "pending") summary.pending += 1; - else if (pattern.status === "approved") summary.approved += 1; - else if (pattern.status === "rejected") summary.rejected += 1; - } - return summary; -} - -function countWaitingAgentTasks(tasks = []) { - let waiting = 0; - for (const task of tasks) { - if (task.state === "waiting_for_human") waiting += 1; - } - return waiting; -} - -function validationNextAction(validation) { - return validation?.ok - ? "approve_review then apply_review" - : "fix proposal or reject_proposal"; -} - -function reviewPanelNextActions(panel = {}) { - const actions = []; - const blocked = panel.counts?.blockedReviews || 0; - const pending = panel.counts?.pendingReviews || 0; - if (blocked > 0) actions.push("validate blocked reviews, then fix or reject them"); - if (pending > 0) actions.push("preview queued reviews, then approve_review or reject_review"); - if (panel.counts?.pendingProposals > 0) actions.push("validate_proposal for pending proposals not yet reviewed"); - if (!actions.length) actions.push("no review action needed"); - return actions; -} - const HANDLERS = { status(input, p, config, patterns) { const decorated = decoratePatterns(patterns, config); @@ -321,19 +279,9 @@ const HANDLERS = { return JSON.stringify({ ok: true, reviews, nextAction: "show_proposal then preview_proposal" }, null, 2); }, - list_events(input, p) { - return JSON.stringify({ ok: true, events: readEvents(p.learnerDir, { limit: input.limit || 50, entityId: input.id || null }) }, null, 2); - }, - - event_summary(input, p) { - const events = readEvents(p.learnerDir, { limit: input.limit || 5000, entityId: input.id || null }); - return JSON.stringify({ ok: true, summary: replayEventState(events) }, null, 2); - }, - - verify_event_log(input, p) { - const result = verifyEventLog(p.learnerDir); - return JSON.stringify({ ...result, nextAction: result.ok ? "export_audit_bundle or continue" : "inspect event_log.jsonl and restore from trusted backup" }, null, 2); - }, + // Event-log read-only handlers live in control-handlers/events.js + // (C-001 HANDLERS split — events domain): list_events, event_summary, verify_event_log. + ...eventHandlers, list_agent_tasks(input, p) { const tasks = listAgentTaskStates(p.learnerDir, { limit: input.limit || 50 }); @@ -440,26 +388,15 @@ const HANDLERS = { return JSON.stringify({ ok: result.ok, counts: result.counts, autoSkillFileWriteBlocked: result.autoSkillFileWriteBlocked, nextAction: "list_skill_candidates or export_audit_bundle" }, null, 2); }, - list_skill_candidates(input, p) { - const store = loadSkillCandidates(p.learnerDir); - const candidates = store.candidates.slice(0, input.limit || 50).map((c) => ({ id: c.id, status: c.status, rule: c.rule, evidence: c.evidence, scope: c.scope, updatedAt: c.updatedAt })); - return JSON.stringify({ ok: true, candidates, nextAction: "run_skill_promotion_loop or list_active_skills" }, null, 2); - }, - - list_active_skills(input, p) { - const registry = loadActiveSkills(p.learnerDir); - return JSON.stringify({ ok: true, skills: registry.skills.slice(0, input.limit || 50), nextAction: "export_audit_bundle" }, null, 2); - }, + // Skill-promotion & policy read-only handlers live in control-handlers/skill-policy.js + // (C-001 HANDLERS split pilot): list_skill_candidates, list_active_skills, list_policy_profiles. + ...skillPolicyHandlers, doctor(input, p) { const report = runDoctorFromDisk(p.learnerDir); return input.format === "json" ? JSON.stringify(report, null, 2) : formatReport(report); }, - list_policy_profiles(input, p, config) { - return JSON.stringify({ ok: true, profiles: listPolicyProfiles(), current: config.governanceProfile || "balanced" }, null, 2); - }, - set_policy_profile(input, p, config, patterns) { const profileName = input.governanceProfile || input.id || "balanced"; const result = applyPolicyProfile(config, profileName); @@ -642,55 +579,7 @@ export const parameters = { enum: Object.keys(HANDLERS), description: "Control action to run.", }, - id: { type: "string", description: "Pattern id for approve/reject." }, - proposalId: { type: "string", description: "Proposal id for show/apply/reject proposal actions." }, - taskId: { type: "string", description: "Agent task id for agent task show/approve/reject/resume actions." }, - candidateId: { type: "string", description: "Cross-project transfer candidate id for transfer registry actions." }, - benchmarkId: { type: "string", description: "Optional benchmark scenario id for run_benchmarks." }, - benchmarkOutputDir: { type: "string", description: "Optional output directory for benchmark reports." }, - benchmarkRunsDir: { type: "string", description: "Optional benchmark-runs directory for audit dashboard lookup." }, - benchmarkReportPath: { type: "string", description: "Optional explicit benchmark-report.json path for audit dashboard lookup." }, - releaseOutputDir: { type: "string", description: "Optional output directory for release readiness reports." }, - projectRoot: { type: "string", description: "Optional source checkout root for release/benchmark actions." }, - sourceRoot: { type: "string", description: "Alias of projectRoot for runtime package source checkout resolution." }, - candidate: { type: "object", description: "Cross-project transfer candidate object for register_transfer_candidate." }, - validationStatus: { type: "string", enum: ["passed", "failed"], description: "Target validation status for record_transfer_validation." }, - evidence: { type: "array", items: { type: "string" }, description: "Validation evidence lines for transfer registry actions." }, - requestId: { type: "string", description: "Approval request id for agent task approval actions." }, - reason: { type: "string", description: "Optional reason for proposal rejection." }, - status: { type: "string", description: "Optional proposal status filter: pending, applied, or rejected." }, - format: { type: "string", enum: ["text", "json"], description: "Output format for the doctor action. Default text." }, - governanceProfile: { type: "string", enum: ["conservative", "balanced", "autonomous"], description: "Governance policy profile to apply." }, - limit: { type: "number", description: "Maximum number of events/reviews to return for list actions." }, - autoInjectHighConfidence: { type: "boolean" }, - autoApproveHighConfidence: { type: "boolean" }, - minInjectScore: { type: "number" }, - minInjectCount: { type: "number" }, - decayHalfLifeDays: { type: "number" }, - includePendingPreferences: { type: "boolean" }, - learnFromUsage: { type: "boolean" }, - includeUsageInAdvisorPrompt: { type: "boolean" }, - officialMemoryBridgeEnabled: { type: "boolean" }, - officialMemoryBridgeMaxResults: { type: "number" }, - durableMemoryMaxCount: { type: "number" }, - largeUsageTokenThreshold: { type: "number" }, - officialUtilityModelDisplay: { type: "string" }, - modelAdvisorEnabled: { type: "boolean" }, - modelAdvisorSource: { type: "string", enum: ["official", "private", "off"] }, - modelAdvisorBaseUrl: { type: "string" }, - modelAdvisorApiKey: { type: "string" }, - modelAdvisorModel: { type: "string" }, - modelAdvisorMaxTokens: { type: "number" }, - modelAdvisorMinIntervalMinutes: { type: "number" }, - workStatusEnabled: { type: "boolean" }, - workStatusText: { type: "string" }, - proposalChatNotificationsEnabled: { type: "boolean" }, - requireReviewForAutoApply: { type: "boolean" }, - semanticSearchEnabled: { type: "boolean" }, - semanticEmbeddingBaseUrl: { type: "string" }, - semanticEmbeddingApiKey: { type: "string" }, - semanticEmbeddingModel: { type: "string" }, - semanticCacheMaxEntries: { type: "number" }, + ...CONTROL_PARAM_PROPERTIES, }, required: ["action"], }; From 6c3c8a3f9f3674ca27bcd45e15d39822903ca85d Mon Sep 17 00:00:00 2001 From: Hanako Maintainer Date: Wed, 24 Jun 2026 19:23:59 +0800 Subject: [PATCH 2/4] v4.3.23: complexity governance release --- ARCHITECTURE.md | 2 +- CHANGELOG.md | 8 + INSTALL.md | 4 +- README.md | 10 +- benchmark-results/benchmark-report.json | 686 +++++++++++++----------- benchmark-results/benchmark-report.md | 4 +- docs/ACCEPTANCE-v4.3.23.md | 38 ++ docs/COMPLEXITY_REPORT.md | 2 +- docs/DESIGN_GOAL_COMPLETION_MATRIX.md | 4 +- manifest.json | 2 +- package-lock.json | 4 +- package.json | 2 +- 12 files changed, 435 insertions(+), 331 deletions(-) create mode 100644 docs/ACCEPTANCE-v4.3.23.md diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index b1e3a40..0856b85 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,6 +1,6 @@ # 架构说明 -Runtime Self-Learning `v4.3.22` 当前由 `1` 个入口点、`88` 个 `lib` 模块和一组工具面组成。整体目标不是“尽量自动化”,而是“在不放宽边界的前提下,把本地经验整理成后续可复用的受限提示和低风险动作”。 +Runtime Self-Learning `v4.3.23` 当前由 `1` 个入口点、`88` 个 `lib` 模块和一组工具面组成。整体目标不是“尽量自动化”,而是“在不放宽边界的前提下,把本地经验整理成后续可复用的受限提示和低风险动作”。 --- diff --git a/CHANGELOG.md b/CHANGELOG.md index 7360808..eca3242 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ 本文档记录 Runtime Self-Learning 的版本演进。`v4.x` 为 LTS 维护线,因此该阶段的记录重点放在缺陷修复、审计加固、性能整理和发布治理,不再扩张自动化边界。 +## 4.3.23 + +- 新增复杂度治理门:`lib/complexity.js` 定义 hard limit / soft target,`npm run complexity:check` 在超出 hard limit 时失败,`npm run complexity:report` 生成 `docs/COMPLEXITY_REPORT.md`。 +- 发布门纳入 `complexity.within_budget` 检查,并补充 `docs/COMPLEXITY_BUDGET.md` / `docs/COMPLEXITY_DEBT.md`,把 v4.x LTS 的模块数、单文件 LOC、import/export 数和 TODO/FIXME 预算写成可审计规则。 +- 拆分控制面热点复杂度:新增 `tools/control-parameters.js`、`tools/control-summaries.js` 和 `tools/control-handlers/*`,让 `tools/control.js` 从 712 LOC 收敛到 602 LOC,并保留行为特征回归。 +- 测试总数 `606 -> 665`:新增控制面参数、摘要、脱敏、handler characterization 与 release-readiness 复杂度门回归;README 徽章和发布门默认测试基线同步到 665。 +- 边界未放宽:本版只增加本地静态治理、文档和控制面结构整理,无新增自动放行、网络、发布或外部副作用能力。 + ## 4.3.22 - **新增自学习控制台(`chat.surface`,Hanako v0.344+)**:新增只读工具 `self_learning_console`,把"最近活动 + 待处理提案"快照投递进一条插件自有的 `plugin_private` 会话,并以原生 `chat.surface` transcript 卡片在当前聊天内嵌展示,可点开滚动查看历史快照。这是 UX/呈现层的可选增强,**不扩张自动化边界**:控台只读、由用户显式调用工具触发,不自动应用任何动作、不在后台主动推送。 diff --git a/INSTALL.md b/INSTALL.md index 7747566..5b29f8d 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -50,10 +50,10 @@ OK skills/self-learning/SKILL.md ## 固定版本安装 -如需固定到某个 release,例如 `v4.3.22`: +如需固定到某个 release,例如 `v4.3.23`: ```powershell -git clone --branch v4.3.22 https://github.com/326sun/Hanako-runtime-learner.git +git clone --branch v4.3.23 https://github.com/326sun/Hanako-runtime-learner.git cd Hanako-runtime-learner npm run install-plugin ``` diff --git a/README.md b/README.md index 720351a..cba91dd 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

- version + version CI license platform @@ -60,7 +60,7 @@ npm run install-plugin 固定版本安装: ```powershell -git clone --branch v4.3.22 https://github.com/326sun/Hanako-runtime-learner.git +git clone --branch v4.3.23 https://github.com/326sun/Hanako-runtime-learner.git cd Hanako-runtime-learner npm run install-plugin ``` @@ -210,10 +210,10 @@ npm run perf -- --json npm run release:check ``` -`v4.3.22` 的预期结果: +`v4.3.23` 的预期结果: ```text -package version: 4.3.22 +package version: 4.3.23 npm run check: passed npm test: 665 tests, 660 passed, 5 skipped npm run benchmark: passed, 17 scenarios @@ -240,7 +240,7 @@ npm run release:check: Score 100 | [docs/MIGRATION_v3_to_v4.md](docs/MIGRATION_v3_to_v4.md) | v3 到 v4 迁移说明。 | | [docs/LTS_MAINTENANCE_PLAN.md](docs/LTS_MAINTENANCE_PLAN.md) | v4.x LTS 维护策略。 | | [docs/DESIGN_GOAL_COMPLETION_MATRIX.md](docs/DESIGN_GOAL_COMPLETION_MATRIX.md) | 设计目标完成矩阵。 | -| [docs/ACCEPTANCE-v4.3.22.md](docs/ACCEPTANCE-v4.3.22.md) | 当前版本验收记录。 | +| [docs/ACCEPTANCE-v4.3.23.md](docs/ACCEPTANCE-v4.3.23.md) | 当前版本验收记录。 | | [CHANGELOG.md](CHANGELOG.md) | 版本历史。 | ## 许可证 diff --git a/benchmark-results/benchmark-report.json b/benchmark-results/benchmark-report.json index 0181acc..cc2259c 100644 --- a/benchmark-results/benchmark-report.json +++ b/benchmark-results/benchmark-report.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "generatedAt": "2026-06-15T04:52:50.839Z", + "generatedAt": "2026-06-24T11:23:09.438Z", "runs": [ { "scenarioId": "audit.dashboard_surface", @@ -15,14 +15,14 @@ "rollbackOk": false, "repairAttempted": false, "repairOk": false, - "durationMs": 6, + "durationMs": 7, "stepResults": [ { "ok": true, "status": "generated", - "dir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-audit.dashboard_surface-1FS50v\\.hanako\\audit-dashboard\\benchmark", - "jsonPath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-audit.dashboard_surface-1FS50v\\.hanako\\audit-dashboard\\benchmark\\dashboard.json", - "mdPath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-audit.dashboard_surface-1FS50v\\.hanako\\audit-dashboard\\benchmark\\dashboard.md", + "dir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-audit.dashboard_surface-gOxHEA\\.hanako\\audit-dashboard\\benchmark", + "jsonPath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-audit.dashboard_surface-gOxHEA\\.hanako\\audit-dashboard\\benchmark\\dashboard.json", + "mdPath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-audit.dashboard_surface-gOxHEA\\.hanako\\audit-dashboard\\benchmark\\dashboard.md", "summary": { "benchmarkAvailable": true, "benchmarkOk": true, @@ -86,14 +86,14 @@ "rollbackOk": true, "repairAttempted": true, "repairOk": true, - "durationMs": 117, + "durationMs": 92, "stepResults": [ { "ok": true, "state": { "schemaVersion": 1, "taskId": "task:e006b07b0464", - "runId": "agent_run:13f9cbfff254", + "runId": "agent_run:d31f86c0b5e7", "state": "completed", "currentNode": "finalize", "graph": { @@ -118,7 +118,7 @@ "files": [] } }, - "updatedAt": "2026-06-15T04:52:49.757Z" + "updatedAt": "2026-06-24T11:23:08.702Z" }, { "id": "plan", @@ -156,7 +156,7 @@ } ] }, - "updatedAt": "2026-06-15T04:52:49.757Z" + "updatedAt": "2026-06-24T11:23:08.703Z" }, { "id": "policy", @@ -184,12 +184,12 @@ { "name": "risk_tier", "status": "pass", - "message": "R2" + "message": "base=R2; declared=R2; effective=R2" } ] } }, - "updatedAt": "2026-06-15T04:52:49.758Z" + "updatedAt": "2026-06-24T11:23:08.704Z" }, { "id": "scope", @@ -224,7 +224,7 @@ "requiresTestsUpdate": false } }, - "updatedAt": "2026-06-15T04:52:49.759Z" + "updatedAt": "2026-06-24T11:23:08.704Z" }, { "id": "execute", @@ -242,7 +242,8 @@ "logs": [], "artifacts": [], "errors": [], - "transactionId": "txn:1781499169760:65edf3f9", + "warnings": [], + "transactionId": "txn:1782300188705:65edf3f9", "changedFiles": [], "applied": { "writeResults": [ @@ -257,13 +258,13 @@ "command": "node --check src/repair-target.mjs", "status": "failed", "stdout": "", - "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.repair_branch-EJdIcg\\src\\repair-target.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", + "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.repair_branch-KVoI2g\\src\\repair-target.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", "exitCode": 1, "error": "command exited with code 1" } ], "error": "verification command failed", - "durationMs": 54, + "durationMs": 45, "verification": { "verified": false, "checks": [ @@ -290,7 +291,7 @@ "changedFiles": [] } }, - "updatedAt": "2026-06-15T04:52:49.815Z" + "updatedAt": "2026-06-24T11:23:08.752Z" }, { "id": "verify_initial", @@ -328,7 +329,7 @@ "error": "verification failed" }, "recoveryTarget": "repair", - "updatedAt": "2026-06-15T04:52:49.815Z" + "updatedAt": "2026-06-24T11:23:08.752Z" }, { "id": "repair", @@ -353,7 +354,8 @@ "logs": [], "artifacts": [], "errors": [], - "transactionId": "txn:1781499169815:3416c35d", + "warnings": [], + "transactionId": "txn:1782300188753:3416c35d", "changedFiles": [ "src\\repair-target.mjs" ], @@ -375,7 +377,7 @@ "error": null } ], - "durationMs": 55, + "durationMs": 40, "verification": { "verified": true, "checks": [ @@ -420,7 +422,7 @@ "confidence": 0.86 } }, - "updatedAt": "2026-06-15T04:52:49.870Z" + "updatedAt": "2026-06-24T11:23:08.792Z" }, { "id": "verify_after_repair", @@ -456,7 +458,7 @@ }, "sourceNode": "repair" }, - "updatedAt": "2026-06-15T04:52:49.871Z" + "updatedAt": "2026-06-24T11:23:08.792Z" }, { "id": "feedback", @@ -471,7 +473,7 @@ "status": "succeeded", "note": "FeedbackNode recorded" }, - "updatedAt": "2026-06-15T04:52:49.871Z" + "updatedAt": "2026-06-24T11:23:08.792Z" }, { "id": "finalize", @@ -486,91 +488,91 @@ "status": "succeeded", "note": "finalized" }, - "updatedAt": "2026-06-15T04:52:49.871Z" + "updatedAt": "2026-06-24T11:23:08.792Z" } ], - "createdAt": "2026-06-15T04:52:49.756Z" + "createdAt": "2026-06-24T11:23:08.702Z" }, "history": [ { - "at": "2026-06-15T04:52:49.756Z", + "at": "2026-06-24T11:23:08.702Z", "from": null, "to": "created", "node": null, "reason": "created" }, { - "at": "2026-06-15T04:52:49.757Z", + "at": "2026-06-24T11:23:08.702Z", "from": "created", "to": "observing", "node": "observe", "reason": "enter ObserveNode" }, { - "at": "2026-06-15T04:52:49.757Z", + "at": "2026-06-24T11:23:08.703Z", "from": "observing", "to": "planning", "node": "observe", "reason": "advance to PlanNode" }, { - "at": "2026-06-15T04:52:49.757Z", + "at": "2026-06-24T11:23:08.703Z", "from": "planning", "to": "policy_checking", "node": "plan", "reason": "advance to PolicyNode" }, { - "at": "2026-06-15T04:52:49.758Z", + "at": "2026-06-24T11:23:08.704Z", "from": "policy_checking", "to": "scope_checking", "node": "policy", "reason": "advance to ScopeNode" }, { - "at": "2026-06-15T04:52:49.759Z", + "at": "2026-06-24T11:23:08.704Z", "from": "scope_checking", "to": "executing", "node": "scope", "reason": "advance to ExecuteNode" }, { - "at": "2026-06-15T04:52:49.815Z", + "at": "2026-06-24T11:23:08.752Z", "from": "executing", "to": "verifying", "node": "execute", "reason": "advance to VerifyNode" }, { - "at": "2026-06-15T04:52:49.815Z", + "at": "2026-06-24T11:23:08.752Z", "from": "verifying", "to": "repairing", "node": "verify_initial", "reason": "VerifyNode routed to RepairNode" }, { - "at": "2026-06-15T04:52:49.870Z", + "at": "2026-06-24T11:23:08.792Z", "from": "repairing", "to": "verifying", "node": "repair", "reason": "advance to VerifyNode" }, { - "at": "2026-06-15T04:52:49.871Z", + "at": "2026-06-24T11:23:08.792Z", "from": "verifying", "to": "learning", "node": "verify_after_repair", "reason": "advance to FeedbackNode" }, { - "at": "2026-06-15T04:52:49.871Z", + "at": "2026-06-24T11:23:08.792Z", "from": "learning", "to": "learning", "node": "feedback", "reason": "advance to FinalizeNode" }, { - "at": "2026-06-15T04:52:49.871Z", + "at": "2026-06-24T11:23:08.792Z", "from": "learning", "to": "completed", "node": "finalize", @@ -579,7 +581,7 @@ ], "artifacts": [ { - "createdAt": "2026-06-15T04:52:49.757Z", + "createdAt": "2026-06-24T11:23:08.702Z", "kind": "node_result", "node": "observe", "nodeType": "ObserveNode", @@ -593,7 +595,7 @@ } }, { - "createdAt": "2026-06-15T04:52:49.757Z", + "createdAt": "2026-06-24T11:23:08.703Z", "kind": "node_result", "node": "plan", "nodeType": "PlanNode", @@ -626,7 +628,7 @@ } }, { - "createdAt": "2026-06-15T04:52:49.758Z", + "createdAt": "2026-06-24T11:23:08.704Z", "kind": "node_result", "node": "policy", "nodeType": "PolicyNode", @@ -647,14 +649,14 @@ { "name": "risk_tier", "status": "pass", - "message": "R2" + "message": "base=R2; declared=R2; effective=R2" } ] } } }, { - "createdAt": "2026-06-15T04:52:49.759Z", + "createdAt": "2026-06-24T11:23:08.704Z", "kind": "node_result", "node": "scope", "nodeType": "ScopeNode", @@ -684,7 +686,7 @@ } }, { - "createdAt": "2026-06-15T04:52:49.815Z", + "createdAt": "2026-06-24T11:23:08.752Z", "kind": "node_result", "node": "execute", "nodeType": "ExecuteNode", @@ -695,7 +697,8 @@ "logs": [], "artifacts": [], "errors": [], - "transactionId": "txn:1781499169760:65edf3f9", + "warnings": [], + "transactionId": "txn:1782300188705:65edf3f9", "changedFiles": [], "applied": { "writeResults": [ @@ -710,13 +713,13 @@ "command": "node --check src/repair-target.mjs", "status": "failed", "stdout": "", - "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.repair_branch-EJdIcg\\src\\repair-target.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", + "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.repair_branch-KVoI2g\\src\\repair-target.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", "exitCode": 1, "error": "command exited with code 1" } ], "error": "verification command failed", - "durationMs": 54, + "durationMs": 45, "verification": { "verified": false, "checks": [ @@ -745,7 +748,7 @@ } }, { - "createdAt": "2026-06-15T04:52:49.815Z", + "createdAt": "2026-06-24T11:23:08.752Z", "kind": "node_result", "node": "verify_initial", "nodeType": "VerifyNode", @@ -777,7 +780,7 @@ } }, { - "createdAt": "2026-06-15T04:52:49.870Z", + "createdAt": "2026-06-24T11:23:08.792Z", "kind": "node_result", "node": "repair", "nodeType": "RepairNode", @@ -795,7 +798,8 @@ "logs": [], "artifacts": [], "errors": [], - "transactionId": "txn:1781499169815:3416c35d", + "warnings": [], + "transactionId": "txn:1782300188753:3416c35d", "changedFiles": [ "src\\repair-target.mjs" ], @@ -817,7 +821,7 @@ "error": null } ], - "durationMs": 55, + "durationMs": 40, "verification": { "verified": true, "checks": [ @@ -864,7 +868,7 @@ } }, { - "createdAt": "2026-06-15T04:52:49.871Z", + "createdAt": "2026-06-24T11:23:08.792Z", "kind": "node_result", "node": "verify_after_repair", "nodeType": "VerifyNode", @@ -895,7 +899,7 @@ } }, { - "createdAt": "2026-06-15T04:52:49.871Z", + "createdAt": "2026-06-24T11:23:08.792Z", "kind": "node_result", "node": "feedback", "nodeType": "FeedbackNode", @@ -905,7 +909,7 @@ } }, { - "createdAt": "2026-06-15T04:52:49.871Z", + "createdAt": "2026-06-24T11:23:08.792Z", "kind": "node_result", "node": "finalize", "nodeType": "FinalizeNode", @@ -920,17 +924,17 @@ "risk": { "riskTier": "R2" }, - "createdAt": "2026-06-15T04:52:49.756Z", - "updatedAt": "2026-06-15T04:52:49.871Z" + "createdAt": "2026-06-24T11:23:08.702Z", + "updatedAt": "2026-06-24T11:23:08.792Z" }, "trace": { "schemaVersion": 1, - "traceId": "audit:task:e006b07b0464:1781499169756", + "traceId": "audit:task:e006b07b0464:1782300188702", "taskId": "task:e006b07b0464", - "runId": "agent_run:13f9cbfff254", + "runId": "agent_run:d31f86c0b5e7", "events": [ { - "at": "2026-06-15T04:52:49.756Z", + "at": "2026-06-24T11:23:08.702Z", "type": "state.created", "node": null, "state": "created", @@ -938,7 +942,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:49.757Z", + "at": "2026-06-24T11:23:08.702Z", "type": "node.start", "node": "observe", "state": "observing", @@ -946,7 +950,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:49.757Z", + "at": "2026-06-24T11:23:08.702Z", "type": "node.completed", "node": "observe", "state": "observing", @@ -956,7 +960,7 @@ } }, { - "at": "2026-06-15T04:52:49.757Z", + "at": "2026-06-24T11:23:08.703Z", "type": "node.start", "node": "plan", "state": "planning", @@ -964,7 +968,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:49.757Z", + "at": "2026-06-24T11:23:08.703Z", "type": "node.completed", "node": "plan", "state": "planning", @@ -974,7 +978,7 @@ } }, { - "at": "2026-06-15T04:52:49.757Z", + "at": "2026-06-24T11:23:08.703Z", "type": "node.start", "node": "policy", "state": "policy_checking", @@ -982,7 +986,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:49.758Z", + "at": "2026-06-24T11:23:08.704Z", "type": "node.completed", "node": "policy", "state": "policy_checking", @@ -992,7 +996,7 @@ } }, { - "at": "2026-06-15T04:52:49.758Z", + "at": "2026-06-24T11:23:08.704Z", "type": "node.start", "node": "scope", "state": "scope_checking", @@ -1000,7 +1004,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:49.759Z", + "at": "2026-06-24T11:23:08.704Z", "type": "node.completed", "node": "scope", "state": "scope_checking", @@ -1010,7 +1014,7 @@ } }, { - "at": "2026-06-15T04:52:49.759Z", + "at": "2026-06-24T11:23:08.704Z", "type": "node.start", "node": "execute", "state": "executing", @@ -1018,7 +1022,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:49.815Z", + "at": "2026-06-24T11:23:08.752Z", "type": "node.completed", "node": "execute", "state": "executing", @@ -1028,7 +1032,7 @@ } }, { - "at": "2026-06-15T04:52:49.815Z", + "at": "2026-06-24T11:23:08.752Z", "type": "node.start", "node": "verify_initial", "state": "verifying", @@ -1036,7 +1040,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:49.815Z", + "at": "2026-06-24T11:23:08.752Z", "type": "node.recovery_branch", "node": "verify_initial", "state": "repairing", @@ -1068,7 +1072,7 @@ } }, { - "at": "2026-06-15T04:52:49.815Z", + "at": "2026-06-24T11:23:08.752Z", "type": "node.start", "node": "repair", "state": "repairing", @@ -1076,7 +1080,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:49.870Z", + "at": "2026-06-24T11:23:08.792Z", "type": "node.completed", "node": "repair", "state": "repairing", @@ -1086,7 +1090,7 @@ } }, { - "at": "2026-06-15T04:52:49.871Z", + "at": "2026-06-24T11:23:08.792Z", "type": "node.start", "node": "verify_after_repair", "state": "verifying", @@ -1094,7 +1098,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:49.871Z", + "at": "2026-06-24T11:23:08.792Z", "type": "node.completed", "node": "verify_after_repair", "state": "verifying", @@ -1104,7 +1108,7 @@ } }, { - "at": "2026-06-15T04:52:49.871Z", + "at": "2026-06-24T11:23:08.792Z", "type": "node.start", "node": "feedback", "state": "learning", @@ -1112,7 +1116,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:49.871Z", + "at": "2026-06-24T11:23:08.792Z", "type": "node.completed", "node": "feedback", "state": "learning", @@ -1122,7 +1126,7 @@ } }, { - "at": "2026-06-15T04:52:49.871Z", + "at": "2026-06-24T11:23:08.792Z", "type": "node.start", "node": "finalize", "state": "learning", @@ -1130,7 +1134,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:49.871Z", + "at": "2026-06-24T11:23:08.792Z", "type": "node.completed", "node": "finalize", "state": "learning", @@ -1140,7 +1144,7 @@ } }, { - "at": "2026-06-15T04:52:49.871Z", + "at": "2026-06-24T11:23:08.793Z", "type": "state.completed", "node": "finalize", "state": "completed", @@ -1148,8 +1152,8 @@ "data": {} } ], - "createdAt": "2026-06-15T04:52:49.756Z", - "updatedAt": "2026-06-15T04:52:49.871Z" + "createdAt": "2026-06-24T11:23:08.702Z", + "updatedAt": "2026-06-24T11:23:08.793Z" }, "status": "completed", "type": "run_agent_controller", @@ -1181,14 +1185,14 @@ "rollbackOk": true, "repairAttempted": false, "repairOk": false, - "durationMs": 130, + "durationMs": 44, "stepResults": [ { "ok": true, "state": { "schemaVersion": 1, "taskId": "task:b932bd6c5854", - "runId": "agent_run:6717a622eb02", + "runId": "agent_run:369051c672ab", "state": "completed", "currentNode": "finalize", "graph": { @@ -1213,7 +1217,7 @@ "files": [] } }, - "updatedAt": "2026-06-15T04:52:49.873Z" + "updatedAt": "2026-06-24T11:23:08.794Z" }, { "id": "plan", @@ -1252,7 +1256,7 @@ } ] }, - "updatedAt": "2026-06-15T04:52:49.873Z" + "updatedAt": "2026-06-24T11:23:08.794Z" }, { "id": "policy", @@ -1280,12 +1284,12 @@ { "name": "risk_tier", "status": "pass", - "message": "R2" + "message": "base=R2; declared=R2; effective=R2" } ] } }, - "updatedAt": "2026-06-15T04:52:49.873Z" + "updatedAt": "2026-06-24T11:23:08.794Z" }, { "id": "scope", @@ -1320,7 +1324,7 @@ "requiresTestsUpdate": false } }, - "updatedAt": "2026-06-15T04:52:49.873Z" + "updatedAt": "2026-06-24T11:23:08.794Z" }, { "id": "execute", @@ -1338,7 +1342,8 @@ "logs": [], "artifacts": [], "errors": [], - "transactionId": "txn:1781499169874:92b7d810", + "warnings": [], + "transactionId": "txn:1782300188795:92b7d810", "changedFiles": [], "applied": { "writeResults": [ @@ -1353,13 +1358,13 @@ "command": "node --check src/rollback-target.mjs", "status": "failed", "stdout": "", - "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.rollback_branch-GMapTz\\src\\rollback-target.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", + "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.rollback_branch-8A2sXa\\src\\rollback-target.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", "exitCode": 1, "error": "command exited with code 1" } ], "error": "verification command failed", - "durationMs": 125, + "durationMs": 40, "verification": { "verified": false, "checks": [ @@ -1391,7 +1396,7 @@ "changedFiles": [] } }, - "updatedAt": "2026-06-15T04:52:50.000Z" + "updatedAt": "2026-06-24T11:23:08.836Z" }, { "id": "verify", @@ -1434,7 +1439,7 @@ "error": "verification failed" }, "recoveryTarget": "rollback", - "updatedAt": "2026-06-15T04:52:50.000Z" + "updatedAt": "2026-06-24T11:23:08.836Z" }, { "id": "rollback", @@ -1479,7 +1484,7 @@ }, "sourceNode": "execute" }, - "updatedAt": "2026-06-15T04:52:50.000Z" + "updatedAt": "2026-06-24T11:23:08.836Z" }, { "id": "feedback", @@ -1494,7 +1499,7 @@ "status": "succeeded", "note": "FeedbackNode recorded" }, - "updatedAt": "2026-06-15T04:52:50.000Z" + "updatedAt": "2026-06-24T11:23:08.836Z" }, { "id": "finalize", @@ -1509,84 +1514,84 @@ "status": "succeeded", "note": "finalized" }, - "updatedAt": "2026-06-15T04:52:50.001Z" + "updatedAt": "2026-06-24T11:23:08.837Z" } ], - "createdAt": "2026-06-15T04:52:49.873Z" + "createdAt": "2026-06-24T11:23:08.794Z" }, "history": [ { - "at": "2026-06-15T04:52:49.873Z", + "at": "2026-06-24T11:23:08.794Z", "from": null, "to": "created", "node": null, "reason": "created" }, { - "at": "2026-06-15T04:52:49.873Z", + "at": "2026-06-24T11:23:08.794Z", "from": "created", "to": "observing", "node": "observe", "reason": "enter ObserveNode" }, { - "at": "2026-06-15T04:52:49.873Z", + "at": "2026-06-24T11:23:08.794Z", "from": "observing", "to": "planning", "node": "observe", "reason": "advance to PlanNode" }, { - "at": "2026-06-15T04:52:49.873Z", + "at": "2026-06-24T11:23:08.794Z", "from": "planning", "to": "policy_checking", "node": "plan", "reason": "advance to PolicyNode" }, { - "at": "2026-06-15T04:52:49.873Z", + "at": "2026-06-24T11:23:08.794Z", "from": "policy_checking", "to": "scope_checking", "node": "policy", "reason": "advance to ScopeNode" }, { - "at": "2026-06-15T04:52:49.873Z", + "at": "2026-06-24T11:23:08.794Z", "from": "scope_checking", "to": "executing", "node": "scope", "reason": "advance to ExecuteNode" }, { - "at": "2026-06-15T04:52:50.000Z", + "at": "2026-06-24T11:23:08.836Z", "from": "executing", "to": "verifying", "node": "execute", "reason": "advance to VerifyNode" }, { - "at": "2026-06-15T04:52:50.000Z", + "at": "2026-06-24T11:23:08.836Z", "from": "verifying", "to": "rolling_back", "node": "verify", "reason": "VerifyNode routed to RollbackNode" }, { - "at": "2026-06-15T04:52:50.000Z", + "at": "2026-06-24T11:23:08.836Z", "from": "rolling_back", "to": "learning", "node": "rollback", "reason": "advance to FeedbackNode" }, { - "at": "2026-06-15T04:52:50.000Z", + "at": "2026-06-24T11:23:08.836Z", "from": "learning", "to": "learning", "node": "feedback", "reason": "advance to FinalizeNode" }, { - "at": "2026-06-15T04:52:50.001Z", + "at": "2026-06-24T11:23:08.837Z", "from": "learning", "to": "completed", "node": "finalize", @@ -1595,7 +1600,7 @@ ], "artifacts": [ { - "createdAt": "2026-06-15T04:52:49.873Z", + "createdAt": "2026-06-24T11:23:08.794Z", "kind": "node_result", "node": "observe", "nodeType": "ObserveNode", @@ -1609,7 +1614,7 @@ } }, { - "createdAt": "2026-06-15T04:52:49.873Z", + "createdAt": "2026-06-24T11:23:08.794Z", "kind": "node_result", "node": "plan", "nodeType": "PlanNode", @@ -1643,7 +1648,7 @@ } }, { - "createdAt": "2026-06-15T04:52:49.873Z", + "createdAt": "2026-06-24T11:23:08.794Z", "kind": "node_result", "node": "policy", "nodeType": "PolicyNode", @@ -1664,14 +1669,14 @@ { "name": "risk_tier", "status": "pass", - "message": "R2" + "message": "base=R2; declared=R2; effective=R2" } ] } } }, { - "createdAt": "2026-06-15T04:52:49.873Z", + "createdAt": "2026-06-24T11:23:08.794Z", "kind": "node_result", "node": "scope", "nodeType": "ScopeNode", @@ -1701,7 +1706,7 @@ } }, { - "createdAt": "2026-06-15T04:52:50.000Z", + "createdAt": "2026-06-24T11:23:08.836Z", "kind": "node_result", "node": "execute", "nodeType": "ExecuteNode", @@ -1712,7 +1717,8 @@ "logs": [], "artifacts": [], "errors": [], - "transactionId": "txn:1781499169874:92b7d810", + "warnings": [], + "transactionId": "txn:1782300188795:92b7d810", "changedFiles": [], "applied": { "writeResults": [ @@ -1727,13 +1733,13 @@ "command": "node --check src/rollback-target.mjs", "status": "failed", "stdout": "", - "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.rollback_branch-GMapTz\\src\\rollback-target.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", + "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.rollback_branch-8A2sXa\\src\\rollback-target.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", "exitCode": 1, "error": "command exited with code 1" } ], "error": "verification command failed", - "durationMs": 125, + "durationMs": 40, "verification": { "verified": false, "checks": [ @@ -1767,7 +1773,7 @@ } }, { - "createdAt": "2026-06-15T04:52:50.000Z", + "createdAt": "2026-06-24T11:23:08.836Z", "kind": "node_result", "node": "verify", "nodeType": "VerifyNode", @@ -1804,7 +1810,7 @@ } }, { - "createdAt": "2026-06-15T04:52:50.000Z", + "createdAt": "2026-06-24T11:23:08.836Z", "kind": "node_result", "node": "rollback", "nodeType": "RollbackNode", @@ -1844,7 +1850,7 @@ } }, { - "createdAt": "2026-06-15T04:52:50.000Z", + "createdAt": "2026-06-24T11:23:08.836Z", "kind": "node_result", "node": "feedback", "nodeType": "FeedbackNode", @@ -1854,7 +1860,7 @@ } }, { - "createdAt": "2026-06-15T04:52:50.001Z", + "createdAt": "2026-06-24T11:23:08.837Z", "kind": "node_result", "node": "finalize", "nodeType": "FinalizeNode", @@ -1869,17 +1875,17 @@ "risk": { "riskTier": "R2" }, - "createdAt": "2026-06-15T04:52:49.873Z", - "updatedAt": "2026-06-15T04:52:50.001Z" + "createdAt": "2026-06-24T11:23:08.794Z", + "updatedAt": "2026-06-24T11:23:08.837Z" }, "trace": { "schemaVersion": 1, - "traceId": "audit:task:b932bd6c5854:1781499169873", + "traceId": "audit:task:b932bd6c5854:1782300188794", "taskId": "task:b932bd6c5854", - "runId": "agent_run:6717a622eb02", + "runId": "agent_run:369051c672ab", "events": [ { - "at": "2026-06-15T04:52:49.873Z", + "at": "2026-06-24T11:23:08.794Z", "type": "state.created", "node": null, "state": "created", @@ -1887,7 +1893,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:49.873Z", + "at": "2026-06-24T11:23:08.794Z", "type": "node.start", "node": "observe", "state": "observing", @@ -1895,7 +1901,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:49.873Z", + "at": "2026-06-24T11:23:08.794Z", "type": "node.completed", "node": "observe", "state": "observing", @@ -1905,7 +1911,7 @@ } }, { - "at": "2026-06-15T04:52:49.873Z", + "at": "2026-06-24T11:23:08.794Z", "type": "node.start", "node": "plan", "state": "planning", @@ -1913,7 +1919,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:49.873Z", + "at": "2026-06-24T11:23:08.794Z", "type": "node.completed", "node": "plan", "state": "planning", @@ -1923,7 +1929,7 @@ } }, { - "at": "2026-06-15T04:52:49.873Z", + "at": "2026-06-24T11:23:08.794Z", "type": "node.start", "node": "policy", "state": "policy_checking", @@ -1931,7 +1937,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:49.873Z", + "at": "2026-06-24T11:23:08.794Z", "type": "node.completed", "node": "policy", "state": "policy_checking", @@ -1941,7 +1947,7 @@ } }, { - "at": "2026-06-15T04:52:49.873Z", + "at": "2026-06-24T11:23:08.794Z", "type": "node.start", "node": "scope", "state": "scope_checking", @@ -1949,7 +1955,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:49.873Z", + "at": "2026-06-24T11:23:08.794Z", "type": "node.completed", "node": "scope", "state": "scope_checking", @@ -1959,7 +1965,7 @@ } }, { - "at": "2026-06-15T04:52:49.873Z", + "at": "2026-06-24T11:23:08.794Z", "type": "node.start", "node": "execute", "state": "executing", @@ -1967,7 +1973,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:50.000Z", + "at": "2026-06-24T11:23:08.836Z", "type": "node.completed", "node": "execute", "state": "executing", @@ -1977,7 +1983,7 @@ } }, { - "at": "2026-06-15T04:52:50.000Z", + "at": "2026-06-24T11:23:08.836Z", "type": "node.start", "node": "verify", "state": "verifying", @@ -1985,7 +1991,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:50.000Z", + "at": "2026-06-24T11:23:08.836Z", "type": "node.recovery_branch", "node": "verify", "state": "rolling_back", @@ -2022,7 +2028,7 @@ } }, { - "at": "2026-06-15T04:52:50.000Z", + "at": "2026-06-24T11:23:08.836Z", "type": "node.start", "node": "rollback", "state": "rolling_back", @@ -2030,7 +2036,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:50.000Z", + "at": "2026-06-24T11:23:08.836Z", "type": "node.completed", "node": "rollback", "state": "rolling_back", @@ -2040,7 +2046,7 @@ } }, { - "at": "2026-06-15T04:52:50.000Z", + "at": "2026-06-24T11:23:08.836Z", "type": "node.start", "node": "feedback", "state": "learning", @@ -2048,7 +2054,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:50.000Z", + "at": "2026-06-24T11:23:08.836Z", "type": "node.completed", "node": "feedback", "state": "learning", @@ -2058,7 +2064,7 @@ } }, { - "at": "2026-06-15T04:52:50.001Z", + "at": "2026-06-24T11:23:08.837Z", "type": "node.start", "node": "finalize", "state": "learning", @@ -2066,7 +2072,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:50.001Z", + "at": "2026-06-24T11:23:08.837Z", "type": "node.completed", "node": "finalize", "state": "learning", @@ -2076,7 +2082,7 @@ } }, { - "at": "2026-06-15T04:52:50.001Z", + "at": "2026-06-24T11:23:08.837Z", "type": "state.completed", "node": "finalize", "state": "completed", @@ -2084,8 +2090,8 @@ "data": {} } ], - "createdAt": "2026-06-15T04:52:49.873Z", - "updatedAt": "2026-06-15T04:52:50.001Z" + "createdAt": "2026-06-24T11:23:08.794Z", + "updatedAt": "2026-06-24T11:23:08.837Z" }, "status": "completed", "type": "run_agent_controller", @@ -2117,14 +2123,14 @@ "rollbackOk": true, "repairAttempted": false, "repairOk": false, - "durationMs": 163, + "durationMs": 44, "stepResults": [ { "ok": false, "state": { "schemaVersion": 1, "taskId": "task:35fc503854cd", - "runId": "agent_run:4f72b86cbac8", + "runId": "agent_run:7ac251f04f53", "state": "waiting_for_human", "currentNode": "VerifyNode", "graph": { @@ -2149,7 +2155,7 @@ "files": [] } }, - "updatedAt": "2026-06-15T04:52:50.003Z" + "updatedAt": "2026-06-24T11:23:08.838Z" }, { "id": "PlanNode", @@ -2187,7 +2193,7 @@ } ] }, - "updatedAt": "2026-06-15T04:52:50.003Z" + "updatedAt": "2026-06-24T11:23:08.838Z" }, { "id": "PolicyNode", @@ -2215,12 +2221,12 @@ { "name": "risk_tier", "status": "pass", - "message": "R2" + "message": "base=R2; declared=R2; effective=R2" } ] } }, - "updatedAt": "2026-06-15T04:52:50.003Z" + "updatedAt": "2026-06-24T11:23:08.838Z" }, { "id": "ScopeNode", @@ -2255,7 +2261,7 @@ "requiresTestsUpdate": false } }, - "updatedAt": "2026-06-15T04:52:50.003Z" + "updatedAt": "2026-06-24T11:23:08.838Z" }, { "id": "ExecuteNode", @@ -2273,7 +2279,8 @@ "logs": [], "artifacts": [], "errors": [], - "transactionId": "txn:1781499170004:2f563147", + "warnings": [], + "transactionId": "txn:1782300188839:2f563147", "changedFiles": [], "applied": { "writeResults": [ @@ -2288,13 +2295,13 @@ "command": "node --check src/controller.mjs", "status": "failed", "stdout": "", - "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.verification_human_interrupt-bvDdsu\\src\\controller.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", + "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.verification_human_interrupt-7HeDIR\\src\\controller.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", "exitCode": 1, "error": "command exited with code 1" } ], "error": "verification command failed", - "durationMs": 158, + "durationMs": 41, "verification": { "verified": false, "checks": [ @@ -2321,7 +2328,7 @@ "changedFiles": [] } }, - "updatedAt": "2026-06-15T04:52:50.164Z" + "updatedAt": "2026-06-24T11:23:08.880Z" }, { "id": "VerifyNode", @@ -2364,60 +2371,60 @@ "status": "pending" } ], - "createdAt": "2026-06-15T04:52:50.003Z" + "createdAt": "2026-06-24T11:23:08.838Z" }, "history": [ { - "at": "2026-06-15T04:52:50.003Z", + "at": "2026-06-24T11:23:08.838Z", "from": null, "to": "created", "node": null, "reason": "created" }, { - "at": "2026-06-15T04:52:50.003Z", + "at": "2026-06-24T11:23:08.838Z", "from": "created", "to": "observing", "node": "ObserveNode", "reason": "enter ObserveNode" }, { - "at": "2026-06-15T04:52:50.003Z", + "at": "2026-06-24T11:23:08.838Z", "from": "observing", "to": "planning", "node": "ObserveNode", "reason": "advance to PlanNode" }, { - "at": "2026-06-15T04:52:50.003Z", + "at": "2026-06-24T11:23:08.838Z", "from": "planning", "to": "policy_checking", "node": "PlanNode", "reason": "advance to PolicyNode" }, { - "at": "2026-06-15T04:52:50.003Z", + "at": "2026-06-24T11:23:08.838Z", "from": "policy_checking", "to": "scope_checking", "node": "PolicyNode", "reason": "advance to ScopeNode" }, { - "at": "2026-06-15T04:52:50.003Z", + "at": "2026-06-24T11:23:08.838Z", "from": "scope_checking", "to": "executing", "node": "ScopeNode", "reason": "advance to ExecuteNode" }, { - "at": "2026-06-15T04:52:50.164Z", + "at": "2026-06-24T11:23:08.880Z", "from": "executing", "to": "verifying", "node": "ExecuteNode", "reason": "advance to VerifyNode" }, { - "at": "2026-06-15T04:52:50.164Z", + "at": "2026-06-24T11:23:08.881Z", "from": "verifying", "to": "waiting_for_human", "node": "VerifyNode", @@ -2426,7 +2433,7 @@ ], "artifacts": [ { - "createdAt": "2026-06-15T04:52:50.003Z", + "createdAt": "2026-06-24T11:23:08.838Z", "kind": "node_result", "node": "ObserveNode", "nodeType": "ObserveNode", @@ -2440,7 +2447,7 @@ } }, { - "createdAt": "2026-06-15T04:52:50.003Z", + "createdAt": "2026-06-24T11:23:08.838Z", "kind": "node_result", "node": "PlanNode", "nodeType": "PlanNode", @@ -2473,7 +2480,7 @@ } }, { - "createdAt": "2026-06-15T04:52:50.003Z", + "createdAt": "2026-06-24T11:23:08.838Z", "kind": "node_result", "node": "PolicyNode", "nodeType": "PolicyNode", @@ -2494,14 +2501,14 @@ { "name": "risk_tier", "status": "pass", - "message": "R2" + "message": "base=R2; declared=R2; effective=R2" } ] } } }, { - "createdAt": "2026-06-15T04:52:50.003Z", + "createdAt": "2026-06-24T11:23:08.838Z", "kind": "node_result", "node": "ScopeNode", "nodeType": "ScopeNode", @@ -2531,7 +2538,7 @@ } }, { - "createdAt": "2026-06-15T04:52:50.164Z", + "createdAt": "2026-06-24T11:23:08.880Z", "kind": "node_result", "node": "ExecuteNode", "nodeType": "ExecuteNode", @@ -2542,7 +2549,8 @@ "logs": [], "artifacts": [], "errors": [], - "transactionId": "txn:1781499170004:2f563147", + "warnings": [], + "transactionId": "txn:1782300188839:2f563147", "changedFiles": [], "applied": { "writeResults": [ @@ -2557,13 +2565,13 @@ "command": "node --check src/controller.mjs", "status": "failed", "stdout": "", - "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.verification_human_interrupt-bvDdsu\\src\\controller.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", + "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.verification_human_interrupt-7HeDIR\\src\\controller.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", "exitCode": 1, "error": "command exited with code 1" } ], "error": "verification command failed", - "durationMs": 158, + "durationMs": 41, "verification": { "verified": false, "checks": [ @@ -2592,7 +2600,7 @@ } }, { - "createdAt": "2026-06-15T04:52:50.164Z", + "createdAt": "2026-06-24T11:23:08.881Z", "kind": "node_result", "node": "VerifyNode", "nodeType": "VerifyNode", @@ -2626,7 +2634,7 @@ ], "approvalRequests": [ { - "id": "approval:629395d18b23", + "id": "approval:df0a2552cfc8", "taskId": "task:35fc503854cd", "node": "VerifyNode", "status": "pending", @@ -2639,25 +2647,25 @@ "reject", "cancel" ], - "createdAt": "2026-06-15T04:52:50.164Z", - "updatedAt": "2026-06-15T04:52:50.164Z" + "createdAt": "2026-06-24T11:23:08.881Z", + "updatedAt": "2026-06-24T11:23:08.881Z" } ], "budget": {}, "risk": { "riskTier": "R2" }, - "createdAt": "2026-06-15T04:52:50.003Z", - "updatedAt": "2026-06-15T04:52:50.164Z" + "createdAt": "2026-06-24T11:23:08.838Z", + "updatedAt": "2026-06-24T11:23:08.881Z" }, "trace": { "schemaVersion": 1, - "traceId": "audit:task:35fc503854cd:1781499170003", + "traceId": "audit:task:35fc503854cd:1782300188838", "taskId": "task:35fc503854cd", - "runId": "agent_run:4f72b86cbac8", + "runId": "agent_run:7ac251f04f53", "events": [ { - "at": "2026-06-15T04:52:50.003Z", + "at": "2026-06-24T11:23:08.838Z", "type": "state.created", "node": null, "state": "created", @@ -2665,7 +2673,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:50.003Z", + "at": "2026-06-24T11:23:08.838Z", "type": "node.start", "node": "ObserveNode", "state": "observing", @@ -2673,7 +2681,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:50.003Z", + "at": "2026-06-24T11:23:08.838Z", "type": "node.completed", "node": "ObserveNode", "state": "observing", @@ -2683,7 +2691,7 @@ } }, { - "at": "2026-06-15T04:52:50.003Z", + "at": "2026-06-24T11:23:08.838Z", "type": "node.start", "node": "PlanNode", "state": "planning", @@ -2691,7 +2699,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:50.003Z", + "at": "2026-06-24T11:23:08.838Z", "type": "node.completed", "node": "PlanNode", "state": "planning", @@ -2701,7 +2709,7 @@ } }, { - "at": "2026-06-15T04:52:50.003Z", + "at": "2026-06-24T11:23:08.838Z", "type": "node.start", "node": "PolicyNode", "state": "policy_checking", @@ -2709,7 +2717,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:50.003Z", + "at": "2026-06-24T11:23:08.838Z", "type": "node.completed", "node": "PolicyNode", "state": "policy_checking", @@ -2719,7 +2727,7 @@ } }, { - "at": "2026-06-15T04:52:50.003Z", + "at": "2026-06-24T11:23:08.838Z", "type": "node.start", "node": "ScopeNode", "state": "scope_checking", @@ -2727,7 +2735,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:50.003Z", + "at": "2026-06-24T11:23:08.838Z", "type": "node.completed", "node": "ScopeNode", "state": "scope_checking", @@ -2737,7 +2745,7 @@ } }, { - "at": "2026-06-15T04:52:50.003Z", + "at": "2026-06-24T11:23:08.838Z", "type": "node.start", "node": "ExecuteNode", "state": "executing", @@ -2745,7 +2753,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:50.164Z", + "at": "2026-06-24T11:23:08.880Z", "type": "node.completed", "node": "ExecuteNode", "state": "executing", @@ -2755,7 +2763,7 @@ } }, { - "at": "2026-06-15T04:52:50.164Z", + "at": "2026-06-24T11:23:08.881Z", "type": "node.start", "node": "VerifyNode", "state": "verifying", @@ -2763,7 +2771,7 @@ "data": {} }, { - "at": "2026-06-15T04:52:50.164Z", + "at": "2026-06-24T11:23:08.881Z", "type": "human.interrupt", "node": "VerifyNode", "state": "waiting_for_human", @@ -2772,12 +2780,12 @@ "reasons": [ "verification_failed" ], - "requestId": "approval:629395d18b23" + "requestId": "approval:df0a2552cfc8" } } ], - "createdAt": "2026-06-15T04:52:50.003Z", - "updatedAt": "2026-06-15T04:52:50.164Z" + "createdAt": "2026-06-24T11:23:08.838Z", + "updatedAt": "2026-06-24T11:23:08.881Z" }, "status": "waiting_for_human", "type": "run_agent_controller", @@ -2804,7 +2812,7 @@ "rollbackOk": false, "repairAttempted": false, "repairOk": false, - "durationMs": 180, + "durationMs": 82, "stepResults": [ { "actionId": "bench:plugin_process_isolation", @@ -2813,6 +2821,7 @@ "logs": [], "artifacts": [], "errors": [], + "warnings": [], "registry": { "ok": true, "decision": "allow", @@ -2846,12 +2855,12 @@ "required": false, "strategy": null }, - "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-R4RYHl\\actions\\isolated_action", - "executeModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-R4RYHl\\actions\\isolated_action\\execute.js", - "verifyModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-R4RYHl\\actions\\isolated_action\\verify.js", + "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-zqxS18\\actions\\isolated_action", + "executeModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-zqxS18\\actions\\isolated_action\\execute.js", + "verifyModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-zqxS18\\actions\\isolated_action\\verify.js", "rollbackModulePath": null, "handler": null, - "registeredAt": "2026-06-15T04:52:50.169Z", + "registeredAt": "2026-06-24T11:23:08.884Z", "metadata": {} }, "riskTier": "R1" @@ -2864,7 +2873,7 @@ "rejected": 0, "results": [ { - "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-R4RYHl\\actions\\isolated_action", + "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-zqxS18\\actions\\isolated_action", "ok": true, "decision": "loaded", "errors": [], @@ -2881,31 +2890,33 @@ }, "output": { "status": "succeeded", - "pid": 17596, + "pid": 19840, "hanakoChild": "1", "nodeOptions": null, - "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-R4RYHl" + "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-zqxS18" }, "pluginProcess": { "execute": { "status": "succeeded", "result": { "status": "succeeded", - "pid": 17596, + "pid": 19840, "hanakoChild": "1", "nodeOptions": null, - "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-R4RYHl" + "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-zqxS18" }, "exitCode": 0, "signal": null, "isolated": true, - "pid": 17596, - "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-R4RYHl", + "pid": 19840, + "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-zqxS18", + "workspaceRootImplicit": false, + "warnings": [], "stdout": "", "stderr": "", "stdoutTruncated": false, "stderrTruncated": false, - "durationMs": 110 + "durationMs": 39 } }, "verification": { @@ -2931,20 +2942,22 @@ "exitCode": 0, "signal": null, "isolated": true, - "pid": 19436, - "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-R4RYHl", + "pid": 22680, + "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-zqxS18", + "workspaceRootImplicit": false, + "warnings": [], "stdout": "", "stderr": "", "stdoutTruncated": false, "stderrTruncated": false, - "durationMs": 64 + "durationMs": 38 } } ], "required": true }, "rollback": null, - "durationMs": 177, + "durationMs": 80, "type": "execute_action", "name": "isolated_plugin_action" }, @@ -2968,7 +2981,7 @@ }, { "ok": true, - "actual": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-R4RYHl", + "actual": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-zqxS18", "type": "assert_last_result", "name": "assert_last_result" } @@ -2987,7 +3000,7 @@ "rollbackOk": true, "repairAttempted": false, "repairOk": false, - "durationMs": 151, + "durationMs": 121, "stepResults": [ { "actionId": "bench:plugin_rollback_on_verify_failure", @@ -2996,6 +3009,7 @@ "logs": [], "artifacts": [], "errors": [], + "warnings": [], "registry": { "ok": true, "decision": "allow", @@ -3029,12 +3043,12 @@ "required": true, "strategy": "plugin_rollback" }, - "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-gVwBMZ\\actions\\write_then_fail", - "executeModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-gVwBMZ\\actions\\write_then_fail\\execute.js", - "verifyModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-gVwBMZ\\actions\\write_then_fail\\verify.js", - "rollbackModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-gVwBMZ\\actions\\write_then_fail\\rollback.js", + "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-OOZRcU\\actions\\write_then_fail", + "executeModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-OOZRcU\\actions\\write_then_fail\\execute.js", + "verifyModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-OOZRcU\\actions\\write_then_fail\\verify.js", + "rollbackModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-OOZRcU\\actions\\write_then_fail\\rollback.js", "handler": null, - "registeredAt": "2026-06-15T04:52:50.349Z", + "registeredAt": "2026-06-24T11:23:08.966Z", "metadata": {} }, "riskTier": "R2" @@ -3047,7 +3061,7 @@ "rejected": 0, "results": [ { - "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-gVwBMZ\\actions\\write_then_fail", + "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-OOZRcU\\actions\\write_then_fail", "ok": true, "decision": "loaded", "errors": [], @@ -3074,13 +3088,15 @@ "exitCode": 0, "signal": null, "isolated": true, - "pid": 17840, - "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-gVwBMZ", + "pid": 9784, + "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-OOZRcU", + "workspaceRootImplicit": false, + "warnings": [], "stdout": "", "stderr": "", "stdoutTruncated": false, "stderrTruncated": false, - "durationMs": 53 + "durationMs": 40 } }, "error": "registered action verification failed", @@ -3109,13 +3125,15 @@ "exitCode": 0, "signal": null, "isolated": true, - "pid": 17872, - "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-gVwBMZ", + "pid": 18380, + "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-OOZRcU", + "workspaceRootImplicit": false, + "warnings": [], "stdout": "", "stderr": "", "stdoutTruncated": false, "stderrTruncated": false, - "durationMs": 47 + "durationMs": 37 } } ], @@ -3136,16 +3154,18 @@ "exitCode": 0, "signal": null, "isolated": true, - "pid": 18288, - "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-gVwBMZ", + "pid": 23164, + "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-OOZRcU", + "workspaceRootImplicit": false, + "warnings": [], "stdout": "", "stderr": "", "stdoutTruncated": false, "stderrTruncated": false, - "durationMs": 46 + "durationMs": 40 } }, - "durationMs": 147, + "durationMs": 119, "type": "execute_action", "name": "plugin_write_then_fail" }, @@ -3175,7 +3195,7 @@ "rollbackOk": false, "repairAttempted": false, "repairOk": false, - "durationMs": 126, + "durationMs": 116, "stepResults": [ { "actionId": "bench:plugin_verify_command_success", @@ -3184,6 +3204,7 @@ "logs": [], "artifacts": [], "errors": [], + "warnings": [], "registry": { "ok": true, "decision": "allow", @@ -3219,12 +3240,12 @@ "required": false, "strategy": null }, - "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-QqzbeM\\actions\\verified_action", - "executeModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-QqzbeM\\actions\\verified_action\\execute.js", - "verifyModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-QqzbeM\\actions\\verified_action\\verify.js", + "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-IKmvux\\actions\\verified_action", + "executeModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-IKmvux\\actions\\verified_action\\execute.js", + "verifyModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-IKmvux\\actions\\verified_action\\verify.js", "rollbackModulePath": null, "handler": null, - "registeredAt": "2026-06-15T04:52:50.500Z", + "registeredAt": "2026-06-24T11:23:09.087Z", "metadata": {} }, "riskTier": "R1" @@ -3237,7 +3258,7 @@ "rejected": 0, "results": [ { - "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-QqzbeM\\actions\\verified_action", + "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-IKmvux\\actions\\verified_action", "ok": true, "decision": "loaded", "errors": [], @@ -3266,13 +3287,15 @@ "exitCode": 0, "signal": null, "isolated": true, - "pid": 23348, - "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-QqzbeM", + "pid": 6384, + "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-IKmvux", + "workspaceRootImplicit": false, + "warnings": [], "stdout": "", "stderr": "", "stdoutTruncated": false, "stderrTruncated": false, - "durationMs": 42 + "durationMs": 38 } }, "verification": { @@ -3300,13 +3323,15 @@ "exitCode": 0, "signal": null, "isolated": true, - "pid": 23032, - "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-QqzbeM", + "pid": 5960, + "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-IKmvux", + "workspaceRootImplicit": false, + "warnings": [], "stdout": "", "stderr": "", "stdoutTruncated": false, "stderrTruncated": false, - "durationMs": 42 + "durationMs": 40 } }, { @@ -3327,7 +3352,7 @@ "required": true }, "rollback": null, - "durationMs": 123, + "durationMs": 113, "type": "execute_action", "name": "verified_plugin_action" }, @@ -3352,7 +3377,7 @@ "rollbackOk": false, "repairAttempted": false, "repairOk": false, - "durationMs": 39, + "durationMs": 38, "stepResults": [ { "status": "succeeded", @@ -3379,20 +3404,20 @@ "rollbackOk": false, "repairAttempted": false, "repairOk": false, - "durationMs": 3, + "durationMs": 16, "stepResults": [ { "schemaVersion": 1, - "generatedAt": "2026-06-15T04:52:50.664Z", - "projectRoot": "D:\\openhanako\\hanako-supplement\\self-evolve", + "generatedAt": "2026-06-24T11:23:09.254Z", + "projectRoot": "D:\\openhanako\\official-worktree\\Hanako-runtime-learner", "summary": { "status": "ready", "ok": true, - "version": "4.3.2", + "version": "4.3.23", "score": 100, - "passed": 15, + "passed": 16, "failed": 0, - "total": 15, + "total": 16, "failedChecks": [], "nextAction": "release can proceed after npm run check, npm test, and npm run benchmark pass" }, @@ -3401,9 +3426,9 @@ "id": "package.version_lts_format", "ok": true, "status": "passed", - "message": "package version is release-formatted: 4.3.2", + "message": "package version is release-formatted: 4.3.23", "details": { - "version": "4.3.2" + "version": "4.3.23" } }, { @@ -3412,9 +3437,9 @@ "status": "passed", "message": "package-lock root versions match package.json", "details": { - "packageVersion": "4.3.2", - "lockVersion": "4.3.2", - "rootPackageVersion": "4.3.2" + "packageVersion": "4.3.23", + "lockVersion": "4.3.23", + "rootPackageVersion": "4.3.23" } }, { @@ -3428,61 +3453,61 @@ { "path": "docs/ACTION_API.md", "exists": true, - "sizeBytes": 3197, + "sizeBytes": 2613, "ok": true }, { "path": "docs/POLICY.md", "exists": true, - "sizeBytes": 2335, + "sizeBytes": 1364, "ok": true }, { "path": "docs/TRANSACTION.md", "exists": true, - "sizeBytes": 1596, + "sizeBytes": 933, "ok": true }, { "path": "docs/SANDBOX.md", "exists": true, - "sizeBytes": 2018, + "sizeBytes": 1057, "ok": true }, { "path": "docs/SKILL_PROMOTION.md", "exists": true, - "sizeBytes": 2302, + "sizeBytes": 1127, "ok": true }, { "path": "docs/AUDIT.md", "exists": true, - "sizeBytes": 1594, + "sizeBytes": 1137, "ok": true }, { "path": "docs/BENCHMARKS.md", "exists": true, - "sizeBytes": 2238, + "sizeBytes": 967, "ok": true }, { "path": "docs/MIGRATION_v3_to_v4.md", "exists": true, - "sizeBytes": 2191, + "sizeBytes": 1363, "ok": true }, { "path": "docs/API_FREEZE.md", "exists": true, - "sizeBytes": 1827, + "sizeBytes": 1606, "ok": true }, { "path": "docs/DESIGN_GOAL_COMPLETION_MATRIX.md", "exists": true, - "sizeBytes": 3152, + "sizeBytes": 981, "ok": true } ], @@ -3493,18 +3518,18 @@ "id": "docs.acceptance_current_version", "ok": true, "status": "passed", - "message": "current acceptance report exists: docs/ACCEPTANCE-v4.3.2.md", + "message": "current acceptance report exists: docs/ACCEPTANCE-v4.3.23.md", "details": { - "acceptancePath": "docs/ACCEPTANCE-v4.3.2.md" + "acceptancePath": "docs/ACCEPTANCE-v4.3.23.md" } }, { "id": "docs.changelog_current_section", "ok": true, "status": "passed", - "message": "CHANGELOG has current section ## 4.3.2", + "message": "CHANGELOG has current section ## 4.3.23", "details": { - "heading": "## 4.3.2" + "heading": "## 4.3.23" } }, { @@ -3513,7 +3538,7 @@ "status": "passed", "message": "design goal matrix references current package version", "details": { - "version": "4.3.2" + "version": "4.3.23" } }, { @@ -3551,49 +3576,49 @@ "id": "readme.version_badge", "ok": true, "status": "passed", - "message": "README version badge matches package version: 4.3.2", + "message": "README version badge matches package version: 4.3.23", "details": { - "expected": "4.3.2", - "found": "4.3.2-lts" + "expected": "4.3.23", + "found": "4.3.23-lts" } }, { "id": "readme.test_badge", "ok": true, "status": "passed", - "message": "README test badge matches: 515/515", + "message": "README test badge matches: 665/665", "details": { - "expected": 515, - "found": 515 + "expected": 665, + "found": 665 } }, { "id": "readme.clone_branch", "ok": true, "status": "passed", - "message": "README fixed clone branch matches: v4.3.2", + "message": "README fixed clone branch matches: v4.3.23", "details": { - "expected": "v4.3.2", - "found": "v4.3.2" + "expected": "v4.3.23", + "found": "v4.3.23" } }, { "id": "manifest.version", "ok": true, "status": "passed", - "message": "manifest version matches package.json: 4.3.2", + "message": "manifest version matches package.json: 4.3.23", "details": { - "expected": "4.3.2", - "found": "4.3.2" + "expected": "4.3.23", + "found": "4.3.23" } }, { "id": "docs.api_freeze_version", "ok": true, "status": "passed", - "message": "API_FREEZE.md references current version or major.minor: 4.3.2", + "message": "API_FREEZE.md references current version or major.minor: 4.3.23", "details": { - "expected": "4.3.2", + "expected": "4.3.23", "majorMinor": "4.3" } }, @@ -3601,10 +3626,38 @@ "id": "readme.test_count_text", "ok": true, "status": "passed", - "message": "README test count text matches: 515", + "message": "README test count text matches: 665", "details": { - "expected": 515, - "found": 515 + "expected": 665, + "found": 665 + } + }, + { + "id": "complexity.within_budget", + "ok": true, + "status": "passed", + "message": "complexity within budget: 185 files, max 648 LOC, 0 TODO/FIXME (3 soft warning(s))", + "details": { + "totals": { + "fileCount": 185, + "libModuleCount": 88, + "loc": 26920, + "codeLoc": 22461, + "imports": 857, + "exports": 443, + "todos": 0, + "maxLoc": 648, + "maxImports": 32, + "maxExports": 17 + }, + "violations": [], + "softWarningCount": 3, + "dirs": [ + "lib", + "scripts", + "tests", + "tools" + ] } } ], @@ -3640,7 +3693,7 @@ "rollbackOk": false, "repairAttempted": true, "repairOk": true, - "durationMs": 79, + "durationMs": 78, "stepResults": [ { "actionId": "bench:repair_once_explicit_patch", @@ -3649,7 +3702,8 @@ "logs": [], "artifacts": [], "errors": [], - "transactionId": "txn:1781499170665:7b0daa0c", + "warnings": [], + "transactionId": "txn:1782300189255:7b0daa0c", "changedFiles": [ "src\\target.mjs" ], @@ -3754,6 +3808,7 @@ "logs": [], "artifacts": [], "errors": [], + "warnings": [], "diagnosis": "The failure is deterministic and should not be retried blindly.", "durationMs": 0, "verification": { @@ -3796,7 +3851,7 @@ "rollbackOk": false, "repairAttempted": false, "repairOk": false, - "durationMs": 0, + "durationMs": 1, "stepResults": [ { "actionId": "bench:split_context", @@ -3805,6 +3860,7 @@ "logs": [], "artifacts": [], "errors": [], + "warnings": [], "artifact": { "kind": "split_context", "content": "Section A: inspect runtime. Section B: inspect safety. Section C: inspect feedback. Section D: inspect audit. Section E: inspect benchmark. Section F: inspect rollback. Section G: inspect repair. Section H: inspect governance. Section I: inspect transfer. Section J: inspect controller. Section K: inspect registry. Section L: inspect docs.", @@ -3879,7 +3935,7 @@ "status": "pending" } ], - "createdAt": "2026-06-15T04:52:50.744Z" + "createdAt": "2026-06-24T11:23:09.333Z" } }, "before": { @@ -3889,7 +3945,7 @@ "chars": 340, "subtasks": 3 }, - "durationMs": 0, + "durationMs": 1, "verification": { "verified": true, "checks": [ @@ -3939,6 +3995,7 @@ "logs": [], "artifacts": [], "errors": [], + "warnings": [], "error": "scope gate rejected: file not in allowed scope: src/out-of-scope.js; scope_violation: src/out-of-scope.js outside explicit scope", "scopeGate": { "decision": "reject", @@ -4020,7 +4077,7 @@ "rollbackOk": true, "repairAttempted": false, "repairOk": false, - "durationMs": 40, + "durationMs": 48, "stepResults": [ { "actionId": "bench:rollback_failed_verification", @@ -4029,7 +4086,8 @@ "logs": [], "artifacts": [], "errors": [], - "transactionId": "txn:1781499170746:34461105", + "warnings": [], + "transactionId": "txn:1782300189334:34461105", "changedFiles": [], "applied": { "writeResults": [ @@ -4049,7 +4107,7 @@ "error": null } ], - "durationMs": 37, + "durationMs": 45, "verification": { "verified": false, "checks": [ @@ -4143,7 +4201,7 @@ "rollbackOk": false, "repairAttempted": false, "repairOk": false, - "durationMs": 10, + "durationMs": 12, "stepResults": [ { "ok": true, @@ -4193,10 +4251,10 @@ }, "tokenCost": 0, "status": "staged", - "createdAt": "2026-06-15T04:52:50.788Z", - "updatedAt": "2026-06-15T04:52:50.788Z", + "createdAt": "2026-06-24T11:23:09.385Z", + "updatedAt": "2026-06-24T11:23:09.385Z", "decayed": false, - "stagedAt": "2026-06-15T04:52:50.788Z" + "stagedAt": "2026-06-24T11:23:09.385Z" } ], "activeSkills": [], @@ -4277,11 +4335,11 @@ }, "tokenCost": 0, "status": "active", - "createdAt": "2026-06-15T04:52:50.788Z", - "updatedAt": "2026-06-15T04:52:50.796Z", + "createdAt": "2026-06-24T11:23:09.385Z", + "updatedAt": "2026-06-24T11:23:09.393Z", "decayed": false, - "stagedAt": "2026-06-15T04:52:50.788Z", - "activatedAt": "2026-06-15T04:52:50.796Z" + "stagedAt": "2026-06-24T11:23:09.385Z", + "activatedAt": "2026-06-24T11:23:09.393Z" } ], "activeSkills": [ @@ -4313,7 +4371,7 @@ "tokenCost": 0, "status": "active", "source": "reflexion_cluster", - "activatedAt": "2026-06-15T04:52:50.796Z" + "activatedAt": "2026-06-24T11:23:09.393Z" } ], "events": [ @@ -4375,7 +4433,7 @@ "rollbackOk": false, "repairAttempted": false, "repairOk": false, - "durationMs": 41, + "durationMs": 42, "stepResults": [ { "ok": true, @@ -4416,8 +4474,8 @@ "latestValidationStatus": "passed", "manualPromotionEligible": true, "autoPromotionBlocked": true, - "createdAt": "2026-06-15T04:52:50.798Z", - "updatedAt": "2026-06-15T04:52:50.837Z" + "createdAt": "2026-06-24T11:23:09.396Z", + "updatedAt": "2026-06-24T11:23:09.436Z" }, "type": "transfer_validate", "name": "validate_transfer_candidate" @@ -4446,16 +4504,16 @@ "false_auto_apply_rate": 0, "manual_escalation_rate": 0.11764705882352941, "token_overhead": null, - "latency_overhead": 64, + "latency_overhead": 43.76470588235294, "skill_effectiveness": null }, "corpus": { - "root": "D:\\openhanako\\hanako-supplement\\self-evolve\\benchmarks", + "root": "D:\\openhanako\\official-worktree\\Hanako-runtime-learner\\benchmarks", "scenarioCount": 17, "selectedScenarioCount": 17 }, "baseline": { - "path": "D:\\openhanako\\hanako-supplement\\self-evolve\\benchmarks\\baseline-v4.0.9.json", + "path": "D:\\openhanako\\official-worktree\\Hanako-runtime-learner\\benchmarks\\baseline-v4.0.9.json", "metrics": { "task_success_rate": 1, "auto_execution_success_rate": 1, diff --git a/benchmark-results/benchmark-report.md b/benchmark-results/benchmark-report.md index 6c0381e..fcc223d 100644 --- a/benchmark-results/benchmark-report.md +++ b/benchmark-results/benchmark-report.md @@ -1,6 +1,6 @@ # Benchmark Report -Generated at: 2026-06-15T04:52:50.839Z +Generated at: 2026-06-24T11:23:09.438Z Scenarios: 17 Status: passed @@ -16,7 +16,7 @@ Status: passed | false_auto_apply_rate | 0.0000 | | manual_escalation_rate | 0.1176 | | token_overhead | null | -| latency_overhead | 64.0000 | +| latency_overhead | 43.7647 | | skill_effectiveness | null | ## Scenario Results diff --git a/docs/ACCEPTANCE-v4.3.23.md b/docs/ACCEPTANCE-v4.3.23.md new file mode 100644 index 0000000..3e66959 --- /dev/null +++ b/docs/ACCEPTANCE-v4.3.23.md @@ -0,0 +1,38 @@ +# v4.3.23 验收记录 + +## 版本目标 + +`v4.3.23` 在 `v4.3.22` 自学习控制台基础上,补齐 v4.x LTS 的复杂度治理门:把维护期预算写成可机读规则、发布门检查和可审计文档,同时拆分控制面热点文件,降低后续维护风险。该版本不新增自动化能力。 + +## 实现 + +- `lib/complexity.js`:定义复杂度扫描、hard limit、soft target、报告构建和预算判定。 +- `scripts/complexity-check.js` / `scripts/complexity-report.js`:新增本地门禁与报告生成脚本,均只依赖 Node 内置模块。 +- `lib/release-readiness.js`:发布门新增 `complexity.within_budget` 检查,默认测试基线同步到 665。 +- `docs/COMPLEXITY_BUDGET.md`:记录 v4.x LTS 复杂度预算、模块新增规则和预算调整流程。 +- `docs/COMPLEXITY_DEBT.md` / `docs/COMPLEXITY_REPORT.md`:登记 soft target 债务并生成当前复杂度快照。 +- `tools/control-parameters.js`、`tools/control-summaries.js`、`tools/control-handlers/*`:从 `tools/control.js` 抽出参数解析、摘要格式化和部分控制面 handler,保留行为特征回归覆盖。 + +## 验收结果 + +| 项目 | 结果 | +|---|---| +| `npm run check` | 通过 | +| `npm test` | 665 个测试,660 通过,5 跳过 | +| `npm run benchmark` | 17/17 通过 | +| `npm run perf` | 通过,无阈值越界 | +| `npm run complexity:check` | 通过,0 hard violations | +| `npm run release:check` | Score 100 | + +## 本版确认项 + +1. 复杂度治理扫描范围限定为 `lib/`、`scripts/`、`tests/`、`tools/` 下的 JS/CJS/MJS 文件。 +2. hard limit 超出会阻断 `complexity:check` 和 `release:check`;soft target 只登记债务,不阻断发布。 +3. `docs/COMPLEXITY_BUDGET.md` 与 `lib/complexity.js` 中的预算数值保持一致。 +4. `tools/control.js` 已拆分到 602 LOC,仍仅略高于 600 LOC soft target,登记在复杂度债务中。 +5. README、INSTALL、CHANGELOG、ARCHITECTURE、设计目标矩阵、package/manifest/lockfile 版本均同步到 `4.3.23`。 +6. 自动化边界未放宽:新增的是本地静态治理与文档/测试,不执行 `git tag`、`git push`、`npm publish` 或外部副作用。 + +## 结论 + +`v4.3.23` 满足当前 release gate,作为 v4.x LTS 的复杂度治理与发布门加固版本发布。 diff --git a/docs/COMPLEXITY_REPORT.md b/docs/COMPLEXITY_REPORT.md index 78449fa..8843a31 100644 --- a/docs/COMPLEXITY_REPORT.md +++ b/docs/COMPLEXITY_REPORT.md @@ -3,7 +3,7 @@ > 自动生成,请勿手工编辑。运行 `npm run complexity:report` 刷新。 > 预算与规则见 [COMPLEXITY_BUDGET.md](COMPLEXITY_BUDGET.md),债务清单见 [COMPLEXITY_DEBT.md](COMPLEXITY_DEBT.md)。 -Generated at: 2026-06-24T10:44:26.734Z +Generated at: 2026-06-24T11:22:40.919Z Scan scope: lib, scripts, tests, tools Status: within budget diff --git a/docs/DESIGN_GOAL_COMPLETION_MATRIX.md b/docs/DESIGN_GOAL_COMPLETION_MATRIX.md index 73a9866..69889cc 100644 --- a/docs/DESIGN_GOAL_COMPLETION_MATRIX.md +++ b/docs/DESIGN_GOAL_COMPLETION_MATRIX.md @@ -1,6 +1,6 @@ # 设计目标完成矩阵 -当前版本:`v4.3.22` +当前版本:`v4.3.23` ## 总体状态 @@ -18,7 +18,7 @@ | 项目 | 预期 | |---|---| | `npm run check` | 通过 | -| `npm test` | 570 个测试,565 通过,5 跳过 | +| `npm test` | 665 个测试,660 通过,5 跳过 | | `npm run benchmark` | 17/17 通过 | | `npm run perf` | 无阈值越界 | | `npm run release:check` | Score 100 | diff --git a/manifest.json b/manifest.json index 82b28f1..9f6755a 100644 --- a/manifest.json +++ b/manifest.json @@ -2,7 +2,7 @@ "manifestVersion": 1, "id": "hanako-runtime-learner", "name": "Runtime Self-Learning", - "version": "4.3.22", + "version": "4.3.23", "description": "运行时自学习引擎:观察本地交互,归纳重复工作流、偏好与错误,并生成保守的经验提示。", "author": "Sun", "trust": "full-access", diff --git a/package-lock.json b/package-lock.json index 0bffa57..3b17763 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "hanako-runtime-learner", - "version": "4.3.22", + "version": "4.3.23", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "hanako-runtime-learner", - "version": "4.3.22", + "version": "4.3.23", "license": "MIT" } } diff --git a/package.json b/package.json index 06485d0..d3b5d78 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hanako-runtime-learner", - "version": "4.3.22", + "version": "4.3.23", "description": "Runtime self-learning engine for Hanako: observe local interaction patterns, learn repeated workflows and errors, inject conservative skill hints.", "author": "Sun", "license": "MIT", From 1bff40d2aa50fbe8ab3cefe58b32c71f58a7b58b Mon Sep 17 00:00:00 2001 From: Hanako Maintainer Date: Wed, 24 Jun 2026 19:46:23 +0800 Subject: [PATCH 3/4] chore: remove release artifacts from complexity governance PR --- CHANGELOG.md | 8 - INSTALL.md | 4 +- README.md | 10 +- benchmark-results/benchmark-report.json | 686 +++++++++++------------- benchmark-results/benchmark-report.md | 4 +- docs/ACCEPTANCE-v4.3.23.md | 38 -- docs/COMPLEXITY_REPORT.md | 2 +- docs/DESIGN_GOAL_COMPLETION_MATRIX.md | 4 +- manifest.json | 2 +- package-lock.json | 4 +- package.json | 2 +- 11 files changed, 330 insertions(+), 434 deletions(-) delete mode 100644 docs/ACCEPTANCE-v4.3.23.md diff --git a/CHANGELOG.md b/CHANGELOG.md index eca3242..7360808 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,14 +2,6 @@ 本文档记录 Runtime Self-Learning 的版本演进。`v4.x` 为 LTS 维护线,因此该阶段的记录重点放在缺陷修复、审计加固、性能整理和发布治理,不再扩张自动化边界。 -## 4.3.23 - -- 新增复杂度治理门:`lib/complexity.js` 定义 hard limit / soft target,`npm run complexity:check` 在超出 hard limit 时失败,`npm run complexity:report` 生成 `docs/COMPLEXITY_REPORT.md`。 -- 发布门纳入 `complexity.within_budget` 检查,并补充 `docs/COMPLEXITY_BUDGET.md` / `docs/COMPLEXITY_DEBT.md`,把 v4.x LTS 的模块数、单文件 LOC、import/export 数和 TODO/FIXME 预算写成可审计规则。 -- 拆分控制面热点复杂度:新增 `tools/control-parameters.js`、`tools/control-summaries.js` 和 `tools/control-handlers/*`,让 `tools/control.js` 从 712 LOC 收敛到 602 LOC,并保留行为特征回归。 -- 测试总数 `606 -> 665`:新增控制面参数、摘要、脱敏、handler characterization 与 release-readiness 复杂度门回归;README 徽章和发布门默认测试基线同步到 665。 -- 边界未放宽:本版只增加本地静态治理、文档和控制面结构整理,无新增自动放行、网络、发布或外部副作用能力。 - ## 4.3.22 - **新增自学习控制台(`chat.surface`,Hanako v0.344+)**:新增只读工具 `self_learning_console`,把"最近活动 + 待处理提案"快照投递进一条插件自有的 `plugin_private` 会话,并以原生 `chat.surface` transcript 卡片在当前聊天内嵌展示,可点开滚动查看历史快照。这是 UX/呈现层的可选增强,**不扩张自动化边界**:控台只读、由用户显式调用工具触发,不自动应用任何动作、不在后台主动推送。 diff --git a/INSTALL.md b/INSTALL.md index 5b29f8d..7747566 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -50,10 +50,10 @@ OK skills/self-learning/SKILL.md ## 固定版本安装 -如需固定到某个 release,例如 `v4.3.23`: +如需固定到某个 release,例如 `v4.3.22`: ```powershell -git clone --branch v4.3.23 https://github.com/326sun/Hanako-runtime-learner.git +git clone --branch v4.3.22 https://github.com/326sun/Hanako-runtime-learner.git cd Hanako-runtime-learner npm run install-plugin ``` diff --git a/README.md b/README.md index cba91dd..720351a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

- version + version CI license platform @@ -60,7 +60,7 @@ npm run install-plugin 固定版本安装: ```powershell -git clone --branch v4.3.23 https://github.com/326sun/Hanako-runtime-learner.git +git clone --branch v4.3.22 https://github.com/326sun/Hanako-runtime-learner.git cd Hanako-runtime-learner npm run install-plugin ``` @@ -210,10 +210,10 @@ npm run perf -- --json npm run release:check ``` -`v4.3.23` 的预期结果: +`v4.3.22` 的预期结果: ```text -package version: 4.3.23 +package version: 4.3.22 npm run check: passed npm test: 665 tests, 660 passed, 5 skipped npm run benchmark: passed, 17 scenarios @@ -240,7 +240,7 @@ npm run release:check: Score 100 | [docs/MIGRATION_v3_to_v4.md](docs/MIGRATION_v3_to_v4.md) | v3 到 v4 迁移说明。 | | [docs/LTS_MAINTENANCE_PLAN.md](docs/LTS_MAINTENANCE_PLAN.md) | v4.x LTS 维护策略。 | | [docs/DESIGN_GOAL_COMPLETION_MATRIX.md](docs/DESIGN_GOAL_COMPLETION_MATRIX.md) | 设计目标完成矩阵。 | -| [docs/ACCEPTANCE-v4.3.23.md](docs/ACCEPTANCE-v4.3.23.md) | 当前版本验收记录。 | +| [docs/ACCEPTANCE-v4.3.22.md](docs/ACCEPTANCE-v4.3.22.md) | 当前版本验收记录。 | | [CHANGELOG.md](CHANGELOG.md) | 版本历史。 | ## 许可证 diff --git a/benchmark-results/benchmark-report.json b/benchmark-results/benchmark-report.json index cc2259c..0181acc 100644 --- a/benchmark-results/benchmark-report.json +++ b/benchmark-results/benchmark-report.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "generatedAt": "2026-06-24T11:23:09.438Z", + "generatedAt": "2026-06-15T04:52:50.839Z", "runs": [ { "scenarioId": "audit.dashboard_surface", @@ -15,14 +15,14 @@ "rollbackOk": false, "repairAttempted": false, "repairOk": false, - "durationMs": 7, + "durationMs": 6, "stepResults": [ { "ok": true, "status": "generated", - "dir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-audit.dashboard_surface-gOxHEA\\.hanako\\audit-dashboard\\benchmark", - "jsonPath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-audit.dashboard_surface-gOxHEA\\.hanako\\audit-dashboard\\benchmark\\dashboard.json", - "mdPath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-audit.dashboard_surface-gOxHEA\\.hanako\\audit-dashboard\\benchmark\\dashboard.md", + "dir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-audit.dashboard_surface-1FS50v\\.hanako\\audit-dashboard\\benchmark", + "jsonPath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-audit.dashboard_surface-1FS50v\\.hanako\\audit-dashboard\\benchmark\\dashboard.json", + "mdPath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-audit.dashboard_surface-1FS50v\\.hanako\\audit-dashboard\\benchmark\\dashboard.md", "summary": { "benchmarkAvailable": true, "benchmarkOk": true, @@ -86,14 +86,14 @@ "rollbackOk": true, "repairAttempted": true, "repairOk": true, - "durationMs": 92, + "durationMs": 117, "stepResults": [ { "ok": true, "state": { "schemaVersion": 1, "taskId": "task:e006b07b0464", - "runId": "agent_run:d31f86c0b5e7", + "runId": "agent_run:13f9cbfff254", "state": "completed", "currentNode": "finalize", "graph": { @@ -118,7 +118,7 @@ "files": [] } }, - "updatedAt": "2026-06-24T11:23:08.702Z" + "updatedAt": "2026-06-15T04:52:49.757Z" }, { "id": "plan", @@ -156,7 +156,7 @@ } ] }, - "updatedAt": "2026-06-24T11:23:08.703Z" + "updatedAt": "2026-06-15T04:52:49.757Z" }, { "id": "policy", @@ -184,12 +184,12 @@ { "name": "risk_tier", "status": "pass", - "message": "base=R2; declared=R2; effective=R2" + "message": "R2" } ] } }, - "updatedAt": "2026-06-24T11:23:08.704Z" + "updatedAt": "2026-06-15T04:52:49.758Z" }, { "id": "scope", @@ -224,7 +224,7 @@ "requiresTestsUpdate": false } }, - "updatedAt": "2026-06-24T11:23:08.704Z" + "updatedAt": "2026-06-15T04:52:49.759Z" }, { "id": "execute", @@ -242,8 +242,7 @@ "logs": [], "artifacts": [], "errors": [], - "warnings": [], - "transactionId": "txn:1782300188705:65edf3f9", + "transactionId": "txn:1781499169760:65edf3f9", "changedFiles": [], "applied": { "writeResults": [ @@ -258,13 +257,13 @@ "command": "node --check src/repair-target.mjs", "status": "failed", "stdout": "", - "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.repair_branch-KVoI2g\\src\\repair-target.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", + "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.repair_branch-EJdIcg\\src\\repair-target.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", "exitCode": 1, "error": "command exited with code 1" } ], "error": "verification command failed", - "durationMs": 45, + "durationMs": 54, "verification": { "verified": false, "checks": [ @@ -291,7 +290,7 @@ "changedFiles": [] } }, - "updatedAt": "2026-06-24T11:23:08.752Z" + "updatedAt": "2026-06-15T04:52:49.815Z" }, { "id": "verify_initial", @@ -329,7 +328,7 @@ "error": "verification failed" }, "recoveryTarget": "repair", - "updatedAt": "2026-06-24T11:23:08.752Z" + "updatedAt": "2026-06-15T04:52:49.815Z" }, { "id": "repair", @@ -354,8 +353,7 @@ "logs": [], "artifacts": [], "errors": [], - "warnings": [], - "transactionId": "txn:1782300188753:3416c35d", + "transactionId": "txn:1781499169815:3416c35d", "changedFiles": [ "src\\repair-target.mjs" ], @@ -377,7 +375,7 @@ "error": null } ], - "durationMs": 40, + "durationMs": 55, "verification": { "verified": true, "checks": [ @@ -422,7 +420,7 @@ "confidence": 0.86 } }, - "updatedAt": "2026-06-24T11:23:08.792Z" + "updatedAt": "2026-06-15T04:52:49.870Z" }, { "id": "verify_after_repair", @@ -458,7 +456,7 @@ }, "sourceNode": "repair" }, - "updatedAt": "2026-06-24T11:23:08.792Z" + "updatedAt": "2026-06-15T04:52:49.871Z" }, { "id": "feedback", @@ -473,7 +471,7 @@ "status": "succeeded", "note": "FeedbackNode recorded" }, - "updatedAt": "2026-06-24T11:23:08.792Z" + "updatedAt": "2026-06-15T04:52:49.871Z" }, { "id": "finalize", @@ -488,91 +486,91 @@ "status": "succeeded", "note": "finalized" }, - "updatedAt": "2026-06-24T11:23:08.792Z" + "updatedAt": "2026-06-15T04:52:49.871Z" } ], - "createdAt": "2026-06-24T11:23:08.702Z" + "createdAt": "2026-06-15T04:52:49.756Z" }, "history": [ { - "at": "2026-06-24T11:23:08.702Z", + "at": "2026-06-15T04:52:49.756Z", "from": null, "to": "created", "node": null, "reason": "created" }, { - "at": "2026-06-24T11:23:08.702Z", + "at": "2026-06-15T04:52:49.757Z", "from": "created", "to": "observing", "node": "observe", "reason": "enter ObserveNode" }, { - "at": "2026-06-24T11:23:08.703Z", + "at": "2026-06-15T04:52:49.757Z", "from": "observing", "to": "planning", "node": "observe", "reason": "advance to PlanNode" }, { - "at": "2026-06-24T11:23:08.703Z", + "at": "2026-06-15T04:52:49.757Z", "from": "planning", "to": "policy_checking", "node": "plan", "reason": "advance to PolicyNode" }, { - "at": "2026-06-24T11:23:08.704Z", + "at": "2026-06-15T04:52:49.758Z", "from": "policy_checking", "to": "scope_checking", "node": "policy", "reason": "advance to ScopeNode" }, { - "at": "2026-06-24T11:23:08.704Z", + "at": "2026-06-15T04:52:49.759Z", "from": "scope_checking", "to": "executing", "node": "scope", "reason": "advance to ExecuteNode" }, { - "at": "2026-06-24T11:23:08.752Z", + "at": "2026-06-15T04:52:49.815Z", "from": "executing", "to": "verifying", "node": "execute", "reason": "advance to VerifyNode" }, { - "at": "2026-06-24T11:23:08.752Z", + "at": "2026-06-15T04:52:49.815Z", "from": "verifying", "to": "repairing", "node": "verify_initial", "reason": "VerifyNode routed to RepairNode" }, { - "at": "2026-06-24T11:23:08.792Z", + "at": "2026-06-15T04:52:49.870Z", "from": "repairing", "to": "verifying", "node": "repair", "reason": "advance to VerifyNode" }, { - "at": "2026-06-24T11:23:08.792Z", + "at": "2026-06-15T04:52:49.871Z", "from": "verifying", "to": "learning", "node": "verify_after_repair", "reason": "advance to FeedbackNode" }, { - "at": "2026-06-24T11:23:08.792Z", + "at": "2026-06-15T04:52:49.871Z", "from": "learning", "to": "learning", "node": "feedback", "reason": "advance to FinalizeNode" }, { - "at": "2026-06-24T11:23:08.792Z", + "at": "2026-06-15T04:52:49.871Z", "from": "learning", "to": "completed", "node": "finalize", @@ -581,7 +579,7 @@ ], "artifacts": [ { - "createdAt": "2026-06-24T11:23:08.702Z", + "createdAt": "2026-06-15T04:52:49.757Z", "kind": "node_result", "node": "observe", "nodeType": "ObserveNode", @@ -595,7 +593,7 @@ } }, { - "createdAt": "2026-06-24T11:23:08.703Z", + "createdAt": "2026-06-15T04:52:49.757Z", "kind": "node_result", "node": "plan", "nodeType": "PlanNode", @@ -628,7 +626,7 @@ } }, { - "createdAt": "2026-06-24T11:23:08.704Z", + "createdAt": "2026-06-15T04:52:49.758Z", "kind": "node_result", "node": "policy", "nodeType": "PolicyNode", @@ -649,14 +647,14 @@ { "name": "risk_tier", "status": "pass", - "message": "base=R2; declared=R2; effective=R2" + "message": "R2" } ] } } }, { - "createdAt": "2026-06-24T11:23:08.704Z", + "createdAt": "2026-06-15T04:52:49.759Z", "kind": "node_result", "node": "scope", "nodeType": "ScopeNode", @@ -686,7 +684,7 @@ } }, { - "createdAt": "2026-06-24T11:23:08.752Z", + "createdAt": "2026-06-15T04:52:49.815Z", "kind": "node_result", "node": "execute", "nodeType": "ExecuteNode", @@ -697,8 +695,7 @@ "logs": [], "artifacts": [], "errors": [], - "warnings": [], - "transactionId": "txn:1782300188705:65edf3f9", + "transactionId": "txn:1781499169760:65edf3f9", "changedFiles": [], "applied": { "writeResults": [ @@ -713,13 +710,13 @@ "command": "node --check src/repair-target.mjs", "status": "failed", "stdout": "", - "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.repair_branch-KVoI2g\\src\\repair-target.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", + "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.repair_branch-EJdIcg\\src\\repair-target.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", "exitCode": 1, "error": "command exited with code 1" } ], "error": "verification command failed", - "durationMs": 45, + "durationMs": 54, "verification": { "verified": false, "checks": [ @@ -748,7 +745,7 @@ } }, { - "createdAt": "2026-06-24T11:23:08.752Z", + "createdAt": "2026-06-15T04:52:49.815Z", "kind": "node_result", "node": "verify_initial", "nodeType": "VerifyNode", @@ -780,7 +777,7 @@ } }, { - "createdAt": "2026-06-24T11:23:08.792Z", + "createdAt": "2026-06-15T04:52:49.870Z", "kind": "node_result", "node": "repair", "nodeType": "RepairNode", @@ -798,8 +795,7 @@ "logs": [], "artifacts": [], "errors": [], - "warnings": [], - "transactionId": "txn:1782300188753:3416c35d", + "transactionId": "txn:1781499169815:3416c35d", "changedFiles": [ "src\\repair-target.mjs" ], @@ -821,7 +817,7 @@ "error": null } ], - "durationMs": 40, + "durationMs": 55, "verification": { "verified": true, "checks": [ @@ -868,7 +864,7 @@ } }, { - "createdAt": "2026-06-24T11:23:08.792Z", + "createdAt": "2026-06-15T04:52:49.871Z", "kind": "node_result", "node": "verify_after_repair", "nodeType": "VerifyNode", @@ -899,7 +895,7 @@ } }, { - "createdAt": "2026-06-24T11:23:08.792Z", + "createdAt": "2026-06-15T04:52:49.871Z", "kind": "node_result", "node": "feedback", "nodeType": "FeedbackNode", @@ -909,7 +905,7 @@ } }, { - "createdAt": "2026-06-24T11:23:08.792Z", + "createdAt": "2026-06-15T04:52:49.871Z", "kind": "node_result", "node": "finalize", "nodeType": "FinalizeNode", @@ -924,17 +920,17 @@ "risk": { "riskTier": "R2" }, - "createdAt": "2026-06-24T11:23:08.702Z", - "updatedAt": "2026-06-24T11:23:08.792Z" + "createdAt": "2026-06-15T04:52:49.756Z", + "updatedAt": "2026-06-15T04:52:49.871Z" }, "trace": { "schemaVersion": 1, - "traceId": "audit:task:e006b07b0464:1782300188702", + "traceId": "audit:task:e006b07b0464:1781499169756", "taskId": "task:e006b07b0464", - "runId": "agent_run:d31f86c0b5e7", + "runId": "agent_run:13f9cbfff254", "events": [ { - "at": "2026-06-24T11:23:08.702Z", + "at": "2026-06-15T04:52:49.756Z", "type": "state.created", "node": null, "state": "created", @@ -942,7 +938,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.702Z", + "at": "2026-06-15T04:52:49.757Z", "type": "node.start", "node": "observe", "state": "observing", @@ -950,7 +946,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.702Z", + "at": "2026-06-15T04:52:49.757Z", "type": "node.completed", "node": "observe", "state": "observing", @@ -960,7 +956,7 @@ } }, { - "at": "2026-06-24T11:23:08.703Z", + "at": "2026-06-15T04:52:49.757Z", "type": "node.start", "node": "plan", "state": "planning", @@ -968,7 +964,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.703Z", + "at": "2026-06-15T04:52:49.757Z", "type": "node.completed", "node": "plan", "state": "planning", @@ -978,7 +974,7 @@ } }, { - "at": "2026-06-24T11:23:08.703Z", + "at": "2026-06-15T04:52:49.757Z", "type": "node.start", "node": "policy", "state": "policy_checking", @@ -986,7 +982,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.704Z", + "at": "2026-06-15T04:52:49.758Z", "type": "node.completed", "node": "policy", "state": "policy_checking", @@ -996,7 +992,7 @@ } }, { - "at": "2026-06-24T11:23:08.704Z", + "at": "2026-06-15T04:52:49.758Z", "type": "node.start", "node": "scope", "state": "scope_checking", @@ -1004,7 +1000,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.704Z", + "at": "2026-06-15T04:52:49.759Z", "type": "node.completed", "node": "scope", "state": "scope_checking", @@ -1014,7 +1010,7 @@ } }, { - "at": "2026-06-24T11:23:08.704Z", + "at": "2026-06-15T04:52:49.759Z", "type": "node.start", "node": "execute", "state": "executing", @@ -1022,7 +1018,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.752Z", + "at": "2026-06-15T04:52:49.815Z", "type": "node.completed", "node": "execute", "state": "executing", @@ -1032,7 +1028,7 @@ } }, { - "at": "2026-06-24T11:23:08.752Z", + "at": "2026-06-15T04:52:49.815Z", "type": "node.start", "node": "verify_initial", "state": "verifying", @@ -1040,7 +1036,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.752Z", + "at": "2026-06-15T04:52:49.815Z", "type": "node.recovery_branch", "node": "verify_initial", "state": "repairing", @@ -1072,7 +1068,7 @@ } }, { - "at": "2026-06-24T11:23:08.752Z", + "at": "2026-06-15T04:52:49.815Z", "type": "node.start", "node": "repair", "state": "repairing", @@ -1080,7 +1076,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.792Z", + "at": "2026-06-15T04:52:49.870Z", "type": "node.completed", "node": "repair", "state": "repairing", @@ -1090,7 +1086,7 @@ } }, { - "at": "2026-06-24T11:23:08.792Z", + "at": "2026-06-15T04:52:49.871Z", "type": "node.start", "node": "verify_after_repair", "state": "verifying", @@ -1098,7 +1094,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.792Z", + "at": "2026-06-15T04:52:49.871Z", "type": "node.completed", "node": "verify_after_repair", "state": "verifying", @@ -1108,7 +1104,7 @@ } }, { - "at": "2026-06-24T11:23:08.792Z", + "at": "2026-06-15T04:52:49.871Z", "type": "node.start", "node": "feedback", "state": "learning", @@ -1116,7 +1112,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.792Z", + "at": "2026-06-15T04:52:49.871Z", "type": "node.completed", "node": "feedback", "state": "learning", @@ -1126,7 +1122,7 @@ } }, { - "at": "2026-06-24T11:23:08.792Z", + "at": "2026-06-15T04:52:49.871Z", "type": "node.start", "node": "finalize", "state": "learning", @@ -1134,7 +1130,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.792Z", + "at": "2026-06-15T04:52:49.871Z", "type": "node.completed", "node": "finalize", "state": "learning", @@ -1144,7 +1140,7 @@ } }, { - "at": "2026-06-24T11:23:08.793Z", + "at": "2026-06-15T04:52:49.871Z", "type": "state.completed", "node": "finalize", "state": "completed", @@ -1152,8 +1148,8 @@ "data": {} } ], - "createdAt": "2026-06-24T11:23:08.702Z", - "updatedAt": "2026-06-24T11:23:08.793Z" + "createdAt": "2026-06-15T04:52:49.756Z", + "updatedAt": "2026-06-15T04:52:49.871Z" }, "status": "completed", "type": "run_agent_controller", @@ -1185,14 +1181,14 @@ "rollbackOk": true, "repairAttempted": false, "repairOk": false, - "durationMs": 44, + "durationMs": 130, "stepResults": [ { "ok": true, "state": { "schemaVersion": 1, "taskId": "task:b932bd6c5854", - "runId": "agent_run:369051c672ab", + "runId": "agent_run:6717a622eb02", "state": "completed", "currentNode": "finalize", "graph": { @@ -1217,7 +1213,7 @@ "files": [] } }, - "updatedAt": "2026-06-24T11:23:08.794Z" + "updatedAt": "2026-06-15T04:52:49.873Z" }, { "id": "plan", @@ -1256,7 +1252,7 @@ } ] }, - "updatedAt": "2026-06-24T11:23:08.794Z" + "updatedAt": "2026-06-15T04:52:49.873Z" }, { "id": "policy", @@ -1284,12 +1280,12 @@ { "name": "risk_tier", "status": "pass", - "message": "base=R2; declared=R2; effective=R2" + "message": "R2" } ] } }, - "updatedAt": "2026-06-24T11:23:08.794Z" + "updatedAt": "2026-06-15T04:52:49.873Z" }, { "id": "scope", @@ -1324,7 +1320,7 @@ "requiresTestsUpdate": false } }, - "updatedAt": "2026-06-24T11:23:08.794Z" + "updatedAt": "2026-06-15T04:52:49.873Z" }, { "id": "execute", @@ -1342,8 +1338,7 @@ "logs": [], "artifacts": [], "errors": [], - "warnings": [], - "transactionId": "txn:1782300188795:92b7d810", + "transactionId": "txn:1781499169874:92b7d810", "changedFiles": [], "applied": { "writeResults": [ @@ -1358,13 +1353,13 @@ "command": "node --check src/rollback-target.mjs", "status": "failed", "stdout": "", - "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.rollback_branch-8A2sXa\\src\\rollback-target.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", + "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.rollback_branch-GMapTz\\src\\rollback-target.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", "exitCode": 1, "error": "command exited with code 1" } ], "error": "verification command failed", - "durationMs": 40, + "durationMs": 125, "verification": { "verified": false, "checks": [ @@ -1396,7 +1391,7 @@ "changedFiles": [] } }, - "updatedAt": "2026-06-24T11:23:08.836Z" + "updatedAt": "2026-06-15T04:52:50.000Z" }, { "id": "verify", @@ -1439,7 +1434,7 @@ "error": "verification failed" }, "recoveryTarget": "rollback", - "updatedAt": "2026-06-24T11:23:08.836Z" + "updatedAt": "2026-06-15T04:52:50.000Z" }, { "id": "rollback", @@ -1484,7 +1479,7 @@ }, "sourceNode": "execute" }, - "updatedAt": "2026-06-24T11:23:08.836Z" + "updatedAt": "2026-06-15T04:52:50.000Z" }, { "id": "feedback", @@ -1499,7 +1494,7 @@ "status": "succeeded", "note": "FeedbackNode recorded" }, - "updatedAt": "2026-06-24T11:23:08.836Z" + "updatedAt": "2026-06-15T04:52:50.000Z" }, { "id": "finalize", @@ -1514,84 +1509,84 @@ "status": "succeeded", "note": "finalized" }, - "updatedAt": "2026-06-24T11:23:08.837Z" + "updatedAt": "2026-06-15T04:52:50.001Z" } ], - "createdAt": "2026-06-24T11:23:08.794Z" + "createdAt": "2026-06-15T04:52:49.873Z" }, "history": [ { - "at": "2026-06-24T11:23:08.794Z", + "at": "2026-06-15T04:52:49.873Z", "from": null, "to": "created", "node": null, "reason": "created" }, { - "at": "2026-06-24T11:23:08.794Z", + "at": "2026-06-15T04:52:49.873Z", "from": "created", "to": "observing", "node": "observe", "reason": "enter ObserveNode" }, { - "at": "2026-06-24T11:23:08.794Z", + "at": "2026-06-15T04:52:49.873Z", "from": "observing", "to": "planning", "node": "observe", "reason": "advance to PlanNode" }, { - "at": "2026-06-24T11:23:08.794Z", + "at": "2026-06-15T04:52:49.873Z", "from": "planning", "to": "policy_checking", "node": "plan", "reason": "advance to PolicyNode" }, { - "at": "2026-06-24T11:23:08.794Z", + "at": "2026-06-15T04:52:49.873Z", "from": "policy_checking", "to": "scope_checking", "node": "policy", "reason": "advance to ScopeNode" }, { - "at": "2026-06-24T11:23:08.794Z", + "at": "2026-06-15T04:52:49.873Z", "from": "scope_checking", "to": "executing", "node": "scope", "reason": "advance to ExecuteNode" }, { - "at": "2026-06-24T11:23:08.836Z", + "at": "2026-06-15T04:52:50.000Z", "from": "executing", "to": "verifying", "node": "execute", "reason": "advance to VerifyNode" }, { - "at": "2026-06-24T11:23:08.836Z", + "at": "2026-06-15T04:52:50.000Z", "from": "verifying", "to": "rolling_back", "node": "verify", "reason": "VerifyNode routed to RollbackNode" }, { - "at": "2026-06-24T11:23:08.836Z", + "at": "2026-06-15T04:52:50.000Z", "from": "rolling_back", "to": "learning", "node": "rollback", "reason": "advance to FeedbackNode" }, { - "at": "2026-06-24T11:23:08.836Z", + "at": "2026-06-15T04:52:50.000Z", "from": "learning", "to": "learning", "node": "feedback", "reason": "advance to FinalizeNode" }, { - "at": "2026-06-24T11:23:08.837Z", + "at": "2026-06-15T04:52:50.001Z", "from": "learning", "to": "completed", "node": "finalize", @@ -1600,7 +1595,7 @@ ], "artifacts": [ { - "createdAt": "2026-06-24T11:23:08.794Z", + "createdAt": "2026-06-15T04:52:49.873Z", "kind": "node_result", "node": "observe", "nodeType": "ObserveNode", @@ -1614,7 +1609,7 @@ } }, { - "createdAt": "2026-06-24T11:23:08.794Z", + "createdAt": "2026-06-15T04:52:49.873Z", "kind": "node_result", "node": "plan", "nodeType": "PlanNode", @@ -1648,7 +1643,7 @@ } }, { - "createdAt": "2026-06-24T11:23:08.794Z", + "createdAt": "2026-06-15T04:52:49.873Z", "kind": "node_result", "node": "policy", "nodeType": "PolicyNode", @@ -1669,14 +1664,14 @@ { "name": "risk_tier", "status": "pass", - "message": "base=R2; declared=R2; effective=R2" + "message": "R2" } ] } } }, { - "createdAt": "2026-06-24T11:23:08.794Z", + "createdAt": "2026-06-15T04:52:49.873Z", "kind": "node_result", "node": "scope", "nodeType": "ScopeNode", @@ -1706,7 +1701,7 @@ } }, { - "createdAt": "2026-06-24T11:23:08.836Z", + "createdAt": "2026-06-15T04:52:50.000Z", "kind": "node_result", "node": "execute", "nodeType": "ExecuteNode", @@ -1717,8 +1712,7 @@ "logs": [], "artifacts": [], "errors": [], - "warnings": [], - "transactionId": "txn:1782300188795:92b7d810", + "transactionId": "txn:1781499169874:92b7d810", "changedFiles": [], "applied": { "writeResults": [ @@ -1733,13 +1727,13 @@ "command": "node --check src/rollback-target.mjs", "status": "failed", "stdout": "", - "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.rollback_branch-8A2sXa\\src\\rollback-target.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", + "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.rollback_branch-GMapTz\\src\\rollback-target.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", "exitCode": 1, "error": "command exited with code 1" } ], "error": "verification command failed", - "durationMs": 40, + "durationMs": 125, "verification": { "verified": false, "checks": [ @@ -1773,7 +1767,7 @@ } }, { - "createdAt": "2026-06-24T11:23:08.836Z", + "createdAt": "2026-06-15T04:52:50.000Z", "kind": "node_result", "node": "verify", "nodeType": "VerifyNode", @@ -1810,7 +1804,7 @@ } }, { - "createdAt": "2026-06-24T11:23:08.836Z", + "createdAt": "2026-06-15T04:52:50.000Z", "kind": "node_result", "node": "rollback", "nodeType": "RollbackNode", @@ -1850,7 +1844,7 @@ } }, { - "createdAt": "2026-06-24T11:23:08.836Z", + "createdAt": "2026-06-15T04:52:50.000Z", "kind": "node_result", "node": "feedback", "nodeType": "FeedbackNode", @@ -1860,7 +1854,7 @@ } }, { - "createdAt": "2026-06-24T11:23:08.837Z", + "createdAt": "2026-06-15T04:52:50.001Z", "kind": "node_result", "node": "finalize", "nodeType": "FinalizeNode", @@ -1875,17 +1869,17 @@ "risk": { "riskTier": "R2" }, - "createdAt": "2026-06-24T11:23:08.794Z", - "updatedAt": "2026-06-24T11:23:08.837Z" + "createdAt": "2026-06-15T04:52:49.873Z", + "updatedAt": "2026-06-15T04:52:50.001Z" }, "trace": { "schemaVersion": 1, - "traceId": "audit:task:b932bd6c5854:1782300188794", + "traceId": "audit:task:b932bd6c5854:1781499169873", "taskId": "task:b932bd6c5854", - "runId": "agent_run:369051c672ab", + "runId": "agent_run:6717a622eb02", "events": [ { - "at": "2026-06-24T11:23:08.794Z", + "at": "2026-06-15T04:52:49.873Z", "type": "state.created", "node": null, "state": "created", @@ -1893,7 +1887,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.794Z", + "at": "2026-06-15T04:52:49.873Z", "type": "node.start", "node": "observe", "state": "observing", @@ -1901,7 +1895,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.794Z", + "at": "2026-06-15T04:52:49.873Z", "type": "node.completed", "node": "observe", "state": "observing", @@ -1911,7 +1905,7 @@ } }, { - "at": "2026-06-24T11:23:08.794Z", + "at": "2026-06-15T04:52:49.873Z", "type": "node.start", "node": "plan", "state": "planning", @@ -1919,7 +1913,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.794Z", + "at": "2026-06-15T04:52:49.873Z", "type": "node.completed", "node": "plan", "state": "planning", @@ -1929,7 +1923,7 @@ } }, { - "at": "2026-06-24T11:23:08.794Z", + "at": "2026-06-15T04:52:49.873Z", "type": "node.start", "node": "policy", "state": "policy_checking", @@ -1937,7 +1931,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.794Z", + "at": "2026-06-15T04:52:49.873Z", "type": "node.completed", "node": "policy", "state": "policy_checking", @@ -1947,7 +1941,7 @@ } }, { - "at": "2026-06-24T11:23:08.794Z", + "at": "2026-06-15T04:52:49.873Z", "type": "node.start", "node": "scope", "state": "scope_checking", @@ -1955,7 +1949,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.794Z", + "at": "2026-06-15T04:52:49.873Z", "type": "node.completed", "node": "scope", "state": "scope_checking", @@ -1965,7 +1959,7 @@ } }, { - "at": "2026-06-24T11:23:08.794Z", + "at": "2026-06-15T04:52:49.873Z", "type": "node.start", "node": "execute", "state": "executing", @@ -1973,7 +1967,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.836Z", + "at": "2026-06-15T04:52:50.000Z", "type": "node.completed", "node": "execute", "state": "executing", @@ -1983,7 +1977,7 @@ } }, { - "at": "2026-06-24T11:23:08.836Z", + "at": "2026-06-15T04:52:50.000Z", "type": "node.start", "node": "verify", "state": "verifying", @@ -1991,7 +1985,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.836Z", + "at": "2026-06-15T04:52:50.000Z", "type": "node.recovery_branch", "node": "verify", "state": "rolling_back", @@ -2028,7 +2022,7 @@ } }, { - "at": "2026-06-24T11:23:08.836Z", + "at": "2026-06-15T04:52:50.000Z", "type": "node.start", "node": "rollback", "state": "rolling_back", @@ -2036,7 +2030,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.836Z", + "at": "2026-06-15T04:52:50.000Z", "type": "node.completed", "node": "rollback", "state": "rolling_back", @@ -2046,7 +2040,7 @@ } }, { - "at": "2026-06-24T11:23:08.836Z", + "at": "2026-06-15T04:52:50.000Z", "type": "node.start", "node": "feedback", "state": "learning", @@ -2054,7 +2048,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.836Z", + "at": "2026-06-15T04:52:50.000Z", "type": "node.completed", "node": "feedback", "state": "learning", @@ -2064,7 +2058,7 @@ } }, { - "at": "2026-06-24T11:23:08.837Z", + "at": "2026-06-15T04:52:50.001Z", "type": "node.start", "node": "finalize", "state": "learning", @@ -2072,7 +2066,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.837Z", + "at": "2026-06-15T04:52:50.001Z", "type": "node.completed", "node": "finalize", "state": "learning", @@ -2082,7 +2076,7 @@ } }, { - "at": "2026-06-24T11:23:08.837Z", + "at": "2026-06-15T04:52:50.001Z", "type": "state.completed", "node": "finalize", "state": "completed", @@ -2090,8 +2084,8 @@ "data": {} } ], - "createdAt": "2026-06-24T11:23:08.794Z", - "updatedAt": "2026-06-24T11:23:08.837Z" + "createdAt": "2026-06-15T04:52:49.873Z", + "updatedAt": "2026-06-15T04:52:50.001Z" }, "status": "completed", "type": "run_agent_controller", @@ -2123,14 +2117,14 @@ "rollbackOk": true, "repairAttempted": false, "repairOk": false, - "durationMs": 44, + "durationMs": 163, "stepResults": [ { "ok": false, "state": { "schemaVersion": 1, "taskId": "task:35fc503854cd", - "runId": "agent_run:7ac251f04f53", + "runId": "agent_run:4f72b86cbac8", "state": "waiting_for_human", "currentNode": "VerifyNode", "graph": { @@ -2155,7 +2149,7 @@ "files": [] } }, - "updatedAt": "2026-06-24T11:23:08.838Z" + "updatedAt": "2026-06-15T04:52:50.003Z" }, { "id": "PlanNode", @@ -2193,7 +2187,7 @@ } ] }, - "updatedAt": "2026-06-24T11:23:08.838Z" + "updatedAt": "2026-06-15T04:52:50.003Z" }, { "id": "PolicyNode", @@ -2221,12 +2215,12 @@ { "name": "risk_tier", "status": "pass", - "message": "base=R2; declared=R2; effective=R2" + "message": "R2" } ] } }, - "updatedAt": "2026-06-24T11:23:08.838Z" + "updatedAt": "2026-06-15T04:52:50.003Z" }, { "id": "ScopeNode", @@ -2261,7 +2255,7 @@ "requiresTestsUpdate": false } }, - "updatedAt": "2026-06-24T11:23:08.838Z" + "updatedAt": "2026-06-15T04:52:50.003Z" }, { "id": "ExecuteNode", @@ -2279,8 +2273,7 @@ "logs": [], "artifacts": [], "errors": [], - "warnings": [], - "transactionId": "txn:1782300188839:2f563147", + "transactionId": "txn:1781499170004:2f563147", "changedFiles": [], "applied": { "writeResults": [ @@ -2295,13 +2288,13 @@ "command": "node --check src/controller.mjs", "status": "failed", "stdout": "", - "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.verification_human_interrupt-7HeDIR\\src\\controller.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", + "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.verification_human_interrupt-bvDdsu\\src\\controller.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", "exitCode": 1, "error": "command exited with code 1" } ], "error": "verification command failed", - "durationMs": 41, + "durationMs": 158, "verification": { "verified": false, "checks": [ @@ -2328,7 +2321,7 @@ "changedFiles": [] } }, - "updatedAt": "2026-06-24T11:23:08.880Z" + "updatedAt": "2026-06-15T04:52:50.164Z" }, { "id": "VerifyNode", @@ -2371,60 +2364,60 @@ "status": "pending" } ], - "createdAt": "2026-06-24T11:23:08.838Z" + "createdAt": "2026-06-15T04:52:50.003Z" }, "history": [ { - "at": "2026-06-24T11:23:08.838Z", + "at": "2026-06-15T04:52:50.003Z", "from": null, "to": "created", "node": null, "reason": "created" }, { - "at": "2026-06-24T11:23:08.838Z", + "at": "2026-06-15T04:52:50.003Z", "from": "created", "to": "observing", "node": "ObserveNode", "reason": "enter ObserveNode" }, { - "at": "2026-06-24T11:23:08.838Z", + "at": "2026-06-15T04:52:50.003Z", "from": "observing", "to": "planning", "node": "ObserveNode", "reason": "advance to PlanNode" }, { - "at": "2026-06-24T11:23:08.838Z", + "at": "2026-06-15T04:52:50.003Z", "from": "planning", "to": "policy_checking", "node": "PlanNode", "reason": "advance to PolicyNode" }, { - "at": "2026-06-24T11:23:08.838Z", + "at": "2026-06-15T04:52:50.003Z", "from": "policy_checking", "to": "scope_checking", "node": "PolicyNode", "reason": "advance to ScopeNode" }, { - "at": "2026-06-24T11:23:08.838Z", + "at": "2026-06-15T04:52:50.003Z", "from": "scope_checking", "to": "executing", "node": "ScopeNode", "reason": "advance to ExecuteNode" }, { - "at": "2026-06-24T11:23:08.880Z", + "at": "2026-06-15T04:52:50.164Z", "from": "executing", "to": "verifying", "node": "ExecuteNode", "reason": "advance to VerifyNode" }, { - "at": "2026-06-24T11:23:08.881Z", + "at": "2026-06-15T04:52:50.164Z", "from": "verifying", "to": "waiting_for_human", "node": "VerifyNode", @@ -2433,7 +2426,7 @@ ], "artifacts": [ { - "createdAt": "2026-06-24T11:23:08.838Z", + "createdAt": "2026-06-15T04:52:50.003Z", "kind": "node_result", "node": "ObserveNode", "nodeType": "ObserveNode", @@ -2447,7 +2440,7 @@ } }, { - "createdAt": "2026-06-24T11:23:08.838Z", + "createdAt": "2026-06-15T04:52:50.003Z", "kind": "node_result", "node": "PlanNode", "nodeType": "PlanNode", @@ -2480,7 +2473,7 @@ } }, { - "createdAt": "2026-06-24T11:23:08.838Z", + "createdAt": "2026-06-15T04:52:50.003Z", "kind": "node_result", "node": "PolicyNode", "nodeType": "PolicyNode", @@ -2501,14 +2494,14 @@ { "name": "risk_tier", "status": "pass", - "message": "base=R2; declared=R2; effective=R2" + "message": "R2" } ] } } }, { - "createdAt": "2026-06-24T11:23:08.838Z", + "createdAt": "2026-06-15T04:52:50.003Z", "kind": "node_result", "node": "ScopeNode", "nodeType": "ScopeNode", @@ -2538,7 +2531,7 @@ } }, { - "createdAt": "2026-06-24T11:23:08.880Z", + "createdAt": "2026-06-15T04:52:50.164Z", "kind": "node_result", "node": "ExecuteNode", "nodeType": "ExecuteNode", @@ -2549,8 +2542,7 @@ "logs": [], "artifacts": [], "errors": [], - "warnings": [], - "transactionId": "txn:1782300188839:2f563147", + "transactionId": "txn:1781499170004:2f563147", "changedFiles": [], "applied": { "writeResults": [ @@ -2565,13 +2557,13 @@ "command": "node --check src/controller.mjs", "status": "failed", "stdout": "", - "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.verification_human_interrupt-7HeDIR\\src\\controller.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", + "stderr": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-controller.verification_human_interrupt-bvDdsu\\src\\controller.mjs:1\r\nexport const broken = ;\r\n ^\r\n\r\nSyntaxError: Unexpected token ';'\r\n at checkSyntax (node:internal/main/check_syntax:72:5)\r\n\r\nNode.js v24.15.0\r\n", "exitCode": 1, "error": "command exited with code 1" } ], "error": "verification command failed", - "durationMs": 41, + "durationMs": 158, "verification": { "verified": false, "checks": [ @@ -2600,7 +2592,7 @@ } }, { - "createdAt": "2026-06-24T11:23:08.881Z", + "createdAt": "2026-06-15T04:52:50.164Z", "kind": "node_result", "node": "VerifyNode", "nodeType": "VerifyNode", @@ -2634,7 +2626,7 @@ ], "approvalRequests": [ { - "id": "approval:df0a2552cfc8", + "id": "approval:629395d18b23", "taskId": "task:35fc503854cd", "node": "VerifyNode", "status": "pending", @@ -2647,25 +2639,25 @@ "reject", "cancel" ], - "createdAt": "2026-06-24T11:23:08.881Z", - "updatedAt": "2026-06-24T11:23:08.881Z" + "createdAt": "2026-06-15T04:52:50.164Z", + "updatedAt": "2026-06-15T04:52:50.164Z" } ], "budget": {}, "risk": { "riskTier": "R2" }, - "createdAt": "2026-06-24T11:23:08.838Z", - "updatedAt": "2026-06-24T11:23:08.881Z" + "createdAt": "2026-06-15T04:52:50.003Z", + "updatedAt": "2026-06-15T04:52:50.164Z" }, "trace": { "schemaVersion": 1, - "traceId": "audit:task:35fc503854cd:1782300188838", + "traceId": "audit:task:35fc503854cd:1781499170003", "taskId": "task:35fc503854cd", - "runId": "agent_run:7ac251f04f53", + "runId": "agent_run:4f72b86cbac8", "events": [ { - "at": "2026-06-24T11:23:08.838Z", + "at": "2026-06-15T04:52:50.003Z", "type": "state.created", "node": null, "state": "created", @@ -2673,7 +2665,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.838Z", + "at": "2026-06-15T04:52:50.003Z", "type": "node.start", "node": "ObserveNode", "state": "observing", @@ -2681,7 +2673,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.838Z", + "at": "2026-06-15T04:52:50.003Z", "type": "node.completed", "node": "ObserveNode", "state": "observing", @@ -2691,7 +2683,7 @@ } }, { - "at": "2026-06-24T11:23:08.838Z", + "at": "2026-06-15T04:52:50.003Z", "type": "node.start", "node": "PlanNode", "state": "planning", @@ -2699,7 +2691,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.838Z", + "at": "2026-06-15T04:52:50.003Z", "type": "node.completed", "node": "PlanNode", "state": "planning", @@ -2709,7 +2701,7 @@ } }, { - "at": "2026-06-24T11:23:08.838Z", + "at": "2026-06-15T04:52:50.003Z", "type": "node.start", "node": "PolicyNode", "state": "policy_checking", @@ -2717,7 +2709,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.838Z", + "at": "2026-06-15T04:52:50.003Z", "type": "node.completed", "node": "PolicyNode", "state": "policy_checking", @@ -2727,7 +2719,7 @@ } }, { - "at": "2026-06-24T11:23:08.838Z", + "at": "2026-06-15T04:52:50.003Z", "type": "node.start", "node": "ScopeNode", "state": "scope_checking", @@ -2735,7 +2727,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.838Z", + "at": "2026-06-15T04:52:50.003Z", "type": "node.completed", "node": "ScopeNode", "state": "scope_checking", @@ -2745,7 +2737,7 @@ } }, { - "at": "2026-06-24T11:23:08.838Z", + "at": "2026-06-15T04:52:50.003Z", "type": "node.start", "node": "ExecuteNode", "state": "executing", @@ -2753,7 +2745,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.880Z", + "at": "2026-06-15T04:52:50.164Z", "type": "node.completed", "node": "ExecuteNode", "state": "executing", @@ -2763,7 +2755,7 @@ } }, { - "at": "2026-06-24T11:23:08.881Z", + "at": "2026-06-15T04:52:50.164Z", "type": "node.start", "node": "VerifyNode", "state": "verifying", @@ -2771,7 +2763,7 @@ "data": {} }, { - "at": "2026-06-24T11:23:08.881Z", + "at": "2026-06-15T04:52:50.164Z", "type": "human.interrupt", "node": "VerifyNode", "state": "waiting_for_human", @@ -2780,12 +2772,12 @@ "reasons": [ "verification_failed" ], - "requestId": "approval:df0a2552cfc8" + "requestId": "approval:629395d18b23" } } ], - "createdAt": "2026-06-24T11:23:08.838Z", - "updatedAt": "2026-06-24T11:23:08.881Z" + "createdAt": "2026-06-15T04:52:50.003Z", + "updatedAt": "2026-06-15T04:52:50.164Z" }, "status": "waiting_for_human", "type": "run_agent_controller", @@ -2812,7 +2804,7 @@ "rollbackOk": false, "repairAttempted": false, "repairOk": false, - "durationMs": 82, + "durationMs": 180, "stepResults": [ { "actionId": "bench:plugin_process_isolation", @@ -2821,7 +2813,6 @@ "logs": [], "artifacts": [], "errors": [], - "warnings": [], "registry": { "ok": true, "decision": "allow", @@ -2855,12 +2846,12 @@ "required": false, "strategy": null }, - "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-zqxS18\\actions\\isolated_action", - "executeModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-zqxS18\\actions\\isolated_action\\execute.js", - "verifyModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-zqxS18\\actions\\isolated_action\\verify.js", + "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-R4RYHl\\actions\\isolated_action", + "executeModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-R4RYHl\\actions\\isolated_action\\execute.js", + "verifyModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-R4RYHl\\actions\\isolated_action\\verify.js", "rollbackModulePath": null, "handler": null, - "registeredAt": "2026-06-24T11:23:08.884Z", + "registeredAt": "2026-06-15T04:52:50.169Z", "metadata": {} }, "riskTier": "R1" @@ -2873,7 +2864,7 @@ "rejected": 0, "results": [ { - "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-zqxS18\\actions\\isolated_action", + "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-R4RYHl\\actions\\isolated_action", "ok": true, "decision": "loaded", "errors": [], @@ -2890,33 +2881,31 @@ }, "output": { "status": "succeeded", - "pid": 19840, + "pid": 17596, "hanakoChild": "1", "nodeOptions": null, - "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-zqxS18" + "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-R4RYHl" }, "pluginProcess": { "execute": { "status": "succeeded", "result": { "status": "succeeded", - "pid": 19840, + "pid": 17596, "hanakoChild": "1", "nodeOptions": null, - "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-zqxS18" + "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-R4RYHl" }, "exitCode": 0, "signal": null, "isolated": true, - "pid": 19840, - "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-zqxS18", - "workspaceRootImplicit": false, - "warnings": [], + "pid": 17596, + "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-R4RYHl", "stdout": "", "stderr": "", "stdoutTruncated": false, "stderrTruncated": false, - "durationMs": 39 + "durationMs": 110 } }, "verification": { @@ -2942,22 +2931,20 @@ "exitCode": 0, "signal": null, "isolated": true, - "pid": 22680, - "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-zqxS18", - "workspaceRootImplicit": false, - "warnings": [], + "pid": 19436, + "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-R4RYHl", "stdout": "", "stderr": "", "stdoutTruncated": false, "stderrTruncated": false, - "durationMs": 38 + "durationMs": 64 } } ], "required": true }, "rollback": null, - "durationMs": 80, + "durationMs": 177, "type": "execute_action", "name": "isolated_plugin_action" }, @@ -2981,7 +2968,7 @@ }, { "ok": true, - "actual": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-zqxS18", + "actual": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.process_isolation-R4RYHl", "type": "assert_last_result", "name": "assert_last_result" } @@ -3000,7 +2987,7 @@ "rollbackOk": true, "repairAttempted": false, "repairOk": false, - "durationMs": 121, + "durationMs": 151, "stepResults": [ { "actionId": "bench:plugin_rollback_on_verify_failure", @@ -3009,7 +2996,6 @@ "logs": [], "artifacts": [], "errors": [], - "warnings": [], "registry": { "ok": true, "decision": "allow", @@ -3043,12 +3029,12 @@ "required": true, "strategy": "plugin_rollback" }, - "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-OOZRcU\\actions\\write_then_fail", - "executeModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-OOZRcU\\actions\\write_then_fail\\execute.js", - "verifyModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-OOZRcU\\actions\\write_then_fail\\verify.js", - "rollbackModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-OOZRcU\\actions\\write_then_fail\\rollback.js", + "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-gVwBMZ\\actions\\write_then_fail", + "executeModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-gVwBMZ\\actions\\write_then_fail\\execute.js", + "verifyModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-gVwBMZ\\actions\\write_then_fail\\verify.js", + "rollbackModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-gVwBMZ\\actions\\write_then_fail\\rollback.js", "handler": null, - "registeredAt": "2026-06-24T11:23:08.966Z", + "registeredAt": "2026-06-15T04:52:50.349Z", "metadata": {} }, "riskTier": "R2" @@ -3061,7 +3047,7 @@ "rejected": 0, "results": [ { - "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-OOZRcU\\actions\\write_then_fail", + "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-gVwBMZ\\actions\\write_then_fail", "ok": true, "decision": "loaded", "errors": [], @@ -3088,15 +3074,13 @@ "exitCode": 0, "signal": null, "isolated": true, - "pid": 9784, - "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-OOZRcU", - "workspaceRootImplicit": false, - "warnings": [], + "pid": 17840, + "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-gVwBMZ", "stdout": "", "stderr": "", "stdoutTruncated": false, "stderrTruncated": false, - "durationMs": 40 + "durationMs": 53 } }, "error": "registered action verification failed", @@ -3125,15 +3109,13 @@ "exitCode": 0, "signal": null, "isolated": true, - "pid": 18380, - "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-OOZRcU", - "workspaceRootImplicit": false, - "warnings": [], + "pid": 17872, + "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-gVwBMZ", "stdout": "", "stderr": "", "stdoutTruncated": false, "stderrTruncated": false, - "durationMs": 37 + "durationMs": 47 } } ], @@ -3154,18 +3136,16 @@ "exitCode": 0, "signal": null, "isolated": true, - "pid": 23164, - "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-OOZRcU", - "workspaceRootImplicit": false, - "warnings": [], + "pid": 18288, + "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.rollback_on_verify_failure-gVwBMZ", "stdout": "", "stderr": "", "stdoutTruncated": false, "stderrTruncated": false, - "durationMs": 40 + "durationMs": 46 } }, - "durationMs": 119, + "durationMs": 147, "type": "execute_action", "name": "plugin_write_then_fail" }, @@ -3195,7 +3175,7 @@ "rollbackOk": false, "repairAttempted": false, "repairOk": false, - "durationMs": 116, + "durationMs": 126, "stepResults": [ { "actionId": "bench:plugin_verify_command_success", @@ -3204,7 +3184,6 @@ "logs": [], "artifacts": [], "errors": [], - "warnings": [], "registry": { "ok": true, "decision": "allow", @@ -3240,12 +3219,12 @@ "required": false, "strategy": null }, - "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-IKmvux\\actions\\verified_action", - "executeModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-IKmvux\\actions\\verified_action\\execute.js", - "verifyModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-IKmvux\\actions\\verified_action\\verify.js", + "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-QqzbeM\\actions\\verified_action", + "executeModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-QqzbeM\\actions\\verified_action\\execute.js", + "verifyModulePath": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-QqzbeM\\actions\\verified_action\\verify.js", "rollbackModulePath": null, "handler": null, - "registeredAt": "2026-06-24T11:23:09.087Z", + "registeredAt": "2026-06-15T04:52:50.500Z", "metadata": {} }, "riskTier": "R1" @@ -3258,7 +3237,7 @@ "rejected": 0, "results": [ { - "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-IKmvux\\actions\\verified_action", + "packageDir": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-QqzbeM\\actions\\verified_action", "ok": true, "decision": "loaded", "errors": [], @@ -3287,15 +3266,13 @@ "exitCode": 0, "signal": null, "isolated": true, - "pid": 6384, - "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-IKmvux", - "workspaceRootImplicit": false, - "warnings": [], + "pid": 23348, + "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-QqzbeM", "stdout": "", "stderr": "", "stdoutTruncated": false, "stderrTruncated": false, - "durationMs": 38 + "durationMs": 42 } }, "verification": { @@ -3323,15 +3300,13 @@ "exitCode": 0, "signal": null, "isolated": true, - "pid": 5960, - "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-IKmvux", - "workspaceRootImplicit": false, - "warnings": [], + "pid": 23032, + "cwd": "C:\\Users\\24089\\AppData\\Local\\Temp\\hanako-benchmark-plugin.verify_command_success-QqzbeM", "stdout": "", "stderr": "", "stdoutTruncated": false, "stderrTruncated": false, - "durationMs": 40 + "durationMs": 42 } }, { @@ -3352,7 +3327,7 @@ "required": true }, "rollback": null, - "durationMs": 113, + "durationMs": 123, "type": "execute_action", "name": "verified_plugin_action" }, @@ -3377,7 +3352,7 @@ "rollbackOk": false, "repairAttempted": false, "repairOk": false, - "durationMs": 38, + "durationMs": 39, "stepResults": [ { "status": "succeeded", @@ -3404,20 +3379,20 @@ "rollbackOk": false, "repairAttempted": false, "repairOk": false, - "durationMs": 16, + "durationMs": 3, "stepResults": [ { "schemaVersion": 1, - "generatedAt": "2026-06-24T11:23:09.254Z", - "projectRoot": "D:\\openhanako\\official-worktree\\Hanako-runtime-learner", + "generatedAt": "2026-06-15T04:52:50.664Z", + "projectRoot": "D:\\openhanako\\hanako-supplement\\self-evolve", "summary": { "status": "ready", "ok": true, - "version": "4.3.23", + "version": "4.3.2", "score": 100, - "passed": 16, + "passed": 15, "failed": 0, - "total": 16, + "total": 15, "failedChecks": [], "nextAction": "release can proceed after npm run check, npm test, and npm run benchmark pass" }, @@ -3426,9 +3401,9 @@ "id": "package.version_lts_format", "ok": true, "status": "passed", - "message": "package version is release-formatted: 4.3.23", + "message": "package version is release-formatted: 4.3.2", "details": { - "version": "4.3.23" + "version": "4.3.2" } }, { @@ -3437,9 +3412,9 @@ "status": "passed", "message": "package-lock root versions match package.json", "details": { - "packageVersion": "4.3.23", - "lockVersion": "4.3.23", - "rootPackageVersion": "4.3.23" + "packageVersion": "4.3.2", + "lockVersion": "4.3.2", + "rootPackageVersion": "4.3.2" } }, { @@ -3453,61 +3428,61 @@ { "path": "docs/ACTION_API.md", "exists": true, - "sizeBytes": 2613, + "sizeBytes": 3197, "ok": true }, { "path": "docs/POLICY.md", "exists": true, - "sizeBytes": 1364, + "sizeBytes": 2335, "ok": true }, { "path": "docs/TRANSACTION.md", "exists": true, - "sizeBytes": 933, + "sizeBytes": 1596, "ok": true }, { "path": "docs/SANDBOX.md", "exists": true, - "sizeBytes": 1057, + "sizeBytes": 2018, "ok": true }, { "path": "docs/SKILL_PROMOTION.md", "exists": true, - "sizeBytes": 1127, + "sizeBytes": 2302, "ok": true }, { "path": "docs/AUDIT.md", "exists": true, - "sizeBytes": 1137, + "sizeBytes": 1594, "ok": true }, { "path": "docs/BENCHMARKS.md", "exists": true, - "sizeBytes": 967, + "sizeBytes": 2238, "ok": true }, { "path": "docs/MIGRATION_v3_to_v4.md", "exists": true, - "sizeBytes": 1363, + "sizeBytes": 2191, "ok": true }, { "path": "docs/API_FREEZE.md", "exists": true, - "sizeBytes": 1606, + "sizeBytes": 1827, "ok": true }, { "path": "docs/DESIGN_GOAL_COMPLETION_MATRIX.md", "exists": true, - "sizeBytes": 981, + "sizeBytes": 3152, "ok": true } ], @@ -3518,18 +3493,18 @@ "id": "docs.acceptance_current_version", "ok": true, "status": "passed", - "message": "current acceptance report exists: docs/ACCEPTANCE-v4.3.23.md", + "message": "current acceptance report exists: docs/ACCEPTANCE-v4.3.2.md", "details": { - "acceptancePath": "docs/ACCEPTANCE-v4.3.23.md" + "acceptancePath": "docs/ACCEPTANCE-v4.3.2.md" } }, { "id": "docs.changelog_current_section", "ok": true, "status": "passed", - "message": "CHANGELOG has current section ## 4.3.23", + "message": "CHANGELOG has current section ## 4.3.2", "details": { - "heading": "## 4.3.23" + "heading": "## 4.3.2" } }, { @@ -3538,7 +3513,7 @@ "status": "passed", "message": "design goal matrix references current package version", "details": { - "version": "4.3.23" + "version": "4.3.2" } }, { @@ -3576,49 +3551,49 @@ "id": "readme.version_badge", "ok": true, "status": "passed", - "message": "README version badge matches package version: 4.3.23", + "message": "README version badge matches package version: 4.3.2", "details": { - "expected": "4.3.23", - "found": "4.3.23-lts" + "expected": "4.3.2", + "found": "4.3.2-lts" } }, { "id": "readme.test_badge", "ok": true, "status": "passed", - "message": "README test badge matches: 665/665", + "message": "README test badge matches: 515/515", "details": { - "expected": 665, - "found": 665 + "expected": 515, + "found": 515 } }, { "id": "readme.clone_branch", "ok": true, "status": "passed", - "message": "README fixed clone branch matches: v4.3.23", + "message": "README fixed clone branch matches: v4.3.2", "details": { - "expected": "v4.3.23", - "found": "v4.3.23" + "expected": "v4.3.2", + "found": "v4.3.2" } }, { "id": "manifest.version", "ok": true, "status": "passed", - "message": "manifest version matches package.json: 4.3.23", + "message": "manifest version matches package.json: 4.3.2", "details": { - "expected": "4.3.23", - "found": "4.3.23" + "expected": "4.3.2", + "found": "4.3.2" } }, { "id": "docs.api_freeze_version", "ok": true, "status": "passed", - "message": "API_FREEZE.md references current version or major.minor: 4.3.23", + "message": "API_FREEZE.md references current version or major.minor: 4.3.2", "details": { - "expected": "4.3.23", + "expected": "4.3.2", "majorMinor": "4.3" } }, @@ -3626,38 +3601,10 @@ "id": "readme.test_count_text", "ok": true, "status": "passed", - "message": "README test count text matches: 665", + "message": "README test count text matches: 515", "details": { - "expected": 665, - "found": 665 - } - }, - { - "id": "complexity.within_budget", - "ok": true, - "status": "passed", - "message": "complexity within budget: 185 files, max 648 LOC, 0 TODO/FIXME (3 soft warning(s))", - "details": { - "totals": { - "fileCount": 185, - "libModuleCount": 88, - "loc": 26920, - "codeLoc": 22461, - "imports": 857, - "exports": 443, - "todos": 0, - "maxLoc": 648, - "maxImports": 32, - "maxExports": 17 - }, - "violations": [], - "softWarningCount": 3, - "dirs": [ - "lib", - "scripts", - "tests", - "tools" - ] + "expected": 515, + "found": 515 } } ], @@ -3693,7 +3640,7 @@ "rollbackOk": false, "repairAttempted": true, "repairOk": true, - "durationMs": 78, + "durationMs": 79, "stepResults": [ { "actionId": "bench:repair_once_explicit_patch", @@ -3702,8 +3649,7 @@ "logs": [], "artifacts": [], "errors": [], - "warnings": [], - "transactionId": "txn:1782300189255:7b0daa0c", + "transactionId": "txn:1781499170665:7b0daa0c", "changedFiles": [ "src\\target.mjs" ], @@ -3808,7 +3754,6 @@ "logs": [], "artifacts": [], "errors": [], - "warnings": [], "diagnosis": "The failure is deterministic and should not be retried blindly.", "durationMs": 0, "verification": { @@ -3851,7 +3796,7 @@ "rollbackOk": false, "repairAttempted": false, "repairOk": false, - "durationMs": 1, + "durationMs": 0, "stepResults": [ { "actionId": "bench:split_context", @@ -3860,7 +3805,6 @@ "logs": [], "artifacts": [], "errors": [], - "warnings": [], "artifact": { "kind": "split_context", "content": "Section A: inspect runtime. Section B: inspect safety. Section C: inspect feedback. Section D: inspect audit. Section E: inspect benchmark. Section F: inspect rollback. Section G: inspect repair. Section H: inspect governance. Section I: inspect transfer. Section J: inspect controller. Section K: inspect registry. Section L: inspect docs.", @@ -3935,7 +3879,7 @@ "status": "pending" } ], - "createdAt": "2026-06-24T11:23:09.333Z" + "createdAt": "2026-06-15T04:52:50.744Z" } }, "before": { @@ -3945,7 +3889,7 @@ "chars": 340, "subtasks": 3 }, - "durationMs": 1, + "durationMs": 0, "verification": { "verified": true, "checks": [ @@ -3995,7 +3939,6 @@ "logs": [], "artifacts": [], "errors": [], - "warnings": [], "error": "scope gate rejected: file not in allowed scope: src/out-of-scope.js; scope_violation: src/out-of-scope.js outside explicit scope", "scopeGate": { "decision": "reject", @@ -4077,7 +4020,7 @@ "rollbackOk": true, "repairAttempted": false, "repairOk": false, - "durationMs": 48, + "durationMs": 40, "stepResults": [ { "actionId": "bench:rollback_failed_verification", @@ -4086,8 +4029,7 @@ "logs": [], "artifacts": [], "errors": [], - "warnings": [], - "transactionId": "txn:1782300189334:34461105", + "transactionId": "txn:1781499170746:34461105", "changedFiles": [], "applied": { "writeResults": [ @@ -4107,7 +4049,7 @@ "error": null } ], - "durationMs": 45, + "durationMs": 37, "verification": { "verified": false, "checks": [ @@ -4201,7 +4143,7 @@ "rollbackOk": false, "repairAttempted": false, "repairOk": false, - "durationMs": 12, + "durationMs": 10, "stepResults": [ { "ok": true, @@ -4251,10 +4193,10 @@ }, "tokenCost": 0, "status": "staged", - "createdAt": "2026-06-24T11:23:09.385Z", - "updatedAt": "2026-06-24T11:23:09.385Z", + "createdAt": "2026-06-15T04:52:50.788Z", + "updatedAt": "2026-06-15T04:52:50.788Z", "decayed": false, - "stagedAt": "2026-06-24T11:23:09.385Z" + "stagedAt": "2026-06-15T04:52:50.788Z" } ], "activeSkills": [], @@ -4335,11 +4277,11 @@ }, "tokenCost": 0, "status": "active", - "createdAt": "2026-06-24T11:23:09.385Z", - "updatedAt": "2026-06-24T11:23:09.393Z", + "createdAt": "2026-06-15T04:52:50.788Z", + "updatedAt": "2026-06-15T04:52:50.796Z", "decayed": false, - "stagedAt": "2026-06-24T11:23:09.385Z", - "activatedAt": "2026-06-24T11:23:09.393Z" + "stagedAt": "2026-06-15T04:52:50.788Z", + "activatedAt": "2026-06-15T04:52:50.796Z" } ], "activeSkills": [ @@ -4371,7 +4313,7 @@ "tokenCost": 0, "status": "active", "source": "reflexion_cluster", - "activatedAt": "2026-06-24T11:23:09.393Z" + "activatedAt": "2026-06-15T04:52:50.796Z" } ], "events": [ @@ -4433,7 +4375,7 @@ "rollbackOk": false, "repairAttempted": false, "repairOk": false, - "durationMs": 42, + "durationMs": 41, "stepResults": [ { "ok": true, @@ -4474,8 +4416,8 @@ "latestValidationStatus": "passed", "manualPromotionEligible": true, "autoPromotionBlocked": true, - "createdAt": "2026-06-24T11:23:09.396Z", - "updatedAt": "2026-06-24T11:23:09.436Z" + "createdAt": "2026-06-15T04:52:50.798Z", + "updatedAt": "2026-06-15T04:52:50.837Z" }, "type": "transfer_validate", "name": "validate_transfer_candidate" @@ -4504,16 +4446,16 @@ "false_auto_apply_rate": 0, "manual_escalation_rate": 0.11764705882352941, "token_overhead": null, - "latency_overhead": 43.76470588235294, + "latency_overhead": 64, "skill_effectiveness": null }, "corpus": { - "root": "D:\\openhanako\\official-worktree\\Hanako-runtime-learner\\benchmarks", + "root": "D:\\openhanako\\hanako-supplement\\self-evolve\\benchmarks", "scenarioCount": 17, "selectedScenarioCount": 17 }, "baseline": { - "path": "D:\\openhanako\\official-worktree\\Hanako-runtime-learner\\benchmarks\\baseline-v4.0.9.json", + "path": "D:\\openhanako\\hanako-supplement\\self-evolve\\benchmarks\\baseline-v4.0.9.json", "metrics": { "task_success_rate": 1, "auto_execution_success_rate": 1, diff --git a/benchmark-results/benchmark-report.md b/benchmark-results/benchmark-report.md index fcc223d..6c0381e 100644 --- a/benchmark-results/benchmark-report.md +++ b/benchmark-results/benchmark-report.md @@ -1,6 +1,6 @@ # Benchmark Report -Generated at: 2026-06-24T11:23:09.438Z +Generated at: 2026-06-15T04:52:50.839Z Scenarios: 17 Status: passed @@ -16,7 +16,7 @@ Status: passed | false_auto_apply_rate | 0.0000 | | manual_escalation_rate | 0.1176 | | token_overhead | null | -| latency_overhead | 43.7647 | +| latency_overhead | 64.0000 | | skill_effectiveness | null | ## Scenario Results diff --git a/docs/ACCEPTANCE-v4.3.23.md b/docs/ACCEPTANCE-v4.3.23.md deleted file mode 100644 index 3e66959..0000000 --- a/docs/ACCEPTANCE-v4.3.23.md +++ /dev/null @@ -1,38 +0,0 @@ -# v4.3.23 验收记录 - -## 版本目标 - -`v4.3.23` 在 `v4.3.22` 自学习控制台基础上,补齐 v4.x LTS 的复杂度治理门:把维护期预算写成可机读规则、发布门检查和可审计文档,同时拆分控制面热点文件,降低后续维护风险。该版本不新增自动化能力。 - -## 实现 - -- `lib/complexity.js`:定义复杂度扫描、hard limit、soft target、报告构建和预算判定。 -- `scripts/complexity-check.js` / `scripts/complexity-report.js`:新增本地门禁与报告生成脚本,均只依赖 Node 内置模块。 -- `lib/release-readiness.js`:发布门新增 `complexity.within_budget` 检查,默认测试基线同步到 665。 -- `docs/COMPLEXITY_BUDGET.md`:记录 v4.x LTS 复杂度预算、模块新增规则和预算调整流程。 -- `docs/COMPLEXITY_DEBT.md` / `docs/COMPLEXITY_REPORT.md`:登记 soft target 债务并生成当前复杂度快照。 -- `tools/control-parameters.js`、`tools/control-summaries.js`、`tools/control-handlers/*`:从 `tools/control.js` 抽出参数解析、摘要格式化和部分控制面 handler,保留行为特征回归覆盖。 - -## 验收结果 - -| 项目 | 结果 | -|---|---| -| `npm run check` | 通过 | -| `npm test` | 665 个测试,660 通过,5 跳过 | -| `npm run benchmark` | 17/17 通过 | -| `npm run perf` | 通过,无阈值越界 | -| `npm run complexity:check` | 通过,0 hard violations | -| `npm run release:check` | Score 100 | - -## 本版确认项 - -1. 复杂度治理扫描范围限定为 `lib/`、`scripts/`、`tests/`、`tools/` 下的 JS/CJS/MJS 文件。 -2. hard limit 超出会阻断 `complexity:check` 和 `release:check`;soft target 只登记债务,不阻断发布。 -3. `docs/COMPLEXITY_BUDGET.md` 与 `lib/complexity.js` 中的预算数值保持一致。 -4. `tools/control.js` 已拆分到 602 LOC,仍仅略高于 600 LOC soft target,登记在复杂度债务中。 -5. README、INSTALL、CHANGELOG、ARCHITECTURE、设计目标矩阵、package/manifest/lockfile 版本均同步到 `4.3.23`。 -6. 自动化边界未放宽:新增的是本地静态治理与文档/测试,不执行 `git tag`、`git push`、`npm publish` 或外部副作用。 - -## 结论 - -`v4.3.23` 满足当前 release gate,作为 v4.x LTS 的复杂度治理与发布门加固版本发布。 diff --git a/docs/COMPLEXITY_REPORT.md b/docs/COMPLEXITY_REPORT.md index 8843a31..603e107 100644 --- a/docs/COMPLEXITY_REPORT.md +++ b/docs/COMPLEXITY_REPORT.md @@ -3,7 +3,7 @@ > 自动生成,请勿手工编辑。运行 `npm run complexity:report` 刷新。 > 预算与规则见 [COMPLEXITY_BUDGET.md](COMPLEXITY_BUDGET.md),债务清单见 [COMPLEXITY_DEBT.md](COMPLEXITY_DEBT.md)。 -Generated at: 2026-06-24T11:22:40.919Z +Generated at: 2026-06-24T11:43:28.674Z Scan scope: lib, scripts, tests, tools Status: within budget diff --git a/docs/DESIGN_GOAL_COMPLETION_MATRIX.md b/docs/DESIGN_GOAL_COMPLETION_MATRIX.md index 69889cc..73a9866 100644 --- a/docs/DESIGN_GOAL_COMPLETION_MATRIX.md +++ b/docs/DESIGN_GOAL_COMPLETION_MATRIX.md @@ -1,6 +1,6 @@ # 设计目标完成矩阵 -当前版本:`v4.3.23` +当前版本:`v4.3.22` ## 总体状态 @@ -18,7 +18,7 @@ | 项目 | 预期 | |---|---| | `npm run check` | 通过 | -| `npm test` | 665 个测试,660 通过,5 跳过 | +| `npm test` | 570 个测试,565 通过,5 跳过 | | `npm run benchmark` | 17/17 通过 | | `npm run perf` | 无阈值越界 | | `npm run release:check` | Score 100 | diff --git a/manifest.json b/manifest.json index 9f6755a..82b28f1 100644 --- a/manifest.json +++ b/manifest.json @@ -2,7 +2,7 @@ "manifestVersion": 1, "id": "hanako-runtime-learner", "name": "Runtime Self-Learning", - "version": "4.3.23", + "version": "4.3.22", "description": "运行时自学习引擎:观察本地交互,归纳重复工作流、偏好与错误,并生成保守的经验提示。", "author": "Sun", "trust": "full-access", diff --git a/package-lock.json b/package-lock.json index 3b17763..0bffa57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "hanako-runtime-learner", - "version": "4.3.23", + "version": "4.3.22", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "hanako-runtime-learner", - "version": "4.3.23", + "version": "4.3.22", "license": "MIT" } } diff --git a/package.json b/package.json index d3b5d78..06485d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hanako-runtime-learner", - "version": "4.3.23", + "version": "4.3.22", "description": "Runtime self-learning engine for Hanako: observe local interaction patterns, learn repeated workflows and errors, inject conservative skill hints.", "author": "Sun", "license": "MIT", From 6d65a499123b36feb95cecdb5b7093952363a966 Mon Sep 17 00:00:00 2001 From: Hanako Maintainer Date: Wed, 24 Jun 2026 20:11:56 +0800 Subject: [PATCH 4/4] chore: release v4.3.23 --- CHANGELOG.md | 8 +++++ INSTALL.md | 4 +-- README.md | 12 +++---- docs/ACCEPTANCE-v4.3.23.md | 49 +++++++++++++++++++++++++++ docs/DESIGN_GOAL_COMPLETION_MATRIX.md | 4 +-- manifest.json | 2 +- package-lock.json | 4 +-- package.json | 2 +- 8 files changed, 71 insertions(+), 14 deletions(-) create mode 100644 docs/ACCEPTANCE-v4.3.23.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 7360808..8a2d532 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ 本文档记录 Runtime Self-Learning 的版本演进。`v4.x` 为 LTS 维护线,因此该阶段的记录重点放在缺陷修复、审计加固、性能整理和发布治理,不再扩张自动化边界。 +## 4.3.23 + +- 新增复杂度治理门:`lib/complexity.js` 定义 hard limit / soft target,`npm run complexity:check` 在超出 hard limit 时失败,`npm run complexity:report` 生成 `docs/COMPLEXITY_REPORT.md`。 +- 发布门纳入 `complexity.within_budget` 检查,并补充 `docs/COMPLEXITY_BUDGET.md` / `docs/COMPLEXITY_DEBT.md`,把 v4.x LTS 的模块数、单文件 LOC、import/export 数和 TODO/FIXME 预算写成可审计规则。 +- 拆分控制面热点复杂度:新增 `tools/control-parameters.js`、`tools/control-summaries.js` 与 `tools/control-handlers/*`,让 `tools/control.js` 从 712 LOC 收敛到约 602 LOC,并保留行为特征回归。 +- 测试总数 `606 -> 665`:新增控制面参数、摘要、脱敏、handler characterization 与 release-readiness 复杂度门回归;README 徽章和发布门默认测试基线同步到 665。 +- 边界未放宽:本版只增加本地静态治理、文档和控制面结构整理,无新增自动放行、网络、发布或外部副作用能力。 + ## 4.3.22 - **新增自学习控制台(`chat.surface`,Hanako v0.344+)**:新增只读工具 `self_learning_console`,把"最近活动 + 待处理提案"快照投递进一条插件自有的 `plugin_private` 会话,并以原生 `chat.surface` transcript 卡片在当前聊天内嵌展示,可点开滚动查看历史快照。这是 UX/呈现层的可选增强,**不扩张自动化边界**:控台只读、由用户显式调用工具触发,不自动应用任何动作、不在后台主动推送。 diff --git a/INSTALL.md b/INSTALL.md index 7747566..5b29f8d 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -50,10 +50,10 @@ OK skills/self-learning/SKILL.md ## 固定版本安装 -如需固定到某个 release,例如 `v4.3.22`: +如需固定到某个 release,例如 `v4.3.23`: ```powershell -git clone --branch v4.3.22 https://github.com/326sun/Hanako-runtime-learner.git +git clone --branch v4.3.23 https://github.com/326sun/Hanako-runtime-learner.git cd Hanako-runtime-learner npm run install-plugin ``` diff --git a/README.md b/README.md index 720351a..4f9086d 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

- version + version CI license platform @@ -60,7 +60,7 @@ npm run install-plugin 固定版本安装: ```powershell -git clone --branch v4.3.22 https://github.com/326sun/Hanako-runtime-learner.git +git clone --branch v4.3.23 https://github.com/326sun/Hanako-runtime-learner.git cd Hanako-runtime-learner npm run install-plugin ``` @@ -210,12 +210,12 @@ npm run perf -- --json npm run release:check ``` -`v4.3.22` 的预期结果: +`v4.3.23` 的预期结果: ```text -package version: 4.3.22 +package version: 4.3.23 npm run check: passed -npm test: 665 tests, 660 passed, 5 skipped +npm test: 665 tests, 665 passed, 0 skipped npm run benchmark: passed, 17 scenarios npm run perf: passed, no threshold breaches npm run release:check: Score 100 @@ -240,7 +240,7 @@ npm run release:check: Score 100 | [docs/MIGRATION_v3_to_v4.md](docs/MIGRATION_v3_to_v4.md) | v3 到 v4 迁移说明。 | | [docs/LTS_MAINTENANCE_PLAN.md](docs/LTS_MAINTENANCE_PLAN.md) | v4.x LTS 维护策略。 | | [docs/DESIGN_GOAL_COMPLETION_MATRIX.md](docs/DESIGN_GOAL_COMPLETION_MATRIX.md) | 设计目标完成矩阵。 | -| [docs/ACCEPTANCE-v4.3.22.md](docs/ACCEPTANCE-v4.3.22.md) | 当前版本验收记录。 | +| [docs/ACCEPTANCE-v4.3.23.md](docs/ACCEPTANCE-v4.3.23.md) | 当前版本验收记录。 | | [CHANGELOG.md](CHANGELOG.md) | 版本历史。 | ## 许可证 diff --git a/docs/ACCEPTANCE-v4.3.23.md b/docs/ACCEPTANCE-v4.3.23.md new file mode 100644 index 0000000..8aefc92 --- /dev/null +++ b/docs/ACCEPTANCE-v4.3.23.md @@ -0,0 +1,49 @@ +# v4.3.23 验收记录 + +## 版本目标 + +`v4.3.23` 在 `v4.3.22` 的自学习控制台版本基础上,引入 v4.x LTS 的复杂度治理基础设施,并完成 C-001(`tools/control.js`)低风险拆分。本版目标是让复杂度预算、复杂度债务和发布门形成可审计闭环,同时不放宽运行时安全边界。 + +## 实现范围 + +- 新增 `lib/complexity.js` 作为复杂度预算单一事实源,扫描 `lib/`、`scripts/`、`tests/`、`tools/` 的 LOC、import/export 数和 TODO/FIXME 标记。 +- 新增 `scripts/complexity-check.js` 与 `scripts/complexity-report.js`,分别提供 hard-limit 门禁与 `docs/COMPLEXITY_REPORT.md` 报告生成。 +- 新增 `docs/COMPLEXITY_BUDGET.md` 与 `docs/COMPLEXITY_DEBT.md`,把 v4.x LTS 的复杂度预算、软警告和 deferred 项写入文档。 +- `lib/release-readiness.js` 新增 `complexity.within_budget` 检查项,作为发布就绪度的一部分,保持本地 in-process 检查,无新增外部副作用。 +- C-001 低风险拆分:抽出 `tools/control-parameters.js`、`tools/control-summaries.js`、`tools/control-handlers/skill-policy.js`、`tools/control-handlers/events.js`,降低 `tools/control.js` 热点复杂度。 +- C-004 公共面收敛:`lib/json-io.js` 对外导出面从 17 收敛到 15。 +- 新增 characterization tests,覆盖 control parameters、summaries、redaction、handlers 与 release-readiness complexity gate。 + +## 边界确认 + +| 项目 | 结论 | +|---|---| +| 运行时依赖 | 未新增 | +| 安全策略 | 未放宽 | +| `execute` / `sessionPermission` / `*_ACTIONS` / `describeControlSideEffect` | 未改语义边界 | +| 自动放行 / 网络 / 发布 / 外部副作用能力 | 未新增 | +| 高风险 control handler 迁移 | deferred,不纳入本版 | + +## 验收结果 + +| 项目 | 结果 | +|---|---| +| `npm run check` | 通过 | +| `npm test` | 665 个测试,665 通过,0 跳过 | +| `npm run complexity:check` | 通过,0 violation,3 soft warning | +| `npm run complexity:report` | 通过,生成 `docs/COMPLEXITY_REPORT.md` | +| `npm run release:check` | Score 100 | +| `npm run benchmark` | 17/17 通过 | +| `npm run perf` | 通过,无阈值越界 | + +## 本版确认项 + +1. 复杂度预算成为发布门的一部分,`complexity.within_budget` 可在 `release:check` 中审计。 +2. 复杂度治理只读取本地源码并生成本地报告,不引入网络、凭证、外部写入或发布动作。 +3. `tools/control.js` 的低风险拆分保持参数 schema、摘要输出、脱敏行为和 handler 行为回归稳定。 +4. `docs/COMPLEXITY_DEBT.md` 明确记录 C-001 中高风险/低收益部分为 deferred,避免为了追求 LOC 指标扩大重构风险。 +5. README、CHANGELOG、设计矩阵、package/manifest/package-lock 版本号同步至 `4.3.23`。 + +## 结论 + +`v4.3.23` 满足当前 release gate,可作为 v4.x LTS 的复杂度治理正式版准备发布。实际 tag、GitHub Release 与合并动作仍应由维护者在 PR 审查通过后显式执行。 diff --git a/docs/DESIGN_GOAL_COMPLETION_MATRIX.md b/docs/DESIGN_GOAL_COMPLETION_MATRIX.md index 73a9866..0815d6c 100644 --- a/docs/DESIGN_GOAL_COMPLETION_MATRIX.md +++ b/docs/DESIGN_GOAL_COMPLETION_MATRIX.md @@ -1,6 +1,6 @@ # 设计目标完成矩阵 -当前版本:`v4.3.22` +当前版本:`v4.3.23` ## 总体状态 @@ -18,7 +18,7 @@ | 项目 | 预期 | |---|---| | `npm run check` | 通过 | -| `npm test` | 570 个测试,565 通过,5 跳过 | +| `npm test` | 665 个测试,665 通过,0 跳过 | | `npm run benchmark` | 17/17 通过 | | `npm run perf` | 无阈值越界 | | `npm run release:check` | Score 100 | diff --git a/manifest.json b/manifest.json index 82b28f1..9f6755a 100644 --- a/manifest.json +++ b/manifest.json @@ -2,7 +2,7 @@ "manifestVersion": 1, "id": "hanako-runtime-learner", "name": "Runtime Self-Learning", - "version": "4.3.22", + "version": "4.3.23", "description": "运行时自学习引擎:观察本地交互,归纳重复工作流、偏好与错误,并生成保守的经验提示。", "author": "Sun", "trust": "full-access", diff --git a/package-lock.json b/package-lock.json index 0bffa57..3b17763 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "hanako-runtime-learner", - "version": "4.3.22", + "version": "4.3.23", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "hanako-runtime-learner", - "version": "4.3.22", + "version": "4.3.23", "license": "MIT" } } diff --git a/package.json b/package.json index 06485d0..d3b5d78 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hanako-runtime-learner", - "version": "4.3.22", + "version": "4.3.23", "description": "Runtime self-learning engine for Hanako: observe local interaction patterns, learn repeated workflows and errors, inject conservative skill hints.", "author": "Sun", "license": "MIT",