Skip to content
Closed
143 changes: 143 additions & 0 deletions .fork-features/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,149 @@
"symlink resolution"
]
}
},
"taskctl": {
"status": "active",
"description": "Autonomous pipeline for managing task graphs with dependencies, priority ordering, conflict detection, and adversarial workflow. Phase 1 includes types, store, scheduler, validation, and CLI tool. Phase 2 adds Composer agent for automatic issue decomposition, taskctl start command for spawning full pipeline. Phase 3 adds Pulse execution engine with heartbeat, scheduling, singleton lock, crash recovery, and timeout detection. Phase 3a adds adversarial workflow integration with pipeline stages (developing, reviewing, adversarial, steering), verdict tracking, and auto-retry logic. Phase 3b adds agent.ts integration for spawning adversarial sessions. Phase 3c adds PM control commands: status, stop, resume, override, retry, inspect for manual pipeline intervention. Tasks are JSON files stored in Global.Path.data/tasks/{projectId}/ with atomic writes and append-only activity logging.",
"issue": "https://github.com/randomm/opencode/issues/203",
"newFiles": [
"packages/opencode/src/tasks/types.ts",
"packages/opencode/src/tasks/store.ts",
"packages/opencode/src/tasks/scheduler.ts",
"packages/opencode/src/tasks/validation.ts",
"packages/opencode/src/tasks/composer.ts",
"packages/opencode/src/tasks/pulse.ts",
"packages/opencode/src/tasks/tool.ts",
"packages/opencode/src/tasks/index.ts",
"packages/opencode/src/tasks/LIMITATIONS.md",
"packages/opencode/test/tasks/store.test.ts",
"packages/opencode/test/tasks/scheduler.test.ts",
"packages/opencode/test/tasks/validation.test.ts",
"packages/opencode/test/tasks/composer.test.ts",
"packages/opencode/test/tasks/pulse.test.ts",
"packages/opencode/test/tasks/commands.test.ts"
],
"modifiedFiles": ["packages/opencode/src/tool/registry.ts", "packages/opencode/src/agent/agent.ts"],
"deletedFiles": [],
"criticalCode": [
"sanitizeProjectId",
"sanitizeTaskId",
"getSafeTaskPath",
"IMMUTABLE_FIELDS",
"validateTaskUpdates",
"const TASKS_DIR = \"tasks\"",
"getTasksDir",
"atomicWrite",
"Store.createTask",
"Store.getTask",
"Store.updateTask",
"Store.listTasks",
"Store.updateIndex",
"Store.getIndex",
"Store.logActivity",
"Store.createJob",
"Store.getJob",
"Store.updateJob",
"Store.addComment",
"Store.addPipelineEvent",
"Store.findJobByIssue",
"Scheduler.getNextTasks",
"Validation.validateGraph",
"Validation.validateGraphFromMap",
"startPulse",
"tickStartTime",
"resurrectionScan",
"isSessionAlive",
"lock file management",
"writeLockFile",
"readLockPid",
"removeLockFile",
"isPidAlive",
"checkTimeouts",
"TIMEOUT_MS",
"checkCompletion",
"gracefulStop",
"heartbeatActiveAgents",
"Pulse tick overlap guard",
"Atomic lock file write",
"Crash recovery",
"Worker session spawn",
"TaskctlTool",
"taskctl create",
"taskctl list",
"taskctl get",
"taskctl update",
"taskctl close",
"taskctl comment",
"taskctl depends",
"taskctl split",
"taskctl next",
"taskctl validate",
"taskctl start",
"taskctl start-skip",
"taskctl status",
"taskctl stop",
"taskctl resume",
"taskctl inspect",
"taskctl override",
"taskctl retry",
"command enum (stop, resume, inspect, override, retry)",
"overrideMode parameter (skip, commit-as-is)",
"Terminal state rejection (complete/failed/stopped)",
"Pulse-zombie detection in stop command",
"Job stopping flag validation",
"Session cancellation and worktree cleanup",
"composer agent",
"runComposer",
"Circular dependency detection",
"Missing dependency detection",
"Acceptance criteria warning",
"Conflict label warning",
"Priority ordering (0=highest)",
"Dependency filtering",
"Self-dependency prevention",
"Duplicate dependency check",
"Status validation (only split open, prevent re-close)",
"MAX_COMMENT_LENGTH",
"maxAttempts (1000)",
"append-only.ndjson",
"activity.ndjson",
"index.json",
"slug generation",
"conflict detection"
],
"tests": [
"packages/opencode/test/tasks/store.test.ts",
"packages/opencode/test/tasks/scheduler.test.ts",
"packages/opencode/test/tasks/validation.test.ts",
"packages/opencode/test/tasks/composer.test.ts",
"packages/opencode/test/tasks/pulse.test.ts",
"packages/opencode/test/tasks/commands.test.ts"
],
"upstreamTracking": {
"absorptionSignals": [
"TASKS_DIR",
"taskctl",
"Composer",
"runComposer",
"Scheduler.getNextTasks",
"Validation.validateGraph",
"Validation.validateGraphFromMap",
"sanitizeProjectId",
"sanitizeTaskId",
"IMMUTABLE_FIELDS",
"task.*pipeline.*adversarial",
"Circular dependency",
"conflict labels",
"module:.*file:",
"activity.ndjson",
"atomicWrite",
"slug.*generation",
"LIMITATIONS.md",
"TOCTOU",
"concurrent operation"
]
}
}
}
}
154 changes: 154 additions & 0 deletions packages/opencode/src/agent/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,161 @@ export namespace Agent {
),
prompt: PROMPT_SUMMARY,
},
composer: {
name: "composer",
mode: "subagent",
hidden: true,
native: true,
options: {},
permission: PermissionNext.merge(
defaults,
PermissionNext.fromConfig({
"*": "deny",
}),
user,
),
prompt: `You are the Composer agent for the taskctl autonomous development pipeline.

Your job is to read a GitHub issue and decompose it into a structured, dependency-ordered list of implementation tasks.

RESPONSE FORMAT — you must respond with ONLY valid JSON, nothing else:

If the spec is too vague or missing acceptance criteria:
{
"status": "needs_clarification",
"questions": [
{ "id": 1, "question": "What specific behaviour should change?" }
]
}

If the spec is clear enough to decompose:
{
"status": "ready",
"tasks": [
{
"title": "Add OAuth2 config schema",
"description": "Add zod schema for OAuth2 config to src/config/config.ts",
"acceptance_criteria": "Schema validates clientId, clientSecret, redirectUri. Tests pass.",
"task_type": "implementation",
"labels": ["module:config", "file:src/config/config.ts"],
"depends_on": [],
"priority": 0
}
]
}

RULES FOR GOOD TASK DECOMPOSITION:
1. Each task must be completable by one developer in a single session
2. Every task MUST have non-empty acceptance_criteria
3. Every task MUST have at least one label with "module:" or "file:" prefix
4. Dependencies: tasks that others depend on have lower priority numbers (0 = highest priority)
5. Tasks with no shared module:/file: labels can run in parallel
6. Do not create tasks for work not explicitly required by the issue
7. Validate your own output: check that no depends_on creates a cycle before responding
8. Respond with ONLY the JSON object — no markdown, no explanation, no code blocks`,
},
"developer-pipeline": {
name: "developer-pipeline",
description: "Developer agent working as part of an autonomous pipeline.",
mode: "subagent",
native: true,
permission: PermissionNext.merge(
defaults,
PermissionNext.fromConfig({
task: "deny",
}),
user,
),
options: {},
prompt: `You are a developer agent working as part of an autonomous pipeline.

Your job is to implement the assigned task with TDD discipline.

## Your task
You will receive a task description with:
- Title: what to build
- Description: full context and requirements
- Acceptance criteria: what must be true when done

## Workflow
1. Read the codebase to understand context (check remory, read relevant files)
2. Write failing tests first (TDD)
3. Write minimal code to make tests pass
4. Refactor for clarity following AGENTS.md style guide
5. Run \`bun run typecheck && bun test\` — fix all errors
6. When done: \`taskctl comment <taskId> "Implementation complete: <one-line summary of what was built>"\`

## Rules
- ONLY implement what is explicitly in the task description
- No TODO/FIXME/HACK comments (create a GitHub issue instead)
- No @ts-ignore or as any
- Follow style guide: single-word variable names, early returns, no else, functional array methods
- Do NOT spawn any adversarial agent — the pipeline handles this automatically
- Do NOT commit or push — the pipeline handles this automatically
- Do NOT write any documentation files (PLAN.md, ANALYSIS.md, etc.)

## taskctl commands available to you
- \`taskctl comment <taskId> "<message>"\` — log progress or signal completion
- \`taskctl split <taskId>\` — if task is too large, split it (creates two sub-tasks)
- \`taskctl depends <taskId> --on <otherId>\` — if you discover an undeclared dependency

You may NOT call: taskctl start, taskctl stop, taskctl verdict, taskctl override, taskctl retry, taskctl resume`,
},
"adversarial-pipeline": {
name: "adversarial-pipeline",
description: "Adversarial code reviewer in an autonomous pipeline.",
mode: "subagent",
native: true,
permission: PermissionNext.merge(
defaults,
PermissionNext.fromConfig({
"*": "deny",
bash: "allow",
}),
user,
),
options: {},
prompt: `You are an adversarial code reviewer in an autonomous pipeline.

Your ONLY job is to review code changes in an assigned worktree and record a structured verdict.

## What you receive
- Task title, description, and acceptance criteria
- Path to the worktree containing the implementation
- The task ID

## Your review process
1. Read the implementation files in the worktree
2. Check: Does it meet the acceptance criteria?
3. Check: Are there bugs, security issues, or quality problems?
4. Check: Do the tests actually test meaningful behavior (not just call coverage)?
5. Check: Does typecheck pass? (Run \`bun run typecheck\` in the worktree)

## Recording your verdict — MANDATORY
You MUST call taskctl verdict to record your finding. Never write a text response instead.

**If the code is good:**
\`taskctl verdict <taskId> --verdict APPROVED\`

**If there are fixable issues:**
\`taskctl verdict <taskId> --verdict ISSUES_FOUND --summary "Brief summary" --issues '[{"location":"src/foo.ts:42","severity":"HIGH","fix":"Add null check before calling user.profile"}]'\`

**If there are critical/blocking issues:**
\`taskctl verdict <taskId> --verdict CRITICAL_ISSUES_FOUND --summary "Brief summary" --issues '[...]'\`

## Severity guide
- CRITICAL: Security vulnerability, data loss risk, or complete functional failure
- HIGH: Bug that will cause incorrect behavior in normal use
- MEDIUM: Code quality issue that should be fixed before merging
- LOW: Style or minor improvement suggestion

## Rules
- You may ONLY call: taskctl verdict
- Do NOT spawn any agents
- Do NOT commit or push
- Be specific: every issue must have a location (file:line) and a concrete fix suggestion`,
},
}

for (const [key, value] of Object.entries(cfg.agent ?? {})) {
if (value.disable) {
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/session/async-tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ export async function tryCancel(id: string, sessionID: string): Promise<CancelTa

// Check authorization - task must belong to the same session
const metadata = pendingTaskMetadata.get(id)
if (metadata && metadata.session_id !== sessionID) {
if (metadata && metadata.parent_session_id !== sessionID) {
return {
status: "unauthorized",
message: `Cannot cancel task from a different session`,
Expand Down
Loading