logs: migrate to molecular CoT JSON protocol, fix regressions

- Replace BeliefFormatter with CotJsonFormatter (single-line JSON with
  ts, level, trace_id, src, marker, intent, payload, error, span_id)
- Migrate belief_scope to use cot_log() for structured JSON output
- Update monkey-patched reason/reflect/explore to pass extra= dict
- Strip 200+ inline [REASON]/[REFLECT]/[EXPLORE] markers from 50+ files
- Remove belief_scope from hot-path utilities (_parse_datetime,
  _json_load_if_needed) to eliminate startup log noise
- Register TraceContextMiddleware in app.py (was implemented but unused)
- Seed trace_id in startup_event/shutdown_event for background tasks
- Fix broken relative imports in translate routes (3 dots → 4 dots)
- Migrate 43 translate_routes log calls to logger.reason/explore
- Fix SyntaxError (positional arg after extra=) in _repo_operations_routes
- Fix IndentationError in rbac_permission_catalog except-block
- Add smoke test tests/test_smoke_app.py (catches import errors before run)
This commit is contained in:
2026-05-13 09:44:50 +03:00
parent b6f46fe9f5
commit 83b8f4d999
55 changed files with 644 additions and 539 deletions

View File

@@ -133,7 +133,7 @@ class GitServiceBase:
if source_abs == target_abs:
return source_abs
if os.path.exists(target_abs):
logger.warning(f"[_migrate_repo_directory][REASON] Target already exists, keeping source path: {target_abs}")
logger.reason(f"Target already exists, keeping source path: {target_abs}", extra={"src": "_migrate_repo_directory"})
return source_abs
Path(target_abs).parent.mkdir(parents=True, exist_ok=True)
try:
@@ -210,11 +210,11 @@ class GitServiceBase:
else:
auth_url = remote_url
if os.path.exists(repo_path):
logger.info(f"[init_repo][REASON] Opening existing repo at {repo_path}")
logger.reason(f"Opening existing repo at {repo_path}", extra={"src": "init_repo"})
try:
repo = Repo(repo_path)
except (InvalidGitRepositoryError, NoSuchPathError):
logger.warning(f"[init_repo][REASON] Existing path is not a Git repository, recreating: {repo_path}")
logger.reason(f"Existing path is not a Git repository, recreating: {repo_path}", extra={"src": "init_repo"})
stale_path = Path(repo_path)
if stale_path.exists():
shutil.rmtree(stale_path, ignore_errors=True)
@@ -226,7 +226,7 @@ class GitServiceBase:
repo = Repo.clone_from(auth_url, repo_path)
self._ensure_gitflow_branches(repo, dashboard_id)
return repo
logger.info(f"[init_repo][REASON] Cloning {remote_url} to {repo_path}")
logger.reason(f"Cloning {remote_url} to {repo_path}", extra={"src": "init_repo"})
repo = Repo.clone_from(auth_url, repo_path)
self._ensure_gitflow_branches(repo, dashboard_id)
return repo
@@ -307,7 +307,7 @@ class GitServiceBase:
with repo.config_writer(config_level="repository") as config_writer:
config_writer.set_value("user", "name", normalized_username)
config_writer.set_value("user", "email", normalized_email)
logger.info("[configure_identity][REASON] Applied repository-local git identity for dashboard %s", dashboard_id)
logger.reason(f"Applied repository-local git identity for dashboard {dashboard_id}", extra={"src": "configure_identity"})
except Exception as e:
logger.error(f"[configure_identity][Coherence:Failed] Failed to configure git identity: {e}")
raise HTTPException(status_code=500, detail=f"Failed to configure git identity: {str(e)}")

View File

