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.
- attocode_code_intel-0.1.0.dist-info/METADATA +221 -0
- attocode_code_intel-0.1.0.dist-info/RECORD +345 -0
- attocode_code_intel-0.1.0.dist-info/WHEEL +4 -0
- attocode_code_intel-0.1.0.dist-info/entry_points.txt +2 -0
- attocode_code_intel-0.1.0.dist-info/licenses/LICENSE +21 -0
- attocode_intel/GUIDELINES.md +432 -0
- attocode_intel/__init__.py +52 -0
- attocode_intel/_internal/__init__.py +1 -0
- attocode_intel/_internal/errors/__init__.py +126 -0
- attocode_intel/_internal/integrations/__init__.py +1 -0
- attocode_intel/_internal/integrations/context/__init__.py +1 -0
- attocode_intel/_internal/integrations/context/architecture_drift.py +354 -0
- attocode_intel/_internal/integrations/context/ast_chunker.py +202 -0
- attocode_intel/_internal/integrations/context/ast_service.py +1483 -0
- attocode_intel/_internal/integrations/context/blob_oid.py +423 -0
- attocode_intel/_internal/integrations/context/cache_manifest.py +221 -0
- attocode_intel/_internal/integrations/context/cas.py +466 -0
- attocode_intel/_internal/integrations/context/code_analyzer.py +528 -0
- attocode_intel/_internal/integrations/context/codebase_ast.py +1461 -0
- attocode_intel/_internal/integrations/context/codebase_context.py +1736 -0
- attocode_intel/_internal/integrations/context/cross_mode.py +263 -0
- attocode_intel/_internal/integrations/context/cross_references.py +616 -0
- attocode_intel/_internal/integrations/context/embedding_rotation.py +597 -0
- attocode_intel/_internal/integrations/context/embeddings.py +282 -0
- attocode_intel/_internal/integrations/context/frecency.py +427 -0
- attocode_intel/_internal/integrations/context/fuzzy.py +320 -0
- attocode_intel/_internal/integrations/context/hierarchical_explorer.py +331 -0
- attocode_intel/_internal/integrations/context/hydration.py +90 -0
- attocode_intel/_internal/integrations/context/index_repair.py +87 -0
- attocode_intel/_internal/integrations/context/index_store.py +586 -0
- attocode_intel/_internal/integrations/context/memory_store.py +602 -0
- attocode_intel/_internal/integrations/context/module_resolution.py +52 -0
- attocode_intel/_internal/integrations/context/query_constraints.py +361 -0
- attocode_intel/_internal/integrations/context/query_history.py +417 -0
- attocode_intel/_internal/integrations/context/reranker.py +351 -0
- attocode_intel/_internal/integrations/context/semantic_search.py +2931 -0
- attocode_intel/_internal/integrations/context/syntax_evidence.py +94 -0
- attocode_intel/_internal/integrations/context/systemone_ranker.py +245 -0
- attocode_intel/_internal/integrations/context/temporal_coupling.py +411 -0
- attocode_intel/_internal/integrations/context/trigram_index.py +820 -0
- attocode_intel/_internal/integrations/context/trigram_regex.py +171 -0
- attocode_intel/_internal/integrations/context/ts_parser.py +2199 -0
- attocode_intel/_internal/integrations/context/vector_store.py +795 -0
- attocode_intel/_internal/integrations/feature_flags/__init__.py +357 -0
- attocode_intel/_internal/integrations/lsp/__init__.py +1 -0
- attocode_intel/_internal/integrations/lsp/client.py +1140 -0
- attocode_intel/_internal/integrations/mcp/__init__.py +1 -0
- attocode_intel/_internal/integrations/mcp/client.py +253 -0
- attocode_intel/_internal/integrations/security/__init__.py +1 -0
- attocode_intel/_internal/integrations/security/custom_rules.py +176 -0
- attocode_intel/_internal/integrations/security/dataflow.py +522 -0
- attocode_intel/_internal/integrations/security/dependency_audit.py +303 -0
- attocode_intel/_internal/integrations/security/matcher.py +44 -0
- attocode_intel/_internal/integrations/security/patterns.py +724 -0
- attocode_intel/_internal/integrations/security/scanner.py +351 -0
- attocode_intel/_internal/integrations/utilities/__init__.py +1 -0
- attocode_intel/_internal/integrations/utilities/ignore.py +64 -0
- attocode_intel/_internal/integrations/utilities/token_estimate.py +51 -0
- attocode_intel/_internal/tricks/__init__.py +1 -0
- attocode_intel/_internal/tricks/recursive_context.py +357 -0
- attocode_intel/_internal/types/__init__.py +1 -0
- attocode_intel/_internal/types/events.py +289 -0
- attocode_intel/_internal/types/severity.py +15 -0
- attocode_intel/_shared.py +341 -0
- attocode_intel/analysis_coverage.py +56 -0
- attocode_intel/api/__init__.py +1 -0
- attocode_intel/api/access.py +32 -0
- attocode_intel/api/app.py +247 -0
- attocode_intel/api/auth/__init__.py +128 -0
- attocode_intel/api/auth/api_keys.py +61 -0
- attocode_intel/api/auth/context.py +34 -0
- attocode_intel/api/auth/jwt.py +66 -0
- attocode_intel/api/auth/oauth.py +146 -0
- attocode_intel/api/auth/passwords.py +15 -0
- attocode_intel/api/deps.py +351 -0
- attocode_intel/api/knowledge_bridge.py +13 -0
- attocode_intel/api/mcp_transport.py +51 -0
- attocode_intel/api/middleware.py +411 -0
- attocode_intel/api/models.py +698 -0
- attocode_intel/api/providers/__init__.py +1 -0
- attocode_intel/api/providers/db_provider.py +1106 -0
- attocode_intel/api/providers/local_provider.py +476 -0
- attocode_intel/api/providers/remote_provider.py +740 -0
- attocode_intel/api/repository_source.py +21 -0
- attocode_intel/api/routes/__init__.py +1 -0
- attocode_intel/api/routes/_pin_helper.py +158 -0
- attocode_intel/api/routes/activity.py +148 -0
- attocode_intel/api/routes/adr.py +102 -0
- attocode_intel/api/routes/analysis.py +515 -0
- attocode_intel/api/routes/api_keys.py +170 -0
- attocode_intel/api/routes/auth.py +428 -0
- attocode_intel/api/routes/branches.py +262 -0
- attocode_intel/api/routes/cross_repo_search.py +198 -0
- attocode_intel/api/routes/embeddings.py +386 -0
- attocode_intel/api/routes/files.py +132 -0
- attocode_intel/api/routes/files_v2.py +388 -0
- attocode_intel/api/routes/gc.py +322 -0
- attocode_intel/api/routes/git_v2.py +433 -0
- attocode_intel/api/routes/graph.py +174 -0
- attocode_intel/api/routes/graph_viz.py +160 -0
- attocode_intel/api/routes/health.py +62 -0
- attocode_intel/api/routes/history.py +175 -0
- attocode_intel/api/routes/jobs.py +84 -0
- attocode_intel/api/routes/learning.py +205 -0
- attocode_intel/api/routes/lsp.py +272 -0
- attocode_intel/api/routes/notify.py +623 -0
- attocode_intel/api/routes/operations.py +27 -0
- attocode_intel/api/routes/orgs.py +924 -0
- attocode_intel/api/routes/preferences.py +100 -0
- attocode_intel/api/routes/presence.py +42 -0
- attocode_intel/api/routes/projects.py +116 -0
- attocode_intel/api/routes/repos.py +47 -0
- attocode_intel/api/routes/search.py +180 -0
- attocode_intel/api/routes/snapshots.py +614 -0
- attocode_intel/api/routes/webhooks.py +313 -0
- attocode_intel/api/routes/websocket.py +116 -0
- attocode_intel/api/static/graph.html +239 -0
- attocode_intel/api/utils.py +96 -0
- attocode_intel/artifacts/__init__.py +45 -0
- attocode_intel/artifacts/hashing.py +230 -0
- attocode_intel/artifacts/provenance.py +190 -0
- attocode_intel/artifacts/retrieval_pin.py +159 -0
- attocode_intel/audit.py +59 -0
- attocode_intel/bug_finder.py +206 -0
- attocode_intel/bundle.py +80 -0
- attocode_intel/catalog.py +242 -0
- attocode_intel/change_review.py +75 -0
- attocode_intel/cli.py +3062 -0
- attocode_intel/community.py +63 -0
- attocode_intel/confidence/__init__.py +144 -0
- attocode_intel/confidence/jev.py +234 -0
- attocode_intel/confidence/llm.py +330 -0
- attocode_intel/confidence/redact.py +44 -0
- attocode_intel/confidence/settings.py +74 -0
- attocode_intel/config.py +256 -0
- attocode_intel/crypto.py +31 -0
- attocode_intel/db/__init__.py +7 -0
- attocode_intel/db/base.py +33 -0
- attocode_intel/db/engine.py +61 -0
- attocode_intel/db/models.py +529 -0
- attocode_intel/entrypoint.py +185 -0
- attocode_intel/focused_evidence.py +130 -0
- attocode_intel/freshness.py +154 -0
- attocode_intel/gateway.py +734 -0
- attocode_intel/git/__init__.py +7 -0
- attocode_intel/git/credentials.py +75 -0
- attocode_intel/git/manager.py +493 -0
- attocode_intel/git/models.py +96 -0
- attocode_intel/git/storage.py +98 -0
- attocode_intel/graph_query_parser.py +761 -0
- attocode_intel/helpers.py +712 -0
- attocode_intel/indexing/__init__.py +29 -0
- attocode_intel/indexing/debouncer.py +136 -0
- attocode_intel/indexing/delta_indexer.py +151 -0
- attocode_intel/indexing/full_indexer.py +323 -0
- attocode_intel/indexing/incremental.py +240 -0
- attocode_intel/indexing/parser.py +403 -0
- attocode_intel/indexing/summarizer.py +264 -0
- attocode_intel/installer.py +1896 -0
- attocode_intel/knowledge.py +269 -0
- attocode_intel/local_knowledge.py +77 -0
- attocode_intel/migrations/alembic.ini +36 -0
- attocode_intel/migrations/env.py +71 -0
- attocode_intel/migrations/script.py.mako +27 -0
- attocode_intel/migrations/versions/001_initial_schema.py +219 -0
- attocode_intel/migrations/versions/002_branch_version_and_embeddings.py +61 -0
- attocode_intel/migrations/versions/003_embedding_uniqueness_and_cascade.py +51 -0
- attocode_intel/migrations/versions/004_audit_events.py +38 -0
- attocode_intel/migrations/versions/005_user_preferences.py +30 -0
- attocode_intel/migrations/versions/006_google_oauth.py +27 -0
- attocode_intel/migrations/versions/007_pgvector_embeddings.py +28 -0
- attocode_intel/migrations/versions/008_symbol_references.py +37 -0
- attocode_intel/migrations/versions/009_webhook_secret_encryption.py +25 -0
- attocode_intel/migrations/versions/010_add_commits_table.py +40 -0
- attocode_intel/migrations/versions/011_commit_changed_files.py +25 -0
- attocode_intel/migrations/versions/012_branch_merged_at.py +25 -0
- attocode_intel/migrations/versions/013_revoked_tokens.py +32 -0
- attocode_intel/migrations/versions/014_blame_hunks.py +40 -0
- attocode_intel/migrations/versions/015_commit_file_stats.py +40 -0
- attocode_intel/migrations/versions/016_embedding_provenance_columns.py +179 -0
- attocode_intel/migrations/versions/017_provenance_table.py +102 -0
- attocode_intel/migrations/versions/018_repo_snapshots.py +178 -0
- attocode_intel/migrations/versions/019_symref_caller.py +42 -0
- attocode_intel/migrations/versions/020_shared_knowledge.py +29 -0
- attocode_intel/onboarding.py +258 -0
- attocode_intel/output.py +178 -0
- attocode_intel/precision.py +133 -0
- attocode_intel/presence.py +160 -0
- attocode_intel/probe.py +110 -0
- attocode_intel/project_dir.py +97 -0
- attocode_intel/pubsub.py +113 -0
- attocode_intel/query_ranking.py +242 -0
- attocode_intel/readiness.py +972 -0
- attocode_intel/redis.py +51 -0
- attocode_intel/reference_pages.py +60 -0
- attocode_intel/remote.py +131 -0
- attocode_intel/repo_ranker.py +246 -0
- attocode_intel/request_context.py +82 -0
- attocode_intel/rules/__init__.py +28 -0
- attocode_intel/rules/ci.py +320 -0
- attocode_intel/rules/combinators.py +293 -0
- attocode_intel/rules/enricher.py +127 -0
- attocode_intel/rules/evolution.py +452 -0
- attocode_intel/rules/executor.py +531 -0
- attocode_intel/rules/filters/__init__.py +5 -0
- attocode_intel/rules/filters/pipeline.py +151 -0
- attocode_intel/rules/formatter.py +198 -0
- attocode_intel/rules/hygiene.py +242 -0
- attocode_intel/rules/importers/__init__.py +1 -0
- attocode_intel/rules/importers/semgrep.py +382 -0
- attocode_intel/rules/loader.py +292 -0
- attocode_intel/rules/marketplace.py +375 -0
- attocode_intel/rules/metavar.py +181 -0
- attocode_intel/rules/model.py +317 -0
- attocode_intel/rules/packs/__init__.py +1 -0
- attocode_intel/rules/packs/community/_license_templates/Apache-2.0.txt +176 -0
- attocode_intel/rules/packs/community/_license_templates/MIT.txt +21 -0
- attocode_intel/rules/packs/community/bandit-python/LICENSE +176 -0
- attocode_intel/rules/packs/community/bandit-python/NOTICE +13 -0
- attocode_intel/rules/packs/community/bandit-python/PORTING.md +23 -0
- attocode_intel/rules/packs/community/bandit-python/manifest.yaml +14 -0
- attocode_intel/rules/packs/community/bandit-python/rules/B105-hardcoded-password.yaml +23 -0
- attocode_intel/rules/packs/community/bandit-python/rules/B303-weak-md5.yaml +21 -0
- attocode_intel/rules/packs/community/bandit-python/rules/B304-weak-des.yaml +20 -0
- attocode_intel/rules/packs/community/bandit-python/rules/B307-use-of-eval.yaml +22 -0
- attocode_intel/rules/packs/community/bandit-python/rules/B324-weak-sha1.yaml +21 -0
- attocode_intel/rules/packs/community/bandit-python/rules/B501-no-cert-validation.yaml +23 -0
- attocode_intel/rules/packs/community/bandit-python/rules/B602-shell-true.yaml +22 -0
- attocode_intel/rules/packs/community/bandit-python/rules/B608-sqli.yaml +22 -0
- attocode_intel/rules/packs/community/eslint-typescript/LICENSE +21 -0
- attocode_intel/rules/packs/community/eslint-typescript/NOTICE +13 -0
- attocode_intel/rules/packs/community/eslint-typescript/PORTING.md +23 -0
- attocode_intel/rules/packs/community/eslint-typescript/manifest.yaml +14 -0
- attocode_intel/rules/packs/community/eslint-typescript/rules/eqeqeq.yaml +20 -0
- attocode_intel/rules/packs/community/eslint-typescript/rules/no-eval.yaml +22 -0
- attocode_intel/rules/packs/community/eslint-typescript/rules/no-implied-eval.yaml +20 -0
- attocode_intel/rules/packs/community/eslint-typescript/rules/no-new-func.yaml +21 -0
- attocode_intel/rules/packs/community/eslint-typescript/rules/no-throw-literal.yaml +19 -0
- attocode_intel/rules/packs/community/eslint-typescript/rules/no-var.yaml +20 -0
- attocode_intel/rules/packs/community/eslint-typescript/rules/no-with.yaml +18 -0
- attocode_intel/rules/packs/community/gosec-go/LICENSE +176 -0
- attocode_intel/rules/packs/community/gosec-go/NOTICE +13 -0
- attocode_intel/rules/packs/community/gosec-go/PORTING.md +23 -0
- attocode_intel/rules/packs/community/gosec-go/manifest.yaml +14 -0
- attocode_intel/rules/packs/community/gosec-go/rules/G101-hardcoded-credentials.yaml +22 -0
- attocode_intel/rules/packs/community/gosec-go/rules/G102-bind-all-interfaces.yaml +21 -0
- attocode_intel/rules/packs/community/gosec-go/rules/G103-unsafe-block.yaml +21 -0
- attocode_intel/rules/packs/community/gosec-go/rules/G201-sql-format.yaml +21 -0
- attocode_intel/rules/packs/community/gosec-go/rules/G304-file-inclusion.yaml +23 -0
- attocode_intel/rules/packs/community/gosec-go/rules/G401-weak-cipher.yaml +20 -0
- attocode_intel/rules/packs/community/gosec-go/rules/G402-tls-min-version.yaml +20 -0
- attocode_intel/rules/packs/community/gosec-go/rules/G501-md5-import.yaml +21 -0
- attocode_intel/rules/packs/examples/cpp/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/cpp/rules/security.yaml +90 -0
- attocode_intel/rules/packs/examples/cpp/rules/structural.yaml +42 -0
- attocode_intel/rules/packs/examples/csharp/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/csharp/rules/security.yaml +69 -0
- attocode_intel/rules/packs/examples/csharp/rules/structural.yaml +42 -0
- attocode_intel/rules/packs/examples/go/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/go/rules/performance.yaml +136 -0
- attocode_intel/rules/packs/examples/go/rules/security.yaml +48 -0
- attocode_intel/rules/packs/examples/go/rules/structural.yaml +58 -0
- attocode_intel/rules/packs/examples/go/taint/sanitizers.yaml +24 -0
- attocode_intel/rules/packs/examples/go/taint/sinks.yaml +51 -0
- attocode_intel/rules/packs/examples/go/taint/sources.yaml +26 -0
- attocode_intel/rules/packs/examples/java/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/java/rules/correctness.yaml +55 -0
- attocode_intel/rules/packs/examples/java/rules/structural.yaml +48 -0
- attocode_intel/rules/packs/examples/java/taint/sinks.yaml +37 -0
- attocode_intel/rules/packs/examples/java/taint/sources.yaml +21 -0
- attocode_intel/rules/packs/examples/kotlin/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/kotlin/rules/correctness.yaml +50 -0
- attocode_intel/rules/packs/examples/kotlin/rules/structural.yaml +38 -0
- attocode_intel/rules/packs/examples/php/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/php/rules/security.yaml +66 -0
- attocode_intel/rules/packs/examples/php/rules/structural.yaml +41 -0
- attocode_intel/rules/packs/examples/python/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/python/rules/performance.yaml +90 -0
- attocode_intel/rules/packs/examples/python/rules/structural.yaml +55 -0
- attocode_intel/rules/packs/examples/ruby/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/ruby/rules/security.yaml +73 -0
- attocode_intel/rules/packs/examples/ruby/rules/structural.yaml +41 -0
- attocode_intel/rules/packs/examples/rust/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/rust/rules/correctness.yaml +51 -0
- attocode_intel/rules/packs/examples/rust/rules/structural.yaml +49 -0
- attocode_intel/rules/packs/examples/typescript/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/typescript/rules/performance.yaml +70 -0
- attocode_intel/rules/packs/examples/typescript/rules/structural.yaml +48 -0
- attocode_intel/rules/packs/pack_loader.py +244 -0
- attocode_intel/rules/plugins/__init__.py +1 -0
- attocode_intel/rules/plugins/plugin_loader.py +132 -0
- attocode_intel/rules/profiling.py +332 -0
- attocode_intel/rules/registry.py +213 -0
- attocode_intel/rules/sarif.py +187 -0
- attocode_intel/rules/synthesis.py +420 -0
- attocode_intel/rules/testing.py +326 -0
- attocode_intel/server.py +734 -0
- attocode_intel/service.py +3416 -0
- attocode_intel/sharing.py +57 -0
- attocode_intel/storage/__init__.py +6 -0
- attocode_intel/storage/branch_overlay.py +352 -0
- attocode_intel/storage/content_store.py +220 -0
- attocode_intel/storage/dependency_store.py +143 -0
- attocode_intel/storage/diff_engine.py +187 -0
- attocode_intel/storage/embedding_store.py +594 -0
- attocode_intel/storage/provenance_store.py +137 -0
- attocode_intel/storage/security_scanner_db.py +164 -0
- attocode_intel/storage/symbol_store.py +160 -0
- attocode_intel/structural_evidence.py +144 -0
- attocode_intel/symbol_inspection.py +141 -0
- attocode_intel/symbol_links.py +167 -0
- attocode_intel/telemetry.py +42 -0
- attocode_intel/test_ranking.py +73 -0
- attocode_intel/testing/__init__.py +43 -0
- attocode_intel/testing/fixtures.py +122 -0
- attocode_intel/testing/helpers.py +75 -0
- attocode_intel/testing/mocks.py +99 -0
- attocode_intel/tools/__init__.py +2 -0
- attocode_intel/tools/adr_tools.py +628 -0
- attocode_intel/tools/analysis_tools.py +382 -0
- attocode_intel/tools/composite_tools.py +695 -0
- attocode_intel/tools/cross_mode_tools.py +173 -0
- attocode_intel/tools/dead_code_tools.py +582 -0
- attocode_intel/tools/distill_tools.py +353 -0
- attocode_intel/tools/frecency_tools.py +220 -0
- attocode_intel/tools/fuzzy_tools.py +255 -0
- attocode_intel/tools/history_tools.py +486 -0
- attocode_intel/tools/learning_tools.py +222 -0
- attocode_intel/tools/lsp_tools.py +218 -0
- attocode_intel/tools/maintenance_tools.py +1662 -0
- attocode_intel/tools/navigation_tools.py +220 -0
- attocode_intel/tools/overlay_tools.py +540 -0
- attocode_intel/tools/pin_store.py +432 -0
- attocode_intel/tools/pin_tools.py +266 -0
- attocode_intel/tools/query_constraints_tools.py +262 -0
- attocode_intel/tools/query_history_tools.py +158 -0
- attocode_intel/tools/readiness_tools.py +44 -0
- attocode_intel/tools/rule_tools.py +992 -0
- attocode_intel/tools/search_tools.py +770 -0
- attocode_intel/tools/snapshot_tools.py +624 -0
- attocode_intel/workers/__init__.py +1 -0
- attocode_intel/workers/job_utils.py +111 -0
- attocode_intel/workers/jobs.py +573 -0
- attocode_intel/workers/run.py +30 -0
- attocode_intel/workers/settings.py +61 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Add blame_hunks table for DB-backed blame data.
|
|
2
|
+
|
|
3
|
+
Revision ID: 014
|
|
4
|
+
Revises: 013
|
|
5
|
+
Create Date: 2026-03-15
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Sequence, Union
|
|
10
|
+
|
|
11
|
+
import sqlalchemy as sa
|
|
12
|
+
from alembic import op
|
|
13
|
+
|
|
14
|
+
revision: str = "014"
|
|
15
|
+
down_revision: Union[str, None] = "013"
|
|
16
|
+
branch_labels: Union[str, Sequence[str], None] = None
|
|
17
|
+
depends_on: Union[str, Sequence[str], None] = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def upgrade() -> None:
|
|
21
|
+
op.create_table(
|
|
22
|
+
"blame_hunks",
|
|
23
|
+
sa.Column("id", sa.dialects.postgresql.UUID(as_uuid=True), primary_key=True),
|
|
24
|
+
sa.Column("repo_id", sa.dialects.postgresql.UUID(as_uuid=True), sa.ForeignKey("repositories.id", ondelete="CASCADE"), nullable=False),
|
|
25
|
+
sa.Column("path", sa.Text(), nullable=False),
|
|
26
|
+
sa.Column("branch_name", sa.Text(), nullable=False, server_default="main"),
|
|
27
|
+
sa.Column("commit_oid", sa.Text(), nullable=False),
|
|
28
|
+
sa.Column("author_name", sa.Text(), nullable=False),
|
|
29
|
+
sa.Column("author_email", sa.Text(), nullable=False),
|
|
30
|
+
sa.Column("timestamp", sa.BigInteger(), nullable=False),
|
|
31
|
+
sa.Column("start_line", sa.Integer(), nullable=False),
|
|
32
|
+
sa.Column("end_line", sa.Integer(), nullable=False),
|
|
33
|
+
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
|
34
|
+
)
|
|
35
|
+
op.create_index("ix_blame_hunks_repo_path_branch", "blame_hunks", ["repo_id", "path", "branch_name"])
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def downgrade() -> None:
|
|
39
|
+
op.drop_index("ix_blame_hunks_repo_path_branch", table_name="blame_hunks")
|
|
40
|
+
op.drop_table("blame_hunks")
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Add commit_file_stats table for per-file commit statistics.
|
|
2
|
+
|
|
3
|
+
Revision ID: 015
|
|
4
|
+
Revises: 014
|
|
5
|
+
Create Date: 2026-03-21
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Sequence, Union
|
|
10
|
+
|
|
11
|
+
import sqlalchemy as sa
|
|
12
|
+
from alembic import op
|
|
13
|
+
|
|
14
|
+
revision: str = "015"
|
|
15
|
+
down_revision: Union[str, None] = "014"
|
|
16
|
+
branch_labels: Union[str, Sequence[str], None] = None
|
|
17
|
+
depends_on: Union[str, Sequence[str], None] = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def upgrade() -> None:
|
|
21
|
+
op.create_table(
|
|
22
|
+
"commit_file_stats",
|
|
23
|
+
sa.Column("id", sa.dialects.postgresql.UUID(as_uuid=True), primary_key=True),
|
|
24
|
+
sa.Column("commit_id", sa.dialects.postgresql.UUID(as_uuid=True), sa.ForeignKey("commits.id", ondelete="CASCADE"), nullable=False),
|
|
25
|
+
sa.Column("repo_id", sa.dialects.postgresql.UUID(as_uuid=True), sa.ForeignKey("repositories.id", ondelete="CASCADE"), nullable=False),
|
|
26
|
+
sa.Column("path", sa.Text(), nullable=False),
|
|
27
|
+
sa.Column("lines_added", sa.Integer(), nullable=False, server_default="0"),
|
|
28
|
+
sa.Column("lines_removed", sa.Integer(), nullable=False, server_default="0"),
|
|
29
|
+
sa.Column("symbols_changed", sa.dialects.postgresql.JSONB(), nullable=False, server_default="[]"),
|
|
30
|
+
sa.Column("change_type", sa.Text(), nullable=False),
|
|
31
|
+
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
|
32
|
+
)
|
|
33
|
+
op.create_index("ix_commit_file_stats_repo_path", "commit_file_stats", ["repo_id", "path"])
|
|
34
|
+
op.create_index("ix_commit_file_stats_commit_id", "commit_file_stats", ["commit_id"])
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def downgrade() -> None:
|
|
38
|
+
op.drop_index("ix_commit_file_stats_commit_id", table_name="commit_file_stats")
|
|
39
|
+
op.drop_index("ix_commit_file_stats_repo_path", table_name="commit_file_stats")
|
|
40
|
+
op.drop_table("commit_file_stats")
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""Add provenance columns to embeddings (non-destructive).
|
|
2
|
+
|
|
3
|
+
This migration addresses the "embedding model/dimension silently wipes all
|
|
4
|
+
vectors" footgun by introducing explicit provenance columns — *without*
|
|
5
|
+
touching the primary ``vector`` column. Re-dimensioning is handled via a
|
|
6
|
+
separate rotation flow in a later phase; this migration is purely additive.
|
|
7
|
+
|
|
8
|
+
Changes:
|
|
9
|
+
- Add ``embeddings.embedding_model_version`` (text, default '').
|
|
10
|
+
- Add ``embeddings.embedding_dim`` (int, nullable — backfilled from
|
|
11
|
+
``vector_dims(vector)`` for rows where pgvector is available).
|
|
12
|
+
- Add ``embeddings.embedding_provenance`` (JSONB, default '{}').
|
|
13
|
+
- Replace the ``(content_sha, embedding_model, chunk_type)`` unique with
|
|
14
|
+
``(content_sha, embedding_model, embedding_model_version, chunk_type)``
|
|
15
|
+
so two versions of the same model can coexist during rotation.
|
|
16
|
+
|
|
17
|
+
Revision ID: 016
|
|
18
|
+
Revises: 015
|
|
19
|
+
Create Date: 2026-04-10
|
|
20
|
+
"""
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from typing import TYPE_CHECKING
|
|
24
|
+
|
|
25
|
+
import sqlalchemy as sa
|
|
26
|
+
from alembic import op
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from collections.abc import Sequence
|
|
30
|
+
|
|
31
|
+
revision: str = "016"
|
|
32
|
+
down_revision: str | None = "015"
|
|
33
|
+
branch_labels: str | Sequence[str] | None = None
|
|
34
|
+
depends_on: str | Sequence[str] | None = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def upgrade() -> None:
|
|
38
|
+
# 1. Add new columns with safe defaults so existing rows don't break.
|
|
39
|
+
op.add_column(
|
|
40
|
+
"embeddings",
|
|
41
|
+
sa.Column(
|
|
42
|
+
"embedding_model_version",
|
|
43
|
+
sa.Text(),
|
|
44
|
+
nullable=False,
|
|
45
|
+
server_default="",
|
|
46
|
+
),
|
|
47
|
+
)
|
|
48
|
+
op.add_column(
|
|
49
|
+
"embeddings",
|
|
50
|
+
sa.Column(
|
|
51
|
+
"embedding_dim",
|
|
52
|
+
sa.Integer(),
|
|
53
|
+
nullable=True,
|
|
54
|
+
),
|
|
55
|
+
)
|
|
56
|
+
op.add_column(
|
|
57
|
+
"embeddings",
|
|
58
|
+
sa.Column(
|
|
59
|
+
"embedding_provenance",
|
|
60
|
+
sa.dialects.postgresql.JSONB(),
|
|
61
|
+
nullable=False,
|
|
62
|
+
server_default="{}",
|
|
63
|
+
),
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# 2. Backfill embedding_dim from pgvector's vector_dims() where possible.
|
|
67
|
+
# Rows without a vector (vector IS NULL) stay NULL.
|
|
68
|
+
#
|
|
69
|
+
# Done in a DO block so a connection lacking the `vector` extension
|
|
70
|
+
# or the `vector` column still lets the migration run on brand-new
|
|
71
|
+
# databases where ensure_vector_column() hasn't fired yet.
|
|
72
|
+
op.execute(
|
|
73
|
+
"""
|
|
74
|
+
DO $$
|
|
75
|
+
BEGIN
|
|
76
|
+
IF EXISTS (
|
|
77
|
+
SELECT 1 FROM information_schema.columns
|
|
78
|
+
WHERE table_name = 'embeddings' AND column_name = 'vector'
|
|
79
|
+
) THEN
|
|
80
|
+
EXECUTE 'UPDATE embeddings SET embedding_dim = vector_dims(vector) '
|
|
81
|
+
'WHERE vector IS NOT NULL';
|
|
82
|
+
END IF;
|
|
83
|
+
END $$;
|
|
84
|
+
"""
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# 3. Replace the unique constraint to include embedding_model_version.
|
|
88
|
+
# During rotation two versions of the same model coexist in place.
|
|
89
|
+
op.drop_constraint(
|
|
90
|
+
"uq_embedding_content_model_chunk",
|
|
91
|
+
"embeddings",
|
|
92
|
+
type_="unique",
|
|
93
|
+
)
|
|
94
|
+
op.create_unique_constraint(
|
|
95
|
+
"uq_embedding_content_model_version_chunk",
|
|
96
|
+
"embeddings",
|
|
97
|
+
["content_sha", "embedding_model", "embedding_model_version", "chunk_type"],
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# 4. Helpful secondary index for provenance lookups.
|
|
101
|
+
op.create_index(
|
|
102
|
+
"ix_embeddings_model_version",
|
|
103
|
+
"embeddings",
|
|
104
|
+
["embedding_model", "embedding_model_version"],
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def downgrade() -> None:
|
|
109
|
+
"""Revert the non-destructive dual-column rewrite.
|
|
110
|
+
|
|
111
|
+
A naive downgrade (drop the wide unique key, recreate the narrow
|
|
112
|
+
one) fails outright when a dual-version rotation has left multiple
|
|
113
|
+
``embedding_model_version`` values for the same
|
|
114
|
+
``(content_sha, embedding_model, chunk_type)`` tuple — recreating
|
|
115
|
+
the narrow unique constraint raises at DDL time.
|
|
116
|
+
|
|
117
|
+
Safe default: REFUSE the downgrade unless the operator sets
|
|
118
|
+
``ATTOCODE_ALLOW_DESTRUCTIVE_016_DOWNGRADE``. When set, delete every
|
|
119
|
+
row whose ``embedding_model_version`` is non-empty first (logging
|
|
120
|
+
the count) and then rewrite. Pre-rotation databases (every row has
|
|
121
|
+
``embedding_model_version = ''``) downgrade cleanly without the
|
|
122
|
+
flag.
|
|
123
|
+
"""
|
|
124
|
+
import logging
|
|
125
|
+
import os
|
|
126
|
+
|
|
127
|
+
logger = logging.getLogger("alembic.016")
|
|
128
|
+
|
|
129
|
+
bind = op.get_bind()
|
|
130
|
+
conflicts_row = bind.execute(
|
|
131
|
+
sa.text(
|
|
132
|
+
"SELECT COUNT(*) FROM embeddings "
|
|
133
|
+
"WHERE embedding_model_version IS NOT NULL "
|
|
134
|
+
" AND embedding_model_version != ''"
|
|
135
|
+
)
|
|
136
|
+
).fetchone()
|
|
137
|
+
conflict_count = int(conflicts_row[0] or 0) if conflicts_row else 0
|
|
138
|
+
|
|
139
|
+
if conflict_count > 0:
|
|
140
|
+
allow = os.environ.get(
|
|
141
|
+
"ATTOCODE_ALLOW_DESTRUCTIVE_016_DOWNGRADE", ""
|
|
142
|
+
).lower() in {"1", "true", "yes"}
|
|
143
|
+
if not allow:
|
|
144
|
+
raise RuntimeError(
|
|
145
|
+
f"Refusing to downgrade migration 016: {conflict_count} "
|
|
146
|
+
f"embedding row(s) carry a non-empty "
|
|
147
|
+
f"embedding_model_version, which would violate the "
|
|
148
|
+
f"recreated (content_sha, embedding_model, chunk_type) "
|
|
149
|
+
f"unique constraint. Set "
|
|
150
|
+
f"ATTOCODE_ALLOW_DESTRUCTIVE_016_DOWNGRADE=1 to confirm "
|
|
151
|
+
f"the destructive downgrade (this will DELETE those rows)."
|
|
152
|
+
)
|
|
153
|
+
logger.warning(
|
|
154
|
+
"016 downgrade: destructive flag set, deleting %d "
|
|
155
|
+
"embedding row(s) with non-empty embedding_model_version",
|
|
156
|
+
conflict_count,
|
|
157
|
+
)
|
|
158
|
+
bind.execute(
|
|
159
|
+
sa.text(
|
|
160
|
+
"DELETE FROM embeddings "
|
|
161
|
+
"WHERE embedding_model_version IS NOT NULL "
|
|
162
|
+
" AND embedding_model_version != ''"
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
op.drop_index("ix_embeddings_model_version", table_name="embeddings")
|
|
167
|
+
op.drop_constraint(
|
|
168
|
+
"uq_embedding_content_model_version_chunk",
|
|
169
|
+
"embeddings",
|
|
170
|
+
type_="unique",
|
|
171
|
+
)
|
|
172
|
+
op.create_unique_constraint(
|
|
173
|
+
"uq_embedding_content_model_chunk",
|
|
174
|
+
"embeddings",
|
|
175
|
+
["content_sha", "embedding_model", "chunk_type"],
|
|
176
|
+
)
|
|
177
|
+
op.drop_column("embeddings", "embedding_provenance")
|
|
178
|
+
op.drop_column("embeddings", "embedding_dim")
|
|
179
|
+
op.drop_column("embeddings", "embedding_model_version")
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Add provenance table — one row per derived code-intel artifact.
|
|
2
|
+
|
|
3
|
+
A ``Provenance`` row records how a symbol set / embedding / dep graph /
|
|
4
|
+
repo-map slice was produced: which indexer, which version, which blob was
|
|
5
|
+
the input, when, and by whom. Joined into derived-artifact queries by
|
|
6
|
+
``action_hash``.
|
|
7
|
+
|
|
8
|
+
Per-row storage here rather than a JSONB column on every derived table
|
|
9
|
+
because:
|
|
10
|
+
- Multiple artifact types want the same shape (no duplication).
|
|
11
|
+
- Queries like "find everything produced by indexer X version Y" are
|
|
12
|
+
common for audit / GC and would be O(n_tables) with per-table JSONB.
|
|
13
|
+
- Provenance should outlive the derived row it describes (for audit).
|
|
14
|
+
|
|
15
|
+
Revision ID: 017
|
|
16
|
+
Revises: 016
|
|
17
|
+
Create Date: 2026-04-10
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from typing import TYPE_CHECKING
|
|
22
|
+
|
|
23
|
+
import sqlalchemy as sa
|
|
24
|
+
from alembic import op
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from collections.abc import Sequence
|
|
28
|
+
|
|
29
|
+
revision: str = "017"
|
|
30
|
+
down_revision: str | None = "016"
|
|
31
|
+
branch_labels: str | Sequence[str] | None = None
|
|
32
|
+
depends_on: str | Sequence[str] | None = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def upgrade() -> None:
|
|
36
|
+
op.create_table(
|
|
37
|
+
"provenance",
|
|
38
|
+
sa.Column(
|
|
39
|
+
"id",
|
|
40
|
+
sa.dialects.postgresql.UUID(as_uuid=True),
|
|
41
|
+
primary_key=True,
|
|
42
|
+
server_default=sa.text("gen_random_uuid()"),
|
|
43
|
+
),
|
|
44
|
+
sa.Column("action_hash", sa.Text(), nullable=False),
|
|
45
|
+
sa.Column("artifact_type", sa.Text(), nullable=False),
|
|
46
|
+
sa.Column("input_blob_oid", sa.Text(), nullable=False),
|
|
47
|
+
sa.Column("input_tree_oid", sa.Text(), nullable=True),
|
|
48
|
+
sa.Column("indexer_name", sa.Text(), nullable=False),
|
|
49
|
+
sa.Column("indexer_version", sa.Text(), nullable=False),
|
|
50
|
+
sa.Column("config_digest", sa.Text(), nullable=False),
|
|
51
|
+
sa.Column(
|
|
52
|
+
"produced_at",
|
|
53
|
+
sa.DateTime(timezone=True),
|
|
54
|
+
server_default=sa.func.now(),
|
|
55
|
+
nullable=False,
|
|
56
|
+
),
|
|
57
|
+
sa.Column(
|
|
58
|
+
"producer_service",
|
|
59
|
+
sa.Text(),
|
|
60
|
+
nullable=False,
|
|
61
|
+
server_default="attocode-server",
|
|
62
|
+
),
|
|
63
|
+
sa.Column(
|
|
64
|
+
"producer_user_id",
|
|
65
|
+
sa.dialects.postgresql.UUID(as_uuid=True),
|
|
66
|
+
sa.ForeignKey("users.id", ondelete="SET NULL"),
|
|
67
|
+
nullable=True,
|
|
68
|
+
),
|
|
69
|
+
sa.Column(
|
|
70
|
+
"producer_job_id",
|
|
71
|
+
sa.dialects.postgresql.UUID(as_uuid=True),
|
|
72
|
+
sa.ForeignKey("indexing_jobs.id", ondelete="SET NULL"),
|
|
73
|
+
nullable=True,
|
|
74
|
+
),
|
|
75
|
+
sa.Column("producer_host", sa.Text(), nullable=False, server_default=""),
|
|
76
|
+
sa.Column(
|
|
77
|
+
"extra",
|
|
78
|
+
sa.dialects.postgresql.JSONB(),
|
|
79
|
+
nullable=False,
|
|
80
|
+
server_default="{}",
|
|
81
|
+
),
|
|
82
|
+
)
|
|
83
|
+
# action_hash is not quite unique: if the same artifact is re-produced
|
|
84
|
+
# (idempotent indexer rerun with identical inputs) we still want a new
|
|
85
|
+
# provenance row for audit purposes. So no UNIQUE, but an index for fast
|
|
86
|
+
# dedup joins.
|
|
87
|
+
op.create_index("ix_provenance_action_hash", "provenance", ["action_hash"])
|
|
88
|
+
op.create_index("ix_provenance_input_blob", "provenance", ["input_blob_oid"])
|
|
89
|
+
op.create_index("ix_provenance_job", "provenance", ["producer_job_id"])
|
|
90
|
+
op.create_index(
|
|
91
|
+
"ix_provenance_indexer_version",
|
|
92
|
+
"provenance",
|
|
93
|
+
["indexer_name", "indexer_version"],
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def downgrade() -> None:
|
|
98
|
+
op.drop_index("ix_provenance_indexer_version", table_name="provenance")
|
|
99
|
+
op.drop_index("ix_provenance_job", table_name="provenance")
|
|
100
|
+
op.drop_index("ix_provenance_input_blob", table_name="provenance")
|
|
101
|
+
op.drop_index("ix_provenance_action_hash", table_name="provenance")
|
|
102
|
+
op.drop_table("provenance")
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""Add repo_snapshots + repo_snapshot_components tables.
|
|
2
|
+
|
|
3
|
+
A ``repo_snapshot`` row records a point-in-time manifest hash of a repo's
|
|
4
|
+
code-intel state (branch manifest + derived artifacts). Components
|
|
5
|
+
(symbols / embeddings / deps / content slice) are stored in a sibling
|
|
6
|
+
table so downstream tools can walk the component list, fetch individual
|
|
7
|
+
blobs from ``file_contents`` / related stores, and reassemble the
|
|
8
|
+
snapshot without unpacking a monolithic tarball.
|
|
9
|
+
|
|
10
|
+
The shape of this table is forward-compatible with Phase 3b's OCI
|
|
11
|
+
adapter: every row's ``manifest_hash`` matches the SHA-256 of a canonical
|
|
12
|
+
JSON manifest that maps 1:1 to an OCI image manifest (``config`` +
|
|
13
|
+
``layers`` with media types) without schema changes.
|
|
14
|
+
|
|
15
|
+
Revision ID: 018
|
|
16
|
+
Revises: 017
|
|
17
|
+
Create Date: 2026-04-10
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from typing import TYPE_CHECKING
|
|
22
|
+
|
|
23
|
+
import sqlalchemy as sa
|
|
24
|
+
from alembic import op
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from collections.abc import Sequence
|
|
28
|
+
|
|
29
|
+
revision: str = "018"
|
|
30
|
+
down_revision: str | None = "017"
|
|
31
|
+
branch_labels: str | Sequence[str] | None = None
|
|
32
|
+
depends_on: str | Sequence[str] | None = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def upgrade() -> None:
|
|
36
|
+
op.create_table(
|
|
37
|
+
"repo_snapshots",
|
|
38
|
+
sa.Column(
|
|
39
|
+
"id",
|
|
40
|
+
sa.dialects.postgresql.UUID(as_uuid=True),
|
|
41
|
+
primary_key=True,
|
|
42
|
+
server_default=sa.text("gen_random_uuid()"),
|
|
43
|
+
),
|
|
44
|
+
sa.Column(
|
|
45
|
+
"org_id",
|
|
46
|
+
sa.dialects.postgresql.UUID(as_uuid=True),
|
|
47
|
+
sa.ForeignKey("organizations.id", ondelete="CASCADE"),
|
|
48
|
+
nullable=False,
|
|
49
|
+
),
|
|
50
|
+
sa.Column(
|
|
51
|
+
"repo_id",
|
|
52
|
+
sa.dialects.postgresql.UUID(as_uuid=True),
|
|
53
|
+
sa.ForeignKey("repositories.id", ondelete="CASCADE"),
|
|
54
|
+
nullable=False,
|
|
55
|
+
),
|
|
56
|
+
sa.Column(
|
|
57
|
+
"branch_id",
|
|
58
|
+
sa.dialects.postgresql.UUID(as_uuid=True),
|
|
59
|
+
sa.ForeignKey("branches.id", ondelete="SET NULL"),
|
|
60
|
+
nullable=True,
|
|
61
|
+
),
|
|
62
|
+
sa.Column("name", sa.Text(), nullable=False),
|
|
63
|
+
sa.Column("description", sa.Text(), nullable=False, server_default=""),
|
|
64
|
+
sa.Column("manifest_hash", sa.Text(), nullable=False),
|
|
65
|
+
# Total bytes across every component, for at-a-glance sizing in the
|
|
66
|
+
# list endpoint (no need to scan component rows on every list call).
|
|
67
|
+
sa.Column(
|
|
68
|
+
"total_bytes",
|
|
69
|
+
sa.BigInteger(),
|
|
70
|
+
nullable=False,
|
|
71
|
+
server_default="0",
|
|
72
|
+
),
|
|
73
|
+
sa.Column(
|
|
74
|
+
"component_count",
|
|
75
|
+
sa.Integer(),
|
|
76
|
+
nullable=False,
|
|
77
|
+
server_default="0",
|
|
78
|
+
),
|
|
79
|
+
# Commit pinning — stored as a plain string so even repos without a
|
|
80
|
+
# commits table entry (e.g. local_path only) can still be pinned.
|
|
81
|
+
sa.Column("commit_oid", sa.Text(), nullable=True),
|
|
82
|
+
sa.Column(
|
|
83
|
+
"created_at",
|
|
84
|
+
sa.DateTime(timezone=True),
|
|
85
|
+
server_default=sa.func.now(),
|
|
86
|
+
nullable=False,
|
|
87
|
+
),
|
|
88
|
+
sa.Column(
|
|
89
|
+
"created_by_user_id",
|
|
90
|
+
sa.dialects.postgresql.UUID(as_uuid=True),
|
|
91
|
+
sa.ForeignKey("users.id", ondelete="SET NULL"),
|
|
92
|
+
nullable=True,
|
|
93
|
+
),
|
|
94
|
+
sa.Column(
|
|
95
|
+
"extra",
|
|
96
|
+
sa.dialects.postgresql.JSONB(),
|
|
97
|
+
nullable=False,
|
|
98
|
+
server_default="{}",
|
|
99
|
+
),
|
|
100
|
+
)
|
|
101
|
+
# Unique per (repo_id, name) so you can't shadow an existing named
|
|
102
|
+
# snapshot — forces the user to delete + recreate.
|
|
103
|
+
op.create_unique_constraint(
|
|
104
|
+
"uq_repo_snapshots_repo_name",
|
|
105
|
+
"repo_snapshots",
|
|
106
|
+
["repo_id", "name"],
|
|
107
|
+
)
|
|
108
|
+
op.create_index(
|
|
109
|
+
"ix_repo_snapshots_repo_created",
|
|
110
|
+
"repo_snapshots",
|
|
111
|
+
["repo_id", "created_at"],
|
|
112
|
+
)
|
|
113
|
+
op.create_index(
|
|
114
|
+
"ix_repo_snapshots_org",
|
|
115
|
+
"repo_snapshots",
|
|
116
|
+
["org_id"],
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
op.create_table(
|
|
120
|
+
"repo_snapshot_components",
|
|
121
|
+
sa.Column(
|
|
122
|
+
"id",
|
|
123
|
+
sa.dialects.postgresql.UUID(as_uuid=True),
|
|
124
|
+
primary_key=True,
|
|
125
|
+
server_default=sa.text("gen_random_uuid()"),
|
|
126
|
+
),
|
|
127
|
+
sa.Column(
|
|
128
|
+
"snapshot_id",
|
|
129
|
+
sa.dialects.postgresql.UUID(as_uuid=True),
|
|
130
|
+
sa.ForeignKey("repo_snapshots.id", ondelete="CASCADE"),
|
|
131
|
+
nullable=False,
|
|
132
|
+
),
|
|
133
|
+
sa.Column("name", sa.Text(), nullable=False),
|
|
134
|
+
sa.Column("media_type", sa.Text(), nullable=False),
|
|
135
|
+
# Each component is content-addressed by a SHA-256 digest (in the
|
|
136
|
+
# OCI ``sha256:<hex>`` form). For symbol_set_v1 / embedding_blob_v1
|
|
137
|
+
# components this lines up with the content_store action hash.
|
|
138
|
+
sa.Column("digest", sa.Text(), nullable=False),
|
|
139
|
+
sa.Column("size_bytes", sa.BigInteger(), nullable=False, server_default="0"),
|
|
140
|
+
sa.Column(
|
|
141
|
+
"extra",
|
|
142
|
+
sa.dialects.postgresql.JSONB(),
|
|
143
|
+
nullable=False,
|
|
144
|
+
server_default="{}",
|
|
145
|
+
),
|
|
146
|
+
)
|
|
147
|
+
op.create_index(
|
|
148
|
+
"ix_repo_snapshot_components_snapshot",
|
|
149
|
+
"repo_snapshot_components",
|
|
150
|
+
["snapshot_id"],
|
|
151
|
+
)
|
|
152
|
+
op.create_index(
|
|
153
|
+
"ix_repo_snapshot_components_digest",
|
|
154
|
+
"repo_snapshot_components",
|
|
155
|
+
["digest"],
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def downgrade() -> None:
|
|
160
|
+
op.drop_index(
|
|
161
|
+
"ix_repo_snapshot_components_digest",
|
|
162
|
+
table_name="repo_snapshot_components",
|
|
163
|
+
)
|
|
164
|
+
op.drop_index(
|
|
165
|
+
"ix_repo_snapshot_components_snapshot",
|
|
166
|
+
table_name="repo_snapshot_components",
|
|
167
|
+
)
|
|
168
|
+
op.drop_table("repo_snapshot_components")
|
|
169
|
+
op.drop_index("ix_repo_snapshots_org", table_name="repo_snapshots")
|
|
170
|
+
op.drop_index(
|
|
171
|
+
"ix_repo_snapshots_repo_created", table_name="repo_snapshots",
|
|
172
|
+
)
|
|
173
|
+
op.drop_constraint(
|
|
174
|
+
"uq_repo_snapshots_repo_name",
|
|
175
|
+
"repo_snapshots",
|
|
176
|
+
type_="unique",
|
|
177
|
+
)
|
|
178
|
+
op.drop_table("repo_snapshots")
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Add caller_qualified_name to symbol_references for call-graph attribution.
|
|
2
|
+
|
|
3
|
+
Pairs with the function-level call graph in CrossRefIndex (Phase 1, A1).
|
|
4
|
+
Without this column, the full-indexer pipeline persists references but
|
|
5
|
+
loses the enclosing function/method, which makes call_graph queries over
|
|
6
|
+
HTTP-indexed repos return empty edges.
|
|
7
|
+
|
|
8
|
+
Revision ID: 019
|
|
9
|
+
Revises: 018
|
|
10
|
+
Create Date: 2026-05-03
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from typing import Sequence, Union
|
|
15
|
+
|
|
16
|
+
import sqlalchemy as sa
|
|
17
|
+
from alembic import op
|
|
18
|
+
|
|
19
|
+
revision: str = "019"
|
|
20
|
+
down_revision: Union[str, None] = "018"
|
|
21
|
+
branch_labels: Union[str, Sequence[str], None] = None
|
|
22
|
+
depends_on: Union[str, Sequence[str], None] = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def upgrade() -> None:
|
|
26
|
+
op.add_column(
|
|
27
|
+
"symbol_references",
|
|
28
|
+
sa.Column(
|
|
29
|
+
"caller_qualified_name",
|
|
30
|
+
sa.String(500),
|
|
31
|
+
nullable=False,
|
|
32
|
+
server_default="",
|
|
33
|
+
),
|
|
34
|
+
)
|
|
35
|
+
op.create_index(
|
|
36
|
+
"ix_symref_caller", "symbol_references", ["caller_qualified_name"],
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def downgrade() -> None:
|
|
41
|
+
op.drop_index("ix_symref_caller", "symbol_references")
|
|
42
|
+
op.drop_column("symbol_references", "caller_qualified_name")
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Durable repository knowledge shared across clients and committed revisions."""
|
|
2
|
+
from alembic import op
|
|
3
|
+
import sqlalchemy as sa
|
|
4
|
+
|
|
5
|
+
revision = "020"
|
|
6
|
+
down_revision = "019"
|
|
7
|
+
branch_labels = None
|
|
8
|
+
depends_on = None
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def upgrade():
|
|
12
|
+
op.create_table("knowledge_entries",
|
|
13
|
+
sa.Column("id", sa.Integer(), primary_key=True, autoincrement=True),
|
|
14
|
+
sa.Column("repo_id", sa.Uuid(), sa.ForeignKey("repositories.id", ondelete="CASCADE"), nullable=False),
|
|
15
|
+
sa.Column("author_id", sa.String(64), nullable=False),
|
|
16
|
+
sa.Column("kind", sa.String(16), nullable=False),
|
|
17
|
+
sa.Column("status", sa.String(16), nullable=False),
|
|
18
|
+
sa.Column("scope", sa.Text(), nullable=False),
|
|
19
|
+
sa.Column("payload", sa.JSON(), nullable=False),
|
|
20
|
+
sa.Column("revision", sa.String(64), nullable=False),
|
|
21
|
+
sa.Column("anchor_hash", sa.String(64), nullable=False),
|
|
22
|
+
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
|
|
23
|
+
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
|
|
24
|
+
)
|
|
25
|
+
op.create_index("ix_knowledge_entries_repo_id", "knowledge_entries", ["repo_id"])
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def downgrade():
|
|
29
|
+
op.drop_table("knowledge_entries")
|