right-rag 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. right_rag-0.1.0/.claude/skills/right-rag/SKILL.md +112 -0
  2. right_rag-0.1.0/.codex/skills/right-rag/SKILL.md +128 -0
  3. right_rag-0.1.0/.codex/skills/right-rag/agents/openai.yaml +4 -0
  4. right_rag-0.1.0/.github/workflows/ci.yml +34 -0
  5. right_rag-0.1.0/.github/workflows/publish.yml +37 -0
  6. right_rag-0.1.0/.gitignore +19 -0
  7. right_rag-0.1.0/.python-version +1 -0
  8. right_rag-0.1.0/AGENTS.md +222 -0
  9. right_rag-0.1.0/CLAUDE.md +200 -0
  10. right_rag-0.1.0/LICENSE +21 -0
  11. right_rag-0.1.0/PKG-INFO +552 -0
  12. right_rag-0.1.0/README.md +541 -0
  13. right_rag-0.1.0/eval/cases.example.json +23 -0
  14. right_rag-0.1.0/main.py +0 -0
  15. right_rag-0.1.0/pyproject.toml +21 -0
  16. right_rag-0.1.0/spec/__init__.py +0 -0
  17. right_rag-0.1.0/spec/answer_generators/__init__.py +1 -0
  18. right_rag-0.1.0/spec/answer_generators/default.py +21 -0
  19. right_rag-0.1.0/spec/chunk_parsers/__init__.py +0 -0
  20. right_rag-0.1.0/spec/chunk_parsers/default.py +47 -0
  21. right_rag-0.1.0/spec/chunk_savers/__init__.py +0 -0
  22. right_rag-0.1.0/spec/chunk_savers/default.py +88 -0
  23. right_rag-0.1.0/spec/document_loaders/__init__.py +0 -0
  24. right_rag-0.1.0/spec/document_loaders/default.py +97 -0
  25. right_rag-0.1.0/spec/evaluators/__init__.py +1 -0
  26. right_rag-0.1.0/spec/evaluators/default.py +119 -0
  27. right_rag-0.1.0/spec/reporters/__init__.py +1 -0
  28. right_rag-0.1.0/spec/reporters/default.py +100 -0
  29. right_rag-0.1.0/spec/retrievers/__init__.py +1 -0
  30. right_rag-0.1.0/spec/retrievers/default.py +55 -0
  31. right_rag-0.1.0/spec/unit_parsers/__init__.py +0 -0
  32. right_rag-0.1.0/spec/unit_parsers/default.py +50 -0
  33. right_rag-0.1.0/spec/unit_savers/__init__.py +0 -0
  34. right_rag-0.1.0/spec/unit_savers/default.py +93 -0
  35. right_rag-0.1.0/src/right_rag/__init__.py +2 -0
  36. right_rag-0.1.0/src/right_rag/__main__.py +6 -0
  37. right_rag-0.1.0/src/right_rag/cli.py +130 -0
  38. right_rag-0.1.0/src/right_rag/interfaces.py +155 -0
  39. right_rag-0.1.0/src/right_rag/model.py +481 -0
  40. right_rag-0.1.0/src/right_rag/pipeline.py +1011 -0
  41. right_rag-0.1.0/src/right_rag/pipeline_state.py +154 -0
  42. right_rag-0.1.0/src/right_rag/registry.py +455 -0
  43. right_rag-0.1.0/src/right_rag/spec/__init__.py +1 -0
  44. right_rag-0.1.0/src/right_rag/spec/answer_generators/__init__.py +1 -0
  45. right_rag-0.1.0/src/right_rag/spec/answer_generators/default.py +21 -0
  46. right_rag-0.1.0/src/right_rag/spec/chunk_parsers/__init__.py +1 -0
  47. right_rag-0.1.0/src/right_rag/spec/chunk_parsers/default.py +47 -0
  48. right_rag-0.1.0/src/right_rag/spec/chunk_savers/__init__.py +1 -0
  49. right_rag-0.1.0/src/right_rag/spec/chunk_savers/default.py +88 -0
  50. right_rag-0.1.0/src/right_rag/spec/document_loaders/__init__.py +1 -0
  51. right_rag-0.1.0/src/right_rag/spec/document_loaders/default.py +97 -0
  52. right_rag-0.1.0/src/right_rag/spec/evaluators/__init__.py +1 -0
  53. right_rag-0.1.0/src/right_rag/spec/evaluators/default.py +119 -0
  54. right_rag-0.1.0/src/right_rag/spec/reporters/__init__.py +1 -0
  55. right_rag-0.1.0/src/right_rag/spec/reporters/default.py +100 -0
  56. right_rag-0.1.0/src/right_rag/spec/retrievers/__init__.py +1 -0
  57. right_rag-0.1.0/src/right_rag/spec/retrievers/default.py +55 -0
  58. right_rag-0.1.0/src/right_rag/spec/unit_parsers/__init__.py +1 -0
  59. right_rag-0.1.0/src/right_rag/spec/unit_parsers/default.py +50 -0
  60. right_rag-0.1.0/src/right_rag/spec/unit_savers/__init__.py +1 -0
  61. right_rag-0.1.0/src/right_rag/spec/unit_savers/default.py +93 -0
  62. right_rag-0.1.0/tests/__init__.py +1 -0
  63. right_rag-0.1.0/tests/test_cli.py +33 -0
  64. right_rag-0.1.0/tests/test_default_plugins.py +136 -0
  65. right_rag-0.1.0/tests/test_evaluation.py +75 -0
  66. right_rag-0.1.0/tests/test_model.py +67 -0
  67. right_rag-0.1.0/tests/test_pipeline.py +53 -0
  68. right_rag-0.1.0/tests/test_pipeline_state.py +48 -0
  69. right_rag-0.1.0/tests/test_registry.py +35 -0
  70. right_rag-0.1.0/uv.lock +3291 -0
@@ -0,0 +1,112 @@
1
+ ---
2
+ name: right-rag
3
+ description: Work on right-rag domain-structured RAG repositories. Use when the user asks to inspect, extend, run, debug, query, or evaluate a right-rag project; create or modify document loaders, unit parsers, chunk parsers, savers, retrievers, answer generators, evaluators, reporters, pipelines, or tests; design domain-specific RAG behavior without coupling the framework to vector, keyword, SQL, graph, or any specific search method.
4
+ ---
5
+
6
+ # Right RAG
7
+
8
+ ## Core Model
9
+
10
+ Treat right-rag as a domain-structured RAG template, not a plain text chunker.
11
+ Keep the framework generic and put domain logic in `spec/` plugins.
12
+
13
+ The pipeline shape is:
14
+
15
+ - Index: document loader -> unit parser -> chunk parser -> unit/chunk savers -> artifacts.
16
+ - Query: one user query -> retrievers read artifacts -> answer generator combines retrieved values.
17
+ - Evaluate: eval cases -> query pipeline -> evaluator -> reporter.
18
+
19
+ Do not assume retrieval means vector search, keyword search, BM25, SQL, or graph traversal. A saver may store however it wants, and its produced artifact should expose the retrieval/use behavior needed by retrievers.
20
+
21
+ ## Before Editing
22
+
23
+ Inspect local state first:
24
+
25
+ ```powershell
26
+ git status --short
27
+ uv sync
28
+ python -m compileall src/right_rag
29
+ python -m unittest discover -s tests
30
+ uv run python -c "from right_rag.registry import build_registry; r=build_registry(); print(r.document_loaders, r.unit_parsers, r.chunk_parsers, r.unit_savers, r.chunk_savers)"
31
+ ```
32
+
33
+ Respect dirty worktrees. Do not revert user changes or generated outputs unless explicitly asked.
34
+
35
+ ## Extension Points
36
+
37
+ Prefer adding or editing plugins under `spec/` before changing core files:
38
+
39
+ - `spec/document_loaders/`: load source documents.
40
+ - `spec/unit_parsers/`: parse domain units and return `ParsedUnit`.
41
+ - `spec/chunk_parsers/`: chunk from semantic `Unit` objects.
42
+ - `spec/unit_savers/`: persist units and return unit artifacts.
43
+ - `spec/chunk_savers/`: persist chunks and return chunk artifacts.
44
+ - `spec/retrievers/`: read all artifacts and decide how to retrieve.
45
+ - `spec/answer_generators/`: generate an answer from retrieved values.
46
+ - `spec/evaluators/`: score query outputs.
47
+ - `spec/reporters/`: write evaluation reports.
48
+
49
+ Keep contracts small. Add a new abstraction only when it removes real coupling or is required for third-party plugins.
50
+
51
+ ## Parser Rules
52
+
53
+ Model the author's real document structure:
54
+
55
+ - laws: act -> chapter -> section -> clause -> subclause
56
+ - contracts: agreement -> article -> clause -> schedule item
57
+ - policies: policy -> section -> rule -> exception
58
+ - manuals: manual -> chapter -> procedure -> step
59
+ - academic papers: paper -> section -> subsection -> paragraph/table/figure note
60
+
61
+ `UnitParser.parse_document()` returns `Iterable[ParsedUnit]`, not `Unit`. The core assigns ids, document ids, parent ids, raw text, source spans, and change replay.
62
+
63
+ Do not add catch-all production parsers. `parser_can_handle_document()` should be narrow, and parser acceptance should not overlap. Reject unsupported formats visibly instead of creating one huge document unit.
64
+
65
+ ## Chunking
66
+
67
+ Chunk inside one `Unit`; every chunk must trace back to its unit. Keep chunks semantically complete: definition, rule, exception, method, condition, table row, or procedure step.
68
+
69
+ Carry citation and filtering attributes when available: source path, page, heading path, section number, role, unit type, effective date, jurisdiction, party, method, dataset, or citation.
70
+
71
+ ## Artifacts And Retrieval
72
+
73
+ Savers define how stored data can be used later. A unit saver creates a unit artifact; a chunk saver creates a chunk artifact. Retrievers receive all artifacts, usually grouped by saver name, and decide how to combine them.
74
+
75
+ Design artifacts by capability, not storage brand:
76
+
77
+ - A vector-backed artifact may expose semantic search.
78
+ - A SQL-backed artifact may expose filter/query methods.
79
+ - A graph-backed artifact may expose traversal.
80
+ - A custom artifact may expose any domain-specific lookup.
81
+
82
+ Retrievers should not need to know saver implementation details. They should call artifact methods or protocol capabilities and produce retrieved value items for answer generation.
83
+
84
+ ## Query Parameters
85
+
86
+ Keep user-facing query simple: one query string plus optional parameters. Broadcast generic parameters to all retrievers. Allow retriever-specific overrides only when explicitly namespaced by the CLI or request format.
87
+
88
+ Do not hard-code parameter names like `top_k` in core logic. Let each retriever declare or parse the parameters it supports so command help and validation can follow the selected retrievers.
89
+
90
+ ## Evaluation
91
+
92
+ Provide a default evaluator only as smoke/regression coverage. Real projects should customize `spec/evaluators/` for their domain.
93
+
94
+ Useful default checks:
95
+
96
+ - expected retrieved ids are present
97
+ - answer contains required text
98
+ - retrieved evidence contains required text
99
+ - forbidden text is absent
100
+ - simple hit/recall/overlap metrics
101
+
102
+ Reports should be generated by reporters under `spec/reporters/`, commonly Markdown for humans and JSON for automation.
103
+
104
+ ## Useful Commands
105
+
106
+ ```powershell
107
+ right-rag index
108
+ right-rag query "your question"
109
+ right-rag evaluate eval\cases.example.json --output output\evaluation\report.md
110
+ python -m compileall src/right_rag
111
+ python -m unittest discover -s tests
112
+ ```
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: right-rag
3
+ description: Work on right-rag domain-structured RAG repositories. Use when Codex needs to inspect, extend, run, debug, query, or evaluate a right-rag project; create or modify document loaders, unit parsers, chunk parsers, savers, retrievers, answer generators, evaluators, reporters, pipelines, or tests; design domain-specific RAG behavior without coupling the framework to vector, keyword, SQL, graph, or any specific search method.
4
+ ---
5
+
6
+ # Right RAG
7
+
8
+ ## Core Model
9
+
10
+ Treat right-rag as a domain-structured RAG template, not a plain text chunker.
11
+ Keep the framework generic and put domain logic in `spec/` plugins.
12
+
13
+ The default shape is:
14
+
15
+ - Index: document loader -> unit parser -> chunk parser -> unit/chunk savers -> artifacts.
16
+ - Query: one user query -> retrievers read artifacts -> answer generator combines retrieved values.
17
+ - Evaluate: eval cases -> query pipeline -> evaluator -> reporter.
18
+
19
+ Do not assume retrieval means vector search, keyword search, BM25, SQL, or graph traversal. A saver may store however it wants, and its produced artifact should expose the retrieval/use behavior needed by retrievers.
20
+
21
+ ## Before Editing
22
+
23
+ In a right-rag repo, first inspect local structure and current state:
24
+
25
+ ```powershell
26
+ uv sync
27
+ python -m compileall src/right_rag
28
+ python -m unittest discover -s tests
29
+ uv run python -c "from right_rag.registry import build_registry; r=build_registry(); print(r.document_loaders, r.unit_parsers, r.chunk_parsers, r.unit_savers, r.chunk_savers)"
30
+ ```
31
+
32
+ Respect dirty worktrees. Do not revert user changes or generated outputs unless explicitly asked.
33
+
34
+ ## Extension Points
35
+
36
+ Prefer adding or editing plugins under `spec/` before changing core files:
37
+
38
+ - `spec/document_loaders/`: load source documents.
39
+ - `spec/unit_parsers/`: parse domain units and return `ParsedUnit`.
40
+ - `spec/chunk_parsers/`: chunk from semantic `Unit` objects.
41
+ - `spec/unit_savers/`: persist units and return unit artifacts.
42
+ - `spec/chunk_savers/`: persist chunks and return chunk artifacts.
43
+ - `spec/retrievers/`: read all artifacts and decide how to retrieve.
44
+ - `spec/answer_generators/`: generate an answer from retrieved values.
45
+ - `spec/evaluators/`: score query outputs.
46
+ - `spec/reporters/`: write evaluation reports.
47
+
48
+ Keep contracts small. Add a new abstraction only when it removes real coupling or is required for third-party plugins.
49
+
50
+ ## Parser Rules
51
+
52
+ Model the author's real document structure. Examples:
53
+
54
+ - laws: act -> chapter -> section -> clause -> subclause
55
+ - contracts: agreement -> article -> clause -> schedule item
56
+ - policies: policy -> section -> rule -> exception
57
+ - manuals: manual -> chapter -> procedure -> step
58
+ - academic papers: paper -> section -> subsection -> paragraph/table/figure note
59
+
60
+ `UnitParser.parse_document()` returns `Iterable[ParsedUnit]`, not `Unit`. The core assigns ids, document ids, parent ids, raw text, source spans, and change replay.
61
+
62
+ Do not add catch-all production parsers. `parser_can_handle_document()` should be narrow, and parser acceptance should not overlap. Reject unsupported formats visibly instead of creating one huge document unit.
63
+
64
+ ## Chunking
65
+
66
+ Chunk inside one `Unit`; every chunk must trace back to its unit. Keep chunks semantically complete: definition, rule, exception, method, condition, table row, or procedure step.
67
+
68
+ Carry citation and filtering attributes when available: source path, page, heading path, section number, role, unit type, effective date, jurisdiction, party, method, dataset, or citation.
69
+
70
+ ## Artifacts And Retrieval
71
+
72
+ Savers define how stored data can be used later. A unit saver creates a unit artifact; a chunk saver creates a chunk artifact. Retrievers receive all artifacts, usually grouped by saver name, and decide how to combine them.
73
+
74
+ Design artifacts by capability, not storage brand:
75
+
76
+ - A vector-backed artifact may expose semantic search.
77
+ - A SQL-backed artifact may expose filter/query methods.
78
+ - A graph-backed artifact may expose traversal.
79
+ - A custom artifact may expose any domain-specific lookup.
80
+
81
+ Retrievers should not need to know the implementation details of a saver. They should call artifact methods or protocol capabilities and produce retrieved value items for answer generation.
82
+
83
+ ## Query Parameters
84
+
85
+ Keep user-facing query simple: one query string plus optional parameters. Broadcast generic parameters to all retrievers. Allow retriever-specific overrides only when explicitly namespaced by the CLI or request format.
86
+
87
+ Do not hard-code parameter names like `top_k` in core logic. Let each retriever declare or parse the parameters it supports so command help and validation can follow the selected retrievers.
88
+
89
+ ## Evaluation
90
+
91
+ Provide a default evaluator only as smoke/regression coverage. Make it explicit that real projects should customize `spec/evaluators/` for their domain.
92
+
93
+ Useful default checks:
94
+
95
+ - expected retrieved ids are present
96
+ - answer contains required text
97
+ - retrieved evidence contains required text
98
+ - forbidden text is absent
99
+ - simple hit/recall/overlap metrics
100
+
101
+ Reports should be generated by reporters under `spec/reporters/`, commonly Markdown for humans and JSON for automation.
102
+
103
+ ## Useful Commands
104
+
105
+ Run index:
106
+
107
+ ```powershell
108
+ right-rag index
109
+ ```
110
+
111
+ Run query:
112
+
113
+ ```powershell
114
+ right-rag query "your question"
115
+ ```
116
+
117
+ Run evaluation:
118
+
119
+ ```powershell
120
+ right-rag evaluate eval\cases.example.json --output output\evaluation\report.md
121
+ ```
122
+
123
+ Final verification after changes:
124
+
125
+ ```powershell
126
+ python -m compileall src/right_rag
127
+ python -m unittest discover -s tests
128
+ ```
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Right RAG"
3
+ short_description: "Work on right-rag RAG pipelines"
4
+ default_prompt: "Use $right-rag to inspect, extend, run, query, or evaluate a right-rag repository."
@@ -0,0 +1,34 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: ["main"]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - uses: actions/setup-python@v5
15
+ with:
16
+ python-version: "3.13"
17
+
18
+ - name: Install uv
19
+ run: python -m pip install uv
20
+
21
+ - name: Install dependencies
22
+ run: uv sync
23
+
24
+ - name: Compile package
25
+ run: uv run python -m compileall src/right_rag
26
+
27
+ - name: Run tests
28
+ run: uv run python -m unittest discover -s tests
29
+
30
+ - name: Check CLI
31
+ run: uv run right-rag --help
32
+
33
+ - name: Build package
34
+ run: uv build
@@ -0,0 +1,37 @@
1
+ name: Publish
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ publish:
12
+ runs-on: ubuntu-latest
13
+ environment: pypi
14
+ permissions:
15
+ id-token: write
16
+ contents: read
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - uses: actions/setup-python@v5
21
+ with:
22
+ python-version: "3.13"
23
+
24
+ - name: Install uv
25
+ run: python -m pip install uv
26
+
27
+ - name: Install dependencies
28
+ run: uv sync
29
+
30
+ - name: Run tests
31
+ run: uv run python -m unittest discover -s tests
32
+
33
+ - name: Build package
34
+ run: uv build
35
+
36
+ - name: Publish to PyPI
37
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,19 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Local source documents
13
+ data/documents/*
14
+
15
+ # Pipeline state
16
+ .right-rag-state.json
17
+
18
+ # Generated pipeline output
19
+ output/
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,222 @@
1
+ # AGENTS.md
2
+
3
+ Guide for AI agents working in this repository.
4
+
5
+ ## Setup First
6
+
7
+ Before changing code, make sure the project is runnable:
8
+
9
+ ```powershell
10
+ uv sync
11
+ python -m compileall src/right_rag
12
+ python -m unittest discover -s tests
13
+ uv run python -c "from right_rag.registry import build_registry; r=build_registry(); print(r.document_loaders, r.unit_parsers, r.chunk_parsers, r.unit_savers, r.chunk_savers)"
14
+ ```
15
+
16
+ Source documents go in `data/documents/`. Pipeline output goes to `output/`.
17
+
18
+ ## How This Repo Works
19
+
20
+ `right-rag` keeps the pipeline core separate from task-specific logic in
21
+ `spec/`.
22
+
23
+ Core files:
24
+
25
+ - `src/right_rag/interfaces.py`: plugin contracts.
26
+ - `src/right_rag/model.py`: `Document`, `ParsedUnit`, `Unit`, `Chunk`, change replay helpers.
27
+ - `src/right_rag/registry.py`: auto-discovers plugins under `spec/`.
28
+ - `src/right_rag/pipeline.py`: orchestration, incremental state, parser output normalization.
29
+ - `src/right_rag/pipeline_state.py`: JSON serialization and stable hashing.
30
+ - `tests/`: stdlib `unittest` suite for core behavior and default plugins.
31
+
32
+ Prefer editing or adding plugins in `spec/` before changing the pipeline core.
33
+
34
+ ## Adapt Plugins To The Documents
35
+
36
+ `right-rag` is a domain-structured RAG pipeline, not a plain text chunker. The
37
+ default unit/chunk parsers are smoke-test tools only. Never use them for
38
+ production data. Real work belongs in domain parsers under `spec/`.
39
+
40
+ Read the user's actual document type before editing. Choose domain units from
41
+ the document's real structure:
42
+
43
+ - laws: act -> chapter -> section -> clause -> subclause
44
+ - contracts: agreement -> article -> clause -> schedule item
45
+ - policies: policy -> section -> rule -> exception
46
+ - manuals: manual -> chapter -> procedure -> step
47
+ - academic papers: paper -> section -> subsection -> paragraph/table/figure note
48
+
49
+ Plugin folders:
50
+
51
+ - `spec/document_loaders/`: `DocumentLoader`
52
+ - `spec/unit_parsers/`: `UnitParser` returning `ParsedUnit`
53
+ - `spec/chunk_parsers/`: `ChunkParser`
54
+ - `spec/unit_savers/`: `UnitSaver`
55
+ - `spec/chunk_savers/`: `ChunkSaver`
56
+
57
+ Use the default loader unless the source is not a normal local document folder
58
+ or its output is not enough. For PDFs, the default loader uses `pypdfium2` text
59
+ extraction first to avoid Docling OCR/preprocess memory errors; non-PDF files
60
+ still go through Docling. Scanned PDFs without a text layer need a custom OCR
61
+ loader.
62
+
63
+ ## How To Design Parsers
64
+
65
+ 1. Survey the documents and count the real formats.
66
+ 2. Create one parser per stable document format.
67
+ 3. Identify structural markers: `หมวด`, `มาตรา`, `ข้อ`, `Section`, `Chapter`,
68
+ definitions, penalties, duties, prohibitions, transitory provisions, tables,
69
+ schedules, and appendices.
70
+ 4. Make `UnitParser` return a `ParsedUnit` tree.
71
+ 5. Make `ChunkParser` chunk from semantic `Unit` objects, not raw text.
72
+
73
+ Each parser must implement `parser_can_handle_document()` narrowly. Multiple
74
+ parsers are fine when the corpus has multiple formats, but their acceptance
75
+ rules should not overlap. Do not add catch-all parsers. Do not fallback to
76
+ `ParsedUnit(topic="document", fulltext=document.text)` or one huge chunk when
77
+ structure is missing. Reject/report the reason instead.
78
+
79
+ ## Unit Parser Contract
80
+
81
+ `UnitParser.parse_document()` returns `Iterable[ParsedUnit]`, not `Unit`.
82
+
83
+ A `ParsedUnit` contains only what the parser should know:
84
+
85
+ ```python
86
+ ParsedUnit(
87
+ topic="Clause 1",
88
+ fulltext="...",
89
+ temporal_topic="Procurement Rules 2569 Clause 1",
90
+ source_start=120,
91
+ source_end=260,
92
+ attributes={"page_start": 4, "page_end": 5, "clause": "1"},
93
+ metadata={"heading_path": ["Part 1", "Clause 1"]},
94
+ subunits=[
95
+ ParsedUnit(
96
+ topic="Subclause 1",
97
+ temporal_topic="Procurement Rules 2569 Clause 1 Subclause 1",
98
+ fulltext="...",
99
+ )
100
+ ],
101
+ )
102
+ ```
103
+
104
+ The library handles:
105
+
106
+ - `Unit.id`
107
+ - `document_id`
108
+ - `parent_id`
109
+ - copying `source_start` and `source_end` to `Unit`
110
+ - computing `Unit.raw_text` from `Document.text[source_start:source_end]`
111
+ - recursive subunit conversion
112
+ - `AddUnit`, `EditUnit`, `DeleteUnit`
113
+ - attribute changes
114
+ - parent `AddSubUnit`, `EditSubUnit`, `DeleteSubUnit`
115
+ - current-stage replay from changes
116
+
117
+ Identity uses `topic` plus the parent path. Metadata is stored only and is not
118
+ used for processing decisions. Child ids include the parent path, so repeated
119
+ topics under different parents do not collide.
120
+
121
+ ## Unit Parser Best Practices
122
+
123
+ - Preserve the author's hierarchy. Do not flatten meaningful sections.
124
+ - Keep `topic` stable and normalized.
125
+ - Use `temporal_topic` only when the parser can identify version/date context.
126
+ - Put hierarchy in `ParsedUnit.subunits`.
127
+ - Set `source_start` and `source_end` to character offsets in `Document.text`.
128
+ - Keep `fulltext` faithful: no summaries, rewrites, translations, or invented
129
+ text.
130
+ - Store provenance in `attributes` or `metadata`: page, heading path, section
131
+ number, table id, figure id, citation, source filename.
132
+ - Mark heuristic extraction in metadata instead of pretending it is certain.
133
+ - Do not create `Unit` inside a unit parser.
134
+
135
+ ## Chunk Parser Best Practices
136
+
137
+ Chunk parsers should make retrieval accurate and citable.
138
+
139
+ - Chunk inside one `Unit`; do not mix unrelated units.
140
+ - Include parent context in chunk text or attributes: `temporal_topic`, section
141
+ number, citation, and source path.
142
+ - Keep chunks semantically complete: definition, rule, exception, method,
143
+ condition, table row, or procedure step.
144
+ - Do not split controlling language from its exception or limitation.
145
+ - Use attributes for filters: document type, jurisdiction, effective date,
146
+ section number, party, topic, method, dataset, citation.
147
+ - Include `parser_name`, `unit_type`, `hierarchy`, and `role` when available.
148
+ - Keep source text grounded. Add labels only when they improve citation without
149
+ changing meaning.
150
+ - Prefer fewer useful chunks over many tiny fragments.
151
+
152
+ ## Anti-Patterns
153
+
154
+ - One parser accepts every document.
155
+ - `parser_can_handle_document()` returns `True` for any non-empty text.
156
+ - Fallback to one document-sized unit or chunk.
157
+ - Chunking by text length before parsing structure.
158
+ - Using metadata instead of `ParsedUnit.subunits` for hierarchy.
159
+ - Assuming every document has the same format.
160
+
161
+ ## Production Checklist
162
+
163
+ - Each parser documents what it accepts.
164
+ - Parser acceptance does not overlap.
165
+ - Documents with no parser are visible and reviewed.
166
+ - `unit_type` covers the domain.
167
+ - Domain roles are represented: definition, penalty, duty, prohibition,
168
+ transitory provision, or equivalents.
169
+ - Chunk attributes carry `parser_name`, `unit_type`, `hierarchy`, and `role`.
170
+ - No default parser and no catch-all parser is selected for production.
171
+
172
+ ## Academic And Practical Accuracy
173
+
174
+ For scholarly, legal, policy, and technical RAG, accuracy means retrieval can be
175
+ audited back to source evidence.
176
+
177
+ - Preserve source file, page, section, heading path, citation, table, and figure
178
+ references when available.
179
+ - Separate extraction from interpretation. Parsers extract; downstream systems
180
+ summarize or reason.
181
+ - Preserve version context: date, edition, amendment, jurisdiction, policy
182
+ version, publication year.
183
+ - Make boundaries match the argument or rule in the document.
184
+ - Test with real domain questions and inspect saved unit/chunk JSON.
185
+ - Use parser coverage in `.right-rag-state.json` as the development loop after
186
+ a full pipeline run.
187
+
188
+ ## Parser Coverage
189
+
190
+ After `uv run right-rag index`, each unit parser state records:
191
+
192
+ - `document_chars`
193
+ - `covered_chars`
194
+ - `uncovered_chars`
195
+ - `coverage_percent`
196
+ - `units_total`
197
+ - `units_with_source_span`
198
+ - merged `source_ranges`
199
+
200
+ Coverage is computed from all active units and subunits with source spans. Low
201
+ coverage means the parser missed source text. Very high coverage with very few
202
+ units may mean the units are too broad.
203
+
204
+ ## Rules
205
+
206
+ - Keep changes small and local.
207
+ - Prefer editing `spec/` over core files.
208
+ - Use stdlib before adding dependencies.
209
+ - Add the smallest runnable check for non-trivial parser logic.
210
+ - Do not commit generated `output/`, `.right-rag-state.json`, or local source
211
+ documents.
212
+
213
+ ## Checks
214
+
215
+ ```powershell
216
+ python -m compileall src/right_rag
217
+ python -m unittest discover -s tests
218
+ uv run python -c "from right_rag.registry import build_registry; r=build_registry(); print(r.document_loaders, r.unit_parsers, r.chunk_parsers, r.unit_savers, r.chunk_savers)"
219
+ ```
220
+
221
+ The test suite covers model conversion and change replay, parser coverage,
222
+ state serialization, registry discovery, and default plugins.