# Export & Ticket Note Improvements — Merged Specification

> **Date:** February 13, 2026  
> **Status:** Draft — Pending Implementation  
> **Source:** Merged from two independent improvement proposals  
> **Scope:** Backend export generators, frontend export UX, session model changes  
> **Dependencies:** Existing session export system (`sessions.py`), `SessionExport` schema, `TreeNavigationPage`, `SessionDetailPage`

---

## Problem Statement

ResolutionFlow's export system currently treats sessions as all-or-nothing: either a session is complete and exports fully, or it's incomplete and exports with no indication of status, missing context, or next steps. This creates several gaps for MSP engineers:

1. **No mid-session export** — Engineers pulled away mid-troubleshooting can't grab progress notes from the active navigation page without leaving the flow.
2. **Outcome notes silently dropped** — Engineers write outcome notes in the completion modal, but these never appear in exported ticket notes.
3. **No follow-up / next steps section** — MSP tickets almost always need follow-up actions documented, but the export has no dedicated field for this.
4. **No export control granularity** — No way to export a subset of steps, control verbosity, or review/redact content before copying to a ticket system.
5. **Custom steps not differentiated** — Steps added by the engineer during a session (via Step Library) look identical to tree-authored steps in the export.

---

## Feature Summary

| # | Feature | Priority | Effort | Source |
|---|---------|----------|--------|--------|
| 1 | Mid-session export from TreeNavigationPage | High | Medium | Proposal 2 |
| 2 | Partial export with step cutoff | High | Small | Proposal 1 |
| 3 | Include outcome_notes in export | High | Small | Both |
| 4 | Next Steps / Follow-Up field + export section | High | Medium | Proposal 2 + new DB field |
| 5 | Structured Ticket Summary block | Medium | Medium | Proposal 1 |
| 6 | Custom step differentiation in export | Medium | Small | Proposal 2 |
| 7 | Verbosity / detail level controls | Medium | Medium | Proposal 1 |
| 8 | Editable preview before copy | Medium | Medium | Both |
| 9 | Sensitive data review / redaction | Low | Large | Proposal 1 |

---

## Implementation Phases

### Phase A: Core Export Gaps (High Priority)

These address missing data in exports — things that should be there today but aren't.

#### A1. Include Outcome Notes in Export

**Problem:** `outcome_notes` is captured during session completion but not rendered in any export format.

**Current state:** The session model already has `outcome_notes` as a `Text` column, and `SessionComplete` already accepts `outcome_notes: Optional[str]`. However, none of the four export generators (`generate_markdown_export`, `generate_text_export`, `generate_html_export`, `generate_psa_export`) in `backend/app/services/export_service.py` reference it.

> **VERIFIED:** `outcome_notes` exists on the Session model and SessionComplete schema. No migration needed for this field — only export generator changes.

**Changes required:**

- **Backend:** Add an "Outcome / Resolution Notes" section to all four export generators (markdown, text, HTML, PSA), rendered after the Troubleshooting Steps section and before any Next Steps section. Only render if `outcome_notes` is non-empty. For PSA format, update the existing `--- RESOLUTION ---` section to use `outcome_notes` instead of the last decision's answer.
- **Schema:** Add `include_outcome_notes: bool = True` to `SessionExport`.

**Markdown output example:**
```markdown
## Resolution

Replaced failed DIMM in slot A2. Memtest passed 3 cycles post-replacement.
Server returned to production at 14:45.
```

#### A2. Next Steps / Follow-Up Field

**Problem:** MSP tickets almost always need follow-up actions documented ("Monitor for 24 hours", "Schedule firmware update for maintenance window", "Escalate to vendor if recurs"), but there is no field for this in the session model or the export.

**Changes required:**

- **Database:** Add `next_steps` column to the `sessions` table as a new `Text` field (nullable, `server_default=sa.text("''")`), following the same pattern as the `scratchpad` column.
- **Migration:** Alembic migration to add the column with backfill of existing rows.
- **Schema:** Add `next_steps: Optional[str] = None` to `SessionUpdate`. Add `next_steps: str = ""` with normalizing validator to `SessionResponse`.
- **Frontend — Completion modal:** Add a "Next Steps / Follow-Up" text area to the session completion flow, so engineers can capture follow-up actions at the same time they write outcome notes.
- **Frontend — Session detail:** Display next steps in the session detail view.
- **Backend — Export:** Add a "Next Steps" section to all four export generators (markdown, text, HTML, PSA), rendered after the Resolution section. Only render if non-empty.

