doc3gpp 0.3.2__tar.gz → 0.3.3__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.3}/AGENTS.md +2 -2
  2. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/PKG-INFO +6 -6
  3. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/README.md +5 -5
  4. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/architecture.md +8 -5
  5. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/cli.md +11 -10
  6. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/web-server.md +8 -9
  7. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/pyproject.toml +1 -1
  8. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/cli.py +11 -4
  9. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/data/skills/doc3gpp/SKILL.md +2 -2
  10. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/models/spec_doc.py +5 -0
  11. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/scraping/spec_doc_source.py +32 -1
  12. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/errors.py +4 -0
  13. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/mcp_server.py +12 -7
  14. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/routes/jobs.py +11 -5
  15. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/static/js/spec_doc_parse.js +1 -1
  16. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/workers/handlers.py +12 -8
  17. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_mcp_end_to_end.py +15 -0
  18. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_spec_doc_cli.py +61 -0
  19. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_spec_doc_web.py +22 -1
  20. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_job_worker.py +8 -17
  21. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_jobs_sql.py +2 -2
  22. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_spec_doc_source.py +7 -1
  23. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_web_routes.py +2 -1
  24. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/.env.example +0 -0
  25. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/.github/workflows/publish.yml +0 -0
  26. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/.gitignore +0 -0
  27. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/.superpowers/sdd/.gitignore +0 -0
  28. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/.superpowers/sdd/2026-09-24-spec-doc-metadata-and-portal-fix/final-fix-report.md +0 -0
  29. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/.superpowers/sdd/2026-09-24-spec-doc-metadata-and-portal-fix/task-2-report.md +0 -0
  30. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/.superpowers/sdd/2026-09-24-spec-doc-web-portal/final-fix-report.md +0 -0
  31. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/.superpowers/sdd/2026-09-24-spec-doc-web-portal/task-2-report.md +0 -0
  32. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/.superpowers/sdd/2026-09-25-spec-parsed-output/final-fix-report.md +0 -0
  33. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/.superpowers/sdd/2026-09-25-spec-parsed-output/task-3-report.md +0 -0
  34. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/.superpowers/sdd/2026-09-25-spec-parsed-output/task-4-report.md +0 -0
  35. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/.superpowers/sdd/2026-09-25-spec-parsed-output/task-7-report.md +0 -0
  36. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/LICENSE +0 -0
  37. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/doc3gpp.toml.example +0 -0
  38. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/3gpp-knowledge.md +0 -0
  39. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/TODO.md +0 -0
  40. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/code-map.md +0 -0
  41. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/conventions.md +0 -0
  42. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/known-constraints.md +0 -0
  43. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-07-28-compact-output.md +0 -0
  44. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-07-28-tdoc-size-limit.md +0 -0
  45. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-07-29-fts5-search.md +0 -0
  46. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-07-29-tdoc-cr-body-changes.md +0 -0
  47. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-07-30-fts5-perf.md +0 -0
  48. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-07-31-embedding-search.md +0 -0
  49. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-01-fts5-hit-rename.md +0 -0
  50. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-01-fts5-semantic-rerank.md +0 -0
  51. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-01-semantic-search-revision.md +0 -0
  52. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-02-all-extra.md +0 -0
  53. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-02-remove-mysql-postgres.md +0 -0
  54. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-02-web-and-mcp-server.md +0 -0
  55. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-03-meeting-sync-display.md +0 -0
  56. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-03-meetings-tdoc-filter.md +0 -0
  57. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-04-web-ui-enhancements.md +0 -0
  58. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-05-tdoc-columns-dropdown.md +0 -0
  59. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-05-web-parse-and-search.md +0 -0
  60. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-07-mcp-sse-transport.md +0 -0
  61. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-10-spec-sync-list-show.md +0 -0
  62. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-11-spec-rapporteurs-comment.md +0 -0
  63. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-11-spec-rapporteurs-filter.md +0 -0
  64. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-12-mcp-spec-sync.md +0 -0
  65. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-12-single-spec-sync.md +0 -0
  66. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-12-spec-show-version-limit.md +0 -0
  67. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-12-spec-sync-dynareport-fetch.md +0 -0
  68. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-13-idempotent-cancel.md +0 -0
  69. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-13-mcp-parse-tdoc-url.md +0 -0
  70. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-13-per-spec-skip-rule.md +0 -0
  71. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-13-spec-per-version-details-flag.md +0 -0
  72. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-13-web-job-detail-params.md +0 -0
  73. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-13-web-mcp-meeting-name-filter.md +0 -0
  74. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-14-cover-page-summary-of-change.md +0 -0
  75. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-14-job-progress.md +0 -0
  76. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-14-sync-hub-page.md +0 -0
  77. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-14-version-display.md +0 -0
  78. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-14-web-tdoc-extracted-changes.md +0 -0
  79. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-19-tdocs-xlsx-metadata.md +0 -0
  80. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-08-19-web-mcp-tdoc-show-by-url.md +0 -0
  81. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-09-11-ran5-testcases.md +0 -0
  82. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-09-12-testcase-nested-statuses.md +0 -0
  83. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-09-12-testcase-separate-db.md +0 -0
  84. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-09-13-resource-schema.md +0 -0
  85. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-09-22-remote-embedding-api.md +0 -0
  86. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-09-23-spec-doc-parse.md +0 -0
  87. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-09-24-spec-doc-fetch-command-removal.md +0 -0
  88. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-09-24-spec-doc-metadata-and-portal-fix.md +0 -0
  89. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-09-24-tdoc-search-namespace.md +0 -0
  90. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-09-25-spec-doc-chunk-context.md +0 -0
  91. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-09-25-spec-doc-pagination.md +0 -0
  92. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-09-25-spec-parsed-output.md +0 -0
  93. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-09-26-unified-search-index.md +0 -0
  94. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/plans/2026-09-27-mcp-stdio.md +0 -0
  95. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-07-28-compact-output-design.md +0 -0
  96. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-07-28-tdoc-size-limit-design.md +0 -0
  97. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-07-29-fts5-search-design.md +0 -0
  98. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-07-29-tdoc-cr-body-changes-design.md +0 -0
  99. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-07-30-fts5-perf.md +0 -0
  100. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-07-31-embedding-search-design.md +0 -0
  101. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-01-fts5-hit-rename-design.md +0 -0
  102. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-01-fts5-semantic-rerank-design.md +0 -0
  103. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-01-semantic-search-revision-design.md +0 -0
  104. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-02-all-extra-design.md +0 -0
  105. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-02-remove-mysql-postgres-design.md +0 -0
  106. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-02-web-and-mcp-server-design.md +0 -0
  107. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-03-meeting-sync-display-design.md +0 -0
  108. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-03-meetings-tdoc-filter-design.md +0 -0
  109. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-04-web-ui-enhancements-design.md +0 -0
  110. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-05-tdoc-columns-dropdown-design.md +0 -0
  111. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-05-web-parse-and-search-design.md +0 -0
  112. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-10-spec-sync-list-show-design.md +0 -0
  113. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-11-spec-rapporteurs-comment-design.md +0 -0
  114. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-11-spec-rapporteurs-filter-design.md +0 -0
  115. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-12-mcp-spec-sync-design.md +0 -0
  116. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-12-single-spec-sync-design.md +0 -0
  117. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-12-spec-show-version-limit-design.md +0 -0
  118. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-12-spec-sync-dynareport-fetch-design.md +0 -0
  119. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-13-docs-review-design.md +0 -0
  120. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-13-idempotent-cancel-design.md +0 -0
  121. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-13-mcp-parse-tdoc-url-design.md +0 -0
  122. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-13-spec-per-version-details-flag-design.md +0 -0
  123. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-13-web-job-detail-params-design.md +0 -0
  124. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-13-web-mcp-meeting-name-filter-design.md +0 -0
  125. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-14-cover-page-summary-of-change-design.md +0 -0
  126. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-14-sync-hub-page-design.md +0 -0
  127. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-14-version-display-design.md +0 -0
  128. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-14-web-jobs-cancel-button-design.md +0 -0
  129. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-14-web-tdoc-extracted-changes-design.md +0 -0
  130. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-19-tdocs-xlsx-metadata-design.md +0 -0
  131. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-08-19-web-mcp-tdoc-show-by-url-design.md +0 -0
  132. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-09-11-ran5-testcases-design.md +0 -0
  133. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-09-12-testcase-nested-statuses-design.md +0 -0
  134. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-09-12-testcase-separate-db-design.md +0 -0
  135. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-09-13-resource-schema-design.md +0 -0
  136. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-09-22-remote-embedding-api-design.md +0 -0
  137. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-09-23-spec-doc-parse-design.md +0 -0
  138. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-09-24-spec-doc-fetch-command-removal-design.md +0 -0
  139. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-09-24-spec-doc-metadata-and-portal-fix-design.md +0 -0
  140. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-09-24-spec-doc-web-portal-design.md +0 -0
  141. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-09-24-tdoc-search-namespace-design.md +0 -0
  142. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-09-25-spec-doc-chunk-context-design.md +0 -0
  143. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-09-25-spec-doc-pagination-design.md +0 -0
  144. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-09-25-spec-parsed-output-design.md +0 -0
  145. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-09-26-unified-search-index-design.md +0 -0
  146. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/docs/superpowers/specs/2026-09-27-mcp-stdio-design.md +0 -0
  147. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/scripts/dev_run.sh +0 -0
  148. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/scripts/test_sqlite.sh +0 -0
  149. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/__init__.py +0 -0
  150. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/cli_auto_sync.py +0 -0
  151. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/cli_filters.py +0 -0
  152. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/cli_server.py +0 -0
  153. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/cli_url_helpers.py +0 -0
  154. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/config.py +0 -0
  155. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/data/doc3gpp.toml.example +0 -0
  156. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/models/__init__.py +0 -0
  157. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/models/index.py +0 -0
  158. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/models/jobs.py +0 -0
  159. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/models/meeting.py +0 -0
  160. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/models/schema_info.py +0 -0
  161. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/models/search.py +0 -0
  162. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/models/semantic_search.py +0 -0
  163. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/models/spec.py +0 -0
  164. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/models/sync.py +0 -0
  165. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/models/tdoc.py +0 -0
  166. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/models/tdoc_cr.py +0 -0
  167. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/models/tdoc_cr_change_details.py +0 -0
  168. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/models/tdoc_file.py +0 -0
  169. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/models/tdoc_show.py +0 -0
  170. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/models/testcase.py +0 -0
  171. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/models/tsg.py +0 -0
  172. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/models/unified_search.py +0 -0
  173. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/models/wi.py +0 -0
  174. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/__init__.py +0 -0
  175. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/calendar_parser.py +0 -0
  176. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/cr/__init__.py +0 -0
  177. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/cr/body_changes.py +0 -0
  178. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/cr/cover_page.py +0 -0
  179. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/cr/cr_parsers.py +0 -0
  180. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/cr/header.py +0 -0
  181. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/cr/helpers.py +0 -0
  182. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/cr/ttcn_functions.py +0 -0
  183. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/cr/ttcn_sections.py +0 -0
  184. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/cr_parser.py +0 -0
  185. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/direct_extractor.py +0 -0
  186. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/docx_converter.py +0 -0
  187. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/html_parsers.py +0 -0
  188. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/normalizers.py +0 -0
  189. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/spec_doc.py +0 -0
  190. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/spec_doc_chunker.py +0 -0
  191. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/spec_parser.py +0 -0
  192. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/spec_release.py +0 -0
  193. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/tdoc_file_parser.py +0 -0
  194. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/tdoc_parser.py +0 -0
  195. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/tdoc_parsers.py +0 -0
  196. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/testcase_parser.py +0 -0
  197. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/parsers/wi_parser.py +0 -0
  198. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/repository/__init__.py +0 -0
  199. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/repository/protocols.py +0 -0
  200. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/scraping/__init__.py +0 -0
  201. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/scraping/cache.py +0 -0
  202. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/scraping/cache_keys.py +0 -0
  203. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/scraping/calendar_source.py +0 -0
  204. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/scraping/client.py +0 -0
  205. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/scraping/ftp_source.py +0 -0
  206. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/scraping/portal_source.py +0 -0
  207. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/scraping/spec_source.py +0 -0
  208. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/scraping/tdoc_zip_source.py +0 -0
  209. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/scraping/testcase_source.py +0 -0
  210. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/scraping/wi_source.py +0 -0
  211. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/__init__.py +0 -0
  212. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/_duration.py +0 -0
  213. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/embedding/__init__.py +0 -0
  214. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/embedding/chunker.py +0 -0
  215. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/embedding/remote_embedder.py +0 -0
  216. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/factory.py +0 -0
  217. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/meetings_service.py +0 -0
  218. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/search_service.py +0 -0
  219. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/semantic_reranker.py +0 -0
  220. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/semantic_search_service.py +0 -0
  221. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/spec_doc_index_service.py +0 -0
  222. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/spec_doc_search_facade.py +0 -0
  223. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/spec_doc_search_service.py +0 -0
  224. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/spec_doc_semantic_service.py +0 -0
  225. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/spec_doc_service.py +0 -0
  226. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/spec_service.py +0 -0
  227. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/tdoc_cr_service.py +0 -0
  228. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/tdoc_file_service.py +0 -0
  229. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/tdoc_index_service.py +0 -0
  230. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/tdoc_search_facade.py +0 -0
  231. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/tdoc_service.py +0 -0
  232. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/tdoc_sync_coordinator.py +0 -0
  233. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/testcase_service.py +0 -0
  234. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/tsg_service.py +0 -0
  235. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/services/wi_service.py +0 -0
  236. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/settings/__init__.py +0 -0
  237. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/settings/config_source.py +0 -0
  238. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/settings/config_writer.py +0 -0
  239. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/settings/loader.py +0 -0
  240. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/settings/schema.py +0 -0
  241. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/__init__.py +0 -0
  242. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/backends/__init__.py +0 -0
  243. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/backends/sqlite.py +0 -0
  244. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/cache.py +0 -0
  245. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/compression.py +0 -0
  246. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/db/__init__.py +0 -0
  247. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/db/base.py +0 -0
  248. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/db/fts5_query.py +0 -0
  249. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/db/migrate.py +0 -0
  250. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/db/migrations/README.md +0 -0
  251. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/db/models.py +0 -0
  252. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/db/session.py +0 -0
  253. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/db/specdata_base.py +0 -0
  254. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/db/testcase_base.py +0 -0
  255. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/export.py +0 -0
  256. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/repositories/__init__.py +0 -0
  257. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/repositories/jobs_sql.py +0 -0
  258. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/repositories/meeting_sql.py +0 -0
  259. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/repositories/rich_filters.py +0 -0
  260. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/repositories/search_sql.py +0 -0
  261. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/repositories/spec_doc_search_sql.py +0 -0
  262. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/repositories/spec_doc_sql.py +0 -0
  263. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/repositories/spec_doc_vector_sql.py +0 -0
  264. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/repositories/spec_sql.py +0 -0
  265. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/repositories/tdoc_cr_change_details_sql.py +0 -0
  266. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/repositories/tdoc_cr_sql.py +0 -0
  267. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/repositories/tdoc_cr_ttcn_sql.py +0 -0
  268. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/repositories/tdoc_file_sql.py +0 -0
  269. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/repositories/tdoc_sql.py +0 -0
  270. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/repositories/testcase_sql.py +0 -0
  271. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/repositories/tsg_sql.py +0 -0
  272. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/repositories/vector_sql.py +0 -0
  273. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/storage/repositories/wi_sql.py +0 -0
  274. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/__init__.py +0 -0
  275. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/app.py +0 -0
  276. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/deps.py +0 -0
  277. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/filters.py +0 -0
  278. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/install.py +0 -0
  279. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/mcp_stdio.py +0 -0
  280. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/render.py +0 -0
  281. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/routes/__init__.py +0 -0
  282. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/routes/landing.py +0 -0
  283. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/routes/meetings.py +0 -0
  284. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/routes/search.py +0 -0
  285. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/routes/spec_docs.py +0 -0
  286. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/routes/specs.py +0 -0
  287. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/routes/sync.py +0 -0
  288. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/routes/tdocs.py +0 -0
  289. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/routes/testcases.py +0 -0
  290. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/routes/tsgs.py +0 -0
  291. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/routes/wis.py +0 -0
  292. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/state.py +0 -0
  293. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/static/htmx.min.js +0 -0
  294. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/static/js/job_poller.js +0 -0
  295. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/static/js/meeting_sync.js +0 -0
  296. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/static/js/search.js +0 -0
  297. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/static/js/spec_clipboard.js +0 -0
  298. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/static/js/spec_sync.js +0 -0
  299. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/static/js/sync_hub.js +0 -0
  300. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/static/js/tdoc_columns.js +0 -0
  301. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/static/js/tdoc_parse.js +0 -0
  302. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/static/style.css +0 -0
  303. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/base.html +0 -0
  304. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/job_status.html +0 -0
  305. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/landing.html +0 -0
  306. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/meeting_list.html +0 -0
  307. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/meeting_show.html +0 -0
  308. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/_job_row.html +0 -0
  309. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/job_status.html +0 -0
  310. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/meeting_filters.html +0 -0
  311. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/meeting_results.html +0 -0
  312. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/pagination.html +0 -0
  313. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/schema_results.html +0 -0
  314. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/search_form.html +0 -0
  315. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/search_resource_tabs.html +0 -0
  316. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/search_results.html +0 -0
  317. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/search_results_table.html +0 -0
  318. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/spec_doc_chunks.html +0 -0
  319. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/spec_doc_search_form.html +0 -0
  320. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/spec_doc_search_results.html +0 -0
  321. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/spec_doc_search_results_table.html +0 -0
  322. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/spec_doc_show_results.html +0 -0
  323. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/spec_doc_toc_results.html +0 -0
  324. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/spec_filters.html +0 -0
  325. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/spec_results.html +0 -0
  326. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/sync_recent_jobs.html +0 -0
  327. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/tdoc_filters.html +0 -0
  328. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/tdoc_results.html +0 -0
  329. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/testcase_filters.html +0 -0
  330. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/testcase_results.html +0 -0
  331. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/wi_filters.html +0 -0
  332. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/partials/wi_results.html +0 -0
  333. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/schema.html +0 -0
  334. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/search_results.html +0 -0
  335. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/spec_doc_show.html +0 -0
  336. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/spec_doc_toc.html +0 -0
  337. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/spec_docs_search.html +0 -0
  338. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/spec_list.html +0 -0
  339. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/spec_show.html +0 -0
  340. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/sync.html +0 -0
  341. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/tdoc_content.html +0 -0
  342. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/tdoc_list.html +0 -0
  343. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/tdoc_show.html +0 -0
  344. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/testcase_list.html +0 -0
  345. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/testcase_show.html +0 -0
  346. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/tsg_list.html +0 -0
  347. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/tsg_show.html +0 -0
  348. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates/wi_list.html +0 -0
  349. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/templates_setup.py +0 -0
  350. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/workers/__init__.py +0 -0
  351. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/src/doc3gpp/web/workers/job_worker.py +0 -0
  352. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/conftest.py +0 -0
  353. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/fixtures/sample_pages/3GPP-meeting-R5.html +0 -0
  354. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/fixtures/search_corpus.py +0 -0
  355. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/fixtures/semantic_search_corpus.py +0 -0
  356. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/fixtures/spec_pages/R5_crs.html +0 -0
  357. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/fixtures/spec_pages/R5_detail.html +0 -0
  358. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/fixtures/spec_pages/R5_detail_portal.html +0 -0
  359. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/fixtures/spec_pages/R5_etsi.html +0 -0
  360. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/fixtures/spec_pages/R5_list.html +0 -0
  361. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/fixtures/tdoc_cr_doc/C6-250028.zip +0 -0
  362. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/fixtures/tdoc_cr_doc/R5-227476.zip +0 -0
  363. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/fixtures/tdoc_cr_doc/R5-253079.zip +0 -0
  364. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/fixtures/tdoc_cr_doc/R5s260009.zip +0 -0
  365. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/fixtures/tdoc_cr_doc/R5s260051.zip +0 -0
  366. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/fixtures/tdoc_cr_doc/R5s260135.zip +0 -0
  367. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/fixtures/tdoc_cr_doc/R5s260176.zip +0 -0
  368. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/fixtures/tdoc_xlsx/TDoc_List_Meeting_85434_portal.xlsx +0 -0
  369. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/fixtures/tdoc_xlsx/TDoc_List_Meeting_RAN5#111.xlsx +0 -0
  370. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/fixtures/wi_pages/R5.html +0 -0
  371. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_auto_sync_sqlite.py +0 -0
  372. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_cli_server.py +0 -0
  373. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_cli_sqlite.py +0 -0
  374. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_config_init_cli_sqlite.py +0 -0
  375. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_config_set_cli_sqlite.py +0 -0
  376. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_db_reset_sqlite.py +0 -0
  377. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_embed_after_parse.py +0 -0
  378. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_meeting_service_sqlite.py +0 -0
  379. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_migrate_tsg_spec_last_sync_drop.py +0 -0
  380. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_online_3gpp_calendar.py +0 -0
  381. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_online_spec_doc_sync.py +0 -0
  382. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_online_spec_sync.py +0 -0
  383. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_online_tdoc_fetch_r5.py +0 -0
  384. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_online_tdoc_parse.py +0 -0
  385. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_online_testcase_sync.py +0 -0
  386. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_parse_tdoc_url_job_sqlite.py +0 -0
  387. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_schema_parity.py +0 -0
  388. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_sdk_integration.py +0 -0
  389. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_search_after_parse.py +0 -0
  390. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_search_extras_disabled.py +0 -0
  391. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_search_filters.py +0 -0
  392. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_search_index_lifecycle.py +0 -0
  393. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_search_indexes.py +0 -0
  394. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_search_previews_per_column.py +0 -0
  395. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_search_query_sem_rerank.py +0 -0
  396. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_search_schema.py +0 -0
  397. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_search_sem_end_to_end.py +0 -0
  398. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_search_snippet_column.py +0 -0
  399. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_search_weighted_bm25.py +0 -0
  400. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_semantic_extras_disabled.py +0 -0
  401. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_spec_cli.py +0 -0
  402. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_spec_doc_repo.py +0 -0
  403. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_spec_doc_search_repo.py +0 -0
  404. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_spec_doc_search_service.py +0 -0
  405. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_spec_doc_service.py +0 -0
  406. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_spec_migration.py +0 -0
  407. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_spec_parsed_output.py +0 -0
  408. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_spec_sql.py +0 -0
  409. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_sqlite_backend.py +0 -0
  410. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_sync_hub_end_to_end.py +0 -0
  411. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_tdoc_cr_change_details_sqlite.py +0 -0
  412. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_tdoc_cr_rename_migration.py +0 -0
  413. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_tdoc_cr_sqlite.py +0 -0
  414. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_tdoc_cr_ttcn_sqlite.py +0 -0
  415. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_tdoc_file_sqlite.py +0 -0
  416. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_tdoc_parse_direct_sqlite.py +0 -0
  417. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_tdoc_parse_ftp_batch_sqlite.py +0 -0
  418. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_tdoc_parse_local_batch_sqlite.py +0 -0
  419. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_tdoc_sqlite.py +0 -0
  420. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_tdocs_xlsx_metadata_migration_sqlite.py +0 -0
  421. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_tdocs_xlsx_metadata_sqlite.py +0 -0
  422. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_testcase_cli.py +0 -0
  423. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_testcase_sql.py +0 -0
  424. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_tsg_sqlite.py +0 -0
  425. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_vector_index_lifecycle.py +0 -0
  426. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_vector_repo_get_min_distance.py +0 -0
  427. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_vector_schema_migration.py +0 -0
  428. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_web_end_to_end.py +0 -0
  429. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_web_search_end_to_end.py +0 -0
  430. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/integration/test_wi_sqlite.py +0 -0
  431. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/_job_polling_smoke.js +0 -0
  432. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_bind_job_polling_on_terminal.py +0 -0
  433. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_body_changes_extractor.py +0 -0
  434. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_cache_cli.py +0 -0
  435. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_cache_keys.py +0 -0
  436. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_calendar_parser.py +0 -0
  437. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_calendar_parser_defensive.py +0 -0
  438. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_chunker.py +0 -0
  439. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_cli_auto_sync.py +0 -0
  440. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_cli_filters.py +0 -0
  441. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_cli_search.py +0 -0
  442. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_cli_search_sem.py +0 -0
  443. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_cli_server_start.py +0 -0
  444. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_cli_server_stubs.py +0 -0
  445. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_cli_version.py +0 -0
  446. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_compact_helpers.py +0 -0
  447. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_config_init_cli.py +0 -0
  448. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_config_set_cli.py +0 -0
  449. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_config_writer.py +0 -0
  450. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_cr_parser.py +0 -0
  451. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_db_reset_cli.py +0 -0
  452. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_docx_converter.py +0 -0
  453. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_factory_semantic.py +0 -0
  454. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_ftp_listing.py +0 -0
  455. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_fts5_query.py +0 -0
  456. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_index_services.py +0 -0
  457. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_job_row_partial.py +0 -0
  458. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_list_output_format.py +0 -0
  459. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_meeting_cli.py +0 -0
  460. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_meeting_cli_filters_combined.py +0 -0
  461. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_meeting_cli_invalid_fields.py +0 -0
  462. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_meeting_cli_location_filter.py +0 -0
  463. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_meeting_repository_combined_filters.py +0 -0
  464. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_meeting_repository_filters.py +0 -0
  465. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_meeting_repository_location_filter.py +0 -0
  466. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_meeting_repository_upsert.py +0 -0
  467. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_meeting_sync_tsg_validation.py +0 -0
  468. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_packaging_semantic.py +0 -0
  469. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_parse_tdoc_url_route.py +0 -0
  470. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_parsers_utils.py +0 -0
  471. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_portal_source.py +0 -0
  472. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_protocols_semantic.py +0 -0
  473. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_remote_embedder.py +0 -0
  474. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_remote_embedding_settings.py +0 -0
  475. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_rrf.py +0 -0
  476. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_schema_info.py +0 -0
  477. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_scraper_client.py +0 -0
  478. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_search_query_builder.py +0 -0
  479. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_search_service.py +0 -0
  480. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_search_settings.py +0 -0
  481. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_semantic_models.py +0 -0
  482. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_semantic_reranker.py +0 -0
  483. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_semantic_search_service.py +0 -0
  484. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_semantic_settings.py +0 -0
  485. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_server_settings.py +0 -0
  486. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_services_factory.py +0 -0
  487. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_settings.py +0 -0
  488. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_settings_config_file.py +0 -0
  489. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_spec_doc_blocks.py +0 -0
  490. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_spec_doc_chunker.py +0 -0
  491. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_spec_doc_models.py +0 -0
  492. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_spec_doc_parser.py +0 -0
  493. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_spec_doc_search_facade.py +0 -0
  494. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_spec_doc_service_reads.py +0 -0
  495. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_spec_doc_settings.py +0 -0
  496. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_spec_doc_vector_metadata.py +0 -0
  497. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_spec_model.py +0 -0
  498. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_spec_orm.py +0 -0
  499. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_spec_parser.py +0 -0
  500. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_spec_release.py +0 -0
  501. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_spec_service.py +0 -0
  502. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_spec_source.py +0 -0
  503. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_storage_utils.py +0 -0
  504. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_sync_hub_page.py +0 -0
  505. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_cache.py +0 -0
  506. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_cli_fields.py +0 -0
  507. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_cr_change_details_model.py +0 -0
  508. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_cr_model.py +0 -0
  509. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_cr_service.py +0 -0
  510. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_cr_service_body_changes.py +0 -0
  511. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_cr_service_ftp_batch.py +0 -0
  512. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_file_parser.py +0 -0
  513. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_file_service.py +0 -0
  514. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_file_source.py +0 -0
  515. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_parse_cli.py +0 -0
  516. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_parse_direct.py +0 -0
  517. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_parse_settings.py +0 -0
  518. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_parse_url_batch.py +0 -0
  519. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_parser.py +0 -0
  520. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_repository_crud.py +0 -0
  521. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_repository_filters.py +0 -0
  522. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_search_facade.py +0 -0
  523. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_service_sync.py +0 -0
  524. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_show_record.py +0 -0
  525. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_sync_cli.py +0 -0
  526. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_sync_coordinator.py +0 -0
  527. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_url_not_found.py +0 -0
  528. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tdoc_zip_source.py +0 -0
  529. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_testcase_db_url.py +0 -0
  530. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_testcase_model.py +0 -0
  531. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_testcase_parser.py +0 -0
  532. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_testcase_service.py +0 -0
  533. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_testcase_source.py +0 -0
  534. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tsg_cli.py +0 -0
  535. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tsg_repository.py +0 -0
  536. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tsg_service.py +0 -0
  537. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_tsg_url_pattern.py +0 -0
  538. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_ttcn_functions.py +0 -0
  539. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_unified_search_models.py +0 -0
  540. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_web_app.py +0 -0
  541. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_web_errors.py +0 -0
  542. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_web_filters.py +0 -0
  543. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_web_install.py +0 -0
  544. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_web_jobs_routes.py +0 -0
  545. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_web_template_filters.py +0 -0
  546. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_wi_cli.py +0 -0
  547. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_wi_parser.py +0 -0
  548. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/test_wi_repository_filters.py +0 -0
  549. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/web/test_landing_version.py +0 -0
  550. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/web/test_mcp_server.py +0 -0
  551. {doc3gpp-0.3.2 → doc3gpp-0.3.3}/tests/unit/web/test_mcp_stdio.py +0 -0
@@ -356,8 +356,8 @@ Workflows in one line (full prose in `docs/architecture.md`):
356
356
  with `--resume`, `--stale-only`, and `--batch`, or `IndexStatus` with no
357
357
  rebuild flag. Hybrid weighting comes from
358
358
  `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`.
359
+ - `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`.
360
+ - `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
361
  - `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
362
  - `doc3gpp spec doc search [--text TEXT] [--semantic TEXT] [filters]` →
363
363
  `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.3
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
@@ -366,7 +366,7 @@ values on every version row.
366
366
 
367
367
  ```bash
368
368
  # 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
369
+ doc3gpp spec doc parse --spec 38.331 --release Rel-18 --version 18.5.0
370
370
  doc3gpp spec doc parse --spec 38.331 --force # re-download and re-parse
371
371
 
372
372
  # inspect the stored TOC and search chunk text/combined metadata
@@ -377,10 +377,10 @@ doc3gpp spec doc index --rebuild
377
377
  doc3gpp spec doc schema --format json
378
378
  ```
379
379
 
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.
380
+ Background `parse_spec_docs` jobs retain the selected spec's failure reason in
381
+ the job result (`failure_details`) and log. Each job targets one spec; when
382
+ both release and version are supplied, their release numbers must align (for
383
+ example, `Rel-18` with `18.5.0`).
384
384
 
385
385
  Spec-document rows live in a separate sibling database, normally
386
386
  `<main-stem>_specdata.db`, and are cached below the dedicated
@@ -283,7 +283,7 @@ values on every version row.
283
283
 
284
284
  ```bash
285
285
  # 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
286
+ doc3gpp spec doc parse --spec 38.331 --release Rel-18 --version 18.5.0
287
287
  doc3gpp spec doc parse --spec 38.331 --force # re-download and re-parse
288
288
 
289
289
  # inspect the stored TOC and search chunk text/combined metadata
@@ -294,10 +294,10 @@ doc3gpp spec doc index --rebuild
294
294
  doc3gpp spec doc schema --format json
295
295
  ```
296
296
 
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.
297
+ Background `parse_spec_docs` jobs retain the selected spec's failure reason in
298
+ the job result (`failure_details`) and log. Each job targets one spec; when
299
+ both release and version are supplied, their release numbers must align (for
300
+ example, `Rel-18` with `18.5.0`).
301
301
 
302
302
  Spec-document rows live in a separate sibling database, normally
303
303
  `<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
@@ -2415,12 +2415,12 @@ strict and fails clearly if that version has no ZIP link. Run
2415
2415
 
2416
2416
  Purpose:
2417
2417
 
2418
- - Fetch-if-missing, convert, chunk, and index one or more specs; prints one
2419
- `ok` / `skipped <reason>` / `failed <reason>` (stderr) line per spec.
2418
+ - Fetch-if-missing, convert, chunk, and index one spec; prints one `ok`,
2419
+ `skipped <reason>`, or `failed <reason>` (stderr) line.
2420
2420
 
