From 8766445a7d992fc2fb0925d06933fa2f9217120d Mon Sep 17 00:00:00 2001 From: Mher Shahinyan Date: Sun, 4 Oct 2026 20:00:08 +0400 Subject: [PATCH] fix(cli): the session start names the chronicle gap also with no open task --- .claude-plugin/marketplace.json | 4 ++-- CHANGELOG.md | 8 ++++++++ Cargo.lock | 6 +++--- Cargo.toml | 4 ++-- crates/tj-cli/src/main.rs | 29 ++++++++++++++++++----------- crates/tj-cli/tests/cli.rs | 26 ++++++++++++++++++++++++++ plugin/.claude-plugin/plugin.json | 2 +- plugin/package.json | 2 +- 8 files changed, 61 insertions(+), 20 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 0cf7f69..46b9178 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -6,14 +6,14 @@ }, "metadata": { "description": "Task Journal — append-only reasoning chain memory for AI-coding tasks", - "version": "0.31.0" + "version": "0.31.1" }, "plugins": [ { "name": "task-journal", "source": "./plugin", "description": "Append-only journal of AI-coding task reasoning chains. Captures hypotheses, decisions, rejections, evidence — renders compact resume packs so an agent can pick up a 2-week-old task with full context.", - "version": "0.31.0", + "version": "0.31.1", "author": { "name": "Digital-Threads" }, diff --git a/CHANGELOG.md b/CHANGELOG.md index 6953b66..0a91acb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.31.1] - 2026-10-04 + +### Fixed +- The session start now names the chronicle's most important gap (for + example "no module map yet") also when the project has no open task. Before, + the hook returned before reaching that line, so a project whose tasks were + all closed never got the invitation to map it. + ## [0.31.0] - 2026-10-04 The journal becomes a map of the system. A project splits into modules — parts diff --git a/Cargo.lock b/Cargo.lock index 376d6cf..5edf7e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2516,7 +2516,7 @@ dependencies = [ [[package]] name = "task-journal-cli" -version = "0.31.0" +version = "0.31.1" dependencies = [ "anyhow", "assert_cmd", @@ -2540,7 +2540,7 @@ dependencies = [ [[package]] name = "task-journal-core" -version = "0.31.0" +version = "0.31.1" dependencies = [ "anyhow", "chrono", @@ -2565,7 +2565,7 @@ dependencies = [ [[package]] name = "task-journal-mcp" -version = "0.31.0" +version = "0.31.1" dependencies = [ "anyhow", "chrono", diff --git a/Cargo.toml b/Cargo.toml index fc93940..caeb0f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ members = [ ] [workspace.package] -version = "0.31.0" +version = "0.31.1" edition = "2021" rust-version = "1.88" license = "MIT" @@ -20,7 +20,7 @@ categories = ["command-line-utilities", "development-tools"] [workspace.dependencies] # `version` must equal workspace.package.version: `cargo publish` uploads it as # the requirement on core. Cargo can't inherit it; plugin_metadata.rs checks it. -tj-core = { package = "task-journal-core", version = "0.31.0", path = "crates/tj-core", default-features = false } +tj-core = { package = "task-journal-core", version = "0.31.1", path = "crates/tj-core", default-features = false } anyhow = "1" thiserror = "1" serde = { version = "1", features = ["derive"] } diff --git a/crates/tj-cli/src/main.rs b/crates/tj-cli/src/main.rs index 2abf7b3..d92816a 100644 --- a/crates/tj-cli/src/main.rs +++ b/crates/tj-cli/src/main.rs @@ -2971,11 +2971,25 @@ fn real_main() -> Result<()> { tj_core::paths::state_dir()?.join(format!("{project_hash}.sqlite")); let conn = tj_core::db::open(&state_path)?; tj_core::db::ingest_new_events(&conn, &events_path, &project_hash)?; + // The chronicle's most important gap, for every client that + // reads this hook (Claude Code with or without the mod, Codex), + // also when no task is open. Advice only: it never fails the + // session start. + let chronicle_line = tj_core::chronicle::Chronicle::open(&cwd) + .and_then(|chr| tj_core::archive::gaps(&chr, &conn, &project_hash, None)) + .ok() + .and_then(|gaps| tj_core::archive::headline(&gaps)); let recent = recent_task_contexts(&conn, &project_hash, 3, live_session_id.as_deref())?; if recent.is_empty() { - if !prefs_block.is_empty() { - emit_session_context(&prefs_block); + let context = [Some(prefs_block.as_str()), chronicle_line.as_deref()] + .into_iter() + .flatten() + .filter(|part| !part.is_empty()) + .collect::>() + .join("\n\n"); + if !context.is_empty() { + emit_session_context(&context); } return Ok(()); } @@ -3038,15 +3052,8 @@ runs in the background and won't block you; it only fills gaps and never closes )); } } - // The chronicle's most important gap, for every client that - // reads this hook (Claude Code with or without the mod, Codex). - // Advice only: it never fails the session start. - if let Some(line) = tj_core::chronicle::Chronicle::open(&cwd) - .and_then(|chr| tj_core::archive::gaps(&chr, &conn, &project_hash, None)) - .ok() - .and_then(|gaps| tj_core::archive::headline(&gaps)) - { - bundle.push_str(&line); + if let Some(line) = &chronicle_line { + bundle.push_str(line); bundle.push_str("\n\n"); } for tc in &recent { diff --git a/crates/tj-cli/tests/cli.rs b/crates/tj-cli/tests/cli.rs index 20c983e..53c5adb 100644 --- a/crates/tj-cli/tests/cli.rs +++ b/crates/tj-cli/tests/cli.rs @@ -8873,3 +8873,29 @@ fn a_worktree_shares_the_main_checkouts_module_map() { .success() .stdout(contains(id.as_str()).not()); } + +#[test] +fn session_start_names_the_chronicle_gap_with_no_open_task() { + // A project whose tasks are all closed has no resume pack to show, yet + // the chronicle still has something to say. + let (xdg, proj) = ( + assert_fs::TempDir::new().unwrap(), + assert_fs::TempDir::new().unwrap(), + ); + let tj = chronicle_cli(&xdg, &proj); + let mut create = tj(); + create.args(["create", "Done work"]); + let id = stdout_of(create); + tj().args(["close", &id, "--outcome", "Shipped"]) + .assert() + .success(); + + let mut hook = tj(); + hook.args(["ingest-hook", "--kind", "SessionStart", "--text", ""]); + let out: serde_json::Value = serde_json::from_str(&stdout_of(hook)).unwrap(); + let ctx = out["hookSpecificOutput"]["additionalContext"] + .as_str() + .unwrap_or(""); + + assert!(ctx.contains("📚 Chronicle: no module map yet"), "{out}"); +} diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index 47b32a8..116ff10 100644 --- a/plugin/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "task-journal", - "version": "0.31.0", + "version": "0.31.1", "description": "Append-only journal of AI-coding task reasoning chains: hypotheses, decisions, rejections, evidence. Renders compact resume packs so an agent can pick up a 2-week-old task with full context.", "author": { "name": "Mher Shahinyan" diff --git a/plugin/package.json b/plugin/package.json index 3eaec0d..40b3672 100644 --- a/plugin/package.json +++ b/plugin/package.json @@ -1,6 +1,6 @@ { "name": "task-journal", - "version": "0.31.0", + "version": "0.31.1", "description": "Append-only journal of AI-coding task reasoning chains. Captures hypotheses, decisions, rejections, evidence — renders compact resume packs so an agent can pick up a 2-week-old task with full context.", "author": { "name": "Mher Shahinyan",