Skip to content

fix(mcp): bloquear conclusões quando o índice não estiver fresh - #32

Merged
neomaike merged 9 commits into
mainfrom
fix/791-mcp-freshness
Jul 30, 2026
Merged

neomaike merged 9 commits into
mainfrom
fix/791-mcp-freshness

Conversation

@neomaike

@neomaike neomaike commented Jul 28, 2026 •

Copy link
Copy Markdown
Contributor

Resultado

Implementa e endurece o contrato de freshness da issue LexNotify/LexNotify-Web#791 sobre a API canônica architecture-map.

Parent Epic: LexNotify/LexNotify-Web#780

Causa-raiz e achado da revisão forense

O índice expunha sourceSha, mas nenhuma tool comparava esse SHA com uma precondição confiável do chamador. A primeira versão do PR ainda permitia um bypass: quando expectedCommit era omitido, o servidor substituía silenciosamente o valor pelo HEAD observado e podia declarar fresh sem precondição externa.

A revisão corrigiu esse bypass: ausência de expectedCommit agora resulta em unknown e bloqueio fail-closed. Não há comparação por prefixo; somente SHA completo de 40 caracteres, existente e ancestral do checkout servido, é aceito.

Alterações

  • get_map_status retorna repositório, branch, commit esperado/observado/indexado, timestamp, schema, contagens, escopos, freshness e motivo;
  • estados: fresh, stale, unknown, incompatible, indexing, failed;
  • tools conclusivas bloqueiam com INDEX_NOT_FRESH quando o estado não é fresh;
  • erro estruturado diferencia freshness, backend indisponível e, na pilha posterior, autenticação;
  • marker .indexing impede conclusões durante construção isolada;
  • marker ausente/corrompido, SHA curto/malformado/inexistente, outra branch e commit divergente falham fechados;
  • respostas históricas são explicitamente marcadas e não apresentadas como atuais.

Matriz resumida

Estado Tools conclusivas Recuperação
fresh permitidas nenhuma
stale bloqueadas reindexar para o SHA esperado
unknown bloqueadas fornecer precondição válida/corrigir metadados
incompatible bloqueadas reconstruir com schema compatível
indexing bloqueadas aguardar publicação válida
failed bloqueadas diagnosticar backend/marker e manter último índice válido

Compatibilidade e escopo

