Skip to content

Commit 59a237b

Browse files
Shahinyanmclaude
andcommitted
fix(journal): TUI Full mode + drop auto-close + reject closed-task attaches (v0.3.1)
Three corrections to the auto-capture pipeline. Symptoms were visible in the TUI: tasks closed without the user closing them, events attached to unrelated old tasks, and compact detail view hid the reasoning chain. TUI: - task_detail now uses pack::assemble(.., Full) instead of Compact. User opens a task expecting the full chain (open/hypothesis/ decision/rejection/evidence with commit hashes/close), Compact's 3-event summary made the screen look empty. Classifier output safeguards (in IngestHook handler, between classifier.classify() and event write): - If kind == "Stop" AND event_type == Close → drop. The Stop hook fires on every Claude Code session end and the classifier was emitting Close events from session endings. Closes are reserved for explicit `task-journal close <id>`. - If task_id_guess points at a non-existent or closed task → route to pending with a descriptive reason. Prevents old tasks from accumulating events that the classifier mistakenly attributes to them based on prompt similarity. Added tj_core::db::task_status helper for the closed-task check. Closes claude-memory-* (3 P1 beads opened earlier this session for v0.3.1 polish). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 1939bbf commit 59a237b

11 files changed

Lines changed: 90 additions & 11 deletions

File tree

