theforge-cli 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -79,3 +79,45 @@ tolerar campos desconhecidos e tratar campos conhecidos ausentes como
79
79
  host loopback + sessão, como toda a API do portal).
80
80
  - Projeção é whitelist de escalares/containers pequenos — nada de
81
81
  objetos arbitrários vindos do disco.
82
+
83
+ ## Consistência e recuperação
84
+
85
+ O CLI trata `state.json` + `install.json` + venv como um único fato.
86
+ `installConsistency(home, name)` compara os três e classifica:
87
+
88
+ - `ok` — as três fontes concordam;
89
+ - `not-installed` — nada no state, nada no disco;
90
+ - `orphaned` — diretório/venv presente sem entrada no state
91
+ (típico de kill entre "venv criada" e "state commitado");
92
+ - `missing-receipt` — entrada no state sem `install.json`;
93
+ - `unreadable-receipt` — `install.json` existe mas não parseia;
94
+ - `forge-mismatch` / `version-mismatch` — receipt diverge do state;
95
+ - `missing-venv` — registro coerente, venv ausente.
96
+
97
+ Regras do contrato de consistência:
98
+
99
+ 1. Qualquer status diferente de `ok`/`not-installed` é exposto por
100
+ `forge-cli registry` (`consistent: false` + `consistency`) — uma
101
+ instalação pela metade nunca aparece saudável.
102
+ 2. `install` recusa com `stage: "incomplete"` sobre estado
103
+ inconsistente; `--clean` é o caminho de reparo (backup da instalação
104
+ anterior até o commit, restauração em qualquer falha).
105
+ 3. `uninstall --method registry` remove também órfãs (disco sem state),
106
+ senão o reparo ficaria sem saída.
107
+ 4. O commit é a última fronteira: receipt + state são gravados juntos
108
+ sob o lifecycle lock; falha nesse ponto é `stage: "persist"` com o
109
+ errno (EACCES/ENOSPC) no detalhe — o disco pode conter a venv, mas
110
+ a instalação permanece "não saudável" até o reparo.
111
+ 5. Escritas são atômicas (tmp + fsync + rename) e varrem `*.tmp`
112
+ órfãos de runs mortos com SIGKILL.
113
+
114
+ ## Lifecycle lock
115
+
116
+ `~/.forge-cli/forges/.forge-lifecycle.lock` é um diretório criado com
117
+ `mkdir` (atômico por construção de fs) contendo `info.json`
118
+ (`pid`, `at`). Titular com PID morto é quebrado na hora; lock sem
119
+ metadata legível só é stale após o threshold de idade; a liberação é
120
+ best-effort. O lock serializa install/uninstall/pin/update — quem
121
+ perde a corrida re-checa o state sob o lock antes de recusar, então
122
+ `already-installed`/`incomplete` refletem o estado pós-serialização,
123
+ nunca uma corrida de observação.
@@ -83,3 +83,49 @@ erro de binário esbuild, rode
83
83
  spec nova.
84
84
  - `vitest` é `^5` para evitar vulnerabilidades do `tinypool` (ver
85
85
  `npm audit`).
86
+
87
+ ## Instalação registry incompleta ou interrompida
88
+
89
+ Uma instalação morta no meio (SIGKILL, queda de energia, disco cheio)
90
+ nunca se apresenta como saudável: `forge-cli registry` marca a linha
91
+ com `incompleta` e o JSON carrega `consistency` com o motivo exato:
92
+
93
+ | `consistency` | Significado |
94
+ | -------------------- | -------------------------------------------------- |
95
+ | `orphaned` | venv/arquivos no disco sem entrada no `state.json` |
96
+ | `missing-receipt` | entrada no state, mas `install.json` ausente |
97
+ | `unreadable-receipt` | `install.json` não parseia |
98
+ | `forge-mismatch` | `install.json` registra outra Forja |
99
+ | `version-mismatch` | `install.json` e `state.json` divergem na versão |
100
+ | `missing-venv` | state+receipt coerentes, mas a venv sumiu |
101
+
102
+ Reparo documentado (as duas únicas portas de saída honestas):
103
+
104
+ ```bash
105
+ forge-cli reinstall --method registry <forja> --clean # regrava tudo com backup da anterior
106
+ forge-cli uninstall --method registry <forja> # remove venv + state + receipt (inclui órfãs)
107
+ ```
108
+
109
+ `install` sem `--clean` recusa com `incomplete` — ela nunca sobrescreve
110
+ uma instalação pela metade fingindo que já existia.
111
+
112
+ ## Lifecycle lock preso (`busy`)
113
+
114
+ Operações registry (install/uninstall/pin) serializam por
115
+ `~/.forge-cli/forges/.forge-lifecycle.lock`. Se o erro diz `busy`:
116
+
117
+ - **Outra execução em andamento** — espere terminar; o titular é o PID
118
+ gravado em `.forge-lifecycle.lock/info.json`.
119
+ - **Titular morto** (o PID não existe mais) — o próprio CLI detecta e
120
+ quebra o lock automaticamente na próxima execução; nada a fazer.
121
+ - **Lock órfão sem metadata legível** — só é considerado stale depois
122
+ de expirar o threshold de idade; se persistir além disso, remova o
123
+ diretório `.forge-lifecycle.lock` manualmente (seguro: quem cria o
124
+ lock nunca tem trabalho a perder fora dele).
125
+
126
+ ## `state.json` corrompido
127
+
128
+ Leitura nunca escreve: um `state.json` que não parseia é tratado como
129
+ vazio (nada é removido nem sobrescrito) e a evidência só vai para
130
+ `state.json.corrupt` no momento em que uma escrita real acontecer —
131
+ assim `--plan`/`--check`/`registry` (list) não têm efeito colateral.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "theforge-cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "CLI interativa para clonar e configurar o ecossistema Forge",
5
5
  "type": "module",
6
6
  "bin": {