Label chapter chips by kind, not raw number

Beats, tags, locations, open questions, and character/arc-stage
responses that reference a chapter now carry a ChapterLabel/DisplayNumber
alongside the raw Number, computed via the new
ChapterDisplayNumberLookup. Front/back matter chips show their title;
body chapters show "Chapter N: Title".
This commit is contained in:
James Wampler
2026-08-19 18:02:53 -07:00
parent ef5260a111
commit 7f56c79b20
18 changed files with 407 additions and 88 deletions
+1 -1
View File
@@ -85,7 +85,7 @@ public class ListingTests : ServiceTestFixture
var agent = new NovelAgentService(
Db.Context,
new ScriptedModelClient([[new AgentTextBlock("Reply.")]]),
new NovelAgentToolset(Novels, Characters, Arcs, Chapters, Beats, Tags, Locations, Questions, NullLogger<NovelAgentToolset>.Instance),
new NovelAgentToolset(Novels, Characters, Arcs, Chapters, ChapterLabels, Beats, Tags, Locations, Questions, NullLogger<NovelAgentToolset>.Instance),
Options.Create(new AgentOptions()),
NullLogger<NovelAgentService>.Instance,
new SendAgentMessageRequestValidator());
@@ -12,7 +12,7 @@ public class NovelAgentServiceTests : ServiceTestFixture
private NovelAgentToolset _toolset = null!;
protected override void OnSetUp() =>
_toolset = new NovelAgentToolset(Novels, Characters, Arcs, Chapters, Beats, Tags, Locations, Questions, NullLogger<NovelAgentToolset>.Instance);
_toolset = new NovelAgentToolset(Novels, Characters, Arcs, Chapters, ChapterLabels, Beats, Tags, Locations, Questions, NullLogger<NovelAgentToolset>.Instance);
private NovelAgentService BuildAgent(ScriptedModelClient model) => new(
Db.Context,
@@ -23,6 +23,7 @@ public abstract class ServiceTestFixture
protected NovelService Novels { get; private set; } = null!;
protected CharacterService Characters { get; private set; } = null!;
protected ChapterService Chapters { get; private set; } = null!;
protected ChapterDisplayNumberLookup ChapterLabels { get; private set; } = null!;
protected BeatService Beats { get; private set; } = null!;
protected CharacterArcService Arcs { get; private set; } = null!;
protected OpenQuestionService Questions { get; private set; } = null!;
@@ -75,7 +76,8 @@ public abstract class ServiceTestFixture
Db.Context, Access, Tags, ActivityLog, CharacterLogs,
new CreateCharacterRequestValidator(), new UpdateCharacterRequestValidator(), new CreateRelationshipRequestValidator(),
new LinkCharacterIdentityRequestValidator());
Chapters = new ChapterService(Db.Context, Access, Tags, Locations, ActivityLog, ChapterLogs, new CreateChapterRequestValidator(), new UpdateChapterRequestValidator());
ChapterLabels = new ChapterDisplayNumberLookup(Db.Context);
Chapters = new ChapterService(Db.Context, Access, Tags, Locations, ChapterLabels, ActivityLog, ChapterLogs, new CreateChapterRequestValidator(), new UpdateChapterRequestValidator());
Beats = new BeatService(
Db.Context, Access, Tags, ActivityLog, BeatLogs,
new CreateBeatRequestValidator(), new UpdateBeatRequestValidator(), new ReorderBeatsRequestValidator(),
@@ -103,6 +103,29 @@ public class TagServiceTests : ServiceTestFixture
});
}
[Test]
public async Task Cross_reference_labels_front_matter_by_title_not_chapter_number()
{
var foreword = await Chapters.CreateAsync(
_novelId, new CreateChapterRequest("Foreword", Number: 1, Kind: ChapterKind.FrontMatter, Tags: ["betrayal"]));
var chapter = await Chapters.CreateAsync(
_novelId, new CreateChapterRequest("Landfall", Number: 2, Tags: ["betrayal"]));
var tagId = (await Tags.ListAsync(_novelId)).Single().Id;
var tag = (await Tags.GetReferencesAsync(tagId))!;
var displayNumbers = await ChapterLabels.ForNovelAsync(_novelId);
var references = tag.ToReferencesResponse(displayNumbers);
var forewordResponse = references.Chapters.Single(c => c.Id == foreword.Id);
var chapterResponse = references.Chapters.Single(c => c.Id == chapter.Id);
Assert.Multiple(() =>
{
Assert.That(forewordResponse.DisplayNumber, Is.Null);
Assert.That(chapterResponse.DisplayNumber, Is.EqualTo(1));
});
}
[Test]
public async Task Usage_counts_are_reported_per_kind()
{