Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
b8b6c07
[AISOS-2298] Define Draft and Item Pydantic Models in src/forge/model…
ekuris-redhat Jul 30, 2026
314dd43
[AISOS-2299] Implement Jira API Attachment Helpers in JiraClient
ekuris-redhat Jul 30, 2026
011567a
[AISOS-2299] Fix concurrent request race condition in JiraClient atta…
ekuris-redhat Jul 30, 2026
1628847
[AISOS-2300] Develop DraftManager in src/forge/workflow/utils/draft_m…
ekuris-redhat Jul 30, 2026
f899aae
[AISOS-2301] Add unit tests for DraftManager
ekuris-redhat Jul 30, 2026
79d40bd
[AISOS-2301] Add Unit Tests for DraftManager and simplify implementation
ekuris-redhat Jul 30, 2026
e7a4c41
[AISOS-2302] Implement Command Classification & Parameter Parsing in …
ekuris-redhat Jul 30, 2026
451c9a5
[AISOS-2302] Extract shared key-value pair parsing logic and update J…
ekuris-redhat Jul 30, 2026
9863dc7
[AISOS-2303] Implement Draft Mutation Logic in DraftManager
ekuris-redhat Jul 30, 2026
4058eaa
[AISOS-2303] Implement Draft Mutation Logic and Refactor DraftManager…
ekuris-redhat Jul 30, 2026
6f5042e
[AISOS-2304] Implement Natural Language Revision LLM Chain in ForgeAgent
ekuris-redhat Jul 30, 2026
606106b
[AISOS-2305] Integrate Comment Commands and Rollback in Orchestrator …
ekuris-redhat Jul 30, 2026
600ac59
[AISOS-2306] Integrate Draft Review Gate in Epic Decomposition Node
ekuris-redhat Jul 30, 2026
f763723
[AISOS-2307] Integrate Draft Review Gate in Task Generation Node
ekuris-redhat Jul 30, 2026
470c60e
[AISOS-2308] Implement Draft-Based Ticket Provisioning on Approval
ekuris-redhat Jul 30, 2026
eb60669
[AISOS-2309] Integrate Comment Commands and Label Events in Orchestra…
ekuris-redhat Jul 30, 2026
16ab0e3
[AISOS-2310] Implement integration tests for draft review flow
ekuris-redhat Jul 30, 2026
025b862
[AISOS-2294-review] Local code review — fix breaking issues
ekuris-redhat Jul 30, 2026
ee2384d
[AISOS-2294-docs] Update stale documentation for draft-based planning…
ekuris-redhat Jul 30, 2026
3bd79c6
[AISOS-2294] review: address PR feedback
ekuris-redhat Jul 30, 2026
aabd224
[AISOS-2294-review-fix] Implement PR review plan for AISOS-2294
ekuris-redhat Jul 30, 2026
c05f646
[AISOS-2294-review-review-impl] Post-review-impl code review
ekuris-redhat Jul 30, 2026
3962d17
[AISOS-2294] review: address PR feedback
ekuris-redhat Jul 30, 2026
ba26fe4
[AISOS-2294-review-fix] Implement PR review plan for AISOS-2294
ekuris-redhat Jul 30, 2026
92905df
[AISOS-2294-review-review-impl] Post-review-impl code review
ekuris-redhat Jul 30, 2026
1aaf31f
[AISOS-2294] review: address PR feedback
ekuris-redhat Jul 30, 2026
aba3a46
[AISOS-2294-review-fix] Implement PR review plan for AISOS-2294
ekuris-redhat Jul 30, 2026
56c38be
[AISOS-2294-review-review-impl] Post-review-impl code review
ekuris-redhat Jul 30, 2026
8433431
[AISOS-2294-review-review-impl] Post-review-impl code review
ekuris-redhat Jul 30, 2026
536bf28
[AISOS-2294-review-analyze] Analyze PR review feedback for AISOS-2294
ekuris-redhat Jul 30, 2026
2daa53d
[AISOS-2294] review: address PR feedback
ekuris-redhat Jul 30, 2026
e3a4192
[AISOS-2294-review-review-impl] Fix draft review flow integration tes…
ekuris-redhat Jul 30, 2026
ce68261
[AISOS-2294-review-analyze] Analyze PR review feedback for AISOS-2294
ekuris-redhat Jul 30, 2026
ebafeee
[AISOS-2294] review: address PR feedback
ekuris-redhat Jul 30, 2026
7ae2ffe
[AISOS-2294-review-review-impl] Refine typing signature of check_yolo…
ekuris-redhat Jul 30, 2026
17b0584
[AISOS-2294-review-fix] review: address PR feedback
ekuris-redhat Aug 2, 2026
5b16ecc
[AISOS-2294-review-review-impl] Post-review-impl code review and robu…
ekuris-redhat Aug 2, 2026
730f449
[AISOS-2294-review-review-impl] Post-review-impl code review
ekuris-redhat Aug 2, 2026
802b8d4
[AISOS-2294-review-fix] Address PR feedback for AISOS-2294
ekuris-redhat Aug 2, 2026
811fbda
[AISOS-2294-review-analyze] Analyze PR review feedback for AISOS-2294
ekuris-redhat Aug 2, 2026
6e7599e
[AISOS-2294] review: address PR feedback
ekuris-redhat Aug 2, 2026
d441e3c
[AISOS-2294-review-review-impl] Post-review-impl code review
ekuris-redhat Aug 2, 2026
b52b671
[AISOS-2294] review: address PR feedback
ekuris-redhat Aug 3, 2026
69b0392
[AISOS-2294-review-review-impl] Post-review-impl code review
ekuris-redhat Aug 3, 2026
165aef4
[AISOS-2294] review: address PR feedback
ekuris-redhat Aug 3, 2026
32b0ab1
[AISOS-2294] review: address PR feedback
ekuris-redhat Aug 3, 2026
d490957
[AISOS-2294-review-review-impl] Fix container entrypoint sandbox test…
ekuris-redhat Aug 3, 2026
164e75e
[AISOS-2294] review: address PR feedback
ekuris-redhat Aug 3, 2026
afd6f8b
[AISOS-2294-review-review-impl] Resolve breaking build, import and ty…
ekuris-redhat Aug 3, 2026
648e728
[AISOS-2294-review-fix] Brief summary of what was implemented
ekuris-redhat Aug 4, 2026
1c981ed
[AISOS-2294] review: address PR feedback
ekuris-redhat Aug 4, 2026
697e974
[AISOS-2294] review: address PR feedback
ekuris-redhat Aug 4, 2026
60a6bf6
[AISOS-2294] review: address PR feedback
ekuris-redhat Aug 6, 2026
0fc8357
[AISOS-2294-review-review-impl] Clean up and verify post-review imple…
ekuris-redhat Aug 6, 2026
7e026f1
[AISOS-2294] review: address PR feedback
ekuris-redhat Aug 9, 2026
af97ddf
[AISOS-2294-review-fix] Implement PR review plan for AISOS-2294
ekuris-redhat Aug 11, 2026
a919fa6
[AISOS-2294-review-fix] Remove .mypy_cache/ directory from Git tracking
ekuris-redhat Aug 11, 2026
070e9de
[AISOS-2294-review-fix] Implement PR review plan for AISOS-2294
ekuris-redhat Aug 11, 2026
35dd220
[AISOS-2294-review-review-impl] Post-review-impl code review
ekuris-redhat Aug 11, 2026
b86b489
[AISOS-2294-review-fix] [AISOS-2294] review: address PR feedback
ekuris-redhat Aug 11, 2026
2b02f1a
[AISOS-2294-review-fix] Implement PR review plan for AISOS-2294
ekuris-redhat Aug 11, 2026
82c246e
[AISOS-2294-review-review-impl] Post-review-impl code review
ekuris-redhat Aug 11, 2026
47d0491
[AISOS-2294-review-review-impl] Add WorkflowState typing casts in pla…
ekuris-redhat Aug 11, 2026
42837af
[AISOS-2294-review-fix] [AISOS-2294] review: address PR feedback
ekuris-redhat Aug 11, 2026
66fb7f4
[AISOS-2294-review-fix] Implement PR review plan for AISOS-2294
ekuris-redhat Aug 17, 2026
105eae5
[AISOS-2294-review-review-impl] Fix and verify post-review-impl draft…
ekuris-redhat Aug 17, 2026
ad17028
[AISOS-2294] review: address PR feedback
ekuris-redhat Aug 17, 2026
8de085d
[AISOS-2294-review-review-impl] Fix missing get_draft_attachment meth…
ekuris-redhat Aug 17, 2026
47838ba
[AISOS-2294-review-fix] review: address PR feedback
ekuris-redhat Aug 18, 2026
6dc435f
[AISOS-2294-review-review-impl] Post-review-impl code review and form…
ekuris-redhat Aug 18, 2026
629eff6
[AISOS-2294] review: address PR feedback
ekuris-redhat Aug 18, 2026
a78a360
[AISOS-2294-review-fix] Implement PR review plan for AISOS-2294
ekuris-redhat Aug 18, 2026
4838a0b
[AISOS-2294-review-review-impl] Post-review-impl code review and mypy…
ekuris-redhat Aug 18, 2026
2b6facb
fix: preserve Jira plan formatting
eshulman2 Aug 27, 2026
ee84e1c
fix: keep Epic draft replies on source issue
eshulman2 Aug 27, 2026
b6db8ef
fix: persist paused draft revisions
eshulman2 Aug 31, 2026
2119a13
fix: preserve planning comment history
eshulman2 Sep 1, 2026
fe3c919
fix: resolve repo collection after rebase
eshulman2 Sep 1, 2026
e2f6ef2
fix: address rebase CI regressions
eshulman2 Sep 1, 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
11 changes: 8 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,16 @@ podman rm $(podman ps -a --filter name=forge- -q)

