update: memoryシステムの"Phase"表記を撤廃

This commit is contained in:
2026-05-11 01:55:28 +09:00
parent a8a6e049bc
commit a2aecbf029
34 changed files with 282 additions and 227 deletions
+8 -8
View File
@@ -1,8 +1,8 @@
//! Phase 2 sub-Worker への最初のユーザー入力を組み立てる。
//! consolidation sub-Worker への最初のユーザー入力を組み立てる。
//!
//! Phase 1 (`extract::build_extract_input`) と同じ方針で、固定 schema の
//! extract (`extract::build_extract_input`) と同じ方針で、固定 schema の
//! markdown セクション列にしてサブWorker に渡す。`docs/plan/memory.md`
//! §Phase 2 入力 / §整理材料 の項目に従い:
//! §Consolidation 入力 / §整理材料 の項目に従い:
//!
//! 1. consumed staging エントリ全文(`source` 込み)
//! 2. 既存 `memory/*` 全文(summary / decisions / requests
@@ -10,7 +10,7 @@
//! 4. 整理材料(Linter Warn ベース、メトリクス未完なら明示 invoke 頻度なし)
//!
//! 既存 `knowledge/*` 本文は埋めず、agent に `KnowledgeQuery` 経由で引かせる
//! 設計(`docs/plan/memory.md` §retrieval 経路 / §Phase 2 の Knowledge アクセス)。
//! 設計(`docs/plan/memory.md` §retrieval 経路 / §Consolidation の Knowledge アクセス)。
use std::fmt::Write;
@@ -20,7 +20,7 @@ use crate::workspace::{RecordKind, WorkspaceLayout};
/// Knowledge 化候補レポート。`tickets/memory-usage-metrics.md` の成果物が
/// 出るまでは空で渡す前提(`docs/plan/memory.md` §Knowledge 化候補レポート)。
/// 空入力時、統合 phase は新規 Knowledge を作らず decisions / requests /
/// 空入力時、統合 step は新規 Knowledge を作らず decisions / requests /
/// summary / 既存 Knowledge update に留まる。
#[derive(Debug, Default, Clone)]
pub struct KnowledgeCandidateReport {
@@ -45,7 +45,7 @@ impl KnowledgeCandidateReport {
}
}
/// Phase 2 sub-Worker の最初の user 入力。
/// consolidation sub-Worker の最初の user 入力。
pub fn build_consolidate_input(
layout: &WorkspaceLayout,
staging: &[StagingEntry],
@@ -54,9 +54,9 @@ pub fn build_consolidate_input(
) -> String {
let mut out = String::new();
out.push_str(
"Phase 2 consolidation input. Run the consolidation phase first \
"consolidation input. Run the integration step first \
(fold the staging activity logs into memory and knowledge), then the \
tidy phase (clean up existing records). Use the memory tools for \
tidy step (clean up existing records). Use the memory tools for \
every write — direct file writes are denied by the pod scope.\n\n",
);
+10 -10
View File
@@ -1,11 +1,11 @@
//! `_staging/.consolidation.lock` による Phase 2 占有ファイル。
//! `_staging/.consolidation.lock` による consolidation 占有ファイル。
//!
//! `docs/plan/memory.md` §並走防止 に従い:
//!
//! - ファイルが存在し、記録された Pod が動作している間、その Pod が排他占有
//! - クラッシュで残った stale lock は、所有者 PID が死んでいれば次回 spawn
//! 時に上書き取得できる
//! - cleanup は consumed ID の staging エントリのみ削除し、実行中に Phase 1
//! - cleanup は consumed ID の staging エントリのみ削除し、実行中に extract
//! が追加した分は残す
//!
//! 占有判定は Linux/macOS の `kill(pid, 0)` 経由で行う(`ESRCH` で死亡判定)。
@@ -29,7 +29,7 @@ pub struct LockRecord {
pub pid: u32,
pub pod_name: String,
pub started_at: DateTime<Utc>,
/// この Phase 2 run が起動時スナップショットで確定した consumed staging
/// この consolidation run が起動時スナップショットで確定した consumed staging
/// entry の UUIDv7 列。完了時はこの列のみ削除し、追加分は残す。
pub consumed_ids: Vec<Uuid>,
}
@@ -38,7 +38,7 @@ pub struct LockRecord {
#[derive(Debug, thiserror::Error)]
pub enum LockError {
/// 占有ファイルが既にあり、所有者 PID が生きているのでスキップ。
#[error("Phase 2 lock held by live pid {pid} (pod {pod_name:?})")]
#[error("consolidation lock held by live pid {pid} (pod {pod_name:?})")]
InUse { pid: u32, pod_name: String },
#[error("io error at {}: {source}", .path.display())]
Io {
@@ -59,7 +59,7 @@ impl LockError {
}
}
/// Phase 2 が走っている間 RAII で持つ占有ハンドル。`Drop` では何もしない —
/// consolidation が走っている間 RAII で持つ占有ハンドル。`Drop` では何もしない —
/// 完了時の cleanup は consumed ID 列削除と一緒に行う必要があるため、明示
/// 解放 [`StagingLock::release_with_cleanup`] を使う。明示解放しないまま
/// drop された場合は占有ファイルがそのまま残り、次回 spawn 時に PID が
@@ -105,10 +105,10 @@ impl StagingLock {
tracing::warn!(
stale_pid = existing.pid,
stale_pod = %existing.pod_name,
"Phase 2 stale lock detected, taking over"
"consolidation stale lock detected, taking over"
);
} else {
tracing::warn!(path = %path.display(), "Phase 2 lock unparseable, treating as stale");
tracing::warn!(path = %path.display(), "consolidation lock unparseable, treating as stale");
}
}
@@ -146,7 +146,7 @@ impl StagingLock {
self.unlink_lock_only();
}
/// 占有ファイルだけ削除し、staging エントリには触らない。Phase 2
/// 占有ファイルだけ削除し、staging エントリには触らない。consolidation
/// sub-Worker が途中で失敗した場合に使う: 入力 staging を残したまま
/// 次回再評価で再処理させる(`docs/plan/memory.md` §並走防止 の
/// 「重複作成は同一 slug update に自然収束」運用)。
@@ -160,7 +160,7 @@ impl StagingLock {
tracing::warn!(
path = %self.path.display(),
error = %e,
"failed to remove Phase 2 lock"
"failed to remove consolidation lock"
);
}
}
@@ -193,7 +193,7 @@ fn pid_is_alive(pid: u32) -> bool {
#[cfg(not(unix))]
fn pid_is_alive(_pid: u32) -> bool {
// Unsupported platforms: assume the lock is live so we never overwrite
// someone else's claim. Phase 2 will skip and try again next post-run.
// someone else's claim. consolidation will skip and try again next post-run.
true
}
+3 -3
View File
@@ -1,6 +1,6 @@
//! Phase 2: 統合 + 整理。
//! consolidation: 統合 + 整理。
//!
//! Phase 1 が staging に残した活動ログを `memory/*` / `knowledge/*` に
//! extract が staging に残した活動ログを `memory/*` / `knowledge/*` に
//! 統合し、続けて既存 record を `outdated | superseded | unused | noisy`
//! の観点で整理する disposable Worker を、Pod 側が組み立てるための
//! ヘルパー群を提供する。Pod は次の手順で sub-Worker を構築する:
@@ -15,7 +15,7 @@
//! (`PodPrompt::MemoryConsolidationSystem`) で管理される。Knowledge 化候補
//! レポートと使用頻度メトリクスは別チケットで供給される想定。本モジュール
//! 時点では空入力として扱い、prompt 側の説明だけ残しておく
//! `docs/plan/memory.md` §Phase 2 / 整理材料)。
//! `docs/plan/memory.md` §Consolidation / 整理材料)。
mod input;
mod lock;
+3 -3
View File
@@ -1,10 +1,10 @@
//! `_staging/*.json` を列挙して [`StagingRecord`] に展開する読み込みヘルパー。
//!
//! Phase 2 起動時のスナップショット(consumed ID list 確定)と、整理 phase
//! consolidation 起動時のスナップショット(consumed ID list 確定)と、整理 step
//! が終わった後の cleanup の双方で使う。`.consolidation.lock` のような
//! 占有ファイルは UUIDv7 として parse できないので自然に除外される。
//!
//! [`StagingRecord`] のスキーマは Phase 1 が書き出す側 (`crate::extract`)
//! [`StagingRecord`] のスキーマは extract が書き出す側 (`crate::extract`)
//! と単一の真実源 — ここでは読み出す側だけを担当する。
use std::path::PathBuf;
@@ -29,7 +29,7 @@ pub struct StagingEntry {
/// `<staging_dir>/*.json` を読んで UUIDv7 順に並べた [`StagingEntry`]
/// 配列を返す。staging_dir が存在しなければ空配列。読めないファイルや
/// JSON parse 失敗は `tracing::warn!` してスキップ(壊れた個別ファイルが
/// Phase 2 全体を止めないように)。
/// consolidation 全体を止めないように)。
pub fn list_staging_entries(layout: &WorkspaceLayout) -> Vec<StagingEntry> {
let dir = layout.staging_dir();
let entries = match std::fs::read_dir(&dir) {
+5 -5
View File
@@ -1,7 +1,7 @@
//! 整理 phase が prompt 入力に乗せる「整理材料」スキャナ。
//! 整理 step が prompt 入力に乗せる「整理材料」スキャナ。
//!
//! `docs/plan/memory.md` §整理(GC 相当)の扱い と
//! `tickets/memory-phase2-consolidation.md` の整理材料リストに従い、
//! `tickets/memory-consolidation.md` の整理材料リストに従い、
//! メトリクス未完の現状で機械的に拾えるヒントだけを集める:
//!
//! - `replaced` chain: `status: replaced` の Decision とその `replaced_by`
@@ -21,13 +21,13 @@ use crate::workspace::{RecordKind, WorkspaceLayout};
/// `sources` overflow を flag する閾値。`linter::warnings::SOURCES_OVERFLOW_THRESHOLD`
/// と同値(10)を踏襲する。Linter Warn で sources 過多が検出されるラインと
/// 整理 phase で勧告するラインを揃える狙い。
/// 整理 step で勧告するラインを揃える狙い。
pub const SOURCES_OVERFLOW_THRESHOLD: usize = 10;
/// 類似 slug クラスタリングの距離。`linter::warnings::SIMILAR_SLUG_DISTANCE`
/// と同値。
pub const SIMILAR_SLUG_DISTANCE: usize = 2;
/// 整理 phase 用の機械集計ヒント。空フィールドは「対象なし」を意味する。
/// 整理 step 用の機械集計ヒント。空フィールドは「対象なし」を意味する。
#[derive(Debug, Default, Clone)]
pub struct TidyHints {
/// `status: replaced` で残っている Decision の slug → `replaced_by` map。
@@ -179,7 +179,7 @@ fn parse_yaml<F: serde::de::DeserializeOwned>(content: &str) -> Option<F> {
/// Connected-component clustering over the `levenshtein <= SIMILAR_SLUG_DISTANCE`
/// graph among same-kind slugs. Returns each cluster of size >= 2 (singleton
/// clusters are not interesting for the integration phase). Returns `None`
/// clusters are not interesting for the integration step). Returns `None`
/// when there are no clusters at all.
fn cluster_similar(slugs: &[&str], kind: RecordKind) -> Option<Vec<SimilarSlugCluster>> {
if slugs.len() < 2 {
+2 -2
View File
@@ -1,4 +1,4 @@
//! Phase 1 sub-Worker への入力テキスト組み立て。
//! extract sub-Worker への入力テキスト組み立て。
//!
//! `crates/pod/src/pod.rs::build_summary_prompt` と同じ方針で
//! Item 列を flat な行に落とす(reasoning は省く、tool call は名前のみ、
@@ -7,7 +7,7 @@
use llm_worker::Item;
/// 与えられた `items` を Phase 1 sub-Worker の最初の user 入力に整形する。
/// 与えられた `items` を extract sub-Worker の最初の user 入力に整形する。
pub fn build_extract_input(items: &[Item]) -> String {
let mut out = String::new();
out.push_str(
+1 -1
View File
@@ -1,4 +1,4 @@
//! Phase 1: 活動抽出。
//! extract: 活動抽出。
//!
//! 通常 Pod の post-run hook で発火する disposable Worker と、その
//! 出力を `<workspace>/.insomnia/memory/_staging/<id>.json` に書き出す
+2 -2
View File
@@ -1,8 +1,8 @@
//! Phase 1 抽出の出力 schema。
//! extract 抽出の出力 schema。
//!
//! LLM は [`ExtractedPayload`] そのもの(source 抜き)を返し、Pod 側
//! ラッパーが [`StagingRecord`] に組み立てて staging へ書き出す。
//! source は機械付与する契約 (`docs/plan/memory.md` §Phase 1)。
//! source は機械付与する契約 (`docs/plan/memory.md` §Extract)。
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
+2 -2
View File
@@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
use super::EXTRACT_DOMAIN;
/// Phase 1 完了境界の永続化 payload。session log の Extension entry
/// extract 完了境界の永続化 payload。session log の Extension entry
/// として 1 回ずつ書かれ、最新の 1 件が現行 pointer として有効になる。
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ExtractPointerPayload {
@@ -21,7 +21,7 @@ pub struct ExtractPointerPayload {
pub staging_id: String,
}
/// `RestoredState.extensions` から最新の Phase 1 pointer を取り出す。
/// `RestoredState.extensions` から最新の extract pointer を取り出す。
/// 未抽出セッションでは `None`。
pub fn fold_pointer(extensions: &[(String, serde_json::Value)]) -> Option<ExtractPointerPayload> {
extensions
+1 -1
View File
@@ -35,7 +35,7 @@ pub enum StagingError {
///
/// 戻り値は割り当てられた staging file の (id, path)。`payload` が
/// 完全に空の場合は呼び出し側が事前に `is_empty()` で skip 推奨だが、
/// この関数は空でも正規に書き出す(仕様 §Phase 1 で空配列許容と
/// この関数は空でも正規に書き出す(仕様 §Extract で空配列許容と
/// 明記されており、書く / 書かないの判断は呼び出し側に委ねる)。
pub fn write_staging(
layout: &WorkspaceLayout,
+1 -1
View File
@@ -17,7 +17,7 @@ Pass an object with `decisions`, `discussions`, `attempts`, and `requests` array
Call this exactly once and end the turn. Do not include `source`, session metadata, or free-form prose — \
the wrapper attaches provenance mechanically.";
/// Phase 1 sub-Worker の出力受け口。`ExtractedPayload` 1 件をホストする。
/// extract sub-Worker の出力受け口。`ExtractedPayload` 1 件をホストする。
#[derive(Debug, Default)]
pub struct ExtractWorkerContext {
payload: Mutex<Option<ExtractedPayload>>,
+1 -1
View File
@@ -183,7 +183,7 @@ impl WorkspaceLayout {
}));
}
if first == STAGING_DIR {
// Linter opts out of `_staging/`; Phase 1 handles its schema.
// Linter opts out of `_staging/`; extract handles its schema.
return Ok(None);
}