Skip to content

sinkra-os: /sinkra-update — output v5-legacy com event_process bee é impossível de migrar (+3 defeitos; test-kit do bundle já vem 5 FAIL) #72

Description

@lk1407

Contexto

Spoke Cohort Student Edition V5, sinkra-os 3.7.0, skill sinkra-update v1.9.0 (a versão do bundle). Rodei /sinkra-update (full-sweep default) num workspace já mapeado e migrado.

Achei 4 defeitos. Os dois primeiros são de dado; os dois últimos, de sinal (o motor mente sobre o que fez). Todos reproduzidos e verificados. Não achei issue relacionada (busquei corpo + título por sinkra-update, process_id, workflow_kind, prepass, dry-run, sweep-all).

Diagnóstico transversal: 3 dos 4 não são bugs de transform — são falhas de composição da cadeia. Cada um dos 7 transforms foi entregue por uma story própria e é correto isoladamente; ninguém é dono da ordem entre eles. A ordem vive em comentário em prosa e na ordem das linhas de sweep-all.js. Não existe nada que verifique a invariante "se a saída de um transform pode ser rejeitada por um portão posterior, ele tem que rodar antes desse portão" — que é exatamente o que os defeitos 2 e 4 violam. O pre-pass do executor (232.W1.6) já foi essa mesma armadilha, resolvida localmente como caso específico em vez de como regra.


Defeito 1 — process_id migrado como se fosse vocabulário → divergência R18 a cada sweep

Onde: scripts/lib/transform-v5.js#migrateProgress (escrita ~L593 e detecção ~L583)

O modo --state renomeia o sufixo _stem dentro de process_id (sp_acme_stemsp_acme_workspace). Mas process_id é identidade do processo, não vocabulário — e o modo artifact do mesmo transform preserva process.id de propósito (o comentário da L356 diz "já-válidos (sp_x_stem) intactos"), migrando só process.type. Os dois modos do mesmo transform discordam entre si.

Efeito: a identidade é declarada em 3 lugares — progress.json (process_id), sinkra-output.json (process.id) e entity-execution-registry.json (process_id). Só o progress.json é reescrito, então ele passa a divergir dos outros dois.

Agravante: _stem no process_id também entra em looksLegacy, o que derrota o early-return already-unified. Um state já migrado é reescrito a cada invocação — a idempotência não protege, porque o defeito é a própria escrita.

Impacto real medido: o full-sweep reverteu uma reconciliação R18 que tinha sido feita à mão numa sessão limpa horas antes.

Correção: process_id/process.id na denylist, mesma classe de exceção permanente de branch_id (D10) — e fora de looksLegacy.


Defeito 2 — v5-legacy com workflow_kind fora do enum é IMPOSSÍVEL de migrar