## Jira Comment Syntax

| Prefix | Effect |
|--------|--------|
| `!` | Revision request — triggers regeneration with feedback |
| Prefix / Command | Effect |
|------------------|--------|
| `!` | Revision request — triggers regeneration/revision with feedback |
| `?` or `@forge ask` | Question — triggers Q&A answer |
| `>option N` | RCA option selection (RCA Option Gate only) |
| `/forge approve` | Approve draft (Epic plan or Tasks) to provision tickets |
| `/forge remove ID` | Remove proposed draft item by ID and re-sequence |
| `/forge exclude ID` | Toggle exclusion of proposed draft item by ID |
| `/forge update ID key=val` | Update fields (`summary`, `description`, `repo`) of proposed draft item |
| `/forge add key=val` | Add a new proposed item to draft |
| _(no prefix)_ | Informational — workflow ignores it |

## GitHub PR Comment Commands
Expand Down
12 changes: 12 additions & 0 deletions docs/developer-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,18 @@ curl -X POST http://localhost:8000/api/v1/webhooks/github \
| `/forge skip-gate <name>` | Skip named CI check | CI stages |
| `/forge unskip-gate <name>` | Remove a skip | CI stages |

### Jira comment commands

These commands are used on the parent Jira ticket during the draft review stages (Epic Plan and Tasks).

