FerryCat
# FerryCat: Contract-Driven Cross-Host AI Agent Handoff Bus
<p align="center">
<b>English</b> β’
<a href="README_zh.md">δΈζ</a>
</p>
[](https://www.npmjs.com/package/ferrycat)
[](https://registry.modelcontextprotocol.io/v0/servers?search=ferrycat)
[](https://nodejs.org/)
[-success?style=flat)]()
[](https://modelcontextprotocol.io/)
[](https://glama.ai/mcp/servers/GuoBug/FerryCat)
[](LICENSE)
> **FerryCat** (`ferrycat`) is a lightweight, deterministic cross-host AI agent handoff, code review, and verification harness built on the Model Context Protocol (MCP) and Node.js built-in `node:sqlite` (WAL mode).
> Purpose-built to safely bridge isolated desktop IDE agent hosts (such as Google Antigravity, WorkBuddy, Claude Code, and Cursor) across separate OS processesβenabling Actor-Critic evaluation loops, static analysis gates, and SWE-bench-style patch verification without context drift or blind reviews on stale files.
<p align="center">
<img src="https://guobug.github.io/assets/images/flowchart-ferrycat-cross-host-demand-and-workflow-en.svg" alt="FerryCat Architecture and Workflow" width="960" />
</p>
> π **Deep Dive**: [Why I Started Building FerryCat: When Two AI Coding Hosts Live on the Same Desktop](https://guobug.github.io/posts/2026/10/22/why-we-built-ferrycat-cross-host-agent-review-bus/)
---
## Quick Glance (See It in 60s)
No IDE setup or config file edits required. Watch the entire deterministic loop execute inside an isolated sandbox in ~2 seconds:
```bash
npx ferrycat demo
```
```text
β‘ FerryCat Zero-Config Sandbox Demo β‘
β [1/4] Contract Dispatched Locked: src/calc.ts (sha256: ce3ba9a5...bc5d)
β [2/4] Contract Claimed Lease: 7424261d-9a3f-4a86-a7ff-de278c273e96
β [3/4] Execution & Check Heartbeat 1 renewal | CHANGES_REQUESTED (loose equality caught)
β [4/4] Contract Completed TOCTOU: 0 dirty files | Status: COMPLETED (elapsed: 2.1s)
π End-to-end multi-agent handoff verified successfully!
```
> π‘ Real benchmark: spins up an isolated child-process sandbox, triggers 1 real lease renewal, and runs a static analysis & verification gate that catches loose equality at `src/calc.ts:6`. Options (`--keep` / `--json` / `--no-color`) detailed in [Quickstart Β§0](#0-see-what-it-actually-does-first-ferrycat-demo).
---
## Core Pain Points & Engineering Solutions
While the official MCP `Tasks` specification (2025-11 / 2026-07) addresses long-running operations within a **single `Client β Server` connection**, it does not solve cross-host collaboration on a developer workstation:
1. **`stdio` Process Isolation**: Desktop IDEs spawn local MCP servers as independent child processes with isolated memory spaces and distinct Node.js runtimes.
2. **Silent Blind Reviews on Stale Code (`Stale Context`)**: When handing off a task from Host A to Host B, unushed IDE buffers, branch switches, or mid-review edits cause the receiving agent to audit outdated code and produce plausible but invalid conclusions.
3. **Context Window Bloat**: Pasting hundreds of lines of source code across chat windows duplicates token consumption on both ends.
4. **Unstructured Handoff Outputs**: Free-form prose responses lack deterministic schema enforcement, making downstream consumption unreliable and prone to infinite retry loops.
**FerryCat resolves these failure modes through a human-scheduled, contract-driven SQLite WAL bus:**
```
[Developer] [Host A: Antigravity] [~/.ferrycat/mesh.db] [Host B: WorkBuddy]
| | | |
|-- 1. "Dispatch review" --->| | |
| |-- dispatch_contract() -------->| Write status = SUBMITTED |
| | (Full SHA-256 + Line Bounds) | Store size / sha256 snapshot |
| | | |
|-- 2. Switch IDE: "Poll" ---|--------------------------------|------------------------------>|
| | |<-- poll_next_task() ----------|
| | | (Atomic claim changes===1 |
| | | + Verify disk SHA-256) |
| | | |-- Read local files
| | |<-- fulfill_contract() --------|-- Re-verify SHA-256
| | | (Validate Ajv JSON Schema | & submit JSON
| | | -> status = COMPLETED) |
| | | |
|-- 3. Switch IDE: "Inspect" | | |
| |-- inspect_mesh_state() ------->| Read-only query of COMPLETED |
|<-- Present structured res -| | |
```
---
## Key Architectural Pillars
### 1. Deterministic 7-State Machine
Every state transition is bound to an explicit SQL transaction with optimistic concurrency control (`changes === 1`), eliminating phantom or dangling states:
```
dispatch_contract()
β
βΌ
βββββββββββββ
β SUBMITTED β (Queue TTL: default 1800s)
βββββββ¬ββββββ
β
poll_next_task()
ββββββββββββββββββΌβββββββββββββββββ
β (Queue TTL) β (Hash Match) β (Missing / Size Diff / SHA-256 Mismatch)
βΌ βΌ βΌ
βββββββββββββ βββββββββββββ βββββββββββββββββ
β TIMED_OUT β β CLAIMED β β POINTER_STALE β (Terminal: Rejects blind review)
βββββββββββββ βββββββ¬ββββββ βββββββββββββββββ
β² β (Exec TTL: default 300s)
β fulfill_contract()
β ββββββββββββββββββΌβββββββββββββββββ¬βββββββββββββββββ
ββββ€ (Exec TTL) β (Schema Valid) β (Retries > 2) β (Mid-Review File Edit)
β βΌ βΌ βΌ
β βββββββββββββ βββββββββββββββββββββ βββββββββββββββββ
ββββββββββββ COMPLETED β β VALIDATION_FAILED β β POINTER_STALE β
βββββββββββββ βββββββββββββββββββββ βββββββββββββββββ
```
> *Note on `POINTER_STALE` vs. `SELF_CONTAINED` Patch Mode (FC-202)*: `POINTER_STALE` is terminal whenever on-disk pointer files mismatch the dispatched SHA-256 baseline, **except** when a read-only contract (`code_review` / `architecture_audit`) attaches a verified unified `git_patch` (`--patch-file`) that satisfies all three gates: every declared file block carries at least one `@@` hunk with real `+`/`-` content (`EMPTY_PATCH_HUNKS` / `EMPTY_PATCH_CONTENT` otherwise), and the touched paths cover every pointer file (`PATCH_DOES_NOT_COVER_POINTERS` otherwise). In that `SELF_CONTAINED` mode, `poll_next_task` returns `patch_mode: "SELF_CONTAINED"` with an explicit `workspace_warning` instructing the reviewer to inspect `git_patch` rather than stale on-disk files.
Holder-side lifecycle controls on a `CLAIMED` contract (all require `agent_id === target_agent`):
| Call | Transition | Guard rails |
| :--- | :--- | :--- |
| `release_contract` | `CLAIMED β SUBMITTED` (tail of queue, fresh queue window) | Resets `extend_count` (renewal budget is per holder) but **preserves** `retry_count`: the schema-failure circuit breaker is a contract-level quota, so releasing cannot launder retry budget. Refused past the absolute lifetime deadline. Max 3 releases per contract. |
| `extend_ttl` | `CLAIMED β CLAIMED` (`claimed_at = now`) | Must be called inside the active window; max 5 renewals per holder; refused once `now β₯ created_at + 4 Γ queue_ttl_sec`. |
| `fail_contract` | `CLAIMED β FAILED` (terminal) | Requires a `reason`, stored as `WORKER_FAILED: <reason>`. A report arriving after the window closed becomes `TIMED_OUT` with the reason attached. |
### 2. Full-File SHA-256 Fingerprinting, Pre-Flight Verify & TOCTOU Soft-Failure Buffering (ADR-0004)
- **Dispatch Snapshot**: `dispatch_contract` reads target files on disk, recording byte `size`, full-file `sha256` (64 hex chars), `first_line`, and `last_line`. If a file was modified within the last 3 seconds, a `warning` is emitted to alert the requester of potential unsaved IDE buffers.
- **Claim Verification**: `poll_next_task` verifies `Existence -> Byte Size -> Full-File SHA-256` (never rejecting on `mtime_ms` alone to tolerate no-op IDE saves). On mismatch, it waits `150ms` asynchronously (non-blocking, FC-203) and retries once to rule out transient IDE flushes before marking the contract `POINTER_STALE` β unless the contract is a read-only review/audit carrying a verified unified `git_patch` covering all pointer files (`SELF_CONTAINED` patch mode, FC-202), in which case it claims with an explicit `workspace_warning` and records `__verified_on_disk__: "NO"` and `__unverified_pointers__` in `verified_sha256`.
- **Pre-Flight Verify (`verify_contract`)**: A strictly read-only check allowing workers or scripts to audit pointer health (`valid: true` vs `valid: false` with dirty files list) before submitting.
- **TOCTOU Soft-Failure Buffering**: `fulfill_contract` checks on-disk pointer fingerprints. If dirty pointers are detected:
- **Strike 1** returns `RETRY_NEEDED`, leaves the contract `CLAIMED`, renews the execution window, and lists dirty files with recovery instructions (`git stash` / `git checkout -- <file>`).
- **Strike 2** transitions to terminal `POINTER_STALE` and saves the full submitted review payload into `salvaged_result_json` so review effort is never lost.
- **TOCTOU Race Narrowing**: While no file-based system can eliminate the microsecond race between reading disk bytes and committing a database row, FerryCat's dual-verification (at `poll` and at `fulfill`) drastically narrows the TOCTOU window from minutes of blind review down to the sub-millisecond atomic SQL transition boundary.
### 3. Bounded Schema Validation (`RETRY_NEEDED` $\le 2$)
- Outputs are validated against the contract's JSON Schema via cached Ajv validators, with errors flattened into concise single-line paths (e.g., `["/passed: must be boolean"]`).
- Up to 2 validation failures return `RETRY_NEEDED` while keeping the contract `CLAIMED` for immediate self-correction (each rejection also renews the execution window); a 3rd failure transitions the contract to terminal `VALIDATION_FAILED`.
### 4. Dual-Stage TTL & Read-Before-Write Sweeping
- Separates `queue_ttl_sec` (default 1800s for `SUBMITTED`) from `exec_ttl_sec` (measured from `claimed_at`), preventing tasks claimed late in the queue window from timing out prematurely during execution.
- `exec_ttl_sec` defaults are tiered by `task_type`: `code_review` / `architecture_audit` β 1800s (deep reasoning or human-in-the-loop), `implementation` / `test_generation` β 300s. An explicit `exec_ttl_sec` always wins.
- `poll_next_task` executes a lock-free `SELECT COUNT(*)` probe before attempting any `UPDATE` on expired rows; `inspect_mesh_state` is strictly read-only.
### 5. Defense-in-Depth Path Security
- Enforces path containment within `workspace_root` and blocks sensitive file patterns (`.env*`, `*.pem`, `*.key`, `id_rsa*`, `*credentials*`, `*secret*`, `.npmrc`, `.git/config`) on both `dispatch` and `worker` verification paths.
- **Path Resolution**: `workspace_root` is strongly recommended to be passed as an absolute path. If a relative path is provided, it is deterministically resolved against `process.cwd()` via `path.resolve`.
### 6. Threat Model & Security Assumptions (Local Workstation IPC)
- **Assumed Trust Environment**: FerryCat is intentionally designed as a **Trusted Local Workstation Bus** bridging isolated IDE hosts operating under the **same OS user account**. Callers and workers share the local file system (`workspace_root`) and SQLite database (`~/.ferrycat/mesh.db`).
- **Defense Scope**: Path containment (`assertSafePointerPath`) and sensitive file pattern filters prevent unintentional workspace escapes, symlink traversals, or accidental leakage of credentials/secret files during handoffs.
- **Daemon Execution Surface**: In daemon mode (`ferrycat-cli daemon`), handler scripts are invoked directly via `execFile`. Write permissions to `ferrycat.config.json` or handler script paths are equivalent to host execution privileges (ADR-0005 Β§2.2). Keep configurations local and version-controlled.
- **Out of Scope (Non-Goals)**: FerryCat does **not** provide adversarial sandboxing, multi-tenant isolation, or network authorization against malicious local processes or adversarial agents spoofing `AGENT_NAME`.
---
## Applicability Boundaries (When NOT to Use FerryCat)
1. **Disjoint File Systems**: FerryCat offloads context by file reference; both hosts must share access to the same local `workspace_root`.
2. **Trivial Single-Turn Queries**: Each cross-host handoff involves 3 explicit human window switches (~15β25s overhead).
3. **Multi-Step Dynamic DAG Pipelines**: FerryCat's background daemon (`ferrycat-cli daemon`, ADR-0005) manages single-step contract dispatch and supervised worker lifecycles; for complex multi-step branching DAG orchestrations, use dedicated DAG workflow engines such as [PatchCat](https://github.com/GuoBug/ai-prompt-orchestrator).
---
## Controlled Daemon & Declarative Handlers (ADR-0005)
FerryCat provides a controlled daemon mode governed by SQLite microsecond CAS leases on `daemon_locks` and declarative scripts defined in `ferrycat.config.json` (ideal for binding static analysis gates like `tsc` / `eslint` / AST checks or automated test suites like `Jest` / `Vitest` / `Pytest` as deterministic verification workers):
```bash
# Run a single dispatch cycle with declared handlers
ferrycat-cli daemon --config ./ferrycat.config.json --once
# Start continuous daemon loop
ferrycat-cli daemon --config ./ferrycat.config.json
# Inspect lease ownership + write-ahead journal health (read-only)
ferrycat-cli daemon --status
```
`daemon --status` reports `journal_degraded` / `last_journal_error` / `journal_path` / `journal_entries` alongside lease ownership, so a silently unwritable write-ahead journal (which would disarm crash recovery) is visible instead of failing mute.
Example `ferrycat.config.json`:
```json
{
"version": 1,
"daemon": {
"poll_interval_ms": 1000,
"lease_ttl_sec": 30,
"heartbeat_interval_ms": 10000,
"notify": true
},
"agents": {
"workbuddy": {
"handlers": {
"code_review": {
"exec_file": "./scripts/review-handler.js",
"pass_env": ["CUSTOM_ANALYZER_PATH"]
}
}
}
}
}
```
---
## Quickstart
### 0. See what it actually does first (`ferrycat demo`)
Before configuring any MCP server, watch the whole loop in ~3 seconds:
```bash
npx ferrycat demo
```
*(or `npx -p ferrycat ferrycat-cli demo`)*
It spins up a fully isolated sandbox under the OS temp dir (dedicated SQLite database,
sample source file, sample handler), runs `dispatch β claim β supervised execution β
TOCTOU re-verification β COMPLETED`, then deletes the sandbox.
**Your real workspace and `~/.ferrycat/mesh.db` are never read or written.**
```text
β‘ FerryCat Zero-Config Sandbox Demo β‘
Sandbox: /tmp/ferrycat-demo-c5iK6V
β [1/4] Contract Dispatched
Locked src/calc.ts (sha256: ce3ba9a5...bc5d)
β [2/4] Contract Claimed
Lease 7424261d-9a3f-4a86-a7ff-de278c273e96
β [3/4] Execution & Schema Check
Heartbeat 1 lease renewal(s)
β [4/4] Contract Completed
TOCTOU fingerprints matched (0 dirty files)
π End-to-end multi-agent handoff verified successfully!
```
| Option | Effect |
| :--- | :--- |
| `--json` | Emit a structured result for CI / upstream assertions |
| `--keep` | Keep the sandbox and print its path for manual `sqlite3` inspection |
| `--no-color` | Disable ANSI colors (auto-off when not a TTY or `NO_COLOR` is set) |
### 1. Recommended: Zero-Install MCP Mounting (via npx)
No local clone or manual compilation required. Run `npx ferrycat init --yes` to auto-detect installed hosts (Antigravity, WorkBuddy, Cursor, VS Code, Windsurf, Claude Desktop, Claude Code) and safely merge the `ferrycat` server entry (with backup and JSONC protection), or mount manually into your IDE's MCP configuration using `npx -y ferrycat`:
#### Antigravity Configuration
Edit `%USERPROFILE%\.gemini\antigravity\mcp_config.json` (or `~/.gemini/antigravity/mcp_config.json` on macOS/Linux):
```json
{
"mcpServers": {
"ferrycat": {
"command": "npx",
"args": ["-y", "ferrycat"],
"env": {
"AGENT_NAME": "antigravity"
}
}
}
}
```
#### WorkBuddy / Claude Code / Cursor / Codex Configuration
Configure the target host's MCP settings with its respective `AGENT_NAME`:
```json
{
"mcpServers": {
"ferrycat": {
"command": "npx",
"args": ["-y", "ferrycat"],
"env": {
"AGENT_NAME": "workbuddy"
}
}
}
}
```
### 2. Global Installation & Source Build
- **Global CLI Installation**:
```bash
npm install -g ferrycat
```
Then simply set `"command": "ferrycat"` in your MCP config.
- **Local Source Build (For Contributors)**:
```bash
git clone https://github.com/GuoBug/FerryCat.git
cd FerryCat
npm install
npm run build
npm test
```
Mount using your local path:
`"command": "node"`, `"args": ["--experimental-sqlite", "/path/to/FerryCat/dist/index.js"]`.
---
## First-Class CLI Experience (`ferrycat-cli`)
Beyond serving as an in-IDE MCP Server, FerryCat provides a first-class CLI utility (`ferrycat-cli`). The single `ferrycat` executable dispatches on its first argument: no argv starts the MCP stdio server, a known subcommand routes to the CLI. So you can run `npx ferrycat <command>` with zero install (or `ferrycat-cli <command>` once installed globally). Sharing the same `~/.ferrycat/mesh.db` SQLite WAL database and Zod validation contracts, the CLI allows developers to inspect mesh health, verify working tree safety, and hook into automated CI/Git scripts without leaving their terminal:
### 1. Terminal Visual Inspection (ASCII Status Table)
Inspect all cross-host handoffs and error reasons formatted as an ASCII table:
```bash
# View all contracts in an ASCII table
npx ferrycat inspect
# Filter by target agent (e.g., only tasks for antigravity)
npx ferrycat inspect --target antigravity
```
### 2. Pre-Flight Verification (Auditing Before Mutation)
Audit on-disk pointer file integrity against the contract's baseline SHA-256 before committing review work (ADR-0004), eliminating false-positive deliveries:
```bash
npx ferrycat verify <CONTRACT_ID>
```
### 3. Local CI & Headless Worker Execution
Poll the queue, claim tasks, and execute local review/test scripts with automatic failure closure:
```bash
# Safely run a script upon task arrival (no shell quotation hazards)
npx ferrycat worker --agent codex --mode=exec --exec-file ./scripts/review.sh --delivery envelope
# See docs/examples/ for dispatch.example.json, fulfill.example.json, and handler.example.cjs templates.
```
### 4. Zero-Config Sandbox Walkthrough (demo)
Run the self-contained multi-agent lifecycle simulation without configuring an MCP server:
```bash
npx ferrycat demo
```
Options (`--keep` to retain SQLite for manual inspection, `--json` for CI assertions) detailed in [Quickstart Β§0](#0-see-what-it-actually-does-first-ferrycat-demo).
### 5. Production Controlled Daemon (daemon, ADR-0005)
Run the headless orchestration daemon coordinating background workers via atomic CAS leases and declarative script bindings:
```bash
# Launch daemon using project-local declarative configuration
npx ferrycat daemon --config ./ferrycat.config.json
# Run single poll/dispatch iteration for a targeted agent and exit
npx ferrycat daemon --agent workbuddy --once
```
---
## Exposed MCP Tools
| Tool Name | Role | Transactional Behavior |
| :--- | :--- | :--- |
| **`dispatch_contract`** | Requester | Enforces path blacklist $\to$ computes full-file SHA-256 & line bounds (plus optional unified `git_patch` / `--patch-file` validation covering pointers, FC-202) $\to$ inserts `SUBMITTED`. |
| **`poll_next_task`** | Worker | Sweeps expired TTLs (read-before-write) $\to$ claims via `changes === 1` (`CLAIMED`) $\to$ verifies disk SHA-256 with async 150ms retry (FC-203) or falls back to `SELF_CONTAINED` patch mode with `workspace_warning` (otherwise marks `POINTER_STALE`). |
| **`fulfill_contract`** | Worker | Re-verifies SHA-256 (TOCTOU guard) $\to$ validates Ajv JSON Schema ($\le 2$ retries via `RETRY_NEEDED`, 3rd marks `VALIDATION_FAILED`) $\to$ marks `COMPLETED`. |
| **`release_contract`** | Worker (holder) | Hands a `CLAIMED` contract back to the queue tail as `SUBMITTED`. `retry_count` is preserved (contract-level circuit breaker); `extend_count` is reset. Max 3 releases; refused once past the absolute lifetime deadline. |
| **`extend_ttl`** | Worker (holder) | Heartbeat: restarts the execution window (`claimed_at = now`), max 5 renewals and bounded total lifetime. |
| **`fail_contract`** | Worker (holder) | Marks a `CLAIMED` contract terminal `FAILED` with a mandatory `reason`. |
| **`verify_contract`** | Worker / Any (Read-Only) | Pre-flight check: validates on-disk pointer files against the baseline SHA-256 without mutating contract state (ADR-0004). |
| **`inspect_mesh_state`** | Any (Read-Only) | Pure read-only query of contract states, outputs, `patch_sha256` / `patch_bytes` (opt-in `include_patch: true`), and dynamic `is_logically_expired` flags. |
> π‘ **Architecture Decision Records & Engineering Verification**:
> - [ADR-0001: Cross-Host SQLite WAL Bus & Human-Scheduled Poll Model](docs/adr/0001-cross-host-sqlite-wal-and-poll-model.md) (Why `node:sqlite` WAL + `poll_next_task` instead of 60s Blocking Hold or single-connection MCP Tasks)
> - [ADR-0002: Full-File SHA-256 Fingerprinting, TOCTOU Guard & Cross-Model Audit](docs/adr/0002-fingerprint-toctou-and-cross-model-audit.md) (Stale-context prevention and real-world cross-host verification records)
> - [ADR-0003: GUI Sandbox vs CLI Process Hybrid Layering Architecture & Wake-Up Boundaries](docs/adr/0003-gui-cli-hybrid-architecture.md) (Desktop HITL approval vs in-house CLI dual-channel autonomy)
> - [ADR-0004: Separation of Review and Mutation (SRM), TOCTOU Soft-Failure Buffering & Immutable Baseline Protocol](docs/adr/0004-review-mutation-separation-and-toctou-buffer.md) (How to prevent review abortion from concurrent or well-intended reviewer edits; live mesh case: contract `fc_1793098036393_aea8bb9b`)
> - [ADR-0005: Controlled Daemon, Atomic CAS Micro-Leases & Safe Execution Boundaries](docs/adr/0005-controlled-daemon-and-process-supervision.md) (Microsecond atomic CAS leases on `daemon_locks` and declarative whitelist handler enforcement)
> - [ADR-0006: Dual-Track TOCTOU Semantics & Post-Mutation SHA-256 Fulfillment](docs/adr/0006-dual-track-mutation-fulfillment.md) (Why `implementation` / `test_generation` tasks commit post-mutation SHA-256 into `verified_sha256` while preserving pre-mutation baselines in `pointers`; live mesh case: `fc_1793100801409_4eedf544`)
> - [FerryCat Requirements Backlog & Roadmap](docs/roadmap.md)
---
> **About the Author**
> **Guo Qiang (GuoBug)**, Product Engineer specializing in platform engineering and growth. Currently focused on AI workflow orchestration, DAG state machines, and deterministic system architectures.
> Open Source & Homepage: [https://github.com/GuoBug](https://github.com/GuoBug) Β· [https://guobug.github.io](https://guobug.github.io)
> Inquiries and discussions on workflow engine architecture, topological scheduling, and developer experience are warmly welcomed.
TDQS
Scored across 8 tools
Each tool maps to a distinct lifecycle action (dispatch/create, poll/claim, fulfill, fail, release, extend, inspect, verify) with explicit 'do NOT use X, use Y' guidance that pre-empts confusion between close pairs like inspect_mesh_state vs verify_contract and extend_ttl vs release_contract. Boundaries are unusually crisp.
All eight names follow a consistent snake_case verb-first pattern (dispatch_contract, poll_next_task, fulfill_contract, fail_contract, release_contract, extend_ttl, inspect_mesh_state, verify_contract). Noun phrases vary slightly in length but the convention is uniform and predictable.
Eight tools cleanly cover one coherent domain (a cross-host contract queue). Every tool earns its place in the lifecycle β no redundant wrappers and no filler.
The lifecycle is nearly fully covered: create, claim, heartbeat, release, fail, deliver, plus read-only state and file-consistency inspection. The notable gap is a requester-side cancel/revoke for a SUBMITTED contract (the only remedy is waiting for queue TTL), which is a minor but real dead end.