feat: add repository mesh reconciliation round

This commit is contained in:
2026-04-30 10:50:07 -03:00
parent 3d2748adf5
commit b79fdce99d
113 changed files with 81555 additions and 22807 deletions

View File

@@ -0,0 +1,389 @@
from __future__ import annotations
import json
import unittest
from pathlib import Path
from typing import Sequence
from mais_humana.cli import main
from mais_humana.repository_mesh import (
CommandResult,
MeshActionKind,
MeshEnvironment,
MeshEnvironmentKind,
MeshErrorKind,
MeshPresence,
MeshRiskLevel,
RepositoryTarget,
automation_markdown,
build_mesh_report,
classify_command_error,
command_is_destructive,
default_repository_targets,
mesh_actions_csv,
mesh_inventory_csv,
mesh_markdown,
mesh_orders_payload,
mesh_summary_payload,
normalize_remote_url,
repository_mesh_artifact_records,
run_repository_mesh,
validate_report,
)
from tests.helpers import make_tmp
def make_repo(root: Path, name: str) -> Path:
repo = root / name
(repo / ".git").mkdir(parents=True)
return repo
class FakeGit:
def __init__(self) -> None:
self.calls: list[tuple[str, ...]] = []
self.status_by_repo: dict[str, tuple[str, ...]] = {}
self.remote_by_repo: dict[str, str] = {}
self.head_by_repo: dict[str, str] = {}
self.branch_by_repo: dict[str, str] = {}
self.upstream_by_repo: dict[str, str] = {}
self.ahead_behind_by_repo: dict[str, str] = {}
self.fetch_error_by_repo: dict[str, str] = {}
def set_repo(
self,
repo: Path,
*,
branch: str = "main",
head: str = "abc1230000000000000000000000000000000000",
remote: str = "https://git.ami.app.br/admin/repo.git",
status: Sequence[str] = (),
upstream: str = "origin/main",
ahead_behind: str = "0 0",
fetch_error: str = "",
) -> None:
key = str(repo)
self.branch_by_repo[key] = branch
self.head_by_repo[key] = head
self.remote_by_repo[key] = remote
self.status_by_repo[key] = tuple(status)
self.upstream_by_repo[key] = upstream
self.ahead_behind_by_repo[key] = ahead_behind
if fetch_error:
self.fetch_error_by_repo[key] = fetch_error
def __call__(self, argv: Sequence[str], cwd: Path | None = None, timeout: int = 60) -> CommandResult:
del cwd, timeout
args = tuple(str(item) for item in argv)
self.calls.append(args)
try:
repo = args[args.index("-C") + 1]
except ValueError:
repo = ""
command = args[args.index(repo) + 1 :] if repo else args
if command[:3] == ("fetch", "--all", "--prune"):
if repo in self.fetch_error_by_repo:
stderr = self.fetch_error_by_repo[repo]
return CommandResult(args, 1, "", stderr, error_kind=classify_command_error(stderr, 1))
return CommandResult(args, 0, "", "")
if command[:2] == ("branch", "--show-current"):
return CommandResult(args, 0, self.branch_by_repo.get(repo, "main") + "\n", "")
if command[:2] == ("rev-parse", "HEAD"):
return CommandResult(args, 0, self.head_by_repo.get(repo, "abc123") + "\n", "")
if command[:3] == ("remote", "get-url", "origin"):
return CommandResult(args, 0, self.remote_by_repo.get(repo, "https://git.ami.app.br/admin/repo.git") + "\n", "")
if command[:2] == ("status", "--short"):
return CommandResult(args, 0, "\n".join(self.status_by_repo.get(repo, ())) + "\n", "")
if command[:3] == ("log", "-1", "--oneline"):
return CommandResult(args, 0, self.head_by_repo.get(repo, "abc123")[:7] + " test\n", "")
if command[:4] == ("rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"):
upstream = self.upstream_by_repo.get(repo, "origin/main")
if upstream:
return CommandResult(args, 0, upstream + "\n", "")
return CommandResult(args, 1, "", "fatal: no upstream configured", error_kind=MeshErrorKind.UNKNOWN)
if command[:3] == ("rev-list", "--left-right", "--count"):
return CommandResult(args, 0, self.ahead_behind_by_repo.get(repo, "0 0") + "\n", "")
return CommandResult(args, 1, "", "unexpected command", error_kind=MeshErrorKind.UNKNOWN)
class RepositoryMeshTests(unittest.TestCase):
def test_default_targets_include_nominal_reconciliation_cases(self) -> None:
targets = default_repository_targets()
names = {item.declared_name: item for item in targets}
self.assertIn("tudo-para-ia-mais-humana-plataform", names)
self.assertIn("tudo-para-ia-mais-humana", names["tudo-para-ia-mais-humana-plataform"].aliases)
self.assertTrue(names["tudo-para-ia-mais-humana-plataform"].requires_nominal_reconciliation)
self.assertIn("tudo-para-ia-integracoes-platform", names)
self.assertEqual(names["tudo-para-ia-integracoes-platform"].canonical_name, "tudo-para-ia-integracoes-plataform")
def test_remote_normalization_ignores_git_suffix_and_slash(self) -> None:
self.assertEqual(
normalize_remote_url("https://git.ami.app.br/admin/repo.git/"),
normalize_remote_url("https://git.ami.app.br/admin/repo"),
)
def test_error_classifier_handles_dubious_credentials_auth_network_and_repo(self) -> None:
self.assertEqual(classify_command_error("fatal: detected dubious ownership", 1), MeshErrorKind.DUBIOUS_OWNERSHIP)
self.assertEqual(classify_command_error("SEC_E_NO_CREDENTIALS credenciais nao disponiveis", 1), MeshErrorKind.CREDENTIALS_MISSING)
self.assertEqual(classify_command_error("Authentication failed for https://x", 1), MeshErrorKind.AUTHENTICATION)
self.assertEqual(classify_command_error("Could not resolve host: git.ami.app.br", 1), MeshErrorKind.NETWORK)
self.assertEqual(classify_command_error("fatal: not a git repository", 1), MeshErrorKind.NOT_A_REPOSITORY)
self.assertEqual(classify_command_error("", 0), MeshErrorKind.NONE)
def test_command_destructive_guard_allows_fetch_and_ff_only_but_blocks_reset_pull_clean(self) -> None:
self.assertFalse(command_is_destructive("git fetch --all --prune"))
self.assertFalse(command_is_destructive("git merge --ff-only @{u}"))
self.assertTrue(command_is_destructive("git reset --hard HEAD"))
self.assertTrue(command_is_destructive("git clean -fdx"))
self.assertTrue(command_is_destructive("git pull origin main"))
self.assertTrue(command_is_destructive("git checkout main"))
self.assertTrue(command_is_destructive("git restore ."))
def test_clean_repository_gets_safe_fetch_action(self) -> None:
root = make_tmp()
repo = make_repo(root, "alpha")
fake = FakeGit()
fake.set_repo(
repo,
remote="https://git.ami.app.br/admin/alpha.git",
head="1111111111111111111111111111111111111111",
)
target = RepositoryTarget("alpha", "admin/alpha", "alpha", "01_alpha")
env = MeshEnvironment("primary", MeshEnvironmentKind.WINDOWS_PRIMARY, str(root), "test")
report = build_mesh_report(root, targets=(target,), environments=(env,), runner=fake, fetch=True)
self.assertEqual(report.ok_count, 1)
self.assertEqual(report.blocked_count, 0)
self.assertEqual(report.summaries[0].risk, MeshRiskLevel.OK)
self.assertTrue(any(action.kind == MeshActionKind.FETCH for action in report.summaries[0].actions))
self.assertTrue(any("--prune" in " ".join(call) for call in fake.calls))
self.assertFalse(validate_report(report))
def test_dirty_repository_blocks_destructive_sync(self) -> None:
root = make_tmp()
repo = make_repo(root, "alpha")
fake = FakeGit()
fake.set_repo(
repo,
remote="https://git.ami.app.br/admin/alpha.git",
status=(" M README.md", "?? src/new.py"),
)
target = RepositoryTarget("alpha", "admin/alpha", "alpha", "01_alpha")
env = MeshEnvironment("primary", MeshEnvironmentKind.WINDOWS_PRIMARY, str(root), "test")
report = build_mesh_report(root, targets=(target,), environments=(env,), runner=fake, fetch=False)
summary = report.summaries[0]
self.assertEqual(summary.risk, MeshRiskLevel.BLOCKED)
self.assertEqual(summary.dirty_count, 1)
self.assertTrue(any(action.kind == MeshActionKind.BLOCK_DESTRUCTIVE_SYNC for action in summary.actions))
self.assertIn("working tree sujo", mesh_markdown(report))
def test_divergent_branch_blocks_automatic_reconciliation(self) -> None:
root = make_tmp()
repo = make_repo(root, "alpha")
fake = FakeGit()
fake.set_repo(
repo,
remote="https://git.ami.app.br/admin/alpha.git",
ahead_behind="2 3",
)
target = RepositoryTarget("alpha", "admin/alpha", "alpha", "01_alpha")
env = MeshEnvironment("primary", MeshEnvironmentKind.WINDOWS_PRIMARY, str(root), "test")
report = build_mesh_report(root, targets=(target,), environments=(env,), runner=fake, fetch=False)
self.assertEqual(report.summaries[0].risk, MeshRiskLevel.BLOCKED)
reasons = " ".join(action.reason for action in report.summaries[0].actions)
self.assertIn("ahead/behind", reasons)
self.assertIn("bloqueios contra sync destrutiva", mesh_markdown(report))
def test_remote_mismatch_creates_fix_remote_action_without_auto_execution(self) -> None:
root = make_tmp()
repo = make_repo(root, "alpha")
fake = FakeGit()
fake.set_repo(repo, remote="https://git.ami.app.br/admin/wrong.git")
target = RepositoryTarget("alpha", "admin/alpha", "alpha", "01_alpha")
env = MeshEnvironment("primary", MeshEnvironmentKind.WINDOWS_PRIMARY, str(root), "test")
report = build_mesh_report(root, targets=(target,), environments=(env,), runner=fake, fetch=False)
actions = report.summaries[0].actions
self.assertEqual(report.summaries[0].remote_mismatch_count, 1)
fix_actions = [item for item in actions if item.kind == MeshActionKind.FIX_REMOTE_URL]
self.assertEqual(len(fix_actions), 1)
self.assertFalse(fix_actions[0].can_execute_automatically)
self.assertIn("git remote set-url origin", " ".join(fix_actions[0].command_preview))
def test_missing_repository_creates_clone_plan_but_not_success(self) -> None:
root = make_tmp()
target = RepositoryTarget("alpha", "admin/alpha", "alpha", "01_alpha")
env = MeshEnvironment("primary", MeshEnvironmentKind.WINDOWS_PRIMARY, str(root), "test")
report = build_mesh_report(root, targets=(target,), environments=(env,), runner=FakeGit(), fetch=False)
summary = report.summaries[0]
self.assertEqual(summary.risk, MeshRiskLevel.ATTENTION)
self.assertEqual(summary.missing_count, 1)
self.assertTrue(any(action.kind == MeshActionKind.CLONE_MISSING for action in summary.actions))
self.assertIn("espelho ausente", mesh_markdown(report))
def test_unreachable_environment_is_blocked_as_external_access(self) -> None:
root = make_tmp()
target = RepositoryTarget("alpha", "admin/alpha", "alpha", "01_alpha")
env = MeshEnvironment("server", MeshEnvironmentKind.CODEX_SERVER, str(root / "missing-root"), "remote", local=False)
report = build_mesh_report(root, targets=(target,), environments=(env,), runner=FakeGit(), fetch=False)
summary = report.summaries[0]
self.assertEqual(summary.risk, MeshRiskLevel.BLOCKED)
self.assertEqual(summary.observations[0].presence, MeshPresence.UNREACHABLE_ENVIRONMENT)
self.assertTrue(any(action.kind == MeshActionKind.REQUIRE_ENVIRONMENT_ACCESS for action in summary.actions))
def test_present_non_git_directory_blocks_replace_or_clone(self) -> None:
root = make_tmp()
(root / "alpha").mkdir()
target = RepositoryTarget("alpha", "admin/alpha", "alpha", "01_alpha")
env = MeshEnvironment("primary", MeshEnvironmentKind.WINDOWS_PRIMARY, str(root), "test")
report = build_mesh_report(root, targets=(target,), environments=(env,), runner=FakeGit(), fetch=False)
summary = report.summaries[0]
self.assertEqual(summary.risk, MeshRiskLevel.BLOCKED)
self.assertEqual(summary.observations[0].presence, MeshPresence.PRESENT_NOT_GIT)
self.assertTrue(any(action.destructive for action in summary.actions))
def test_alias_materialization_creates_nominal_rename_action(self) -> None:
root = make_tmp()
repo = make_repo(root, "alpha-old")
fake = FakeGit()
fake.set_repo(repo, remote="https://git.ami.app.br/admin/alpha.git")
target = RepositoryTarget(
"alpha-new",
"admin/alpha",
"alpha-new",
"01_alpha",
aliases=("alpha-old",),
canonical_name="alpha-new",
requires_nominal_reconciliation=True,
)
env = MeshEnvironment("primary", MeshEnvironmentKind.WINDOWS_PRIMARY, str(root), "test")
report = build_mesh_report(root, targets=(target,), environments=(env,), runner=fake, fetch=False)
summary = report.summaries[0]
self.assertEqual(summary.nominal_mismatch_count, 1)
self.assertTrue(any(action.kind == MeshActionKind.RENAME_LOCAL_FOLDER for action in summary.actions))
self.assertIn("materializado como alias", mesh_markdown(report))
def test_fetch_credential_error_is_reported_as_blocker_not_plugin_blocker(self) -> None:
root = make_tmp()
repo = make_repo(root, "alpha")
fake = FakeGit()
fake.set_repo(
repo,
remote="https://git.ami.app.br/admin/alpha.git",
fetch_error="fatal: unable to access url: SEC_E_NO_CREDENTIALS",
)
target = RepositoryTarget("alpha", "admin/alpha", "alpha", "01_alpha")
env = MeshEnvironment("primary", MeshEnvironmentKind.WINDOWS_PRIMARY, str(root), "test")
report = build_mesh_report(
root,
targets=(target,),
environments=(env,),
runner=fake,
fetch=True,
plugin_auth_attempt="user rejected MCP tool call",
)
self.assertEqual(report.credential_errors, 1)
self.assertEqual(report.summaries[0].risk, MeshRiskLevel.BLOCKED)
md = mesh_markdown(report)
self.assertIn("user rejected MCP tool call", md)
self.assertIn("credentials_missing", json.dumps(mesh_summary_payload(report), ensure_ascii=False))
def test_inventory_and_actions_csv_include_required_columns(self) -> None:
root = make_tmp()
repo = make_repo(root, "alpha")
fake = FakeGit()
fake.set_repo(repo, remote="https://git.ami.app.br/admin/alpha.git")
target = RepositoryTarget("alpha", "admin/alpha", "alpha", "01_alpha")
env = MeshEnvironment("primary", MeshEnvironmentKind.WINDOWS_PRIMARY, str(root), "test")
report = build_mesh_report(root, targets=(target,), environments=(env,), runner=fake, fetch=False)
inventory = mesh_inventory_csv(report)
actions = mesh_actions_csv(report)
self.assertIn("nome_declarado,nome_esperado,repositorio_gitea", inventory)
self.assertIn("ahead_behind", inventory)
self.assertIn("action_id,nome_declarado,ambiente,tipo", actions)
def test_automation_markdown_documents_five_minute_safe_cycle(self) -> None:
root = make_tmp()
target = RepositoryTarget("alpha", "admin/alpha", "alpha", "01_alpha")
env = MeshEnvironment("primary", MeshEnvironmentKind.WINDOWS_PRIMARY, str(root), "test")
report = build_mesh_report(root, targets=(target,), environments=(env,), runner=FakeGit(), fetch=False)
text = automation_markdown(report)
self.assertIn("5 minutos", text)
self.assertIn("New-ScheduledTaskAction", text)
self.assertIn("*/5 * * * *", text)
self.assertIn("nunca executar reset", text.lower())
def test_orders_payload_maps_central_active_orders(self) -> None:
root = make_tmp()
central_root = root / "central" / "projects"
order_dir = central_root / "01_alpha" / "orders" / "executivas"
order_dir.mkdir(parents=True)
(order_dir / "0001_EXECUTIVA__teste.md").write_text("# teste\n", encoding="utf-8")
repo = make_repo(root, "alpha")
fake = FakeGit()
fake.set_repo(repo, remote="https://git.ami.app.br/admin/alpha.git")
target = RepositoryTarget("alpha", "admin/alpha", "alpha", "01_alpha")
env = MeshEnvironment("primary", MeshEnvironmentKind.WINDOWS_PRIMARY, str(root), "test")
report = build_mesh_report(root, central_root=central_root, targets=(target,), environments=(env,), runner=fake)
payload = mesh_orders_payload(report)
self.assertEqual(payload["repositories"][0]["centralFolder"], "01_alpha")
self.assertEqual(len(payload["repositories"][0]["activeOrders"]), 1)
def test_run_repository_mesh_writes_all_artifacts(self) -> None:
tmp = make_tmp()
ecosystem = tmp / "eco"
project = tmp / "human"
central = tmp / "central" / "projects" / "15_repo_tudo-para-ia-mais-humana"
ecosystem.mkdir()
project.mkdir()
central.mkdir(parents=True)
repo = make_repo(ecosystem, "alpha")
fake = FakeGit()
fake.set_repo(repo, remote="https://git.ami.app.br/admin/alpha.git")
target = RepositoryTarget("alpha", "admin/alpha", "alpha", "01_alpha")
env = MeshEnvironment("primary", MeshEnvironmentKind.WINDOWS_PRIMARY, str(ecosystem), "test")
report = build_mesh_report(ecosystem, targets=(target,), environments=(env,), runner=fake)
self.assertFalse(validate_report(report))
records = repository_mesh_artifact_records(project, central)
self.assertTrue(any("repository-mesh-inventory.json" in item["path"] for item in records))
report, written = run_repository_mesh(
ecosystem,
project,
central_platform_folder=central,
fetch=False,
plugin_auth_attempt="user rejected MCP tool call",
runner=fake,
)
self.assertTrue((project / "dados" / "repository-mesh-inventory.json").exists())
self.assertTrue((project / "matrizes" / "repository-mesh-actions.csv").exists())
self.assertTrue((project / "ecossistema" / "REPOSITORY-MESH-SYNC.md").exists())
self.assertTrue((central / "reports" / "EXECUTADO__repository-mesh-sync.md").exists())
self.assertGreaterEqual(len(written), 8)
def test_cli_repo_mesh_writes_payload(self) -> None:
tmp = make_tmp()
ecosystem = tmp / "eco"
project = tmp / "human"
central = tmp / "central" / "projects" / "15_repo_tudo-para-ia-mais-humana"
ecosystem.mkdir()
project.mkdir()
central.mkdir(parents=True)
code = main(
[
"repo-mesh",
"--ecosystem-root",
str(ecosystem),
"--project-root",
str(project),
"--central-platform-folder",
str(central),
"--plugin-auth-attempt",
"user rejected MCP tool call",
]
)
self.assertEqual(code, 0)
summary = json.loads((project / "dados" / "repository-mesh-summary.json").read_text(encoding="utf-8"))
self.assertEqual(summary["pluginAuthAttempt"], "user rejected MCP tool call")
self.assertEqual(summary["targets"], len(default_repository_targets()))
if __name__ == "__main__":
unittest.main()