chore: update CLAUDE.md and enhance project documentation

- Revised the CLAUDE.md file for clarity and improved structure, including a more concise project overview and a formatted quick orientation section.
- Added a new function `resolve_create_project_id` to handle project ID resolution for meeting creation, enhancing the project management logic.
- Updated the `MeetingMixin` to utilize the new project ID resolution function, improving the handling of project IDs during meeting creation.
- Enhanced logging in the streaming mixins to provide better insights during stream initialization and transcription processes.
- Updated various linting and configuration files for improved diagnostics and code quality.
This commit is contained in:
2026-01-07 03:16:22 +00:00
parent 9e688aa02c
commit 207fa8e9b1
10 changed files with 1051 additions and 930 deletions

View File

@@ -1,13 +1,13 @@
{
"version": "1.36.1",
"time": "1767753071383",
"time": "1767755561086",
"generalDiagnostics": [],
"summary": {
"filesAnalyzed": 700,
"errorCount": 0,
"warningCount": 0,
"informationCount": 0,
"timeInSec": 12.389
"timeInSec": 11.725
}
}

View File

@@ -1 +1 @@
{"summary":{"changed":0,"unchanged":396,"matches":0,"duration":{"secs":0,"nanos":115511739},"scannerDuration":{"secs":0,"nanos":2646535},"errors":0,"warnings":0,"infos":0,"skipped":0,"suggestedFixesSkipped":0,"diagnosticsNotPrinted":0},"diagnostics":[],"command":"lint"}
{"summary":{"changed":0,"unchanged":396,"matches":0,"duration":{"secs":0,"nanos":94347688},"scannerDuration":{"secs":0,"nanos":3009402},"errors":0,"warnings":0,"infos":0,"skipped":0,"suggestedFixesSkipped":0,"diagnosticsNotPrinted":0},"diagnostics":[],"command":"lint"}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

1234
CLAUDE.md

File diff suppressed because it is too large Load Diff

2
client

Submodule client updated: c7a152a3fe...24832b691d

View File

@@ -7,6 +7,7 @@ from uuid import UUID
from noteflow.config.constants import ERROR_INVALID_PROJECT_ID_PREFIX
from noteflow.domain.constants.fields import PROJECT_ID
from noteflow.domain.identity import OperationContext
from noteflow.infrastructure.logging import get_logger, get_workspace_id
from ...proto import noteflow_pb2
@@ -129,3 +130,50 @@ async def resolve_active_project_id(
repo, workspace_uuid
)
return active_project.id if active_project else None
async def resolve_create_project_id(
host: ServicerHost,
repo: MeetingRepositoryProvider,
op_context: OperationContext,
project_id: UUID | None,
) -> UUID | None:
"""Validate project_id for CreateMeeting and fall back to active/default.
Returns a resolved project_id or None if no valid project is available.
"""
if project_id is None:
return await resolve_active_project_id(host, repo)
if not repo.supports_projects:
logger.debug(
"CreateMeeting: project_id ignored (projects not supported)",
project_id=str(project_id),
)
return None
project = await repo.projects.get(project_id)
if project is None:
logger.warning(
"CreateMeeting: project not found, falling back to default",
project_id=str(project_id),
)
return await resolve_active_project_id(host, repo)
if project.is_archived:
logger.warning(
"CreateMeeting: project archived, falling back to default",
project_id=str(project_id),
)
return await resolve_active_project_id(host, repo)
if project.workspace_id != op_context.workspace_id:
logger.warning(
"CreateMeeting: project workspace mismatch, falling back",
project_id=str(project_id),
project_workspace_id=str(project.workspace_id),
workspace_id=str(op_context.workspace_id),
)
return await resolve_active_project_id(host, repo)
return project_id

View File

@@ -21,6 +21,7 @@ from ._project_scope import (
parse_project_id_or_abort,
parse_project_ids_or_abort,
resolve_active_project_id,
resolve_create_project_id,
)
from ._stop_ops import (
cleanup_audio_writer,
@@ -72,8 +73,7 @@ class MeetingMixin:
op_context = self.get_operation_context(context)
async with cast(MeetingRepositoryProvider, self.create_repository_provider()) as repo:
if project_id is None:
project_id = await resolve_active_project_id(self, repo)
project_id = await resolve_create_project_id(self, repo, op_context, project_id)
meeting = Meeting.create(
title=request.title,

View File

@@ -89,7 +89,9 @@ class StreamingMixin:
persist segments, and yield transcript updates.
Works with both database and memory backends via RepositoryProvider.
"""
logger.info("StreamTranscription opened")
if self.asr_engine is None or not self.asr_engine.is_loaded:
logger.warning("StreamTranscription rejected: ASR engine not loaded")
await abort_failed_precondition(context, "ASR engine not loaded")
stream_state = _StreamState()
@@ -188,6 +190,12 @@ class StreamingMixin:
if current_meeting_id is None:
# Track meeting_id BEFORE init to guarantee cleanup on any exception
initialized_meeting_id = meeting_id
logger.info(
"StreamTranscription initializing meeting %s (sample_rate=%s channels=%s)",
meeting_id,
chunk.sample_rate,
chunk.channels,
)
init_result = await self.init_stream_for_meeting(meeting_id, context)
return None if init_result is None else (meeting_id, initialized_meeting_id)
if meeting_id != current_meeting_id:

View File

@@ -3,6 +3,7 @@
from __future__ import annotations
import asyncio
import time
from dataclasses import dataclass
from typing import TYPE_CHECKING
from typing import Protocol as TypingProtocol
@@ -167,15 +168,33 @@ class StreamSessionManager:
Returns:
Initialization result, or None if error was sent.
"""
init_started = time.monotonic()
logger.info("Stream init requested for meeting %s", meeting_id)
if not await StreamSessionManager._reserve_stream_slot(host, meeting_id, context):
return None
init_result = await StreamSessionManager._init_stream_session(host, meeting_id)
success = init_result.success
error_code = init_result.error_code
error_message = init_result.error_message or ""
if not init_result.success:
if not success:
host.active_streams.discard(meeting_id)
error_code = init_result.error_code if init_result.error_code is not None else grpc.StatusCode.INTERNAL
await context.abort(error_code, init_result.error_message or "")
status = error_code if error_code is not None else grpc.StatusCode.INTERNAL
logger.warning(
"Stream init failed for meeting %s (%s): %s",
meeting_id,
status,
error_message,
)
await context.abort(status, error_message)
else:
elapsed_ms = (time.monotonic() - init_started) * 1000.0
logger.info(
"Stream init completed for meeting %s in %.1fms",
meeting_id,
elapsed_ms,
)
return init_result