.claude-plugin/marketplace.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
},
77
"metadata": {
88
"description": "Task Journal — append-only reasoning chain memory for AI-coding tasks",
9-
"version": "0.3.0"
9+
"version": "0.3.1"
1010
},
1111
"plugins": [
1212
{
1313
"name": "task-journal",
1414
"source": "./plugin",
1515
"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.",
16-
"version": "0.3.0",
16+
"version": "0.3.1",
1717
"author": {
1818
"name": "Digital-Threads"
1919
},

CHANGELOG.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.3.1] - 2026-05-08
11+
12+
Three correctness fixes for the auto-capture pipeline. The journal
13+
was technically working but producing confusing output: events
14+
attached to the wrong tasks, sessions auto-closed tasks they had no
15+
business closing, and TUI's compact summary hid the reasoning chain
16+
the user actually wanted to see.
17+
18+
### Fixed
19+
- TUI task detail now renders the **Full** pack instead of Compact —
20+
every event, decisions, rejections, evidence (including commit
21+
hashes), and close lines, in chronological order. Compact's three-
22+
line "Active decisions / Recent events" summary made the detail
23+
view look empty.
24+
- Stop hook no longer auto-closes tasks. The Stop hook fires every
25+
time a Claude Code session ends, and the classifier was happily
26+
emitting `Close` events from those endings. Sessions ending !=
27+
task done. Closes are now reserved for explicit
28+
`task-journal close <id>` calls.
29+
- Closed and missing tasks are no longer silently appended to. When
30+
the classifier's `task_id_guess` points at a task that doesn't
31+
exist or is already closed, the event is routed to `pending/`
32+
instead of being attached. Old tasks ("Demo task", "Тест plugin"
33+
in our case) stop accumulating events from unrelated work.
34+
35+
### Added
36+
- `tj_core::db::task_status(&conn, task_id)` helper for the closed-
37+
task safeguard above.
38+
1039
## [0.3.0] - 2026-05-08
1140

1241
### Changed

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ members = [
77
]
88

99
[workspace.package]
10-
version = "0.3.0"
10+
version = "0.3.1"
1111
edition = "2021"
1212
rust-version = "1.88"
1313
license = "MIT"

crates/tj-cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ name = "task-journal"
1616
path = "src/main.rs"
1717

1818
[dependencies]
19-
tj-core = { package = "task-journal-core", version = "0.3.0", path = "../tj-core" }
19+
tj-core = { package = "task-journal-core", version = "0.3.1", path = "../tj-core" }
2020
anyhow = { workspace = true }
2121
clap = { workspace = true }
2222
tracing = { workspace = true }

crates/tj-cli/src/main.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,6 +1326,44 @@ fn main() -> Result<()> {
13261326
let Some(tid) = out.task_id_guess else {
13271327
return Ok(());
13281328
};
1329+
1330+
// Journal-integrity safeguards. The classifier sometimes
1331+
// mis-attributes events to old or closed tasks (no fault
1332+
// of the model — its prompt only sees recent_tasks). We
1333+
// reject three patterns that produce confusing journals:
1334+
//
1335+
// 1. Stop-hook → Close event. The Stop hook fires at
1336+
// every Claude Code session end. Session ending
1337+
// != task done. Closes happen via explicit
1338+
// `task-journal close <id>` only.
1339+
// 2. task_id_guess pointing at a non-existent task —
1340+
// route to pending so the user can decide later.
1341+
// 3. task_id_guess pointing at a CLOSED task — same
1342+
// treatment; closed tasks must stay closed.
1343+
use tj_core::event::EventType;
1344+
if matches!(out.event_type, EventType::Close) && kind == "Stop" {
1345+
return Ok(());
1346+
}
1347+
match tj_core::db::task_status(&conn, &tid)? {
1348+
None => {
1349+
persist_pending(
1350+
&events_path,
1351+
&text,
1352+
&format!("task_id_guess `{tid}` not found"),
1353+
)?;
1354+
return Ok(());
1355+
}
1356+
Some(s) if s == "closed" => {
1357+
persist_pending(
1358+
&events_path,
1359+
&text,
1360+
&format!("task_id_guess `{tid}` is closed"),
1361+
)?;
1362+
return Ok(());
1363+
}
1364+
_ => {}
1365+
}
1366+
13291367
(
13301368
out.event_type,
13311369
tid,

crates/tj-cli/src/tui/app.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,10 @@ impl App {
230230
let project_hash = tj_core::project_hash::from_path(&cwd)?;
231231
let state_path = tj_core::paths::state_dir()?.join(format!("{project_hash}.sqlite"));
232232
let conn = tj_core::db::open(&state_path)?;
233-
let pack = tj_core::pack::assemble(&conn, task_id, tj_core::pack::PackMode::Compact)?;
233+
// Full mode: show the complete reasoning chain — every event,
234+
// decisions, rejections, evidence with commit hashes — instead
235+
// of the 3-line Compact summary the CLI default uses.
236+
let pack = tj_core::pack::assemble(&conn, task_id, tj_core::pack::PackMode::Full)?;
234237
Ok(pack.text)
235238
}
236239

crates/tj-core/src/db.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,15 @@ pub fn task_exists(conn: &Connection, task_id: &str) -> anyhow::Result<bool> {
254254
Ok(count > 0)
255255
}
256256

257+
/// Status string for an existing task (e.g. "open", "closed"). Returns
258+
/// `None` when the task is unknown — caller decides whether that's a
259+
/// hard error or a route-to-pending case.
260+
pub fn task_status(conn: &Connection, task_id: &str) -> anyhow::Result<Option<String>> {
261+
let mut stmt = conn.prepare("SELECT status FROM tasks WHERE task_id = ?1")?;
262+
let mut rows = stmt.query(rusqlite::params![task_id])?;
263+
Ok(rows.next()?.map(|r| r.get::<_, String>(0)).transpose()?)
264+
}
265+
257266
/// Look up the most recent `event_id` we've ingested for this project.
258267
/// Returns `None` when the project has never been indexed (first call,
259268
/// or migration v002 just landed on an existing 0.1.x DB).

crates/tj-mcp/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ name = "task-journal-mcp"
1616
path = "src/main.rs"
1717

1818
[dependencies]
19-
tj-core = { package = "task-journal-core", version = "0.3.0", path = "../tj-core" }
19+
tj-core = { package = "task-journal-core", version = "0.3.1", path = "../tj-core" }
2020
anyhow = { workspace = true }
2121
tokio = { workspace = true }
2222
tracing = { workspace = true }

plugin/.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "task-journal",
3-
"version": "0.3.0",
3+
"version": "0.3.1",
44
"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.",
55
"author": {
66
"name": "Mher Shahinyan"

0 commit comments

Comments
 (0)