Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 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
c19bda7
fix task takeover planning and PR output
eshulman2 Jul 6, 2026
68c6fd9
keep task takeover local review silent in Jira
eshulman2 Jul 6, 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
42 changes: 42 additions & 0 deletions skills/default/task-takeover-planning/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
name: task-takeover-planning
description: Produce a concrete implementation plan for a standalone Task or Epic takeover ticket. Use when a Jira Task/Epic is approved for Task Takeover planning and needs target files, implementation steps, tests, and repository scope.
---

# Task Takeover Planning Skill

Create a concise, executable implementation plan for the standalone task.

## Repository Selection

1. Use only repositories listed in the prompt's Available Repositories section.
2. Infer the target repository from the ticket summary, description, comments, and any existing `repo:<owner>/<repo>` labels in context.
3. Inspect the selected repository with available repository, GitHub, or filesystem tools before naming files, commands, or test locations.
4. If multiple repositories are genuinely involved, include each one. For ordinary single-repo tasks, choose exactly one.
5. Do not choose the first configured repository as a fallback. If the target repository cannot be determined from the ticket and repository inspection, write a blocking note instead of guessing.

## Repository Grounding

Before writing the plan:

1. Read repository guidance when available, including `AGENTS.md`, `CLAUDE.md`, `.claude/AGENTS.md`, `.claude/CLAUDE.md`, `README.md`, `CONTRIBUTING.md`, `Makefile`, language-specific project files, and docs.
2. Inspect only files relevant to the requested change and nearby tests or validation commands.
3. Confirm target files and validation commands against real repository contents.
4. Follow existing repository conventions for documentation style, tests, generated files, formatting, and local workflow.
5. Prefer the smallest plan that safely implements the ticket.

If repository inspection is unavailable, make that explicit in the plan and do not invent paths or commands.

## Plan Requirements

The plan must include:

1. **Repository Scope** with explicit `repo:<owner>/<repo>` tag(s). These tags drive downstream automation and Jira labels.
2. **Target Files** with specific existing files or clearly justified new files.
3. **Implementation Steps** in execution order.
4. **Validation Plan** with concrete commands or manual checks appropriate for the change.
5. **Risks / Open Questions** only when something remains uncertain.

## Output Format

Return structured Markdown only. Do not include JSON, tool logs, or meta-commentary.
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
49 changes: 49 additions & 0 deletions src/forge/prompts/v1/task-takeover-planning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
## 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}

## Planning Context

No local repository clone is provided during planning. Use available repository, GitHub, or filesystem tools to identify and inspect the correct repository from the configured list before naming target files or validation commands.

{file_metadata}

## Repository Grounding Requirements

Before returning 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.
- All target files and file references MUST be repository-relative paths such as `README.md` or `src/forge/workflow/nodes/task_takeover_planning.py`. Never include absolute local, container, or host paths such as `/home/...`, `/tmp/...`, or `/workspace/...`.

## 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, using repository-relative paths only.
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.
4. **Repository Tags**: Include at least one `repo:<owner>/<repo>` tag using only names from the Available Repositories section. For single-repository tasks, include exactly one repo tag.

---

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.
Loading
Loading