# Interview Lifecycle - Technical Manual {#interview-lifecycle---technical-manual}


**Version:** 1.0  
**Last Updated:** 2026-07-16

This guide is intended for Developers, Architects, DevOps, and System Integrators.

---


## Purpose {#purpose}


The `InterviewExecution` entity serves as the aggregate root for all survey interview operations. It represents a single survey session with a respondent and manages the complete lifecycle from creation to final submission.

Unlike traditional form-filling approaches that treat each submission as independent, MQUAD's interview model provides:

- **State Persistence**: Interview progress survives app restarts and connectivity loss
- **Lifecycle Tracking**: Explicit status transitions enable workflow automation
- **Multi-Device Support**: Interviews can be resumed across devices
- **Revision Management**: Rejected interviews maintain context for corrections

---


## Responsibilities {#responsibilities}



### InterviewExecution {#interviewexecution}


The `InterviewExecution` aggregate owns:

- Overall interview status and state machine
- Session token for authentication
- Progress tracking (`current_page`, `answered_count`)
- Response linkage (`current_response_id`)
- Interview metadata and timestamps


### FormSession {#formsession}


The `FormSession` entity manages:

- Runtime form state during filling
- Draft responses while offline
- Position and navigation state
- Session-specific validation cache


### SurveyResponse {#surveyresponse}


The `SurveyResponse` entity owns:

- Final response data after completion
- Status transitions (draft → submitted → approved/rejected)
- Answer records (`ResponseAnswer`)
- Edit history (`ResponseEdit`)
- Review workflow state


### Sync Service {#sync-service}


The sync-service handles:

- Offline queue management
- Conflict detection and resolution
- Device synchronization
- Interview state persistence across devices


### Form Service {#form-service}


The form-service manages:

- Interview lifecycle transitions
- State validation and business rules
- Event publishing for state changes
- Response submission coordination


### Data Service {#data-service}


The data-service manages:

- Response storage and retrieval
- Answer validation against schemas
- Webhook event generation
- Audit trail persistence

---


## State Machine {#state-machine}






```mermaid
stateDiagram-v2
    [*] --> Created: Initial state
    Created --> InProgress: First question answered
    InProgress --> Paused: Explicit pause or timeout
    InProgress --> Interrupted: System error
    InProgress --> Completed: All questions answered
    InProgress --> Terminated: Premature end
    Paused --> InProgress: Resume session
    Interrupted --> InProgress: Recovery action
    Completed --> NeedsRevision: Supervisor rejection
    NeedsRevision --> InProgress: Revision started
    NeedsRevision --> Completed: Revision complete
```





---


## State Transitions {#state-transitions}



### Created → In Progress {#created--in-progress}


| Property | Value |
|----------|-------|
| Trigger | First answer saved or explicit start |
| Validation | Survey must be published |
| Database Updates | `status = 'in_progress'`, `started_at = now()` |
| Events Published | None (implicit in first answer) |
| Services Involved | Form Service |
| Failure Behaviour | Interview remains in "created" state |
| Idempotency | Starting an already-in-progress interview is idempotent |


### In Progress → Paused {#in-progress--paused}


| Property | Value |
|----------|-------|
| Trigger | Explicit pause or session timeout |
| Validation | Interview must exist and be active |
| Database Updates | `status = 'paused'`, `paused_at = now()` |
| Events Published | None |
| Services Involved | Form Service |
| Failure Behaviour | Interview remains in "in_progress" |


### In Progress → Interrupted {#in-progress--interrupted}


| Property | Value |
|----------|-------|
| Trigger | System error or unexpected closure |
| Validation | None required |
| Database Updates | `status = 'interrupted'` |
| Events Published | None |
| Services Involved | Form Service |
| Failure Behaviour | Requires manual recovery |


### In Progress → Completed {#in-progress--completed}


| Property | Value |
|----------|-------|
| Trigger | All questions answered and submit called |
| Validation | All required questions must have answers |
| Database Updates | `status = 'completed'`, `completed_at = now()`, `response_rid`, `response_uid` populated |
| Events Published | `response.submitted` |
| Services Involved | Form Service, Data Service |
| Failure Behaviour | Interview remains in "in_progress" |


### In Progress → Terminated {#in-progress--terminated}


| Property | Value |
|----------|-------|
| Trigger | Explicit termination or system failure |
| Validation | Termination reason required |
| Database Updates | `status = 'terminated'`, `terminated_at = now()`, `termination_reason` set |
| Events Published | None |
| Services Involved | Form Service |
| Failure Behaviour | Interview remains in "in_progress" |


### Completed → Needs Revision {#completed--needs-revision}


