Session titles live on sessions; search names sessions by title; titles never contain quotes - #965
Session titles live on sessions; search names sessions by title; titles never contain quotes#965ColemanDuPlessie wants to merge 11 commits into
Conversation
…es never contain quotes Three changes, ported from the stash-benchmarking pilot (4dfd3ddc): - Fold session_titles into sessions (migration 0179). The side table was 1:1 with sessions and FK'd to it; every reader joined it and every writer upserted against it. title/title_source_hash/title_user_set/ title_updated_at are now columns on sessions, existing titles are carried forward in one shot, and the table is dropped. - Session search hits carry `name` — the session's display title in the VFS's spelling (new titles_for_session_ids + public stashvfs.safe_name) — so `stash search` prints titles, not ids, and a hit can be followed straight into /sessions/<name>/. Unlike the benchmarking commit, `ref` is kept: the web search page links sessions with it (and drops ref-less hits entirely). - Stored titles never contain quotes or backticks: generated, derived, and user-set titles all pass _strip_quotes (after reply-shape detection, whose patterns need the apostrophes), and VFS names strip shell-hostile characters — titles become VFS directory names, which agents reach through two layers of shell parsing. Co-Authored-By: Claude Fable 5 <[email protected]>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
henry-dowling
left a comment
There was a problem hiding this comment.
(1) Getting rid of session_titles table: love it, great call
(2) stash search returns titles of sessions- I have a couple thoughts:
- I believe we unintentionally affect our search rankings here! Look- now we rank based on both title and snippet, not just snippet. was this intended? if not, we should keep existing ranking behavior.
- why hide the session ref when searching for sessions? why not include both the ref and the title? refs are nice because they're guaranteed to be unique
- this also introduces inconsistency between our API and CLI- the API returns both, and the CLI returns only the name after this change.
- Edge case to remember- when we have two separate files in the VFS with the same name, we'll automatically disambiguate them with a UUID. Eg
/sessions/Fix auth--12345678
/sessions/Fix auth--abcdef12
this is important because it means if you search just using the name of the file you're looking for, it may fail if there were two files created with this name.
(3) I think we're doing this at the wrong place- we're sanitizing titles at import- why not sanitize titles only when we're about to display them in the VFS? This will let us preserve the important invariant that if you rename a session, its name in Stash is the same name that you typed in.
Basically, the overall takeaway for (3) is- we should persist the ground truth data, not anything that we derive from it. This is so that we don't lose information (that might be important!)
Import/storage no longer strips quotes from titles: _strip_quotes is gone from set_user_title, title_from_text, title_from_events, and clean_generated_title, so the original spelling — user-set or generated — is what lands in sessions.title. Shell-hostile characters (quotes, backticks, $) are removed in exactly one place, stashvfs safe_name, when a title is projected into a VFS path or search-hit name. Backticks still disappear from generated titles via _strip_markdown — that is markdown cleanup of LLM output, not shell sanitization. Co-Authored-By: Claude Fable 5 <[email protected]>
Session hits were printing only the title, on the theory that the raw id is not a usable handle. But the id is still the stable reference — titles can change or collide — so print the generic line for every hit: title in bold, (sessions: <id>) dimmed after it. Co-Authored-By: Claude Fable 5 <[email protected]>
--session now takes either handle: a raw session id (recognized by its local transcript, exactly as before) or a session title. Titles match in both the stored spelling and the VFS safe_name spelling — the one `stash search` and `stash vfs ls` print — and resolve to the matching session's id via the overview. No match or an ambiguous title fails loud, listing the colliding ids. Co-Authored-By: Claude Fable 5 <[email protected]>
Generalize share's title support into one resolver used by every session-consuming command: a handle matching a session title (stored or VFS spelling) resolves to the session's id; anything else passes through as an id for the server to accept or reject loudly. Ambiguous titles error, listing the matching ids. Wired into: share --session, rm/restore/mv session refs, shares add/rm session objects (these take the row id; the resolver's field arg picks which id), and agent chat -s / status / watch / stop (chats are sessions too). Deliberately not wired into `sessions push`: that's the ingest path — hooks push events under freshly minted ids at volume, where an overview fetch per event is waste and a title alias could silently append to an old conversation instead of starting a new stream. Co-Authored-By: Claude Fable 5 <[email protected]>
Main independently merged its own 0179 (rewalk_x_bookmarks) plus 0180-0184, so this branch's migration moves to 0185 on top of 0184 — one head again. The source_service conflict resolves by keeping both sides: main's modified_after/before pushdown into search_scope_events and this branch's title-named session hits. Co-Authored-By: Claude Fable 5 <[email protected]>
The GitHub-side merge of main kept both 0179 migrations (two alembic heads) and left an unformatted seam in source_service.py — the two CI failures. This keeps the renumbered 0185 migration and the formatted source_service resolution. Co-Authored-By: Claude Fable 5 <[email protected]>
|
Just updated this, resolving your comment: stash search now returns both title and ID, and titles are sanitized at use, rather than at import. I did not, however, allow CLI users to lookup sessions by ID (since currently the system is sessions/[title]/transcript.md): we could add some sort of alias system, but that seems heavy and expensive, so I'm not sure if it's a net win. |
…the docs The CLI resolver landed in b00a452, but four surfaces still named a session by an id a person never sees. `stash restore session:"<title>"` could not work: the resolver matches against the overview, which excludes trashed sessions, so a title fell through as an id and 404'd. Restore now resolves against the trash — and `stash trash list` names a session by its title rather than its raw session_id, so the listing prints the name restore takes. An untitled session still shows its id, via titles_for_sessions, the one function that answers what a session is called. The session viewer showed the raw id in three places it already had the title loaded: the breadcrumb placeholder (now "Session"), the trash-confirm dialog, and the downloaded transcript's filename (now the title, filename-sanitized). The chat agent is told to cite a session by title; it is handed titles and no ids, so citing an id asked for something it does not have. Ten guidance surfaces documented `stash share <session_id>` as a positional argument the CLI has never accepted — it is `--session/-s`. Corrected, along with the www docs entry for `stash sessions transcript`, a command that no longer exists, replaced by the VFS path that reads a transcript by title. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
One conflict, in cli/tests/test_mount_vfs.py: this branch and main each appended a FakeClient subclass plus its test at the same point in the file. The two are independent — the branch's covers shell-hostile characters being stripped from VFS names, main's covers a table filed inside a skill folder not taking the mount down — so both are kept, in that order. Migration numbering needed no change: main tops out at 0184 and this branch's 0185_session_titles_into_sessions still follows it.
… that resolves Two gaps meant an agent still had to handle session ids. The name `ls /sessions` shows for a duplicated title — `<title>--<id8>` — did not resolve. `_match_session_title` compared against the stored title and its safe_name spelling only, so the suffixed directory name fell through as an id and 404'd, while the bare title errored as ambiguous. Both routes dead-ended on the one case the suffix exists to solve. The VFS's naming is now public as `stashvfs.session_dir_names`, the filesystem and the resolver both spell names with it, and resolution accepts that spelling. An ambiguous title now answers with the suffixed names instead of raw ids, so the error names its own way out. Resolution was CLI-only, so the MCP tools — the surface an agent is most likely to be on — took ids and nothing else. It moves to session_ref_service behind GET /me/sessions/resolve, and the CLI, the MCP tools, and the sources API all ask the same matcher. stash_session_transcript, stash_delete_session, stash_assign_session, stash_restore/purge(kind=session), and read_source(source=sessions) now take a title; source_document resolves too, so HTTP callers get it without going through the VFS shell. The pass-through contract is unchanged and now lives in one place: a handle naming no title is already an id, so the endpoint echoes it back with matched=false and every caller stays on one codepath. Ambiguity is still refused rather than guessed — the callers are delete and publish. Verified end to end against a live backend: transcript-by-title, by the VFS spelling, read_source by title, a duplicated title refused with both suffixed names, each of those resolving, and a raw id still passing through.
|
Updated again: ID is now still mentioned parenthetically in search results, but is entirely redundant (in the CLI; it's still used to generate unique URLs). Everything that models could do in the CLI with a pre-existing transcript's ID can now also be done with the session title. |
|
Closing the loop on this: Confirming that I think we can drop session_id from CLI search results; agents don't really need it for anything |
Ports three changes made in our benchmarking repo, centered around reorganizing how we deal with session titles:
session_titlestable remove, now is a part ofsessionsstash searchCLI command now gives session titles, rather than IDs. The underlying endpoint still returns both, since web search still gets sessions by IDs.AI-generated longer summary below...
1.
session_titlesfolded intosessionsThe side table was 1:1 with sessions and FK'd to it — every reader had to LEFT JOIN it, every writer upserted against a table that couldn't exist without its sessions row. Migration 0179 adds
title,title_source_hash,title_user_set,title_updated_attosessions, carries existing titles forward in one shot, and drops the table. All consumers (title service, generation task, share service, sidebar etag) now read and write the sessions columns;set_user_titlefails loud if the session row doesn't exist.2.
stash searchnames sessions by titleSession search hits now carry
name— the session's display title in the VFS's spelling (newsession_title_service.titles_for_session_ids+ publicstashvfs.safe_name) — so a hit can be followed straight into/sessions/<name>/. The CLI prints the title and omits the raw session id, which isn't a usable handle there.Deliberate deviation from the benchmarking commit: it removed
reffrom session hits entirely. Hererefis kept — the web search page (frontend/src/app/search/page.tsx) links sessions viahit.refand skips ref-less hits altogether, so dropping it would have removed sessions from web search. No frontend changes needed.3. Stored titles never contain quotes
Generated, derived, and user-set titles all pass
_strip_quotes(removes',", and backticks). Stripping happens after reply-shape detection, whose patterns need the apostrophes ("I'll …", "you're …") intact. VFS names additionally strip$— titles become VFS directory names, which agents reach through two layers of shell parsing instash vfs "<script>".Testing
test_admin_creates_workspace_with_working_bootstrap_key, which fails identically on main — pre-existingauth0_subenv issue).test_ls_unknown_source_fails_loudly, console-rendering assertion).clean_generated_title/title_from_textstrip quotes; VFS drops shell-hostile characters from session dir names.ruff checkandruff format --checkclean.No GUI changes — the web search page's behavior is intentionally unchanged.
🤖 Generated with Claude Code