Repository navigation
feat: heuristic session titles and grouped sub-task tree - #128
Conversation
…ummaries - Scan first message from each session file to power deterministic, heuristic titles without calling an LLM. - Add display_title, kind and parent_id to SessionSummary. - Group sub-tasks (team/task/workflow) under the most recent preceding main session using creation-time heuristics. - Bump scan cache to v3 so existing files are rescanned for the new fields.
- Render the session picker as a tree: main sessions shown by default and sub-tasks are indented underneath their inferred parent. - Use display_title from storage instead of the raw persisted title. - Right/Left arrows expand or collapse a main session; Left also jumps to the parent when already collapsed. - Preserve expansion state and stable ordering across refreshes.
|
Warning Review limit reached
Next review available in: 58 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughSession scanning now derives display and parent metadata from session content. The ChangesSession tree and metadata
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant SessionScanner
participant SessionSummary
participant SessionsPicker
SessionScanner->>SessionSummary: classify first message and title
SessionScanner->>SessionSummary: assign parent_id
SessionSummary->>SessionsPicker: provide grouped session metadata
SessionsPicker->>SessionsPicker: build and flatten visible tree
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- Sort `all` by stable rank/recency before assigning `board.nodes`, so filtered (non-tree) results maintain the same order as the tree roots. - Remove the unused `collect_nodes` helper.
Document the new display_title, kind, parent_id, cwd, and model fields returned by n00n.session.list().
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
There was a problem hiding this comment.
Criterion
Details
| Benchmark suite | Current: c02e3af | Previous: 6351b86 | Ratio |
|---|---|---|---|
fib/jit_mlua_hook |
6461804 ns/iter (± 111684) |
6715029 ns/iter (± 106679) |
0.96 |
fib/jit_watchdog |
2442525 ns/iter (± 11745) |
2219773 ns/iter (± 5057) |
1.10 |
fib/jit_none |
2434861 ns/iter (± 31832) |
2215866 ns/iter (± 61366) |
1.10 |
fib/interp_mlua_hook |
7711219 ns/iter (± 183085) |
8066518 ns/iter (± 41141) |
0.96 |
fib/interp_watchdog |
3870892 ns/iter (± 19506) |
4289230 ns/iter (± 13656) |
0.90 |
fib/interp_none |
3871341 ns/iter (± 22611) |
4294011 ns/iter (± 13506) |
0.90 |
buffer_rw/jit_mlua_hook |
555021 ns/iter (± 1533) |
584109 ns/iter (± 1928) |
0.95 |
buffer_rw/jit_watchdog |
167968 ns/iter (± 228) |
191602 ns/iter (± 335) |
0.88 |
buffer_rw/jit_none |
167969 ns/iter (± 375) |
191549 ns/iter (± 423) |
0.88 |
buffer_rw/interp_mlua_hook |
1051452 ns/iter (± 11526) |
1037627 ns/iter (± 12956) |
1.01 |
buffer_rw/interp_watchdog |
621892 ns/iter (± 10401) |
588242 ns/iter (± 2255) |
1.06 |
buffer_rw/interp_none |
621717 ns/iter (± 3305) |
588528 ns/iter (± 1416) |
1.06 |
splash_render_120x40 |
75520 ns/iter (± 733) |
61846 ns/iter (± 5593) |
1.22 |
splash_render_200x60 |
118980 ns/iter (± 10534) |
191757 ns/iter (± 4008) |
0.62 |
This comment was automatically generated by workflow using github-action-benchmark.
…y fields - Add byte limits to first-message parsing and cached text to avoid memory and scan-cache bloat on sessions with huge initial prompts. - Avoid splitting snippets on periods, which cut abbreviations short. - Cap extracted snippets to a small display budget before title truncation. - Add serde defaults to new SessionSummary fields for forward compatibility. - Fall back to updated_at when created_at is missing on older sessions so sub-task parent ordering stays sensible. - Remove the stale scan_cache_v2.json file when writing the v3 cache.
…dling - Auto-bucket any parent with more than 20 direct children into grouped nodes labelled with task count and age range (e.g. "20 tasks · today"). - Preserve expansion state for generated group nodes across refreshes. - Parse team/workflow/task prefixes from live session titles so background sessions are grouped correctly before the storage scan lands. - Handle group nodes specially: Enter/Right toggles, rename and delete are blocked, and the right column shows child counts. - Keep filtered and tree lists sorted by stable recency rank.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fda89b4421
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
plugins/sessions/init.lua (1)
252-268: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winFiltered view mixes in group placeholder rows.
board.nodescontains the bucket nodes (they are appended toallbymake_bucket), so a query liketaskmatches a group'sdisplay_title(21 tasks) and renders a group row whose children are not in the result list. Skipn.is_groupin the filter loop.🐛 Proposed fix
for _, n in ipairs(board.nodes) do n.depth = 0 - if ListPicker.matches(n.display_title, words) or ListPicker.matches(n.title, words) then + if not n.is_group + and (ListPicker.matches(n.display_title, words) or ListPicker.matches(n.title, words)) + then table.insert(board.items, n) end end🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/sessions/init.lua` around lines 252 - 268, Update the filtering loop in apply_filter to skip nodes where n.is_group is true before matching titles and inserting into board.items, while preserving matching behavior for regular nodes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@n00n-lua/tests/plugin_host.rs`:
- Around line 2647-2678: The sessions grouping probe only validates bucket
structure, not child link stability. Extend the Lua probe and its Rust
assertions around group_node to verify each child retains its original parent_id
while group membership is tracked separately, then invoke a second
group_node/build_tree pass and assert those relationships remain unchanged.
In `@n00n-storage/src/sessions.rs`:
- Line 1762: Move MAX_FIRST_MESSAGE_BYTES and MSG_RECORD_PREFIX from their
mid-file declarations to the top-level constants section immediately after the
imports, alongside the existing sibling limits. Remove the original declarations
and preserve their values and visibility.
- Around line 571-583: The subtask prefix classification uses inconsistent
casing and is redundantly performed. In n00n-storage/src/sessions.rs:571-583,
lowercase title_norm before matching SUBTASK_TITLE_PREFIXES; in
plugins/sessions/init.lua:412-425, retain the lowercase comparison but remove
the local re-classification and use the stored kind as authoritative for live
rows.
- Around line 592-602: Merge the identical conditional arms in the display
selection logic: combine the title_norm == DEFAULT_TITLE and
title_norm.starts_with("Use the ") conditions into one branch while preserving
the existing snippet_from_text fallback behavior and the title_norm path for all
other titles.
- Line 1664: Update the cleanup around SCAN_CACHE_FILE_V2 to handle
fs::remove_file errors explicitly: ignore only the NotFound case, and log other
failures using an explicitly named fallback with sanitized structured logging.
Do not discard the Result anonymously with let _; preserve cleanup behavior when
the file is absent.
- Around line 483-494: Update generate_title to delegate its final title
truncation and ellipsis handling to truncate_label, removing the duplicated
boundary and word-boundary logic from generate_title while preserving its
existing title-generation behavior.
In `@plugins/sessions/init.lua`:
- Around line 188-199: The group_label function incorrectly assumes slice
endpoints represent the newest and oldest timestamps. Compute the minimum and
maximum age or updated_at values across children[start_idx] through
children[finish], then use those extrema for the label while preserving the
existing singular/plural and equal-age formatting.
- Around line 367-375: Extract the duplicated task-count formatting from the
group branch near the visible session rendering logic into a small reusable
helper, then use it here and at the other task-count construction around the
earlier session code. Preserve the existing singular/plural behavior, returning
“task” only for a count of one and “tasks” otherwise.
- Around line 201-226: Update make_bucket so grouping does not overwrite each
child’s real parent_id; store the bucket relationship in a separate group_id
field instead. Update collapse_or_parent to prefer s.group_id when traversing
grouped nodes, while retaining parent_id as the fallback for ordinary hierarchy
relationships.
---
Outside diff comments:
In `@plugins/sessions/init.lua`:
- Around line 252-268: Update the filtering loop in apply_filter to skip nodes
where n.is_group is true before matching titles and inserting into board.items,
while preserving matching behavior for regular nodes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 4231f030-109f-4ef0-935a-cd917c761737
📒 Files selected for processing (6)
changelog.d/session-tree-ui.added.mdn00n-lua/src/api/session.rsn00n-lua/tests/plugin_host.rsn00n-storage/src/sessions.rsplugins/sessions/init.luasite/docs/content/lua-api/_index.md
📜 Review details
⏰ Context from checks skipped due to timeout. (16)
- GitHub Check: Build (Windows)
- GitHub Check: Coverage
- GitHub Check: MSRV (1.97)
- GitHub Check: Test (macOS)
- GitHub Check: Rustdoc
- GitHub Check: Test (Windows)
- GitHub Check: Lint (macOS)
- GitHub Check: Lint
- GitHub Check: Docs
- GitHub Check: Build
- GitHub Check: Lint (Windows)
- GitHub Check: Test
- GitHub Check: Unused deps
- GitHub Check: Criterion
- GitHub Check: Analyze (rust)
- GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (6)
**/*
📄 CodeRabbit inference engine (AGENTS.md)
**/*: Non-trivial or multi-file changes should use a dedicated git worktree and new branch; do not modify unrelated user changes, force-push, or push to main.
Ship finished work with a clear Conventional Commit message, a pushed branch, and a draft pull request; never add AI-agent attribution to authored content.
Before investigating unfamiliar failures or third-party behavior, research documented behavior first and distinguish unrelated baseline failures from regressions in the touched surface.
Use structural tools before broad searches: prefercodegraphorarborfor cross-file relationships,indexbefore reading files, targeted reads, parallel calls, andcode_executionfor filtering large outputs.
Files:
changelog.d/session-tree-ui.added.mdn00n-lua/src/api/session.rsn00n-lua/tests/plugin_host.rssite/docs/content/lua-api/_index.mdplugins/sessions/init.luan00n-storage/src/sessions.rs
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Workspace Rust lint rules are mandatory: deny unsafe code, productionunwrap_used,expect_used,panic,todo!,unimplemented!,dbg!, wildcard imports, and silent-default error handling.
Do not add unsafe code, FFI, global mutable state,static mut, or unchecked transmute-like behavior without written review and an explicit crate-level lint exception.
Use explicit error handling withResult<T, E>rather than panics; propagate typed errors with?,ok_or_else, andmap_err.
Usethiserrorfor library and domain-specific errors, andcolor-eyreat binary edges.
Do not silently discard errors with.ok(),unwrap_or,unwrap_or_default, or equivalent defaults; return an error, reject the operation, or use an explicitly named fallback with sanitized structured logging.
Follow Rust idioms, use descriptive variable and function names, avoid unnecessary state and bloat, and keep each line justified.
Import types at the top of the file, use short imported names instead of inline qualified paths, and place constants immediately after imports.
Do not use inline magic numbers or strings; deriveCopyonly for structs with one primitive field.
Use structured logging with useful fields and provide helpful, sanitized error messages.
Do not commit credentials, API keys, tokens, cookies, authorization headers, or user data, and do not log raw provider payloads, prompts, credentials, or session data.
Validate and authorize HTTP, file, queue, configuration/environment, LLM output, and provider callbacks before mutation or persistence.
Tool execution requires allowlisted tools, scoped credentials, explicit user context, audit events, and refusal or denial tests.
Write meaningful, non-tautological, non-flaky tests; avoid arbitrary sleeps, and define shared constant error/status messages for assertions.
Files:
n00n-lua/src/api/session.rsn00n-lua/tests/plugin_host.rsn00n-storage/src/sessions.rs
**/*.{rs,toml}
📄 CodeRabbit inference engine (AGENTS.md)
All crates must opt into the workspace lint configuration with
[lints] workspace = true; the root workspace lint configuration is authoritative.
Files:
n00n-lua/src/api/session.rsn00n-lua/tests/plugin_host.rsn00n-storage/src/sessions.rs
**/*.{rs,ron,json,toml,yaml,yml}
📄 CodeRabbit inference engine (AGENTS.md)
Treat LLM and provider output as untrusted input; validate it against schemas, domain constraints, and source evidence before persistence or action.
Files:
n00n-lua/src/api/session.rsn00n-lua/tests/plugin_host.rsn00n-storage/src/sessions.rs
site/docs/**/*
📄 CodeRabbit inference engine (AGENTS.md)
User documentation should be warm, simple, concise, easy for non-native English speakers, story-oriented, and contain no em-dashes, emojis, or AI-like tone.
Files:
site/docs/content/lua-api/_index.md
plugins/**/*.lua
📄 CodeRabbit inference engine (AGENTS.md)
Built-in Lua plugins belong under
./pluginsand should use the repository's plugin tooling and conventions.
Files:
plugins/sessions/init.lua
🪛 markdownlint-cli2 (0.23.0)
changelog.d/session-tree-ui.added.md
[warning] 1-1: First line in a file should be a top-level heading
(MD041, first-line-heading, first-line-h1)
🔇 Additional comments (14)
n00n-storage/src/sessions.rs (6)
37-43: LGTM!Also applies to: 241-246
496-513: LGTM!
515-565: LGTM!
1605-1653: LGTM!Also applies to: 1666-1679
1712-1760: LGTM!Also applies to: 1764-1795, 1827-1833, 1845-1903
2054-2055: LGTM!Also applies to: 2069-2069, 2120-2121, 3283-3343
n00n-lua/src/api/session.rs (1)
41-42: LGTM!site/docs/content/lua-api/_index.md (1)
2645-2646: LGTM!plugins/sessions/init.lua (5)
1-13: LGTM!Also applies to: 33-47, 56-59
75-94: LGTM!Also applies to: 107-119, 134-137
148-186: LGTM!Also applies to: 228-250
278-344: LGTM!
445-511: LGTM!Also applies to: 524-532, 557-560, 586-590, 614-623, 665-668, 711-712
changelog.d/session-tree-ui.added.md (1)
1-1: LGTM!
There was a problem hiding this comment.
⚠️ Performance Alert ⚠️
Possible performance regression was detected for benchmark 'Criterion'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 2.
| Benchmark suite | Current: fda89b4 | Previous: 0d313ba | Ratio |
|---|---|---|---|
splash_render_200x60 |
203672 ns/iter (± 617) |
97776 ns/iter (± 9202) |
2.08 |
This comment was automatically generated by workflow using github-action-benchmark.
Forward-declare render before navigation callbacks so Luau captures it as an upvalue, persist kind-prefixed titles in the in-memory stored snapshot on rename, and rename leftover N00nId uses to n00nId after #145.
Resolve SessionRequest parent_id + steer/control and storage test imports.
Summary
display_title,kind, andparent_idfor every session summary by scanning the first user message.team,task, andworkflowprompts as sub-tasks and group them under the most recent preceding main session.display_titlein the picker instead of the raw persisted title.scan_cache_v3.jsonso existing session files are rescanned for the new metadata.Test plan
cargo fmt --allcargo clippy --all --tests -- -D warningscargo nextest run --workspacestylua --check plugins/sessions/init.lualuac -p plugins/sessions/init.lua