attocode-code-intel 0.1.0__py3-none-any.whl

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 (345) hide show
  1. attocode_code_intel-0.1.0.dist-info/METADATA +221 -0
  2. attocode_code_intel-0.1.0.dist-info/RECORD +345 -0
  3. attocode_code_intel-0.1.0.dist-info/WHEEL +4 -0
  4. attocode_code_intel-0.1.0.dist-info/entry_points.txt +2 -0
  5. attocode_code_intel-0.1.0.dist-info/licenses/LICENSE +21 -0
  6. attocode_intel/GUIDELINES.md +432 -0
  7. attocode_intel/__init__.py +52 -0
  8. attocode_intel/_internal/__init__.py +1 -0
  9. attocode_intel/_internal/errors/__init__.py +126 -0
  10. attocode_intel/_internal/integrations/__init__.py +1 -0
  11. attocode_intel/_internal/integrations/context/__init__.py +1 -0
  12. attocode_intel/_internal/integrations/context/architecture_drift.py +354 -0
  13. attocode_intel/_internal/integrations/context/ast_chunker.py +202 -0
  14. attocode_intel/_internal/integrations/context/ast_service.py +1483 -0
  15. attocode_intel/_internal/integrations/context/blob_oid.py +423 -0
  16. attocode_intel/_internal/integrations/context/cache_manifest.py +221 -0
  17. attocode_intel/_internal/integrations/context/cas.py +466 -0
  18. attocode_intel/_internal/integrations/context/code_analyzer.py +528 -0
  19. attocode_intel/_internal/integrations/context/codebase_ast.py +1461 -0
  20. attocode_intel/_internal/integrations/context/codebase_context.py +1736 -0
  21. attocode_intel/_internal/integrations/context/cross_mode.py +263 -0
  22. attocode_intel/_internal/integrations/context/cross_references.py +616 -0
  23. attocode_intel/_internal/integrations/context/embedding_rotation.py +597 -0
  24. attocode_intel/_internal/integrations/context/embeddings.py +282 -0
  25. attocode_intel/_internal/integrations/context/frecency.py +427 -0
  26. attocode_intel/_internal/integrations/context/fuzzy.py +320 -0
  27. attocode_intel/_internal/integrations/context/hierarchical_explorer.py +331 -0
  28. attocode_intel/_internal/integrations/context/hydration.py +90 -0
  29. attocode_intel/_internal/integrations/context/index_repair.py +87 -0
  30. attocode_intel/_internal/integrations/context/index_store.py +586 -0
  31. attocode_intel/_internal/integrations/context/memory_store.py +602 -0
  32. attocode_intel/_internal/integrations/context/module_resolution.py +52 -0
  33. attocode_intel/_internal/integrations/context/query_constraints.py +361 -0
  34. attocode_intel/_internal/integrations/context/query_history.py +417 -0
  35. attocode_intel/_internal/integrations/context/reranker.py +351 -0
  36. attocode_intel/_internal/integrations/context/semantic_search.py +2931 -0
  37. attocode_intel/_internal/integrations/context/syntax_evidence.py +94 -0
  38. attocode_intel/_internal/integrations/context/systemone_ranker.py +245 -0
  39. attocode_intel/_internal/integrations/context/temporal_coupling.py +411 -0
  40. attocode_intel/_internal/integrations/context/trigram_index.py +820 -0
  41. attocode_intel/_internal/integrations/context/trigram_regex.py +171 -0
  42. attocode_intel/_internal/integrations/context/ts_parser.py +2199 -0
  43. attocode_intel/_internal/integrations/context/vector_store.py +795 -0
  44. attocode_intel/_internal/integrations/feature_flags/__init__.py +357 -0
  45. attocode_intel/_internal/integrations/lsp/__init__.py +1 -0
  46. attocode_intel/_internal/integrations/lsp/client.py +1140 -0
  47. attocode_intel/_internal/integrations/mcp/__init__.py +1 -0
  48. attocode_intel/_internal/integrations/mcp/client.py +253 -0
  49. attocode_intel/_internal/integrations/security/__init__.py +1 -0
  50. attocode_intel/_internal/integrations/security/custom_rules.py +176 -0
  51. attocode_intel/_internal/integrations/security/dataflow.py +522 -0
  52. attocode_intel/_internal/integrations/security/dependency_audit.py +303 -0
  53. attocode_intel/_internal/integrations/security/matcher.py +44 -0
  54. attocode_intel/_internal/integrations/security/patterns.py +724 -0
  55. attocode_intel/_internal/integrations/security/scanner.py +351 -0
  56. attocode_intel/_internal/integrations/utilities/__init__.py +1 -0
  57. attocode_intel/_internal/integrations/utilities/ignore.py +64 -0
  58. attocode_intel/_internal/integrations/utilities/token_estimate.py +51 -0
  59. attocode_intel/_internal/tricks/__init__.py +1 -0
  60. attocode_intel/_internal/tricks/recursive_context.py +357 -0
  61. attocode_intel/_internal/types/__init__.py +1 -0
  62. attocode_intel/_internal/types/events.py +289 -0
  63. attocode_intel/_internal/types/severity.py +15 -0
  64. attocode_intel/_shared.py +341 -0
  65. attocode_intel/analysis_coverage.py +56 -0
  66. attocode_intel/api/__init__.py +1 -0
  67. attocode_intel/api/access.py +32 -0
  68. attocode_intel/api/app.py +247 -0
  69. attocode_intel/api/auth/__init__.py +128 -0
  70. attocode_intel/api/auth/api_keys.py +61 -0
  71. attocode_intel/api/auth/context.py +34 -0
  72. attocode_intel/api/auth/jwt.py +66 -0
  73. attocode_intel/api/auth/oauth.py +146 -0
  74. attocode_intel/api/auth/passwords.py +15 -0
  75. attocode_intel/api/deps.py +351 -0
  76. attocode_intel/api/knowledge_bridge.py +13 -0
  77. attocode_intel/api/mcp_transport.py +51 -0
  78. attocode_intel/api/middleware.py +411 -0
  79. attocode_intel/api/models.py +698 -0
  80. attocode_intel/api/providers/__init__.py +1 -0
  81. attocode_intel/api/providers/db_provider.py +1106 -0
  82. attocode_intel/api/providers/local_provider.py +476 -0
  83. attocode_intel/api/providers/remote_provider.py +740 -0
  84. attocode_intel/api/repository_source.py +21 -0
  85. attocode_intel/api/routes/__init__.py +1 -0
  86. attocode_intel/api/routes/_pin_helper.py +158 -0
  87. attocode_intel/api/routes/activity.py +148 -0
  88. attocode_intel/api/routes/adr.py +102 -0
  89. attocode_intel/api/routes/analysis.py +515 -0
  90. attocode_intel/api/routes/api_keys.py +170 -0
  91. attocode_intel/api/routes/auth.py +428 -0
  92. attocode_intel/api/routes/branches.py +262 -0
  93. attocode_intel/api/routes/cross_repo_search.py +198 -0
  94. attocode_intel/api/routes/embeddings.py +386 -0
  95. attocode_intel/api/routes/files.py +132 -0
  96. attocode_intel/api/routes/files_v2.py +388 -0
  97. attocode_intel/api/routes/gc.py +322 -0
  98. attocode_intel/api/routes/git_v2.py +433 -0
  99. attocode_intel/api/routes/graph.py +174 -0
  100. attocode_intel/api/routes/graph_viz.py +160 -0
  101. attocode_intel/api/routes/health.py +62 -0
  102. attocode_intel/api/routes/history.py +175 -0
  103. attocode_intel/api/routes/jobs.py +84 -0
  104. attocode_intel/api/routes/learning.py +205 -0
  105. attocode_intel/api/routes/lsp.py +272 -0
  106. attocode_intel/api/routes/notify.py +623 -0
  107. attocode_intel/api/routes/operations.py +27 -0
  108. attocode_intel/api/routes/orgs.py +924 -0
  109. attocode_intel/api/routes/preferences.py +100 -0
  110. attocode_intel/api/routes/presence.py +42 -0
  111. attocode_intel/api/routes/projects.py +116 -0
  112. attocode_intel/api/routes/repos.py +47 -0
  113. attocode_intel/api/routes/search.py +180 -0
  114. attocode_intel/api/routes/snapshots.py +614 -0
  115. attocode_intel/api/routes/webhooks.py +313 -0
  116. attocode_intel/api/routes/websocket.py +116 -0
  117. attocode_intel/api/static/graph.html +239 -0
  118. attocode_intel/api/utils.py +96 -0
  119. attocode_intel/artifacts/__init__.py +45 -0
  120. attocode_intel/artifacts/hashing.py +230 -0
  121. attocode_intel/artifacts/provenance.py +190 -0
  122. attocode_intel/artifacts/retrieval_pin.py +159 -0
  123. attocode_intel/audit.py +59 -0
  124. attocode_intel/bug_finder.py +206 -0
  125. attocode_intel/bundle.py +80 -0
  126. attocode_intel/catalog.py +242 -0
  127. attocode_intel/change_review.py +75 -0
  128. attocode_intel/cli.py +3062 -0
  129. attocode_intel/community.py +63 -0
  130. attocode_intel/confidence/__init__.py +144 -0
  131. attocode_intel/confidence/jev.py +234 -0
  132. attocode_intel/confidence/llm.py +330 -0
  133. attocode_intel/confidence/redact.py +44 -0
  134. attocode_intel/confidence/settings.py +74 -0
  135. attocode_intel/config.py +256 -0
  136. attocode_intel/crypto.py +31 -0
  137. attocode_intel/db/__init__.py +7 -0
  138. attocode_intel/db/base.py +33 -0
  139. attocode_intel/db/engine.py +61 -0
  140. attocode_intel/db/models.py +529 -0
  141. attocode_intel/entrypoint.py +185 -0
  142. attocode_intel/focused_evidence.py +130 -0
  143. attocode_intel/freshness.py +154 -0
  144. attocode_intel/gateway.py +734 -0
  145. attocode_intel/git/__init__.py +7 -0
  146. attocode_intel/git/credentials.py +75 -0
  147. attocode_intel/git/manager.py +493 -0
  148. attocode_intel/git/models.py +96 -0
  149. attocode_intel/git/storage.py +98 -0
  150. attocode_intel/graph_query_parser.py +761 -0
  151. attocode_intel/helpers.py +712 -0
  152. attocode_intel/indexing/__init__.py +29 -0
  153. attocode_intel/indexing/debouncer.py +136 -0
  154. attocode_intel/indexing/delta_indexer.py +151 -0
  155. attocode_intel/indexing/full_indexer.py +323 -0
  156. attocode_intel/indexing/incremental.py +240 -0
  157. attocode_intel/indexing/parser.py +403 -0
  158. attocode_intel/indexing/summarizer.py +264 -0
  159. attocode_intel/installer.py +1896 -0
  160. attocode_intel/knowledge.py +269 -0
  161. attocode_intel/local_knowledge.py +77 -0
  162. attocode_intel/migrations/alembic.ini +36 -0
  163. attocode_intel/migrations/env.py +71 -0
  164. attocode_intel/migrations/script.py.mako +27 -0
  165. attocode_intel/migrations/versions/001_initial_schema.py +219 -0
  166. attocode_intel/migrations/versions/002_branch_version_and_embeddings.py +61 -0
  167. attocode_intel/migrations/versions/003_embedding_uniqueness_and_cascade.py +51 -0
  168. attocode_intel/migrations/versions/004_audit_events.py +38 -0
  169. attocode_intel/migrations/versions/005_user_preferences.py +30 -0
  170. attocode_intel/migrations/versions/006_google_oauth.py +27 -0
  171. attocode_intel/migrations/versions/007_pgvector_embeddings.py +28 -0
  172. attocode_intel/migrations/versions/008_symbol_references.py +37 -0
  173. attocode_intel/migrations/versions/009_webhook_secret_encryption.py +25 -0
  174. attocode_intel/migrations/versions/010_add_commits_table.py +40 -0
  175. attocode_intel/migrations/versions/011_commit_changed_files.py +25 -0
  176. attocode_intel/migrations/versions/012_branch_merged_at.py +25 -0
  177. attocode_intel/migrations/versions/013_revoked_tokens.py +32 -0
  178. attocode_intel/migrations/versions/014_blame_hunks.py +40 -0
  179. attocode_intel/migrations/versions/015_commit_file_stats.py +40 -0
  180. attocode_intel/migrations/versions/016_embedding_provenance_columns.py +179 -0
  181. attocode_intel/migrations/versions/017_provenance_table.py +102 -0
  182. attocode_intel/migrations/versions/018_repo_snapshots.py +178 -0
  183. attocode_intel/migrations/versions/019_symref_caller.py +42 -0
  184. attocode_intel/migrations/versions/020_shared_knowledge.py +29 -0
  185. attocode_intel/onboarding.py +258 -0
  186. attocode_intel/output.py +178 -0
  187. attocode_intel/precision.py +133 -0
  188. attocode_intel/presence.py +160 -0
  189. attocode_intel/probe.py +110 -0
  190. attocode_intel/project_dir.py +97 -0
  191. attocode_intel/pubsub.py +113 -0
  192. attocode_intel/query_ranking.py +242 -0
  193. attocode_intel/readiness.py +972 -0
  194. attocode_intel/redis.py +51 -0
  195. attocode_intel/reference_pages.py +60 -0
  196. attocode_intel/remote.py +131 -0
  197. attocode_intel/repo_ranker.py +246 -0
  198. attocode_intel/request_context.py +82 -0
  199. attocode_intel/rules/__init__.py +28 -0
  200. attocode_intel/rules/ci.py +320 -0
  201. attocode_intel/rules/combinators.py +293 -0
  202. attocode_intel/rules/enricher.py +127 -0
  203. attocode_intel/rules/evolution.py +452 -0
  204. attocode_intel/rules/executor.py +531 -0
  205. attocode_intel/rules/filters/__init__.py +5 -0
  206. attocode_intel/rules/filters/pipeline.py +151 -0
  207. attocode_intel/rules/formatter.py +198 -0
  208. attocode_intel/rules/hygiene.py +242 -0
  209. attocode_intel/rules/importers/__init__.py +1 -0
  210. attocode_intel/rules/importers/semgrep.py +382 -0
  211. attocode_intel/rules/loader.py +292 -0
  212. attocode_intel/rules/marketplace.py +375 -0
  213. attocode_intel/rules/metavar.py +181 -0
  214. attocode_intel/rules/model.py +317 -0
  215. attocode_intel/rules/packs/__init__.py +1 -0
  216. attocode_intel/rules/packs/community/_license_templates/Apache-2.0.txt +176 -0
  217. attocode_intel/rules/packs/community/_license_templates/MIT.txt +21 -0
  218. attocode_intel/rules/packs/community/bandit-python/LICENSE +176 -0
  219. attocode_intel/rules/packs/community/bandit-python/NOTICE +13 -0
  220. attocode_intel/rules/packs/community/bandit-python/PORTING.md +23 -0
  221. attocode_intel/rules/packs/community/bandit-python/manifest.yaml +14 -0
  222. attocode_intel/rules/packs/community/bandit-python/rules/B105-hardcoded-password.yaml +23 -0
  223. attocode_intel/rules/packs/community/bandit-python/rules/B303-weak-md5.yaml +21 -0
  224. attocode_intel/rules/packs/community/bandit-python/rules/B304-weak-des.yaml +20 -0
  225. attocode_intel/rules/packs/community/bandit-python/rules/B307-use-of-eval.yaml +22 -0
  226. attocode_intel/rules/packs/community/bandit-python/rules/B324-weak-sha1.yaml +21 -0
  227. attocode_intel/rules/packs/community/bandit-python/rules/B501-no-cert-validation.yaml +23 -0
  228. attocode_intel/rules/packs/community/bandit-python/rules/B602-shell-true.yaml +22 -0
  229. attocode_intel/rules/packs/community/bandit-python/rules/B608-sqli.yaml +22 -0
  230. attocode_intel/rules/packs/community/eslint-typescript/LICENSE +21 -0
  231. attocode_intel/rules/packs/community/eslint-typescript/NOTICE +13 -0
  232. attocode_intel/rules/packs/community/eslint-typescript/PORTING.md +23 -0
  233. attocode_intel/rules/packs/community/eslint-typescript/manifest.yaml +14 -0
  234. attocode_intel/rules/packs/community/eslint-typescript/rules/eqeqeq.yaml +20 -0
  235. attocode_intel/rules/packs/community/eslint-typescript/rules/no-eval.yaml +22 -0
  236. attocode_intel/rules/packs/community/eslint-typescript/rules/no-implied-eval.yaml +20 -0
  237. attocode_intel/rules/packs/community/eslint-typescript/rules/no-new-func.yaml +21 -0
  238. attocode_intel/rules/packs/community/eslint-typescript/rules/no-throw-literal.yaml +19 -0
  239. attocode_intel/rules/packs/community/eslint-typescript/rules/no-var.yaml +20 -0
  240. attocode_intel/rules/packs/community/eslint-typescript/rules/no-with.yaml +18 -0
  241. attocode_intel/rules/packs/community/gosec-go/LICENSE +176 -0
  242. attocode_intel/rules/packs/community/gosec-go/NOTICE +13 -0
  243. attocode_intel/rules/packs/community/gosec-go/PORTING.md +23 -0
  244. attocode_intel/rules/packs/community/gosec-go/manifest.yaml +14 -0
  245. attocode_intel/rules/packs/community/gosec-go/rules/G101-hardcoded-credentials.yaml +22 -0
  246. attocode_intel/rules/packs/community/gosec-go/rules/G102-bind-all-interfaces.yaml +21 -0
  247. attocode_intel/rules/packs/community/gosec-go/rules/G103-unsafe-block.yaml +21 -0
  248. attocode_intel/rules/packs/community/gosec-go/rules/G201-sql-format.yaml +21 -0
  249. attocode_intel/rules/packs/community/gosec-go/rules/G304-file-inclusion.yaml +23 -0
  250. attocode_intel/rules/packs/community/gosec-go/rules/G401-weak-cipher.yaml +20 -0
  251. attocode_intel/rules/packs/community/gosec-go/rules/G402-tls-min-version.yaml +20 -0
  252. attocode_intel/rules/packs/community/gosec-go/rules/G501-md5-import.yaml +21 -0
  253. attocode_intel/rules/packs/examples/cpp/manifest.yaml +4 -0
  254. attocode_intel/rules/packs/examples/cpp/rules/security.yaml +90 -0
  255. attocode_intel/rules/packs/examples/cpp/rules/structural.yaml +42 -0
  256. attocode_intel/rules/packs/examples/csharp/manifest.yaml +4 -0
  257. attocode_intel/rules/packs/examples/csharp/rules/security.yaml +69 -0
  258. attocode_intel/rules/packs/examples/csharp/rules/structural.yaml +42 -0
  259. attocode_intel/rules/packs/examples/go/manifest.yaml +4 -0
  260. attocode_intel/rules/packs/examples/go/rules/performance.yaml +136 -0
  261. attocode_intel/rules/packs/examples/go/rules/security.yaml +48 -0
  262. attocode_intel/rules/packs/examples/go/rules/structural.yaml +58 -0
  263. attocode_intel/rules/packs/examples/go/taint/sanitizers.yaml +24 -0
  264. attocode_intel/rules/packs/examples/go/taint/sinks.yaml +51 -0
  265. attocode_intel/rules/packs/examples/go/taint/sources.yaml +26 -0
  266. attocode_intel/rules/packs/examples/java/manifest.yaml +4 -0
  267. attocode_intel/rules/packs/examples/java/rules/correctness.yaml +55 -0
  268. attocode_intel/rules/packs/examples/java/rules/structural.yaml +48 -0
  269. attocode_intel/rules/packs/examples/java/taint/sinks.yaml +37 -0
  270. attocode_intel/rules/packs/examples/java/taint/sources.yaml +21 -0
  271. attocode_intel/rules/packs/examples/kotlin/manifest.yaml +4 -0
  272. attocode_intel/rules/packs/examples/kotlin/rules/correctness.yaml +50 -0
  273. attocode_intel/rules/packs/examples/kotlin/rules/structural.yaml +38 -0
  274. attocode_intel/rules/packs/examples/php/manifest.yaml +4 -0
  275. attocode_intel/rules/packs/examples/php/rules/security.yaml +66 -0
  276. attocode_intel/rules/packs/examples/php/rules/structural.yaml +41 -0
  277. attocode_intel/rules/packs/examples/python/manifest.yaml +4 -0
  278. attocode_intel/rules/packs/examples/python/rules/performance.yaml +90 -0
  279. attocode_intel/rules/packs/examples/python/rules/structural.yaml +55 -0
  280. attocode_intel/rules/packs/examples/ruby/manifest.yaml +4 -0
  281. attocode_intel/rules/packs/examples/ruby/rules/security.yaml +73 -0
  282. attocode_intel/rules/packs/examples/ruby/rules/structural.yaml +41 -0
  283. attocode_intel/rules/packs/examples/rust/manifest.yaml +4 -0
  284. attocode_intel/rules/packs/examples/rust/rules/correctness.yaml +51 -0
  285. attocode_intel/rules/packs/examples/rust/rules/structural.yaml +49 -0
  286. attocode_intel/rules/packs/examples/typescript/manifest.yaml +4 -0
  287. attocode_intel/rules/packs/examples/typescript/rules/performance.yaml +70 -0
  288. attocode_intel/rules/packs/examples/typescript/rules/structural.yaml +48 -0
  289. attocode_intel/rules/packs/pack_loader.py +244 -0
  290. attocode_intel/rules/plugins/__init__.py +1 -0
  291. attocode_intel/rules/plugins/plugin_loader.py +132 -0
  292. attocode_intel/rules/profiling.py +332 -0
  293. attocode_intel/rules/registry.py +213 -0
  294. attocode_intel/rules/sarif.py +187 -0
  295. attocode_intel/rules/synthesis.py +420 -0
  296. attocode_intel/rules/testing.py +326 -0
  297. attocode_intel/server.py +734 -0
  298. attocode_intel/service.py +3416 -0
  299. attocode_intel/sharing.py +57 -0
  300. attocode_intel/storage/__init__.py +6 -0
  301. attocode_intel/storage/branch_overlay.py +352 -0
  302. attocode_intel/storage/content_store.py +220 -0
  303. attocode_intel/storage/dependency_store.py +143 -0
  304. attocode_intel/storage/diff_engine.py +187 -0
  305. attocode_intel/storage/embedding_store.py +594 -0
  306. attocode_intel/storage/provenance_store.py +137 -0
  307. attocode_intel/storage/security_scanner_db.py +164 -0
  308. attocode_intel/storage/symbol_store.py +160 -0
  309. attocode_intel/structural_evidence.py +144 -0
  310. attocode_intel/symbol_inspection.py +141 -0
  311. attocode_intel/symbol_links.py +167 -0
  312. attocode_intel/telemetry.py +42 -0
  313. attocode_intel/test_ranking.py +73 -0
  314. attocode_intel/testing/__init__.py +43 -0
  315. attocode_intel/testing/fixtures.py +122 -0
  316. attocode_intel/testing/helpers.py +75 -0
  317. attocode_intel/testing/mocks.py +99 -0
  318. attocode_intel/tools/__init__.py +2 -0
  319. attocode_intel/tools/adr_tools.py +628 -0
  320. attocode_intel/tools/analysis_tools.py +382 -0
  321. attocode_intel/tools/composite_tools.py +695 -0
  322. attocode_intel/tools/cross_mode_tools.py +173 -0
  323. attocode_intel/tools/dead_code_tools.py +582 -0
  324. attocode_intel/tools/distill_tools.py +353 -0
  325. attocode_intel/tools/frecency_tools.py +220 -0
  326. attocode_intel/tools/fuzzy_tools.py +255 -0
  327. attocode_intel/tools/history_tools.py +486 -0
  328. attocode_intel/tools/learning_tools.py +222 -0
  329. attocode_intel/tools/lsp_tools.py +218 -0
  330. attocode_intel/tools/maintenance_tools.py +1662 -0
  331. attocode_intel/tools/navigation_tools.py +220 -0
  332. attocode_intel/tools/overlay_tools.py +540 -0
  333. attocode_intel/tools/pin_store.py +432 -0
  334. attocode_intel/tools/pin_tools.py +266 -0
  335. attocode_intel/tools/query_constraints_tools.py +262 -0
  336. attocode_intel/tools/query_history_tools.py +158 -0
  337. attocode_intel/tools/readiness_tools.py +44 -0
  338. attocode_intel/tools/rule_tools.py +992 -0
  339. attocode_intel/tools/search_tools.py +770 -0
  340. attocode_intel/tools/snapshot_tools.py +624 -0
  341. attocode_intel/workers/__init__.py +1 -0
  342. attocode_intel/workers/job_utils.py +111 -0
  343. attocode_intel/workers/jobs.py +573 -0
  344. attocode_intel/workers/run.py +30 -0
  345. attocode_intel/workers/settings.py +61 -0
