parseTaskLedger function
Parses the LAST task-ledger fenced block out of text — the shape the
FinalizeGate contract mandates for the final answer. Returns null when
the text carries no block (legacy answers replay unchanged) or the block
holds no requirement-bearing entries.
Entries are - key: value lines; further key: value lines (deeper
indented) extend the current entry. Missing statuses parse as
TaskLedgerItemStatus.fail; unknown keys are ignored.
Implementation
TaskLedger? parseTaskLedger(String text) {
final block = _lastLedgerBlock(text);
if (block == null) return null;
final items = <TaskLedgerItem>[];
Map<String, String> current = {};
void flush() {
final requirement = (current['requirement'] ?? '').trim();
if (requirement.isNotEmpty) {
items.add(
TaskLedgerItem(
requirement: requirement,
command: current['command'] ?? '',
expected: current['expected'] ?? '',
actual: current['actual'] ?? '',
status: TaskLedgerItemStatus.fromName(current['status']),
),
);
}
current = {};
}
for (final line in block.split('\n')) {
final entry = RegExp(r'^\s*-\s+([A-Za-z_]+)\s*:\s?(.*)$').firstMatch(line);
final field = RegExp(r'^\s+([A-Za-z_]+)\s*:\s?(.*)$').firstMatch(line);
if (entry != null) {
flush();
current[entry.group(1)!] = entry.group(2) ?? '';
} else if (field != null && current.isNotEmpty) {
current[field.group(1)!] = field.group(2) ?? '';
}
}
flush();
if (items.isEmpty) return null;
return TaskLedger(items: items);
}