| Property | Value |
|----------|-------|
| Trigger | Supervisor rejection |
| Validation | Response must exist and be rejected |
| Database Updates | `status = 'needs_revision'`, `current_response_id` set |
| Events Published | `response.rejected` (triggers interview transition) |
| Services Involved | Data Service (via event), Form Service |
| Failure Behaviour | Interview remains in "completed" |


### Needs Revision → In Progress {#needs-revision--in-progress}


| Property | Value |
|----------|-------|
| Trigger | Enumerator begins revision |
| Validation | Interview must be in "needs_revision" state |
| Database Updates | `status = 'in_progress'` |
| Events Published | None |
| Services Involved | Form Service |


### Needs Revision → Completed {#needs-revision--completed}


| Property | Value |
|----------|-------|
| Trigger | Revision finished and resubmitted |
| Validation | All corrections must be applied |
| Database Updates | `status = 'completed'`, `current_response_id` cleared |
| Events Published | Response update events |
| Services Involved | Form Service, Data Service |

---


## Needs Revision Workflow {#needs-revision-workflow}






```mermaid
sequenceDiagram
    participant Sup as Supervisor
    participant DS as Data Service
    participant Event as Event Bus
    participant FS as Form Service
    participant Enum as Enumerator

    Sup->>DS: PATCH /responses/{id}/reject
    Note over DS: Mark response rejected
    Note over DS: Publish response.rejected event

    Event-->>FS: Receive response.rejected
    Note over FS: Lookup InterviewExecution<br/>by current_response_id

    FS->>FS: Update status to needs_revision
    Note over FS: Set current_response_id

    Enum->>FS: GET /interviews/{id}
    Note over FS: Return interview with needs_revision status

    Enum->>FS: PATCH /interviews/{id}/transition
    Note over FS: Set status = in_progress

    Enum->>FS: PATCH /responses/{id}/edit
    Note over FS: Apply corrections

    Enum->>FS: POST /interviews/{id}/complete
    Note over FS: Clear current_response_id<br/>Set completed_at
```





---


## Aggregate Design {#aggregate-design}



### InterviewExecution as Aggregate Root {#interviewexecution-as-aggregate-root}


`InterviewExecution` is the aggregate root because:

- Single source of truth for interview lifecycle
- Clear ownership boundary for state transitions
- Facilitates optimistic locking via version columns
- Enables CQRS pattern for query separation


### SurveyResponse Separation {#surveyresponse-separation}


`SurveyResponse` is separate because:

- Responses may exist without interviews (direct submission)
- Audit trail requires immutable response history
- Multiple interview revisions map to one response chain
- Review workflow operates at response level


### FormSession Runtime State {#formsession-runtime-state}


`FormSession` owns runtime state because:

- Sessions are ephemeral compared to interviews
- Multiple sessions may reference one interview
- Session data is cache-like and can be rebuilt
- Separation enables efficient cleanup

---


## Event Driven Architecture {#event-driven-architecture}



### Events {#events}


| Event | Source | Trigger |
|-------|--------|---------|
| `response.submitted` | data-service | Response created |
| `response.approved` | data-service | Supervisor approves |
| `response.rejected` | data-service | Supervisor rejects |
| `response.edited` | data-service | Response modified |
| `interview.created` | form-service | Interview initialized |
| `interview.started` | form-service | Status → in_progress |
| `interview.completed` | form-service | Status → completed |
| `interview.paused` | form-service | Status → paused |
| `interview.terminated` | form-service | Status → terminated |
| `interview.needs_revision` | form-service | Response rejection handler |

---


## Session Management {#session-management}



### Heartbeat {#heartbeat}


Heartbeats are sent every 30 seconds while interviews are in progress:

- Updates `last_heartbeat_at` timestamp
- Enables crash detection and recovery
- Prevents stale session conflicts
- Triggers timeout if missed for > 2 minutes


### Pause {#pause}


Pausing preserves interview state:

- No heartbeats sent while paused
- Interview unlocked but locked to session
- Draft data saved locally
- Can be resumed across devices


### Resume {#resume}


Resuming validates session integrity:

- Checks interview is in pausable state
- Verifies no other active session exists
- Loads latest draft data
- Begins heartbeats immediately


### Timeouts {#timeouts}


Timeout rules:

- 2 minutes without heartbeat → session considered stale
- 24 hours in "created" → auto-pause
- 7 days without activity → session cleanup


### Multi-Tab Synchronization {#multi-tab-synchronization}


Tab ownership is enforced via:

- Single active tab per interview
- Tab switch triggers heartbeat
- Lost tab → automatic recovery

---


## Offline Behaviour {#offline-behaviour}



### Offline Edits {#offline-edits}


While offline:

- Answers saved to local SQLite database
- Interview state updated locally
- Draft responses created/updated
- Sync queue populated for later submission