Preserva os nomes das sete tools observadas. expectedCommit continua interpretável como parâmetro opcional para clientes antigos, mas a omissão não autoriza conclusão: falha fechada. Auth, reindexação e carga permanecem em PRs separados (#37, #36 e #38).

Testes locais

  • freshness: 17 passed;
  • suíte completa desta branch: 87 passed;
  • git diff --check: limpo;
  • head revisado: a660745cc7e755d43144be65bcf22d6afef97123.

Cobertura inclui todos os estados, SHA ausente/curto/inválido/inexistente, commit de outra branch/anterior/divergente, marker ausente/corrompido, schema incompatível, backend indisponível e bypass por omissão.

Riscos e gates

Clientes que omitem a precondição deixam de receber resposta conclusiva. Isso é intencional. O PR permanece draft: não há revisão humana nem checks de CI observados no repositório do kit; rollout depende da integração ordenada #32 → #36 → #37 → #38 e dos gates do Epic.

Rollback

Reverter os commits desta branch. Nenhum deploy, merge, migration ou produção foi executado.

neomaike and others added 7 commits July 28, 2026 15:42
The master plan (section 7) is explicit that "the service name is
architecture-map, not lexnotify-map" -- the server was already generic
(single --repo-root arg, no LexNotify-specific logic), just mislabeled.
Renames the FastMCP instance and its primary tool names to match section
7.1 (search_capability, get_component, get_map_status), keeping the old
names as deprecated aliases for backward compatibility. Adds
verify_references, the one tool from section 7.1 that had no implementation
at all (evidence cross-checking per section 3.5). find_similar and
get_impact_graph remain unimplemented -- both need import-edge extraction
the scanner doesn't produce yet, documented as a known gap in the module
docstring rather than silently missing.

Companion PR in LexNotify-Web (#805) updates map-guardian.md to the new
tool names.
…liases

Review feedback on this PR: the only confirmed consumer org-wide
(map-guardian.md in LexNotify-Web, the sole hit for `lexnotify-map` across
a full gh api search/code sweep) is already migrated to the new names in
the companion PR (LexNotify/LexNotify-Web#805). Keeping unvalidated
duplicate tools around for a consumer that doesn't exist just doubles the
tool surface an agent has to parse, with no repoId validation on the old
path -- not backward compatibility, just dead weight from day one.
Follow-up filed to add test coverage for mcp_server.py (had none before or
after this PR).
@neomaike

Copy link
Copy Markdown
Contributor Author

Checkpoint de revisão Fase 4

Este PR foi revisado na base empilhada exata e permanece draft/mergeável, sem review humano ou thread pendente. A pilha combinada termina em 2bfa8edd67a0c8125a4b793c99d7c5c452ed56d2; o envelope de CI #40 termina em f9a1f11c5e9e664d1a709420685b757899fd99a7.

CI combinada: Python 3.10/3.12 e dependency audit/SBOM verdes no run https://github.com/MHX-Digital/agent-architecture-kit/actions/runs/30477544972. Validação local final: 123/123, secret scan 0 matches, compileall/diff check verdes.

Correções foram mantidas no PR que introduziu o comportamento: binding explícito do golden corpus ao commit no #32; fronteira autenticada sanitizada do corpus no #37. Nenhum gate foi relaxado.

Estado: implementado/testado; aguardando revisão humana; não integrado; não implantado. Rollback por revert dos commits da unidade, sem mutação de índice ou ambiente externo.

@neomaike

Copy link
Copy Markdown
Contributor Author

Integração ordenada concluída — 2026-07-30 UTC

main avançou de 63921de34f5265f7c3cb7c45de2df9f43872e932 para 926130c7c9c1e54a6508c5a0cf30f3c012cb6f30.

Unidade Head revisado Merge resultante
PR #32 / #791 2e5a9f992c78b20420ea120d92072feeb7d1931e 02886de9f167eca9df389ef6a4dfa355084ff9c8
PR #36 / #793 39f37d69e75c006ef270b31fdbefe286d599c75f e5e919520741b2c61cb39c17cee451fe0385ed56
PR #37 / #792 10556be22914d540142e1044c098c86526fc3b1f f2e731925d59ef191d78cffa6e7099bd587edce5
PR #40 / #39 f9a1f11c5e9e664d1a709420685b757899fd99a7 46b58ccddd0a853817a0f927181080d667ee2a8d na branch #38
correção PR #45 / #39 bc421de2a8fa099284bf920796fcf525b17f2c85 84116f861aaab875e23ff50824e302f21fe44e99 na branch #38
PR #38 / #795 84116f861aaab875e23ff50824e302f21fe44e99 926130c7c9c1e54a6508c5a0cf30f3c012cb6f30 em main

O #40 já havia sido empilhado no #38; portanto seu conteúdo chegou a main pelo merge final do #38. Nenhum force push, bypass de proteção, deploy ou alteração do LexNotify foi realizado.

Validação no SHA final

Run manual exato: https://github.com/MHX-Digital/agent-architecture-kit/actions/runs/30570028307

  • Python 3.10: verde, 123 testes, 0 falhas;
  • Python 3.12: verde, 123 testes, 0 falhas;
  • dependency audit: verde;
  • sete suítes explícitas locais: 70/70;
  • suíte completa local: 123/123;
  • secret scan: 79 arquivos, 0 matches;
  • schemas, manifestos, compileall e whitespace gate: verdes;
  • as sete tools existentes permanecem nominalmente cobertas.

Artefatos novos, vinculados somente ao SHA final

  • arquivo Git determinístico: c9cb996338ce33d0e8895b83d98aeffcc4ea10d2d1908ad44b4aabc79541299c;
  • SBOM CycloneDX 1.4 (34 componentes): 4005c8a4937f996d4da87448249c85b4158d4325d28cf992fc6959b5f4583bcd;
  • JUnit Python 3.10: 5402ac5a8a407229045e06120dadcec03bfc03c02021a2946043c0238442da64;
  • JUnit Python 3.12: 18750397c61ef086c02babb33a3ae50174e88e79ce61550656181525039d56d7.

Os artefatos da RC anterior não foram reutilizados ou reatribuídos.

Estado: integrado e validado localmente/na CI; não implantado; canário não executado. Próximo gate: autorização humana explícita para o canário sanitizado.

neomaike added a commit that referenced this pull request Jul 30, 2026
…s added since PR #42

PRs #37 (auth) and #32/#36 (index freshness) merged into main after this
test file was written. The mcp_repo fixture now inits a real git repo and
commits it so ARCHITECTURE_MAP.json's sourceSha can match HEAD, and every
tool call passes expectedCommit accordingly. get_adr()'s return shape also
changed to a trust-wrapped dict; updated the two assertions that relied on
the old plain-string return.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant