doc3gpp 0.3.2__tar.gz → 0.3.4__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 (551) hide show
  1. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/AGENTS.md +6 -2
  2. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/PKG-INFO +8 -6
  3. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/README.md +7 -5
  4. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/architecture.md +8 -5
  5. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/cli.md +33 -26
  6. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/code-map.md +1 -1
  7. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/web-server.md +9 -10
  8. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/pyproject.toml +1 -1
  9. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/__init__.py +1 -1
  10. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/cli.py +18 -4
  11. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/cli_auto_sync.py +17 -18
  12. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/cli_filters.py +7 -6
  13. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/cli_url_helpers.py +19 -9
  14. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/data/skills/doc3gpp/SKILL.md +2 -2
  15. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/models/spec_doc.py +5 -0
  16. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/cr/header.py +3 -2
  17. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/direct_extractor.py +165 -50
  18. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/tdoc_parser.py +2 -2
  19. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/scraping/spec_doc_source.py +31 -1
  20. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/scraping/tdoc_zip_source.py +3 -3
  21. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/tdoc_cr_service.py +25 -19
  22. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/errors.py +4 -0
  23. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/mcp_server.py +20 -14
  24. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/routes/jobs.py +17 -11
  25. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/static/js/spec_doc_parse.js +1 -1
  26. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/workers/handlers.py +54 -23
  27. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_mcp_end_to_end.py +15 -0
  28. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_spec_doc_cli.py +61 -0
  29. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_spec_doc_web.py +22 -1
  30. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_tdoc_parse_direct_sqlite.py +6 -8
  31. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_cli_auto_sync.py +5 -2
  32. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_job_worker.py +65 -17
  33. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_jobs_sql.py +2 -2
  34. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_parse_tdoc_url_route.py +40 -0
  35. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_spec_doc_source.py +17 -1
  36. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_cr_service.py +16 -28
  37. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_cr_service_ftp_batch.py +19 -0
  38. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_parse_direct.py +26 -19
  39. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_parse_url_batch.py +23 -5
  40. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_web_routes.py +2 -1
  41. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/web/test_mcp_server.py +87 -0
  42. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/.env.example +0 -0
  43. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/.github/workflows/publish.yml +0 -0
  44. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/.gitignore +0 -0
  45. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/.superpowers/sdd/.gitignore +0 -0
  46. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/.superpowers/sdd/2026-09-24-spec-doc-metadata-and-portal-fix/final-fix-report.md +0 -0
  47. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/.superpowers/sdd/2026-09-24-spec-doc-metadata-and-portal-fix/task-2-report.md +0 -0
  48. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/.superpowers/sdd/2026-09-24-spec-doc-web-portal/final-fix-report.md +0 -0
  49. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/.superpowers/sdd/2026-09-24-spec-doc-web-portal/task-2-report.md +0 -0
  50. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/.superpowers/sdd/2026-09-25-spec-parsed-output/final-fix-report.md +0 -0
  51. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/.superpowers/sdd/2026-09-25-spec-parsed-output/task-3-report.md +0 -0
  52. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/.superpowers/sdd/2026-09-25-spec-parsed-output/task-4-report.md +0 -0
  53. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/.superpowers/sdd/2026-09-25-spec-parsed-output/task-7-report.md +0 -0
  54. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/LICENSE +0 -0
  55. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/doc3gpp.toml.example +0 -0
  56. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/3gpp-knowledge.md +0 -0
  57. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/TODO.md +0 -0
  58. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/conventions.md +0 -0
  59. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/known-constraints.md +0 -0
  60. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-07-28-compact-output.md +0 -0
  61. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-07-28-tdoc-size-limit.md +0 -0
  62. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-07-29-fts5-search.md +0 -0
  63. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-07-29-tdoc-cr-body-changes.md +0 -0
  64. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-07-30-fts5-perf.md +0 -0
  65. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-07-31-embedding-search.md +0 -0
  66. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-01-fts5-hit-rename.md +0 -0
  67. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-01-fts5-semantic-rerank.md +0 -0
  68. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-01-semantic-search-revision.md +0 -0
  69. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-02-all-extra.md +0 -0
  70. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-02-remove-mysql-postgres.md +0 -0
  71. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-02-web-and-mcp-server.md +0 -0
  72. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-03-meeting-sync-display.md +0 -0
  73. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-03-meetings-tdoc-filter.md +0 -0
  74. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-04-web-ui-enhancements.md +0 -0
  75. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-05-tdoc-columns-dropdown.md +0 -0
  76. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-05-web-parse-and-search.md +0 -0
  77. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-07-mcp-sse-transport.md +0 -0
  78. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-10-spec-sync-list-show.md +0 -0
  79. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-11-spec-rapporteurs-comment.md +0 -0
  80. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-11-spec-rapporteurs-filter.md +0 -0
  81. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-12-mcp-spec-sync.md +0 -0
  82. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-12-single-spec-sync.md +0 -0
  83. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-12-spec-show-version-limit.md +0 -0
  84. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-12-spec-sync-dynareport-fetch.md +0 -0
  85. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-13-idempotent-cancel.md +0 -0
  86. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-13-mcp-parse-tdoc-url.md +0 -0
  87. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-13-per-spec-skip-rule.md +0 -0
  88. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-13-spec-per-version-details-flag.md +0 -0
  89. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-13-web-job-detail-params.md +0 -0
  90. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-13-web-mcp-meeting-name-filter.md +0 -0
  91. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-14-cover-page-summary-of-change.md +0 -0
  92. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-14-job-progress.md +0 -0
  93. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-14-sync-hub-page.md +0 -0
  94. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-14-version-display.md +0 -0
  95. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-14-web-tdoc-extracted-changes.md +0 -0
  96. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-19-tdocs-xlsx-metadata.md +0 -0
  97. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-08-19-web-mcp-tdoc-show-by-url.md +0 -0
  98. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-09-11-ran5-testcases.md +0 -0
  99. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-09-12-testcase-nested-statuses.md +0 -0
  100. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-09-12-testcase-separate-db.md +0 -0
  101. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-09-13-resource-schema.md +0 -0
  102. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-09-22-remote-embedding-api.md +0 -0
  103. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-09-23-spec-doc-parse.md +0 -0
  104. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-09-24-spec-doc-fetch-command-removal.md +0 -0
  105. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-09-24-spec-doc-metadata-and-portal-fix.md +0 -0
  106. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-09-24-tdoc-search-namespace.md +0 -0
  107. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-09-25-spec-doc-chunk-context.md +0 -0
  108. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-09-25-spec-doc-pagination.md +0 -0
  109. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-09-25-spec-parsed-output.md +0 -0
  110. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-09-26-unified-search-index.md +0 -0
  111. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/plans/2026-09-27-mcp-stdio.md +0 -0
  112. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-07-28-compact-output-design.md +0 -0
  113. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-07-28-tdoc-size-limit-design.md +0 -0
  114. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-07-29-fts5-search-design.md +0 -0
  115. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-07-29-tdoc-cr-body-changes-design.md +0 -0
  116. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-07-30-fts5-perf.md +0 -0
  117. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-07-31-embedding-search-design.md +0 -0
  118. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-01-fts5-hit-rename-design.md +0 -0
  119. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-01-fts5-semantic-rerank-design.md +0 -0
  120. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-01-semantic-search-revision-design.md +0 -0
  121. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-02-all-extra-design.md +0 -0
  122. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-02-remove-mysql-postgres-design.md +0 -0
  123. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-02-web-and-mcp-server-design.md +0 -0
  124. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-03-meeting-sync-display-design.md +0 -0
  125. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-03-meetings-tdoc-filter-design.md +0 -0
  126. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-04-web-ui-enhancements-design.md +0 -0
  127. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-05-tdoc-columns-dropdown-design.md +0 -0
  128. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-05-web-parse-and-search-design.md +0 -0
  129. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-10-spec-sync-list-show-design.md +0 -0
  130. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-11-spec-rapporteurs-comment-design.md +0 -0
  131. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-11-spec-rapporteurs-filter-design.md +0 -0
  132. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-12-mcp-spec-sync-design.md +0 -0
  133. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-12-single-spec-sync-design.md +0 -0
  134. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-12-spec-show-version-limit-design.md +0 -0
  135. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-12-spec-sync-dynareport-fetch-design.md +0 -0
  136. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-13-docs-review-design.md +0 -0
  137. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-13-idempotent-cancel-design.md +0 -0
  138. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-13-mcp-parse-tdoc-url-design.md +0 -0
  139. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-13-spec-per-version-details-flag-design.md +0 -0
  140. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-13-web-job-detail-params-design.md +0 -0
  141. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-13-web-mcp-meeting-name-filter-design.md +0 -0
  142. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-14-cover-page-summary-of-change-design.md +0 -0
  143. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-14-sync-hub-page-design.md +0 -0
  144. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-14-version-display-design.md +0 -0
  145. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-14-web-jobs-cancel-button-design.md +0 -0
  146. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-14-web-tdoc-extracted-changes-design.md +0 -0
  147. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-19-tdocs-xlsx-metadata-design.md +0 -0
  148. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-08-19-web-mcp-tdoc-show-by-url-design.md +0 -0
  149. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-09-11-ran5-testcases-design.md +0 -0
  150. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-09-12-testcase-nested-statuses-design.md +0 -0
  151. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-09-12-testcase-separate-db-design.md +0 -0
  152. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-09-13-resource-schema-design.md +0 -0
  153. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-09-22-remote-embedding-api-design.md +0 -0
  154. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-09-23-spec-doc-parse-design.md +0 -0
  155. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-09-24-spec-doc-fetch-command-removal-design.md +0 -0
  156. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-09-24-spec-doc-metadata-and-portal-fix-design.md +0 -0
  157. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-09-24-spec-doc-web-portal-design.md +0 -0
  158. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-09-24-tdoc-search-namespace-design.md +0 -0
  159. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-09-25-spec-doc-chunk-context-design.md +0 -0
  160. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-09-25-spec-doc-pagination-design.md +0 -0
  161. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-09-25-spec-parsed-output-design.md +0 -0
  162. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-09-26-unified-search-index-design.md +0 -0
  163. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/docs/superpowers/specs/2026-09-27-mcp-stdio-design.md +0 -0
  164. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/scripts/dev_run.sh +0 -0
  165. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/scripts/test_sqlite.sh +0 -0
  166. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/cli_server.py +0 -0
  167. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/config.py +0 -0
  168. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/data/doc3gpp.toml.example +0 -0
  169. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/models/__init__.py +0 -0
  170. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/models/index.py +0 -0
  171. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/models/jobs.py +0 -0
  172. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/models/meeting.py +0 -0
  173. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/models/schema_info.py +0 -0
  174. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/models/search.py +0 -0
  175. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/models/semantic_search.py +0 -0
  176. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/models/spec.py +0 -0
  177. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/models/sync.py +0 -0
  178. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/models/tdoc.py +0 -0
  179. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/models/tdoc_cr.py +0 -0
  180. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/models/tdoc_cr_change_details.py +0 -0
  181. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/models/tdoc_file.py +0 -0
  182. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/models/tdoc_show.py +0 -0
  183. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/models/testcase.py +0 -0
  184. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/models/tsg.py +0 -0
  185. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/models/unified_search.py +0 -0
  186. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/models/wi.py +0 -0
  187. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/__init__.py +0 -0
  188. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/calendar_parser.py +0 -0
  189. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/cr/__init__.py +0 -0
  190. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/cr/body_changes.py +0 -0
  191. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/cr/cover_page.py +0 -0
  192. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/cr/cr_parsers.py +0 -0
  193. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/cr/helpers.py +0 -0
  194. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/cr/ttcn_functions.py +0 -0
  195. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/cr/ttcn_sections.py +0 -0
  196. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/cr_parser.py +0 -0
  197. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/docx_converter.py +0 -0
  198. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/html_parsers.py +0 -0
  199. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/normalizers.py +0 -0
  200. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/spec_doc.py +0 -0
  201. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/spec_doc_chunker.py +0 -0
  202. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/spec_parser.py +0 -0
  203. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/spec_release.py +0 -0
  204. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/tdoc_file_parser.py +0 -0
  205. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/tdoc_parsers.py +0 -0
  206. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/testcase_parser.py +0 -0
  207. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/parsers/wi_parser.py +0 -0
  208. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/repository/__init__.py +0 -0
  209. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/repository/protocols.py +0 -0
  210. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/scraping/__init__.py +0 -0
  211. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/scraping/cache.py +0 -0
  212. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/scraping/cache_keys.py +0 -0
  213. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/scraping/calendar_source.py +0 -0
  214. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/scraping/client.py +0 -0
  215. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/scraping/ftp_source.py +0 -0
  216. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/scraping/portal_source.py +0 -0
  217. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/scraping/spec_source.py +0 -0
  218. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/scraping/testcase_source.py +0 -0
  219. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/scraping/wi_source.py +0 -0
  220. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/__init__.py +0 -0
  221. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/_duration.py +0 -0
  222. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/embedding/__init__.py +0 -0
  223. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/embedding/chunker.py +0 -0
  224. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/embedding/remote_embedder.py +0 -0
  225. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/factory.py +0 -0
  226. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/meetings_service.py +0 -0
  227. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/search_service.py +0 -0
  228. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/semantic_reranker.py +0 -0
  229. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/semantic_search_service.py +0 -0
  230. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/spec_doc_index_service.py +0 -0
  231. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/spec_doc_search_facade.py +0 -0
  232. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/spec_doc_search_service.py +0 -0
  233. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/spec_doc_semantic_service.py +0 -0
  234. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/spec_doc_service.py +0 -0
  235. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/spec_service.py +0 -0
  236. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/tdoc_file_service.py +0 -0
  237. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/tdoc_index_service.py +0 -0
  238. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/tdoc_search_facade.py +0 -0
  239. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/tdoc_service.py +0 -0
  240. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/tdoc_sync_coordinator.py +0 -0
  241. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/testcase_service.py +0 -0
  242. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/tsg_service.py +0 -0
  243. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/services/wi_service.py +0 -0
  244. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/settings/__init__.py +0 -0
  245. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/settings/config_source.py +0 -0
  246. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/settings/config_writer.py +0 -0
  247. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/settings/loader.py +0 -0
  248. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/settings/schema.py +0 -0
  249. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/__init__.py +0 -0
  250. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/backends/__init__.py +0 -0
  251. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/backends/sqlite.py +0 -0
  252. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/cache.py +0 -0
  253. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/compression.py +0 -0
  254. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/db/__init__.py +0 -0
  255. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/db/base.py +0 -0
  256. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/db/fts5_query.py +0 -0
  257. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/db/migrate.py +0 -0
  258. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/db/migrations/README.md +0 -0
  259. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/db/models.py +0 -0
  260. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/db/session.py +0 -0
  261. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/db/specdata_base.py +0 -0
  262. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/db/testcase_base.py +0 -0
  263. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/export.py +0 -0
  264. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/repositories/__init__.py +0 -0
  265. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/repositories/jobs_sql.py +0 -0
  266. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/repositories/meeting_sql.py +0 -0
  267. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/repositories/rich_filters.py +0 -0
  268. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/repositories/search_sql.py +0 -0
  269. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/repositories/spec_doc_search_sql.py +0 -0
  270. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/repositories/spec_doc_sql.py +0 -0
  271. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/repositories/spec_doc_vector_sql.py +0 -0
  272. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/repositories/spec_sql.py +0 -0
  273. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/repositories/tdoc_cr_change_details_sql.py +0 -0
  274. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/repositories/tdoc_cr_sql.py +0 -0
  275. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/repositories/tdoc_cr_ttcn_sql.py +0 -0
  276. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/repositories/tdoc_file_sql.py +0 -0
  277. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/repositories/tdoc_sql.py +0 -0
  278. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/repositories/testcase_sql.py +0 -0
  279. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/repositories/tsg_sql.py +0 -0
  280. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/repositories/vector_sql.py +0 -0
  281. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/storage/repositories/wi_sql.py +0 -0
  282. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/__init__.py +0 -0
  283. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/app.py +0 -0
  284. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/deps.py +0 -0
  285. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/filters.py +0 -0
  286. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/install.py +0 -0
  287. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/mcp_stdio.py +0 -0
  288. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/render.py +0 -0
  289. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/routes/__init__.py +0 -0
  290. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/routes/landing.py +0 -0
  291. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/routes/meetings.py +0 -0
  292. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/routes/search.py +0 -0
  293. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/routes/spec_docs.py +0 -0
  294. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/routes/specs.py +0 -0
  295. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/routes/sync.py +0 -0
  296. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/routes/tdocs.py +0 -0
  297. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/routes/testcases.py +0 -0
  298. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/routes/tsgs.py +0 -0
  299. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/routes/wis.py +0 -0
  300. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/state.py +0 -0
  301. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/static/htmx.min.js +0 -0
  302. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/static/js/job_poller.js +0 -0
  303. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/static/js/meeting_sync.js +0 -0
  304. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/static/js/search.js +0 -0
  305. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/static/js/spec_clipboard.js +0 -0
  306. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/static/js/spec_sync.js +0 -0
  307. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/static/js/sync_hub.js +0 -0
  308. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/static/js/tdoc_columns.js +0 -0
  309. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/static/js/tdoc_parse.js +0 -0
  310. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/static/style.css +0 -0
  311. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/base.html +0 -0
  312. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/job_status.html +0 -0
  313. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/landing.html +0 -0
  314. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/meeting_list.html +0 -0
  315. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/meeting_show.html +0 -0
  316. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/_job_row.html +0 -0
  317. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/job_status.html +0 -0
  318. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/meeting_filters.html +0 -0
  319. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/meeting_results.html +0 -0
  320. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/pagination.html +0 -0
  321. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/schema_results.html +0 -0
  322. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/search_form.html +0 -0
  323. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/search_resource_tabs.html +0 -0
  324. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/search_results.html +0 -0
  325. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/search_results_table.html +0 -0
  326. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/spec_doc_chunks.html +0 -0
  327. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/spec_doc_search_form.html +0 -0
  328. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/spec_doc_search_results.html +0 -0
  329. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/spec_doc_search_results_table.html +0 -0
  330. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/spec_doc_show_results.html +0 -0
  331. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/spec_doc_toc_results.html +0 -0
  332. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/spec_filters.html +0 -0
  333. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/spec_results.html +0 -0
  334. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/sync_recent_jobs.html +0 -0
  335. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/tdoc_filters.html +0 -0
  336. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/tdoc_results.html +0 -0
  337. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/testcase_filters.html +0 -0
  338. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/testcase_results.html +0 -0
  339. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/wi_filters.html +0 -0
  340. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/partials/wi_results.html +0 -0
  341. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/schema.html +0 -0
  342. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/search_results.html +0 -0
  343. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/spec_doc_show.html +0 -0
  344. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/spec_doc_toc.html +0 -0
  345. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/spec_docs_search.html +0 -0
  346. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/spec_list.html +0 -0
  347. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/spec_show.html +0 -0
  348. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/sync.html +0 -0
  349. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/tdoc_content.html +0 -0
  350. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/tdoc_list.html +0 -0
  351. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/tdoc_show.html +0 -0
  352. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/testcase_list.html +0 -0
  353. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/testcase_show.html +0 -0
  354. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/tsg_list.html +0 -0
  355. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/tsg_show.html +0 -0
  356. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates/wi_list.html +0 -0
  357. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/templates_setup.py +0 -0
  358. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/workers/__init__.py +0 -0
  359. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/src/doc3gpp/web/workers/job_worker.py +0 -0
  360. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/conftest.py +0 -0
  361. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/fixtures/sample_pages/3GPP-meeting-R5.html +0 -0
  362. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/fixtures/search_corpus.py +0 -0
  363. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/fixtures/semantic_search_corpus.py +0 -0
  364. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/fixtures/spec_pages/R5_crs.html +0 -0
  365. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/fixtures/spec_pages/R5_detail.html +0 -0
  366. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/fixtures/spec_pages/R5_detail_portal.html +0 -0
  367. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/fixtures/spec_pages/R5_etsi.html +0 -0
  368. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/fixtures/spec_pages/R5_list.html +0 -0
  369. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/fixtures/tdoc_cr_doc/C6-250028.zip +0 -0
  370. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/fixtures/tdoc_cr_doc/R5-227476.zip +0 -0
  371. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/fixtures/tdoc_cr_doc/R5-253079.zip +0 -0
  372. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/fixtures/tdoc_cr_doc/R5s260009.zip +0 -0
  373. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/fixtures/tdoc_cr_doc/R5s260051.zip +0 -0
  374. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/fixtures/tdoc_cr_doc/R5s260135.zip +0 -0
  375. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/fixtures/tdoc_cr_doc/R5s260176.zip +0 -0
  376. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/fixtures/tdoc_xlsx/TDoc_List_Meeting_85434_portal.xlsx +0 -0
  377. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/fixtures/tdoc_xlsx/TDoc_List_Meeting_RAN5#111.xlsx +0 -0
  378. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/fixtures/wi_pages/R5.html +0 -0
  379. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_auto_sync_sqlite.py +0 -0
  380. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_cli_server.py +0 -0
  381. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_cli_sqlite.py +0 -0
  382. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_config_init_cli_sqlite.py +0 -0
  383. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_config_set_cli_sqlite.py +0 -0
  384. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_db_reset_sqlite.py +0 -0
  385. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_embed_after_parse.py +0 -0
  386. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_meeting_service_sqlite.py +0 -0
  387. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_migrate_tsg_spec_last_sync_drop.py +0 -0
  388. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_online_3gpp_calendar.py +0 -0
  389. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_online_spec_doc_sync.py +0 -0
  390. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_online_spec_sync.py +0 -0
  391. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_online_tdoc_fetch_r5.py +0 -0
  392. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_online_tdoc_parse.py +0 -0
  393. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_online_testcase_sync.py +0 -0
  394. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_parse_tdoc_url_job_sqlite.py +0 -0
  395. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_schema_parity.py +0 -0
  396. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_sdk_integration.py +0 -0
  397. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_search_after_parse.py +0 -0
  398. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_search_extras_disabled.py +0 -0
  399. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_search_filters.py +0 -0
  400. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_search_index_lifecycle.py +0 -0
  401. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_search_indexes.py +0 -0
  402. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_search_previews_per_column.py +0 -0
  403. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_search_query_sem_rerank.py +0 -0
  404. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_search_schema.py +0 -0
  405. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_search_sem_end_to_end.py +0 -0
  406. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_search_snippet_column.py +0 -0
  407. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_search_weighted_bm25.py +0 -0
  408. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_semantic_extras_disabled.py +0 -0
  409. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_spec_cli.py +0 -0
  410. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_spec_doc_repo.py +0 -0
  411. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_spec_doc_search_repo.py +0 -0
  412. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_spec_doc_search_service.py +0 -0
  413. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_spec_doc_service.py +0 -0
  414. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_spec_migration.py +0 -0
  415. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_spec_parsed_output.py +0 -0
  416. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_spec_sql.py +0 -0
  417. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_sqlite_backend.py +0 -0
  418. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_sync_hub_end_to_end.py +0 -0
  419. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_tdoc_cr_change_details_sqlite.py +0 -0
  420. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_tdoc_cr_rename_migration.py +0 -0
  421. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_tdoc_cr_sqlite.py +0 -0
  422. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_tdoc_cr_ttcn_sqlite.py +0 -0
  423. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_tdoc_file_sqlite.py +0 -0
  424. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_tdoc_parse_ftp_batch_sqlite.py +0 -0
  425. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_tdoc_parse_local_batch_sqlite.py +0 -0
  426. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_tdoc_sqlite.py +0 -0
  427. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_tdocs_xlsx_metadata_migration_sqlite.py +0 -0
  428. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_tdocs_xlsx_metadata_sqlite.py +0 -0
  429. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_testcase_cli.py +0 -0
  430. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_testcase_sql.py +0 -0
  431. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_tsg_sqlite.py +0 -0
  432. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_vector_index_lifecycle.py +0 -0
  433. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_vector_repo_get_min_distance.py +0 -0
  434. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_vector_schema_migration.py +0 -0
  435. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_web_end_to_end.py +0 -0
  436. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_web_search_end_to_end.py +0 -0
  437. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/integration/test_wi_sqlite.py +0 -0
  438. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/_job_polling_smoke.js +0 -0
  439. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_bind_job_polling_on_terminal.py +0 -0
  440. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_body_changes_extractor.py +0 -0
  441. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_cache_cli.py +0 -0
  442. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_cache_keys.py +0 -0
  443. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_calendar_parser.py +0 -0
  444. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_calendar_parser_defensive.py +0 -0
  445. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_chunker.py +0 -0
  446. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_cli_filters.py +0 -0
  447. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_cli_search.py +0 -0
  448. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_cli_search_sem.py +0 -0
  449. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_cli_server_start.py +0 -0
  450. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_cli_server_stubs.py +0 -0
  451. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_cli_version.py +0 -0
  452. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_compact_helpers.py +0 -0
  453. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_config_init_cli.py +0 -0
  454. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_config_set_cli.py +0 -0
  455. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_config_writer.py +0 -0
  456. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_cr_parser.py +0 -0
  457. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_db_reset_cli.py +0 -0
  458. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_docx_converter.py +0 -0
  459. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_factory_semantic.py +0 -0
  460. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_ftp_listing.py +0 -0
  461. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_fts5_query.py +0 -0
  462. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_index_services.py +0 -0
  463. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_job_row_partial.py +0 -0
  464. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_list_output_format.py +0 -0
  465. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_meeting_cli.py +0 -0
  466. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_meeting_cli_filters_combined.py +0 -0
  467. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_meeting_cli_invalid_fields.py +0 -0
  468. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_meeting_cli_location_filter.py +0 -0
  469. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_meeting_repository_combined_filters.py +0 -0
  470. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_meeting_repository_filters.py +0 -0
  471. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_meeting_repository_location_filter.py +0 -0
  472. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_meeting_repository_upsert.py +0 -0
  473. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_meeting_sync_tsg_validation.py +0 -0
  474. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_packaging_semantic.py +0 -0
  475. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_parsers_utils.py +0 -0
  476. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_portal_source.py +0 -0
  477. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_protocols_semantic.py +0 -0
  478. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_remote_embedder.py +0 -0
  479. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_remote_embedding_settings.py +0 -0
  480. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_rrf.py +0 -0
  481. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_schema_info.py +0 -0
  482. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_scraper_client.py +0 -0
  483. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_search_query_builder.py +0 -0
  484. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_search_service.py +0 -0
  485. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_search_settings.py +0 -0
  486. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_semantic_models.py +0 -0
  487. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_semantic_reranker.py +0 -0
  488. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_semantic_search_service.py +0 -0
  489. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_semantic_settings.py +0 -0
  490. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_server_settings.py +0 -0
  491. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_services_factory.py +0 -0
  492. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_settings.py +0 -0
  493. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_settings_config_file.py +0 -0
  494. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_spec_doc_blocks.py +0 -0
  495. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_spec_doc_chunker.py +0 -0
  496. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_spec_doc_models.py +0 -0
  497. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_spec_doc_parser.py +0 -0
  498. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_spec_doc_search_facade.py +0 -0
  499. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_spec_doc_service_reads.py +0 -0
  500. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_spec_doc_settings.py +0 -0
  501. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_spec_doc_vector_metadata.py +0 -0
  502. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_spec_model.py +0 -0
  503. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_spec_orm.py +0 -0
  504. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_spec_parser.py +0 -0
  505. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_spec_release.py +0 -0
  506. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_spec_service.py +0 -0
  507. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_spec_source.py +0 -0
  508. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_storage_utils.py +0 -0
  509. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_sync_hub_page.py +0 -0
  510. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_cache.py +0 -0
  511. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_cli_fields.py +0 -0
  512. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_cr_change_details_model.py +0 -0
  513. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_cr_model.py +0 -0
  514. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_cr_service_body_changes.py +0 -0
  515. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_file_parser.py +0 -0
  516. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_file_service.py +0 -0
  517. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_file_source.py +0 -0
  518. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_parse_cli.py +0 -0
  519. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_parse_settings.py +0 -0
  520. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_parser.py +0 -0
  521. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_repository_crud.py +0 -0
  522. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_repository_filters.py +0 -0
  523. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_search_facade.py +0 -0
  524. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_service_sync.py +0 -0
  525. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_show_record.py +0 -0
  526. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_sync_cli.py +0 -0
  527. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_sync_coordinator.py +0 -0
  528. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_url_not_found.py +0 -0
  529. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tdoc_zip_source.py +0 -0
  530. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_testcase_db_url.py +0 -0
  531. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_testcase_model.py +0 -0
  532. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_testcase_parser.py +0 -0
  533. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_testcase_service.py +0 -0
  534. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_testcase_source.py +0 -0
  535. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tsg_cli.py +0 -0
  536. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tsg_repository.py +0 -0
  537. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tsg_service.py +0 -0
  538. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_tsg_url_pattern.py +0 -0
  539. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_ttcn_functions.py +0 -0
  540. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_unified_search_models.py +0 -0
  541. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_web_app.py +0 -0
  542. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_web_errors.py +0 -0
  543. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_web_filters.py +0 -0
  544. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_web_install.py +0 -0
  545. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_web_jobs_routes.py +0 -0
  546. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_web_template_filters.py +0 -0
  547. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_wi_cli.py +0 -0
  548. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_wi_parser.py +0 -0
  549. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/test_wi_repository_filters.py +0 -0
  550. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/web/test_landing_version.py +0 -0
  551. {doc3gpp-0.3.2 → doc3gpp-0.3.4}/tests/unit/web/test_mcp_stdio.py +0 -0
@@ -275,6 +275,10 @@ Workflows in one line (full prose in `docs/architecture.md`):
275
275
  the result is still emitted with a warning). The zip cache is
276
276
  keyed on the **original (sanitized) filename** (D10 fix) so
277
277
  multiple revisions of the same tdoc_id never collide. When
278
+ the URL targets the canonical 3GPP FTP site, HTTPS is required (the
279
+ bare `www.3gpp.org/ftp/...` form is normalized), `.docx`/`.zip` files
280
+ must have a TDoc-shaped basename including `RP-...` plenary ids, and
281
+ any other canonical path is a folder with an optional trailing slash.
278
282
  `Settings.sync.auto_sync` is enabled, `tdoc parse --from-url`
279
283
  on a 3GPP FTP URL extracts tdoc_id candidates from the URL
280
284
  (basename for file URLs; BFS up to `--max-depth` / `--recursive`
@@ -356,8 +360,8 @@ Workflows in one line (full prose in `docs/architecture.md`):
356
360
  with `--resume`, `--stale-only`, and `--batch`, or `IndexStatus` with no
357
361
  rebuild flag. Hybrid weighting comes from
358
362
  `Settings.semantic_search.fts5_weight`.
359
- - `doc3gpp spec doc parse --spec <id> [--release R] [--version V] [--force]` → `SpecDocService.parse_many` → resolve the numeric-newest `SpecVersion` with a non-empty `.zip` URL via `resolve_spec_doc_version` (after optional release filtering); explicit `--version` is strict and fails clearly when its ZIP link is missing. Fetch the ZIP with internal `fetch_spec_doc_zip` when absent (or use the zip cache at `{spec_doc.cache_dir}/zips/<spec_id>/<version>.zip`), and record the download. `--force` re-downloads before re-parsing, including pairs that already carry `parsed_at`.
360
- - `doc3gpp spec doc parse --spec <id> [--release R] [--version V] [--force]` → per spec: immutable-skip when `(spec_id, version)` already carries `parsed_at` (unless `--force`) → `parse` = fetch-if-missing + `list_spec_docx` + `convert_document_to_blocks` + `order_spec_files` + `extract_spec_toc` + `chunk_blocks` → `upsert_toc` + `replace_chunks` + per-file markdown cache (`{spec_doc.cache_dir}/markdown/<spec_id>/<version>/`) + `record_parsed` + best-effort auto-index (`upsert_for_version`) / auto-embed (`index_for_version`). Oversized zips → skip bucket; unknown spec/version or empty (no-docx) zips → failure bucket; one spec never aborts the batch (`SpecDocBatchResult(successes/skipped/failures)`). Prints `ok/skipped/failed` buckets only (`--format/--output/--compact` accepted but ignored). Background `parse_spec_docs` jobs log and store per-spec errors in `result.failure_details`; all-failed batches have job status `failed`, while partial successes remain `succeeded`. Chunk/search metadata uses newline-delimited combined `sections` and `tables`; TOC entries retain `section_no` / `title`.
363
+ - `doc3gpp spec doc parse --spec <id> [--release R] [--version V] [--force]` accepts exactly one spec and calls `SpecDocService.parse_many` with a one-item list → resolve the numeric-newest `SpecVersion` with a non-empty `.zip` URL via `resolve_spec_doc_version` (after optional release filtering); when both `--release` and `--version` are given, their release numbers must align (for example, `Rel-18` with `18.5.0`); explicit `--version` is strict and fails clearly when its ZIP link is missing. Fetch the ZIP with internal `fetch_spec_doc_zip` when absent (or use the zip cache at `{spec_doc.cache_dir}/zips/<spec_id>/<version>.zip`), and record the download. `--force` re-downloads before re-parsing, including pairs that already carry `parsed_at`.
364
+ - `doc3gpp spec doc parse --spec <id> [--release R] [--version V] [--force]` → immutable-skip when `(spec_id, version)` already carries `parsed_at` (unless `--force`) → `parse` = fetch-if-missing + `list_spec_docx` + `convert_document_to_blocks` + `order_spec_files` + `extract_spec_toc` + `chunk_blocks` → `upsert_toc` + `replace_chunks` + per-file markdown cache (`{spec_doc.cache_dir}/markdown/<spec_id>/<version>/`) + `record_parsed` + best-effort auto-index (`upsert_for_version`) / auto-embed (`index_for_version`). Oversized zips → skip bucket; unknown spec/version or empty (no-docx) zips → failure bucket. Prints `ok/skipped/failed` buckets only (`--format/--output/--compact` accepted but ignored). Background `parse_spec_docs` jobs use scalar `spec_id` / optional `release` / `version` / `force`, log and store the result in `result.failure_details`. Chunk/search metadata uses newline-delimited combined `sections` and `tables`; TOC entries retain `section_no` / `title`.
361
365
  - `doc3gpp spec doc toc show --spec <id> --version <v> [--fields] [--format/--output/--compact]` → `SpecDocService.get_toc` (stored `(spec_id, version)` rows; miss → `BadParameter`). Default fields `section_no, title, level, source_file` (`[output.fields] spec_doc_toc`).
362
366
  - `doc3gpp spec doc search [--text TEXT] [--semantic TEXT] [filters]` →
363
367
  `SpecDocSearchFacade.search` with the same four mode-selection rules and
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: doc3gpp
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: Provide 3GPP TDoc information with full text search capability — a Python CLI/library, a local web UI, and an MCP server for AI clients.
5
5
  Project-URL: Homepage, https://github.com/jerrywang121/doc3gpp
6
6
  Project-URL: Repository, https://github.com/jerrywang121/doc3gpp
@@ -296,6 +296,8 @@ doc3gpp tdoc parse --from-url https://www.3gpp.org/ftp/.../R5s260009.zip # 3GPP
296
296
  doc3gpp tdoc parse --from-url https://example.com/some.zip --format json -o /tmp/out.json # non-3GPP URL → in-memory only
297
297
  doc3gpp tdoc parse --from-path ./tdocs --output ./parsed --recursive --format json # local batch
298
298
  doc3gpp tdoc parse --from-url https://www.3gpp.org/ftp/.../Docs/ --recursive --output ./parsed # online batch
299
+ # 3GPP URLs also accept bare www.3gpp.org/ftp/...; HTTPS is required,
300
+ # and .docx/.zip basenames must contain a TDoc id (including RP-... plenary ids).
299
301
 
300
302
  # show — --tdoc and --ftp-url are mutually exclusive
301
303
  doc3gpp tdoc show --tdoc R5s260009 --format json -o r5s260009.json
@@ -366,7 +368,7 @@ values on every version row.
366
368
 
367
369
  ```bash
368
370
  # use newest version with a published .zip, fetch-if-missing, convert every .docx, chunk, and auto-index
369
- doc3gpp spec doc parse --spec 38.331 --spec 38.523-1
371
+ doc3gpp spec doc parse --spec 38.331 --release Rel-18 --version 18.5.0
370
372
  doc3gpp spec doc parse --spec 38.331 --force # re-download and re-parse
371
373
 
372
374
  # inspect the stored TOC and search chunk text/combined metadata
@@ -377,10 +379,10 @@ doc3gpp spec doc index --rebuild
377
379
  doc3gpp spec doc schema --format json
378
380
  ```
379
381
 
380
- Background `parse_spec_docs` jobs retain per-spec failure reasons in the job
381
- result (`failure_details`) and log. A batch where every spec fails has status
382
- `failed`; a partial success retains status `succeeded` with the failed specs
383
- listed in the result.
382
+ Background `parse_spec_docs` jobs retain the selected spec's failure reason in
383
+ the job result (`failure_details`) and log. Each job targets one spec; when
384
+ both release and version are supplied, their release numbers must align (for
385
+ example, `Rel-18` with `18.5.0`).
384
386
 
385
387
  Spec-document rows live in a separate sibling database, normally
386
388
  `<main-stem>_specdata.db`, and are cached below the dedicated
@@ -213,6 +213,8 @@ doc3gpp tdoc parse --from-url https://www.3gpp.org/ftp/.../R5s260009.zip # 3GPP
213
213
  doc3gpp tdoc parse --from-url https://example.com/some.zip --format json -o /tmp/out.json # non-3GPP URL → in-memory only
214
214
  doc3gpp tdoc parse --from-path ./tdocs --output ./parsed --recursive --format json # local batch
215
215
  doc3gpp tdoc parse --from-url https://www.3gpp.org/ftp/.../Docs/ --recursive --output ./parsed # online batch
216
+ # 3GPP URLs also accept bare www.3gpp.org/ftp/...; HTTPS is required,
217
+ # and .docx/.zip basenames must contain a TDoc id (including RP-... plenary ids).
216
218
 
217
219
  # show — --tdoc and --ftp-url are mutually exclusive
218
220
  doc3gpp tdoc show --tdoc R5s260009 --format json -o r5s260009.json
@@ -283,7 +285,7 @@ values on every version row.
283
285
 
284
286
  ```bash
285
287
  # use newest version with a published .zip, fetch-if-missing, convert every .docx, chunk, and auto-index
286
- doc3gpp spec doc parse --spec 38.331 --spec 38.523-1
288
+ doc3gpp spec doc parse --spec 38.331 --release Rel-18 --version 18.5.0
287
289
  doc3gpp spec doc parse --spec 38.331 --force # re-download and re-parse
288
290
 
289
291
  # inspect the stored TOC and search chunk text/combined metadata
@@ -294,10 +296,10 @@ doc3gpp spec doc index --rebuild
294
296
  doc3gpp spec doc schema --format json
295
297
  ```
296
298
 
297
- Background `parse_spec_docs` jobs retain per-spec failure reasons in the job
298
- result (`failure_details`) and log. A batch where every spec fails has status
299
- `failed`; a partial success retains status `succeeded` with the failed specs
300
- listed in the result.
299
+ Background `parse_spec_docs` jobs retain the selected spec's failure reason in
300
+ the job result (`failure_details`) and log. Each job targets one spec; when
301
+ both release and version are supplied, their release numbers must align (for
302
+ example, `Rel-18` with `18.5.0`).
301
303
 
302
304
  Spec-document rows live in a separate sibling database, normally
303
305
  `<main-stem>_specdata.db`, and are cached below the dedicated
@@ -622,12 +622,15 @@ and syncs each through the `--tsg` path below.
622
622
 
623
623
  ### Spec-document corpus (parse → TOC → search)
624
624
 
625
- 1. `doc3gpp spec doc parse --spec <id>... [--release R] [--version V]
626
- [--force]` calls `SpecDocService.parse_many`. The version resolves via
625
+ 1. `doc3gpp spec doc parse --spec <id> [--release R] [--version V]
626
+ [--force]` accepts exactly one spec and calls `SpecDocService.parse_many`
627
+ with a one-item list. The version resolves via
627
628
  `resolve_spec_doc_version` — for automatic selection, filters to rows with
628
629
  a non-empty `.zip` URL, then applies numeric sort on `SpecVersion.version`
629
630
  (segment-wise ints, non-numeric → 0); optional `release` filters before
630
- selection. An explicit `version` is strict and raises
631
+ selection. When both `release` and `version` are supplied, their release
632
+ numbers must agree (for example, `Rel-18` with `18.5.0`). An explicit
633
+ `version` is strict and raises
631
634
  `SpecDocUnknownVersionError` if its ZIP link is missing. A miss raises
632
635
  `SpecDocUnknownSpecError` (no
633
636
  `spec_versions` rows — run `spec sync --spec-id` first) or
@@ -1093,8 +1096,8 @@ the `spec doc` / `toc` / `search` triplet, 40 commands) plus the `server` group
1093
1096
  - `sync` — `--tsg`
1094
1097
  - `list` — filters by `--tsg`, `--name`, `--acronym`, `--release`
1095
1098
  - `spec doc` (nested under `spec` as `spec doc ...`):
1096
- - `parse` — repeatable `--spec` (at least one required), `--release`,
1097
- `--version`, `--force/-f`; resolves the numeric-newest or pinned
1099
+ - `parse` — exactly one `--spec` (required), `--release`, `--version`,
1100
+ `--force/-f`; resolves the numeric-newest or pinned
1098
1101
  version, fetches the ZIP when missing, and records the download;
1099
1102
  `--force` re-downloads and re-parses, including already-parsed pairs.
1100
1103
  Also accepts `--format/--output/--compact` (currently ignored); prints
@@ -1269,18 +1269,23 @@ Options:
1269
1269
  detected: a file is parsed as a single source, a directory is
1270
1270
  processed as a batch. The cache and database are never touched for
1271
1271
  local files.
1272
- - `--from-url URL`: download the URL (HTTP or HTTPS) and parse it.
1273
- When the URL is on the canonical 3GPP FTP root
1274
- (`https://www.3gpp.org/ftp/...`) the result is cached on disk and
1275
- written to `tdoc_extracts` + `tdoc_cr_cover_page` (and to
1272
+ - `--from-url URL`: download the URL and parse it. For the canonical
1273
+ 3GPP FTP site, only HTTPS URLs under
1274
+ `https://www.3gpp.org/ftp/...` are accepted; the bare form
1275
+ `www.3gpp.org/ftp/...` is also accepted and normalized to HTTPS,
1276
+ while an explicit `http://www.3gpp.org/ftp/...` URL is rejected.
1277
+ A canonical `.docx` or `.zip` file must have a TDoc-shaped basename
1278
+ (including plenary ids such as `RP-2601234`); a spec archive such as
1279
+ `36508-j31.zip` is rejected. Every other canonical 3GPP path is
1280
+ treated as a folder, and its trailing slash is optional. Canonical
1281
+ single files are parsed directly; folders are processed as batches.
1282
+ Canonical 3GPP results are cached on disk and written to
1283
+ `tdoc_extracts` + `tdoc_cr_cover_page` (and to
1276
1284
  `tdoc_cr_ttcn_details` when the parser produced a TTCN sidecar —
1277
- subject to the FK matrix below); any other URL is parsed
1278
- in-memory only. Schemes other than `http(s)` (e.g. `ftp://`,
1279
- `file://`) are rejected — download out of band and use
1280
- `--from-path` instead. A URL ending in `/` is treated as a 3GPP
1281
- FTP folder and processed as a batch; URLs ending in `.docx`/`.zip`
1282
- are treated as single files;
1283
- ambiguous URLs are probed once and routed accordingly.
1285
+ subject to the FK matrix below). Other HTTP(S) URLs retain the
1286
+ existing in-memory parsing behavior. Schemes other than `http(s)`
1287
+ (e.g. `ftp://`, `file://`) are rejected — download out of band and
1288
+ use `--from-path` instead.
1284
1289
  - `--format {table,markdown,json,raw}`: output format. Default
