databricks-tellr-app 0.5.1.dev1__tar.gz → 0.5.1.dev3__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 (246) hide show
  1. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/PKG-INFO +1 -1
  2. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/databricks_tellr_app.egg-info/PKG-INFO +1 -1
  3. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/databricks_tellr_app.egg-info/SOURCES.txt +2 -0
  4. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/pyproject.toml +1 -1
  5. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/services/session_manager.py +82 -22
  6. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/services/slide_repository.py +149 -20
  7. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/skills/architect.py +85 -50
  8. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/domain/skill_io.py +23 -4
  9. databricks_tellr_app-0.5.1.dev3/src/domain/slide_operations.py +520 -0
  10. databricks_tellr_app-0.5.1.dev3/src/services/agent_definition_manifest_v1.py +3 -0
  11. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/agent_model_payload.py +4 -2
  12. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/agent_schema_registry.py +4 -2
  13. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/graph/nodes.py +1200 -234
  14. databricks_tellr_app-0.5.1.dev1/src/services/agent_definition_manifest_v1.py +0 -3
  15. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/README.md +0 -0
  16. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/databricks_tellr_app/__init__.py +0 -0
  17. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/databricks_tellr_app/run.py +0 -0
  18. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/databricks_tellr_app.egg-info/dependency_links.txt +0 -0
  19. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/databricks_tellr_app.egg-info/requires.txt +0 -0
  20. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/databricks_tellr_app.egg-info/top_level.txt +0 -0
  21. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/setup.cfg +0 -0
  22. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/setup.py +0 -0
  23. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/__init__.py +0 -0
  24. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/__init__.py +0 -0
  25. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/_otel_bootstrap.py +0 -0
  26. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/fixtures/tour_demo_deck.json +0 -0
  27. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/main.py +0 -0
  28. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/mcp_auth.py +0 -0
  29. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/mcp_server.py +0 -0
  30. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/middleware/__init__.py +0 -0
  31. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/middleware/csrf.py +0 -0
  32. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/middleware/request_logging.py +0 -0
  33. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/middleware/security_headers.py +0 -0
  34. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/__init__.py +0 -0
  35. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/_authz.py +0 -0
  36. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/admin.py +0 -0
  37. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/admin_usage.py +0 -0
  38. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/agent_config.py +0 -0
  39. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/agent_definitions.py +0 -0
  40. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/chat.py +0 -0
  41. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/deck_contributors.py +0 -0
  42. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/export.py +0 -0
  43. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/feedback.py +0 -0
  44. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/google_slides.py +0 -0
  45. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/images.py +0 -0
  46. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/local_version.py +0 -0
  47. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/profiles.py +0 -0
  48. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/sessions.py +0 -0
  49. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/settings/__init__.py +0 -0
  50. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/settings/contributors.py +0 -0
  51. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/settings/deck_prompts.py +0 -0
  52. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/settings/design_systems.py +0 -0
  53. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/settings/identities.py +0 -0
  54. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/settings/slide_styles.py +0 -0
  55. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/setup.py +0 -0
  56. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/slides.py +0 -0
  57. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/tools.py +0 -0
  58. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/tour.py +0 -0
  59. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/verification.py +0 -0
  60. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/routes/version.py +0 -0
  61. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/schemas/__init__.py +0 -0
  62. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/schemas/agent_config.py +0 -0
  63. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/schemas/agent_definitions.py +0 -0
  64. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/schemas/feedback.py +0 -0
  65. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/schemas/graph_release_history.py +0 -0
  66. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/schemas/graph_releases.py +0 -0
  67. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/schemas/requests.py +0 -0
  68. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/schemas/responses.py +0 -0
  69. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/schemas/settings/__init__.py +0 -0
  70. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/schemas/settings/requests.py +0 -0
  71. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/schemas/settings/responses.py +0 -0
  72. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/schemas/streaming.py +0 -0
  73. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/services/__init__.py +0 -0
  74. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/services/chat_service.py +0 -0
  75. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/services/deck_level_writer.py +0 -0
  76. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/services/export_job_queue.py +0 -0
  77. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/services/feedback_service.py +0 -0
  78. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/services/job_queue.py +0 -0
  79. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/services/session_naming.py +0 -0
  80. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/services/slide_style_preview_queue.py +0 -0
  81. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/services/usage_events.py +0 -0
  82. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/services/usage_service.py +0 -0
  83. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/utils/__init__.py +0 -0
  84. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/api/utils/validation.py +0 -0
  85. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/__init__.py +0 -0
  86. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/backfill_session_slides_startup.py +0 -0
  87. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/checkpointer.py +0 -0
  88. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/config_loader.py +0 -0
  89. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/context_utils.py +0 -0
  90. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/database.py +0 -0
  91. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/databricks_client.py +0 -0
  92. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/defaults.py +0 -0
  93. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/encryption.py +0 -0
  94. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/init_default_profile.py +0 -0
  95. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/lakebase.py +0 -0
  96. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/migrate_profiles_to_agent_config.py +0 -0
  97. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/mlflow_agent_spans.py +0 -0
  98. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/mlflow_tracing.py +0 -0
  99. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/otel_logging.py +0 -0
  100. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/permission_context.py +0 -0
  101. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/prompt_modules.py +0 -0
  102. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/settings_db.py +0 -0
  103. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/skills/__init__.py +0 -0
  104. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/skills/build_reviewer.py +0 -0
  105. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/skills/builder.py +0 -0
  106. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/skills/data_analyst.py +0 -0
  107. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/skills/deck_reviewer.py +0 -0
  108. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/skills/fix_reviewer.py +0 -0
  109. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/skills/fixer.py +0 -0
  110. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/strip_retired_prompt_keys.py +0 -0
  111. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/core/user_context.py +0 -0
  112. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/__init__.py +0 -0
  113. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/__init__.py +0 -0
  114. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/data_migration.py +0 -0
  115. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/deck_contributor.py +0 -0
  116. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/deck_review.py +0 -0
  117. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/design_system.py +0 -0
  118. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/encryption_key.py +0 -0
  119. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/feedback.py +0 -0
  120. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/genie_space.py +0 -0
  121. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/google_global_credentials.py +0 -0
  122. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/google_oauth_token.py +0 -0
  123. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/graph_checkpoint.py +0 -0
  124. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/graph_configuration.py +0 -0
  125. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/identity.py +0 -0
  126. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/image.py +0 -0
  127. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/profile.py +0 -0
  128. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/profile_contributor.py +0 -0
  129. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/prompts.py +0 -0
  130. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/request_log.py +0 -0
  131. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/session.py +0 -0
  132. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/slide_deck_prompt.py +0 -0
  133. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/slide_style_library.py +0 -0
  134. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/slide_style_preview.py +0 -0
  135. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/usage_event.py +0 -0
  136. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/models/user_preference.py +0 -0
  137. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/database/types.py +0 -0
  138. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/domain/__init__.py +0 -0
  139. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/domain/conversation_engine.py +0 -0
  140. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/domain/conversation_turns.py +0 -0
  141. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/domain/deck_spec.py +0 -0
  142. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/domain/deck_spec_slides.py +0 -0
  143. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/domain/finding.py +0 -0
  144. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/domain/finding_copy.py +0 -0
  145. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/domain/slide.py +0 -0
  146. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/domain/slide_alignment.py +0 -0
  147. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/domain/slide_deck.py +0 -0
  148. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/domain/slide_numbering.py +0 -0
  149. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/__init__.py +0 -0
  150. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/agent.py +0 -0
  151. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/agent_factory.py +0 -0
  152. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/agent_resolution.py +0 -0
  153. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/agent_runtime.py +0 -0
  154. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/agent_runtime_identity.py +0 -0
  155. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/agent_schema_types.py +0 -0
  156. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/agent_test_workbench.py +0 -0
  157. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/collaboration_history.py +0 -0
  158. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/config_service.py +0 -0
  159. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/config_validator.py +0 -0
  160. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/conversation_pins.py +0 -0
  161. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/converter_jail/__init__.py +0 -0
  162. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/converter_jail/ast_guard.py +0 -0
  163. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/converter_jail/codeprep.py +0 -0
  164. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/converter_jail/gslides_runner.py +0 -0
  165. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/converter_jail/jail.py +0 -0
  166. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/converter_jail/pptx_runner.py +0 -0
  167. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/converter_jail/protocol.py +0 -0
  168. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/deck_css_aggregator.py +0 -0
  169. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/deck_review_store.py +0 -0
  170. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/design_system_compiler.py +0 -0
  171. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/design_system_service.py +0 -0
  172. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/design_system_templates.py +0 -0
  173. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/drive_uploader.py +0 -0
  174. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/evaluation/__init__.py +0 -0
  175. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/evaluation/llm_judge.py +0 -0
  176. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/foreman_service.py +0 -0
  177. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/gateway_openai.py +0 -0
  178. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/genie_service.py +0 -0
  179. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/google_slides_auth.py +0 -0
  180. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/google_slides_prompts_defaults.py +0 -0
  181. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/graph/__init__.py +0 -0
  182. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/graph/builder.py +0 -0
  183. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/graph/event_emitter.py +0 -0
  184. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/graph/progress.py +0 -0
  185. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/graph/routers.py +0 -0
  186. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/graph/state.py +0 -0
  187. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/graph/user_notices.py +0 -0
  188. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/graph_configuration.py +0 -0
  189. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/graph_configuration_bootstrap.py +0 -0
  190. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/graph_configuration_content.py +0 -0
  191. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/graph_configuration_draft.py +0 -0
  192. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/graph_configuration_publication.py +0 -0
  193. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/graph_configuration_rollback.py +0 -0
  194. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/graph_configuration_seed.py +0 -0
  195. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/graph_configuration_workbench.py +0 -0
  196. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/graph_definition_manifest.py +0 -0
  197. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/graph_release_evidence.py +0 -0
  198. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/graph_release_history.py +0 -0
  199. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/html_to_google_slides.py +0 -0
  200. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/html_to_pptx.py +0 -0
  201. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/identity_provider.py +0 -0
  202. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/identity_providers/__init__.py +0 -0
  203. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/identity_providers/account_provider.py +0 -0
  204. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/identity_providers/local_provider.py +0 -0
  205. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/identity_providers/scim_filter.py +0 -0
  206. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/identity_providers/workspace_provider.py +0 -0
  207. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/image_service.py +0 -0
  208. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/image_tools.py +0 -0
  209. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/model_endpoint_catalog.py +0 -0
  210. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/model_endpoint_probe.py +0 -0
  211. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/permission_service.py +0 -0
  212. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/persisted_graph_release.py +0 -0
  213. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/pptx_from_html_huashu.py +0 -0
  214. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/pptx_from_records.py +0 -0
  215. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/pptx_prompts_defaults.py +0 -0
  216. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/profile_service.py +0 -0
  217. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/prompt_assembler.py +0 -0
  218. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/shared_deck_attribution.py +0 -0
  219. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/slide_style_preview_fixture.py +0 -0
  220. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/slide_style_preview_storage.py +0 -0
  221. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/spec_sync.py +0 -0
  222. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/streaming_callback.py +0 -0
  223. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/svg_sanitizer.py +0 -0
  224. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/template_sections.py +0 -0
  225. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/tools/__init__.py +0 -0
  226. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/tools/agent_bricks_tool.py +0 -0
  227. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/tools/ds_asset_tool.py +0 -0
  228. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/tools/genie_tool.py +0 -0
  229. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/tools/mcp_tool.py +0 -0
  230. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/tools/model_endpoint_tool.py +0 -0
  231. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/tools/vector_tool.py +0 -0
  232. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/services/validator.py +0 -0
  233. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/utils/__init__.py +0 -0
  234. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/utils/css_utils.py +0 -0
  235. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/utils/ds_asset_utils.py +0 -0
  236. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/utils/error_handling.py +0 -0
  237. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/utils/graph_safety.py +0 -0
  238. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/utils/html_safety.py +0 -0
  239. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/utils/html_utils.py +0 -0
  240. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/utils/image_utils.py +0 -0
  241. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/utils/js_validator.py +0 -0
  242. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/utils/logging_config.py +0 -0
  243. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/utils/pi_filter.py +0 -0
  244. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/utils/slide_hash.py +0 -0
  245. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/src/utils/spotlight.py +0 -0
  246. {databricks_tellr_app-0.5.1.dev1 → databricks_tellr_app-0.5.1.dev3}/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.5.1.dev1