### Draft Saving {#draft-saving}


Draft saving strategy:

- Every answered question triggers save
- Position state stored with each answer
- Conflict-free merge on sync
- Retry logic for failed submissions


### Sync {#sync}


Sync process:

- Queue processed on connectivity restoration
- Conflicts detected and recorded
- Responses submitted with idempotency
- Interview status updated on success


### Conflict Handling {#conflict-handling}


Conflict detection:

- HTTP 409 on push triggers conflict creation
- Both versions preserved in `SyncConflict`
- Interview status set to indicate conflict
- Supervisor notification (future feature)

---


## Sequence Diagrams {#sequence-diagrams}



### Normal Interview {#normal-interview}






```mermaid
sequenceDiagram
    participant Enum as Enumerator
    participant FS as Form Service
    participant DS as Data Service
    participant SS as Sync Service

    Enum->>FS: POST /interviews (survey_id, respondent)
    FS-->>Enum: {id, session_token}

    loop Question Loop
        Enum->>FS: PATCH /interviews/{id} (answers)
        Note over FS: Update progress<br/>Save heartbeat
    end

    Enum->>FS: POST /interviews/{id}/complete
    FS->>DS: POST /responses (answers)
    DS-->>FS: {response_id}
    FS-->>Enum: Success

    Enum->>SS: POST /sync/push
    SS->>DS: Forward response
    DS-->>SS: 201 Created
    SS-->>Enum: Status completed
```






### Rejected Interview {#rejected-interview}






```mermaid
sequenceDiagram
    participant Sup as Supervisor
    participant DS as Data Service
    participant Event as Event Bus
    participant FS as Form Service
    participant Enum as Enumerator

    Sup->>DS: PATCH /responses/{id}/reject
    Note over DS: Set status = rejected

    DS->>Event: Publish response.rejected
    Event-->>FS: Subscribe

    Note over FS: Lookup interview<br/>Set needs_revision<br/>Store response_id
```






### Revision Resume {#revision-resume}






```mermaid
sequenceDiagram
    participant Enum as Enumerator
    participant FS as Form Service

    Enum->>FS: GET /interviews/{id}
    Note over FS: Check needs_revision<br/>Return editable questions

    Enum->>FS: PATCH /interviews/{id}/transition
    Note over FS: Status → in_progress

    Enum->>FS: PATCH /responses/{id}/edit
    Note over FS: Apply corrections
```





---


## State Diagram {#state-diagram}






```mermaid
stateDiagram-v2
    [*] --> Created
    Created --> InProgress: Start Interview
    InProgress --> Paused: Pause
    InProgress --> Interrupted: System Error
    InProgress --> Completed: All Answered
    InProgress --> Terminated: Terminate
    Paused --> InProgress: Resume
    Interrupted --> InProgress: Recovery
    Completed --> NeedsRevision: Rejected
    NeedsRevision --> InProgress: Start Revision
    NeedsRevision --> Completed: Revision Done
```





---


## Error Handling {#error-handling}



### Illegal Transitions {#illegal-transitions}


Invalid transitions return HTTP 400 with descriptive messages. Common invalid transitions:

- Created → Completed (must go through In Progress)
- Paused → Completed (must resume first)
- Needs Revision → Terminated (revision required)


### Stale Sessions {#stale-sessions}


Stale session detection:

- Heartbeat timeout triggers stale flag
- New session requests denied if active exists
- Supervisor can force session reset


### Revision Failures {#revision-failures}


Revision failure handling:

- Validation errors prevent transition
- Original rejection reason preserved
- Revision attempt logged


### Sync Failures {#sync-failures}


Sync failure behavior:

- Failed pushes remain in queue
- Exponential backoff retry
- Permanent failure after max attempts


### Concurrent Edits {#concurrent-edits}


Concurrent edit handling:

- Last-write-wins with conflict detection
- Conflicts recorded for supervision
- Interview state remains consistent

---


## Performance Considerations {#performance-considerations}



### Heartbeat Frequency {#heartbeat-frequency}


- 30-second intervals balance load and safety
- Batch updates reduce database writes
- Connection pooling handles concurrent heartbeats


### Database Writes {#database-writes}


Optimizations:

- Single UPDATE per answer save
- Progress tracked incrementally
- Batch inserts for multiple answers


### Polling {#polling}


Client polling strategy:

- Long polling for interview status
- WebSocket consideration for future
- Backoff on connection loss


### Event Processing {#event-processing}


Event processing:

- Async handlers for non-critical updates
- Dead letter queue for failures
- Idempotent event handlers


### Redis {#redis}


Redis usage:

- Session state cache
- Heartbeat tracking
- Queue management for sync
