35 KiB
Design: Doc tab: chunk mode returns 404
- Issue: #256
- Title on issue: [BUG] Doc tab: chunk mode returns 404
- Author: Pier-Jean Malandrino
- Date: 2026-05-07
- Status: Draft
- Target milestone: 0.6.0 — Doc-centric ingest
- Impacted layers: backend: api · services · (persistence read-only — déjà en place) · frontend: features/chunks (zéro changement attendu, déjà aligné sur le contrat) · e2e: nouveau scénario Karate
- Audit dimensions likely touched: Hexagonal Architecture · DDD · Decoupling · Tests · Documentation
- ADR spawned?: no — le modèle canonical
Chunk(port + repository) existe déjà depuis #205, on ne fait que combler une couche service + API absente.
1. Problem
Dans l'onglet Doc (DocWorkspacePage → DocChunksTab), l'activation du mode chunk déclenche un HTTP 404.
L'investigation a montré que ce n'est pas un endpoint isolé qui manque, mais 9 routes complètes côté /api/documents/{id}/... qui sont appelées depuis le front mais n'existent pas côté backend. La couche frontend a été développée sur la cible 0.6.0 (doc-centric) avant que la couche API correspondante ne soit posée.
Le domain et la persistence du modèle canonical Chunk (entité de première classe, audit log via ChunkEdit, snapshots ChunkPush) existent déjà depuis l'issue #205 (document-parser/domain/models.py, domain/ports.py, persistence/chunk_repo.py, persistence/chunk_edit_repo.py). Ce qui manque, c'est la couche service et la couche API qui les exposent au front.
Note importante: le mode OCR Debug de StudioPage (/api/analyses/*) doit rester intact. Il opère sur des AnalysisJob éphémères avec pipeline options variables, ce qui est conceptuellement différent des chunks canonical d'un document. Les deux flux partagent le service de chunking sous-jacent (port DocumentChunker) mais exposent des ressources distinctes — pas de duplication d'endpoints.
2. Goals
- Mode chunk de l'onglet Doc renvoie la liste des chunks canonical sans 404 (endpoint
GET /api/documents/{id}/chunksopérationnel). - Toutes les actions d'édition fonctionnent: add, edit, delete, split, merge, rechunk (endpoints
POST/PATCH/DELETEcorrespondants). - Le push vers un store et le diff par store fonctionnent (
POST /push,GET /diff?store=…). - L'arbre structurel s'affiche sur l'onglet Inspect (
GET /tree). - Le mode OCR Debug (
StudioPage→/api/analyses/*) reste totalement fonctionnel — non-régression. - Promotion automatique: à la première analyse réussie d'un document, ses chunks deviennent canonical (peuplent
chunkstable viaChunkRepository). - Tests: unit Vitest sur les nouveaux appels, pytest sur le service + router, e2e Karate sur le flow
Doc tab → mode chunk.
3. Non-goals
- Pas de refonte du modèle de données. Le domain
Chunk/ChunkEdit/ChunkPushest déjà posé (#205). On ne touche ni aux dataclasses ni au schéma SQLite. - Pas de refonte de StudioPage / OCR Debug. Le flux
/api/analyses/*reste tel quel; aucun port ni service de l'AnalysisService n'est modifié au-delà du hook de promotion canonical. - Pas de fusion
features/chunking/↔features/chunks/côté front. Ce sont deux features sémantiquement distinctes (acte de chunking debug vs état canonical persistant). - Pas de refactor des fichiers > 300 lignes identifiés dans l'audit (StudioPage, GraphView, ChunksEditor) — sera traité dans des issues séparées du milestone 0.7.0.
- Pas d'ajout de nouvelle table ni de nouvelles colonnes — tout est déjà présent.
- Pas de versioning d'API — première exposition de ces routes, pas de breaking change.
4. Context & constraints
Cartographie des 9 routes manquantes
Côté front (déjà écrit, fonctionne dès que le back répond):
| Méthode | URL | Front caller |
|---|---|---|
GET |
/api/documents/{id}/chunks |
features/chunks/api.ts:5 |
POST |
/api/documents/{id}/chunks |
features/chunks/api.ts:42 |
PATCH |
/api/documents/{id}/chunks/{chunkId} |
features/chunks/api.ts:13 |
DELETE |
/api/documents/{id}/chunks/{chunkId} |
features/chunks/api.ts:38 |
POST |
/api/documents/{id}/chunks/{chunkId}/split |
features/chunks/api.ts:31 |
POST |
/api/documents/{id}/chunks/merge |
features/chunks/api.ts:20 |
POST |
/api/documents/{id}/rechunk |
features/document/api.ts:31 |
GET |
/api/documents/{id}/diff?store={name} |
features/chunks/api.ts:49 |
POST |
/api/documents/{id}/chunks/push |
features/chunks/api.ts:56 |
POST |
/api/documents/{id}/push |
features/document/api.ts:35 (alias par doc — voir §5) |
GET |
/api/documents/{id}/tree |
features/document/api.ts:42 |
Surface de code touchée
-
Backend (à créer):
document-parser/services/chunk_service.py(nouveau)document-parser/api/document_chunks.py(nouveau router)document-parser/api/schemas.py(DTOs camelCase)document-parser/main.py(registration du router + injection du service)document-parser/services/analysis_service.py(hook de promotion canonical)document-parser/tests/services/test_chunk_service.py(nouveau)document-parser/tests/api/test_document_chunks.py(nouveau)
-
Backend (existant, lecture seule):
document-parser/domain/models.py—Chunk,ChunkEdit,ChunkPush✅document-parser/domain/ports.py—ChunkRepository,ChunkEditRepository,ChunkPushRepository,DocumentChunker✅document-parser/persistence/chunk_repo.py,chunk_edit_repo.py✅document-parser/services/ingestion_service.py(réutilisé pour push)document-parser/services/store_service.py(réutilisé pour diff par store)
-
Frontend (zéro changement attendu):
frontend/src/features/chunks/api.ts— déjà aligné sur le contratfrontend/src/features/document/api.ts— déjà aligné
-
E2E (à créer):
e2e/ui/src/test/resources/documents/doc-tab-chunk-mode.feature
Hexagonal Architecture
- Le domain ne change pas. Aucun nouveau port n'est ajouté: tous les besoins sont déjà couverts (
ChunkRepository,ChunkEditRepository,DocumentChunker). - Le nouveau
ChunkServiceorchestre des ports existants et délègue àAnalysisService.rechunkpour le rechunk du doc canonical (DRY: la mécanique chunk-via-Docling est une seule implémentation). - L'API ne franchit pas de couche: le router
document_chunks.pyparle uniquement aux services, jamais àchunk_repodirectement. - Conséquence: zéro nouvelle dépendance entre couches.
Deployment modes
- Supporté:
latest-localetlatest-remoteà l'identique. Aucun comportement spécifique auCONVERSION_ENGINE—ChunkServicene touche pas au converter, seulement au chunker (portDocumentChunkerqui a une seule implémentation). - HF Space: identique à
latest-remote. Pas de feature flag à ajouter dans/api/health— les chunks canonical font partie du flow doc-centric standard.
Hard constraints
- Pas de migration SQLite (tables existent déjà).
- Pas de breaking change API (premières routes exposées).
- Performance: les opérations chunk lisent/écrivent ≤ N chunks pour un doc (typiquement < 200). Aucune requête non-bornée. Le rechunk lance Docling chunker en thread (
asyncio.to_thread), commeanalysis_service.rechunkactuel.
5. Proposed design
5.1 Domain
Aucun changement. Tout est en place depuis #205:
Chunk(id, document_id, sequence, text, headings, source_page, bboxes, doc_items, token_count, created_at, updated_at, deleted_at)ChunkEdit(id, document_id, chunk_id, action ∈ INSERT|UPDATE|DELETE|MERGE|SPLIT, actor, at, before, after, parents, children, reason)ChunkPush(id, document_id, store_id, chunkset_hash, chunk_ids, …)- Ports:
ChunkRepository,ChunkEditRepository,ChunkPushRepository,DocumentChunker.
5.2 Persistence
Aucun changement. chunk_repo.py et chunk_edit_repo.py couvrent CRUD + audit. Le schéma SQLite existe déjà (vérifier database.py). Si une colonne ou un index manque pour les nouvelles requêtes (par ex. lookup chunks d'un doc ordonnés par sequence), ajouter dans la PR via un script idempotent.
5.3 Infra adapters
Aucun changement. DocumentChunker est déjà implémenté (le même qui sert analysis_service.rechunk).
5.4 Services
Nouveau: ChunkService — document-parser/services/chunk_service.py
Orchestre toutes les opérations sur le chunkset canonical d'un document. Délègue aux ports existants. Écrit les ChunkEdit en transaction avec chaque mutation.
class ChunkService:
def __init__(
self,
chunk_repo: ChunkRepository,
chunk_edit_repo: ChunkEditRepository,
chunker: DocumentChunker,
document_repo: DocumentRepository,
analysis_service: AnalysisService, # pour le rechunk
): ...
async def list_chunks(self, doc_id: str) -> list[Chunk]: ...
async def add_chunk(self, doc_id: str, text: str, after_id: str | None) -> Chunk: ...
async def update_chunk(self, doc_id: str, chunk_id: str, *, text: str | None, headings: list[str] | None) -> Chunk: ...
async def delete_chunk(self, doc_id: str, chunk_id: str) -> None: ...
async def split_chunk(self, doc_id: str, chunk_id: str, cursor_offset: int) -> list[Chunk]: ...
async def merge_chunks(self, doc_id: str, ids: list[str]) -> Chunk: ...
async def rechunk_document(self, doc_id: str, opts: ChunkingOptions | None) -> list[Chunk]: ...
async def get_tree(self, doc_id: str) -> list[DocTreeNode]: ...
async def diff_against_store(self, doc_id: str, store: str) -> list[ChunkDiff]: ...
async def push_to_store(self, doc_id: str, store: str) -> ChunkPush: ...
Règles d'invariants:
sequencedes chunks reste dense ascendant.add_chunkinsère et incrémente les suivants.delete_chunkest soft (marquedeleted_at) pour préserver l'audit; les listings filtrent.- Toute mutation produit une ligne
ChunkEditatomiquement. rechunk_document: appelleanalysis_service.rechunk_for_document(doc_id, opts)(helper public à exposer dansAnalysisServicesi pas déjà), récupère la liste de chunks, remplace le chunkset canonical (delete soft de l'ancien + insert nouveau), enregistre unChunkEditRESET.get_tree: dérive de la dernièreAnalysisJobréussie (lecture depages_json/document_json). Pas de calcul nouveau.push_to_storeetdiff_against_store: délèguent àingestion_service/store_serviceexistants. Aucune logique dupliquée; le service expose juste l'interface "doc-centric".
Hook de promotion canonical — AnalysisService.create
À la fin d'une analyse réussie, si le document n'a pas encore de chunkset canonical (chunk_repo.count_for_document(doc_id) == 0), peupler depuis job.chunks_json:
async def create(self, ...) -> AnalysisJob:
job = await self._run_analysis(...)
if job.status == AnalysisStatus.COMPLETED and job.chunks_json:
if await self._chunk_repo.count_for_document(job.document_id) == 0:
await self._promote_to_canonical(job) # nouveau private method
return job
Promotion = parse chunks_json → instancie Chunk(document_id=…) → chunk_repo.bulk_insert(chunks) → chunk_edit_repo.insert(ChunkEdit(action=INSERT, actor='system:initial-analysis')).
C'est le seul point qui croise les deux contextes (analyse → doc canonical). Reste local à AnalysisService.
5.5 API
Nouveau router: document-parser/api/document_chunks.py
Préfixe /api/documents/{doc_id}, tag documents. Toutes les routes injectent ChunkService via Depends.
@router.get("/{doc_id}/chunks", response_model=list[ChunkResponse])
@router.post("/{doc_id}/chunks", response_model=ChunkResponse) # body: AddChunkRequest
@router.patch("/{doc_id}/chunks/{chunk_id}", response_model=ChunkResponse) # body: UpdateChunkRequest
@router.delete("/{doc_id}/chunks/{chunk_id}", status_code=204)
@router.post("/{doc_id}/chunks/{chunk_id}/split", response_model=list[ChunkResponse]) # body: SplitChunkRequest { cursorOffset }
@router.post("/{doc_id}/chunks/merge", response_model=ChunkResponse) # body: MergeChunksRequest { ids }
@router.post("/{doc_id}/rechunk", response_model=list[ChunkResponse]) # body: RechunkRequest (optionnel)
@router.get("/{doc_id}/tree", response_model=list[DocTreeNodeResponse])
@router.get("/{doc_id}/diff", response_model=list[ChunkDiffResponse]) # query: store
@router.post("/{doc_id}/chunks/push", response_model=PushSummaryResponse) # body: PushRequest { store }
Note: deux URLs présentes côté front pour push (/{id}/push dans document/api.ts et /{id}/chunks/push dans chunks/api.ts). Une seule route doit exister côté backend pour respecter la directive "no duplicate". Choix: garder /{id}/chunks/push (sémantiquement plus juste — c'est bien des chunks qu'on pousse). Mettre à jour features/document/api.ts:34-39 pour cibler la même URL et virer la duplication.
DTOs Pydantic: tous camelCase (alias_generator), chunkId au lieu de chunk_id côté wire, sourcePage etc.
Erreurs:
404si doc inexistant ou chunk inexistant409simerge_chunksreçoit des ids non contigus (invariantsequence)400sisplitreçoit uncursorOffsethors range
5.6 Frontend — feature module
Zéro changement attendu. features/chunks/api.ts et features/document/api.ts sont déjà alignés.
Une seule correction à apporter pour respecter "no duplicate endpoints": modifier features/document/api.ts:35 pour pointer sur /api/documents/${id}/chunks/push au lieu de /api/documents/${id}/push. Ensuite cette fonction pushDocumentToStore peut être supprimée et le caller (document/store.ts) bascule sur pushChunksToStore de features/chunks/api.ts.
Bug latents identifiés par l'audit, à corriger dans la même PR (scope #256):
ChunksEditor.vue:235—saveTimernon clearé suronUnmounted. AjouteronBeforeUnmount(() => clearTimeout(saveTimer)).ChunksEditor.vue:215— remplaceralert(...)natif par le pattern toast partagé.
5.7 Cross-cutting (feature flags, i18n, shared types)
- Feature flags: aucun. Le mode chunk est un flow standard du doc-centric, pas optionnel.
- i18n: aucune nouvelle clé requise; les strings existent déjà côté
chunks.*. - Shared types:
DocChunk,ChunkDiff,PushSummary,DocTreeNodeexistent déjà dansfrontend/src/shared/types.ts. Vérifier la correspondance camelCase avec les nouveaux DTOs Pydantic; aucun champ nouveau attendu.
6. Alternatives considered
Alternative A — Recâbler le front sur /api/analyses/*
- Summary: Côté front, résoudre
docId → latest analysis jobIddansDocWorkspacePage, puis appeler les endpoints/api/analyses/{jobId}/...existants (rechunk, chunks/{idx}, etc.). Aucun nouveau endpoint backend. - Why not: Conceptuellement faux. Une analyse est une run éphémère avec ses propres pipeline options; un document a un état chunk canonical persistant édité dans le temps. Identifier les deux casserait le modèle 0.6.0 doc-centric et empêcherait d'avoir plusieurs analyses parallèles d'un doc (ce que StudioPage permet) sans muter l'état canonical. Casse aussi l'audit log par chunk (
ChunkEdit) qui est par doc, pas par job.
Alternative B — Fusion Analysis ↔ Document chunks
- Summary: Supprimer
analysis_jobs.chunks_json, faire que toute analyse écrive directement dans la tablechunksdu doc. StudioPage devient une UI de re-chunking d'un doc. - Why not: Casse l'usage debug de StudioPage (plusieurs runs avec pipeline options ≠ ne peuvent pas coexister). Refactor majeur, hors scope d'un bug fix. Pourrait être envisagé en 0.7.0+ si l'usage de StudioPage évolue.
7. API & data contract
Endpoints (tous nouveaux, additifs)
| Method | Path | Request | Response | Breaking? |
|---|---|---|---|---|
| GET | /api/documents/{id}/chunks |
— | ChunkResponse[] |
additive |
| POST | /api/documents/{id}/chunks |
AddChunkRequest {text, afterId?} |
ChunkResponse |
additive |
| PATCH | /api/documents/{id}/chunks/{chunkId} |
UpdateChunkRequest {text?, headings?} |
ChunkResponse |
additive |
| DELETE | /api/documents/{id}/chunks/{chunkId} |
— | 204 | additive |
| POST | /api/documents/{id}/chunks/{chunkId}/split |
SplitChunkRequest {cursorOffset} |
ChunkResponse[] |
additive |
| POST | /api/documents/{id}/chunks/merge |
MergeChunksRequest {ids} |
ChunkResponse |
additive |
| POST | /api/documents/{id}/rechunk |
RechunkRequest {chunkingOptions?} |
ChunkResponse[] |
additive |
| GET | /api/documents/{id}/tree |
— | DocTreeNodeResponse[] |
additive |
| GET | /api/documents/{id}/diff?store={name} |
— | ChunkDiffResponse[] |
additive |
| POST | /api/documents/{id}/chunks/push |
PushRequest {store} |
PushSummaryResponse |
additive |
Sérialisation camelCase côté wire (Pydantic alias_generator), snake_case interne. pages_json reste l'exception documentée si réutilisé.
Persistence schema
-- Aucune migration. Tables existantes (issue #205):
-- chunks(id, document_id, sequence, text, headings_json, source_page, bboxes_json,
-- doc_items_json, token_count, created_at, updated_at, deleted_at)
-- chunk_edits(id, document_id, chunk_id, action, actor, at, before_json, after_json,
-- parents_json, children_json, reason)
-- chunk_pushes(id, document_id, store_id, chunkset_hash, chunk_ids_json, ...)
Env vars / config
| Name | Default | Allowed | Notes |
|---|---|---|---|
| (aucune) | Le ChunkService ne lit aucune nouvelle config. |
Breaking changes
Aucun. Toutes les routes sont additives, premier passage live. Côté front, suppression de l'alias pushDocumentToStore (URL /push) au profit de pushChunksToStore (URL /chunks/push) — pas exposé externement.
8. Risks & mitigations
| Risk | Audit dimension | Likelihood | Impact | How we notice | Mitigation / rollback |
|---|---|---|---|---|---|
| Promotion canonical s'exécute deux fois (race) sur deux analyses simultanées d'un doc neuf, dupliquant le chunkset | DDD / Decoupling | basse | élevée (corruption canonical) | tests d'intégration concurrent + count_for_document inattendu en prod | Garde idempotente dans _promote_to_canonical (vérif count + insert sous transaction unique) |
Régression OCR Debug si analysis_service.create modifié maladroitement |
Tests / Decoupling | moyenne | élevée (StudioPage cassé) | non-régression e2e Karate full-happy-path.feature |
Hook de promotion isolé en méthode privée, tests pytest qui couvrent les deux cas (analyse 1ère, analyse N) |
Endpoint /diff lent si beaucoup de chunks × store comparé |
Performance | basse | moyenne | logs de durée > 1s | Borne dure (max 500 chunks par doc), pagination si dépassé en 0.7 |
ChunkEdit audit log non écrit en cas d'erreur partielle (ex: split crash après insert chunk 1/2) |
Hexagonal Architecture / DDD | moyenne | moyenne (audit incohérent) | tests intégration + assert chunk_edit count après chaque mutation | Toutes les mutations en transaction aiosqlite unique chunk + edit |
pushDocumentToStore (URL legacy /push) encore appelé quelque part en prod |
Decoupling | basse | basse (404 silencieux) | grep + e2e | Suppression complète de la fonction TS, pas d'alias |
| Test e2e Karate flaky si l'analyse initiale n'a pas eu le temps de promouvoir | Tests | moyenne | basse | flaky en CI | E2E poll sur lifecycle_state == 'parsed' avant de cliquer sur l'onglet chunk |
9. Testing strategy
Backend — pytest (document-parser/tests/)
- Unit
tests/services/test_chunk_service.py:list_chunksfiltre lesdeleted_atnon nulladd_chunkinsert + auditINSERT+ recale lessequencesuivantsupdate_chunkmodifie + auditUPDATEavecbefore/aftercorrectement remplisdelete_chunksoft-delete + auditDELETEsplit_chunkproduit 2 chunks + 2 auditSPLITavecparents = [source_id]merge_chunksproduit 1 chunk + 1 auditMERGEavecparents = [ids]merge_chunksrejette ids non contigus (409)rechunk_documentremplace canonical et écrit un auditRESETpush_to_storedélègue àingestion_service(mock)diff_against_storedélègue àstore_service(mock)
- Unit
tests/services/test_analysis_service.py(existant, à étendre):create()peuple chunks canonical à la 1ère analyse réussiecreate()ne touche PAS au canonical aux analyses suivantes (idempotence)create()ne crée rien si l'analyse échoue
- Integration
tests/api/test_document_chunks.py:- Chaque route → 200 happy path
- Chaque route → 404 si doc inexistant
- Chaque route mutante → 404 si chunk inexistant
merge→ 409 si non contigussplit→ 400 si offset hors range- DTOs camelCase respectés sur input ET output
- Architecture tests: vérifier que
api/document_chunks.pyn'importe paspersistence/*directement.
Frontend — Vitest
features/chunks/api.test.ts(existant, à étendre):- Tests d'erreur HTTP (404, 500) sur
fetchChunks,updateChunk,mergeChunks,splitChunk,dropChunk,addChunk,fetchChunkDiff,pushChunksToStore
- Tests d'erreur HTTP (404, 500) sur
features/chunks/store.test.ts(existant, à étendre):$resetà chaque changement dedocId(corrige le bug latent identifié dans l'audit).features/document/api.test.ts: ajouter tests pourfetchDocumentTree,rechunkDocument. SupprimerpushDocumentToStore(réécriture verspushChunksToStore).features/document/store.test.ts: idem.
E2E — Karate UI (e2e/)
- Nouveau
e2e/ui/src/test/resources/documents/doc-tab-chunk-mode.feature:- Setup via API: upload doc → wait
lifecycle_state == 'parsed' - UI: ouvrir
/docs/{id}→ cliquer onglet Doc → activer modechunk - Assert: liste de chunks visible, pas de bannière 404, count > 0
- Edit un chunk → assert persistence après reload
- Cleanup via API: delete doc
- Tags:
@critical @ui
- Setup via API: upload doc → wait
- Non-régression sur OCR Debug: vérifier que
e2e/ui/src/test/resources/full-happy-path.feature(StudioPage) reste vert après les modifications.
Manual QA
docker-compose -f docker-compose.dev.yml up- Upload un PDF via
/docs/new, attendre statusparsed - Ouvrir
/docs/{id}→ onglet Doc → bouton chunk → vérifier liste affichée - Ouvrir
/studio→ upload + run analyse OCR → vérifier que c'est inchangé - Push doc vers un store → vérifier
/api/documents/{id}/diff?store=…
Performance / load
Pas d'objectif chiffré particulier. Surveiller: rechunk_document doit rester sous le budget MAX_CONCURRENT_ANALYSES (réutilise le même mécanisme que analysis_service.rechunk).
10. Rollout & observability
Release branch
release/0.6.0 — c'est l'objet du milestone "Doc-centric ingest". Branche de travail: fix/256-doc-tab-chunk-mode-404.
Feature flag / staged rollout
Aucun flag. Le mode chunk fait partie du flow standard 0.6.0; pas de fallback derrière un toggle. La promotion canonical des chunks à la 1ère analyse est inconditionnelle.
Observability
- Logs structurés à ajouter dans
ChunkService:chunk.add docId=… chunkId=… sequence=…chunk.update docId=… chunkId=…chunk.delete docId=… chunkId=…chunk.split docId=… sourceId=… newIds=…chunk.merge docId=… sourceIds=… newId=…chunk.rechunk docId=… count=… durationMs=…chunk.push docId=… store=… count=…chunk.promote docId=… count=… (initial-analysis)
- Pas de nouvelle métrique Prometheus.
- Erreurs: les
HTTPException4xx ne loggent pas (normal); les 5xx loggent avec stack vialogger.exception(pattern existant dansdocuments.py:131).
Rollback plan
- Le déploiement est entièrement additif (nouvelles routes + nouveau service). Rollback = revert du commit / image précédente. Aucun changement de schéma à défaire.
- Si un bug critique apparaît côté promotion canonical: désactiver le hook dans
analysis_service.createvia revert ciblé; les analyses continuent de fonctionner sans canonical (l'onglet chunk redeviendrait 404, état antérieur). Pas de perte de données. - Si un bug critique apparaît côté API chunks: revert du router suffit; l'onglet chunk redevient 404, le reste de l'app reste fonctionnel.
Liens:
- Deployment:
docs/release/*(/release:deploy) - Rollback:
/release:rollback - Incident:
docs/operations/*(/ops:incident)
11. Open questions
- Le port
DocumentChunkeraccepte-t-ilcursor_offsetpoursplit, ou faut-il l'implémenter manuellement dansChunkService.split_chunk(split textuel basique sans repasser dans Docling) ? À vérifier au moment de l'implémentation. - Faut-il enregistrer un
ChunkEditRESETlors durechunk_document, ou unDELETEpar chunk +INSERTpar nouveau chunk ? Décision: une seule entréeRESET(lisibilité audit), avecparents = [old_ids],children = [new_ids]. get_tree: lit-onpages_jsonoudocument_json? À confirmer en lisantanalysis_service.find_by_id— probablementdocument_jsonqui contient la structure docling complète.
12. References
- Issue: https://github.com/scub-france/Docling-Studio/issues/256
- Related PRs / commits:
- ADRs: <ADR-NNN or "none planned">
- Project docs:
- Architecture:
docs/architecture.md - Coding standards:
docs/architecture/coding-standards.md - ADR guide / template:
docs/architecture/adr-guide.md,docs/architecture/adr-template.md - Audit master:
docs/audit/master.md - E2E conventions:
e2e/CONVENTIONS.md
- Architecture:
- External: <specs, upstream issues, dashboards, third-party docs>