3
+ Version: 0.5.1.dev3
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.5.1.dev1
3
+ Version: 0.5.1.dev3
4
4
  Summary: Tellr application package for Databricks Apps
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -133,6 +133,7 @@ setup.py
133
133
  ./src/domain/slide_alignment.py
134
134
  ./src/domain/slide_deck.py
135
135
  ./src/domain/slide_numbering.py
136
+ ./src/domain/slide_operations.py
136
137
  ./src/services/__init__.py
137
138
  ./src/services/agent.py
138
139
  ./src/services/agent_definition_manifest_v1.py
@@ -372,6 +373,7 @@ src/domain/slide.py
372
373
  src/domain/slide_alignment.py
373
374
  src/domain/slide_deck.py
374
375
  src/domain/slide_numbering.py
376
+ src/domain/slide_operations.py
375
377
  src/services/__init__.py
376
378
  src/services/agent.py
377
379
  src/services/agent_definition_manifest_v1.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "databricks-tellr-app"
7
- version = "0.5.1.dev1"
7
+ version = "0.5.1.dev3"
8
8
  description = "Tellr application package for Databricks Apps"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -712,8 +712,11 @@ def _rewrite_slide_rows(
712
712
  ) -> None:
713
713
  """THE per-slide rewrite of a whole deck: one copy, so its callers cannot diverge.
714
714
 
715
- ``save_slide_deck``, ``restore_version`` and ``SlideWriter.insert_pending_slides``
716
- all rewrite every row of a deck through here. Hand-copied versions of this
715
+ ``save_slide_deck``, ``restore_version`` and :func:`_lay_out_slide_rows` all
716
+ rewrite every row of a deck through here. ``_lay_out_slide_rows`` is the
717
+ exact-map shift behind ``SlideWriter.apply_slide_layout`` (and so its
718
+ ``insert_pending_slides`` special case) and, through :func:`_splice_slide_rows`,
719
+ the manual ``SessionManager.insert_slide``. Hand-copied versions of this
717
720
  block once drifted apart, and a reorder then attached slide identity and
718
721
  verdicts to the wrong slide.
719
722
 
@@ -817,22 +820,22 @@ def _splice_slide_rows(
817
820
  old_rows: List[SessionSlide],
818
821
  new_slides: Mapping[int, Tuple[Dict[str, Any], Dict[str, Any]]],
819
822
  ) -> None:
820
- """THE shift: splice new slides in among a deck's rows with the EXACT old-row map.
823
+ """Splice new slides in among a deck's rows, in order: the in-order wrapper.
821
824
 
822
- Shared by the graph's ``SlideWriter.insert_pending_slides`` and the manual
823
- ``SessionManager.insert_slide``, so there is one shift primitive and one row
824
- writer.
825
+ The manual ``SessionManager.insert_slide``'s shift. It only builds the
826
+ in-order layout and hands it to :func:`_lay_out_slide_rows`, THE shift
827
+ primitive, which the graph's ``SlideWriter.apply_slide_layout`` (and so its
828
+ ``insert_pending_slides`` special case) calls directly — so there is one
829
+ shift primitive and one row writer.
825
830
 
826
- 1. Every old row is snapshotted (:func:`_snapshot_row`) and attributed
827
- (:func:`_attribution_from`) BEFORE any write: ``_upsert_slide_row``
828
- rewrites those ORM rows in place.
829
- 2. ``len(old_rows) + len(new_slides)`` positions are laid out: position ``i``
831
+ 1. ``len(old_rows) + len(new_slides)`` positions are laid out: position ``i``
830
832
  holds ``new_slides[i]``'s slide when ``i`` is a key, otherwise the next old
831
833
  row in order.
832
- 3. Every position is rewritten through ``_rewrite_slide_rows`` with that map,
833
- never the attribution heuristic, and with ``now_dt=None`` /
834
- ``author_fallback=None``, so a moved row with a NULL author or timestamp
835
- stays NULL and no moved row gets a fresh ``modified_at``.
834
+ 2. :func:`_lay_out_slide_rows` snapshots and attributes every old row BEFORE
835
+ any write and rewrites every position with that exact map, never the
836
+ attribution heuristic, and with ``now_dt=None`` / ``author_fallback=None``,
837
+ so a moved row with a NULL author or timestamp stays NULL and no moved row
838
+ gets a fresh ``modified_at``.
836
839
 
837
840
  Args:
838
841
  db: live SQLAlchemy session; the caller owns the transaction.
@@ -854,26 +857,82 @@ def _splice_slide_rows(
854
857
  f"{new_count} positions"
855
858
  )
856
859
 
860
+ layout = []
861
+ old_index = 0
862
+ for new_index in range(new_count):
863
+ if new_index in new_slides:
864
+ layout.append(None)
865
+ else:
866
+ layout.append(old_index)
867
+ old_index += 1
868
+ _lay_out_slide_rows(db, owner_id, old_rows, layout, new_slides)
869
+
870
+
871
+ def _lay_out_slide_rows(
872
+ db: Session,
873
+ owner_id: int,
874
+ old_rows: List[SessionSlide],
875
+ layout: Sequence[Optional[int]],
876
+ new_slides: Mapping[int, Tuple[Dict[str, Any], Dict[str, Any]]],
877
+ ) -> None:
878
+ """THE shift primitive: lay a deck's rows out in any order, with the EXACT map.
879
+
880
+ ``layout[i]`` is the INDEX into ``old_rows`` (position order) whose row lands
881
+ at new index ``i``, or ``None`` for a new slide, whose
882
+ ``(slide_dict, incoming_record)`` is ``new_slides[i]``. An old index may
883
+ appear at most once; one that is absent is deleted (pruned, with its record
884
+ and spec fragment). Every exact-map writer goes through here:
885
+ ``SlideWriter.apply_slide_layout`` (the graph's operations layout, and its
886
+ ``insert_pending_slides`` special case) directly, and the manual
887
+ ``SessionManager.insert_slide`` through :func:`_splice_slide_rows`, the
888
+ in-order wrapper.
889
+
890
+ 1. Every old row is snapshotted (:func:`_snapshot_row`) and attributed
891
+ (:func:`_attribution_from`) BEFORE any write: ``_upsert_slide_row``
892
+ rewrites those ORM rows in place.
893
+ 2. Every position is rewritten through ``_rewrite_slide_rows`` with that exact
894
+ map, never the attribution heuristic, and with ``now_dt=None`` /
895
+ ``author_fallback=None``, so a moved row with a NULL author or timestamp
896
+ stays NULL and no moved row gets a fresh ``modified_at``.
897
+ 3. Rows beyond ``len(layout)`` are pruned (inside ``_rewrite_slide_rows``).
898
+
899
+ Raises:
900
+ ValueError: the layout is malformed (an old index repeated or outside
901
+ ``range(len(old_rows))``, or ``new_slides`` keys that are not exactly
902
+ the ``None`` indices). Raised before anything is written.
903
+ """
904
+ layout = list(layout)
905
+ old_count = len(old_rows)
906
+ kept = [index for index in layout if index is not None]
907
+ bad = sorted({index for index in kept if not 0 <= index < old_count})
908
+ if bad:
909
+ raise ValueError(f"layout names old rows {bad} outside range({old_count})")
910
+ if len(set(kept)) != len(kept):
911
+ raise ValueError(f"layout names an old row more than once: {layout}")
912
+ new_positions = {i for i, index in enumerate(layout) if index is None}
913
+ if set(new_slides) != new_positions:
914
+ raise ValueError(
915
+ f"new_slides keys {sorted(new_slides)} are not the layout's new "
916
+ f"positions {sorted(new_positions)}"
917
+ )
918
+
857
919
  # 1. Snapshots and attributions from the ORM rows BEFORE any write.
858
920
  snapshots = [_snapshot_row(row) for row in old_rows]
859
921
  attributions = [_attribution_from(row) for row in old_rows]
860
922
 
861
- # 2. The layout: each new slide at its index, the old rows in order around them.
862
923
  slides: List[Dict[str, Any]] = []
863
924
  attribution: Dict[int, SlideAttribution] = {}
864
925
  incoming: Dict[int, Dict[str, Any]] = {}
865
- old_index = 0
866
- for new_index in range(new_count):
867
- if new_index in new_slides:
926
+ for new_index, old_index in enumerate(layout):
927
+ if old_index is None:
868
928
  slide_dict, record = new_slides[new_index]
869
929
  slides.append(slide_dict)
870
930
  incoming[new_index] = record
871
931
  else:
872
932
  slides.append(snapshots[old_index])
873
933
  attribution[new_index] = attributions[old_index]
874
- old_index += 1
875
934
 
876
- # 3. THE whole-deck rewrite, with the exact map.
935
+ # 2-3. THE whole-deck rewrite, with the exact map, then the prune.
877
936
  _rewrite_slide_rows(
878
937
  db,
879
938
  owner_id,
@@ -2168,8 +2227,9 @@ class SessionManager:
2168
2227
  """Insert one slide (the viewer's manual insert) in ONE transaction.
2169
2228
 
2170
2229
  The manual insert shifts every later slide by one, so it moves rows with
2171
- the EXACT old-row map (:func:`_splice_slide_rows`, shared with the graph's
2172
- ``SlideWriter.insert_pending_slides``), never ``save_slide_deck``'s
2230
+ the EXACT old-row map (:func:`_splice_slide_rows`, the in-order wrapper of
2231
+ :func:`_lay_out_slide_rows`, the shift primitive the graph's
2232
+ ``SlideWriter.apply_slide_layout`` also uses), never ``save_slide_deck``'s
2173
2233
  attribution heuristic. Rows, spec, deck columns and evidence are written
2174
2234
  in one ``get_db_session()``, in this order:
2175
2235
 
@@ -25,8 +25,8 @@ from sqlalchemy import and_
25
25
  from src.api.services.session_manager import (
26
26
  SessionManager,
27
27
  SlideRowsChangedError,
28
+ _lay_out_slide_rows,
28
29
  _snapshot_row, # noqa: F401 — re-exported; it moved to session_manager
29
- _splice_slide_rows,
30
30
  get_session_manager,
31
31
  )
32
32
  from src.core.database import get_db_session
@@ -465,8 +465,9 @@ class SlideWriter:
465
465
  EXACT old-row map, never the attribution heuristic, and with
466
466
  ``now_dt=None``/``author_fallback=None``, so a moved row with a NULL
467
467
  author or timestamp stays NULL and no moved row gets a fresh
468
- ``modified_at``. Steps 2-4 are ``session_manager._splice_slide_rows``,
469
- the one shift primitive, shared with ``SessionManager.insert_slide``.
468
+ ``modified_at``. Steps 2-4 are ``session_manager._lay_out_slide_rows``
469
+ (via :meth:`apply_slide_layout`), the one shift primitive, shared with
470
+ ``SessionManager.insert_slide``.
470
471
  5. ``deck_spec_json`` is set to *deck_spec*, ``version`` is bumped (which
471
472
  invalidates every worker's deck cache) and ``last_activity`` touched.
472
473
  ``slide_count`` is NOT written: its single writer is the post-commit
@@ -516,8 +517,103 @@ class SlideWriter:
516
517
  "insert_pending_slides records an insert_slide/deck mutation, not "
517
518
  f"{mutation.operation}/{mutation.object_type}"
518
519
  )
519
- expected = [(slide_id, content_hash) for slide_id, content_hash in expected_rows]
520
520
  spec_count = len(spec.slides)
521
+ if insert_at[-1] >= spec_count:
522
+ raise ValueError(
523
+ f"insert_at {insert_at} is beyond the spec's {spec_count} slides"
524
+ )
525
+ layout: List[Optional[int]]
526
+ if not expected_rows:
527
+ # Row-less (spec-only) deck: no rows to lay out, only None slots.
528
+ layout = [None] * len(insert_at)
529
+ else:
530
+ new_count = len(expected_rows) + len(insert_at)
531
+ if insert_at[-1] >= new_count:
532
+ raise ValueError(
533
+ f"insert_at {insert_at} is beyond the shifted deck's "
534
+ f"{new_count} slides"
535
+ )
536
+ # The in-order layout: the old rows in order around each inserted index.
537
+ layout = []
538
+ old_index = 0
539
+ for new_index in range(new_count):
540
+ if new_index in insert_at:
541
+ layout.append(None)
542
+ else:
543
+ layout.append(old_index)
544
+ old_index += 1
545
+ return self.apply_slide_layout(
546
+ session_id,
547
+ layout,
548
+ deck_spec=deck_spec,
549
+ expected_rows=expected_rows,
550
+ created_by=created_by,
551
+ mutation=mutation,
552
+ )
553
+
554
+ def apply_slide_layout(
555
+ self,
556
+ session_id: str,
557
+ layout: Sequence[Optional[int]],
558
+ *,
559
+ deck_spec: str,
560
+ expected_rows: Sequence[Tuple[Optional[str], str]],
561
+ created_by: Optional[str],
562
+ mutation: DeckMutationContext,
563
+ ) -> List[str]:
564
+ """Lay the deck's rows out as *layout*: move, delete and insert in one transaction.
565
+
566
+ The general form of :meth:`insert_pending_slides`. ``layout[i]`` is the
567
+ INDEX into the current rows (position order) that lands at new index
568
+ ``i``, or ``None`` for a pending placeholder (``PENDING_SLIDE_TEXT``,
569
+ fresh ``slide_id``, authored by *created_by*). A current row whose index
570
+ is absent is deleted. Every kept row keeps its whole identity
571
+ (``slide_id``, authorship, timestamps, verification record, spec
572
+ fragment), through ``session_manager._lay_out_slide_rows``.
573
+
574
+ One ``get_db_session()`` transaction: the rows are re-read and
575
+ ``[(slide_id, content_hash)]`` must equal *expected_rows*
576
+ (:class:`SlideRowsChangedError` otherwise, also for a session with no deck
577
+ row); the layout is validated against the committed rows; the rows are
578
+ laid out; ``deck_spec_json`` is set to *deck_spec*, ``version`` bumped,
579
+ ``last_activity`` touched (``slide_count`` is NOT written); and ONE
580
+ mutation event is recorded. A row-less (spec-only) deck takes the last
581
+ two steps only, and its layout must be all ``None``.
582
+
583
+ Args:
584
+ session_id: the requesting session (a contributor writes the owner's deck).
585
+ layout: see above.
586
+ deck_spec: the canonical spec JSON of the deck after the edit; with
587
+ rows, its slide count must equal ``len(layout)``.
588
+ expected_rows: ``[(slide_id, content_hash)]`` the caller planned against.
589
+ created_by: the author of the turn that opens the pending rows.
590
+ mutation: provenance; its actor must be the requesting session, its
591
+ object type ``deck`` and its operation
592
+ ``layout_mutation_operation(layout, len(rows))``.
593
+
594
+ Returns:
595
+ The new slides' ``slide_id`` values in ascending new-index order
596
+ (``[]`` for a row-less deck).
597
+
598
+ Raises:
599
+ ValueError: the layout is malformed or the identity; *deck_spec* is
600
+ not a valid DeckSpec or its count differs from the layout's;
601
+ *mutation* does not match the layout; or the evidence write
602
+ refuses the actor.
603
+ SlideRowsChangedError: the committed rows are not *expected_rows*, or
604
+ the session has no deck row.
605
+ SessionNotFoundError: *session_id* does not match any session.
606
+
607
+ Nothing is written on any raise.
608
+ """
609
+ layout = list(layout)
610
+ kept = [index for index in layout if index is not None]
611
+ if any(index < 0 for index in kept) or len(set(kept)) != len(kept):
612
+ raise ValueError(f"malformed layout: {layout}")
613
+ spec = DeckSpec.from_json(deck_spec)
614
+ if spec is None:
615
+ raise ValueError("deck_spec is not a valid DeckSpec")
616
+ expected = [(slide_id, content_hash) for slide_id, content_hash in expected_rows]
521
617
 
522
618
  with get_db_session() as db:
523
619
  session = self.session_manager._get_session_or_raise(db, session_id)
@@ -527,7 +623,7 @@ class SlideWriter:
527
623
  # The caller's prior spec came from this row; its absence means
528
624
  # the deck changed under the planner.
529
625
  raise SlideRowsChangedError(
530
- f"session {session_id} has no deck row to insert into"
626
+ f"session {session_id} has no deck row to lay out"
531
627
  )
532
628
 
533
629
  # 1. The rows the caller planned against, or nothing is written.
@@ -546,29 +642,37 @@ class SlideWriter:
546
642
  f"planned against ({len(committed)} committed, {len(expected)} "
547
643
  "expected)"
548
644
  )