@@ -31,25 +31,28 @@ class GitServiceBranchMixin:
if "main" in local_heads:
base_commit = local_heads["main"].commit
if base_commit is None:
logger.warning(
f"[_ensure_gitflow_branches][REASON] Skipping branch bootstrap for dashboard {dashboard_id}: repository has no commits"
logger.reason(
f"Skipping branch bootstrap for dashboard {dashboard_id}: repository has no commits",
extra={"src": "_ensure_gitflow_branches"},
)
return
if "main" not in local_heads:
local_heads["main"] = repo.create_head("main", base_commit)
logger.info(f"[_ensure_gitflow_branches][REASON] Created local branch main for dashboard {dashboard_id}")
logger.reason(f"Created local branch main for dashboard {dashboard_id}", extra={"src": "_ensure_gitflow_branches"})
for branch_name in ("dev", "preprod"):
if branch_name in local_heads:
continue
local_heads[branch_name] = repo.create_head(branch_name, local_heads["main"].commit)
logger.info(
f"[_ensure_gitflow_branches][REASON] Created local branch {branch_name} for dashboard {dashboard_id}"
logger.reason(
f"Created local branch {branch_name} for dashboard {dashboard_id}",
extra={"src": "_ensure_gitflow_branches"},
)
try:
origin = repo.remote(name="origin")
except ValueError:
logger.info(
f"[_ensure_gitflow_branches][REASON] Remote origin is not configured for dashboard {dashboard_id}; skipping remote branch creation"
logger.reason(
f"Remote origin is not configured for dashboard {dashboard_id}; skipping remote branch creation",
extra={"src": "_ensure_gitflow_branches"},
)
return
remote_branch_names = set()
@@ -60,14 +63,15 @@ class GitServiceBranchMixin:
if remote_head:
remote_branch_names.add(str(remote_head))
except Exception as e:
logger.warning(f"[_ensure_gitflow_branches][REASON] Failed to fetch origin refs: {e}")
logger.reason(f"Failed to fetch origin refs: {e}", extra={"src": "_ensure_gitflow_branches"})
for branch_name in required_branches:
if branch_name in remote_branch_names:
continue
try:
origin.push(refspec=f"{branch_name}:{branch_name}")
logger.info(
f"[_ensure_gitflow_branches][REASON] Pushed branch {branch_name} to origin for dashboard {dashboard_id}"
logger.reason(
f"Pushed branch {branch_name} to origin for dashboard {dashboard_id}",
extra={"src": "_ensure_gitflow_branches"},
)
except Exception as e:
logger.error(f"[_ensure_gitflow_branches][Coherence:Failed] Failed to push branch {branch_name} to origin: {e}")
@@ -77,9 +81,9 @@ class GitServiceBranchMixin:
)
try:
repo.git.checkout("dev")
logger.info(f"[_ensure_gitflow_branches][REASON] Checked out default branch dev for dashboard {dashboard_id}")
logger.reason(f"Checked out default branch dev for dashboard {dashboard_id}", extra={"src": "_ensure_gitflow_branches"})
except Exception as e:
logger.warning(f"[_ensure_gitflow_branches][REASON] Could not checkout dev branch for dashboard {dashboard_id}: {e}")
logger.reason(f"Could not checkout dev branch for dashboard {dashboard_id}: {e}", extra={"src": "_ensure_gitflow_branches"})
# [/DEF:_ensure_gitflow_branches:Function]
# [DEF:list_branches:Function]
@@ -90,7 +94,7 @@ class GitServiceBranchMixin:
def list_branches(self, dashboard_id: int) -> List[dict]:
with belief_scope("GitService.list_branches"):
repo = self.get_repo(dashboard_id)
logger.info(f"[list_branches][REASON] Listing branches for {dashboard_id}. Refs: {repo.refs}")
logger.reason(f"Listing branches for {dashboard_id}. Refs: {repo.refs}", extra={"src": "list_branches"})
branches = []
for ref in repo.refs:
try:
@@ -104,7 +108,7 @@ class GitServiceBranchMixin:
"last_updated": datetime.fromtimestamp(ref.commit.committed_date) if hasattr(ref, 'commit') else datetime.utcnow()
})
except Exception as e:
logger.warning(f"[list_branches][REASON] Skipping ref {ref}: {e}")
logger.reason(f"Skipping ref {ref}: {e}", extra={"src": "list_branches"})
try:
active_name = repo.active_branch.name
if not any(b['name'] == active_name for b in branches):
@@ -113,7 +117,7 @@ class GitServiceBranchMixin:
"is_remote": False, "last_updated": datetime.utcnow()
})
except Exception as e:
logger.warning(f"[list_branches][REASON] Could not determine active branch: {e}")
logger.reason(f"Could not determine active branch: {e}", extra={"src": "list_branches"})
if not branches:
branches.append({
"name": "dev", "commit_hash": "0000000",
@@ -131,9 +135,9 @@ class GitServiceBranchMixin:
def create_branch(self, dashboard_id: int, name: str, from_branch: str = "main"):
with belief_scope("GitService.create_branch"):
repo = self.get_repo(dashboard_id)
logger.info(f"[create_branch][REASON] Creating branch {name} from {from_branch}")
logger.reason(f"Creating branch {name} from {from_branch}", extra={"src": "create_branch"})
if not repo.heads and not repo.remotes:
logger.warning("[create_branch][REASON] Repository is empty. Creating initial commit to enable branching.")
logger.reason("Repository is empty. Creating initial commit to enable branching.", extra={"src": "create_branch"})
readme_path = os.path.join(repo.working_dir, "README.md")
if not os.path.exists(readme_path):
with open(readme_path, "w") as f:
@@ -143,7 +147,7 @@ class GitServiceBranchMixin:
try:
repo.commit(from_branch)
except Exception:
logger.warning(f"[create_branch][REASON] Source branch {from_branch} not found, using HEAD")
logger.reason(f"Source branch {from_branch} not found, using HEAD", extra={"src": "create_branch"})
from_branch = repo.head
try:
new_branch = repo.create_head(name, from_branch)
@@ -160,7 +164,7 @@ class GitServiceBranchMixin:
def checkout_branch(self, dashboard_id: int, name: str):
with belief_scope("GitService.checkout_branch"):
repo = self.get_repo(dashboard_id)
logger.info(f"[checkout_branch][REASON] Checking out branch {name}")
logger.reason(f"Checking out branch {name}", extra={"src": "checkout_branch"})
repo.git.checkout(name)
# [/DEF:checkout_branch:Function]
@@ -174,13 +178,13 @@ class GitServiceBranchMixin:
with belief_scope("GitService.commit_changes"):
repo = self.get_repo(dashboard_id)
if not repo.is_dirty(untracked_files=True) and not files:
logger.info(f"[commit_changes][REASON] No changes to commit for dashboard {dashboard_id}")
logger.reason(f"No changes to commit for dashboard {dashboard_id}", extra={"src": "commit_changes"})
return
if files:
logger.info(f"[commit_changes][REASON] Staging files: {files}")
logger.reason(f"Staging files: {files}", extra={"src": "commit_changes"})
repo.index.add(files)
else:
logger.info("[commit_changes][REASON] Staging all changes")
logger.reason("Staging all changes", extra={"src": "commit_changes"})
repo.git.add(A=True)
repo.index.commit(message)
logger.info(f"[commit_changes][Coherence:OK] Committed changes with message: {message}")

View File

@@ -23,7 +23,7 @@ class GitServiceGiteaMixin:
async def test_connection(self, provider: GitProvider, url: str, pat: str) -> bool:
with belief_scope("GitService.test_connection"):
if ".local" in url or "localhost" in url:
logger.info("[test_connection][REASON] Local/Offline mode detected for URL")
logger.reason("Local/Offline mode detected for URL", extra={"src": "test_connection"})
return True
if not url.startswith(('http://', 'https://')):
logger.error(f"[test_connection][Coherence:Failed] Invalid URL protocol: {url}")
@@ -220,9 +220,9 @@ class GitServiceGiteaMixin:
exc.status_code == 404 and fallback_url and fallback_url != normalized_primary
)
if should_retry_with_fallback:
logger.warning(
"[create_gitea_pull_request][REASON] Primary Gitea URL not found, retrying with remote host: %s",
fallback_url,
logger.explore(
f"Primary Gitea URL not found, retrying with remote host: {fallback_url}",
extra={"src": "create_gitea_pull_request"},
)
active_server_url = fallback_url
try:

View File

@@ -153,7 +153,7 @@ class GitServiceStatusMixin:
"files_changed": list(commit.stats.files.keys())
})
except Exception as e:
logger.warning(f"[get_commit_history][REASON] Could not retrieve commit history for dashboard {dashboard_id}: {e}")
logger.explore(f"Could not retrieve commit history for dashboard {dashboard_id}: {e}", extra={"src": "get_commit_history"})
return []
return commits
# [/DEF:get_commit_history:Function]

