# How the Pack Cockpit Persists Lieutenant Chat and Injects Responses in SwarmForge

> Discover how the Pack Cockpit persists lieutenant chat and injects responses in SwarmForge. Learn about the text file persistence and pane-status-lines-for function.

- Repository: [Robert C. Martin/swarm-forge](https://github.com/unclebob/swarm-forge)
- Tags: internals
- Published: 2026-08-30

---

**The pack cockpit persists lieutenant chat by writing messages to a text file in [`.swarmforge/sessions/lieutenant/pane.txt`](https://github.com/unclebob/swarm-forge/blob/main/.swarmforge/sessions/lieutenant/pane.txt) and injects responses by reading that same file via the `pane-status-lines-for` function.**

The **pack cockpit** is the browser-based dashboard that visualizes SwarmForge state. Understanding how the pack cockpit persists lieutenant chat and injects responses reveals a lightweight, file-based approach that enables durable, real-time chat functionality without complex database infrastructure.

## Persisting Lieutenant Chat to Disk

When a lieutenant pane is created, SwarmForge writes chat lines to a file inside the project's hidden `.swarmforge` directory:

```

.swarmforge/sessions/lieutenant/pane.txt

```

This plain-text storage provides durability across sessions. The conversation history survives browser refreshes and system restarts because it lives on disk rather than in volatile memory.

The `pane-status-lines-for` function reads this file and returns the lines as a list of status strings. In [`swarmforge/scripts/pack_web.bb`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/pack_web.bb), line 803 implements this retrieval:

```clojure
:lieutenant_status (pane-status-lines-for root "lieutenant")

```

This key-value pair becomes part of the data object served to the cockpit interface.

## Injecting Responses into the Cockpit UI

The cockpit's HTML page ([[`swarmforge/scripts/pack/dashboard.html`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/pack/dashboard.html)](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/pack/dashboard.html)) expects the `lieutenant_status` field. When the page loads, JavaScript pulls the data and renders each line in the cockpit area.

Line 1372 of [`dashboard.html`](https://github.com/unclebob/swarm-forge/blob/main/dashboard.html) shows the initialization:

```javascript
const statusLines = data.lieutenant_status || [];

```

The front-end then updates the display whenever new lines appear in the pane file. This polling-based mechanism lets lieutenant replies appear in real-time without WebSocket infrastructure.

The UI includes **pending-reply markers**—visualized as a green "|" under the request—indicating when the lieutenant is processing a message.

## End-to-End Message Flow

The complete lifecycle of a chat message follows this pattern:

1. **User input**: A message typed in the cockpit's chat composer is written to the pane file via the SwarmForge CLI
2. **Agent processing**: The lieutenant agent (default: **grok**) reads the new entry and generates a response
3. **Response persistence**: The lieutenant appends its reply to the same pane file
4. **UI refresh**: The next poll of `pane-status-lines-for` returns the updated list, which the cockpit renders

This file-based architecture decouples the web interface from the agent logic, allowing either component to be restarted independently without message loss.

## Test Coverage for Chat Persistence

The implementation is verified by two key test suites:

- **"pending lieutenant chat shows green status under the request"** ([[`test/dashboard/dashboard.spec.js`](https://github.com/unclebob/swarm-forge/blob/main/test/dashboard/dashboard.spec.js)](https://github.com/unclebob/swarm-forge/blob/main/test/dashboard/dashboard.spec.js), lines 347–355): Confirms visual feedback for pending replies
- **"forge-state-includes-lieutenant-status-lines"** ([`test/swarmforge/pack_ui_test.clj`](https://github.com/unclebob/swarm-forge/blob/main/test/swarmforge/pack_ui_test.clj), lines 2525–2543): Validates that state objects contain the `lieutenant_status` lines read from the pane file

## Summary

- **Persistence mechanism**: Plain-text file at [`.swarmforge/sessions/lieutenant/pane.txt`](https://github.com/unclebob/swarm-forge/blob/main/.swarmforge/sessions/lieutenant/pane.txt)
- **Read function**: `pane-status-lines-for` in `pack_web.bb` extracts status lines
- **Data binding**: `:lieutenant_status` key in the cockpit data object
- **UI rendering**: JavaScript in [`dashboard.html`](https://github.com/unclebob/swarm-forge/blob/main/dashboard.html) polls and displays status lines
- **Real-time updates**: File modifications propagate through repeated function calls
- **Visual indicators**: Green "|" markers show pending lieutenant activity

## Frequently Asked Questions

### What file format does SwarmForge use for lieutenant chat history?

SwarmForge stores lieutenant chat as **plain text** in [`.swarmforge/sessions/lieutenant/pane.txt`](https://github.com/unclebob/swarm-forge/blob/main/.swarmforge/sessions/lieutenant/pane.txt). This format prioritizes simplicity and human readability over structured serialization. Each line represents one status entry or message, making debugging and manual inspection straightforward.

### How does the cockpit detect new lieutenant responses?

The cockpit detects new responses through **polling the `pane-status-lines-for` function**. Rather than using push-based mechanisms like WebSockets, the dashboard periodically re-reads the pane file. When the returned line count or content changes, the UI updates to display the new messages. This trade-off reduces infrastructure complexity at the cost of slight latency.

### Can multiple users view the same lieutenant conversation simultaneously?

Yes. Because the chat state lives in a **shared file on disk**, any number of cockpit instances can read from [`.swarmforge/sessions/lieutenant/pane.txt`](https://github.com/unclebob/swarm-forge/blob/main/.swarmforge/sessions/lieutenant/pane.txt) simultaneously. Each viewer independently polls `pane-status-lines-for` and receives the same conversation history. However, write operations typically require coordination through the SwarmForge CLI to prevent race conditions.

### What happens to chat history when the lieutenant process restarts?

The chat history **persists across restarts** because it is file-based rather than memory-resident. When the lieutenant agent restarts, it reopens [`pane.txt`](https://github.com/unclebob/swarm-forge/blob/main/pane.txt) and continues appending to the existing content. The cockpit similarly resumes polling from the same file, displaying the complete conversation history including messages from before the restart.