---
title: AI Coaching sidebar (interview prompts and artifacts) (part 2)
---

# AI Coaching sidebar (interview prompts and artifacts) (part 2)

## What it is

This is part 2 of the [AI Coaching sidebar (interview prompts and artifacts)](ai-coaching.md) page. It covers the artifact half of AI Coaching — how a finished interview document is saved, edited, versioned, restored, deleted and imported, how pinned context and detail levels decide what the coach reads, and how a saved artifact reaches your other sessions and the KMS vault.

## Where to find it

AI Coaching is the virtual project in the left sidebar, and the [parent page](ai-coaching.md) walks through turning it on and using it. Inside it, artifacts are the **Artifacts (N)** section of the AI Coaching sub-sidebar, and clicking one opens the artifact editor in the right pane. The pin pickers live on a completed card’s **⋮** menu on the Dashboard and on the pin button in a running session’s header, and **Import existing work** and **Bulk Import** sit on the Dashboard’s prompt cards and header.

## How it behaves

### How artifacts get saved

There is no separate "save the transcript" round-trip. The interview prompt loaded into Claude's project-level instructions teaches it to do two things once the interview has captured enough material: **save the finished document by calling Omniscio's CLI** — a `POST` to a session-scoped save endpoint — and **show that same document inline in the chat** as clean prose. The save is an explicit call that returns success or failure, so Claude knows it worked (and can retry, or tell you, if it didn't) — more reliable than the old approach of writing a file that Omniscio then had to _detect_.

The endpoint is locked to the interview's own artifact: Claude posts only the document text, and Omniscio fills in the title from the session's configured `producesArtifactTitle` — so Claude can never write into a different artifact, and a stray or injected title can't end up in the sidebar. Omniscio writes a new version into the artifacts table and pushes a refresh — your artifact pops into the **Artifacts** section seconds after Claude finishes. Re-saves are deduped by content: re-posting the same document creates no new version, so "refine it later" simply means posting the updated text.

The save applies **immediately, with no inbox approval**, because it's the session's own artifact (the retired file-detection saved with no approval too). The general "create / update / delete any artifact" CLI stays approval-gated and is deliberately **hidden from the coach** — that boundary is what keeps text inside your own saved profile from ever turning into a silent edit of a _different_ document.

The **Save artifact now** button is a UX nudge, not a separate save path. Pressing it sends Claude a one-line operator message asking it to save the document (and show it) right now, deferring to the same instructions. It works while the session is alive (running, ready, starting, or needs-you) — not for paused, archived, errored, or ended sessions, because there's no live agent to respond.

The save is bound to the coach's OWN session: another session's token can't save into this one, and a session that isn't an AI-Coaching interview is rejected. This is the prompt-injection defense — the coach can only ever write the single artifact its interview is configured to produce, with the title enforced server-side. Separately, a display filter still strips any stray `<artifact>` / `[PHASE:N]` / `[SEGMENT:N]` markers out of coaching messages before they're shown, so those obscure tags can never leak into the chat the way a bare `</artifact>` once did — while keeping the document prose itself intact.

### Editing artifacts

When you open an artifact from the **Artifacts** section, the right pane shows the rendered markdown plus a small toolbar at the top. As long as you're looking at the latest version, that toolbar includes an **Edit** button. Click it and the markdown render swaps for a monospace textarea pre-filled with the current contents — the cursor lands inside, ready to type.

There is a hard 20,000-character cap. The textarea won't accept more than that, and the IPC handler enforces the same limit on the server side, so the cap holds even if a client tries to slip something larger through. While you're editing, the **Save** button is enabled only when (a) you've actually changed something, (b) the buffer isn't blank, and (c) you're under the limit. When (b) or (c) is what's holding it back, the toolbar says so next to the button — "Add some content to save." or "Too long to save — trim it under 20,000 characters." A body of nothing but spaces counts as blank, which is the case that used to look like a broken button: the textarea still appeared full while Save sat greyed out with no explanation. **Ctrl+S** (Cmd+S on Mac) saves without leaving the keyboard.

If you change your mind, the **Cancel** button — or **Escape** — takes you back to the rendered view. If you haven't typed anything, it returns immediately. If you have unsaved changes, it pops a "Discard changes?" confirmation first — so an accidental Cancel won't throw away ten minutes of writing. The editor compares your draft against the snapshot of content captured at the moment you clicked Edit, not the live store value, so a background refresh of the artifact (for example, another open window updating it) doesn't move the goalposts on whether your buffer counts as "dirty." Your draft survives.

There is one cross-window edge case worth knowing. If someone else (or another window of yours) saves a new version of the same artifact while you're mid-edit, your save will fail with a toast: "This artifact was updated elsewhere. Reload it to see the latest version before saving." That toast carries a **Reload latest** button, so the reload it asks for is one click away rather than something you have to go find. Your draft is **not** discarded by it — the reload pulls in the newer version as the save target and leaves your text exactly where it is, then warns you that saving now replaces that newer copy. Same behaviour on mobile.

A successful Save bumps the version number to MAX+1: a v1 artifact becomes v2 the first time you save, v3 if you save again, and so on. Older versions are kept — saving never overwrites them.

Implementation: [ArtifactPane.tsx](/src/renderer/src/features/ai-coaching/artifacts/ArtifactPane.tsx) drives the view/edit toggle, dirty-buffer tracking, and the keyboard shortcut. Saves go through the `AI_COACHING_UPDATE_ARTIFACT` IPC channel.

### Version history

Whenever an artifact has more than one saved version, the metadata strip under the title shows a **Version history** toggle. Expanding it lists every version, newest first — each row shows the version number and date, with a **latest** tag on the newest — and a **View** button (older rows also get a **Restore** button). With only one version saved there's nothing to expand, so the toggle is hidden.

Clicking **View** on an older row loads that version's content into the right pane in **read-only** mode (markdown render only, no textarea). The toolbar reshapes to fit: instead of an **Edit** button, you see a **Back to latest** button. **Copy** and **Export** stay available and operate on whatever version is currently displayed, which is handy if you just want to grab the old text without changing anything in the artifact itself.

Clicking **Back to latest** (or **View** on the latest row) returns the pane to its normal editable state.

### Restore a version

Each older version in the **Version history** list has its own **Restore** button. Clicking it does **not** instantly overwrite anything. Instead, it opens the same textarea editor you'd get from clicking Edit, pre-filled with that version's content, with the dirty flag already set so **Save** is immediately enabled.

When you save, the result is a brand-new version on top: if v4 was the latest and you restored from v1, your save creates v5 with the v1 content as its starting point. v1 stays in the history unchanged. v4 stays in the history unchanged. Nothing is destroyed.

The mental model: restore is "make this old thing the basis of a new save," not "rewind." If you want to tweak the restored content before saving, you can — the editor is fully open. If you want it exactly as it was, just save without changes.

### Delete an artifact

In view mode the artifact toolbar has a **"⋮" (more actions)** button; opening it reveals **Reset interview** and a red **Delete**. Choosing Delete opens a confirmation dialog: "Delete this artifact?" with body text like "This will permanently hide all 4 versions." (the count reflects however many versions the artifact actually has).

Confirming soft-deletes the **whole artifact** — every version of it, all at once. The right pane closes, and the artifact disappears from the **Artifacts** section in the sub-sidebar. The version count next to that section header drops accordingly.

If the delete fails (network blip, IPC error, etc.), a toast surfaces the reason ("Delete failed: …") and the dialog stays open so you can retry without re-clicking the button. On success, the dialog closes itself.

"Soft-delete" here means the row stays in the database with `is_deleted = 1` rather than being physically removed. This keeps any cross-references — for example, session metadata that pointed at the artifact — from dangling, while listings filter the deleted row out everywhere it would otherwise appear. There is no in-app "undelete" button at the moment; restoring a soft-deleted artifact today is a database-level operation.

### Pinned context + the pull menu

As you complete more interviews you build up a stack of artifacts, and an interview about your week shouldn't have to digest all of them up front. So inside an AI Coaching interview, your coach gets your past work in two ways:

- **Pinned** — the artifacts you mark as "load this in full for this interview." The coach reads these completely before the conversation starts. Two always-on extras come free here: your **Core Profile** and your bookmarked **Key Moments**.
- **A pull menu** — every _other_ artifact is listed as a single line: its title, a one-sentence gist, and roughly how big each detail level is. The coach scans the menu, decides which one or two are actually relevant to today's topic, and pulls just those — so a check-in about a job decision can reach for your "Career history" without also re-reading fifteen unrelated reflections. A pinned artifact disappears from the menu (no point listing it twice).

You choose what's pinned in two places, both on desktop:

- On the Dashboard, open a **completed** card's **⋮** menu to set the pins for _that interview prompt_ — they apply every time that interview is started.
- Inside a running coaching session, the header has a pin button to set the pins for _just this session_.

Pins are stored by artifact title, so deleting an artifact you had pinned leaves a pin with nothing behind it. The picker no longer hides those: a pin whose artifact is gone shows up in its own struck-through section at the bottom with a short explanation, and un-ticking it clears the leftover for good. (Your coach was already skipping them — this just makes that visible instead of silent.)

A couple of things worth knowing: pins are locked in when a session is built, so changing them mid-conversation doesn't rewrite the chat you're already in (re-pin for the next one). If you pin so much that it won't all fit, Omniscio shrinks the biggest pinned artifact down a level at a time and, if it still won't fit, quietly moves the overflow into the pull menu with a short note — nothing is ever silently dropped. And a prompt you've configured to "pin nothing" stays that way; it's not the same as never having set it (which falls back to a sensible default).

Every time an artifact is included in a session's pinned context, the system increments a `context_pull_count` on that artifact. The Dashboard's stats bar shows the **most-referenced** artifact (the one pulled into context most often) alongside the regular artifact/version/bookmark/session counts, so you can see at a glance which parts of your coaching portfolio are doing the most work. Backed by `AI_COACHING_GET_MOST_REFERENCED` IPC (desktop-only, blocked on mobile).

### How artifacts inform your other sessions

Saved artifacts don't just sit in the AI Coaching pane — they also become **soft context** that every new regular session sees. When you spawn a session in any of your real projects (not the virtual AI Coaching project), the agent silently receives a "Things to know about the user" block at the top of its first prompt: each saved artifact rendered as a labeled markdown section, wrapped in `[BEGIN PROFILE]` / `[END PROFILE]` sentinels, with an instruction to use the profile naturally — not to quote it back, not to announce it has it, not to bring it up unless relevant to the task.

The intent is "be informed by who I am," not "perform the profile at me." If you've captured an Identity artifact saying you're a non-programmer relying on engineering AI for codebase work, the agent will frame explanations accordingly without ever saying "I see from your profile…"

A few mechanics worth knowing:

- **Gated by Settings → AI Coach → "Enable AI Coaching."** With the toggle off, no artifact context is injected, full stop. The toggle is the on/off switch for the entire feature surface, including this side effect.
- **Skipped for virtual projects.** Omniscio's own UI agents (Automations, Quick Replies, Cron, Tools, etc.) get their own carefully-tuned seed bundles and would not benefit from the user-profile block; injecting it would dilute their instructions.
- **Skipped when another seed already fired.** Cron heal sessions and the SEED_BUNDLE virtual-project flow each prepend their own context to the prompt; if either one already mutated the launch prompt, the artifact block stands down rather than double-injecting.
- **Budget-aware, multi-level.** The whole profile block is fitted to a total budget (`PROFILE_BLOCK_TOTAL_CAP`, 6,000 chars) rather than chopping every artifact at a fixed length. Each artifact is shown at the richest of its four levels — **Full → Key Points → Summary → Micro** — that fits; when the block is over budget the _largest_ artifact is stepped down a level at a time until it fits, so a couple of huge artifacts can't crowd everything else out. If even the smallest levels don't fit, a per-artifact floor truncates with a `... (truncated — open this artifact in AI Coaching for the full text)` marker — nothing silently disappears. You can also override the automatic choice per artifact (see **Choosing a context level** below).
- **Two delivery paths.** If you start the session with an initial prompt (the common case — typing into "Start a new session" or kicking off via Send Later, web access, recipe step, etc.), the profile block is prepended to that prompt before the CLI spawns. If you create a session with no prompt (the deferred-spawn flow — opening an empty session and typing later), the block is held in `pendingPrompt` and folded into your first stdin write so the agent still sees it on turn one.

When you save a new artifact (or bump an existing one's version), future sessions you spawn will see the new content; sessions that were already spawned before the save are not retroactively updated — Claude Code doesn't have a way to amend an in-flight system context, and quietly re-prompting would be confusing.

### Choosing a context level (Full / Key Points / Summary / Micro)

Every saved artifact is automatically condensed into **four** versions, so the same profile can be fed into a session at four different sizes:

- **Full** — the complete artifact text.
- **Key Points** — the essentials as structured bullets.
- **Summary** — a 2–4 paragraph executive summary.
- **Micro** — a one-or-two-sentence one-liner.

The three condensed versions are generated in the background right after an artifact is saved, using a fast, low-cost model with the same proven condensing prompts as ContextDock (the prompts are copied into Omniscio, so nothing is sent to ContextDock's cloud and no ContextDock account is needed). Generation is cost-capped (it shares the `ai-coaching-summary` daily cap), never blocks anything, and skips work it doesn't need: artifacts already too short to condense are left alone, a condensed version that didn't actually come out shorter is discarded, and levels are only re-generated when the artifact's text changes.

**Who picks the level?** By default, **the AI picks for you** — when your profile is injected into a session, the budget-aware selector (above) chooses the richest level of each artifact that fits. But you can override it: open any artifact in the AI Coaching pane and use the labelled **Detail level** control near the top. Pick **Auto** to let the AI choose, or pin **Full / Key Points / Summary / Micro** to force that version into every session. Levels that haven't been generated yet are greyed out. Switching to a shorter level previews it right there in the pane — with a "you're viewing a condensed version" banner so a summary is never mistaken for your full profile — so you can see exactly what each size looks like before pinning it. Your pin is saved per artifact and survives relaunch.

### Session feedback

After an interview completes, a feedback dialog appears showing which interview you just finished (the prompt title) and a 1-to-5 star overall rating. Below the overall rating, three optional aspect ratings let you score conversation quality, artifact quality, and relevance individually. A comment field (up to 2,000 characters) is also available. Submit is disabled until you choose an overall rating; everything else is optional.

If you dismiss the dialog without submitting, your draft (rating, comment, aspect scores) is preserved and restored when the dialog reopens for the same session. Drafts are kept in memory for the current app session (capped at 50).

The dialog auto-triggers when an interview completes. The `useSessionFeedbackTrigger` hook (mounted in `AICoachingRightPane`) listens for `AI_COACHING_ARTIFACTS_CHANGED` pushes, detects a newly completed interview, and shows the dialog after a 2-second delay. If multiple interviews complete in quick succession, only the last one triggers the dialog (the earlier timer is cancelled). Each source session only triggers the dialog once per app session — tracked by `feedbackShownSessionIds` in the store (resets on restart). You can also rate a past interview from the completed card’s overflow menu (“Rate” action).

A “Don’t ask again” checkbox persists `aiCoachingFeedbackDismissed` in settings. When checked (on either submit or close), the auto-trigger is permanently suppressed until the user re-enables it. The feedback dialog lives exclusively in `AICoachingRightPane` (single-owner pattern) — it was removed from `AICoachingDashboard` during consolidation.

Feedback is stored locally in the `ai_coaching_feedback` table (soft-deleted, never physically removed) with columns for each aspect rating. It’s not sent to any external service. Two IPC channels handle it: `AI_COACHING_SUBMIT_FEEDBACK` (write) and `AI_COACHING_LIST_FEEDBACK` (read). Both are desktop-only — blocked from the mobile/web bridge.

Implementation: [SessionFeedbackDialog.tsx](/src/renderer/src/features/ai-coaching/feedback/SessionFeedbackDialog.tsx) (dialog UI, `StarRating`, `DialogShell`), [useSessionFeedbackTrigger.ts](/src/renderer/src/features/ai-coaching/feedback/useSessionFeedbackTrigger.ts) (auto-trigger hook).

### Growth Timeline

The Dashboard shows a **Growth Timeline** strip when the user has at least one artifact. It plots each artifact chronologically as a dot on a horizontal line, sized by version depth (more revisions = larger dot). The strip gives a visual sense of how the coaching journey has evolved over time.

- **Dot positioning** — each artifact is placed at a percentage along the timeline based on its `createdAt` timestamp relative to the earliest and latest artifact. When all artifacts share the same timestamp, dots spread evenly.
- **Version depth** — dot diameter scales from 8px (version 1) to 20px (highest version), using `rgb(var(--accent-rgb))` for theming.
- **Month axis** — when artifacts span multiple months, the first and last month labels appear at the timeline edges; a single month centers.
- **Summary stats** — artifact count, total versions across all artifacts, and day span (only shown when artifacts span multiple days).

The component returns `null` when the artifacts array is empty — no empty-state chrome. It is renderer-only (no IPC call of its own; it reads `artifacts` from the existing store).

Implementation: [GrowthTimeline.tsx](/src/renderer/src/features/ai-coaching/goals/GrowthTimeline.tsx) (pure renderer, `rendererT()` i18n, accessible `role="img"` with aria-label). Tests: [growth-timeline.test.tsx](/tests/unit/features/ai-coaching/growth-timeline.test.tsx).

### Coaching Analytics

The Dashboard includes a read-only **Analytics** section at the bottom, showing four panels that summarize your coaching activity:

- **Sessions per Week** — a horizontal bar chart of how many coaching sessions you ran each ISO week, using the session's `created_at` timestamp grouped by `strftime('%Y-W%W')`. Each bar is proportional to the busiest week.
- **Artifacts by Category** — a horizontal bar chart of how many artifacts you've produced per prompt category (Foundation, Deep Analysis, Work, etc.), sorted largest-first. Hidden when you have no categorized artifacts.
- **Avg Messages / Session** — the mean message count across your coaching sessions, displayed as a single large number (one decimal place).
- **Total Coaching Cost** — cumulative AI spend across all coaching sessions, formatted as `$X.XX`. Sub-cent amounts show `<$0.01`; zero shows `$0.00`. Uses the monetary-integer-twin pattern (`SESSION_COST_MICRO / MICRO_USD_DIVISOR`) from `cost-sql-fragments.ts`.

The section renders only when there is at least one timeline entry or one categorized artifact — otherwise it returns `null` (no empty-state chrome). All four panels are loaded by a single `AI_COACHING_GET_ANALYTICS` IPC call (Zod-validated, feature-gated via `withAICoachingEnabled`), backed by `getCoachingAnalytics()` in `bookmarks-profile.ts`. The store action `loadAnalytics()` fires inside the Dashboard's existing `Promise.all` alongside prompts, progress, artifacts, and core profile.

Implementation: [CoachingAnalyticsSection.tsx](/src/renderer/src/features/ai-coaching/insights/CoachingAnalyticsSection.tsx) (pure CSS bars, memo-wrapped, `rendererT()` i18n, aria-labels).

### Importing existing work

If you've already done coaching work outside of Omniscio (in a standalone Claude session, a Google Doc, or anywhere else), you can import that content directly into a prompt without re-running the full interview. On the Dashboard, any **available** (not yet completed) prompt card shows a **⋯** overflow menu on hover (desktop only). Opening it reveals an **Import existing work** option. Clicking it opens a dialog where you can:

- **Paste** your previously completed content into a textarea, or
- **Load from file** — click the button to pick a `.md`, `.txt`, `.markdown`, or `.json` file from your computer. The file's contents are loaded into the textarea.

The textarea enforces the same **20,000-character cap** as artifact editing. Content that arrives from outside the textarea — a loaded file or a fetched URL — is truncated at that cap, and either way you get a warning toast naming the limit plus a "(truncated)" indicator next to the character counter. The warning is wired to the one place imported content lands rather than to each intake path, so a new intake path cannot ship mute the way the URL fetch did.

Click **Import** (or press **Ctrl+Enter**) and the content is saved as the artifact for that prompt — the prompt immediately shows as completed on the Dashboard, exactly as if you'd run the full interview. The post-save pipeline fires normally: Key Points / Summary / Micro levels are generated in the background, and the Core Profile refreshes if you've crossed the 3-artifact threshold.

Import is **desktop-only** — the overflow menu is hidden on mobile. There is no CLI endpoint for import; the two IPC channels (`AI_COACHING_IMPORT_ARTIFACT`, `AI_COACHING_READ_FILE_FOR_IMPORT`) are blocked from mobile/web access via `BLOCKED_CHANNELS`.

#### Bulk import

For users with many pre-existing artifacts (e.g. migrating from a Google Docs or file-based coaching workflow), the Dashboard header's **Bulk Import** button opens a dialog that handles multiple files at once:

1. **Select files** — a native file picker (`.md`, `.txt`, `.markdown`, `.json`; multi-select). Each file is read via `AI_COACHING_READ_FILES_FOR_BULK_IMPORT` (per-file path-deny check via `userPickedPathDenyReason`, capped at `MAX_IMPORT_BYTES` per file, and at `MAX_BULK_IMPORT_ITEMS` rows per batch). The dialog enforces that batch cap at intake on both paths — the file picker and the URL paste box — and says what it left out, rather than letting the grid fill past it and lose the whole call to schema validation at Import time. Over-cap URLs are handed back to the paste box so a large paste is never lost.
2. **Auto-matching** — each file is scored against available (uncompleted) prompts using the shared `matchFileToPrompts()` from [bulk-import-matching.ts](/src/shared/bulk-import-matching.ts). Matching uses the file name and content title against each prompt's `title` and `producesArtifactTitle`, with confidence levels (high / medium / low) based on score thresholds and the gap to the runner-up.
3. **Confirmation grid** — a table showing each file, its auto-matched prompt (editable via a dropdown), and a confidence badge. Users can reassign any file to a different prompt or skip it. Duplicate detection prevents two files from targeting the same prompt.
4. **Import** — `AI_COACHING_BULK_IMPORT_ARTIFACTS` saves each matched file's content via `saveArtifactVersion`, then emits a single `AI_COACHING_ARTIFACTS_CHANGED` push (not per-file) and refreshes the Core Profile once.

Desktop-only. Implementation: [BulkImportDialog.tsx](/src/renderer/src/features/ai-coaching/artifacts/BulkImportDialog.tsx), matching logic in [bulk-import-matching.ts](/src/shared/bulk-import-matching.ts), handlers in [import-handlers.ts](/src/main/ipc/ai-coaching/import-handlers.ts). Tests: [bulk-import-matching.test.ts](/tests/unit/shared/bulk-import-matching.test.ts), [bulk-import-handlers.test.ts](/tests/unit/ipc/bulk-import-handlers.test.ts).

### KMS vault mirror

When enabled, every artifact save (new version or update) is mirrored as a read-only markdown note in the user's default KMS vault. This makes coaching artifacts searchable, taggable, and bookmarkable alongside regular vault notes — without the user manually copying anything.

The feature is **off by default**. To enable it, flip **Settings → AI Coach → "Mirror artifacts to vault"** (`coachingVaultMirror`). Three conditions must ALL be true for a mirror write to fire:

1. `coachingVaultMirror` setting is **on**.
2. KMS is **enabled** (`isKmsEnabled()`).
3. A **default vault** is configured (`getConfiguredVaultId()` returns non-null).

If any gate fails, the mirror is silently skipped — no error, no toast, no log noise.

Mirror writes are **debounced** (3 seconds) and **fire-and-forget** with an in-flight guard, following the same pattern as profile regeneration and goal extraction in `ai-coaching-artifacts.ts`. A KMS failure never blocks the artifact save pipeline. Each note lands under the `ai-coaching/` folder in the vault with a slug derived from the artifact title plus an 8-character ID suffix (e.g. `ai-coaching/weekly-review-abc12345.md`). The note body carries an HTML comment header (`<!-- AI Coaching artifact: Title, vN -->`) followed by the artifact content. If the note already exists at that path, it's updated in place; otherwise a new note is created.

Implementation: [ai-coaching-vault-mirror.ts](/src/main/services/ai/ai-coaching/data/vault-mirror.ts) (`scheduleMirrorToVault` called from the post-save hook in `ai-coaching-artifacts.ts`). Tests: [ai-coaching-vault-mirror.test.ts](/tests/unit/ai-coaching-vault-mirror.test.ts).

## Related

The interviews that produce these artifacts, and the Dashboard you start them from, are on the [AI Coaching sidebar (interview prompts and artifacts)](ai-coaching.md) page. What the coach remembers between them is in [part 3](ai-coaching-part-3.md), and the CLI surface plus the implementation pointers are in [part 4](ai-coaching-part-4.md).