View File

@@ -60,9 +60,9 @@ class GitServiceSyncMixin:
finally:
session.close()
except Exception as diag_error:
logger.warning(
"[push_changes][REASON] Failed to load repository binding diagnostics for dashboard %s: %s",
dashboard_id, diag_error,
logger.reason(
"Failed to load repository binding diagnostics",
extra={"src": "push_changes", "dashboard_id": dashboard_id, "error": str(diag_error)},
)
realigned_origin_url = self._align_origin_host_with_config(
dashboard_id=dashboard_id,
@@ -75,13 +75,13 @@ class GitServiceSyncMixin:
origin_urls = list(origin.urls)
except Exception:
origin_urls = []
logger.info(
"[push_changes][REASON] Push diagnostics dashboard=%s config_id=%s config_url=%s binding_remote_url=%s origin_urls=%s origin_realigned=%s",
dashboard_id, binding_config_id, binding_config_url, binding_remote_url, origin_urls, bool(realigned_origin_url),
logger.reason(
f"Push diagnostics dashboard={dashboard_id} config_id={binding_config_id} config_url={binding_config_url} binding_remote_url={binding_remote_url} origin_urls={origin_urls} origin_realigned={bool(realigned_origin_url)}",
extra={"src": "push_changes"},
)
try:
current_branch = repo.active_branch
logger.info(f"[push_changes][REASON] Pushing branch {current_branch.name} to origin")
logger.reason(f"Pushing branch {current_branch.name} to origin", extra={"src": "push_changes"})
tracking_branch = None
try:
tracking_branch = current_branch.tracking_branch()
@@ -120,10 +120,9 @@ class GitServiceSyncMixin:
merge_head_path = os.path.join(repo.git_dir, "MERGE_HEAD")
if os.path.exists(merge_head_path):
payload = self._build_unfinished_merge_payload(repo)
logger.warning(
"[pull_changes][REASON] Unfinished merge detected for dashboard %s (repo_path=%s git_dir=%s branch=%s merge_head=%s merge_msg=%s)",
dashboard_id, payload["repository_path"], payload["git_dir"],
payload["current_branch"], payload["merge_head"], payload["merge_message_preview"],
logger.reason(
f"Unfinished merge detected for dashboard {dashboard_id} (repo_path={payload['repository_path']} git_dir={payload['git_dir']} branch={payload['current_branch']} merge_head={payload['merge_head']} merge_msg={payload['merge_message_preview']})",
extra={"src": "pull_changes"},
)
raise HTTPException(status_code=409, detail=payload)
try:
@@ -133,23 +132,23 @@ class GitServiceSyncMixin:
origin_urls = list(origin.urls)
except Exception:
origin_urls = []
logger.info(
"[pull_changes][REASON] Pull diagnostics dashboard=%s repo_path=%s branch=%s origin_urls=%s",
dashboard_id, repo.working_tree_dir, current_branch, origin_urls,
logger.reason(
f"Pull diagnostics dashboard={dashboard_id} repo_path={repo.working_tree_dir} branch={current_branch} origin_urls={origin_urls}",
extra={"src": "pull_changes"},
)
origin.fetch(prune=True)
remote_ref = f"origin/{current_branch}"
has_remote_branch = any(ref.name == remote_ref for ref in repo.refs)
logger.info(
"[pull_changes][REASON] Pull remote branch check dashboard=%s branch=%s remote_ref=%s exists=%s",
dashboard_id, current_branch, remote_ref, has_remote_branch,
logger.reason(
f"Pull remote branch check dashboard={dashboard_id} branch={current_branch} remote_ref={remote_ref} exists={has_remote_branch}",
extra={"src": "pull_changes"},
)
if not has_remote_branch:
raise HTTPException(
status_code=409,
detail=f"Remote branch '{current_branch}' does not exist yet. Push this branch first.",
)
logger.info(f"[pull_changes][REASON] Pulling changes from origin/{current_branch}")
logger.reason(f"Pulling changes from origin/{current_branch}", extra={"src": "pull_changes"})
repo.git.pull("--no-rebase", "origin", current_branch)
except ValueError:
logger.error(f"[pull_changes][Coherence:Failed] Remote 'origin' not found for dashboard {dashboard_id}")

View File

@@ -115,9 +115,9 @@ class GitServiceUrlMixin:
aligned_url = self._replace_host_in_url(source_origin_url, config_url)
if not aligned_url:
return None
logger.warning(
"[_align_origin_host_with_config][REASON] Host mismatch for dashboard %s: config_host=%s origin_host=%s, applying origin.set_url",
dashboard_id, config_host, origin_host,
logger.reason(
f"Host mismatch for dashboard {dashboard_id}: config_host={config_host} origin_host={origin_host}, applying origin.set_url",
extra={"src": "_align_origin_host_with_config"},
)
try:
origin.set_url(aligned_url)
@@ -141,9 +141,9 @@ class GitServiceUrlMixin:
finally:
session.close()
except Exception as e:
logger.warning(
"[_align_origin_host_with_config][REASON] Failed to persist aligned remote_url for dashboard %s: %s",
dashboard_id, e,
logger.explore(
f"Failed to persist aligned remote_url for dashboard {dashboard_id}: {e}",
extra={"src": "_align_origin_host_with_config"},
)
return aligned_url
# [/DEF:_align_origin_host_with_config:Function]