From 46c7ef2c5cd551d7f49b3ee0a150c3198d52ae29 Mon Sep 17 00:00:00 2001 From: typebasedio Date: Tue, 21 Apr 2026 17:16:54 -0400 Subject: [PATCH] docs(v0.4): document --force, session lease, stale-workspace detection, and coexisting-session limitation --- docs/commands.md | 89 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/docs/commands.md b/docs/commands.md index 607c883..1a690a7 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -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 `/.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: + Host: + Agent: + Started: ( ago) + Last seen: 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 `/.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: (, ) + + 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 `/.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 |