feat: add GET /projects/{id}/ontology/index-status endpoint#76
Conversation
The frontend already calls this endpoint to display index status in project settings and poll after reindex, but it returned 404 because only the POST /reindex route existed. Closes #48. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 4 minutes and 29 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughAdded DI factories for index/change/embedding services; implemented GET /{project_id}/ontology/index-status; added an ontology index WebSocket /{project_id}/ontology/index-ws with token auth, Redis pub/sub, and per-project connection manager; introduced get_git_ontology_path helper; updated embedding/worker fallbacks; and added unit tests for DI, index endpoints, websockets, embedding, and helpers. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Frontend Client
participant API as Projects API
participant ProjectSvc as ProjectService/GitService
participant IndexSvc as OntologyIndexService
participant DB as Database
Client->>API: GET /projects/{id}/ontology/index-status?branch=...
API->>ProjectSvc: get(project_id)
ProjectSvc->>DB: SELECT Project
DB-->>ProjectSvc: Project
alt branch not provided
API->>ProjectSvc: get_default_branch(project_id)
ProjectSvc-->>API: branch
end
API->>IndexSvc: get_index_status(project_id, branch)
IndexSvc->>DB: SELECT OntologyIndexStatus
DB-->>IndexSvc: record | null
IndexSvc-->>API: record | None
alt record found
API-->>Client: 200 {status, entity_count, indexed_at, commit_hash, error_message}
else no record
API-->>Client: 404 {"detail":"No index status found"}
end
sequenceDiagram
participant Client as Frontend (WS)
participant WS as API WebSocket Handler
participant Manager as IndexConnectionManager
participant Redis as Redis Pub/Sub
participant Pub as Redis Pubsub Loop
participant DB as Database
Client->>WS: Open WS /projects/{id}/ontology/index-ws?token=...
WS->>WS: authenticate_ws(websocket, project_id, token)
alt auth & access ok
WS->>Manager: connect(project_id, websocket)
Manager-->>WS: registered
WS->>Redis: subscribe "ontology_index:updates"
loop poll pubsub
Pub->>WS: message JSON
alt message.project_id == connected project_id
WS->>Client: send_json(payload)
else
Note right of WS: ignore message
end
end
else auth failed
WS-->>Client: close(code=4001/4003/4004/1011)
end
Note right of WS: on disconnect/error -> unsubscribe, Manager.disconnect()
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…erage Rewrite tests to use authed_client fixture and FastAPI dependency overrides, asserting actual response status codes, JSON fields, and service call arguments. Covers success, 404, branch param, default branch fallback, and failed status scenarios. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The worker and embedding service used getattr(project, "git_ontology_path", None) which always returned None since that field only exists on ProjectResponse, not the Project ORM model. This caused indexing/embedding to look for "ontology.ttl" instead of the actual file path (e.g. "sources/ontology-semantic-canon.ttl"). Extract get_git_ontology_path() into ontokit/models/project.py and eagerly load the github_integration relationship so the correct file path is resolved. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@ontokit/api/routes/projects.py`:
- Around line 1423-1432: The route currently instantiates OntologyIndexService
directly; change it to accept the service via FastAPI DI by adding a parameter
like index_service: OntologyIndexService = Depends(get_ontology_index_service)
(or a factory that itself depends on AsyncSession) and remove the direct
construction in the handler; then call
index_service.get_index_status(project.id, resolved_branch) as before. Ensure
the get_ontology_index_service provider (or factory) injects the AsyncSession
(get_db) so overrides/mock wiring will work in tests.
In `@ontokit/services/embedding_service.py`:
- Around line 260-264: The code rejects projects early by checking
Project.source_file_path before attempting git-backed resolution; update the
logic around the proj_result handling so that if Project.source_file_path is
falsy you call get_git_ontology_path(project) and/or attempt load_from_git(...)
to derive a path and only raise the error if both the explicit source_file_path
and the git-derived path are missing; apply the same change to the second
occurrence noted (around the later source_file_path check) and reference
Project.source_file_path, get_git_ontology_path, and load_from_git in the
updated flow.
In `@ontokit/worker.py`:
- Around line 65-70: The current eager project load in worker.py checks Project
and its github_integration but still requires source_file_path before attempting
git-based index loading; update the logic in the project-loading/indexing path
(look for select(Project).options(selectinload(Project.github_integration)) and
any subsequent checks of source_file_path) to only enforce source_file_path when
performing file-system or import-specific indexing, and instead branch to use
Project.github_integration metadata when present to drive git-based indexing;
ensure the code path that handles github_integration is reached even if
source_file_path is None and add a clear conditional that prefers
github_integration when available.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 2dd413b5-80cb-4b80-bc71-0688f55d4287
📒 Files selected for processing (5)
ontokit/api/routes/projects.pyontokit/models/project.pyontokit/services/embedding_service.pyontokit/worker.pytests/unit/test_index_status_route.py
Add WS /projects/{id}/ontology/index-ws that forwards index_started,
index_complete, and index_failed events from the background worker via
Redis pubsub, mirroring the existing lint WebSocket pattern.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use FastAPI DI for OntologyIndexService in index-status route via get_index dependency, replacing direct construction. Tests updated to use dependency overrides instead of @patch. - Relax source_file_path guard in worker and embedding service: allow projects with github_integration but no source_file_path to proceed with git-based loading via get_git_ontology_path(). - Storage fallback in embedding service now checks source_file_path before attempting load_from_storage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace direct Service(db) construction with Depends() factories in all route handlers for consistency and testability: - EmbeddingService: 7 instances across embeddings.py and semantic_search.py - ChangeEventService: 5 instances across analytics.py and projects.py - OntologyIndexService: 1 remaining instance in delete_branch handler All route-level services now use the same DI pattern. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 6 tests for get_git_ontology_path() covering all branches - 7 tests for IndexConnectionManager and WebSocket endpoint (4004 close) - 6 tests for DI factory functions across routes - 1 test for worker no-git-no-storage error path - 4 tests for embedding service relaxed source_file_path guard 24 new tests, bringing total to 1366. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add async unit tests for ontology_index_websocket function covering: - Project not found (closes with 4004) - Successful connection, message forwarding for matching project - Skipping messages for other projects - Malformed JSON handling (skip and continue) - Cleanup on unexpected exceptions (disconnect + unsubscribe) Brings projects.py coverage from 88% to 94%. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@ontokit/api/routes/projects.py`:
- Around line 1533-1553: The WebSocket handler ontology_index_websocket
currently only checks project existence and allows unauthenticated access;
change it to require and validate the authenticated user via FastAPI dependency
injection and ProjectService.get before accepting the socket: add a dependency
parameter for the current user (e.g., user: User = Depends(get_current_user))
and replace the manual db lookup with a call to await
ProjectService.get(project_id, user) (or call it after fetching to enforce
access), and if ProjectService.get raises NotFound/Unauthorized or returns None
then close the websocket with an appropriate code/reason instead of calling
index_ws_manager.connect; finally only call index_ws_manager.connect(websocket,
project_id_str) after ProjectService.get confirms access.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b3d71484-6e53-47ab-ac74-2db6d19d0afd
📒 Files selected for processing (12)
ontokit/api/routes/analytics.pyontokit/api/routes/embeddings.pyontokit/api/routes/projects.pyontokit/api/routes/semantic_search.pyontokit/services/embedding_service.pyontokit/worker.pytests/unit/test_di_factories.pytests/unit/test_embedding_service.pytests/unit/test_get_git_ontology_path.pytests/unit/test_index_status_route.pytests/unit/test_index_ws.pytests/unit/test_worker.py
✅ Files skipped from review due to trivial changes (1)
- tests/unit/test_index_status_route.py
🚧 Files skipped from review as they are similar to previous changes (1)
- ontokit/services/embedding_service.py
The WebSocket endpoint now requires a `token` query parameter for authentication. The token is validated via Zitadel JWT, and project access is verified through ProjectService.get before accepting the connection. Unauthenticated requests are closed with 4001, and unauthorized/not-found with 4004. Tests updated with auth mocking for all WebSocket test cases. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Apply the same authentication pattern as the index WebSocket: require a token query parameter, validate via Zitadel JWT, and verify project access through ProjectService.get before accepting the connection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7 tests covering: no token (4001), invalid token (4001), project not found (4004), message forwarding, project filtering, malformed JSON, and cleanup on exception. Brings lint.py coverage from 72% to 96%. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@CodeRabbit review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
ontokit/api/routes/lint.py (1)
550-583: Extract the WebSocket auth/access gate into a shared helper.This block is now duplicated almost verbatim here and in
ontokit/api/routes/projects.py, and it opens its own session / constructsProjectServiceinside the route. Centralizing it behind a reusable helper or dependency will keep the two sockets from drifting and restore the API-layer DI pattern.As per coding guidelines
ontokit/api/**/*.py: "Use dependency injection via FastAPI'sDepends()for service access".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ontokit/api/routes/lint.py` around lines 550 - 583, The WebSocket auth and project-access logic in this route is duplicated and should be extracted into a shared helper/dependency; create a reusable async helper (e.g., websocket_auth_and_authorize) that encapsulates token validation (validate_token), userinfo fetching (fetch_userinfo), CurrentUser construction, opening a DB session via async_session_maker, and project access check using ProjectService.get, and have the route call/await that helper instead of duplicating the block and constructing ProjectService inline; ensure the helper returns the CurrentUser and the opened DB/session or raises/returns appropriate errors so the route can close the websocket with the same codes/reasons used here (4001 for auth failures) and keep existing semantics.ontokit/api/routes/projects.py (1)
1506-1506: Import the channel name fromontokit.workerinstead of re-declaring it.The worker already publishes on
ONTOLOGY_INDEX_UPDATES_CHANNEL. Duplicating the raw string here means the publisher and subscriber can silently drift apart on the next rename.Suggested fix
+from ontokit.worker import ONTOLOGY_INDEX_UPDATES_CHANNEL ... -ONTOLOGY_INDEX_UPDATES_CHANNEL = "ontology_index:updates"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ontokit/api/routes/projects.py` at line 1506, The constant ONTOLOGY_INDEX_UPDATES_CHANNEL is being redeclared in projects.py which can drift from the publisher; remove the local declaration and import ONTOLOGY_INDEX_UPDATES_CHANNEL from ontokit.worker (where the worker publishes) and update any references in this file to use the imported symbol so publisher and subscriber use the same canonical channel name.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@ontokit/api/routes/lint.py`:
- Around line 560-585: The current blanket except Exception around
validate_token/fetch_userinfo/CurrentUser construction and around
ProjectService.get collapses server-side failures into client errors; change
these to explicitly catch the framework's HTTPException for expected auth/access
failures (from validate_token and ProjectService.get) and handle those by
closing the websocket with the existing client-facing codes (e.g., keep the 4001
for auth failures and 4004 for access failures), but let other exceptions
propagate or catch them separately and close the websocket with an
internal-error code (e.g., 1011) and/or log the full exception; update the
try/except blocks around validate_token/fetch_userinfo/CurrentUser and the
async_session_maker()/ProjectService.get call to reflect this separation.
In `@ontokit/api/routes/projects.py`:
- Around line 1432-1445: The route handler get_ontology_index_status is using
RequiredUser which prevents anonymous viewers from accessing public project
index status; change the user parameter type to OptionalUser so visibility rules
in ProjectService.get(project_id, user) can apply and allow read-only access for
viewers/anonymous users. Update the function signature to accept user:
OptionalUser (and add or adjust the import for OptionalUser if missing), keep
calling service.get(project_id, user) and leave all other logic
(resolved_branch, index_service.get_index_status) unchanged.
---
Nitpick comments:
In `@ontokit/api/routes/lint.py`:
- Around line 550-583: The WebSocket auth and project-access logic in this route
is duplicated and should be extracted into a shared helper/dependency; create a
reusable async helper (e.g., websocket_auth_and_authorize) that encapsulates
token validation (validate_token), userinfo fetching (fetch_userinfo),
CurrentUser construction, opening a DB session via async_session_maker, and
project access check using ProjectService.get, and have the route call/await
that helper instead of duplicating the block and constructing ProjectService
inline; ensure the helper returns the CurrentUser and the opened DB/session or
raises/returns appropriate errors so the route can close the websocket with the
same codes/reasons used here (4001 for auth failures) and keep existing
semantics.
In `@ontokit/api/routes/projects.py`:
- Line 1506: The constant ONTOLOGY_INDEX_UPDATES_CHANNEL is being redeclared in
projects.py which can drift from the publisher; remove the local declaration and
import ONTOLOGY_INDEX_UPDATES_CHANNEL from ontokit.worker (where the worker
publishes) and update any references in this file to use the imported symbol so
publisher and subscriber use the same canonical channel name.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 68053c22-15c7-4475-b8db-91a810937a83
📒 Files selected for processing (4)
ontokit/api/routes/lint.pyontokit/api/routes/projects.pytests/unit/test_index_ws.pytests/unit/test_lint_ws.py
✅ Files skipped from review due to trivial changes (1)
- tests/unit/test_index_ws.py
…nonymous index-status Address CodeRabbit review findings: - Extract duplicated WebSocket auth logic into shared authenticate_ws() helper in ontokit/api/utils/ws_auth.py. Distinguishes HTTPException (4001/4003/4004) from unexpected errors (1011 with logging). - Move Redis pubsub channel constants from worker.py to core/constants.py. Both publisher (worker) and subscribers (lint.py, projects.py) now import from the same canonical source. - Change index-status route from RequiredUser to OptionalUser so public projects are accessible to anonymous/viewer users. - Add 7 tests for authenticate_ws covering all error paths and success. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Starlette returns HTTP 403 if websocket.close() is called before websocket.accept(). Move accept() into authenticate_ws() so error close frames are delivered properly to clients. Remove duplicate accept() from connection managers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
ontokit/api/routes/projects.py (1)
1259-1410: InjectEmbeddingServiceviaDepends()for consistency.Line 1388 instantiates
EmbeddingService(db)directly instead of via FastAPI'sDepends(). The route already injectsChangeEventServicethis way, so the auto-embed path should follow the same pattern for consistency and testability.<summary>Line 1388 example:</summary> embed_config = await EmbeddingService(db).get_config(project_id)Per coding guideline
ontokit/api/**/*.py: "Use dependency injection via FastAPI'sDepends()for service access".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ontokit/api/routes/projects.py` around lines 1259 - 1410, The route directly constructs EmbeddingService(db) (see embed_config = await EmbeddingService(db).get_config(project_id)); change this to inject EmbeddingService via FastAPI Depends by adding a parameter like embedding_service: Annotated[EmbeddingService, Depends(get_embedding_service)] to save_source_content and replace the direct instantiation with embed_config = await embedding_service.get_config(project_id); ensure you import or use the existing provider function (get_embedding_service) consistent with other injected services and update any references to EmbeddingService(db) in this function to use the injected embedding_service instead.ontokit/api/utils/ws_auth.py (1)
72-75: InjectProjectServicevia dependency injection instead of constructing it directly.Opening a fresh session and building
ProjectServiceinside the helper bypasses the API-layer DI pattern established elsewhere. Have the websocket route inject the service viaDepends()and pass it intoauthenticate_ws(...)instead, matching the HTTP auth pattern in the codebase. This improves testability and consistency with the guideline forontokit/api/**/*.py: "Use dependency injection via FastAPI'sDepends()for service access".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ontokit/api/utils/ws_auth.py` around lines 72 - 75, The helper authenticate_ws currently opens a fresh session and constructs ProjectService via async_session_maker/ProjectService; change authenticate_ws to accept a ProjectService instance injected from the FastAPI route (use Depends(ProjectService) in the websocket route) and remove the local async_session_maker/ProjectService construction; inside authenticate_ws call the injected svc.get(project_id, user) instead. Update the websocket route to declare a parameter svc: ProjectService = Depends(...) and pass that svc into authenticate_ws so the function follows the API-layer DI pattern and becomes testable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@ontokit/api/routes/projects.py`:
- Around line 1553-1584: The realtime setup can fail
(get_arq_pool()/pubsub/subscribe) after authenticate_ws has accepted the
connection, so ensure the WebSocket is deterministically closed with a close
code and reason whenever setup or the pubsub loop raises before normal
operation: in the try/except around get_arq_pool(), pool.pubsub(), and await
pubsub.subscribe(...) (and in the top-level except Exception e block) call await
websocket.close(code=1011, reason=f"realtime setup failed: {e}" or a static
reason when pool is None) before letting the function proceed to finally; keep
index_ws_manager.disconnect(websocket, project_id_str) and the
pubsub.unsubscribe logic in finally so disconnection is still tracked and
resources cleaned up.
In `@tests/unit/test_ws_auth.py`:
- Around line 29-152: Add assertions that the WebSocket was accepted before
being closed (or left open) in each test that calls authenticate_ws: for tests
test_no_token_closes_4001, test_invalid_token_http_exception_closes_4001,
test_unexpected_auth_error_closes_1011, test_project_not_found_closes_4004,
test_access_denied_closes_4003, test_unexpected_project_error_closes_1011, and
test_success_returns_true, assert that the AsyncMock WebSocket's accept() was
awaited (e.g., ws.accept.assert_awaited_once() or assert_awaited) prior to
checking close behavior so the test verifies the helper calls ws.accept() before
closing or leaving the socket open.
---
Nitpick comments:
In `@ontokit/api/routes/projects.py`:
- Around line 1259-1410: The route directly constructs EmbeddingService(db) (see
embed_config = await EmbeddingService(db).get_config(project_id)); change this
to inject EmbeddingService via FastAPI Depends by adding a parameter like
embedding_service: Annotated[EmbeddingService, Depends(get_embedding_service)]
to save_source_content and replace the direct instantiation with embed_config =
await embedding_service.get_config(project_id); ensure you import or use the
existing provider function (get_embedding_service) consistent with other
injected services and update any references to EmbeddingService(db) in this
function to use the injected embedding_service instead.
In `@ontokit/api/utils/ws_auth.py`:
- Around line 72-75: The helper authenticate_ws currently opens a fresh session
and constructs ProjectService via async_session_maker/ProjectService; change
authenticate_ws to accept a ProjectService instance injected from the FastAPI
route (use Depends(ProjectService) in the websocket route) and remove the local
async_session_maker/ProjectService construction; inside authenticate_ws call the
injected svc.get(project_id, user) instead. Update the websocket route to
declare a parameter svc: ProjectService = Depends(...) and pass that svc into
authenticate_ws so the function follows the API-layer DI pattern and becomes
testable.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e380fada-93a8-42ee-ae8c-43bf260c3242
📒 Files selected for processing (9)
ontokit/api/routes/lint.pyontokit/api/routes/projects.pyontokit/api/utils/ws_auth.pyontokit/core/constants.pyontokit/worker.pytests/unit/test_index_ws.pytests/unit/test_lint_routes_extended.pytests/unit/test_lint_ws.pytests/unit/test_ws_auth.py
✅ Files skipped from review due to trivial changes (2)
- ontokit/core/constants.py
- tests/unit/test_lint_ws.py
🚧 Files skipped from review as they are similar to previous changes (1)
- ontokit/api/routes/lint.py
… EmbeddingService Address CodeRabbit findings: - Close WebSocket with 1011 when pubsub setup fails (both lint and index endpoints), preventing clients from hanging on setup errors. - Add ws.accept.assert_awaited_once() to all authenticate_ws tests, verifying the WebSocket is accepted before any close. - Replace last direct EmbeddingService(db) in save_source_content with DI via Depends(get_embeddings). Remove now-unused db parameter. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
This PR started as a fix for issue #48 (missing GET index-status endpoint) but expanded to address several related improvements discovered during implementation and code review.
New endpoints
/projects/{id}/ontology/index-status— returns index status fields (status,entity_count,indexed_at,commit_hash,error_message); 404 if no record exists/projects/{id}/ontology/index-ws— real-time WebSocket forindex_started,index_complete,index_failedevents via Redis pubsub (mirrors lint WS pattern)Bug fixes
getattr(project, "git_ontology_path", None)which always returnedNone(field only exists onProjectResponse, not the ORM model). Extractedget_git_ontology_path()intomodels/project.pyand eagerly loadgithub_integrationrelationshipsource_file_pathguard — worker and embedding service now allow projects withgithub_integrationbut nosource_file_pathto proceed with git-based loadingtokenquery parameter, validated via Zitadel JWT +ProjectService.getaccess check (previously unauthenticated)Refactoring
Service(db)construction withDepends()factories:OntologyIndexService(2 instances inprojects.py)EmbeddingService(7 instances acrossembeddings.py,semantic_search.py)ChangeEventService(5 instances acrossanalytics.py,projects.py)Tests
get_git_ontology_path, worker/embedding edge casesCloses #48
Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Refactoring
Tests