549
- if insert_at[-1] >= spec_count:
645
+
646
+ # 2. The layout against the committed rows, before any write.
647
+ if any(index >= len(old_rows) for index in kept):
648
+ raise ValueError(
649
+ f"layout {layout} names a row beyond the deck's {len(old_rows)}"
650
+ )
651
+ operation = layout_mutation_operation(layout, len(old_rows))
652
+ if mutation.operation != operation or mutation.object_type != "deck":
550
653
  raise ValueError(
551
- f"insert_at {insert_at} is beyond the spec's {spec_count} slides"
654
+ f"this layout records a {operation}/deck mutation, not "
655
+ f"{mutation.operation}/{mutation.object_type}"
552
656
  )
553
- new_count = len(old_rows) + len(insert_at)
554
- if old_rows and spec_count != new_count:
657
+ if old_rows and len(spec.slides) != len(layout):
555
658
  raise ValueError(
556
- f"deck_spec has {spec_count} slides, but {len(old_rows)} rows plus "
557
- f"{len(insert_at)} inserted make {new_count}"
659
+ f"deck_spec has {len(spec.slides)} slides, but the layout "
660
+ f"makes {len(layout)}"
558
661
  )
559
662
 
560
663
  new_ids: List[str] = []
561
664
  now = datetime.utcnow()
