Skip to content

feat(index): publicar e reverter índices atomicamente - #36

Merged
neomaike merged 11 commits into
mainfrom
feat/793-atomic-index
Jul 30, 2026
Merged

neomaike merged 11 commits into
mainfrom
feat/793-atomic-index

Conversation

@neomaike

Copy link
Copy Markdown
Contributor

Resultado

Implementa a unidade isolada da LexNotify-Web #793, empilhada sobre o contrato de freshness do PR #32.

Escopo

  • estados requested → indexing → validating → ready → publishing → published;
  • terminais failed|cancelled|rolled_back;
  • validação de SHA completo, existência e ancestralidade na branch;
  • worktree detached temporário no commit resolvido;
  • reutilização do scanner canônico por scanner_builder;
  • candidato e manifesto isolados por run;
  • validação de repo, commit, schema, componentes e SHA-256;
  • lock exclusivo, recuperação conservadora de lock órfão e preservação para investigação;
  • compare-and-swap do índice ativo;
  • snapshot anterior e promoção/rollback com os.replace;
  • auditoria metadata-only;
  • ADR com decisão, segurança, limitações e rollback.

Fora de escopo

Auth/RBAC/tenant (#792), carga/SLOs (#795), expansão de tools (#794), deploy, rollout e produção.

Evidência

  • lifecycle: 11 passed;
  • freshness herdada: 17 passed;
  • suíte completa empilhada: 98 passed;
  • git diff --check: limpo;
  • diff próprio: 3 arquivos, 641 inserções.

Cobertura inclui sucesso, candidato inválido, commit/schema divergente, SHA curto/inexistente/outra branch, lock concorrente e órfão, CAS, snapshot, rollback, snapshot corrompido, cancelamento e auditoria sem conteúdo.

Segurança e compatibilidade

O builder nunca recebe o caminho ativo. O worktree isolado pertence à execução e é o único temporário removido. Falhas preservam candidato/snapshot para investigação e não substituem o índice válido. Não altera as sete tools nem o contrato público do MCP.

Rollback

Reverter este PR remove o lifecycle sem tocar em índices existentes. O rollback operacional valida o checksum do snapshot e troca atomicamente o arquivo ativo. Nenhum rollout foi executado.

Dependência

Este PR deve ser revisado depois do PR #32. A base empilhada evita misturar #791 e #793; após integração da #791, poderá ser retarget para main.

@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
neomaike changed the base branch from fix/791-mcp-freshness to main July 30, 2026 18:09
@neomaike
neomaike marked this pull request as ready for review July 30, 2026 18:10
@neomaike
neomaike merged commit e5e9195 into main Jul 30, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

if target in ACTIVE_STATES or target == "failed":
_atomic_json(self.marker_path, state)

P1 Badge Remove the indexing marker when cancellation completes

When either cancellation check succeeds, _state(..., "cancelled") skips this branch, leaving the earlier requested or indexing marker in place. The MCP freshness path treats any such marker as an active build and blocks every architecture query, so a cancelled run leaves the last valid index unusable indefinitely unless another publication succeeds or someone manually removes the marker.


orphan = self.state_root / f"publish.lock.orphaned.{existing.get('runId', 'unknown')}"
try:
os.replace(self.lock_path, orphan)
except OSError as move_error:

P1 Badge Make orphan-lock takeover conditional on the observed lock

If two publishers simultaneously inspect the same dead-PID lock, the first can move it and acquire a fresh lock before the second reaches this os.replace; the second then moves the first publisher's new lock to the orphan path and acquires another lock itself. Both publishers can consequently enter the critical section, and either publisher's unconditional release can also unlink the other's lock, defeating the serialization needed for publication.


os.replace(publish_tmp, self.active_path)
if _sha256(self.active_path) != artifact_hash:
raise IndexLifecycleError("published index failed integrity verification")

P1 Badge Recover publication failures after replacing the active index

Once this replacement succeeds, several fallible operations still remain, including integrity hashing and atomic manifest/state/audit writes. If any of them fails—for example because the state filesystem is full—the exception path reports the run as failed while leaving the candidate installed as the active index, so callers observe a failed publication even though the previously valid index was replaced; the post-replacement failure path should restore the verified snapshot or recover the run as published.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@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