1285
1290
  `table` (tab-separated header + single data row, matching
1286
1291
  `tdoc list --format table`). `markdown` writes a single-row GFM
@@ -1379,7 +1384,7 @@ FK-aware behaviour matrix (3GPP URL):
1379
1384
  |---|---|---|---|---|---|---|
1380
1385
  | `tdoc_id ∈ tdocs` (extracted from filename) | yes | yes | yes (skipped when `--format raw`) | yes — only when the parser emitted a TTCN sidecar (i.e. `is_ttcn_tdoc(tdoc_id)` and the document contained a TTCN overview / corrections section) | always | no |
1381
1386
  | `tdoc_id ∉ tdocs` (extracted but no FK target) | no | no | no | no | always | yes — actionable `meeting sync --tsg R5` recipe |
1382
- | No `tdoc_id` pattern in filename | no | no | no | no | always | yes — pattern-miss notice |
1387
+ | Canonical `.docx`/`.zip` basename has no `tdoc_id` pattern | no | no | no | no | no — rejected at the URL boundary | yes — validation error mentioning TDoc |
1383
1388
 
1384
1389
  Local files and non-3GPP URLs always emit output and never touch
1385
1390
  the cache or the database.
@@ -1390,13 +1395,14 @@ the URL **before** dispatching to the parse helpers, then fires
1390
1395
  `trigger_auto_sync(...)` with the candidate set:
1391
1396
 
1392
1397
  - **3GPP FTP file URL** (`.docx` / `.zip` suffix) → one candidate parsed
1393
- out of the basename via `extract_tdoc_id_from_filename()`. Empty when
1394
- the basename has no recognised `tdoc_id` pattern.
1395
- - **3GPP FTP folder URL** (trailing `/`) → BFS via
1398
+ out of the TDoc-shaped basename via
1399
+ `extract_tdoc_id_from_filename()`.
1400
+ - **3GPP FTP folder URL** (trailing `/` optional) → BFS via
1396
1401
  `TDocCrService.collect_3gpp_file_urls()` (up to `--max-depth` /
1397
1402
  `--recursive`), then extract `tdoc_id`s per file. Empty when BFS
1398
1403
  fails (warning only — the parse still proceeds).
1399
- - **3GPP URL of unknown shape** → best-effort basename extraction.
1404
+ - **Other canonical 3GPP paths** → treated as folders, including paths
1405
+ without a trailing `/`.
1400
1406
  - **Non-3GPP URL** → empty. The CLI gates the call on
1401
1407
  `is_3gpp_ftp_url()` so non-3GPP URLs never trigger the candidate
1402
1408
  helper.
@@ -2415,12 +2421,12 @@ strict and fails clearly if that version has no ZIP link. Run
2415
2421
 
2416
2422
  Purpose:
2417
2423
 
2418
- - Fetch-if-missing, convert, chunk, and index one or more specs; prints one
2419
- `ok` / `skipped <reason>` / `failed <reason>` (stderr) line per spec.
2424
+ - Fetch-if-missing, convert, chunk, and index one spec; prints one `ok`,
2425
+ `skipped <reason>`, or `failed <reason>` (stderr) line.
2420
2426
 
2421
2427
  Options:
2422
2428
 
2423
- - --spec: spec id to parse; repeat per spec (at least one required).
2429
+ - --spec: spec id to parse; exactly one is required.
2424
2430
  - --release: release marker, e.g. `Rel-18`. Optional pin.
2425
2431
  - --version: exact version, e.g. `18.5.0`. Optional pin.
2426
2432
  - --force, -f: re-download and re-parse the resolved version, including
@@ -2439,11 +2445,12 @@ Behavior:
2439
2445
  `spec_doc_sources.downloaded_at` before conversion.
2440
2446
  - `--force` re-downloads the resolved ZIP and then re-parses it, including when
2441
2447
  the `(spec_id, version)` pair already has `parsed_at`.
2442
- - Per spec: immutable-skip when the `(spec_id, version)` source row already
2443
- carries `parsed_at` (unless `--force`); oversized zips land in the `skipped`
2444
- bucket; unknown spec/version, empty (no-`.docx`) zips, and every other error
2445
- land in the `failures` bucket. One spec never aborts the batch
2446
- (`SpecDocBatchResult(successes/skipped/failures)`).
2448
+ - The `(spec_id, version)` source row is skipped when it already carries
2449
+ `parsed_at` (unless `--force`); oversized zips land in the `skipped` bucket;
2450
+ unknown spec/version, empty (no-`.docx`) zips, and every other error land in
2451
+ the `failures` bucket. When both `--release` and `--version` are supplied,
2452
+ their release numbers must agree (for example, `Rel-18` requires a version
2453
+ beginning with `18.`).
2447
2454
  - `parse` = fetch-if-missing + `list_spec_docx` (`.doc` entries warn-skip) +
2448
2455
  `convert_document_to_blocks` + `order_spec_files` (front-matter TOC file first,
2449
2456
  then section-tuple sort, TOC tiebreak, unnumbered lexicographic tail) +
@@ -2468,8 +2475,8 @@ Examples:
2468
2475
  # Parse the newest version of 38.331 with a published ZIP download link.
2469
2476
  doc3gpp spec doc parse --spec 38.331
2470
2477
 