| Command | Effect | Active at |
|---------|--------|-----------|
| `/forge approve` | Approve draft, provision sub-tickets, and delete draft attachment | `plan_approval_gate`, `task_approval_gate` |
| `/forge remove <ID>` | Remove a draft item by local sequential ID | `plan_approval_gate`, `task_approval_gate` |
| `/forge exclude <ID>` | Toggle the exclusion flag of a draft item | `plan_approval_gate`, `task_approval_gate` |
| `/forge update <ID> key=val` | Update fields (`summary`, `description`, `repo`) of a draft item | `plan_approval_gate`, `task_approval_gate` |
| `/forge add key=val` | Add a new proposed item to the draft | `plan_approval_gate`, `task_approval_gate` |

### Jira labels

| Label | Meaning |
Expand Down
54 changes: 36 additions & 18 deletions docs/guide/feature-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,22 +66,31 @@ Forge generates a behavioral specification from the approved PRD, typically usin

Forge breaks the feature into logical epics — high-level areas of work that map to implementation phases.

**Human action:** Review the epic plan. You have four options at this stage:
By default, Forge uses an interactive **Draft Review Flow** at this stage (unless YOLO mode is active):
1. Instead of creating Jira tickets immediately, Forge serializes the proposed epics into `forge-epics-draft.json` and uploads it as an attachment on the Feature ticket.
2. Forge posts a markdown table comment on the Feature ticket outlining the proposed Epics.
3. The workflow pauses at `plan_approval_gate`.

| Action | How |
|--------|-----|
| Approve | Change label to `forge:plan-approved` |
| Ask a question | Comment with `?` prefix — Forge answers without re-decomposing |
| Revise one epic | `!` comment on the **specific epic sub-ticket** — Forge updates only that epic |
| Redo the full decomposition | `!` comment on the **feature ticket** — Forge regenerates all epics with your feedback |
**Human action:** Review the epic plan draft. You have several options at this stage:

| Action | How | Description |
|--------|-----|-------------|
| **Approve** | Comment `/forge approve` OR set label to `forge:plan-approved` | Forge provisions the Epic sub-tickets on Jira from the draft, deletes the draft attachment, and advances to Task Generation. |
| **Direct Edit** | Use `/forge` commands (e.g. `/forge update`, `/forge remove`, etc.) | Directly modify the draft attachment and regenerate the proposal comment. See [Jira Labels & Comments](labels.md) for a list of commands. |
| **Ask a question** | Comment with `?` prefix or `@forge ask` | Forge answers your question without regenerating the draft. |
| **Request revisions** | Comment with `!` prefix followed by your feedback | Forge uses LLM assistance to revise the entire draft JSON and update the proposal comment with your feedback. |

If `forge:yolo` mode is active, the draft review is bypassed. Epics are created in Jira immediately, and the workflow automatically proceeds to Task Generation.

If `forge:direct-mode` is active, the draft review is also bypassed and Epics are created in Jira immediately, but the workflow still pauses at the `plan_approval_gate` waiting for manual human approval (via label or commands) before proceeding.

