23 KiB
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),SessionExportschema,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:
- No mid-session export — Engineers pulled away mid-troubleshooting can't grab progress notes from the active navigation page without leaving the flow.
- Outcome notes silently dropped — Engineers write outcome notes in the completion modal, but these never appear in exported ticket notes.
- No follow-up / next steps section — MSP tickets almost always need follow-up actions documented, but the export has no dedicated field for this.
- No export control granularity — No way to export a subset of steps, control verbosity, or review/redact content before copying to a ticket system.
- 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_notesexists 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_notesis non-empty. For PSA format, update the existing--- RESOLUTION ---section to useoutcome_notesinstead of the last decision's answer. - Schema: Add
include_outcome_notes: bool = TruetoSessionExport.
Markdown output example:
## 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_stepscolumn to thesessionstable as a newTextfield (nullable,server_default=sa.text("''")), following the same pattern as thescratchpadcolumn. - Migration: Alembic migration to add the column with backfill of existing rows.
- Schema: Add
next_steps: Optional[str] = NonetoSessionUpdate. Addnext_steps: str = ""with normalizing validator toSessionResponse. - 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:
## 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 Progressindicator 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).
- Include a
- Backend: The export endpoint currently sets
session.exported = Trueunconditionally. Add logic: only setexported = Trueif the session has acompleted_attimestamp. Alternatively, add aninclude_in_progress_header: bool = Falseoption toSessionExportthat the TreeNavigationPage sets toTrue.
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] = NonetoSessionExport. This is 1-based and inclusive (e.g.,max_step_index=3exports steps 1, 2, and 3). - Backend: All four export generators slice the
session.decisionslist bymax_step_indexbefore iterating. - Validation: If
max_step_indexis 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 = FalsetoSessionExport. - 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_atis set, otherwise "In Progress — paused at step N". - Resolution: Auto-populated from
outcome_notesif available. - Next Steps: Auto-populated from
next_stepsfield 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:
## 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_idstarts withcustom-(this is the canonical marker — custom steps always usecustom-{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:
### 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"toSessionExport. - 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
SessionDetailPageandTreeNavigationPage.
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:
- Engineer clicks "Copy for Ticket" or "Preview".
- Modal opens with generated export content in an editable text area.
- Engineer reviews and optionally edits the content.
- 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"toSessionExport. - 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)
- IPv4/IPv6 addresses →
- 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:
class SessionExport(BaseModel):
format: str = Field(default="markdown", pattern="^(text|markdown|html|psa)$")
include_timestamps: bool = True
include_tree_info: bool = True
Updated:
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:
outcome_notes: Optional[str] = None
next_steps: Optional[str] = None
Add to SessionResponse:
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:
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
# 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)
# 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
- 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.
- Next steps in export — Session with next_steps includes "Next Steps" section in all formats. Empty next_steps omits the section.
- Mid-session export — Exporting an in-progress session includes "In Progress" status, completed steps only, and does NOT set
exported = True. - Partial export —
max_step_index=3returns only first 3 decisions.max_step_indexgreater than decision count returns all decisions (no error).max_step_index=0or negative returns 422. - 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
- Summary block —
include_summary=Trueadds Summary table at top with auto-populated fields. - Custom step marking — Decisions from custom steps are prefixed with
[CUSTOM]in markdown/text and styled with a badge in HTML. - Detail levels —
summaryexcludes step details.standardtruncates long outputs.fullincludes everything. - Editable preview — Preview modal allows text editing. Copied content reflects edits. Original session data is unchanged.
Phase C Tests
- Redaction —
redaction_mode=maskreplaces IP addresses, emails, and token patterns with[REDACTED]placeholders.redaction_mode=nonereturns original content unchanged.
Assumptions and Defaults
- All new
SessionExportfields have backward-compatible defaults — existing integrations are unaffected. - Mid-session export from
TreeNavigationPageuses the existing export API endpoint but requires a backend change: only setexported=Truewhen session hascompleted_at. - The
next_stepsfield 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.pywith four generators (markdown, text, HTML, PSA). The export endpoint inbackend/app/api/endpoints/sessions.pycalls these generators.
Open Questions
Outcome notes storage:RESOLVED —outcome_notesalready exists on the Session model andSessionCompleteschema. No migration needed.PSA export format:RESOLVED —generate_psa_exportexists inexport_service.pyand is fully functional. All four formats (markdown, text, HTML, PSA) must receive the same improvements.- 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?
- 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
next_stepsis frozen after completion. Theupdate_sessionendpoint blocks updates to completed sessions (sessions.py:190).next_stepsfollows 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.- Custom step detection: The canonical marker is
node_idstarting withcustom-. No ambiguity — this is what the frontend generates for all custom steps. - PSA format included everywhere. All four generators (markdown, text, HTML, PSA) receive every improvement. The PSA
--- RESOLUTION ---section usesoutcome_noteswhen available, falling back to last decision answer for backward compatibility. - 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.
- Export audit trail is out of scope. The boolean
exportedfield is sufficient for Phase A. Richer audit (who/when/options) can be a Phase D feature if needed. - Selective step inclusion (checkbox/range) is out of scope.
max_step_indexcovers the escalation handoff use case. Non-linear step selection adds significant UX complexity for marginal value. - 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).