562
665
  if old_rows:
563
- # 2-4. A pending placeholder at each inserted index, spliced in
564
- # through THE shift (session_manager._splice_slide_rows):
565
- # snapshots and attributions before any write, then the
566
- # whole-deck rewrite with the exact old-row map.
666
+ # 3. A pending placeholder at each None, then THE shift
667
+ # (session_manager._lay_out_slide_rows): snapshots and
668
+ # attributions before any write, the exact-map rewrite, the prune.
567
669
  pending_html, pending_record = placeholder_slide(
568
670
  PENDING_SLIDE_MESSAGE, text=PENDING_SLIDE_TEXT
569
671
  )
570
672
  new_slides: Dict[int, Tuple[Dict[str, Any], Dict[str, Any]]] = {}
571
- for new_index in insert_at:
673
+ for new_index, old_index in enumerate(layout):
674
+ if old_index is not None:
675
+ continue
572
676
  slide_id = str(uuid.uuid4())
573
677
  new_ids.append(slide_id)
574
678
  new_slides[new_index] = (
@@ -586,16 +690,16 @@ class SlideWriter:
586
690
  for content_hash, verdict in pending_record.items()
587
691
  },
588
692
  )
589
- _splice_slide_rows(db, deck_owner.id, old_rows, new_slides)
693
+ _lay_out_slide_rows(db, deck_owner.id, old_rows, layout, new_slides)
590
694
 
