Skip to content

feat(orbit): proactive returning-user greeting (§9.3)#172

Open
Hackshaven wants to merge 2 commits into
mainfrom
claude/phase-7-orbit-greeting
Open

feat(orbit): proactive returning-user greeting (§9.3)#172
Hackshaven wants to merge 2 commits into
mainfrom
claude/phase-7-orbit-greeting

Conversation

@Hackshaven

@Hackshaven Hackshaven commented Jun 5, 2026

Copy link
Copy Markdown
Member

What

Phase 7 §9.3 of docs/WEB_CATALOG_FEATURES_PLAN.mdproactive Orbit greeting for returning visitors.

When someone reopens the catalog after a gap of more than 24 h, Orbit proactively opens the chat panel and streams a short, warm "welcome back" turn. The greeting is steered by a local-only returning-user context block:

  • how long since the last visit (whole days),
  • titles of datasets added since their last visit (capped at 5),
  • titles they recently viewed (capped at 3, resolved to current catalog titles).

Orbit opens with one short paragraph and offers one or two concrete next steps (a new dataset, returning to something they viewed, or a tour) rather than dumping a list.

How

Layer Change
docentContext.ts New ReturningUserContext interface + buildReturningUserBlock() pure string builder; optional returningUserBlock threaded into buildSystemPrompt (prepended after the role intro, turn-0 only). Lines with no data are omitted.
docentService.ts triggerOpeningTurn() — once-per-session latch (resetOpeningTurnForTests() for tests), skips entirely when the LLM is unconfigured (a canned local-engine greeting would feel off), delegates to processMessage so the greeting reuses the full streaming + discovery-tool + <<LOAD:>>→chip path. New optional returningUserBlock param on processMessage.
chatUI.ts playReturningGreeting() opens the panel, streams the turn, and attaches a one-time "Orbit remembers locally" disclosure footnote (gated by sos-orbit-greeting-disclosed.v1). Self-gates on: callbacks present, not already streaming, no existing user turn, LLM configured.
main.ts maybeGreetReturningVisitor() gates on catalog mode + getLastSession() existing + a >24 h gap, then hands local visit memory to the UI. Fire-and-forget.
types/index.ts ChatMessage.disclosureFootnote?: boolean.
chat.css / en.json Footnote styling + two i18n strings (chat.greeting.disclosure, chat.greeting.disclosure.link).

Privacy

All inputs are local-only (visit memory from §9.2). Nothing new is sent server-side except the resulting prompt block to the LLM the user has already configured for chat. No new telemetry events. A one-time, dismissible-by-reading disclosure footnote explains the local memory and links to the privacy surface.

Incidental fix

restoreSession() left the module-level messages array stale when sessionStorage was empty — it now resets to []. This is correct production behavior (re-init shouldn't resurrect a prior conversation) and makes the chat tests order-independent.

Testing

  • npm run test — 3840 passing / 1 skipped (new: buildReturningUserBlock + system-prompt inclusion in docentContext.test.ts; triggerOpeningTurn latch/skip/injection in docentService.test.ts; playReturningGreeting stream/footnote/empty-drop in chatUI.test.ts).
  • npm run type-check (incl. check:i18n-strings) — clean.
  • npm run check:locales — artifacts up to date.
  • npm run build — clean.

When a returning visitor reopens the catalog after a gap of more than
24 h, Orbit proactively opens the chat and streams a short, warm
welcome-back turn — steered by a local-only returning-user context
block (days since last visit, datasets added since, recently viewed).

- docentContext: ReturningUserContext + buildReturningUserBlock; thread
  an optional returningUserBlock into buildSystemPrompt (turn-0 only).
- docentService: triggerOpeningTurn — once-per-session latch, skips when
  the LLM is unconfigured, delegates to processMessage for the full
  streaming + discovery-tool + load-chip path.
- chatUI: playReturningGreeting opens the panel, streams the turn, and
  attaches a one-time 'Orbit remembers locally' disclosure footnote.
- main: maybeGreetReturningVisitor gates on catalog mode + >24 h gap and
  hands local visit memory (new-since / recently-viewed titles) to the UI.

All inputs are local-only (visit memory); nothing is sent server-side
except the resulting prompt block to the configured LLM. Also fixes a
latent bug where restoreSession left stale messages on re-init when
sessionStorage was empty.

Signed-off-by: Claude <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements Phase 7 §9.3 “proactive returning-user greeting” for Orbit chat: when a visitor returns after a >24h gap in catalog mode, the chat panel auto-opens and streams an LLM-generated welcome-back message steered by a local-only returning-visitor context block, with a one-time privacy disclosure footnote.

Changes:

  • Add a ReturningUserContext + buildReturningUserBlock() and thread an optional returningUserBlock into the system prompt (turn-0 only).
  • Add triggerOpeningTurn() in docentService (once-per-session latch; skips when LLM unconfigured) and playReturningGreeting() in chatUI (auto-open, stream, one-time disclosure).
  • Add UI + i18n + styling for the disclosure footnote, plus a session-restore fix to reset stale messages when sessionStorage is empty.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/ui/chatUI.ts Adds proactive greeting streaming + one-time disclosure footnote; fixes restoreSession() stale messages when storage is empty.
src/ui/chatUI.test.ts Adds tests for greeting streaming, disclosure gating, and empty-stream placeholder drop.
src/types/index.ts Extends ChatMessage with disclosureFootnote?: boolean.
src/styles/chat.css Styles the disclosure footnote as muted, small text.
src/services/docentService.ts Adds triggerOpeningTurn() and threads returningUserBlock through processMessagebuildSystemPrompt.
src/services/docentService.test.ts Adds coverage for opening-turn latch, skip when unconfigured, and prompt injection.
src/services/docentContext.ts Adds ReturningUserContext, buildReturningUserBlock(), and optional returning-user block inclusion in system prompt.
src/services/docentContext.test.ts Adds tests for returning-user block construction and conditional system-prompt inclusion.
src/main.ts Adds maybeGreetReturningVisitor() gating (>24h gap, catalog mode) and passes local visit context to UI.
locales/en.json Adds disclosure footnote strings.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/ui/chatUI.ts
Comment thread src/ui/chatUI.ts
Address Copilot review on #172:

- playReturningGreeting now handles the full DocentStreamChunk union
  like handleSend: 'rewrite' applies the validated final text (invalid
  dataset ids stripped) and 'done' converts <<LOAD:...>> markers to
  inline [[LOAD:...]] placeholders + attaches llmContext (non-enumerable)
  for feedback payloads. Without this, raw marker syntax could leak into
  the greeting text and load chips wouldn't render inline.
- Announce 'docent responded' on greeting completion so screen-reader
  users get a cue for the auto-opened turn, matching handleSend.
- Add a test asserting marker conversion + rewrite application.

Signed-off-by: Claude <noreply@anthropic.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.

3 participants