databricks-tellr-app 0.4.3.dev3458__tar.gz → 0.4.3.dev3460__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 (241) hide show
  1. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/PKG-INFO +1 -1
  2. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/databricks_tellr_app.egg-info/PKG-INFO +1 -1
  3. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/databricks_tellr_app.egg-info/SOURCES.txt +4 -0
  4. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/pyproject.toml +1 -1
  5. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/slides.py +37 -14
  6. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/services/chat_service.py +145 -119
  7. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/services/session_manager.py +555 -86
  8. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/services/slide_repository.py +240 -13
  9. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/skills/architect.py +35 -4
  10. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/usage_event.py +2 -1
  11. databricks_tellr_app-0.4.3.dev3460/src/domain/deck_spec_slides.py +347 -0
  12. databricks_tellr_app-0.4.3.dev3460/src/domain/slide_alignment.py +198 -0
  13. databricks_tellr_app-0.4.3.dev3460/src/services/agent_definition_manifest_v1.py +3 -0
  14. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/graph/nodes.py +524 -56
  15. databricks_tellr_app-0.4.3.dev3458/src/services/agent_definition_manifest_v1.py +0 -3
  16. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/README.md +0 -0
  17. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/databricks_tellr_app/__init__.py +0 -0
  18. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/databricks_tellr_app/run.py +0 -0
  19. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/databricks_tellr_app.egg-info/dependency_links.txt +0 -0
  20. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/databricks_tellr_app.egg-info/requires.txt +0 -0
  21. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/databricks_tellr_app.egg-info/top_level.txt +0 -0
  22. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/setup.cfg +0 -0
  23. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/setup.py +0 -0
  24. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/__init__.py +0 -0
  25. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/__init__.py +0 -0
  26. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/_otel_bootstrap.py +0 -0
  27. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/fixtures/tour_demo_deck.json +0 -0
  28. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/main.py +0 -0
  29. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/mcp_auth.py +0 -0
  30. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/mcp_server.py +0 -0
  31. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/middleware/__init__.py +0 -0
  32. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/middleware/csrf.py +0 -0
  33. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/middleware/request_logging.py +0 -0
  34. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/middleware/security_headers.py +0 -0
  35. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/__init__.py +0 -0
  36. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/_authz.py +0 -0
  37. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/admin.py +0 -0
  38. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/admin_usage.py +0 -0
  39. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/agent_config.py +0 -0
  40. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/agent_definitions.py +0 -0
  41. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/chat.py +0 -0
  42. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/deck_contributors.py +0 -0
  43. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/export.py +0 -0
  44. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/feedback.py +0 -0
  45. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/google_slides.py +0 -0
  46. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/images.py +0 -0
  47. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/local_version.py +0 -0
  48. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/profiles.py +0 -0
  49. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/sessions.py +0 -0
  50. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/settings/__init__.py +0 -0
  51. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/settings/contributors.py +0 -0
  52. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/settings/deck_prompts.py +0 -0
  53. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/settings/design_systems.py +0 -0
  54. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/settings/identities.py +0 -0
  55. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/settings/slide_styles.py +0 -0
  56. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/setup.py +0 -0
  57. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/tools.py +0 -0
  58. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/tour.py +0 -0
  59. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/verification.py +0 -0
  60. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/routes/version.py +0 -0
  61. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/schemas/__init__.py +0 -0
  62. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/schemas/agent_config.py +0 -0
  63. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/schemas/agent_definitions.py +0 -0
  64. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/schemas/feedback.py +0 -0
  65. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/schemas/graph_release_history.py +0 -0
  66. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/schemas/graph_releases.py +0 -0
  67. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/schemas/requests.py +0 -0
  68. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/schemas/responses.py +0 -0
  69. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/schemas/settings/__init__.py +0 -0
  70. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/schemas/settings/requests.py +0 -0
  71. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/schemas/settings/responses.py +0 -0
  72. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/schemas/streaming.py +0 -0
  73. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/services/__init__.py +0 -0
  74. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/services/deck_level_writer.py +0 -0
  75. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/services/export_job_queue.py +0 -0
  76. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/services/feedback_service.py +0 -0
  77. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/services/job_queue.py +0 -0
  78. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/services/session_naming.py +0 -0
  79. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/services/usage_events.py +0 -0
  80. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/services/usage_service.py +0 -0
  81. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/utils/__init__.py +0 -0
  82. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/api/utils/validation.py +0 -0
  83. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/__init__.py +0 -0
  84. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/backfill_session_slides_startup.py +0 -0
  85. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/checkpointer.py +0 -0
  86. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/config_loader.py +0 -0
  87. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/context_utils.py +0 -0
  88. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/database.py +0 -0
  89. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/databricks_client.py +0 -0
  90. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/defaults.py +0 -0
  91. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/encryption.py +0 -0
  92. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/init_default_profile.py +0 -0
  93. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/lakebase.py +0 -0
  94. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/migrate_profiles_to_agent_config.py +0 -0
  95. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/mlflow_agent_spans.py +0 -0
  96. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/mlflow_tracing.py +0 -0
  97. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/otel_logging.py +0 -0
  98. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/permission_context.py +0 -0
  99. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/prompt_modules.py +0 -0
  100. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/settings_db.py +0 -0
  101. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/skills/__init__.py +0 -0
  102. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/skills/build_reviewer.py +0 -0
  103. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/skills/builder.py +0 -0
  104. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/skills/data_analyst.py +0 -0
  105. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/skills/deck_reviewer.py +0 -0
  106. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/skills/fix_reviewer.py +0 -0
  107. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/skills/fixer.py +0 -0
  108. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/strip_retired_prompt_keys.py +0 -0
  109. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/core/user_context.py +0 -0
  110. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/__init__.py +0 -0
  111. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/__init__.py +0 -0
  112. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/data_migration.py +0 -0
  113. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/deck_contributor.py +0 -0
  114. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/deck_review.py +0 -0
  115. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/design_system.py +0 -0
  116. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/encryption_key.py +0 -0
  117. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/feedback.py +0 -0
  118. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/genie_space.py +0 -0
  119. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/google_global_credentials.py +0 -0
  120. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/google_oauth_token.py +0 -0
  121. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/graph_checkpoint.py +0 -0
  122. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/graph_configuration.py +0 -0
  123. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/identity.py +0 -0
  124. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/image.py +0 -0
  125. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/profile.py +0 -0
  126. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/profile_contributor.py +0 -0
  127. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/prompts.py +0 -0
  128. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/request_log.py +0 -0
  129. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/session.py +0 -0
  130. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/slide_deck_prompt.py +0 -0
  131. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/slide_style_library.py +0 -0
  132. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/models/user_preference.py +0 -0
  133. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/database/types.py +0 -0
  134. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/domain/__init__.py +0 -0
  135. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/domain/conversation_engine.py +0 -0
  136. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/domain/conversation_turns.py +0 -0
  137. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/domain/deck_spec.py +0 -0
  138. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/domain/finding.py +0 -0
  139. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/domain/finding_copy.py +0 -0
  140. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/domain/skill_io.py +0 -0
  141. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/domain/slide.py +0 -0
  142. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/domain/slide_deck.py +0 -0
  143. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/domain/slide_numbering.py +0 -0
  144. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/__init__.py +0 -0
  145. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/agent.py +0 -0
  146. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/agent_factory.py +0 -0
  147. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/agent_model_payload.py +0 -0
  148. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/agent_resolution.py +0 -0
  149. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/agent_runtime.py +0 -0
  150. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/agent_runtime_identity.py +0 -0
  151. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/agent_schema_registry.py +0 -0
  152. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/agent_schema_types.py +0 -0
  153. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/agent_test_workbench.py +0 -0
  154. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/collaboration_history.py +0 -0
  155. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/config_service.py +0 -0
  156. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/config_validator.py +0 -0
  157. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/conversation_pins.py +0 -0
  158. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/converter_jail/__init__.py +0 -0
  159. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/converter_jail/ast_guard.py +0 -0
  160. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/converter_jail/codeprep.py +0 -0
  161. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/converter_jail/gslides_runner.py +0 -0
  162. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/converter_jail/jail.py +0 -0
  163. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/converter_jail/pptx_runner.py +0 -0
  164. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/converter_jail/protocol.py +0 -0
  165. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/deck_css_aggregator.py +0 -0
  166. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/deck_review_store.py +0 -0
  167. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/design_system_compiler.py +0 -0
  168. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/design_system_service.py +0 -0
  169. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/design_system_templates.py +0 -0
  170. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/drive_uploader.py +0 -0
  171. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/evaluation/__init__.py +0 -0
  172. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/evaluation/llm_judge.py +0 -0
  173. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/foreman_service.py +0 -0
  174. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/gateway_openai.py +0 -0
  175. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/genie_service.py +0 -0
  176. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/google_slides_auth.py +0 -0
  177. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/google_slides_prompts_defaults.py +0 -0
  178. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/graph/__init__.py +0 -0
  179. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/graph/builder.py +0 -0
  180. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/graph/event_emitter.py +0 -0
  181. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/graph/progress.py +0 -0
  182. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/graph/routers.py +0 -0
  183. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/graph/state.py +0 -0
  184. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/graph/user_notices.py +0 -0
  185. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/graph_configuration.py +0 -0
  186. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/graph_configuration_bootstrap.py +0 -0
  187. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/graph_configuration_content.py +0 -0
  188. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/graph_configuration_draft.py +0 -0
  189. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/graph_configuration_publication.py +0 -0
  190. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/graph_configuration_rollback.py +0 -0
  191. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/graph_configuration_seed.py +0 -0
  192. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/graph_configuration_workbench.py +0 -0
  193. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/graph_definition_manifest.py +0 -0
  194. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/graph_release_evidence.py +0 -0
  195. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/graph_release_history.py +0 -0
  196. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/html_to_google_slides.py +0 -0
  197. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/html_to_pptx.py +0 -0
  198. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/identity_provider.py +0 -0
  199. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/identity_providers/__init__.py +0 -0
  200. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/identity_providers/account_provider.py +0 -0
  201. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/identity_providers/local_provider.py +0 -0
  202. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/identity_providers/scim_filter.py +0 -0
  203. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/identity_providers/workspace_provider.py +0 -0
  204. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/image_service.py +0 -0
  205. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/image_tools.py +0 -0
  206. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/model_endpoint_catalog.py +0 -0
  207. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/model_endpoint_probe.py +0 -0
  208. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/permission_service.py +0 -0
  209. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/persisted_graph_release.py +0 -0
  210. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/pptx_from_html_huashu.py +0 -0
  211. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/pptx_from_records.py +0 -0
  212. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/pptx_prompts_defaults.py +0 -0
  213. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/profile_service.py +0 -0
  214. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/prompt_assembler.py +0 -0
  215. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/shared_deck_attribution.py +0 -0
  216. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/spec_sync.py +0 -0
  217. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/streaming_callback.py +0 -0
  218. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/svg_sanitizer.py +0 -0
  219. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/template_sections.py +0 -0
  220. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/tools/__init__.py +0 -0
  221. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/tools/agent_bricks_tool.py +0 -0
  222. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/tools/ds_asset_tool.py +0 -0
  223. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/tools/genie_tool.py +0 -0
  224. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/tools/mcp_tool.py +0 -0
  225. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/tools/model_endpoint_tool.py +0 -0
  226. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/tools/vector_tool.py +0 -0
  227. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/services/validator.py +0 -0
  228. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/utils/__init__.py +0 -0
  229. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/utils/css_utils.py +0 -0
  230. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/utils/ds_asset_utils.py +0 -0
  231. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/utils/error_handling.py +0 -0
  232. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/utils/graph_safety.py +0 -0
  233. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/utils/html_safety.py +0 -0
  234. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/utils/html_utils.py +0 -0
  235. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/utils/image_utils.py +0 -0
  236. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/utils/js_validator.py +0 -0
  237. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/utils/logging_config.py +0 -0
  238. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/utils/pi_filter.py +0 -0
  239. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/utils/slide_hash.py +0 -0
  240. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/utils/spotlight.py +0 -0
  241. {databricks_tellr_app-0.4.3.dev3458 → databricks_tellr_app-0.4.3.dev3460}/src/utils/text_caps.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: databricks-tellr-app
3
- Version: 0.4.3.dev3458
3
+ Version: 0.4.3.dev3460
4
4
  Summary: Tellr application package for Databricks Apps
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: databricks-tellr-app
3
- Version: 0.4.3.dev3458
3
+ Version: 0.4.3.dev3460
4
4
  Summary: Tellr application package for Databricks Apps
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -123,10 +123,12 @@ setup.py
123
123
  ./src/domain/conversation_engine.py
124
124
  ./src/domain/conversation_turns.py
125
125
  ./src/domain/deck_spec.py
126
+ ./src/domain/deck_spec_slides.py
126
127
  ./src/domain/finding.py
127
128
  ./src/domain/finding_copy.py
128
129
  ./src/domain/skill_io.py
129
130
  ./src/domain/slide.py
131
+ ./src/domain/slide_alignment.py
130
132
  ./src/domain/slide_deck.py
131
133
  ./src/domain/slide_numbering.py
132
134
  ./src/services/__init__.py
@@ -356,10 +358,12 @@ src/domain/__init__.py
356
358
  src/domain/conversation_engine.py
357
359
  src/domain/conversation_turns.py
358
360
  src/domain/deck_spec.py
361
+ src/domain/deck_spec_slides.py
359
362
  src/domain/finding.py
360
363
  src/domain/finding_copy.py