591
- # 5. Deck columns, in the same transaction as the rows.
695
+ # 4. Deck columns, in the same transaction as the rows.
592
696
  deck.deck_spec_json = deck_spec
593
697
  deck.version = (deck.version or 0) + 1
594
698
  session.last_activity = now
595
699
  if session.id != deck_owner.id:
596
700
  deck_owner.last_activity = now
597
701
 
598
- # 6. Evidence; a refusal here rolls the whole shift back.
702
+ # 5. Evidence; a refusal here rolls the whole layout back.
599
703
  db.flush()
600
704
  record_shared_deck_mutation(
601
705
  db,
@@ -603,13 +707,38 @@ class SlideWriter:
603
707
  deck_owner=deck_owner,
604
708
  deck=deck,
605
709
  actor=mutation.actor,
606
- operation="insert_slide",
710
+ operation=operation,
607
711
  object_type="deck",
608
712
  object_id=mutation.object_id,
609
713
  )
610
714
  return new_ids
611
715
 
612
716
 
717
+ def layout_mutation_operation(layout: Sequence[Optional[int]], old_count: int) -> str:
718
+ """The ``MutationOperation`` that records *layout* over *old_count* current rows.
719
+
720
+ ``insert_slide`` when every old index is present in order and at least one
721
+ slot is ``None``; ``delete_slide`` when there is no ``None``, the kept
722
+ indices ascend and some are missing; ``reorder_slides`` when there is no
723
+ ``None``, every old index is present and they are out of order;
724
+ ``save_deck_slides`` for anything else (a mix).
725
+
726
+ Raises:
727
+ ValueError: the identity layout, which has nothing to write.
728
+ """
729
+ layout = list(layout)
730
+ kept = [index for index in layout if index is not None]
731
+ if len(kept) != len(layout):
732
+ return "insert_slide" if kept == list(range(old_count)) else "save_deck_slides"
733
+ if kept == list(range(old_count)):
734
+ raise ValueError("the identity layout changes nothing; there is nothing to write")
735
+ if all(a < b for a, b in zip(kept, kept[1:])):
736
+ return "delete_slide"
737
+ if len(kept) == old_count and set(kept) == set(range(old_count)):
738
+ return "reorder_slides"
739
+ return "save_deck_slides"
740
+
741
+
613
742
  # ------------------------------------------------------------------
