feat(learn): an assistant explains a card missed and checks what was written

A conversation has a task (ADR 0021, D14): asking, explaining a card or
checking what was written, which the system prompt says and the history
shows. Rating a card Bad offers Explain in a snack bar lifted above the
ratings (D53); a reading typed wrong asks Why? (D54), and the field's
Check has the text checked in the studyset's language, the answer in the
app's (D54). Explaining sends the card's own parts, in a conversation
named by its front.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-25 21:16:44 +01:00
commit 8ba758c020
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -113,6 +113,8 @@ void main() {
String? conversationId,
AssistantChoice? choice = const GrantedAssistant(_claude),
AssistantGateway? asking,
AssistantTask task = AssistantTask.ask,
String? title,
}) => ConversationCubit(
assistants: AssistantRepository(asking ?? gateway),
lookups: lookups,
@ -121,6 +123,9 @@ void main() {
assistant: const AssistantRef(id: 'claude'),
scope: scope,
conversationId: conversationId,
task: task,
title: title,
answerIn: 'en',
);
/// The states [cubit] goes through while [act] runs.
@ -453,4 +458,69 @@ void main() {
expect((await conversations.all()).single.title, 'する');
await conversation.close();
});
test('a conversation for a task tells the model so with every question, '
'and is kept with it and the title it was given (D53)', () async {
final conversation = cubit(task: AssistantTask.explain, title: '日');
await conversation.open();
gateway.answers
..add(_says('It is the sun.'))
..add(_says('Also a day.'));
await conversation.ask('Why is this the answer?', const [
AssistantPart(kind: AssistantPartKind.front, text: '日'),
]);
await conversation.ask('And the other meaning?', const []);
for (final (_, request) in gateway.asked) {
expect(request.system, contains('got this card wrong'));
}
final kept = (await conversations.all()).single;
expect(kept.title, '日');
expect(kept.task, AssistantTask.explain);
await conversation.close();
});
test(
"checking tells the model the studyset's language and the app's",
() async {
lookups = AssistantLookups(
studysets: () async => [
Studyset(
id: 'ja',
content: StudysetContent(
title: 'Japanese',
description: RichContent.empty(),
frontLanguage: 'ja',
backLanguage: 'en',
),
),
Studyset(
id: 'verbs',
parentId: 'ja',
content: StudysetContent(
title: 'Verbs',
description: RichContent.empty(),
),
),
],
cardsOf: (_) async => const [],
notesOf: (_) async => const [],
);
final conversation = cubit(
task: AssistantTask.check,
scope: const AssistantScope(studysetId: 'verbs'),
);
await conversation.open();
gateway.answers.add(_says('Nothing is wrong.'));
await conversation.ask('私は学生です。', const []);
final system = gateway.asked.single.$2.system;
expect(system, contains('the language tagged ja'));
expect(system, contains('Answer in the language tagged en'));
expect((await conversations.all()).single.title, '私は学生です。');
await conversation.close();
},
);
}