Files
Aakash BakhleandGitHub 6d38b4d39c fix(captain): improve complex migration instructions (#15002)
Improves Captain V1 → V2 migration for complex legacy instructions so
mandatory triggers, workflows, language rules, and escalation behavior
remain active while query-dependent product knowledge is prepared as
pending FAQ candidates.

## What changed

- Added explicit preservation rules for mandatory triggers, verification
steps, escalation conditions, exceptions, and language behavior.
- Added an auditor that checks the draft and fixes any issues before
manual review.
- Kept the existing migration application contract and schema limits
unchanged
- Added focused regression coverage for the complex-prompt classifier
contract.

## How to reproduce

Generate a migration draft for an assistant with dense legacy
instructions containing mandatory handoff triggers, verification rules,
product facts, and multi-step workflows. The resulting draft should keep
actions active, place query-dependent facts in FAQ candidates, and avoid
silently dropping or reversing source requirements.

Focused Captain migration specs and RuboCop checks pass locally.
2026-07-15 14:51:51 +05:30

37 lines
1.1 KiB
Ruby

class Captain::AssistantMigration::FaqApplier
pattr_initialize [:assistant!, :candidates!]
def changes
@changes ||= candidates.each_with_object({ create: [] }) do |candidate, result|
categorize(candidate, result)
end.compact_blank.presence
end
def apply(changes)
Array(changes[:create]).each do |candidate|
assistant.responses.create!(candidate.slice('question', 'answer', 'status'))
end
end
private
def categorize(candidate, result)
existing_answers = assistant.responses.approved.where(question: candidate['question']).pluck(:answer)
planned_answers = result[:create].filter_map do |response|
response['answer'] if response['question'] == candidate['question']
end
answers = existing_answers + planned_answers
ensure_no_conflict!(candidate, answers)
return if answers.include?(candidate['answer'])
result[:create] << candidate.merge('status' => 'approved')
end
def ensure_no_conflict!(candidate, answers)
return if answers.all?(candidate['answer'])
raise ArgumentError, "FAQ candidate conflicts with an existing FAQ: #{candidate['question']}"
end
end