Module: Ace::Review::Molecules::DeltaResolver

Defined in:
lib/ace/review/molecules/delta_resolver.rb

Overview

Resolves and validates the reference head for a delta review round.

A delta round reviews only the diff between a reference head (an earlier reviewed commit) and the PR's current head. The reference is either given explicitly or auto-resolved from the most recent prior review session of the same PR. Every failure mode fails closed: no prior session, a non-ancestor reference (e.g. rewritten history) or missing local objects refuse the round instead of falling back to a full review.

Constant Summary collapse

SHA_PATTERN =
/\A[0-9a-f]{40}\z/

Class Method Summary collapse

Class Method Details

.latest_session_head(pr_url, project_root: nil) ⇒ Hash

Find the head SHA recorded by the most recent prior session of this PR.

Parameters:

  • pr_url (String) —

    canonical PR URL stored in session metadata

  • project_root (String, nil) (defaults to: nil)

Returns:

  • (Hash) —

    reference_head:, session_dir:, error:



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/ace/review/molecules/delta_resolver.rb', line 61

def self.latest_session_head(pr_url, project_root: nil)
  root = project_root || Dir.pwd
  sessions_dir = File.join(root, ".ace-local", "review", "sessions")
  return {success: false, error: no_prior_session_message(pr_url)} unless Dir.exist?(sessions_dir)

  candidates = Dir.glob(File.join(sessions_dir, "review-*"))
    .select { |path| File.directory?(path) }
    # Order by the session's own last write (metadata.yml), not the
    # directory entry — directory mtimes tie when sessions are created
    # within the same second.
    .sort_by { |path| -session_mtime(path).to_f }

  candidates.each do |dir|
     = File.join(dir, "metadata.yml")
    next unless File.file?()

     = YAML.safe_load_file(, permitted_classes: [Time, Date, Symbol])
    next unless .is_a?(Hash)
    next unless ["pr_url"].to_s == pr_url

    head = .dig("diff_manifest", "head_sha") ||
           .dig("diff_manifest", :head_sha)
    next unless head.to_s.match?(SHA_PATTERN)

    return {success: true, reference_head: head, session_dir: dir}
  end

  {success: false, error: no_prior_session_message(pr_url)}
rescue Psych::Exception, Errno::ENOENT => e
  {success: false, error: "Cannot read prior review sessions for delta resolution: #{e.message}"}
end

.resolve(reference, pr_metadata, project_root: nil) ⇒ Hash

Resolve the reference head and compute the delta diff.

Parameters:

  • reference (String, Symbol, nil) —

    explicit head, or :auto to resolve from the most recent prior session of this PR

  • pr_metadata (Hash) —

    PR metadata (requires headRefOid and url)

  • project_root (String, nil) (defaults to: nil) —

    repository root for git commands

Returns:

  • (Hash) —

    reference_head:, source:, session_dir:, diff:, error:



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/ace/review/molecules/delta_resolver.rb', line 27

def self.resolve(reference, , project_root: nil)
  head = ["headRefOid"].to_s
  return {success: false, error: "Delta review requires the PR head SHA"} unless head.match?(SHA_PATTERN)

  if reference == :auto || reference.nil?
    session = latest_session_head(["url"].to_s, project_root: project_root)
    return session unless session[:success]

    reference = session[:reference_head]
    session_dir = session[:session_dir]
    source = :session
  else
    source = :explicit
  end

  resolved = rev_parse(reference, project_root: project_root)
  return resolved unless resolved[:success]

  reference_head = resolved[:stdout]
  return {success: false, error: "Delta reference '#{reference}' is not a commit SHA"} unless reference_head.match?(SHA_PATTERN)
  return {success: false, error: "Delta reference head #{reference_head} is not an ancestor of the current PR head #{head}; history was likely rewritten — re-run a full review round"} unless ancestor?(reference_head, head, project_root: project_root)

  diff = run_git(["diff", "#{reference_head}..#{head}"], project_root: project_root)
  return diff unless diff[:success]

  {success: true, reference_head: reference_head, source: source,
   session_dir: session_dir, diff: diff[:stdout]}
end

.rev_parse(reference, project_root:) ⇒ Object



105
106
107
108
109
110
111
# File 'lib/ace/review/molecules/delta_resolver.rb', line 105

def self.rev_parse(reference, project_root:)
  result = run_git(["rev-parse", "#{reference}^{commit}"], project_root: project_root)
  return result unless result[:success]

  result[:stdout] = result[:stdout].strip
  result
end