Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4087e9f
[AISOS-1984] Extend Settings Pydantic configuration schema to support…
forgeSmith-bot Jun 29, 2026
abb8060
[AISOS-1985] Define Task Takeover state-transition and workflow labels
forgeSmith-bot Jun 29, 2026
aee262d
[AISOS-1986] Support standalone Task Webhook routing and parentless c…
forgeSmith-bot Jun 29, 2026
82c02d1
[AISOS-1987] Implement TaskTakeoverWorkflow class and matches conditions
forgeSmith-bot Jun 29, 2026
acb5adc
[AISOS-1988] Register TaskTakeoverWorkflow in Registry and implement …
forgeSmith-bot Jun 29, 2026
c095725
[AISOS-1989] Support Task Plan Approval Resumption and YOLO Gate in O…
forgeSmith-bot Jun 29, 2026
8e67809
[AISOS-1990] Define Task Takeover workflow state and graph structure
forgeSmith-bot Jun 29, 2026
fa7aa26
[AISOS-1991] Add markdown prompt templates for Task Takeover stages
forgeSmith-bot Jun 29, 2026
ca65a3a
[AISOS-1992] Implement Automated Task Takeover Triage Node (triage_task)
forgeSmith-bot Jun 29, 2026
a0ae004
[AISOS-1993] Implement Task Takeover Planning Node (generate_plan)
forgeSmith-bot Jun 29, 2026
b35c8ae
[AISOS-1994] Implement Interactive Plan Approval Gate and Routing Logic
forgeSmith-bot Jun 29, 2026
741ecbb
[AISOS-1995] Add Unit and Integration Tests for Task Takeover
forgeSmith-bot Jun 29, 2026
e951e39
[AISOS-1996] Implement Task Takeover Execution Node
forgeSmith-bot Jun 29, 2026
2ff9755
[AISOS-1997] Implement Task Takeover Qualitative Review Node
forgeSmith-bot Jun 29, 2026
f82c997
[AISOS-1998] Implement Task Takeover PR and Ticket Transition Node
forgeSmith-bot Jun 29, 2026
b2698dd
[AISOS-1999] Add unit tests for Task Takeover Qualitative Review Node
forgeSmith-bot Jun 29, 2026
657a18b
[AISOS-2000] Add Integrated Sandbox Tests for Task Execution
forgeSmith-bot Jun 29, 2026
4095007
[AISOS-2001] Wire Up Task Takeover Nodes in the Workflow Graph
forgeSmith-bot Jun 29, 2026
0db7830
[AISOS-1977-review] Local code review — fix breaking issues
forgeSmith-bot Jun 29, 2026
8eb0bf3
[AISOS-1977-docs] Update stale documentation for Task Takeover
forgeSmith-bot Jun 29, 2026
ee57199
[AISOS-1977] review: address PR feedback
forgeSmith-bot Jun 30, 2026
ccf2f48
[AISOS-1977-review-review-impl] Fix task takeover routing and exact m…
forgeSmith-bot Jun 30, 2026
dfe2ae3
[AISOS-1977] simplify task takeover routing
eshulman2 Jul 2, 2026
33bf3d6
[AISOS-1977] remove task takeover settings
eshulman2 Jul 2, 2026
e501fc6
Refine task takeover workflow loop
eshulman2 Jul 2, 2026
610ac48
Align task takeover PR messaging
eshulman2 Jul 2, 2026
a539d7f
Align task takeover workflow messaging
eshulman2 Jul 2, 2026
7bb70cc
Route task takeover through PR lifecycle gates
eshulman2 Jul 2, 2026
67bba38
style: format PR creation node
eshulman2 Jul 2, 2026
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
5 changes: 4 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,14 @@ podman rm $(podman ps -a --filter name=forge- -q)
| `forge:spec-pending` | Awaiting spec approval |
| `forge:plan-pending` | Awaiting plan approval |
| `forge:task-pending` | Awaiting task approval |
| `forge:task-triage-pending` | Task takeover awaiting triage completion |
| `forge:managed:task` | Task identity preservation label |
| `forge:managed:task-takeover` | Task takeover identity preservation label |
| `forge:blocked` | Workflow blocked, needs intervention |
| `forge:retry` | Trigger retry of failed step |
| `forge:yolo` | Autonomous mode — skip all artifact approval gates (see warning below) |