2471
- # Parse two specs at a pinned release.
2472
- doc3gpp spec doc parse --spec 38.331 --spec 38.523-1 --release Rel-18
2478
+ # Parse one spec at a pinned release and version.
2479
+ doc3gpp spec doc parse --spec 38.331 --release Rel-18 --version 18.5.0
2473
2480
 
2474
2481
  # Re-parse even when already parsed.
2475
2482
  doc3gpp spec doc parse --spec 38.331 --force
@@ -115,7 +115,7 @@ table below is for navigation only.
115
115
  | `list_spec_docx` / `order_spec_files` / `extract_spec_toc` | functions | `parsers/spec_doc.py` | List `.docx` zip members, order split spec files by first section/TOC rules while preserving alphanumeric identifiers such as `7.2A.3A`, and build the ordered TOC/file map. |
116
116
  | `chunk_blocks` | function | `parsers/spec_doc_chunker.py` | Pure paragraph/sentence/table-row chunker using semantic token budgets plus the spec-doc character ceiling; carries newline-delimited combined `sections` / `tables` and source-file metadata. |
117
117
  | `parse_cr_details` | function | `parsers/cr_parser.py` | Markdown → `TDocCRDetails` (cover-page, optional TTCN overview, optional corrections). |
118
- | `is_3gpp_ftp_url` / `direct_parse_bytes` / `derive_zip_cache_key` / `extract_tdoc_id_from_filename` | functions | `parsers/direct_extractor.py` | Helpers for the `tdoc parse --from-path/--from-url` direct path. `is_3gpp_ftp_url` is the 3GPP-FTP detection rule; `direct_parse_bytes` glues docx conversion + cover-page parsing. |
118
+ | `is_3gpp_ftp_url` / `normalize_3gpp_ftp_url` / `validate_tdoc_ftp_url` / `direct_parse_bytes` / `derive_zip_cache_key` / `extract_tdoc_id_from_filename` | functions | `parsers/direct_extractor.py` | Helpers for the `tdoc parse --from-path/--from-url` direct path. The URL normalizer/validator is the shared HTTPS canonical-site, file-vs-folder, and TDoc-basename policy; `direct_parse_bytes` glues docx conversion + cover-page parsing. |
119
119
  | `TDocParser` Protocol / `TDocParserRegistry` / `build_default_registry` | Protocol / class / function | `parsers/tdoc_parsers.py` | `TDocParser.parse()` returns a `TDocCRParseResult` (cover + optional TTCN sidecar). `build_default_registry` registers `TTCNCRParser` before the generic `CRParser` so TTCN CRs route to the overview + corrections parser and everything else falls through. |
120
120
  | `CRParserBase` / `CRParser` / `TTCNCRParser` | classes | `parsers/cr/cr_parsers.py` | Concrete parser implementations. `CRParserBase.parse()` returns `TDocCRParseResult`; `TTCNCRParser` populates the sidecar from the TTCN overview + corrections section payloads; `CRParser` always emits `ttcn=None`. |
121
121
  | `ttcn_functions` module | module | `parsers/cr/ttcn_functions.py` | Regex-driven helpers for the `tdoc_cr_ttcn_details.changed_functions` aggregate: `extract_module_basename` (handles Unix paths, Windows paths, bare basenames), `extract_function_name` (matches `f_`/`fl_`/`fx_`/`a_`/`tsc_`/`cs_`/`cr_`/`crs_`/`cas_`/`car_`/`cds_`/`cdr_`/`cms_`/`cmr_`/`cads_` prefixes with a `*_type` fallback), and the thin aggregator `extract_changed_functions` that returns a sorted, deduplicated `list[str]` of `"<module_basename>.<function_name>"` entries derived from `required_changes`. Partial-extraction markers (`"<module>."` trailing-dot, `".<function>"` leading-dot) cover the cases where one regex returns `None`; entries where both regexes return `None` are dropped. Consumed by `CRParserBase.parse()` when building the `TDocCRTTCNDetails` sidecar. |
@@ -206,17 +206,16 @@ the target is missing or not `X-Doc3gpp-Managed` by doc3gpp.
206
206
  | POST | `/jobs/tdocs/index` | Enqueue unified TDoc FTS5/vector index maintenance. |
207
207
  | POST | `/jobs/spec-docs/index` | Enqueue unified spec-doc FTS5/vector index maintenance. |
208
208
  | POST | `/jobs/cache/purge` | Enqueue `cache_purge` (requires `yes: true`). |
209
- | POST | `/jobs/parse/tdoc-url` | Enqueue `parse_tdoc_url` (a single 3GPP FTP URL or folder; `url` must be `https://www.3gpp.org/ftp/...`, `recursive` XOR `max_depth`). |
210
- | POST | `/jobs/parse/spec-docs` | Enqueue `parse_spec_docs` for one or more spec ids (`spec_ids`, optional `release`, `version`, and `force`); progress is streamed through the normal job SSE endpoint. This job is intentionally not a panel on the eleven-form `/sync` hub. |
209
+ | POST | `/jobs/parse/tdoc-url` | Enqueue `parse_tdoc_url` (a single TDoc file or folder on the canonical 3GPP FTP site; accepts HTTPS `https://www.3gpp.org/ftp/...` or bare `www.3gpp.org/ftp/...`, normalizes the latter, rejects explicit HTTP and non-TDoc spec archives, and permits an optional folder trailing slash; `recursive` XOR `max_depth`). |
210
+ | POST | `/jobs/parse/spec-docs` | Enqueue `parse_spec_docs` for one spec id (`spec_id`, optional `release`, `version`, and `force`); progress is streamed through the normal job SSE endpoint. This job is intentionally not a panel on the eleven-form `/sync` hub. |
211
211
  | POST | `/jobs/{id}/cancel` | Cancel a queued/running job. Accepts `?format=html` to return the refreshed job row as an `outerHTML` swap target for the list page's per-row Cancel button. JSON otherwise. |
212
212
  | POST | `/jobs/sync_tdocs` | Flat alias for `sync_tdocs` (form or JSON). |
213
213
  | GET | `/sync` | Sync hub page: eleven enqueue forms (meetings, tdocs, all-tdocs, specs-by-tsg, specs-by-id, parse-tdocs, parse-tdoc-url, TDoc index maintenance, spec-document index maintenance, cache-purge, testcases) + a "Recent sync jobs" table. |
214
214
  | GET | `/sync?format=fragment` | Recent-jobs table fragment (wrapped in `<div id="recent-jobs">`) for HTMX `outerHTML` swap. |
215
215
 
216
- `parse_spec_docs` records per-spec errors in `result.failure_details` and the
217
- job log. If every requested spec fails, the job is `failed` with a populated
218
- `error` and result; partial successes remain `succeeded` with failure details
219
- in the result.
216
+ `parse_spec_docs` records the spec error in `result.failure_details` and the
217
+ job log. A failed parse job has a populated `error` and result; a successful or
218
+ skipped parse reports its corresponding one-spec result.
220
219
 
221
220
  Append `?format=json` to any list/detail route to get the CLI-equivalent
222
221
  JSON. Append `?format=html` (or omit) for the browsable HTML view.
@@ -279,8 +278,8 @@ specdata source row has not been downloaded or parsed; a downloaded source is
279
278
  waiting for parsing; and a parsed source shows its parse timestamp, DOCX and
280
279
  chunk counts. The only document action is `Parse document` (or `Force
281
280
  re-parse`, available only for a parsed source. It enqueues the existing
282
- `POST /jobs/parse/spec-docs` job with `spec_ids`, the selected exact `version`,
283
- and `force`, then
281
+ `POST /jobs/parse/spec-docs` job with scalar `spec_id`, the selected exact
282
+ `version`, and `force`, then
284
283
  polls the normal job status. The parser downloads a missing ZIP internally;
285
284
  there is intentionally no standalone spec-document fetch route or fetch
286
285
  control.
@@ -530,8 +529,8 @@ The spec-document tools use the separate specdata database. `get_spec_toc`
530
529
  reads one parsed `(spec_id, version)` TOC, `search_spec_docs` selects among
531
530
  FTS5, vector, hybrid, and filter-only chunk search, and `get_spec_doc_index`
532
531
  reports index status. `parse_spec_docs` enqueues the same
533
- `PARSE_SPEC_DOCS` job as `POST /jobs/parse/spec-docs`; it accepts
534
- `spec_ids`, optional `release`/`version`, and `force`.
532
+ `PARSE_SPEC_DOCS` job as `POST /jobs/parse/spec-docs`; it accepts one
533
+ `spec_id`, optional `release`/`version`, and `force`.
535
534
 
536
535
  The MCP `serverInfo` block returned on every `initialize` handshake carries `name` ("doc3gpp"), `version` (from `importlib.metadata.version("doc3gpp")`, falling back to `doc3gpp.__version__`), `title`, `description`, and `website_url`. Clients do not need a tool call to read the version.
537
536
 
@@ -7,7 +7,7 @@ allow-direct-references = true
7
7
 
8
8
  [project]
9
9
  name = "doc3gpp"
10
- version = "0.3.2"
10
+ version = "0.3.4"
11
11
  description = "Provide 3GPP TDoc information with full text search capability — a Python CLI/library, a local web UI, and an MCP server for AI clients."
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.10"
@@ -2,4 +2,4 @@
2
2
 
3
3
  __all__ = ["__version__"]
4
4
 
5
- __version__ = "0.3.0"
5
+ __version__ = "0.3.4"
@@ -30,13 +30,15 @@ from doc3gpp.cli_server import server_app
30
30
  from doc3gpp.cli_url_helpers import (
31
31
  _looks_like_3gpp_file_url,
32
32
  _looks_like_3gpp_folder_url,
33
+ is_3gpp_ftp_candidate_url,
33
34
  is_3gpp_ftp_url,
35
+ validate_tdoc_ftp_url,
34
36
  )
35
37
  from doc3gpp.config import get_settings
36
38
  from doc3gpp.models.meeting import Meeting
37
39
  from doc3gpp.models.schema_info import SCHEMA_FIELDS, schema_payload
38
40
  from doc3gpp.models.spec import Spec, SpecVersion
39
- from doc3gpp.models.spec_doc import SpecDocSearchFilters
41
+ from doc3gpp.models.spec_doc import SpecDocSearchFilters, SpecDocSelectionError
40
42
  from doc3gpp.models.sync import BulkSyncOutcome, SyncOutcome
41
43
  from doc3gpp.models.tdoc import TDoc, TDocWithMeeting
42
44
  from doc3gpp.models.tdoc_cr import (
@@ -55,6 +57,7 @@ from doc3gpp.parsers.docx_converter import PythonDocxNotInstalledError
55
57
  from doc3gpp.parsers.normalizers import normalize_ftp_path
56
58
  from doc3gpp.scraping.cache import CacheStatus, TDocCache
57
59
  from doc3gpp.scraping.cache_keys import derive_cache_file
60
+ from doc3gpp.scraping.spec_doc_source import validate_release_version_alignment
58
61
  from doc3gpp.scraping.tdoc_zip_source import canonicalise_tdoc_id
59
62
  from doc3gpp.services.factory import (
60
63
  build_meeting_service,
@@ -2237,6 +2240,11 @@ def tdoc_parse(
2237
2240
  f"--from-path is neither a file nor a directory: {from_path}"
2238
2241
  )
2239
2242
  else:
2243
+ if is_3gpp_ftp_candidate_url(from_url):
2244
+ try:
2245
+ from_url = validate_tdoc_ftp_url(from_url)
2246
+ except ValueError as exc:
2247
+ raise typer.BadParameter(str(exc)) from exc
2240
2248
  effective_depth = _resolve_url_batch_depth(
2241
2249
  recursive=recursive,
2242
2250
  max_depth=max_depth,
@@ -5132,7 +5140,7 @@ def spec_doc_parse(
5132
5140
  spec: list[str] | None = typer.Option( # noqa: B008 - Typer option declaration
5133
5141
  None,
5134
5142
  "--spec",
5135
- help="Spec id to parse; repeat per spec (at least one required).",
5143
+ help="Spec id to parse (exactly one required).",
5136
5144
  ),
5137
5145
  release: str | None = typer.Option(
5138
5146
  None, "--release", help="Release marker, e.g. Rel-18.",
@@ -5160,10 +5168,16 @@ def spec_doc_parse(
5160
5168
  help="Strip output formatting: JSON drops indent and operator-space. No-op for ``table``.",
5161
5169
  ),
5162
5170
  ) -> None:
5163
- """Batch form: --spec may repeat; success/skip/failure buckets printed."""
5171
+ """Parse one spec; print its success/skip/failure result."""
5164
5172
  create_schema("all")
5165
5173
  if not spec:
5166
- raise typer.BadParameter("pass at least one --spec <id>")
5174
+ raise typer.BadParameter("pass exactly one --spec <id>")
5175
+ if len(spec) != 1:
5176
+ raise typer.BadParameter("--spec may be provided only once")
5177
+ try:
5178
+ validate_release_version_alignment(release, version)
5179
+ except SpecDocSelectionError as exc:
5180
+ raise typer.BadParameter(str(exc)) from exc
5167
5181
  svc = build_spec_doc_service()
5168
5182
  result = svc.parse_many(spec, release=release, version=version, force=force)
5169
5183
  for sid in result.successes:
@@ -19,8 +19,8 @@ from typing import TYPE_CHECKING
19
19
  from doc3gpp.cli_filters import parse_tdoc_id
20
20
  from doc3gpp.cli_url_helpers import (
21
21
  _looks_like_3gpp_file_url,
22
- _looks_like_3gpp_folder_url,
23
- is_3gpp_ftp_url,
22
+ is_3gpp_ftp_candidate_url,
23
+ validate_tdoc_ftp_url,
24
24
  )
25
25
  from doc3gpp.models.meeting import Meeting
26
26
  from doc3gpp.parsers.direct_extractor import (
@@ -38,7 +38,7 @@ if TYPE_CHECKING:
38
38
 
39
39
  logger = logging.getLogger(__name__)
40
40
 
41
- _TSG_PREFIX_RE = re.compile(r"^([RSC][1-9])", re.IGNORECASE)
41
+ _TSG_PREFIX_RE = re.compile(r"^([RSC][1-9P])", re.IGNORECASE)
42
42
 
43
43
 
44
44
  def _build_meeting_url(tsg: str, ext: str = "htm") -> str:
@@ -176,7 +176,7 @@ def sync_meeting_internal(
176
176
  def collect_tdoc_candidates_for_url(
177
177
  url: str,
178
178
  *,
179
- tdoc_service: "TDocCrService | None" = None,
179
+ tdoc_service: TDocCrService | None = None,
180
180
  max_depth: int = 0,
181
181
  ) -> set[str]:
182
182
  """Return distinct tdoc_ids implied by ``url``.
@@ -188,12 +188,12 @@ def collect_tdoc_candidates_for_url(
188
188
  - 3GPP FTP file URL (ends with ``.docx`` / ``.zip``) → the tdoc_id
189
189
  parsed out of the basename, or empty if the basename has no
190
190
  recognised TDoc-id pattern.
191
- - 3GPP FTP folder URL (ends with ``/``) → BFS over the folder up to
192
- ``max_depth`` via the supplied service and extract tdoc_ids from
193
- each file's basename. Returns empty when ``tdoc_service`` is
191
+ - 3GPP FTP folder URL (trailing ``/`` optional) → BFS over the folder
192
+ up to ``max_depth`` via the supplied service and extract tdoc_ids
193
+ from each file's basename. Returns empty when ``tdoc_service`` is
194
194
  ``None`` (the helper is a pure no-op in that case).
195
- - 3GPP URL of unknown shape → try basename extraction; empty if it
196
- doesn't parse.
195
+ - Any other canonical 3GPP FTP path → treat it as a folder and scan
196
+ breadth-first using the supplied service.
197
197
 
198
198
  Network or BFS failures are logged at ``WARNING`` and the helper
199
199
  returns whatever candidates were already collected — it never raises
@@ -215,7 +215,12 @@ def collect_tdoc_candidates_for_url(
215
215
  failure mode.
216
216
  """
217
217
  candidates: set[str] = set()
218
- if not is_3gpp_ftp_url(url):
218
+ if not is_3gpp_ftp_candidate_url(url):
219
+ return candidates
220
+ try:
221
+ url = validate_tdoc_ftp_url(url)
222
+ except ValueError as exc:
223
+ logger.warning("Auto-sync candidate collection rejected %s: %s", url, exc)
219
224
  return candidates
220
225
 
221
226
  if _looks_like_3gpp_file_url(url):
@@ -224,18 +229,12 @@ def collect_tdoc_candidates_for_url(
224
229
  candidates.add(extracted)
225
230
  return candidates
226
231
 
227
- if _looks_like_3gpp_folder_url(url):
228
- return _collect_from_3gpp_folder(url, tdoc_service, max_depth)
229
-
230
- extracted = extract_tdoc_id_from_filename(url)
231
- if extracted is not None:
232
- candidates.add(extracted)
233
- return candidates
232
+ return _collect_from_3gpp_folder(url, tdoc_service, max_depth)
234
233
 
235
234
 
236
235
  def _collect_from_3gpp_folder(
237
236
  url: str,
238
- tdoc_service: "TDocCrService | None",
237
+ tdoc_service: TDocCrService | None,
239
238
  max_depth: int,
240
239
  ) -> set[str]:
241
240
  """BFS-collect tdoc_ids from a 3GPP FTP folder URL; empty on any failure."""
@@ -67,13 +67,13 @@ DATE_FILTER_RE = re.compile(
67
67
  )
68
68
 
69
69
 
70
- # CR-shape TDoc identifier: ``[RSC][1-9][-sw]\d{6,7}`` — TSG group
71
- # initial (R/S/C), non-zero TSG digit, shape marker (- / s / w for
70
+ # CR-shape TDoc identifier: ``[RSC][1-9P][-sw]\d{6,7}`` — TSG group
71
+ # initial (R/S/C), non-zero TSG digit or plenary marker P, shape marker (- / s / w for
72
72
  # canonical / TTCN / workshop), 6- or 7-digit sequence number.
73
73
  # 3GPP RAN4 has used 7-digit numbers since 2016 (e.g. ``R4-2607922``);
74
74
  # every other working group is still on 6 digits. Do not narrow without
75
75
  # re-checking the RAN4 DynaReport.
76
- TDOC_ID_RE = re.compile(r"[RSC][1-9][-sw]\d{6,7}", re.IGNORECASE)
76
+ TDOC_ID_RE = re.compile(r"[RSC][1-9P][-sw]\d{6,7}", re.IGNORECASE)
77
77
 
78
78
 
79
79
  def is_null_token(value: str) -> bool:
@@ -142,8 +142,9 @@ def parse_tdoc_id(value: str) -> tuple[str, int]:
142
142
  raise ValueError(
143
143
  f"Invalid TDoc id {value!r}. Expected a 9- or 10-character "
144
144
  f"CR-shape id like 'R5-260013', 'R5s260009', 'R5w260013', or "
145
- f"'R4-2607922' — TSG group initial (R/S/C), non-zero TSG digit, "
146
- f"shape marker (-/s/w), then 6 or 7 decimal digits."
145
+ f"'R4-2607922' or 'RP-2601234' — TSG group initial (R/S/C), "
146
+ f"non-zero TSG digit or plenary marker P, shape marker "
147
+ f"(-/s/w), then 6 or 7 decimal digits."
147
148
  )
148
149
  return stripped[:3], int(stripped[3:])
149
150
 
@@ -360,4 +361,4 @@ class SearchQueryBuilder:
360
361
  out.append("\\" + ch)
361
362
  else:
362
363
  out.append(ch)
363
- return "".join(out)
364
+ return "".join(out)
@@ -2,8 +2,8 @@
2
2
 
3
3
  The CLI's ``--from-url`` direct-parse path needs a tiny set of
4
4
  predicate functions to decide whether a URL points at a 3GPP FTP
5
- asset, whether it ends in a known file extension, and whether it
6
- trails with a slash (a folder marker). The same predicates are
5
+ asset, whether it ends in a known file extension, and whether it is a
6
+ folder path (with or without a trailing slash). The same predicates are
7
7
  needed from :mod:`doc3gpp.cli_auto_sync` so the auto-sync helper can
8
8
  derive TDoc-id candidates from a URL without dragging the full
9
9
  parser module into its import surface.
@@ -16,26 +16,36 @@ import one consistent set of names:
16
16
  parser implementation stays in one place.
17
17
  * :func:`_looks_like_3gpp_file_url` — true when the URL ends with a
18
18
  known document extension.
19
- * :func:`_looks_like_3gpp_folder_url` — true when the URL ends with
20
- a slash (folder marker).
19
+ * :func:`_looks_like_3gpp_folder_url` — true for any other canonical
20
+ 3GPP FTP path.
21
21
  """
22
22
 
23
23
  from __future__ import annotations
24
24
 
25
- from doc3gpp.parsers.direct_extractor import is_3gpp_ftp_url
25
+ from doc3gpp.parsers.direct_extractor import (
26
+ is_3gpp_file_url,
27
+ is_3gpp_ftp_candidate_url,
28
+ is_3gpp_ftp_url,
29
+ normalize_3gpp_ftp_url,
30
+ validate_tdoc_ftp_url,
31
+ )
26
32
 
27
33
  __all__ = [
28
34
  "_looks_like_3gpp_file_url",
29
35
  "_looks_like_3gpp_folder_url",
36
+ "is_3gpp_file_url",
37
+ "is_3gpp_ftp_candidate_url",
30
38
  "is_3gpp_ftp_url",
39
+ "normalize_3gpp_ftp_url",
40
+ "validate_tdoc_ftp_url",
31
41
  ]
32
42
 
33
43
 
34
44
  def _looks_like_3gpp_folder_url(url: str) -> bool:
35
- """Return ``True`` when ``url`` is unambiguously a folder path."""
36
- return url.endswith("/")
45
+ """Return ``True`` for a canonical non-file FTP path."""
46
+ return is_3gpp_ftp_url(url) and not is_3gpp_file_url(url)
37
47
 
38
48
 
39
49
  def _looks_like_3gpp_file_url(url: str) -> bool:
40
- """Return ``True`` when ``url`` ends with a known file extension."""
41
- return url.lower().endswith((".docx", ".zip"))
50
+ """Return ``True`` when a canonical URL ends with a known extension."""
51
+ return is_3gpp_file_url(url)
@@ -20,7 +20,7 @@ Read tools return a single text item containing JSON (except `get_tdoc_content`,
20
20
  | Meeting calendar | `list_meetings` with `tsg`, `name`, or `year`; `get_meeting` with numeric `meeting_id` | `sync_meetings({"tsg":"R5"})` fetches and parses that TSG's calendar | Re-list, then use `get_meeting` to obtain its meeting ID and FTP URL. There is no separate meeting-parse tool. |
21
21
  | Meeting TDocs | `list_tdocs` with `meeting_id`, `tdoc_id`, `title`, `spec`, etc.; `get_tdoc` with `tdoc_id` or `ftp_url` | `sync_tdocs({"meeting_id":260013})` after the meeting exists; alternatively `sync_tdocs_by_meeting({"meeting":"RAN5#106"})`. For document extraction, `parse_tdocs({"filter":{"tdoc_id":"R5-260013"}})`; for a direct 3GPP FTP URL use `parse_tdoc_url({"url":"https://www.3gpp.org/ftp/..."})`. | `get_tdoc` for cover/extract metadata; `get_tdoc_content({"tdoc_id":"R5-260013"})` for cached markdown if available; `search_tdoc` for indexed content and filters. |
22
22
  | Specifications | `list_specs` with `tsg`, `spec_id`, `title`, or `parsed`; `get_spec` with dotted `spec_id` | `sync_specs({"tsg":"R5"})` or `sync_specs({"spec_id":"38.331"})` (exactly one selector). `force` bypasses the sync interval; `per_version_details` fetches extra PDF/CR-list details. | `get_spec` returns versions, release markers, ZIP URLs, and per-version `parsed` state. Paginate versions with `limit`/`offset` if needed. |
23
- | Specification documents | Start with `get_spec` to identify a version with a ZIP URL; `get_spec_toc` needs an already parsed exact `(spec_id, version)` | `parse_spec_docs({"spec_ids":["38.331"],"version":"18.5.0"})` downloads if needed and parses; omit `version` to resolve a suitable stored version (optionally constrain with `release`). `force` re-downloads and re-parses. | `get_spec_toc({"spec_id":"38.331","version":"18.5.0"})`; `search_spec_docs` with `spec_id`, `version`, `text` or `semantic`, `sections`, `tables`. Note '%' wildcards to match section / table titles to improve search. If search section numbers or table numbers, use '%' at the end to match sub-sections or sub-tables. e.g. `"section":"5.3.2%"` or `"table":"6.1.1.2.3-%"`. |
23
+ | Specification documents | Start with `get_spec` to identify a version with a ZIP URL; `get_spec_toc` needs an already parsed exact `(spec_id, version)` | `parse_spec_docs({"spec_id":"38.331","version":"18.5.0"})` downloads if needed and parses one spec; omit `version` to resolve a suitable stored version (optionally constrain with `release`). When both `release` and `version` are supplied, their release numbers must align, e.g. `Rel-18` with `18.5.0`. `force` re-downloads and re-parses. | `get_spec_toc({"spec_id":"38.331","version":"18.5.0"})`; `search_spec_docs` with `spec_id`, `version`, `text` or `semantic`, `sections`, `tables`. Note '%' wildcards to match section / table titles to improve search. If search section numbers or table numbers, use '%' at the end to match sub-sections or sub-tables. e.g. `"section":"5.3.2%"` or `"table":"6.1.1.2.3-%"`. |
24
24
  | RAN5 testcases | `list_testcases` with `testcase` (ID pattern), `title`, `spec`, `group`, `status` (TTCN), or `gcf_status` (GCF/PTCRB) | `sync_testcases({})` fetches and parses the latest RAN5 status snapshot in one job; `{"force":true}` re-downloads and re-parses a snapshot already recorded. No separate testcase parse tool. | `get_testcase({"testcase_id":"<id>"})` returns all stored groups; pass `group` for one group. Each result has nested `statuses` entries containing `path`, `gcf_ptcrb`, and `ttcn_status`. |
25
25
 
26
26
  Filters on list/search tools generally accept SQL-LIKE `%` wildcards (for example `title:"%handover%"`); plain values match exactly. Testcase `group` is an exact match: `5G`, `LTE`, `IMS`, `UTRA`, `POS`, or `MCX`. `list_meetings`, `list_tdocs`, `list_specs`, and `list_testcases` default to 50 rows: use `limit` and `offset` to page. `search_tdoc` and `search_spec_docs` select FTS5 with `text`, vector search with `semantic`, hybrid with both, and filter-only with neither. Semantic search requires its configured embedding service. If text search reports an unavailable or corrupt index, inspect `get_tdoc_index` or `get_spec_doc_index`; use `index_tdocs({"rebuild":true})` or `index_spec_docs({"rebuild":true})` when rebuilding is needed, then poll that job. Parse normally attempts automatic indexing, but indexing can fail independently.
@@ -44,7 +44,7 @@ search_tdoc {"text":"handover","meeting_id":<meeting_id>}
44
44
  sync_specs {"spec_id":"38.331"}
45
45
  get_job {"job_id":"<spec-sync job_id>"} # poll until terminal
46
46
  get_spec {"spec_id":"38.331"}
47
- parse_spec_docs {"spec_ids":["38.331"],"version":"<ZIP-backed version from get_spec>"}
47
+ parse_spec_docs {"spec_id":"38.331","version":"<ZIP-backed version from get_spec>"}
48
48
  get_job {"job_id":"<spec-doc-parse job_id>"} # poll until terminal
49
49
  get_spec_toc {"spec_id":"38.331","version":"<parsed version>"}
50
50
  search_spec_docs {"text":"handover","spec_id":"38.331","version":"<parsed version>", "sections":"5.2.2.3%"}
@@ -1,5 +1,6 @@
1
1
  """DTOs for the spec-document corpus (separate specdata sqlite file)."""
2
2
  from __future__ import annotations
3
+
3
4
  from dataclasses import dataclass, field
4
5
  from datetime import datetime
5
6
 
@@ -18,6 +19,10 @@ class SpecDocUnknownVersionError(SpecDocError):
18
19
  self.available = available or []
19
20
 
20
21
 
22
+ class SpecDocSelectionError(SpecDocError):
23
+ """Raised when a release/version selection is internally inconsistent."""
24
+
25
+
21
26
  class SpecDocNoDocxError(SpecDocError):
22
27
  pass
23
28
 
@@ -21,9 +21,10 @@ class CRHeaderMissingError(ValueError):
21
21
 
22
22
 
23
23
  # Union: ``R5-227476`` (dash) AND ``R5s260009`` / ``R5w260176`` (single
24
- # letter) AND ``C6-250028`` (dash) AND RAN4's 7-digit ``R4-2607922``.
24
+ # letter) AND ``C6-250028`` (dash), plenary ``RP-2601234`` IDs, and
25
+ # RAN4's 7-digit ``R4-2607922``.
25
26
  # Matches in the document header near the top of the markdown.
26
- _TDOC_HEADER_PATTERN = re.compile(r"([RSC][1-6](?:[-sw])\d{6,7})", re.IGNORECASE)
27
+ _TDOC_HEADER_PATTERN = re.compile(r"([RSC][1-6P](?:[-sw])\d{6,7})", re.IGNORECASE)
27
28
  # Email-meeting TTCN pattern. Overview / corrections are only parsed
28
29
  # for TDocs matching this shape.
29
30
  _TTCN_TDOC_PATTERN = re.compile(r"R5s\d{6}", re.IGNORECASE)