```mermaid
flowchart TD
Gate([plan_approval_gate])
Gate -->|forge:plan-approved| Next[Generate Tasks]
Gate -->|forge:plan-approved or /forge approve| Next[Generate Tasks]
Gate -->|"? on feature ticket"| QA[Answer Question]
Gate -->|"! on feature ticket"| Regen[Regenerate All Epics]
Gate -->|"! on epic sub-ticket"| Update[Update Single Epic]
Gate -->|"/forge update/remove/exclude/add"| Update[Modify Draft]
QA --> Gate
Regen --> Gate
Update --> Gate
Expand All @@ -93,22 +102,31 @@ flowchart TD

Forge generates granular implementation tasks scoped to individual repositories. Each task is sized to fit in a single container execution pass.

**Human action:** Review the tasks. You have four options at this stage:
By default, Forge uses an interactive **Draft Review Flow** at this stage (unless YOLO mode is active):
1. Instead of creating Jira tickets immediately, Forge serializes the proposed tasks into `forge-tasks-draft.json` and uploads it as an attachment on the Feature ticket.
2. Forge posts a markdown table comment on the Feature ticket outlining the proposed Tasks.
3. The workflow pauses at `task_approval_gate`.

**Human action:** Review the task draft. You have several options at this stage:

| Action | How | Description |
|--------|-----|-------------|
| **Approve** | Comment `/forge approve` OR set label to `forge:task-approved` | Forge provisions the Task sub-tickets on Jira from the draft, deletes the draft attachment, and advances to Implementation. |
| **Direct Edit** | Use `/forge` commands (e.g. `/forge update`, `/forge remove`, etc.) | Directly modify the draft attachment and regenerate the proposal comment. See [Jira Labels & Comments](labels.md) for a list of commands. |
| **Ask a question** | Comment with `?` prefix or `@forge ask` | Forge answers your question without regenerating the draft. |
| **Request revisions** | Comment with `!` prefix followed by your feedback | Forge uses LLM assistance to revise the entire draft JSON and update the proposal comment with your feedback. |

If `forge:yolo` mode is active, the draft review is bypassed. Tasks are created in Jira immediately, and the workflow automatically proceeds to Implementation.

| Action | How |
|--------|-----|
| Approve | Change label to `forge:task-approved` |
| Ask a question | Comment with `?` prefix — Forge answers without regenerating |
| Revise one task | `!` comment on the **specific task sub-ticket** — Forge updates only that task |
| Regenerate all tasks | `!` comment on the **feature or epic ticket** — Forge regenerates the full task list with your feedback |
If `forge:direct-mode` is active, the draft review is also bypassed and Tasks are created in Jira immediately, but the workflow still pauses at the `task_approval_gate` waiting for manual human approval (via label or commands) before proceeding.

```mermaid
flowchart TD
Gate([task_approval_gate])
Gate -->|forge:task-approved| Next[Implement Tasks]
Gate -->|forge:task-approved or /forge approve| Next[Implement Tasks]
Gate -->|"? on ticket"| QA[Answer Question]
Gate -->|"! on feature/epic"| Regen[Regenerate All Tasks]
Gate -->|"! on task sub-ticket"| Update[Update Single Task]
Gate -->|"/forge update/remove/exclude/add"| Update[Modify Draft]
QA --> Gate
Regen --> Gate
Update --> Gate
Expand Down
29 changes: 25 additions & 4 deletions docs/guide/labels.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,40 @@ Standalone Tasks and Epics can be processed with the standard `forge:managed` la
| `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, or to transition from `review_response_gate` back to `human_review_gate` (clearing contested review comments). Forge removes it after resuming. |
| `forge:yolo` | Auto-approve supported planning gates. Human PR review still remains a gate. |
| `forge:direct-mode` | Direct ticket creation mode (bypasses draft JSON attachments and creates Epic/Task tickets immediately in Jira), but still pauses for human approval at the planning gates (instead of auto-approving like `forge:yolo`). |
| `repo:<owner>/<repo>` | Identifies repositories selected for planning and implementation. |

## How to Use Labels

**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.
**Approving a stage:** When Forge posts an artifact (such as a PRD or Spec), it sets the `forge:*-pending` label. You can approve it by changing the label to `forge:*-approved` to advance the workflow. For draft-based stages (Epic Plan and Tasks), you can also approve by commenting `/forge approve` on the ticket.

**Requesting revisions:** Start a comment with `!` followed by your feedback. Forge regenerates the artifact and resets the pending label.
**Interactive Draft Review:** For Epic Decomposition and Task Generation stages, Forge uses a draft-based review flow by default (unless `forge:yolo` or `forge:direct-mode` mode is active).
1. Instead of creating sub-tickets immediately, Forge serializes the proposed items into a JSON draft file (`forge-epics-draft.json` or `forge-tasks-draft.json`) and uploads it as a Jira attachment.
2. Forge posts a formatted markdown table comment on the ticket detailing the proposed plan.
3. While the stage is pending, you can modify the draft directly using **Jira comment commands** (see below) or request a natural language revision.
4. Once you approve (via `/forge approve` or setting the approved label), Forge downloads the draft, provisions the actual Jira tickets from it, and deletes the draft attachment.

**Asking questions:** Start a comment with `?` or `@forge ask`. Forge answers without advancing or regenerating.
### Jira Comment Commands

**Informational comments:** Comments without a recognized prefix (`!`, `?`, `@forge ask`, `>option`) are ignored by the workflow — use them for team discussion without triggering Forge.
For stages using the draft-based review flow (Epic Plan and Tasks), you can post comments on the parent ticket with the following commands:

| Command | Description | Example |
|---------|-------------|---------|
| `/forge approve` | Approve the draft, provision all non-excluded items as Jira tickets, and delete the draft attachment. | `/forge approve` |
| `/forge remove <ID>` | Remove a draft item by its local sequential ID. Remaining items are automatically re-sequenced. | `/forge remove 3` |
| `/forge exclude <ID>` | Toggle the exclusion flag of a draft item. Excluded items are skipped during ticket provisioning. | `/forge exclude 2` |
| `/forge update <ID> key=val` | Update fields of a draft item (supported keys: `summary`, `description`, `repo`). | `/forge update 1 repo="my-org/custom-repo"` |
| `/forge add key=val` | Add a new proposed item to the draft. | `/forge add summary="New Story" repo="my-org/repo"` |

*Note: Successful command/revision comments are automatically edited by Forge to prepend `✅`. If a command or revision fails, Forge posts a comment detailing the error with a leading `❌`.*

**Requesting revisions:** Start a comment with `!` followed by your feedback (e.g., `! update the repositories to use the new service`). For standard artifacts, Forge regenerates them. For drafts, Forge uses LLM assistance to revise the draft JSON attachment and update the proposed plan table.

**Asking questions:** Start a comment with `?` or `@forge ask`. Forge answers without advancing or regenerating/modifying the drafts.

**Informational comments:** Comments without a recognized prefix (such as `!`, `?`, `@forge ask`, `>option`, or `/forge`) are ignored by the workflow — use them for team discussion without triggering Forge.

**Handling failures:** When `forge:blocked` appears, read the Forge comment for the error. Fix the underlying issue if needed, then add `forge:retry`.

Expand Down
82 changes: 82 additions & 0 deletions src/forge/integrations/agents/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -1333,6 +1333,88 @@ async def answer_question(
logger.info(f"Generated answer ({len(result)} chars)")
return result.strip() if result else ""

async def revise_draft_with_feedback(
self,
draft_content: str,
feedback: str,
context: dict[str, Any] | None = None,
) -> str:
"""Revise draft content based on user feedback.

Uses the 'revision-draft' prompt template to guide the LLM to output
the revised draft JSON.

Args:
draft_content: The current draft JSON content.
feedback: Natural language feedback.
context: Optional context from the workflow state.

Returns:
The updated draft JSON string.
"""
from langchain_core.output_parsers import StrOutputParser

# Format context into a readable string/JSON
context_str = json.dumps(context, indent=2) if context else "None provided"

# Load the prompt template using project's load_prompt
prompt_text = load_prompt(
"revision-draft",
draft_content=draft_content,
feedback=feedback,
context=context_str,
)

model = self._create_model()
chain = model | StrOutputParser()

logger.info("Revising draft using direct LangChain model chain")
response = await chain.ainvoke(prompt_text)

# Strip preamble/narration and validate as JSON
cleaned_text = response.strip()

# Check markdown code blocks first
pattern = r"```(?:json)?\s*([\s\S]*?)\s*```"
match = re.search(pattern, cleaned_text)
if match:
cleaned_text = match.group(1).strip()
else:
# If no code block, look for the JSON object/list boundary
# Find the first occurrence of '{' or '[' and the last of '}' or ']'
start_brace = cleaned_text.find("{")
start_bracket = cleaned_text.find("[")

# Determine which starts first
start_idx = -1
if start_brace != -1 and start_bracket != -1:
start_idx = min(start_brace, start_bracket)
elif start_brace != -1:
start_idx = start_brace
elif start_bracket != -1:
start_idx = start_bracket

if start_idx != -1:
# Find the last brace or bracket matching the start type
if start_idx == start_brace:
end_idx = cleaned_text.rfind("}")
else:
end_idx = cleaned_text.rfind("]")

if end_idx > start_idx:
cleaned_text = cleaned_text[start_idx : end_idx + 1].strip()

try:
parsed_json = json.loads(cleaned_text)
validated_json_str = json.dumps(parsed_json, indent=2)
logger.info(
f"Successfully revised draft and validated JSON ({len(validated_json_str)} chars)"
)
return validated_json_str
except json.JSONDecodeError as e:
logger.error(f"Failed to parse LLM response as valid JSON: {e}\nResponse: {response}")
raise ValueError(f"Failed to parse revised draft as JSON: {e}")

async def close(self) -> None:
"""Close the agent and cleanup resources."""
pass
Loading
Loading