> **⚠️ Warning — `forge:yolo`:** This label removes all human checkpoints for PRD, spec, plan, and task approval. Forge will proceed autonomously from ticket creation to implementation without pausing for review. Only use this on tickets where you are confident in the requirements and comfortable with Forge making all planning decisions. It does not bypass code review (the human review gate on the implementation PR is always required).
> **⚠️ Warning — `forge:yolo`:** This label removes all human checkpoints for PRD, spec, plan, task, and task plan approval. Forge will proceed autonomously from ticket creation to implementation without pausing for review. Only use this on tickets where you are confident in the requirements and comfortable with Forge making all planning decisions. It does not bypass code review (the human review gate on the implementation PR is always required).

## Jira Comment Syntax

Expand Down
13 changes: 12 additions & 1 deletion docs/guide/labels.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,28 @@ These labels advance the pipeline. Forge watches for label changes via Jira webh
| Plan Approval Gate | `forge:plan-pending` | Forge | Plan posted; waiting for approval |
| Plan Approval Gate | `forge:plan-approved` | Human | Approve plan and trigger task decomposition + implementation |

### Task Takeover Workflow

Standalone Tasks and Epics can be processed with the standard `forge:managed` label. These tickets bypass the standard parent Feature validation.

| Stage | Pending Label | Approved Label | Purpose |
|-------|--------------|----------------|---------|
| Triage | `forge:task-triage-pending` | _N/A_ | Standalone ticket is missing required fields; waiting for update |
| Plan Approval | `forge:plan-pending` | `forge:plan-approved` | Plan is posted; waiting for approval |

## Control Labels

| Label | Purpose |
|-------|---------|
| `forge:managed` | Marks the ticket for Forge automation. Add this when creating a ticket to start the workflow. |
| `forge:managed:task` | Identity preservation label used during Task Takeover transitions. |
| `forge:managed:task-takeover` | Identity preservation label used during Task Takeover transitions. |
| `forge:blocked` | Set by Forge when a stage fails. Forge posts a comment with the error. |
| `forge:retry` | Add this to resume from the exact node that failed. Forge removes it after resuming. |

## How to Use Labels

**Starting a workflow:** Create a Jira issue and add `forge:managed`. Forge detects the issue type (Feature or Bug) and begins the appropriate pipeline.
**Starting a workflow:** Create a Jira issue and add `forge:managed`. Forge detects the issue type and begins the appropriate pipeline: Feature/Story, Bug, or standalone Task/Epic takeover.

**Approving a stage:** When Forge posts a PRD, spec, or other artifact, it sets the `forge:*-pending` label. Change it to `forge:*-approved` to advance the workflow. Do not add the approved label manually before Forge posts — it won't be recognized until the pending state is set.

