os-context extract: read_write_profile denominator for read-write sub-tag

Total main-loop Read/Write/Edit calls regardless of adjacency, split into
in-flagged-runs vs scattered (with line/target per scattered call), so the
rubric's missed-delegation/read-write sub-tag has a deterministic
denominator the consecutive-run scan cannot provide. Drops the stale
"sequential-dependent work is a valid reason" trailer superseded by
ADR-0043.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jared 2026-07-17 10:19:38 -04:00
parent 930ac6e80d
commit 357d13270f
1 changed files with 47 additions and 14 deletions

View File

@ -14,6 +14,7 @@ require "optparse"
module OrchAudit
READ_TOOLS = %w[Read Grep Glob Bash].freeze
RUN_TOOLS = %w[Read Grep Glob Edit Write].freeze
RW_TOOLS = %w[Read Write Edit].freeze
class Line
attr_reader :number, :raw
@ -139,19 +140,23 @@ module OrchAudit
@threshold = threshold
end
def candidates
runs = []
current = []
@calls.each do |call|
if extend_run?(current, call)
current << call
else
runs << current if qualifying?(current)
current = RUN_TOOLS.include?(call.name) ? [call] : []
def candidates = runs.map { |r| describe(r) }
def runs
@runs ||= begin
found = []
current = []
@calls.each do |call|
if extend_run?(current, call)
current << call
else
found << current if qualifying?(current)
current = RUN_TOOLS.include?(call.name) ? [call] : []
end
end
found << current if qualifying?(current)
found
end
runs << current if qualifying?(current)
runs.map { |r| describe(r) }
end
private
@ -220,7 +225,16 @@ module OrchAudit
m[:targets_sample].each { |t| md << " - `#{t}`\n" }
end
end
md << "\nHeuristic candidates require auditor judgment; sequential-dependent work is a valid reason not to delegate.\n"
rw = d[:read_write_profile]
md << "\n## Main-loop Read/Write/Edit profile (missed-delegation/read-write denominator)\n\n"
counts = rw[:tool_counts].map { |k, v| "#{k}:#{v}" }.join(" ")
md << "- Total: #{rw[:total]} (#{counts}); in flagged runs: #{rw[:in_flagged_runs]}; scattered: #{rw[:scattered].size}\n"
rw[:scattered].each do |c|
md << " - #{c[:tool]} line #{c[:line]} `#{c[:target]}`\n"
end
md << "\nHeuristic candidates and scattered calls require auditor judgment via the rubric's " \
"exemption decision procedure (E-a..E-d); under ADR-0043 there are no validity defenses " \
"for direct grunt work, only severity modifiers.\n"
md
end
@ -235,12 +249,31 @@ module OrchAudit
attach_rounds(main, agent_spawns)
orchestrator_calls = calls.values.reject { |c| c.name == "Agent" }
scan = MissedDelegationScan.new(orchestrator_calls, @run_threshold)
{
metadata: metadata(main, agent_spawns),
agent_spawns: agent_spawns.map(&:to_h),
segments: segments(main).map(&:to_h),
missed_delegation_candidates:
MissedDelegationScan.new(orchestrator_calls, @run_threshold).candidates
missed_delegation_candidates: scan.candidates,
read_write_profile: read_write_profile(orchestrator_calls, scan)
}
end
# Denominator for the rubric's missed-delegation/read-write sub-tag: total
# main-loop Read/Write/Edit calls regardless of adjacency. The run scan only
# sees consecutive same-tool runs; "scattered" lists the calls it can't
# flag, for per-call exemption judgment by the auditor.
def read_write_profile(orchestrator_calls, scan)
rw = orchestrator_calls.select { |c| RW_TOOLS.include?(c.name) }
in_run = scan.runs.flatten.select { |c| RW_TOOLS.include?(c.name) }
scattered = rw - in_run
{
total: rw.size,
tool_counts: rw.map(&:name).tally,
in_flagged_runs: in_run.size,
scattered: scattered.map do |c|
{ tool: c.name, line: c.line.number, target: c.target.to_s[0, 120] }
end
}
end