614
743
  # Internal helpers
615
744
  # ------------------------------------------------------------------
@@ -25,8 +25,10 @@ INSTRUCTIONS: str = (
25
25
  "PAYLOAD RULES:\n"
26
26
  " build → deck_spec must be set\n"
27
27
  " ask_data → data_request must be set\n"
28
- " edit → target_positions must be a non-empty list AND "
29
- "deck_spec must be set (an edit without deck_spec cannot be applied)\n"
28
+ " edit → operations must be set for slide changes, and "
29
+ "deck_changes for a deck-level change the user asks for (one or both); "
30
+ "target_positions and deck_spec are not needed, except that re-describing the "
31
+ "deck as it stands returns the full deck_spec\n"
30
32
  " confirm_design_contract → proposed_design_contract must be set; "
31
33
  "never set deck_spec until the user confirms\n"
32
34
  " discuss → message only; no payload required\n\n"
@@ -71,35 +73,69 @@ INSTRUCTIONS: str = (
71
73
  "content-bearing section whose frame fits; the exhibit replaces that "
72
74
  "section's body while its eyebrow, title and footer stay.\n\n"
73
75
  "EDITING (edit intent):\n"
74
- "On an edit, start from current_deck_spec and return it in full as deck_spec. "
75
- "Revise only what the user asked for on the target slides; keep every slide's "
76
- "position and every other slide unchanged, except that adding a slide moves "
77
- "each slide after it up by one position. Change deck-level fields only when "
78
- "the user asks. Keep design_contract unchanged on an edit unless the user has "
79
- "agreed to the contract you proposed with confirm_design_contract; otherwise "
80
- "a change of design system or slide style still goes through "
81
- "confirm_design_contract. Keep "
82
- "resolved_data unchanged unless new data has been fetched for this edit.\n"
83
- "Adding one or more new slides to an existing deck is an edit. Put each new "
84
- "slide where the user asks. When the user gives no place, put it where it fits "
85
- "the argument: before the slides that close the deck (a closing or "
86
- "call-to-action slide), unless the new slide is itself a closing slide. When "
87
- "the user asks for the new slide to come last, put it after the last slide, "
88
- "even after a closing slide. Write the new slide's SlideSpec as for any other "
89
- "slide, with slide_type and template_section_index decided by the VISUAL / "
90
- "TEMPLATE BINDING rules, at the position it takes. Slides before it keep their "
91
- "positions; every slide after it keeps every field exactly as in "
92
- "current_deck_spec (purpose, content_brief, assumes, hands_off, "
93
- "data_references, slide_type and template_section_index), and only its "
94
- "position goes up by one. Do not change any other slide to reflect the new "
95
- "one, not a neighbour's assumes or hands_off and not a slide that lists or "
96
- "sums up the deck, and do not change any deck-level field, narrative_arc "
97
- "included, unless the user asks for that change. target_positions lists the "
98
- "positions, in the returned deck_spec, of every new slide and of every slide "
99
- "the user asked to change. Say in message where the new slide went, by its "
100
- "slide number in the revised deck.\n"
101
- "Removing or reordering slides is not an edit: if the user asks for that, use "
102
- "discuss and explain that it cannot be applied as an edit.\n\n"
76
+ "On an edit, return `operations` instead of deck_spec: the ordered list of changes "
77
+ "to make to current_deck_spec. They are applied together as one change, and nothing "
78
+ "else in the deck changes. The operations are:\n"
79
+ " update — `position` and `slides` with exactly one entry: that slide gets this "
80
+ "content and is rebuilt in place\n"
81
+ " replace — `position` and `slides` with one or more entries: that slide becomes "
82
+ "these slides; the first takes its place and the rest follow it directly\n"
83
+ " insert — `after_position` and `slides` with one or more entries: new slides "
84
+ "directly after that slide, in list order; after_position -1 puts them at the start "
85
+ "of the deck\n"
86
+ " delete — `position`: that slide is removed, and nothing is rebuilt\n"
87
+ " move — `position` and `after_position`: that slide moves to directly after the "
88
+ "after_position slide (-1 = to the start); it is not rebuilt and its content does "
89
+ "not change\n"
90
+ "Each entry of `slides` is a SlideSpec without `position`, with slide_type and "
91
+ "template_section_index decided by the VISUAL / TEMPLATE BINDING rules. An update or "
92
+ "a replace describes the whole slide: start from the slide's entry in "
93
+ "current_deck_spec and revise only what the user asked for.\n"
94
+ "Every position and after_position is a position of current_deck_spec, the deck "
95
+ "before this edit, even for a slide that an earlier operation in the list shifts. "
96
+ "Give each slide the user asks about exactly one operation that does everything "
97
+ "asked of it: a slide can be the `position` of at most one operation. Slides placed "
98
+ "after the same slide go directly after it, in the order of their operations; "
99
+ "placed after a replaced slide, they follow all of its replacement slides; placed "
100
+ "after a moved slide, they follow it to its new place; placed after a deleted slide, "
101
+ "they fill its gap. Never move a slide after itself.\n"
102
+ "Changing what a slide says or shows is an update, even when the user says "
103
+ "\"replace\" about something on the slide. Expanding a slide into several slides, "
104
+ "splitting a slide, or replacing a slide with one or more new slides is a replace. "
105
+ "Rebuilding a slide without changing its content is an update with its current "
106
+ "content. Adding slides is an insert, removing a slide is a delete, and reordering "
107
+ "slides is a move.\n"
108
+ "Put each new slide where the user asks. When the user gives no place, put it where "
109
+ "it fits the argument: before the slides that close the deck (a closing or "
110
+ "call-to-action slide), unless the new slide is itself a closing slide. When the "
111
+ "user asks for the new slide to come last, put it after the last slide, even after "
112
+ "a closing slide.\n"
113
+ "Touch only the slides the user asks about: every other slide keeps its content "
114
+ "exactly, and only its number may change. Do not change any other slide to reflect "
115
+ "the edit, not a neighbour's assumes or hands_off and not a slide that lists or sums "
116
+ "up the deck, unless the user asks for that change.\n"
117
+ "Change deck-level fields only when the user asks, and then put only the changed "
118
+ "fields in `deck_changes` (title, audience, purpose, argument, call_to_action, "
119
+ "narrative_arc, design_contract, resolved_data); otherwise leave deck_changes unset. "
120
+ "Do not change narrative_arc to reflect an added, deleted or moved slide unless the "
121
+ "user asks for that change. Keep design_contract unchanged on an edit unless the "
122
+ "user has agreed to the contract you proposed with confirm_design_contract, and then "
123
+ "commit it in deck_changes.design_contract; otherwise a change of design system or "
124
+ "slide style still goes through confirm_design_contract. Keep resolved_data "
125
+ "unchanged unless new data has been fetched for this edit.\n"
126
+ "target_positions is not needed on an edit given as operations: the operations say "
127
+ "which slides are built.\n"
128
+ "Say in message what the edit will do: name each new or moved slide by its slide "
129
+ "number in the revised deck and each deleted slide by the number it had.\n"
130
+ "When the payload carries `rejected_edit`, your previous answer to this message "
131
+ "(`your_previous_answer`) was not applied and nothing in the deck changed; "
132
+ "`problems` lists every reason it was refused. Fix every listed problem and answer "
133
+ "again with the whole corrected answer. If a problem shows that the request cannot "
134
+ "be done as asked, use discuss and say why.\n"
135
+ "The one edit that returns deck_spec is a re-description: when the message asks you "
136
+ "to re-describe the deck as it now stands rather than change it, return the full "
137
+ "deck_spec describing every slide as it is, with target_positions listing its "
138
+ "positions and no operations.\n\n"
103
139
  "SLIDESPEC FIELDS (one per slide):\n"
104
140
  " position: 0-indexed integer, unique within the deck\n"
105
141
  " purpose: what this slide contributes to the narrative arc\n"
@@ -141,25 +177,24 @@ INSTRUCTIONS: str = (
141
177
  "and is never the number the user says. The payload's `slide_numbering` maps "
142
178
  "each slide of the current deck from its `slide_number` to its `position`, "
143
179
  "and `slide_references` lists the slides the user's latest message names by "
144
- "number or ordinal, already resolved to `positions`. Take target_positions "
145
- "from `slide_references` when the user's phrase was resolved; for a slide "
146
- "number or ordinal it does not resolve, look the slide up in "
147
- "`slide_numbering`. Never compute a position as the slide number minus one. "
148
- "A reference with numbers in `out_of_range` names a slide this deck does not "
149
- "have: do not edit; use discuss and say how many slides the deck has. When "
150
- "the user names a slide by its content rather than its number, find it in "
151
- "current_deck_spec. A reference says which slide the user means, not that "
180
+ "number or ordinal, already resolved to `positions`. Take each operation's "
181
+ "position and after_position from `slide_references` when the user's phrase "
182
+ "was resolved; for a slide number or ordinal it does not resolve, look the "
183
+ "slide up in `slide_numbering`. Never compute a position as the slide number "
184
+ "minus one. A reference with numbers in `out_of_range` names a slide this deck "
185
+ "does not have: do not edit; use discuss and say how many slides the deck has. "
186
+ "When the user names a slide by its content rather than its number, find it "
187
+ "in current_deck_spec. A reference says which slide the user means, not that "
152
188
  "they want it changed: decide the intent from what the user asks for.\n"
153
- "The exception to taking target_positions from `slide_references` is a request "
154
- "to add a slide. Read every slide number in it against `slide_numbering`, the "
155
- "deck before the new slide goes in. A slide the user names to say where the new "
156
- "slide goes is its anchor, not a target: a new slide placed after the named "
157
- "slide takes the position after it, and a new slide placed before the named "
158
- "slide, or given the named slide's number, takes the named slide's position "
159
- "while the named slide moves up by one. A named slide the user also asks to "
160
- "change is a target at its position in the returned deck_spec, which is one "
161
- "higher than in `slide_references` when it comes after the new slide.\n"
162
- "In `message` only, speak about slides 1-indexed, matching the UI. "
163
- "`position` and `target_positions` stay 0-indexed.\n\n"
189
+ "Read every slide number in an edit against `slide_numbering`, the deck before "
190
+ "the edit, even when the same message also adds, deletes or moves slides. A "
191
+ "slide the user names to say where a new or moved slide goes is its anchor, not "
192
+ "a slide to change. A slide placed after the named slide takes the named "
193
+ "slide's position as after_position. A slide placed before the named slide, or "
194
+ "given the named slide's number, takes as after_position the position of the "
195
+ "slide just before the named one in `slide_numbering`, or -1 when the named "
196
+ "slide is the first.\n"
197
+ "In `message` only, speak about slides 1-indexed, matching the UI. `position`, "
198
+ "`after_position` and `target_positions` stay 0-indexed.\n\n"
164
199
  "Return an ArchitectOutput. Do not include prose outside the structured output."
165
200
  )