Expand Down
7 changes: 7 additions & 0 deletions src/forge/api/routes/jira.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,13 @@ async def receive_jira_webhook(
f"Routing {issue_type} {source_ticket_key} webhook "
f"to parent Feature {routing_ticket_key}"
)
elif issue_type in ("Epic", "Task"):
# Bypass parent validation for standalone managed Epic/Task issues.
# routing_ticket_key remains webhook_data.ticket_key, source_ticket_key remains None.
logger.info(
f"Bypassing parent checks for standalone {issue_type} "
f"{webhook_data.ticket_key}."
)
else:
# Epics/Tasks without forge:parent are invalid - reject
span.set_attribute("forge.skipped", True)
Expand Down
41 changes: 32 additions & 9 deletions src/forge/integrations/agents/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -1159,17 +1159,40 @@ async def answer_question(
generation_context = context.get("generation_context", {})
raw_requirements = generation_context.get("raw_requirements", "Not available")

prompt = load_prompt(
"answer-question",
artifact_type=artifact_type,
artifact_content=artifact_content,
raw_requirements=raw_requirements,
question=question,
)
ticket_type = context.get("ticket_type")
ticket_type_str = ""
if ticket_type is not None:
ticket_type_str = (
ticket_type.value if hasattr(ticket_type, "value") else str(ticket_type)
)

if (
artifact_type == "plan"
and context.get("current_node") == "task_plan_approval_gate"
and ticket_type_str == "task"
):
prompt = load_prompt(
"task-takeover-qa",
ticket_key=context.get("ticket_key", ""),
summary=context.get("summary", ""),
description=context.get("description", ""),
plan_content=artifact_content,
question=question,
)
task_name = "task-takeover-qa"
else:
prompt = load_prompt(
"answer-question",
artifact_type=artifact_type,
artifact_content=artifact_content,
raw_requirements=raw_requirements,
question=question,
)
task_name = "answer-question"

logger.info(f"Answering question about {artifact_type}")
logger.info(f"Answering question about {artifact_type} using task={task_name}")
result = await self.run_task(
task="answer-question",
task=task_name,
prompt=prompt,
context={
"artifact_type": artifact_type,
Expand Down
4 changes: 3 additions & 1 deletion src/forge/integrations/jira/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -742,13 +742,15 @@ async def set_workflow_label(
# Get current labels
current_labels = await self.get_labels(issue_key)

# Find forge: labels to remove (except the new one and forge:managed)
# Find forge: labels to remove (except the new one, forge:managed, and identity preservation labels)
labels_to_remove = [
label
for label in current_labels
if label.startswith(remove_prefix)
and label != new_label.value
and label != ForgeLabel.FORGE_MANAGED.value
and label != "forge:managed:task"
and label != "forge:managed:task-takeover"
]

# Build update operations
Expand Down
2 changes: 2 additions & 0 deletions src/forge/integrations/jira/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ def extract_children(nodes: list[dict[str, Any]]) -> list[str]:
return [block for block in blocks if block]

blocks = extract_blocks(adf)
if adf.get("type") == "doc" and not blocks:
return ""
return "\n\n".join(blocks) if blocks else str(adf)


Expand Down
3 changes: 3 additions & 0 deletions src/forge/models/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ class ForgeLabel(StrEnum):
RCA_APPROVED = "forge:rca-approved"
TRIAGE_PENDING = "forge:triage-pending"

# Task Takeover workflow
TASK_TRIAGE_PENDING = "forge:task-triage-pending"

# General
FORGE_MANAGED = "forge:managed"
BLOCKED = "forge:blocked"
Expand Down
18 changes: 11 additions & 7 deletions src/forge/orchestrator/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def _is_workflow_errored(state: dict) -> bool:
"prd_approval_gate",
"spec_approval_gate",
"plan_approval_gate",
"task_plan_approval_gate",
"task_approval_gate",
"rca_option_gate",
}
Expand Down Expand Up @@ -258,9 +259,10 @@ async def _process_workflow(self, message: QueueMessage) -> None:
)
else:
# Use router to resolve which workflow to use
labels = message.payload.get("issue", {}).get("fields", {}).get("labels", []) or []
workflow_instance = self.router.resolve(
ticket_type=ticket_type,
labels=[], # TODO: Extract labels from message payload
labels=labels,
event=message.payload,
)

Expand Down Expand Up @@ -597,11 +599,11 @@ async def _handle_resume_event(
"decompose_epics": "plan",
"regenerate_all_epics": "plan",
"update_single_epic": "plan",
"task_plan_approval_gate": "plan",
"task_approval_gate": "task",
"generate_tasks": "task",
}
expected_stage = node_to_stage.get(current_node)

if approval_stage and expected_stage and approval_stage == expected_stage:
is_approved = True
logger.info(
Expand All @@ -623,6 +625,7 @@ async def _handle_resume_event(
"prd_approval_gate": "forge:prd-approved",
"spec_approval_gate": "forge:spec-approved",
"plan_approval_gate": "forge:plan-approved",
"task_plan_approval_gate": "forge:plan-approved",
"task_approval_gate": "forge:task-approved",
}
expected_label = gate_to_approved_label.get(current_node)
Expand Down Expand Up @@ -1121,6 +1124,7 @@ async def _handle_resume_event(
"plan_approval_gate",
"task_approval_gate",
"plan_approval_gate_bug",
"task_plan_approval_gate",
}
prev_error = current_state.get("last_error")
is_paused_at_gate = current_state.get("is_paused") and current_node in approval_gates
Expand Down Expand Up @@ -1314,6 +1318,7 @@ def _stage_label_for_node(current_node: str) -> str:
"update_single_epic": "the plan",
"rca_option_gate": "the RCA",
"plan_approval_gate_bug": "the plan",
"task_plan_approval_gate": "the task plan",
"task_approval_gate": "the tasks",
"generate_tasks": "the tasks",
"regenerate_all_tasks": "the tasks",
Expand Down Expand Up @@ -1531,12 +1536,11 @@ def _extract_ticket_type(self, message: QueueMessage) -> TicketType:
issue_type = fields.get("issuetype", {})
ticket_type_str = issue_type.get("name", "Unknown")

# Child ticket events (Epic, Task) are re-routed to the parent Feature
# by the Jira webhook handler. The payload still carries the child's
# issue type, which won't match any workflow. Fall through to UNKNOWN
# so _find_workflow_by_state resolves it from checkpoint.
# Child ticket events are re-routed to the parent Feature by the Jira
# webhook handler. The payload still carries the child's issue type,
# so fall through to UNKNOWN only when this message is from a child.
child_types = {"Epic", "Task", "Sub-task"}
if ticket_type_str in child_types:
if ticket_type_str in child_types and message.payload.get("source_ticket_key"):
return TicketType.UNKNOWN

# Map string to TicketType enum
Expand Down
47 changes: 47 additions & 0 deletions src/forge/prompts/v1/task-takeover-planning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
## Task Ticket

**Key:** {ticket_key}
**Summary:** {summary}

**Description:**
{description}

**Comments:**
{comments}

## Available Repositories

Use only these exact repository names when tagging `repo:<owner>/<name>` in the plan:

{known_repos}

## File Metadata

Here is the file metadata gathered from the repository to help guide your plan:

{file_metadata}

## Repository Grounding Requirements

Before producing the plan, inspect the relevant repository using available repository, GitHub, or filesystem tools.

- Read repo guidance when present: `AGENTS.md`, `CLAUDE.md`, `.claude/AGENTS.md`, `.claude/CLAUDE.md`, `README.md`, `CONTRIBUTING.md`, `Makefile`, language-specific project files, docs, and repo-local skills or agent instructions.
- Confirm planned files, functions/classes, test locations, generated-file requirements, and validation commands against real repository contents.
- Follow discovered repository standards for architecture, naming, error handling, testing, packaging, documentation, and local agent workflow.
- Prefer codebase exploration focused on the ticket description, proposed solution/approach, nearby code, and validation commands. Broaden the search when needed to understand the context safely. Do not inspect project-management metadata such as unrelated branches, open issues, pull requests, milestones, or release boards unless explicitly required.
- Use nearby code and test patterns instead of guessing from path names alone.
- Do not invent generic paths, symbols, frameworks, test runners, or directory layouts. If repository inspection is unavailable, write the plan with an explicit blocking note explaining what repo access or configuration is required.

## Formulate Implementation Plan

Formulate a concrete implementation plan mapping the proposed solution to specific target files and test plans.

Your plan MUST include:
1. **Target Files**: List the specific, existing repository files to be modified, or new files to be created, incorporating the gathered file metadata and repository inspection.
2. **Implementation Steps**: Clear, sequential steps for implementing the proposed solution/approach.
3. **Test Plans**: A detailed validation plan describing how the changes will be tested. Map the proposed solutions to concrete unit or integration tests, naming specific test commands and test files (existing or new) to run.

---

Produce a detailed implementation plan as Markdown.
Return only the plan content; do not wrap it in code fences and do not write files.
30 changes: 30 additions & 0 deletions src/forge/prompts/v1/task-takeover-qa.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
You are answering a user's clarifying question during the interactive planning gate of Task Takeover.

## Task Ticket

**Key:** {ticket_key}
**Summary:** {summary}

**Description:**
{description}

## Current Implementation Plan

{plan_content}

## Question / Feedback

{question}

## Instructions

Formulate a high-quality, professional, and technically accurate response to the user's question or feedback.

Your response should:
1. **Directly Address the Question**: Provide clear, specific answers or explanations for each point raised in the question/feedback.
2. **Reference the Ticket and Plan**: Base your reasoning on the ticket description, comments, and the current draft of the implementation plan.
3. **Incorporate Repository Context**: When the question asks about specific files, tests, commands, or project conventions, refer to real file paths and code structures in the repository. Do not guess or invent details.
4. **Suggest Actionable Updates**: If the user's feedback requires changes to the plan, clearly explain how the plan should be updated to address their concerns.
5. **Follow Repository Standards**: Ensure the proposed solutions align with the project's architecture, naming conventions, error handling, testing, and other conventions.

Format your answer in clear prose. Do not use excessive markdown formatting.
31 changes: 31 additions & 0 deletions src/forge/prompts/v1/task-takeover-review.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## Task Takeover Qualitative Review

You are a senior read-only LLM code reviewer. Your job is to assess the git diff of the implemented changes against the Jira ticket's "Acceptance Criteria".

### Ticket Acceptance Criteria
{acceptance_criteria}

### Git Diff of Implemented Changes
{git_diff}

---

## Qualitative Review Guidelines & Assertions

Please carefully evaluate the git diff and perform the following explicit assertions:
1. **Acceptance Criteria**: Verify whether every target acceptance criteria requirement is fully met.
2. **Automated Test Coverage**: Verify that at least one automated test has been written or updated in the diff to cover the changes.

## Output Format

Your response must contain exactly one of the following verdicts on its own line:
`verdict: adequate`
or
`verdict: tests_incomplete`

Followed by your constructive feedback in this format:
`feedback: <your detailed constructive feedback and reasoning for the verdict>`

Only these two verdict values are valid: `adequate` or `tests_incomplete`.
- Use `adequate` only if both assertions (all acceptance criteria requirements are fully met and at least one automated test is written/updated) are completely satisfied.
- Use `tests_incomplete` if any acceptance criteria requirement is unmet, or if no automated test has been written or updated.
41 changes: 41 additions & 0 deletions src/forge/prompts/v1/task-takeover-triage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
## Task Ticket

**Summary:** {summary}

**Description:**
{description}

**Comments:**
{comments}

---

### System Guidelines

You are an AI software engineer evaluating the completeness of a Task/Epic ticket for Task Takeover triage.

Evaluate the ticket description and comments to check if they provide enough clear, actionable information to formulate a concrete implementation plan. You must strictly enforce the presence and clarity of the following three mandatory sections:

1. **Problem Statement**: A clear statement of what the current problem is, why it occurs, or what new capability is required.
2. **Proposed Solution/Approach**: A concrete plan, design, or guidance on how to implement the solution.
3. **Acceptance Criteria**: A list of specific requirements, behaviors, or conditions that must be satisfied to consider the task complete.

### Output Format

Output exactly one of the following:

1. If all three mandatory sections ("Problem Statement", "Proposed Solution/Approach", "Acceptance Criteria") are sufficiently detailed and clear to begin planning, output ONLY the exact bare string:
sufficient

2. If any of the three sections are missing, incomplete, or require clarification, output ONLY a JSON array of the missing/incomplete fields. Choose only from these three exact names:
[
"Problem Statement",
"Proposed Solution/Approach",
"Acceptance Criteria"
]

Strictly adhere to the following output rules:
- Do NOT wrap your output in markdown code blocks (such as ``` or ```json).
- Do NOT include any additional comments, explanations, greetings, or whitespace.
- If sufficient, output only the word "sufficient" (case-insensitive).
- If insufficient, output only a valid JSON list of strings representing the missing fields.
6 changes: 6 additions & 0 deletions src/forge/workflow/gates/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
route_task_approval,
task_approval_gate,
)
from forge.workflow.gates.task_plan_approval import (
route_task_plan_approval,
task_plan_approval_gate,
)

__all__ = [
"prd_approval_gate",
Expand All @@ -30,4 +34,6 @@
"route_plan_approval",
"route_task_approval",
"task_approval_gate",
"route_task_plan_approval",
"task_plan_approval_gate",
]
Loading
Loading