From 56e8aac8597db8ccb76ce7707f664417933abfce Mon Sep 17 00:00:00 2001 From: Eli Ma Date: Wed, 23 Sep 2026 19:23:46 +0800 Subject: [PATCH 1/2] feat(hooks): decide AgentTraces state in session_capture Keep the stored session state and checkpoint class on one pure match so later capture work does not grow a second kind list in the ingest path. Signed-off-by: Eli Ma --- Cargo.lock | 2 +- Cargo.toml | 2 +- docs/development/internal/session-capture.md | 18 ++ docs/development/plan/plan-20260925.md | 76 +++--- docs/development/plan/plan-long.md | 2 +- docs/development/plan/plan-status.md | 9 +- install.ps1 | 2 +- install.sh | 2 +- src/internal/ai/hooks/mod.rs | 2 + src/internal/ai/hooks/runtime.rs | 34 +-- src/internal/ai/hooks/session_capture.rs | 232 +++++++++++++++++++ 11 files changed, 315 insertions(+), 66 deletions(-) create mode 100644 docs/development/internal/session-capture.md create mode 100644 src/internal/ai/hooks/session_capture.rs diff --git a/Cargo.lock b/Cargo.lock index 5d0790ac2..ca926ac37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3783,7 +3783,7 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libra" -version = "0.23.51" +version = "0.23.52" dependencies = [ "anyhow", "assert_cmd", diff --git a/Cargo.toml b/Cargo.toml index 3db2f1c9c..e14e9799e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libra" -version = "0.23.51" +version = "0.23.52" edition = "2024" license = "MIT" description = "AI agent-native version control system with Git on-disk compatibility, SQLite-backed metadata, and tiered cloud storage" diff --git a/docs/development/internal/session-capture.md b/docs/development/internal/session-capture.md new file mode 100644 index 000000000..4c48e7bd1 --- /dev/null +++ b/docs/development/internal/session-capture.md @@ -0,0 +1,18 @@ +# Session Capture decision + +AgentTraces ingest (`ingest_agent_traces_payload_with_scope`) asks +`session_capture::decide` for two facts: the SQLite `agent_session.state` +string, and whether the existing committed or subagent checkpoint writer +should run. `decide` is a pure match over `LifecycleEventKind`. It does not +open a repository, read a transcript, or call `transition_phase`. + +`repo_path == None` still skips checkpoint writes after the decision returns +`Committed` or `SubagentBoundary`. Owner filtering for `SessionStart` and +`TurnStart`, and the `stopped_at` column, stay in `runtime.rs`. Import, +`libra agent session` stop/resume, and the coverage gate write state on +their own paths and do not call `decide`. + +AgentTraces 的 session_state 与 checkpoint 类别只来自 session_capture::decide。 +transition_phase 只服务 HookTarget::AiIntent 的 session_phase,本文件不修改它。 +decide 不读取 SessionPhase,也不复制 Entire 的 git session 存储。 +本计划不编辑 docs/development/tracing/agent.md。 diff --git a/docs/development/plan/plan-20260925.md b/docs/development/plan/plan-20260925.md index f81d5eb8e..b05ae6e8a 100644 --- a/docs/development/plan/plan-20260925.md +++ b/docs/development/plan/plan-20260925.md @@ -8,7 +8,7 @@ > > **计划执行登记:** 成稿提交同步 [`plan-status.md`](plan-status.md) 与 [`plan-long.md`](plan-long.md)。卡推进时在同一变更中更新 `plan-status.md`。该登记是协调写集,不计入任务卡行为落点。SCAP-02 允许改 `plan-status.md` 里本计划自己的现状单元格与第四节指针,不得改写其它计划的段落。 > -> **开工门:** 任一卡在 Codex review log 与 Claude review log 的最新一轮对**同一修订版**均为字面 `VERDICT: PASS`,且该轮 P0/P1/P2 均为 0 之前,不得标 `in-progress`。R7 的 PASS 不覆盖 2026-09-23 删除 Hermes 的修订。 +> **开工门:** 任一卡在 Codex review log 与 Claude review log 的最新一轮对**同一修订版**均为字面 `VERDICT: PASS`,且该轮 P0/P1/P2 均为 0 之前,不得标 `in-progress`。R9 覆盖删除 Hermes 与 SCAP-02 改为核对已写入行。证据 `/tmp/scap-exec/codex-r9.txt`、`/tmp/scap-exec/claude-r9.log`。 ## 文档职责 @@ -54,18 +54,18 @@ | Entire owner bypass | `SessionStart` 与 `TurnStart` 不做 owner 过滤 | `cmd/entire/cli/lifecycle.go:37-47`、`:87` | | Libra 事件平面 | `LifecycleEventKind` 有 13 个变体,`#[non_exhaustive]`,新变体必须追加。`HookProvider::parse_hook_event` 是解析入口 | `src/internal/ai/hooks/lifecycle.rs:41-60`;`src/internal/ai/hooks/provider.rs:116-128` | | AgentTraces ingest | `HookTarget::AgentTraces` 进入 `ingest_agent_traces`,再进入 `ingest_agent_traces_payload_with_scope` | `src/internal/ai/hooks/runtime.rs:186-198`、`:521-529` | -| SQLite state 字符串 | `SessionStart→active`,`SessionEnd→stopped`,`Compaction→condensed`,`CompactionCompleted→active`,其余→`active` | `src/internal/ai/hooks/runtime.rs:750-756` | -| checkpoint 进入条件 | 仅 `SessionEnd`、`TurnEnd`、`SubagentStart`、`SubagentEnd`,且 `repo_path` 为 `Some` | `src/internal/ai/hooks/runtime.rs:928-935` | -| checkpoint 分流 | 后两者调 `write_subagent_checkpoint`;前两者走 else,并调用 `write_committed_checkpoint` | `src/internal/ai/hooks/runtime.rs:1008-1023`、`:1086` | +| SQLite state 字符串 | `decide(event.kind).session_state`。表仍是 `SessionStart→active`,`SessionEnd→stopped`,`Compaction→condensed`,`CompactionCompleted→active`,其余→`active` | `src/internal/ai/hooks/runtime.rs:751`;`src/internal/ai/hooks/session_capture.rs` | +| checkpoint 进入条件 | `decide` 的 checkpoint 不是 `None`,且 `repo_path` 为 `Some`。类别仍只覆盖 `SessionEnd`、`TurnEnd`、`SubagentStart`、`SubagentEnd` | `src/internal/ai/hooks/runtime.rs:923-924` | +| checkpoint 分流 | `SubagentBoundary` 调 `write_subagent_checkpoint`;`Committed` 走 else,并调用 `write_committed_checkpoint` | `src/internal/ai/hooks/runtime.rs:997`、`:1074` | | owner 豁免 | `SessionStart` 与 `TurnStart` 不查询 owner。其它 kind 在 owner 不一致时跳过 | `src/internal/ai/hooks/runtime.rs:692-712`、`:730-747` | -| stopped_at | INSERT 值只在 `SessionEnd` 时为 `now`。UPDATE 在 `excluded.state = 'stopped'` 时写下 | `src/internal/ai/hooks/runtime.rs:863-864`、`:839-840` | +| stopped_at | INSERT 值只在 `SessionEnd` 时为 `now`。UPDATE 在 `excluded.state = 'stopped'` 时写下 | `src/internal/ai/hooks/runtime.rs:858-859`、`:834-835` | | 其它 state 写入 | `libra agent session` stop/resume 直接 UPDATE。import 与 coverage gate 另有写入。它们不调用上面的 ingest | `src/command/agent/session.rs:597-611`;`src/internal/ai/agent_import.rs:813-815`;`src/internal/ai/coverage_gate.rs:926-932` | | state 读者 | status 按 `active`/`stopped`/其它计数。clean 按 `stopped` 选择 | `src/command/agent/status.rs:45-57`;`src/command/agent/clean.rs:465-469` | | AiIntent 相位 | `transition_phase` 使用 Active/Stopped/Ended。`TurnEnd` 在这里是 Stopped,在 SQLite 列是 `active`。AgentTraces ingest 不调用它 | `src/internal/ai/hooks/runtime.rs:93-102`、`:348`、`:3050-3076` | -| 过时注释 | `process_hook_event_with_target` 仍写 AgentTraces 只在 SessionEnd 写 checkpoint | `src/internal/ai/hooks/runtime.rs:186-189` | -| 模块声明位置 | hook 子模块在 `hooks/mod.rs`。`src/internal/ai/mod.rs` 由 plan-20260819 的 memory 卡占用,本计划不改 | `src/internal/ai/hooks/mod.rs:25-32` | +| 过时注释 | `process_hook_event_with_target` 的文档改为 checkpoint 类别来自 `decide`,覆盖 TurnEnd、SessionEnd、SubagentStart、SubagentEnd | `src/internal/ai/hooks/runtime.rs:186-190` | +| 模块声明位置 | `hooks/mod.rs` 声明 `mod session_capture`。`src/internal/ai/mod.rs` 由 plan-20260819 的 memory 卡占用,本计划不改 | `src/internal/ai/hooks/mod.rs:33` | | AgentKind | 七个变体。没有 Pi。Pi 由 `plan-20260911` 增加 | `src/internal/ai/observed_agents/adapter.rs:30-38` | -| 版本与发布 | `Cargo.toml` version `0.23.50`。`release.yml` jobs:`build-and-upload`、`upload-install-scripts`、`update-homebrew-tap`、`verify-homebrew-formula`、`request-stable-manifest` | `Cargo.toml:3`;`.github/workflows/release.yml:15`、`:310`、`:480`、`:628`、`:690` | +| 版本与发布 | `Cargo.toml` version `0.23.52`(本卡 `patch + 1`,自已发布的 `0.23.51`)。`release.yml` jobs:`build-and-upload`、`upload-install-scripts`、`update-homebrew-tap`、`verify-homebrew-formula`、`request-stable-manifest` | `Cargo.toml:3`;`.github/workflows/release.yml:15`、`:310`、`:480`、`:628`、`:690` | | 兄弟计划 | OG-00..15、CX/RG、CC-00..06、PI-01..06 均未开工 | `plan-status.md` 计划一览 | ### Entire 中层与 Libra 的对应 @@ -91,7 +91,7 @@ Libra 的 AgentTraces 路径已有事件平面和写入函数。缺的是决策 | `plan-long.md` | 日期索引 | 成稿登记一行。不改 LR/MEM 状态 | | `plan-20260906.md` | 已占用 `SC-*` / `DEP-SC-01` | 本计划用 `SCAP-*`,不改那份计划 | | `plan-20260819.md` | 进行中,写 `src/internal/ai/mod.rs` | 本计划不改该文件 | -| `plan-20260902.md` | OpenCode 采集 | SCAP-02 只追加关系表与修订历史 | +| `plan-20260902.md` | OpenCode 采集 | SCAP-02 核对已写入的关系表与修订历史。缺行才补,不追加第二份 | | `plan-20260904.md` | Codex / reasoning | 同上 | | `plan-20260905.md` | Claude Code hooks | 同上 | | `plan-20260911.md` | Pi capture | 同上。PI-01 的 migration 不在 DEP-SCAP-03 名单里 | @@ -128,6 +128,7 @@ SCAP-02 追加的行不改兄弟计划的任务卡正文,也不代替它们已 | 2026-09-23 | Codex R4 FAIL(P1×1,P2×1);Claude R4 FAIL(P2×2) | D 组脚本加上 `set -euo pipefail`,并对 release.yml 与 codeql.yml 各轮询最多 60 次。失败的 `jq -e` 会在 CodeQL 检查之前退出 | 无 | SCAP-01 D 组、review log | | 2026-09-23 | 用户指示:忽略 Hermes | 删除 `DEFER-SCAP-01`、`DEP-SCAP-02`、`GAP-SCAP-04`。这三个 ID 废弃,不再复用。R7 双 PASS 不覆盖本行 | 无 | 成功定义、依赖表、延后项、plan-status、plan-long | | 2026-09-23 | 用户指示:关联四份采集计划并对齐 Entire | 把前置句写入 plan-20260902/04/05/11,并把 B3-00 互斥写入 plan-20260907。关系表第二格改为 `DEP-SCAP-01/03/04`,第三格仍是前置句。功能对齐节说明 `Transition` 不写 checkpoint | 无 | 四份采集计划、plan-20260907、本文件 AC、plan-status | +| 2026-09-23 | Codex R8 FAIL(P1×1);Claude R8 PASS | SCAP-02 改为核对 `fba6cbc` 已写入的行。缺行才补,不追加第二份 | 无 | SCAP-02、Phase 0 | ## 已决议设计决策 @@ -193,7 +194,7 @@ upsert 使用 `session_state`。checkpoint 块在 `checkpoint != None` 且 `repo - **Context:** 多数采集卡不改这张表。另一些卡的写集含 `runtime.rs`,即使不改表也不能和 SCAP-01 并发。 - **Decision:** `DEP-SCAP-01` 只阻塞「修改 AgentTraces ingest 的 state 字符串或 checkpoint 类别」的卡。`DEP-SCAP-03` 阻塞写集含 `src/internal/ai/hooks/runtime.rs`、且尚未开工的具名卡,直到 SCAP-01 `Lifecycle=done` 且 `Acceptance=complete`。名单:OG-11、OG-05、CX-06、CX-28、CX-02、CX-07、CX-19、CX-09、CX-26、CC-02、CC-05、PI-04。`DEP-SCAP-04` 是与已可开工的 `plan-20260907` B3-00 的互斥:双方都写 `runtime.rs`,不得同时 `in-progress`。先完成的一方之后,另一方再改该文件。PI-01 的 migration 不在这两份名单里。 - **Alternatives considered:** 阻塞四份计划的每一张卡。PI-01 与 OpenCode parser 不写 `runtime.rs`。 -- **Consequences:** SCAP-02 只追加关系表一行。不改那些卡的 AC。 +- **Consequences:** SCAP-02 核对已写入的关系表一行,缺行才补。不改那些卡的 AC。 - **Revisit when:** 某张未列入的卡把 `runtime.rs` 加进写集。先改本 ADR 与关系表,再让它开工。 ## 全局工程约束 @@ -239,16 +240,16 @@ SCAP-01 不调用 SCAP-02 的代码。顺序的含义是:兄弟计划先写上 ### Phase 0: 登记前置 -**目标:** 四份计划承认语义前置和 `runtime.rs` 文件锁。 +**目标:** 四份计划承认语义前置和 `runtime.rs` 文件锁。该登记已随 `fba6cbc` 写入。本阶段核对现有行,不把同一句再追加一次。 **进入条件:** -- 本文双评审已 PASS。 +- 本文最新一轮 Codex 与 Claude 对同一修订版均为 `VERDICT: PASS`,且该轮覆盖删除 Hermes 之后的正文。 - `libra status --short --branch` 确认分支,且 SCAP-02 的六个写集文件没有他人未确认改动。 **退出条件:** -- SCAP-02 的 A/B 门已过并已提交。`Acceptance=locally-accepted`。本阶段不把 SCAP-02 标成 `done`。 +- SCAP-02 的核对命令通过。某一要求的行缺失时才补上并提交。行已各出现一次时,不再改这六个文件。`Acceptance=locally-accepted`。本阶段不把 SCAP-02 标成 `done`。 ### Phase 1: 落地 `decide` 并发布 @@ -304,9 +305,9 @@ SCAP-01 不调用 SCAP-02 的代码。顺序的含义是:兄弟计划先写上 **Task type:** `handoff` -**Lifecycle / Acceptance:** `pending` / 空 +**Lifecycle / Acceptance:** `in-progress` / `locally-accepted` -**Description:** 在四份采集计划的关系表各追加一行,并在各自修订历史追加一行。不改它们的任务卡。这是本计划唯一的移交轴。 +**Description:** 核对四份采集计划的关系表、修订历史、`plan-20260907` 的互斥行,以及 `plan-status.md` 的三个现状单元格。这些行已在 `fba6cbc` 写入。缺了才补一行。已经各出现一次时不要再追加,否则前置句的「恰好一次」核对会失败。不改它们的任务卡。这是本计划唯一的移交轴。 **Out of scope:** @@ -323,6 +324,7 @@ SCAP-01 不调用 SCAP-02 的代码。顺序的含义是:兄弟计划先写上 | Pi 关系表 | `docs/development/plan/plan-20260911.md:60-67` | | 修订历史列不同 | `plan-20260902.md:138-139`;`plan-20260904.md:175-177`;`plan-20260905.md:107-109`;`plan-20260911.md:574-577` | | `SC-*` 已被占用 | `plan-20260906.md` 的 `DEP-SC-01` | +| 登记已落盘 | `fba6cbc` 写入四份采集计划、`plan-20260907` 与 `plan-status.md`。核对通过后不再追加 | **前置句**(关系表第三格,四份文件相同): @@ -330,18 +332,18 @@ SCAP-01 不调用 SCAP-02 的代码。顺序的含义是:兄弟计划先写上 **Acceptance criteria:** -- [ ] `plan-20260902.md` 关系表新增一行,三格为 `` `plan-20260925.md` ``、`Session Capture 决策前置(DEP-SCAP-01/03/04)`、前置句。第三格仍是前置句。第二格标出同一格里的三个依赖。 -- [ ] `plan-20260904.md` 关系表新增同样一行。 -- [ ] `plan-20260905.md` 关系表新增同样一行。 -- [ ] `plan-20260911.md` 关系表新增同样一行。 -- [ ] `plan-20260902.md` 修订历史追加一行,轮次列是 `SCAP-HIST-01`,修订内容是 `关系表登记 Session Capture 决策前置`。 +- [ ] `plan-20260902.md` 关系表含有一行,三格为 `` `plan-20260925.md` ``、`Session Capture 决策前置(DEP-SCAP-01/03/04)`、前置句。第三格仍是前置句。第二格标出同一格里的三个依赖。该行已在时不追加第二份。 +- [ ] `plan-20260904.md` 关系表含有同样一行。该行已在时不追加第二份。 +- [ ] `plan-20260905.md` 关系表含有同样一行。该行已在时不追加第二份。 +- [ ] `plan-20260911.md` 关系表含有同样一行。该行已在时不追加第二份。 +- [ ] `plan-20260902.md` 修订历史含有一行,轮次列是 `SCAP-HIST-01`,修订内容是 `关系表登记 Session Capture 决策前置`。该行已在时不追加第二份。 - [ ] `plan-20260904.md` 修订历史的触发列出现一次 `SCAP-HIST-01`。 - [ ] `plan-20260905.md` 修订历史的触发列出现一次 `SCAP-HIST-01`。 - [ ] `plan-20260911.md` 修订历史的 Round 列出现一次 `SCAP-HIST-01`。 -- [ ] `plan-status.md` 中 `DEP-SCAP-01` 的现状单元格改为 `已写入 plan-20260902/04/05/11 关系表`。 -- [ ] `plan-status.md` 里 `DEP-SCAP-03` 的现状单元格改为 `已写入同一关系表的文件锁名单`。 -- [ ] `plan-20260907.md` 关系表新增一行,第三格为 `SCAP-01 与 B3-00 不得同时 in-progress。先完成的一方之后,另一方再改 src/internal/ai/hooks/runtime.rs。` 修订历史含 `SCAP-HIST-01`。 -- [ ] `plan-status.md` 里 `DEP-SCAP-04` 的现状单元格改为 `与 B3-00 互斥已写入 plan-20260907`。 +- [ ] `plan-status.md` 中 `DEP-SCAP-01` 的现状单元格为 `已写入 plan-20260902/04/05/11 关系表`。 +- [ ] `plan-status.md` 里 `DEP-SCAP-03` 的现状单元格为 `已写入同一关系表的文件锁名单`。 +- [ ] `plan-20260907.md` 关系表含有一行,第三格为 `SCAP-01 与 B3-00 不得同时 in-progress。先完成的一方之后,另一方再改 src/internal/ai/hooks/runtime.rs。` 修订历史含 `SCAP-HIST-01`。该行已在时不追加第二份。 +- [ ] `plan-status.md` 里 `DEP-SCAP-04` 的现状单元格为 `与 B3-00 互斥已写入 plan-20260907`。 - [ ] 本卡暂存路径不含 `docs/commands/`。ER-06a 为 N/A,因为用户可见命令表面不变。 **Verification:** @@ -356,7 +358,7 @@ SCAP-01 不调用 SCAP-02 的代码。顺序的含义是:兄弟计划先写上 **Dependencies:** 无 -**Deliverables:** 上列六份计划文件中的追加行。 +**Deliverables:** 上列六份计划文件中已存在的登记行。缺行时才补交。 **Implementation write set:** `docs/development/plan/plan-20260902.md`、`docs/development/plan/plan-20260904.md`、`docs/development/plan/plan-20260905.md`、`docs/development/plan/plan-20260907.md`、`docs/development/plan/plan-20260911.md`、`docs/development/plan/plan-status.md`。 @@ -390,7 +392,7 @@ G-03 对关系表按整行计,不按单元格计。上面 13 条 AC 就是 13 **Task type:** `implementation` -**Lifecycle / Acceptance:** `pending` / 空 +**Lifecycle / Acceptance:** `in-progress` / 空 **Description:** 实现 ADR-SCAP-01,并让 `ingest_agent_traces_payload_with_scope` 使用它。捕获结果与替换前相同。这是本计划唯一的代码轴。 @@ -405,14 +407,14 @@ G-03 对关系表按整行计,不按单元格计。上面 13 条 AC 就是 13 | 事实 | 证据 | |---|---| -| state `match` | `src/internal/ai/hooks/runtime.rs:750-756` | -| checkpoint `matches!` | `src/internal/ai/hooks/runtime.rs:928-935` | -| subagent 分流 | `src/internal/ai/hooks/runtime.rs:1008-1023` | -| owner 豁免 | `src/internal/ai/hooks/runtime.rs:709-712` | -| `stopped_at` | `src/internal/ai/hooks/runtime.rs:863-864` | -| hook 模块声明 | `src/internal/ai/hooks/mod.rs:31` | -| TurnEnd 回归 | `runtime.rs` 内 `ingest_turn_end_writes_committed_checkpoint`(`:4304`) | -| SessionEnd 回归 | `ingest_session_end_writes_checkpoint_when_repo_path_provided`(`:4374`) | +| state 来自 `decide` | `src/internal/ai/hooks/runtime.rs:751` | +| checkpoint 来自 `decide` | `src/internal/ai/hooks/runtime.rs:923-924` | +| subagent 分流 | `src/internal/ai/hooks/runtime.rs:997` | +| owner 豁免 | `src/internal/ai/hooks/runtime.rs:710-712` | +| `stopped_at` | `src/internal/ai/hooks/runtime.rs:858-859` | +| hook 模块声明 | `src/internal/ai/hooks/mod.rs:33` | +| TurnEnd 回归 | `runtime.rs` 内 `ingest_turn_end_writes_committed_checkpoint`(`:4292`) | +| SessionEnd 回归 | `ingest_session_end_writes_checkpoint_when_repo_path_provided`(`:4362`) | | SubagentEnd 回归 | `tests/agent_lifecycle_event_test.rs:593` `subagent_end_materializes_distinct_subagent_scope_checkpoint` | **判据规范(非计数正文):** 下列命令是 VER 的正文。零命中的 nextest 过滤器必须非 0 退出。每个 nextest 命令的摘要必须包含 `1 passed`。 @@ -631,6 +633,8 @@ test "${CODEQL_OK}" -ge 1 | R5 | R4 修订版 | `FAIL` | P0×0;P1×0 | P2×1:修订历史缺 R4 行。该行已补。计划正文无其它问题 | `/tmp/sc-codex-r5-last.txt` | | R6 | R5 修订版(补上 R4 修订历史行) | `PASS` | P0×0;P1×0 | P2×0 | `/tmp/sc-codex-r6-last.txt` | | R7 | 补上 R5/R6 日志行后的同一正文 | `PASS` | P0×0;P1×0 | P2×0 | `/tmp/sc-codex-r7-last.txt` | +| R8 | 删除 Hermes 与关联登记之后的正文 | `FAIL` | P1×1:SCAP-02 仍要求追加 `fba6cbc` 已写入的行 | 已改为核对,缺行才补 | `/tmp/scap-exec/codex-r8.txt` | +| R9 | SCAP-02 改为核对已写入行 | `PASS` | P0×0;P1×0 | P2×0 | `/tmp/scap-exec/codex-r9.txt` | ## Claude review log @@ -643,6 +647,8 @@ test "${CODEQL_OK}" -ge 1 | R5 | R4 修订版 | `PASS` | P0×0;P1×0 | P2×0。另有不阻断的 P3:修订历史当时缺 R4 行、plan-status 轮次口径、失败 run 的 remote-pending 用词 | `/tmp/sc-claude-r5.log` | | R6 | 含 R4 修订历史行、尚无 R5 日志行的文本 | `FAIL` | P0×0;P1×0 | P2×1:review log 缺 R5 行。本行与 Codex R5/R6 同时补上 | `/tmp/sc-claude-r6.log` | | R7 | 已含 R5/R6 日志行的同一正文 | `PASS` | P0×0;P1×0 | P2×0。R7 行是该 PASS 的记录,不改变计划正文 | `/tmp/sc-claude-r7.log` | +| R8 | 删除 Hermes 与关联登记之后的正文 | `PASS` | P0×0;P1×0 | P2×0。备注:登记行已在 `fba6cbc`,执行时不要再追加 | `/tmp/scap-exec/claude-r8.log` | +| R9 | SCAP-02 改为核对已写入行 | `PASS` | P0×0;P1×0 | P2×0 | `/tmp/scap-exec/claude-r9.log` | ## 非目标与延后项 @@ -650,6 +656,8 @@ test "${CODEQL_OK}" -ge 1 |---|---|---|---|---| | DEFER-SCAP-03 | 用 Entire 的 git phase 重写 Libra | 会改已存储的 state 字符串 | 产品单独要求,并有 ADR | 不在 OG/CX/CC/PI 里顺带做 | | DEFER-SCAP-04 | 重写 `docs/development/tracing/agent.md` 里与代码并存的旧句 | 本计划不改那些句子所描述的行为,也没有被指定为该文件的 owner | 下一次编辑该文件的卡自行处理。本计划不向某一份采集计划派发义务 | 无 outgoing DEP | +| DEFER-SCAP-05 | 本机 umask `0002` 下,可执行位测试得到 `0o775`(509),断言要 `0o755`(493) | 与 `decide` 无关。基线 `HEAD`(`0.23.51`,尚无 `session_capture.rs`)上 `command::checkout_test::test_checkout_restores_entry_mode` 同样失败 | 在 umask `0022` 的环境或修正物化权限后再跑这组测试 | 不在 SCAP-01 | +| DEFER-SCAP-06 | `codex_hooks_create_no_upgrade_lock_in_writable_install` 里普通 `status` 不创建 `.libra-upgrade.lock` | 与 `decide` 无关。同一基线 `HEAD` 上该测试同样失败 | 单独的 upgrade recovery 卡 | 不在 SCAP-01 | ## 完成判据 diff --git a/docs/development/plan/plan-long.md b/docs/development/plan/plan-long.md index e2a77cf3d..2826dea8f 100644 --- a/docs/development/plan/plan-long.md +++ b/docs/development/plan/plan-long.md @@ -722,7 +722,7 @@ MEM-03 → MEM-04;LR-09;LR-10;MEM-05 / AG-ATTR 按需;MEM-06(并行协 | 日期计划 | 主要归属 | 当前状态 | 说明 | |---|---|---|---| -| [`plan-20260925.md`](plan-20260925.md) | B(Session Capture 决策中层) | 已排期 | 把 AgentTraces ingest 的 `agent_session.state` 与 checkpoint 类别收成 `session_capture::decide`。OpenCode、Codex、Claude Code、Pi 计划已登记该前置,并与 Entire 的解析/纯决策/I/O 分工对齐。SCAP-01 尚未实现 | +| [`plan-20260925.md`](plan-20260925.md) | B(Session Capture 决策中层) | 执行中 | 把 AgentTraces ingest 的 `agent_session.state` 与 checkpoint 类别收成 `session_capture::decide`。OpenCode、Codex、Claude Code、Pi 计划已登记该前置。SCAP-02 已核对。SCAP-01 实现中 | | [`plan-20260923.md`](plan-20260923.md) | Cross-cutting CLI completion | 已排期 | Cover implemented Git-compatible and Libra-only capabilities; static acceptance before opt-in dynamic completion. CP-00..20 pending; capability inventory may add bounded cards. No implementation claimed. | | [`plan-20260708.md`](plan-20260708.md) | A(LR-04/05/09 相邻基础) | 已完成 | 主线记为历史完成,活跃残留另行排期;不关闭对应 LR | | [`plan-20260713.md`](plan-20260713.md) | B(LR-06/07/10 捕获前置) | 已完成 | 不覆盖 seal/preflight/capsule | diff --git a/docs/development/plan/plan-status.md b/docs/development/plan/plan-status.md index 9184b3ee6..2e24565d2 100644 --- a/docs/development/plan/plan-status.md +++ b/docs/development/plan/plan-status.md @@ -20,7 +20,7 @@ | 计划 | 类别 | 状态 | 一句话进度(卡片状态) | |---|---|---|---| -| [`plan-20260925.md`](plan-20260925.md) | B(Session Capture 决策中层) | 已排期 | SCAP-02 → SCAP-01 均 `pending`。前置句已写入 OpenCode、Codex、Claude Code、Pi 计划,并与 B3-00 互斥。实现卡尚未开工 | +| [`plan-20260925.md`](plan-20260925.md) | B(Session Capture 决策中层) | 执行中 | SCAP-02 `in-progress` / `locally-accepted`。SCAP-01 `in-progress`。R9 双 `PASS`。B3-00 仍 `pending`,未与本卡同时开工 | | [`plan-20260923.md`](plan-20260923.md) | Cross-cutting (implemented CLI completion) | 已排期 | English static-first rewrite; CP-00..20 pending; CP-00 inventory precedes implementation approval; CP-06 static acceptance blocks all dynamic work; no implementation/release claimed | | [`plan-20260918.md`](plan-20260918.md) | 横切(`add` 命令收口) | **实施中** | OI-01..03 `done/complete`(v0.23.4/5/6);**OI-04 `in-progress`(v0.23.7)**;OI-05..WT-07 `pending` | | [`plan-20260919.md`](plan-20260919.md) | 横切(global 配置迁 XDG) | 实施中 | GCX-01 `done/complete`(v0.23.1);GCX-02/03/04 于 2026-09-22 完成实现+测试+文档,`locally-accepted`(版本 bump 与 D 组发布未执行;本轮按操作者指示未调用 Codex review) | @@ -83,7 +83,7 @@ | 计划 | 全部待执行卡 | 开工前置条件 | |---|---|---| -| [`plan-20260925.md`](plan-20260925.md) | SCAP-02、SCAP-01 | 同一修订版 Codex 与 Claude 均为字面 `VERDICT: PASS` 且 P0/P1/P2 为 0。SCAP-01 依赖 SCAP-02 的关系表登记 | +| [`plan-20260925.md`](plan-20260925.md) | SCAP-02、SCAP-01 | R9 对同一修订版 Codex 与 Claude 均为字面 `VERDICT: PASS` 且 P0/P1/P2 为 0。SCAP-02 的登记已在 `fba6cbc`,本卡只核对。SCAP-01 依赖该登记 | | [`plan-20260923.md`](plan-20260923.md) | CP-00..20 pending; expand bounded domain cards after inventory if needed | Audit handoff, required review PASS and DEP-CP-01..07; dynamic implementation requires CP-06 done/complete | | [`plan-20260904.md`](plan-20260904.md) | CX-00..CX-30(35 卡) | Phase 0:Codex `PASS`;CX-30 受 DEP-CLI-mirror 三态串行约束 | | [`plan-20260905.md`](plan-20260905.md) | CC-00..CC-06 | CC-02..06 依赖 plan-20260904 的 RG 卡完成 | @@ -220,7 +220,8 @@ SBX-01..05 `done/locally-accepted`;**发布步按 DEFER-SBX-06 正式延后** ## 四、当前执行指针(next action) -- **当前正在执行:** `issues/474` → `CL-03` 发布窗口(`v0.23.49`,PR #512)。`CL-04` 与 `CL-05` 已 `locally-accepted`,均未 bump。 +- **当前正在执行:** `plan-20260925` SCAP-01(发布者:本会话执行该卡的 Agent)。`issues/474` 的 `CL-03` 发布窗口记录仍是 `v0.23.49` / PR #512;仓库版本面已是 `0.23.51`,本卡从该版本做 `patch + 1`。 +- **SCAP-01 与 B3-00:** B3-00 保持 `pending`,不与本卡同时 `in-progress`。 - **下一步(CL-03 D 组落地后):** bump 并发布 `CL-04`,再发布 `CL-05`。 - **并行窗口(不在本执行指针):** `issues/476` WT-03 仍等 DEP-WT-08;`plan-20260918` 其余 add 卡、`plan-20260819` M2 仍登记为实施中,但不抢本卡的 `fsck.rs` 写集。 @@ -245,7 +246,7 @@ SBX-01..05 `done/locally-accepted`;**发布步按 DEFER-SBX-06 正式延后** | [`plan-20260906`](plan-20260906.md) | SC-01 / SC-02(可并发) | SC-01 `base.yml` 最小权限加固;SC-02 会话入口 id 守卫 | 无 | **未定稿**(R2 PASS 已作废;R22 `FAIL`) | SC-04 受 DEP-SC-01/04/05/06;SC-07 受 DEP-SC-07 | ❌ 禁止开工 | | [`plan-20260902`](plan-20260902.md) | OG-00 | opencode 1.18.29 Hook/export 契约探测 | 无 | **未取得双 PASS**(Claude 限额,Codex 仍在 FAIL 循环) | 无(Phase 1 与 RG 六卡解耦) | ❌ 禁止开工 | | [`plan-20260911`](plan-20260911.md) | PI-01 | Repository-only `agent_kind=pi` migration | 无(DEP-PI-04 已满足:9/10 已收口) | **Claude Code 429 无 verdict,禁止开工** | DEP-PI-01/03 | ❌ 禁止开工 | -| [`plan-20260925`](plan-20260925.md) | SCAP-02 | 向四份采集计划登记 AgentTraces `decide` 前置与 `runtime.rs` 文件锁,并与 B3-00 互斥 | 无 | 关联段落已由 Codex 与 Claude 分别 `PASS` | 无 | 实现卡仍 `pending`,本表不标记开工 | +| [`plan-20260925`](plan-20260925.md) | SCAP-01 | AgentTraces ingest 改调 `decide`。发布者是本会话执行该卡的 Agent。B3-00 仍 `pending` | SCAP-02(登记已核对) | R9 双 `PASS` | 与 B3-00 不得同时 `in-progress` | 执行中 | | [`issues/470`](issues/470.md) | FM-01 | 共享写入原语与 `restore` 系物化 | 无 | 尚未 Codex review | 关闭依赖 plan-20260918 FM-03/04(DEP-FM-06/07) | ❌ 禁止开工 | | [`issues/473`](issues/473.md) | IN-01 / IN-03 / IN-02 | 空模板自引用防护 / 存储路径前置检测 / 换格式 reinit fail-closed | 无 | 尚未 Codex review | 无 | ❌ 禁止开工 | | [`issues/474`](issues/474.md) | CL-03 | 其余历史遍历尊重 shallow 边界 | CL-02 | **R6 `PASS`** | 无 | ✅ `locally-accepted`(C 组 `v0.23.49`) | diff --git a/install.ps1 b/install.ps1 index 411126968..0793f33fb 100644 --- a/install.ps1 +++ b/install.ps1 @@ -22,7 +22,7 @@ $ErrorActionPreference = "Stop" # One of the release version surfaces. `compat_version_surface_sync` pins it # to Cargo.toml: this value is substituted verbatim into the download URL, so # a stale value silently installs an old binary when -Version is not given. -$DefaultVersion = "v0.23.51" +$DefaultVersion = "v0.23.52" # Public-only trust anchor for stable-manifest verification. It deliberately # has no environment override: the install-smoke harness rewrites these # clearly-marked constants in a temporary COPY of this script. diff --git a/install.sh b/install.sh index 979a56c93..1ae8135dc 100755 --- a/install.sh +++ b/install.sh @@ -18,7 +18,7 @@ INSTALL_DIR="${LIBRA_INSTALL_DIR:-$LIBRA_HOME/bin}" # user opts in with LIBRA_ALLOW_FALLBACK=1. Default behaviour is fail-fast so # offline installs cannot silently regress to a stale version. Bump this on # every release so the opt-in fallback remains useful. -DEFAULT_VERSION="v0.23.51" +DEFAULT_VERSION="v0.23.52" # Public-only trust anchor for stable-manifest verification. It deliberately # has no environment override: the install-smoke harness rewrites these # clearly-marked constants in a temporary COPY of this script, never through diff --git a/src/internal/ai/hooks/mod.rs b/src/internal/ai/hooks/mod.rs index 79f0bf2c8..4e5c4551f 100644 --- a/src/internal/ai/hooks/mod.rs +++ b/src/internal/ai/hooks/mod.rs @@ -19,6 +19,7 @@ //! - [`runner`]: spawns hook commands and translates their exit codes into //! [`event::HookAction`]. //! - [`runtime`]: turns stdin envelopes into recorded session updates. +//! - `session_capture`: pure AgentTraces state and checkpoint decision. //! - `setup`: helper for materialising hook scripts on disk during `libra code` //! bootstrap. @@ -29,6 +30,7 @@ pub mod provider; pub mod providers; pub mod runner; pub mod runtime; +mod session_capture; mod setup; pub use config::{HookConfig, HookDefinition, load_hook_config}; diff --git a/src/internal/ai/hooks/runtime.rs b/src/internal/ai/hooks/runtime.rs index 5e1b5cfb7..4f9101220 100644 --- a/src/internal/ai/hooks/runtime.rs +++ b/src/internal/ai/hooks/runtime.rs @@ -185,8 +185,9 @@ pub struct HookEnvelopeInvalid(pub String); /// For [`HookTarget::AiIntent`] the function is exactly the historical /// behaviour (1:1 byte-compatible). For [`HookTarget::AgentTraces`] the /// function runs the external-Agent capture ingest — stdin parse, validate, -/// redact, upsert into `agent_session`, and (on `SessionEnd`) write an -/// E4-libra checkpoint commit on `refs/libra/traces`. +/// redact, and upsert into `agent_session`. Checkpoint class comes from +/// `session_capture::decide` and covers `TurnEnd`, `SessionEnd`, +/// `SubagentStart`, and `SubagentEnd`. pub async fn process_hook_event_with_target( command: super::provider::ProviderHookCommand, expected_kind: LifecycleEventKind, @@ -747,13 +748,7 @@ async fn ingest_agent_traces_payload_with_scope( } } - let new_state = match event.kind { - LifecycleEventKind::SessionStart => "active", - LifecycleEventKind::SessionEnd => "stopped", - LifecycleEventKind::Compaction => "condensed", - LifecycleEventKind::CompactionCompleted => "active", - _ => "active", - }; + let new_state = super::session_capture::decide(event.kind).session_state; // UPSERT: insert a fresh row on first sight; otherwise just bump // `last_event_at`, `state`, and `redaction_report`. We key by @@ -925,13 +920,9 @@ async fn ingest_agent_traces_payload_with_scope( // redacted transcript blob (now the agent's full on-disk transcript, see // the writer); events-blob inclusion remains a follow-up. Per-turn // checkpoints give `libra agent checkpoint rewind` turn-level granularity. - if matches!( - event.kind, - LifecycleEventKind::SessionEnd - | LifecycleEventKind::TurnEnd - | LifecycleEventKind::SubagentStart - | LifecycleEventKind::SubagentEnd - ) && let Some(repo) = repo_path + let checkpoint = super::session_capture::decide(event.kind).checkpoint; + if checkpoint != super::session_capture::CheckpointWrite::None + && let Some(repo) = repo_path { // AG-19 owner-race closure: the pre-upsert owner check above is a // fast path, but two providers racing on a fresh provider session @@ -1005,10 +996,7 @@ async fn ingest_agent_traces_payload_with_scope( // `doctor` surface nested runs as first-class checkpoints instead of // leaving them as bounded `subagent_events` metadata on the main // checkpoint. `SessionEnd` / `TurnEnd` keep the `committed` path. - if matches!( - event.kind, - LifecycleEventKind::SubagentStart | LifecycleEventKind::SubagentEnd - ) { + if checkpoint == super::session_capture::CheckpointWrite::SubagentBoundary { write_subagent_checkpoint( conn, repo, @@ -3375,7 +3363,7 @@ impl SessionPhase { } #[cfg(test)] -mod tests { +pub(crate) mod tests { use serde_json::Map; use serial_test::serial; @@ -3674,7 +3662,7 @@ mod tests { const LEGACY_BOOTSTRAP_SQL: &str = include_str!("../../../../sql/sqlite_20260309_init.sql"); - async fn ingest_fresh_conn() -> (TempDir, DatabaseConnection) { + pub(crate) async fn ingest_fresh_conn() -> (TempDir, DatabaseConnection) { let dir = tempfile::tempdir().expect("tempdir"); // Use the canonical `libra.db` filename here so the Phase 3.5c // object_index queue (`enqueue_agent_blob_object_index_update`) @@ -3715,7 +3703,7 @@ mod tests { (dir, conn) } - fn ingest_envelope( + pub(crate) fn ingest_envelope( hook_event_name: &str, session_id: &str, extra: serde_json::Value, diff --git a/src/internal/ai/hooks/session_capture.rs b/src/internal/ai/hooks/session_capture.rs new file mode 100644 index 000000000..97ce74feb --- /dev/null +++ b/src/internal/ai/hooks/session_capture.rs @@ -0,0 +1,232 @@ +//! Pure decision for AgentTraces ingest. +//! +//! `decide` maps a [`LifecycleEventKind`] to the SQLite `agent_session.state` +//! string and the checkpoint write class. It performs no I/O and does not +//! read paths, prompts, or transcripts. + +use super::lifecycle::LifecycleEventKind; + +/// Which checkpoint writer, if any, AgentTraces ingest should call. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum CheckpointWrite { + /// Leave checkpoint tables untouched. + None, + /// Existing committed-branch writer (`write_committed_checkpoint`). + Committed, + /// Existing subagent-boundary writer (`write_subagent_checkpoint`). + SubagentBoundary, +} + +/// One row of the AgentTraces ingest decision table. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct CaptureDecision { + /// Value stored in `agent_session.state`. + pub session_state: &'static str, + /// Checkpoint class. `repo_path == None` still writes nothing. + pub checkpoint: CheckpointWrite, +} + +/// Return the AgentTraces state string and checkpoint class for `kind`. +/// +/// The match is exhaustive inside this crate. A new [`LifecycleEventKind`] +/// fails compilation here until this table is updated. +pub(crate) fn decide(kind: LifecycleEventKind) -> CaptureDecision { + let (session_state, checkpoint) = match kind { + LifecycleEventKind::SessionStart => ("active", CheckpointWrite::None), + LifecycleEventKind::TurnStart => ("active", CheckpointWrite::None), + LifecycleEventKind::ToolUse => ("active", CheckpointWrite::None), + LifecycleEventKind::ModelUpdate => ("active", CheckpointWrite::None), + LifecycleEventKind::Compaction => ("condensed", CheckpointWrite::None), + LifecycleEventKind::CompactionCompleted => ("active", CheckpointWrite::None), + LifecycleEventKind::PermissionRequest => ("active", CheckpointWrite::None), + LifecycleEventKind::SourceEnabled => ("active", CheckpointWrite::None), + LifecycleEventKind::SourceDisabled => ("active", CheckpointWrite::None), + LifecycleEventKind::TurnEnd => ("active", CheckpointWrite::Committed), + LifecycleEventKind::SessionEnd => ("stopped", CheckpointWrite::Committed), + LifecycleEventKind::SubagentStart => ("active", CheckpointWrite::SubagentBoundary), + LifecycleEventKind::SubagentEnd => ("active", CheckpointWrite::SubagentBoundary), + }; + CaptureDecision { + session_state, + checkpoint, + } +} + +#[cfg(test)] +mod tests { + use super::{super::lifecycle::LifecycleEventKind, CheckpointWrite, decide}; + + #[test] + fn decide_matches_adr_matrix() { + let rows = [ + ( + LifecycleEventKind::SessionStart, + "active", + CheckpointWrite::None, + ), + ( + LifecycleEventKind::TurnStart, + "active", + CheckpointWrite::None, + ), + (LifecycleEventKind::ToolUse, "active", CheckpointWrite::None), + ( + LifecycleEventKind::ModelUpdate, + "active", + CheckpointWrite::None, + ), + ( + LifecycleEventKind::Compaction, + "condensed", + CheckpointWrite::None, + ), + ( + LifecycleEventKind::CompactionCompleted, + "active", + CheckpointWrite::None, + ), + ( + LifecycleEventKind::PermissionRequest, + "active", + CheckpointWrite::None, + ), + ( + LifecycleEventKind::SourceEnabled, + "active", + CheckpointWrite::None, + ), + ( + LifecycleEventKind::SourceDisabled, + "active", + CheckpointWrite::None, + ), + ( + LifecycleEventKind::TurnEnd, + "active", + CheckpointWrite::Committed, + ), + ( + LifecycleEventKind::SessionEnd, + "stopped", + CheckpointWrite::Committed, + ), + ( + LifecycleEventKind::SubagentStart, + "active", + CheckpointWrite::SubagentBoundary, + ), + ( + LifecycleEventKind::SubagentEnd, + "active", + CheckpointWrite::SubagentBoundary, + ), + ]; + assert_eq!(rows.len(), 13); + for (kind, state, checkpoint) in rows { + let decision = decide(kind); + assert_eq!(decision.session_state, state, "{kind}"); + assert_eq!(decision.checkpoint, checkpoint, "{kind}"); + } + } + + #[test] + fn runtime_checkpoint_kind_matches_are_gone() { + let src = include_str!("runtime.rs"); + let four = "\ + LifecycleEventKind::SessionEnd + | LifecycleEventKind::TurnEnd + | LifecycleEventKind::SubagentStart + | LifecycleEventKind::SubagentEnd"; + let subagent_line = + " LifecycleEventKind::SubagentStart | LifecycleEventKind::SubagentEnd"; + assert!( + !src.contains(four), + "checkpoint-kind matches! must be replaced by decide" + ); + assert!( + !src.contains(subagent_line), + "subagent checkpoint split must use decide().checkpoint" + ); + assert!( + !src.contains("let new_state = match event.kind"), + "state match must be replaced by decide().session_state" + ); + assert!(src.contains("decide(event.kind).session_state")); + assert!(src.contains("decide(event.kind).checkpoint")); + } + + #[test] + fn runtime_keeps_owner_exemption_and_stopped_at() { + let src = include_str!("runtime.rs"); + let owner = concat!( + " LifecycleEventKind::SessionStart | ", + "LifecycleEventKind::TurnStart" + ); + let stopped_at = concat!( + " matches!(event.kind, ", + "LifecycleEventKind::SessionEnd).then_some(now);" + ); + assert!( + src.contains(owner), + "SessionStart/TurnStart owner exemption must stay in runtime.rs" + ); + assert!( + src.contains(stopped_at), + "stopped_at assignment must stay in runtime.rs" + ); + } + + #[test] + fn stale_session_end_only_comment_is_gone() { + let src = include_str!("runtime.rs"); + assert!( + !src.contains("(on `SessionEnd`) write an"), + "AgentTraces docs must not say checkpoints are SessionEnd-only" + ); + } + + #[tokio::test] + async fn no_checkpoint_when_repo_path_missing() { + use sea_orm::{ConnectionTrait, Statement}; + + use super::super::{ + provider::ProviderHookCommand, + providers::claude_provider, + runtime::{self, tests as runtime_tests}, + }; + + let (_dir, conn) = runtime_tests::ingest_fresh_conn().await; + runtime::ingest_agent_traces_payload( + &runtime_tests::ingest_envelope("SessionStart", "S-no-repo", serde_json::json!({})), + ProviderHookCommand::SessionStart, + LifecycleEventKind::SessionStart, + claude_provider(), + &conn, + None, + ) + .await + .expect("session start without a repo still upserts"); + runtime::ingest_agent_traces_payload( + &runtime_tests::ingest_envelope("Stop", "S-no-repo", serde_json::json!({})), + ProviderHookCommand::Stop, + LifecycleEventKind::TurnEnd, + claude_provider(), + &conn, + None, + ) + .await + .expect("turn end without a repo still upserts"); + + let backend = conn.get_database_backend(); + let row = conn + .query_one_raw(Statement::from_sql_and_values( + backend, + "SELECT COUNT(*) AS n FROM agent_checkpoint", + [], + )) + .await + .expect("checkpoint count") + .expect("count row"); + assert_eq!(row.try_get_by::("n").unwrap(), 0); + } +} From b92df62b642f1596ff2914f237eb4009434b33b1 Mon Sep 17 00:00:00 2001 From: Eli Ma Date: Wed, 23 Sep 2026 19:33:25 +0800 Subject: [PATCH 2/2] chore(release): bump Session Capture to 0.23.53 v0.23.52 is already published on main, so this card takes the next patch. Signed-off-by: Eli Ma --- Cargo.lock | 2 +- Cargo.toml | 2 +- docs/development/plan/plan-20260925.md | 2 +- install.ps1 | 2 +- install.sh | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ca926ac37..5eb914cc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3783,7 +3783,7 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libra" -version = "0.23.52" +version = "0.23.53" dependencies = [ "anyhow", "assert_cmd", diff --git a/Cargo.toml b/Cargo.toml index e14e9799e..d8e040882 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libra" -version = "0.23.52" +version = "0.23.53" edition = "2024" license = "MIT" description = "AI agent-native version control system with Git on-disk compatibility, SQLite-backed metadata, and tiered cloud storage" diff --git a/docs/development/plan/plan-20260925.md b/docs/development/plan/plan-20260925.md index b05ae6e8a..c1f2ab942 100644 --- a/docs/development/plan/plan-20260925.md +++ b/docs/development/plan/plan-20260925.md @@ -65,7 +65,7 @@ | 过时注释 | `process_hook_event_with_target` 的文档改为 checkpoint 类别来自 `decide`,覆盖 TurnEnd、SessionEnd、SubagentStart、SubagentEnd | `src/internal/ai/hooks/runtime.rs:186-190` | | 模块声明位置 | `hooks/mod.rs` 声明 `mod session_capture`。`src/internal/ai/mod.rs` 由 plan-20260819 的 memory 卡占用,本计划不改 | `src/internal/ai/hooks/mod.rs:33` | | AgentKind | 七个变体。没有 Pi。Pi 由 `plan-20260911` 增加 | `src/internal/ai/observed_agents/adapter.rs:30-38` | -| 版本与发布 | `Cargo.toml` version `0.23.52`(本卡 `patch + 1`,自已发布的 `0.23.51`)。`release.yml` jobs:`build-and-upload`、`upload-install-scripts`、`update-homebrew-tap`、`verify-homebrew-formula`、`request-stable-manifest` | `Cargo.toml:3`;`.github/workflows/release.yml:15`、`:310`、`:480`、`:628`、`:690` | +| 版本与发布 | `Cargo.toml` version `0.23.53`(本卡 `patch + 1`。开工时远端已发布 `v0.23.52`)。`release.yml` jobs:`build-and-upload`、`upload-install-scripts`、`update-homebrew-tap`、`verify-homebrew-formula`、`request-stable-manifest` | `Cargo.toml:3`;`.github/workflows/release.yml:15`、`:310`、`:480`、`:628`、`:690` | | 兄弟计划 | OG-00..15、CX/RG、CC-00..06、PI-01..06 均未开工 | `plan-status.md` 计划一览 | ### Entire 中层与 Libra 的对应 diff --git a/install.ps1 b/install.ps1 index 0793f33fb..2012cf70c 100644 --- a/install.ps1 +++ b/install.ps1 @@ -22,7 +22,7 @@ $ErrorActionPreference = "Stop" # One of the release version surfaces. `compat_version_surface_sync` pins it # to Cargo.toml: this value is substituted verbatim into the download URL, so # a stale value silently installs an old binary when -Version is not given. -$DefaultVersion = "v0.23.52" +$DefaultVersion = "v0.23.53" # Public-only trust anchor for stable-manifest verification. It deliberately # has no environment override: the install-smoke harness rewrites these # clearly-marked constants in a temporary COPY of this script. diff --git a/install.sh b/install.sh index 1ae8135dc..a153b18b1 100755 --- a/install.sh +++ b/install.sh @@ -18,7 +18,7 @@ INSTALL_DIR="${LIBRA_INSTALL_DIR:-$LIBRA_HOME/bin}" # user opts in with LIBRA_ALLOW_FALLBACK=1. Default behaviour is fail-fast so # offline installs cannot silently regress to a stale version. Bump this on # every release so the opt-in fallback remains useful. -DEFAULT_VERSION="v0.23.52" +DEFAULT_VERSION="v0.23.53" # Public-only trust anchor for stable-manifest verification. It deliberately # has no environment override: the install-smoke harness rewrites these # clearly-marked constants in a temporary COPY of this script, never through