fix(health): suppress 404 when health monitor disabled + fix audit test assertion

- Sidebar.svelte: defer healthStore.refresh() until feature flags confirm
  health_monitor is enabled; skip entirely when disabled
- health.js: remove throw error from catch block — log and return null
  instead, preventing 'Uncaught (in promise)' on expected 404
- test_report_audit_immutability.py: fix mock assertions —
  audit_service uses logger.reason/reflect/explore, not logger.info
- HealthStore and Sidebar now produce zero network noise when
  FEATURES__HEALTH_MONITOR=false
This commit is contained in:
2026-05-17 14:18:02 +03:00
parent 58ac89c21e
commit cd868df261
141 changed files with 9631 additions and 10165 deletions

View File

@@ -1,6 +1,6 @@
# #region GitServiceBranchMixin [C:3] [TYPE Module] [SEMANTICS git, branch, checkout, list, namespace]
# #region GitServiceBranchMixin [C:4] [TYPE Module] [SEMANTICS git, branch, checkout, list, namespace, lock]
# @LAYER: Infra
# @BRIEF Branch and commit operations for GitService — gitflow branches, list/create/checkout branches, commit changes.
# @BRIEF Branch and commit operations for GitService — gitflow branches, list/create/checkout branches, commit changes (all concurrent-safe via per-dashboard locks).
# @RELATION USED_BY -> [GitService]
import os
@@ -15,10 +15,11 @@ from src.core.logger import belief_scope, logger
# #region GitServiceBranchMixin [C:3] [TYPE Class]
# @BRIEF Mixin providing branch and commit operations for GitService.
class GitServiceBranchMixin:
# region _ensure_gitflow_branches [TYPE Function]
# region _ensure_gitflow_branches [C:4] [TYPE Function] [SEMANTICS git,gitflow,branch,lock]
# @PURPOSE: Ensure standard GitFlow branches (main/dev/preprod) exist locally and on origin.
# @PRE: repo is a valid GitPython Repo instance.
# @POST: main, dev, preprod are available in local repository and pushed to origin when available.
# Active branch unchanged (no spurious checkout).
def _ensure_gitflow_branches(self, repo: Repo, dashboard_id: int) -> None:
with belief_scope("GitService._ensure_gitflow_branches"):
required_branches = ["main", "dev", "preprod"]
@@ -79,62 +80,74 @@ class GitServiceBranchMixin:
status_code=500,
detail=f"Failed to create default branch '{branch_name}' on remote: {e!s}",
)
# Fix 6: Only checkout if not already on dev
try:
repo.git.checkout("dev")
logger.reason(f"Checked out default branch dev for dashboard {dashboard_id}", extra={"src": "_ensure_gitflow_branches"})
except Exception as e:
logger.reason(f"Could not checkout dev branch for dashboard {dashboard_id}: {e}", extra={"src": "_ensure_gitflow_branches"})
current_branch = repo.active_branch.name
except Exception:
current_branch = None
if current_branch != "dev":
try:
repo.git.checkout("dev")
logger.reason(f"Checked out default branch dev for dashboard {dashboard_id}", extra={"src": "_ensure_gitflow_branches"})
except Exception as e:
logger.reason(f"Could not checkout dev branch for dashboard {dashboard_id}: {e}", extra={"src": "_ensure_gitflow_branches"})
# endregion _ensure_gitflow_branches
# region list_branches [TYPE Function]
# @PURPOSE: List all branches for a dashboard's repository.
# region list_branches [C:4] [TYPE Function] [SEMANTICS git,branch,list,lock]
# @PURPOSE: List all branches (excluding tags) for a dashboard's repository, concurrent-safe.
# @PRE: Repository for dashboard_id exists.
# @POST: Returns a list of branch metadata dictionaries.
# @POST: Returns a list of branch metadata dictionaries (no tag refs).
# @RETURN: List[dict]
def list_branches(self, dashboard_id: int) -> list[dict]:
with belief_scope("GitService.list_branches"):
repo = self.get_repo(dashboard_id)
logger.reason(f"Listing branches for {dashboard_id}. Refs: {repo.refs}", extra={"src": "list_branches"})
branches = []
for ref in repo.refs:
with self._locked(dashboard_id):
with belief_scope("GitService.list_branches"):
repo = self.get_repo(dashboard_id)
logger.reason(f"Listing branches for {dashboard_id}. Refs: {repo.refs}", extra={"src": "list_branches"})
branches = []
for ref in repo.refs:
try:
# Fix 8: Skip tag refs
ref_name = str(ref.name)
if ref_name.startswith('refs/tags/'):
continue
name = ref_name.replace('refs/heads/', '').replace('refs/remotes/origin/', '')
if any(b['name'] == name for b in branches):
continue
branches.append({
"name": name,
"commit_hash": ref.commit.hexsha if hasattr(ref, 'commit') else "0000000",
"is_remote": ref.is_remote() if hasattr(ref, 'is_remote') else False,
"last_updated": datetime.fromtimestamp(ref.commit.committed_date) if hasattr(ref, 'commit') else datetime.utcnow()
})
except Exception as e:
logger.reason(f"Skipping ref {ref}: {e}", extra={"src": "list_branches"})
try:
name = ref.name.replace('refs/heads/', '').replace('refs/remotes/origin/', '')
if any(b['name'] == name for b in branches):
continue
branches.append({
"name": name,
"commit_hash": ref.commit.hexsha if hasattr(ref, 'commit') else "0000000",
"is_remote": ref.is_remote() if hasattr(ref, 'is_remote') else False,
"last_updated": datetime.fromtimestamp(ref.commit.committed_date) if hasattr(ref, 'commit') else datetime.utcnow()
})
active_name = repo.active_branch.name
if not any(b['name'] == active_name for b in branches):
branches.append({
"name": active_name, "commit_hash": "0000000",
"is_remote": False, "last_updated": datetime.utcnow()
})
except Exception as 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):
branches.append({
"name": active_name, "commit_hash": "0000000",
"is_remote": False, "last_updated": datetime.utcnow()
})
except Exception as e:
logger.reason(f"Could not determine active branch: {e}", extra={"src": "list_branches"})
if not branches:
branches.append({
"name": "dev", "commit_hash": "0000000",
"is_remote": False, "last_updated": datetime.utcnow()
})
return branches
logger.reason(f"Could not determine active branch: {e}", extra={"src": "list_branches"})
if not branches:
branches.append({
"name": "dev", "commit_hash": "0000000",
"is_remote": False, "last_updated": datetime.utcnow()
})
return branches
# endregion list_branches
# region create_branch [TYPE Function]
# @PURPOSE: Create a new branch from an existing one.
# region create_branch [C:4] [TYPE Function] [SEMANTICS git,branch,create,lock]
# @PURPOSE: Create a new branch from an existing one (concurrent-safe).
# @PARAM: name (str) - New branch name.
# @PARAM: from_branch (str) - Source branch.
# @PRE: Repository exists; name is valid; from_branch exists or repo is empty.
# @POST: A new branch is created in the repository.
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)
with self._locked(dashboard_id):
with belief_scope("GitService.create_branch"):
repo = self.get_repo(dashboard_id)
logger.reason(f"Creating branch {name} from {from_branch}", extra={"src": "create_branch"})
if not repo.heads and not repo.remotes:
logger.reason("Repository is empty. Creating initial commit to enable branching.", extra={"src": "create_branch"})
@@ -157,26 +170,28 @@ class GitServiceBranchMixin:
raise
# endregion create_branch
# region checkout_branch [TYPE Function]
# @PURPOSE: Switch to a specific branch.
# region checkout_branch [C:4] [TYPE Function] [SEMANTICS git,branch,checkout,lock]
# @PURPOSE: Switch to a specific branch (concurrent-safe).
# @PRE: Repository exists and the specified branch name exists.
# @POST: The repository working directory is updated to the specified branch.
def checkout_branch(self, dashboard_id: int, name: str):
with belief_scope("GitService.checkout_branch"):
repo = self.get_repo(dashboard_id)
with self._locked(dashboard_id):
with belief_scope("GitService.checkout_branch"):
repo = self.get_repo(dashboard_id)
logger.reason(f"Checking out branch {name}", extra={"src": "checkout_branch"})
repo.git.checkout(name)
# endregion checkout_branch
# region commit_changes [TYPE Function]
# @PURPOSE: Stage and commit changes.
# region commit_changes [C:4] [TYPE Function] [SEMANTICS git,commit,stage,lock]
# @PURPOSE: Stage and commit changes (concurrent-safe).
# @PARAM: message (str) - Commit message.
# @PARAM: files (List[str]) - Optional list of specific files to stage.
# @PRE: Repository exists and has changes (dirty) or files are specified.
# @POST: Changes are staged and a new commit is created.
def commit_changes(self, dashboard_id: int, message: str, files: list[str] = None):
with belief_scope("GitService.commit_changes"):
repo = self.get_repo(dashboard_id)
with self._locked(dashboard_id):
with belief_scope("GitService.commit_changes"):
repo = self.get_repo(dashboard_id)
if not repo.is_dirty(untracked_files=True) and not files:
logger.reason(f"No changes to commit for dashboard {dashboard_id}", extra={"src": "commit_changes"})
return