@@ -0,0 +1,529 @@
1
+ """SQLAlchemy ORM models for service mode."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import uuid
6
+ from datetime import datetime
7
+
8
+ from sqlalchemy import BigInteger, Boolean, DateTime, ForeignKey, Integer, String, Text, func
9
+ from sqlalchemy.dialects.postgresql import ARRAY, JSONB, UUID
10
+ from sqlalchemy.orm import Mapped, mapped_column, relationship
11
+
12
+ from attocode_intel.db.base import Base, TimestampMixin, generate_uuid
13
+
14
+ try:
15
+ from pgvector.sqlalchemy import Vector as _PgVector
16
+ except ImportError:
17
+ _PgVector = None
18
+
19
+
20
+ class Organization(TimestampMixin, Base):
21
+ __tablename__ = "organizations"
22
+
23
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=generate_uuid)
24
+ name: Mapped[str] = mapped_column(Text, nullable=False)
25
+ slug: Mapped[str] = mapped_column(Text, unique=True, nullable=False)
26
+ plan: Mapped[str] = mapped_column(Text, nullable=False, server_default="free")
27
+ settings: Mapped[dict] = mapped_column(JSONB, nullable=False, server_default="{}")
28
+
29
+ memberships: Mapped[list[OrgMembership]] = relationship(back_populates="organization", cascade="all, delete-orphan")
30
+ api_keys: Mapped[list[ApiKey]] = relationship(back_populates="organization", cascade="all, delete-orphan")
31
+ repositories: Mapped[list[Repository]] = relationship(back_populates="organization", cascade="all, delete-orphan")
32
+
33
+
34
+ class User(Base):
35
+ __tablename__ = "users"
36
+
37
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=generate_uuid)
38
+ email: Mapped[str] = mapped_column(Text, unique=True, nullable=False)
39
+ name: Mapped[str] = mapped_column(Text, nullable=False, server_default="")
40
+ avatar_url: Mapped[str | None] = mapped_column(Text, nullable=True)
41
+ password_hash: Mapped[str | None] = mapped_column(Text, nullable=True)
42
+ github_id: Mapped[int | None] = mapped_column(BigInteger, unique=True, nullable=True)
43
+ google_id: Mapped[str | None] = mapped_column(Text, unique=True, nullable=True)
44
+ auth_provider: Mapped[str] = mapped_column(Text, nullable=False, server_default="email")
45
+ created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
46
+
47
+ memberships: Mapped[list[OrgMembership]] = relationship(back_populates="user", cascade="all, delete-orphan")
48
+ api_keys: Mapped[list[ApiKey]] = relationship(back_populates="user", cascade="all, delete-orphan")
49
+
50
+
51
+ class OrgMembership(Base):
52
+ __tablename__ = "org_memberships"
53
+
54
+ org_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("organizations.id", ondelete="CASCADE"), primary_key=True)
55
+ user_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), primary_key=True)
56
+ role: Mapped[str] = mapped_column(Text, nullable=False, server_default="member")
57
+ invited_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
58
+ accepted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
59
+
60
+ organization: Mapped[Organization] = relationship(back_populates="memberships")
61
+ user: Mapped[User] = relationship(back_populates="memberships")
62
+
63
+
64
+ class ApiKey(Base):
65
+ __tablename__ = "api_keys"
66
+
67
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=generate_uuid)
68
+ org_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False)
69
+ user_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=True)
70
+ name: Mapped[str] = mapped_column(Text, nullable=False)
71
+ key_hash: Mapped[str] = mapped_column(Text, unique=True, nullable=False)
72
+ key_prefix: Mapped[str] = mapped_column(Text, nullable=False)
73
+ scopes: Mapped[list[str]] = mapped_column(ARRAY(String), nullable=False, server_default="{}")
74
+ expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
75
+ last_used_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
76
+ created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
77
+
78
+ organization: Mapped[Organization] = relationship(back_populates="api_keys")
79
+ user: Mapped[User | None] = relationship(back_populates="api_keys")
80
+
81
+
82
+ class Repository(TimestampMixin, Base):
83
+ __tablename__ = "repositories"
84
+
85
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=generate_uuid)
86
+ org_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False)
87
+ name: Mapped[str] = mapped_column(Text, nullable=False)
88
+ local_path: Mapped[str | None] = mapped_column(Text, nullable=True)
89
+ clone_url: Mapped[str | None] = mapped_column(Text, nullable=True)
90
+ default_branch: Mapped[str] = mapped_column(Text, nullable=False, server_default="main")
91
+ language: Mapped[str | None] = mapped_column(Text, nullable=True)
92
+ last_indexed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
93
+ index_status: Mapped[str] = mapped_column(Text, nullable=False, server_default="pending")
94
+ settings: Mapped[dict] = mapped_column(JSONB, nullable=False, server_default="{}")
95
+ clone_path: Mapped[str | None] = mapped_column(Text, nullable=True)
96
+ disk_usage_bytes: Mapped[int] = mapped_column(BigInteger, nullable=False, server_default="0")
97
+ last_fetched_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
98
+
99
+ organization: Mapped[Organization] = relationship(back_populates="repositories")
100
+ credentials: Mapped[list[RepoCredential]] = relationship(back_populates="repository", cascade="all, delete-orphan")
101
+ branches: Mapped[list[Branch]] = relationship(back_populates="repository", cascade="all, delete-orphan")
102
+ webhook_configs: Mapped[list[WebhookConfig]] = relationship(back_populates="repository", cascade="all, delete-orphan")
103
+ indexing_jobs: Mapped[list[IndexingJob]] = relationship(back_populates="repository", cascade="all, delete-orphan")
104
+
105
+ __table_args__ = (
106
+ # Unique repo name within an org
107
+ {"schema": None},
108
+ )
109
+
110
+ @classmethod
111
+ def __declare_last__(cls) -> None:
112
+ from sqlalchemy import UniqueConstraint
113
+ if not any(isinstance(c, UniqueConstraint) for c in cls.__table__.constraints if hasattr(c, 'columns') and len(c.columns) == 2):
114
+ pass # UniqueConstraint added via migration
115
+
116
+
117
+ class RepoCredential(Base):
118
+ __tablename__ = "repo_credentials"
119
+
120
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=generate_uuid)
121
+ repo_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("repositories.id", ondelete="CASCADE"), nullable=False)
122
+ cred_type: Mapped[str] = mapped_column(Text, nullable=False) # ssh_key|deploy_token|pat
123
+ encrypted_value: Mapped[bytes] = mapped_column(nullable=False)
124
+ created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
125
+
126
+ repository: Mapped[Repository] = relationship(back_populates="credentials")
127
+
128
+
129
+ class Branch(Base):
130
+ __tablename__ = "branches"
131
+
132
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=generate_uuid)
133
+ repo_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("repositories.id", ondelete="CASCADE"), nullable=False)
134
+ name: Mapped[str] = mapped_column(Text, nullable=False)
135
+ parent_branch_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("branches.id"), nullable=True)
136
+ head_commit: Mapped[str | None] = mapped_column(Text, nullable=True)
137
+ last_indexed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
138
+ is_default: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default="false")
139
+ # Monotonic version counter — incremented on every overlay write for consistency checks
140
+ version: Mapped[int] = mapped_column(Integer, nullable=False, server_default="0")
141
+ merged_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
142
+
143
+ repository: Mapped[Repository] = relationship(back_populates="branches")
144
+ parent_branch: Mapped[Branch | None] = relationship(remote_side=[id])
145
+ files: Mapped[list[BranchFile]] = relationship(back_populates="branch", cascade="all, delete-orphan")
146
+
147
+ __table_args__ = (
148
+ {"schema": None},
149
+ )
150
+
151
+
152
+ class FileContent(Base):
153
+ __tablename__ = "file_contents"
154
+
155
+ sha256: Mapped[str] = mapped_column(Text, primary_key=True)
156
+ content: Mapped[bytes] = mapped_column(nullable=False)
157
+ size_bytes: Mapped[int] = mapped_column(nullable=False)
158
+ language: Mapped[str | None] = mapped_column(Text, nullable=True)
159
+ # Supports storing summaries alongside code: source|summary_l1|summary_l2
160
+ content_type: Mapped[str] = mapped_column(Text, nullable=False, server_default="source")
161
+ created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
162
+
163
+
164
+ class BranchFile(Base):
165
+ __tablename__ = "branch_files"
166
+
167
+ branch_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("branches.id", ondelete="CASCADE"), primary_key=True)
168
+ path: Mapped[str] = mapped_column(Text, primary_key=True)
169
+ content_sha: Mapped[str | None] = mapped_column(Text, ForeignKey("file_contents.sha256"), nullable=True)
170
+ status: Mapped[str] = mapped_column(Text, nullable=False) # added|modified|deleted
171
+
172
+ branch: Mapped[Branch] = relationship(back_populates="files")
173
+
174
+
175
+ class Symbol(Base):
176
+ __tablename__ = "symbols"
177
+
178
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=generate_uuid)
179
+ content_sha: Mapped[str] = mapped_column(Text, ForeignKey("file_contents.sha256"), nullable=False)
180
+ name: Mapped[str] = mapped_column(Text, nullable=False)
181
+ kind: Mapped[str] = mapped_column(Text, nullable=False)
182
+ line_start: Mapped[int | None] = mapped_column(nullable=True)
183
+ line_end: Mapped[int | None] = mapped_column(nullable=True)
184
+ signature: Mapped[str | None] = mapped_column(Text, nullable=True)
185
+ exported: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default="false")
186
+ metadata_: Mapped[dict] = mapped_column("metadata", JSONB, nullable=False, server_default="{}")
187
+
188
+
189
+ class Embedding(Base):
190
+ __tablename__ = "embeddings"
191
+
192
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=generate_uuid)
193
+ # N5 fix: CASCADE on FK so deleting file_contents cleans up embeddings
194
+ content_sha: Mapped[str] = mapped_column(Text, ForeignKey("file_contents.sha256", ondelete="CASCADE"), nullable=False)
195
+ embedding_model: Mapped[str] = mapped_column(Text, nullable=False, server_default="default")
196
+ # Migration 016: explicit model version + dim + provenance to replace the
197
+ # old "silently wipe on dim change" flow. embedding_model_version defaults
198
+ # to '' so pre-016 rows stay valid; new code should set it explicitly.
199
+ embedding_model_version: Mapped[str] = mapped_column(Text, nullable=False, server_default="")
200
+ embedding_dim: Mapped[int | None] = mapped_column(Integer, nullable=True)
201
+ embedding_provenance: Mapped[dict] = mapped_column(JSONB, nullable=False, server_default="{}")
202
+ chunk_text: Mapped[str] = mapped_column(Text, nullable=False, server_default="")
203
+ chunk_type: Mapped[str] = mapped_column(Text, nullable=False, server_default="file")
204
+ # Vector column — added at runtime by ensure_vector_columns().
205
+ # Dimension depends on the configured embedding model (384/768/1536).
206
+ # Column is nullable: rows without vectors are pre-pgvector or pending re-embedding.
207
+ if _PgVector is not None:
208
+ vector = mapped_column(_PgVector(), nullable=True)
209
+ created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
210
+
211
+ # Migration 016 replaced the old uq_embedding_content_model_chunk with
212
+ # one that also includes embedding_model_version, so two versions of the
213
+ # same model can coexist during a rotation.
214
+ __table_args__ = (
215
+ __import__("sqlalchemy").UniqueConstraint(
216
+ "content_sha", "embedding_model", "embedding_model_version", "chunk_type",
217
+ name="uq_embedding_content_model_version_chunk",
218
+ ),
219
+ __import__("sqlalchemy").Index(
220
+ "ix_embeddings_model_version", "embedding_model", "embedding_model_version",
221
+ ),
222
+ )
223
+
224
+
225
+ class SymbolReference(Base):
226
+ __tablename__ = "symbol_references"
227
+
228
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=generate_uuid)
229
+ content_sha: Mapped[str] = mapped_column(Text, ForeignKey("file_contents.sha256", ondelete="CASCADE"), nullable=False)
230
+ symbol_name: Mapped[str] = mapped_column(String(500), nullable=False)
231
+ ref_kind: Mapped[str] = mapped_column(String(20), nullable=False)
232
+ line: Mapped[int] = mapped_column(nullable=False)
233
+ # Qualified name of the function/method enclosing this reference. Empty
234
+ # for top-level calls or when the parser could not attribute a caller.
235
+ # Populated by extract_references() in full_indexer's third pass and
236
+ # consumed by call-graph queries over HTTP-indexed repos.
237
+ caller_qualified_name: Mapped[str] = mapped_column(
238
+ String(500), nullable=False, server_default="",
239
+ )
240
+ created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
241
+
242
+
243
+ class Dependency(Base):
244
+ __tablename__ = "dependencies"
245
+
246
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=generate_uuid)
247
+ source_sha: Mapped[str] = mapped_column(Text, ForeignKey("file_contents.sha256"), nullable=False)
248
+ target_sha: Mapped[str] = mapped_column(Text, ForeignKey("file_contents.sha256"), nullable=False)
249
+ # Types: import, call, type_ref, data_flow (extensible for future call graph)
250
+ dep_type: Mapped[str] = mapped_column(Text, nullable=False)
251
+ weight: Mapped[float] = mapped_column(nullable=False, server_default="1.0")
252
+ metadata_: Mapped[dict] = mapped_column("metadata", JSONB, nullable=False, server_default="{}")
253
+
254
+
255
+ class Commit(Base):
256
+ """DB-backed commit history for remote repos without bare clones."""
257
+ __tablename__ = "commits"
258
+
259
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=generate_uuid)
260
+ repo_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("repositories.id", ondelete="CASCADE"), nullable=False)
261
+ oid: Mapped[str] = mapped_column(Text, nullable=False)
262
+ message: Mapped[str] = mapped_column(Text, nullable=False)
263
+ author_name: Mapped[str] = mapped_column(Text, nullable=False)
264
+ author_email: Mapped[str] = mapped_column(Text, nullable=False)
265
+ timestamp: Mapped[int] = mapped_column(BigInteger, nullable=False)
266
+ parent_oids: Mapped[list[str]] = mapped_column(ARRAY(String), nullable=False, server_default="{}")
267
+ branch_name: Mapped[str] = mapped_column(Text, nullable=False, server_default="main")
268
+ changed_files: Mapped[list | None] = mapped_column(JSONB, nullable=True)
269
+ created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
270
+
271
+ __table_args__ = (
272
+ __import__("sqlalchemy").UniqueConstraint("repo_id", "oid", name="uq_commits_repo_oid"),
273
+ )
274
+
275
+
276
+ class CommitFileStat(Base):
277
+ """Per-file statistics for each commit — lines added/removed, symbols changed."""
278
+ __tablename__ = "commit_file_stats"
279
+
280
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=generate_uuid)
281
+ commit_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("commits.id", ondelete="CASCADE"), nullable=False)
282
+ repo_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("repositories.id", ondelete="CASCADE"), nullable=False)
283
+ path: Mapped[str] = mapped_column(Text, nullable=False)
284
+ lines_added: Mapped[int] = mapped_column(Integer, nullable=False, server_default="0")
285
+ lines_removed: Mapped[int] = mapped_column(Integer, nullable=False, server_default="0")
286
+ symbols_changed: Mapped[list] = mapped_column(JSONB, nullable=False, server_default="[]")
287
+ change_type: Mapped[str] = mapped_column(Text, nullable=False) # added|modified|deleted|renamed
288
+ created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
289
+
290
+ __table_args__ = (
291
+ __import__("sqlalchemy").Index("ix_commit_file_stats_repo_path", "repo_id", "path"),
292
+ __import__("sqlalchemy").Index("ix_commit_file_stats_commit_id", "commit_id"),
293
+ )
294
+
295
+
296
+ class IndexingJob(Base):
297
+ __tablename__ = "indexing_jobs"
298
+
299
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=generate_uuid)
300
+ repo_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("repositories.id"), nullable=False)
301
+ job_type: Mapped[str] = mapped_column(Text, nullable=False)
302
+ status: Mapped[str] = mapped_column(Text, nullable=False, server_default="queued")
303
+ branch_name: Mapped[str | None] = mapped_column(Text, nullable=True)
304
+ progress: Mapped[dict] = mapped_column(JSONB, nullable=False, server_default="{}")
305
+ result: Mapped[dict | None] = mapped_column(JSONB, nullable=True)
306
+ error: Mapped[str | None] = mapped_column(Text, nullable=True)
307
+ started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
308
+ completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
309
+ created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
310
+ arq_job_id: Mapped[str | None] = mapped_column(Text, nullable=True)
311
+
312
+ repository: Mapped[Repository] = relationship(back_populates="indexing_jobs")
313
+
314
+
315
+ class AuditEvent(Base):
316
+ __tablename__ = "audit_events"
317
+
318
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=generate_uuid)
319
+ org_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False)
320
+ repo_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("repositories.id", ondelete="SET NULL"), nullable=True)
321
+ user_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
322
+ event_type: Mapped[str] = mapped_column(Text, nullable=False)
323
+ detail: Mapped[dict] = mapped_column(JSONB, nullable=False, server_default="{}")
324
+ created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
325
+
326
+
327
+ class UserPreference(Base):
328
+ __tablename__ = "user_preferences"
329
+
330
+ user_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), primary_key=True)
331
+ preferences: Mapped[dict] = mapped_column(JSONB, nullable=False, server_default="{}")
332
+ updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
333
+
334
+
335
+ class WebhookConfig(Base):
336
+ __tablename__ = "webhook_configs"
337
+
338
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=generate_uuid)
339
+ repo_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("repositories.id"), nullable=False)
340
+ provider: Mapped[str] = mapped_column(Text, nullable=False)
341
+ secret_hash: Mapped[str] = mapped_column(Text, nullable=False)
342
+ secret_encrypted: Mapped[str | None] = mapped_column(Text, nullable=True) # Fernet-encrypted webhook secret
343
+ events: Mapped[list[str]] = mapped_column(ARRAY(String), nullable=False, server_default="{push}")
344
+ active: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default="true")
345
+ created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
346
+
347
+ repository: Mapped[Repository] = relationship(back_populates="webhook_configs")
348
+
349
+
350
+ class RevokedToken(Base):
351
+ """JWT token blocklist for revocation."""
352
+ __tablename__ = "revoked_tokens"
353
+
354
+ jti: Mapped[str] = mapped_column(Text, primary_key=True)
355
+ revoked_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
356
+ expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
357
+
358
+
359
+ class BlameHunk(Base):
360
+ """DB-backed blame data for remote repos."""
361
+ __tablename__ = "blame_hunks"
362
+
363
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=generate_uuid)
364
+ repo_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("repositories.id", ondelete="CASCADE"), nullable=False)
365
+ path: Mapped[str] = mapped_column(Text, nullable=False)
366
+ branch_name: Mapped[str] = mapped_column(Text, nullable=False, server_default="main")
367
+ commit_oid: Mapped[str] = mapped_column(Text, nullable=False)
368
+ author_name: Mapped[str] = mapped_column(Text, nullable=False)
369
+ author_email: Mapped[str] = mapped_column(Text, nullable=False)
370
+ timestamp: Mapped[int] = mapped_column(BigInteger, nullable=False)
371
+ start_line: Mapped[int] = mapped_column(Integer, nullable=False)
372
+ end_line: Mapped[int] = mapped_column(Integer, nullable=False)
373
+ created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
374
+
375
+
376
+ class Provenance(Base):
377
+ """Provenance row for a derived code-intel artifact.
378
+
379
+ Backed by migration 017. Embeddings carry provenance records that
380
+ downstream tools (snapshot restore, orphan scan, model rotation
381
+ audit) join against.
382
+ """
383
+ __tablename__ = "provenance"
384
+
385
+ id: Mapped[uuid.UUID] = mapped_column(
386
+ UUID(as_uuid=True),
387
+ primary_key=True,
388
+ default=generate_uuid,
389
+ )
390
+ action_hash: Mapped[str] = mapped_column(Text, nullable=False)
391
+ artifact_type: Mapped[str] = mapped_column(Text, nullable=False)
392
+ input_blob_oid: Mapped[str] = mapped_column(Text, nullable=False)
393
+ input_tree_oid: Mapped[str | None] = mapped_column(Text, nullable=True)
394
+ indexer_name: Mapped[str] = mapped_column(Text, nullable=False)
395
+ indexer_version: Mapped[str] = mapped_column(Text, nullable=False)
396
+ config_digest: Mapped[str] = mapped_column(Text, nullable=False)
397
+ produced_at: Mapped[datetime] = mapped_column(
398
+ DateTime(timezone=True),
399
+ server_default=func.now(),
400
+ nullable=False,
401
+ )
402
+ producer_service: Mapped[str] = mapped_column(
403
+ Text,
404
+ nullable=False,
405
+ server_default="attocode-server",
406
+ )
407
+ producer_user_id: Mapped[uuid.UUID | None] = mapped_column(
408
+ UUID(as_uuid=True),
409
+ ForeignKey("users.id", ondelete="SET NULL"),
410
+ nullable=True,
411
+ )
412
+ producer_job_id: Mapped[uuid.UUID | None] = mapped_column(
413
+ UUID(as_uuid=True),
414
+ ForeignKey("indexing_jobs.id", ondelete="SET NULL"),
415
+ nullable=True,
416
+ )
417
+ producer_host: Mapped[str] = mapped_column(
418
+ Text,
419
+ nullable=False,
420
+ server_default="",
421
+ )
422
+ extra: Mapped[dict] = mapped_column(JSONB, nullable=False, server_default="{}")
423
+
424
+ __table_args__ = (
425
+ __import__("sqlalchemy").Index(
426
+ "ix_provenance_action_hash", "action_hash",
427
+ ),
428
+ __import__("sqlalchemy").Index(
429
+ "ix_provenance_input_blob", "input_blob_oid",
430
+ ),
431
+ __import__("sqlalchemy").Index(
432
+ "ix_provenance_job", "producer_job_id",
433
+ ),
434
+ __import__("sqlalchemy").Index(
435
+ "ix_provenance_indexer_version", "indexer_name", "indexer_version",
436
+ ),
437
+ )
438
+
439
+
440
+ class RepoSnapshot(Base):
441
+ """A point-in-time manifest of a repo's code-intel state.
442
+
443
+ Phase 3a delivers creation / listing / deletion over this table.
444
+ Phase 3b will add an OCI adapter that pushes the same manifest +
445
+ component rows as an artifact to a registry.
446
+ """
447
+ __tablename__ = "repo_snapshots"
448
+
449
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=generate_uuid)
450
+ org_id: Mapped[uuid.UUID] = mapped_column(
451
+ UUID(as_uuid=True),
452
+ ForeignKey("organizations.id", ondelete="CASCADE"),
453
+ nullable=False,
454
+ )
455
+ repo_id: Mapped[uuid.UUID] = mapped_column(
456
+ UUID(as_uuid=True),
457
+ ForeignKey("repositories.id", ondelete="CASCADE"),
458
+ nullable=False,
459
+ )
460
+ branch_id: Mapped[uuid.UUID | None] = mapped_column(
461
+ UUID(as_uuid=True),
462
+ ForeignKey("branches.id", ondelete="SET NULL"),
463
+ nullable=True,
464
+ )
465
+ name: Mapped[str] = mapped_column(Text, nullable=False)
466
+ description: Mapped[str] = mapped_column(Text, nullable=False, server_default="")
467
+ manifest_hash: Mapped[str] = mapped_column(Text, nullable=False)
468
+ total_bytes: Mapped[int] = mapped_column(BigInteger, nullable=False, server_default="0")
469
+ component_count: Mapped[int] = mapped_column(Integer, nullable=False, server_default="0")
470
+ commit_oid: Mapped[str | None] = mapped_column(Text, nullable=True)
471
+ created_at: Mapped[datetime] = mapped_column(
472
+ DateTime(timezone=True),
473
+ server_default=func.now(),
474
+ nullable=False,
475
+ )
476
+ created_by_user_id: Mapped[uuid.UUID | None] = mapped_column(
477
+ UUID(as_uuid=True),
478
+ ForeignKey("users.id", ondelete="SET NULL"),
479
+ nullable=True,
480
+ )
481
+ extra: Mapped[dict] = mapped_column(JSONB, nullable=False, server_default="{}")
482
+
483
+ components: Mapped[list[RepoSnapshotComponent]] = relationship(
484
+ back_populates="snapshot",
485
+ cascade="all, delete-orphan",
486
+ )
487
+
488
+ __table_args__ = (
489
+ __import__("sqlalchemy").UniqueConstraint(
490
+ "repo_id", "name", name="uq_repo_snapshots_repo_name",
491
+ ),
492
+ __import__("sqlalchemy").Index(
493
+ "ix_repo_snapshots_repo_created", "repo_id", "created_at",
494
+ ),
495
+ __import__("sqlalchemy").Index("ix_repo_snapshots_org", "org_id"),
496
+ )
497
+
498
+
499
+ class RepoSnapshotComponent(Base):
500
+ """One artifact entry inside a :class:`RepoSnapshot`.
501
+
502
+ ``name`` is a logical identifier (``"content"`` / ``"symbols"`` /
503
+ ``"embeddings.bge-small-en-v1.5"``), ``media_type`` is the OCI-shaped
504
+ media type string, and ``digest`` is the SHA-256 content hash.
505
+ """
506
+ __tablename__ = "repo_snapshot_components"
507
+
508
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=generate_uuid)
509
+ snapshot_id: Mapped[uuid.UUID] = mapped_column(
510
+ UUID(as_uuid=True),
511
+ ForeignKey("repo_snapshots.id", ondelete="CASCADE"),
512
+ nullable=False,
513
+ )
514
+ name: Mapped[str] = mapped_column(Text, nullable=False)
515
+ media_type: Mapped[str] = mapped_column(Text, nullable=False)
516
+ digest: Mapped[str] = mapped_column(Text, nullable=False)
517
+ size_bytes: Mapped[int] = mapped_column(BigInteger, nullable=False, server_default="0")
518
+ extra: Mapped[dict] = mapped_column(JSONB, nullable=False, server_default="{}")
519
+
520
+ snapshot: Mapped[RepoSnapshot] = relationship(back_populates="components")
521
+
522
+ __table_args__ = (
523
+ __import__("sqlalchemy").Index(
524
+ "ix_repo_snapshot_components_snapshot", "snapshot_id",
525
+ ),
526
+ __import__("sqlalchemy").Index(
527
+ "ix_repo_snapshot_components_digest", "digest",
528
+ ),
529
+ )