68 lines
2.5 KiB
Ruby
68 lines
2.5 KiB
Ruby
module Adr
|
|
# Generates docs/adr/migration-report.md — the single aggregate surface for
|
|
# migration uncertainty (alongside per-file migration_confidence frontmatter).
|
|
class MigrationReport
|
|
FILENAME = "migration-report.md"
|
|
|
|
# Pilot-gate threshold (task 5.5): a run whose low-confidence flag rate
|
|
# exceeds this is not good enough for wider rollout — tighten heuristics
|
|
# and re-run. Set empirically during the 2026-07-03 pilot.
|
|
FLAG_RATE_THRESHOLD = 0.25
|
|
|
|
def initialize(repository:, detector:)
|
|
@repository = repository
|
|
@detector = detector
|
|
end
|
|
|
|
def path = File.join(@repository.dir, FILENAME)
|
|
|
|
def write!
|
|
File.write(path, content)
|
|
end
|
|
|
|
def migrated_entries
|
|
@repository.entries.select { |e| e.record.frontmatter["migration_source"] }
|
|
end
|
|
|
|
def flag_rate
|
|
entries = migrated_entries
|
|
return 0.0 if entries.empty?
|
|
|
|
low = entries.count { |e| e.record.frontmatter["migration_confidence"] == "low" }
|
|
(low.to_f / entries.size).round(3)
|
|
end
|
|
|
|
def content
|
|
entries = migrated_entries
|
|
unrecognized = @detector.units.select { |u| u.shape == :unrecognized }
|
|
rows = entries.map do |entry|
|
|
record = entry.record
|
|
pending = record.sections.select { |_, v| v == Migrator::PENDING_MARKER }.keys
|
|
not_stated = record.sections.select { |_, v| v == Migrator::NOT_STATED_MARKER }.keys
|
|
"| #{record.id} | [#{record.title}](#{File.basename(entry.path)}) " \
|
|
"| #{record.frontmatter['migration_source']} " \
|
|
"| #{record.frontmatter['migration_confidence']} " \
|
|
"| #{(pending.map { |s| "#{s} (pending)" } + not_stated.map { |s| "#{s} (not stated)" }).join(', ')} |"
|
|
end
|
|
|
|
<<~MD
|
|
<!-- Generated by os-adr migration. Regenerated on every migration pass. -->
|
|
|
|
# ADR Migration Report
|
|
|
|
Converted: #{entries.size} · Low-confidence flag rate: #{(flag_rate * 100).round(1)}% (gate threshold: #{(FLAG_RATE_THRESHOLD * 100).round}% — #{flag_rate > FLAG_RATE_THRESHOLD ? 'EXCEEDED, tighten heuristics before wider rollout' : 'within gate'})
|
|
|
|
| ID | Title | Source | Confidence | Open items |
|
|
| --- | --- | --- | --- | --- |
|
|
#{rows.join("\n")}
|
|
|
|
## Unrecognized content (manual handling required)
|
|
|
|
#{unrecognized.empty? ? '_none_' : unrecognized.map { |u| "- #{u.source_ref}" }.join("\n")}
|
|
|
|
_Sources are untouched. Old-system removal is a separate, explicitly user-approved step._
|
|
MD
|
|
end
|
|
end
|
|
end
|