shellbrain 0.1.20__tar.gz → 0.1.22__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 (210) hide show
  1. {shellbrain-0.1.20 → shellbrain-0.1.22}/PKG-INFO +1 -1
  2. shellbrain-0.1.22/app/boot/_dsn_resolution.py +45 -0
  3. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/boot/admin_db.py +16 -32
  4. shellbrain-0.1.22/app/boot/db.py +45 -0
  5. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/boot/read_policy.py +3 -0
  6. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/contracts/requests.py +10 -8
  7. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/entities/associations.py +1 -0
  8. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/entities/memory.py +20 -0
  9. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/policies/read_policy/pipeline.py +2 -9
  10. shellbrain-0.1.22/app/migrations/versions/20260410_0009_frontier_memory_family.py +96 -0
  11. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/onboarding_assets/claude/skills/shellbrain-session-start/SKILL.md +4 -4
  12. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/onboarding_assets/codex/shellbrain-session-start/SKILL.md +4 -4
  13. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/onboarding_assets/codex/shellbrain-session-start/references/session-workflow.md +4 -4
  14. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/onboarding_assets/cursor/skills/shellbrain-session-start/SKILL.md +4 -4
  15. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/admin/repo_state.py +0 -29
  16. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/cli/schema_validation.py +5 -6
  17. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/repos/relational/read_policy_repo.py +18 -1
  18. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/identity/claude_runtime.py +0 -11
  19. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/identity/compatibility.py +0 -5
  20. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/validation/integrity_validation.py +56 -1
  21. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/validation/semantic_validation.py +1 -1
  22. {shellbrain-0.1.20 → shellbrain-0.1.22}/pyproject.toml +1 -1
  23. {shellbrain-0.1.20 → shellbrain-0.1.22}/shellbrain.egg-info/PKG-INFO +1 -1
  24. {shellbrain-0.1.20 → shellbrain-0.1.22}/shellbrain.egg-info/SOURCES.txt +2 -2
  25. shellbrain-0.1.20/app/boot/db.py +0 -70
  26. shellbrain-0.1.20/app/core/policies/read_policy/scenario_lift.py +0 -11
  27. shellbrain-0.1.20/app/core/policies/read_policy/utility_prior.py +0 -11
  28. {shellbrain-0.1.20 → shellbrain-0.1.22}/README.md +0 -0
  29. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/__init__.py +0 -0
  30. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/__main__.py +0 -0
  31. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/boot/__init__.py +0 -0
  32. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/boot/config.py +0 -0
  33. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/boot/create_policy.py +0 -0
  34. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/boot/embeddings.py +0 -0
  35. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/boot/home.py +0 -0
  36. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/boot/migrations.py +0 -0
  37. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/boot/repos.py +0 -0
  38. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/boot/retrieval.py +0 -0
  39. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/boot/thresholds.py +0 -0
  40. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/boot/update_policy.py +0 -0
  41. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/boot/use_cases.py +0 -0
  42. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/config/__init__.py +0 -0
  43. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/config/defaults/create_policy.yaml +0 -0
  44. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/config/defaults/read_policy.yaml +0 -0
  45. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/config/defaults/runtime.yaml +0 -0
  46. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/config/defaults/thresholds.yaml +0 -0
  47. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/config/defaults/update_policy.yaml +0 -0
  48. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/config/loader.py +0 -0
  49. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/__init__.py +0 -0
  50. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/contracts/__init__.py +0 -0
  51. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/contracts/errors.py +0 -0
  52. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/contracts/responses.py +0 -0
  53. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/entities/__init__.py +0 -0
  54. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/entities/episodes.py +0 -0
  55. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/entities/evidence.py +0 -0
  56. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/entities/facts.py +0 -0
  57. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/entities/guidance.py +0 -0
  58. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/entities/identity.py +0 -0
  59. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/entities/runtime_context.py +0 -0
  60. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/entities/session_state.py +0 -0
  61. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/entities/telemetry.py +0 -0
  62. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/entities/utility.py +0 -0
  63. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/interfaces/__init__.py +0 -0
  64. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/interfaces/clock.py +0 -0
  65. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/interfaces/config.py +0 -0
  66. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/interfaces/embeddings.py +0 -0
  67. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/interfaces/idgen.py +0 -0
  68. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/interfaces/repos.py +0 -0
  69. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/interfaces/retrieval.py +0 -0
  70. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/interfaces/session_state_store.py +0 -0
  71. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/interfaces/unit_of_work.py +0 -0
  72. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/policies/__init__.py +0 -0
  73. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/policies/_shared/__init__.py +0 -0
  74. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/policies/_shared/executor.py +0 -0
  75. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/policies/_shared/side_effects.py +0 -0
  76. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/policies/create_policy/__init__.py +0 -0
  77. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/policies/create_policy/pipeline.py +0 -0
  78. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/policies/read_policy/__init__.py +0 -0
  79. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/policies/read_policy/bm25.py +0 -0
  80. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/policies/read_policy/context_pack_builder.py +0 -0
  81. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/policies/read_policy/expansion.py +0 -0
  82. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/policies/read_policy/fusion_rrf.py +0 -0
  83. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/policies/read_policy/lexical_query.py +0 -0
  84. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/policies/read_policy/scoring.py +0 -0
  85. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/policies/read_policy/seed_retrieval.py +0 -0
  86. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/policies/update_policy/__init__.py +0 -0
  87. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/policies/update_policy/pipeline.py +0 -0
  88. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/use_cases/__init__.py +0 -0
  89. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/use_cases/build_guidance.py +0 -0
  90. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/use_cases/create_memory.py +0 -0
  91. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/use_cases/manage_session_state.py +0 -0
  92. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/use_cases/read_memory.py +0 -0
  93. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/use_cases/record_episode_sync_telemetry.py +0 -0
  94. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/use_cases/record_operation_telemetry.py +0 -0
  95. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/use_cases/sync_episode.py +0 -0
  96. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/core/use_cases/update_memory.py +0 -0
  97. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/migrations/__init__.py +0 -0
  98. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/migrations/env.py +0 -0
  99. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/migrations/versions/20260226_0001_initial_schema.py +0 -0
  100. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/migrations/versions/20260312_0002_add_hard_invariants.py +0 -0
  101. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/migrations/versions/20260312_0003_drop_create_confidence.py +0 -0
  102. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/migrations/versions/20260313_0004_episode_sync_hardening.py +0 -0
  103. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/migrations/versions/20260313_0005_evidence_episode_event_refs.py +0 -0
  104. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/migrations/versions/20260318_0006_usage_telemetry_schema.py +0 -0
  105. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/migrations/versions/20260319_0007_identity_session_guidance.py +0 -0
  106. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/migrations/versions/20260320_0008_instance_metadata_and_backup_safety.py +0 -0
  107. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/migrations/versions/__init__.py +0 -0
  108. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/onboarding_assets/__init__.py +0 -0
  109. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/onboarding_assets/claude/CLAUDE.md +0 -0
  110. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/onboarding_assets/claude/skills/shellbrain-usage-review/SKILL.md +0 -0
  111. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/onboarding_assets/codex/AGENTS.md +0 -0
  112. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/onboarding_assets/codex/shellbrain-session-start/agents/openai.yaml +0 -0
  113. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/onboarding_assets/codex/shellbrain-session-start/assets/shellbrain-large.svg +0 -0
  114. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/onboarding_assets/codex/shellbrain-session-start/assets/shellbrain-small.svg +0 -0
  115. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/onboarding_assets/codex/shellbrain-session-start/assets/shellbrain_logo.png +0 -0
  116. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/onboarding_assets/codex/shellbrain-session-start/references/request-shapes.md +0 -0
  117. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/onboarding_assets/codex/shellbrain-usage-review/SKILL.md +0 -0
  118. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/onboarding_assets/codex/shellbrain-usage-review/agents/openai.yaml +0 -0
  119. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/onboarding_assets/codex/shellbrain-usage-review/assets/shellbrain-small.svg +0 -0
  120. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/onboarding_assets/codex/shellbrain-usage-review/assets/shellbrain_logo.png +0 -0
  121. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/onboarding_assets/cursor/skills/shellbrain-usage-review/SKILL.md +0 -0
  122. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/__init__.py +0 -0
  123. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/admin/__init__.py +0 -0
  124. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/admin/agent_behavior_analysis.py +0 -0
  125. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/admin/analytics.py +0 -0
  126. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/admin/analytics_diagnostics.py +0 -0
  127. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/admin/analytics_queries.py +0 -0
  128. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/admin/backup.py +0 -0
  129. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/admin/destructive_guard.py +0 -0
  130. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/admin/doctor.py +0 -0
  131. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/admin/external_runtime.py +0 -0
  132. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/admin/init.py +0 -0
  133. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/admin/init_errors.py +0 -0
  134. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/admin/instance_guard.py +0 -0
  135. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/admin/machine_state.py +0 -0
  136. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/admin/managed_runtime.py +0 -0
  137. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/admin/privileges.py +0 -0
  138. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/admin/restore.py +0 -0
  139. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/admin/storage_setup.py +0 -0
  140. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/admin/upgrade.py +0 -0
  141. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/cli/__init__.py +0 -0
  142. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/cli/handlers.py +0 -0
  143. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/cli/hydration.py +0 -0
  144. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/cli/main.py +0 -0
  145. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/cli/presenter_json.py +0 -0
  146. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/__init__.py +0 -0
  147. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/engine.py +0 -0
  148. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/models/__init__.py +0 -0
  149. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/models/associations.py +0 -0
  150. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/models/episodes.py +0 -0
  151. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/models/evidence.py +0 -0
  152. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/models/experiences.py +0 -0
  153. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/models/instance_metadata.py +0 -0
  154. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/models/memories.py +0 -0
  155. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/models/metadata.py +0 -0
  156. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/models/registry.py +0 -0
  157. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/models/telemetry.py +0 -0
  158. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/models/utility.py +0 -0
  159. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/models/views.py +0 -0
  160. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/repos/__init__.py +0 -0
  161. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/repos/relational/__init__.py +0 -0
  162. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/repos/relational/associations_repo.py +0 -0
  163. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/repos/relational/episodes_repo.py +0 -0
  164. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/repos/relational/evidence_repo.py +0 -0
  165. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/repos/relational/experiences_repo.py +0 -0
  166. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/repos/relational/memories_repo.py +0 -0
  167. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/repos/relational/telemetry_repo.py +0 -0
  168. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/repos/relational/utility_repo.py +0 -0
  169. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/repos/semantic/__init__.py +0 -0
  170. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/repos/semantic/keyword_retrieval_repo.py +0 -0
  171. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/repos/semantic/semantic_retrieval_repo.py +0 -0
  172. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/session.py +0 -0
  173. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/db/uow.py +0 -0
  174. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/embeddings/__init__.py +0 -0
  175. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/embeddings/local_provider.py +0 -0
  176. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/embeddings/query_vector_search.py +0 -0
  177. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/episodes/__init__.py +0 -0
  178. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/episodes/claude_code.py +0 -0
  179. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/episodes/codex.py +0 -0
  180. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/episodes/cursor.py +0 -0
  181. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/episodes/launcher.py +0 -0
  182. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/episodes/normalization.py +0 -0
  183. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/episodes/poller.py +0 -0
  184. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/episodes/poller_lock.py +0 -0
  185. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/episodes/source_discovery.py +0 -0
  186. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/episodes/tool_filter.py +0 -0
  187. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/identity/__init__.py +0 -0
  188. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/identity/claude_hook_install.py +0 -0
  189. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/identity/codex_runtime.py +0 -0
  190. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/identity/resolver.py +0 -0
  191. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/metrics/__init__.py +0 -0
  192. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/metrics/artifacts.py +0 -0
  193. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/metrics/browser.py +0 -0
  194. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/metrics/queries.py +0 -0
  195. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/metrics/render_html.py +0 -0
  196. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/metrics/service.py +0 -0
  197. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/onboarding/__init__.py +0 -0
  198. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/onboarding/host_assets.py +0 -0
  199. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/session_state/__init__.py +0 -0
  200. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/session_state/file_store.py +0 -0
  201. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/telemetry/__init__.py +0 -0
  202. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/telemetry/operation_summary.py +0 -0
  203. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/telemetry/session_selection.py +0 -0
  204. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/telemetry/sync_summary.py +0 -0
  205. {shellbrain-0.1.20 → shellbrain-0.1.22}/app/periphery/validation/__init__.py +0 -0
  206. {shellbrain-0.1.20 → shellbrain-0.1.22}/setup.cfg +0 -0
  207. {shellbrain-0.1.20 → shellbrain-0.1.22}/shellbrain.egg-info/dependency_links.txt +0 -0
  208. {shellbrain-0.1.20 → shellbrain-0.1.22}/shellbrain.egg-info/entry_points.txt +0 -0
  209. {shellbrain-0.1.20 → shellbrain-0.1.22}/shellbrain.egg-info/requires.txt +0 -0
  210. {shellbrain-0.1.20 → shellbrain-0.1.22}/shellbrain.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shellbrain
3
- Version: 0.1.20
3
+ Version: 0.1.22
4
4
  Summary: Repo-scoped Shellbrain CLI with explicit evidence-backed writes.
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -0,0 +1,45 @@
1
+ """Shared helpers for resolving configured application and admin DSNs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from typing import Any, Callable
7
+
8
+
9
+ def resolve_database_dsn(
10
+ *,
11
+ load_machine_config: Callable[[], tuple[object | None, str | None]],
12
+ runtime_provider: Callable[[], dict[str, Any]],
13
+ machine_field: str,
14
+ runtime_env_key: str,
15
+ required: bool,
16
+ ) -> str | None:
17
+ """Resolve one configured DSN from machine config first, then runtime env config."""
18
+
19
+ machine_config, machine_error = load_machine_config()
20
+ if machine_error:
21
+ if required:
22
+ raise RuntimeError(
23
+ "Shellbrain machine config is unreadable. Rerun `shellbrain init` to repair it."
24
+ )
25
+ return None
26
+ if machine_config is not None:
27
+ return getattr(machine_config.database, machine_field)
28
+
29
+ runtime = runtime_provider()
30
+ database = runtime.get("database")
31
+ if not isinstance(database, dict):
32
+ if required:
33
+ raise RuntimeError("runtime.database must be configured")
34
+ return None
35
+
36
+ env_name = database.get(runtime_env_key)
37
+ if not isinstance(env_name, str) or not env_name:
38
+ if required:
39
+ raise RuntimeError(f"runtime.database.{runtime_env_key} must be configured")
40
+ return None
41
+
42
+ dsn = os.getenv(env_name)
43
+ if not dsn and required:
44
+ raise RuntimeError(f"{env_name} is not set")
45
+ return dsn or None
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import os
6
6
  from pathlib import Path
7
7
 
8
+ from app.boot._dsn_resolution import resolve_database_dsn
8
9
  from app.boot.config import get_config_provider
9
10
  from app.boot.home import get_machine_backups_dir
10
11
  from app.periphery.admin.machine_state import try_load_machine_config
@@ -13,44 +14,15 @@ from app.periphery.admin.machine_state import try_load_machine_config
13
14
  def get_admin_db_dsn() -> str:
14
15
  """Resolve the privileged admin DSN from environment-backed runtime config."""
15
16
 
16
- machine_config, machine_error = try_load_machine_config()
17
- if machine_error:
18
- raise RuntimeError(
19
- "Shellbrain machine config is unreadable. Rerun `shellbrain init` to repair it."
20
- )
21
- if machine_config is not None:
22
- return machine_config.database.admin_dsn
23
-
24
- runtime = get_config_provider().get_runtime()
25
- database = runtime.get("database")
26
- if not isinstance(database, dict):
27
- raise RuntimeError("runtime.database must be configured")
28
- admin_dsn_env = database.get("admin_dsn_env")
29
- if not isinstance(admin_dsn_env, str) or not admin_dsn_env:
30
- raise RuntimeError("runtime.database.admin_dsn_env must be configured")
31
- dsn = os.getenv(admin_dsn_env)
32
- if not dsn:
33
- raise RuntimeError(f"{admin_dsn_env} is not set")
17
+ dsn = _resolve_admin_db_dsn(required=True)
18
+ assert dsn is not None
34
19
  return dsn
35
20
 
36
21
 
37
22
  def get_optional_admin_db_dsn() -> str | None:
38
23
  """Resolve the privileged admin DSN when present, otherwise return None."""
39
24
 
40
- machine_config, machine_error = try_load_machine_config()
41
- if machine_error:
42
- return None
43
- if machine_config is not None:
44
- return machine_config.database.admin_dsn
45
-
46
- runtime = get_config_provider().get_runtime()
47
- database = runtime.get("database")
48
- if not isinstance(database, dict):
49
- return None
50
- admin_dsn_env = database.get("admin_dsn_env")
51
- if not isinstance(admin_dsn_env, str) or not admin_dsn_env:
52
- return None
53
- return os.getenv(admin_dsn_env)
25
+ return _resolve_admin_db_dsn(required=False)
54
26
 
55
27
 
56
28
  def get_backup_dir() -> Path:
@@ -86,3 +58,15 @@ def get_instance_mode_default() -> str:
86
58
  """Resolve the default instance mode used when stamping metadata for the current DB."""
87
59
 
88
60
  return os.getenv("SHELLBRAIN_INSTANCE_MODE", "live").strip().lower() or "live"
61
+
62
+
63
+ def _resolve_admin_db_dsn(*, required: bool) -> str | None:
64
+ """Resolve the admin DSN from machine config or runtime env config."""
65
+
66
+ return resolve_database_dsn(
67
+ load_machine_config=try_load_machine_config,
68
+ runtime_provider=lambda: get_config_provider().get_runtime(),
69
+ machine_field="admin_dsn",
70
+ runtime_env_key="admin_dsn_env",
71
+ required=required,
72
+ )
@@ -0,0 +1,45 @@
1
+ """This module defines boot-time factory helpers for database engine and sessions."""
2
+
3
+ from app.boot._dsn_resolution import resolve_database_dsn
4
+ from app.boot.config import get_config_provider
5
+ from app.periphery.admin.machine_state import try_load_machine_config
6
+ from app.periphery.db.engine import get_engine
7
+ from app.periphery.db.session import get_session_factory
8
+
9
+
10
+ def get_db_dsn() -> str:
11
+ """This function resolves the database DSN from environment configuration."""
12
+
13
+ dsn = _resolve_app_db_dsn(required=True)
14
+ assert dsn is not None
15
+ return dsn
16
+
17
+
18
+ def get_optional_db_dsn() -> str | None:
19
+ """Resolve the application DSN when present, otherwise return None."""
20
+
21
+ return _resolve_app_db_dsn(required=False)
22
+
23
+
24
+ def get_engine_instance():
25
+ """This function builds a shared SQLAlchemy engine for the application."""
26
+
27
+ return get_engine(get_db_dsn())
28
+
29
+
30
+ def get_session_factory_instance():
31
+ """This function builds a reusable SQLAlchemy session factory for the app."""
32
+
33
+ return get_session_factory(get_engine_instance())
34
+
35
+
36
+ def _resolve_app_db_dsn(*, required: bool) -> str | None:
37
+ """Resolve the application DSN from machine config or runtime env config."""
38
+
39
+ return resolve_database_dsn(
40
+ load_machine_config=try_load_machine_config,
41
+ runtime_provider=lambda: get_config_provider().get_runtime(),
42
+ machine_field="app_dsn",
43
+ runtime_env_key="dsn_env",
44
+ required=required,
45
+ )
@@ -4,6 +4,7 @@ from copy import deepcopy
4
4
  from typing import Any
5
5
 
6
6
  from app.boot.config import get_config_provider
7
+ from app.core.entities.memory import MATURE_MEMORY_KIND_VALUES
7
8
 
8
9
 
9
10
  _SUPPORTED_MODES = ("targeted", "ambient")
@@ -164,6 +165,8 @@ def resolve_read_payload_defaults(payload: dict[str, Any]) -> dict[str, Any]:
164
165
  resolved["include_global"] = settings["include_global"]
165
166
  if resolved.get("limit") is None:
166
167
  resolved["limit"] = settings["limits_by_mode"][resolved["mode"]]
168
+ if resolved.get("kinds") is None:
169
+ resolved["kinds"] = list(MATURE_MEMORY_KIND_VALUES)
167
170
 
168
171
  incoming_expand = resolved.get("expand")
169
172
  merged_expand = deepcopy(settings["expand"])
@@ -5,6 +5,10 @@ from typing import Annotated, Literal
5
5
  from pydantic import BaseModel, ConfigDict, Field, field_validator
6
6
 
7
7
 
8
+ MemoryKindValue = Literal["problem", "solution", "failed_tactic", "fact", "preference", "change", "frontier"]
9
+ AssociationRelationValue = Literal["depends_on", "associated_with", "matures_into"]
10
+
11
+
8
12
  class StrictBaseModel(BaseModel):
9
13
  """This base model enforces strict schemas by rejecting unknown fields."""
10
14
 
@@ -30,9 +34,7 @@ class MemoryReadRequest(StrictBaseModel):
30
34
  mode: Literal["ambient", "targeted"]
31
35
  query: str = Field(min_length=1)
32
36
  include_global: bool | None = None
33
- kinds: (
34
- list[Literal["problem", "solution", "failed_tactic", "fact", "preference", "change"]] | None
35
- ) = None
37
+ kinds: list[MemoryKindValue] | None = Field(default=None, min_length=1)
36
38
  limit: int | None = Field(default=None, ge=1, le=100)
37
39
  expand: ReadExpandRequest | None = None
38
40
 
@@ -40,8 +42,8 @@ class MemoryReadRequest(StrictBaseModel):
40
42
  @classmethod
41
43
  def _validate_kinds_unique(
42
44
  cls,
43
- value: list[Literal["problem", "solution", "failed_tactic", "fact", "preference", "change"]] | None,
44
- ) -> list[Literal["problem", "solution", "failed_tactic", "fact", "preference", "change"]] | None:
45
+ value: list[MemoryKindValue] | None,
46
+ ) -> list[MemoryKindValue] | None:
45
47
  """This validator enforces unique kinds filters for read requests."""
46
48
 
47
49
  if value is None:
@@ -63,7 +65,7 @@ class MemoryCreateAssociationLink(StrictBaseModel):
63
65
  """This model defines a typed explicit association link payload on create."""
64
66
 
65
67
  to_memory_id: str
66
- relation_type: Literal["depends_on", "associated_with"]
68
+ relation_type: AssociationRelationValue
67
69
  confidence: float | None = Field(default=None, ge=0.0, le=1.0)
68
70
  salience: float | None = Field(default=None, ge=0.0, le=1.0)
69
71
  rationale: str | None = None
@@ -91,7 +93,7 @@ class MemoryCreateBody(StrictBaseModel):
91
93
 
92
94
  text: str
93
95
  scope: Literal["repo", "global"]
94
- kind: Literal["problem", "solution", "failed_tactic", "fact", "preference", "change"]
96
+ kind: MemoryKindValue
95
97
  rationale: str | None = None
96
98
  links: MemoryCreateLinks = Field(default_factory=MemoryCreateLinks)
97
99
  evidence_refs: list[str] = Field(min_length=1)
@@ -165,7 +167,7 @@ class AssociationLinkUpdate(StrictBaseModel):
165
167
 
166
168
  type: Literal["association_link"]
167
169
  to_memory_id: str
168
- relation_type: Literal["depends_on", "associated_with"]
170
+ relation_type: AssociationRelationValue
169
171
  confidence: float | None = Field(default=None, ge=0.0, le=1.0)
170
172
  salience: float | None = Field(default=None, ge=0.0, le=1.0)
171
173
  rationale: str | None = None
@@ -9,6 +9,7 @@ class AssociationRelationType(str, Enum):
9
9
 
10
10
  DEPENDS_ON = "depends_on"
11
11
  ASSOCIATED_WITH = "associated_with"
12
+ MATURES_INTO = "matures_into"
12
13
 
13
14
 
14
15
  class AssociationSourceMode(str, Enum):
@@ -2,6 +2,7 @@
2
2
 
3
3
  from dataclasses import dataclass
4
4
  from enum import Enum
5
+ from typing import Final
5
6
 
6
7
 
7
8
  class MemoryKind(str, Enum):
@@ -13,6 +14,25 @@ class MemoryKind(str, Enum):
13
14
  FACT = "fact"
14
15
  PREFERENCE = "preference"
15
16
  CHANGE = "change"
17
+ FRONTIER = "frontier"
18
+
19
+
20
+ MATURE_MEMORY_KINDS: Final[tuple[MemoryKind, ...]] = (
21
+ MemoryKind.PROBLEM,
22
+ MemoryKind.SOLUTION,
23
+ MemoryKind.FAILED_TACTIC,
24
+ MemoryKind.FACT,
25
+ MemoryKind.PREFERENCE,
26
+ MemoryKind.CHANGE,
27
+ )
28
+ MATURE_MEMORY_KIND_VALUES: Final[tuple[str, ...]] = tuple(kind.value for kind in MATURE_MEMORY_KINDS)
29
+
30
+
31
+ def is_mature_memory_kind(kind: MemoryKind | str) -> bool:
32
+ """Return whether one kind belongs to the mature durable-memory set."""
33
+
34
+ normalized_kind = kind if isinstance(kind, MemoryKind) else MemoryKind(kind)
35
+ return normalized_kind in MATURE_MEMORY_KINDS
16
36
 
17
37
 
18
38
  class MemoryScope(str, Enum):
@@ -8,10 +8,8 @@ from app.core.interfaces.retrieval import IVectorSearch
8
8
  from app.core.policies.read_policy.context_pack_builder import assemble_context_pack
9
9
  from app.core.policies.read_policy.expansion import expand_candidates
10
10
  from app.core.policies.read_policy.fusion_rrf import fuse_with_rrf
11
- from app.core.policies.read_policy.scenario_lift import derive_scenarios
12
11
  from app.core.policies.read_policy.scoring import score_candidates
13
12
  from app.core.policies.read_policy.seed_retrieval import retrieve_seeds
14
- from app.core.policies.read_policy.utility_prior import apply_utility_prior
15
13
 
16
14
 
17
15
  def build_context_pack(
@@ -45,13 +43,8 @@ def build_context_pack(
45
43
  "implicit": expanded_candidates["implicit"],
46
44
  }
47
45
  scored_candidates = score_candidates(bucketed_candidates, payload)
48
- adjusted_candidates = {
49
- bucket_name: apply_utility_prior(candidates, payload)
50
- for bucket_name, candidates in scored_candidates.items()
51
- }
52
- pack = assemble_context_pack(adjusted_candidates, payload)
53
- hydrated_pack = _hydrate_pack_items(pack, memories)
54
- return derive_scenarios(hydrated_pack, payload)["pack"]
46
+ pack = assemble_context_pack(scored_candidates, payload)
47
+ return _hydrate_pack_items(pack, memories)
55
48
 
56
49
 
57
50
  def _resolve_read_defaults(payload: dict[str, Any]) -> dict[str, Any]:
@@ -0,0 +1,96 @@
1
+ """Add frontier memory kind and matures_into association relation constraints."""
2
+
3
+ from alembic import op
4
+
5
+
6
+ revision = "20260410_0009"
7
+ down_revision = "20260320_0008"
8
+ branch_labels = None
9
+ depends_on = None
10
+
11
+
12
+ def _drop_check_constraint(table_name: str, required_snippets: list[str]) -> None:
13
+ """Drop one unnamed CHECK constraint by matching its rendered definition."""
14
+
15
+ like_clauses = " AND ".join(
16
+ f"pg_get_constraintdef(con.oid) LIKE '%{snippet}%'" for snippet in required_snippets
17
+ )
18
+ op.execute(
19
+ f"""
20
+ DO $$
21
+ DECLARE constraint_name TEXT;
22
+ BEGIN
23
+ SELECT con.conname
24
+ INTO constraint_name
25
+ FROM pg_constraint con
26
+ JOIN pg_class rel ON rel.oid = con.conrelid
27
+ JOIN pg_namespace nsp ON nsp.oid = rel.relnamespace
28
+ WHERE nsp.nspname = current_schema()
29
+ AND rel.relname = '{table_name}'
30
+ AND con.contype = 'c'
31
+ AND {like_clauses}
32
+ LIMIT 1;
33
+
34
+ IF constraint_name IS NOT NULL THEN
35
+ EXECUTE format('ALTER TABLE {table_name} DROP CONSTRAINT %I', constraint_name);
36
+ END IF;
37
+ END $$;
38
+ """
39
+ )
40
+
41
+
42
+ def upgrade() -> None:
43
+ """Widen durable-memory and association relation constraints for frontier support."""
44
+
45
+ _drop_check_constraint("memories", ["kind", "problem", "change"])
46
+ _drop_check_constraint("association_edges", ["relation_type", "depends_on", "associated_with"])
47
+ _drop_check_constraint("association_observations", ["relation_type", "depends_on", "associated_with"])
48
+
49
+ op.execute(
50
+ """
51
+ ALTER TABLE memories
52
+ ADD CONSTRAINT ck_memories_kind_ratified
53
+ CHECK (kind IN ('problem', 'solution', 'failed_tactic', 'fact', 'preference', 'change', 'frontier'));
54
+
55
+ ALTER TABLE association_edges
56
+ ADD CONSTRAINT ck_association_edges_relation_type
57
+ CHECK (relation_type IN ('depends_on', 'associated_with', 'matures_into'));
58
+
59
+ ALTER TABLE association_observations
60
+ ADD CONSTRAINT ck_association_observations_relation_type
61
+ CHECK (relation_type IN ('depends_on', 'associated_with', 'matures_into'));
62
+ """
63
+ )
64
+
65
+
66
+ def downgrade() -> None:
67
+ """Restore the prior durable-memory and association relation constraint set."""
68
+
69
+ op.execute(
70
+ """
71
+ ALTER TABLE association_observations
72
+ DROP CONSTRAINT IF EXISTS ck_association_observations_relation_type;
73
+
74
+ ALTER TABLE association_edges
75
+ DROP CONSTRAINT IF EXISTS ck_association_edges_relation_type;
76
+
77
+ ALTER TABLE memories
78
+ DROP CONSTRAINT IF EXISTS ck_memories_kind_ratified;
79
+ """
80
+ )
81
+
82
+ op.execute(
83
+ """
84
+ ALTER TABLE memories
85
+ ADD CONSTRAINT ck_memories_kind_ratified
86
+ CHECK (kind IN ('problem', 'solution', 'failed_tactic', 'fact', 'preference', 'change'));
87
+
88
+ ALTER TABLE association_edges
89
+ ADD CONSTRAINT ck_association_edges_relation_type
90
+ CHECK (relation_type IN ('depends_on', 'associated_with'));
91
+
92
+ ALTER TABLE association_observations
93
+ ADD CONSTRAINT ck_association_observations_relation_type
94
+ CHECK (relation_type IN ('depends_on', 'associated_with'));
95
+ """
96
+ )
@@ -26,10 +26,10 @@ Treat current repo state as ground truth. Treat Shellbrain as advisory long-term
26
26
  2. Otherwise, do not rerun `init` just because a new agent session started. Start with focused `read` queries right away.
27
27
  3. If readiness is unclear, inspect with `shellbrain admin doctor` instead of rerunning `init` by reflex.
28
28
  4. If `doctor` reports `repair_needed`, rerun `shellbrain init` instead of trying to repair Shellbrain manually.
29
- 5. In Claude Code, if direct `shellbrain` calls fail in the current session, retry through a login shell that sources the user's login profile:
30
- - `zsh -lc 'source ~/.zprofile >/dev/null 2>&1; shellbrain --help'`
31
- - `bash -lc 'source ~/.bash_profile >/dev/null 2>&1; shellbrain --help'`
32
- 6. If the wrapped login-shell check still cannot find `shellbrain`, inspect Python's user script directory:
29
+ 5. In Claude Code, if direct `shellbrain` calls fail in the current session, do a one-time login-shell retry that sources the user's login profile and resolves the CLI path:
30
+ - `zsh -lc 'source ~/.zprofile >/dev/null 2>&1; command -v shellbrain'`
31
+ - `bash -lc 'source ~/.bash_profile >/dev/null 2>&1; command -v shellbrain'`
32
+ 6. Do not keep sourcing the login profile on every Shellbrain command. Once `shellbrain` is visible, use plain `shellbrain ...`. If the host tool keeps starting fresh shells without your login PATH, reuse the absolute path returned by `command -v shellbrain` instead of re-sourcing the profile on every call. If the one-time login-shell retry still cannot find `shellbrain`, inspect Python's user script directory:
33
33
  - `python3 -c "import sysconfig; print(sysconfig.get_path('scripts', 'posix_user'))"`
34
34
  If `shellbrain` exists there, call it directly or add that directory to the login profile PATH and retry. If it does not, reinstall the Shellbrain CLI.
35
35
  7. Resolve the target repo:
@@ -31,10 +31,10 @@ Treat current repo state as ground truth. Treat Shellbrain as advisory long-term
31
31
  2. Otherwise, do not rerun `init` just because a new agent session started. Start with focused `read` queries right away.
32
32
  3. If readiness is unclear, inspect with `shellbrain admin doctor` instead of rerunning `init` by reflex.
33
33
  4. If `doctor` reports `repair_needed`, rerun `shellbrain init` instead of trying to repair Shellbrain manually.
34
- 5. In Codex or similar tool shells, if direct `shellbrain` calls fail in the current session, retry through a login shell that sources the user's login profile:
35
- - `zsh -lc 'source ~/.zprofile >/dev/null 2>&1; shellbrain --help'`
36
- - `bash -lc 'source ~/.bash_profile >/dev/null 2>&1; shellbrain --help'`
37
- 6. If the wrapped login-shell check still cannot find `shellbrain`, inspect Python's user script directory:
34
+ 5. In Codex or similar tool shells, if direct `shellbrain` calls fail in the current session, do a one-time login-shell retry that sources the user's login profile and resolves the CLI path:
35
+ - `zsh -lc 'source ~/.zprofile >/dev/null 2>&1; command -v shellbrain'`
36
+ - `bash -lc 'source ~/.bash_profile >/dev/null 2>&1; command -v shellbrain'`
37
+ 6. Do not keep sourcing the login profile on every Shellbrain command. Once `shellbrain` is visible, use plain `shellbrain ...`. If the host tool keeps starting fresh shells without your login PATH, reuse the absolute path returned by `command -v shellbrain` instead of re-sourcing the profile on every call. If the one-time login-shell retry still cannot find `shellbrain`, inspect Python's user script directory:
38
38
  - `python3 -c "import sysconfig; print(sysconfig.get_path('scripts', 'posix_user'))"`
39
39
  If `shellbrain` exists there, call it directly or add that directory to the login profile PATH and retry. If it does not, reinstall the Shellbrain CLI.
40
40
  7. Resolve the target repo:
@@ -43,13 +43,13 @@ Use Shellbrain normally after `doctor` shows the machine bootstrap is `ready` an
43
43
 
44
44
  If `doctor` reports `repair_needed`, rerun `shellbrain init` instead of trying to patch Shellbrain manually.
45
45
 
46
- In Codex desktop and similar tool shells, if direct `shellbrain` calls fail in the current session, retry through a login shell that sources `~/.zprofile`:
46
+ In Codex desktop and similar tool shells, if direct `shellbrain` calls fail in the current session, do a one-time login-shell retry that sources `~/.zprofile` and resolves the CLI path:
47
47
 
48
48
  ```bash
49
- zsh -lc 'source ~/.zprofile >/dev/null 2>&1; shellbrain --help'
49
+ zsh -lc 'source ~/.zprofile >/dev/null 2>&1; command -v shellbrain'
50
50
  ```
51
51
 
52
- Assume Shellbrain comes from a one-time machine install. Do not rerun `init` just because a new agent starts. If direct calls fail in the current Codex session, keep using the `zsh -lc 'source ~/.zprofile ...'` wrapper for Shellbrain invocations before declaring Shellbrain blocked. Only if the wrapped check fails should you ask the operator to restore the machine-level install.
52
+ Assume Shellbrain comes from a one-time machine install. Do not rerun `init` just because a new agent starts. Do not keep sourcing the login profile on every Shellbrain command. The login-shell step is only a one-time availability and PATH check when direct calls fail. Once `shellbrain` is visible, use plain `shellbrain ...`. If the host tool keeps starting fresh shells without your login PATH, resolve the absolute path once with `command -v shellbrain` and reuse that path for later invocations. Only if the one-time login-shell retry fails should you ask the operator to restore the machine-level install.
53
53
 
54
54
  Drop into the advanced/operator path only when `doctor` says the managed runtime is blocked.
55
55
 
@@ -228,7 +228,7 @@ Important modeling pattern for changed truth:
228
228
  Do not rerun `init`. Start with `read`. Use `doctor` only if readiness is unclear.
229
229
 
230
230
  - `shellbrain: command not found`
231
- Retry through `zsh -lc 'source ~/.zprofile >/dev/null 2>&1; shellbrain --help'` first. Only if that still fails should you ask the operator to restore the one-time machine install.
231
+ Retry through `zsh -lc 'source ~/.zprofile >/dev/null 2>&1; command -v shellbrain'` first. Do not keep prefixing every Shellbrain command with profile sourcing. Only if that still fails should you ask the operator to restore the one-time machine install.
232
232
 
233
233
  - `shellbrain init` fails or `doctor` shows `repair_needed`
234
234
  Rerun `shellbrain init`. That is the normal repair path.
@@ -27,10 +27,10 @@ Shellbrain has two layers:
27
27
  2. Otherwise, do not rerun `init` just because a new agent session started. Start with focused `read` queries right away.
28
28
  3. If readiness is unclear, inspect with `shellbrain admin doctor` instead of rerunning `init` by reflex.
29
29
  4. If `doctor` reports `repair_needed`, rerun `shellbrain init`.
30
- 5. If direct `shellbrain` calls fail in the current Cursor shell, retry through a login shell that sources the user's login profile:
31
- - `zsh -lc 'source ~/.zprofile >/dev/null 2>&1; shellbrain --help'`
32
- - `bash -lc 'source ~/.bash_profile >/dev/null 2>&1; shellbrain --help'`
33
- 6. If the wrapped login-shell check still cannot find `shellbrain`, inspect Python's user script directory:
30
+ 5. If direct `shellbrain` calls fail in the current Cursor shell, do a one-time login-shell retry that sources the user's login profile and resolves the CLI path:
31
+ - `zsh -lc 'source ~/.zprofile >/dev/null 2>&1; command -v shellbrain'`
32
+ - `bash -lc 'source ~/.bash_profile >/dev/null 2>&1; command -v shellbrain'`
33
+ 6. Do not keep sourcing the login profile on every Shellbrain command. Once `shellbrain` is visible, use plain `shellbrain ...`. If the host tool keeps starting fresh shells without your login PATH, reuse the absolute path returned by `command -v shellbrain` instead of re-sourcing the profile on every call. If the one-time login-shell retry still cannot find `shellbrain`, inspect Python's user script directory:
34
34
  - `python3 -c "import sysconfig; print(sysconfig.get_path('scripts', 'posix_user'))"`
35
35
  7. Resolve the target repo:
36
36
  - use the current working directory when already inside the repo
@@ -124,35 +124,6 @@ def save_repo_registration(registration: RepoRegistration, repo_root: Path) -> P
124
124
  return path
125
125
 
126
126
 
127
- def register_repo(
128
- *,
129
- repo_root: Path,
130
- machine_instance_id: str,
131
- explicit_repo_id: str | None = None,
132
- claude_status: str = "not_checked",
133
- claude_settings_path: str | None = None,
134
- claude_note: str | None = None,
135
- ) -> RepoRegistration:
136
- """Resolve and persist one repo registration."""
137
-
138
- identity = resolve_repo_identity(repo_root=repo_root, explicit_repo_id=explicit_repo_id)
139
- registration_root = Path(identity.git_root).resolve() if identity.git_root is not None else Path(repo_root).resolve()
140
- registration = RepoRegistration(
141
- repo_state_version=REPO_STATE_VERSION,
142
- repo_id=identity.repo_id,
143
- identity_strength=identity.identity_strength,
144
- git_root=identity.git_root,
145
- source_remote=identity.source_remote,
146
- registered_at=datetime.now(timezone.utc).isoformat(),
147
- machine_instance_id=machine_instance_id,
148
- claude_status=claude_status,
149
- claude_settings_path=claude_settings_path,
150
- claude_note=claude_note,
151
- )
152
- save_repo_registration(registration, registration_root)
153
- return registration
154
-
155
-
156
127
  def register_repo_for_target(
157
128
  *,
158
129
  repo_root: Path,
@@ -8,6 +8,7 @@ from app.core.contracts.errors import ErrorCode, ErrorDetail
8
8
  from app.core.contracts.requests import (
9
9
  BatchUtilityVoteItem,
10
10
  EpisodeEventsRequest,
11
+ MemoryKindValue,
11
12
  MemoryBatchUpdateRequest,
12
13
  MemoryCreateLinks,
13
14
  MemoryCreateRequest,
@@ -27,17 +28,15 @@ class AgentReadRequest(StrictBaseModel):
27
28
  mode: Literal["ambient", "targeted"] | None = None
28
29
  query: str = Field(min_length=1)
29
30
  include_global: bool | None = None
30
- kinds: (
31
- list[Literal["problem", "solution", "failed_tactic", "fact", "preference", "change"]] | None
32
- ) = None
31
+ kinds: list[MemoryKindValue] | None = Field(default=None, min_length=1)
33
32
  limit: int | None = Field(default=None, ge=1, le=100)
34
33
 
35
34
  @field_validator("kinds")
36
35
  @classmethod
37
36
  def _validate_kinds_unique(
38
37
  cls,
39
- value: list[Literal["problem", "solution", "failed_tactic", "fact", "preference", "change"]] | None,
40
- ) -> list[Literal["problem", "solution", "failed_tactic", "fact", "preference", "change"]] | None:
38
+ value: list[MemoryKindValue] | None,
39
+ ) -> list[MemoryKindValue] | None:
41
40
  """This validator enforces unique kinds filters for agent read requests."""
42
41
 
43
42
  if value is None:
@@ -52,7 +51,7 @@ class AgentCreateBody(StrictBaseModel):
52
51
 
53
52
  text: str
54
53
  scope: Literal["repo", "global"] | None = None
55
- kind: Literal["problem", "solution", "failed_tactic", "fact", "preference", "change"]
54
+ kind: MemoryKindValue
56
55
  rationale: str | None = None
57
56
  links: MemoryCreateLinks = Field(default_factory=MemoryCreateLinks)
58
57
  evidence_refs: list[str] = Field(min_length=1)
@@ -150,7 +150,24 @@ class ReadPolicyRepo(IReadPolicyRepo):
150
150
  *self._visibility_filters(repo_id=repo_id, include_global=include_global, kinds=kinds),
151
151
  )
152
152
  )
153
- union_stmt = union_all(from_stmt, reverse_associated_stmt).subquery()
153
+ reverse_matures_into_stmt = (
154
+ select(
155
+ association_edges.c.from_memory_id.label("memory_id"),
156
+ association_edges.c.relation_type,
157
+ association_edges.c.strength,
158
+ literal("association").label("expansion_type"),
159
+ )
160
+ .select_from(association_edges.join(memories, memories.c.id == association_edges.c.from_memory_id))
161
+ .where(
162
+ association_edges.c.repo_id == repo_id,
163
+ association_edges.c.to_memory_id == anchor_memory_id,
164
+ association_edges.c.relation_type == "matures_into",
165
+ association_edges.c.state != "deprecated",
166
+ association_edges.c.strength >= min_strength,
167
+ *self._visibility_filters(repo_id=repo_id, include_global=include_global, kinds=kinds),
168
+ )
169
+ )
170
+ union_stmt = union_all(from_stmt, reverse_associated_stmt, reverse_matures_into_stmt).subquery()
154
171
  stmt = (
155
172
  select(
156
173
  union_stmt.c.memory_id,
@@ -46,17 +46,6 @@ def detect_claude_runtime_without_hook() -> bool:
46
46
  )
47
47
 
48
48
 
49
- def detect_claude_runtime_signal() -> bool:
50
- """Return whether one real Claude runtime signal is present in the current environment."""
51
-
52
- if os.getenv("SHELLBRAIN_HOST_APP") == "claude_code":
53
- return True
54
- return any(
55
- os.getenv(name)
56
- for name in ("CLAUDE_SESSION_ID", "CLAUDE_CODE_REMOTE_SESSION_ID", "CLAUDE_CODE_AGENT_NAME")
57
- )
58
-
59
-
60
49
  def resolve_trusted_claude_transcript_path() -> Path | None:
61
50
  """Return the trusted Claude transcript path injected by the Shellbrain hook when present."""
62
51
 
@@ -31,8 +31,3 @@ def host_identity_drifted_error(*, caller_id: str) -> ErrorDetail:
31
31
  message=f"Trusted caller identity drifted and could not be resolved for `{caller_id}`. Verify the host thread/session still exists and rerun `events`.",
32
32
  )
33
33
 
34
-
35
- def transcript_source_not_found_error(*, message: str) -> ErrorDetail:
36
- """Return the canonical error for transcript-source resolution failures."""
37
-
38
- return ErrorDetail(code=ErrorCode.TRANSCRIPT_SOURCE_NOT_FOUND, message=message)