Skip to content
Open
Changes from all commits
Commits
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
97 changes: 97 additions & 0 deletions internal/workflows/maintenance/branch_docs_impact.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# typed: true
# frozen_string_literal: true

#: self as Roast::Workflow

# Gets the committed diff of the current branch vs origin/main, then analyzes it for potential
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider looking at the graphite branch's parent branch instead of always main

# documentation impacts, and optionally applies fixes. This is meant to be run as a pre-merge check, to
# catch any potential documentation issues before they get merged into main.
#
# Accepts a `--fix` flag to apply any suggested fixes in place. Otherwise, it just reports the analysis
# and recommended fixes without applying them.

config do
agent do
provider :claude
model "claude-opus-4-7"
quiet!
end
chat do
provider :openai
model "gpt-5"
quiet!
end
end

execute do
cmd(:diff) do
merge_base = %x(git merge-base origin/main HEAD).strip
fail!("could not determine merge-base with origin/main — run `git fetch origin main` first") if merge_base.empty?
"git diff #{merge_base} HEAD"
end

agent(:analyzer) do
skip! if cmd!(:diff).text.strip.empty?
fail!("diff too large (#{cmd!(:diff).text.bytesize} bytes) to analyze — narrow the branch or exclude generated files") if cmd!(:diff).text.bytesize > 500_000
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice edge case to check! Consider adding a force arg to bypass it? Or a max_diff_size kwarg to override the default and 0 is unlimited?

<<~PROMPT
You are checking whether a git diff makes any existing documentation stale.
Rules:
- Use ONLY the diff below. Do not run any commands.
- Be thorough about finding real issues — do not be conservative.
- But do NOT speculate about docs you cannot see in the diff.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be good to have it look at the code diff vs all the docs? not just changed docs?

- No markdown headers, no preamble, no caveats, no "limitations" sections.
Output format — pick exactly one:
If nothing in the diff affects existing docs, output a single line:
No documentation impact.
Otherwise, output one block per affected doc, separated by blank lines:
<doc/path.md>
Stale because: <one sentence>
Fix: <one sentence>
--- DIFF START ---
#{cmd!(:diff).text}
--- DIFF END ---
PROMPT
end

chat(:report) do
skip! if cmd!(:diff).text.strip.empty?
<<~PROMPT
Based on the following analysis, summarize the impact of the changes in this branch on the project's documentation.
Highlight any significant improvements or regressions, and provide recommendations for any additional documentation updates that may be necessary.
Do not suggest updates that are not needed.
Do not be verbose. Be super concise.
Analysis:
#{agent!(:analyzer).response}
PROMPT
end

agent(:fixer) do
skip! unless arg?(:fix)
skip! if cmd!(:diff).text.strip.empty?
skip! if agent!(:analyzer).response.strip == "No documentation impact."
<<~PROMPT
Apply the documentation fixes suggested in the analysis below. Edit the
affected files in place. Do not modify any code files; docs only.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would want it to modify method and class doc comments in code files, though, right?

#{agent!(:analyzer).response}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just resume from the analyzer's session

do my.session = agent!(:analyzer.session) before the prompt

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can still include the report in your prompt, potentially, to be extra precise about what you want, though

PROMPT
end

ruby(:output) do
if cmd!(:diff).text.strip.empty?
puts "No changes vs origin/main — nothing to analyze."
else
files = cmd!(:diff).out.scan(%r{^diff --git a/.+ b/(.+)$}).flatten
puts "Files considered (#{files.size}):"
files.each { |f| puts " #{f}" }
puts "ANALYSIS:\n#{chat!(:report).response}"
puts(agent?(:fixer) ? "Fixes applied." : "(Next time, run with `-- fix` to auto-apply suggested edits)")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be worth storing some report data/metadata so that when you run with fix right after a dry run it doesn't have to re-analyze

end
end
end
Loading