Skip to content

[P0][Index Lock Regression] Recuperar lock órfão e impedir deep pass preso em deep_running #201

Description

@wesleysimplicio

Regression after: #176
Related: #120, #123, wesleysimplicio/simplicio-runtime#2989 e #3005

Problema medido

O mapper publicado ainda pode ficar preso indefinidamente por um .simplicio/index.lock órfão.

Reprodução em 2026-07-11, Windows, após o preflight atualizar para simplicio-mapper 0.19.0:

  1. simplicio-mapper scan . --json foi executado em simplicio-agent e simplicio-runtime.
  2. O comando retornou deep-pass PIDs 13204 e 15764.
  3. simplicio-mapper status . --await --timeout 120 --json permaneceu:
    • phase=deep_running
    • terminal=false
    • lock=true
  4. Os dois PIDs já não existiam.
  5. Ambos os background-index.log continham repetidamente:
    index skipped: lock already exists at .../.simplicio/index.lock.
  6. No Agent, artifacts_present=false; no Runtime, apenas artefatos anteriores/stale estavam presentes.

Resultado: o survey obrigatório não converge, inspect/handoff não pode produzir prova fresca e o usuário precisa remover lock manualmente, contrariando o contrato de #176.

Objetivo

Tornar aquisição, recuperação e liberação do lock de indexação crash-safe, cross-platform e observável, sem roubar lock de processo vivo.

Contrato proposto

Registro versionado no lock, por exemplo:

simplicio.mapper-index-lock/v1 com pid, process_start_identity, host, created_at, heartbeat_at, owner_token, root_fingerprint, mapper_version e operation.

Reason codes mínimos:

  • lock_acquired
  • lock_live_owner
  • lock_dead_owner_reclaimed
  • lock_expired_reclaimed
  • lock_malformed_reclaimed
  • lock_legacy_reclaimed
  • lock_owner_mismatch
  • worker_died_before_terminal.

Critérios de aceite

  • scan/index detecta PID morto e recupera o lock automaticamente.
  • PID reutilizado não é confundido com o processo original; liveness usa start identity ou equivalente robusto.
  • Lock de processo vivo nunca é roubado; status retorna owner/age/operation e retry guidance.
  • Lock malformed, vazio, legacy PID-only e expirado têm política determinística e reason code.
  • Acquire é atômico; duas execuções concorrentes não entram no deep pass juntas.
  • Release exige owner_token correspondente e não apaga lock alheio.
  • Crash/kill do worker é detectado; status --await termina como failed ou recupera/reinicia conforme policy, nunca fica deep_running com PID morto.
  • --timeout retorna estado terminal/timeout honesto e exit code documentado.
  • Um novo scan após crash converge sem Remove-Item index.lock manual.
  • Paths Unicode/long path e filesystem semantics de Windows são cobertos.
  • Testes cobrem dead/live/reused PID, malformed/legacy lock, concorrência, crash antes/depois de escrever state e owner mismatch.
  • Integração scan -> status --await -> inspect -> handoff passa em repositório temporário.
  • Wheel/CLI instalado é testado, não somente import in-tree.
  • Logs/JSON não expõem dados sensíveis e incluem evidence suficiente para diagnóstico.
  • Benchmark prova que o heartbeat/liveness check não reintroduz latência relevante no macro fast path.

Evidência exigida

  • fixtures de lock por caso;
  • testes unit/integration cross-platform;
  • transcript do kill/recovery;
  • JSON status antes/depois;
  • prova de dois scanners concorrentes;
  • smoke com pacote publicado/instalado;
  • changelog/release note da regressão.

Não objetivos

  • Permitir deep passes concorrentes no mesmo root;
  • apagar locks indiscriminadamente por idade;
  • depender de remoção manual;
  • usar apenas kill(pid, 0) POSIX como prova cross-platform;
  • mascarar falha retornando artefato stale como fresco.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions