Skip to content

[codex] Add quick session cards demo and plan#47

Draft
flame4 wants to merge 18 commits into
mainfrom
codex/quick-session-demo-plan
Draft

[codex] Add quick session cards demo and plan#47
flame4 wants to merge 18 commits into
mainfrom
codex/quick-session-demo-plan

Conversation

@flame4

@flame4 flame4 commented Jul 2, 2026

Copy link
Copy Markdown
Member

Summary

  • Add a hoverable top-nav quick session hub demo with seeded sessions, local create/archive/send behavior, and simulated agent replies.
  • Add drag-to-reference demo support from quick session rows into channel rows.
  • Add quick session cards requirements and implementation plan covering persisted objects, runtime state isolation, scoped events, API shape, frontend wiring, and QA phases.
  • Apply rustfmt to the runtime HTTP module so the repository format hook passes.

Validation

  • npm --prefix products/gitim/frontend run build
  • cargo fmt --all -- --check
  • Pre-commit hook: cargo clippy --workspace --all-targets --no-deps --locked

flame4 and others added 18 commits July 2, 2026 22:12
Add a hoverable quick session hub demo in the top navigation, including local session creation, archive behavior, simulated replies, and drag-to-reference support for channel rows.

Add the quick session cards requirements and implementation plan covering storage, runtime state, scoped events, API shape, frontend wiring, and QA phases.

Apply rustfmt to the runtime HTTP module so the repository format hook passes.

Test: npm --prefix products/gitim/frontend run build

Test: cargo fmt --all -- --check

Co-authored-by: Codex <codex@openai.com>
- Add cross-node routing contract (git-synced persisted objects, runtime-local execution)
- Replace async title generation with title API gate (needs_title → api_set)
- Update goals, creation flow, status values, API endpoints, acceptance criteria
- Add cross-node daemon dispatch to plan Phase 3
- Replace Phase 5 with Title API Gate And Compression
- Add cross-node E2E test to Phase 10
- Add title gate risk and cross-node offline risk to Key Risks
…ef disambiguation)

Resolves plan-review High P1 items:
- P1-1: poll-based cross-node dispatch via quick-sessions/<id>/ path detection
- P1-2b/P1-5c: ULID-based session IDs (qs-<ulid>, 29 chars, Crockford base32)
- P1-5a: bare session:<id> ref parser with line ref support; new LinkKind::QuickSession
- Fix: validate_quick_session_id() lives in types/quick_session.rs (not validator.rs)
- Add: daemon create checks existence, retries ULID up to 3 times on collision
- Add: typed error QUICK_SESSION_ID_COLLISION if all retries exhausted
Phase 1 (gitim-core):
- New types/quick_session.rs: QuickSessionMeta, QuickSessionStatus,
  QuickSessionTitleSource, QuickSessionRuntimeState, request/response
  types, validate_quick_session_id(), validate_quick_session_title()
- LinkKind::QuickSession variant in types/link.rs
- SESSION_REF_RE regex and extraction in link.rs for bare
  'session:qs-<ulid>' refs with optional :L000001 line refs
- Preceding-char boundary check prevents false positives
- 10 new tests for quick session ref parsing

Phase 2 (gitim-daemon):
- New quick_session_handlers.rs: create/list/read/send/archive/title
  daemon handlers with collision detection (3 retries)
- ULID generator (Crockford base32, 26 chars)
- Request enum variants: CreateQuickSession, ListQuickSessions,
  ReadQuickSession, SetQuickSessionTitle, SendQuickSessionMessage,
  ArchiveQuickSession
- Handler dispatch in handlers/mod.rs
- LinkKind::QuickSession serialization in handlers/serde.rs

Co-authored-by: flame4 <flame0743@gmail.com>
- quick_session_state.rs: read/write/delete .gitim-runtime/quick-sessions/<id>.state.json
- quick_session_runner.rs: check_title_gate(), title_gate_prompt_instruction(),
  run_quick_session_turn() stub with documented integration points
- Both modules registered in runtime lib.rs

Co-authored-by: flame4 <flame0743@gmail.com>
Phase 4 (runtime):
- AgentActivityEvent: added scope (agent_main/quick_session),
  session_id, ref_ fields with backward-compatible defaults
- All construction sites updated across agent_loop, http, workspace,
  fleet_http, multi_workspace modules/tests

Phase cross-cut (daemon poll):
- handle_poll: quick-sessions/<id>/ path detection for cross-node dispatch
- Emits quick_session_meta when session.meta.yaml changes and agent_id
  matches a locally-hosted agent
- Emits quick_session_thread when discussion.thread changes

Co-authored-by: flame4 <flame0743@gmail.com>
- 6 HTTP endpoints added to workspace router:
  POST   /im/quick-sessions              → create_quick_session
  GET    /im/quick-sessions              → list_quick_sessions
  GET    /im/quick-sessions/{id}         → read_quick_session
  POST   /im/quick-sessions/{id}/messages → send_quick_session_message
  POST   /im/quick-sessions/{id}/title   → set_quick_session_title
  POST   /im/quick-sessions/{id}/archive → archive_quick_session
- All handlers delegate to daemon via GitimClient::request()
- ListQuickSessionsQuery struct for query params

This completes the backend HTTP surface. The title API gate
is enforced server-side in the daemon handler (Phase 2).

Co-authored-by: flame4 <flame0743@gmail.com>
- New QuickSessionLoop struct that runs alongside AgentLoop as a
  separate background task, polling for quick sessions assigned
  to local agents
- Per-agent FIFO serialization via shared AgentLockMap to prevent
  provider profile corruption between concurrent quick session turns
- Fresh provider per turn: instantiate from agent config, execute,
  write assistant response via daemon, then tear down
- Title gate enforced in execution path: sessions with status
  needs_title are skipped until title is set via API
- Scoped activity events emitted for quick session turns (thinking,
  done, error with scope=quick_session and session_id)
- build_provider_config made pub(crate) for reuse by QuickSessionLoop
- Idempotent workspace-level spawn guarded by quick_session_loop_running
  flag on WorkspaceContext

Known gap: main AgentLoop and QuickSessionLoop do not yet share the
AgentLockMap; per-agent serialization between main and quick-session
turns requires further plumbing.

Co-authored-by: flame4 <flame0743@gmail.com>
- Added agent_locks field to WorkspaceContext (initialized in new())
- AgentLoop now acquires per-agent lock before provider.execute()
  via set_agent_locks(), preventing concurrent same-agent main +
  quick-session turns from racing on provider profile state
- QuickSessionLoop spawn reuses the shared AgentLockMap instead
  of creating its own
- Lock is held for duration of provider execute + streaming loop

This closes the P1 concurrency gap — main AgentLoop and
QuickSessionLoop are now serialized per agent.

Co-authored-by: flame4 <flame0743@gmail.com>
Phase 7 (data layer):
- QuickSessionListItem, QuickSessionDetail, SessionRefDragPayload types
- Client methods: list/create/read/send/setTitle/archive
- Zustand store (use-quick-session-store.ts) with polling, detail
  loading, message sending, archiving
- SESSION_REF_RE regex for session:<id>(:L<line>)? parsing

Phase 8 (hub UI):
- ConversationHub component replaces ConversationHubDemo with
  real API-backed quick session hub
- Popover + pinned panel modes, session list, thread display,
  create/send/archive flow, live 8s polling
- Status rendering (needs_title/running/active/error/archived)

Phase 9 (refs/drag):
- session-ref-dnd.ts with SessionRefDragPayload and drop helpers
- Sidebar updated to use SessionRefDragPayload
- parseSessionRef() for session:<id>(:L<line>)? format

Phase 10 (cleanup):
- Removed conversation-hub-demo.tsx and session-demo-dnd.ts
- Updated app-shell.tsx import to ConversationHub
- AgentActivityEvent: added scope/session_id/ref_ fields
  and quick_session_blocked event type

Verification:
- npm run build: successful
- vitest: 68 files / 562 tests pass
- cargo check: clean

Co-authored-by: flame4 <flame0743@gmail.com>
… frontend contract bugs

P0 fixes:
- Include needs_title sessions in QuickSessionLoop dispatch (were filtered out)
- Implement agent-side title generation via dedicated provider call
  when session is needs_title: generate title from thread, call
  set_quick_session_title via daemon IPC, then proceed to normal turn
- Add QUICK_SESSION_TITLE_REQUIRED gate in handle_send_quick_session_message
  (blocks non-creator write when status=needs_title)
- Add validate_quick_session_id to all daemon handlers (path traversal fix)

P1 fixes:
- Fix archived query param: include_archived -> archived (client.ts)
- Fix create response shape: return QuickSessionListItem instead of
  CreateQuickSessionResponse (drops nested meta, matches store expectation)
- Refresh open session detail on poll (loadDetail after list refresh)
- Replace DnD phantom sent message with CustomEvent-driven composer
  text insert (session ref text goes to input, not chat)
- Add inline editable title UI in ConversationHub (click to edit,
  Enter/blur to save, Escape to cancel)

Co-authored-by: flame4 <flame0743@gmail.com>
…ration tests

- Pass showArchived state to refresh() so toggling the filter refetches
  archived sessions from the API (use-quick-session-store.ts + ConversationHub)
- Use showArchivedRef to keep poll interval in sync with current toggle state
- Add 6 integration tests in quick_session_gate_test.rs:
  - create sets needs_title status
  - agent write blocked at needs_title (QUICK_SESSION_TITLE_REQUIRED)
  - creator (human) exempt from title gate
  - set_title transitions to active, allows agent write
  - archived sessions reject writes
  - invalid/path-traversal session_id rejected

