Follow-up from the #660 reordering stack (#784 in particular). Not introduced by that stack — this predates it — but #784 establishes sectionId immutability, and this is a live allocation site that sits outside the new factory.
What
packages/agents/src/tools/book-tools.ts allocates section ids for agent-created sections as:
const nextIndex = existingSectioning.sections.length
const sectionId = `${pageId}_s${nextIndex}`
There is a second identical site further down the same file (~line 595).
This runs in production: packages/agents is wired into the API through apps/api/src/services/agents-service.ts.
Two problems
1. The id is positional. nextIndex is sections.length, so it is derived from the array's current size rather than from a high-water mark. Delete a section and add a new one and the id is reissued — the exact failure mode #784 fixed for clone / split / merge / delete, where a reused id silently inherits the retired section's sign_language_videos.section_id, toc-generation entry and ${sectionId}_ans_* text-catalog keys (and so its translations and generated TTS audio).
2. The format is not canonical. These are pg001_s0, not pg001_sec001. #784's parser
export function parseSectionId(id: string): { pageId: string; seq: number } | null {
const match = /^(.+)_sec(\d+)$/.exec(id)
...
}
returns null for _s0. So an agent-created section is invisible to createSectionIdFactory's high-water-mark scan. The two id namespaces do not collide with each other today, but the agent-created ids get none of the no-reuse protection, and any code that treats parseSectionId as a type guard will silently skip these sections.
Suggested fix
Route both sites in book-tools.ts through createSectionIdFactory / formatSectionId so agent-created sections get canonical, never-reused ids like every other creation path.
Worth deciding separately what to do about books that already contain _sN ids in the wild — as with #784, they must not be auto-remapped, since a wrong guess reattaches a sign-language video to unrelated content.
Notes
Follow-up from the #660 reordering stack (#784 in particular). Not introduced by that stack — this predates it — but #784 establishes
sectionIdimmutability, and this is a live allocation site that sits outside the new factory.What
packages/agents/src/tools/book-tools.tsallocates section ids for agent-created sections as:There is a second identical site further down the same file (~line 595).
This runs in production:
packages/agentsis wired into the API throughapps/api/src/services/agents-service.ts.Two problems
1. The id is positional.
nextIndexissections.length, so it is derived from the array's current size rather than from a high-water mark. Delete a section and add a new one and the id is reissued — the exact failure mode #784 fixed for clone / split / merge / delete, where a reused id silently inherits the retired section'ssign_language_videos.section_id,toc-generationentry and${sectionId}_ans_*text-catalog keys (and so its translations and generated TTS audio).2. The format is not canonical. These are
pg001_s0, notpg001_sec001. #784's parserreturns
nullfor_s0. So an agent-created section is invisible tocreateSectionIdFactory's high-water-mark scan. The two id namespaces do not collide with each other today, but the agent-created ids get none of the no-reuse protection, and any code that treatsparseSectionIdas a type guard will silently skip these sections.Suggested fix
Route both sites in
book-tools.tsthroughcreateSectionIdFactory/formatSectionIdso agent-created sections get canonical, never-reused ids like every other creation path.Worth deciding separately what to do about books that already contain
_sNids in the wild — as with #784, they must not be auto-remapped, since a wrong guess reattaches a sign-language video to unrelated content.Notes
apps/api/src/routes/pages.ts, andpackages/agentsis the pipeline team's area.createSectionIdFactory.