Feature/update docs v2 - #59
Conversation
When forge.docs_repo Jira project property is set, the update_documentation node clones the separate docs repo, runs the update agent in a container with both repos mounted (code repo read-only, docs repo read-write), and creates a fork-based PR for the docs changes. Changes: - Add get_project_docs_repo() to JiraClient for reading the optional forge.docs_repo project property - Add extra_mounts parameter to ContainerRunner for mounting additional read-only volumes into containers - Expand docs_updater node with _update_separate_docs_repo() that handles clone, dual-mount container run, fork, push, and PR creation - Add update-docs-separate.md prompt template that tells the agent where both repos are mounted - Add docs_pr_url field to feature and bug workflow state - Improve update-docs skill: broader file discovery, -rlFi grep, two-pass evaluation, step 6 review beyond grep, always read README, skip auto-generated files, add new information from diff to existing docs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add "Documentation Update" step to workflow diagrams and tables - Add forge.docs_repo project property to config documentation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move separate docs repo logic from docs_updater.py into a dedicated update_docs_repo node that runs post-merge. When forge.docs_repo is set, clones both repos, runs the update agent with the code repo mounted read-only, and creates a fork-based PR for the docs changes. Wire into both workflows: - bug: post_merge_summary → update_docs_repo → END - feature: human_review_gate → update_docs_repo → complete_tasks Non-blocking — failures log a warning and proceed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
# Conflicts: # src/forge/workflow/feature/graph.py
Update bug workflow guide, config reference, README, and workflow graph to reflect the new post-merge docs repo update step and the forge.docs_repo Jira project property. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- README.md: take upstream restructured content (Human Control + Customization sections) - jira/client.py: keep both get_project_docs_repo() and get_prd_proposals_repo()/get_proposals_path() - sandbox/runner.py: keep both extra_mounts and trace_context parameters - workflow/bug/graph.py: keep both update_docs_repo routing and complete_tasks→END routing
There was a problem hiding this comment.
Hey, CI is failing because update_docs_repo.py imports _clean_forge_gitignore
from forge.workflow.nodes.implementation but that function doesn't exist
there or anywhere in the codebase. Every test suite that touches either graph
fails to collect because of this. You either need to define and export it
from implementation.py, or drop this approach entirely and use
.git/info/exclude the way workspace_setup.py:327 already does, that's the
existing pattern for keeping .forge/ local without modifying tracked files.
A few more things to fix while you're at it:
state.get("fork_owner", "") on line 67 returns None when the stored state
value is None, not "" Python's .get() default only kicks in when the key is
missing entirely. Both states default fork_owner and fork_repo to None, so
this produces github.com/None/None.git and fails silently. Use
state.get("fork_owner") or "" instead.
On line 72, if docs_manager.create_workspace() raises, the inner try/finally
at line 85 is never entered and code_workspace (already created on line 73)
leaks. Restructure so cleanup is guaranteed regardless of which workspace
creation fails.
On line 171, docs_repo.split("/") will raise ValueError if someone configures
a value like "org/sub/repo" , get_project_docs_repo only checks that a /
exists, not that there's exactly one. Use split("/", 1) or tighten the
validation in the Jira client.
- Remove broken import of _clean_forge_gitignore from implementation.py
(function does not exist); replace .gitignore manipulation with
.git/info/exclude following the workspace_setup.py pattern
- Use a single WorkspaceManager instance; initialize both workspaces
before the try block and clean up in a finally so neither leaks if
the other fails to create
- Remove invalid -code-ref suffix from ticket_key (validate_ticket_key
rejects anything outside PROJECT-NUMBER format)
- Fix state.get("fork_owner", "") to use `or ""` so stored None values
are handled correctly
- Use split("/", 1) in _create_docs_pr to avoid ValueError on
multi-slash values that pass Jira client validation
- Remove redundant get_settings() call (settings already fetched before
the try block)
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
The run() method now passes extra_mounts as a keyword argument to _build_podman_command. Update the mock side_effect to absorb it. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Extract .forge/ gitignore-exclude block into _configure_forge_exclude()
in workspace_setup.py, eliminating the verbatim duplication in the new
update_docs_repo node
- Resolve the docs repo's actual default branch via get_repository() before
cloning, so create_branch(), _branch_has_commits(), and the PR base all
use the real default instead of the hardcoded "main"
- Add base_branch parameter to _create_docs_pr() and _branch_has_commits()
- Guard pr_data.get("number") with a "?" fallback to prevent None in Jira comments
- Add happy-path test verifying docs_pr_url is set in returned state after a
successful docs PR creation, plus a no-commits test for the skip path
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
When fork info is absent (same-repo PR or state not populated), calling add_fork_remote with empty strings produced an invalid git URL that failed silently. Now falls back to checkout from origin; if the branch was auto-deleted after merge, checkout_branch raises and the outer try/except logs a warning rather than swallowing a meaningless git error. Adds test_origin_fallback_when_no_fork_info to verify add_fork_remote is never called with empty strings and the origin remote is used instead. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
|
|
||
| The documentation repository is mounted at `/workspace` (read-write). The code repository is mounted at `/code-repo` (read-only). | ||
|
|
||
| Run `cd /code-repo && git diff origin/main...HEAD` to see the code changes, and check `/code-repo/.forge/handoff.md` for context on what was implemented and why. |
There was a problem hiding this comment.
git diff origin/main...HEAD hardcodes the name of the default branch. We should pass the default branch into a template variable. Something like {code_default_branch}
There was a problem hiding this comment.
Added a code_default_branch template variable to the prompt and fetch the code repo's default branch via GitHub API alongside the existing docs-repo fetch. The diff command in the prompt is now git diff origin/{code_default_branch}...HEAD.
| code_git.clone() | ||
| if fork_owner and fork_repo: | ||
| code_git.add_fork_remote(fork_owner, fork_repo) | ||
| code_git.checkout_branch(branch_name, remote="fork") |
There was a problem hiding this comment.
In the case where a feature branch is deleted following a GitHub merge, attempting to checkout a feature branch will fail
There was a problem hiding this comment.
Wrapped both checkout paths in a try/except GitError. When checkout fails (branch deleted after merge), we look up the PR's merge_commit_sha via the GitHub API using current_pr_number from state and do a detached-HEAD checkout of that commit instead. Added GitOperations.checkout_commit(sha) for this. If neither the branch nor the PR number is available, we log a warning and proceed from default branch HEAD.
- Fetch code repo default branch via GitHub API; pass as {code_default_branch}
into the update-docs-separate prompt instead of hardcoding 'main'
- Catch GitError when checking out the feature branch (deleted after merge)
and fall back to checking out the merge commit SHA via the GitHub PR API
- Add GitOperations.checkout_commit(sha) for detached-HEAD checkout
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
86dd84e to
3289f00
Compare
Summary
Adds support for updating documentation that lives in a separate repository from the code. When the
forge.docs_repoJira project property is set, Forge automatically updates the docs repo after a code PR is merged.update_docs_repo— post-merge node that clones both repos, runs the update-docs skill with the code repo mounted read-only, and creates a fork-based PR for the docs changesforge.docs_repo— optional project property pointing to the docs repo (owner/repoformat)extra_mountsin ContainerRunner — allows mounting additional read-only volumes into containerspost_merge_summary → update_docs_repo → END, feature:human_review_gate → update_docs_repo → complete_tasksforge.docs_repois not set or the update fails, the workflow proceeds normally-rlFi), two-pass evaluation, "review beyond grep" step, skip auto-generated filesHow it works
update_docs_repochecksforge.docs_repovia Jira project properties/workspace= docs,/code-repo= code read-only)Test plan