Co-authored-by: flame4 <flame0743@gmail.com>
…ety, meta freshness, commit locking

flame4 owner-review fixes:

P0 — .thread protocol compliance:
- Use format_message() from gitim-core (produces [L...][P...][@handler][timestamp] body)
- Use parse_thread() for reading existing threads (line numbering, author parsing)
- Remove custom format_line_number / manual line parse helpers

P1 — Activity event isolation:
- applyUsageActivityEvent returns early when scope==="quick_session"
- New applyQuickSessionActivityEvent routes quick-session events to
  useQuickSessionStore (thinking → running, done → active, error → error)
- useAgentActivity updated to dispatch scoped events to both stores

P1 — Chinese title safety:
- validate_quick_session_title: chars().count() instead of byte length
- generate_and_set_title: chars().take(MAX) instead of byte-indexed [..80]
- message_preview(): chars().take(80) for correct grapheme-aware truncation
- Added validate_title_counts_chars_not_bytes test

P1 — Meta freshness (stale list fix):
- send_quick_session_message now writes updated_at + last_message_preview
- write_meta() called after each write to keep meta in sync
- Handler::new() validation for author on create/send paths
- Empty message validation (first_message / body)

P1 — DnD cross-channel race fix:
- Event detail now carries {text, workspaceKey, scopeKey}
- input-area checks scope match before inserting; falls back to localStorage draft
- sidebar dispatches event before channel switch

P1 — Provider session continuity (architecture debt):
- Read QuickSessionRuntimeState before execution, pass resume_token to provider
- Write state back after execution with session_token + usage from ExecResult
- Session token extracted before match to avoid partial-move

P2 — Commit lock + defensive cleanup:
- Create/send/archive all wrap write+commit inside state.commit_lock
- path.parent().unwrap() replaced with proper error handling
- quick_session_runner.rs stub removed; doc trimmed to active helpers only
- gate tests expanded: parse_thread compatibility, Chinese char title validation

Co-authored-by: flame4 <flame0743@gmail.com>
…reshness, P2 frontend tests)

P1 runtime state path:
- Thread repo_root through poll_and_process_sessions → execute_quick_session_turn
- quick_session_state::read_state/write_state now use repo_root (gitim repo)
  instead of agent_info.repo_path (agent clone), avoiding untracked
  .gitim-runtime/ pollution in agent repos
- Add .gitim-runtime/ to root .gitignore as defense-in-depth

P2 title edit freshness:
- useQuickSessionStore.setTitle() now patches both item.title and
  detail.meta.title in local store state on API success, so UI reflects
  the new title immediately without waiting for next poll/refresh

P2 frontend coverage gap:
- use-quick-session-store.test.ts: 6 parseThread tests (single/multi msg,
  empty input, event line rejection, CJK) + 3 setTitle store update tests
- use-agent-activity.test.ts: 7 quick session scope isolation tests
  (applyUsageActivityEvent ignores qs events, applyQuickSessionActivityEvent
  routes thinking/error/done to useQuickSessionStore)

Verification:
- cargo check, cargo fmt, cargo clippy: pass
- cargo test -p gitim-core --lib: 343 passed
- cargo test -p gitim-daemon --lib: 167 passed
- cargo test -p gitim-daemon --test quick_session_gate_test: 7 passed
- cargo test -p gitim-runtime --lib -- quick_session: 3 passed
- npx vitest run src: 589 passed

Co-authored-by: flame4 <flame0743@gmail.com>
Owner L81: state root must be workspace_root, not repo_root and not
agent_info.repo_path. Thread workspace_root through poll_and_process_sessions
and execute_quick_session_turn for quick_session_state::{read,write}_state.
repo_root remains for GitimClient and provider cwd only.

Co-authored-by: flame4 <flame0743@gmail.com>
QuickSessionLoop now uses workspace_root (stable workspace-level path) for
its GitimClient instead of the first-started agent's repo_root. This keeps
the loop independent of agent startup order — all session operations route
through the workspace daemon, not an agent-specific clone.

Co-authored-by: flame4 <flame0743@gmail.com>
…ursor P0

QuickSessionLoop now uses human_repo (resolved from WorkspaceContext::human_repo
with persistent-human-repo fallback, same logic as http::human_repo_path) for
its GitimClient, instead of workspace_root or first-agent repo_root. This
matches the HTTP quick-session handler client binding.

- human_repo: daemon IPC root (where daemon socket lives)
- workspace_root: state root for .gitim-runtime/quick-sessions/
- agent_info.repo_path: provider cwd (unchanged)

Code-path evidence:
- http.rs spawn: ctx.human_repo with persistent fallback → qs_human_repo
- quick_session_loop.rs: GitimClient::new(human_repo) for all daemon IPC
- state read/write: continues using workspace_root

Co-authored-by: flame4 <flame0743@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant