Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ packages/
│ ├── event-emitter.ts # Workflow observability events
│ ├── logger.ts # JSONL file logger
│ ├── validator.ts # Resource validation (command files, MCP configs, skill dirs)
│ ├── gates/ # Quality gate engine, parsers, and built-in gate definitions
│ ├── defaults/ # Bundled default commands and workflows
│ └── utils/ # Variable substitution, tool formatting, execution utilities
├── git/ # @archon/git - Git operations (no @archon/core dep)
Expand Down Expand Up @@ -375,7 +376,7 @@ import type { DagNode, WorkflowDefinition } from '@/lib/api';

### Database Schema

**8 Tables (all prefixed with `remote_agent_`):**
**10 Tables (all prefixed with `remote_agent_`):**
1. **`codebases`** - Repository metadata and commands (JSONB)
2. **`conversations`** - Track platform conversations with titles and soft-delete support
3. **`sessions`** - Track AI SDK sessions with resume capability
Expand All @@ -384,6 +385,8 @@ import type { DagNode, WorkflowDefinition } from '@/lib/api';
6. **`workflow_events`** - Step-level workflow event log (step transitions, artifacts, errors)
7. **`messages`** - Conversation message history with tool call metadata (JSONB)
8. **`codebase_env_vars`** - Per-project env vars injected into Claude SDK subprocess env (managed via Web UI or `env:` in config)
9. **`node_states`** - Per-node execution state for DAG resume and quality gate results
10. **`test_results`** - Parsed test suite results linked to node states (gate evidence)