**Markdown output example:**
```markdown
## Next Steps

- Monitor Event Log for Event ID 41 recurrence over next 48 hours
- Schedule firmware update for next maintenance window (Feb 20)
- If issue recurs, escalate to Dell ProSupport case #SR-4482991
```

#### A3. Mid-Session Export from TreeNavigationPage

**Problem:** Export currently only works from `SessionDetailPage` after navigating away from the tree. If an engineer gets pulled away mid-troubleshooting, they can't grab what they've done so far without leaving the flow.

**Changes required:**

- **Frontend — TreeNavigationPage:** Add a "Copy for Ticket" button (same pattern as the existing one on `SessionDetailPage`) that exports steps completed up to the current point.
- **Export behavior:** Uses the existing export endpoint but the session is still in-progress. The export should:
  - Include a `**Status:** In Progress` indicator in the header metadata.
  - Show "Session still in progress — exported at step N of path" note.
  - Include all decisions recorded so far.
  - Include scratchpad content captured so far.
  - NOT mark the session as `exported = True` (since it's still active).
- **Backend:** The export endpoint currently sets `session.exported = True` unconditionally. Add logic: only set `exported = True` if the session has a `completed_at` timestamp. Alternatively, add an `include_in_progress_header: bool = False` option to `SessionExport` that the TreeNavigationPage sets to `True`.

**UX detail:** The button should be accessible but not prominent — engineers shouldn't accidentally think it ends their session. A secondary/outline-style button with a clipboard icon in the navigation page toolbar is appropriate.

#### A4. Partial Export with Step Cutoff

**Problem:** When reviewing a completed (or abandoned) session, there's no way to export only the first N steps — useful for escalation handoff snapshots.

**Changes required:**

- **Schema:** Add `max_step_index: Optional[int] = None` to `SessionExport`. This is 1-based and inclusive (e.g., `max_step_index=3` exports steps 1, 2, and 3).
- **Backend:** All four export generators slice the `session.decisions` list by `max_step_index` before iterating.
- **Validation:** If `max_step_index` is provided, it must be >= 1. Values greater than the actual decision count are clamped to the full list (no error). Zero or negative values return a 422 validation error.
- **Frontend — SessionDetailPage:** Add an "Export through step N" dropdown/slider control in the export options area. Default: all steps (no cutoff).

---

### Phase B: Export Quality & Readability (Medium Priority)

These improve the usefulness and polish of exports for their audience (dispatchers, managers, ticket reviewers).

#### B1. Structured Ticket Summary Block

**Problem:** Exports dive straight into step-by-step detail, which is great for engineers reviewing their own work but overwhelming for dispatchers and managers who need a quick overview.

**Changes required:**

- **Schema:** Add `include_summary: bool = False` to `SessionExport`.
- **Backend:** When enabled, generate a "Summary" section at the top of the export (after metadata, before Evidence/Steps) with these fields:
  - **Issue:** Auto-populated from tree name/description.
  - **Impact:** Blank by default (user-editable in preview).
  - **Current Status:** "Resolved" if `completed_at` is set, otherwise "In Progress — paused at step N".
  - **Resolution:** Auto-populated from `outcome_notes` if available.
  - **Next Steps:** Auto-populated from `next_steps` field if available.
- **Frontend:** When summary is enabled, show an editable preview of these fields before export, allowing the engineer to fill in blanks or adjust auto-populated values.

**Markdown output example:**
```markdown
## Summary

| Field | Detail |
|-------|--------|
| **Issue** | VPN Connection Failure |
| **Impact** | User unable to access internal resources remotely |
| **Status** | Resolved |
| **Resolution** | DNS misconfiguration on VPN adapter — updated DNS servers |
| **Next Steps** | Monitor for recurrence over 48 hours |
```

#### B2. Custom Step Differentiation in Export

**Problem:** When engineers add custom steps during a session (via the Step Library), those steps are stored in `custom_steps` but the export treats all decisions identically. Ticket reviewers have no visibility into where the engineer deviated from the standard path.

**Changes required:**

- **Backend:** In all four export generators, detect custom steps by checking if `node_id` starts with `custom-` (this is the canonical marker — custom steps always use `custom-{uuid}` as their node_id).
- **Markdown format:** Prefix custom steps with `[CUSTOM]` in the heading and add an italic note.
- **HTML format:** Add a visual badge/tag (styled like the frontend's purple custom step badge).
- **Text format:** Prefix with `[CUSTOM]` label.

**Markdown output example:**
```markdown
### Step 5: [CUSTOM] Check Additional Event Logs
*Custom step added by engineer*

**Action:** Reviewed Application log for correlated errors
**Notes:** Found repeated .NET runtime errors starting 2 hours before reported issue
```

#### B3. Verbosity / Detail Level Controls

**Problem:** Exports can become extremely long when sessions include command outputs, detailed scratchpad notes, and many steps. Pasting a wall of text into a ConnectWise ticket isn't practical.

**Changes required:**

- **Schema:** Add `detail_level: Literal["summary", "standard", "full"] = "standard"` to `SessionExport`.
- **Backend behavior by level:**
  - **summary:** Header metadata + Summary block (auto-enabled) + Resolution + Next Steps. No individual step details. Designed for management/dispatch visibility.
  - **standard:** Everything in summary, plus all troubleshooting steps with notes. Command outputs longer than 5 lines are truncated with "*(full output omitted)*". This is the default and matches current behavior plus new sections.
  - **full:** Everything included with no truncation. Command outputs, scratchpad, all notes rendered in full. Designed for detailed review or archival.
- **Frontend:** Add a detail level selector (3-option toggle or dropdown) in the export options area on both `SessionDetailPage` and `TreeNavigationPage`.

#### B4. Editable Preview Before Copy

**Problem:** The "Copy for Ticket" button generates content and copies it to the clipboard immediately with no chance to review. Engineers often need to clean up notes, add context, or remove sensitive info before pasting into a PSA.

**Changes required:**

- **Frontend — ExportPreviewModal enhancement:** Instead of the current read-only preview modal, make the preview content editable. The flow becomes:
  1. Engineer clicks "Copy for Ticket" or "Preview".
  2. Modal opens with generated export content in an editable text area.
  3. Engineer reviews and optionally edits the content.
  4. Engineer clicks "Copy" (copies the edited version) or "Download" (saves the edited version).
- **Important:** Edits in the preview are NOT saved back to the session. This is a one-way "edit before copy" flow.
- **The existing "Copy" button** (direct copy without preview) should remain available for engineers who want the quick path. The preview/edit step is opt-in.

---

### Phase C: Advanced Features (Lower Priority)

#### C1. Sensitive Data Review / Redaction

**Problem:** Scratchpad and command outputs encourage capturing detailed data (IPs, hostnames, tokens, account IDs), but ticket systems often need sanitized notes. Engineers currently have to manually scan and redact before pasting.

**Changes required:**

- **Schema:** Add `redaction_mode: Literal["none", "mask"] = "none"` to `SessionExport`.
- **Backend — Redaction pipeline:** When `redaction_mode="mask"`, apply regex-based detection and masking of common sensitive patterns before returning export content:
  - IPv4/IPv6 addresses → `[IP REDACTED]`
  - Email addresses → `[EMAIL REDACTED]`
  - Common token/key patterns (API keys, bearer tokens) → `[TOKEN REDACTED]`
  - UNC paths and hostnames → optionally masked (configurable)
- **Frontend — Preview integration:** When the editable preview modal is open, add a toggle for "Show sensitive data highlights." When enabled, likely sensitive values are visually highlighted (yellow background or similar). A "Mask All" button applies redaction. Individual items can be toggled on/off.
- **Copy actions:** "Copy" copies current content as-is. "Copy Redacted" applies the masking pipeline to whatever is currently in the editor.

**Implementation note:** Start with a conservative set of regex patterns. False positives (masking things that aren't sensitive) are annoying but safe. False negatives (missing real sensitive data) are the actual risk. The editable preview (B4) gives engineers a safety net regardless.

---

## Data Model Changes

### New Database Columns

| Column | Table | Type | Default | Migration Required |
|--------|-------|------|---------|--------------------|
| `outcome_notes` | sessions | Text, nullable | `''` | No — already exists  |
| `next_steps` | sessions | Text, nullable | `''` | Yes |

Both columns follow the same pattern as the existing `scratchpad` column: `Text` type, nullable, `server_default=sa.text("''")`, with backfill of existing rows in the migration.

### Schema Changes — `SessionExport`

Current:
```python
class SessionExport(BaseModel):
    format: str = Field(default="markdown", pattern="^(text|markdown|html|psa)$")
    include_timestamps: bool = True
    include_tree_info: bool = True
```

Updated:
```python
class SessionExport(BaseModel):
    format: str = Field(default="markdown", pattern="^(text|markdown|html|psa)$")
    include_timestamps: bool = True
    include_tree_info: bool = True
    
    # Phase A additions
    include_outcome_notes: bool = True
    max_step_index: Optional[int] = Field(None, ge=1, description="1-based inclusive step cutoff")
    
    # Phase B additions
    include_summary: bool = False
    detail_level: Literal["summary", "standard", "full"] = "standard"
    
    # Phase C additions
    redaction_mode: Literal["none", "mask"] = "none"
```

### Schema Changes — `SessionUpdate` and `SessionResponse`

Add to `SessionUpdate`:
```python
outcome_notes: Optional[str] = None
next_steps: Optional[str] = None
```

Add to `SessionResponse`:
```python
outcome_notes: str = ""
next_steps: str = ""

@validator('outcome_notes', 'next_steps', pre=True, always=True)
def normalize_text_fields(cls, v):
    return v or ""
```

### Session Completion Endpoint

Update `POST /sessions/{id}/complete` to accept:
```python
class SessionComplete(BaseModel):
    outcome_notes: Optional[str] = None
    next_steps: Optional[str] = None
```

---

## Export Output Structure (All Formats)

After all phases, the full export structure in order:

```
1. Header Metadata (tree name, ticket #, client, timestamps, status)
2. Summary Block (Phase B1, optional — enabled by include_summary or detail_level=summary)
3. Evidence / Reference (existing scratchpad section)
4. Troubleshooting Steps (existing, enhanced with custom step markers and step cutoff)
5. Resolution / Outcome Notes (Phase A1)
6. Next Steps / Follow-Up (Phase A2)
7. Session Duration (existing timestamp-derived, shown when include_timestamps=true)
```

### Markdown Example — Complete Session, Standard Detail

```markdown
# VPN Connection Failure

**Ticket:** SR-2847  
**Client:** Contoso Ltd  
**Started:** 2026-02-13 09:15  
**Completed:** 2026-02-13 09:42  
**Status:** Resolved

---

## Evidence / Reference

- Server IP: 10.0.1.50
- VPN Client: GlobalProtect 6.2.1
- Affected user: jsmith@contoso.com

---

## Troubleshooting Steps

### Step 1: Is the VPN client installed and up to date?
**Answer:** Yes
**Notes:** GlobalProtect 6.2.1 confirmed

### Step 2: Can the user reach the VPN gateway?
**Answer:** Yes
**Notes:** Ping to vpn.contoso.com successful

### Step 3: Check DNS configuration on VPN adapter
**Answer:** DNS servers incorrect
**Notes:** VPN adapter had 8.8.8.8 instead of internal DC

### Step 4: [CUSTOM] Verify DNS propagation after fix
*Custom step added by engineer*
**Action:** Ran nslookup against internal resources
**Notes:** All internal names resolving correctly after DNS update

---

## Resolution

Updated DNS configuration on VPN adapter to point to internal DCs (10.0.1.10, 10.0.1.11).
Flushed DNS cache. Verified internal name resolution working. User confirmed full access restored.

---

## Next Steps

- Monitor for recurrence over next 48 hours
- Check if GPO is failing to push correct DNS settings to this machine
- Schedule follow-up with user on Friday if no recurrence
```

### Markdown Example — In-Progress Session (Mid-Session Export)

```markdown
# VPN Connection Failure

**Ticket:** SR-2847  
**Client:** Contoso Ltd  
**Started:** 2026-02-13 09:15  
**Status:** In Progress (exported at step 3)

---

## Evidence / Reference

- Server IP: 10.0.1.50
- VPN Client: GlobalProtect 6.2.1

---

## Troubleshooting Steps

### Step 1: Is the VPN client installed and up to date?
**Answer:** Yes

### Step 2: Can the user reach the VPN gateway?
**Answer:** Yes

### Step 3: Check DNS configuration on VPN adapter
**Answer:** DNS servers incorrect
**Notes:** VPN adapter had 8.8.8.8 instead of internal DC — investigating
```

---

## Frontend Changes Summary

| Page | Change | Phase |
|------|--------|-------|
| `TreeNavigationPage` | Add "Copy for Ticket" button with in-progress export | A |
| `SessionDetailPage` | Add step cutoff control to export options | A |
| `SessionDetailPage` | Add detail level selector | B |
| `ExportPreviewModal` | Make preview content editable before copy | B |
| `ExportPreviewModal` | Add sensitive data highlighting and mask toggle | C |
| Session completion modal | Add "Next Steps" text area | A |
| Session completion modal | Ensure outcome_notes are saved (verify current behavior) | A |

---

## Test Cases

### Phase A Tests

1. **Outcome notes in export** — Completed session with outcome_notes includes "Resolution" section in markdown, text, and HTML formats. Session without outcome_notes omits the section cleanly.
2. **Next steps in export** — Session with next_steps includes "Next Steps" section in all formats. Empty next_steps omits the section.
3. **Mid-session export** — Exporting an in-progress session includes "In Progress" status, completed steps only, and does NOT set `exported = True`.
4. **Partial export** — `max_step_index=3` returns only first 3 decisions. `max_step_index` greater than decision count returns all decisions (no error). `max_step_index=0` or negative returns 422.
5. **Backward compatibility** — Existing export calls with no new parameters produce identical output to current behavior. All existing export tests continue to pass.

### Phase B Tests

6. **Summary block** — `include_summary=True` adds Summary table at top with auto-populated fields.
7. **Custom step marking** — Decisions from custom steps are prefixed with `[CUSTOM]` in markdown/text and styled with a badge in HTML.
8. **Detail levels** — `summary` excludes step details. `standard` truncates long outputs. `full` includes everything.
9. **Editable preview** — Preview modal allows text editing. Copied content reflects edits. Original session data is unchanged.

### Phase C Tests

10. **Redaction** — `redaction_mode=mask` replaces IP addresses, emails, and token patterns with `[REDACTED]` placeholders. `redaction_mode=none` returns original content unchanged.

---

## Assumptions and Defaults

- All new `SessionExport` fields have backward-compatible defaults — existing integrations are unaffected.
- Mid-session export from `TreeNavigationPage` uses the existing export API endpoint but requires a backend change: only set `exported=True` when session has `completed_at`.
- The `next_steps` field is a new dedicated column on the session model (not piggyback on scratchpad or outcome_notes).
- Initial scope covers the session detail export flow and the active navigation page. Bulk export or scheduled reports are out of scope.
- Redaction (Phase C) starts with conservative regex patterns; false positives are preferred over false negatives.
- Export logic lives in `backend/app/services/export_service.py` with four generators (markdown, text, HTML, PSA). The export endpoint in `backend/app/api/endpoints/sessions.py` calls these generators.

---

## Open Questions

1. ~~**Outcome notes storage:**~~ **RESOLVED** — `outcome_notes` already exists on the Session model and `SessionComplete` schema. No migration needed.
2. ~~**PSA export format:**~~ **RESOLVED** — `generate_psa_export` exists in `export_service.py` and is fully functional. All four formats (markdown, text, HTML, PSA) must receive the same improvements.
3. **Step cutoff UX:** Should the step cutoff be a simple number input, a dropdown of step numbers with labels, or a clickable timeline in the session detail view?
4. **Redaction scope:** Should hostname redaction be on by default in mask mode, or opt-in? MSP ticket notes often legitimately need hostnames for context.

---

## Locked Decisions

1. **`next_steps` is frozen after completion.** The `update_session` endpoint blocks updates to completed sessions (`sessions.py:190`). `next_steps` follows the same lifecycle — engineers set it during or at completion, not after. If post-completion editing is needed later, a dedicated PATCH endpoint for completion fields can be added as a separate feature.
2. **Custom step detection:** The canonical marker is `node_id` starting with `custom-`. No ambiguity — this is what the frontend generates for all custom steps.
3. **PSA format included everywhere.** All four generators (markdown, text, HTML, PSA) receive every improvement. The PSA `--- RESOLUTION ---` section uses `outcome_notes` when available, falling back to last decision answer for backward compatibility.
4. **Redaction (Phase C) is server-side only.** The editable preview (Phase B4) is a client-side text area — redaction applies to the generated content before it reaches the preview. Edits in the preview are not re-processed.
5. **Export audit trail is out of scope.** The boolean `exported` field is sufficient for Phase A. Richer audit (who/when/options) can be a Phase D feature if needed.
6. **Selective step inclusion (checkbox/range) is out of scope.** `max_step_index` covers the escalation handoff use case. Non-linear step selection adds significant UX complexity for marginal value.
7. **Export presets by PSA destination are out of scope.** The PSA format already targets ConnectWise-style tools. Destination-specific presets can be added when PSA integrations ship (Phase 4 roadmap).
