[AISOS-2294] Add decompose draft review step before Jira task creation - #242
[AISOS-2294] Add decompose draft review step before Jira task creation#242ekuris-redhat wants to merge 37 commits into
Conversation
…s/draft.py Detailed description: - Created src/forge/models/draft.py with DraftItem and ForgeDecompositionDraft models. - Validated that local sequential IDs are unique and sequential starting from 1 within ForgeDecompositionDraft. - Exported both DraftItem and ForgeDecompositionDraft models in src/forge/models/__init__.py. - Added comprehensive unit tests in tests/unit/models/test_draft.py to verify model validation, serialization, and invalid data/ID rejection. Closes: AISOS-2298
Detailed description: - Implemented list_attachments method to fetch and parse attachment metadata (containing id, filename, and content URL). - Implemented download_attachment method to download raw binary content from given URL. - Re-implemented delete_attachment to leverage _request_with_retry for rate limit retry/backoff handling. - Re-implemented add_attachment supporting bytes content, utilizing _request_with_retry, and setting 'X-Atlassian-Token': 'no-check'. - Enhanced _request_with_retry to dynamically pop and restore the client's default 'Content-Type' header during multipart form requests (when 'files' parameter is present). - Added comprehensive unit tests in tests/unit/integrations/jira/test_client_attachments.py. Closes: AISOS-2299
…chment uploading Detailed description: - Removed 'Content-Type: application/json' from the default HTTP client headers in src/forge/integrations/jira/client.py. - Deleted the dynamic header popping/restoration logic in _request_with_retry to make the client completely race-free under concurrency. - Updated unit tests in tests/unit/integrations/jira/test_client_attachments.py to assert correct multipart request execution without checking for popped/restored client default headers. Closes: AISOS-2299
…anager.py Detailed description: - Created central utility class DraftManager to manage draft CRUD operations as Jira attachments - Enforced single-file constraint in save_draft_attachment - Implemented robust error handling and log warning handlers for validation/parsing failures in get_draft_attachment - Created comprehensive unit tests to achieve full coverage Closes: AISOS-2300
Detailed description: - Updated the existing test suite in tests/unit/workflow/utils/test_draft_manager.py using a parameterized fixture. - Added support for verifying both "stories" and "tasks" draft configurations and filenames (forge-stories-draft.json and forge-tasks-draft.json). - Leveraged robust mocks for JiraClient to verify attachment listing, uploading, downloading, and deletion. - Verified that save_draft_attachment correctly deletes previous draft attachments before uploading to satisfy the single-file constraint. - Verified that get_draft_attachment handles missing attachments, alternate attachment URLs, validation failures, parsing errors, and download errors gracefully. - Verified that delete_draft_attachment deletes target files and exits safely if none exist. - Added mypy ignore comments to clean up pytest mock-induced type check warnings. Closes: AISOS-2301
Detailed description: - Restored original 'add_attachment' signature in 'src/forge/integrations/jira/client.py' to support both 'text/markdown' and 'application/json' dynamically. - Simplified draft delete logic in 'src/forge/workflow/utils/draft_manager.py' by delegating to 'JiraClient.delete_attachments_by_name', removing duplicated loop/delete code. - Fully rewrote unit tests in 'tests/unit/workflow/utils/test_draft_manager.py' to test 'DraftManager' methods under 'stories' and 'tasks' phase configurations. - Removed '# mypy: ignore-errors' from the test file and ensured it is fully typed and passes strict mypy verification. Closes: AISOS-2301
…comment_classifier.py Detailed description: - Added CommentType.COMMAND = 'command' to CommentType StrEnum in src/forge/workflow/utils/comment_classifier.py. - Implemented robust regex-based /forge command parsing in parse_comment_command supporting remove, exclude, approve, add, and update commands. - Integrated parse_comment_command in classify_comment to classify valid /forge commands as CommentType.COMMAND (excluding skip-gate/unskip-gate). - Exported parse_comment_command in src/forge/workflow/utils/__init__.py. - Added comprehensive unit tests in tests/unit/workflow/test_comment_classifier.py and a new test file tests/workflow/utils/test_comment_command.py. Closes: AISOS-2302
…ira client docstring Detailed description: - Extracted key-value parsing loop in comment_classifier.py to a private helper function '_parse_key_values' to follow the DRY principle. - Updated the get_attachments docstring in JiraClient to match the actual fields returned by list_attachments. Closes: AISOS-2302
Detailed description: - Implemented static method apply_draft_modification in DraftManager class to perform list-based mutation operations on story/task draft lists. - Handled remove, add, update, and exclude commands with strict type validation, descriptive error messages, and automatic sequence ID re-ordering. - Added excluded boolean property to DraftItem model to support full JSON roundtrip serialization/deserialization. - Added robust, multi-scenario tests covering success and failure paths in tests/workflow/utils/test_comment_command.py. Closes: AISOS-2303
… and JiraClient Detailed description: - Extracted strict type validation inside apply_draft_modification into a private _validate_item_params static method. - Moved standard library copy import to the top of draft_manager.py. - Refactored JiraClient to merge list_attachments and get_attachments, removing list_attachments and updating all calling references. - Removed redundant class-level file name constants inside DraftManager class namespace. - Added comprehensive unit tests in test_comment_command.py covering deepcopy isolation, invalid commands, and strict type checking. Closes: AISOS-2303
Detailed description: - Created prompt template 'src/forge/prompts/v1/revision-draft.md' following project conventions to instruct the LLM to update JSON drafts based on parent issue context and natural language feedback. - Modified 'src/forge/integrations/agents/agent.py' to add 'revise_draft_with_feedback' async method. - Implemented a direct LangChain chain using the loaded prompt template to revise drafts. - Added extremely robust and secure JSON parser that strips out preamble, postamble, and markdown formatting blocks and validates the JSON before returning. - Added thorough unit tests in 'tests/unit/integrations/agents/test_agent.py' using a high-fidelity 'MockChatModel' subclassing LangChain's 'SimpleChatModel' to verify happy paths, markdown block stripping, preamble stripping without codeblocks, validation failures on invalid JSON, and correct prompt variable rendering/formatting. Closes: AISOS-2304
…Worker Detailed description: - Modified src/forge/orchestrator/worker.py to route interactive comment commands (/forge) and natural language feedback (!) when paused in PENDING_APPROVAL (plan and task approval gates). - Implemented State Consistency Guard (BR-006): wrapped draft mutations and LLM revisions in a try-except block, rolling back the attachment on error to its previously attached JSON version, keeping the workflow state in PENDING_APPROVAL, and posting an error reply comment explaining the failure. - Implemented /forge approve command handling to unpause and transition the workflow seamlessly. - Added edit_comment to JiraClient in src/forge/integrations/jira/client.py. - Added unit and integration tests in tests/unit/orchestrator/test_worker.py covering successful mutations, revisions, approvals, and rollback guard behaviors under failure. Closes: AISOS-2305
Detailed description: - Added yolo_mode field to the Settings model to support global configuration of autonomous yolo_mode. - Modified decompose_epics in epic_decomposition.py to embed the draft review gate into the epic decomposition workflow node. - Integrated checking of parent ticket labels for forge:yolo and settings.yolo_mode to conditionally bypass the draft review flow. - Added deletion of older forge-stories-draft.json attachments and serialization/saving of the newly generated draft model using DraftManager. - Formatted Markdown comments outlining proposed items with BR-003 truncation and condensing boundary logic based on character length and item count. - Ensured state correctly transitions and pauses at plan_approval_gate setting is_paused=True. - Updated existing tests and added thorough unit tests covering both YOLO and non-YOLO code paths. Closes: AISOS-2306
Detailed description: - Modified src/forge/workflow/nodes/task_generation.py to embed the draft review gate into the generate_tasks workflow node. - Added support to check parent Jira ticket labels for 'forge:yolo' label and global config settings for 'yolo_mode'. - When YOLO mode is active: immediately create Task tickets in Jira. - When YOLO mode is inactive: generate and serialize tasks draft list, check and delete older 'forge-tasks-draft.json' attachments, save the generated tasks draft JSON to 'forge-tasks-draft.json' on the parent Jira ticket, and post a formatted Markdown review comment outlining all proposed tasks (with BR-003 truncation fallback boundary logic to format condensed tables if items count > 15 or comment length > 32,767 characters). - Paused workflow execution at task_approval_gate setting is_paused = True. - Added comprehensive unit tests in tests/unit/workflow/nodes/test_task_generation.py covering YOLO and draft review gate paths. Closes: AISOS-2307
Detailed description: - Implemented draft-based ticket provisioning on approval for Epic plan approval gate and Task approval gate when YOLO mode is inactive. - Added epic_key field support to DraftItem and DraftManager validation to correctly map Tasks to their parent Epics. - Adjusted plan_approval_gate and task_approval_gate nodes to permit zero initial tickets when in draft approval mode. - Re-implemented route_plan_approval and route_task_approval as async routing gates that download approved draft JSON files, iterate items while skipping excluded entries, provision Jira tickets sequentially, and delete the draft attachment only on 100% successful provisioning. - Added thorough test coverage for both happy paths, exclusion skips, and error-handling robustness (retaining draft attachments on halfway failures). Closes: AISOS-2308
…tor Worker Detailed description: - Refactored ticket provisioning out of route_plan_approval and route_task_approval into reusable asynchronous helper functions provision_epics_from_draft and provision_tasks_from_draft. - Wired detection of the /forge approve structured comment command to trigger direct ticket provisioning inside the orchestrator worker. - Wired detection of label addition events for forge:plan-approved and forge:task-approved to trigger direct ticket provisioning inside the orchestrator worker. - Transitioned the workflow state to exits PENDING_APPROVAL and persisted state changes/resume checkpoints in LangGraph upon successful provisioning. - Implemented robust error handling/rollback to preserve state consistency and post informative failure comments in Jira if provisioning fails. - Wrote thorough unit tests covering successful comment-based and label-based provisioning, as well as rollback behavior under errors. Closes: AISOS-2309
Detailed description: - Created tests/workflow/test_draft_review_flow.py with full integration tests for the new draft review, truncation, and provisioning flows. - Verified the YOLO bypass path for both epic decomposition and task generation. - Verified draft attachment creation, serialization, comment posting, and workflow pausing. - Verified character length and item count truncation fallback boundaries (BR-003). - Verified that items marked with excluded: true are skipped during ticket provisioning. - Verified draft retention on partial ticket provisioning failure. Closes: AISOS-2310
Detailed description: - Reviewed all code changes on the current branch for potential compile errors, runtime crashes, and logic flaws. - Fixed a duplicate variable type annotation for 'inline_comments' in 'src/forge/orchestrator/worker.py' to resolve a Mypy 'no-redef' warning. - Validated that all unit and integration tests continue to pass 100% successfully. Closes: AISOS-2294-review
… review flow Detailed description: - Updated CLAUDE.md to document the Jira comment commands for the interactive draft review flow. - Updated docs/guide/labels.md to detail how the draft review flow functions (JSON attachments, markdown proposal tables, and direct draft modifications) and listing all 5 command actions. - Updated docs/guide/feature-workflow.md to align Epic Decomposition and Task Generation stages with the new interactive draft flow, revising descriptions and gates in the corresponding Mermaid diagrams. - Updated docs/developer-guide.md to add a dedicated reference table of Jira comment commands. Closes: AISOS-2294-docs
ekuris-redhat
left a comment
There was a problem hiding this comment.
! Please address the following issues found during review:
- Missing empty-draft guard in epic decomposition (non-YOLO path)
In src/forge/workflow/nodes/task_generation.py, the non-YOLO path correctly checks if not proposed_tasks_list: and returns a retry state
before touching Jira. The equivalent path in src/forge/workflow/nodes/epic_decomposition.py has no such check. If the agent returns
zero epics in non-YOLO mode, the code creates an empty ForgeDecompositionDraft, uploads it, posts a comment with an empty table, and
pauses the workflow with nothing for the human to approve. Please add the same guard before the draft creation block in decompose_epics:
if epics_data is empty after the LLM call, return a retry state with an appropriate last_error, matching the YOLO path's else branch.
- edit_comment skips retry logic
In src/forge/integrations/jira/client.py, the edit_comment method uses client = await self._get_client() and calls client.put(...)
directly. Every other new Jira helper added in this PR (download_attachment, delete_attachment, get_attachments, add_attachment) routes
through _request_with_retry. The PR explicitly advertises rate-limit and retry logic as a feature, but edit_comment will fail
immediately on transient 429 responses. Please refactor it to use _request_with_retry like the other helpers.
- Revision comment detection inconsistency
In src/forge/orchestrator/worker.py, the revision comment check uses comment_body.startswith("!"). The classify_comment function in
comment_classifier.py uses _REVISION_PATTERN = re.compile(r"^\s*!") which allows leading whitespace. A comment with a leading space
would be classified as FEEDBACK by the classifier but not caught as a revision comment by the worker, falling through to trigger full
regeneration instead of draft JSON revision. Please change the worker check to use bool(re.match(r"^\s*!", comment_body)) to match the
classifier.
|
Forge is addressing PR review feedback now. This status update is informational. |
1 similar comment
|
Forge is addressing PR review feedback now. This status update is informational. |
Auto-committed by Forge container fallback.
Auto-committed by Forge container fallback.
|
Forge is addressing PR review feedback now. This status update is informational. |
1 similar comment
|
Forge is addressing PR review feedback now. This status update is informational. |
Detailed description: - Added explanatory documentation comments to highlights of the PR review fixes - src/forge/workflow/nodes/epic_decomposition.py: Empty-draft guard in non-YOLO path - src/forge/integrations/jira/client.py: Robust edit_comment retry logic - src/forge/orchestrator/worker.py: Leading whitespace-tolerant revision comment pattern Closes: AISOS-2294-review-fix
Auto-committed by Forge container fallback.
Auto-committed by Forge container fallback.
|
Forge is addressing PR review feedback now. This status update is informational. |
1 similar comment
|
Forge is addressing PR review feedback now. This status update is informational. |
Auto-committed by Forge container fallback.
Detailed description: - Completed post-review-impl code review of all changes on the branch relative to origin/main. - Confirmed draft management, comment classification, and orchestrator worker logic function perfectly. - Staged and committed missing implementations for review comment editing and formatting features to ensure proper state representation. - Verified that all unit tests, flow tests, and lint/formatting checks pass cleanly. Closes: AISOS-2294-review-review-impl
Auto-committed by Forge container fallback.
ekuris-redhat
left a comment
There was a problem hiding this comment.
Hey, a few things I'd like addressed before we move forward with this one.
First, and most importantly: the .mypy_cache/3.11/ directory got committed — 18 binary cache files are showing up in the diff.
These are local mypy artifacts that should never be tracked. Please remove all of them from the branch and also add
.mypy_cache/ to .gitignore since it's currently missing from there.
Duplicate code in the worker: In worker.py, the block that finds the original review comment and edits it appears twice in a
row — once in the forge command handler and once in the revision comment handler. These are nearly identical. Please extract
that logic into a small helper so we don't have to maintain two copies.
format_review_comment duplication: The stories and tasks branches in draft_manager.py are about 95% identical. The only things
that differ are the header text, the field label, and the approval label. Please collapse them into a single helper that
accepts those as parameters.
Unnecessary LangChain wrapping: In revise_draft_with_feedback, load_prompt already returns a fully formatted string. Wrapping
it in PromptTemplate.from_template("{prompt_text}") just to pass it through is extra ceremony that doesn't add anything. A
direct call to model.ainvoke(prompt_text) would do the same thing with fewer moving parts.
A few smaller things:
The nested from datetime import datetime inside the worker function body should move to the top of the file alongside the
existing from datetime import UTC.
available_repos: Any = set() in epic_decomposition.py should use the concrete type set[str] instead of Any.
In get_attachments, each attachment dict gets both a content and a content_url key pointing to the same value. One is enough.
The _validate_item_params method in DraftManager manually replicates the type checks that Pydantic's DraftItem.model_validate
would already enforce. Consider leaning on Pydantic for this instead.
Finally, provision_epics_from_draft and provision_tasks_from_draft are called from both the worker and the route functions. The
guard on epic_keys prevents double execution, but the split ownership is confusing. Please add a comment explaining why both
call sites need to exist, or consolidate them.
|
Forge is addressing PR review feedback now. This status update is informational. |
1 similar comment
|
Forge is addressing PR review feedback now. This status update is informational. |
Auto-committed by Forge container fallback.
…ts via wrapped mocking Detailed description: - Updated 'tests/workflow/test_draft_review_flow.py' to mock DraftManager with wraps=DraftManager. - This ensures that when testing the draft review flow, calls to 'format_review_comment' execute the actual logic and return real comment body strings, fixing the assertion failures where mock objects were returned instead of string text. - Keeps I/O methods like 'delete_draft_attachment' and 'save_draft_attachment' mockable without leaving unused local mock variables. Closes: AISOS-2294-review-review-impl
ekuris-redhat
left a comment
There was a problem hiding this comment.
A few concrete issues I want to flag before merging:
- format_review_comment ignores the module's own constants
draft_manager.py defines FORGE_STORIES_DRAFT_FILENAME and FORGE_TASKS_DRAFT_FILENAME at module level, but format_review_comment
hardcodes the literal strings "forge-stories-draft.json" and "forge-tasks-draft.json" in two places. If the filenames ever
change, this method will silently diverge. Replace with the constants.
- getattr(settings, "yolo_mode", False) is stale — the field exists
This PR adds yolo_mode: bool = Field(default=False) to config.py, which means the getattr fallback in epic_decomposition.py and
task_generation.py is unnecessary. Use settings.yolo_mode directly.
- Duplicate ValidationError import inside _validate_item_params
from pydantic import ValidationError is already imported at the top of draft_manager.py. The same import inside
_validate_item_params's body is redundant — remove it.
- YOLO detection copy-pasted 4 times
The same three-component check — "forge:yolo" in labels or getattr(settings, "yolo_mode", False) or state.get("yolo_mode",
False) — appears identically in epic_decomposition.py, task_generation.py, plan_approval.py, and task_approval.py. This should
be a shared helper so all four sites stay in sync.
- provision_epics_from_draft and provision_tasks_from_draft use Any for typed parameters
Both functions are declared (state: Any, jira: Any). They should use WorkflowState and JiraClient.
|
Forge is addressing PR review feedback now. This status update is informational. |
1 similar comment
|
Forge is addressing PR review feedback now. This status update is informational. |
Auto-committed by Forge container fallback.
…_mode utility Detailed description: - Refined check_yolo_mode typing from dict[str, Any] to Any for its state parameter. This resolves potential type mismatch and casting issues when invoking yolo mode checks with different state formats (like FeatureState, BugState, and TaskTakeoverState). - Verified that all unit tests and lint/format checks pass perfectly. Closes: AISOS-2294-review-review-impl
Auto-committed by Forge container fallback.
Summary
This pull request implements an interactive, draft-based planning review flow for epic decomposition and task generation workflows. It introduces Pydantic models for draft representation, robust Jira attachment helpers and management utilities, comment commands and natural language feedback revision logic, and integrates a State Consistency Guard with rollback capabilities in the orchestrator worker. Ultimately, this allows human-in-the-loop validation, modification, and direct provisioning of generated tickets from structured draft JSONs attached to Jira issues.
Changes
Models & Schema Verification
src/forge/models/draft.pydefiningDraftItem(supporting anepic_keyfield to correctly map Tasks to parent Epics) andForgeDecompositionDraftPydantic v2 models, enforcing unique sequential item IDs and exclusion flags (via anexcludedboolean property).src/forge/models/__init__.py.Jira Integrations & Utilities
src/forge/integrations/jira/client.pyto add robust Jira attachment helpers (get_attachments,download_attachment,delete_attachment,delete_attachments_by_name,edit_commentwith robust retry logic, andadd_attachment) with automatic rate-limit and retry logic.src/forge/workflow/utils/draft_manager.pyimplementingDraftManagerto handle JSON draft serialization, secure downloading, deserialization, single-draft attachment constraints, and transactional in-memory draft modifications (add, update, remove, exclude).Comment Command & Natural Language LLM Parsing
src/forge/workflow/utils/comment_classifier.pyby addingCommentType.COMMANDand introducing regex-based comment command classification.parse_comment_commandto extract operations and arguments for/forge add,/forge update,/forge remove,/forge exclude, and/forge approve.src/forge/prompts/v1/revision-draft.mdto feed draft edits and natural language feedback into an LLM chain.revise_draft_with_feedbackinsrc/forge/integrations/agents/agent.pyto revise draft structures using LLM feedback with a clean, Markdown-stripping JSON parsing utility.Orchestration & State Machine Integration
src/forge/workflow/nodes/epic_decomposition.pyandsrc/forge/workflow/nodes/task_generation.pyto conditionally bypass or enter the draft review gate (pausing atPENDING_APPROVAL, implementing an empty-draft guard in the non-YOLO path for epic decomposition, and writing markdown previews with a 32,767 character/15-item truncation threshold).src/forge/orchestrator/worker.pyto capture comments while paused, applying either/forgemutation commands or leading whitespace-tolerant!natural language revisions on attached draft JSONs./forge approve) or transition label events (forge:plan-approved,forge:task-approved) inside the orchestrator worker, skipping excluded items and ensuring attachment deletion only after successful creation of all items.Documentation Updates
CLAUDE.md,docs/guide/labels.md,docs/guide/feature-workflow.md, anddocs/developer-guide.mdto reflect interactive comment commands, workflow diagrams, and state-machine characteristics.Implementation Notes
forge-stories-draft.json) and one tasks draft (forge-tasks-draft.json) exists at any time on a ticket by systematically deleting obsolete attachments before uploading newly generated ones.forge:yololabel or globalyolo_modeto skip the draft review loop and provision issues immediately.Testing
tests/workflow/test_draft_review_flow.pyverifying full end-to-end flows: YOLO bypass, draft attachments, comment comment modifications, LLM-based draft revisions, truncation rules, and state rollbacks on execution failures.tests/unit/models/test_draft.pytests/unit/integrations/jira/test_client_attachments.pytests/unit/workflow/utils/test_draft_manager.pytests/unit/workflow/test_comment_classifier.pytests/workflow/utils/test_comment_command.pytests/unit/integrations/agents/test_agent.pytests/unit/orchestrator/test_worker.pytests/unit/workflow/nodes/test_task_generation.pytests/workflow/test_draft_review_flow.pyRelated Tickets
Fixed Add decompose draft review step before Jira task creation #218
Generated by Forge SDLC Orchestrator