361
364
  src/domain/skill_io.py
362
365
  src/domain/slide.py
366
+ src/domain/slide_alignment.py
363
367
  src/domain/slide_deck.py
364
368
  src/domain/slide_numbering.py
365
369
  src/services/__init__.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "databricks-tellr-app"
7
- version = "0.4.3.dev3458"
7
+ version = "0.4.3.dev3460"
8
8
  description = "Tellr application package for Databricks Apps"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -11,7 +11,10 @@ Slide access is controlled by session permissions:
11
11
  The spec-dirty trigger lives in THIS module and nowhere else
12
12
  ------------------------------------------------------------
13
13
  Five handlers below call ``spec_sync.mark_dirty`` after their mutation commits:
14
- insert, reorder, update, duplicate and delete. That is the whole of the trigger surface —
14
+ insert, reorder, update, duplicate and delete. Insert marks only when it carries
15
+ ``html``: a blank insert (the viewer's "New slide") splices a spec entry that is
16
+ already the exact description of the blank, in the same transaction as the rows, so
17
+ there is nothing for the sweeper to describe. That is the whole of the trigger surface —
15
18
  the LangGraph deck build calls the ``chat_service`` / ``slide_repository`` /
16
19
  ``deck_level_writer`` methods these handlers wrap, and never issues an HTTP
17
20
  request, so "arrived via a route" *means* "a human did this" by construction. No
@@ -41,9 +44,12 @@ from src.api.routes._authz import (
41
44
  _check_deck_permission_for_session,
42
45
  _require_slide_permission,
43
46
  )
44
- from src.api.services.chat_service import get_chat_service
47
+ from src.api.routes.sessions import CONVERSATION_BUSY_DETAIL
48
+ from src.api.services.chat_service import ArcReviewInFlightError, get_chat_service
45
49
  from src.api.services.session_manager import (
50
+ DeckBusyError,
46
51
  SessionNotFoundError,
52
+ SlideRowsChangedError,
47
53
  VersionConflictError,
48
54
  get_session_manager,
49
55
  )
@@ -58,6 +64,12 @@ logger = logging.getLogger(__name__)
58
64
 
59
65
  router = APIRouter(prefix="/api/slides", tags=["slides"])
60
66
 
67
+ # The insert's rows moved between the viewer's deck and the write (another editor, or
68
+ # a stale worker cache): nothing was written, and a refresh followed by a retry works.
69
+ DECK_CHANGED_DETAIL = (
70
+ "The deck changed while the slide was being added. Refresh and try again."
71
+ )
72
+
61
73
 
62
74
  class ReorderRequest(BaseModel):
63
75
  """Request to reorder slides."""
@@ -93,8 +105,8 @@ class InsertSlideRequest(BaseModel):
93
105
  as counting backwards from the end and land the slide somewhere the caller did
94
106
  not ask for.
95
107
 
96
- ``html`` is optional: omitted, an empty slide is inserted for a human or the
97
- architect to fill.
108
+ ``html`` is optional: omitted, the deck's blank shell (its own slide frame with
109
+ nothing on it) is inserted for a human or the architect to fill.
98
110
  """
99
111
 
100
112
  session_id: str
@@ -168,12 +180,15 @@ async def insert_slide(request: InsertSlideRequest, db: Session = Depends(get_db
168
180
  request: InsertSlideRequest with session_id, position and optional html
169
181
 
170
182
  Returns:
171
- Updated slide deck
183
+ Updated slide deck, plus ``inserted_slide_id`` and ``inserted_position``
184
+ (the viewer selects the slide it just inserted from them)
172
185
 
173
186
  Raises:
174
187
  HTTPException: 403 if no permission, 400 for validation errors, 409 if
175
- session busy or version conflict, 423 if another user holds the
176
- editing lock, 500 on error
188
+ something is running on the deck (``CONVERSATION_BUSY_DETAIL``), the
189
+ deck's rows moved under the insert (``DECK_CHANGED_DETAIL``) or the
190
+ version is stale, 423 if another user holds the editing lock, 500 on
191
+ error
177
192
  """
178
193
  _require_slide_permission(request.session_id, db, PermissionLevel.CAN_EDIT)
179
194
 
@@ -189,10 +204,8 @@ async def insert_slide(request: InsertSlideRequest, db: Session = Depends(get_db
189
204
  request.session_id,
190
205
  )
191
206
  if not locked:
192
- raise HTTPException(
193
- status_code=409,
194
- detail="Session is currently processing another request. Please wait.",
195
- )
207
+ # The lock belongs to whoever holds it: do not release it.
208
+ raise HTTPException(status_code=409, detail=CONVERSATION_BUSY_DETAIL)
196
209
 
197
210
  try:
198
211
  chat_service = get_chat_service()
@@ -202,6 +215,7 @@ async def insert_slide(request: InsertSlideRequest, db: Session = Depends(get_db
202
215
  request.position,
203
216
  html=request.html,
204
217
  expected_version=request.expected_version,
218
+ inserted_by=get_current_user(),
205
219
  )
206
220
 
207
221
  logger.info(
@@ -209,11 +223,20 @@ async def insert_slide(request: InsertSlideRequest, db: Session = Depends(get_db
209
223
  extra={"position": request.position, "session_id": request.session_id},
210
224
  )
211
225
  # A slide was added: the deck's slide list no longer matches the spec's.
212
- # The service gave the new position a PLACEHOLDER spec entry with no
213
- # purpose, so this is also what schedules the description of it.
214
- await asyncio.to_thread(mark_dirty, request.session_id, get_current_user())
226
+ # An insert that carries HTML has no description yet (the spliced spec entry
227
+ # is blank), so this is what schedules the arc review of it. A blank insert
228
+ # (no html) is already exactly described by that blank entry, so it
229
+ # schedules nothing.
230
+ if request.html is not None:
231
+ await asyncio.to_thread(mark_dirty, request.session_id, get_current_user())
215
232
  return result
216
233
 
234
+ except (ArcReviewInFlightError, DeckBusyError):
235
+ # An arc review holds a claim on the deck, or a turn is running on another
236
+ # session of it; nothing was written and the insert can be retried.
237
+ raise HTTPException(status_code=409, detail=CONVERSATION_BUSY_DETAIL)
238
+ except SlideRowsChangedError:
239
+ raise HTTPException(status_code=409, detail=DECK_CHANGED_DETAIL)
217
240
  except VersionConflictError as e:
218
241
  raise HTTPException(status_code=409, detail=str(e))
219
242
  except PermissionError as e:
@@ -8,6 +8,7 @@ its scripts are automatically replaced with it - no separate cleanup needed.
8
8
  """
9
9
 
10
10
  import contextvars
11
+ import copy
11
12
  import logging
12
13
  import queue
13
14
  import re
@@ -40,6 +41,13 @@ from src.domain.conversation_engine import (
40
41
  selects_graph_engine,
41
42
  )
42
43
  from src.domain.conversation_turns import turn_message_ids
44
+ # BLANK_SLIDE_HTML is re-exported: callers and tests import it from here.
45
+ from src.domain.deck_spec_slides import (
46
+ BLANK_DECK_SPEC_ENTRY,
47
+ BLANK_SLIDE_HTML as BLANK_SLIDE_HTML,
48
+ blank_slide_shell,
49
+ renumber_spec_slides,
50
+ )
43
51
  from src.domain.slide import Slide, has_slide_wrapper
44
52
  from src.domain.slide_deck import SlideDeck
45
53
  from src.api.schemas.agent_config import resolve_agent_config
@@ -62,27 +70,17 @@ from src.utils.ds_asset_utils import (
62
70
  substitute_ds_asset_placeholders,
63
71
  )
64
72
  from src.utils.image_utils import substitute_deck_dict_images, substitute_image_placeholders
73
+ from src.utils.slide_hash import compute_slide_hash
65
74
 
66
75
  logger = logging.getLogger(__name__)
67
76
 
68
- #: The slide ``insert_slide`` adds when the caller supplies no HTML.
69
- #: The wrapper is not decoration: ``has_slide_wrapper`` is what the parser, the
70
- #: frontend thumbnail panel and every export path use to recognise a slide, so an
71
- #: empty slide still has to carry it. Left otherwise bare deliberately — the deck's
72
- #: own CSS styles it, and any content here would be content nobody asked for.
73
- BLANK_SLIDE_HTML = '<div class="slide"></div>'
74
-
75
- # The deck-spec entry a newly inserted slide gets: contiguous with its neighbours
76
- # (`DeckSpec.slide_at` depends on that) and deliberately undescribed. The route
77
- # that inserts fires the spec-dirty marker and the arc-review sweeper writes the
78
- # prose; inventing a purpose here would put words in the architect's mouth.
79
- _BLANK_DECK_SPEC_ENTRY: Dict[str, Any] = {
80
- "purpose": "",
81
- "content_brief": "",
82
- "assumes": "",
83
- "hands_off": "",
84
- "data_references": [],
85
- }
77
+ # The deck-spec entry a newly inserted slide gets (defined in
78
+ # src.domain.deck_spec_slides): contiguous with its neighbours (`DeckSpec.slide_at`
79
+ # depends on that) and deliberately undescribed. The spliced entry is the blank's
80
+ # description; inventing a purpose here would put words in the architect's mouth.
81
+ # SessionManager.insert_slide splices it, in the same transaction as the rows; this
82
+ # alias stays so there is one definition and callers that import it from here work.
83
+ _BLANK_DECK_SPEC_ENTRY = BLANK_DECK_SPEC_ENTRY
86
84
 
87
85
 
88
86
  def _pinned_graph_failure_release_id(
@@ -3940,13 +3938,30 @@ class ChatService:
3940
3938
  *,
3941
3939
  html: Optional[str] = None,
3942
3940
  expected_version: Optional[int] = None,
3941
+ inserted_by: Optional[str] = None,
3943
3942
  ) -> Dict[str, Any]:
3944
3943
  """Insert a new slide at *position*, shifting every higher slide up.
3945
3944
 
3946
- Follows ``duplicate_slide``'s shape (insert, ``_reindex_slide_ids``,
3947
- ``save_slide_deck``, ONE save point) and adds the deck-spec half: the spec
3948
- gains an entry for the new position and every entry above it shifts, so
3949
- spec positions stay contiguous and keep describing the right slides.
3945
+ The write is ONE transaction in :meth:`SessionManager.insert_slide`: the
3946
+ rows (moved with the exact old-row map, so every existing slide keeps its
3947
+ identity, attribution, verdict and fragment), the deck spec's blank entry,
3948
+ the deck columns, one version bump and the evidence. This method plans
3949
+ the insert against a COPY of the cached deck and hands the writer the rows
3950
+ it planned against (``expected_rows``): a cache that has drifted from the
3951
+ database is a ``SlideRowsChangedError`` (a 409 at the route) and writes
3952
+ nothing, never a write-back of the stale deck.
3953
+
3954
+ WHAT THE NEW SLIDE IS
3955
+ ---------------------
3956
+ With *html* it is stored verbatim. Without it, it is the deck's blank
3957
+ shell (:func:`blank_slide_shell` over this deck's slides at the landing
3958
+ position): the deck's own frame with nothing on it, so it looks like a
3959
+ blank slide of THIS deck. Its ``slide_id`` is minted here, and it is
3960
+ stamped with the current user and one explicit ``created_at`` /
3961
+ ``modified_at``: the writer fabricates no timestamps, so a slide inserted
3962
+ while the username lookup fails still carries its creation time. The
3963
+ other slides' ids are left alone (no reindex): an id-less legacy row stays
3964
+ id-less, and the writer's map depends on it.
3950
3965
 
3951
3966
  WHICH LAYER OWNS THE OUT-OF-RANGE CLAMP
3952
3967
  ---------------------------------------
@@ -3959,27 +3974,46 @@ class ChatService:
3959
3974
  (``insert(-1, x)`` lands second-to-last, not last). A negative position is
3960
3975
  therefore rejected rather than clamped: it almost certainly means the
3961
3976
  caller computed it wrongly, and quietly inserting somewhere else is worse
3962
- than a 400.
3963
-
3964
- ``landed_at`` is computed BEFORE the insert precisely because of that
3965
- clamp — after it, ``position`` may not be where the slide actually is, and
3966
- the deck-spec entry has to go where the slide really landed.
3977
+ than a 400. The check comes BEFORE ``landed`` and the shell are computed:
3978
+ ``min(-1, n)`` is -1, which would feed the shell a negative scan index.
3979
+
3980
+ ``landed`` is computed BEFORE the insert precisely because of that clamp —
3981
+ after it, ``position`` may not be where the slide actually is, and the
3982
+ shell is derived for where the slide really goes.
3983
+
3984
+ THE CACHE
3985
+ ---------
3986
+ The cached deck is never mutated before the write (the insert is built on a
3987
+ ``copy.deepcopy``), and it is invalidated on EVERY exit, so a refused write
3988
+ leaves no ghost slide in this worker's cache and a stale-rows 409 heals on
3989
+ the next try. After a successful write the deck is reloaded from the
3990
+ database, and that reloaded deck is what the save point snapshots and the
3991
+ response carries.
3967
3992
 
3968
3993
  Args:
3969
3994
  session_id: Session ID. A contributor session resolves to its deck
3970
3995
  owner in the layers below, exactly as every other mutation does.
3971
3996
  position: 0-based position to insert at. Beyond the end appends.
3972
3997
  html: HTML for the new slide. Must carry a ``<div class="slide">``
3973
- wrapper. Omitted, an empty slide is inserted for a human or the
3974
- architect to fill.
3998
+ wrapper. Omitted, the deck's blank shell is inserted for a human
3999
+ or the architect to fill.
3975
4000
  expected_version: If provided, reject the write when the deck has moved on.
4001
+ inserted_by: The editor, for the ``slide_inserted`` usage event; None
4002
+ writes none.
3976
4003
 
3977
4004
  Returns:
3978
- Updated slide deck dictionary.
4005
+ The reloaded slide deck dictionary plus ``inserted_slide_id`` and
4006
+ ``inserted_position`` (where the slide landed), which the viewer uses
4007
+ to select the slide it just inserted.
3979
4008
 
3980
4009
  Raises:
3981
4010
  ValueError: If no slide deck exists, position is negative, or the
3982
4011
  supplied HTML has no slide wrapper.
4012
+ VersionConflictError: *expected_version* is stale.
4013
+ SlideRowsChangedError: The committed rows are not the rows this insert
4014
+ planned against (the cache was stale).
4015
+ ArcReviewInFlightError: The deck's arc review holds a live claim.
4016
+ DeckBusyError: A turn holds another session of this deck.
3983
4017
  """
3984
4018
  current_deck = self._get_or_load_deck(session_id)
3985
4019
  if not current_deck:
@@ -3988,14 +4022,34 @@ class ChatService:
3988
4022
  if position < 0:
3989
4023
  raise ValueError(f"Invalid slide position: {position}")
3990
4024
 
3991
- slide_html = html if html is not None else BLANK_SLIDE_HTML
4025
+ # Where the slide will ACTUALLY be, given the domain's clamp.
4026
+ landed = min(position, len(current_deck.slides))
4027
+
4028
+ slide_html = (
4029
+ html
4030
+ if html is not None
4031
+ else blank_slide_shell([s.html for s in current_deck.slides], landed)
4032
+ )
3992
4033
  if not has_slide_wrapper(slide_html):
3993
4034
  raise ValueError("HTML must contain <div class='slide'> wrapper")
3994
4035
 
3995
- # Where the slide will ACTUALLY be, given the domain's clamp.
3996
- landed_at = min(position, len(current_deck.slides))
4036
+ # The rows this insert plans against, read off the cached deck BEFORE the
4037
+ # copy: the writer re-reads the committed rows and refuses (409) unless
4038
+ # they are exactly these. The cached html equals the row's html.
4039
+ expected_rows = [
4040
+ (s.slide_id, compute_slide_hash(s.html or "")) for s in current_deck.slides
4041
+ ]
4042
+
4043
+ # Never mutate the cached deck before the write: build on a copy.
4044
+ working = copy.deepcopy(current_deck)
3997
4045
 
3998
- new_slide = Slide(html=slide_html)
4046
+ created = datetime.utcnow().isoformat() + "Z"
4047
+ new_slide = Slide(
4048
+ html=slide_html,
4049
+ slide_id=str(uuid.uuid4()),
4050
+ created_at=created,
4051
+ modified_at=created,
4052
+ )
3999
4053
  try:
4000
4054
  _user = get_current_username()
4001
4055
  except Exception:
@@ -4003,36 +4057,42 @@ class ChatService:
4003
4057
  if _user:
4004
4058
  new_slide.stamp_created(_user)
4005
4059
 
4006
- current_deck.insert_slide(new_slide, position)
4007
-
4008
- self._reindex_slide_ids(current_deck)
4060
+ working.insert_slide(new_slide, position)
4009
4061
 
4010
- # Persist to database
4011
- deck_dict = current_deck.to_dict()
4062
+ deck_dict = working.to_dict()
4012
4063
  session_manager = get_session_manager()
4013
- save_result = session_manager.save_slide_deck(
4014
- session_id=session_id,
4015
- title=current_deck.title,
4016
- html_content=current_deck.knit(),
4017
- scripts_content=current_deck.scripts,
4018
- slide_count=len(current_deck.slides),
4019
- deck_dict=deck_dict,
4020
- expected_version=expected_version,
4021
- mutation=session_manager.deck_mutation_context(
4064
+ try:
4065
+ write_result = session_manager.insert_slide(
4022
4066
  session_id,
4023
- operation="insert_slide",
4024
- object_type="deck",
4025
- object_id=new_slide.slide_id,
4026
- ),
4027
- )
4028
- self._record_deck_version(session_id, save_result)
4067
+ position,
4068
+ slide=deck_dict["slides"][landed],
4069
+ expected_rows=expected_rows,
4070
+ deck_dict=deck_dict,
4071
+ title=working.title,
4072
+ html_content=working.knit(),
4073
+ scripts_content=working.scripts,
4074
+ mutation=session_manager.deck_mutation_context(
4075
+ session_id,
4076
+ operation="insert_slide",
4077
+ object_type="deck",
4078
+ object_id=new_slide.slide_id,
4079
+ ),
4080
+ expected_version=expected_version,
4081
+ inserted_by=inserted_by,
4082
+ )
4083
+ finally:
4084
+ # On every exit: a refused write leaves no ghost, and after a commit
4085
+ # the next load is the database's deck, not a plan.
4086
+ self._invalidate_deck_cache(session_id)
4029
4087
 
4030
- # The deck spec's slide list has to move with the deck's. AFTER
4031
- # save_slide_deck, never before: the spec write bumps deck.version, which
4032
- # would make the caller's expected_version stale and 409 a legitimate save.
4033
- spec_result = self._insert_deck_spec_slide(session_id, landed_at)
4034
- if spec_result:
4035
- self._record_deck_version(session_id, spec_result)
4088
+ # Prefer the writer's position: it is where the row actually landed.
4089
+ landed_at = write_result.get("position", landed)
4090
+
4091
+ reloaded: Optional[SlideDeck] = None
4092
+ try:
4093
+ reloaded = self._get_or_load_deck(session_id)
4094
+ except Exception as e:
4095
+ logger.warning(f"Failed to reload deck after insert_slide: {e}")
4036
4096
 
4037
4097
  # ONE save point for the whole operation, after the work commits.
4038
4098
  # VERSION_LIMIT evicts the OLDEST version, so a save point per shifted
@@ -4041,7 +4101,7 @@ class ChatService:
4041
4101
  self.create_save_point(
4042
4102
  session_id=session_id,
4043
4103
  description=f"Inserted slide {landed_at + 1}",
4044
- deck=current_deck,
4104
+ deck=reloaded if reloaded is not None else working,
4045
4105
  )
4046
4106
  except Exception as e:
4047
4107
  logger.warning(f"Failed to create save point (insert_slide): {e}")
@@ -4051,13 +4111,16 @@ class ChatService:
4051
4111
  extra={
4052
4112
  "requested_position": position,
4053
4113
  "position": landed_at,
4054
- "new_count": len(current_deck.slides),
4114
+ "new_count": write_result.get("slide_count", len(working.slides)),
4055
4115
  "session_id": session_id,
4056
4116
  },
4057
4117
  )
4058
4118
 
4059
- deck_dict, _ = self._substitute_images_for_response(deck_dict, session_id=session_id)
4060
- return deck_dict
4119
+ result = reloaded.to_dict() if reloaded is not None else deck_dict
4120
+ result, _ = self._substitute_images_for_response(result, session_id=session_id)
4121
+ result["inserted_slide_id"] = new_slide.slide_id
4122
+ result["inserted_position"] = landed_at
4123
+ return result
4061
4124
 
4062
4125
  def _rewrite_deck_spec_slides(
4063
4126
  self,
@@ -4086,9 +4149,13 @@ class ChatService:
4086
4149
  --------------------------------------
4087
4150
  Four hand-copied row-writers diverging is the defect class the previous
4088
4151
  workstream's final review found, and C1 is itself an instance of it — insert
4089
- renumbered, its three siblings did not. So the four routes supply only
4090
- *which entries end up in which order*; the position stamping, the raw-dict
4091
- handling, the author stamp and the failure policy live here once.
4152
+ renumbered, its three siblings did not. So the three routes that still use
4153
+ this method (delete, duplicate, reorder) supply only *which entries end up in
4154
+ which order*; the position stamping, the raw-dict handling, the author stamp
4155
+ and the failure policy live here once. The fourth, insert, no longer writes
4156
+ the spec through this method at all: ``SessionManager.insert_slide`` splices
4157
+ its blank entry in the same transaction as the rows, through the same pure
4158
+ core (``renumber_spec_slides``), so the two cannot disagree on the renumber.
4092
4159
 
4093
4160
  *reorder* receives the positioned entries **in position order** and returns
4094
4161
  the new list in the new order — it does not touch ``position`` at all, which
@@ -4144,45 +4211,31 @@ class ChatService:
4144
4211
  )
4145
4212
  return None
4146
4213
 
4147
- # One pass, and identity is never inferred from equality: two entries with
4148
- # the same content are still two entries.
4149
- positioned: List[Dict[str, Any]] = []
4150
- unpositioned: List[Any] = []
4151
- for entry in slides:
4152
- if isinstance(entry, dict) and isinstance(entry.get("position"), int):
4153
- positioned.append(entry)
4154
- else:
4155
- unpositioned.append(entry)
4156
- positioned.sort(key=lambda e: e["position"])
4157
-
4214
+ # The split, the reorder and the re-stamp are the pure core, shared with the
4215
+ # manual insert's transaction (src.domain.deck_spec_slides).
4158
4216
  try:
4159
- rewritten = reorder(list(positioned))
4217
+ spec = renumber_spec_slides(spec, reorder)
4160
4218
  except IndexError:
4161
4219
  # The route validated its index against the DECK, so an index the spec
4162
4220
  # cannot answer means the two have already drifted. Leaving a drifted
4163
4221
  # spec alone is recoverable; renumbering it against the wrong slides is
4164
- # not.
4222
+ # not. The count is the entries renumber_spec_slides handed to reorder.
4223
+ positioned = sum(
4224
+ 1
4225
+ for entry in slides
4226
+ if isinstance(entry, dict) and isinstance(entry.get("position"), int)
4227
+ )
4165
4228
  logger.warning(
4166
4229
  "deck spec has %s entries and cannot answer this mutation; "
4167
4230
  "leaving it untouched",
4168
- len(positioned),
4231
+ positioned,
4169
4232
  extra={"session_id": session_id, "route": label},
4170
4233
  )
4171
4234
  return None
4172
4235
 
4173
- if rewritten is None:
4236
+ if spec is None:
4174
4237
  return None
4175
4238
 
4176
- renumbered: List[Any] = []
4177
- for index, entry in enumerate(rewritten):
4178
- entry = dict(entry)
4179
- entry["position"] = index
4180
- renumbered.append(entry)
4181
- renumbered.extend(unpositioned)
4182
-
4183
- spec = dict(spec)
4184
- spec["slides"] = renumbered
4185
-
4186
4239
  try:
4187
4240
  _user = get_current_username()
4188
4241
  except Exception:
@@ -4198,33 +4251,6 @@ class ChatService:
4198
4251
  logger.warning(f"Failed to write deck spec ({label}): {e}")
4199
4252
  return None
4200
4253
 
4201
- def _insert_deck_spec_slide(
4202
- self,
4203
- session_id: str,
4204
- position: int,
4205
- ) -> Optional[Dict[str, Any]]:
4206
- """Give the deck spec a placeholder entry at *position*, shifting the rest up.
4207
-
4208
- *position* is where the slide ACTUALLY landed, which the caller computes
4209
- before the insert: ``SlideDeck.insert_slide`` delegates to ``list.insert``,
4210
- which clamps a too-large position into an append, so the requested position
4211
- and the real one diverge on an append. The clamp is re-applied here against
4212
- the spec's own length rather than trusted, because a spec shorter than the
4213
- deck would otherwise leave a hole.
4214
- """
4215
-
4216
- def _splice(entries: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
4217
- landed = min(position, len(entries))
4218
- return (
4219
- entries[:landed]
4220
- + [dict(_BLANK_DECK_SPEC_ENTRY)]
4221
- + entries[landed:]
4222
- )
4223
-
4224
- return self._rewrite_deck_spec_slides(
4225
- session_id, _splice, label="insert_slide"
4226
- )
4227
-
4228
4254
  def _delete_deck_spec_slide(
4229
4255
  self,
4230
4256
  session_id: str,