Channels & Signals
cliq agents don’t share memory. Each agent runs in its own process with its own context window. There is no message bus, no shared database, no API layer between them. Instead, coordination happens through two filesystem-based mechanisms:
- Channels — directories where agents write structured handoff documents for downstream phases
- Signals — tiny files the orchestrator uses to track completion, verdicts, and pipeline state
This page covers both in detail.
Why the Filesystem?
Section titled “Why the Filesystem?”Most multi-agent frameworks use message queues, shared memory, or API calls for inter-agent communication. cliq uses plain files. This is a deliberate choice, not a limitation.
Transparency. Every handoff between agents is a markdown file you can open and read. There’s no serialization format to decode, no message broker to query. When something goes wrong, you read the file.
Auditability. The .cliq/ directory is a complete record of what happened. Channels show what each agent told the next. Signals show when each phase completed. You can reconstruct the entire pipeline run from the filesystem alone.
Portability. Channels and signals work anywhere there’s a filesystem — your laptop, a CI runner, a container, a remote server. No daemon to install, no port to bind.
Debuggability. You can inspect, edit, or even pre-populate channel files mid-run. If an agent wrote a bad handoff, fix the file and re-run the downstream phase. The filesystem is your API.
Channels
Section titled “Channels”A channel is a directory under .cliq/channels/ dedicated to one edge in the workflow DAG. When phase A depends on phase B, there is a channel directory where B writes handoff notes for A to read.
Naming Convention
Section titled “Naming Convention”Channel directories follow the pattern:
{from_phase}--{to_phase}The from phase is the writer; the to phase is the reader. The double-dash -- separator is used instead of a single dash because phase names themselves may contain hyphens (e.g., security-auditor).
Examples:
architect--developer # architect writes handoff for developerarchitect--security-auditor # architect writes handoff for security-auditordeveloper--reviewer # developer writes handoff for reviewerreviewer--developer # reviewer writes feedback to developer (gate route)How Channels Are Created
Section titled “How Channels Are Created”The orchestrator creates channel directories at pipeline startup, before any agent runs. The ChannelManager.create_channels() method walks the workflow DAG’s dependency edges and creates one directory per edge:
for (const edge of edges) { const channel_name = `${edge.from}--${edge.to}`; fs.mkdirSync(path.join(channels_dir, channel_name), { recursive: true });}Gate phases get additional bidirectional channels created via ChannelManager.create_gate_channels() — more on that below.
The result: every possible handoff path has a directory ready before the first agent starts.
How Channel Content Flows into Prompts
Section titled “How Channel Content Flows into Prompts”When the orchestrator activates a phase, the prompt generator builds a prompt that tells the agent exactly which channels to read and write.
Incoming channels. For each dependency (predecessor phase), the prompt includes:
Read your incoming channels:- .cliq/channels/architect--developer/ (handoff from Architect)- .cliq/channels/tester--developer/ (handoff from Tester)Read any instructions.md files in those channels if present.The agent reads these directories, finds handoff files written by predecessor phases, and uses that context to inform its work.
Outgoing channels. For each successor phase, the prompt includes:
When complete, write your handoff notes to these channels:- .cliq/channels/developer--reviewer/handoff.mdThe agent writes a handoff.md file (or other artifacts) into each outgoing channel directory before signaling completion.
Fan-Out: One Phase, Multiple Successors
Section titled “Fan-Out: One Phase, Multiple Successors”When a phase has multiple successors in the DAG, it writes to multiple outgoing channels. Each handoff can — and should — be tailored to the downstream agent’s needs.
For example, an architect phase with both a developer and a security-auditor downstream:
.cliq/channels/├── architect--developer/│ └── handoff.md # Implementation plan, API contracts, file structure├── architect--security-auditor/│ └── handoff.md # Threat model, auth boundaries, data flow concernsThe architect writes different content to each channel. The developer gets an implementation plan; the security auditor gets a threat model. Same source phase, targeted handoffs.
Gate Channels: Bidirectional Communication
Section titled “Gate Channels: Bidirectional Communication”Gate phases (like a reviewer) create bidirectional channels between the gate and every phase it can route work to. This is handled by ChannelManager.create_gate_channels(), which creates both the forward and reverse channel:
.cliq/channels/├── developer--reviewer/ # developer → reviewer (normal DAG flow)└── reviewer--developer/ # reviewer → developer (gate feedback)When the reviewer gate routes work back to the developer, it writes specific feedback into reviewer--developer/handoff.md — what failed, what needs fixing, and any relevant context. The developer reads this feedback on its next activation, fixes the issues, and writes back to developer--reviewer/ so the gate can re-evaluate.
This feedback loop can repeat up to the gate’s max_iterations budget (typically 3).
Channel Instructions
Section titled “Channel Instructions”Any channel directory can contain an instructions.md file with role-specific guidance for that handoff. These are included in the agent’s prompt context and read before the agent produces its handoff.
.cliq/channels/architect--developer/├── instructions.md # "Include file paths, API signatures, and test expectations"└── handoff.md # The actual handoff (written by the architect at runtime)Channel instructions are optional. When present, they shape the handoff’s format and content. You can use them to enforce structure — requiring specific sections, file path references, or acceptance criteria.
Empty and Missing Channels
Section titled “Empty and Missing Channels”If a channel directory exists but contains no handoff files, the downstream agent simply has no predecessor context for that edge. The agent proceeds with whatever other context it has (role instructions, task board, design docs, other incoming channels).
If a channel directory doesn’t exist at all, it won’t appear in the agent’s prompt — the prompt generator only includes channels for edges that exist in the DAG.
Neither case is an error. Phases without predecessors (like the first phase in a pipeline) naturally have no incoming channels.
Signals
Section titled “Signals”Signals are small files in .cliq/signals/ that the orchestrator uses to coordinate the pipeline. They answer questions like: Has this phase finished? What did the gate decide? Is the pipeline still running?
Agents don’t read signals (with one exception: gate agents write verdict signals). Signals are primarily an orchestrator-to-orchestrator mechanism.
Signal Reference
Section titled “Signal Reference”| Signal file | Written by | Contents | Purpose |
|---|---|---|---|
{phase}_done | Cliq Agent SDK | Empty file | Phase completed its work |
{gate}_verdict_raw | Gate agent | VERDICT: PASS\nREASON: ... | Raw verdict text from the gate agent |
{gate}_verdict | Orchestrator | JSON GateVerdict object | Parsed, structured verdict |
{gate}_gate_iteration | Orchestrator | Integer (e.g., 2) | Current gate loop iteration |
{phase}_routed | Orchestrator | Empty file | Support phase was activated by a gate route |
_pipeline_status | Orchestrator | COMPLETED, ESCALATED, FAILED, or CANCELLED | Final pipeline outcome |
Signal Lifecycle
Section titled “Signal Lifecycle”The lifecycle of a standard phase follows this sequence:
-
Clear — The orchestrator calls
SignalManager.clear_done()to remove any stale{phase}_donesignal from a previous run or iteration. -
Activate — The orchestrator writes a dispatch file and spawns the agent process in a tmux pane.
-
Agent works — The cliq agent SDK reads the dispatch, assembles context (role, channels, requirements, task board), calls the implementation, and captures the result. The implementation does the creative work (running a CLI, calling an API, waiting for human review).
-
SDK signals done — The SDK writes
.cliq/signals/{phase}_doneafter successfully processing the result. This creates an empty file. -
Orchestrator detects — The orchestrator polls for the
_donefile every 3 seconds (thePOLL_INTERVAL). When it finds the file, it knows the agent is finished. -
Advance — The agent process exits cleanly. The orchestrator checks which phases are now ready (all dependencies met) and activates the next batch.
Gate Signal Flow
Section titled “Gate Signal Flow”Gate phases have a more complex signal flow than standard phases:
-
Orchestrator runs automated checks — Before the gate agent even starts, the orchestrator runs any configured check commands (tests, linters, etc.) and records results.
-
Orchestrator builds gate context — Check results, team manifest, and verdict history are written to
.cliq/gate_context.md. -
Orchestrator clears previous verdict —
clear_verdict()removes the old{gate}_verdictand{gate}_verdict_rawfiles. -
Gate agent evaluates — The orchestrator injects the verdict protocol into the gate agent’s prompt automatically. The agent reads the gate context, reviews the work, and writes its verdict to
{gate}_verdict_raw:VERDICT: PASSREASON: All checks pass and code meets requirements.Or to route work back:
VERDICT: ROUTE:developerREASON: Tests fail due to incomplete error handling.Or to escalate to a human:
VERDICT: ESCALATEREASON: Merge conflicts require human judgment. -
Orchestrator parses verdict — The orchestrator reads the raw verdict, parses it into a structured
GateVerdict, and writes the parsed version to{gate}_verdict. -
Branch on outcome:
- PASS — Gate is done. Pipeline continues to post-gate phases.
- ROUTE — Orchestrator activates the target phase (e.g., developer), waits for it to complete, increments
{gate}_gate_iteration, and loops back to step 1. - ESCALATE — Pipeline halts. Human intervention required.
-
Budget check — If the gate has exhausted its
max_iterationsbudget without passing, the orchestrator auto-escalates.
Poll Interval and Timeouts
Section titled “Poll Interval and Timeouts”| Parameter | Value | Description |
|---|---|---|
POLL_INTERVAL | 3 seconds | How often the orchestrator checks for signal files |
PHASE_TIMEOUT | 20 minutes | Max time a standard phase can run before escalation |
GATE_AGENT_TIMEOUT | 15 minutes | Max time a gate agent can take to produce a verdict |
If a phase times out without writing its _done signal, the orchestrator escalates and halts the pipeline. The agent may be stuck, crashed, or simply taking too long.
Pipeline Cancellation
Section titled “Pipeline Cancellation”The _pipeline_status signal doubles as a cancellation mechanism. When a user runs cliq cancel or sends SIGINT/SIGTERM to the orchestrator:
- The orchestrator writes
CANCELLEDto.cliq/signals/_pipeline_status - Every poll loop checks for cancellation before checking for
_done - If cancelled, the orchestrator cleans up and exits
This means cancellation is detected within one POLL_INTERVAL (3 seconds) regardless of what the agents are doing.
Directory Layout
Section titled “Directory Layout”Here’s the complete .cliq/ directory structure for a pipeline with four phases (architect → developer → reviewer gate → developer support loop):
.cliq/├── channels/│ ├── architect--developer/│ │ ├── instructions.md # (optional) guidance for this handoff│ │ └── handoff.md # architect's plan for the developer│ ├── architect--security-auditor/│ │ └── handoff.md # architect's threat model notes│ ├── developer--reviewer/│ │ └── handoff.md # developer's summary for the reviewer│ └── reviewer--developer/│ └── handoff.md # reviewer's feedback (gate route)│├── signals/│ ├── architect_done # empty file — architect finished│ ├── developer_done # empty file — developer finished│ ├── security-auditor_done # empty file — security-auditor finished│ ├── reviewer_verdict_raw # "VERDICT: PASS\nREASON: ..."│ ├── reviewer_verdict # {"outcome":"PASS","reason":"..."}│ ├── reviewer_gate_iteration # "2"│ ├── developer_routed # empty — developer was re-activated by gate│ └── _pipeline_status # "COMPLETED"│├── roles/│ ├── architect.md│ ├── developer.md│ ├── security-auditor.md│ └── reviewer.md│├── prompts/│ ├── 1_architect.md│ ├── 2_developer.md│ ├── 3_security-auditor.md│ └── 4_reviewer.md│├── design/ # shared design documents├── task_board.md # shared task board├── gate_context.md # built by orchestrator for gate agents└── orchestrator.log # full orchestrator outputDebugging
Section titled “Debugging”Reading Channels During a Run
Section titled “Reading Channels During a Run”Channels are just directories with markdown files. You can read them at any time, even while the pipeline is running:
# See all channelsls .cliq/channels/
# Read a specific handoffcat .cliq/channels/architect--developer/handoff.md
# Check if a channel has contentls -la .cliq/channels/developer--reviewer/If a downstream agent is behaving unexpectedly, check its incoming channels first — the handoff it received may be incomplete or misleading.
Checking Signal State
Section titled “Checking Signal State”# See all signalsls -la .cliq/signals/
# Check if a specific phase is donetest -f .cliq/signals/developer_done && echo "done" || echo "not done"
# Read the current gate verdictcat .cliq/signals/reviewer_verdict_raw
# Check the gate iteration countcat .cliq/signals/reviewer_gate_iteration
# Check pipeline statuscat .cliq/signals/_pipeline_statusCommon Issues
Section titled “Common Issues”Phase never signals done. The agent process may have crashed or the implementation may have hung. Check the agent’s tmux pane output and .cliq/signals/{phase}_error for error details. The orchestrator will eventually time out (20 minutes) and escalate.
Gate stuck in a loop. The gate keeps routing to the same phase without progress. Check reviewer_gate_iteration — if it’s approaching max_iterations, the gate will auto-escalate. Read the verdict history in gate_context.md to see what’s happening across iterations.
Stale signals from a previous run. The orchestrator calls SignalManager.clear_done() before activating each phase, so stale _done files are not normally an issue. If you’re debugging manually, you can clear all signals:
rm .cliq/signals/*_donerm .cliq/signals/*_verdict*rm .cliq/signals/*_gate_iterationrm .cliq/signals/*_routedChannel handoff is empty or wrong. If a predecessor phase wrote a poor handoff, you can edit the file directly and re-run the downstream phase. The channel file is the input — fix the input, get better output.
Example: A Three-Phase Pipeline
Section titled “Example: A Three-Phase Pipeline”Consider a simple pipeline: architect → developer → reviewer (gate).
Workflow
Section titled “Workflow”phases: - name: architect type: standard - name: developer type: standard depends_on: [architect] - name: reviewer type: gate depends_on: [developer] max_iterations: 3Run Trace
Section titled “Run Trace”Step 1: Orchestrator starts. Creates channels and clears signals.
.cliq/channels/architect--developer/ (empty).cliq/channels/developer--reviewer/ (empty).cliq/channels/reviewer--developer/ (empty, gate route channel)Step 2: Architect runs. No incoming channels (first phase). Writes its plan:
# Architecture Handoff
## ApproachImplement the REST API with three endpoints: GET /items, POST /items, DELETE /items/:id.
## File Structure- src/routes/items.ts — route handlers- src/models/item.ts — data model- src/db/connection.ts — database setup
## Key Decisions- Use Express for routing- SQLite for storage (portable, no daemon)- Input validation with zod schemasArchitect touches architect_done. Orchestrator detects it, kills the architect process.
Step 3: Developer runs. Reads architect--developer/handoff.md, implements the plan, writes its summary:
# Developer Handoff
## What Was Built- Implemented all three endpoints in src/routes/items.ts- Added zod validation schemas in src/models/item.ts- SQLite connection pool in src/db/connection.ts- Added 12 unit tests in tests/items.test.ts
## Notes for Review- Used transactions for DELETE to handle concurrent access- Tests cover happy path and validation errorsDeveloper touches developer_done. Orchestrator detects it, moves to the gate.
Step 4: Reviewer gate (iteration 1). Orchestrator runs checks (tests, linter). Tests fail. Orchestrator builds gate_context.md, launches the gate agent. The reviewer writes:
VERDICT: ROUTE:developerREASON: 2 of 12 tests fail — DELETE endpoint returns 500 instead of 404 for missing items.Orchestrator parses the verdict, writes feedback to the gate channel:
# Gate Feedback (Iteration 1)
Fix the DELETE endpoint: it should return 404 when the item doesn't exist,not 500. The missing-item test and the concurrent-delete test both fail.Step 5: Developer runs again (routed). Reads reviewer--developer/handoff.md, fixes the bug, writes back to developer--reviewer/, touches developer_done.
Step 6: Reviewer gate (iteration 2). Orchestrator re-runs checks. All tests pass. Gate agent writes:
VERDICT: PASSREASON: All 12 tests pass. Code is clean and well-structured.Orchestrator writes COMPLETED to _pipeline_status. Pipeline done.