docs(v0.4): document --force, session lease, stale-workspace detection, and coexisting-session limitation
This commit is contained in:
@@ -123,6 +123,7 @@ Resolves the workspace by query (exact directory name, exact slug, or case-insen
|
||||
|------|-------|---------|-------------|
|
||||
| `--shell` | | off | Open shell instead of agent |
|
||||
| `--agent` | `-a` | from task.yaml | Override agent command |
|
||||
| `--force` | | off | Skip active-session and stale-workspace warnings |
|
||||
| `--container` | | off | Deferred to a future release |
|
||||
|
||||
**Examples:**
|
||||
@@ -132,6 +133,7 @@ ctask resume auth-bug
|
||||
ctask resume backup
|
||||
ctask resume --shell auth-bug
|
||||
ctask resume --agent aider auth-bug
|
||||
ctask resume --force auth-bug
|
||||
```
|
||||
|
||||
If multiple workspaces match, prints all matches and exits. If none match, prints an error.
|
||||
@@ -155,6 +157,7 @@ Spawns a new subshell in the workspace directory. Does not modify the caller's s
|
||||
| Flag | Short | Default | Description |
|
||||
|------|-------|---------|-------------|
|
||||
| `--all` | `-a` | off | Include archived workspaces in query resolution |
|
||||
| `--force` | | off | Skip active-session and stale-workspace warnings |
|
||||
|
||||
**Examples:**
|
||||
|
||||
@@ -221,6 +224,7 @@ ctask last [flags]
|
||||
|------|-------|---------|-------------|
|
||||
| `--shell` | | off | Open shell instead of agent |
|
||||
| `--agent` | `-a` | from task.yaml | Override agent command |
|
||||
| `--force` | | off | Skip active-session and stale-workspace warnings |
|
||||
|
||||
**Examples:**
|
||||
|
||||
@@ -335,6 +339,91 @@ Configure ctask behavior with:
|
||||
|
||||
---
|
||||
|
||||
## Concurrency and safety
|
||||
|
||||
ctask v0.4 protects workspaces from conflicts when multiple sessions (or manual file edits) touch the same workspace.
|
||||
|
||||
### Session lease
|
||||
|
||||
Each active `ctask resume`, `open`, `last`, or `new` writes a lease file at `<workspace>/.ctask/session.json` identifying the ctask process, hostname, user, agent, mode, and a heartbeat timestamp. A background goroutine updates the heartbeat every 30 seconds.
|
||||
|
||||
On session start, if a fresh lease already exists (heartbeat within 60 seconds), ctask warns:
|
||||
|
||||
```
|
||||
[ctask] This workspace has an active session:
|
||||
Session: <id>
|
||||
Host: <host>
|
||||
Agent: <agent>
|
||||
Started: <timestamp> (<elapsed> ago)
|
||||
Last seen: <seconds> ago
|
||||
|
||||
Opening a second session may cause conflicts.
|
||||
Continue anyway? [y/N]
|
||||
```
|
||||
|
||||
If the user answers `y`, the second session proceeds **without writing its own lease** (see "Known limitation: coexisting sessions" below). If the lease is older than 60 seconds (crash, lost connection), ctask cleans it up silently and proceeds.
|
||||
|
||||
### Metadata write lock
|
||||
|
||||
All ctask-owned file writes (`task.yaml`, `logs/sessions.log`, `.ctask/session.json`, `.ctask/manifest-start.json`, `.ctask/last-session-summary.json`) are serialized through `<workspace>/.ctask/write.lock`. The lock is held for the duration of one write only. If the lock cannot be acquired within 2 seconds, the write is skipped with a warning rather than blocking.
|
||||
|
||||
### Stale-workspace detection
|
||||
|
||||
On session start, ctask compares the current workspace state against the end-state recorded by the previous session's summary. If anything changed outside a ctask session (another machine, manual edits), ctask warns:
|
||||
|
||||
```
|
||||
[ctask] Workspace modified since last session ended:
|
||||
|
||||
Last session: <timestamp> (<host>, <agent>)
|
||||
|
||||
Modified since then:
|
||||
notes.md (modified)
|
||||
output/report.md (new file)
|
||||
|
||||
These changes were not made during a ctask session.
|
||||
Review before continuing? [Y/n]
|
||||
```
|
||||
|
||||
Press Enter (or `y`) to proceed. Press `n` to exit without launching.
|
||||
|
||||
This check is skipped silently for workspaces that have never completed a v0.4 session (no `last-session-summary.json`).
|
||||
|
||||
### Session handoff summary
|
||||
|
||||
At end of session, ctask writes `<workspace>/.ctask/last-session-summary.json` containing:
|
||||
|
||||
- `session_id`, `hostname`, `agent`, `mode`
|
||||
- `started_at`, `ended_at`, `duration_seconds`
|
||||
- `files_added`, `files_modified`, `files_deleted`
|
||||
- `notes_updated`
|
||||
- `end_manifest` (snapshot of workspace file list at session end -- used by the stale-workspace detector)
|
||||
|
||||
The next session prints a short orientation banner from this file:
|
||||
|
||||
```
|
||||
[ctask] local :: api-cleanup
|
||||
[ctask] ~/ai-workspaces/general/2026-04-21_api-cleanup
|
||||
[ctask] Last session: 2026-04-21 14:30-15:45 (warren-desktop, claude)
|
||||
[ctask] Changed: notes.md, output/plan.md
|
||||
```
|
||||
|
||||
### `--force`
|
||||
|
||||
`--force` on `resume`, `open`, and `last` suppresses both the active-session warning and the stale-workspace warning. It does **not** disable the metadata write lock or the session summary -- those are always active.
|
||||
|
||||
Use `--force` only for automation where the human has already decided to proceed.
|
||||
|
||||
### Known limitation: coexisting sessions
|
||||
|
||||
When the user confirms "Continue anyway?" on an active-session warning (or passes `--force`), the second session runs **without writing its own lease**. This keeps the lease model simple (one lease file per workspace), but has two consequences:
|
||||
|
||||
1. A third session attempt will only see the original lease. The second (coexisting) session is invisible to lease-based detection.
|
||||
2. If the original session exits and removes its lease before the coexisting session finishes, the coexisting session is unprotected for its remaining lifetime.
|
||||
|
||||
The metadata write lock still serializes all ctask-owned file writes regardless of session count, so no state corruption can occur. If you need stronger guarantees, exit the existing session before starting another one.
|
||||
|
||||
---
|
||||
|
||||
## Exit Codes
|
||||
|
||||
| Code | Meaning |
|
||||
|
||||
Reference in New Issue
Block a user