2421
2421
  Options:
2422
2422
 
2423
- - --spec: spec id to parse; repeat per spec (at least one required).
2423
+ - --spec: spec id to parse; exactly one is required.
2424
2424
  - --release: release marker, e.g. `Rel-18`. Optional pin.
2425
2425
  - --version: exact version, e.g. `18.5.0`. Optional pin.
2426
2426
  - --force, -f: re-download and re-parse the resolved version, including
@@ -2439,11 +2439,12 @@ Behavior:
2439
2439
  `spec_doc_sources.downloaded_at` before conversion.
2440
2440
  - `--force` re-downloads the resolved ZIP and then re-parses it, including when
2441
2441
  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)`).
2442
+ - The `(spec_id, version)` source row is skipped when it already carries
2443
+ `parsed_at` (unless `--force`); oversized zips land in the `skipped` bucket;
2444
+ unknown spec/version, empty (no-`.docx`) zips, and every other error land in
2445
+ the `failures` bucket. When both `--release` and `--version` are supplied,
2446
+ their release numbers must agree (for example, `Rel-18` requires a version
2447
+ beginning with `18.`).
2447
2448
  - `parse` = fetch-if-missing + `list_spec_docx` (`.doc` entries warn-skip) +
2448
2449
  `convert_document_to_blocks` + `order_spec_files` (front-matter TOC file first,
2449
2450
  then section-tuple sort, TOC tiebreak, unnumbered lexicographic tail) +
@@ -2468,8 +2469,8 @@ Examples:
2468
2469
  # Parse the newest version of 38.331 with a published ZIP download link.
2469
2470
  doc3gpp spec doc parse --spec 38.331
2470
2471
 
2471
- # Parse two specs at a pinned release.
2472
- doc3gpp spec doc parse --spec 38.331 --spec 38.523-1 --release Rel-18
2472
+ # Parse one spec at a pinned release and version.
2473
+ doc3gpp spec doc parse --spec 38.331 --release Rel-18 --version 18.5.0
2473
2474
 
2474
2475
  # Re-parse even when already parsed.
2475
2476
  doc3gpp spec doc parse --spec 38.331 --force
@@ -207,16 +207,15 @@ the target is missing or not `X-Doc3gpp-Managed` by doc3gpp.
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
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. |
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.3"
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"
@@ -36,7 +36,7 @@ from doc3gpp.config import get_settings
36
36
  from doc3gpp.models.meeting import Meeting
37
37
  from doc3gpp.models.schema_info import SCHEMA_FIELDS, schema_payload
38
38
  from doc3gpp.models.spec import Spec, SpecVersion
39
- from doc3gpp.models.spec_doc import SpecDocSearchFilters
39
+ from doc3gpp.models.spec_doc import SpecDocSearchFilters, SpecDocSelectionError
40
40
  from doc3gpp.models.sync import BulkSyncOutcome, SyncOutcome
41
41
  from doc3gpp.models.tdoc import TDoc, TDocWithMeeting
42
42
  from doc3gpp.models.tdoc_cr import (
@@ -55,6 +55,7 @@ from doc3gpp.parsers.docx_converter import PythonDocxNotInstalledError
55
55
  from doc3gpp.parsers.normalizers import normalize_ftp_path
56
56
  from doc3gpp.scraping.cache import CacheStatus, TDocCache
57
57
  from doc3gpp.scraping.cache_keys import derive_cache_file
58
+ from doc3gpp.scraping.spec_doc_source import validate_release_version_alignment
58
59
  from doc3gpp.scraping.tdoc_zip_source import canonicalise_tdoc_id
59
60
  from doc3gpp.services.factory import (
60
61
  build_meeting_service,
@@ -5132,7 +5133,7 @@ def spec_doc_parse(
5132
5133
  spec: list[str] | None = typer.Option( # noqa: B008 - Typer option declaration
5133
5134
  None,
5134
5135
  "--spec",
5135
- help="Spec id to parse; repeat per spec (at least one required).",
5136
+ help="Spec id to parse (exactly one required).",
5136
5137
  ),
5137
5138
  release: str | None = typer.Option(
5138
5139
  None, "--release", help="Release marker, e.g. Rel-18.",
@@ -5160,10 +5161,16 @@ def spec_doc_parse(
5160
5161
  help="Strip output formatting: JSON drops indent and operator-space. No-op for ``table``.",
5161
5162
  ),
5162
5163
  ) -> None:
5163
- """Batch form: --spec may repeat; success/skip/failure buckets printed."""
5164
+ """Parse one spec; print its success/skip/failure result."""
5164
5165
  create_schema("all")
5165
5166
  if not spec:
5166
- raise typer.BadParameter("pass at least one --spec <id>")
5167
+ raise typer.BadParameter("pass exactly one --spec <id>")
5168
+ if len(spec) != 1:
5169
+ raise typer.BadParameter("--spec may be provided only once")
5170
+ try:
5171
+ validate_release_version_alignment(release, version)
5172
+ except SpecDocSelectionError as exc:
5173
+ raise typer.BadParameter(str(exc)) from exc
5167
5174
  svc = build_spec_doc_service()
5168
5175
  result = svc.parse_many(spec, release=release, version=version, force=force)
5169
5176
  for sid in result.successes:
@@ -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
 
@@ -2,13 +2,43 @@
2
2
  from __future__ import annotations
3
3
 
4
4
  import logging
5
+ import re
5
6
 
6
7
  from doc3gpp.models.spec import SpecVersion
7
- from doc3gpp.models.spec_doc import SpecDocUnknownVersionError
8
+ from doc3gpp.models.spec_doc import SpecDocSelectionError, SpecDocUnknownVersionError
8
9
 
9
10
  logger = logging.getLogger(__name__)
10
11
 
11
12
 
13
+ def validate_release_version_alignment(
14
+ release: str | None,
15
+ version: str | None,
16
+ ) -> None:
17
+ """Ensure an explicitly selected release agrees with the version major."""
18
+ if release is None or version is None:
19
+ return
20
+
21
+ release_match = re.fullmatch(r"Rel-(\d+)", release)
22
+ if release_match is None:
23
+ raise SpecDocSelectionError(
24
+ f"release {release!r} must use the form 'Rel-<number>' when paired with a version"
25
+ )
26
+
27
+ version_match = re.match(r"(\d+)(?:\.|$)", version)
28
+ if version_match is None:
29
+ raise SpecDocSelectionError(
30
+ f"version {version!r} must begin with a numeric release when paired with {release!r}"
31
+ )
32
+
33
+ release_major = release_match.group(1)
34
+ version_major = version_match.group(1)
35
+ if release_major != version_major:
36
+ raise SpecDocSelectionError(
37
+ f"release {release!r} does not match version {version!r}; "
38
+ f"expected a version beginning with {release_major}."
39
+ )
40
+
41
+
12
42
  def _version_key(v: SpecVersion) -> tuple[int, ...]:
13
43
  parts: list[int] = []
14
44
  for seg in v.version.split("."):
@@ -22,6 +52,7 @@ def _version_key(v: SpecVersion) -> tuple[int, ...]:
22
52
  def resolve_spec_doc_version(
23
53
  versions: list[SpecVersion], release: str | None = None, version: str | None = None
24
54
  ) -> SpecVersion:
55
+ validate_release_version_alignment(release, version)
25
56
  if not versions:
26
57
  raise SpecDocUnknownVersionError("no versions stored for this spec", available=[])
27
58
 
@@ -38,6 +38,7 @@ from doc3gpp.models.semantic_search import (
38
38
  )
39
39
  from doc3gpp.models.spec_doc import (
40
40
  SpecDocNoDocxError,
41
+ SpecDocSelectionError,
41
42
  SpecDocTooLargeError,
42
43
  SpecDocUnknownSpecError,
43
44
  SpecDocUnknownVersionError,
@@ -137,6 +138,7 @@ _MCP_RESOURCE_BY_EXC: dict[type[Exception], tuple[str, int]] = {
137
138
  SpecUnknownOnUpstreamError: ("spec", MCP_CODE_NOT_FOUND),
138
139
  SpecDocUnknownSpecError: ("spec_doc", MCP_CODE_NOT_FOUND),
139
140
  SpecDocUnknownVersionError: ("spec_doc", MCP_CODE_NOT_FOUND),
141
+ SpecDocSelectionError: ("spec_doc", MCP_CODE_INVALID_PARAMS),
140
142
  SpecDocTooLargeError: ("spec_doc", MCP_CODE_TOO_LARGE),
141
143
  SpecDocNoDocxError: ("spec_doc", MCP_CODE_INVALID_PARAMS),
142
144
  JobNotFoundError: ("job", MCP_CODE_NOT_FOUND),
@@ -184,6 +186,7 @@ _ERROR_SLUGS: dict[type[Exception], str] = {
184
186
  SpecUnknownOnUpstreamError: "spec_unknown_on_upstream",
185
187
  SpecDocUnknownSpecError: "spec_doc_unknown_spec",
186
188
  SpecDocUnknownVersionError: "spec_doc_unknown_version",
189
+ SpecDocSelectionError: "spec_doc_invalid_selection",
187
190
  SpecDocTooLargeError: "spec_doc_too_large",
188
191
  SpecDocNoDocxError: "spec_doc_no_docx",
189
192
  InvalidFilterError: "invalid_filter",
@@ -210,6 +213,7 @@ _STATUS_BY_EXC: dict[type[Exception], int] = {
210
213
  SpecUnknownOnUpstreamError: 404,
211
214
  SpecDocUnknownSpecError: 404,
212
215
  SpecDocUnknownVersionError: 404,
216
+ SpecDocSelectionError: 400,
213
217
  SpecDocTooLargeError: 422,
214
218
  SpecDocNoDocxError: 422,
215
219
  InvalidFilterError: 400,
@@ -33,12 +33,13 @@ from doc3gpp.models.semantic_search import (
33
33
  SemanticSearchUnavailableError,
34
34
  VectorIndexUnavailableError,
35
35
  )
36
- from doc3gpp.models.spec_doc import SpecDocSearchFilters
36
+ from doc3gpp.models.spec_doc import SpecDocSearchFilters, SpecDocSelectionError
37
37
  from doc3gpp.models.unified_search import (
38
38
  spec_doc_search_result_to_dict,
39
39
  tdoc_search_result_to_dict,
40
40
  )
41
41
  from doc3gpp.parsers.direct_extractor import is_3gpp_ftp_url
42
+ from doc3gpp.scraping.spec_doc_source import validate_release_version_alignment
42
43
  from doc3gpp.web import render
43
44
  from doc3gpp.web.errors import (
44
45
  MCP_CODE_INTERNAL_ERROR,
@@ -915,18 +916,22 @@ def build_mcp_server(state: WebState) -> MCPServer:
915
916
  ) -> str:
916
917
  return _enqueue(state, JobKind.SYNC_TESTCASES, {"force": force}, "queued sync_testcases")
917
918
 
918
- @server.tool(name="parse_spec_docs", description="Enqueue a spec-doc parse batch (mirrors `doc3gpp spec doc parse`). Each spec resolves its version via the stored spec_versions rows; results land as {requested, successes, skipped, failures}. Returns a job_id; poll `get_job` for progress and `cancel_job` to abort.")
919
+ @server.tool(name="parse_spec_docs", description="Enqueue a spec-doc parse (mirrors `doc3gpp spec doc parse`). The spec resolves its version via the stored spec_versions rows; results land as {requested, successes, skipped, failures}. Returns a job_id; poll `get_job` for progress and `cancel_job` to abort.")
919
920
  @_mcp_error_guard
920
921
  def parse_spec_docs(
921
- spec_ids: Annotated[list[str], Field(description="Spec ids to parse (at least one, e.g. ['38.331']).")],
922
+ spec_id: Annotated[str, Field(description="Spec id to parse (e.g. '38.331').")],
922
923
  release: Annotated[str | None, Field(description="Release marker (e.g. 'Rel-18').")] = None,
923
924
  version: Annotated[str | None, Field(description="Exact version (e.g. '18.5.0').")] = None,
924
925
  force: Annotated[bool, Field(description="Re-parse already-parsed pairs.")] = False,
925
926
  ) -> str:
926
- if not spec_ids:
927
- raise InvalidFilterError("spec_ids is required")
927
+ if not spec_id.strip():
928
+ raise InvalidFilterError("spec_id is required")
929
+ try:
930
+ validate_release_version_alignment(release, version)
931
+ except SpecDocSelectionError as exc:
932
+ raise InvalidFilterError(str(exc)) from exc
928
933
  params: dict[str, Any] = {
929
- "spec_ids": list(spec_ids),
934
+ "spec_id": spec_id,
930
935
  "force": force,
931
936
  }
932
937
  if release is not None:
@@ -935,7 +940,7 @@ def build_mcp_server(state: WebState) -> MCPServer:
935
940
  params["version"] = version
936
941
  return _enqueue(
937
942
  state, JobKind.PARSE_SPEC_DOCS, params,
938
- f"queued parse_spec_docs for {len(spec_ids)} spec(s)",
943
+ f"queued parse_spec_docs for {spec_id}",
939
944
  )
940
945
 
941
946
  @server.tool(name="index_tdocs", description="Enqueue TDoc FTS5/vector index maintenance.")
@@ -20,7 +20,9 @@ from fastapi.responses import JSONResponse, StreamingResponse
20
20
  from pydantic import BaseModel
21
21
 
22
22
  from doc3gpp.models.jobs import Job, JobKind, JobStatus, JSONValue
23
+ from doc3gpp.models.spec_doc import SpecDocSelectionError
23
24
  from doc3gpp.repository.protocols import JobRepository
25
+ from doc3gpp.scraping.spec_doc_source import validate_release_version_alignment
24
26
  from doc3gpp.web.deps import get_job_repo, get_job_worker
25
27
  from doc3gpp.web.errors import (
26
28
  InvalidFilterError,
@@ -143,7 +145,7 @@ class _ParseTDocURLBody(BaseModel):
143
145
 
144
146
 
145
147
  class _ParseSpecDocsBody(BaseModel):
146
- spec_ids: list[str]
148
+ spec_id: str
147
149
  release: str | None = None
148
150
  version: str | None = None
149
151
  force: bool = False
@@ -324,13 +326,17 @@ async def post_parse_spec_docs(
324
326
  body: _ParseSpecDocsBody,
325
327
  job_repo: JobRepository = Depends(get_job_repo),
326
328
  ) -> JSONResponse:
327
- """Enqueue a spec-doc parse batch (mirrors ``doc3gpp spec doc parse``)."""
328
- if not body.spec_ids:
329
+ """Enqueue a single spec-doc parse (mirrors ``doc3gpp spec doc parse``)."""
330
+ if not body.spec_id.strip():
329
331
  raise InvalidFilterError(
330
- "parse/spec-docs requires a non-empty 'spec_ids' list in the body"
332
+ "parse/spec-docs requires a non-empty 'spec_id' string in the body"
331
333
  )
334
+ try:
335
+ validate_release_version_alignment(body.release, body.version)
336
+ except SpecDocSelectionError as exc:
337
+ raise InvalidFilterError(str(exc)) from exc
332
338
  params: dict[str, JSONValue] = {
333
- "spec_ids": list(body.spec_ids),
339
+ "spec_id": body.spec_id,
334
340
  "force": body.force,
335
341
  }
336
342
  if body.release is not None:
@@ -16,7 +16,7 @@
16
16
  buildBody: function (form) {
17
17
  var forceEl = form.querySelector('input[name="force"]');
18
18
  return JSON.stringify({
19
- "spec_ids": [specId],
19
+ "spec_id": specId,
20
20
  "version": version,
21
21
  "force": sourceParsed && !!forceEl && forceEl.checked,
22
22
  });
@@ -24,7 +24,9 @@ from doc3gpp.cli_auto_sync import (
24
24
  )
25
25
  from doc3gpp.models.index import IndexRequest
26
26
  from doc3gpp.models.jobs import Job, JobKind, JSONValue
27
+ from doc3gpp.models.spec_doc import SpecDocSelectionError
27
28
  from doc3gpp.parsers.direct_extractor import is_3gpp_ftp_url
29
+ from doc3gpp.scraping.spec_doc_source import validate_release_version_alignment
28
30
  from doc3gpp.settings.schema import Settings
29
31
  from doc3gpp.web.state import ServiceContainer
30
32
 
@@ -456,24 +458,26 @@ async def _parse_spec_docs(
456
458
  progress: ProgressFn,
457
459
  cancel_event: asyncio.Event,
458
460
  ) -> Mapping[str, JSONValue]:
459
- spec_ids = job.params.get("spec_ids")
460
- if not isinstance(spec_ids, list) or not spec_ids or not all(
461
- isinstance(s, str) for s in spec_ids
462
- ):
461
+ spec_id = job.params.get("spec_id")
462
+ if not isinstance(spec_id, str) or not spec_id.strip():
463
463
  raise ValueError(
464
- "parse_spec_docs job requires a non-empty 'spec_ids' list parameter"
464
+ "parse_spec_docs job requires a non-empty 'spec_id' string parameter"
465
465
  )
466
466
  release = job.params.get("release")
467
467
  version = job.params.get("version")
468
468
  force = bool(job.params.get("force", False))
469
469
  release = release if isinstance(release, str) else None
470
470
  version = version if isinstance(version, str) else None
471
+ try:
472
+ validate_release_version_alignment(release, version)
473
+ except SpecDocSelectionError as exc:
474
+ raise ValueError(str(exc)) from exc
471
475
 
472
476
  service = getattr(services, "spec_doc", None)
473
477
  if service is None:
474
478
  raise RuntimeError("spec-doc parse is not available in this build")
475
479
 
476
- progress(f"parsing {len(spec_ids)} spec doc(s)")
480
+ progress(f"parsing spec doc {spec_id}")
477
481
 
478
482
  def on_progress(event: str, data: Mapping[str, object]) -> None:
479
483
  if event == "spec_done":
@@ -483,7 +487,7 @@ async def _parse_spec_docs(
483
487
  raise asyncio.CancelledError()
484
488
  result = await asyncio.to_thread(
485
489
  service.parse_many,
486
- list(spec_ids),
490
+ [spec_id],
487
491
  release=release,
488
492
  version=version,
489
493
  force=force,
@@ -499,7 +503,7 @@ async def _parse_spec_docs(
499
503
  force=True,
500
504
  )
501
505
  return {
502
- "requested": len(spec_ids),
506
+ "requested": 1,
503
507
  "successes": len(result.successes),
504
508
  "skipped": len(result.skipped),
505
509
  "failures": len(result.failures),
@@ -113,6 +113,21 @@ def test_list_tools_exposes_read_and_job_tools(sqlite_env) -> None:
113
113
  assert not any(name.startswith("fetch_spec") for name in names)
114
114
 
115
115
 
116
+ def test_parse_spec_docs_uses_scalar_spec_id(sqlite_env) -> None:
117
+ import asyncio
118
+
119
+ _, server = _state_and_server()
120
+
121
+ async def run():
122
+ tools = await server.list_tools()
123
+ return next(tool for tool in tools if tool.name == "parse_spec_docs")
124
+
125
+ tool = asyncio.run(run())
126
+ properties = tool.input_schema["properties"]
127
+ assert "spec_id" in properties
128
+ assert "spec_ids" not in properties
129
+
130
+
116
131
  def test_call_list_meetings_empty(sqlite_env) -> None:
117
132
  """Empty result is a single ``[]`` JSON string (not zero content items)."""
118
133
  import asyncio
@@ -59,3 +59,64 @@ def test_spec_doc_parse_forwards_force(monkeypatch):
59
59
 
60
60
  assert result.exit_code == 0, result.output
61
61
  assert calls == [(["38.331"], None, None, True)]
62
+
63
+
64
+ def test_spec_doc_parse_rejects_repeated_spec(monkeypatch):
65
+ calls = []
66
+
67
+ class FakeResult:
68
+ successes = {}
69
+ skipped = {}
70
+ failures = {}
71
+
72
+ class FakeService:
73
+ def parse_many(self, spec_ids, *, release, version, force):
74
+ calls.append((spec_ids, release, version, force))
75
+ return FakeResult()
76
+
77
+ monkeypatch.setattr("doc3gpp.cli.create_schema", lambda _scope: None)
78
+ monkeypatch.setattr("doc3gpp.cli.build_spec_doc_service", lambda: FakeService())
79
+
80
+ result = CliRunner().invoke(
81
+ app,
82
+ ["spec", "doc", "parse", "--spec", "38.331", "--spec", "38.523-1"],
83
+ )
84
+
85
+ assert result.exit_code != 0, result.output
86
+ assert calls == []
87
+
88
+
89
+ def test_spec_doc_parse_rejects_misaligned_release_and_version(monkeypatch):
90
+ calls = []
91
+
92
+ class FakeResult:
93
+ successes = {}
94
+ skipped = {}
95
+ failures = {}
96
+
97
+ class FakeService:
98
+ def parse_many(self, spec_ids, *, release, version, force):
99
+ calls.append((spec_ids, release, version, force))
100
+ return FakeResult()
101
+
102
+ monkeypatch.setattr("doc3gpp.cli.create_schema", lambda _scope: None)
103
+ monkeypatch.setattr("doc3gpp.cli.build_spec_doc_service", lambda: FakeService())
104
+
105
+ result = CliRunner().invoke(
106
+ app,
107
+ [
108
+ "spec",
109
+ "doc",
110
+ "parse",
111
+ "--spec",
112
+ "38.331",
113
+ "--release",
114
+ "Rel-18",
115
+ "--version",
116
+ "17.5.0",
117
+ ],
118
+ )
119
+
120
+ assert result.exit_code != 0, result.output
121
+ assert "does not match" in result.output
122
+ assert calls == []