**Key Patterns:**
- Conversation ID format: Platform-specific (`thread_ts`, `chat_id`, `user/repo#123`)
Expand Down Expand Up @@ -681,7 +684,7 @@ async function createSession(conversationId: string, codebaseId: string) {
2. **Workflows** (YAML-based):
- Stored in `.archon/workflows/` (searched recursively)
- Multi-step AI execution chains, discovered at runtime
- **`nodes:` (DAG format)**: Nodes with explicit `depends_on` edges; independent nodes in the same topological layer run concurrently. Node types: `command:` (named command file), `prompt:` (inline prompt), `bash:` (shell script, stdout captured as `$nodeId.output`, no AI), `loop:` (iterative AI prompt until completion signal), `approval:` (human gate; pauses until user approves or rejects; `capture_response: true` stores the user's comment as `$<node-id>.output` for downstream nodes, default false), `script:` (inline TypeScript/Python or named script from `.archon/scripts/`, runs via `bun` or `uv`, stdout captured as `$nodeId.output`, no AI, supports `deps:` for dependency installation and `timeout:` in ms, requires `runtime: bun` or `runtime: uv`) . Supports `when:` conditions, `trigger_rule` join semantics, `$nodeId.output` substitution, `output_format` for structured JSON output (Claude and Codex), `allowed_tools`/`denied_tools` for per-node tool restrictions (Claude only), `hooks` for per-node SDK hook callbacks (Claude only), `mcp` for per-node MCP server config files (Claude only, env vars expanded at execution time), and `skills` for per-node skill preloading via AgentDefinition wrapping (Claude only), and `effort`/`thinking`/`maxBudgetUsd`/`systemPrompt`/`fallbackModel`/`betas`/`sandbox` for Claude SDK advanced options (Claude only, also settable at workflow level)
- **`nodes:` (DAG format)**: Nodes with explicit `depends_on` edges; independent nodes in the same topological layer run concurrently. Node types: `command:` (named command file), `prompt:` (inline prompt), `bash:` (shell script, stdout captured as `$nodeId.output`, no AI), `loop:` (iterative AI prompt until completion signal), `approval:` (human gate; pauses until user approves or rejects; `capture_response: true` stores the user's comment as `$<node-id>.output` for downstream nodes, default false), `script:` (inline TypeScript/Python or named script from `.archon/scripts/`, runs via `bun` or `uv`, stdout captured as `$nodeId.output`, no AI, supports `deps:` for dependency installation and `timeout:` in ms, requires `runtime: bun` or `runtime: uv`) . Supports `when:` conditions, `trigger_rule` join semantics, `$nodeId.output` substitution, `output_format` for structured JSON output (Claude and Codex), `allowed_tools`/`denied_tools` for per-node tool restrictions (Claude only), `hooks` for per-node SDK hook callbacks (Claude only), `mcp` for per-node MCP server config files (Claude only, env vars expanded at execution time), and `skills` for per-node skill preloading via AgentDefinition wrapping (Claude only), `gates` for per-node quality gate definitions (bash commands run after node completion; `severity`, `type`, `maxRetries`), and `effort`/`thinking`/`maxBudgetUsd`/`systemPrompt`/`fallbackModel`/`betas`/`sandbox` for Claude SDK advanced options (Claude only, also settable at workflow level)
- Provider inherited from `.archon/config.yaml` unless explicitly set; per-node `provider` and `model` overrides supported
- Model and options can be set per workflow or inherited from config defaults
- `interactive: true` at the workflow level forces foreground execution on web (required for approval-gate workflows in the web UI)
Expand Down Expand Up @@ -756,6 +759,8 @@ Pattern: Use `classifyIsolationError()` (from `@archon/isolation`) to map git er
- `POST /api/workflows/runs/{runId}/resume` - Mark a failed run as ready for auto-resume on next invocation
- `POST /api/workflows/runs/{runId}/abandon` - Abandon a non-terminal run (marks as cancelled)
- `DELETE /api/workflows/runs/{runId}` - Delete a terminal workflow run and its events
- `GET /api/workflows/runs/{runId}/summary` - Get run summary including node states and gate results
- `POST /api/workflows/runs/{runId}/gate-result` - Store a gate result for a node (appends to existing results)

**Codebases:**
- `GET /api/codebases` / `GET /api/codebases/:id` - List / fetch codebases
Expand Down
38 changes: 38 additions & 0 deletions migrations/022_node_states.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
-- Node state store: persists validated node execution state
CREATE TABLE IF NOT EXISTS remote_agent_node_states (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
workflow_run_id UUID NOT NULL REFERENCES remote_agent_workflow_runs(id) ON DELETE CASCADE,
node_id VARCHAR(255) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
output TEXT DEFAULT '',
output_validated BOOLEAN DEFAULT FALSE,
gate_results JSONB DEFAULT '[]',
attempt_count INTEGER DEFAULT 0,
started_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
completed_at TIMESTAMP WITH TIME ZONE,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(workflow_run_id, node_id)
);

CREATE INDEX IF NOT EXISTS idx_node_states_run_id
ON remote_agent_node_states(workflow_run_id);
CREATE INDEX IF NOT EXISTS idx_node_states_status
ON remote_agent_node_states(status);

-- Test result evidence: stores actual test run data per node
CREATE TABLE IF NOT EXISTS remote_agent_test_results (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
node_state_id UUID NOT NULL REFERENCES remote_agent_node_states(id) ON DELETE CASCADE,
suite_name VARCHAR(255) NOT NULL,
total INTEGER NOT NULL DEFAULT 0,
passed INTEGER NOT NULL DEFAULT 0,
failed INTEGER NOT NULL DEFAULT 0,
skipped INTEGER NOT NULL DEFAULT 0,
failures JSONB DEFAULT '[]',
stdout TEXT DEFAULT '',
exit_code INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_test_results_node_state
ON remote_agent_test_results(node_state_id);
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"./state/*": "./src/state/*.ts"
},
"scripts": {
"test": "bun test src/clients/codex-binary-guard.test.ts && bun test src/utils/codex-binary-resolver.test.ts && bun test src/utils/codex-binary-resolver-dev.test.ts && bun test src/clients/claude.test.ts src/clients/codex.test.ts src/clients/factory.test.ts && bun test src/handlers/command-handler.test.ts && bun test src/handlers/clone.test.ts && bun test src/db/adapters/postgres.test.ts && bun test src/db/adapters/sqlite.test.ts src/db/codebases.test.ts src/db/connection.test.ts src/db/conversations.test.ts src/db/env-vars.test.ts src/db/isolation-environments.test.ts src/db/messages.test.ts src/db/sessions.test.ts src/db/workflow-events.test.ts src/db/workflows.test.ts src/utils/defaults-copy.test.ts src/utils/worktree-sync.test.ts src/utils/conversation-lock.test.ts src/utils/credential-sanitizer.test.ts src/utils/port-allocation.test.ts src/utils/error.test.ts src/utils/error-formatter.test.ts src/utils/github-graphql.test.ts src/utils/env-allowlist.test.ts src/utils/env-leak-scanner.test.ts src/config/ src/state/ && bun test src/utils/path-validation.test.ts && bun test src/services/cleanup-service.test.ts && bun test src/services/title-generator.test.ts && bun test src/workflows/ && bun test src/operations/workflow-operations.test.ts && bun test src/operations/isolation-operations.test.ts && bun test src/orchestrator/orchestrator.test.ts && bun test src/orchestrator/orchestrator-agent.test.ts && bun test src/orchestrator/orchestrator-isolation.test.ts",
"test": "bun test src/clients/codex-binary-guard.test.ts && bun test src/utils/codex-binary-resolver.test.ts && bun test src/utils/codex-binary-resolver-dev.test.ts && bun test src/clients/claude.test.ts src/clients/codex.test.ts src/clients/factory.test.ts && bun test src/handlers/command-handler.test.ts && bun test src/handlers/clone.test.ts && bun test src/db/adapters/postgres.test.ts && bun test src/db/adapters/sqlite.test.ts src/db/codebases.test.ts src/db/connection.test.ts src/db/conversations.test.ts src/db/env-vars.test.ts src/db/isolation-environments.test.ts src/db/messages.test.ts src/db/sessions.test.ts src/db/workflow-events.test.ts src/db/workflows.test.ts src/db/node-states.test.ts src/utils/defaults-copy.test.ts src/utils/worktree-sync.test.ts src/utils/conversation-lock.test.ts src/utils/credential-sanitizer.test.ts src/utils/port-allocation.test.ts src/utils/error.test.ts src/utils/error-formatter.test.ts src/utils/github-graphql.test.ts src/utils/env-allowlist.test.ts src/utils/env-leak-scanner.test.ts src/config/ src/state/ && bun test src/utils/path-validation.test.ts && bun test src/services/cleanup-service.test.ts && bun test src/services/title-generator.test.ts && bun test src/workflows/ && bun test src/operations/workflow-operations.test.ts && bun test src/operations/isolation-operations.test.ts && bun test src/orchestrator/orchestrator.test.ts && bun test src/orchestrator/orchestrator-agent.test.ts && bun test src/orchestrator/orchestrator-isolation.test.ts",
"type-check": "bun x tsc --noEmit",
"build": "echo 'No build needed - Bun runs TypeScript directly'"
},
Expand Down
35 changes: 35 additions & 0 deletions packages/core/src/db/adapters/sqlite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,37 @@ export class SqliteAdapter implements IDatabase {
created_at TEXT DEFAULT (datetime('now'))
);

-- Node states table (validated node execution state)
CREATE TABLE IF NOT EXISTS remote_agent_node_states (
id TEXT PRIMARY KEY,
workflow_run_id TEXT NOT NULL REFERENCES remote_agent_workflow_runs(id) ON DELETE CASCADE,
node_id TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
output TEXT DEFAULT '',
output_validated INTEGER DEFAULT 0,
gate_results TEXT DEFAULT '[]',
attempt_count INTEGER DEFAULT 0,
started_at TEXT DEFAULT (datetime('now')),
completed_at TEXT,
updated_at TEXT DEFAULT (datetime('now')),
UNIQUE(workflow_run_id, node_id)
);

-- Test results table (test run evidence per node)
CREATE TABLE IF NOT EXISTS remote_agent_test_results (
id TEXT PRIMARY KEY,
node_state_id TEXT NOT NULL REFERENCES remote_agent_node_states(id) ON DELETE CASCADE,
suite_name TEXT NOT NULL,
total INTEGER NOT NULL DEFAULT 0,
passed INTEGER NOT NULL DEFAULT 0,
failed INTEGER NOT NULL DEFAULT 0,
skipped INTEGER NOT NULL DEFAULT 0,
failures TEXT DEFAULT '[]',
stdout TEXT DEFAULT '',
exit_code INTEGER NOT NULL DEFAULT 0,
created_at TEXT DEFAULT (datetime('now'))
);

-- Messages table (conversation history for Web UI)
CREATE TABLE IF NOT EXISTS remote_agent_messages (
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
Expand Down Expand Up @@ -383,6 +414,10 @@ export class SqliteAdapter implements IDatabase {
CREATE INDEX IF NOT EXISTS idx_sessions_codebase ON remote_agent_sessions(codebase_id);
CREATE INDEX IF NOT EXISTS idx_isolation_env_status ON remote_agent_isolation_environments(status);

CREATE INDEX IF NOT EXISTS idx_node_states_run_id ON remote_agent_node_states(workflow_run_id);
CREATE INDEX IF NOT EXISTS idx_node_states_status ON remote_agent_node_states(status);
CREATE INDEX IF NOT EXISTS idx_test_results_node_state ON remote_agent_test_results(node_state_id);

-- From PG migration 009: staleness detection for running workflows
CREATE INDEX IF NOT EXISTS idx_workflow_runs_last_activity
ON remote_agent_workflow_runs(last_activity_at) WHERE status = 'running';
Expand Down
Loading
Loading