Onde: ordem da cadeia em scripts/sweep-all.js (o #7 roda depois do #4)

O transform #1 valida AJV strict contra o schema final antes de gravar, e o schema rejeita as 3 formas que o transform #7 converte (workflow_kind fora do enum, activation_condition escalar, honey_artifact). Como o #7 roda depois do #4, um artefato v5-legacy que carregue qualquer delas HALTa permanentemente no #1 — o #7 nunca chega a agir.

Repro: a fixture do próprio bundle, scripts/test/fixtures/legacy-with-managed-agent.json (derivada do corpus Cohort T1), tem event_processes[2].workflow_kind: "bee". Rodar sweep-all.js sobre ela: transform1 → halt-invalid. É a causa das 5 asserções T60b falhando no test-kit distribuído (node scripts/test/run-tests.js → 556 PASS / 5 FAIL no bundle como entregue).

Atenção ao diagnosticar: o relatório mostra erros AJV espalhados (enum @ /process/type, required @ /entity_types, additionalProperties @ (root)). Isso é ruído a jusante — o #1 aborta, o arquivo fica em disco ainda legacy, e as asserções seguintes validam o arquivo não-migrado. O erro real é um só: enum @ /event_processes/2/workflow_kind.

Impacto: aluno com output legado contendo um event_process bee não tem caminho de migração nenhum.

Correção: pre-pass estrutural antes do #1, irmão de prepass-executor-rename.js. Não dá para reusar o CLI do #7: ele é AJV-strict-or-halt e valida o doc inteiro, que nessa posição ainda é v5-legacy por definição — ele converte certo e então recusa gravar. O pre-pass tem de reusar a lib pura (transform-structural-5x.js#migrateStructural5x) sem o portão AJV, exatamente como o pre-pass do executor reusa renameManagedAgentExecutors.


Defeito 3 — exit code 1 permanente: o alarme que se aprende a ignorar

Onde: scripts/lib/open-decisions.js (MARCAS_DE_COBERTURA) + discovery de sweep-all.js

O sweep sai com exit 1 quando há open_decisions. Num spoke saudável todas as entradas eram benignas, então o exit 1 ficava permanentemente ligado e parava de carregar informação. Duas famílias:

  1. Os pre-passes num doc já na versão final reportam noop com razão declarada ("o prepass cobre versões < X"), que o classificador não cobre → 1 open_decision por pre-pass, por documento.
  2. O discovery adota como candidato a output qualquer arquivo sinkra-output*.json, o que inclui sidecars que o próprio motor escreve (*.migration-report.json) e templates (.sinkra/templates/). O motor reportava a própria saída como "classificar manualmente".

O item 2 tem um risco latente junto: a varredura de outputs alcança .sinkra/templates/, então um template que por acaso satisfizesse o reconhecimento por conteúdo seria migrado de fato — migrar o molde em vez do objeto.

O código já reconhece esse modo de falha: o comentário da marca G3.1 diz textualmente "o alarme que se aprende a ignorar". São as próximas instâncias do mesmo problema.

Correção: marca de cobertura para o noop dos pre-passes — ancorada em schema_version final, porque o veredito de classify() é final OU unknown, e unknown (sem versão, major diferente, semver inválido, versão maior que o alvo) é justamente o caso que precisa de olho humano. Cobrir a frase toda silenciaria os dois. Mais uma política de "nunca foi alvo" (distinta da frozen, que é "foi alvo, hoje é histórico") para sidecar e template, com skip governado — log nomeado + contador.


Defeito 4 — --dry-run reporta HALT falso e assusta sobre artefato migrável

Onde: estrutura da cadeia em sweep-all.js

Cada estágio é um processo separado que lê o arquivo do disco. Com --dry-run nenhum grava, então o estágio N+1 lê o arquivo original, não o resultado do estágio N. Em cadeia que depende de pre-pass isso produz HALT que o run real não produz.

Repro: mesma fixture do defeito 2. --dry-runtransform1: halt-invalid. Run real → transform1: migrated, 0 halts.

Impacto: um aluno que rode o dry-run antes de migrar lê "impossível migrar" sobre um artefato perfeitamente migrável, e para. Um relatório que assusta sem motivo é tão ruim quanto um que cala — os dois ensinam a não confiar no relatório.

Correção: simular estágio a estágio é estruturalmente impossível num pipeline encadeado de processos. O caminho é rodar a cadeia de verdade sobre uma cópia em temp, remapear os paths de volta ao arquivo real (pelo diretório, não pelo arquivo — os estágios derivam irmãos do basename: .legacy.bak, .migration-report.json) e limpar o temp no finally. O contrato read-only (VC-3) se mantém: o temp fica fora da árvore varrida.


Sugestão estrutural

Consertar os 4 individualmente resolve estes 4. O que retira a classe é tornar a cadeia um objeto declarado — ordem e restrições entre transforms em dado, com um teste que falhe quando um transform novo entra num ponto onde um portão posterior rejeita a saída dele. Assim o transform #8 não nasce com o defeito 2 de novo.

Nota sobre distribuição

Apliquei as 4 correções localmente para desbloquear e validar (test-kit 556→599 PASS, 0 FAIL; sweep do spoke em 0 halts / 0 open_decisions / exit 0 nos 3 modos). Elas não sobrevivem: .claude/skills/sinkra-*/ é verbatim-install no MANIFEST, então a próxima atualização sobrescreve tudo. Estou reportando, não propondo patch — se o time quiser os diffs, é só pedir.

Relacionado: o G5 já registrado no próprio SKILL.md (drift entre ~/.claude/skills/sinkra-update e a cópia do repo) fica pior com defeito de motor, porque a máquina pode rodar uma versão diferente da que o aluno acha que tem.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions