feat(observer): heuristic reasoning capture in primary_rationale
Closes brain-retro 2026-05-20 #6 — extractTriggers/Candidates/Boundaries scan assistant.text for Pravila §N / ADR-N / PSR_v1 RX / routing-off-phase LN / hard-floor + numbered/bulleted lists (≥2). Populates previously- always-empty primary_rationale arrays. Conservative-broad: false positives accepted (mention ≠ application); /brain-retro determines applied validity. Phase 2 agent-judge out of scope. 19 new tests, 282/282 GREEN. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -290,6 +290,77 @@ export function extractTokenUsage(turn) {
|
||||
* For each AskUserQuestion toolUseResult in the turn, emit one event per question.
|
||||
* answer_kind: 'option' (exact label match), 'custom' (free-text), 'no_answer' (missing/empty).
|
||||
*/
|
||||
/** Collect concatenated text from all assistant text blocks in the turn. */
|
||||
function assistantTextOfTurn(turn) {
|
||||
const parts = [];
|
||||
for (const e of turn || []) {
|
||||
if (!e || !e.message || e.message.role !== 'assistant') continue;
|
||||
const content = Array.isArray(e.message.content) ? e.message.content : [];
|
||||
for (const b of content) {
|
||||
if (b && b.type === 'text' && typeof b.text === 'string') parts.push(b.text);
|
||||
}
|
||||
}
|
||||
return parts.join('\n');
|
||||
}
|
||||
|
||||
const TRIGGER_PATTERNS = [
|
||||
/\bPravila\s+§\d+(?:\.\d+)?/g,
|
||||
/\bADR-\d+/g,
|
||||
/\bPSR_v1\s+R\d+(?:\.\d+)?/g,
|
||||
/\brouting-off-phase\s+L\d+/g,
|
||||
/\bL\d+\s+chain/g,
|
||||
/\bhard-(?:floor|rule)\b/gi,
|
||||
];
|
||||
|
||||
/** Heuristic triggers from assistant text. Conservative-broad — false positives OK. */
|
||||
export function extractTriggers(turn) {
|
||||
const text = assistantTextOfTurn(turn);
|
||||
const out = new Set();
|
||||
for (const re of TRIGGER_PATTERNS) {
|
||||
const matches = text.match(re);
|
||||
if (matches) for (const m of matches) {
|
||||
const norm = /^L\d+\s+chain$/.test(m) ? `routing-off-phase ${m.split(/\s+/)[0]}` : m;
|
||||
out.add(norm);
|
||||
}
|
||||
}
|
||||
return [...out];
|
||||
}
|
||||
|
||||
const CANDIDATE_NUMBERED_RE = /^\s*\d+[.\)]\s+([^\n]+)$/gm;
|
||||
const CANDIDATE_BULLET_RE = /^\s*[-*]\s+([^\n]+)$/gm;
|
||||
|
||||
/** Heuristic candidates: ≥2 numbered (preferred) or bulleted items. */
|
||||
export function extractCandidates(turn) {
|
||||
const text = assistantTextOfTurn(turn);
|
||||
const numbered = [...text.matchAll(CANDIDATE_NUMBERED_RE)].map((m) => m[1].trim());
|
||||
if (numbered.length >= 2) return numbered;
|
||||
const bulleted = [...text.matchAll(CANDIDATE_BULLET_RE)].map((m) => m[1].trim());
|
||||
if (bulleted.length >= 2) return bulleted;
|
||||
return [];
|
||||
}
|
||||
|
||||
const BOUNDARY_PATTERNS = [
|
||||
/\bADR-\d+(?:\s+§\d+(?:\.\d+)?)?/g,
|
||||
/\bPSR_v1\s+R\d+(?:\.\d+)?/g,
|
||||
/\bPravila\s+§\d+(?:\.\d+)?/g,
|
||||
/\brouting-off-phase\s+L\d+/g,
|
||||
/\bL\d+\s+chain/g,
|
||||
];
|
||||
|
||||
/** Heuristic boundaries — overlaps with triggers, dedup per-array only. */
|
||||
export function extractBoundaries(turn) {
|
||||
const text = assistantTextOfTurn(turn);
|
||||
const out = new Set();
|
||||
for (const re of BOUNDARY_PATTERNS) {
|
||||
const matches = text.match(re);
|
||||
if (matches) for (const m of matches) {
|
||||
const norm = /^L\d+\s+chain$/.test(m) ? `routing-off-phase ${m.split(/\s+/)[0]}` : m;
|
||||
out.add(norm);
|
||||
}
|
||||
}
|
||||
return [...out];
|
||||
}
|
||||
|
||||
export function extractAskUserQuestionEvents(turn) {
|
||||
const events = [];
|
||||
for (const e of turn || []) {
|
||||
@@ -528,9 +599,9 @@ export function parseTranscript(transcriptText, fallbackSessionId = null) {
|
||||
primary_rationale: {
|
||||
step: 1,
|
||||
node_chosen: skills.length > 0 ? skills[0] : 'direct',
|
||||
triggers_matched: [],
|
||||
candidates_considered: [],
|
||||
boundaries_applied: [],
|
||||
triggers_matched: extractTriggers(turn),
|
||||
candidates_considered: extractCandidates(turn),
|
||||
boundaries_applied: extractBoundaries(turn),
|
||||
hard_floor: usedSuperpowers
|
||||
? { invoked: true, rules: ['Pravila §12'] }
|
||||
: { invoked: false, rules: [] },
|
||||
|
||||
Reference in New Issue
Block a user